# Alarme und Bedingungen

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

**Beispielanwendungen:**

- 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.
- 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.

Ereignis-Nodes mit Bedingungen melden Ereignisse, die an den Zustand einer Bedingung geknüpft sind. Diese Seite zeigt, wie ein Server Ereignis-Nodes für die einzelnen Bedingungsarten bereitstellt, von einfachen Bedingungen bis zu Grenzwert-Alarmen.

## Bedingungen

Beteiligte Typen: `OpcServer`, `OpcNodeManager`, `OpcConditionNode`, `OpcConditionRetainPolicy`

Eine Spezialisierung von `OpcEventNode` ist die Klasse `OpcConditionNode`. Den `OpcEventNode` stellt der Abschnitt [Ereignisse an einem Node bereitstellen](https://www.traeger.de/docs/development/opcua/server/events#ereignisse-an-einem-node-bereitstellen) vor. Mit `OpcConditionNode` definieren Sie Ereignisse, an die bestimmte Bedingungen geknüpft sind. Ein solches Ereignis sollte nur ausgelöst werden, wenn die Bedingung des Ereignis-Nodes zutrifft. Zu den Informationen des Nodes gehören auch der Zustand dieser Bedingung und Informationen, die an die Auswertung der Bedingung geknüpft sind.

Da diese Informationen je nach Szenario unterschiedlich komplex sein können, ist `OpcConditionNode` die Basisklasse aller Ereignis-Nodes, an die eine Bedingung geknüpft ist. Sie erzeugen einen solchen Node wie einen `OpcEventNode`. Die folgenden Abschnitte zeigen deshalb nur die zusätzlichen Eigenschaften.

Ob eine Bedingung für einen Client von Interesse ist und bei einem ConditionRefresh gemeldet wird, gibt die Eigenschaft `IsRetained` an. Das OPC UA .NET SDK führt sie standardmäßig automatisch anhand der Zustände der Bedingung nach. Nach welcher Regel, legt die Eigenschaft `IsRetainedPolicy` fest (Enumeration `OpcConditionRetainPolicy`):

- `EnabledOnly` (Standard von `OpcConditionNode`): gesetzt, solange die Bedingung aktiviert ist.
- `ActiveOrUnacknowledged` (Standard von `OpcAcknowledgeableConditionNode` und `OpcAlarmConditionNode`): gesetzt, solange der Alarm aktiv oder noch nicht quittiert ist.
- `ActiveOrUnconfirmed`: gesetzt, solange der Alarm aktiv oder noch nicht bestätigt ist.
- `ActiveOnly`: gesetzt, solange der Alarm aktiv ist, unabhängig von der Quittierung.
- `Manual`: Das SDK ändert die Eigenschaft nicht; die Anwendung setzt sie selbst.

Unabhängig von der Regel ist `IsRetained` bei einer deaktivierten Bedingung nie gesetzt. Ein manuell gesetzter Wert der Eigenschaft `IsRetained` bleibt deshalb nur mit der Regel `Manual` erhalten:

```csharp
var maintenanceEvent = new OpcConditionNode(machineOne, "Maintenance");
 
// Default: EnabledOnly for an OpcConditionNode,
// ActiveOrUnacknowledged for acknowledgeable conditions and alarms.
maintenanceEvent.IsRetainedPolicy = OpcConditionRetainPolicy.Manual;
 
// Interesting for a client yes or no (only kept in case of the Manual policy).
maintenanceEvent.IsRetained = true;
 
// Status of the source the condition is based upon.
maintenanceEvent.Quality = OpcStatusCode.Good;
```

```vbnet
Dim maintenanceEvent = New OpcConditionNode(machineOne, "Maintenance")

'Default: EnabledOnly for an OpcConditionNode,
'ActiveOrUnacknowledged for acknowledgeable conditions and alarms.
maintenanceEvent.IsRetainedPolicy = OpcConditionRetainPolicy.Manual

'Interesting for a client yes or no (only kept in case of the Manual policy).
maintenanceEvent.IsRetained = True

'Status of the source the condition is based upon.
maintenanceEvent.Quality = OpcStatusCode.Good
```

Mit der Methode `AddComment` und dem gleichnamigen Kind-Node kann ein Client die Eigenschaft `Comment` des Nodes ändern. Auf den Aufruf reagiert der Server über die Eigenschaft `AddCommentCallback`. Das Ergebnis der Änderung werten Sie über die folgenden Eigenschaften aus:

```csharp
// Identifier of the user who supplied the Comment
maintenanceEvent.ClientUserId = ...;

// Last comment provided by a user
maintenanceEvent.Comment = ...;

```

```vbnet
' Identifier of the user who supplied the Comment
maintenanceEvent.ClientUserId = ...

' Last comment provided by a user
maintenanceEvent.Comment = ...

```

Serverseitig ändern Sie die Zustände einer Bedingung mit den Methoden, deren Name mit `Change` beginnt, zum Beispiel `ChangeIsEnabled` oder `ChangeComment`. Diese Methoden stehen erst zur Verfügung, wenn der Node im Adressraum angelegt ist. Vorher lösen sie eine `InvalidOperationException` aus. Wenn die Eigenschaft `AutoReportChanges` gesetzt ist (standardmäßig nicht), meldet der Node nach jeder dieser Änderungen automatisch ein Ereignis:

```csharp
// Use the Change methods once the node has been added to the address space.
maintenanceEvent.ChangeIsEnabled(this.SystemContext, value: false);
maintenanceEvent.ChangeComment(this.SystemContext, value: "Planned maintenance");
 
// Reports an event whenever one of the Change methods completes.
maintenanceEvent.AutoReportChanges = true;
```

```vbnet
'Use the Change methods once the node has been added to the address space.
maintenanceEvent.ChangeIsEnabled(Me.SystemContext, value:=False)
maintenanceEvent.ChangeComment(Me.SystemContext, value:="Planned maintenance")

'Reports an event whenever one of the Change methods completes.
maintenanceEvent.AutoReportChanges = True
```

Wenn derselbe Ereignis-Node mehrgleisig bearbeitbar sein soll, können Sie einen neuen Ereigniszweig (engl. Event Branch) eröffnen. Dazu dient die Methode `CreateBranch` des Nodes. Den eindeutigen Schlüssel des Zweigs speichert der Node in der Eigenschaft `BranchId`. Der folgende Code zeigt die wichtigsten Bestandteile von `OpcConditionNode` für Zweige:

```csharp
// Uses a new GUID as BranchId.
var maintenanceBranchA = maintenanceEvent.CreateBranch(this.SystemContext);
 
// Uses a custom NodeId as BranchId.
var maintenanceBranchB = maintenanceEvent.CreateBranch(
        this.SystemContext,
        branchId: new OpcNodeId(10001));
```

```vbnet
'Uses a new GUID as BranchId.
Dim maintenanceBranchA = maintenanceEvent.CreateBranch(Me.SystemContext)

'Uses a custom NodeId as BranchId.
Dim maintenanceBranchB = maintenanceEvent.CreateBranch( _
        Me.SystemContext, _
        branchId:=New OpcNodeId(10001))
```

Die Eigenschaft `BranchId` identifiziert den Zweig, zu dem ein Ereignis gehört. Ein Null-Verweis steht für den aktuellen Zustand. Die Eigenschaft `LastSeverity` liefert den vorherigen Schweregrad des Zweigs.

## Dialog-Bedingungen

Beteiligte Typen: `OpcServer`, `OpcNodeManager`, `OpcDialogConditionNode`

Eine Spezialisierung von `OpcConditionNode` ist die Klasse `OpcDialogConditionNode`. Die Bedingung dieses Nodes ist ein Dialog mit den Abonnenten. Eine solche Bedingung besteht aus einer Meldung (engl. Prompt) und Antwortoptionen (engl. Response Options). Dazu kommen Informationen, welche Option standardmäßig ausgewählt ist (Eigenschaft `DefaultResponse`), welche Option den Dialog bestätigt (Eigenschaft `OkResponse`) und welche ihn abbricht (Eigenschaft `CancelResponse`). Wenn der Server ein solches Dialog-Ereignis auslöst, wartet er darauf, dass einer der Abonnenten antwortet. Die Antwort ist die Auswahl aus den vorgegebenen Antwortoptionen.

Die Bedingung für die weitere Verarbeitung der Operationen, die an den Dialog geknüpft sind, ist also die Antwort auf eine Aufgabenstellung, eine Frage, eine Information oder eine Warnung. Wenn Sie den Node wie gewohnt erstellt haben, legen Sie die Eigenschaften je nach Szenario fest:

```csharp
var outOfMaterial = new OpcDialogConditionNode(machineOne, "MaterialAlert");

outOfMaterial.Message = "Out of Material"; // Generic event message
outOfMaterial.Prompt = "The machine is out of material. Refill material supply to continue.";
outOfMaterial.ResponseOptions = new OpcText[] { "Continue", "Cancel" };
outOfMaterial.DefaultResponse = 0; // Index of ResponseOption to use
outOfMaterial.CancelResponse = 1;  // Index of ResponseOption to use
outOfMaterial.OkResponse = 0;      // Index of ResponseOption to use

```

```vbnet
Dim outOfMaterial As New OpcDialogConditionNode(machineOne, "MaterialAlert")

outOfMaterial.Message = "Out of Material" ' Generic event message
outOfMaterial.Prompt = "The machine is out of material. Refill material supply to continue."
outOfMaterial.ResponseOptions = New OpcText() {"Continue", "Cancel"}
outOfMaterial.DefaultResponse = 0 ' Index of ResponseOption to use
outOfMaterial.CancelResponse = 1 ' Index of ResponseOption to use
outOfMaterial.OkResponse = 0 ' Index of ResponseOption to use

```

Damit die Abonnenten den Dialog beantworten können, aktivieren Sie ihn mit der Methode `ActivateDialog`, sobald der Node im Adressraum angelegt ist. Die Antwort eines Abonnenten behandeln Sie im `RespondCallback` des Nodes. Die Methode `RespondDialog` übernimmt die Antwort und deaktiviert den Dialog wieder. Die zuletzt übernommene Antwort liefert die Eigenschaft `LastResponse`:

```csharp
outOfMaterial.RespondCallback = this.HandleOutOfMaterialResponse;
outOfMaterial.ActivateDialog(this.SystemContext);
...
private OpcStatusCode HandleOutOfMaterialResponse(
        OpcNodeContext<OpcDialogConditionNode> context,
        int selectedResponse)
{
    // Handle the response.
    if (context.Node.OkResponse == selectedResponse)
        ContinueJob();
    else
        CancelJob();
 
    // Apply the response.
    context.Node.RespondDialog(context, selectedResponse);
 
    return OpcStatusCode.Good;
}
```

```vbnet
outOfMaterial.RespondCallback = AddressOf Me.HandleOutOfMaterialResponse
outOfMaterial.ActivateDialog(Me.SystemContext)
...
Private Function HandleOutOfMaterialResponse( _
        ByVal context As OpcNodeContext(Of OpcDialogConditionNode), _
        ByVal selectedResponse As Integer) As OpcStatusCode
    'Handle the response.
    If context.Node.OkResponse = selectedResponse Then
        ContinueJob()
    Else
        CancelJob()
    End If

    'Apply the response.
    context.Node.RespondDialog(context, selectedResponse)

    Return OpcStatusCode.Good
End Function
```

## Feedback-Bedingungen

Beteiligte Typen: `OpcServer`, `OpcNodeManager`, `OpcAcknowledgeableConditionNode`

Die Klasse `OpcAcknowledgeableConditionNode` ist eine Spezialisierung von `OpcConditionNode`. Sie dient als Basisklasse für Bedingungen mit Feedback-Anforderung. Wenn die Bedingung eines solchen Ereignisses erfüllt ist, setzt der Server eine Art „Meldung mit Rückschein“ ab. Der Rückschein, also das Feedback, kann weitere Abläufe steuern oder einfach Hinweise und Warnungen quittieren.
Die Spezifikation (OPC UA Teil 9) sieht dafür einen Feedback-Mechanismus mit zwei Arten der Rückmeldung vor. Mit der Quittierung (engl. Acknowledge) bestätigt das Bedienpersonal, dass es die Meldung zur Kenntnis genommen hat. Mit der optionalen Bestätigung (engl. Confirm) meldet es anschließend, dass die Ursache behoben ist. Für beide Rückmeldungen stellt der Node die Kind-Nodes Acknowledge und Confirm bereit.

Das SDK behandelt beide Rückmeldungen unabhängig voneinander: Eine Quittierung setzt nur den Zustand Acked, eine Bestätigung nur den Zustand Confirmed. Welche Reihenfolge der Server verlangt, legt die Anwendung fest. Bei beiden Rückmeldungen kann das Bedienpersonal optional einen Kommentar angeben. Sie erstellen einen solchen Node wie gewohnt. Auch hier lassen sich die Zustände erst setzen, wenn der Node im Adressraum angelegt ist:

```csharp
var outOfProcessableBounds = new OpcAcknowledgeableConditionNode(machineOne, "OutOfBoundsAlert");
 
// Once the node has been added to the address space:
// Define the condition as: Needs to be acknowledged.
outOfProcessableBounds.ChangeIsAcked(this.SystemContext, value: false);
 
// Define the condition as: Needs to be confirmed.
outOfProcessableBounds.ChangeIsConfirmed(this.SystemContext, value: false);
```

```vbnet
Dim outOfProcessableBounds = New OpcAcknowledgeableConditionNode(machineOne, "OutOfBoundsAlert")

'Once the node has been added to the address space:
'Define the condition as: Needs to be acknowledged.
outOfProcessableBounds.ChangeIsAcked(Me.SystemContext, value:=False)

'Define the condition as: Needs to be confirmed.
outOfProcessableBounds.ChangeIsConfirmed(Me.SystemContext, value:=False)
```

Im weiteren Prozessablauf prüfen Sie ein eingegangenes Feedback mit den Eigenschaften `IsAcked` und `IsConfirmed` des Nodes:

```csharp
if (outOfProcessableBounds.IsAcked) {
    ...
}

if (outOfProcessableBounds.IsConfirmed) {
    ...
}

```

```vbnet
If outOfProcessableBounds.IsAcked Then
    ...
End If

If outOfProcessableBounds.IsConfirmed Then
    ...
End If

```

Auf die Rückmeldungen eines Clients reagiert der Server über die Eigenschaften `AcknowledgeCallback` und `ConfirmCallback`. Der Server selbst quittiert oder bestätigt eine Bedingung mit den Methoden `Acknowledge` und `Confirm`:

```csharp
outOfProcessableBounds.AcknowledgeCallback = (context, eventId, comment) => {
    Console.WriteLine("Acknowledged: {0}", comment);
    return OpcStatusCode.Good;
};
 
// Acknowledge or confirm the condition on the server side.
outOfProcessableBounds.Acknowledge(this.SystemContext, comment: "Acknowledged by the server");
outOfProcessableBounds.Confirm(this.SystemContext);
```

```vbnet
outOfProcessableBounds.AcknowledgeCallback = Function(context, eventId, comment)
    Console.WriteLine("Acknowledged: {0}", comment)
    Return OpcStatusCode.Good
End Function

'Acknowledge or confirm the condition on the server side.
outOfProcessableBounds.Acknowledge(Me.SystemContext, comment:="Acknowledged by the server")
outOfProcessableBounds.Confirm(Me.SystemContext)
```

Die Interpretation des Feedbacks und die darauf folgende Logik muss der Server immer **selbst definieren**. Ob ein Server beide Feedback-Optionen nutzt oder nur eine, entscheidet das Entwicklungsteam. Ein Server sollte mindestens die Quittierung (Acknowledge) unterstützen. Die Bestätigung (Confirm) ist laut Spezifikation optional.

## Alarm-Bedingungen

Beteiligte Typen: `OpcServer`, `OpcNodeManager`, `OpcAlarmConditionNode`

Die wohl wichtigste Implementierung von `OpcAcknowledgeableConditionNode` in OPC UA ist die Klasse `OpcAlarmConditionNode`. Mit ihr definieren Sie Ereignis-Nodes, deren Verhalten mit einem Nachttischwecker vergleichbar ist. Der Node wird aktiv (Eigenschaft `IsActive`), wenn die mit ihm verknüpfte Bedingung erfüllt ist.
Beim Wecker ist das zum Beispiel das Erreichen der Weckzeit. Ein Alarm, der zum Beispiel mit einer Weckzeit eingestellt ist, beim Erreichen der Weckzeit aber nicht aktiv werden soll, heißt unterdrückter Alarm (engl. Suppressed Alarm, Eigenschaften `IsSuppressed` und `IsSuppressedOrShelved`). Ein aktiver Alarm lässt sich zurückstellen (engl. shelved, Eigenschaft `IsSuppressedOrShelved`). Ein Alarm kann einmalig („One Shot Shelving“) oder zeitlich („Timed Shelving“) zurückgestellt werden (Eigenschaft `Shelving`). Die Zurückstellung eines Alarms lässt sich auch wieder aufheben (engl. unshelved, Eigenschaft `Shelving`).

Der folgende Code zeigt ein Beispiel für die API von `OpcAlarmConditionNode`:

```csharp
var overheating = new OpcAlarmConditionNode(machineOne, "OverheatingAlert");
var idle = new OpcAlarmConditionNode(machineOne, "IdleAlert");

...

overheating.ChangeIsActive(this.SystemContext, true);
idle.ChangeIsActive(this.SystemContext, true);

...

if (overheating.IsActive)
    CancelJob();

if (!idle.IsActive)
    ProcessJob();
else if (idle.IsSuppressed)
    SimulateJob();

```

```vbnet
Dim overheating As New OpcAlarmConditionNode(machineOne, "OverheatingAlert")
Dim idle As New OpcAlarmConditionNode(machineOne, "IdleAlert")
'...
overheating.ChangeIsActive(Me.SystemContext, True)
idle.ChangeIsActive(Me.SystemContext, True)

...

If overheating.IsActive Then
    CancelJob()
End If

If Not idle.IsActive Then
    ProcessJob()
ElseIf idle.IsSuppressed Then
    SimulateJob()
End If

```

Serverseitig unterdrücken Sie einen Alarm mit der Methode `ChangeIsSuppressed` und stellen ihn mit der Methode `ChangeShelving` zurück:

```csharp
// Suppress the alarm, e.g. while the machine is in maintenance.
overheating.ChangeIsSuppressed(this.SystemContext, value: true);
 
// Shelve the alarm for 30 minutes.
overheating.ChangeShelving(
        this.SystemContext,
        shelved: true,
        oneShot: false,
        shelvingTime: TimeSpan.FromMinutes(30));
 
// Unshelve the alarm.
overheating.ChangeShelving(this.SystemContext, shelved: false);
```

```vbnet
'Suppress the alarm, e.g. while the machine is in maintenance.
overheating.ChangeIsSuppressed(Me.SystemContext, value:=True)

'Shelve the alarm for 30 minutes.
overheating.ChangeShelving( _
        Me.SystemContext, _
        shelved:=True, _
        oneShot:=False, _
        shelvingTime:=TimeSpan.FromMinutes(30))

'Unshelve the alarm.
overheating.ChangeShelving(Me.SystemContext, shelved:=False)
```

## Diskrete Alarme

Beteiligte Typen: `OpcServer`, `OpcNodeManager`, `OpcDiscreteAlarmNode`, `OpcOffNormalAlarmNode`, `OpcTripAlarmNode`

Von `OpcAlarmConditionNode` gibt es mehrere Spezialisierungen für bestimmte Arten von Alarmen. Bei ihnen beschreibt schon die Art des Alarms seine Form, seinen Grund oder seinen Inhalt genauer. Eine Unterklasse solcher selbstbeschreibenden Alarme sind die diskreten Alarme. Basis eines diskreten Alarms ist die Klasse `OpcDiscreteAlarmNode`. Sie definiert einen Alarmzustands-Node, mit dem sich Typen in Alarmzustände klassifizieren lassen. Der Eingang für den Alarm kann dabei nur eine bestimmte Anzahl möglicher Werte annehmen, zum Beispiel wahr und falsch oder läuft, angehalten und beendet. Wenn ein Alarm einen diskreten Zustand darstellen soll, der als nicht normal gilt, sollten Sie die Klasse `OpcOffNormalAlarmNode` oder eine ihrer Unterklassen verwenden.

Auf dieser Alarmklasse aufbauend bietet das SDK mit der Klasse `OpcTripAlarmNode` eine weitere Konkretisierung. Ein `OpcTripAlarmNode` wird aktiv, wenn an einem überwachten Gerät ein anomaler Fehler auftritt, zum Beispiel wenn der Motor wegen einer Überlastung abgeschaltet wird. So erstellen Sie die genannten Nodes:

```csharp
var x = new OpcDiscreteAlarmNode(machineOne, "discreteAlert");
var y = new OpcOffNormalAlarmNode(machineOne, "offNormalAlert");
var z = new OpcTripAlarmNode(machineOne, "tripAlert");

```

```vbnet
Dim x As New OpcDiscreteAlarmNode(machineOne, "discreteAlert")
Dim y As New OpcOffNormalAlarmNode(machineOne, "offNormalAlert")
Dim z As New OpcTripAlarmNode(machineOne, "tripAlert")

```

## Grenzwert-Alarme

Beteiligte Typen: `OpcServer`, `OpcNodeManager`, `OpcLimitAlarmNode`

Wenn der Server prozessspezifische Grenzwerte prüfen und das Ergebnis bei Über- oder Unterschreitung veröffentlichen soll, ist die Klasse `OpcLimitAlarmNode` der Einstieg in die Klassen der Grenzwert-Alarme (engl. Limit Alarms). Mit ihr lassen sich Grenzwerte in bis zu vier Stufen unterteilen.

Die Stufen heißen `LowLow`, `Low`, `High` und `HighHigh`, genannt in der Reihenfolge ihrer metrischen Ordnung. Per Definition müssen nicht alle Grenzwerte definiert sein. Deshalb können Sie die gewünschten Grenzen von Anfang an festlegen:

```csharp
var positionLimit = new OpcLimitAlarmNode(
        machineOne, "PositionLimit", OpcLimitAlarmStates.HighHigh | OpcLimitAlarmStates.LowLow);

positionLimit.HighHighLimit = 120; // e.g. mm
positionLimit.LowLowLimit = 0;     // e.g. mm

```

```vbnet
Dim positionLimit As New OpcLimitAlarmNode(
        machineOne, "PositionLimit", OpcLimitAlarmStates.HighHigh Or OpcLimitAlarmStates.LowLow)

positionLimit.HighHighLimit = 120 ' e.g. mm
positionLimit.LowLowLimit = 0 ' e.g. mm

```

## Ausschließliche Grenzwert-Alarme

Beteiligte Typen: `OpcServer`, `OpcNodeManager`, `OpcExclusiveLimitAlarmNode`

Eine Unterklasse von `OpcLimitAlarmNode` ist die Klasse `OpcExclusiveLimitAlarmNode`. Wie der Name sagt, definiert sie Grenzwert-Alarme für ausschließliche Grenzen. Die Werte ihrer Grenzen schließen sich gegenseitig aus. Wenn also ein Grenzwert über- oder unterschritten ist, kann nicht zugleich ein anderer Grenzwert über- oder unterschritten sein. Die verletzte Grenze beschreibt der Zustandsautomat in der Eigenschaft `Limit` des Nodes. Sie setzen sie mit der Methode `ChangeLimitState`:

```csharp
var temperatureLimit = new OpcExclusiveLevelAlarmNode(
        machineOne,
        "TemperatureLimit",
        supportedLimits: OpcLimitAlarmStates.High | OpcLimitAlarmStates.HighHigh);
 
temperatureLimit.HighLimit = 80;      // e.g. degree Celsius
temperatureLimit.HighHighLimit = 100; // e.g. degree Celsius
 
// Once the node has been added to the address space:
temperatureLimit.ChangeIsActive(this.SystemContext, value: true);
temperatureLimit.ChangeLimitState(this.SystemContext, value: OpcLimitAlarmStates.High);
```

```vbnet
Dim temperatureLimit = New OpcExclusiveLevelAlarmNode( _
        machineOne, _
        "TemperatureLimit", _
        supportedLimits:=OpcLimitAlarmStates.High Or OpcLimitAlarmStates.HighHigh)

temperatureLimit.HighLimit = 80      'e.g. degree Celsius
temperatureLimit.HighHighLimit = 100 'e.g. degree Celsius

'Once the node has been added to the address space:
temperatureLimit.ChangeIsActive(Me.SystemContext, value:=True)
temperatureLimit.ChangeLimitState(Me.SystemContext, value:=OpcLimitAlarmStates.High)
```

In OPC UA gibt es drei weitere Spezialisierungen von `OpcExclusiveLimitAlarmNode`.

`OpcExclusiveDeviationAlarmNode`

Verwenden Sie diese Art von Alarm, wenn der Server die Abweichung eines Werts von seinem Sollwert überwacht. Den Node des Sollwerts geben Sie über die Eigenschaft `SetpointNodeId` an.

`OpcExclusiveLevelAlarmNode`

Diese Art von Alarm meldet normalerweise, dass ein Grenzwert überschritten ist. Das betrifft typischerweise ein Instrument, zum Beispiel einen Temperatursensor. Der Alarm wird aktiv, wenn der beobachtete Wert über einem oberen oder unter einem unteren Grenzwert liegt.

`OpcExclusiveRateOfChangeAlarmNode`

Diese Art von Alarm meldet üblicherweise eine ungewöhnliche oder fehlende Änderung eines gemessenen Werts, bezogen auf die Geschwindigkeit, mit der sich der Wert ändert. Der Alarm wird aktiv, wenn die Änderungsrate des Werts einen definierten Grenzwert über- oder unterschreitet.

## Nicht-ausschließliche Grenzwert-Alarme

Beteiligte Typen: `OpcServer`, `OpcNodeManager`, `OpcNonExclusiveLimitAlarmNode`

Eine Unterklasse von `OpcLimitAlarmNode` ist die Klasse `OpcNonExclusiveLimitAlarmNode`. Wie der Name sagt, definiert sie Grenzwert-Alarme für nicht-ausschließliche Grenzen. Die Werte ihrer Grenzen schließen sich gegenseitig **nicht aus**. Wenn also ein Grenzwert über- oder unterschritten ist, kann zugleich ein anderer Grenzwert über- oder unterschritten sein. Die verletzten Grenzen prüfen Sie mit den Eigenschaften `IsLowLow`, `IsLow`, `IsHigh` und `IsHighHigh` des Nodes.

In OPC UA gibt es drei weitere Spezialisierungen von `OpcNonExclusiveLimitAlarmNode`.

`OpcNonExclusiveDeviationAlarmNode`

Verwenden Sie diese Art von Alarm, wenn der Server die Abweichung eines Werts von seinem Sollwert überwacht. Den Node des Sollwerts geben Sie über die Eigenschaft `SetpointNodeId` an.

`OpcNonExclusiveLevelAlarmNode`

Diese Art von Alarm meldet normalerweise, dass ein Grenzwert überschritten ist. Das betrifft typischerweise ein Instrument, zum Beispiel einen Temperatursensor. Der Alarm wird aktiv, wenn der beobachtete Wert über einem oberen oder unter einem unteren Grenzwert liegt.

`OpcNonExclusiveRateOfChangeAlarmNode`

Diese Art von Alarm meldet üblicherweise eine ungewöhnliche oder fehlende Änderung eines gemessenen Werts, bezogen auf die Geschwindigkeit, mit der sich der Wert ändert. Der Alarm wird aktiv, wenn die Änderungsrate des Werts einen definierten Grenzwert über- oder unterschreitet.
