Files
soft_bus_edge/README.md
flower_linux 5e5e608dad first commit
2026-05-19 16:38:09 +08:00

11 KiB
Raw Permalink Blame History

softbus_edge_agent

独立边缘进程:连接核心 softbus_daemonedgeIngress TCP 端口,发送 HELLODEBUG_TUNNEL(含周期性边缘心跳)、DATA_RAWBUS(真实总线数据),以及通过极简 本地 HTTPGET /GET /uiPOST /propose 等)触发 TREE_PROPOSE 反向同步。

目录布局(与 softbus_daemonsrc/ 对齐)

路径 说明
src/device_bus/tree/ 与核心同源的 DeviceTreeModel(本地子树、TREE_FULL 落盘加载)
src/device_bus/monitor/ udev 串口发现EdgeDiscoveryCoordinator(防抖后:有核心则 TREE_PROPOSE,无核心则写本地树并 reconcile
src/device_bus/manager/ SerialDeviceManagerCanDeviceManager;串口可选 EdgeStreamPipelinedaemon 同源 PluginHost + framer/parser/mapper
src/device_bus/EdgeDeviceBus.* 边缘总线编排:加载本地树、reconcile 串口与 SocketCAN 实例
src/devices/src/hardware/ 与核心同源的 SerialDeviceSerialQtDriver
src/message_bus/ingress/ EdgeSessionEdgeUplinkWireEdgeTcpUplinkPortRawBusMessage → SBUP 上行)

协议与核心侧文档对齐:

构建

cmake -S . -B build -DCMAKE_PREFIX_PATH=/path/to/Qt/6.x/gcc_64
cmake --build build
# 无参:使用 $HOME/softbus/edge/edge_manifest.json首次启动会自动创建目录并生成默认 manifest 与 local_tree.json
./build/bin/softbus_edge_agent
# 或显式指定 manifest例如仓库内示例示例中 localTreePath 使用 $HOME/ 前缀便于各用户通用):
./build/bin/softbus_edge_agent config/edge_manifest.example.json

