FreeOpcUa/opcua-asyncio

Migration freeOpcua to opcua-asyncio subscribe_data_change SubHandler method not called after subscription

lkaupp opened this issue · 8 comments

Discussed in #1617

Originally posted by lkaupp April 16, 2024
Dear community,

i migrate my old freeopcua code to opcua-asyncio. Under freeopcua the following worked well (pseudo code):

class SubHandler:
    """
    Subscription Handler. To receive events from server for a subscription
    data_change and event methods are called directly from receiving thread.
    Do not do expensive, slow or network operation there. Create another
    thread if you need to do such a thing
    """
    def datachange_notification(self, node: Node, val, data):
        print(data)
 

Node.nodeid.to_string() -> save to text
read txt -> nodeid= NodeId.from_string(nodeid)
nodetosubscribe = Node(self._client.uaclient, nodeid)
handler = SubHandler()
self._opcua_subscription = self._client.create_subscription(100, handler)
self._opcua_handle = self._opcua_subscription.subscribe_data_change(nodetosubscribe)

In opcua-asyncio the code seems to working similarly (nodes will be subscribed, [function call takes different time with different amount of nodes subscribe to, equal to the amount in the freeopcua implementation]), but the datachange_notification on the Subhandler is never called. With UAExpert the changes on the same node can be monitored as usual, freeopcua implementation works as usual, but only the opcua-asyncio implementation seems to be not working. Is there something different in reinitializing the nodes from plain txt, is there more to do?

new code:

class SubHandler:
    """
    Subscription Handler. To receive events from server for a subscription
    data_change and event methods are called directly from receiving thread.
    Do not do expensive, slow or network operation there. Create another
    thread if you need to do such a thing
    """
    def datachange_notification(self, node: Node, val, data):
        print(data)
 

Node.nodeid.to_string() -> save to text
read txt -> nodeid= NodeId.from_string(nodeid)

async with self._client:
    nodetosubscribe = Node(self._client.uaclient, nodeid)
    handler = SubHandler()
    self._opcua_subscription = await self._client.create_subscription(100, handler)
    self._opcua_handle = await self._opcua_subscription.subscribe_data_change(nodetosubscribe)

I also used the method self._client.get_node(), which is the same as my method implementation basically. However, still no events. What was altered during refactoring / rewriting the library to asyncio in the subscription process? Any ideas to debug the issue?

Whats the code after your posted code? If you leave the with Statement the client will disconnect.

Well here. Keep_running is true the whole time (same code as with freeopcua, which is working fine).:

            while self._keep_running:
                pass

            print("unsubscribe")
            await self._opcua_subscription.unsubscribe(self._opcua_handle)
            time.sleep(10)
            await self._opcua_subscription.delete()

Maybe next week I get the chance to capture a debug log. Which information do you need, and what commands should I use to get the necessary information?

I think you need to give the asyncio some timeslot, also you are wasting cpu cycles. Try this:

while self._keep_running:
        await asyncio.sleep(1.0)

Thanks for the tip. I will try your snippet as soon as I can enter the factory again.

await asyncio.sleep(1.0)

you are officially my hero, never expected that a while pass loop prevents messages from being delivered. Is this asyncio related - do I prevent any internal loops?

In asyncio you block the async loop until you call the next await. If you need to run code that doesn't call await for a while, there some solutions, like asyncio.to_thread for I/O bound or run_in_executor for cpu bound tasks.

Thanks for the explanation. I close my issue and reference your answer in the discussion!