# Subscriptions

> How-to guide: Subscribe to value changes and events of nodes, handle notifications, manage subscriptions, and narrow them down with filters.

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

**Sample applications:**

- Client.BulkChanges.HiRes ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Subscriptions/Client.BulkChanges.HiRes), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Subscriptions/Client.BulkChanges.HiRes)): This sample demonstrates how to receive data changes of a large number of nodes with minimal latency, using a publishing interval of zero and a callback for each data set.
- Client.HandshakeChanges ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Subscriptions/Client.HandshakeChanges), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Subscriptions/Client.HandshakeChanges)): This sample demonstrates how a client reads a block of data as soon as the server signals it as available and confirms its processing through a handshake node.
- Server.BulkChanges.HiRes ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Subscriptions/Server.BulkChanges.HiRes), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Subscriptions/Server.BulkChanges.HiRes)): This sample demonstrates how to configure an OPC UA server for high resolution publishing, to deliver changes of a large number of nodes in intervals of a few milliseconds.
- Server.HandshakeChanges ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Subscriptions/Server.HandshakeChanges), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Subscriptions/Server.HandshakeChanges)): This sample demonstrates how to setup an OPC UA server that provides blocks of data using a handshake: it signals new data and waits until a client confirms their processing.

## Create subscriptions

Types used: `OpcClient`, `OpcNodeId`, `OpcSubscribeDataChange`, `OpcSubscribeEvent`, `OpcSubscription`, `OpcMonitoredItem`, `OpcMonitoredItemCollection`, `OpcDataChangeReceivedEventHandler`, `OpcDataChangeReceivedEventArgs`, `OpcEventReceivedEventHandler`, `OpcEventReceivedEventArgs`, `OpcNotification`

A subscription in OPC UA is comparable to a subscription to one or more magazines. Instead of magazines, however, the client subscribes to events of nodes or to changes of their values. For each subscribed node, the subscription contains a MonitoredItem. For each subscription, you define the interval at which the server publishes the notifications of the MonitoredItems (`OpcSubscription.PublishingInterval`). The notifications are instances of `OpcNotification`. The NodeId defines which node the client subscribes to. By default, the server monitors the Value attribute. To have the server monitor another attribute of the node, pass the corresponding `OpcAttribute` when you call `SubscribeDataChange` or in the respective `OpcSubscribeDataChange` instance.

**Subscribe to value changes of a node**:

```csharp
OpcSubscription subscription = client.SubscribeDataChange(
        "ns=2;s=Machine/IsRunning",
        HandleDataChanged);
```

```vbnet
Dim subscription As OpcSubscription = client.SubscribeDataChange( _
        "ns=2;s=Machine/IsRunning", _
        AddressOf HandleDataChanged)
```

**Subscribe to value changes of multiple nodes**:

```csharp
OpcSubscribeDataChange[] commands = new OpcSubscribeDataChange[] {
    new OpcSubscribeDataChange("ns=2;s=Machine/IsRunning", HandleDataChanged),
    new OpcSubscribeDataChange("ns=2;s=Machine/Job/Speed", HandleDataChanged)
};
 
OpcSubscription subscription = client.SubscribeNodes(commands);
```

```vbnet
Dim commands As OpcSubscribeDataChange() = New OpcSubscribeDataChange() { _
    New OpcSubscribeDataChange("ns=2;s=Machine/IsRunning", AddressOf HandleDataChanged), _
    New OpcSubscribeDataChange("ns=2;s=Machine/Job/Speed", AddressOf HandleDataChanged) _
}

Dim subscription As OpcSubscription = client.SubscribeNodes(commands)
```

**Handle notifications about value changes**:

```csharp
private static void HandleDataChanged(
        object sender,
        OpcDataChangeReceivedEventArgs e)
{
    // Your code to execute on each data change.
    // The 'sender' variable contains the OpcMonitoredItem with the NodeId.
    OpcMonitoredItem item = (OpcMonitoredItem)sender;

    Console.WriteLine(
            "Data Change from NodeId '{0}': {1}",
            item.NodeId,
            e.Item.Value);
}
```

```vbnet
Private Shared Sub HandleDataChanged( _
        ByVal sender As Object, _
        ByVal e As OpcDataChangeReceivedEventArgs)
    'Your code to execute on each data change.
    'The 'sender' variable contains the OpcMonitoredItem with the NodeId.
    Dim item As OpcMonitoredItem = CType(sender, OpcMonitoredItem)

    Console.WriteLine( _
            "Data Change from NodeId '{0}': {1}", _
            item.NodeId, _
            e.Item.Value)
End Sub

```

