# 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]` 去驱动明确业务判断，除非后续拿到新的现场对照证据。
