opcua-asyncio
opcua-asyncio copied to clipboard
BadSessionIdInvalid after 30s
Describe the bug
Connect to an S7 with opcua-client, client attempts to negotiate a timeout of an hour but gets only 30s.
asyncua.client.client - WARNING - Requested session timeout to be 3600000ms, got 30000ms instead')
After 30s client gets BadSessionIdInvalid and crashes.
asyncua.client.ua_client.UASocketProtocol - WARNING - ServiceFault (BadSessionIdInvalid, diagnostics: DiagnosticInfo(SymbolicId=None, NamespaceURI=None, Locale=None, LocalizedText=None, AdditionalInfo=None, InnerStatusCode=None, InnerDiagnosticInfo=None)) from server received in response to ReadRequest')
uawidgets.utils - ERROR - "The session id is not valid."(BadSessionIdInvalid)')
Traceback (most recent call last):
File "/home/jtatsch/.local/lib/python3.8/site-packages/opcua_widgets-0.6.1-py3.8.egg/uawidgets/utils.py", line 21, in wrapper
result = func(self, *args)
File "/home/jtatsch/.local/lib/python3.8/site-packages/opcua_client-0.8.4-py3.8.egg/uaclient/mainwindow.py", line 437, in _update_actions_state
if node.read_node_class() == ua.NodeClass.Method:
File "/home/jtatsch/.local/lib/python3.8/site-packages/asyncua-0.9.94-py3.8.egg/asyncua/sync.py", line 94, in wrapper
result = self.tloop.post(aio_func(*args, **kwargs))
File "/home/jtatsch/.local/lib/python3.8/site-packages/asyncua-0.9.94-py3.8.egg/asyncua/sync.py", line 52, in post
return futur.result()
File "/usr/lib/python3.8/concurrent/futures/_base.py", line 444, in result
return self.__get_result()
File "/usr/lib/python3.8/concurrent/futures/_base.py", line 389, in __get_result
raise self._exception
File "/home/jtatsch/.local/lib/python3.8/site-packages/asyncua-0.9.94-py3.8.egg/asyncua/common/node.py", line 144, in read_node_class
result = await self.read_attribute(ua.AttributeIds.NodeClass)
File "/home/jtatsch/.local/lib/python3.8/site-packages/asyncua-0.9.94-py3.8.egg/asyncua/common/node.py", line 302, in read_attribute
result = await self.server.read(params)
File "/home/jtatsch/.local/lib/python3.8/site-packages/asyncua-0.9.94-py3.8.egg/asyncua/client/ua_client.py", line 365, in read
data = await self.protocol.send_request(request)
File "/home/jtatsch/.local/lib/python3.8/site-packages/asyncua-0.9.94-py3.8.egg/asyncua/client/ua_client.py", line 154, in send_request
self.check_answer(data, f" in response to {request.__class__.__name__}")
File "/home/jtatsch/.local/lib/python3.8/site-packages/asyncua-0.9.94-py3.8.egg/asyncua/client/ua_client.py", line 163, in check_answer
hdr.ServiceResult.check()
File "/home/jtatsch/.local/lib/python3.8/site-packages/asyncua-0.9.94-py3.8.egg/asyncua/ua/uatypes.py", line 328, in check
raise UaStatusCodeError(self.value)
asyncua.ua.uaerrors._auto.BadSessionIdInvalid: "The session id is not valid."(BadSessionIdInvalid)
Expected behavior
I would expect some kind of keep alive mechanism to keep the session alive so it does not become invalid and crash.
Version
Python-Version: 3.8
opcua-asyncio Version (e.g. master branch, 0.9): master branch
Looked into the code and found that there is a keep alive implemented, just the calculation of the duration is wrong.
Clients should request a new SecurityToken after 75 % of its lifetime has elapsed
duration = self.secure_channel_timeout * 0.75 / 1000
instead of the self.secure_channel_timeout it should use the permitted session timeout received from the server in self.session_timeout.
No this is no bug, this is how opc ua works. The session timeout is reset when you send a request, so just have one subscription is enough, to prevent a session timeout. No seecure channel has nothing todo with a session and changing the securechannel renewal is not good because conforming servers can raise errors if you do it to early (earliest 0.25 of the time is allowed!). The whole reconnect and session reconnect is currently missing, so there is no easy fix atm.
Guess I abused the secure channel timeout watchdog then. However it mitigated my issue effectively 😆 However, if the session has timed out the secure channel is dead as well, right?
Would you be fine with a second watchdog mechanism to exclusively manage the session timeout? Is there something like a Nop that we can send to not abuse the secure channel stuff?
A secure channel can outlive a a session. Also a secure channel can have multiple sessions (our client doesn't support that). Currently we are missing reconnecting strategies so there it doesn't work.
The easiest thing currently is to subscribe to a random variable or read a variable with a timer.
To keep alive the connection I subcribe for the time in the server.