# softbus_opcua **路径:** `src/softbus_opcua` **构建目标:** `softbus_opcua`(STATIC 库) **依赖:** open62541 v1.4.6 **层级:** L2 服务层 ## 1. 概述 `softbus_opcua` 基于 **open62541** 封装 OPC UA 服务端,将 SoftBus 对象模型动态映射为 OPC UA 地址空间,实现: - 从 `ObjectModelTree` **动态构建**地址空间节点树 - 将 `DOMMessage` **实时绑定**到 UA Variable 节点 - 暴露 `ExecuteCommand` 为 OPC UA **Method**(反向控制入口) > **Phase 1 范围:** 动态地址空间、Double Variable、ExecuteCommand Method;TLS 与 Event 为 stub。 ## 2. CONSTRAINTS - OPC UA 层不得引入 SoftBus 业务逻辑,仅负责地址空间构建、值绑定与方法转发。 - NodeId 策略统一:Namespace Index = 1,Identifier 类型 = String,值为 `objectRef.toPath()`。 - 所有 open62541 调用须包裹 `#if SOFTBUS_HAS_OPCUA` 条件编译。 - Method 入参/出参均使用 JSON 字符串(UA String),与 `softbus_api::ExecuteCommand` 对齐。 - Event 发射 Phase 1 为 stub,仅打日志,不得伪装为已生效功能。 - Phase 2 静态 NodeSet(`config/ua_model.xml`)导入时,不得破坏动态构建的 NodeId 约定。 ## 3. 组件架构 ``` UaServerEngine ← open62541 生命周期管理 │ ├── UaAddressSpaceBuilder ← JSON 模型 → UA 节点树 ├── UaModelBinder ← DOMMessage → Variable 写值 ├── UaMethodBinder ← ExecuteCommand Method 回调 └── UaEventBridge ← Event 发射(Phase 1 stub) ``` ## 4. UaServerEngine ### 4.1 职责 管理 `UA_Server` 实例的创建、配置、运行与销毁。 ### 4.2 公共 API | 方法 | 入参 | 返回 | 说明 | |------|------|------|------| | `start(port, applicationName, security)` | 端口、应用名、安全配置 | bool | 启动 OPC UA 服务 | | `stop()` | — | void | 停止服务 | | `iterate()` | — | void | 驱动事件循环(主循环调用) | | `isRunning()` | — | bool | 运行状态 | | `nativeServer()` | — | UA_Server* | 获取原生服务器指针 | ### 4.3 SecurityConfig | 字段 | 类型 | Phase 1 | |------|------|---------| | `enableTls` | bool | 未实现,忽略 | | `certPath` | string | 未实现 | | `keyPath` | string | 未实现 | | `trustListPath` | string | 未实现 | Phase 1 使用 `UA_ServerConfig_setMinimal()` 创建无安全策略的服务端。 ### 4.4 配置来源 | 字段 | 配置路径 | 默认值 | |------|----------|--------| | 端口 | `daemon_profile.json` → `opcua.port` | 4840 | | 应用名 | `daemon_profile.json` → `opcua.applicationName` | SoftBusDaemon | | TLS | `daemon_profile.json` → `security.*` | 关闭 | **连接端点:** `opc.tcp://localhost:4840` ## 5. 地址空间 ### 5.1 节点树结构 ``` ObjectsFolder (NS0, 标准节点) ├── SoftBusControl (Object, NS1) │ └── ExecuteCommand (Method, NS1) │ ├── InputArguments: commandJson (String) │ └── OutputArguments: resultJson (String) └── {Site} (Object, NS1) └── {System} (Object, NS1) └── {Asset} (Object, NS1) └── {stableDeviceKey} (Object, NS1) └── {pointName} (Variable, NS1, Double, R/W) ``` ### 5.2 示例(基于默认模型) ``` ObjectsFolder ├── SoftBusControl │ └── ExecuteCommand └── DemoSite └── DemoSystem └── DemoAsset └── SimModbusDevice └── Temperature (Double, 初始值 0.0) ``` ### 5.3 NodeId 约定 | 节点类型 | Namespace | Identifier 类型 | Identifier 值 | |----------|-----------|----------------|----------------| | Site | 1 | String | `{siteName}` | | System | 1 | String | `{systemName}` | | Asset | 1 | String | `{assetName}` | | Device | 1 | String | `{stableDeviceKey}` | | Point (Variable) | 1 | String | `{objectRef.toPath()}` | | SoftBusControl | 1 | String | `SoftBusControl` | | ExecuteCommand | 1 | String | `ExecuteCommand` | **Point 节点完整路径示例:** ``` NodeId: ns=1;s=DemoSite/DemoSystem/DemoAsset/SimModbusDevice/Temperature ``` ### 5.4 Variable 属性 | 属性 | 值 | |------|-----| | DataType | Double (`UA_TYPES_DOUBLE`) | | AccessLevel | Read + Write | | TypeDefinition | BaseDataVariableType | | 初始值 | 0.0 | | ReferenceType | HasComponent | ### 5.5 模型映射规则 | softbus_model.json 字段 | OPC UA 节点 | |------------------------|-------------| | `site` | Site Object | | `system` | System Object(Site 子节点) | | `asset` | Asset Object(System 子节点) | | `devices[].stableDeviceKey` | Device Object(Asset 子节点) | | `devices[].points[].name` | Variable(Device 子节点) | 构建完成后,`UaModelBinder::registerPointNode(objectRef, nodeId)` 建立 `objectRef → UA_NodeId` 映射。 ## 6. UaModelBinder ### 6.1 职责 维护 `objectRef → UA_NodeId` 映射表,将 `DOMMessage` 实时写入对应 Variable。 ### 6.2 公共 API | 方法 | 说明 | |------|------| | `registerPointNode(objectRef, nodeId)` | 注册映射(构建阶段调用) | | `bind(DOMMessage)` | 写值到 OPC UA Variable | ### 6.3 绑定逻辑 1. 查找键 = `message.id`(非空时)或 `message.sourceId` 2. 从 `message.value` 提取数值: - 优先 `value["value"]` - 备选 `value["registerValue"]` 3. 调用 `UA_Server_writeValue(server, nodeId, doubleValue)` ### 6.4 数据流 ``` PipelineEngine::process(RawPacket) → ModbusParser → DOMMessage → UaModelBinder::bind(DOMMessage) → UA_Server_writeValue → OPC UA Client 可读 ``` ## 7. UaMethodBinder — ExecuteCommand Method ### 7.1 Method 定义 | 属性 | 值 | |------|-----| | BrowseName | `ExecuteCommand` | | Parent Object | `SoftBusControl` | | InputArguments | `commandJson` (String) — JSON 格式的 ExecuteCommand | | OutputArguments | `resultJson` (String) — JSON 格式响应 | ### 7.2 入参 JSON Schema ```json { "traceId": "string (required)", "objectRef": "string (required, 五层路径)", "command": "string (required)", "params": "object (required)" } ``` > 注:OPC UA Method 入参不含 `type` 字段,由 `ExecuteCommand::fromJson()` 直接解析。 ### 7.3 出参 JSON Schema **成功:** ```json {"success": true} ``` **失败:** ```json {"success": false, "error": "错误描述"} ``` ### 7.4 调用流程 ``` OPC UA Client 调用 ExecuteCommand(commandJson) → executeCommandMethod() 回调 → nlohmann::json::parse(commandJson) → ExecuteCommand::fromJson() + validate() → dispatchCommand() → handler_(cmd) → CommandDispatcher → TCP 下行 → 返回 resultJson ``` ### 7.5 客户端调用示例(UaExpert) 1. 连接 `opc.tcp://localhost:4840` 2. 浏览至 `Objects → SoftBusControl → ExecuteCommand` 3. 右键调用 Method,输入: ```json {"traceId":"trace-ua-001","objectRef":"DemoSite/DemoSystem/DemoAsset/SimModbusDevice/Temperature","command":"write","params":{"value":42.5}} ``` ## 8. UaEventBridge(Phase 1 stub) | 方法 | 说明 | Phase 1 行为 | |------|------|-------------| | `emitHeartbeat()` | 心跳事件 | 仅日志 | | `emitDeviceOnline(objectRef)` | 设备上线 | 仅日志 | | `emitDeviceOffline(objectRef)` | 设备离线 | 仅日志 | | `emitParseError(traceId, detail)` | 解析错误 | 仅日志 | ## 9. UaAddressSpaceBuilder ### 9.1 职责 从 `ObjectModelTree` 递归创建 OPC UA 对象树与 Variable 节点。 ### 9.2 构建流程 1. 获取 `ObjectsFolder` 标准节点作为根 2. 创建 `SoftBusControl` Object 与 `ExecuteCommand` Method 3. 按 Site → System → Asset → Device → Point 层级递归 `UA_Server_addObjectNode` / `addVariableNode` 4. 每个 Point 调用 `binder->registerPointNode(objectRef, nodeId)` ## 10. 条件编译 | 宏 | 说明 | |----|------| | `SOFTBUS_HAS_OPCUA` | CMake 选项 `SOFTBUS_WITH_OPCUA=ON` 时定义 | | `SOFTBUS_WITH_OPCUA` | CMake option,默认 ON | 未启用时,所有 OPC UA 方法返回 false 或空操作。 ## 11. open62541 配置 CMake 通过 `SoftbusThirdParty.cmake` 以 vendor 方式引入 `third_party/open62541`(v1.4.6): | 选项 | 值 | 说明 | |------|-----|------| | UA_ENABLE_SUBSCRIPTIONS | ON | 订阅支持 | | UA_ENABLE_METHODCALLS | ON | 方法调用 | | UA_ENABLE_DISCOVERY | ON | 发现服务 | | UA_ENABLE_ENCRYPTION | OFF | Phase 1 无加密 | ## 12. Phase 路线图 | 阶段 | 功能 | 状态 | |------|------|------| | Phase 1 | 动态地址空间、Double Variable、ExecuteCommand、无 TLS | **当前** | | Phase 2 | `ua_model.xml` NodeSet2 导入、TLS/mTLS、StatusCode 映射 | 规划 | | Phase 2 | 真实 Event 发射(DeviceOnline/Offline、ParseError) | 规划 | | Phase 3 | Subscription 优化、历史数据、多 DataType 支持 | 规划 | ### 12.1 Phase 2 NodeSet 占位 `config/ua_model.xml` 当前为空 shell,注释标明 Phase 2 可能导入静态 NodeSet2 XML,届时与动态构建策略需协调 NodeId 一致性。 ## 13. 依赖 - `softbus_core`(ObjectModelTree, DOMMessage, ObjectRef) - `softbus_api`(ExecuteCommand) - `open62541`(条件依赖) - **被依赖:** softbus_daemon