OPCUA
概述
OPC UA(Open Platform Communications Unified Architecture)是一种工业自动化领域的跨平台通信协议,支持数据访问、报警与事件、历史数据等功能。本控制器支持作为 OPC UA 客户端连接远程服务器,同时也可作为 OPC UA 服务器供其他设备访问,实现双向数据交互。
核心功能
| 功能 | 说明 |
|---|---|
| 连接管理 | 支持客户端/服务器双模式,可独立启停 |
| 节点读写 | 读写 OPC UA 服务器中的变量节点 |
| 订阅监控 | 订阅节点数据变化,实时推送更新 |
| 安全认证 | 支持用户名/密码认证方式 |
工作模式
- 客户端模式:控制器作为 OPC UA 客户端,连接外部 OPC UA 服务器,读写对方提供的变量节点
- 服务器模式:控制器作为 OPC UA 服务器,开放本地变量供外部客户端访问
上位机设置 OPCUA 参数
命令字: 0x7350
参数说明
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| client | object | 否 | 客户端参数 |
| client.enable | bool | 是 | 客户端连接使能,true-开启,false-关闭 |
| client.ip | string | 是 | OPC UA 服务器 IP 地址 |
| client.port | int | 是 | 服务器端口号,范围 [0, 65535],默认 4840 |
| client.resource | string | 否 | 服务器资源路径/名称 |
| client.username | string | 否 | 登录用户名(需服务器支持) |
| client.password | string | 否 | 登录密码 |
| client.security | string | 否 | 安全策略,可选 "None" / "Basic256Sha256" / "Basic128Rsa15" |
| server | object | 否 | 服务器参数 |
| server.enable | bool | 是 | 服务器使能,true-开启,false-关闭 |
| server.ip | string | 是 | 控制器用作服务器时的 IP 地址 |
| server.port | int | 是 | 服务器端口号,范围 [0, 65535],默认 4840 |
配置说明
client.enable和server.enable可以同时开启,控制器将同时运行客户端和服务端- 客户端模式下,
client.ip和client.port必须指向有效的 OPC UA 服务器 - 若服务器需要认证,需同时填写
client.username和client.password - 安全策略选择需与目标服务器支持的策略一致
请求示例
json
{
"client":
{
"enable": true,
"ip": "192.168.1.240",
"port": 49400,
"resource": "ProsysServer",
"username": "admin",
"password": "123456",
"security": "None"
},
"server":
{
"enable": true,
"ip": "192.168.0.229",
"port": 4840
}
}上位机查询 OPCUA 参数
命令字: 0x7351
请求示例
json
{}控制器回复上位机 OPCUA 参数
命令字: 0x7352
参数说明
| 字段 | 类型 | 描述 |
|---|---|---|
| client | object | 客户端参数 |
| client.enable | bool | 连接使能 |
| client.ip | string | 服务器 IP 地址 |
| client.port | int | 端口号 |
| client.resource | string | 服务器名称 |
| client.connected | bool | 客户端连接状态,true-已连接,false-未连接 |
| server | object | 服务器参数 |
| server.enable | bool | 服务器使能 |
| server.ip | string | 控制器用作服务器时的 IP 地址 |
| server.port | int | 端口号 |
| server.connected | bool | 是否有客户端连接,true-有客户端连接,false-无连接 |
响应示例
json
{
"client":
{
"enable": true,
"ip": "192.168.1.240",
"port": 49400,
"resource": "ProsysServer",
"connected": true
},
"server":
{
"enable": true,
"ip": "192.168.0.229",
"port": 4840,
"connected": false
}
}OPC UA 节点读写
控制器作为客户端连接 OPC UA 服务器后,可读写服务器中的变量节点。
上位机请求读取节点
命令字: 0x7353
参数说明
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| nodeId | string | 是 | 目标节点的 NodeId,如 "ns=2;s=Temperature" |
| nodeIdType | int | 否 | NodeId 类型,0-字符串,1-数字,2-GUID,3-Opaque,默认 0 |
| namespace | int | 否 | 命名空间索引,默认 0 |
节点标识说明
OPC UA 节点通过以下方式定位:
| 标识方式 | 格式示例 | 说明 |
|---|---|---|
| 字符串 ID | ns=2;s=MyVariable | 使用字符串标识 |
| 数字 ID | ns=2;i=1001 | 使用数字标识 |
| GUID | ns=2;g=ABC123... | 使用 GUID |
| 浏览路径 | /Objects/MyDevice/Temperature | 使用路径浏览 |
请求示例
json
{
"nodeId": "ns=2;s=Temperature",
"nodeIdType": 0,
"namespace": 2
}控制器回复节点读取结果
命令字: 0x7354
参数说明
| 字段 | 类型 | 描述 |
|---|---|---|
| nodeId | string | 读取的节点 ID |
| value | variant | 节点当前值 |
| dataType | string | 数据类型,如 "Int32" / "Double" / "Boolean" / "String" |
| timestamp | string | 数据时间戳(ISO 8601 格式) |
| status | int | 读取状态,0-成功,非0-失败 |
| errorMsg | string | 错误信息(仅失败时返回) |
响应示例
json
{
"nodeId": "ns=2;s=Temperature",
"value": 25.6,
"dataType": "Double",
"timestamp": "2024-03-15T10:30:00Z",
"status": 0
}上位机请求写入节点
命令字: 0x7355
参数说明
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| nodeId | string | 是 | 目标节点的 NodeId |
| value | variant | 是 | 要写入的值 |
| dataType | string | 是 | 写入值的数据类型 |
请求示例
json
{
"nodeId": "ns=2;s=SetPoint",
"value": 100.0,
"dataType": "Double"
}控制器回复节点写入结果
命令字: 0x7356
参数说明
| 字段 | 类型 | 描述 |
|---|---|---|
| nodeId | string | 写入的节点 ID |
| status | int | 写入状态,0-成功,非0-失败 |
| errorMsg | string | 错误信息(仅失败时返回) |
响应示例
json
{
"nodeId": "ns=2;s=SetPoint",
"status": 0
}批量读写节点
命令字: 0x7357
参数说明
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| operations | object[] | 是 | 批量操作列表 |
| operations[].opType | string | 是 | 操作类型,"read"-读取,"write"-写入 |
| operations[].nodeId | string | 是 | 节点 ID |
| operations[].value | variant | 否 | 写入值(opType="write" 时必填) |
请求示例
json
{
"operations": [
{ "opType": "read", "nodeId": "ns=2;s=Temperature" },
{ "opType": "read", "nodeId": "ns=2;s=Pressure" },
{ "opType": "write", "nodeId": "ns=2;s=SetPoint", "value": 50.0 }
]
}OPC UA 订阅机制
订阅功能允许控制器持续监控 OPC UA 服务器中的指定节点,当节点值发生变化时,服务器主动推送更新数据,无需轮询。
上位机创建订阅
命令字: 0x7358
参数说明
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| subscriptionId | string | 是 | 订阅唯一标识符,用于后续管理 |
| publishingInterval | int | 是 | 发布间隔(毫秒),服务器以此周期检查并推送变化数据 |
| lifetimeCount | int | 否 | 订阅生存周期计数,默认 10000 |
| maxKeepAliveCount | int | 否 | 最大保活计数,默认 3000 |
| monitoredItems | object[] | 是 | 监控项列表 |
| monitoredItems[].nodeId | string | 是 | 要监控的节点 ID |
| monitoredItems[].samplingInterval | int | 是 | 采样间隔(毫秒) |
| monitoredItems[].queueSize | int | 否 | 队列大小,默认 1 |
| monitoredItems[].discardOldest | bool | 否 | 队列满时是否丢弃最旧数据,默认 true |
| monitoredItems[].deadbandType | int | 否 | 死区类型,0-无,1-绝对值,2-百分比 |
| monitoredItems[].deadbandValue | double | 否 | 死区值,仅当值变化超过此阈值时才触发通知 |
订阅参数建议
| 参数 | 推荐值 | 说明 |
|---|---|---|
| publishingInterval | 100~1000 ms | 发布周期,越小实时性越好但网络负载越大 |
| samplingInterval | publishingInterval 的 1/2 ~ 1/5 | 采样周期应小于发布周期 |
| queueSize | 1~10 | 数据变化队列,应对突发变化 |
| lifetimeCount | ≥ 3 × maxKeepAliveCount | 订阅有效期,超时无保活则自动清理 |
| deadbandValue | 根据精度需求设定 | 变化阈值,过滤微小波动 |
请求示例
json
{
"subscriptionId": "sub_temp_001",
"publishingInterval": 200,
"lifetimeCount": 10000,
"maxKeepAliveCount": 3000,
"monitoredItems": [
{
"nodeId": "ns=2;s=Temperature",
"samplingInterval": 100,
"queueSize": 5,
"discardOldest": true,
"deadbandType": 1,
"deadbandValue": 0.5
},
{
"nodeId": "ns=2;s=Pressure",
"samplingInterval": 200,
"queueSize": 1
}
]
}控制器上报订阅数据
命令字: 0x7359
当订阅的节点数据发生变化时,控制器主动推送数据至上位机。
参数说明
| 字段 | 类型 | 描述 |
|---|---|---|
| subscriptionId | string | 订阅 ID |
| dataChanges | object[] | 变化的数据列表 |
| dataChanges[].nodeId | string | 节点 ID |
| dataChanges[].value | variant | 节点当前值 |
| dataChanges[].timestamp | string | 时间戳 |
| sequenceNumber | int | 序列号,递增 |
响应示例
json
{
"subscriptionId": "sub_temp_001",
"dataChanges": [
{
"nodeId": "ns=2;s=Temperature",
"value": 26.8,
"timestamp": "2024-03-15T10:30:01Z"
}
],
"sequenceNumber": 42
}上位机修改订阅
命令字: 0x735A
修改已有订阅的监控项或发布参数。
参数说明
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| subscriptionId | string | 是 | 要修改的订阅 ID |
| publishingInterval | int | 否 | 新的发布间隔 |
| addItems | object[] | 否 | 新增监控项列表 |
| removeItems | string[] | 否 | 移除监控项的 nodeId 列表 |
请求示例
json
{
"subscriptionId": "sub_temp_001",
"publishingInterval": 500,
"addItems": [
{ "nodeId": "ns=2;s=Humidity", "samplingInterval": 500 }
],
"removeItems": ["ns=2;s=Pressure"]
}上位机删除订阅
命令字: 0x735B
停止并删除指定订阅。
参数说明
| 字段 | 类型 | 必填 | 描述 |
|---|---|---|---|
| subscriptionId | string | 是 | 要删除的订阅 ID |
请求示例
json
{
"subscriptionId": "sub_temp_001"
}连接状态管理
控制器主动上报连接状态
命令字: 0x735C
当 OPC UA 客户端或服务器连接状态发生变化时,控制器主动推送状态更新。
参数说明
| 字段 | 类型 | 描述 |
|---|---|---|
| role | string | 角色,"client" / "server" |
| status | int | 状态码:0-已断开,1-连接中,2-已连接,3-连接失败 |
| errorMsg | string | 错误描述(连接失败时返回) |
| timestamp | string | 状态变化时间 |
响应示例
json
{
"role": "client",
"status": 2,
"timestamp": "2024-03-15T10:29:55Z"
}常见问题
连接失败排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 客户端无法连接 | IP/端口错误 | 检查 client.ip 和 client.port 是否正确 |
| 客户端无法连接 | 安全策略不匹配 | 确认 client.security 与服务器一致 |
| 客户端无法连接 | 网络不通 | 用 ping 测试网络连通性 |
| 节点读取失败 | NodeId 格式错误 | 确认 nodeId 格式,如 ns=2;s=xxx |
| 节点读取失败 | 命名空间索引错误 | 确认 namespace 值匹配服务器实际命名空间 |
| 写入失败 | 权限不足 | 确认服务器对目标节点开放了写入权限 |
| 订阅无数据推送 | 发布间隔过大 | 减小 publishingInterval 值 |
| 订阅无数据推送 | 死区设置过大 | 减小 deadbandValue 或关闭死区 |
性能建议
- 批量操作优先使用批量读写命令(
0x7357),减少通信开销 - 订阅监控项不宜过多,建议单个订阅不超过 50 个节点
- 采样间隔应根据数据变化频率合理设置,避免过度采样
- 发布间隔不宜过小,推荐 100ms ~ 1000ms