Files
softbus_daemon/docs/EDGE_UPLINK_PROTOCOL_v1.md
flower_linux fcafa81db1 edge
2026-05-19 16:42:01 +08:00

180 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SoftBus 边缘上行协议 v1EDGE_UPLINK_PROTOCOL_v1
本文档定义 **边缘 `edge_agent` → 核心 `softbus_daemon`** 的 TCP 二进制帧格式,支持:
- **数据面**`DATA_RAWBUS`(载荷语义对齐 [`RawBusMessage`](file:///home/dt/myCode/bus/soft_bus/softbus_daemon/src/core/models/RawBusMessage.h)
- **控制面**:会话、`CTRL_TREE_*`**反向同步** `CTRL_TREE_PROPOSE` / `CTRL_TREE_RESULT`
- **调试面**`DEBUG_TUNNEL`(双向 UTF-8 JSON见 §10须已 HELLO
边缘仓库应维护与本文档 **字节级一致** 的副本(如 `docs/UPLINK_PROTOCOL_v1.md`)。
## 1. 传输
- **传输层**TCP由核心监听`edgeIngress.listenAddress` / `port`),边缘主动连接。
- **字节序**:多字节整数均为 **大端(网络序)**
- **帧大小上限**:由核心配置 `maxFrameBytes`(默认 4MiB强制超限则断开连接。
## 2. 外层帧(所有 `frame_kind` 共用)
| 偏移 | 长度 | 字段 | 说明 |
|------|------|------|------|
| 0 | 4 | magic | 固定 ASCII `SBUP`0x53 0x42 0x55 0x50 |
| 4 | 2 | wire_version | 当前为 **1** |
| 6 | 2 | frame_kind | 见 §3 |
| 8 | 4 | reserved | 填 0 |
| 12 | 4 | payload_len | 后续 payload 字节数 |
| 16 | payload_len | payload | 依 `frame_kind` 解析 |
## 3. frame_kind 枚举u16
| 值 | 名称 | 方向 | 说明 |
|---:|------|------|------|
| 1 | HELLO | 边缘→核心 | UTF-8 JSON见 §4 |
| 2 | ACK | 边缘→核心 | UTF-8 JSON可选应用下行树后确认 |
| 3 | DATA_RAWBUS | 边缘→核心 | 二进制,见 §5 |
| 4 | TREE_FULL | 核心→边缘 | UTF-8 JSON见 §4.1 |
| 5 | TREE_PATCH | 核心→边缘 | UTF-8 JSON预留下行 |
| 6 | TREE_PROPOSE | 边缘→核心 | UTF-8 JSON见 §6 |
| 7 | TREE_RESULT | 核心→边缘 | UTF-8 JSON见 §7 |
| 8 | REPORT | 边缘→核心 | UTF-8 JSON预留 |
| 9 | DEBUG_TUNNEL | **双向** | UTF-8 JSON见 §10**仅**在会话已 HELLO 后允许 |
## 4. HELLO边缘→核心
若核心配置了 `authToken`(非空),则 **首帧必须是 HELLO**,否则核心断开。
JSON 字段:
- `edgeId`string必填与会话绑定后续 `TREE_PROPOSE` 须与此一致。
- `authToken`string若核心配置了 token 则必填且相等)。
- `currentTreeRevision`integer可选边缘当前已应用的 `treeRevision`,供核心决定是否下行全量。
核心响应:可紧跟发送 `TREE_RESULT`**HELLO 成功后**核心会再发送一帧 **`TREE_FULL`**(与 `push_edge_tree` 下行格式相同,见 §4.1)。
## 4.1 TREE_FULL核心→边缘
UTF-8 JSON置于外层 `frame_kind=4` 的 payload 中:
- `kind`:固定字符串 `"TREE_FULL"`(便于与纯数组载荷区分)。
- `edgeId`string与会话绑定边缘应校验与本地 `edgeId` 一致。
- `treeRevision`integer导出子树时刻的**全局** `treeRevision`(与 `TREE_PROPOSE``baseTreeRevision` 语义一致)。
- `tree`array节点对象数组字段形状与核心 `get_device_tree` 中单节点一致(`id`/`name`/`type`/`endpoint`/`driver`/`capabilitiesRef`/`params`/`enabled`/`children`);仅包含 **属于该 edge 作用域** 的节点(规则同 §6.2)。
`tree` 表示核心当前未为该 edge 分配任何节点(仍合法)。
## 5. DATA_RAWBUS 的 payload二进制
**外层 payload** 内为定长头 + 变长 `traceId` + 变长 **bus payload**
| 偏移 | 长度 | 字段 | 类型 |
|------|------|------|------|
| 0 | 2 | trace_len | u16 BEUTF-8 字节数,最大 512 |
| 2 | 4 | device_id | i32 BE |
| 6 | 4 | endpoint_hash | u32 BE |
| 10 | 2 | protocol | u16 BE`ProtocolType` 数值一致 |
| 12 | 2 | reserved0 | u16 BE0 |
| 14 | 4 | logical_address | u32 BE |
| 18 | 8 | routing_key | u64 BE |
| 26 | 8 | protocol_header | 8 字节原始拷贝至 `ProtocolHeader` union |
| 34 | 8 | timestamp_ms | i64 BE |
| 42 | 1 | direction | i8`BusDirection` 枚举数值 |
| 43 | 5 | reserved1 | 填 0 |
| 48 | trace_len | trace_utf8 | UTF-8 |
| 48+trace_len | 剩余 | bus_payload | 写入 `PayloadRef` |
总长度:`48 + trace_len + bus_payload_len` 须等于外层 `payload_len`
### 5.1 SocketCAN / `CAN_RAW`(边缘与核心本机可选)
- **endpoint 字符串**:建议使用 **`can:<linux_ifname>`**,例如 `can:vcan0`。设备树节点 `type``can` 或仅依赖 `endpoint` 前缀推断时,边缘/核心 `EdgeDeviceBus` / `DeviceBusManager` 使用 Qt **SocketCAN** 插件(`socketcan`)打开对应接口;`makeEndpointHash(endpoint)` 与串口一致为 `qHash(endpoint)`
- **bus_payload**:单帧 **数据场** 原始字节08 字节)。**DLC=0 的数据帧合法**;核心 `EdgeTcpIngressService``protocol == CAN_RAW``CAN_OPEN` 时允许空 `bus_payload`,仍携带 `protocol_header` 中的 CAN id 元数据。
- **protocolu16**:与 `ProtocolType` 枚举数值一致CAN 上行应使用 **`CAN_RAW`**(设备树 `params.protocol` 常用字符串 `can_raw`,与 `protocolTypeFromHint` 一致)。
- **protocol_header8 字节)**:与 `ProtocolHeader::can` 一致:`cobId`29 位有效)、`isExtended``isRtr`;编码侧将 `RawBusMessage::header` 按 8 字节写入再参与 `DATA_RAWBUS` 定长头(与现有 Modbus 头填法相同,见实现 `encodeDataRawBusPayload`)。
## 6. TREE_PROPOSE边缘→核心反向同步
UTF-8 JSON。
### 6.1 固定字段
- `edgeId`string须与 HELLO 中一致。
- `baseTreeRevision`integer**全局树 revision**(与核心 `DeviceTreeModel::treeRevision()` 一致);不一致则 **CONFLICT**
- `entries`array必填每项为下列之一。
#### 6.1.1 `patch`(默认,与历史行为一致)
- `op` 省略或 `"patch"`
- `id`string必填须已存在于设备树。
- `patch`object必填字段规则与 D-Bus `patch_device_tree_node``patch` 相同(`name`/`type`/`endpoint`/`driver`/`capabilitiesRef`/`enabled`/`params` 对象合并)。
#### 6.1.2 `upsert`(边缘注册新节点或整节点合并)
- `op``"upsert"`
- `id`string必填目标节点 id。新建时 **必须**`edges/<edgeId>``edges/<edgeId>/...` 前缀(与 §6.2 作用域一致);已存在节点则按作用域校验后 **合并** `node` 字段。
- `node`object必填`get_device_tree` 单节点形状一致:`id`/`name`/`type`/`endpoint`/`driver`/`capabilitiesRef`/`params`/`enabled`/`children``children` 为子节点 id 字符串数组)。新建时若省略 `node.id` 则使用条目 `id`;若 `node.id` 与条目 `id` 均非空则须相等。
- **新建**:核心在 `params` 中未带 `edgeId` 时自动写入会话 `edgeId`;合并后节点须满足 §6.2。
- **`children`**:仅允许引用同一边缘子树内的 id`edges/<edgeId>` 或其前缀下的子 id否则 `FORBIDDEN_SCOPE`
### 6.2 作用域MVP
仅允许修改满足以下 **任一** 条件的节点:
- `node.params.edgeId`string等于会话 `edgeId`;或
- `node.id` 等于 `edges/<edgeId>` 或以 `edges/<edgeId>/` 为前缀。
否则整批 **拒绝**`FORBIDDEN_SCOPE`)。
### 6.3 并发
与 D-Bus `patch_device_tree_node` 共用 **`DeviceTreeModel` 写锁**,保证 revision 单调、无丢失更新。成功应用后,核心还会向该会话 **`edgeId` 的所有已 HELLO 连接** 再发一帧 **`TREE_FULL`**(与 `push_edge_tree` 载荷一致),便于边缘落盘与 revision 对齐。
## 7. TREE_RESULT核心→边缘
UTF-8 JSON
- `ok`bool
- `code`string`OK` | `CONFLICT` | `FORBIDDEN_SCOPE` | `INVALID_JSON` | `NODE_NOT_FOUND` | `VALIDATION_ERROR`
- `message`string可选
- `newTreeRevision`integer成功时应用后的 revision
- `currentTreeRevision`integerCONFLICT 时建议带):核心当前 revision
## 8. treeRevision 语义MVP
- 核心在进程内维护单调递增的 **`treeRevision`**(从 1 起,每次成功变更设备树内存模型后 +1
- 守护进程重启后 revision **重置**MVP边缘应在 HELLO 后若收到 `CONFLICT` 或 revision 不明,依赖核心自动下发的 **`TREE_FULL`**、`push_edge_tree` 运维命令,或重新下发配置。
## 9. 与 orchestrator / D-Bus
核心侧设备树持久化与 `reconcile` 行为与现有 `patch_device_tree_node` 一致;本协议为 **额外入口**,不得绕过校验逻辑。
## 10. DEBUG_TUNNEL`frame_kind = 9`,双向)
用于主机与边缘之间的 **调试指令与监控** 类 JSON不替代 `DATA_RAWBUS` / `TREE_*` 控制面。
### 10.1 会话要求
- **两端**均须在 **HELLO 成功之后** 才能发送本帧;核心对未 HELLO 连接上的 `DEBUG_TUNNEL` 会断开(与 `TREE_FULL` 等下行一致)。
- 载荷为 **UTF-8 JSON 对象**(建议总大小远小于 `maxFrameBytes`)。
### 10.2 推荐信封(可选)
为便于对账与过滤,建议载荷使用统一形状(实现不强制解析,可原样记录日志):
- `v`integer信封版本当前 **1**
- `from`string`"host"``"edge"`
- `tsMs`integer发送端单调时钟毫秒`QDateTime::currentMSecsSinceEpoch()`)。
- `body`object实际调试负载指令、采样快照、订阅键等由产品约定
### 10.3 主机 → 边缘
- 核心通过 `executeCommand` 的 action **`edge_debug_send`** 向指定 `edgeId` 的已 HELLO 会话 **编码一帧** `DEBUG_TUNNEL` 并写入 TCP。
- `edge_debug_send``params`**必填** `edgeId`;调试 JSON 可放在 **`debug` 对象**中,或省略 `debug` 时除 `edgeId` 外的其余 `params` 字段合并为 `body`;核心会再包一层 `v`/`from:"host"`/`tsMs`/`body` 后下行(见 `CommandDispatcher`)。
### 10.4 边缘 → 主机
- 边缘在已连接且 HELLO 后,将 JSON 对象编码为 `DEBUG_TUNNEL` 上行;核心在 `EdgeTcpIngressService` 中解析并 **打日志**(可扩展为内部总线/订阅)。
### 10.5 与边缘 Ops HTTP 的关系
- 边缘进程内 **`OpsUiHttp`** 提供本机 **`GET /debug_host`**(最近从主机收到的调试 JSON 环形缓冲)与 **`POST /debug_edge`**(将 body JSON 经 `DEBUG_TUNNEL` 发往核心),便于在现场用 `curl` 与核心侧 `edge_debug_send`**闭环联调**