Files
softbus_OPC/docs/softbus_edge.md
flower_linux 628ccf1409 phase 2 serial
2026-06-12 18:34:49 +08:00

15 KiB
Raw Blame History

softbus_edge

路径: src/softbus_edge
构建目标: softbus_edgeEXECUTABLE
层级: L3 可执行层

1. 概述

softbus_edge 是边缘代理进程,运行在靠近现场设备的一侧,负责:

  • 物理域采集ingress:按 physical_*.json 轮询 Modbus RTU/TCP 等设备,产出 RawPacket
  • 上行egress/uplink:经 TCP 将 RawPacket、心跳、注册信息发送给 softbus_daemon
  • 下行egress:接收 daemon 下发的 ExecuteCommand写回物理设备Modbus 写寄存器等)

edge 加载逻辑点表、操作 OPC UA、运行 protocol_modbus 插件。协议解析与 OPC 绑定均在 daemon 侧完成。

模式 说明
正常模式 libmodbus + ModbusIngress,读真实硬件
Mock 模式 --mock,无硬件,模拟 Modbus 帧上行
独立模式 daemon 未启动时 edge 仍可运行,后台重连 uplink

2. 与 daemon 的职责边界(双域)

┌─────────────────────────────────────────────────────────────────┐
│  edge物理域                    daemon逻辑域              │
├─────────────────────────────────────────────────────────────────┤
│  config/edge/                      config/daemon/               │
│    edge.json                         daemon_profile.json        │
│    physical_channels.json            logical_points.json        │
│    physical_devices.json             bindings_global.json       │
│    bindings_local.json可选       topic_rule.json             │
│                                                                 │
│  读硬件 → RawPacket                  RawPacket → 插件解析        │
│  写硬件 ← physicalPointId            → bindings → OPC UA 节点   │
└─────────────────────────────────────────────────────────────────┘
配置 edge 是否加载 用途
physical_channels.json 串口 / TCP 通道参数
physical_devices.json 从站、寄存器、物理点 ID
bindings_local.json 可选 下行写命令的本地备用解析(正常联调几乎不用)
logical_points.json daemon 专用
bindings_global.json daemon 专用(上行绑定 + 下行路由)

edge 上行时不做 binding 初筛:所有已配置物理点都会采集并上报;是否绑定 OPC 由 daemon 的 bindings_global.json 决定(未绑定点进入 edge/unbound/raw/...)。


3. 配置加载流程

3.1 入口文件

默认配置:config/edge/edge.json。子路径相对 edge.json 所在目录解析。

{
  "edge": {
    "edgeId": "edge01",
    "deviceId": "EdgeDevice_01",
    "daemonHost": "127.0.0.1",
    "daemonPort": 9000,
    "heartbeatPeriodMs": 1000,
    "uplinkReconnectPeriodMs": 5000,
    "physicalChannels": "physical_channels.json",
    "physicalDevices": "physical_devices.json",
    "bindingsLocal": "bindings_local.json"
  }
}
字段 说明
edgeId 边缘实例 IDuplink 注册与 daemon 路由用
deviceId 逻辑设备名ACK 等场景)
daemonHost / daemonPort 核心 uplink 地址
heartbeatPeriodMs 心跳周期(仅 uplink 已连接时发送)
uplinkReconnectPeriodMs daemon 不可达时的重连间隔
physicalChannels / physicalDevices 物理层配置
bindingsLocal 可选;空则跳过
bindingsPatch 可选;热加载绑定补丁

3.2 启动时加载顺序(EdgeRuntime::init

1. EdgeConfig.loadFromFile(--config)
2. PhysicalRegistry.loadChannels(physical_channels.json)
3. PhysicalRegistry.loadDevices(physical_devices.json, edgeId)
      → 物理点 ID 自动加前缀edge01/phy_modbus_s1_r40001
4. [可选] BindingRegistry.loadLocal(bindings_local.json)
5. UplinkClient + CommandExecutor 初始化
6. DeviceFactory.createIngress → ModbusIngress 或 MockModbusIngress
7. ModbusChannelManager.openChannel逐通道 libmodbus 连接)
8. uplink.connect();成功则 sendEdgeRegister(edgeId)
      → 失败仅 WARNedge 独立运行并后台重连

