Dmitriy Alekseev
Dmitriy Alekseev

Reputation: 23

OPC UA client KeepAlive

Dear OPC UA community!

I want to develop an OPC UA client for our software using examples from OPC Foundation and MS Azure. I am concerned about the difference between these examples. In the first example, reconnecting after a KeepAlive failure is performed using ReconnectHandler:

        private void Client_KeepAlive(Session sender, KeepAliveEventArgs e)
        {
            if (e.Status != null && ServiceResult.IsNotGood(e.Status))
            {
                Console.WriteLine("{0} {1}/{2}", e.Status, sender.OutstandingRequestCount, sender.DefunctRequestCount);

                if (reconnectHandler == null)
                {
                    Console.WriteLine("--- RECONNECTING ---");
                    reconnectHandler = new SessionReconnectHandler();
                    reconnectHandler.BeginReconnect(sender, ReconnectPeriod * 1000, Client_ReconnectComplete);
                }
            }
        }

        private void Client_ReconnectComplete(object sender, EventArgs e)
        {
            // ignore callbacks from discarded objects.
            if (!Object.ReferenceEquals(sender, reconnectHandler))
            {
                return;
            }

            session = reconnectHandler.Session;
            reconnectHandler.Dispose();
            reconnectHandler = null;

            Console.WriteLine("--- RECONNECTED ---");
        }

In the second, the session is disconnected and re-created:

        private void StandardClient_KeepAlive(Session session, KeepAliveEventArgs e)
        {
            // ignore if we are shutting down
            if (ShutdownTokenSource.IsCancellationRequested == true)
            {
                return;
            }

            if (e != null && session != null && session.ConfiguredEndpoint != null && OpcUaClientSession != null)
            {
                try
                {
                    if (!ServiceResult.IsGood(e.Status))
                    {
                        Logger.Warning($"Session endpoint: {session.ConfiguredEndpoint.EndpointUrl} has Status: {e.Status}");
                        Logger.Information($"Outstanding requests: {session.OutstandingRequestCount}, Defunct requests: {session.DefunctRequestCount}");
                        Logger.Information($"Good publish requests: {session.GoodPublishRequestCount}, KeepAlive interval: {session.KeepAliveInterval}");
                        Logger.Information($"SessionId: {session.SessionId}");

                        if (State == SessionState.Connected)
                        {
                            MissedKeepAlives++;
                            Logger.Information($"Missed KeepAlives: {MissedKeepAlives}");
                            if (MissedKeepAlives >= OpcKeepAliveDisconnectThreshold)
                            {
                                Logger.Warning($"Hit configured missed keep alive threshold of {OpcKeepAliveDisconnectThreshold}. Disconnecting the session to endpoint {session.ConfiguredEndpoint.EndpointUrl}.");
                                session.KeepAlive -= StandardClient_KeepAlive;
                                Task t = Task.Run(async () => await DisconnectAsync());
                            }
                        }
                    }
                    else
                    {
                        if (MissedKeepAlives != 0)
                        {
                            // reset missed keep alive count
                            Logger.Information($"Session endpoint: {session.ConfiguredEndpoint.EndpointUrl} got a keep alive after {MissedKeepAlives} {(MissedKeepAlives == 1 ? "was" : "were")} missed.");
                            MissedKeepAlives = 0;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.Error(ex, $"Error in keep alive handling for endpoint '{session.ConfiguredEndpoint.EndpointUrl}'. (message: '{ex.Message}'");
                }
            }
            else
            {
                Logger.Warning("Keep alive arguments seems to be wrong.");
            }
        }

Which example is correct? Will the subscription to monitoring items be discarded by the server for any of these options? Can I rely on these examples to create a reliable client, or is there something missing?

Thank you for any help!

Upvotes: 2

Views: 7299

Answers (1)

koepalex
koepalex

Reputation: 215

It is the best way to look into the implementation of Reference Client and Reference Server (e.g. via "UA Quickstart Application" solution) because they are certified. In case of doubt, use them as reference:

From ConnectServerCtrl:

private void Session_KeepAlive(Session session, KeepAliveEventArgs e)
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(new KeepAliveEventHandler(Session_KeepAlive), session, e);
        return;
    }

    try
    {
        // check for events from discarded sessions.
        if (!Object.ReferenceEquals(session, m_session))
        {
            return;
        }

        // start reconnect sequence on communication error.
        if (ServiceResult.IsBad(e.Status))
        {
            if (m_reconnectPeriod <= 0)
            {
                UpdateStatus(true, e.CurrentTime, "Communication Error ({0})", e.Status);
                return;
            }

            UpdateStatus(true, e.CurrentTime, "Reconnecting in {0}s", m_reconnectPeriod);

            if (m_reconnectHandler == null)
            {
                if (m_ReconnectStarting != null)
                {
                    m_ReconnectStarting(this, e);
                }

                m_reconnectHandler = new SessionReconnectHandler();
                m_reconnectHandler.BeginReconnect(m_session, m_reconnectPeriod * 1000, Server_ReconnectComplete);
            }

            return;
        }

        // update status.
        UpdateStatus(false, e.CurrentTime, "Connected [{0}]", session.Endpoint.EndpointUrl);
                        
        // raise any additional notifications.
        if (m_KeepAliveComplete != null)
        {
            m_KeepAliveComplete(this, e);
        }
    }
    catch (Exception exception)
    {
        ClientUtils.HandleException(this.Text, exception);
    }
}

Upvotes: 3

Related Questions