# Alarme und Bedingungen

> Anleitung: Ereignis-Nodes mit Bedingungen: Dialog- und Feedback-Bedingungen, Alarm-Bedingungen bis zu Grenzwert-Alarmen.

**Beispielanwendungen:**

- Client.AE ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Basic/Client.AE), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Basic/Client.AE)): Abonniert die Ereignisse einer Maschine, gefiltert nach Schweregrad, quittiert Alarme und beantwortet Dialoge an der Konsole.
- Server.AE ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Basic/Server.AE), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Basic/Server.AE)): Stellt eine Maschine mit Dialog-Bedingung, Grenzwert-Alarm und Temperatur-Alarm bereit und ändert deren Zustände laufend.

Ereignis-Nodes mit Bedingungen melden Ereignisse, die an den Zustand einer Bedingung geknüpft sind. Diese Seite zeigt, wie ein Client die Ereignisdaten der einzelnen Bedingungsarten verarbeitet, von einfachen Bedingungen bis zu Grenzwert-Alarmen, und wie er die Bedingungen eines OPC AE Servers (Alarms & Events) empfängt.

## Bedingungen

Beteiligte Typen: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcEvent`, `OpcCondition`, `OpcEventSeverity`

Die Klasse `OpcCondition` ist eine Spezialisierung der Klasse `OpcEvent`, die die Seite [Ereignisse](https://www.traeger.de/docs/development/opcua/client/events) vorstellt. Sie stellt Ereignisdaten bereit, die an bestimmte Bedingungen geknüpft sind. Der Server löst ein solches Ereignis nur aus, wenn die Bedingung zutrifft, die dem Ereignis-Node zugeordnet ist. Die Ereignisdaten enthalten Informationen über den Zustand der Bedingung und Informationen, die an die Auswertung der Bedingung geknüpft sind. Diese Informationen können je nach Anwendungsfall unterschiedlich komplex sein. Deshalb ist `OpcCondition` die Basisklasse aller Ereignisdaten, an die eine Bedingung geknüpft ist.

Neben den allgemeinen Eigenschaften eines Ereignisses aus der Basisklasse `OpcEvent` liefert eine Instanz von `OpcCondition` weitere Informationen, die für die Verarbeitung im Client wichtig sein können. Die Eigenschaft `IsRetained` zeigt, ob der Server das Ereignis allgemein als relevant für Clients beurteilt. Die Eigenschaft `IsEnabled` zeigt, ob die Bedingung aktiviert oder deaktiviert ist. Ob ein Alarm aktiv ist, zeigt dagegen die Eigenschaft `IsActive` der Alarm-Bedingungen (Abschnitt [Alarm-Bedingungen](https://www.traeger.de/docs/development/opcua/client/alarms-conditions#alarm-bedingungen)).

```csharp
private static void HandleLocalEvents(
        object sender,
        OpcEventReceivedEventArgs e)
{
    var condition = e.Event as OpcCondition;
 
    if (condition != null && condition.IsRetained) {
        Console.Write((condition.ClientUserId ?? "Comment") + ":");
        Console.WriteLine(condition.Comment);
    }
}
```

```vbnet
Private Shared Sub HandleLocalEvents( _
        ByVal sender As Object, _
        ByVal e As OpcEventReceivedEventArgs)
    Dim condition = TryCast(e.Event, OpcCondition)

    If condition IsNot Nothing AndAlso condition.IsRetained Then
        Console.Write(If(condition.ClientUserId, "Comment") & ":")
        Console.WriteLine(condition.Comment)
    End If
End Sub

```

Wenn der Client die Bedingung nicht weiter auswerten soll, deaktiviert er sie:

```csharp
private static void HandleLocalEvents(object sender, OpcEventReceivedEventArgs e)
{
    var condition = e.Event as OpcCondition;
 
    if (condition.IsEnabled && condition.Severity < OpcEventSeverity.Medium)
        condition.Disable(client);
}

