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:
@@ -70,6 +70,9 @@ const REQUIRED = [
|
||||
"probe", "fetchStatus", "describeStatus", "reportedIp",
|
||||
"fetchMotorState", "fetchFeedback", "sendFrames", "sendBurst",
|
||||
"fetchVersionNode", "setPeriod", "teardown",
|
||||
/* 电机 OTA 整套:少一个就是"点开始才崩",流程只在 ota.js 写一遍,两条链路都得给 */
|
||||
"otaHostSendsBootFrame", "otaIntegrity",
|
||||
"otaUploadAttempt", "otaStartRelay", "otaStatus", "otaCancel",
|
||||
];
|
||||
const wifi = createWifiTransport(CTX);
|
||||
const ecat = createEcatTransport(CTX);
|
||||
@@ -120,7 +123,17 @@ section("[2] WiFi /motor/state?bin=1 二进制解析");
|
||||
/* ── 3. 能力位 ── */
|
||||
section("[3] 链路能力位");
|
||||
check("WiFi 支持 OTA", wifi.supportsOta === true);
|
||||
check("EtherCAT 不支持 OTA(没有固件上传+relay 通路)", ecat.supportsOta === false);
|
||||
/* EtherCAT 的料走 CoE 邮箱进 GD32 暂存区(DCU_APP comm/ecat_motor_ota.cpp),
|
||||
* 中继引擎与 WiFi 同一个 ⇒ 能力位必须为真。曾经这里是 false,
|
||||
* 因为那条链路只有 ESP32 上传这一条进料路(纯网线打不通设备 IP)。 */
|
||||
check("EtherCAT 支持 OTA(邮箱进料 + 同一个 GD32 relay)", ecat.supportsOta === true);
|
||||
/* 两处真实差异必须被钉住,别被"看着一样"糊过去:
|
||||
* - 进 BOOT 帧谁发(EtherCAT 必须 GD32 在已加锁通道上发,否则运动帧抢邮箱)
|
||||
* - 完整性口径(WiFi 回读 SHA-256,EtherCAT 从站核 CRC32,不许互相冒充) */
|
||||
check("WiFi 由上位机先进 BOOT", wifi.otaHostSendsBootFrame === true);
|
||||
check("EtherCAT 由 GD32 在已加锁通道上先进 BOOT", ecat.otaHostSendsBootFrame === false);
|
||||
check("WiFi 完整性口径 = ESP32 flash 回读 SHA-256", wifi.otaIntegrity === "sha256-esp32-flash");
|
||||
check("EtherCAT 完整性口径 = 从站核 CRC32(进 BOOT 前)", ecat.otaIntegrity === "crc32-verify-in-slave");
|
||||
check("WiFi 支持 0xC5 周期同步", wifi.supportsPeriodSync === true);
|
||||
check("EtherCAT 不做周期同步(PDO 周期由后台循环维持)", ecat.supportsPeriodSync === false);
|
||||
check("EtherCAT 不回报设备 IP", ecat.reportedIp({}) === null);
|
||||
@@ -499,6 +512,13 @@ section("[8] 快照→契约翻译(构造快照,跑生产代码)");
|
||||
JSON.stringify({ periodHits, startHits2 }));
|
||||
check("(i4) 周期值持久化到 localStorage(服务重启后不用重敲)",
|
||||
globalThis.localStorage.getItem("dcu_ecat_period_ms") === "0.25");
|
||||
/* 放开热改:0.5ms 档必须原样下发,不得被前端钳回 0.25 */
|
||||
const got05 = await tp.setPdoPeriod(0.5);
|
||||
check("(i4) setPdoPeriod(0.5) 不钉死 0.25:原样下发并持久化",
|
||||
got05 === 0.5 && periodBody.period_ms === 0.5
|
||||
&& globalThis.localStorage.getItem("dcu_ecat_period_ms") === "0.5",
|
||||
JSON.stringify({ got05, periodBody }));
|
||||
await tp.setPdoPeriod(0.25); /* 回落到默认档,后续 ensureStarted 用例口径不变 */
|
||||
await tp.probe();
|
||||
check("(i4) 循环重启(ensureStarted)自动带上持久化的周期",
|
||||
startHits2 === 1 && startBody2.period_ms === 0.25, JSON.stringify(startBody2));
|
||||
|
||||
Reference in New Issue
Block a user