✨ feat(compat): 补齐飞拍执行等待与 FANUC 状态驱动链路
- 为 ExecuteFlyShotTraj 补齐 wait 语义,并让 move_to_start 先完成临时 PTP 运动后再启动正式飞拍轨迹 - 将 J519 命令发送改为由机器人 UDP status sequence 驱动, 避免在未收到状态包时主动发周期命令 - 将 10010 状态通道关节字段统一按 JointRadians 命名, 同步更新运行时读取逻辑与协议测试 - 新增 FANUC 10010 状态帧、流运动手册和 Python client 逆向文档,并更新 README 与兼容需求说明 - 补充兼容层编排测试与 HTTP 集成测试,覆盖 wait 和 move_to_start 串行化行为
This commit is contained in:
165
docs/fanuc-10010-state-frame.md
Normal file
165
docs/fanuc-10010-state-frame.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# FANUC TCP 10010 状态帧字段说明
|
||||
|
||||
本文档整理当前现场真实抓包中 `TCP 10010` 状态通道的已确认字段布局,并明确哪些结论已经由抓包和代码验证,哪些仍然只是工作假设。
|
||||
|
||||
读取时间:2026-05-03
|
||||
|
||||
## 1. 结论范围
|
||||
|
||||
本文基于以下证据整理:
|
||||
|
||||
- `../analysis/UTTC_20260428_packet_validation.md`
|
||||
- `../analysis/J519_stream_motion_analysis.md`
|
||||
- `Rvbust/uttc-20260428/20260428.pcap`
|
||||
- `src/Flyshot.Runtime.Fanuc/Protocol/FanucStateProtocol.cs`
|
||||
- `tests/Flyshot.Core.Tests/FanucProtocolTests.cs`
|
||||
|
||||
当前结论仅覆盖现场已确认的 `R30iB + RVBUSTSM` 这一路状态通道行为,不提前推广为所有 FANUC 机型或所有旧版本协议的通用结论。
|
||||
|
||||
## 2. 通道性质
|
||||
|
||||
真实抓包显示,`TCP 10010` 是控制柜到上位机的单向状态流:
|
||||
|
||||
- 上位机先主动建立 TCP 连接。
|
||||
- 建连后,带应用层 payload 的业务包全部来自 `192.168.10.11:10010 -> 192.168.10.10:41726`。
|
||||
- 上位机在该通道上只回 TCP `ACK`,没有观察到应用层请求体。
|
||||
|
||||
因此当前实现应把 `10010` 当作“持续推送的固定长度状态帧”处理,而不是像 `TCP 10012` 那样按请求/响应语义建模。
|
||||
|
||||
## 3. 整体布局
|
||||
|
||||
当前现场抓包确认,状态帧固定为 `90B`:
|
||||
|
||||
```text
|
||||
doz 3 bytes
|
||||
length u32 = 90
|
||||
msg_id u32
|
||||
pose[6] f32
|
||||
joint_or_ext[9] f32
|
||||
tail[4] u32
|
||||
zod 3 bytes
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- 帧头 magic 固定为 `doz`
|
||||
- 帧尾 magic 固定为 `zod`
|
||||
- 长度字段固定为 `0x5a`,即 `90`
|
||||
- 当前全量抓包中 `msg_id` 恒为 `0`
|
||||
- `tail[4]` 当前全量抓包中恒为 `(2, 0, 0, 1)`
|
||||
|
||||
## 4. 样例帧
|
||||
|
||||
以下样例帧来自 `20260428.pcap` 中首条 `tcp.port == 10010 && tcp.len > 0` 的 payload:
|
||||
|
||||
```text
|
||||
646f7a0000005a000000004388a23243f1ed7f43e9de6bc265031ec2b33cc3c278e0153f8742f53c3f128dbc929529bc7861d63cb0184c3c1ca1a7000000000000000000000000000000020000000000000000000000017a6f64
|
||||
```
|
||||
|
||||
对应解析值:
|
||||
|
||||
- `pose[6]`
|
||||
- `273.26715`
|
||||
- `483.85544`
|
||||
- `467.73764`
|
||||
- `-57.253044`
|
||||
- `-89.618675`
|
||||
- `-62.21883`
|
||||
- `joint_or_ext[9]`
|
||||
- `1.0567309`
|
||||
- `0.011662138`
|
||||
- `-0.01789339`
|
||||
- `-0.015160045`
|
||||
- `0.02149596`
|
||||
- `0.009560025`
|
||||
- `0`
|
||||
- `0`
|
||||
- `0`
|
||||
- `tail[4]`
|
||||
- `2`
|
||||
- `0`
|
||||
- `0`
|
||||
- `1`
|
||||
|
||||
## 5. 正式字段表
|
||||
|
||||
| 偏移 | 长度 | 类型 | 样例值(hex) | 样例值(解析后) | 当前推断含义 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `0x00` | `3` | `char[3]` | `64 6f 7a` | `"doz"` | 固定帧头 magic |
|
||||
| `0x03` | `4` | `u32 be` | `00 00 00 5a` | `90` | 帧总长度 |
|
||||
| `0x07` | `4` | `u32 be` | `00 00 00 00` | `0` | `msg_id`,当前抓包全为 `0` |
|
||||
| `0x0B` | `4` | `f32 be` | `43 88 a2 32` | `273.26715` | `pose[0]`,推断为 TCP `X(mm)` |
|
||||
| `0x0F` | `4` | `f32 be` | `43 f1 ed 7f` | `483.85544` | `pose[1]`,推断为 TCP `Y(mm)` |
|
||||
| `0x13` | `4` | `f32 be` | `43 e9 de 6b` | `467.73764` | `pose[2]`,推断为 TCP `Z(mm)` |
|
||||
| `0x17` | `4` | `f32 be` | `c2 65 03 1e` | `-57.253044` | `pose[3]`,推断为姿态角 `W(deg)` |
|
||||
| `0x1B` | `4` | `f32 be` | `c2 b3 3c c3` | `-89.618675` | `pose[4]`,推断为姿态角 `P(deg)` |
|
||||
| `0x1F` | `4` | `f32 be` | `c2 78 e0 15` | `-62.21883` | `pose[5]`,推断为姿态角 `R(deg)` |
|
||||
| `0x23` | `4` | `f32 be` | `3f 87 42 f5` | `1.0567309` | `joint_or_ext[0]`,推断为 `J1(rad)` |
|
||||
| `0x27` | `4` | `f32 be` | `3c 3f 12 8d` | `0.011662138` | `joint_or_ext[1]`,推断为 `J2(rad)` |
|
||||
| `0x2B` | `4` | `f32 be` | `bc 92 95 29` | `-0.01789339` | `joint_or_ext[2]`,推断为 `J3(rad)` |
|
||||
| `0x2F` | `4` | `f32 be` | `bc 78 61 d6` | `-0.015160045` | `joint_or_ext[3]`,推断为 `J4(rad)` |
|
||||
| `0x33` | `4` | `f32 be` | `3c b0 18 4c` | `0.02149596` | `joint_or_ext[4]`,推断为 `J5(rad)` |
|
||||
| `0x37` | `4` | `f32 be` | `3c 1c a1 a7` | `0.009560025` | `joint_or_ext[5]`,推断为 `J6(rad)` |
|
||||
| `0x3B` | `4` | `f32 be` | `00 00 00 00` | `0` | `joint_or_ext[6]`,扩展轴槽位,当前样本恒 `0` |
|
||||
| `0x3F` | `4` | `f32 be` | `00 00 00 00` | `0` | `joint_or_ext[7]`,扩展轴槽位,当前样本恒 `0` |
|
||||
| `0x43` | `4` | `f32 be` | `00 00 00 00` | `0` | `joint_or_ext[8]`,扩展轴槽位,当前样本恒 `0` |
|
||||
| `0x47` | `4` | `u32 be` | `00 00 00 02` | `2` | `tail[0]`,诊断状态字,物理语义未坐实 |
|
||||
| `0x4B` | `4` | `u32 be` | `00 00 00 00` | `0` | `tail[1]`,诊断状态字,物理语义未坐实 |
|
||||
| `0x4F` | `4` | `u32 be` | `00 00 00 00` | `0` | `tail[2]`,诊断状态字,物理语义未坐实 |
|
||||
| `0x53` | `4` | `u32 be` | `00 00 00 01` | `1` | `tail[3]`,诊断状态字,物理语义未坐实 |
|
||||
| `0x57` | `3` | `char[3]` | `7a 6f 64` | `"zod"` | 固定帧尾 magic |
|
||||
|
||||
## 6. 已确认结论
|
||||
|
||||
### 6.1 已由真实抓包确认
|
||||
|
||||
1. `TCP 10010` 是独立状态流,不是 `TCP 10012` 的请求/响应复用。
|
||||
2. 当前现场状态帧固定为 `90B`,不是早期静态分析里出现过的 `134B`。
|
||||
3. `msg_id` 在 `20260428.pcap` 当前全量样本中恒为 `0`。
|
||||
4. `tail[4]` 在 `20260428.pcap` 当前全量样本中恒为 `(2, 0, 0, 1)`。
|
||||
5. `pose[6]` 的量纲表现符合 `X/Y/Z(mm) + W/P/R(deg)`。
|
||||
6. `joint_or_ext[6..8]` 在当前现场样本中恒为 `0`。
|
||||
|
||||
### 6.2 已由数值范围和交叉对照强支持
|
||||
|
||||
1. `joint_or_ext[0..5]` 更像关节角 `rad`,而不是 `deg`。
|
||||
2. 该判断与 `../analysis/UTTC_20260428_packet_validation.md` 的结论一致。
|
||||
3. 该判断也与 `UDP 60015` 响应包中的关节 `deg` 形成互补关系:二者不能简单视作同单位直接复用。
|
||||
|
||||
## 7. 待确认项
|
||||
|
||||
以下内容当前不要写死为最终协议真义:
|
||||
|
||||
1. `tail[4]` 四个 `u32` 分别代表什么控制器语义。
|
||||
2. `msg_id` 是否在其他控制柜版本、程序状态或异常态下会出现非零值。
|
||||
3. `pose[3..5]` 是否可以严格命名为 FANUC 标准 `W/P/R`,还是只是与其数值表现一致。
|
||||
4. `joint_or_ext[6..8]` 在带外部轴的现场是否仍复用同一布局。
|
||||
|
||||
## 8. 与当前代码实现的对齐情况
|
||||
|
||||
当前仓库里 `Flyshot.Runtime.Fanuc` 已按 `90B` 固定帧解析:
|
||||
|
||||
- `src/Flyshot.Runtime.Fanuc/Protocol/FanucStateProtocol.cs`
|
||||
- `tests/Flyshot.Core.Tests/FanucProtocolTests.cs`
|
||||
|
||||
当前实现已经与抓包对齐的部分:
|
||||
|
||||
1. 固定长度 `90B`
|
||||
2. `doz ... zod` 帧头帧尾校验
|
||||
3. `pose[6] + joint_or_ext[9] + tail[4]` 的字节布局
|
||||
4. `tail[4]` 原样保留到 `ControllerStateSnapshot.StateTailWords`
|
||||
|
||||
当前仍建议后续关注的点:
|
||||
|
||||
1. `FanucStateFrame` 已把该字段从 `JointDegrees` 更正为 `JointRadians`,后续新增代码应继续沿用弧度制命名。
|
||||
2. 如果后续状态页或运行时逻辑需要直接展示该通道关节值,仍需明确标注这是 `10010` 的弧度值,避免和 `UDP 60015` 的 degree 语义混淆。
|
||||
|
||||
## 9. 建议用法
|
||||
|
||||
在当前 replacement 实现里,`TCP 10010` 更适合作为以下用途:
|
||||
|
||||
1. 提供机器人当前笛卡尔位姿和关节反馈快照。
|
||||
2. 提供状态通道是否健康、是否陈旧的连接诊断依据。
|
||||
3. 保留 `tail[4]` 原始状态字,供现场排错或后续继续逆向。
|
||||
|
||||
当前不建议直接用 `tail[4]` 去驱动明确业务判断,除非后续拿到新的现场对照证据。
|
||||
Reference in New Issue
Block a user