Files
softbus_OPC/docs/softbus_edge.md
flower_linux ebff25e0d5 phase 2 tcp
2026-06-12 21:23:21 +08:00

16 KiB
Raw Blame History

softbus_edge

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

1. 概述

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

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

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

模式 说明
正常模式 CompositeIngressModbus + CANopen + Eth读真实硬件
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 → CompositeIngress按 protocol 创建子 ingress
7. 各 ChannelManager.openChannellibmodbus / SocketCAN / asio
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 通道默认轮询周期(点可覆盖)
interface / bitrate CANopen 通道:can0500000
host / tcpPort Modbus TCP 或裸以太网

CANopen 通道示例见 config/edge/physical_channels_can.json

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,
          "pollIntervalMs": 1000,
          "reportOnChange": true,
          "deadband": 0.5
        }
      ]
    }
  ]
}

点级可选字段:

字段 说明
pollIntervalMs 覆盖通道默认周期
reportOnChange true 时启用变化上报
deadband 工程值死区(配合 reportOnChange
objectIndex / subIndex CANopen 对象字典索引
pdoType tpdo / sdo / rpdo
cobId PDO COB-ID 覆盖
nodeId / edsPath 设备级 CANopen 字段

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

CANopen 示例见 config/edge/physical_devices_can.jsonconfig/edge/samples/minimal.eds

3.5 热加载

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

4. 组件架构

4.0 目录结构

include/softbus_edge/
  ingress/          IIngressPort, CompositeIngress
    modbus/         ModbusIngress, MockModbusIngress
    can/            CanIngress
    eth/            EthIngress
  driver/
    modbus/         ModbusChannelManager (libmodbus)
    can/            CanChannelManager (SocketCAN)
    eth/            EthChannelManager (asio)
  egress/
    modbus/         ModbusEgress
    can/            CanEgress (SDO 写)
    eth/            EthEgress
  scheduler/        ModbusPollScheduler, PollPlanner, ChangeFilter
  canopen/          NmtManager, SdoClient, PdoMapper
  config/ runtime/ factory/ discovery/
main.cpp
  └── EdgeRuntime
        ├── PhysicalRegistry
        ├── CompositeIngress
        │     ├── ModbusIngress   + ModbusPollScheduler / PollPlanner / ChangeFilter
        │     ├── CanIngress    + NmtManager / PdoMapper / SdoClient
        │     └── EthIngress
        ├── ModbusEgress / CanEgress / EthEgress
        ├── CommandExecutor     ← 按 protocol 分发写
        └── UplinkClient

4.1 IIngressPort — 入站抽象

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

ModbusIngressPollPlanner 调度到期点 → ModbusPollScheduler 合并相邻寄存器批量读 → ChangeFilter 变化过滤 → RawPacket

CanIngress:启动时 NMT Start + 加载 EDSTPDO 监听 + SDO 慢点轮询;protocolTag: "canopen"

EthIngress:裸 TCP/UDP 收字节;protocolTag: "raw_eth"

MockModbusIngress--mock 模式,无硬件模拟帧。

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   → ModbusEgress
canopen  → CanEgress (SDO download)
raw      → EthEgress

仅当命令缺少 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/O
SocketCAN Linux linux/can.hSOFTBUS_HAS_SOCKETCAN
softbus_canopen_common EDS 解析、对象字典
asio Uplink + EthChannel

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. 多协议与物理层优化(已实现)

9.1 多 Ingress 并存

physical_channels.json 中可混合 protocol: modbus | canopen | rawDeviceFactory 为每种协议创建子 ingressCompositeIngress 统一 poll()

9.2 Modbus 采集优化

组件 行为
ModbusPollScheduler 同 (channel, slave, function) 下合并相邻寄存器,单次最多 125 寄存器
PollPlanner 每点独立 pollIntervalMs(缺省继承通道)
ChangeFilter reportOnChange + deadband 物理层过滤

9.3 CANopenedge 已实现)

组件 说明
CanChannelManager SocketCAN 收发(无头文件时模拟降级)
NmtManager 启动时 NMT Start Remote Node
EdsDcfLoader softbus_canopen_common 解析 EDS
PdoMapper TPDO 订阅表 + 帧解包
SdoClient SDO upload慢点/ download下行写
daemon protocol_canopen.so 解析仍占位

CAN 前置:sudo ip link set can0 up type can bitrate 500000

9.4 裸以太网(骨架)

protocol: "raw" + transport: tcp|udpEthIngress 原样上行;EthEgress 支持原始字节写。

Modbus TCP 仍走 protocol: modbus + transport: tcp

9.5 待增强

说明
DeviceDiscovery 总线扫描、动态写入 physical_devices
daemon CANopen 解析 CanopenParser 完整实现
Eth 帧解析 自定义帧格式插件化

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/tcp_rtu_callstack.md Modbus TCP 点完整调用栈(含 RTU-over-TCP
edge_device_setup.md 配置目录、双域、libmodbus、Mock
UPLINK_WIRE_PROTOCOL.md TCP JSON 线协议
softbus_daemon.md daemon 侧 Pipeline 与 OPC
plugins.md protocol 插件daemon 解析用)