Can't connect asynchronously to opcua server

Question:

I’m trying to get a value of a node in my OPCUA server.

I’m using asyncua on a windows 10 x64 computer.
The server is on a PLC.

When I write this in a normal task it works

client = Client("opc.tcp://192.168.1.88:4840")
# client.max_chunkcount = 5000 # in case of refused connection by the server
# client.max_messagesize = 16777216 # in case of refused connection by the server
client.connect()

But when I use the basic example using an async function with the await the line,

await client.connect()

Returns a timeoutError with asyncio.exceptions.CancelledError but nothing else to explain why it doesn’t work.

I would’ve kept trying without the the await but when I try to get my value with client.nodes.root.get_child([...]) the returned value prints <coroutine object Node.get_child at 0x000002C38EE45E40> (when it should be a simple integer or boolean) and I don’t know what to do with that so I guess I should keep going with the examples.

Do you have any idea why await client.connect() return this exception ?

I also tried with 2 different (and built under different langages) opcua clients just to be sure it wasn’t the server that was broken. And the clients can connect properly.


EDIT: in case that helps, the console gives :

disconnect_socket was called but connection is closed
close_session was called but connection is closed
close_secure_channel was called but connection is closed
disconnect_socket was called but connection is closed
Traceback (most recent call last):
  File "Python311Libasynciotasks.py", line 490, in wait_for
    return fut.result()
           ^^^^^^^^^^^^
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "OPCgpt.py", line 28, in <module>
    loop.run_until_complete(connect_to_opc_server())
  File "Python311Libasynciobase_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "OPCgpt.py", line 12, in connect_to_opc_server
    await client.connect()
  File "Python311Libsite-packagesasyncuaclientclient.py", line 287, in connect
    await self.open_secure_channel()
  File "Python311Libsite-packagesasyncuaclientclient.py", line 374, in open_secure_channel
    result = await self.uaclient.open_secure_channel(params)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "Python311Libsite-packagesasyncuaclientua_client.py", line 315, in open_secure_channel
    return await self.protocol.open_secure_channel(params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "Python311Libsite-packagesasyncuaclientua_client.py", line 232, in open_secure_channel
    await asyncio.wait_for(self._send_request(request, message_type=ua.MessageType.SecureOpen), self.timeout)
  File "Python311Libasynciotasks.py", line 492, in wait_for
    raise exceptions.TimeoutError() from exc
TimeoutError

EDIT 2 : Someone asked me to post my code for people to be able to reproduce it. It’s very simple so the bug is pretty odd. Someone on github mentionned that I may be blocking the eventloop but I didn’t go too far off from freeopcua’s examples..

import asyncio
from asyncua import Client

async def connect_to_opc_server():
    # Créez un client OPC UA
    client = Client(url="opc.tcp://192.168.1.88:4840")
    # client.max_chunkcount = 5000
    # client.max_messagesize = 16777216
    client.name = "Re_Shoes"

    try:
        # Connexion au serveur OPC UA
        await client.connect()

        # Lister tous les nœuds enfants du nœud racine (Object)
        root_node = await client.get_root_node()
        print("root node is: ", root_node)

    finally:
        # Déconnexion du serveur OPC UA
        await client.disconnect()

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(connect_to_opc_server())
    loop.close()

This is my main code but I have tried many variations of this.

Asked By: Jack

||

Answers:

After some back&forth I found that the issue came from the Nonce being empty.

If you dive into the library the Nonce is created in the open_secure_channel() method when you call the connect() function.
If you call the connect() method without specifying a security_policy the nonce size will be 0 (b'' on windows and apparently None and some other platforms) and as I’ve seen in another github post, some servers do not like empty nonce string…

So to solve this problem with the asyncua lib I added this code before the connect() method and it solved my issue, I can now connect to the server.

client.security_policy.secure_channel_nonce_length = 8
def signature(self, data):
    return None
fixed_signature = types.MethodType(signature, CryptographyNone)
client.security_policy.asymmetric_cryptography.signature = fixed_signature
Answered By: Jack
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.