Files
DCU_DesktopController/docs/compose/spec/pdo-fast-send.md
T

110 lines
4.7 KiB
Markdown
Raw Normal View History

---
feature: pdo-fast-send
status: delivered
updated: 2026-09-23
branch: main
commits: working-tree (uncommitted; dual-repo main authorized)
---
# PDO 快发(网页按钮 → 原生入队二进制直通)
## Report
**What was built** — 桌面 APP(esp32_app)下,电机页 EtherCAT 链路的**全部** motor
发送**强制**走 ESF1 二进制快发:JS `desktopEcatSendRaw` → GUI 进程
`ecat_send_raw` 打包 → native 持久 TCP 口(health.`fast_send_port`)入 `g_cmd_q`。
**已堵死慢发**:Python 桥删除 HTTP `send_raw` / JSON `motor/send` 回落;前端在
`hasDesktopFastSend()` 为真时失败直接 `ok:false`,不再 `api("motor/send")`。
浏览器无壳标签仍走 HTTP `/ecat/api/motor/send`(脚本/诊断同理)。布局:请求
magic `ESF1` + nframes + 72B/帧;响应 `FSOK` 8 字节。
**Verification** —
- `build_native.bat` PASS;health `fast_send_port` 存在,loop=1 wkc=3
- `scripts/test_esf_pack.py` PASS;`py_compile esp32_server.py` PASS
- `node --check` desktop-bridge / transport-ecat / transport / main PASS
- `scripts/check_esf_latency.py`:TCP FSOK p50≈0.046–0.08ms(HTTP 对照仍在 native 路由上)
- 源码检查:`ecat_send_raw_frames` 无 `motor/send`/`http_send_raw` 回落;
`sendViaFastOrJson` 有桥时不调 `api("motor/send")`
- `test_motor_transport.mjs` 68/68 PASS(Node 无 pywebview → 走 HTTP 契约路径)
- `test_motor_refresh.mjs` 62/62 PASS
- 独立评审 CRITICAL/MAJOR(锁/静态缓冲/端口缓存)此前已修复
**Journey log** —
- 用户期望「纳秒进 PDO」不可达;可压的是控制面,入队后仍是 250µs 一拍 PDO
- pywebview JS→Python hop 仍在,TCP 快发已把 native 侧压到 ~50–80µs
- 沙箱拦 `git worktree add` → 用户明确授权双仓 main
- 持久 TCP 必须 send+recv 整段持锁,否则 FSOK 错配
- **2026-09-23:用户要求堵死慢发**——桌面强制 ESF1、桥内删除 HTTP/JSON 回落;
浏览器/脚本仍保留 native HTTP 路由
## [S1] Problem
电机控制台在 EtherCAT 链路上点发送时,信号要走很长控制面才进 `ecat_native`
的 `g_cmd_q`:浏览器 fetch→代理→JSON,桌面 APP 再叠 pywebview 桥。用户要
「一点按钮极短 CPU 周期内进 PDO」。纳秒不可达;native 入队后到总线由 PDO
周期决定(250µs 档),可压缩的是控制面。HTTP 路径还存在 **8–12ms 调度尾部**,
必须防止桌面壳误走慢路径。
## [S2] Design
### 目标行为(2026-09-23 修订:堵死慢发)
1. 桌面 APP + 桥可用:ECAT 全部 motor 发送 **只走** ESF1 TCP;失败 `ok:false`,
**无** HTTP/JSON 回落。
2. Python `ecat_send_raw_frames`:**仅** TCP ESF1(失败可重探端口后再试 TCP 一次)。
3. 浏览器标签(无 pywebview):仍走 `/ecat/api/motor/send`。
4. native `/api/motor/send` 与 `/api/motor/send_raw` **保留**(浏览器/诊断脚本)。
5. UI 不新增专用开关;`sendHint` / `desktopBridgeLabel` 反映通道形态。
6. 不承诺纳秒。
### 工作区(用户授权覆盖)
- ESP32 与 tools/ecat 均在 **main** 上改(2026-09-23 授权;沙箱拦 worktree add)。
- native:`tools/ecat/ecat_native.c` + `build_native.bat`。
### 二进制帧布局(小端)
**请求**:
```
u32 magic = 0x31465345 /* ESF1 */
u8 nframes /* 1..64 */
u8 flags
u16 reserved
repeat nframes: /* 72B each */
u8 ch, u8 dlc, u16 pad, u32 id, u8 data[64]
```
**响应**:`u32 FSOK(0x4B4F5346) | u8 ok | u8 queued | u16 backlog`
### Native
- `127.0.0.1` `bind(0)` 快发口;health 增加 `fast_send_port`。
- 持久连接读 ESF1 → `parse_esf` → 共用 `enqueue_frames` → FSOK。
- HTTP `/api/motor/send_raw` 与 JSON `/api/motor/send` 仍服务浏览器/脚本。
- `send_raw` 解析缓冲为栈上(HTTP 多线程)。
### GUI 桥(esp32_server.py)
- `pack_esf` / `unpack_fsok`;`_GuiApi.ecat_send_raw`。
- **仅**持久 TCP;connect+sendall+recv 整段持锁。
- 失败:关 socket + `g_fast_send_port=0` → 重探后再试 TCP 一次 → 仍败则 ok:false。
- **无** HTTP/JSON 回落代码。
### 前端
- `desktop-bridge.js`:`desktopEcatSendRaw` / `hasDesktopFastSend`。
- `transport-ecat.js`:`sendViaFastOrJson` 有桥时只调快发。
- 缓存版本 `?v=20260923i`。
### Out of Scope
- 纳秒;共享内存;预装载+位触发;浏览器强制禁 HTTP;删除 native JSON 路由;
改 PDO 周期;WiFi 链路。
## Tasks
- [x] T1: native 快发口 + `/api/motor/send_raw` + health 字段 (covers: S2)
- [x] T2: Python 打包 + `ecat_send_raw` TCP-only(无回落) (covers: S2; depends: T1)
- [x] T3: desktop-bridge + transport-ecat 有桥强制快发 (covers: S2; depends: T2)
- [x] T4: 延迟脚本 + 文档 + 堵死慢发修订 (covers: S2; depends: T1)