# Alarms and conditions

> How-to guide: Event nodes with conditions: dialog and acknowledgeable conditions, alarm conditions up to limit alarms.

> This content was machine translated and may contain errors. [Read in German](https://www.traeger.de/docs/development/opcua/client/alarms-conditions)

**Sample applications:**

- 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)): This sample demonstrates how to perform handling of Alarm + Events (AE) on the nodes provided by the OPC UA server.
- 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)): This sample demonstrates how to setup an OPC UA server to provide AE to its nodes provided.

Event nodes with conditions report events that are tied to the state of a condition. This page shows how a client processes the event data of each kind of condition, from simple conditions to limit alarms, and how it receives the conditions of an OPC AE server (Alarms & Events).

## Conditions

Types used: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcEvent`, `OpcCondition`, `OpcEventSeverity`

The `OpcCondition` class is a specialization of the `OpcEvent` class, which the page [Events](https://www.traeger.de/en/docs/development/opcua/client/events) introduces. It provides event data that is tied to specific conditions. The server triggers such an event only if the condition assigned to the event node applies. The event data contains information about the state of the condition and information tied to the evaluation of the condition. Depending on the use case, this information can vary in complexity. For this reason, `OpcCondition` is the base class of all event data tied to a condition.

Besides the general properties of an event from the `OpcEvent` base class, an instance of `OpcCondition` provides further information that can be important for processing in the client. The `IsRetained` property shows whether the server generally considers the event relevant for clients. The `IsEnabled` property shows whether the condition is enabled or disabled. Whether an alarm is active, in contrast, is shown by the `IsActive` property of the alarm conditions (section [Alarm conditions](https://www.traeger.de/en/docs/development/opcua/client/alarms-conditions#alarm-conditions)).

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

```

If the client should no longer evaluate the condition, it disables it:

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

```

The client can also add a comment on the current state of the condition:

```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 conditions

Types used: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcCondition`, `OpcDialogCondition`, `OpcEventSeverity`, `OpcDialogRequestedEventArgs`

The `OpcDialogCondition` class is a specialization of `OpcCondition`. The condition of this event is a dialog with the subscribers. Such a condition consists of a prompt and response options. Three more details complete it: the `DefaultResponse` property names the option that should be selected by default. The `OkResponse` property names the option that confirms the dialog, and the `CancelResponse` property the option that cancels it. When the server triggers such an event, it waits until one of the subscribers responds with one of the given response options. The operations tied to the dialog therefore require a response: to a task, a question, a piece of information, or a warning. This is how the client processes the information of the event and reports the response back to the server:

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

Besides the standard way of handling events, the `OpcClient` class offers the `DialogRequested` event. If the client receives event data of the type `OpcDialogCondition` and no handler has set the `Handled` property of the event arguments to `true`, the client raises the `DialogRequested` event. You process the dialog through this event. For this reason, if a handler already answers the dialog itself, set the `Handled` property as in the previous example. Otherwise, the client answers the dialog a second time. With `DialogRequested`, handling the event data is somewhat easier because the event handler receives it typed:

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

```

Only set the `SelectedResponse` property of the event arguments. `OpcClient` calls the `Respond` method of the `OpcDialogCondition` class itself after the event handler. If `SelectedResponse` remains at the default value `-1` or lies outside the response options, the client sends no response.

## Acknowledgeable conditions

Types used: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcEvent`, `OpcCondition`, `OpcAcknowledgeableCondition`, `OpcEventSeverity`

The `OpcAcknowledgeableCondition` class is a specialization of `OpcCondition`. It is the base class for conditions that request feedback. Such an event works like a message with a return receipt: when its condition is met, the server sends the message and requests feedback. The return receipt, that is the feedback, can control further processes or simply acknowledge notes and warnings. The OPC UA specification (Part 9) provides two kinds of feedback for this. Acknowledge confirms that someone has taken note of the message. Confirm then confirms that the cause of the message has been resolved. This second stage is optional, and not every server uses it. For both kinds, the events provide the `Acknowledge` and `Confirm` methods. The two states are independent of each other: acknowledging does not also confirm, and vice versa. Optionally, you pass a comment from the person handling the event when you acknowledge or confirm. This is how you implement acknowledgment as 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

```

When it processes such events, the client also checks whether the event has already been confirmed (`IsConfirmed` property) or acknowledged (`IsAcked` property). The server always defines the interpretation of the feedback and the logic that follows. Whether a server uses both kinds of feedback or only one is therefore up to the team that develops it. Ideally, a server supports at least the `Acknowledge` method, since the specification provides confirmation as an optional second stage after acknowledgment.

## Alarm conditions

