147 lines
8.2 KiB
Markdown
147 lines
8.2 KiB
Markdown
# SoftBus 边缘上行协议 v1(EDGE_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`
|
||
|
||
边缘仓库应维护与本文档 **字节级一致** 的副本(如 `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(预留) |
|
||
|
||
## 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 BE,UTF-8 字节数,最大 512 |
|
||
| 2 | 4 | device_id | i32 BE |
|
||
| 6 | 4 | endpoint_hash | u32 BE |
|
||
| 10 | 2 | protocol | u16 BE,与 `ProtocolType` 数值一致 |
|
||
| 12 | 2 | reserved0 | u16 BE,0 |
|
||
| 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**:单帧 **数据场** 原始字节(0–8 字节)。**DLC=0 的数据帧合法**;核心 `EdgeTcpIngressService` 对 `protocol == CAN_RAW` 或 `CAN_OPEN` 时允许空 `bus_payload`,仍携带 `protocol_header` 中的 CAN id 元数据。
|
||
- **protocol(u16)**:与 `ProtocolType` 枚举数值一致;CAN 上行应使用 **`CAN_RAW`**(设备树 `params.protocol` 常用字符串 `can_raw`,与 `protocolTypeFromHint` 一致)。
|
||
- **protocol_header(8 字节)**:与 `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`(integer,CONFLICT 时建议带):核心当前 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` 一致;本协议为 **额外入口**,不得绕过校验逻辑。
|