```

```vbnet
Private Shared Sub HandleLocalEvents( _
        ByVal sender As Object, _
        ByVal e As OpcEventReceivedEventArgs)
    Dim condition = TryCast(e.Event, OpcCondition)

    If condition.IsEnabled AndAlso condition.Severity < OpcEventSeverity.Medium Then
        condition.Disable(client)
    End If
End Sub

```

Zusätzlich kann der Client einen Kommentar zum aktuellen Zustand der Bedingung hinzufügen:

```csharp
private static void HandleLocalEvents(object sender, OpcEventReceivedEventArgs e)
{
    var condition = e.Event as OpcCondition;
 
    if (condition != null)
        condition.AddComment(client, "Evaluated by me!");
}

```

```vbnet
Private Shared Sub HandleLocalEvents( _
        ByVal sender As Object, _
        ByVal e As OpcEventReceivedEventArgs)
    Dim condition = TryCast(e.Event, OpcCondition)

    If condition IsNot Nothing Then
        condition.AddComment(client, "Evaluated by me!")
    End If
End Sub

```

## Dialog-Bedingungen

Beteiligte Typen: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcCondition`, `OpcDialogCondition`, `OpcEventSeverity`, `OpcDialogRequestedEventArgs`

Die Klasse `OpcDialogCondition` ist eine Spezialisierung von `OpcCondition`. Die Bedingung dieses Ereignisses ist ein Dialog mit den Abonnenten. Eine solche Bedingung besteht aus einer Meldung (engl. Prompt) und Antwortoptionen (engl. Response Options). Dazu kommen drei Angaben: Die Eigenschaft `DefaultResponse` nennt die Option, die standardmäßig ausgewählt sein sollte. Die Eigenschaft `OkResponse` nennt die Option zur Bestätigung des Dialogs, die Eigenschaft `CancelResponse` die Option zum Abbruch. Wenn der Server ein solches Ereignis auslöst, wartet er, bis einer der Abonnenten mit einer der vorgegebenen Antwortoptionen antwortet. Die Operationen, die an den Dialog geknüpft sind, setzen also eine Antwort voraus: auf eine Aufgabenstellung, eine Frage, eine Information oder eine Warnung. So verarbeitet der Client die Informationen des Ereignisses und meldet die Antwort an den Server zurück:

```csharp
private void HandleLocalEvents(
        object sender,
        OpcEventReceivedEventArgs e)
{
    var condition = e.Event as OpcDialogCondition;
 
    if (condition != null && condition.IsActive) {
        Console.WriteLine(condition.Prompt);
        Console.WriteLine("    Options:");
 
        var responseOptions = condition.ResponseOptions;
 
        for (int index = 0; index < responseOptions.Length; index++) {
            Console.Write("      [{0}] = {1}", index, responseOptions[index].Value);
 
            if (index == condition.DefaultResponse)
                Console.Write(" (default)");
 
            Console.WriteLine();
        }
 
        var respond = string.Empty;
        var respondOption = condition.DefaultResponse;
 
        do {
            Console.Write("Enter the number of the option and press Enter to respond: ");
            respond = Console.ReadLine();
 
            if (string.IsNullOrEmpty(respond))
                break;
        } while (!int.TryParse(respond, out respondOption));
 
        condition.Respond(client, respondOption);
 
        // The dialog has been answered, the DialogRequested event is not raised.
        e.Handled = true;
    }
}

```

```vbnet
Private Sub HandleLocalEvents( _
        ByVal sender As Object, _
        ByVal e As OpcEventReceivedEventArgs)
    Dim condition = TryCast(e.Event, OpcDialogCondition)

    If condition IsNot Nothing AndAlso condition.IsActive Then
        Console.WriteLine(condition.Prompt)
        Console.WriteLine("    Options:")

        Dim responseOptions = condition.ResponseOptions

        For index As Integer = 0 To responseOptions.Length - 1
            Console.Write("      [{0}] = {1}", index, responseOptions(index).Value)

            If index = condition.DefaultResponse Then
                Console.Write(" (default)")
            End If

            Console.WriteLine()
        Next

        Dim respond = String.Empty
        Dim respondOption = condition.DefaultResponse

        Do
            Console.Write("Enter the number of the option and press Enter to respond: ")
            respond = Console.ReadLine()

            If String.IsNullOrEmpty(respond) Then
                Exit Do
            End If
        Loop While Not Integer.TryParse(respond, respondOption)

        condition.Respond(client, respondOption)

        'The dialog has been answered, the DialogRequested event is not raised.
        e.Handled = True
    End If
End Sub
```

