docs: 新增 .qoder Repowiki 知识库文档

This commit is contained in:
cbb
2026-09-07 12:19:26 +08:00
parent 43d42ad891
commit ef9472334a
105 changed files with 1313322 additions and 263 deletions
+20 -14
View File
@@ -101,21 +101,27 @@ esp32_app.vbs esp32_web.vbs serve.bat 三个启动入口
| 链路 | 默认 | 下限 | 为什么 |
|------|------|------|--------|
| `wifi` | 250 ms | 50 ms | 每拍是一次无线 HTTP 往返,还要与 GD32 `0xC5` 主动推送周期对齐;再快只会让请求排队积压 |
| `ecat` | 50 ms | **1 ms** | 只是读本机后台 PDO 快照,无总线代价;总线周期另由后台循环维持(默认 4 ms) |
| `ecat` | 50 ms | **6 ms** | 只是读本机后台 PDO 快照,无总线代价;总线周期另由后台循环维持(默认 4 ms)。下限取 6 ms 是因为绘制上限由显示器刷新率封顶(本机 160 Hz),再快的轮询没有消费者 |
两条链路**不能共用一个变量**:ECAT 的快值带到 WiFi 上会让 HTTP 请求排队
(`tickStuck` 常亮黄),WiFi 的 250ms 带到 ECAT 上又白白浪费了本地快照的低延迟。
旧数据(早期只存单个 `refreshRate`)只迁进 `wifi` —— 那个值是为无线调的,
不该套到 ECAT 上。
- **ECAT 的 1ms 语义是「不限速」而不是 1000Hz**(`UNTHROTTLED_MAX_MS = 20`)。
单飞语义下上一拍完成才发下一拍,而每拍要过一次 HTTP(代理 → `ecat_server` → 回来),
浏览器里 1ms 档实测约 **125 Hz**。
- **ECAT 的 6ms 语义是「不限速」而不是 166Hz**(`UNTHROTTLED_MAX_MS = 20`)。
单飞语义下上一拍完成才发下一拍,而每拍要过一次 HTTP(代理 → `ecat_native` → 回来),
实测均值 ≈1.8ms → 6ms 档约 **160 Hz**,正好贴着本机 160Hz 屏的绘制上限。
所以低于 20ms 的档位要**同时**关掉两处显示,否则界面会撒谎:
1. `tickStuck` 卡顿黄标 —— 该档每拍都会撞上"上一拍还在飞",这是设计行为,
再报"连续 N 拍未按时完成"就是误报;
2. 「≈N Hz」理论值 —— 1ms 硬算是 1000Hz。这时只显示 `statusHzText`
1. `tickStuck` 卡顿黄标 —— 该档贴着往返耗时,尾部抖动就会撞上"上一拍还在飞",
这是设计行为,再报"连续 N 拍未按时完成"就是误报;
2. 「≈N Hz」理论值 —— 6ms 硬算是 166Hz。这时只显示 `statusHzText`
按数据到达时间实测出来的频率(其滑动窗口在该档改用固定 2s,
否则 `1ms × 2 × 10 = 20ms` 的旧数据 cutoff 会把刚到的样本全清空)。
否则 `6ms × 2 × 10 = 120ms` 的旧数据 cutoff 会把刚到的样本全清空)。
- **轮询不要快过绘制上限**:1ms 档实测能跑到 **~550 Hz**(`ecat_native` 侧
写字节 ÷ 快照 1.6KB 反推),但画面最多只能显示 160 Hz —— 多出来的 390 次/s
在 renderer / 网络服务 / browser 进程 / Python 代理上各点一次,实测这条链
≈1.0 核(renderer 0.35 + 网络服务 0.26 + browser 0.27 + 代理 0.15),
降到 160 Hz 约省 0.7 核。所以下限抬到 6ms;存量 localStorage 里的 1ms
会在读取时钳到 6ms。
- **代理转发绝不能给 socket 传 timeout**(`_ecat_post`,已踩过)。
给 socket 设 timeout 会让 Python 把它切成非阻塞模式,而 **Windows 上非阻塞
connect 要靠 select 轮询完成,于是撞上约 10ms 的系统调度粒度**。
@@ -131,7 +137,7 @@ esp32_app.vbs esp32_web.vbs serve.bat 三个启动入口
排查时很容易误判成"EtherCAT 就这么快"。现在 `_ecat_post` 手写最小 HTTP/1.0
客户端:`create_connection` 不传 timeout(本机回环要么立刻成功要么立刻
`ECONNREFUSED`,不会挂住),连上之后才 `settimeout` 管收发。
这一改让 1ms 档从 62 Hz 提到 125 Hz。
这一改让当时的 1ms 档从 62 Hz 提到 125 Hz(该档现已按绘制上限抬到 6ms 下限)。
- **别把 EtherCAT 当以太网用:PDO 窗口要自适应**(`ecat_server._cover_bases`,已踩过)。
从站一个 PDO 只带 7 个状态槽,而槽表有 27 个(9 节点 × 3 通道是**容量**,
不是实际接入数)。旧实现每周期把窗口 `+7` 无脑轮扫 4 个窗口,于是
@@ -343,17 +349,17 @@ esp32_app.vbs esp32_web.vbs serve.bat 三个启动入口
其覆盖结论记录在 `tools/ecat/MIGRATION.md`。
- `node scripts/test_ecat_autostart.mjs` —— 自动接入链条(stub 后端,
不依赖板子,19 项覆盖开网卡/幂等/换网卡/各级失败语义)。
- `node scripts/test_motor_refresh.mjs` —— 刷新速度每链路一份(53 项,
- `node scripts/test_motor_refresh.mjs` —— 刷新速度每链路一份(55 项,
纯逻辑、不依赖板子/浏览器:默认值、**按链路下限**、旧单值迁移、
两链路互不污染、不限速谓词边界)。
存量 1ms 钳到 6ms、两链路互不污染、不限速谓词边界)。
- 浏览器实测(需 headless Chrome/Edge 开 `--remote-debugging-port=9333`,
页面服务端口可作第一个参数传入,默认 8080):
`node scripts/browser_check_refresh.mjs [port]` 验 DOM 回填与
跨刷新持久化(22 项);`node scripts/browser_check_tick.mjs [port]` 数真实请求频率,
确认切链路后**实际节拍**跟着变而不只是 state 变了(9 项,实测 20.0/2.0 Hz);
`node scripts/browser_check_1ms.mjs [port]` 验 ECAT 1ms 档(24 项:`min`/`step` 同步、
通过浏览器原生校验、实测 124 Hz、无卡顿黄标、面板 Hz 不谎报、
切 WiFi 时 1ms 被钳到 50)。
`node scripts/browser_check_floor.mjs [port]` 验 ECAT 下限档 6ms(26 项:
`min`/`step` 同步、通过浏览器原生校验、实测 ≈160 Hz、无卡顿黄标、
面板 Hz 不谎报、硬填旧的 1ms 被抬到 6ms、切 WiFi 时 6ms 被钳到 50)。
`node scripts/browser_check_period.mjs [port]` 验内核级周期热调(12 项:
`setPdoPeriod(0.25)` 走 `/motor/period` 不碰 `/motor/start`、状态栏显示
`PDO 250µs`(亚毫秒用 µs)、自动自旋标注、localStorage 持久化、页面重载后