Zum Inhalt springen

Anleitung

Clients für den Dauerbetrieb

Clients für den Dauerbetrieb: wie das SDK Verbindungsabbrüche erkennt, Sitzungen und Subscriptions wiederherstellt und was die Anwendung selbst übernimmt.

Anleitung

Clients für den Dauerbetrieb ​

Ein Client, der dauerhaft mit einer Anlage verbunden ist, muss Netzwerkausfälle, Neustarts des Servers und Wartungsfenster überstehen. Den größten Teil davon übernimmt das SDK: Es erkennt den Abbruch, verbindet sich selbstständig neu und stellt Subscriptions wieder her. Diese Seite fasst zusammen, was dabei automatisch geschieht und welche Aufgaben bei der Anwendung bleiben. Die einzelnen Einstellungen beschreibt der Abschnitt Verbindungsüberwachung der Konfiguration.

Was das SDK bei einem Abbruch übernimmt ​

Beteiligte TypenOpcClientOpcClientStateOpcClientKeepAlive

Solange UseBreakDetection aktiviert ist (Standard), prüft der Client die Verbindung mit KeepAlive-Anfragen im Abstand von KeepAlive.Interval (Standard 5000 ms). Wenn eine KeepAlive-Anfrage fehlschlägt oder ein Service-Aufruf einen Verbindungsverlust meldet (zum Beispiel mit BadConnectionClosed oder BadSessionIdInvalid), läuft sofort der folgende Ablauf ab:

SchrittWas das SDK tutZustand und Ereignis
1Der Client erkennt den Abbruch.Reconnecting, Ereignis Reconnecting
2Im Abstand von ReconnectInterval (Standard 10000 ms) versucht der Client, die bestehende Sitzung wiederherzustellen, längstens für MaxReconnectDuration (Standard 0, unbegrenzt).bleibt Reconnecting
3aDie Sitzung lässt sich wiederherstellen: Der Client fordert die ausstehenden Benachrichtigungen seiner Subscriptions erneut an.Reconnected, Ereignis Reconnected
3bDie Sitzung ist auf dem Server abgelaufen: Der Client erzeugt eine neue Sitzung und legt alle Subscriptions mit ihren MonitoredItems neu an.Connected, Ereignis Connected
4In beiden Fällen lädt der Client die Namespace-Tabelle des Servers neu. Wenn UseDynamic oder die Typauflösung aktiv ist, lädt er auch das Typsystem neu.

Für die Anwendung heißt das: Subscriptions, MonitoredItems und ihre Ereignishandler bleiben erhalten. Sie müssen nach einem Abbruch nichts neu abonnieren.

Verbindungsereignisse im Überblick ​

Beteiligte TypenOpcClientOpcClientState

Ob die Verbindung zum Server besteht, ermittelt der Client über die KeepAlive-Logik und über die Ergebnisse seiner Service-Aufrufe. Wenn ein Aufruf wie ReadNode als Ganzes mit einem Status fehlschlägt, der auf eine verlorene Verbindung hinweist (zum Beispiel BadConnectionClosed oder BadSessionIdInvalid), ändert der Client seinen Zustand sofort und wartet nicht auf das nächste KeepAlive. Andere Fehler einzelner Aufrufe und ein schlechter Status einzelner Werte im Ergebnis ändern den Zustand nicht. Jede Zustandsänderung meldet der Client über die folgenden Ereignisse der Klasse OpcClient:

EreignisWann der Client es auslöstBedeutung
Connecting, Connectedbeim Aufruf von Connect und nach einem Verbindungsverlust, wenn der Client dabei eine neue Sitzung erstellt hatConnecting meldet den Beginn eines Verbindungsversuchs, Connected seinen erfolgreichen Abschluss.
Reconnecting, Reconnectednach einem Verbindungsverlust, wenn UseBreakDetection aktiviert und ReconnectInterval größer als 0 istNach Reconnecting versucht der Client im Abstand von ReconnectInterval, die Verbindung wiederherzustellen. Reconnected meldet, dass er die bestehende Sitzung weiterverwenden konnte; musste er eine neue Sitzung erstellen, löst er stattdessen Connected aus.
Disconnecting, Disconnectedbeim Aufruf von Disconnect oder implizit von Dispose; außerdem nach einem Verbindungsverlust, wenn ReconnectInterval 0 ist oder MaxReconnectDuration abgelaufen ist (dann nur Disconnected, ohne vorheriges Disconnecting)Die Verbindung ist manuell getrennt, oder der Client hat einen Verbindungsverlust festgestellt, den er nicht selbst behebt.
BreakDetectednach einem Verbindungsverlust, wenn UseBreakDetection aktiviert und ReconnectInterval 0 ist oder MaxReconnectDuration abgelaufen ist, im Anschluss an DisconnectedAnlass, die Verbindung selbst wiederherzustellen (siehe Selbst wiederverbinden).
StateChangedbei jeder Änderung der Eigenschaft State, jeweils vor den Ereignissen obenallgemeine Überwachung aller Zustandswechsel (siehe Zustandswechsel protokollieren)