Neben dem Standardverfahren zur Ereignisbehandlung bietet die Klasse `OpcClient` das Ereignis `DialogRequested`. Wenn der Client Ereignisdaten vom Typ `OpcDialogCondition` empfängt und kein Handler die Eigenschaft `Handled` der Ereignisargumente auf `true` gesetzt hat, löst der Client das Ereignis `DialogRequested` aus. Darüber verarbeiten Sie den Dialog. Wenn ein Handler den Dialog bereits selbst beantwortet, setzen Sie deshalb wie im vorherigen Beispiel die Eigenschaft `Handled`. Andernfalls beantwortet der Client den Dialog ein zweites Mal. Mit `DialogRequested` ist die Handhabung der Ereignisdaten etwas einfacher, weil der Ereignishandler sie typisiert erhält:

```csharp
client.DialogRequested += HandleDialogRequested;
...
private static void HandleDialogRequested(
        object sender,
        OpcDialogRequestedEventArgs e)
{
    // Just use the default response, here.
    e.SelectedResponse = e.Dialog.DefaultResponse;
}
```

```vbnet
AddHandler client.DialogRequested, AddressOf HandleDialogRequested
...
Private Shared Sub HandleDialogRequested( _
        ByVal sender As Object, _
        ByVal e As OpcDialogRequestedEventArgs)
    'Just use the default response, here.
    e.SelectedResponse = e.Dialog.DefaultResponse
End Sub

```

Setzen Sie nur die Eigenschaft `SelectedResponse` der Ereignisargumente. Die Methode `Respond` der Klasse `OpcDialogCondition` ruft `OpcClient` nach dem Ereignishandler selbst auf. Wenn `SelectedResponse` beim Standardwert `-1` bleibt oder außerhalb der Antwortoptionen liegt, sendet der Client keine Antwort.

## Feedback-Bedingungen

Beteiligte Typen: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcEvent`, `OpcCondition`, `OpcAcknowledgeableCondition`, `OpcEventSeverity`

Die Klasse `OpcAcknowledgeableCondition` ist eine Spezialisierung von `OpcCondition`. Sie ist die Basisklasse für Bedingungen, die ein Feedback anfordern. Ein solches Ereignis funktioniert wie eine Meldung mit Rückschein: Wenn seine Bedingung erfüllt ist, sendet der Server die Meldung und fordert ein Feedback an. Der Rückschein, also das Feedback, kann weitere Abläufe steuern oder Hinweise und Warnungen einfach quittieren. Die OPC-UA-Spezifikation (Teil 9) sieht dafür zwei Arten von Feedback vor. Die Quittierung (engl. Acknowledge) bestätigt, dass die Meldung zur Kenntnis genommen wurde. Die Bestätigung (engl. Confirm) bestätigt im Anschluss, dass die Ursache der Meldung behoben ist. Diese zweite Stufe ist optional, nicht jeder Server verwendet sie. Für beide Arten stellen die Ereignisse die Methoden `Acknowledge` und `Confirm` bereit. Die beiden Zustände sind unabhängig voneinander: Eine Quittierung setzt nicht zugleich die Bestätigung und umgekehrt. Optional geben Sie bei der Quittierung und bei der Bestätigung einen Kommentar der bearbeitenden Person an. So implementieren Sie eine Quittierung als Feedback:

```csharp
private static void HandleLocalEvents(object sender, OpcEventReceivedEventArgs e)
{
    var condition = e.Event as OpcAcknowledgeableCondition;
 
    if (condition != null && !condition.IsAcked) {
        Console.WriteLine($"Acknowledgment is required for condtion: {condition.ConditionName}");
        Console.WriteLine($"  -> {condition.Message}");
        Console.Write("Enter your acknowlegment comment and press Enter to acknowledge: ");
 
        var comment = Console.ReadLine();
        condition.Acknowledge(client, comment);
    }
}

