eclipse / milo

Eclipse Milo™ - an open source implementation of OPC UA (IEC 62541).
http://www.eclipse.org/milo
Eclipse Public License 2.0
1.16k stars 427 forks source link

How to detect the status of a Subscription? #393

Closed WimVriend closed 5 years ago

WimVriend commented 5 years ago

Subscribing to items in the (Siemens S7-1500) OPC UA Server works fine. But when the PLC-code is changed (during normal development) and the PLC code is downloaded to the PLC, notifications for the MonitoredItems stop arriving (which makes sense, since the data in the server was changed).

So my question is: how can the client detect changes in the PLC, which require renewing the subscription(s)? An example would be very much appreciated!

Below is a screenshot of the messages UAExpert generates, when the PLC is downloaded: response to download plc

kevinherron commented 5 years ago

Add a SubscriptionListener to the UaSubscriptionManager from client.getSubscriptionManager(). Implement onSubscriptionTransferFailed and re-create any subscriptions that you get this call for.

Milo SDK automatically reconnects and tries to resume the old session or transfer the old subscriptions to the new session, but when you download a program to the PLC its server is restarted and these attempts will fail because the server does not retain any of that information. So you'll end up getting an onSubscriptionTransferFailed called for each of your subscriptions.

WimVriend commented 5 years ago

Hello Kevin,

Thanks (again) for your answers. I found a sample of this in the OpcUaClientIT.java file (which was outside the samples section...) and added this to my code to check if any events arrive:

       m_readOPCInterface.getSubscriptionManager()
          .addSubscriptionListener(new UaSubscriptionManager.SubscriptionListener()
          {
            @Override
            public void onKeepAlive(UaSubscription subscription,
                DateTime publishTime)
            {
              System.out.println(String.format("onKeepAlive event for %s",
                                               subscription.toString()));
            }

            @Override
            public void onStatusChanged(UaSubscription subscription,
                StatusCode status)
            {
              System.out.println(String.format("onStatusChanged event for %s, status = %s",
                                               subscription.toString(),
                                               status.toString()));
            }

            @Override
            public void onPublishFailure(UaException exception)
            {
              System.out.println(String.format("onPublishFailure exception: %s",
                                               exception.toString()));
            }

            @Override
            public void onNotificationDataLost(UaSubscription subscription)
            {
              System.out.println(String.format("onNotificationDataLost event for %s",
                                               subscription.toString()));
            }

            @Override
            public void onSubscriptionTransferFailed(UaSubscription subscription,
                StatusCode statusCode)
            {
              System.out.println(String.format("onSubscriptionTransferFailed event for %s, status = %s",
                                               subscription.toString(),
                                               statusCode.toString()));
            }
          });

I expected to see at least one 'onStatusChanged' event for each subscription (I now only have one, so I expected one) and frequent 'onKeepAlive' events. When do I get those?

So the 'onSubscriptionTransferFailed' indicates 'some' failure. What's the best way to 're-setup' the subscription?

kevinherron commented 5 years ago

onStatusChanged will only be called if the subscription times out due to inactivity. You shouldn't see this with any regularity. If you do, you probably need to re-create that subscription.

onKeepAlive will happen only when a keep alive response is received, which only happens if no data is changing.

When onSubscriptionTransferFailed is called you have to re-create your subscriptions the same way you created them the first time (call CreateSubscription service, add monitored items, etc...)

Between the old subscription instance provided in the callback and any bookkeeping your application does you should be able to set up your subscriptions again.

WimVriend commented 5 years ago

OK, I have adapted the code, so I recreate the subscription after the onSubscriptionTransferFailed event.

Your comment

You shouldn't see this with any regularity. If you do, you probably need to re-create that subscription.

worries me a bit. When do these events get fired?

kevinherron commented 5 years ago

They get fired if the server sends a StatusChangeNotification to us. This only happens when the Subscription is closed due to inactivity, which only happens if the server were to stop receiving PublishRequests from the client for some reason.

There's more information about this in OPC UA Part 4, Section 5.13.1.1