Wenn UseBreakDetection deaktiviert ist, stellt der Client die Verbindung nicht automatisch wieder her. Solche Situationen muss dann Ihre Anwendung selbst behandeln.

Beim Start verbinden ​

Beteiligte TypenOpcClientOpcException

Das automatische Wiederverbinden greift erst, wenn eine Verbindung einmal bestanden hat. Wenn der Server beim Start der Anwendung nicht erreichbar ist, löst Connect eine OpcException aus, und der Client kehrt in seinen vorherigen Zustand zurück. Ein Client für den Dauerbetrieb wiederholt den Verbindungsaufbau deshalb selbst:

csharp
var client = new OpcClient("opc.tcp://localhost:4840/");
client.ReconnectInterval = 5000;
 
while (true) {
    try {
        client.Connect();
        break;
    }
    catch (OpcException ex) {
        Console.WriteLine("Connect failed: {0}", ex.Code);
        Thread.Sleep(TimeSpan.FromSeconds(5));
    }
}
vbnet
Dim client = New OpcClient("opc.tcp://localhost:4840/")
client.ReconnectInterval = 5000

Do
    Try
        client.Connect()
        Exit Do
    Catch ex As OpcException
        Console.WriteLine("Connect failed: {0}", ex.Code)
        Thread.Sleep(TimeSpan.FromSeconds(5))
    End Try
Loop

Subscriptions einmal anlegen ​

Beteiligte TypenOpcClientOpcSubscriptionOpcDataChangeReceivedEventArgs

Legen Sie Subscriptions einmal nach dem ersten erfolgreichen Connect an. Weil das SDK sie bei einer neuen Sitzung selbst wiederherstellt, würde ein erneutes Abonnieren im Handler des Ereignisses Connected jede Subscription nach einem Abbruch verdoppeln:

csharp
// Subscribe once after Connect: the client recreates the subscription for a new session.
OpcSubscription subscription = client.SubscribeDataChange(
        "ns=2;s=Machine/Speed",
        HandleDataChanged);
...
private static void HandleDataChanged(
        object sender,
        OpcDataChangeReceivedEventArgs e)
{
    Console.WriteLine("Speed: {0}", e.Item.Value);
}
vbnet
'Subscribe once after Connect: the client recreates the subscription for a new session.
Dim subscription As OpcSubscription = client.SubscribeDataChange( _
        "ns=2;s=Machine/Speed", _
        AddressOf HandleDataChanged)
...
Private Shared Sub HandleDataChanged( _
        ByVal sender As Object, _
        ByVal e As OpcDataChangeReceivedEventArgs)
    Console.WriteLine("Speed: {0}", e.Item.Value)
End Sub

Wenn Benachrichtigungen ankommen, die sich nicht dekodieren lassen, meldet das SDK das über das Ereignis NotificationFailed, siehe Benachrichtigungen behandeln.

Zustandswechsel protokollieren ​

Beteiligte TypenOpcClientOpcClientStateOpcClientStateChangedEventArgs

Für Betrieb und Fehlersuche ist es hilfreich, jeden Zustandswechsel zu protokollieren. Das Ereignis StateChanged tritt vor dem jeweiligen Einzelereignis auf und nennt alten und neuen Zustand. Der Wechsel von Reconnecting nach Connected zeigt an, dass der Client eine neue Sitzung erstellt hat:

csharp
client.StateChanged += HandleStateChanged;
...
private static void HandleStateChanged(
        object sender,
        OpcClientStateChangedEventArgs e)
{
    Console.WriteLine("{0} -> {1}", e.OldState, e.NewState);
 
    if (e.OldState == OpcClientState.Reconnecting && e.NewState == OpcClientState.Connected)
        Console.WriteLine("New session, the subscriptions have been recreated.");
}
vbnet
AddHandler client.StateChanged, AddressOf HandleStateChanged
...
Private Shared Sub HandleStateChanged( _
        ByVal sender As Object, _
        ByVal e As OpcClientStateChangedEventArgs)
    Console.WriteLine("{0} -> {1}", e.OldState, e.NewState)

    If e.OldState = OpcClientState.Reconnecting AndAlso e.NewState = OpcClientState.Connected Then
        Console.WriteLine("New session, the subscriptions have been recreated.")
    End If
End Sub

Was nach einer neuen Sitzung zu tun bleibt ​

Beteiligte TypenOpcClientOpcNodeIdCollection

Alles, was der Server einer einzelnen Sitzung zuordnet, gilt nach einer neuen Sitzung nicht mehr. Das betrifft vor allem registrierte Nodes (siehe Häufig gelesene Nodes registrieren). Die von RegisterNodes gelieferten Identifier gelten nur in der Sitzung, in der der Client sie registriert hat. Registrieren Sie die Nodes deshalb im Handler des Ereignisses Connected. Das Ereignis tritt beim ersten Verbinden und nach jeder neuen Sitzung auf:

csharp
client.Connected += HandleConnected;
...
private OpcNodeId[] nodeIds = { "ns=2;s=Machine/Speed", "ns=2;s=Machine/Temperature" };
private OpcNodeIdCollection registeredNodeIds;
 
