4. OPC UA客户端数据订阅
任务说明
本案例实现一个OPC UA客户端,连接指定的服务器,订阅一个或多个节点的数据变化,并在数据变化时实时打印新值。案例使用Python与asyncua库完成,目标是让读者掌握OPC UA订阅机制的最小可运行实现。
环境依赖
需要Python 3.8+,并安装OPC UA异步库:
pip install asyncua
完整实现代码
import asyncio
import sys
from asyncua import Client
# OPC UA服务器地址(根据实际服务器修改)
SERVER_URL = "opc.tcp://localhost:4840"
# 要订阅的节点ID列表(根据实际服务器节点修改)
NODE_IDS = [
"ns=2;i=2", # 示例:模拟量
"ns=2;i=3", # 示例:开关量
]
async def subscribe_data_change():
"""连接服务器并订阅指定节点的数据变化"""
client = Client(SERVER_URL)
try:
# 建立连接
await client.connect()
print(f"已连接服务器: {SERVER_URL}")
# 获取节点对象
nodes = [client.get_node(node_id) for node_id in NODE_IDS]
# 创建订阅
subscription = await client.create_subscription(100, handler) # 100ms发布间隔
# 订阅节点数据变化
for node in nodes:
await subscription.subscribe_data_change(node)
print(f"已订阅节点: {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 SubHandler:
"""数据变化回调处理类"""
def datachange_notification(self, node, val, data):
"""当订阅的节点数据变化时,此方法被调用"""
print(f"[数据变化] 节点: {node}, 新值: {val}")
# 全局回调实例
handler = SubHandler()
if __name__ == "__main__":
try:
asyncio.run(subscribe_data_change())
except KeyboardInterrupt:
sys.exit(0)
运行与验证
运行前提:需要有一个可访问的OPC UA服务器。若本地没有服务器,可使用asyncua自带的示例服务器快速验证:
# 终端1:启动一个简易OPC UA服务器(含上述节点)
python -m asyncua.server
# 终端2:运行本案例客户端
python opcua_subscriber.py
验证方法:
- 客户端启动后,控制台输出“已连接服务器”和“已订阅节点”即表示连接与订阅成功。
- 在服务器端修改对应节点的值(例如使用UaExpert或另一个脚本写入新值),客户端控制台应立即打印
[数据变化] 节点: ... 新值: ...。 - 若服务器端值不变,客户端不会打印任何数据变化信息,这是正常现象。
通过条件:当服务器节点值发生变化时,客户端能在1秒内打印出新值,且无异常报错,即视为案例验证通过。
关键说明
NODE_IDS中的节点ID需与服务器实际存在的节点匹配,否则订阅时会报BadNodeIdUnknown错误。- 订阅发布间隔设为100ms,即服务器每100ms检查一次是否有变化;实际延迟取决于服务器端采样间隔。
- 回调类
SubHandler必须实现datachange_notification方法,否则无法接收通知。 - 若需订阅更多节点,直接在
NODE_IDS列表中追加节点ID即可。