# softbus_edge_agent 独立边缘进程:连接核心 `softbus_daemon` 的 **edgeIngress** TCP 端口,发送 **HELLO**、**DATA_RAWBUS**,以及通过极简 **本地 HTTP**(`GET /`、`GET /ui`、`POST /propose` 等)触发 **TREE_PROPOSE** 反向同步。 ## 目录布局(与 `softbus_daemon` 的 `src/` 对齐) | 路径 | 说明 | |------|------| | `src/device_bus/tree/` | 与核心同源的 `DeviceTreeModel`(本地子树、`TREE_FULL` 落盘加载) | | `src/device_bus/monitor/` | **udev 串口发现**、`EdgeDiscoveryCoordinator`(防抖后组 `TREE_PROPOSE` / `upsert` 向核心注册) | | `src/device_bus/manager/` | `SerialDeviceManager`、`CanDeviceManager`(无 Registry / Pipeline,仅 `IIngressPort` + 内存池) | | `src/device_bus/EdgeDeviceBus.*` | 边缘总线编排:加载本地树、**reconcile** 串口与 **SocketCAN** 实例 | | `src/devices/`、`src/hardware/` | 与核心同源的 `SerialDevice`、`SerialQtDriver` 等 | | `src/message_bus/ingress/` | `EdgeSession`、`EdgeUplinkWire`、`EdgeTcpUplinkPort`(`RawBusMessage` → SBUP 上行) | 协议与核心侧文档对齐: - 核心权威说明:[softbus_daemon/docs/EDGE_UPLINK_PROTOCOL_v1.md](../softbus_daemon/docs/EDGE_UPLINK_PROTOCOL_v1.md)(若仓库路径不同请自行调整相对链接)。 ## 构建 ```bash cmake -S . -B build -DCMAKE_PREFIX_PATH=/path/to/Qt/6.x/gcc_64 cmake --build build ./build/bin/softbus_edge_agent config/edge_manifest.example.json ``` 依赖 **Qt6 Core、Network、SerialPort**;若 Qt 安装包含 **SerialBus** 模块,CMake 会自动检测并链接,从而在边缘启用 **SocketCAN reconcile**(设备树 `can:...` 节点)。未安装时仍可完整构建,CAN 节点将被忽略。 ## 配置 `edge_manifest.json`(可选) 若默认路径 **`config/edge_manifest.json`** 不存在、或传入的 manifest 路径无法打开、或 JSON 非法,进程会使用**内置默认值**启动(等价于示例 manifest 的字段),并在日志中提示。 仍可通过命令行指定路径覆盖默认文件位置: ```text ./build/bin/softbus_edge_agent /path/to/edge_manifest.json ``` 见 [config/edge_manifest.schema.json](config/edge_manifest.schema.json) 与示例 [config/edge_manifest.example.json](config/edge_manifest.example.json)。 要点: - **`coreHost` / `corePort`**:与 `daemon_config.json` 中 `edgeIngress` 一致。 - **`edgeId`**:HELLO 与会话绑定;`TREE_PROPOSE` 必须相同。 - **`edgeDiscoveryEnabled`**(bool,默认 `true`):在 TCP 连接且 **HELLO 成功** 后启动 udev 发现协调器,将串口以 `op: upsert` 写入核心设备树(需 Linux + `libudev`)。设为 `false` 可仅依赖手工 `TREE_PROPOSE` 或核心下发树。 - **`endpoint`**:用于计算 `endpointHash`(与核心一致:`qHash(endpoint)` 即 Qt 默认字符串哈希)。 - **`fakeDeviceId`**:组装 `routingKey = (endpointHash << 32) | deviceId` 与核心 `makeRoutingKey` 一致。 - **`fakeIntervalMs`**:设为 `0` 可关闭假数据定时器;**真实串口**由核心下发的设备树节点驱动(`type`/`endpoint` 可识别为 Serial,且 `params.online` 为 true)。节点 `params.deviceId` 会写入上行 `RawBusMessage::deviceId`(未设则 `-1`)。 ## 运维 HTTP 若 `opsListenPort > 0`,监听 `127.0.0.1:opsListenPort`: - `GET /`:返回 `localTreePath` 文件内容(无文件则返回占位 HTML,含指向配置页的链接)。 - **`GET /ui`**:**阶段 5** 简易配置页——从 **`GET /discovered`** 同源列表选择已发现串口节点,填写 `params.protocol`(如 `modbus_rtu`)、波特率等,提交后由边缘组装 **`op: upsert`** 的 `TREE_PROPOSE` 发往核心(使用当前会话的 **`baseTreeRevision`**,须已 HELLO 且收到过 `TREE_RESULT`)。 - **`POST /register_discovered`**:可由浏览器表单(`application/x-www-form-urlencoded`)或 **JSON** 调用;字段含 `nodeId`(必填,一般为 `edges//discovered/`)、`devnode`(可选,若不在发现列表中需填 `/dev/tty…`)、`protocol`、`baudRate`、`name`。表单提交成功返回简短 **HTML** 确认页;JSON 请求返回 `{"accepted":true,"sentNodeId":...}`。若尚未拿到有效 `treeRevision`,返回 **503**。 - **`GET /discovered`**:返回最近 udev 发现事件 JSON(`{ "items": [ ... ] }`),便于无核心时调试;与 `EdgeDiscoveryCoordinator` 内存环缓冲一致(最多约 64 条)。 - `POST /propose`:body 为 **完整** `CTRL_TREE_PROPOSE` JSON(含 `edgeId`、`baseTreeRevision`、`entries`),将原样经 TCP 发往核心。 示例: ```bash curl -s http://127.0.0.1:9080/ curl -s http://127.0.0.1:9080/ui curl -s http://127.0.0.1:9080/discovered curl -s -X POST http://127.0.0.1:9080/register_discovered \ -H 'Content-Type: application/json' \ -d '{"nodeId":"edges/edge-dev-1/discovered/12345","protocol":"modbus_rtu","baudRate":115200}' curl -s -X POST http://127.0.0.1:9080/propose \ -H 'Content-Type: application/json' \ -d '{"edgeId":"edge-dev-1","baseTreeRevision":1,"entries":[{"id":"edges/edge-dev-1/gw","patch":{"name":"gw"}}]}' ``` (`entries` 中节点 id 必须满足核心 `DeviceTreeModel::nodeBelongsToEdge` 规则。) ## 本地树文件 `TREE_FULL` 落盘格式为 `{ "treeRevision", "edgeId", "tree": [ ... ] }`。**`EdgeDeviceBus`** 会将其中的 `tree` 当作 `device_tree` 加载,并对 **Serial** 与 **CAN**(`endpoint` 形如 `can:vcan0`,`type`/`endpoint` 推断为 CAN)执行 **reconcile**:打开设备后读到的数据经 `EdgeTcpUplinkPort` 编码为 `DATA_RAWBUS` 发往核心。与核心真源同步仍通过 **TREE_PROPOSE**(含边缘 **`upsert`** 自动注册)、核心侧 D-Bus `patch_device_tree_node` 或运维 **`push_edge_tree`**。 ### 发现 → 注册流程(简要) 1. 边缘连接核心并 **HELLO**;核心下发 **`TREE_FULL`**,本地落盘并 reconcile 已有节点。 2. `edgeDiscoveryEnabled` 为真时,`EdgeDiscoveryCoordinator` 扫描/监听 udev 串口 **add/remove**,在拿到有效 **`baseTreeRevision`**(来自 `TREE_RESULT`)后发送 **`TREE_PROPOSE`**:`op: upsert` 注册 `edges//discovered/`,remove 时 **`patch`** `params.online: false`。 3. 核心 `applyEdgePropose` 成功后再次 **`TREE_FULL`**;边缘 reload 树文件后对 **Serial/CAN** 执行 reconcile,**raw 仅上行**。