```

```vbnet
Private Shared Sub HandleLocalEvents( _
        ByVal sender As Object, _
        ByVal e As OpcEventReceivedEventArgs)
    Dim condition = TryCast(e.Event, OpcAcknowledgeableCondition)

    If condition IsNot Nothing AndAlso Not condition.IsAcked Then
        Console.WriteLine("Acknowledgment is required for condtion: {0}", condition.ConditionName)
        Console.WriteLine("  -> {0}", condition.Message)
        Console.Write("Enter your acknowlegment comment and press Enter to acknowledge: ")

        Dim comment = Console.ReadLine()
        condition.Acknowledge(client, comment)
    End If
End Sub

```

Bei der Verarbeitung solcher Ereignisse prüft der Client zusätzlich, ob das Ereignis bereits bestätigt (Eigenschaft `IsConfirmed`) oder quittiert (Eigenschaft `IsAcked`) wurde. Der Server definiert die Interpretation des Feedbacks und die darauf folgende Logik stets selbst. Ob ein Server beide Feedback-Arten nutzt oder nur eine, entscheidet also das Team, das ihn entwickelt. Im besten Fall unterstützt ein Server mindestens die Methode `Acknowledge`, da die Spezifikation die Bestätigung als optionale zweite Stufe nach der Quittierung vorsieht.

## Alarm-Bedingungen

Beteiligte Typen: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcAlarmCondition`, `OpcAcknowledgeableCondition`, `OpcEventSeverity`

Die wohl wichtigste Implementierung von `OpcAcknowledgeableCondition` in OPC UA ist die Klasse `OpcAlarmCondition`. Ereignisse dieser Klasse verhalten sich wie ein Nachttischwecker. Ein solches Ereignis wird aktiv (Eigenschaft `IsActive`), wenn die verknüpfte Bedingung erfüllt ist. Beim Wecker ist das zum Beispiel das Erreichen der Weckzeit. Ein Alarm, der zwar eine Weckzeit hat, aber beim Erreichen nicht aktiv werden soll, heißt unterdrückter Alarm (engl. suppressed alarm, Eigenschaften `IsSuppressed` und `IsSuppressedOrShelved`). Wenn ein Alarm aktiv wird, kann der Server ihn zurückstellen (engl. shelved): einmalig („One Shot Shelving“) oder zeitlich („Timed Shelving“). Ob ein Alarm unterdrückt oder zurückgestellt ist, zeigt die Eigenschaft `IsSuppressedOrShelved`. Wie lange ein Alarm höchstens zurückgestellt sein darf, zeigt die Eigenschaft `MaxTimeShelved`.

```csharp
private static void HandleLocalEvents(
        object sender,
        OpcEventReceivedEventArgs e)
{
    var alarm = e.Event as OpcAlarmCondition;
 
    if (alarm != null) {
        Console.Write($"Alarm {alarm.ConditionName} is");
        Console.WriteLine($"{(alarm.IsActive ? "active" : "inactive")}!");
    }
}

```

```vbnet
Private Shared Sub HandleLocalEvents( _
        ByVal sender As Object, _
        ByVal e As OpcEventReceivedEventArgs)
    Dim alarm = TryCast(e.Event, OpcAlarmCondition)

    If alarm IsNot Nothing Then
        Console.Write("Alarm {0} is", alarm.ConditionName)
        Console.WriteLine("{0}!", If(alarm.IsActive, "active", "inactive"))
    End If
End Sub

```

## Diskrete Alarme