**Subscribe to events of a node**:

```csharp
OpcSubscription subscription = client.SubscribeEvent(
        "ns=2;s=Machine",
        HandleEvent);
```

```vbnet
Dim subscription As OpcSubscription = client.SubscribeEvent( _
        "ns=2;s=Machine", _
        AddressOf HandleEvent)
```

**Subscribe to events of multiple nodes**:

```csharp
OpcSubscribeEvent[] commands = new OpcSubscribeEvent[] {
    new OpcSubscribeEvent("ns=2;s=Machine", HandleEvent),
    new OpcSubscribeEvent("ns=2;s=Machine/Job", HandleEvent)
};
 
OpcSubscription subscription = client.SubscribeNodes(commands);
```

```vbnet
Dim commands As OpcSubscribeEvent() = New OpcSubscribeEvent() { _
    New OpcSubscribeEvent("ns=2;s=Machine", AddressOf HandleEvent), _
    New OpcSubscribeEvent("ns=2;s=Machine/Job", AddressOf HandleEvent) _
}

Dim subscription As OpcSubscription = client.SubscribeNodes(commands)
```

**Handle notifications about events**:

```csharp
private void HandleEvent(object sender, OpcEventReceivedEventArgs e)
{
    // Your code to execute on each event raise.
}
```

```vbnet
Private Sub HandleEvent(ByVal sender As Object, ByVal e As OpcEventReceivedEventArgs)
    'Your code to execute on each event raise.
End Sub

```

**Configure a subscription**: By default, a subscription uses a `KeepAliveCount` of 10, a `LifetimeCount` of 100, and at most 1000 notifications per publish (`MaxNotificationsPerPublish`). The `PublishingInterval` has no default value. A value of 0 or less requests the smallest interval the server supports. The client only sends changes to the subscription to the server when you call `ApplyChanges`:

```csharp
subscription.PublishingInterval = 2000;
 
// Always call apply changes after modifying the subscription; otherwise
// the server will not know the new subscription configuration.
subscription.ApplyChanges();
```

```vbnet
subscription.PublishingInterval = 2000

'Always call apply changes after modifying the subscription; otherwise
'the server will not know the new subscription configuration.
subscription.ApplyChanges()
```

**Subscribe to multiple nodes through one subscription**: The example subscribes to value changes of multiple nodes through a single subscription and sets custom values for the `Tag` property of the MonitoredItems. The `SamplingInterval` property defines how often the server checks the value. The value 0 requests the fastest rate, the value -1 uses the PublishingInterval of the subscription:

```csharp
string[] nodeIds = {
    "ns=2;s=Machine/IsRunning",
    "ns=2;s=Machine/Job/Speed",
    "ns=2;s=Machine/Diagnostics"
};
 
// Create an (empty) subscription to which we will addd OpcMonitoredItems.
OpcSubscription subscription = client.SubscribeNodes();
 
for (int index = 0; index < nodeIds.Length; index++) {
    // Create an OpcMonitoredItem for the NodeId.
    var item = new OpcMonitoredItem(nodeIds[index], OpcAttribute.Value);
    item.DataChangeReceived += HandleDataChanged;
 
    // You can set your own values on the "Tag" property
    // that allows you to identify the source later.
    item.Tag = index;
 
    // Set a custom sampling interval on the 
    // monitored item.
    item.SamplingInterval = 200;
 
    // Add the item to the subscription.
    subscription.AddMonitoredItem(item);
}
 
// After adding the items (or configuring the subscription), apply the changes.
subscription.ApplyChanges();
```

```vbnet
Dim nodeIds As String() = { _
    "ns=2;s=Machine/IsRunning", _
    "ns=2;s=Machine/Job/Speed", _
    "ns=2;s=Machine/Diagnostics" _
}

'Create an (empty) subscription to which we will addd OpcMonitoredItems.
Dim subscription As OpcSubscription = client.SubscribeNodes()

For index As Integer = 0 To nodeIds.Length - 1
    'Create an OpcMonitoredItem for the NodeId.
    Dim item = New OpcMonitoredItem(nodeIds(index), OpcAttribute.Value)
    AddHandler item.DataChangeReceived, AddressOf HandleDataChanged

    'You can set your own values on the "Tag" property
    'that allows you to identify the source later.
    item.Tag = index

    'Set a custom sampling interval on the 
    'monitored item.
    item.SamplingInterval = 200

    'Add the item to the subscription.
    subscription.AddMonitoredItem(item)
Next

'After adding the items (or configuring the subscription), apply the changes.
subscription.ApplyChanges()
```

