# 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