Beteiligte Typen: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcDiscreteAlarm`, `OpcOffNormalAlarm`, `OpcTripAlarm`

Für die Ereignisdaten von `OpcAlarmCondition` gibt es mehrere Spezialisierungen für bestimmte Arten von Alarmen. Bei ihnen beschreibt bereits die Art des Alarms seine Form, seinen Grund oder seinen Inhalt genauer. Eine Unterklasse solcher selbstbeschreibenden Alarme sind die diskreten Alarme. Ihre Basis ist die Klasse `OpcDiscreteAlarm`. Sie steht für Alarme, deren Eingang nur eine bestimmte Anzahl möglicher Werte annehmen kann, zum Beispiel wahr und falsch oder läuft, angehalten und beendet. Wenn ein Alarm einen diskreten Zustand darstellt, der als nicht normal gilt, beschreibt ihn die Klasse `OpcOffNormalAlarm` oder eine ihrer Unterklassen. Auf den Zustand, der als normal gilt, verweist die Eigenschaft `NormalStateId`. Eine weitere Konkretisierung dieser Alarmklasse bietet das SDK mit der Klasse `OpcTripAlarm`. Dieser Alarm wird aktiv, wenn an einem überwachten Gerät ein anomaler Fehler auftritt, zum Beispiel wenn der Motor wegen Überlastung abschaltet.

```csharp
private static void HandleLocalEvents(object sender, OpcEventReceivedEventArgs e)
{
    var alarm = e.Event as OpcDiscreteAlarm;
 
    if (alarm != null) {
        if (alarm is OpcTripAlarm)
            Console.WriteLine("Trip Alarm!");
        else if (alarm is OpcOffNormalAlarm)
            Console.WriteLine("Off Normal Alarm!");
    }
}

```

```vbnet
Private Shared Sub HandleLocalEvents( _
        ByVal sender As Object, _
        ByVal e As OpcEventReceivedEventArgs)
    Dim alarm = TryCast(e.Event, OpcDiscreteAlarm)

    If alarm IsNot Nothing Then
        If TypeOf alarm Is OpcTripAlarm Then
            Console.WriteLine("Trip Alarm!")
        ElseIf TypeOf alarm Is OpcOffNormalAlarm Then
            Console.WriteLine("Off Normal Alarm!")
        End If
    End If
End Sub

```

## Grenzwert-Alarme

Beteiligte Typen: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcLimitAlarm`

Wenn der Server prozessspezifische Grenzwerte prüft und das Ergebnis bei Über- oder Unterschreitung veröffentlicht, ist die Klasse `OpcLimitAlarm` der zentrale Einstieg in die Klassen der Grenzwert-Alarme (engl. limit alarms). Diese Klasse unterteilt Grenzwerte in bis zu vier Stufen. In der Reihenfolge ihrer metrischen Ordnung heißen sie `LowLow`, `Low`, `High` und `HighHigh`. Laut Definition muss der Server nicht alle Grenzwerte definieren.

```csharp
private static void HandleLocalEvents(
        object sender,
        OpcEventReceivedEventArgs e)
{
    var alarm = e.Event as OpcLimitAlarm;
 
    if (alarm != null) {
        Console.Write(alarm.LowLowLimit);
        Console.Write(" ≤ ");
        Console.Write(alarm.LowLimit);
        Console.Write(" ≤ ");
        Console.Write(alarm.HighLimit);
        Console.Write(" ≤ ");
        Console.Write(alarm.HighHighLimit);
    }
}

```

```vbnet
Private Shared Sub HandleLocalEvents( _
        ByVal sender As Object, _
        ByVal e As OpcEventReceivedEventArgs)
    Dim alarm = TryCast(e.Event, OpcLimitAlarm)

    If alarm IsNot Nothing Then
        Console.Write(alarm.LowLowLimit)
        Console.Write(" ≤ ")
        Console.Write(alarm.LowLimit)
        Console.Write(" ≤ ")
        Console.Write(alarm.HighLimit)
        Console.Write(" ≤ ")
        Console.Write(alarm.HighHighLimit)
    End If
End Sub

```

## Ausschließliche Grenzwert-Alarme