依赖 Qt6 Core、Network、SerialPort;若 Qt 安装包含 SerialBus 模块CMake 会自动检测并链接,从而在边缘启用 SocketCAN reconcile(设备树 can:... 节点。未安装时仍可完整构建CAN 节点将被忽略。

配置 edge_manifest.json(可选)

无命令行参数时,默认 manifest 为 $HOME/softbus/edge/edge_manifest.json(启动前会 mkpath 该目录;若缺少 edge_manifest.json / local_tree.json 则写入首次启动模板)。若该文件仍无法打开、或 JSON 非法、或你传入的其它 manifest 路径无效,进程会使用内置默认值合并/回退,并在日志中提示。

仍可通过命令行指定路径覆盖默认文件位置:

./build/bin/softbus_edge_agent /path/to/edge_manifest.json

config/edge_manifest.schema.json 与示例 config/edge_manifest.example.json

要点:

  • coreHost / corePort:与 daemon_config.jsonedgeIngress 一致。核心晚于边缘启动时,进程会自动每约 3 秒重试 TCP,连上后自动发送 HELLOcurrentTreeRevision 为「核心侧已知 revision」与「本地设备树 revision」的较大值),之后 DATA_RAWBUS / TREE_PROPOSE 才会真正发到核心。
  • edgeIdHELLO 与会话绑定;TREE_PROPOSE 必须相同。
  • edgeHeartbeatIntervalMsint默认 50000 关闭):已连接核心 TCP 时,周期性经 DEBUG_TUNNEL 上送 kind: edge_heartbeat JSONtreeRevisionnodeCounttreeFingerprint(设备树 tree 紧凑 JSON 的 qHash)、coreTreeRevisionKnown、时间戳),核心在 EdgeTcpIngressService 中打日志便于对账。未设置时可回退读取旧字段 fakeIntervalMs
  • edgeDiscoveryEnabledbool默认 true):启动 udev 串口发现。已连接核心且收到有效 treeRevision 后,将串口以 op: upsertTREE_PROPOSE 同步到核心;核心不可达或 HELLO 失败时仍运行发现,并把结果写入本地 localTreePath(与核心上线后的双向同步兼容)。设为 false 可仅依赖手工 TREE_PROPOSE 或核心下发树。需 Linux + libudev
  • 真实串口:由设备树节点驱动(type/endpoint 可识别为 Serialparams.online 为 true。节点 params.deviceId 会写入上行 RawBusMessage::deviceId(未设则 -1endpoint / protocol 等字段写在设备树节点 params 中(与核心约定一致),不再在 manifest 顶层使用假数据字段。

协议插件与流式解析(与 daemon Pipeline 对齐)

当 manifest 中 plugins.protocols 非空时,进程会加载与 softbus_daemon 相同 ABI.so(如 libmodbus_rtu.so),并在串口 read 路径上启用:

  1. Framer:优先使用插件内的 IProtocolFramerSession(如 Modbus RTU 粘包);否则退回 PassthroughFramer(每块 read 视为一帧,与未启用插件时行为一致)。
  2. 分帧线程与有界队列:与 daemon EndpointFramingState 对齐——按 endpointHash 各起一条消费线程,串口读回调仅 tryEnqueueSPSC LockFreeQueue(容量见 manifest edgeSerialChunkQueueCapacity,默认 4096edgeUseEndpointFramerThreads 默认 truefalse 时在同线程同步分帧供调试)。队列满时上报 edge stream chunk queue full
  3. Parser + Mapper:与核心 PipelineEngine::stage2Parser 一致mapper 在边缘使用 sniffingEnabled=true 且无 mappingLookup,故无映射规则时仍会产出带 unmapped 标记的 DOM 样本(仅 日志 EdgeStreamPipeline: parsed DOM ...不上送 DOM 到核心)。
  4. 上行:仍只对 完整帧 发送 DATA_RAWBUS(与核心解码路径兼容)。

plugins.protocols 路径:可为绝对路径,或与 manifest 文件同目录的相对路径。请确保边缘运行时能 dlopen 到该文件(通常与核心构建产物 bin/plugins/*.so 一致)。

运维 HTTP

opsListenPort > 0,在 TCP 连接核心之前即启动监听(便于核心未启动时仍能打开本页排查配置)。默认 opsListenAllInterfaces 为 false:仅绑定 127.0.0.1,必须在运行 edge_agent 的本机浏览器打开。若需从局域网其他电脑访问,在 manifest 中设 "opsListenAllInterfaces": true(绑定 0.0.0.0,请自行评估内网安全)。

  • GET /:返回 localTreePath 文件内容(无文件则返回占位 HTML含指向配置页的链接
  • GET /ui阶段 5 简易配置页——从 GET /discovered 同源列表选择已发现串口节点,填写 params.protocol(如 modbus_rtu)、波特率等,提交后由边缘组装 op: upsertTREE_PROPOSE 发往核心(使用当前会话的 baseTreeRevision,须已 HELLO 且收到过 TREE_RESULT)。
  • POST /register_discovered:可由浏览器表单(application/x-www-form-urlencoded)或 JSON 调用;字段含 nodeId(必填,一般为 edges/<edgeId>/discovered/<stableKey>)、devnode(可选,若不在发现列表中需填 /dev/tty…)、protocolbaudRatename。表单提交成功返回简短 HTML 确认页JSON 请求返回 {"accepted":true,"sentNodeId":...}。若尚未拿到有效 treeRevision,返回 503
  • GET /discovered:返回最近 udev 发现事件 JSON{ "items": [ ... ] }),便于无核心时调试;与 EdgeDiscoveryCoordinator 内存环缓冲一致(最多约 64 条)。
  • POST /proposebody 为 完整 CTRL_TREE_PROPOSE JSONedgeIdbaseTreeRevisionentries),将原样经 TCP 发往核心。

示例:

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 规则。)

打不开 /ui 时排查

  1. opsListenPort 是否为 0:为 0 则完全不监听;示例见 config/edge_manifest.example.json(如 9080)。
  2. 是否用错地址:默认只能 http://127.0.0.1:<端口>/ui边缘本机打开;在别的电脑浏览器里填边缘的 127.0.0.1 是无效的,需 opsListenAllInterfaces: true 后用 http://<边缘局域网IP>:<端口>/ui,或 SSH 端口转发。
  3. 看进程日志:启动成功会打印 ops UI http://127.0.0.1:...http://0.0.0.0:...;若出现 OpsUiHttp: listen failed,多为端口被占用。
  4. 核心连不上:自本版本起,只要 opsListenPort > 0,即使核心 TCP/HELLO 失败进程也会继续运行并保留运维 HTTP日志中会提示 continuing with ops UI only);此前版本会在启动 Ops 前退出,表现为「完全打不开」。

本地树文件

TREE_FULL 落盘格式为 { "treeRevision", "edgeId", "tree": [ ... ] }EdgeDeviceBus 会将其中的 tree 当作 device_tree 加载,并对 SerialCANendpoint 形如 can:vcan0type/endpoint 推断为 CAN执行 reconcile:打开设备后读到的数据经 EdgeTcpUplinkPort 编码为 DATA_RAWBUS 发往核心。串口节点可在 params 中设置 serialReadCoalesceMs0500默认 0):读字节先入驱动内缓冲,静音该毫秒数后合并再交给上层;启用流式 pipeline 时主路径为 每 endpoint 分帧线程 + 有界队列,一般保持 0 即可,仅在仍需驱动侧攒包时再调大。可选 serialReadBufferCapBytes 限制缓冲上限。

发现 → 注册流程(简要)

  1. 边缘连接核心并 HELLO;核心下发 TREE_FULL,本地落盘并 reconcile 已有节点。
  2. edgeDiscoveryEnabled 为真时,EdgeDiscoveryCoordinator 扫描/监听 udev 串口 add/remove:若已拿到核心 baseTreeRevision 则发送 TREE_PROPOSE;若无核心(或 HELLO 未成功)则把 op: upsert / patch 应用到本地 DeviceTreeModel 并落盘 local_tree.json,再 reconcile 串口。核心恢复后仍通过 TREE_PROPOSETREE_FULL 与真源对齐。
  3. 核心 applyEdgePropose 成功后再次 TREE_FULL;边缘 reload 树文件后对 Serial/CAN 执行 reconcileraw 仅上行