The corresponding handler:

```csharp
private static void HandleDataChanged(
        object sender,
        OpcDataChangeReceivedEventArgs e)
{
    // The tag property contains the previously set value.
    OpcMonitoredItem item = (OpcMonitoredItem)sender;
    
    Console.WriteLine(
            "Data Change from Index {0}: {1}",
            item.Tag,
            e.Item.Value);
}
```

```vbnet
Private Shared Sub HandleDataChanged( _
        ByVal sender As Object, _
        ByVal e As OpcDataChangeReceivedEventArgs)
    'The tag property contains the previously set value.
    Dim item As OpcMonitoredItem = CType(sender, OpcMonitoredItem)

    Console.WriteLine( _
        "Data Change from Index {0}: {1}", _
        item.Tag,
        e.Item.Value)
End Sub

```

## Handle notifications

Types used: `OpcClient`, `OpcSubscription`, `OpcMonitoredItem`, `OpcDataChangeReceivedEventArgs`, `OpcEventReceivedEventArgs`, `OpcNotificationFailedEventArgs`

A notification about a value change or an event passes through the handlers in a fixed order. The handler of the MonitoredItem receives it first, for example the handler you passed when you called `SubscribeDataChange`. Next come the `DataChangeReceived` or `EventReceived` event of the `OpcSubscription` class and finally the event of the same name of the `OpcClient` class. The client only passes the notification on as long as no handler sets the `Handled` property of the event arguments to `true`:

```csharp
OpcSubscription subscription = client.SubscribeDataChange(
        "ns=2;s=Machine/IsRunning",
        HandleItemDataChanged);
 
subscription.DataChangeReceived += HandleSubscriptionDataChanged;
client.DataChangeReceived += HandleClientDataChanged;
...
private static void HandleItemDataChanged(
        object sender,
        OpcDataChangeReceivedEventArgs e)
{
    if (e.Item.Value.Status.IsGood) {
        // Your code to process the value.
 
        // The handlers of the subscription and the client are not called.
        e.Handled = true;
    }
}
 
private static void HandleSubscriptionDataChanged(
        object sender,
        OpcDataChangeReceivedEventArgs e)
{
    // Called for notifications not handled by the monitored item.
}
 
private static void HandleClientDataChanged(
        object sender,
        OpcDataChangeReceivedEventArgs e)
{
    // Called for notifications not handled by the monitored item or the subscription.
}
```

```vbnet
Dim subscription As OpcSubscription = client.SubscribeDataChange( _
        "ns=2;s=Machine/IsRunning", _
        AddressOf HandleItemDataChanged)

AddHandler subscription.DataChangeReceived, AddressOf HandleSubscriptionDataChanged
AddHandler client.DataChangeReceived, AddressOf HandleClientDataChanged
...
Private Shared Sub HandleItemDataChanged( _
        ByVal sender As Object, _
        ByVal e As OpcDataChangeReceivedEventArgs)
    If e.Item.Value.Status.IsGood Then
        'Your code to process the value.

        'The handlers of the subscription and the client are not called.
        e.Handled = True
    End If
End Sub

Private Shared Sub HandleSubscriptionDataChanged( _
        ByVal sender As Object, _
        ByVal e As OpcDataChangeReceivedEventArgs)
    'Called for notifications not handled by the monitored item.
End Sub

Private Shared Sub HandleClientDataChanged( _
        ByVal sender As Object, _
        ByVal e As OpcDataChangeReceivedEventArgs)
    'Called for notifications not handled by the monitored item or the subscription.
End Sub

```

If the client cannot receive a notification message from the server, its value changes and events do not reach the MonitoredItems. This happens, for example, if a value in it cannot be decoded. Instead, the client raises the `NotificationFailed` event. It raises it first on the subscription the message belongs to. If the event is not handled there, the client raises it on the `OpcClient`. The `Exception` property of the event arguments states the reason, for example `BadDecodingError` or `BadEncodingLimitsExceeded`.

The client requests a failed message from the server again as long as the server still holds it. The event occurs once per message, regardless of how often the message fails. If the message is lost, the event occurs one more time with the status `BadMessageNotAvailable`. If a message cannot be assigned to any subscription, only the `OpcClient` reports it. The `Subscription` property of the event arguments is then `null`:

```csharp
subscription.NotificationFailed += HandleSubscriptionNotificationFailed;
client.NotificationFailed += HandleClientNotificationFailed;
...
private static void HandleSubscriptionNotificationFailed(
        object sender,
        OpcNotificationFailedEventArgs e)
{
    Console.WriteLine("Notification failed: {0}", e.Exception.Code);
 
    // The OpcClient.NotificationFailed event is not raised for this failure.
    e.Handled = true;
}
 
private static void HandleClientNotificationFailed(
        object sender,
        OpcNotificationFailedEventArgs e)
{
    // The subscription is a null reference in case the message cannot be assigned to one.
    if (e.Subscription == null) {
        Console.WriteLine("Notification failed: {0}", e.Exception.Message);
    }
    else {
        Console.WriteLine(
                "Notification of subscription {0} failed: {1}",
                e.Subscription.Id,
                e.Exception.Message);
    }
}
```

```vbnet
AddHandler subscription.NotificationFailed, AddressOf HandleSubscriptionNotificationFailed
AddHandler client.NotificationFailed, AddressOf HandleClientNotificationFailed
...
Private Shared Sub HandleSubscriptionNotificationFailed( _
        ByVal sender As Object, _
        ByVal e As OpcNotificationFailedEventArgs)
    Console.WriteLine("Notification failed: {0}", e.Exception.Code)

    'The OpcClient.NotificationFailed event is not raised for this failure.
    e.Handled = True
End Sub

Private Shared Sub HandleClientNotificationFailed( _
        ByVal sender As Object, _
        ByVal e As OpcNotificationFailedEventArgs)
    'The subscription is Nothing in case the message cannot be assigned to one.
    If e.Subscription Is Nothing Then
        Console.WriteLine("Notification failed: {0}", e.Exception.Message)
    Else
        Console.WriteLine( _
                "Notification of subscription {0} failed: {1}", _
                e.Subscription.Id, _
                e.Exception.Message)
    End If
End Sub

```