Types used: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcAlarmCondition`, `OpcAcknowledgeableCondition`, `OpcEventSeverity`

Probably the most important implementation of `OpcAcknowledgeableCondition` in OPC UA is the `OpcAlarmCondition` class. Events of this class behave like a bedside alarm clock. Such an event becomes active (`IsActive` property) when the linked condition is met. For the alarm clock, this is reaching the wake-up time, for example. An alarm that has a wake-up time but should not become active when that time is reached is called a suppressed alarm (`IsSuppressed` and `IsSuppressedOrShelved` properties). When an alarm becomes active, the server can shelve it: once ("One Shot Shelving") or for a period of time ("Timed Shelving"). The `IsSuppressedOrShelved` property shows whether an alarm is suppressed or shelved. The `MaxTimeShelved` property shows the maximum time an alarm may stay shelved.

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

```

## Discrete alarms

Types used: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcDiscreteAlarm`, `OpcOffNormalAlarm`, `OpcTripAlarm`

For the event data of `OpcAlarmCondition`, there are several specializations for specific kinds of alarms. For them, the kind of alarm itself describes its form, its cause, or its content in more detail. One subclass of such self-describing alarms are the discrete alarms. Their base is the `OpcDiscreteAlarm` class. It represents alarms whose input can take only a certain number of possible values, for example true and false, or running, stopped, and finished. If an alarm represents a discrete state that is considered not normal, the `OpcOffNormalAlarm` class or one of its subclasses describes it. The `NormalStateId` property refers to the state that is considered normal. The OPC UA .NET SDK provides a further specialization of this alarm class with the `OpcTripAlarm` class. This alarm becomes active when an abnormal fault occurs on a monitored device, for example when the motor shuts down because of overload.

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

```

## Limit alarms

Types used: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcLimitAlarm`

If the server checks process-specific limits and publishes the result when a value exceeds or falls below them, the `OpcLimitAlarm` class is the central entry point to the limit alarm classes. This class divides limits into up to four levels. In the order of their metric ranking, they are called `LowLow`, `Low`, `High`, and `HighHigh`. By definition, the server does not have to define all limits.

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

```

## Exclusive limit alarms

Types used: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcLimitAlarm`, `OpcExclusiveLimitAlarm`, `OpcExclusiveDeviationAlarm`, `OpcExclusiveLevelAlarm`, `OpcExclusiveRateOfChangeAlarm`

The `OpcExclusiveLimitAlarm` class is a subclass of `OpcLimitAlarm`. As its name says, it defines limit alarms for exclusive limits. The limits of such an alarm are mutually exclusive. If one limit is exceeded or undershot, no other limit can be exceeded or undershot at the same time.

OPC UA has three further specializations of the `OpcExclusiveLimitAlarm` class.

`OpcExclusiveDeviationAlarm`

This alarm reports that the value deviates from a setpoint by more than the limits. The `SetpointNodeId` property refers to the node of the setpoint.

`OpcExclusiveLevelAlarm`

This alarm reports a limit violation, typically on an instrument such as a temperature sensor. It becomes active when the observed value is above an upper limit or below a lower limit.

`OpcExclusiveRateOfChangeAlarm`

This alarm reports an unusual or missing change of a measured value, relative to the speed of the change. It becomes active when the rate of change of the value exceeds or falls below a defined limit.

## Non-exclusive limit alarms

Types used: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcLimitAlarm`, `OpcNonExclusiveLimitAlarm`, `OpcNonExclusiveDeviationAlarm`, `OpcNonExclusiveLevelAlarm`, `OpcNonExclusiveRateOfChangeAlarm`

The `OpcNonExclusiveLimitAlarm` class is a subclass of `OpcLimitAlarm`. As its name says, it defines limit alarms for non-exclusive limits. The limits of such an alarm are not mutually exclusive. If one limit is exceeded or undershot, another limit can be exceeded or undershot at the same time. You check the violated limits with the `IsLowLow`, `IsLow`, `IsHigh`, and `IsHighHigh` properties of the event data.

OPC UA has three further specializations of the `OpcNonExclusiveLimitAlarm` class.

`OpcNonExclusiveDeviationAlarm`

This alarm reports that the value deviates from a setpoint by more than the limits. The `SetpointNodeId` property refers to the node of the setpoint.

`OpcNonExclusiveLevelAlarm`

This alarm reports a limit violation, typically on an instrument such as a temperature sensor. It becomes active when the observed value is above an upper limit or below a lower limit.

`OpcNonExclusiveRateOfChangeAlarm`

This alarm reports an unusual or missing change of a measured value, relative to the speed of the change. It becomes active when the rate of change of the value exceeds or falls below a defined limit.

## Conditions of an OPC AE server

The client receives and acknowledges the events and conditions of an OPC AE server with the same methods. The section [Alarms and events (AE)](https://www.traeger.de/en/docs/development/opcua/client/opc-classic#alarms-and-events-ae) on the OPC Classic page describes the procedure.
