DCU_DesktopController 专家评审

评审日期 2026-10-19(成稿 2026-10-20)· 仓库 cbb/DCU_DesktopController · 分支 main · 基线 commit eac3717(feat: 添加局域网设备发现功能和链路胶囊优化)· 评审方式:多专家组并行通读 + 抽样复核行号
致命8
严重27
一般33
建议7
问题总数75
综合评分5.4 / 10

1. 项目概述与架构

本仓库是 DCU(GD32E508 主控 + LAN9252 EtherCAT 从站 + ESP32-C3 通信中继)的 上位机调试控制台:HTML/CSS/JS(ES module)纯前端 + 本地 Python 页面服务与桌面窗口外壳(pywebview/WebView2)。仓库内不含固件;业务 API 由 ESP32 固件 80 端口跨域提供,EtherCAT 主站 ecat_native.exe 位于外部 tools/ecat,由本仓 Python 代码拉起/代理。代码约 1.9 MB、184 个文件。

┌──────────────────── 桌面窗口 esp32_app.vbs (pywebview, GUI 进程) ────────────────────┐
│  tabs.html  标签外壳 · DeviceLink Owner(BroadcastChannel) · app-lease WebSocket      │
│   ├─ pages/index.html   OTA:ESP32 WiFi OTA / GD32 中继 / BLE OTA / EtherCAT 直灌     │
│   ├─ pages/can.html     CAN:DBC 解析、信号编码、批量发送 (+ can_monitor 内嵌)       │
│   ├─ pages/motor.html   电机:CAN FD 指令、周期发送、命令序列、示波器 (WiFi | ECAT)   │
│   └─ lab/*.html         SPI/ECAT 测速、LAN9252 电源管理、PDO 布局                    │
│  js_api 桥: ecat_api / ecat_send_raw ──(ESF1 TCP 快发)──┐                           │
└──────────────────────────────────────────────────────────┼───────────────────────────┘
   HTTP 127.0.0.1:2625 (esp32_server.py 子进程)            │
   ├─ 静态文件 + _reload 注入                               ▼
   ├─ /ecat/api/*  反向代理 ──► ecat_client.py ──► ecat_native.exe (8090+, Npcap/I210)
   │                            ecat_proc.py (拉起/互斥/RealTime/强杀)      │ EtherCAT
   ├─ /api/lan/probe ─► lan_probe.py (局域网发现)                          ▼
   └─ WS 租约:全部窗口关闭 → op/stop                          GD32 + LAN9252 从站 ──CAN FD──► 电机
   浏览器 ──HTTP(跨域)──► ESP32:80 (/system/status, /can/*, /ota/*) ──SPI──► GD32
   浏览器 ──Web Bluetooth GATT──► ESP32 (BLE OTA)
模块主要文件职责评分
主机服务/安全esp32_server.py, ecat_client.py, ecat_proc.py, lan_probe.py, *.vbs/bat本地静态服务、ECAT 代理、桌面桥、native 进程治理、局域网发现6/10
电机控制/故障保护js/motor/*, js/common/ecat-bus.js, device-link.js, app-lease.js, desktop-bridge.js, pages/motor.html双链路电机指令、周期发送、序列、示波器、总线所有权4.5/10
OTA/刷写js/app.js, js/ota/*, js/ble/*, js/ble_ota.js, pages/index.htmlESP32/GD32 多通道刷写会话与进度5/10
CAN/公共层js/can.js, js/can/*, js/can_monitor.js, js/common/*DBC 解析编码、发送、监控;连接、轮询、日志6/10
仓库卫生/文档README.md, docs/, .gitignore, .qoder/, .zcode/, scripts/文档、报告、测试脚本与版本控制卫生5.5/10

2. 评审范围与方法

3. 问题清单

3.1 总览

编号严重度模块标题位置
M-1致命电机控制/故障保护无急停/全部失能,停止类操作均不下发失能帧js/motor/main.js:809-811, 2391, 2403, 2494; js/motor/seq.js:449; js/motor/main.js:1103, 11…
M-2致命电机控制/故障保护CMD 数值无范围校验,整数编码会回绕js/motor/main.js:744-756; pages/motor.html:141-149; js/motor/main.js:2462-2470
M-3致命电机控制/故障保护真实链路丢失永远判不出离线js/motor/main.js:969-987, 1175
M-4致命电机控制/故障保护序列停止后立即重启会出现两个并发执行循环js/motor/seq.js:431-447, 449-455, 461-504
O-1致命OTA/刷写同一块 GD32 可被两条通道同时刷写js/ota/session.js:128; js/ota/boot_ecat.js:176-189, 576-577; js/ota/console-mode.js:67, 12…
O-2致命OTA/刷写HEX 解析可放过损坏或截断文件js/ota/file-handle.js:458-516(校验 472-477,分配 506-509)
P-1致命主机服务/安全本地服务无 Origin/Host/Content-Type 校验,任意网页可跨站驱动电机esp32_server.py:480-541(do_POST),504-532(ECAT 代理分支)
P-2致命主机服务/安全ESF1 快发在 sendall 成功后出错仍整包重发ecat_client.py:247-279
C-1严重CAN/公共层DBC 中 [0|0] 被当成真实范围js/can/dbc.js:164-165; js/can/signals.js:299-304
C-11严重CAN/公共层独立 CAN 监控页无视所选设备pages/can_monitor.html:55; js/can_monitor.js:60-68; js/can/dbc.js:40-47
C-12严重CAN/公共层监控页超时永不显示离线js/can_monitor.js:199
C-2严重CAN/公共层扩展多路复用信号被静默丢弃js/can/dbc.js:153
C-4严重CAN/公共层扩展/标准帧靠 ID 数值猜测,发送载荷无帧类型标志js/can/dbc.js:146; js/can/send.js:304, 373, 453, 461
C-5严重CAN/公共层DLC 未按 CAN FD 合法长度校验js/can/send.js:406; js/can/signals.js:233-235
H-1严重仓库卫生/文档AI 工具目录被提交.qoder/repowiki/(约 75 个文件); .zcode/plans/plan-sess_*.md; .gitignore
H-5严重仓库卫生/文档默认 ECAT 周期 0.25 ms 与既有报告建议冲突且缺复测依据js/common/ecat-bus.js:28; js/motor/transport-ecat.js:428-431
M-10严重电机控制/故障保护周期发送与序列计时在后台标签被节流,且无可见性处理js/motor/main.js:2488; js/motor/seq.js:503
M-11严重电机控制/故障保护ECAT 发送不核对 queued,失败也报成功js/motor/transport-ecat.js:803-810
M-5严重电机控制/故障保护ECAT 活性保持把陈旧数据显示为在线js/motor/transport-ecat.js:526, 552, 572-590; js/motor/refresh-rate.js:138
M-6严重电机控制/故障保护「谁可以动总线」红线被多处绕过js/motor/transport-ecat.js:394-459, 668; js/common/device-link.js:250-287, 322-327
M-7严重电机控制/故障保护OTA 忙标志写在 iframe 窗口,外壳 Owner 读不到js/motor/transport-ecat.js:80-82; js/common/device-link.js:255
M-8严重电机控制/故障保护ECAT 桥失败回落 HTTP,非幂等命令可能执行两次js/motor/transport-ecat.js:259-271; js/common/desktop-bridge.js:59-74
M-9严重电机控制/故障保护修改网卡输入框即关闭运行中总线,无任何保护js/motor/transport-ecat.js:899-908; js/motor/main.js:3215-3227
O-3严重OTA/刷写ESP32 重启按钮无 OTA 守卫js/ota/upload-esp32.js:594-613; pages/index.html:368
O-4严重OTA/刷写GD32 完成判定可被陈旧 alive 标志误判js/ota/stages.js:497-499
O-5严重OTA/刷写ESP32 重启后成功判定不核对新版本js/common/polling.js:~236; js/ota/upload-esp32.js:435-449, 468-474
O-6严重OTA/刷写WiFi/BLE OTA 不通知其它标签,端点可中途切换js/ota/boot_ecat.js:598; js/app.js:63-71, 403-421
O-7严重OTA/刷写BLE abort 总发会话 0;中继阶段可随意中止js/ble/session.js:89; js/ble/state.js
O-8严重OTA/刷写BLE GD32 身份检查未 awaitjs/ble/file.js:174, 182-205
O-9严重OTA/刷写前端不校验 ESP32 镜像js/ota/file-handle.js:725-751
P-3严重主机服务/安全健康探测 1 s 超时即无差别强杀所有 ecat_nativeecat_proc.py:124-140, 143-194, 250-253
P-4严重主机服务/安全跨进程锁超时或 CreateMutexW 失败仍继续执行ecat_proc.py:67-94(尤其 72-81)
P-5严重主机服务/安全桌面桥路径不记录循环状态,『关 app 必停从站』可能失效ecat_client.py:470-497; esp32_server.py:331-340, 403-420, 533
P-6严重主机服务/安全外链在窗口内导航,外部页面可调用桌面桥 js_apiesp32_server.py:142-146, 998-1000
P-7严重主机服务/安全ecat_native 提到 REALTIME 优先级,配合自旋线程可能拖死系统;复用实例不提权ecat_proc.py:96-121, 212-215, 273-276
C-10一般CAN/公共层单条发送无防重入js/can/send.js:282, 358, 435
C-13一般CAN/公共层对设备的并发轮询过多js/common/device-link.js:30; js/can.js:131, 424; js/can_monitor.js:487, 490; tabs.html:76-…
C-14一般CAN/公共层轮询超时计时器可能丢失js/common/polling.js:194
C-16一般CAN/公共层读取已存 IP 不校验;网关猜测探测js/common/base-url.js:44-50; js/common/connection.js:761-789
C-3一般CAN/公共层多行 CM_ 注释丢失js/can/dbc.js:178, 184
C-6一般CAN/公共层手动 ID 解析过宽js/can/dbc.js:58
C-7一般CAN/公共层枚举/多路复用/浮点信号编码细节错误js/can/signals.js:45-51, 76, 294-296, 406
C-8一般CAN/公共层持久化静默失败且状态只增不减js/can/persist.js:93-124; js/can.js:229
C-9一般CAN/公共层toast 模块被加载成两个实例js/can/send.js:17; js/can.js:19-20, 47
H-2一般仓库卫生/文档受版本控制文件含个人绝对路径scripts/build_mem.bat:21; README.md:3, 449; docs/compose/spec/host-code-quality.md:67
H-3一般仓库卫生/文档README 脚本清单与 scripts/ 不一致;.gitignore 规则重叠README.md; scripts/check_fe_guard_mutation.py; scripts/ui_diag_motor.mjs; .gitignore; .git…
H-4一般仓库卫生/文档注释/文档与代码不符(默认周期等)js/motor/transport-ecat.js:37, 166, 416-417, 607; README.md:190
H-6一般仓库卫生/文档GD32 WiFi 中继刷写缺『刷写≠生效』版本比对,守卫脚本已删除js/ota/upload-gd32.js; js/ota/stages.js:497-499
H-8一般仓库卫生/文档ecat_integral_report 结论与其附带脚本输出自相矛盾docs/reports/ecat_integral_report.html
M-12一般电机控制/故障保护运行中导入/水合配置会替换正在使用的参数js/motor/main.js:242-289; js/motor/seq.js:223
M-13一般电机控制/故障保护危险默认值js/motor/seq.js:76-89; pages/motor.html:105, 123, 127
M-14一般电机控制/故障保护NaN/Inf 浮点被显示为 0js/motor/main.js:624-627, 666-669
M-15一般电机控制/故障保护磁盘水合后链路状态不一致js/motor/main.js:268, 3172
M-16一般电机控制/故障保护节点 ID 修改不核对 ACK;去重表键不含通道js/motor/main.js:3044-3046
M-19一般电机控制/故障保护自动化测试未覆盖停机/序列竞态/数值范围scripts/test_motor_transport.mjs; scripts/test_motor_refresh.mjs; scripts/test_ecat_autost…
O-10一般OTA/刷写BLE ACK/重传机制薄弱js/ble/gatt.js:249-255; js/ble/transfer.js:45-57, 75-78, 282
O-11一般OTA/刷写BLE 载荷固定 480 B,无 MTU 回退js/ble/constants.js:38, 50; js/ble/state.js:76
O-12一般OTA/刷写BLE 断开不拒绝结果等待js/ble/gatt.js:139-150; js/ble/transfer.js:394
O-13一般OTA/刷写GD32 中继触发超时后可能误判失败js/ota/upload-gd32.js:191-216
O-14一般OTA/刷写ECAT 直灌无完整性校验,api() 不查 HTTP 状态js/ota/boot_ecat.js:89-99, 635-661
P-10一般主机服务/安全代理读取 Content-Length 与 native 响应均无上限esp32_server.py:509-512; ecat_client.py:350-355
P-11一般主机服务/安全ESF 打包静默修改数据ecat_client.py:140-141, 157-166
P-12一般主机服务/安全静态服务暴露整个 BASE_DIR,无扩展名文件被当 HTML 注入脚本esp32_server.py:434, 454-466, 474
P-13一般主机服务/安全WebSocket 实现不完整且不校验 Originesp32_server.py:551-564, 604-606
P-14一般主机服务/安全端口复用与单实例检测存在竞态esp32_server.py:756-779, 897-898, 954
P-15一般主机服务/安全退出路径可能跳过停机ecat_proc.py:301-311; esp32_server.py:348, 873-878
P-8一般主机服务/安全非幂等请求首次可能误报 503ecat_client.py:405, 415, 426-439
P-9一般主机服务/安全桥接路径可写入任意 HTTP 请求行ecat_client.py:93-107, 337-344, 470
C-15建议CAN/公共层大量隐式全局与重复代码js/common/polling.js:10-29; js/common/connection.js:6-13; js/can/send.js:36; js/can_monito…
H-7建议仓库卫生/文档报告中的资源与脚本链接随归档失效docs/reports/ECAT_RATE_TRADEOFF.md; docs/reports/ecat_integral_report.html; docs/reports/R…
M-17建议电机控制/故障保护示波器数据经滤波与重采样,定时器从不停止js/motor/scope.js:31, 228-256, 269
M-18建议电机控制/故障保护main.js 约 3250 行,流控逻辑重复js/motor/main.js:807-948
O-15建议OTA/刷写大量 window.* 全局、猴补 showOpenFilePicker、内存复制js/ota/console-mode.js:170-199; js/ota/upload-esp32.js:213-247; js/ota/progress.js:177; js…
P-16建议主机服务/安全native_stdout.log 只追加不轮转ecat_proc.py:259-262
P-17建议主机服务/安全LAN 探测接口可被当端口扫描器lan_probe.py:320-333; esp32_server.py:673-684

3.2 详细说明

电机控制/故障保护

致命 [M-1] 无急停/全部失能,停止类操作均不下发失能帧

模块
电机控制/故障保护
位置
js/motor/main.js:809-811, 2391, 2403, 2494; js/motor/seq.js:449; js/motor/main.js:1103, 1193; pages/motor.html
现象
页面没有 E-STOP 或「全部失能」。失能需切 mode 0 再经 sendBatch 发送,而 sendBatch 在上一次发送在途时直接跳过(返回 busy),总线冷却期也拒发。停止周期发送、停止序列、断开连接、切换链路都只停本地定时器,不发 Disable。
风险
速度模式下电机将按最后目标持续转动;操作员以为『已停止』,实际电机仍在运动,可能造成机械碰撞或人身伤害。
修改建议
增加独立急停按钮与快捷键(如 Esc/空格),绕过 sendInFlight/冷却门直接对所有节点下发 mode 0 + 零目标(并重复 2~3 次);stopRepeat/stopSeq/disconnect/切链路统一调用 safeStopAll();急停走最短路径(WiFi/ECAT 各自直发)。

致命 [M-2] CMD 数值无范围校验,整数编码会回绕

模块
电机控制/故障保护
位置
js/motor/main.js:744-756; pages/motor.html:141-149; js/motor/main.js:2462-2470
现象
buildCmdBytes 中电压 Math.round(v*1000)&0xFFFF(输入 −1 V 实际下发约 64.5 V);PID 增益走 setInt16(40000 回绕为负增益);电流/速度/角度无上限;HTML min/max 不被 JS 强制;±9999 快捷按钮无确认即发送。
风险
误输入或误点即可下发远超额定的电压/速度或负增益,导致失控、过流、驱动器或电机损坏。
修改建议
在 buildCmdBytes 入口集中 clamp/拒绝:电压 0~额定值、电流/速度/角度按电机参数限幅、PID 0~32767;非法值抛错而非静默编码;大幅值按钮二次确认;为编码函数补单元测试。

致命 [M-3] 真实链路丢失永远判不出离线

模块
电机控制/故障保护
位置
js/motor/main.js:969-987, 1175
现象
isTransientError 把超时、AbortError、Failed to fetch、ECONNREFUSED 等全部判为瞬时;noteHardFailure 对 transient 直接 return,不计数。注释所称『网络类加权/更高阈值兜底』在代码中并不存在。
风险
WiFi 断开/设备掉电时页面永不进入离线态,周期发送与序列静默继续,界面把陈旧数据当实时显示,操作员失去态势感知。
修改建议
瞬时错误单独计数(如连续 N 次或持续 T 秒无成功即判离线);离线时自动停止周期发送/序列并提示;补一个『拔线』契约测试。

致命 [M-4] 序列停止后立即重启会出现两个并发执行循环

模块
电机控制/故障保护
位置
js/motor/seq.js:431-447, 449-455, 461-504
现象
stopSeq 只置 running=false、stopRequested=true,不等待旧循环退出,也无代次令牌;startSeq 又把 running=true、stopRequested=false。旧循环若正处于 await sleep(holdMs)(最长 600 s)醒来后看到 running 仍为 true 便继续执行。
风险
两个循环交错下发不同步骤,电机运动序列错乱。
修改建议
引入 runId/generation:startSeq 自增,循环每个 await 后校验 myId===seq.runId;或 stopSeq 返回 Promise,待旧循环退出后才允许重启;sleep 改为可中断(AbortSignal)。

严重 [M-10] 周期发送与序列计时在后台标签被节流,且无可见性处理

模块
电机控制/故障保护
位置
js/motor/main.js:2488; js/motor/seq.js:503
现象
周期发送用主线程 setInterval,序列保持用 setTimeout;隐藏 iframe/最小化后被浏览器降到 ≥1 s 乃至分钟级。只有状态轮询走 Worker。无 visibilitychange 停止/告警;WiFi 链路关窗时无停机。
风险
后台时控制节拍严重失真,序列时间轴不可预测。
修改建议
发送节拍迁入 tick.worker.js;visibilitychange 时提示或按策略安全停机;pagehide 时对 WiFi 也发停机(sendBeacon)。

严重 [M-11] ECAT 发送不核对 queued,失败也报成功

模块
电机控制/故障保护
位置
js/motor/transport-ecat.js:803-810
现象
对每帧都返回 ok,未比较 queued 与帧数。
风险
native 队列满/丢帧时界面显示发送成功。
修改建议
queued < frames.length 时逐帧标记失败并上报 backlog。

严重 [M-5] ECAT 活性保持把陈旧数据显示为在线

模块
电机控制/故障保护
位置
js/motor/transport-ecat.js:526, 552, 572-590; js/motor/refresh-rate.js:138
现象
槽位消失后在 staleLimitMs(可达 30 s)内仍报 online:true 并返回旧数据,另叠加 500 ms 软保持。
风险
电机已离线/从站已掉 OP 时界面仍显示正常状态。
修改建议
保持期内明确标记 stale(灰显+时间戳),上限压到 1~2 s;超过即 online:false。

严重 [M-6] 「谁可以动总线」红线被多处绕过

模块
电机控制/故障保护
位置
js/motor/transport-ecat.js:394-459, 668; js/common/device-link.js:250-287, 322-327
现象
transport-ecat.ensureStarted 在 isBusHealthy 为 false(含 unknown)时调用 motor/start,不带 reason、不走 ensureEcatLoopRepair 预算,并重复实现 ecat-bus.resolveEcatStartBody 的周期/DC 逻辑;Owner bootstrapEcatBus 同样无 reason 且把 unknown 当需要启动;repairEcatDc 无间隔/次数预算,pollEcat 每 2 s 与 healthCheck 都可触发。
风险
健康总线被误重启、OTA/刷写期间总线被抢、DC 修复风暴。
修改建议
所有 start/repair 统一经 ecat-bus 单入口(带 reason、预算与 OTA 闸);unknown 状态不得触发启动;删除 transport-ecat 内重复的周期计算(与 host-code-quality R6 同源)。

严重 [M-7] OTA 忙标志写在 iframe 窗口,外壳 Owner 读不到

模块
电机控制/故障保护
位置
js/motor/transport-ecat.js:80-82; js/common/device-link.js:255
现象
setMotorOtaEcatBusy 写的是电机页 iframe 自身的 window.motorOtaEcatBusy;Owner 运行于 tabs.html 顶层窗口并读自己的 window,闸门永不生效;页面中途关闭时标志也不会清除。
风险
OTA 期间 Owner 仍可能 open/motor/start 抢总线,导致刷写失败或半截镜像。
修改建议
改用 BroadcastChannel/localStorage 带 TTL 的跨帧忙标志(与 boot_ecat.js:598 统一一处实现),Owner 读取同一来源。

严重 [M-8] ECAT 桥失败回落 HTTP,非幂等命令可能执行两次

模块
电机控制/故障保护
位置
js/motor/transport-ecat.js:259-271; js/common/desktop-bridge.js:59-74
现象
低频路径(motor/send、motor/start 等)在桌面桥失败或超时后无条件回落 HTTP 再发一次;desktopEcatPost/desktopEcatSendRaw 在 JS 侧无超时。
风险
桥已把命令送达 native 但响应超时时,HTTP 再送一次,造成重复运动指令/重复 start;与 pdo-fast-send『堵死慢发』原则冲突。
修改建议
按 ecat_client.py 的幂等白名单处理:非幂等尾部仅在确认未送达(连接失败)时回落;JS 桥调用加 Promise.race 超时。

严重 [M-9] 修改网卡输入框即关闭运行中总线,无任何保护

模块
电机控制/故障保护
位置
js/motor/transport-ecat.js:899-908; js/motor/main.js:3215-3227
现象
adapter 输入框 change 事件立即调用 api('close'),不确认、不检查 OTA 或电机运动状态,误输入即触发。
风险
运动中总线被关,从站失去 PDO 输出(取决于从站看门狗行为),OTA 中断。
修改建议
改为显式『应用』按钮+确认;运动/OTA 中禁止;先安全停机再 close。

一般 [M-12] 运行中导入/水合配置会替换正在使用的参数

模块
电机控制/故障保护
位置
js/motor/main.js:242-289; js/motor/seq.js:223
现象
applyUiSnapshot 在序列/周期发送运行时替换 seq.steps 与 cmdCommitted;运行中键盘编辑步骤未被阻止。
风险
运行中途参数被替换,运动不可预期。
修改建议
运行中禁止导入/水合,或先停止再应用。

一般 [M-13] 危险默认值

模块
电机控制/故障保护
位置
js/motor/seq.js:76-89; pages/motor.html:105, 123, 127
现象
actionFromAny 缺省回落 mode 3、速度 100、电压 24 V;页面默认 mode 3、速度 360、角度 180。
风险
缺字段或首次使用即带非零运动目标。
修改建议
默认 mode 0(失能)、目标 0;缺字段拒绝而非补默认。

一般 [M-14] NaN/Inf 浮点被显示为 0

模块
电机控制/故障保护
位置
js/motor/main.js:624-627, 666-669
现象
decodeStatus/decodeConfig 把非有限值转成 0。
风险
坏数据看起来像『已停止』。
修改建议
显示为 '--' 或 ERR 并计数。

一般 [M-15] 磁盘水合后链路状态不一致

模块
电机控制/故障保护
位置
js/motor/main.js:268, 3172
现象
写入 LS_TRANSPORT 但未切换 state.tp。
风险
界面显示的链路与实际发送链路不一致。
修改建议
水合后统一走 setTransport()。

一般 [M-16] 节点 ID 修改不核对 ACK;去重表键不含通道

模块
电机控制/故障保护
位置
js/motor/main.js:3044-3046
现象
setNodeId 未确认 ACK 即更改 selectedNode;lastSentMode/lastSentTargets 只按 node 区分。
风险
命令发往错误节点;多通道同号节点去重误判。
修改建议
等 ACK 成功再切换;去重键改为 ch:node。

一般 [M-19] 自动化测试未覆盖停机/序列竞态/数值范围

模块
电机控制/故障保护
位置
scripts/test_motor_transport.mjs; scripts/test_motor_refresh.mjs; scripts/test_ecat_autostart.mjs
现象
现有测试覆盖传输契约、刷新速率、自动接入链(均以 process.exit 返回码判定),但没有任何用例覆盖:停止路径是否发失能、序列重启竞态、CMD 编码边界值、链路真实丢失判定。
风险
M-1~M-4 这类致命问题无法被回归发现。
修改建议
为 buildCmdBytes、stopSeq/startSeq、isTransientError/noteHardFailure 补纯逻辑单测并接入现有验收链。

建议 [M-17] 示波器数据经滤波与重采样,定时器从不停止

模块
电机控制/故障保护
位置
js/motor/scope.js:31, 228-256, 269
现象
33 ms rAF 采样并做 IIR 平滑,非实时波形;setInterval 与 rAF 永不停止。
风险
波形误导调参;隐藏页持续耗电。
修改建议
提供原始数据模式;隐藏/暂停时停止循环。

建议 [M-18] main.js 约 3250 行,流控逻辑重复

模块
电机控制/故障保护
位置
js/motor/main.js:807-948
现象
单文件过大、依赖函数提升的闭包顺序;sendBatch 与 sendBurst 流控代码复制。
风险
改动易引入回归。
修改建议
拆分 cmd 编码、流控、UI 绑定、持久化模块;抽公共流控。

主机服务/安全

致命 [P-1] 本地服务无 Origin/Host/Content-Type 校验,任意网页可跨站驱动电机

模块
主机服务/安全
位置
esp32_server.py:480-541(do_POST),504-532(ECAT 代理分支)
现象
服务只绑 127.0.0.1,但不检查 Origin/Host。用户浏览器中任意网页可 fetch('http://127.0.0.1:<port>/ecat/api/motor/start',{method:'POST',mode:'no-cors',body:'{}'});text/plain 简单请求无预检直达,同样可打 motor/send、/api/shutdown、/api/lan/probe。不校验 Host 还使 DNS rebinding 能读取响应。
风险
恶意或被劫持的网页可在用户不知情时启动总线、下发运动帧或关闭服务。
修改建议
所有 POST/WebSocket 校验 Origin 与 Host ∈ {127.0.0.1:port, localhost:port};强制 Content-Type: application/json(触发预检);可加启动时随机令牌。

致命 [P-2] ESF1 快发在 sendall 成功后出错仍整包重发

模块
主机服务/安全
位置
ecat_client.py:247-279
现象
_tcp_once 持锁 sendall(body) 后读 8 字节 FSOK;recv 超时/断连抛异常时 attempt 0 会 continue,attempt 1 再发同一 body。与同文件非幂等不重发原则(_ECAT_IDEMPOTENT_TAILS)矛盾。
风险
同一帧运动指令被 native 入队两次,电机执行两次增量/相对命令。
修改建议
仅当 connect 阶段失败时重试;sendall 之后的任何异常直接返回 ok:false(可附 maybe_sent 标记)。

严重 [P-3] 健康探测 1 s 超时即无差别强杀所有 ecat_native

模块
主机服务/安全
位置
ecat_proc.py:124-140, 143-194, 250-253
现象
health 只等 1.0 s;代理失败触发 autostart → _kill_all_ecat_native 按进程名 taskkill /F。native 忙于 OTA/statecheck 时 health 超时即被杀。
风险
正在进行的 EtherCAT 刷写被打断,可能留下半截镜像;运行中总线被硬断。
修改建议
端口仍在监听则不杀;只杀自己拉起的 pid;刷写期间(共享忙标志)禁止 kill。

严重 [P-4] 跨进程锁超时或 CreateMutexW 失败仍继续执行

模块
主机服务/安全
位置
ecat_proc.py:67-94(尤其 72-81)
现象
_EcatProcXLock 未拿到锁(_owned=False)时不中止,后续 kill/拉起照常进行。
风险
多实例并发 kill/start,串行保护失效。
修改建议
未拿到锁时返回失败(503)或只读复用,不做 kill/start。

严重 [P-5] 桌面桥路径不记录循环状态,『关 app 必停从站』可能失效

模块
主机服务/安全
位置
ecat_client.py:470-497; esp32_server.py:331-340, 403-420, 533
现象
GUI 进程经桥发出的 motor/start/stop 不调用 _note_ecat_loop,服务子进程的 g_ecat_motor_live 不更新;关窗时租约与 _shutdown_self 据此可能不停循环(复用外部 native 时尤甚)。
风险
关闭软件后 PDO 循环仍在跑,电机保持最后输出。
修改建议
桥调用后同步通知服务子进程(或由 native health 的 loop 字段判定),关窗统一按 native 实际状态停机。

严重 [P-6] 外链在窗口内导航,外部页面可调用桌面桥 js_api

模块
主机服务/安全
位置
esp32_server.py:142-146, 998-1000
现象
注入脚本把 window.open/_blank 改为当前窗口导航;pywebview js_api 暴露 ecat_send_raw/ecat_api,导航到外部站点后该站点即可调用。
风险
外部页面直接发 EtherCAT 原始帧。
修改建议
外链改为系统浏览器打开(webbrowser.open);js_api 调用前校验 window.location.origin。

严重 [P-7] ecat_native 提到 REALTIME 优先级,配合自旋线程可能拖死系统;复用实例不提权

模块
主机服务/安全
位置
ecat_proc.py:96-121, 212-215, 273-276
现象
新拉起进程设 REALTIME_PRIORITY_CLASS;README 称循环为自旋+TIME_CRITICAL。复用已有实例分支不设置(行为不一致)。
风险
饿死同核系统线程,UI 冻结后无法下达停机;不同启动路径实时性不一致。
修改建议
改用 HIGH_PRIORITY_CLASS + 线程亲和性绑核;如需 REALTIME,确保至少留一核并在复用路径一致处理。

一般 [P-10] 代理读取 Content-Length 与 native 响应均无上限

模块
主机服务/安全
位置
esp32_server.py:509-512; ecat_client.py:350-355
现象
Content-Length 不设上限;接收无总时长和大小上限。
风险
内存耗尽/线程挂死。
修改建议
限制请求体(如 1 MB)与响应总时长。

一般 [P-11] ESF 打包静默修改数据

模块
主机服务/安全
位置
ecat_client.py:140-141, 157-166
现象
奇数位 hex 静默前补 0(字节错位);不校验 data 长度与 dlc、不校验 CAN FD 合法 DLC 集合;id 被 &0xFFFFFFFF 静默截断。
风险
下发与用户输入不一致的帧。
修改建议
非法输入直接报错。

一般 [P-12] 静态服务暴露整个 BASE_DIR,无扩展名文件被当 HTML 注入脚本

模块
主机服务/安全
位置
esp32_server.py:434, 454-466, 474
现象
/.git/HEAD、/.gitignore 等可读并以 text/html 注入 _reload 脚本;注入用 replace('</body>',...,1) 大小写敏感且可能落在脚本字符串里;重复 Cache-Control 头;注释提到的 Last-Modified 未发送。
风险
信息泄露、页面被破坏。
修改建议
静态白名单目录(pages/ js/ css/ lab/ tabs.html);仅对 .html 注入;用正则定位最后一个 </body>。

一般 [P-13] WebSocket 实现不完整且不校验 Origin

模块
主机服务/安全
位置
esp32_server.py:551-564, 604-606
现象
Pong 只用一字节长度编码,payload>125 出错;外站可连 WS 挂住租约,阻止停机。
风险
关窗不停机。
修改建议
完整实现长度编码;握手校验 Origin。

一般 [P-14] 端口复用与单实例检测存在竞态

模块
主机服务/安全
位置
esp32_server.py:756-779, 897-898, 954
现象
未用 SO_EXCLUSIVEADDRUSE,探测与绑定间 TOCTOU;查已有实例只试 port..+2,绑定最多漂移 6 个;windll GetLastError 不可靠。
风险
多实例并存、旧页面连到错误实例。
修改建议
SO_EXCLUSIVEADDRUSE;探测范围与漂移范围一致;ctypes.WinDLL(use_last_error=True)。

一般 [P-15] 退出路径可能跳过停机

模块
主机服务/安全
位置
ecat_proc.py:301-311; esp32_server.py:348, 873-878
现象
watch_parent 用 os._exit 退出跳过停循环,OpenProcess 失败时静默放弃;父进程只等 3 s 就 kill,短于 op/stop 的 5 s 超时。
风险
关窗后循环未停。
修改建议
退出前同步 stop;父进程等待 ≥ stop 超时。

一般 [P-8] 非幂等请求首次可能误报 503

模块
主机服务/安全
位置
ecat_client.py:405, 415, 426-439
现象
非缓存端口 0.3 s 连接超时不算『拒绝』,非幂等请求在 8090 即 break,不尝试 8091+;随后 autostart 走复用分支返回 503『已自动拉起请重试』。
风险
首次操作失败,用户困惑。
修改建议
端口发现与请求分离:先发现可用端口再发非幂等请求。

一般 [P-9] 桥接路径可写入任意 HTTP 请求行

模块
主机服务/安全
位置
ecat_client.py:93-107, 337-344, 470
现象
路径未校验,'..' 与控制字符原样进入请求行。
风险
请求走私/打到 native 非预期路由。
修改建议
路径白名单(^[a-z0-9_/]+$)。

建议 [P-16] native_stdout.log 只追加不轮转

模块
主机服务/安全
位置
ecat_proc.py:259-262
现象
日志无限增长。
风险
长期运行占满磁盘。
修改建议
按大小轮转(RotatingFileHandler 或启动时截断)。

建议 [P-17] LAN 探测接口可被当端口扫描器

模块
主机服务/安全
位置
lan_probe.py:320-333; esp32_server.py:673-684
现象
端口数量与范围不受限,IP 不排除回环与公网(路径有白名单,值得肯定);配合 P-1 外站可借此扫内网。
风险
内网侦察。
修改建议
端口限制为 {80},IP 限私网段,数量上限 64。

OTA/刷写

致命 [O-1] 同一块 GD32 可被两条通道同时刷写

模块
OTA/刷写
位置
js/ota/session.js:128; js/ota/boot_ecat.js:176-189, 576-577; js/ota/console-mode.js:67, 120; js/ota/ecat_load.js:114-117; js/ota/upload-esp32.js:62-91; js/ble/transfer.js:457-472
现象
WiFi 上传进入 VERIFYING/RELAYING 后 window.isUploading 置 false;依赖它的所有忙判(ECAT 按钮、模式切换、ECAT 负载轮询)都放行。bootEcatFlash 只查 bootEcatBusy;startUpload 与 BLE autoFlash 从不查 bootEcatBusy。
风险
ESP32 正经 SPI 中继写 GD32 时,ECAT 或 BLE 又开始刷同一芯片,Flash 内容交错,设备变砖。
修改建议
建立唯一的 flashBusy 互斥(跨帧共享,含 WiFi/中继/BLE/ECAT 全阶段),所有入口统一检查;isUploading 仅作 UI 用途。

致命 [O-2] HEX 解析可放过损坏或截断文件

模块
OTA/刷写
位置
js/ota/file-handle.js:458-516(校验 472-477,分配 506-509)
现象
非十六进制字符使 parseInt 得 NaN,sum 变 NaN,NaN & 0xFF === 0 通过校验;不要求 EOF 记录(截断文件被接受);不校验 byteCount 与行长;不校验基址是否为 0x08048000;稀疏 HEX 可令 maxAddr-minAddr 分配数百 MB。端到端 SHA 只证明传输完整。
风险
截断或错位的 APP 被刷入,设备无法启动。
修改建议
逐字符正则校验 ^:[0-9A-Fa-f]+$、byteCount*2+11===len、必须有 01 记录、基址/大小落在 APP 分区内、跨度上限。

严重 [O-3] ESP32 重启按钮无 OTA 守卫

模块
OTA/刷写
位置
js/ota/upload-esp32.js:594-613; pages/index.html:368
现象
restartEsp32 仅 confirm 一次,不检查 OTA 会话/中继状态。
风险
GD32 中继刷写中途重启 ESP32,GD32 留下半截镜像。
修改建议
OTA 会话非 IDLE/DONE/FAILED 时禁用按钮并拒绝。

严重 [O-4] GD32 完成判定可被陈旧 alive 标志误判

模块
OTA/刷写
位置
js/ota/stages.js:497-499
现象
gd32AppAlive && relaySeq>=relayTotal && rs>=3 即判完成,TRANSFERRING/FINISHING 阶段旧的 alive 也会计入。
风险
失败的刷写被报告为成功。
修改建议
完成判定要求刷后版本/CRC 与目标一致且 alive 时间戳晚于刷写结束。

严重 [O-5] ESP32 重启后成功判定不核对新版本

模块
OTA/刷写
位置
js/common/polling.js:~236; js/ota/upload-esp32.js:435-449, 468-474
现象
以 uptime<120 s、观察到离线或 10 s 兜底判完成;verify 路径猜测『已生效』。
风险
回滚或崩溃重启也被报成功。
修改建议
重启后读取版本号/镜像 SHA 与刚上传文件比对。

严重 [O-6] WiFi/BLE OTA 不通知其它标签,端点可中途切换

模块
OTA/刷写
位置
js/ota/boot_ecat.js:598; js/app.js:63-71, 403-421
现象
跨标签忙标志只由 ECAT 写;电机/CAN 页、LAN 选择器、端点切换不感知 WiFi/BLE OTA;IP 输入框 change 与 getEspIp 可在 OTA 中改端点。
风险
OTA 中其它页面动总线或切到另一台设备。
修改建议
与 O-1/M-7 共用跨帧忙标志;OTA 中锁定端点。

严重 [O-7] BLE abort 总发会话 0;中继阶段可随意中止

模块
OTA/刷写
位置
js/ble/session.js:89; js/ble/state.js
现象
abortTask 使用从未定义的 state.sessionId(恒为 0/undefined);GD32 SPI 中继阶段 abort 按钮仍可用且无警告。
风险
中止命令无效或在最危险阶段中止。
修改建议
使用真实会话 ID;中继阶段禁用或二次确认。

严重 [O-8] BLE GD32 身份检查未 await

模块
OTA/刷写
位置
js/ble/file.js:174, 182-205
现象
finalizeAndHash 返回检查 Promise 但 updateFileInfo 丢弃,热重载后 uploadFirmware 可在检查完成前开始。
风险
错误固件仅靠设备侧 0x87 检查兜底。
修改建议
await 身份检查,失败禁用上传。

严重 [O-9] 前端不校验 ESP32 镜像

模块
OTA/刷写
位置
js/ota/file-handle.js:725-751
现象
不检查 0xE9 魔数、芯片 ID、app 描述符、分区大小,任意拖入文件都会完整上传。
风险
刷错镜像依赖设备侧拒绝;浪费时间或触发异常。
修改建议
上传前解析 esp_image_header/app_desc,比对 chip_id 与分区大小。

一般 [O-10] BLE ACK/重传机制薄弱

模块
OTA/刷写
位置
js/ble/gatt.js:249-255; js/ble/transfer.js:45-57, 75-78, 282
现象
短 ACK 立即 resolve 且 10 ms 后重发,每窗仅 3 次重试;代次检查为空操作;注释提到 CRC 实际没有。
风险
弱信号下误失败或重复写。
修改建议
退避重传、实现代次校验、加包 CRC。

一般 [O-11] BLE 载荷固定 480 B,无 MTU 回退

模块
OTA/刷写
位置
js/ble/constants.js:38, 50; js/ble/state.js:76
现象
不按协商 MTU 调整。
风险
部分主机/适配器下写失败。
修改建议
按 MTU-3 计算并回退。

一般 [O-12] BLE 断开不拒绝结果等待

模块
OTA/刷写
位置
js/ble/gatt.js:139-150; js/ble/transfer.js:394
现象
断开后 UI 最长等待 120 s。
风险
体验差、误判。
修改建议
gattserverdisconnected 时 reject 所有 pending。

一般 [O-13] GD32 中继触发超时后可能误判失败

模块
OTA/刷写
位置
js/ota/upload-gd32.js:191-216
现象
触发超时且探测也失败时进 FAILED,设备可能仍在中继。
风险
用户重试导致二次刷写。
修改建议
进入『未知』态并持续轮询中继状态。

一般 [O-14] ECAT 直灌无完整性校验,api() 不查 HTTP 状态

模块
OTA/刷写
位置
js/ota/boot_ecat.js:89-99, 635-661
现象
不向 native 传 CRC/SHA,不核对 p.received === u8.length;api() 直接 json()。
风险
传输缺字节不被发现(虽有刷前后 0x100A 比对)。
修改建议
传 SHA 并比对 received;检查 resp.ok。

建议 [O-15] 大量 window.* 全局、猴补 showOpenFilePicker、内存复制

模块
OTA/刷写
位置
js/ota/console-mode.js:170-199; js/ota/upload-esp32.js:213-247; js/ota/progress.js:177; js/ota/upload-gd32.js:85-104
现象
上传体为完整文件再复制一份;失败清理写死按钮文案;存在 FAILED→DONE 转移路径。
风险
可维护性差,状态机可出现非法转移。
修改建议
收敛状态机转移表;模块化状态;流式上传。

CAN/公共层

严重 [C-1] DBC 中 [0|0] 被当成真实范围

模块
CAN/公共层
位置
js/can/dbc.js:164-165; js/can/signals.js:299-304
现象
DBC 惯例 [0|0] 表示未声明范围,代码却据此校验,任何非 0 物理值编码失败。
风险
大量常见 DBC 信号无法发送。
修改建议
min==max==0 视为无范围。

严重 [C-11] 独立 CAN 监控页无视所选设备

模块
CAN/公共层
位置
pages/can_monitor.html:55; js/can_monitor.js:60-68; js/can/dbc.js:40-47
现象
页面输入框写死 value="192.168.31.127" 且优先于共享端点,监控标签总连 .127;绕过 IPv4 校验;与 dbc.js 优先级不一致,破坏 README 所述单端点写入者设计。
风险
监控的不是正在控制的设备。
修改建议
移除硬编码值,统一从 deviceLink 读取。

严重 [C-12] 监控页超时永不显示离线

模块
CAN/公共层
位置
js/can_monitor.js:199
现象
超时产生 AbortError 后直接 return,不标离线;设备无响应时永远显示『已连接』(polling.js:254-257 已修过同类问题)。
风险
误判在线。
修改建议
超时计入失败并标离线。

严重 [C-2] 扩展多路复用信号被静默丢弃

模块
CAN/公共层
位置
js/can/dbc.js:153
现象
SG_ 正则只接受 M|m\d+,m3M 匹配失败,信号丢失无报错;不支持 SG_MUL_VAL_。
风险
信号缺失且无提示。
修改建议
支持扩展多路复用或至少报警告。

严重 [C-4] 扩展/标准帧靠 ID 数值猜测,发送载荷无帧类型标志

模块
CAN/公共层
位置
js/can/dbc.js:146; js/can/send.js:304, 373, 453, 461
现象
isExtended = idRaw > 0x7FF,应看 DBC ID bit31;载荷只有 {id,dlc,data},无 ide/fd/brs;≤0x7FF 的扩展帧只能以标准帧发出;VECTOR__INDEPENDENT_SIG_MSG 伪消息未过滤,显示为扩展 ID 0。
风险
帧类型错误,目标节点收不到。
修改建议
按 bit31 判定并在载荷加 ide/fd/brs 字段(需固件配合);过滤伪消息。

严重 [C-5] DLC 未按 CAN FD 合法长度校验

模块
CAN/公共层
位置
js/can/send.js:406; js/can/signals.js:233-235
现象
0~64 任意值均可(如 10),合法值仅 0~8,12,16,20,24,32,48,64;DBC dlc 未映射。
风险
固件侧行为未定义或截断。
修改建议
限定合法集合,向上取整并提示。

一般 [C-10] 单条发送无防重入

模块
CAN/公共层
位置
js/can/send.js:282, 358, 435
现象
发送所选消息/手动帧无在途保护;批量发送静默跳过失效键。
风险
重复发帧。
修改建议
加在途标志并提示跳过项。

一般 [C-13] 对设备的并发轮询过多

模块
CAN/公共层
位置
js/common/device-link.js:30; js/can.js:131, 424; js/can_monitor.js:487, 490; tabs.html:76-81; js/common/polling.js:350-352
现象
/system/status 被 Owner(1 Hz)、OTA 页(1 Hz)、can.js(5 s)、can_monitor(5 s) 同时轮询;/can/feedback 被抽屉与独立监控页各 1 Hz;隐藏 iframe 继续轮询,无可见性暂停与离线退避。README 称设备 httpd 仅 6 个 socket 且 lru_purge。
风险
连接被挤占驱逐,业务请求失败。
修改建议
状态统一由 Owner 广播;隐藏页暂停;离线指数退避。

一般 [C-14] 轮询超时计时器可能丢失

模块
CAN/公共层
位置
js/common/polling.js:194
现象
计时器闭包读取模块级 statusAbortCtrl,pause/resume 可能将其置 null,新请求永不超时。
风险
轮询挂起。
修改建议
闭包捕获本次 controller。

一般 [C-16] 读取已存 IP 不校验;网关猜测探测

模块
CAN/公共层
位置
js/common/base-url.js:44-50; js/common/connection.js:761-789
现象
getSavedDeviceIp 原样返回;按 .1 猜网关并 no-cors 探测。
风险
脏数据导致请求异常。
修改建议
读时校验 IPv4。

一般 [C-3] 多行 CM_ 注释丢失

模块
CAN/公共层
位置
js/can/dbc.js:178, 184
现象
正则要求 "; 同行。
风险
注释缺失。
修改建议
按语句(以 ; 结束)解析。

一般 [C-6] 手动 ID 解析过宽

模块
CAN/公共层
位置
js/can/dbc.js:58
现象
parseInt('0x12G',16) 静默得 0x12;无 0x1FFFFFFF 上限。
风险
发错 ID。
修改建议
严格正则与范围校验。

一般 [C-7] 枚举/多路复用/浮点信号编码细节错误

模块
CAN/公共层
位置
js/can/signals.js:45-51, 76, 294-296, 406
现象
枚举默认 '0' 即便 0 不是合法键(下拉显示首项但编码失败);mux 比较用物理值而非原始值;浮点信号忽略 factor/offset。
风险
编码失败或数值错误。
修改建议
默认取首个合法键;mux 用 raw;浮点应用 factor/offset。

一般 [C-8] 持久化静默失败且状态只增不减

模块
CAN/公共层
位置
js/can/persist.js:93-124; js/can.js:229
现象
完整 DBC 文本存 localStorage,超配额三次保存都静默失败;已删除 DBC 源的 signalValues/checkedKeys 不清理;添加任一 DBC 会重置所有已编辑信号值。
风险
配置丢失无提示。
修改建议
改用 IndexedDB、失败提示、按源清理。

一般 [C-9] toast 模块被加载成两个实例

模块
CAN/公共层
位置
js/can/send.js:17; js/can.js:19-20, 47
现象
send.js 以 ?v= 查询串导入 toast.js,违反 can.js 自身规则,产生两个模块实例,去重与隐藏计时不共享。
风险
重复提示。
修改建议
JS 间 import 去掉 ?v=。

建议 [C-15] 大量隐式全局与重复代码

模块
CAN/公共层
位置
js/common/polling.js:10-29; js/common/connection.js:6-13; js/can/send.js:36; js/can_monitor.js:14, 100; js/can.js:86
现象
20+ window.* 全局耦合 ES 模块;复制/连接/格式化代码重复。
风险
维护困难。
修改建议
收敛为显式导入的共享模块。

仓库卫生/文档

严重 [H-1] AI 工具目录被提交

模块
仓库卫生/文档
位置
.qoder/repowiki/(约 75 个文件); .zcode/plans/plan-sess_*.md; .gitignore
现象
.qoder 自动生成的知识库与 .zcode 个人调试计划(含 G:\ 路径与固件计划)入库,且未列入 .gitignore。
风险
仓库噪声、信息泄露、与真实代码不同步误导读者。
修改建议
移出仓库(需用户决定,本次评审不删除任何文件)并加入 .gitignore。

严重 [H-5] 默认 ECAT 周期 0.25 ms 与既有报告建议冲突且缺复测依据

模块
仓库卫生/文档
位置
js/common/ecat-bus.js:28; js/motor/transport-ecat.js:428-431
现象
ECAT_RATE_TRADEOFF 建议 0.5 ms(称 0.25 ms 为功能损坏),REPORT_ecat_skip_opt 建议生产 2 ms;代码默认 0.25 ms,仓库无带 CAN 工况的 0.25 ms 复测证据(BEAT_TAIL 自列为待办)。
风险
高负载下丢拍/跳拍、控制抖动。
修改建议
补带 CAN 工况复测并在报告中记录;若无证据,默认回到 0.5 ms。

一般 [H-2] 受版本控制文件含个人绝对路径

模块
仓库卫生/文档
位置
scripts/build_mem.bat:21; README.md:3, 449; docs/compose/spec/host-code-quality.md:67
现象
G:\Robot_v2\... 等路径。
风险
换机即失效。
修改建议
改为相对路径或环境变量。

一般 [H-3] README 脚本清单与 scripts/ 不一致;.gitignore 规则重叠

模块
仓库卫生/文档
位置
README.md; scripts/check_fe_guard_mutation.py; scripts/ui_diag_motor.mjs; .gitignore; .gitattributes
现象
check_fe_guard_mutation.py、ui_diag_motor.mjs 未登记;test_lan_probe.py、check_lan_discovery.py 不在『正式清单』表;*.jsonl 重复、scripts/_* 与 scripts/_*_out.txt 重叠;.gitattributes 为 * -text。
风险
清单失真。
修改建议
同步清单;整理忽略规则;按类型设 eol。

一般 [H-4] 注释/文档与代码不符(默认周期等)

模块
仓库卫生/文档
位置
js/motor/transport-ecat.js:37, 166, 416-417, 607; README.md:190
现象
注释写 supportsOta=false,实际 :607 为 true;注释与 README 写『默认 4 ms 由后端决定/删键跟随后端』,实际回落 0.25 ms。
风险
误导调参与维护。
修改建议
统一以 ecat-bus.js 常量为准并修正文档。

一般 [H-6] GD32 WiFi 中继刷写缺『刷写≠生效』版本比对,守卫脚本已删除

模块
仓库卫生/文档
位置
js/ota/upload-gd32.js; js/ota/stages.js:497-499
现象
ECAT 直灌有 0x100A 刷前后比对,WiFi 中继路径没有;原 flash_gd32.py 生效守卫随 beeae8a 删除。
风险
刷写成功但未生效不被发现。
修改建议
中继完成后读取 APP 版本比对。

一般 [H-8] ecat_integral_report 结论与其附带脚本输出自相矛盾

模块
仓库卫生/文档
位置
docs/reports/ecat_integral_report.html
现象
正文称积分差大头在『停车段』、N3 停车更拖;附带脚本 JSON 却为 main_source_of_integral_error=起动段(phase on 3.54 > stop 1.09),who_lags_at_stop=N2,plain 中又写『N3 停车更拖』。
风险
依据该报告调参可能方向错误。
修改建议
重跑脚本核对口径(均值 vs 绝对值均值)并修正结论。

建议 [H-7] 报告中的资源与脚本链接随归档失效

模块
仓库卫生/文档
位置
docs/reports/ECAT_RATE_TRADEOFF.md; docs/reports/ecat_integral_report.html; docs/reports/REPORT_ecat_skip_opt_20260917.md
现象
lab/*.png、lab/*.csv、ecat_calib.json、sweep/plot/build 脚本与 scripts/skip_*.py 在 beeae8a 删除,报告不可复现。
风险
无法复核结论。
修改建议
把关键数据与脚本归档到 docs/reports/assets/ 或标注出处 commit。

4. 既有评审 / 报告结论跟踪

对仓库内全部既有报告与规格逐条核对(原文件保留不动),每份另出修订附录:

原文件修订附录已修复部分仍存在/存疑已过时无法核实
docs/reports/ECAT_BEAT_TAIL_REPORT.htmlECAT_BEAT_TAIL_REPORT_REV_2026-10-19.html20121
docs/reports/ECAT_RATE_TRADEOFF.html + .mdECAT_RATE_TRADEOFF_REV_2026-10-19.html00311
docs/reports/REPORT_ecat_skip_opt_20260917.mdREPORT_ecat_skip_opt_20260917_REV_2026-10-19.html01121
docs/reports/GD32_OTA_FLOW_COMPARE.htmlGD32_OTA_FLOW_COMPARE_REV_2026-10-19.html11123
docs/reports/ecat_integral_report.htmlecat_integral_report_REV_2026-10-19.html00211
docs/compose/spec/device-link.mddevice-link_REV_2026-10-19.html22100
docs/compose/spec/tab-keepalive.mdtab-keepalive_REV_2026-10-19.html20100
docs/compose/spec/ecat-rx-stuck.mdecat-rx-stuck_REV_2026-10-19.html00102
docs/compose/spec/pdo-fast-send.mdpdo-fast-send_REV_2026-10-19.html21100
docs/compose/spec/host-code-quality.mdhost-code-quality_REV_2026-10-19.html25200
.zcode/plans/plan-sess_*.md(无附录,仅本表跟踪)20002
来源编号原结论 / 建议当前状态证据(文件:行号)备注
ECAT_BEAT_TAIL_REPORTBT-1前端每 PDO 电机槽数与扫描拍数按 dense 布局改(MOT_SLOTS_PER_PDO=9、MOTOR_SCAN_CYCLES=1)已修复js/motor/transport-ecat.js:57, 88
ECAT_BEAT_TAIL_REPORTBT-2测速页跳拍卡改读 cyc_us.late/lost已修复lab/spi_test.html:506, 1522-1534, 2154-2167
ECAT_BEAT_TAIL_REPORTBT-3刷写≠生效:flash_gd32.py 增加生效守卫已过时—(beeae8a 随 scripts 收敛删除)网页 WiFi 中继路径无替代版本比对,见新问题 H-6;ECAT 直灌有 0x100A 比对(boot_ecat.js:629-632, 792-794)
ECAT_BEAT_TAIL_REPORTBT-4345µs 归因:win 布局固有成本 vs 主站 97fab23 回归,需一次刷板钉死实验无法在本仓库核实—涉及固件布局与 ecat_native.c,不在本仓
ECAT_BEAT_TAIL_REPORTBT-50.25ms 档实测列为待办仍存在js/common/ecat-bus.js:28默认已是 0.25ms 却无复测记录,见 H-5
ECAT_BEAT_TAIL_REPORTBT-6报告引用 lab/ecat_rate_sweep.csv 等证据数据已过时—数据随归档删除,报告不可复现,见 H-7
ECAT_RATE_TRADEOFFRT-1建议周期停在 0.5ms,0.25ms 为功能损坏仍存在js/common/ecat-bus.js:28; js/motor/transport-ecat.js:428-431代码默认 0.25ms,建议未采纳且无复测依据(H-5)
ECAT_RATE_TRADEOFFRT-2周期可被 localStorage 覆盖(原 :227-228)仍存在js/motor/transport-ecat.js:167, 428-430设计如此,但应在 UI 明示并限定合法档位
ECAT_RATE_TRADEOFFRT-3drop 口径、fillRxWindow 计时待办无法在本仓库核实—属固件/native 侧
ECAT_RATE_TRADEOFFRT-4sweep_ecat_rate.py / calibrate_ecat_metrics.py / plot_* / build_ecat_report_html.py 与图表数据已过时—beeae8a 删除,.md 中图片链接失效(H-7)
ECAT_RATE_TRADEOFFRT-5注释/README 称默认 4ms 由后端决定仍存在js/motor/transport-ecat.js:166, 416-417; README.md:190文档与代码不符(H-4)
REPORT_ecat_skip_opt_20260917SK-1ecat_native 提权为 RealTime部分修复ecat_proc.py:97-120, 273-276仅新拉起进程提权;复用实例分支 :212-215 不提权;且 REALTIME 有饿死系统线程风险(P-7)
REPORT_ecat_skip_opt_20260917SK-2生产建议 2ms仍存在js/common/ecat-bus.js:28默认 0.25ms,与建议冲突(H-5)
REPORT_ecat_skip_opt_20260917SK-3第四节回滚命令依赖 scripts/skip_*.py已过时—脚本已删除,回滚步骤不可直接执行
REPORT_ecat_skip_opt_20260917SK-4I210 协议绑定/高级属性调整无法在本仓库核实—属主机网卡配置
REPORT_ecat_skip_opt_20260917SK-5标定/实验脚本(第五节)已过时—已出库(H-7)
GD32_OTA_FLOW_COMPAREOF-1新流程 0 次上板实测已过时js/ota/boot_ecat.js:55-58, 307-322EtherCAT 直灌已移植入本仓(d73e2fb 起),注释含 2026-10-12/13 实测记录
GD32_OTA_FLOW_COMPAREOF-2正在跑 APP 的机器还不能直接刷部分修复js/ota/boot_ecat.js:663-676主机侧已写 CoE 0x2502:00=1 交接;固件侧无法核实
GD32_OTA_FLOW_COMPAREOF-3刷前刷后 0x100A 版本比对已修复js/ota/boot_ecat.js:629-632, 792-794
GD32_OTA_FLOW_COMPAREOF-44 字节对齐补 0xFF无法在本仓库核实—发生在 native 侧
GD32_OTA_FLOW_COMPAREOF-5固件 OTA.cpp 尾巴丢弃缺陷无法在本仓库核实—固件仓
GD32_OTA_FLOW_COMPAREOF-6老流程无断点续传,每次从偏移 0 重写仍存在js/ota/upload-esp32.js:213-247整文件 POST 且内存双份(O-15)
GD32_OTA_FLOW_COMPAREOF-7报告引用行号 upload-gd32.js:58,194已过时(行号漂移)js/ota/upload-gd32.js:67-68; js/ota/file-handle.js:458, 563file-handle 引用仍有效
GD32_OTA_FLOW_COMPAREOF-8两条路互为备份、复用同一 flash 写入者无法在本仓库核实—前端侧两条路缺统一互斥,见新问题 O-1(致命)
ecat_integral_reportIN-1数据源 lab/ecat_passive_latest.csv已过时—CSV 不在仓库,报告不可复现(H-7)
ecat_integral_reportIN-2结论:积分差大头在停车段存疑docs/reports/ecat_integral_report.html(附带脚本输出)附带 JSON 的 main_source_of_integral_error=起动段、who_lags_at_stop=N2,与正文『N3 停车更拖』相矛盾(H-8)
ecat_integral_reportIN-3建议④ 序列 hold 不短于两台都停稳时间仍存在js/motor/seq.js:95holdMs 范围 0~600000,无『停稳确认』机制;且序列重启存在并发循环(M-4)
ecat_integral_reportIN-4建议①~③ 统一减速段、对称负载、v2+v3 纠偏无法在本仓库核实—属固件控制器
device-linkDL-1外壳 Owner + BroadcastChannel 快照已修复(已交付)tabs.html:205; js/common/device-link.js:117, 389
device-linkDL-2端点配置只在 ConnectionHub部分修复pages/can_monitor.html:55; js/can_monitor.js:60-68; js/app.js:63-71CAN 监控页硬编码 IP 优先于 Hub(C-11);OTA 页 IP 输入可绕过
device-linkDL-3功能页不再各自判定在线部分修复js/common/polling.js; js/can.js:424; js/can_monitor.js:487多处仍独立轮询 /system/status(C-13)
device-linkDL-4外壳启动强制开总线,失败 10s 退避仍存在风险js/common/device-link.js:32, 250-287无 reason、unknown 视为需启动、OTA 闸读不到 iframe 标志(M-6、M-7)
device-linkDL-5BLE 经 reportBle 上报已修复(已交付)js/ble/gatt.js:119, 123, 128
tab-keepaliveTK-1多 iframe 宿主 + visibility 切换已修复(已交付)tabs.html:70-81, 100, 113
tab-keepaliveTK-2F5/⟳ 只 reload .page-frame.active已修复(已交付)esp32_server.py:126, 150, 166
tab-keepaliveTK-3不做后台页降频(Out of Scope)仍存在风险tabs.html:76-81; js/common/polling.js:350-352隐藏帧持续轮询挤占设备 6 个 socket(C-13);电机页发送节拍在后台被节流(M-10)——建议重新评估此决策
ecat-rx-stuckRX-1主站 OP 后 seq 沿踢帧 + 活性判据加宽无法在本仓库核实—ecat_native.c 不在本仓
ecat-rx-stuckRX-2从站新鲜度救援先 Process 再 InputMapping无法在本仓库核实—固件仓
ecat-rx-stuckRX-3规格状态仍为 in-progress,但 T1~T3 已勾选仍存在docs/compose/spec/ecat-rx-stuck.md:3建议改为 delivered 或注明剩余事项
pdo-fast-sendFS-1Python 桥 ESF1 仅走 TCP,无 HTTP 回落已修复(已交付)ecat_client.py:231-287
pdo-fast-sendFS-2前端 sendViaFastOrJson 有桥时只调快发已修复(已交付)js/motor/transport-ecat.js:282-300
pdo-fast-sendFS-3失败重探端口后再试 TCP 一次仍存在风险ecat_client.py:247-279sendall 成功后 recv 失败也会重发,同一帧可能入队两次(P-2,致命)
pdo-fast-sendFS-4堵死慢发原则部分修复js/motor/transport-ecat.js:259-271低频路径(motor/send、motor/start)桥失败仍回落 HTTP(M-8)
host-code-qualityHQ-R1非幂等不自动重发部分修复ecat_client.py:58-62, 290-299, 418-436HTTP 代理已遵守;ESF1 快发重试违反同一原则(P-2)
host-code-qualityHQ-R2网卡默认 I210已修复ecat_proc.py:28
host-code-qualityHQ-R3跨进程互斥/复用部分修复ecat_proc.py:67-94, 125-140锁超时/CreateMutexW 失败仍继续执行(P-4)
host-code-qualityHQ-R4拆 main.js部分修复js/motor/main.js(3254 行)仍为上帝文件(M-18)
host-code-qualityHQ-R5版本/参数下沉仍未实施—
host-code-qualityHQ-R6ECAT bootstrap 单点部分修复js/motor/transport-ecat.js:418-441; js/common/ecat-bus.js:65-75电机侧仍自算周期并绕过预算(M-6)
host-code-qualityHQ-R7toast/quiet 去重仍未实施js/can/send.js:17CAN 侧还出现 toast 双实例(C-9)
host-code-qualityHQ-R8~R14服务端拆分与卫生已修复(大部分)ecat_client.py; ecat_proc.py; docs/reports/但 .qoder/.zcode 入库(H-1)
host-code-qualityHQ-R15README部分修复README.md:3, 190, 449个人路径与默认周期描述过时(H-2、H-4)
.zcode-planZC-A1ecat_native 新增 /api/pm/hold无法在本仓库核实—native 不在本仓
.zcode-planZC-A2lan9252_pm.html enterMode 后发 pm/hold已修复lab/lan9252_pm.html:447
.zcode-planZC-A3睡眠标记另写带时间戳 localStorage已修复lab/lan9252_pm.html:289-309
.zcode-planZC-BGD32 固件 HBI 门控无法在本仓库核实—固件仓

5. 优先级修复路线图

P0(立即,1~3 天)· 人身与设备安全

P1(1~2 周)· 功能正确性

P2(1 个月内)· 健壮性与可维护性

P3(持续)· 仓库卫生与文档

6. 总结评分

维度评分说明
架构与模块划分6.5外壳/功能页/链路层分层清晰,DeviceLink、多 iframe 保活、双链路 transport 抽象设计合理;但跨帧状态依赖 window.* 全局,main.js 过大。
实时性与通信协议6ESF1 快发、幂等白名单、Worker 状态轮询等用心;后台节流、重复轮询、默认 0.25ms 周期缺依据。
安全与故障保护3.5无急停、无数值限幅、离线判不出、序列并发、本地服务无来源校验——是本次评审最主要的扣分项。
OTA/刷写可靠性5设备侧 SHA/身份检查、ECAT 0x100A 比对等较好;跨通道互斥缺失与 HEX 解析漏洞可致变砖。
代码质量与可维护性5.5注释详尽、经验教训有记录;上帝文件、重复逻辑、注释与代码不符。
测试5有真机与浏览器验收链、返回码规范;安全关键路径无用例。
构建与仓库卫生5.5scripts 已收敛、报告归档;AI 工具目录与个人路径入库,报告证据随归档丢失。
综合5.4功能完整度高、工程经验积累扎实,但作为可直接驱动电机的工具,安全兜底明显不足。完成 P0 项后预计可达 7 分。

本评审只新增 docs/ 下文件,未修改任何源码、未删除任何文件。