Files
DCU_DesktopController/docs/compose/spec/pdo-fast-send.md
T
cbb 2c6235d920 feat(ecat): PDO 快发 ESF1 桌面强制直通,堵死慢发回落
桌面 APP 下 ECAT motor 发送只走 pywebview ESF1→native TCP;
删除桥内 HTTP/JSON 回落。附延迟测量与 pack 自测脚本与规格文档。
2026-09-21 02:11:49 +08:00

4.7 KiB
Raw Blame History

feature, status, updated, branch, commits
feature status updated branch commits
pdo-fast-send delivered 2026-09-23 main 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

  • T1: native 快发口 + /api/motor/send_raw + health 字段 (covers: S2)
  • T2: Python 打包 + ecat_send_raw TCP-only(无回落) (covers: S2; depends: T1)
  • T3: desktop-bridge + transport-ecat 有桥强制快发 (covers: S2; depends: T2)
  • T4: 延迟脚本 + 文档 + 堵死慢发修订 (covers: S2; depends: T1)