41 lines
92 KiB
HTML
41 lines
92 KiB
HTML
<!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)&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 < 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('close'),不确认、不检查 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>显示为 '--' 或 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('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 能读取响应。</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 被 &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('</body>',...,1) 大小写敏感且可能落在脚本字符串里;重复 Cache-Control 头;注释提到的 Last-Modified 未发送。</dd><dt>风险</dt><dd>信息泄露、页面被破坏。</dd><dt>修改建议</dt><dd>静态白名单目录(pages/ js/ css/ lab/ tabs.html);仅对 .html 注入;用正则定位最后一个 </body>。</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>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>路径未校验,'..' 与控制字符原样进入请求行。</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 & 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 && relaySeq>=relayTotal && rs>=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<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="192.168.31.127" 且优先于共享端点,监控标签总连 .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 > 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>正则要求 "; 同行。</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('0x12G',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>枚举默认 '0' 即便 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 > 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>
|