8.2 KiB
8.2 KiB
SoftBus 边缘上行协议 v1(EDGE_UPLINK_PROTOCOL_v1)
本文档定义 边缘 edge_agent → 核心 softbus_daemon 的 TCP 二进制帧格式,支持:
- 数据面:
DATA_RAWBUS(载荷语义对齐RawBusMessage) - 控制面:会话、
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-Buspatch_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_ERRORmessage(string,可选)newTreeRevision(integer,成功时):应用后的 revisioncurrentTreeRevision(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 一致;本协议为 额外入口,不得绕过校验逻辑。