Beteiligte Typen: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcLimitAlarm`, `OpcExclusiveLimitAlarm`, `OpcExclusiveDeviationAlarm`, `OpcExclusiveLevelAlarm`, `OpcExclusiveRateOfChangeAlarm`

Die Klasse `OpcExclusiveLimitAlarm` ist eine Unterklasse von `OpcLimitAlarm`. Wie ihr Name sagt, definiert sie Grenzwert-Alarme für ausschließliche Grenzen. Die Grenzwerte eines solchen Alarms schließen sich gegenseitig aus. Wenn ein Grenzwert über- oder unterschritten ist, kann also nicht zugleich ein anderer Grenzwert über- oder unterschritten sein.

In OPC UA gibt es drei weitere Spezialisierungen der Klasse `OpcExclusiveLimitAlarm`.

`OpcExclusiveDeviationAlarm`

Dieser Alarm meldet, dass der Wert um mehr als die Grenzwerte von einem Sollwert abweicht. Auf den Node des Sollwerts verweist die Eigenschaft `SetpointNodeId`.

`OpcExclusiveLevelAlarm`

Dieser Alarm meldet eine Grenzwertüberschreitung, typischerweise an einem Instrument wie einem Temperatursensor. Er wird aktiv, wenn der beobachtete Wert über einem oberen oder unter einem unteren Grenzwert liegt.

`OpcExclusiveRateOfChangeAlarm`

Dieser Alarm meldet eine ungewöhnliche oder fehlende Änderung eines gemessenen Werts, bezogen auf die Geschwindigkeit der Änderung. Er wird aktiv, wenn die Änderungsrate des Werts einen definierten Grenzwert über- oder unterschreitet.

## Nicht-ausschließliche Grenzwert-Alarme

Beteiligte Typen: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcLimitAlarm`, `OpcNonExclusiveLimitAlarm`, `OpcNonExclusiveDeviationAlarm`, `OpcNonExclusiveLevelAlarm`, `OpcNonExclusiveRateOfChangeAlarm`

Die Klasse `OpcNonExclusiveLimitAlarm` ist eine Unterklasse von `OpcLimitAlarm`. Wie ihr Name sagt, definiert sie Grenzwert-Alarme für nicht-ausschließliche Grenzen. Die Grenzwerte eines solchen Alarms schließen sich gegenseitig nicht aus. Wenn ein Grenzwert über- oder unterschritten ist, kann also zugleich ein anderer Grenzwert über- oder unterschritten sein. Die verletzten Grenzen prüfen Sie mit den Eigenschaften `IsLowLow`, `IsLow`, `IsHigh` und `IsHighHigh` der Ereignisdaten.

In OPC UA gibt es drei weitere Spezialisierungen der Klasse `OpcNonExclusiveLimitAlarm`.

`OpcNonExclusiveDeviationAlarm`

Dieser Alarm meldet, dass der Wert um mehr als die Grenzwerte von einem Sollwert abweicht. Auf den Node des Sollwerts verweist die Eigenschaft `SetpointNodeId`.

`OpcNonExclusiveLevelAlarm`

Dieser Alarm meldet eine Grenzwertüberschreitung, typischerweise an einem Instrument wie einem Temperatursensor. Er wird aktiv, wenn der beobachtete Wert über einem oberen oder unter einem unteren Grenzwert liegt.

`OpcNonExclusiveRateOfChangeAlarm`

Dieser Alarm meldet eine ungewöhnliche oder fehlende Änderung eines gemessenen Werts, bezogen auf die Geschwindigkeit der Änderung. Er wird aktiv, wenn die Änderungsrate des Werts einen definierten Grenzwert über- oder unterschreitet.

## Bedingungen eines OPC AE Servers

Ereignisse und Bedingungen eines OPC AE Servers empfängt und quittiert der Client mit denselben Methoden. Den Ablauf beschreibt der Abschnitt [Alarme und Ereignisse (AE)](https://www.traeger.de/docs/development/opcua/client/opc-classic#alarme-und-ereignisse-ae) auf der Seite OPC Classic.
