opcua-asyncio
opcua-asyncio copied to clipboard
How to auto-reconnect from client, after restart of CNC machine
====== Scenario ======
[ CNC ] <---> [ client_OPC ]
My acquisition script python use asyncua library and is very similar to the client-substription.py sample script.
The structure is below:
import sys
import os
import asyncio
import logging
import Configuration as C
from asyncua import Client, Node
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('asyncua')
class SubscriptionHandler:
"""
The SubscriptionHandler is used to handle the data that is received for the subscription.
"""
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.
"""
logger.info('datachange_notification %r %s', node, val)
async def main():
"""
Main task of this Client-Subscription example.
"""
client_OPC = Client(url=C.url)
async with client:
handler = SubscriptionHandler()
subscription = await client.create_subscription(C.TIME_UPDATE, handler)
await subscription.subscribe_data_change(C.node_list)
await asyncio.sleep(C.TIME_TO_CAPTURE_ACQUISITION)
await asyncio.sleep(1)
if __name__ == "__main__":
asyncio.run(main())
I would need automatic reconnection management if the CNC machine is turned off and on again. At the moment I am forced to restart the client-subscription script manually after the CNC is started.
Please do you know if there is a method to check when the server is down? Thanks you in advance.
We lately added the function check_connection which throws an exception if the connection is lost. In the near future i want to add a callback to the subscription handler if the connection is lost.
@schroeder- great idea. I never thought at it
We lately added the function
check_connectionwhich throws an exception if the connection is lost. In the near future i want to add a callback to the subscription handler if the connection is lost.
Wow! I hadn't seen this method for the CLIENT object.
But where can I put this check on the connection in my code structure (of the same type as client-substription.py)? I don't see a block of loops where to insert the check and the possible reconnection.
for the most simplistic usecase: https://github.com/AndreasHeine/opcua-sub-to-websocket/blob/b272dca8c8abf371e5e35f05ce93949bdb775723/client.py#L61
Try something like this:
def run_client(client: Client):
async with client:
handler = SubscriptionHandler()
subscription = await client.create_subscription(C.TIME_UPDATE, handler)
await subscription.subscribe_data_change(C.node_list)
while 1:
await asyncio.sleep(1)
client.check_connection()
async def main():
"""
Main task of this Client-Subscription example.
"""
client_OPC = Client(url=C.url)
while 1:
try:
run_client(client_opc)
return
catch Exception as e:
pass
Hello, I am trying to implement a class that keeps always same client and keeps client connected. But I always get CancelledError on second call. I am not very familiar with async programming but could you check my code?
this is simplified code of mine:
import asyncio
from asyncua import Client, Node, ua
import logging
logging.basicConfig(level=logging.INFO)
_logger = logging.getLogger('asyncua')
url = "opc.tcp://uademo.prosysopc.com:53530/OPCUA/SimulationServer"
ua_Client = Client(url)
class OpcuaMyClient:
def __init__(self):
self.client = ua_Client
async def check_conn(self):
try:
conStatus = await self.client.check_connection()
if not(conStatus):
await asyncio.shield(self.client.connect())
except Exception as e:
_logger.info("OPCUA connection is lost, reconnecting")
print(e)
print("--------------------------")
await self.client.connect()
async def __aenter__(self):
await self.check_conn()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
pass
async def getServerStatus():
async with OpcuaMyClient() as oclient:
var = oclient.client.get_node('i=2256')
value = await var.read_value()
_logger.info(value.StartTime)
return str(value.StartTime)
async def getServerStatus2():
async with OpcuaMyClient() as oclient:
var = oclient.client.get_node('i=2258')
value = await var.read_value()
_logger.info(value)
return str(value)
async def wait():
await asyncio.sleep(1)
if __name__ == "__main__":
asyncio.run(getServerStatus2())
asyncio.run(wait())
asyncio.run(getServerStatus())
and this is what I get :
py .\opcua-myclass.py
INFO:asyncua.client.client:connect
INFO:asyncua.client.ua_client.UaClient:opening connection
WARNING:asyncua.uaprotocol:updating client limits to: TransportLimits(max_recv_buffer=8196, max_send_buffer=8196, max_chunk_count=0, max_message_size=4194240)
INFO:asyncua.client.ua_client.UASocketProtocol:open_secure_channel
INFO:asyncua.client.ua_client.UaClient:create_session
INFO:asyncua.client.client:find_endpoint ...... a lot of info here.....
INFO:asyncua.client.ua_client.UaClient:activate_session
INFO:asyncua:2022-10-13 20:35:05.846000 (user note)this is successful result from first call
Traceback (most recent call last):
File "C:\Users\User1\AppData\Local\Programs\Python\Python310\lib\site-packages\asyncua\client\client.py", line 472, in _renew_channel_loop
await asyncio.sleep(duration)
File "C:\Users\User1\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 609, in sleep
return await future
asyncio.exceptions.CancelledError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\User1\Documents\GitHub\thu-wms-backend\flask\opcua-myclass.py", line 40, in getServerStatus
async with OpcuaMyClient() as oclient:
File "C:\Users\User1\Documents\GitHub\thu-wms-backend\flask\opcua-myclass.py", line 33, in __aenter__
await self.check_conn()
File "C:\Users\User1\Documents\GitHub\thu-wms-backend\flask\opcua-myclass.py", line 23, in check_conn
conStatus = await self.client.check_connection()
File "C:\Users\User1\AppData\Local\Programs\Python\Python310\lib\site-packages\asyncua\client\client.py", line 439, in check_connection
await self._renew_channel_task
asyncio.exceptions.CancelledError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\User1\Documents\GitHub\thu-wms-backend\flask\opcua-myclass.py", line 62, in <module>
asyncio.run(getServerStatus())
File "C:\Users\User1\AppData\Local\Programs\Python\Python310\lib\asyncio\runners.py", line 44, in run
return loop.run_until_complete(main)
File "C:\Users\User1\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 641, in run_until_complete
return future.result()
asyncio.exceptions.CancelledError
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x000001AD270CB760>
Traceback (most recent call last):
File "C:\Users\User1\AppData\Local\Programs\Python\Python310\lib\asyncio\proactor_events.py", line 116, in __del__
File "C:\Users\User1\AppData\Local\Programs\Python\Python310\lib\asyncio\proactor_events.py", line 108, in close
File "C:\Users\User1\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 745, in call_soon
File "C:\Users\User1\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 510, in _check_closed
RuntimeError: Event loop is closed
client.check_connection doesn't return any value. It just throws a exception if there is a error. So conStatus is always None and you will reconnect which triggers an exception.
See examples/client-reconnect.py