feat(link): ConnectionHub 统一 WiFi/ECAT/BLE 端点与健康态

各页不再各自读写 dcu_esp_ip / dcu_ecat_adapter:deviceLink 提供
wifiBaseUrl/ecatPost/adapter API,OTA/CAN/motor/ecat_load/lab 走同一入口。
连接灯只覆盖在线布尔,保留调用方语义文案。
This commit is contained in:
cbb
2026-09-16 12:12:24 +08:00
parent 353d936abb
commit 033ece2cca
11 changed files with 210 additions and 110 deletions
+15 -4
View File
@@ -35,13 +35,14 @@
/* IP 存取公共实现(localStorage dcu_esp_ip,与 OTA/CAN 页共用) */
import {
DEFAULT_ESP_IP, getDeviceIp, saveDeviceIp
DEFAULT_ESP_IP, saveDeviceIp
} from "../common/base-url.js";
import { escapeHtml } from "../common/utils.js";
/* 传输层:WiFi(HTTP) / EtherCAT(过程数据) 二选一,业务逻辑完全共用 */
import {
createTransport, loadTransportId, saveTransportId
} from "./transport.js";
import { deviceLink } from "../common/device-link.js";
/* 刷新速度「每链路一份」的纯逻辑(默认值/钳制/旧数据迁移),
* 抽出去是为了能脱离 DOM 自测,见 refresh-rate.js 头注释。 */
import {
@@ -167,9 +168,9 @@ const $ = (s) => document.querySelector(s);
const nodeListEl = $("#nodeList");
/* ─── 工具函数 ─── */
/** 读取主页写入的共享设备 IP(localStorage dcu_esp_ip) */
/** 读取 ConnectionHub / base-url 共享设备 IP(dcu_esp_ip) */
function getSharedDeviceIp() {
return getDeviceIp();
return deviceLink.getIp();
}
/* ─── 输入持久化:刷新/重开页面后恢复上次输入 ───
@@ -267,6 +268,7 @@ function setSharedDeviceIp(ip) {
if (!ip || !String(ip).trim()) return;
const v = String(ip).trim();
state.ip = v;
try { deviceLink.setIp(v); } catch (_) {}
saveDeviceIp(v);
const label = $("#ipLabel");
if (label) label.textContent = "IP " + v;
@@ -4011,8 +4013,17 @@ const scopeSys = (() => {
/* ═══════════════════════ 初始化 ═══════════════════════ */
/* 传输必须最先建:updateConnUi / renderStatusPanel 等都会读 state.tp。 */
useTransport(loadTransportId());
/* 本页不提供连接配置:WiFi 链路读主页 localStorage dcu_esp_ip,进入后自动探测 */
/* 本页不提供连接配置:WiFi 链路读 ConnectionHub 共享 IP,进入后自动探测 */
state.ip = getSharedDeviceIp();
/* 其它页改 IP 后同步本页缓存(外壳 Owner 会再广播 set-ip 后的快照) */
deviceLink.subscribe(function (snap) {
if (!snap || !snap.ip) return;
if (snap.ip !== state.ip) {
state.ip = snap.ip;
const label = $("#ipLabel");
if (label) label.textContent = "IP " + state.ip;
}
});
/* 先恢复用户上次输入(modeSelect / 目标值 / PID / 通道 / 选中节点),
* 再 applyModeVisibility 才能让保存的 mode 决定字段可见性。 */
loadUiState();
+4 -9
View File
@@ -35,6 +35,7 @@
/* 判活阈值纯逻辑(随实测 seq 间隔自适应)住在 refresh-rate.js 的
* 「ECAT 槽判活阈值」一节,纯函数以便脱离 DOM 自测。 */
import { staleLimitMs } from "./refresh-rate.js";
import { deviceLink } from "../common/device-link.js";
/** 单个 PDO 周期回传的状态槽数。
* 固件 1.6.21 起段 2 密排整轮:一拍带齐总线电机(最多 9),空槽不占位。
@@ -94,14 +95,8 @@ function fmtPeriod(ms) {
export function createEcatTransport(ctx) {
const { recordSlow, isTransient } = ctx;
/* 网卡匹配子串。与 ecat.html 的输入框默认值一致(I210,2026-09 网卡 I226→I210);
* 存 localStorage 以便换网卡后不用每次重设。 */
const LS_ADAPTER = "dcu_ecat_adapter";
let adapterSubstr = "I210";
try {
const saved = localStorage.getItem(LS_ADAPTER);
if (saved && saved.trim()) adapterSubstr = saved.trim();
} catch (_) {}
/* 网卡匹配子串:统一从 ConnectionHub 读写(dcu_ecat_adapter),与 OTA/lab 页共用。 */
let adapterSubstr = deviceLink.getEcatAdapter();
/* ── 传输内部累积状态 ──
* 这些都是"WiFi 侧由固件/GD32 代为缓存、EtherCAT 侧必须前端自己攒"的东西。 */
@@ -530,7 +525,7 @@ export function createEcatTransport(ctx) {
const v = String(substr || "").trim();
if (!v || v === adapterSubstr) return false;
adapterSubstr = v;
try { localStorage.setItem(LS_ADAPTER, v); } catch (_) {}
try { deviceLink.setEcatAdapter(v); } catch (_) {}
started = false;
lastSnapshot = null;
try { await api("close", {}, 8000); } catch (_) { /* 没连接时无所谓 */ }