3. OPC UA客户端连接与数据订阅

OPC UA客户端连接与数据订阅 学习卡片

任务目标

本案例实现一个OPC UA客户端,完成以下闭环:连接OPC UA服务器、浏览指定节点、订阅数据变化并持续接收通知。客户端将订阅服务器端每秒更新的模拟温度值,并在控制台打印每次变化。

环境与依赖

使用Python与asyncua库(纯Python实现,跨平台)。安装命令:

pip install asyncua

服务器端使用asyncua自带的示例服务器(server_example.py),它默认在opc.tcp://localhost:4840暴露一个每秒更新的模拟量节点。若需手动启动:

python -m opcua.server.server_example

完整实现代码

import asyncio
from asyncua import Client, ua

# OPC UA服务器地址(asyncua自带示例服务器)
SERVER_URL = "opc.tcp://localhost:4840"
# 示例服务器中模拟温度节点的NodeId(字符串格式)
NODE_ID = "ns=2;i=5"

async def subscribe_data_change():
    """连接服务器,订阅节点数据变化并打印"""
    client = Client(SERVER_URL)
    try:
        # 建立连接
        await client.connect()
        print(f"[INFO] 已连接服务器: {SERVER_URL}")

        # 获取目标节点
        node = client.get_node(NODE_ID)
        # 读取一次当前值,确认节点可访问
        current_val = await node.read_value()
        print(f"[INFO] 节点 {NODE_ID} 当前值: {current_val}")

        # 创建订阅对象
        subscription = await client.create_subscription(100, handler)  # 100ms轮询间隔
        # 订阅节点数据变化
        handle = await subscription.subscribe_data_change(node)
        print("[INFO] 已订阅数据变化,等待通知... (Ctrl+C退出)")

        # 持续运行,接收回调
        while True:
            await asyncio.sleep(1)
    except KeyboardInterrupt:
        print("\n[INFO] 用户中断,退出")
    except Exception as e:
        print(f"[ERROR] 连接或订阅失败: {e}")
    finally:
        # 清理订阅与连接
        if 'subscription' in locals():
            await subscription.delete()
        await client.disconnect()
        print("[INFO] 已断开连接")

class handler:
    """数据变化回调类(asyncua要求回调类实现datachange_notification方法)"""
    def datachange_notification(self, node, value, data):
        print(f"[DATA] 节点 {node} 新值: {value.value}")

if __name__ == "__main__":
    asyncio.run(subscribe_data_change())

运行与验证

  1. 先启动服务器(若未运行):python -m opcua.server.server_example
  2. 运行客户端脚本:python opcua_client.py
  3. 预期输出(示例,实际值随时间变化):
[INFO] 已连接服务器: opc.tcp://localhost:4840
[INFO] 节点 ns=2;i=5 当前值: 21.4
[INFO] 已订阅数据变化,等待通知... (Ctrl+C退出)
[DATA] 节点 NodeId(ns=2, i=5) 新值: 21.5
[DATA] 节点 NodeId(ns=2, i=5) 新值: 21.7
[DATA] 节点 NodeId(ns=2, i=5) 新值: 21.9
...

通过判定标准:客户端能打印出服务器推送的连续变化数值(每秒约1次),且数值与服务器端模拟值一致。若10秒内无任何[DATA]输出,说明订阅未生效或节点ID不匹配,请检查服务器地址与节点ID。

关键说明

  • 示例服务器中ns=2;i=5是官方示例的模拟温度节点,若使用其他服务器,需通过client.get_children()或UA Expert工具浏览实际节点ID。
  • 订阅轮询间隔设为100ms,实际通知频率由服务器端数据变化频率决定(示例服务器约1秒更新一次)。
  • 回调类handler必须实现datachange_notification方法,否则asyncua会静默丢弃通知。
OPC UA 客户端订阅数据流 OPC UA 服务器 opc.tcp://localhost:4840 节点 ns=2;i=5 每秒更新模拟温度 OPC UA 客户端 asyncua Python 订阅数据变化 回调打印新值 ① 建立连接 ② 订阅数据变化 ③ 推送新值 (约1次/秒) 控制台输出 [DATA] 新值: 21.5 [DATA] 新值: 21.7 [DATA] 新值: 21.9 回调 订阅后服务器每次变化自动推送,无需客户端轮询