4. OPC UA 客户端数据订阅
任务目标
编写一个 OPC UA 客户端程序,连接到指定的 OPC UA 服务器,订阅服务器上某个节点的数据变化,并在数据值发生变化时实时打印新值。本案例使用 asyncua 库实现,服务器地址与节点 ID 作为可配置参数。
环境依赖
- Python 3.8+
- 安装依赖:
pip install asyncua - 需要一个可访问的 OPC UA 服务器。若本地无服务器,可使用
asyncua自带的示例服务器(见下文验证方法)
关键完整代码
import asyncio
import sys
from asyncua import Client, ua
# 配置参数
SERVER_URL = "opc.tcp://localhost:4840" # 服务器地址
NODE_ID = "ns=2;i=2" # 要订阅的节点 ID
async def subscribe_data_change():
"""连接服务器并订阅指定节点的数据变化"""
client = Client(SERVER_URL)
try:
await client.connect()
print(f"已连接服务器: {SERVER_URL}")
# 获取目标节点
node = client.get_node(NODE_ID)
# 验证节点是否存在
await node.read_value()
print(f"订阅节点: {NODE_ID}")
# 创建订阅
subscription = await client.create_subscription(100, handler) # 100ms 推送间隔
await subscription.subscribe_data_change(node)
# 保持运行,等待数据变化
print("等待数据变化... (Ctrl+C 退出)")
while True:
await asyncio.sleep(1)
except KeyboardInterrupt:
print("\n用户中断,退出订阅")
except Exception as e:
print(f"错误: {e}")
finally:
await client.disconnect()
print("已断开连接")
class handler:
"""数据变化回调处理类"""
def datachange_notification(self, node, val, data):
"""当订阅的节点数据变化时,此方法被调用"""
print(f"[{node}] 新值: {val}")
if __name__ == "__main__":
try:
asyncio.run(subscribe_data_change())
except KeyboardInterrupt:
pass
运行方式
- 确保 OPC UA 服务器已启动并监听
opc.tcp://localhost:4840,且ns=2;i=2节点存在且可读。 - 将上述代码保存为
opcua_subscriber.py。 - 运行:
python opcua_subscriber.py
验证方法与判定标准
验证步骤:
- 若本地没有 OPC UA 服务器,可先启动
asyncua自带的示例服务器(在另一个终端运行python -m asyncua.server),该服务器默认在opc.tcp://localhost:4840监听,并包含ns=2;i=2节点(该节点值会周期性变化)。 - 运行订阅客户端程序。
- 观察控制台输出。
通过条件:
- 程序输出“已连接服务器”和“订阅节点”信息,说明连接与节点访问成功。
- 当服务器端节点值变化时(示例服务器约每 1 秒变化一次),客户端实时打印类似
[Node(...)] 新值: 42的信息,且数值随服务器端变化而更新。 - 按
Ctrl+C能正常退出并显示“已断开连接”。
预期输出示例(与代码逻辑一致,非实测结果):
已连接服务器: opc.tcp://localhost:4840
订阅节点: ns=2;i=2
等待数据变化... (Ctrl+C 退出)
[Node(...)] 新值: 1
[Node(...)] 新值: 2
[Node(...)] 新值: 3
常见问题
- 连接失败:确认服务器地址正确且服务器已启动;检查防火墙是否阻止 4840 端口。
- 节点不存在:若报
BadNodeIdUnknown,请将NODE_ID改为服务器上实际存在的节点 ID。 - 无数据变化输出:确认所订阅的节点值确实在变化;若节点值不变,订阅自然不会触发回调。