3.3 物理通道(physical_channels.json

{
  "channels": [
    {
      "channelId": "serial_0",
      "protocol": "modbus",
      "transport": "rtu",
      "port": "/dev/ttyS3",
      "baudRate": 9600,
      "parity": "N",
      "dataBits": 8,
      "stopBits": 1,
      "pollIntervalMs": 500
    }
  ]
}
字段 说明
transport rtu(串口)或 tcpModbus TCPhost + tcpPort
pollIntervalMs 该通道下所有点的轮询周期(当前整通道统一)

3.4 物理设备与点(physical_devices.json

{
  "devices": [
    {
      "physicalDeviceId": "phy_modbus_slave_1",
      "channelId": "serial_0",
      "protocol": "modbus",
      "slaveId": 1,
      "points": [
        {
          "physicalPointId": "phy_modbus_s1_r40001",
          "register": 40001,
          "function": 3,
          "scale": 0.1,
          "signalType": "AI",
          "writable": false
        }
      ]
    }
  ]
}

运行时 physicalPointId 会变为 {edgeId}/{physicalPointId},作为 RawPacket.sourceId 上报。

3.5 热加载

信号 行为
SIGHUP reloadPhysical():重载 channels + devices重建 Modbus 连接
SIGHUP reloadBindingsPatch():若配置了 bindingsPatch 则增量应用
文件变更 ConfigWatcher 监视 physical_channelsphysical_devicesbindingsPatch

4. 组件架构

main.cpp
  └── EdgeRuntime
        ├── PhysicalRegistry          ← physical_*.json
        ├── BindingRegistry (local)   ← bindings_local.json可选
        ├── ModbusChannelManager      ← libmodbus RTU/TCP I/O
        ├── IIngressPort
        │     ├── ModbusIngress       ← 正常模式
        │     └── MockModbusIngress   ← --mock
        ├── ModbusEgress              ← 下行写寄存器
        ├── CommandExecutor           ← ExecuteCommand → Modbus 写 / mock
        └── UplinkClient              ← TcpUplinkClient 封装

4.1 IIngressPort — 入站抽象

方法 说明
open() 打开采集通道
close() 关闭采集通道
poll() 轮询,返回 vector<RawPacket>

ModbusIngress:按 pollIntervalMs 遍历 pollPoints(),经 ModbusChannelManager 读寄存器,组装 Modbus 0x03 响应帧作为 payload

MockModbusIngress:无硬件,生成递增值模拟帧。

4.2 ModbusChannelManager — 硬件 I/O

  • 构建时链接 libmodbus(系统包或 third_party/libmodbus vendor
  • RTUmodbus_new_rtu + modbus_read_registers / modbus_write_register
  • TCPmodbus_new_tcptransport: tcp 时)
  • 无 libmodbus 时退化为模拟读写(编译宏 SOFTBUS_HAS_LIBMODBUS=0

edge 不使用 daemon 侧的 protocol_modbus.so 插件;插件仅用于 daemon 解析上行 RawPacket。

4.3 UplinkClient — 上行/下行

方法 说明
connect() / disconnect() TCP 连接 daemon
sendEdgeRegister(edgeId) 注册边缘实例
sendHeartbeat(deviceId) 心跳
sendRawPacket(RawPacket) 上行原始包
sendAck(SoftbusAck) 写命令 ACK
poll() 接收下行 ExecuteCommand
setCommandHandler(handler) 注册命令回调

协议详见 UPLINK_WIRE_PROTOCOL.md

4.4 CommandExecutor — 下行写

daemon 经 bindings_global 在命令中注入 physicalPointId + edgeIdedge 直接写 Modbus

ExecuteCommand.params.physicalPointId → PhysicalRegistry → ModbusEgress.write

仅当命令缺少 physicalPointId 时,才用 bindings_local.jsonlogicalPointId → physicalPointId 备用解析。


5. 数据流

5.1 上行(采集 → OPC

sequenceDiagram
    participant HW as 现场设备
    participant Edge as softbus_edge
    participant Daemon as softbus_daemon
    participant OPC as OPC UA

    HW->>Edge: Modbus RTU/TCP 读寄存器
    Edge->>Edge: ModbusIngress.poll → RawPacket
    Note over Edge: sourceId = edge01/phy_xxx<br/>protocolTag = modbus
    Edge->>Daemon: TCP rawPacket + edgeRegister/heartbeat
    Daemon->>Daemon: protocol_modbus 解析帧
    Daemon->>Daemon: bindings_global → logicalPointId
    Daemon->>Daemon: logical_points → objectRef + scale
    Daemon->>OPC: UaModelBinder 写节点值

逐步说明:

  1. edgeModbusIngress 读硬件 → RawPacket{ sourceId=physicalPointId, payload=Modbus帧 }
  2. edgeUplinkClient.sendRawPacket → TCP JSON 行
  3. daemonTransportBusBridge 发布到总线 edge/raw/{physicalPointId}
  4. daemonPipelineEngine + protocol_modbus 插件解析 → 寄存器值
  5. daemonbindings_global 查绑定 → logical_points 得 OPC 路径 → scale → 写 OPC UA

未绑定物理点daemon 发布到 edge/unbound/raw/{physicalPointId},不写 OPC。

5.2 下行OPC 写 → 设备)

sequenceDiagram
    participant OPC as OPC UA 客户端
    participant Daemon as softbus_daemon
    participant Edge as softbus_edge
    participant HW as 现场设备

    OPC->>Daemon: ExecuteCommand (objectRef + value)
    Daemon->>Daemon: bindings_global → physicalPointId + edgeId
    Daemon->>Edge: TCP executeCommand
    Edge->>Edge: CommandExecutor → ModbusEgress
    Edge->>HW: libmodbus 写寄存器
    Edge->>Daemon: TCP ack

5.3 主循环(main.cpp

while (!shouldStop) {
    ConfigWatcher.poll();
    if (SIGHUP) { reloadPhysical(); reloadBindingsPatch(); }

    runtime.tick();          // uplink.poll + ingress.poll + sendRawPacket

    if (uplinkConnected && heartbeatDue)
        sendHeartbeat(edgeId);

    sleep(50ms);
}

6. CLI 参数

参数 默认值 说明
--config <path> config/edge/edge.json 边缘入口配置
--mock 关闭 使用 MockModbusIngress跳过 physical 配置与 libmodbus
--object-ref <path> DemoSite/.../MotorTemperature Mock 模式下的模拟 sourceId

不再使用 --daemon host:portuplink 地址由 edge.jsondaemonHost / daemonPort 指定。


7. 依赖

依赖 用途
softbus_core RawPacket、Logger、PlatformSignal、ConfigWatcher
softbus_registry BindingRegistry仅 local 可选)
softbus_transport TcpUplinkClient、UplinkWire
softbus_api ExecuteCommand、SoftbusAck
libmodbus Modbus RTU/TCP 硬件 I/Ovendor 或系统包)
asio TcpUplinkClient 底层

8. 构建与运行

8.1 libmodbus

构建时自动选择:系统 libmodbus-dev 优先,否则编译 third_party/libmodbus 静态库进 edge。

cmake -B build
cmake --build build --target softbus_edge

串口权限:sudo usermod -aG dialout $USER

8.2 运行示例

# 1. 先启动 daemon
./build/bin/softbus_daemon \
  --config config/daemon/daemon_profile.json \
  --logical-points config/daemon/logical_points.json \
  --bindings-global config/daemon/bindings_global.json

# 2. 启动 edge真实 Modbus
./build/bin/softbus_edge --config config/edge/edge.json

# 3. 无硬件 / 无 daemon 调试
./build/bin/softbus_edge --config config/edge/edge.json --mock

# 4. 热加载物理配置
kill -HUP $(pidof softbus_edge)

更多配置说明见 edge_device_setup.md


9. 未来扩展CANopen 与以太网设备

当前仅 Modbus RTU/TCP 在 edge 侧有完整 ingress/egress。CANopen 与更多以太网协议按同一双域模型扩展。

9.1 目标架构

physical_channels.json  ──→  ChannelDriver按 protocol 选择)
physical_devices.json   ──→  PhysicalRegistry
                                │
                    ┌───────────┼───────────┐
                    ▼           ▼           ▼
              ModbusIngress  CanIngress  EthIngress规划
                    │           │           │
                    └───────────┴───────────┘
                                ▼
                          RawPacket → UplinkClient

daemon 侧已有对应解析插件(protocol_modbusprotocol_canopenedge 只需换 ingress/driver上行仍是 RawPacket + protocolTag

9.2 CANopen规划

层级 计划
edge CanIngress + SocketCAN/can0 通道SDO/PDO 读对象字典 → RawPacketprotocolTag: "canopen"
配置 physical_channels 增加 transport: caninterface: can0bitrate: 500000physical_devices 增加 nodeId、对象索引
daemon 已有 protocol_canopen.soFramer/Parser 占位),绑定仍走 bindings_global
EDS/DCF edge 侧加载 EDS 映射物理点 ↔ COB-ID/索引daemon 不接收 EDS 全量

9.3 以太网设备(规划)

类型 edge 侧 说明
Modbus TCP 已支持 physical_channelstransport: tcphost + tcpPort
裸 TCP / 自定义帧 EthIngress(规划) 按 channel 配置帧格式或插件化 driver
UDP 组播 待定 适用于部分工业相机/传感器

以太网通道示例Modbus TCP已可用

{
  "channelId": "eth_modbus_0",
  "protocol": "modbus",
  "transport": "tcp",
  "host": "192.168.1.10",
  "tcpPort": 502,
  "pollIntervalMs": 200
}

9.4 edge 侧待增强Modbus 及通用)

说明
ModbusPollScheduler 合并相邻寄存器读,减少串口占用
按点 pollInterval 不同信号类型不同采集周期
变化上报 / deadband 物理层过滤,减少 uplink 流量
DeviceDiscovery 总线扫描、动态写入 physical_devices
多 ingress 并存 同一 edge 上 Modbus + CANopen 并行

上述优化均在物理域完成,不改变 daemon 双域模型。


10. 约束CONSTRAINTS

  • edge 不得直接操作 OPC UA 或加载 protocol_*.so 解析插件
  • edge 不得加载 logical_points.json / bindings_global.json
  • ingress 实现须遵循 IIngressPort,便于接入 CANopen/Ethernet driver
  • 上行统一为 RawPacketsourceId 必须是 physicalPointId(含 edgeId 前缀)
  • daemon 不可达时 edge 必须能独立启动并采集uplink 后台重连)

11. 相关文档

文档 内容
edge_device_setup.md 配置目录、双域、libmodbus、Mock
UPLINK_WIRE_PROTOCOL.md TCP JSON 线协议
softbus_daemon.md daemon 侧 Pipeline 与 OPC
plugins.md protocol 插件daemon 解析用)