javaopc-uamilo

How to handle resubscribing after server is disconnected or restarted with OPC UA milo?


Hi I would like to know how to handle (correctly) resubscribing with OPC UA milo. Currently I use this code to create subscribtion:

private CompletableFuture<DataValue> subscribe(NodeId nodeToSubscribe, double samplingInterval, Consumer<DataValue> onChangeDo){
    CompletableFuture<DataValue> result = new CompletableFuture<>();
    try{
        //sets parameters of subscription
        ManagedSubscription subscription = ManagedSubscription.create(client, samplingInterval);
        subscription.setDefaultSamplingInterval(samplingInterval);
        subscription.setDefaultQueueSize(uint(10));

        //adds "onChange" action
        ManagedDataItem managedDataItem = subscription.createDataItem(nodeToSubscribe, item -> item.addDataValueListener(onChangeDo));

        //wait till first value was read
        ManagedDataItem.DataValueListener listener = managedDataItem.addDataValueListener(result::complete);
        result.whenComplete((v,e) -> managedDataItem.removeDataValueListener(listener));
    } catch (UaException e){
        result.completeExceptionally(e);
    }

    return result;
}

But whenever the server is restarted or the connection between client and server goes down than the subscription is not working.

I could add session activity listener and when ever the connection goes down clear the subscription manager like that (code between stars).

addSessionActivityListener(new SessionActivityListener(){
                @Override
                public void onSessionActive(UaSession session) {
                    log.info("Connecting PLC with IP address {}",ipAddress);
                        setConnectionStatus(ConnectionStatus.CONNECTED);
                        **subscribeAll();**

                }

                @Override
                public void onSessionInactive(UaSession session) {
                    log.info("Disconnecting PLC with IP address {}",ipAddress);
                    setConnectionStatus(ConnectionStatus.DISCONNECTED);
                    **uaClient.getSubscriptionManager().clearSubscriptions();**
                }
});

But maybe there is better way how to handle this. All my code can be found here GitHub

UPDATED: Based on Kevin Harron's reply I can see that it should be done automatically but I am receiving this every time the server is disconnected (my log for subscription and statusCode):

ON SUBSCRIPTION TRANSFER FAILED: subscription org.eclipse.milo.opcua.sdk.client.subscriptions.OpcUaSubscription@3f344a79, status code StatusCode{name=Bad_ServiceUnsupported, value=0x800B0000, quality=bad}

As you can see, there is probably problem, that server doesn't support transfer of subscription. Kevin advised me that this should be handled using SubscriptionListener::onSubscriptionTransferFailed so I implemented it like this:

uaClient.getSubscriptionManager().addSubscriptionListener(new UaSubscriptionManager.SubscriptionListener() {
               
                @Override
                public void onSubscriptionTransferFailed(UaSubscription subscription, StatusCode statusCode) {
                    uaClient.getSubscriptionManager().clearSubscriptions();
                    subscribeAll();

                }

It seams to work but I would like to know if this is the best way or I missed something. Thanks all for answers.


Solution

  • The correct approach is mostly to do nothing - all of the details of reconnecting and resubscribing are handled automatically.

    The only case you need to handle is when the subscriptions were unable to be transferred after a new session was created. Implement SubscriptionListener::onSubscriptionTransferFailed to re-create any subscription this callback indicates has failed to transfer.

    There's not really any other scenario where you should be manually clearing and re-creating your subscriptions. The onPublishFailure() callback is largely informational and does not require action. onSessionActive() and onSessionInactive() are purely informational and do not require any action from you.