feat: 添加局域网设备发现功能和链路胶囊优化
- 实现局域网设备发现面板,支持探测ESP32中继板并自动切换端点 - 添加POST /api/lan/probe接口用于设备探测,返回三态判决结果 - 实现快扫和全段扫描两种模式,支持种子地址优先探测 - 添加端点唯一写手机制,防止多处同时修改设备IP - 优化链路胶囊UI显示WiFi/EtherCAT/BLE连接状态和设备名称 - 修复EtherCAT链路OTA刷写期间PDO循环暂停和AL状态处理问题 - 调整PDO周期设置范围为0.125-4ms,支持亚毫秒显示为微秒 - 添加ESP32 WiFi使能开关控件样式和数据来源角标显示
This commit is contained in:
+132
-14
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* connection.js - 连接管理 + 系统状态显示
|
||||
*
|
||||
* 操作 DOM 元素 #pillConn / #pillConnTxt / #bConn 等(连接文案仅顶栏)。
|
||||
* 操作 DOM 元素 #pillWifi/#pillEcat/#pillBle / #bConn 等(三链路胶囊在顶栏)。
|
||||
*
|
||||
* 共享状态(通过 window.* 引用):
|
||||
* window.latestSystemStatus — 最近一次 /system/status 响应
|
||||
@@ -14,7 +14,7 @@
|
||||
*/
|
||||
import {
|
||||
formatBytes, formatTime, formatUptime, escapeHtml,
|
||||
setTextIfChanged, setClassIfChanged, setQuietMode,
|
||||
setTextIfChanged, setClassIfChanged, setQuietMode, setCellSource,
|
||||
} from './utils.js';
|
||||
import { addLog } from './log.js';
|
||||
import { showToast } from './toast.js';
|
||||
@@ -72,37 +72,155 @@ function transferOngoing() {
|
||||
} catch (_) { return false; }
|
||||
}
|
||||
|
||||
/**
|
||||
* 顶栏三链路胶囊:WiFi / ECAT / BLE 独立亮灭。
|
||||
* **唯一写入点**:WiFi 文案只在这里生成(mode + 连接态),别处禁止再拼字符串。
|
||||
* ECAT 文案由 app.js 写入(0x1008 slave.name)。
|
||||
* @param {boolean|null} wifiOn WiFi 在线;null=按 DeviceLink 快照
|
||||
* @param {boolean|null} [bleOn] BLE 在线;null=读 window.__bleConnState
|
||||
*/
|
||||
/** @description EtherCAT AL 状态位 → 人话(1=INIT 2=PREOP 3=BOOT 4=SAFEOP 8=OP)。 */
|
||||
function ecatAlText(state) {
|
||||
if (state === null || state === undefined || !Number.isFinite(Number(state))) return '';
|
||||
switch (Number(state) & 0x0F) {
|
||||
case 1: return 'INIT';
|
||||
case 2: return 'PREOP';
|
||||
case 3: return 'BOOT';
|
||||
case 4: return 'SAFEOP';
|
||||
case 8: return 'OP';
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function updateLinkPills(wifiOn, bleOn) {
|
||||
try {
|
||||
var snap = deviceLink.getSnapshot();
|
||||
var fresh = snap && snap.ts && (Date.now() - snap.ts) < 5000;
|
||||
var w = wifiOn;
|
||||
if (fresh && snap.links && snap.links.esp32) w = !!snap.links.esp32.online;
|
||||
if (typeof wifiOn === 'boolean' && !fresh) w = wifiOn;
|
||||
/* WiFi 胶囊:文案 = mode + 连接态 + IP + 信号(STA=Station 客户端模式) */
|
||||
var pw = getEl('pillWifi'), pwT = getEl('pillWifiTxt');
|
||||
if (pw) {
|
||||
var mode = 'STA';
|
||||
var ip = '';
|
||||
var rssi = '';
|
||||
try {
|
||||
if (fresh && snap && snap.esp32Status) {
|
||||
var stWifi = snap.esp32Status.wifi || {};
|
||||
if (stWifi.mode) mode = stWifi.mode;
|
||||
if (stWifi.ip) ip = stWifi.ip;
|
||||
if (stWifi.rssi != null && stWifi.rssi !== '') rssi = String(stWifi.rssi) + ' dBm';
|
||||
}
|
||||
if (!ip) ip = deviceLink.getIp() || '';
|
||||
} catch (_) {}
|
||||
var wifiLabel = mode + (w ? ' 已连接' : ' 未连接');
|
||||
if (w && ip) wifiLabel += ' · ' + ip;
|
||||
if (w && rssi) wifiLabel += ' · ' + rssi;
|
||||
/* 运行时覆盖(重启中/上传中/设备忙):优先显示,避免被 1Hz 轮询冲掉 */
|
||||
if (window.__wifiPillOverride) wifiLabel = window.__wifiPillOverride;
|
||||
setClassIfChanged(pw, 'pill link-pill ' + (window.__wifiPillOverride ? 'processing' : (w ? 'online' : 'offline')));
|
||||
if (pwT) setTextIfChanged(pwT, wifiLabel);
|
||||
}
|
||||
/* ECAT 胶囊:灯色与文案**都由这里出**。此前 app.js 的 0x1008 probe 也写同一颗
|
||||
pill 的 className,两边节奏/判据都不同,实测出现过"绿点 + 文字离线"的矛盾。
|
||||
分级:后端/网卡没开=离线;开了且 PDO 循环在跑=在线;开了但循环没跑=琥珀并标 AL 档。 */
|
||||
var pe = getEl('pillEcat'), peT = getEl('pillEcatTxt');
|
||||
if (pe) {
|
||||
var E = (fresh && snap && snap.links && snap.links.ecat) ? snap.links.ecat : null;
|
||||
var eName = (E && E.deviceName) || window.__ecatProbeName || '';
|
||||
var eCls, eTxt;
|
||||
if (!E) { eCls = 'idle'; eTxt = '未探测'; }
|
||||
else if (!E.busOpen) { eCls = 'offline'; eTxt = '离线(后端/网卡未开)'; }
|
||||
else if (E.loop) { eCls = 'online'; eTxt = eName || (E.op ? 'OP 循环中' : '循环中'); }
|
||||
else {
|
||||
var al = ecatAlText(E.slaveState);
|
||||
eCls = 'processing';
|
||||
eTxt = (eName ? eName + ' · ' : '') + (al ? al + ' 无循环' : '未进 OP');
|
||||
}
|
||||
setClassIfChanged(pe, 'pill link-pill ' + eCls);
|
||||
if (peT) setTextIfChanged(peT, eTxt);
|
||||
}
|
||||
/* BLE 胶囊 */
|
||||
var pb = getEl('pillBle'), pbT = getEl('pillBleTxt');
|
||||
if (pb) {
|
||||
var b = bleOn;
|
||||
if (b === null || b === undefined) {
|
||||
try {
|
||||
var st = window.__bleConnState;
|
||||
if (st === true || st === false) b = st;
|
||||
} catch (_) {}
|
||||
}
|
||||
if (b === null || b === undefined) {
|
||||
setClassIfChanged(pb, 'pill link-pill idle');
|
||||
if (pbT) setTextIfChanged(pbT, '未连接');
|
||||
} else if (b) {
|
||||
setClassIfChanged(pb, 'pill link-pill online');
|
||||
var nm = window.__bleDeviceName || '已连接';
|
||||
var did = window.__bleDeviceId;
|
||||
var bl = did ? (nm + ' · ' + did) : nm;
|
||||
if (pbT) setTextIfChanged(pbT, bl);
|
||||
} else {
|
||||
setClassIfChanged(pb, 'pill link-pill offline');
|
||||
if (pbT) setTextIfChanged(pbT, '未连接');
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
/** WiFi 胶囊临时覆盖文案(重启中/上传中/设备忙);传 null 清除。 */
|
||||
export function setWifiPillOverride(text) {
|
||||
window.__wifiPillOverride = text || null;
|
||||
updateLinkPills(null, null);
|
||||
}
|
||||
|
||||
export function updateConnectionUi(connected, text) {
|
||||
/* DeviceLink 统一层:外壳 Owner 广播的新鲜快照优先于本页局部轮询结论,
|
||||
避免 OTA「已连接」与 LAN9252「失败」同屏分裂。独立直开页无快照则用参数。 */
|
||||
避免 OTA「已连接」与 LAN9252「失败」同屏分裂。独立直开页无快照则用参数。
|
||||
双通路:WiFi 或 ECAT 任一在线即「在线」;角标标出实际在线链路。 */
|
||||
var src = 'wifi';
|
||||
try {
|
||||
var snap = deviceLink.getSnapshot();
|
||||
if (snap && snap.ts && (Date.now() - snap.ts) < 5000) {
|
||||
var w = !!(snap.links && snap.links.esp32 && snap.links.esp32.online);
|
||||
var e = !!(snap.links && snap.links.ecat && snap.links.ecat.online);
|
||||
/* Hub 可把在线压成离线;**不得**把本页刚判的离线改回在线。
|
||||
* Owner 要连失败 3 次才翻离线,期间若无脑覆盖,
|
||||
* OTA 页超时判离线后灯又会被点绿 → 拔线后永远「已连接」。 */
|
||||
if (!snap.links.esp32.online) connected = false;
|
||||
* OTA 页超时判离线后灯又会被点绿 → 拔线后永远「已连接」。
|
||||
* 双通路修正:WiFi 掉线但 ECAT 仍在线时,整体仍算在线(ECAT 撑着)。 */
|
||||
if (!w && !e) connected = false;
|
||||
else if (!connected && (w || e)) {
|
||||
/* 仅当两路里至少一路 Hub 判在线,且本页因 WiFi 超时误判离线时,
|
||||
* 才允许 Hub 把「在线」加回来(ECAT 撑场)。 */
|
||||
if (e && !w) connected = true;
|
||||
}
|
||||
if (w && e) src = 'dual';
|
||||
else if (e && !w) src = 'ecat';
|
||||
else src = 'wifi';
|
||||
if (!text) text = connected ? '已连接' : '离线';
|
||||
if (connected && e && !w) text = text && text.indexOf('ECAT') < 0
|
||||
? (text + ' · ECAT') : text;
|
||||
} else {
|
||||
/* 无快照:参数 connected 来自 WiFi 轮询 */
|
||||
src = connected ? 'wifi' : 'off';
|
||||
}
|
||||
} catch (_) {}
|
||||
} catch (_) {
|
||||
src = connected ? 'wifi' : 'off';
|
||||
}
|
||||
var label = text || (connected ? '已连接' : '未连接');
|
||||
/* 判离线立刻清实时指标:只改灯不等于清数据,否则 APP/缓存下会
|
||||
* 出现「离线 + 温度/电压仍显示」的假活状态。 */
|
||||
if (!connected && typeof window.clearLiveMetricsOnDisconnect === 'function') {
|
||||
try { window.clearLiveMetricsOnDisconnect(); } catch (_) {}
|
||||
}
|
||||
// 仅顶栏连接胶囊(下方连接卡不再重复显示文案)
|
||||
var pc = getEl('pillConn');
|
||||
var pct = getEl('pillConnTxt');
|
||||
if (pc) {
|
||||
setClassIfChanged(pc, connected ? 'pill online' : 'pill offline');
|
||||
if (pct) setTextIfChanged(pct, label);
|
||||
}
|
||||
// 系统状态卡徽章
|
||||
// 顶栏三链路:只刷灯/来源角标;WiFi 文案由 updateLinkPills 统一生成
|
||||
updateLinkPills(connected, null);
|
||||
// 系统状态卡徽章(整体:任一数据通路在线即在线)
|
||||
var bc = getEl('bConn');
|
||||
if (bc) {
|
||||
setTextIfChanged(bc, connected ? '在线' : '离线');
|
||||
setClassIfChanged(bc, 'v ' + (connected ? 'ok' : 'err'));
|
||||
setCellSource('bConn', connected ? src : 'off');
|
||||
}
|
||||
setQuietMode(!connected && !transferOngoing());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user