How-to guide
Subscriptions
Subscribe to value changes and events of nodes, handle notifications, manage subscriptions, and narrow them down with filters.
How-to guide
Subscriptions
Create subscriptions
Types usedOpcClientOpcNodeIdOpcSubscribeDataChangeOpcSubscribeEventOpcSubscriptionOpcMonitoredItemOpcMonitoredItemCollectionOpcDataChangeReceivedEventHandlerOpcDataChangeReceivedEventArgsOpcEventReceivedEventHandlerOpcEventReceivedEventArgsOpcNotification
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:
OpcSubscription subscription = client.SubscribeDataChange(
"ns=2;s=Machine/IsRunning",
HandleDataChanged);Dim subscription As OpcSubscription = client.SubscribeDataChange( _
"ns=2;s=Machine/IsRunning", _
AddressOf HandleDataChanged)Subscribe to value changes of multiple nodes:
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);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:
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);
}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 SubSubscribe to events of a node:
OpcSubscription subscription = client.SubscribeEvent(
"ns=2;s=Machine",
HandleEvent);Dim subscription As OpcSubscription = client.SubscribeEvent( _
"ns=2;s=Machine", _
AddressOf HandleEvent)Subscribe to events of multiple nodes:
OpcSubscribeEvent[] commands = new OpcSubscribeEvent[] {
new OpcSubscribeEvent("ns=2;s=Machine", HandleEvent),
new OpcSubscribeEvent("ns=2;s=Machine/Job", HandleEvent)
};
OpcSubscription subscription = client.SubscribeNodes(commands);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:
private void HandleEvent(object sender, OpcEventReceivedEventArgs e)
{
// Your code to execute on each event raise.
}Private Sub HandleEvent(ByVal sender As Object, ByVal e As OpcEventReceivedEventArgs)
'Your code to execute on each event raise.
End SubConfigure 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:
subscription.PublishingInterval = 2000;
// Always call apply changes after modifying the subscription; otherwise
// the server will not know the new subscription configuration.
subscription.ApplyChanges();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:
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();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:
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);
}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 SubHandle notifications
Types usedOpcClientOpcSubscriptionOpcMonitoredItemOpcDataChangeReceivedEventArgsOpcEventReceivedEventArgsOpcNotificationFailedEventArgs
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:
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.
}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 SubIf 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:
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);
}
}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 SubIf decoding fails because values are too large, increase the 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 usedOpcClientOpcSubscriptionOpcSubscriptionSetupOpcMonitoredItemOpcMonitoringModeOpcMonitoredItemQueueMode
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:
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);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:
// 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();'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):
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();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 usedOpcClientOpcNodeIdOpcSubscribeDataChangeOpcDataChangeFilterOpcDataChangeTriggerOpcDeadbandTypeOpcSubscriptionOpcMonitoredItemOpcMonitoredItemCollectionOpcDataChangeReceivedEventHandlerOpcDataChangeReceivedEventArgsOpcNotification
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:
OpcSubscription subscription = client.SubscribeDataChange(
"ns=2;s=Machine/IsRunning",
OpcDataChangeTrigger.StatusValueTimestamp,
HandleDataChanged);Dim subscription As OpcSubscription = client.SubscribeDataChange( _
"ns=2;s=Machine/IsRunning", _
OpcDataChangeTrigger.StatusValueTimestamp, _
AddressOf HandleDataChanged)Subscribe to value and timestamp changes of multiple nodes:
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);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:
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);
}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 SubYou 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:
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);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:
// 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);'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)
