# Events

> How-to guide: Receive, filter, and evaluate the events of a server, work with event nodes, and receive audit events.

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

**Sample applications:**

- Client.EventTypes ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Basic/Client.EventTypes), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Basic/Client.EventTypes)): This sample demonstrates how to subscribe to events of a custom event type and how to access their structured event data through an own event class.
- Client.GenericEvents ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Scenarios/Client.GenericEvents), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Scenarios/Client.GenericEvents)): This use case demonstrates how to receive events of a custom event type without an own class for its fields, using a generic event class that exposes all selected event fields.
- Server.EventTypes ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Basic/Server.EventTypes), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Basic/Server.EventTypes)): This sample demonstrates how to setup an OPC UA server to provide a custom event type defined in a nodeset. Its events carry structured data and are raised when a client calls the StartJob method.

This page shows how a client receives, filters, and evaluates the events (Alarms & Events) of a server. The page [Alarms and conditions](https://www.traeger.de/en/docs/development/opcua/client/alarms-conditions) describes events with conditions and alarms.

Types used: `OpcClient`, `OpcObjectTypes`, `OpcEventTypes`, `OpcEventReceivedEventArgs`, `OpcEvent`, `OpcAlarmCondition`, `OpcSimpleAttributeOperand`, `OpcFilter`, `OpcEventFilter`, `OpcEventSeverity`

## Receive global events

Events inform a subscriber, for example a client, about processes, states, and system-specific occurrences. Through **global events**, subscribers receive such information directly. A server always publishes a global event through its Server object. For this reason, the client also subscribes to global events through this node:

```csharp
client.SubscribeEvent(OpcObjectTypes.Server, HandleGlobalEvents);

```

```vbnet
client.SubscribeEvent(OpcObjectTypes.Server, AddressOf HandleGlobalEvents)

```

You usually handle an event with a method of this signature:

```csharp
private static void HandleGlobalEvents(
        object sender,
        OpcEventReceivedEventArgs e)
{
    Console.WriteLine(e.Event.Message);
}

```

```vbnet
Private Shared Sub HandleGlobalEvents( _
        ByVal sender As Object, _
        ByVal e As OpcEventReceivedEventArgs)
    Console.WriteLine(e.Event.Message)
End Sub

```

The `Event` property of the event arguments (class `OpcEventReceivedEventArgs`) returns the event data received. It always returns an instance of the `OpcEvent` class. If the event data belongs to a specialization of the `OpcEvent` class, simply cast it:

```csharp
private static void HandleGlobalEvents(
        object sender,
        OpcEventReceivedEventArgs e)
{
    var alarm = e.Event as OpcAlarmCondition;
 
    if (alarm != null)
        Console.WriteLine("Alarm: " + alarm.Message);
}
```

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

    If alarm IsNot Nothing Then
        Console.WriteLine("Alarm: " & alarm.Message.Value)
    End If
End Sub
```

## Filter events

The call above subscribes to all events that the server publishes globally. With an event filter, you restrict the information you receive and also the events that the server sends to the client at all. The client transmits the filter to the server when it creates the subscription:

```csharp
// Define an attribute operand using the identifier of the type which defines the
// attribute / property including the name of the attribute / property to evaluate
// by the operand.
var severity = new OpcSimpleAttributeOperand(OpcEventTypes.Event, "Severity");
var conditionName = new OpcSimpleAttributeOperand(OpcEventTypes.Condition, "ConditionName");
 
var filter = OpcFilter.Using(client)
        .FromEvents(OpcEventTypes.AlarmCondition)
        .Where(severity > OpcEventSeverity.Medium & conditionName.Like("Temperature"))
        .Select();
 
client.SubscribeEvent(
        OpcObjectTypes.Server,
        filter,
        HandleGlobalEvents);

```

```vbnet
'Define an attribute operand using the identifier of the type which defines the
'attribute / property including the name of the attribute / property to evaluate
'by the operand.
Dim severity = New OpcSimpleAttributeOperand(OpcEventTypes.Event, "Severity")
Dim conditionName = New OpcSimpleAttributeOperand(OpcEventTypes.Condition, "ConditionName")

Dim filter = OpcFilter.Using(client) _
        .FromEvents(OpcEventTypes.AlarmCondition) _
        .Where(severity > OpcEventSeverity.Medium And conditionName.Like("Temperature")) _
        .Select()

client.SubscribeEvent(
        OpcObjectTypes.Server,
        filter,
        AddressOf HandleGlobalEvents)

```

To create an event filter, you always need an instance of `OpcClient` that is already connected to the target server. While it builds the filter, the client retrieves the relevant type information from the server. In the example above, the client collects all properties of the node type `OpcAlarmConditionNode` recursively up to `OpcNode`. From the Where and Select clauses, it creates rules for selecting the event data. Pass the node types to analyze to the `FromEvents` method, separated by commas:

```csharp
var severity = new OpcSimpleAttributeOperand(OpcEventTypes.Event, "Severity");
var conditionName = new OpcSimpleAttributeOperand(OpcEventTypes.Condition, "ConditionName");
 
var filter = OpcFilter.Using(client)
        .FromEvents(
            OpcEventTypes.AlarmCondition,
            OpcEventTypes.ExclusiveLimitAlarm,
            OpcEventTypes.DialogCondition)
        .Where(severity > OpcEventSeverity.Medium & conditionName.Like("Temperature"))
        .Select();
 
client.SubscribeEvent(
        OpcObjectTypes.Server,
        filter,
        HandleGlobalEvents);

```

```vbnet
Dim severity = New OpcSimpleAttributeOperand(OpcEventTypes.Event, "Severity")
Dim conditionName = New OpcSimpleAttributeOperand(OpcEventTypes.Condition, "ConditionName")

Dim filter = OpcFilter.Using(client) _
        .FromEvents( _
            OpcEventTypes.AlarmCondition, _
            OpcEventTypes.ExclusiveLimitAlarm, _
            OpcEventTypes.DialogCondition) _
        .Where(severity > OpcEventSeverity.Medium And conditionName.Like("Temperature")) _
        .Select()

client.SubscribeEvent(
        OpcObjectTypes.Server,
        filter,
        AddressOf HandleGlobalEvents)

```

With the `Where` method, you restrict the information that `FromEvents` collects through the properties of the node types. For this, the OPC UA .NET SDK offers several operator overloads (`<=`, `<`, `>`, `>=`, and `==`). The operator `!=` throws a `NotSupportedException`. Use `(a < b) | (a > b)` instead. To combine operands logically, the operators OR (`|`) and AND (`&`) are also available. For further restrictions, there are methods such as `Like`, `Between`, `InList`, `IsNull`, `Not`, `OfType`, `Cast`, and `InView`. For languages without these operators (such as Visual Basic), the equivalent methods `EqualsTo`, `GreaterThan`, `GreaterThanOrEqualsTo`, `LessThan`, and `LessThanOrEqualsTo` are available.

As of version 2.59, `Between` takes the operands in OPC UA order: `a.Between(lower, upper)` checks whether `a` lies between `lower` and `upper`. As of version 2.59, several `Where` calls in a row combine their conditions with AND, like a single `Where` with `&`. In older versions, only the condition of the last call applies.

Then use the `Select` method to choose the properties that the server additionally transmits to the client. The server does so when an event occurs that matches the conditions from `Where`.

The following example subscribes to all events that are currently active but not yet acknowledged:

```csharp
var isActive = new OpcSimpleAttributeOperand(
        OpcEventTypes.AlarmCondition,
        "ActiveState", "Id");
var isAcked = new OpcSimpleAttributeOperand(
        OpcEventTypes.AcknowledgeableCondition,
        "AckedState", "Id");

var filter = OpcFilter.Using(client)
    .FromEvents(OpcEventTypes.AlarmCondition)
    .Where(OpcFilterOperand.OfType(OpcEventTypes.AlarmCondition)
        & isAcked == false
        & isActive == true)
    .Select();

var subscription = client.SubscribeEvent("ns=2;s=Machine", filter, (sender, e) => {
    if (e.Event is OpcAlarmCondition alarm) {
        Console.WriteLine(
                "{0}: {1}, IsActive = {2}, IsAcked = {3} ({4})",
                alarm.GetType().Name,
                alarm.Message,
                alarm.IsActive,
                alarm.IsAcked,
                alarm.Severity);
    }
});
```

```vbnet
Dim isActive = New OpcSimpleAttributeOperand( _
        OpcEventTypes.AlarmCondition, _
        "ActiveState", "Id")
Dim isAcked = New OpcSimpleAttributeOperand( _
        OpcEventTypes.AcknowledgeableCondition, _
        "AckedState", "Id")

Dim filter = OpcFilter.Using(client) _
    .FromEvents(OpcEventTypes.AlarmCondition) _
    .Where(OpcFilterOperand.OfType(OpcEventTypes.AlarmCondition) _
        And isAcked = False _
        And isActive = True) _
    .Select()

Dim subscription = client.SubscribeEvent("ns=2;s=Machine", filter, _
    Sub(sender, e)
        Dim alarm = TryCast(e.Event, OpcAlarmCondition)

        If alarm IsNot Nothing Then
            Console.WriteLine( _
                    "{0}: {1}, IsActive = {2}, IsAcked = {3} ({4})", _
                    alarm.GetType().Name, alarm.Message, alarm.IsActive, alarm.IsAcked, alarm.Severity)
        End If
    End Sub)

```

## Receive events of a node

Types used: `OpcClient`, `OpcEventReceivedEventArgs`, `OpcEvent`, `OpcSubscription`

It does not always make sense for a server to send all events globally through the Server object to all subscribers. Often the context decides whether an event is of interest to a subscriber. For such local events, there are event nodes. If the server uses an event node to provide event data for a node, the event node is a notifier of that node. A HasNotifier reference therefore shows which event node reports event data to a node. An event node can be the notifier of multiple nodes. For this reason, the client always subscribes to local events through the notified nodes:

```csharp
client.SubscribeEvent(machineNodeId, HandleLocalEvents);

```

```vbnet
client.SubscribeEvent(machineNodeId, AddressOf HandleLocalEvents)

```

You usually handle an event with a method of this signature. The client processes the event data further the same way as for global events (see section [Receive global events](https://www.traeger.de/en/docs/development/opcua/client/events#receive-global-events)).

```csharp
private static void HandleLocalEvents(
        object sender,
        OpcEventReceivedEventArgs e)
{
    Console.WriteLine(e.Event.Message);
}

```

```vbnet
Private Shared Sub HandleLocalEvents( _
        ByVal sender As Object, _
        ByVal e As OpcEventReceivedEventArgs)
    Console.WriteLine(e.Event.Message)
End Sub

```

You can also filter local events, as shown in the section [Filter events](https://www.traeger.de/en/docs/development/opcua/client/events#filter-events).

A subscriber (a client) only learns about events while it is connected to the server and has created a subscription. After it connects, it therefore does not know the current state of the conditions of the server (see [Alarms and conditions](https://www.traeger.de/en/docs/development/opcua/client/alarms-conditions)). With the following call, it requests this state. The server then sends an event again for all conditions that are currently in a relevant state (`IsRetained` is `true`). This call does not return past events. If the server provides them, the client reads them through the event history, see [Read the event history](https://www.traeger.de/en/docs/development/opcua/client/historical-data#read-the-event-history).

```csharp
var subscription = client.SubscribeEvent(
        machineNodeId,
        HandleLocalEvents);
 
// Query most recent event information.
subscription.RefreshConditions();

```

```vbnet
Dim subscription = client.SubscribeEvent( _
        machineNodeId, _
        AddressOf HandleLocalEvents)
 
'Query most recent event information.
subscription.RefreshConditions()

```

## Receive audit events

Types used: `OpcClient`, `OpcFilter`, `OpcEventFilter`, `OpcObjectTypes`, `OpcEvent`

Audit events log the security-related actions of a server. A server developed with the SDK reports them through the Server object if auditing is turned on in the server (see [Audit events](https://www.traeger.de/en/docs/development/opcua/security/server#audit-events)). It reports them when a secure channel is opened and closed (AuditOpenSecureChannelEventType, AuditChannelEventType) and when a session is created (AuditCreateSessionEventType) and activated (AuditActivateSessionEventType). As defined by OPC UA, a client receives them only through an endpoint with the mode `SignAndEncrypt` or through HTTPS, and only if the logged-on user belongs to the `SecurityAdmin` role. A client subscribes to them like other events, through a filter on the event types, in the following example on the two event types of the session:

```csharp
OpcEventFilter filter = OpcFilter.Using(client)
        .FromEvents(
                OpcObjectTypes.GetNodeId(OpcObjectType.AuditCreateSessionEventType),
                OpcObjectTypes.GetNodeId(OpcObjectType.AuditActivateSessionEventType))
        .Select();
 
client.SubscribeEvent(OpcObjectTypes.Server, filter, HandleAuditEvent);
...
private static void HandleAuditEvent(
        object sender,
        OpcEventReceivedEventArgs e)
{
    OpcEvent audit = e.Event;
 
    Console.WriteLine(
            "{0:u} {1} {2}: {3}",
            audit.Time,
            audit.EventTypeId,
            audit.SourceName,
            audit.Message);
}
```

```vbnet
Dim filter As OpcEventFilter = OpcFilter.Using(client) _
        .FromEvents( _
                OpcObjectTypes.GetNodeId(OpcObjectType.AuditCreateSessionEventType), _
                OpcObjectTypes.GetNodeId(OpcObjectType.AuditActivateSessionEventType)) _
        .Select()

client.SubscribeEvent(OpcObjectTypes.Server, filter, AddressOf HandleAuditEvent)
...
Private Shared Sub HandleAuditEvent( _
        ByVal sender As Object, _
        ByVal e As OpcEventReceivedEventArgs)
    Dim audit As OpcEvent = e.Event

    Console.WriteLine( _
            "{0:u} {1} {2}: {3}", _
            audit.Time, _
            audit.EventTypeId, _
            audit.SourceName, _
            audit.Message)
End Sub

```

The client can read the general fields of an event, for example `Time`, `EventTypeId`, `SourceName`, and `Message`. The SDK does not provide the specific fields of audit events in the client, such as the identity of the user or the ID of the session.