If decoding fails because values are too large, increase the [data transfer limits](https://www.traeger.de/en/docs/development/opcua/client/configuration#data-transfer-limits).

In addition, the client raises the `OpcClient.NotificationReceived` event for every notification it receives. This also applies to notifications without a specific MonitoredItem: if the PublishingInterval of a subscription has elapsed and the server has not sent a notification in the meantime, the client automatically requests an update, and the server responds with an empty notification. In most cases, you do not have to handle this event, because you process the relevant notifications through the handlers of the MonitoredItems.

## Manage subscriptions

Types used: `OpcClient`, `OpcSubscription`, `OpcSubscriptionSetup`, `OpcMonitoredItem`, `OpcMonitoringMode`, `OpcMonitoredItemQueueMode`

**Create a subscription in one step**: An instance of the `OpcSubscriptionSetup` class defines the MonitoredItems, the properties, and the handlers of the subscription. The `CreateSubscription` method then creates the subscription on the server together with its MonitoredItems. As of version 2.59, `CreateSubscription` throws an `OpcException` with the status `BadServerNotConnected` if the client is not connected. You do not have to call `ApplyChanges` afterward:

```csharp
var setup = new OpcSubscriptionSetup(
        new OpcMonitoredItem("ns=2;s=Machine/IsRunning", OpcAttribute.Value),
        new OpcMonitoredItem("ns=2;s=Machine/Job/Speed", OpcAttribute.Value)) {
    PublishingInterval = 1000,
    DataChangeReceived = HandleDataChanged,
    NotificationFailed = HandleNotificationFailed
};
 
// Creates the subscription including its monitored items on the server.
OpcSubscription subscription = client.CreateSubscription(setup);
```

```vbnet
Dim setup = New OpcSubscriptionSetup( _
        New OpcMonitoredItem("ns=2;s=Machine/IsRunning", OpcAttribute.Value), _
        New OpcMonitoredItem("ns=2;s=Machine/Job/Speed", OpcAttribute.Value)) With { _
    .PublishingInterval = 1000, _
    .DataChangeReceived = AddressOf HandleDataChanged, _
    .NotificationFailed = AddressOf HandleNotificationFailed _
}

'Creates the subscription including its monitored items on the server.
Dim subscription As OpcSubscription = client.CreateSubscription(setup)
```

**Control an existing subscription**: You can pause and resume the publishing of notifications, request the current values again, and disable or remove individual MonitoredItems. The `StopPublishing`, `StartPublishing`, `ResendData`, and `ChangeMonitoringMode` methods take effect directly on the server. The client only sends the removal of a MonitoredItem with `ApplyChanges`:

```csharp
// Pause and resume the delivery of notifications of the subscription.
subscription.StopPublishing();
subscription.StartPublishing();
 
// Request the current values of all reporting monitored items again.
subscription.ResendData();
 
// Stop the sampling of a single monitored item and remove it afterwards.
subscription.ChangeMonitoringMode(OpcMonitoringMode.Disabled, item);
subscription.RemoveMonitoredItem(item);
 
// Always call apply changes after modifying the monitored items.
subscription.ApplyChanges();
```

```vbnet
'Pause and resume the delivery of notifications of the subscription.
subscription.StopPublishing()
subscription.StartPublishing()

'Request the current values of all reporting monitored items again.
subscription.ResendData()

'Stop the sampling of a single monitored item and remove it afterwards.
subscription.ChangeMonitoringMode(OpcMonitoringMode.Disabled, item)
subscription.RemoveMonitoredItem(item)

'Always call apply changes after modifying the monitored items.
subscription.ApplyChanges()
```

**Configure the queue of a MonitoredItem**: If more value changes occur than a single publish transfers, the server buffers them in the queue of the MonitoredItem (`QueueSize`, 1000 by default). If the queue is full, the server discards the oldest value by default (`OpcMonitoredItemQueueMode.DiscardOldest`):

```csharp
var item = new OpcMonitoredItem("ns=2;s=Machine/Job/Speed", OpcAttribute.Value);
 
item.SamplingInterval = 100;    // 0 = fastest rate, -1 = publishing interval
item.QueueSize = 10;            // Default: 1000
item.QueueMode = OpcMonitoredItemQueueMode.DiscardOldest;
 
subscription.AddMonitoredItem(item);
subscription.ApplyChanges();
```

```vbnet
Dim item = New OpcMonitoredItem("ns=2;s=Machine/Job/Speed", OpcAttribute.Value)

item.SamplingInterval = 100     '0 = fastest rate, -1 = publishing interval
item.QueueSize = 10             'Default: 1000
item.QueueMode = OpcMonitoredItemQueueMode.DiscardOldest

subscription.AddMonitoredItem(item)
subscription.ApplyChanges()
```

## Set change filters

Types used: `OpcClient`, `OpcNodeId`, `OpcSubscribeDataChange`, `OpcDataChangeFilter`, `OpcDataChangeTrigger`, `OpcDeadbandType`, `OpcSubscription`, `OpcMonitoredItem`, `OpcMonitoredItemCollection`, `OpcDataChangeReceivedEventHandler`, `OpcDataChangeReceivedEventArgs`, `OpcNotification`

Through a subscription, a client application by default only receives notifications about changes of the status and the value of a node. To make the server notify the client according to other rules, define the trigger of a notification. You set the trigger with a value of the `OpcDataChangeTrigger` enumeration. The following examples use it to subscribe to notifications about changes of the status, the value, or the timestamp of a node.

**Subscribe to value and timestamp changes of a node**:

```csharp
OpcSubscription subscription = client.SubscribeDataChange(
        "ns=2;s=Machine/IsRunning",
        OpcDataChangeTrigger.StatusValueTimestamp,
        HandleDataChanged);
```

```vbnet
Dim subscription As OpcSubscription = client.SubscribeDataChange( _
        "ns=2;s=Machine/IsRunning", _
        OpcDataChangeTrigger.StatusValueTimestamp, _
        AddressOf HandleDataChanged)
```

**Subscribe to value and timestamp changes of multiple nodes**:

```csharp
OpcSubscribeDataChange[] commands = new OpcSubscribeDataChange[] {
    new OpcSubscribeDataChange(
            "ns=2;s=Machine/IsRunning",
            OpcDataChangeTrigger.StatusValueTimestamp,
            HandleDataChanged),
    new OpcSubscribeDataChange(
            "ns=2;s=Machine/Job/Speed",
            OpcDataChangeTrigger.StatusValueTimestamp,
            HandleDataChanged)
};
 
OpcSubscription subscription = client.SubscribeNodes(commands);
```

```vbnet
Dim commands As OpcSubscribeDataChange() = New OpcSubscribeDataChange() { _
    New OpcSubscribeDataChange( _
            "ns=2;s=Machine/IsRunning", _
            OpcDataChangeTrigger.StatusValueTimestamp, _
            AddressOf HandleDataChanged), _
    New OpcSubscribeDataChange( _
            "ns=2;s=Machine/Job/Speed", _
            OpcDataChangeTrigger.StatusValueTimestamp, _
            AddressOf HandleDataChanged) _
}

Dim subscription As OpcSubscription = client.SubscribeNodes(commands)
```

**Handle notifications about value and timestamp changes**:

```csharp
private static void HandleDataChanged(
        object sender,
        OpcDataChangeReceivedEventArgs e)
{
    // Your code to execute on each data change.
    // The 'sender' variable contains the OpcMonitoredItem with the NodeId.
    OpcMonitoredItem item = (OpcMonitoredItem)sender;

    Console.WriteLine(
            "Data Change from NodeId '{0}': {1} at {2}",
            item.NodeId,
            e.Item.Value,
            e.Item.Value.SourceTimestamp);
}
```

```vbnet
Private Shared Sub HandleDataChanged( _
        ByVal sender As Object, _
        ByVal e As OpcDataChangeReceivedEventArgs)
    'Your code to execute on each data change.
    'The 'sender' variable contains the OpcMonitoredItem with the NodeId.
    Dim item As OpcMonitoredItem = CType(sender, OpcMonitoredItem)

    Console.WriteLine( _
            "Data Change from NodeId '{0}': {1} at {2}", _
            item.NodeId, _
            e.Item.Value, _
            e.Item.Value.SourceTimestamp)
End Sub

```

You can also set the `OpcDataChangeTrigger` value from the previous examples through an instance of the `OpcDataChangeFilter` class. This way, you use a filter you define once for multiple subscriptions and MonitoredItems:

```csharp
var filter = new OpcDataChangeFilter();
filter.Trigger = OpcDataChangeTrigger.StatusValueTimestamp;
 
OpcSubscription subscriptionA = client.SubscribeDataChange(
        "ns=2;s=Machine/IsRunning",
        filter,
        HandleDataChanged);
 
// or
 
OpcSubscribeDataChange[] commands = new OpcSubscribeDataChange[] {
    new OpcSubscribeDataChange(
            "ns=2;s=Machine/IsRunning",
            filter,
            HandleDataChanged),
    new OpcSubscribeDataChange(
            "ns=2;s=Machine/Job/Speed",
            filter,
            HandleDataChanged)
};
 
OpcSubscription subscriptionB = client.SubscribeNodes(commands);
```

```vbnet
Dim filter = New OpcDataChangeFilter()
filter.Trigger = OpcDataChangeTrigger.StatusValueTimestamp

Dim subscriptionA As OpcSubscription = client.SubscribeDataChange( _
        "ns=2;s=Machine/IsRunning", _
        filter, _
        AddressOf HandleDataChanged)

'or

Dim commands As OpcSubscribeDataChange() = New OpcSubscribeDataChange() { _
    New OpcSubscribeDataChange( _
            "ns=2;s=Machine/IsRunning", _
            filter, _
            AddressOf HandleDataChanged), _
    New OpcSubscribeDataChange( _
            "ns=2;s=Machine/Job/Speed", _
            filter, _
            AddressOf HandleDataChanged) _
}

Dim subscriptionB As OpcSubscription = client.SubscribeNodes(commands)
```

**Report only value changes above a certain size**: The `DeadbandType` and `DeadbandValue` properties of the `OpcDataChangeFilter` class define the size of value change at which the server sends a notification. With `OpcDeadbandType.Absolute`, `DeadbandValue` specifies the absolute change. With `OpcDeadbandType.Percent`, it specifies the change as a percentage of the value range of the node (`EngineeringUnitRange`). Whether a server supports a percent deadband depends on the server and the node:

```csharp
// Report only changes of the value which exceed 0.5 (absolute).
var filter = new OpcDataChangeFilter(OpcDataChangeTrigger.StatusValue) {
    DeadbandType = OpcDeadbandType.Absolute,
    DeadbandValue = 0.5
};
 
OpcSubscription subscription = client.SubscribeDataChange(
        "ns=2;s=Machine/Job/Speed",
        filter,
        HandleDataChanged);
```

```vbnet
'Report only changes of the value which exceed 0.5 (absolute).
Dim filter = New OpcDataChangeFilter(OpcDataChangeTrigger.StatusValue) With { _
    .DeadbandType = OpcDeadbandType.Absolute, _
    .DeadbandValue = 0.5 _
}

Dim subscription As OpcSubscription = client.SubscribeDataChange( _
        "ns=2;s=Machine/Job/Speed", _
        filter, _
        AddressOf HandleDataChanged)
```