private void HandleConnected(object sender, EventArgs e)
{
    // Registered node ids are only valid in the session they have been registered in.
    this.registeredNodeIds = ((OpcClient)sender).RegisterNodes(this.nodeIds);
}
vbnet
AddHandler client.Connected, AddressOf HandleConnected
...
Private nodeIds As OpcNodeId() = {"ns=2;s=Machine/Speed", "ns=2;s=Machine/Temperature"}
Private registeredNodeIds As OpcNodeIdCollection

Private Sub HandleConnected(ByVal sender As Object, ByVal e As EventArgs)
    'Registered node ids are only valid in the session they have been registered in.
    Me.registeredNodeIds = CType(sender, OpcClient).RegisterNodes(Me.nodeIds)
End Sub

Verwenden Sie für Nodes fremder Namespaces außerdem die Namespace-URI statt eines fest eingetragenen Index, siehe Nodes über die Namespace-URI adressieren. Nach einem Neustart des Servers kann sich die Reihenfolge seiner Namespaces ändern.

Selbst wiederverbinden ​

Beteiligte TypenOpcClient

Wenn ReconnectInterval 0 ist, verbindet sich der Client nicht selbst neu. Er wechselt in den Zustand Disconnected und löst das Ereignis BreakDetected aus. Die Anwendung entscheidet dann selbst, wann und wie sie die Verbindung wieder aufbaut, zum Beispiel nach einer eigenen Wartezeit:

csharp
client.ReconnectInterval = 0;
client.BreakDetected += HandleBreakDetected;
...
private static void HandleBreakDetected(object sender, EventArgs e)
{
    OpcClient client = (OpcClient)sender;
 
    // Decide on your own when and how to connect again.
    Task.Run(() => {
        Thread.Sleep(TimeSpan.FromSeconds(10));
        client.Connect();
    });
}
vbnet
client.ReconnectInterval = 0
AddHandler client.BreakDetected, AddressOf HandleBreakDetected
...
Private Shared Sub HandleBreakDetected(ByVal sender As Object, ByVal e As EventArgs)
    Dim client As OpcClient = CType(sender, OpcClient)

    'Decide on your own when and how to connect again.
    Task.Run( _
            Sub()
                Thread.Sleep(TimeSpan.FromSeconds(10))
                client.Connect()
            End Sub)
End Sub

Aus mehreren Threads zugreifen ​

Beteiligte TypenOpcClientOpcSubscriptionOpcTaskSchedule

Eine Instanz von OpcClient können Sie aus mehreren Threads verwenden. Ihre Service-Aufrufe führt der Client dabei nacheinander aus: Jeder Aufruf sperrt den Client, bis die Antwort des Servers vorliegt oder die Zeit abgelaufen ist. Das betrifft die Services Read, Write, Browse, BrowseNext, Call und TranslateBrowsePathsToNodeIds, den Zugriff auf historische Daten, das Hinzufügen und Löschen von Nodes und Referenzen sowie das Registrieren von Nodes. Ein langsamer Aufruf hält damit alle anderen Aufrufe desselben Clients auf. Connect und Disconnect verwenden dieselbe Sperre.

Die Methode BrowseNodes läuft dagegen ohne diese Sperre. Sie liest über den Node-Cache des Stacks, und dieser Cache sperrt gleichzeitige Zugriffe nicht. Wir empfehlen deshalb, BrowseNodes eines Clients nicht aus mehreren Threads gleichzeitig aufzurufen.

Subscriptions arbeiten parallel zu den Service-Aufrufen. Ihre Benachrichtigungen verteilt das SDK standardmäßig auf Threads des .NET-Thread-Pools; Einzelheiten beschreibt die Seite Abläufe im SDK.

Solange der Client die Verbindung wiederherstellt, scheitern neue Aufrufe sofort mit dem Status BadServerNotConnected. Aufrufe, die zu diesem Zeitpunkt schon auf eine Antwort warten, enden mit BadConnectionClosed oder BadRequestInterrupted. Behandeln Sie diese Fälle wie andere fehlgeschlagene Aufrufe (siehe Fehler behandeln).

Checkliste ​

ThemaEmpfehlung
StartConnect in einer Schleife wiederholen, bis der Server erreichbar ist.
AbbrucherkennungUseBreakDetection aktiviert lassen, KeepAlive.Interval und ReconnectInterval an die Anlage anpassen, bei Bedarf MaxReconnectDuration begrenzen.
Subscriptionseinmal nach dem ersten Connect anlegen, nicht im Handler von Connected.
Registrierte Nodesim Handler von Connected registrieren.
ThreadsService-Aufrufe eines Clients laufen nacheinander; BrowseNodes eines Clients nicht aus mehreren Threads gleichzeitig aufrufen.
NodeIdsNamespaces über die URI adressieren (nsu=).
Fehlerhafte BenachrichtigungenNotificationFailed behandeln.
Fehler einzelner OperationenStatus auswerten, siehe Fehler behandeln.