文心一言给出的解释如下:
asyncua(可能是指opcua的异步库)监控节点数据变化的原理通常涉及以下几个步骤:
建立订阅(Subscription):首先,客户端(使用asyncua库的程序)需要向OPC UA(OLE for Process Control Unified Architecture)服务器请求建立一个订阅。这个订阅用于接收服务器上的节点数据变化通知。
定义数据变化处理函数:在客户端,需要定义一个处理数据变化事件的函数,比如datachange_notification。这个函数会在服务器上的节点数据发生变化时被调用。
注册数据变化处理函数:在创建订阅时,需要将定义好的数据变化处理函数注册到订阅中。这样,当服务器上的节点数据发生变化时,OPC UA服务器就会发送一个数据变化通知给客户端,客户端的订阅就会调用注册的数据变化处理函数来处理这个通知。
关于“发现节点write_value()但是节点前后数值一样情况下,还是会产生data_change事件”的问题,这可能是由于以下几个原因:
OPC UA服务器实现:不同的OPC UA服务器实现可能有不同的行为。有些服务器可能会在每次调用write_value()时都发送一个数据变化通知,即使数值没有改变。这取决于服务器的设计和配置。
时间戳或质量戳的变化:即使节点的值没有改变,但与其关联的时间戳或质量戳可能发生了变化。OPC UA规范允许服务器在这些元数据发生变化时发送数据变化通知。
客户端或服务器缓存:在某些情况下,客户端或服务器可能使用缓存来存储节点值。如果缓存策略导致即使值没有改变也发送通知,那么可能会出现这种情况。
网络或传输层问题:有时,网络或传输层的问题可能导致数据包被重传或错误地解释为新的数据变化。
为了解决这个问题,你可以尝试以下方法:
检查OPC UA服务器文档和配置:了解服务器如何处理数据变化通知,并查看是否有相关的配置选项可以调整。
使用调试工具:使用OPC UA调试工具来监控网络流量和服务器响应,以便更好地理解问题的根源。
更新或修复软件:如果问题是由软件缺陷引起的,考虑更新到最新版本的服务器和客户端库。
实现额外的逻辑来过滤不必要的通知:在客户端实现额外的逻辑来检查接收到的数据变化通知是否真正表示了值的改变,并忽略不必要的通知。
监控节点数据变化的代码如下:
class SubscriptionHandler:
"""
The SubscriptionHandler is used to handle the data that is received for the subscription.
"""
async def datachange_notification(self, node: Node, val, data):
"""
Callback for asyncua Subscription.
This method will be called when the Client received a data change message from the Server.
"""
# 数据更新
monitor_node_q.append(node)
task4 = asyncio.create_task(update_data())
await task4
# print(node, val, data)
browse_name = await node.read_browse_name()
channel_idx = int(str(str(str(browse_name).split(",")[0]).split("=")[-1]))
r.hset(f"Channel{channel_idx}", f"Channel{channel_idx}", str(channel_idx))
def event_notification(self, event: Event):
"""
called for every event notification from server
"""
print(event)
def status_change_notification(self, status):
"""
called for every status change notification from server
"""
print(status)
async def subscription_data_change(client, monitor_nodes):
# 建立数据改变监控事件
handler = SubscriptionHandler()
subscription = await client.create_subscription(500, handler) # 500ms 发布间隔
await subscription.subscribe_data_change(monitor_nodes)