Files
DCU_DesktopController/docs/review/DCU_DesktopController_专家评审_2026-10-19.html
T

41 lines
92 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>DCU_DesktopController 专家评审 2026-10-19</title><style>
body{font-family:-apple-system,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;margin:0;background:#f5f6f8;color:#1d2330;line-height:1.6}
.wrap{max-width:1180px;margin:0 auto;padding:28px 24px 60px}
h1{font-size:26px;margin:0 0 6px}h2{font-size:20px;margin:36px 0 12px;padding-bottom:6px;border-bottom:2px solid #d9dee8}
h3{font-size:16px;margin:22px 0 8px}
.meta{color:#5b6476;font-size:13px}
.card{background:#fff;border:1px solid #e1e5ec;border-radius:10px;padding:16px 18px;margin:12px 0}
table{border-collapse:collapse;width:100%;background:#fff;font-size:13px;margin:8px 0}
th,td{border:1px solid #e1e5ec;padding:6px 8px;vertical-align:top;text-align:left}
th{background:#eef1f6;font-weight:600}
code{font-family:Consolas,Menlo,monospace;font-size:12px;background:#f0f2f6;padding:1px 4px;border-radius:3px;word-break:break-all}
.sev{display:inline-block;padding:1px 8px;border-radius:10px;font-size:12px;font-weight:600;color:#fff;white-space:nowrap}
.s0{background:#b3261e}.s1{background:#e8710a}.s2{background:#3367d6}.s3{background:#5f6b7a}
.kpis{display:flex;gap:12px;flex-wrap:wrap}.kpi{flex:1;min-width:130px;background:#fff;border:1px solid #e1e5ec;border-radius:10px;padding:12px 14px}
.kpi b{display:block;font-size:26px}
.issue{background:#fff;border:1px solid #e1e5ec;border-left:5px solid #ccc;border-radius:8px;padding:12px 16px;margin:10px 0}
.issue.s0b{border-left-color:#b3261e}.issue.s1b{border-left-color:#e8710a}.issue.s2b{border-left-color:#3367d6}.issue.s3b{border-left-color:#5f6b7a}
.issue h4{margin:0 0 6px;font-size:15px}.issue dl{margin:0;display:grid;grid-template-columns:72px 1fr;gap:3px 10px;font-size:13px}
.issue dt{color:#5b6476}.issue dd{margin:0}
.st{white-space:nowrap;font-weight:600}.st-ok{color:#1e7e34}.st-part{color:#b26a00}.st-open{color:#b3261e}.st-na{color:#5f6b7a}.st-old{color:#6b4fbb}
pre.arch{background:#1f2430;color:#e6e9ef;padding:14px;border-radius:8px;overflow:auto;font-size:12.5px;line-height:1.45}
a{color:#2457c5}
.note{font-size:12.5px;color:#5b6476}
</style></head><body><div class="wrap"><h1>DCU_DesktopController 专家评审</h1><div class="meta">评审日期 2026-10-19(成稿 2026-10-20)· 仓库 cbb/DCU_DesktopController · 分支 main · 基线 commit <code>eac3717</code>(feat: 添加局域网设备发现功能和链路胶囊优化)· 评审方式:多专家组并行通读 + 抽样复核行号</div><div class="kpis" style="margin-top:16px"><div class="kpi"><span class="sev s0">致命</span><b>8</b></div><div class="kpi"><span class="sev s1">严重</span><b>27</b></div><div class="kpi"><span class="sev s2">一般</span><b>33</b></div><div class="kpi"><span class="sev s3">建议</span><b>7</b></div><div class="kpi">问题总数<b>75</b></div><div class="kpi">综合评分<b>5.4 / 10</b></div></div><h2>1. 项目概述与架构</h2><div class="card"><p>本仓库是 DCU(GD32E508 主控 + LAN9252 EtherCAT 从站 + ESP32-C3 通信中继)的 <b>上位机调试控制台</b>:HTML/CSS/JS(ES module)纯前端 + 本地 Python 页面服务与桌面窗口外壳(pywebview/WebView2)。仓库内<b>不含固件</b>;业务 API 由 ESP32 固件 80 端口跨域提供,EtherCAT 主站 <code>ecat_native.exe</code> 位于外部 <code>tools/ecat</code>,由本仓 Python 代码拉起/代理。代码约 1.9 MB、184 个文件。</p><pre class="arch">┌──────────────────── 桌面窗口 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)</pre><table><tr><th>模块</th><th>主要文件</th><th>职责</th><th>评分</th></tr><tr><td>主机服务/安全</td><td>esp32_server.py, ecat_client.py, ecat_proc.py, lan_probe.py, *.vbs/bat</td><td>本地静态服务、ECAT 代理、桌面桥、native 进程治理、局域网发现</td><td>6/10</td></tr><tr><td>电机控制/故障保护</td><td>js/motor/*, js/common/ecat-bus.js, device-link.js, app-lease.js, desktop-bridge.js, pages/motor.html</td><td>双链路电机指令、周期发送、序列、示波器、总线所有权</td><td>4.5/10</td></tr><tr><td>OTA/刷写</td><td>js/app.js, js/ota/*, js/ble/*, js/ble_ota.js, pages/index.html</td><td>ESP32/GD32 多通道刷写会话与进度</td><td>5/10</td></tr><tr><td>CAN/公共层</td><td>js/can.js, js/can/*, js/can_monitor.js, js/common/*</td><td>DBC 解析编码、发送、监控;连接、轮询、日志</td><td>6/10</td></tr><tr><td>仓库卫生/文档</td><td>README.md, docs/, .gitignore, .qoder/, .zcode/, scripts/</td><td>文档、报告、测试脚本与版本控制卫生</td><td>5.5/10</td></tr></table></div><h2>2. 评审范围与方法</h2><div class="card"><ul><li><b>范围</b>:基线 <code>eac3717</code> 下全部源码与配置——Python 服务 4 个、JS 模块 50+、页面 5 个、lab 页 3 个、CSS 6 个、scripts/ 测试与探针 31 个,以及 docs/ 下全部既有报告和规格、.zcode 计划。<code>.qoder/repowiki</code> 为工具生成物,仅做卫生评估。</li><li><b>专家组</b>:① 主机服务与安全;② 实时控制与故障保护(电机);③ OTA/Bootloader 刷写可靠性;④ 前端架构、CAN 协议与仓库卫生;⑤ 既有评审/报告核对。各组独立通读负责模块后汇总,主审对致命/严重条目逐条按行号复核。</li><li><b>严重度定义</b>:<span class="sev s0">致命</span> 可能导致电机误动作、设备损坏/变砖或安全事故;<span class="sev s1">严重</span> 功能性缺陷或明显安全漏洞;<span class="sev s2">一般</span> 健壮性/可维护性;<span class="sev s3">建议</span> 改进项。</li><li><b>局限</b>:静态审阅,未上真机运行;固件(ESP32_COMM、GD32 APP/BOOT)与 <code>tools/ecat/ecat_native.c</code> 不在本仓,相关结论标注为“无法在本仓库核实”。行号以基线 commit 为准。</li><li><b>测试有效性</b>:<code>scripts/</code> 中 .mjs 与多数 .py 以 <code>process.exit</code>/<code>sys.exit</code> 返回码判定,<code>test_app_lease.py</code>、<code>test_esf_pack.py</code> 有较多断言;<code>probe_*.py</code> 为采样器无断言(合理)。覆盖集中在传输契约、刷新速率、租约与快发打包,<b>不覆盖停机路径、序列竞态、指令数值边界、链路丢失判定、HEX 解析</b>(见 M-19)。</li></ul></div><h2>3. 问题清单</h2><h3>3.1 总览</h3><table><tr><th>编号</th><th>严重度</th><th>模块</th><th>标题</th><th>位置</th></tr><tr><td><a href="#M-1">M-1</a></td><td><span class="sev s0">致命</span></td><td>电机控制/故障保护</td><td>无急停/全部失能,停止类操作均不下发失能帧</td><td><code>js/motor/main.js:809-811, 2391, 2403, 2494; js/motor/seq.js:449; js/motor/main.js:1103, 11…</code></td></tr><tr><td><a href="#M-2">M-2</a></td><td><span class="sev s0">致命</span></td><td>电机控制/故障保护</td><td>CMD 数值无范围校验,整数编码会回绕</td><td><code>js/motor/main.js:744-756; pages/motor.html:141-149; js/motor/main.js:2462-2470</code></td></tr><tr><td><a href="#M-3">M-3</a></td><td><span class="sev s0">致命</span></td><td>电机控制/故障保护</td><td>真实链路丢失永远判不出离线</td><td><code>js/motor/main.js:969-987, 1175</code></td></tr><tr><td><a href="#M-4">M-4</a></td><td><span class="sev s0">致命</span></td><td>电机控制/故障保护</td><td>序列停止后立即重启会出现两个并发执行循环</td><td><code>js/motor/seq.js:431-447, 449-455, 461-504</code></td></tr><tr><td><a href="#O-1">O-1</a></td><td><span class="sev s0">致命</span></td><td>OTA/刷写</td><td>同一块 GD32 可被两条通道同时刷写</td><td><code>js/ota/session.js:128; js/ota/boot_ecat.js:176-189, 576-577; js/ota/console-mode.js:67, 12…</code></td></tr><tr><td><a href="#O-2">O-2</a></td><td><span class="sev s0">致命</span></td><td>OTA/刷写</td><td>HEX 解析可放过损坏或截断文件</td><td><code>js/ota/file-handle.js:458-516(校验 472-477,分配 506-509)</code></td></tr><tr><td><a href="#P-1">P-1</a></td><td><span class="sev s0">致命</span></td><td>主机服务/安全</td><td>本地服务无 Origin/Host/Content-Type 校验,任意网页可跨站驱动电机</td><td><code>esp32_server.py:480-541(do_POST),504-532(ECAT 代理分支)</code></td></tr><tr><td><a href="#P-2">P-2</a></td><td><span class="sev s0">致命</span></td><td>主机服务/安全</td><td>ESF1 快发在 sendall 成功后出错仍整包重发</td><td><code>ecat_client.py:247-279</code></td></tr><tr><td><a href="#C-1">C-1</a></td><td><span class="sev s1">严重</span></td><td>CAN/公共层</td><td>DBC 中 [0|0] 被当成真实范围</td><td><code>js/can/dbc.js:164-165; js/can/signals.js:299-304</code></td></tr><tr><td><a href="#C-11">C-11</a></td><td><span class="sev s1">严重</span></td><td>CAN/公共层</td><td>独立 CAN 监控页无视所选设备</td><td><code>pages/can_monitor.html:55; js/can_monitor.js:60-68; js/can/dbc.js:40-47</code></td></tr><tr><td><a href="#C-12">C-12</a></td><td><span class="sev s1">严重</span></td><td>CAN/公共层</td><td>监控页超时永不显示离线</td><td><code>js/can_monitor.js:199</code></td></tr><tr><td><a href="#C-2">C-2</a></td><td><span class="sev s1">严重</span></td><td>CAN/公共层</td><td>扩展多路复用信号被静默丢弃</td><td><code>js/can/dbc.js:153</code></td></tr><tr><td><a href="#C-4">C-4</a></td><td><span class="sev s1">严重</span></td><td>CAN/公共层</td><td>扩展/标准帧靠 ID 数值猜测,发送载荷无帧类型标志</td><td><code>js/can/dbc.js:146; js/can/send.js:304, 373, 453, 461</code></td></tr><tr><td><a href="#C-5">C-5</a></td><td><span class="sev s1">严重</span></td><td>CAN/公共层</td><td>DLC 未按 CAN FD 合法长度校验</td><td><code>js/can/send.js:406; js/can/signals.js:233-235</code></td></tr><tr><td><a href="#H-1">H-1</a></td><td><span class="sev s1">严重</span></td><td>仓库卫生/文档</td><td>AI 工具目录被提交</td><td><code>.qoder/repowiki/(约 75 个文件); .zcode/plans/plan-sess_*.md; .gitignore</code></td></tr><tr><td><a href="#H-5">H-5</a></td><td><span class="sev s1">严重</span></td><td>仓库卫生/文档</td><td>默认 ECAT 周期 0.25 ms 与既有报告建议冲突且缺复测依据</td><td><code>js/common/ecat-bus.js:28; js/motor/transport-ecat.js:428-431</code></td></tr><tr><td><a href="#M-10">M-10</a></td><td><span class="sev s1">严重</span></td><td>电机控制/故障保护</td><td>周期发送与序列计时在后台标签被节流,且无可见性处理</td><td><code>js/motor/main.js:2488; js/motor/seq.js:503</code></td></tr><tr><td><a href="#M-11">M-11</a></td><td><span class="sev s1">严重</span></td><td>电机控制/故障保护</td><td>ECAT 发送不核对 queued,失败也报成功</td><td><code>js/motor/transport-ecat.js:803-810</code></td></tr><tr><td><a href="#M-5">M-5</a></td><td><span class="sev s1">严重</span></td><td>电机控制/故障保护</td><td>ECAT 活性保持把陈旧数据显示为在线</td><td><code>js/motor/transport-ecat.js:526, 552, 572-590; js/motor/refresh-rate.js:138</code></td></tr><tr><td><a href="#M-6">M-6</a></td><td><span class="sev s1">严重</span></td><td>电机控制/故障保护</td><td>「谁可以动总线」红线被多处绕过</td><td><code>js/motor/transport-ecat.js:394-459, 668; js/common/device-link.js:250-287, 322-327</code></td></tr><tr><td><a href="#M-7">M-7</a></td><td><span class="sev s1">严重</span></td><td>电机控制/故障保护</td><td>OTA 忙标志写在 iframe 窗口,外壳 Owner 读不到</td><td><code>js/motor/transport-ecat.js:80-82; js/common/device-link.js:255</code></td></tr><tr><td><a href="#M-8">M-8</a></td><td><span class="sev s1">严重</span></td><td>电机控制/故障保护</td><td>ECAT 桥失败回落 HTTP,非幂等命令可能执行两次</td><td><code>js/motor/transport-ecat.js:259-271; js/common/desktop-bridge.js:59-74</code></td></tr><tr><td><a href="#M-9">M-9</a></td><td><span class="sev s1">严重</span></td><td>电机控制/故障保护</td><td>修改网卡输入框即关闭运行中总线,无任何保护</td><td><code>js/motor/transport-ecat.js:899-908; js/motor/main.js:3215-3227</code></td></tr><tr><td><a href="#O-3">O-3</a></td><td><span class="sev s1">严重</span></td><td>OTA/刷写</td><td>ESP32 重启按钮无 OTA 守卫</td><td><code>js/ota/upload-esp32.js:594-613; pages/index.html:368</code></td></tr><tr><td><a href="#O-4">O-4</a></td><td><span class="sev s1">严重</span></td><td>OTA/刷写</td><td>GD32 完成判定可被陈旧 alive 标志误判</td><td><code>js/ota/stages.js:497-499</code></td></tr><tr><td><a href="#O-5">O-5</a></td><td><span class="sev s1">严重</span></td><td>OTA/刷写</td><td>ESP32 重启后成功判定不核对新版本</td><td><code>js/common/polling.js:~236; js/ota/upload-esp32.js:435-449, 468-474</code></td></tr><tr><td><a href="#O-6">O-6</a></td><td><span class="sev s1">严重</span></td><td>OTA/刷写</td><td>WiFi/BLE OTA 不通知其它标签,端点可中途切换</td><td><code>js/ota/boot_ecat.js:598; js/app.js:63-71, 403-421</code></td></tr><tr><td><a href="#O-7">O-7</a></td><td><span class="sev s1">严重</span></td><td>OTA/刷写</td><td>BLE abort 总发会话 0;中继阶段可随意中止</td><td><code>js/ble/session.js:89; js/ble/state.js</code></td></tr><tr><td><a href="#O-8">O-8</a></td><td><span class="sev s1">严重</span></td><td>OTA/刷写</td><td>BLE GD32 身份检查未 await</td><td><code>js/ble/file.js:174, 182-205</code></td></tr><tr><td><a href="#O-9">O-9</a></td><td><span class="sev s1">严重</span></td><td>OTA/刷写</td><td>前端不校验 ESP32 镜像</td><td><code>js/ota/file-handle.js:725-751</code></td></tr><tr><td><a href="#P-3">P-3</a></td><td><span class="sev s1">严重</span></td><td>主机服务/安全</td><td>健康探测 1 s 超时即无差别强杀所有 ecat_native</td><td><code>ecat_proc.py:124-140, 143-194, 250-253</code></td></tr><tr><td><a href="#P-4">P-4</a></td><td><span class="sev s1">严重</span></td><td>主机服务/安全</td><td>跨进程锁超时或 CreateMutexW 失败仍继续执行</td><td><code>ecat_proc.py:67-94(尤其 72-81)</code></td></tr><tr><td><a href="#P-5">P-5</a></td><td><span class="sev s1">严重</span></td><td>主机服务/安全</td><td>桌面桥路径不记录循环状态,『关 app 必停从站』可能失效</td><td><code>ecat_client.py:470-497; esp32_server.py:331-340, 403-420, 533</code></td></tr><tr><td><a href="#P-6">P-6</a></td><td><span class="sev s1">严重</span></td><td>主机服务/安全</td><td>外链在窗口内导航,外部页面可调用桌面桥 js_api</td><td><code>esp32_server.py:142-146, 998-1000</code></td></tr><tr><td><a href="#P-7">P-7</a></td><td><span class="sev s1">严重</span></td><td>主机服务/安全</td><td>ecat_native 提到 REALTIME 优先级,配合自旋线程可能拖死系统;复用实例不提权</td><td><code>ecat_proc.py:96-121, 212-215, 273-276</code></td></tr><tr><td><a href="#C-10">C-10</a></td><td><span class="sev s2">一般</span></td><td>CAN/公共层</td><td>单条发送无防重入</td><td><code>js/can/send.js:282, 358, 435</code></td></tr><tr><td><a href="#C-13">C-13</a></td><td><span class="sev s2">一般</span></td><td>CAN/公共层</td><td>对设备的并发轮询过多</td><td><code>js/common/device-link.js:30; js/can.js:131, 424; js/can_monitor.js:487, 490; tabs.html:76-…</code></td></tr><tr><td><a href="#C-14">C-14</a></td><td><span class="sev s2">一般</span></td><td>CAN/公共层</td><td>轮询超时计时器可能丢失</td><td><code>js/common/polling.js:194</code></td></tr><tr><td><a href="#C-16">C-16</a></td><td><span class="sev s2">一般</span></td><td>CAN/公共层</td><td>读取已存 IP 不校验;网关猜测探测</td><td><code>js/common/base-url.js:44-50; js/common/connection.js:761-789</code></td></tr><tr><td><a href="#C-3">C-3</a></td><td><span class="sev s2">一般</span></td><td>CAN/公共层</td><td>多行 CM_ 注释丢失</td><td><code>js/can/dbc.js:178, 184</code></td></tr><tr><td><a href="#C-6">C-6</a></td><td><span class="sev s2">一般</span></td><td>CAN/公共层</td><td>手动 ID 解析过宽</td><td><code>js/can/dbc.js:58</code></td></tr><tr><td><a href="#C-7">C-7</a></td><td><span class="sev s2">一般</span></td><td>CAN/公共层</td><td>枚举/多路复用/浮点信号编码细节错误</td><td><code>js/can/signals.js:45-51, 76, 294-296, 406</code></td></tr><tr><td><a href="#C-8">C-8</a></td><td><span class="sev s2">一般</span></td><td>CAN/公共层</td><td>持久化静默失败且状态只增不减</td><td><code>js/can/persist.js:93-124; js/can.js:229</code></td></tr><tr><td><a href="#C-9">C-9</a></td><td><span class="sev s2">一般</span></td><td>CAN/公共层</td><td>toast 模块被加载成两个实例</td><td><code>js/can/send.js:17; js/can.js:19-20, 47</code></td></tr><tr><td><a href="#H-2">H-2</a></td><td><span class="sev s2">一般</span></td><td>仓库卫生/文档</td><td>受版本控制文件含个人绝对路径</td><td><code>scripts/build_mem.bat:21; README.md:3, 449; docs/compose/spec/host-code-quality.md:67</code></td></tr><tr><td><a href="#H-3">H-3</a></td><td><span class="sev s2">一般</span></td><td>仓库卫生/文档</td><td>README 脚本清单与 scripts/ 不一致;.gitignore 规则重叠</td><td><code>README.md; scripts/check_fe_guard_mutation.py; scripts/ui_diag_motor.mjs; .gitignore; .git…</code></td></tr><tr><td><a href="#H-4">H-4</a></td><td><span class="sev s2">一般</span></td><td>仓库卫生/文档</td><td>注释/文档与代码不符(默认周期等)</td><td><code>js/motor/transport-ecat.js:37, 166, 416-417, 607; README.md:190</code></td></tr><tr><td><a href="#H-6">H-6</a></td><td><span class="sev s2">一般</span></td><td>仓库卫生/文档</td><td>GD32 WiFi 中继刷写缺『刷写≠生效』版本比对,守卫脚本已删除</td><td><code>js/ota/upload-gd32.js; js/ota/stages.js:497-499</code></td></tr><tr><td><a href="#H-8">H-8</a></td><td><span class="sev s2">一般</span></td><td>仓库卫生/文档</td><td>ecat_integral_report 结论与其附带脚本输出自相矛盾</td><td><code>docs/reports/ecat_integral_report.html</code></td></tr><tr><td><a href="#M-12">M-12</a></td><td><span class="sev s2">一般</span></td><td>电机控制/故障保护</td><td>运行中导入/水合配置会替换正在使用的参数</td><td><code>js/motor/main.js:242-289; js/motor/seq.js:223</code></td></tr><tr><td><a href="#M-13">M-13</a></td><td><span class="sev s2">一般</span></td><td>电机控制/故障保护</td><td>危险默认值</td><td><code>js/motor/seq.js:76-89; pages/motor.html:105, 123, 127</code></td></tr><tr><td><a href="#M-14">M-14</a></td><td><span class="sev s2">一般</span></td><td>电机控制/故障保护</td><td>NaN/Inf 浮点被显示为 0</td><td><code>js/motor/main.js:624-627, 666-669</code></td></tr><tr><td><a href="#M-15">M-15</a></td><td><span class="sev s2">一般</span></td><td>电机控制/故障保护</td><td>磁盘水合后链路状态不一致</td><td><code>js/motor/main.js:268, 3172</code></td></tr><tr><td><a href="#M-16">M-16</a></td><td><span class="sev s2">一般</span></td><td>电机控制/故障保护</td><td>节点 ID 修改不核对 ACK;去重表键不含通道</td><td><code>js/motor/main.js:3044-3046</code></td></tr><tr><td><a href="#M-19">M-19</a></td><td><span class="sev s2">一般</span></td><td>电机控制/故障保护</td><td>自动化测试未覆盖停机/序列竞态/数值范围</td><td><code>scripts/test_motor_transport.mjs; scripts/test_motor_refresh.mjs; scripts/test_ecat_autost…</code></td></tr><tr><td><a href="#O-10">O-10</a></td><td><span class="sev s2">一般</span></td><td>OTA/刷写</td><td>BLE ACK/重传机制薄弱</td><td><code>js/ble/gatt.js:249-255; js/ble/transfer.js:45-57, 75-78, 282</code></td></tr><tr><td><a href="#O-11">O-11</a></td><td><span class="sev s2">一般</span></td><td>OTA/刷写</td><td>BLE 载荷固定 480 B,无 MTU 回退</td><td><code>js/ble/constants.js:38, 50; js/ble/state.js:76</code></td></tr><tr><td><a href="#O-12">O-12</a></td><td><span class="sev s2">一般</span></td><td>OTA/刷写</td><td>BLE 断开不拒绝结果等待</td><td><code>js/ble/gatt.js:139-150; js/ble/transfer.js:394</code></td></tr><tr><td><a href="#O-13">O-13</a></td><td><span class="sev s2">一般</span></td><td>OTA/刷写</td><td>GD32 中继触发超时后可能误判失败</td><td><code>js/ota/upload-gd32.js:191-216</code></td></tr><tr><td><a href="#O-14">O-14</a></td><td><span class="sev s2">一般</span></td><td>OTA/刷写</td><td>ECAT 直灌无完整性校验,api() 不查 HTTP 状态</td><td><code>js/ota/boot_ecat.js:89-99, 635-661</code></td></tr><tr><td><a href="#P-10">P-10</a></td><td><span class="sev s2">一般</span></td><td>主机服务/安全</td><td>代理读取 Content-Length 与 native 响应均无上限</td><td><code>esp32_server.py:509-512; ecat_client.py:350-355</code></td></tr><tr><td><a href="#P-11">P-11</a></td><td><span class="sev s2">一般</span></td><td>主机服务/安全</td><td>ESF 打包静默修改数据</td><td><code>ecat_client.py:140-141, 157-166</code></td></tr><tr><td><a href="#P-12">P-12</a></td><td><span class="sev s2">一般</span></td><td>主机服务/安全</td><td>静态服务暴露整个 BASE_DIR,无扩展名文件被当 HTML 注入脚本</td><td><code>esp32_server.py:434, 454-466, 474</code></td></tr><tr><td><a href="#P-13">P-13</a></td><td><span class="sev s2">一般</span></td><td>主机服务/安全</td><td>WebSocket 实现不完整且不校验 Origin</td><td><code>esp32_server.py:551-564, 604-606</code></td></tr><tr><td><a href="#P-14">P-14</a></td><td><span class="sev s2">一般</span></td><td>主机服务/安全</td><td>端口复用与单实例检测存在竞态</td><td><code>esp32_server.py:756-779, 897-898, 954</code></td></tr><tr><td><a href="#P-15">P-15</a></td><td><span class="sev s2">一般</span></td><td>主机服务/安全</td><td>退出路径可能跳过停机</td><td><code>ecat_proc.py:301-311; esp32_server.py:348, 873-878</code></td></tr><tr><td><a href="#P-8">P-8</a></td><td><span class="sev s2">一般</span></td><td>主机服务/安全</td><td>非幂等请求首次可能误报 503</td><td><code>ecat_client.py:405, 415, 426-439</code></td></tr><tr><td><a href="#P-9">P-9</a></td><td><span class="sev s2">一般</span></td><td>主机服务/安全</td><td>桥接路径可写入任意 HTTP 请求行</td><td><code>ecat_client.py:93-107, 337-344, 470</code></td></tr><tr><td><a href="#C-15">C-15</a></td><td><span class="sev s3">建议</span></td><td>CAN/公共层</td><td>大量隐式全局与重复代码</td><td><code>js/common/polling.js:10-29; js/common/connection.js:6-13; js/can/send.js:36; js/can_monito…</code></td></tr><tr><td><a href="#H-7">H-7</a></td><td><span class="sev s3">建议</span></td><td>仓库卫生/文档</td><td>报告中的资源与脚本链接随归档失效</td><td><code>docs/reports/ECAT_RATE_TRADEOFF.md; docs/reports/ecat_integral_report.html; docs/reports/R…</code></td></tr><tr><td><a href="#M-17">M-17</a></td><td><span class="sev s3">建议</span></td><td>电机控制/故障保护</td><td>示波器数据经滤波与重采样,定时器从不停止</td><td><code>js/motor/scope.js:31, 228-256, 269</code></td></tr><tr><td><a href="#M-18">M-18</a></td><td><span class="sev s3">建议</span></td><td>电机控制/故障保护</td><td>main.js 约 3250 行,流控逻辑重复</td><td><code>js/motor/main.js:807-948</code></td></tr><tr><td><a href="#O-15">O-15</a></td><td><span class="sev s3">建议</span></td><td>OTA/刷写</td><td>大量 window.* 全局、猴补 showOpenFilePicker、内存复制</td><td><code>js/ota/console-mode.js:170-199; js/ota/upload-esp32.js:213-247; js/ota/progress.js:177; js…</code></td></tr><tr><td><a href="#P-16">P-16</a></td><td><span class="sev s3">建议</span></td><td>主机服务/安全</td><td>native_stdout.log 只追加不轮转</td><td><code>ecat_proc.py:259-262</code></td></tr><tr><td><a href="#P-17">P-17</a></td><td><span class="sev s3">建议</span></td><td>主机服务/安全</td><td>LAN 探测接口可被当端口扫描器</td><td><code>lan_probe.py:320-333; esp32_server.py:673-684</code></td></tr></table><h3>3.2 详细说明</h3><h3>电机控制/故障保护</h3><div class="issue s0b" id="M-1"><h4><span class="sev s0">致命</span> [M-1] 无急停/全部失能,停止类操作均不下发失能帧</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/main.js:809-811, 2391, 2403, 2494; js/motor/seq.js:449; js/motor/main.js:1103, 1193; pages/motor.html</code></dd><dt>现象</dt><dd>页面没有 E-STOP 或「全部失能」。失能需切 mode 0 再经 sendBatch 发送,而 sendBatch 在上一次发送在途时直接跳过(返回 busy),总线冷却期也拒发。停止周期发送、停止序列、断开连接、切换链路都只停本地定时器,不发 Disable。</dd><dt>风险</dt><dd>速度模式下电机将按最后目标持续转动;操作员以为『已停止』,实际电机仍在运动,可能造成机械碰撞或人身伤害。</dd><dt>修改建议</dt><dd>增加独立急停按钮与快捷键(如 Esc/空格),绕过 sendInFlight/冷却门直接对所有节点下发 mode 0 + 零目标(并重复 2~3 次);stopRepeat/stopSeq/disconnect/切链路统一调用 safeStopAll();急停走最短路径(WiFi/ECAT 各自直发)。</dd></dl></div><div class="issue s0b" id="M-2"><h4><span class="sev s0">致命</span> [M-2] CMD 数值无范围校验,整数编码会回绕</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/main.js:744-756; pages/motor.html:141-149; js/motor/main.js:2462-2470</code></dd><dt>现象</dt><dd>buildCmdBytes 中电压 Math.round(v*1000)&amp;0xFFFF(输入 −1 V 实际下发约 64.5 V);PID 增益走 setInt16(40000 回绕为负增益);电流/速度/角度无上限;HTML min/max 不被 JS 强制;±9999 快捷按钮无确认即发送。</dd><dt>风险</dt><dd>误输入或误点即可下发远超额定的电压/速度或负增益,导致失控、过流、驱动器或电机损坏。</dd><dt>修改建议</dt><dd>在 buildCmdBytes 入口集中 clamp/拒绝:电压 0~额定值、电流/速度/角度按电机参数限幅、PID 0~32767;非法值抛错而非静默编码;大幅值按钮二次确认;为编码函数补单元测试。</dd></dl></div><div class="issue s0b" id="M-3"><h4><span class="sev s0">致命</span> [M-3] 真实链路丢失永远判不出离线</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/main.js:969-987, 1175</code></dd><dt>现象</dt><dd>isTransientError 把超时、AbortError、Failed to fetch、ECONNREFUSED 等全部判为瞬时;noteHardFailure 对 transient 直接 return,不计数。注释所称『网络类加权/更高阈值兜底』在代码中并不存在。</dd><dt>风险</dt><dd>WiFi 断开/设备掉电时页面永不进入离线态,周期发送与序列静默继续,界面把陈旧数据当实时显示,操作员失去态势感知。</dd><dt>修改建议</dt><dd>瞬时错误单独计数(如连续 N 次或持续 T 秒无成功即判离线);离线时自动停止周期发送/序列并提示;补一个『拔线』契约测试。</dd></dl></div><div class="issue s0b" id="M-4"><h4><span class="sev s0">致命</span> [M-4] 序列停止后立即重启会出现两个并发执行循环</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/seq.js:431-447, 449-455, 461-504</code></dd><dt>现象</dt><dd>stopSeq 只置 running=false、stopRequested=true,不等待旧循环退出,也无代次令牌;startSeq 又把 running=true、stopRequested=false。旧循环若正处于 await sleep(holdMs)(最长 600 s)醒来后看到 running 仍为 true 便继续执行。</dd><dt>风险</dt><dd>两个循环交错下发不同步骤,电机运动序列错乱。</dd><dt>修改建议</dt><dd>引入 runId/generation:startSeq 自增,循环每个 await 后校验 myId===seq.runId;或 stopSeq 返回 Promise,待旧循环退出后才允许重启;sleep 改为可中断(AbortSignal)。</dd></dl></div><div class="issue s1b" id="M-10"><h4><span class="sev s1">严重</span> [M-10] 周期发送与序列计时在后台标签被节流,且无可见性处理</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/main.js:2488; js/motor/seq.js:503</code></dd><dt>现象</dt><dd>周期发送用主线程 setInterval,序列保持用 setTimeout;隐藏 iframe/最小化后被浏览器降到 ≥1 s 乃至分钟级。只有状态轮询走 Worker。无 visibilitychange 停止/告警;WiFi 链路关窗时无停机。</dd><dt>风险</dt><dd>后台时控制节拍严重失真,序列时间轴不可预测。</dd><dt>修改建议</dt><dd>发送节拍迁入 tick.worker.js;visibilitychange 时提示或按策略安全停机;pagehide 时对 WiFi 也发停机(sendBeacon)。</dd></dl></div><div class="issue s1b" id="M-11"><h4><span class="sev s1">严重</span> [M-11] ECAT 发送不核对 queued,失败也报成功</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/transport-ecat.js:803-810</code></dd><dt>现象</dt><dd>对每帧都返回 ok,未比较 queued 与帧数。</dd><dt>风险</dt><dd>native 队列满/丢帧时界面显示发送成功。</dd><dt>修改建议</dt><dd>queued &lt; frames.length 时逐帧标记失败并上报 backlog。</dd></dl></div><div class="issue s1b" id="M-5"><h4><span class="sev s1">严重</span> [M-5] ECAT 活性保持把陈旧数据显示为在线</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/transport-ecat.js:526, 552, 572-590; js/motor/refresh-rate.js:138</code></dd><dt>现象</dt><dd>槽位消失后在 staleLimitMs(可达 30 s)内仍报 online:true 并返回旧数据,另叠加 500 ms 软保持。</dd><dt>风险</dt><dd>电机已离线/从站已掉 OP 时界面仍显示正常状态。</dd><dt>修改建议</dt><dd>保持期内明确标记 stale(灰显+时间戳),上限压到 1~2 s;超过即 online:false。</dd></dl></div><div class="issue s1b" id="M-6"><h4><span class="sev s1">严重</span> [M-6] 「谁可以动总线」红线被多处绕过</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/transport-ecat.js:394-459, 668; js/common/device-link.js:250-287, 322-327</code></dd><dt>现象</dt><dd>transport-ecat.ensureStarted 在 isBusHealthy 为 false(含 unknown)时调用 motor/start,不带 reason、不走 ensureEcatLoopRepair 预算,并重复实现 ecat-bus.resolveEcatStartBody 的周期/DC 逻辑;Owner bootstrapEcatBus 同样无 reason 且把 unknown 当需要启动;repairEcatDc 无间隔/次数预算,pollEcat 每 2 s 与 healthCheck 都可触发。</dd><dt>风险</dt><dd>健康总线被误重启、OTA/刷写期间总线被抢、DC 修复风暴。</dd><dt>修改建议</dt><dd>所有 start/repair 统一经 ecat-bus 单入口(带 reason、预算与 OTA 闸);unknown 状态不得触发启动;删除 transport-ecat 内重复的周期计算(与 host-code-quality R6 同源)。</dd></dl></div><div class="issue s1b" id="M-7"><h4><span class="sev s1">严重</span> [M-7] OTA 忙标志写在 iframe 窗口,外壳 Owner 读不到</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/transport-ecat.js:80-82; js/common/device-link.js:255</code></dd><dt>现象</dt><dd>setMotorOtaEcatBusy 写的是电机页 iframe 自身的 window.motorOtaEcatBusy;Owner 运行于 tabs.html 顶层窗口并读自己的 window,闸门永不生效;页面中途关闭时标志也不会清除。</dd><dt>风险</dt><dd>OTA 期间 Owner 仍可能 open/motor/start 抢总线,导致刷写失败或半截镜像。</dd><dt>修改建议</dt><dd>改用 BroadcastChannel/localStorage 带 TTL 的跨帧忙标志(与 boot_ecat.js:598 统一一处实现),Owner 读取同一来源。</dd></dl></div><div class="issue s1b" id="M-8"><h4><span class="sev s1">严重</span> [M-8] ECAT 桥失败回落 HTTP,非幂等命令可能执行两次</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/transport-ecat.js:259-271; js/common/desktop-bridge.js:59-74</code></dd><dt>现象</dt><dd>低频路径(motor/send、motor/start 等)在桌面桥失败或超时后无条件回落 HTTP 再发一次;desktopEcatPost/desktopEcatSendRaw 在 JS 侧无超时。</dd><dt>风险</dt><dd>桥已把命令送达 native 但响应超时时,HTTP 再送一次,造成重复运动指令/重复 start;与 pdo-fast-send『堵死慢发』原则冲突。</dd><dt>修改建议</dt><dd>按 ecat_client.py 的幂等白名单处理:非幂等尾部仅在确认未送达(连接失败)时回落;JS 桥调用加 Promise.race 超时。</dd></dl></div><div class="issue s1b" id="M-9"><h4><span class="sev s1">严重</span> [M-9] 修改网卡输入框即关闭运行中总线,无任何保护</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/transport-ecat.js:899-908; js/motor/main.js:3215-3227</code></dd><dt>现象</dt><dd>adapter 输入框 change 事件立即调用 api(&#x27;close&#x27;),不确认、不检查 OTA 或电机运动状态,误输入即触发。</dd><dt>风险</dt><dd>运动中总线被关,从站失去 PDO 输出(取决于从站看门狗行为),OTA 中断。</dd><dt>修改建议</dt><dd>改为显式『应用』按钮+确认;运动/OTA 中禁止;先安全停机再 close。</dd></dl></div><div class="issue s2b" id="M-12"><h4><span class="sev s2">一般</span> [M-12] 运行中导入/水合配置会替换正在使用的参数</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/main.js:242-289; js/motor/seq.js:223</code></dd><dt>现象</dt><dd>applyUiSnapshot 在序列/周期发送运行时替换 seq.steps 与 cmdCommitted;运行中键盘编辑步骤未被阻止。</dd><dt>风险</dt><dd>运行中途参数被替换,运动不可预期。</dd><dt>修改建议</dt><dd>运行中禁止导入/水合,或先停止再应用。</dd></dl></div><div class="issue s2b" id="M-13"><h4><span class="sev s2">一般</span> [M-13] 危险默认值</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/seq.js:76-89; pages/motor.html:105, 123, 127</code></dd><dt>现象</dt><dd>actionFromAny 缺省回落 mode 3、速度 100、电压 24 V;页面默认 mode 3、速度 360、角度 180。</dd><dt>风险</dt><dd>缺字段或首次使用即带非零运动目标。</dd><dt>修改建议</dt><dd>默认 mode 0(失能)、目标 0;缺字段拒绝而非补默认。</dd></dl></div><div class="issue s2b" id="M-14"><h4><span class="sev s2">一般</span> [M-14] NaN/Inf 浮点被显示为 0</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/main.js:624-627, 666-669</code></dd><dt>现象</dt><dd>decodeStatus/decodeConfig 把非有限值转成 0。</dd><dt>风险</dt><dd>坏数据看起来像『已停止』。</dd><dt>修改建议</dt><dd>显示为 &#x27;--&#x27; 或 ERR 并计数。</dd></dl></div><div class="issue s2b" id="M-15"><h4><span class="sev s2">一般</span> [M-15] 磁盘水合后链路状态不一致</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/main.js:268, 3172</code></dd><dt>现象</dt><dd>写入 LS_TRANSPORT 但未切换 state.tp。</dd><dt>风险</dt><dd>界面显示的链路与实际发送链路不一致。</dd><dt>修改建议</dt><dd>水合后统一走 setTransport()。</dd></dl></div><div class="issue s2b" id="M-16"><h4><span class="sev s2">一般</span> [M-16] 节点 ID 修改不核对 ACK;去重表键不含通道</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/main.js:3044-3046</code></dd><dt>现象</dt><dd>setNodeId 未确认 ACK 即更改 selectedNode;lastSentMode/lastSentTargets 只按 node 区分。</dd><dt>风险</dt><dd>命令发往错误节点;多通道同号节点去重误判。</dd><dt>修改建议</dt><dd>等 ACK 成功再切换;去重键改为 ch:node。</dd></dl></div><div class="issue s2b" id="M-19"><h4><span class="sev s2">一般</span> [M-19] 自动化测试未覆盖停机/序列竞态/数值范围</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>scripts/test_motor_transport.mjs; scripts/test_motor_refresh.mjs; scripts/test_ecat_autostart.mjs</code></dd><dt>现象</dt><dd>现有测试覆盖传输契约、刷新速率、自动接入链(均以 process.exit 返回码判定),但没有任何用例覆盖:停止路径是否发失能、序列重启竞态、CMD 编码边界值、链路真实丢失判定。</dd><dt>风险</dt><dd>M-1~M-4 这类致命问题无法被回归发现。</dd><dt>修改建议</dt><dd>为 buildCmdBytes、stopSeq/startSeq、isTransientError/noteHardFailure 补纯逻辑单测并接入现有验收链。</dd></dl></div><div class="issue s3b" id="M-17"><h4><span class="sev s3">建议</span> [M-17] 示波器数据经滤波与重采样,定时器从不停止</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/scope.js:31, 228-256, 269</code></dd><dt>现象</dt><dd>33 ms rAF 采样并做 IIR 平滑,非实时波形;setInterval 与 rAF 永不停止。</dd><dt>风险</dt><dd>波形误导调参;隐藏页持续耗电。</dd><dt>修改建议</dt><dd>提供原始数据模式;隐藏/暂停时停止循环。</dd></dl></div><div class="issue s3b" id="M-18"><h4><span class="sev s3">建议</span> [M-18] main.js 约 3250 行,流控逻辑重复</h4><dl><dt>模块</dt><dd>电机控制/故障保护</dd><dt>位置</dt><dd><code>js/motor/main.js:807-948</code></dd><dt>现象</dt><dd>单文件过大、依赖函数提升的闭包顺序;sendBatch 与 sendBurst 流控代码复制。</dd><dt>风险</dt><dd>改动易引入回归。</dd><dt>修改建议</dt><dd>拆分 cmd 编码、流控、UI 绑定、持久化模块;抽公共流控。</dd></dl></div><h3>主机服务/安全</h3><div class="issue s0b" id="P-1"><h4><span class="sev s0">致命</span> [P-1] 本地服务无 Origin/Host/Content-Type 校验,任意网页可跨站驱动电机</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>esp32_server.py:480-541(do_POST),504-532(ECAT 代理分支)</code></dd><dt>现象</dt><dd>服务只绑 127.0.0.1,但不检查 Origin/Host。用户浏览器中任意网页可 fetch(&#x27;http://127.0.0.1:&lt;port&gt;/ecat/api/motor/start&#x27;,{method:&#x27;POST&#x27;,mode:&#x27;no-cors&#x27;,body:&#x27;{}&#x27;});text/plain 简单请求无预检直达,同样可打 motor/send、/api/shutdown、/api/lan/probe。不校验 Host 还使 DNS rebinding 能读取响应。</dd><dt>风险</dt><dd>恶意或被劫持的网页可在用户不知情时启动总线、下发运动帧或关闭服务。</dd><dt>修改建议</dt><dd>所有 POST/WebSocket 校验 Origin 与 Host ∈ {127.0.0.1:port, localhost:port};强制 Content-Type: application/json(触发预检);可加启动时随机令牌。</dd></dl></div><div class="issue s0b" id="P-2"><h4><span class="sev s0">致命</span> [P-2] ESF1 快发在 sendall 成功后出错仍整包重发</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>ecat_client.py:247-279</code></dd><dt>现象</dt><dd>_tcp_once 持锁 sendall(body) 后读 8 字节 FSOK;recv 超时/断连抛异常时 attempt 0 会 continue,attempt 1 再发同一 body。与同文件非幂等不重发原则(_ECAT_IDEMPOTENT_TAILS)矛盾。</dd><dt>风险</dt><dd>同一帧运动指令被 native 入队两次,电机执行两次增量/相对命令。</dd><dt>修改建议</dt><dd>仅当 connect 阶段失败时重试;sendall 之后的任何异常直接返回 ok:false(可附 maybe_sent 标记)。</dd></dl></div><div class="issue s1b" id="P-3"><h4><span class="sev s1">严重</span> [P-3] 健康探测 1 s 超时即无差别强杀所有 ecat_native</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>ecat_proc.py:124-140, 143-194, 250-253</code></dd><dt>现象</dt><dd>health 只等 1.0 s;代理失败触发 autostart → _kill_all_ecat_native 按进程名 taskkill /F。native 忙于 OTA/statecheck 时 health 超时即被杀。</dd><dt>风险</dt><dd>正在进行的 EtherCAT 刷写被打断,可能留下半截镜像;运行中总线被硬断。</dd><dt>修改建议</dt><dd>端口仍在监听则不杀;只杀自己拉起的 pid;刷写期间(共享忙标志)禁止 kill。</dd></dl></div><div class="issue s1b" id="P-4"><h4><span class="sev s1">严重</span> [P-4] 跨进程锁超时或 CreateMutexW 失败仍继续执行</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>ecat_proc.py:67-94(尤其 72-81)</code></dd><dt>现象</dt><dd>_EcatProcXLock 未拿到锁(_owned=False)时不中止,后续 kill/拉起照常进行。</dd><dt>风险</dt><dd>多实例并发 kill/start,串行保护失效。</dd><dt>修改建议</dt><dd>未拿到锁时返回失败(503)或只读复用,不做 kill/start。</dd></dl></div><div class="issue s1b" id="P-5"><h4><span class="sev s1">严重</span> [P-5] 桌面桥路径不记录循环状态,『关 app 必停从站』可能失效</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>ecat_client.py:470-497; esp32_server.py:331-340, 403-420, 533</code></dd><dt>现象</dt><dd>GUI 进程经桥发出的 motor/start/stop 不调用 _note_ecat_loop,服务子进程的 g_ecat_motor_live 不更新;关窗时租约与 _shutdown_self 据此可能不停循环(复用外部 native 时尤甚)。</dd><dt>风险</dt><dd>关闭软件后 PDO 循环仍在跑,电机保持最后输出。</dd><dt>修改建议</dt><dd>桥调用后同步通知服务子进程(或由 native health 的 loop 字段判定),关窗统一按 native 实际状态停机。</dd></dl></div><div class="issue s1b" id="P-6"><h4><span class="sev s1">严重</span> [P-6] 外链在窗口内导航,外部页面可调用桌面桥 js_api</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>esp32_server.py:142-146, 998-1000</code></dd><dt>现象</dt><dd>注入脚本把 window.open/_blank 改为当前窗口导航;pywebview js_api 暴露 ecat_send_raw/ecat_api,导航到外部站点后该站点即可调用。</dd><dt>风险</dt><dd>外部页面直接发 EtherCAT 原始帧。</dd><dt>修改建议</dt><dd>外链改为系统浏览器打开(webbrowser.open);js_api 调用前校验 window.location.origin。</dd></dl></div><div class="issue s1b" id="P-7"><h4><span class="sev s1">严重</span> [P-7] ecat_native 提到 REALTIME 优先级,配合自旋线程可能拖死系统;复用实例不提权</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>ecat_proc.py:96-121, 212-215, 273-276</code></dd><dt>现象</dt><dd>新拉起进程设 REALTIME_PRIORITY_CLASS;README 称循环为自旋+TIME_CRITICAL。复用已有实例分支不设置(行为不一致)。</dd><dt>风险</dt><dd>饿死同核系统线程,UI 冻结后无法下达停机;不同启动路径实时性不一致。</dd><dt>修改建议</dt><dd>改用 HIGH_PRIORITY_CLASS + 线程亲和性绑核;如需 REALTIME,确保至少留一核并在复用路径一致处理。</dd></dl></div><div class="issue s2b" id="P-10"><h4><span class="sev s2">一般</span> [P-10] 代理读取 Content-Length 与 native 响应均无上限</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>esp32_server.py:509-512; ecat_client.py:350-355</code></dd><dt>现象</dt><dd>Content-Length 不设上限;接收无总时长和大小上限。</dd><dt>风险</dt><dd>内存耗尽/线程挂死。</dd><dt>修改建议</dt><dd>限制请求体(如 1 MB)与响应总时长。</dd></dl></div><div class="issue s2b" id="P-11"><h4><span class="sev s2">一般</span> [P-11] ESF 打包静默修改数据</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>ecat_client.py:140-141, 157-166</code></dd><dt>现象</dt><dd>奇数位 hex 静默前补 0(字节错位);不校验 data 长度与 dlc、不校验 CAN FD 合法 DLC 集合;id 被 &amp;0xFFFFFFFF 静默截断。</dd><dt>风险</dt><dd>下发与用户输入不一致的帧。</dd><dt>修改建议</dt><dd>非法输入直接报错。</dd></dl></div><div class="issue s2b" id="P-12"><h4><span class="sev s2">一般</span> [P-12] 静态服务暴露整个 BASE_DIR,无扩展名文件被当 HTML 注入脚本</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>esp32_server.py:434, 454-466, 474</code></dd><dt>现象</dt><dd>/.git/HEAD、/.gitignore 等可读并以 text/html 注入 _reload 脚本;注入用 replace(&#x27;&lt;/body&gt;&#x27;,...,1) 大小写敏感且可能落在脚本字符串里;重复 Cache-Control 头;注释提到的 Last-Modified 未发送。</dd><dt>风险</dt><dd>信息泄露、页面被破坏。</dd><dt>修改建议</dt><dd>静态白名单目录(pages/ js/ css/ lab/ tabs.html);仅对 .html 注入;用正则定位最后一个 &lt;/body&gt;。</dd></dl></div><div class="issue s2b" id="P-13"><h4><span class="sev s2">一般</span> [P-13] WebSocket 实现不完整且不校验 Origin</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>esp32_server.py:551-564, 604-606</code></dd><dt>现象</dt><dd>Pong 只用一字节长度编码,payload&gt;125 出错;外站可连 WS 挂住租约,阻止停机。</dd><dt>风险</dt><dd>关窗不停机。</dd><dt>修改建议</dt><dd>完整实现长度编码;握手校验 Origin。</dd></dl></div><div class="issue s2b" id="P-14"><h4><span class="sev s2">一般</span> [P-14] 端口复用与单实例检测存在竞态</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>esp32_server.py:756-779, 897-898, 954</code></dd><dt>现象</dt><dd>未用 SO_EXCLUSIVEADDRUSE,探测与绑定间 TOCTOU;查已有实例只试 port..+2,绑定最多漂移 6 个;windll GetLastError 不可靠。</dd><dt>风险</dt><dd>多实例并存、旧页面连到错误实例。</dd><dt>修改建议</dt><dd>SO_EXCLUSIVEADDRUSE;探测范围与漂移范围一致;ctypes.WinDLL(use_last_error=True)。</dd></dl></div><div class="issue s2b" id="P-15"><h4><span class="sev s2">一般</span> [P-15] 退出路径可能跳过停机</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>ecat_proc.py:301-311; esp32_server.py:348, 873-878</code></dd><dt>现象</dt><dd>watch_parent 用 os._exit 退出跳过停循环,OpenProcess 失败时静默放弃;父进程只等 3 s 就 kill,短于 op/stop 的 5 s 超时。</dd><dt>风险</dt><dd>关窗后循环未停。</dd><dt>修改建议</dt><dd>退出前同步 stop;父进程等待 ≥ stop 超时。</dd></dl></div><div class="issue s2b" id="P-8"><h4><span class="sev s2">一般</span> [P-8] 非幂等请求首次可能误报 503</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>ecat_client.py:405, 415, 426-439</code></dd><dt>现象</dt><dd>非缓存端口 0.3 s 连接超时不算『拒绝』,非幂等请求在 8090 即 break,不尝试 8091+;随后 autostart 走复用分支返回 503『已自动拉起请重试』。</dd><dt>风险</dt><dd>首次操作失败,用户困惑。</dd><dt>修改建议</dt><dd>端口发现与请求分离:先发现可用端口再发非幂等请求。</dd></dl></div><div class="issue s2b" id="P-9"><h4><span class="sev s2">一般</span> [P-9] 桥接路径可写入任意 HTTP 请求行</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>ecat_client.py:93-107, 337-344, 470</code></dd><dt>现象</dt><dd>路径未校验,&#x27;..&#x27; 与控制字符原样进入请求行。</dd><dt>风险</dt><dd>请求走私/打到 native 非预期路由。</dd><dt>修改建议</dt><dd>路径白名单(^[a-z0-9_/]+$)。</dd></dl></div><div class="issue s3b" id="P-16"><h4><span class="sev s3">建议</span> [P-16] native_stdout.log 只追加不轮转</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>ecat_proc.py:259-262</code></dd><dt>现象</dt><dd>日志无限增长。</dd><dt>风险</dt><dd>长期运行占满磁盘。</dd><dt>修改建议</dt><dd>按大小轮转(RotatingFileHandler 或启动时截断)。</dd></dl></div><div class="issue s3b" id="P-17"><h4><span class="sev s3">建议</span> [P-17] LAN 探测接口可被当端口扫描器</h4><dl><dt>模块</dt><dd>主机服务/安全</dd><dt>位置</dt><dd><code>lan_probe.py:320-333; esp32_server.py:673-684</code></dd><dt>现象</dt><dd>端口数量与范围不受限,IP 不排除回环与公网(路径有白名单,值得肯定);配合 P-1 外站可借此扫内网。</dd><dt>风险</dt><dd>内网侦察。</dd><dt>修改建议</dt><dd>端口限制为 {80},IP 限私网段,数量上限 64。</dd></dl></div><h3>OTA/刷写</h3><div class="issue s0b" id="O-1"><h4><span class="sev s0">致命</span> [O-1] 同一块 GD32 可被两条通道同时刷写</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>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</code></dd><dt>现象</dt><dd>WiFi 上传进入 VERIFYING/RELAYING 后 window.isUploading 置 false;依赖它的所有忙判(ECAT 按钮、模式切换、ECAT 负载轮询)都放行。bootEcatFlash 只查 bootEcatBusy;startUpload 与 BLE autoFlash 从不查 bootEcatBusy。</dd><dt>风险</dt><dd>ESP32 正经 SPI 中继写 GD32 时,ECAT 或 BLE 又开始刷同一芯片,Flash 内容交错,设备变砖。</dd><dt>修改建议</dt><dd>建立唯一的 flashBusy 互斥(跨帧共享,含 WiFi/中继/BLE/ECAT 全阶段),所有入口统一检查;isUploading 仅作 UI 用途。</dd></dl></div><div class="issue s0b" id="O-2"><h4><span class="sev s0">致命</span> [O-2] HEX 解析可放过损坏或截断文件</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/ota/file-handle.js:458-516(校验 472-477,分配 506-509)</code></dd><dt>现象</dt><dd>非十六进制字符使 parseInt 得 NaN,sum 变 NaN,NaN &amp; 0xFF === 0 通过校验;不要求 EOF 记录(截断文件被接受);不校验 byteCount 与行长;不校验基址是否为 0x08048000;稀疏 HEX 可令 maxAddr-minAddr 分配数百 MB。端到端 SHA 只证明传输完整。</dd><dt>风险</dt><dd>截断或错位的 APP 被刷入,设备无法启动。</dd><dt>修改建议</dt><dd>逐字符正则校验 ^:[0-9A-Fa-f]+$、byteCount*2+11===len、必须有 01 记录、基址/大小落在 APP 分区内、跨度上限。</dd></dl></div><div class="issue s1b" id="O-3"><h4><span class="sev s1">严重</span> [O-3] ESP32 重启按钮无 OTA 守卫</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/ota/upload-esp32.js:594-613; pages/index.html:368</code></dd><dt>现象</dt><dd>restartEsp32 仅 confirm 一次,不检查 OTA 会话/中继状态。</dd><dt>风险</dt><dd>GD32 中继刷写中途重启 ESP32,GD32 留下半截镜像。</dd><dt>修改建议</dt><dd>OTA 会话非 IDLE/DONE/FAILED 时禁用按钮并拒绝。</dd></dl></div><div class="issue s1b" id="O-4"><h4><span class="sev s1">严重</span> [O-4] GD32 完成判定可被陈旧 alive 标志误判</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/ota/stages.js:497-499</code></dd><dt>现象</dt><dd>gd32AppAlive &amp;&amp; relaySeq&gt;=relayTotal &amp;&amp; rs&gt;=3 即判完成,TRANSFERRING/FINISHING 阶段旧的 alive 也会计入。</dd><dt>风险</dt><dd>失败的刷写被报告为成功。</dd><dt>修改建议</dt><dd>完成判定要求刷后版本/CRC 与目标一致且 alive 时间戳晚于刷写结束。</dd></dl></div><div class="issue s1b" id="O-5"><h4><span class="sev s1">严重</span> [O-5] ESP32 重启后成功判定不核对新版本</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/common/polling.js:~236; js/ota/upload-esp32.js:435-449, 468-474</code></dd><dt>现象</dt><dd>以 uptime&lt;120 s、观察到离线或 10 s 兜底判完成;verify 路径猜测『已生效』。</dd><dt>风险</dt><dd>回滚或崩溃重启也被报成功。</dd><dt>修改建议</dt><dd>重启后读取版本号/镜像 SHA 与刚上传文件比对。</dd></dl></div><div class="issue s1b" id="O-6"><h4><span class="sev s1">严重</span> [O-6] WiFi/BLE OTA 不通知其它标签,端点可中途切换</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/ota/boot_ecat.js:598; js/app.js:63-71, 403-421</code></dd><dt>现象</dt><dd>跨标签忙标志只由 ECAT 写;电机/CAN 页、LAN 选择器、端点切换不感知 WiFi/BLE OTA;IP 输入框 change 与 getEspIp 可在 OTA 中改端点。</dd><dt>风险</dt><dd>OTA 中其它页面动总线或切到另一台设备。</dd><dt>修改建议</dt><dd>与 O-1/M-7 共用跨帧忙标志;OTA 中锁定端点。</dd></dl></div><div class="issue s1b" id="O-7"><h4><span class="sev s1">严重</span> [O-7] BLE abort 总发会话 0;中继阶段可随意中止</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/ble/session.js:89; js/ble/state.js</code></dd><dt>现象</dt><dd>abortTask 使用从未定义的 state.sessionId(恒为 0/undefined);GD32 SPI 中继阶段 abort 按钮仍可用且无警告。</dd><dt>风险</dt><dd>中止命令无效或在最危险阶段中止。</dd><dt>修改建议</dt><dd>使用真实会话 ID;中继阶段禁用或二次确认。</dd></dl></div><div class="issue s1b" id="O-8"><h4><span class="sev s1">严重</span> [O-8] BLE GD32 身份检查未 await</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/ble/file.js:174, 182-205</code></dd><dt>现象</dt><dd>finalizeAndHash 返回检查 Promise 但 updateFileInfo 丢弃,热重载后 uploadFirmware 可在检查完成前开始。</dd><dt>风险</dt><dd>错误固件仅靠设备侧 0x87 检查兜底。</dd><dt>修改建议</dt><dd>await 身份检查,失败禁用上传。</dd></dl></div><div class="issue s1b" id="O-9"><h4><span class="sev s1">严重</span> [O-9] 前端不校验 ESP32 镜像</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/ota/file-handle.js:725-751</code></dd><dt>现象</dt><dd>不检查 0xE9 魔数、芯片 ID、app 描述符、分区大小,任意拖入文件都会完整上传。</dd><dt>风险</dt><dd>刷错镜像依赖设备侧拒绝;浪费时间或触发异常。</dd><dt>修改建议</dt><dd>上传前解析 esp_image_header/app_desc,比对 chip_id 与分区大小。</dd></dl></div><div class="issue s2b" id="O-10"><h4><span class="sev s2">一般</span> [O-10] BLE ACK/重传机制薄弱</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/ble/gatt.js:249-255; js/ble/transfer.js:45-57, 75-78, 282</code></dd><dt>现象</dt><dd>短 ACK 立即 resolve 且 10 ms 后重发,每窗仅 3 次重试;代次检查为空操作;注释提到 CRC 实际没有。</dd><dt>风险</dt><dd>弱信号下误失败或重复写。</dd><dt>修改建议</dt><dd>退避重传、实现代次校验、加包 CRC。</dd></dl></div><div class="issue s2b" id="O-11"><h4><span class="sev s2">一般</span> [O-11] BLE 载荷固定 480 B,无 MTU 回退</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/ble/constants.js:38, 50; js/ble/state.js:76</code></dd><dt>现象</dt><dd>不按协商 MTU 调整。</dd><dt>风险</dt><dd>部分主机/适配器下写失败。</dd><dt>修改建议</dt><dd>按 MTU-3 计算并回退。</dd></dl></div><div class="issue s2b" id="O-12"><h4><span class="sev s2">一般</span> [O-12] BLE 断开不拒绝结果等待</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/ble/gatt.js:139-150; js/ble/transfer.js:394</code></dd><dt>现象</dt><dd>断开后 UI 最长等待 120 s。</dd><dt>风险</dt><dd>体验差、误判。</dd><dt>修改建议</dt><dd>gattserverdisconnected 时 reject 所有 pending。</dd></dl></div><div class="issue s2b" id="O-13"><h4><span class="sev s2">一般</span> [O-13] GD32 中继触发超时后可能误判失败</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/ota/upload-gd32.js:191-216</code></dd><dt>现象</dt><dd>触发超时且探测也失败时进 FAILED,设备可能仍在中继。</dd><dt>风险</dt><dd>用户重试导致二次刷写。</dd><dt>修改建议</dt><dd>进入『未知』态并持续轮询中继状态。</dd></dl></div><div class="issue s2b" id="O-14"><h4><span class="sev s2">一般</span> [O-14] ECAT 直灌无完整性校验,api() 不查 HTTP 状态</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>js/ota/boot_ecat.js:89-99, 635-661</code></dd><dt>现象</dt><dd>不向 native 传 CRC/SHA,不核对 p.received === u8.length;api() 直接 json()。</dd><dt>风险</dt><dd>传输缺字节不被发现(虽有刷前后 0x100A 比对)。</dd><dt>修改建议</dt><dd>传 SHA 并比对 received;检查 resp.ok。</dd></dl></div><div class="issue s3b" id="O-15"><h4><span class="sev s3">建议</span> [O-15] 大量 window.* 全局、猴补 showOpenFilePicker、内存复制</h4><dl><dt>模块</dt><dd>OTA/刷写</dd><dt>位置</dt><dd><code>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</code></dd><dt>现象</dt><dd>上传体为完整文件再复制一份;失败清理写死按钮文案;存在 FAILED→DONE 转移路径。</dd><dt>风险</dt><dd>可维护性差,状态机可出现非法转移。</dd><dt>修改建议</dt><dd>收敛状态机转移表;模块化状态;流式上传。</dd></dl></div><h3>CAN/公共层</h3><div class="issue s1b" id="C-1"><h4><span class="sev s1">严重</span> [C-1] DBC 中 [0|0] 被当成真实范围</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/can/dbc.js:164-165; js/can/signals.js:299-304</code></dd><dt>现象</dt><dd>DBC 惯例 [0|0] 表示未声明范围,代码却据此校验,任何非 0 物理值编码失败。</dd><dt>风险</dt><dd>大量常见 DBC 信号无法发送。</dd><dt>修改建议</dt><dd>min==max==0 视为无范围。</dd></dl></div><div class="issue s1b" id="C-11"><h4><span class="sev s1">严重</span> [C-11] 独立 CAN 监控页无视所选设备</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>pages/can_monitor.html:55; js/can_monitor.js:60-68; js/can/dbc.js:40-47</code></dd><dt>现象</dt><dd>页面输入框写死 value=&quot;192.168.31.127&quot; 且优先于共享端点,监控标签总连 .127;绕过 IPv4 校验;与 dbc.js 优先级不一致,破坏 README 所述单端点写入者设计。</dd><dt>风险</dt><dd>监控的不是正在控制的设备。</dd><dt>修改建议</dt><dd>移除硬编码值,统一从 deviceLink 读取。</dd></dl></div><div class="issue s1b" id="C-12"><h4><span class="sev s1">严重</span> [C-12] 监控页超时永不显示离线</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/can_monitor.js:199</code></dd><dt>现象</dt><dd>超时产生 AbortError 后直接 return,不标离线;设备无响应时永远显示『已连接』(polling.js:254-257 已修过同类问题)。</dd><dt>风险</dt><dd>误判在线。</dd><dt>修改建议</dt><dd>超时计入失败并标离线。</dd></dl></div><div class="issue s1b" id="C-2"><h4><span class="sev s1">严重</span> [C-2] 扩展多路复用信号被静默丢弃</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/can/dbc.js:153</code></dd><dt>现象</dt><dd>SG_ 正则只接受 M|m\d+,m3M 匹配失败,信号丢失无报错;不支持 SG_MUL_VAL_。</dd><dt>风险</dt><dd>信号缺失且无提示。</dd><dt>修改建议</dt><dd>支持扩展多路复用或至少报警告。</dd></dl></div><div class="issue s1b" id="C-4"><h4><span class="sev s1">严重</span> [C-4] 扩展/标准帧靠 ID 数值猜测,发送载荷无帧类型标志</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/can/dbc.js:146; js/can/send.js:304, 373, 453, 461</code></dd><dt>现象</dt><dd>isExtended = idRaw &gt; 0x7FF,应看 DBC ID bit31;载荷只有 {id,dlc,data},无 ide/fd/brs;≤0x7FF 的扩展帧只能以标准帧发出;VECTOR__INDEPENDENT_SIG_MSG 伪消息未过滤,显示为扩展 ID 0。</dd><dt>风险</dt><dd>帧类型错误,目标节点收不到。</dd><dt>修改建议</dt><dd>按 bit31 判定并在载荷加 ide/fd/brs 字段(需固件配合);过滤伪消息。</dd></dl></div><div class="issue s1b" id="C-5"><h4><span class="sev s1">严重</span> [C-5] DLC 未按 CAN FD 合法长度校验</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/can/send.js:406; js/can/signals.js:233-235</code></dd><dt>现象</dt><dd>0~64 任意值均可(如 10),合法值仅 0~8,12,16,20,24,32,48,64;DBC dlc 未映射。</dd><dt>风险</dt><dd>固件侧行为未定义或截断。</dd><dt>修改建议</dt><dd>限定合法集合,向上取整并提示。</dd></dl></div><div class="issue s2b" id="C-10"><h4><span class="sev s2">一般</span> [C-10] 单条发送无防重入</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/can/send.js:282, 358, 435</code></dd><dt>现象</dt><dd>发送所选消息/手动帧无在途保护;批量发送静默跳过失效键。</dd><dt>风险</dt><dd>重复发帧。</dd><dt>修改建议</dt><dd>加在途标志并提示跳过项。</dd></dl></div><div class="issue s2b" id="C-13"><h4><span class="sev s2">一般</span> [C-13] 对设备的并发轮询过多</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>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</code></dd><dt>现象</dt><dd>/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。</dd><dt>风险</dt><dd>连接被挤占驱逐,业务请求失败。</dd><dt>修改建议</dt><dd>状态统一由 Owner 广播;隐藏页暂停;离线指数退避。</dd></dl></div><div class="issue s2b" id="C-14"><h4><span class="sev s2">一般</span> [C-14] 轮询超时计时器可能丢失</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/common/polling.js:194</code></dd><dt>现象</dt><dd>计时器闭包读取模块级 statusAbortCtrl,pause/resume 可能将其置 null,新请求永不超时。</dd><dt>风险</dt><dd>轮询挂起。</dd><dt>修改建议</dt><dd>闭包捕获本次 controller。</dd></dl></div><div class="issue s2b" id="C-16"><h4><span class="sev s2">一般</span> [C-16] 读取已存 IP 不校验;网关猜测探测</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/common/base-url.js:44-50; js/common/connection.js:761-789</code></dd><dt>现象</dt><dd>getSavedDeviceIp 原样返回;按 .1 猜网关并 no-cors 探测。</dd><dt>风险</dt><dd>脏数据导致请求异常。</dd><dt>修改建议</dt><dd>读时校验 IPv4。</dd></dl></div><div class="issue s2b" id="C-3"><h4><span class="sev s2">一般</span> [C-3] 多行 CM_ 注释丢失</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/can/dbc.js:178, 184</code></dd><dt>现象</dt><dd>正则要求 &quot;; 同行。</dd><dt>风险</dt><dd>注释缺失。</dd><dt>修改建议</dt><dd>按语句(以 ; 结束)解析。</dd></dl></div><div class="issue s2b" id="C-6"><h4><span class="sev s2">一般</span> [C-6] 手动 ID 解析过宽</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/can/dbc.js:58</code></dd><dt>现象</dt><dd>parseInt(&#x27;0x12G&#x27;,16) 静默得 0x12;无 0x1FFFFFFF 上限。</dd><dt>风险</dt><dd>发错 ID。</dd><dt>修改建议</dt><dd>严格正则与范围校验。</dd></dl></div><div class="issue s2b" id="C-7"><h4><span class="sev s2">一般</span> [C-7] 枚举/多路复用/浮点信号编码细节错误</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/can/signals.js:45-51, 76, 294-296, 406</code></dd><dt>现象</dt><dd>枚举默认 &#x27;0&#x27; 即便 0 不是合法键(下拉显示首项但编码失败);mux 比较用物理值而非原始值;浮点信号忽略 factor/offset。</dd><dt>风险</dt><dd>编码失败或数值错误。</dd><dt>修改建议</dt><dd>默认取首个合法键;mux 用 raw;浮点应用 factor/offset。</dd></dl></div><div class="issue s2b" id="C-8"><h4><span class="sev s2">一般</span> [C-8] 持久化静默失败且状态只增不减</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/can/persist.js:93-124; js/can.js:229</code></dd><dt>现象</dt><dd>完整 DBC 文本存 localStorage,超配额三次保存都静默失败;已删除 DBC 源的 signalValues/checkedKeys 不清理;添加任一 DBC 会重置所有已编辑信号值。</dd><dt>风险</dt><dd>配置丢失无提示。</dd><dt>修改建议</dt><dd>改用 IndexedDB、失败提示、按源清理。</dd></dl></div><div class="issue s2b" id="C-9"><h4><span class="sev s2">一般</span> [C-9] toast 模块被加载成两个实例</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>js/can/send.js:17; js/can.js:19-20, 47</code></dd><dt>现象</dt><dd>send.js 以 ?v= 查询串导入 toast.js,违反 can.js 自身规则,产生两个模块实例,去重与隐藏计时不共享。</dd><dt>风险</dt><dd>重复提示。</dd><dt>修改建议</dt><dd>JS 间 import 去掉 ?v=。</dd></dl></div><div class="issue s3b" id="C-15"><h4><span class="sev s3">建议</span> [C-15] 大量隐式全局与重复代码</h4><dl><dt>模块</dt><dd>CAN/公共层</dd><dt>位置</dt><dd><code>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</code></dd><dt>现象</dt><dd>20+ window.* 全局耦合 ES 模块;复制/连接/格式化代码重复。</dd><dt>风险</dt><dd>维护困难。</dd><dt>修改建议</dt><dd>收敛为显式导入的共享模块。</dd></dl></div><h3>仓库卫生/文档</h3><div class="issue s1b" id="H-1"><h4><span class="sev s1">严重</span> [H-1] AI 工具目录被提交</h4><dl><dt>模块</dt><dd>仓库卫生/文档</dd><dt>位置</dt><dd><code>.qoder/repowiki/(约 75 个文件); .zcode/plans/plan-sess_*.md; .gitignore</code></dd><dt>现象</dt><dd>.qoder 自动生成的知识库与 .zcode 个人调试计划(含 G:\ 路径与固件计划)入库,且未列入 .gitignore。</dd><dt>风险</dt><dd>仓库噪声、信息泄露、与真实代码不同步误导读者。</dd><dt>修改建议</dt><dd>移出仓库(需用户决定,本次评审不删除任何文件)并加入 .gitignore。</dd></dl></div><div class="issue s1b" id="H-5"><h4><span class="sev s1">严重</span> [H-5] 默认 ECAT 周期 0.25 ms 与既有报告建议冲突且缺复测依据</h4><dl><dt>模块</dt><dd>仓库卫生/文档</dd><dt>位置</dt><dd><code>js/common/ecat-bus.js:28; js/motor/transport-ecat.js:428-431</code></dd><dt>现象</dt><dd>ECAT_RATE_TRADEOFF 建议 0.5 ms(称 0.25 ms 为功能损坏),REPORT_ecat_skip_opt 建议生产 2 ms;代码默认 0.25 ms,仓库无带 CAN 工况的 0.25 ms 复测证据(BEAT_TAIL 自列为待办)。</dd><dt>风险</dt><dd>高负载下丢拍/跳拍、控制抖动。</dd><dt>修改建议</dt><dd>补带 CAN 工况复测并在报告中记录;若无证据,默认回到 0.5 ms。</dd></dl></div><div class="issue s2b" id="H-2"><h4><span class="sev s2">一般</span> [H-2] 受版本控制文件含个人绝对路径</h4><dl><dt>模块</dt><dd>仓库卫生/文档</dd><dt>位置</dt><dd><code>scripts/build_mem.bat:21; README.md:3, 449; docs/compose/spec/host-code-quality.md:67</code></dd><dt>现象</dt><dd>G:\Robot_v2\... 等路径。</dd><dt>风险</dt><dd>换机即失效。</dd><dt>修改建议</dt><dd>改为相对路径或环境变量。</dd></dl></div><div class="issue s2b" id="H-3"><h4><span class="sev s2">一般</span> [H-3] README 脚本清单与 scripts/ 不一致;.gitignore 规则重叠</h4><dl><dt>模块</dt><dd>仓库卫生/文档</dd><dt>位置</dt><dd><code>README.md; scripts/check_fe_guard_mutation.py; scripts/ui_diag_motor.mjs; .gitignore; .gitattributes</code></dd><dt>现象</dt><dd>check_fe_guard_mutation.py、ui_diag_motor.mjs 未登记;test_lan_probe.py、check_lan_discovery.py 不在『正式清单』表;*.jsonl 重复、scripts/_* 与 scripts/_*_out.txt 重叠;.gitattributes 为 * -text。</dd><dt>风险</dt><dd>清单失真。</dd><dt>修改建议</dt><dd>同步清单;整理忽略规则;按类型设 eol。</dd></dl></div><div class="issue s2b" id="H-4"><h4><span class="sev s2">一般</span> [H-4] 注释/文档与代码不符(默认周期等)</h4><dl><dt>模块</dt><dd>仓库卫生/文档</dd><dt>位置</dt><dd><code>js/motor/transport-ecat.js:37, 166, 416-417, 607; README.md:190</code></dd><dt>现象</dt><dd>注释写 supportsOta=false,实际 :607 为 true;注释与 README 写『默认 4 ms 由后端决定/删键跟随后端』,实际回落 0.25 ms。</dd><dt>风险</dt><dd>误导调参与维护。</dd><dt>修改建议</dt><dd>统一以 ecat-bus.js 常量为准并修正文档。</dd></dl></div><div class="issue s2b" id="H-6"><h4><span class="sev s2">一般</span> [H-6] GD32 WiFi 中继刷写缺『刷写≠生效』版本比对,守卫脚本已删除</h4><dl><dt>模块</dt><dd>仓库卫生/文档</dd><dt>位置</dt><dd><code>js/ota/upload-gd32.js; js/ota/stages.js:497-499</code></dd><dt>现象</dt><dd>ECAT 直灌有 0x100A 刷前后比对,WiFi 中继路径没有;原 flash_gd32.py 生效守卫随 beeae8a 删除。</dd><dt>风险</dt><dd>刷写成功但未生效不被发现。</dd><dt>修改建议</dt><dd>中继完成后读取 APP 版本比对。</dd></dl></div><div class="issue s2b" id="H-8"><h4><span class="sev s2">一般</span> [H-8] ecat_integral_report 结论与其附带脚本输出自相矛盾</h4><dl><dt>模块</dt><dd>仓库卫生/文档</dd><dt>位置</dt><dd><code>docs/reports/ecat_integral_report.html</code></dd><dt>现象</dt><dd>正文称积分差大头在『停车段』、N3 停车更拖;附带脚本 JSON 却为 main_source_of_integral_error=起动段(phase on 3.54 &gt; stop 1.09),who_lags_at_stop=N2,plain 中又写『N3 停车更拖』。</dd><dt>风险</dt><dd>依据该报告调参可能方向错误。</dd><dt>修改建议</dt><dd>重跑脚本核对口径(均值 vs 绝对值均值)并修正结论。</dd></dl></div><div class="issue s3b" id="H-7"><h4><span class="sev s3">建议</span> [H-7] 报告中的资源与脚本链接随归档失效</h4><dl><dt>模块</dt><dd>仓库卫生/文档</dd><dt>位置</dt><dd><code>docs/reports/ECAT_RATE_TRADEOFF.md; docs/reports/ecat_integral_report.html; docs/reports/REPORT_ecat_skip_opt_20260917.md</code></dd><dt>现象</dt><dd>lab/*.png、lab/*.csv、ecat_calib.json、sweep/plot/build 脚本与 scripts/skip_*.py 在 beeae8a 删除,报告不可复现。</dd><dt>风险</dt><dd>无法复核结论。</dd><dt>修改建议</dt><dd>把关键数据与脚本归档到 docs/reports/assets/ 或标注出处 commit。</dd></dl></div><h2>4. 既有评审 / 报告结论跟踪</h2><div class="card"><p>对仓库内全部既有报告与规格逐条核对(原文件保留不动),每份另出修订附录:</p><table><tr><th>原文件</th><th>修订附录</th><th>已修复</th><th>部分</th><th>仍存在/存疑</th><th>已过时</th><th>无法核实</th></tr><tr><td><code>docs/reports/ECAT_BEAT_TAIL_REPORT.html</code></td><td><a href="../reports/revisions/ECAT_BEAT_TAIL_REPORT_REV_2026-10-19.html">ECAT_BEAT_TAIL_REPORT_REV_2026-10-19.html</a></td><td>2</td><td>0</td><td>1</td><td>2</td><td>1</td></tr><tr><td><code>docs/reports/ECAT_RATE_TRADEOFF.html + .md</code></td><td><a href="../reports/revisions/ECAT_RATE_TRADEOFF_REV_2026-10-19.html">ECAT_RATE_TRADEOFF_REV_2026-10-19.html</a></td><td>0</td><td>0</td><td>3</td><td>1</td><td>1</td></tr><tr><td><code>docs/reports/REPORT_ecat_skip_opt_20260917.md</code></td><td><a href="../reports/revisions/REPORT_ecat_skip_opt_20260917_REV_2026-10-19.html">REPORT_ecat_skip_opt_20260917_REV_2026-10-19.html</a></td><td>0</td><td>1</td><td>1</td><td>2</td><td>1</td></tr><tr><td><code>docs/reports/GD32_OTA_FLOW_COMPARE.html</code></td><td><a href="../reports/revisions/GD32_OTA_FLOW_COMPARE_REV_2026-10-19.html">GD32_OTA_FLOW_COMPARE_REV_2026-10-19.html</a></td><td>1</td><td>1</td><td>1</td><td>2</td><td>3</td></tr><tr><td><code>docs/reports/ecat_integral_report.html</code></td><td><a href="../reports/revisions/ecat_integral_report_REV_2026-10-19.html">ecat_integral_report_REV_2026-10-19.html</a></td><td>0</td><td>0</td><td>2</td><td>1</td><td>1</td></tr><tr><td><code>docs/compose/spec/device-link.md</code></td><td><a href="../compose/spec/revisions/device-link_REV_2026-10-19.html">device-link_REV_2026-10-19.html</a></td><td>2</td><td>2</td><td>1</td><td>0</td><td>0</td></tr><tr><td><code>docs/compose/spec/tab-keepalive.md</code></td><td><a href="../compose/spec/revisions/tab-keepalive_REV_2026-10-19.html">tab-keepalive_REV_2026-10-19.html</a></td><td>2</td><td>0</td><td>1</td><td>0</td><td>0</td></tr><tr><td><code>docs/compose/spec/ecat-rx-stuck.md</code></td><td><a href="../compose/spec/revisions/ecat-rx-stuck_REV_2026-10-19.html">ecat-rx-stuck_REV_2026-10-19.html</a></td><td>0</td><td>0</td><td>1</td><td>0</td><td>2</td></tr><tr><td><code>docs/compose/spec/pdo-fast-send.md</code></td><td><a href="../compose/spec/revisions/pdo-fast-send_REV_2026-10-19.html">pdo-fast-send_REV_2026-10-19.html</a></td><td>2</td><td>1</td><td>1</td><td>0</td><td>0</td></tr><tr><td><code>docs/compose/spec/host-code-quality.md</code></td><td><a href="../compose/spec/revisions/host-code-quality_REV_2026-10-19.html">host-code-quality_REV_2026-10-19.html</a></td><td>2</td><td>5</td><td>2</td><td>0</td><td>0</td></tr><tr><td><code>.zcode/plans/plan-sess_*.md</code></td><td>(无附录,仅本表跟踪)</td><td>2</td><td>0</td><td>0</td><td>0</td><td>2</td></tr></table></div><table><tr><th>来源</th><th>编号</th><th>原结论 / 建议</th><th>当前状态</th><th>证据(文件:行号)</th><th>备注</th></tr><tr><td>ECAT_BEAT_TAIL_REPORT</td><td>BT-1</td><td>前端每 PDO 电机槽数与扫描拍数按 dense 布局改(MOT_SLOTS_PER_PDO=9、MOTOR_SCAN_CYCLES=1)</td><td class="st st-ok">已修复</td><td><code>js/motor/transport-ecat.js:57, 88</code></td><td></td></tr><tr><td>ECAT_BEAT_TAIL_REPORT</td><td>BT-2</td><td>测速页跳拍卡改读 cyc_us.late/lost</td><td class="st st-ok">已修复</td><td><code>lab/spi_test.html:506, 1522-1534, 2154-2167</code></td><td></td></tr><tr><td>ECAT_BEAT_TAIL_REPORT</td><td>BT-3</td><td>刷写≠生效:flash_gd32.py 增加生效守卫</td><td class="st st-old">已过时</td><td><code>—(beeae8a 随 scripts 收敛删除)</code></td><td>网页 WiFi 中继路径无替代版本比对,见新问题 H-6;ECAT 直灌有 0x100A 比对(boot_ecat.js:629-632, 792-794)</td></tr><tr><td>ECAT_BEAT_TAIL_REPORT</td><td>BT-4</td><td>345µs 归因:win 布局固有成本 vs 主站 97fab23 回归,需一次刷板钉死实验</td><td class="st st-na">无法在本仓库核实</td><td><code>—</code></td><td>涉及固件布局与 ecat_native.c,不在本仓</td></tr><tr><td>ECAT_BEAT_TAIL_REPORT</td><td>BT-5</td><td>0.25ms 档实测列为待办</td><td class="st st-open">仍存在</td><td><code>js/common/ecat-bus.js:28</code></td><td>默认已是 0.25ms 却无复测记录,见 H-5</td></tr><tr><td>ECAT_BEAT_TAIL_REPORT</td><td>BT-6</td><td>报告引用 lab/ecat_rate_sweep.csv 等证据数据</td><td class="st st-old">已过时</td><td><code>—</code></td><td>数据随归档删除,报告不可复现,见 H-7</td></tr><tr><td>ECAT_RATE_TRADEOFF</td><td>RT-1</td><td>建议周期停在 0.5ms,0.25ms 为功能损坏</td><td class="st st-open">仍存在</td><td><code>js/common/ecat-bus.js:28; js/motor/transport-ecat.js:428-431</code></td><td>代码默认 0.25ms,建议未采纳且无复测依据(H-5)</td></tr><tr><td>ECAT_RATE_TRADEOFF</td><td>RT-2</td><td>周期可被 localStorage 覆盖(原 :227-228)</td><td class="st st-open">仍存在</td><td><code>js/motor/transport-ecat.js:167, 428-430</code></td><td>设计如此,但应在 UI 明示并限定合法档位</td></tr><tr><td>ECAT_RATE_TRADEOFF</td><td>RT-3</td><td>drop 口径、fillRxWindow 计时待办</td><td class="st st-na">无法在本仓库核实</td><td><code>—</code></td><td>属固件/native 侧</td></tr><tr><td>ECAT_RATE_TRADEOFF</td><td>RT-4</td><td>sweep_ecat_rate.py / calibrate_ecat_metrics.py / plot_* / build_ecat_report_html.py 与图表数据</td><td class="st st-old">已过时</td><td><code>—</code></td><td>beeae8a 删除,.md 中图片链接失效(H-7)</td></tr><tr><td>ECAT_RATE_TRADEOFF</td><td>RT-5</td><td>注释/README 称默认 4ms 由后端决定</td><td class="st st-open">仍存在</td><td><code>js/motor/transport-ecat.js:166, 416-417; README.md:190</code></td><td>文档与代码不符(H-4)</td></tr><tr><td>REPORT_ecat_skip_opt_20260917</td><td>SK-1</td><td>ecat_native 提权为 RealTime</td><td class="st st-part">部分修复</td><td><code>ecat_proc.py:97-120, 273-276</code></td><td>仅新拉起进程提权;复用实例分支 :212-215 不提权;且 REALTIME 有饿死系统线程风险(P-7)</td></tr><tr><td>REPORT_ecat_skip_opt_20260917</td><td>SK-2</td><td>生产建议 2ms</td><td class="st st-open">仍存在</td><td><code>js/common/ecat-bus.js:28</code></td><td>默认 0.25ms,与建议冲突(H-5)</td></tr><tr><td>REPORT_ecat_skip_opt_20260917</td><td>SK-3</td><td>第四节回滚命令依赖 scripts/skip_*.py</td><td class="st st-old">已过时</td><td><code>—</code></td><td>脚本已删除,回滚步骤不可直接执行</td></tr><tr><td>REPORT_ecat_skip_opt_20260917</td><td>SK-4</td><td>I210 协议绑定/高级属性调整</td><td class="st st-na">无法在本仓库核实</td><td><code>—</code></td><td>属主机网卡配置</td></tr><tr><td>REPORT_ecat_skip_opt_20260917</td><td>SK-5</td><td>标定/实验脚本(第五节)</td><td class="st st-old">已过时</td><td><code>—</code></td><td>已出库(H-7)</td></tr><tr><td>GD32_OTA_FLOW_COMPARE</td><td>OF-1</td><td>新流程 0 次上板实测</td><td class="st st-old">已过时</td><td><code>js/ota/boot_ecat.js:55-58, 307-322</code></td><td>EtherCAT 直灌已移植入本仓(d73e2fb 起),注释含 2026-10-12/13 实测记录</td></tr><tr><td>GD32_OTA_FLOW_COMPARE</td><td>OF-2</td><td>正在跑 APP 的机器还不能直接刷</td><td class="st st-part">部分修复</td><td><code>js/ota/boot_ecat.js:663-676</code></td><td>主机侧已写 CoE 0x2502:00=1 交接;固件侧无法核实</td></tr><tr><td>GD32_OTA_FLOW_COMPARE</td><td>OF-3</td><td>刷前刷后 0x100A 版本比对</td><td class="st st-ok">已修复</td><td><code>js/ota/boot_ecat.js:629-632, 792-794</code></td><td></td></tr><tr><td>GD32_OTA_FLOW_COMPARE</td><td>OF-4</td><td>4 字节对齐补 0xFF</td><td class="st st-na">无法在本仓库核实</td><td><code>—</code></td><td>发生在 native 侧</td></tr><tr><td>GD32_OTA_FLOW_COMPARE</td><td>OF-5</td><td>固件 OTA.cpp 尾巴丢弃缺陷</td><td class="st st-na">无法在本仓库核实</td><td><code>—</code></td><td>固件仓</td></tr><tr><td>GD32_OTA_FLOW_COMPARE</td><td>OF-6</td><td>老流程无断点续传,每次从偏移 0 重写</td><td class="st st-open">仍存在</td><td><code>js/ota/upload-esp32.js:213-247</code></td><td>整文件 POST 且内存双份(O-15)</td></tr><tr><td>GD32_OTA_FLOW_COMPARE</td><td>OF-7</td><td>报告引用行号 upload-gd32.js:58,194</td><td class="st st-old">已过时(行号漂移)</td><td><code>js/ota/upload-gd32.js:67-68; js/ota/file-handle.js:458, 563</code></td><td>file-handle 引用仍有效</td></tr><tr><td>GD32_OTA_FLOW_COMPARE</td><td>OF-8</td><td>两条路互为备份、复用同一 flash 写入者</td><td class="st st-na">无法在本仓库核实</td><td><code>—</code></td><td>前端侧两条路缺统一互斥,见新问题 O-1(致命)</td></tr><tr><td>ecat_integral_report</td><td>IN-1</td><td>数据源 lab/ecat_passive_latest.csv</td><td class="st st-old">已过时</td><td><code>—</code></td><td>CSV 不在仓库,报告不可复现(H-7)</td></tr><tr><td>ecat_integral_report</td><td>IN-2</td><td>结论:积分差大头在停车段</td><td class="st st-open">存疑</td><td><code>docs/reports/ecat_integral_report.html(附带脚本输出)</code></td><td>附带 JSON 的 main_source_of_integral_error=起动段、who_lags_at_stop=N2,与正文『N3 停车更拖』相矛盾(H-8)</td></tr><tr><td>ecat_integral_report</td><td>IN-3</td><td>建议④ 序列 hold 不短于两台都停稳时间</td><td class="st st-open">仍存在</td><td><code>js/motor/seq.js:95</code></td><td>holdMs 范围 0~600000,无『停稳确认』机制;且序列重启存在并发循环(M-4)</td></tr><tr><td>ecat_integral_report</td><td>IN-4</td><td>建议①~③ 统一减速段、对称负载、v2+v3 纠偏</td><td class="st st-na">无法在本仓库核实</td><td><code>—</code></td><td>属固件控制器</td></tr><tr><td>device-link</td><td>DL-1</td><td>外壳 Owner + BroadcastChannel 快照</td><td class="st st-ok">已修复(已交付)</td><td><code>tabs.html:205; js/common/device-link.js:117, 389</code></td><td></td></tr><tr><td>device-link</td><td>DL-2</td><td>端点配置只在 ConnectionHub</td><td class="st st-part">部分修复</td><td><code>pages/can_monitor.html:55; js/can_monitor.js:60-68; js/app.js:63-71</code></td><td>CAN 监控页硬编码 IP 优先于 Hub(C-11);OTA 页 IP 输入可绕过</td></tr><tr><td>device-link</td><td>DL-3</td><td>功能页不再各自判定在线</td><td class="st st-part">部分修复</td><td><code>js/common/polling.js; js/can.js:424; js/can_monitor.js:487</code></td><td>多处仍独立轮询 /system/status(C-13)</td></tr><tr><td>device-link</td><td>DL-4</td><td>外壳启动强制开总线,失败 10s 退避</td><td class="st st-open">仍存在风险</td><td><code>js/common/device-link.js:32, 250-287</code></td><td>无 reason、unknown 视为需启动、OTA 闸读不到 iframe 标志(M-6、M-7)</td></tr><tr><td>device-link</td><td>DL-5</td><td>BLE 经 reportBle 上报</td><td class="st st-ok">已修复(已交付)</td><td><code>js/ble/gatt.js:119, 123, 128</code></td><td></td></tr><tr><td>tab-keepalive</td><td>TK-1</td><td>多 iframe 宿主 + visibility 切换</td><td class="st st-ok">已修复(已交付)</td><td><code>tabs.html:70-81, 100, 113</code></td><td></td></tr><tr><td>tab-keepalive</td><td>TK-2</td><td>F5/⟳ 只 reload .page-frame.active</td><td class="st st-ok">已修复(已交付)</td><td><code>esp32_server.py:126, 150, 166</code></td><td></td></tr><tr><td>tab-keepalive</td><td>TK-3</td><td>不做后台页降频(Out of Scope)</td><td class="st st-open">仍存在风险</td><td><code>tabs.html:76-81; js/common/polling.js:350-352</code></td><td>隐藏帧持续轮询挤占设备 6 个 socket(C-13);电机页发送节拍在后台被节流(M-10)——建议重新评估此决策</td></tr><tr><td>ecat-rx-stuck</td><td>RX-1</td><td>主站 OP 后 seq 沿踢帧 + 活性判据加宽</td><td class="st st-na">无法在本仓库核实</td><td><code>—</code></td><td>ecat_native.c 不在本仓</td></tr><tr><td>ecat-rx-stuck</td><td>RX-2</td><td>从站新鲜度救援先 Process 再 InputMapping</td><td class="st st-na">无法在本仓库核实</td><td><code>—</code></td><td>固件仓</td></tr><tr><td>ecat-rx-stuck</td><td>RX-3</td><td>规格状态仍为 in-progress,但 T1~T3 已勾选</td><td class="st st-open">仍存在</td><td><code>docs/compose/spec/ecat-rx-stuck.md:3</code></td><td>建议改为 delivered 或注明剩余事项</td></tr><tr><td>pdo-fast-send</td><td>FS-1</td><td>Python 桥 ESF1 仅走 TCP,无 HTTP 回落</td><td class="st st-ok">已修复(已交付)</td><td><code>ecat_client.py:231-287</code></td><td></td></tr><tr><td>pdo-fast-send</td><td>FS-2</td><td>前端 sendViaFastOrJson 有桥时只调快发</td><td class="st st-ok">已修复(已交付)</td><td><code>js/motor/transport-ecat.js:282-300</code></td><td></td></tr><tr><td>pdo-fast-send</td><td>FS-3</td><td>失败重探端口后再试 TCP 一次</td><td class="st st-open">仍存在风险</td><td><code>ecat_client.py:247-279</code></td><td>sendall 成功后 recv 失败也会重发,同一帧可能入队两次(P-2,致命)</td></tr><tr><td>pdo-fast-send</td><td>FS-4</td><td>堵死慢发原则</td><td class="st st-part">部分修复</td><td><code>js/motor/transport-ecat.js:259-271</code></td><td>低频路径(motor/send、motor/start)桥失败仍回落 HTTP(M-8)</td></tr><tr><td>host-code-quality</td><td>HQ-R1</td><td>非幂等不自动重发</td><td class="st st-part">部分修复</td><td><code>ecat_client.py:58-62, 290-299, 418-436</code></td><td>HTTP 代理已遵守;ESF1 快发重试违反同一原则(P-2)</td></tr><tr><td>host-code-quality</td><td>HQ-R2</td><td>网卡默认 I210</td><td class="st st-ok">已修复</td><td><code>ecat_proc.py:28</code></td><td></td></tr><tr><td>host-code-quality</td><td>HQ-R3</td><td>跨进程互斥/复用</td><td class="st st-part">部分修复</td><td><code>ecat_proc.py:67-94, 125-140</code></td><td>锁超时/CreateMutexW 失败仍继续执行(P-4)</td></tr><tr><td>host-code-quality</td><td>HQ-R4</td><td>拆 main.js</td><td class="st st-part">部分修复</td><td><code>js/motor/main.js(3254 行)</code></td><td>仍为上帝文件(M-18)</td></tr><tr><td>host-code-quality</td><td>HQ-R5</td><td>版本/参数下沉</td><td class="st st-open">仍未实施</td><td><code>—</code></td><td></td></tr><tr><td>host-code-quality</td><td>HQ-R6</td><td>ECAT bootstrap 单点</td><td class="st st-part">部分修复</td><td><code>js/motor/transport-ecat.js:418-441; js/common/ecat-bus.js:65-75</code></td><td>电机侧仍自算周期并绕过预算(M-6)</td></tr><tr><td>host-code-quality</td><td>HQ-R7</td><td>toast/quiet 去重</td><td class="st st-open">仍未实施</td><td><code>js/can/send.js:17</code></td><td>CAN 侧还出现 toast 双实例(C-9)</td></tr><tr><td>host-code-quality</td><td>HQ-R8~R14</td><td>服务端拆分与卫生</td><td class="st st-ok">已修复(大部分)</td><td><code>ecat_client.py; ecat_proc.py; docs/reports/</code></td><td>但 .qoder/.zcode 入库(H-1)</td></tr><tr><td>host-code-quality</td><td>HQ-R15</td><td>README</td><td class="st st-part">部分修复</td><td><code>README.md:3, 190, 449</code></td><td>个人路径与默认周期描述过时(H-2、H-4)</td></tr><tr><td>.zcode-plan</td><td>ZC-A1</td><td>ecat_native 新增 /api/pm/hold</td><td class="st st-na">无法在本仓库核实</td><td><code>—</code></td><td>native 不在本仓</td></tr><tr><td>.zcode-plan</td><td>ZC-A2</td><td>lan9252_pm.html enterMode 后发 pm/hold</td><td class="st st-ok">已修复</td><td><code>lab/lan9252_pm.html:447</code></td><td></td></tr><tr><td>.zcode-plan</td><td>ZC-A3</td><td>睡眠标记另写带时间戳 localStorage</td><td class="st st-ok">已修复</td><td><code>lab/lan9252_pm.html:289-309</code></td><td></td></tr><tr><td>.zcode-plan</td><td>ZC-B</td><td>GD32 固件 HBI 门控</td><td class="st st-na">无法在本仓库核实</td><td><code>—</code></td><td>固件仓</td></tr></table><h2>5. 优先级修复路线图</h2><div class="card"><h3 style="margin-top:0">P0(立即,1~3 天)· 人身与设备安全</h3><ul><li>M-1 增加急停与 safeStopAll(),所有停止类操作必发失能,绕过 sendInFlight/冷却门</li><li>M-2 buildCmdBytes 统一限幅/拒绝非法值,大幅值二次确认</li><li>M-3 链路丢失判定:瞬时错误也计数并超时判离线,离线自动停发</li><li>M-4 序列运行代次令牌 + 可中断 sleep</li><li>P-1 本地服务校验 Origin/Host + 强制 JSON Content-Type(同时修 P-6/P-13)</li><li>P-2 ESF1 快发 sendall 后不重试</li><li>O-1 建立跨通道唯一刷写互斥(同时修 O-3/O-6/M-7)</li><li>O-2 HEX 解析严格校验(字符、长度、EOF、地址范围、跨度)</li></ul></div><div class="card"><h3 style="margin-top:0">P1(1~2 周)· 功能正确性</h3><ul><li>M-6 总线 start/repair 收敛到 ecat-bus 单入口(带 reason 与预算),完成 host-code-quality R6</li><li>M-8/FS-4 非幂等命令禁止桥→HTTP 回落;JS 桥调用加超时</li><li>P-3/P-4/P-5/P-7 native 进程治理:不强杀刷写中进程、锁失败即中止、桥路径记录循环状态、改 HIGH 优先级</li><li>O-4/O-5/H-6 刷写完成判定以版本/CRC 比对为准</li><li>M-5/M-9/M-10/M-11 陈旧数据标记、网卡切换确认、发送节拍迁入 Worker、核对 queued</li><li>C-1/C-2/C-4/C-5/C-11/C-12 DBC 与 CAN 监控正确性</li><li>H-5 对 0.25ms 默认周期补带 CAN 工况复测,否则回到 0.5ms</li></ul></div><div class="card"><h3 style="margin-top:0">P2(1 个月内)· 健壮性与可维护性</h3><ul><li>M-19 为停机/序列/编码/离线判定/HEX 解析补纯逻辑单测并入验收链</li><li>C-13 轮询收敛到 Owner 广播 + 隐藏页暂停(重新评估 tab-keepalive 不降频决策)</li><li>M-18 main.js 按 UI 门禁分步拆分;C-15 收敛 window.* 全局</li><li>O-7~O-14、P-8~P-15、C-3/C-6~C-10/C-14/C-16 其余一般项</li></ul></div><div class="card"><h3 style="margin-top:0">P3(持续)· 仓库卫生与文档</h3><ul><li>H-1 .qoder/.zcode 移出版本控制并加入 .gitignore(需负责人决定,本次评审未删除任何文件)</li><li>H-2/H-3/H-4 去个人路径、同步脚本清单与默认周期描述</li><li>H-7/H-8 报告数据与脚本归档、修正 ecat_integral_report 自相矛盾结论;ecat-rx-stuck 规格状态更新</li></ul></div><h2>6. 总结评分</h2><table><tr><th>维度</th><th>评分</th><th>说明</th></tr><tr><td>架构与模块划分</td><td>6.5</td><td>外壳/功能页/链路层分层清晰,DeviceLink、多 iframe 保活、双链路 transport 抽象设计合理;但跨帧状态依赖 window.* 全局,main.js 过大。</td></tr><tr><td>实时性与通信协议</td><td>6</td><td>ESF1 快发、幂等白名单、Worker 状态轮询等用心;后台节流、重复轮询、默认 0.25ms 周期缺依据。</td></tr><tr><td>安全与故障保护</td><td>3.5</td><td>无急停、无数值限幅、离线判不出、序列并发、本地服务无来源校验——是本次评审最主要的扣分项。</td></tr><tr><td>OTA/刷写可靠性</td><td>5</td><td>设备侧 SHA/身份检查、ECAT 0x100A 比对等较好;跨通道互斥缺失与 HEX 解析漏洞可致变砖。</td></tr><tr><td>代码质量与可维护性</td><td>5.5</td><td>注释详尽、经验教训有记录;上帝文件、重复逻辑、注释与代码不符。</td></tr><tr><td>测试</td><td>5</td><td>有真机与浏览器验收链、返回码规范;安全关键路径无用例。</td></tr><tr><td>构建与仓库卫生</td><td>5.5</td><td>scripts 已收敛、报告归档;AI 工具目录与个人路径入库,报告证据随归档丢失。</td></tr><tr><td>综合</td><td>5.4</td><td>功能完整度高、工程经验积累扎实,但作为可直接驱动电机的工具,安全兜底明显不足。完成 P0 项后预计可达 7 分。</td></tr></table><p class="note">本评审只新增 docs/ 下文件,未修改任何源码、未删除任何文件。</p></div></body></html>