Skip to content

How-to guide

Process many values efficiently

Read, write, and subscribe to thousands of values with one call: how the SDK adapts large requests to the limits of the server, and what pays off when.

This content was machine translated and may contain errors. Read in German

How-to guide

Process many values efficiently ​

Each call to ReadNode or WriteNode is a separate request to the server with its own network round trip. If you read many values one by one in a loop, you quickly lose seconds. Instead, the methods ReadNodes, WriteNodes, and SubscribeNodes accept any number of commands in one call. The SDK splits them into requests that the server accepts, and it reassembles the responses in the order of the commands.

Read many values in one call ​

Types usedOpcClientOpcReadNodeOpcValue

The results are in the same order as the commands. Evaluate the status of each value separately. An invalid node does not abort the call:

csharp
var commands = new OpcReadNode[5000];
 
for (int index = 0; index < commands.Length; index++)
    commands[index] = new OpcReadNode("ns=2;s=Sensors/Sensor" + (index + 1));
 
IEnumerable<OpcValue> values = client.ReadNodes(commands);
vbnet
Dim commands = New OpcReadNode(4999) {}

For index As Integer = 0 To commands.Length - 1
    commands(index) = New OpcReadNode("ns=2;s=Sensors/Sensor" & (index + 1))
Next

Dim values As IEnumerable(Of OpcValue) = client.ReadNodes(commands)

Write many values in one call ​

Types usedOpcClientOpcWriteNodeOpcStatusOpcStatusCollection

Writing also returns one result per command. Use the index to match each result to its command:

csharp
var commands = new OpcWriteNode[5000];
 
for (int index = 0; index < commands.Length; index++)
    commands[index] = new OpcWriteNode("ns=2;s=Setpoints/Setpoint" + (index + 1), value: 0.0);
 
OpcStatusCollection results = client.WriteNodes(commands);
 
for (int index = 0; index < results.Count; index++) {
    if (results[index].IsBad)
        Console.WriteLine("{0}: {1}", commands[index].NodeId, results[index].Description);
}
vbnet
Dim commands = New OpcWriteNode(4999) {}

For index As Integer = 0 To commands.Length - 1
    commands(index) = New OpcWriteNode("ns=2;s=Setpoints/Setpoint" & (index + 1), value:=0.0)
Next

Dim results As OpcStatusCollection = client.WriteNodes(commands)

For index As Integer = 0 To results.Count - 1
    If results(index).IsBad Then
        Console.WriteLine("{0}: {1}", commands(index).NodeId, results(index).Description)
    End If
Next

How the SDK splits large requests ​

Types usedOpcClientOpcClientServices

Servers limit how many nodes a single request can contain and report these limits under Server/ServerCapabilities/OperationLimits. If a call contains more than one command, the client proceeds as follows:

  1. On the first such call, it reads the limit of the server for the respective service (MaxNodesPerRead, MaxNodesPerWrite, MaxNodesPerBrowse, and MaxNodesPerTranslateBrowsePathsToNodeIds). If the server reports no limit (0), the client uses 1000.
  2. If you set your own limit for the service, the smaller of the two values applies.
  3. The client remembers the value and then splits all further calls into multiple requests. It reassembles the responses in the order of the commands.

This applies to ReadNodes, WriteNodes, browsing, and TranslatePaths. A lower limit of your own makes sense if single requests should take less time, or if the server fails on response sizes despite its reported limit:

csharp
client.Services.ReadNodes.MaxNodesPerRead = 500;
client.Services.WriteNodes.MaxNodesPerWrite = 500;
client.Services.Browse.MaxNodesPerBrowse = 100;
vbnet
client.Services.ReadNodes.MaxNodesPerRead = 500
client.Services.WriteNodes.MaxNodesPerWrite = 500
client.Services.Browse.MaxNodesPerBrowse = 100

The corresponding property with the prefix Used shows which limit the client actually uses. It is null as long as no call with multiple commands has taken place:

csharp
uint? nodesPerRead = client.Services.ReadNodes.UsedMaxNodesPerRead;
vbnet
Dim nodesPerRead As UInteger? = client.Services.ReadNodes.UsedMaxNodesPerRead

Regardless of the number of nodes, the client limits the size of the data transferred. The section Data transfer limits describes how to raise these limits.

Subscribe to many nodes ​

Types usedOpcClientOpcSubscribeDataChangeOpcSubscriptionOpcMonitoredItem

If the values change rarely compared to the read interval, a subscription is cheaper than cyclic reading, because the server reports only changes. SubscribeNodes creates all MonitoredItems in one subscription. In the handler, the sender is the MonitoredItem. Use its NodeId to match the notification:

csharp
var commands = new OpcSubscribeDataChange[1000];
 
for (int index = 0; index < commands.Length; index++) {
    commands[index] = new OpcSubscribeDataChange(
            "ns=2;s=Sensors/Sensor" + (index + 1),
            HandleDataChanged);
}
 
OpcSubscription subscription = client.SubscribeNodes(commands);
...
private static void HandleDataChanged(
        object sender,
        OpcDataChangeReceivedEventArgs e)
{
    OpcMonitoredItem item = (OpcMonitoredItem)sender;
    Console.WriteLine("{0}: {1}", item.NodeId, e.Item.Value);
}
vbnet
Dim commands = New OpcSubscribeDataChange(999) {}

For index As Integer = 0 To commands.Length - 1
    commands(index) = New OpcSubscribeDataChange( _
            "ns=2;s=Sensors/Sensor" & (index + 1), _
            AddressOf HandleDataChanged)
Next

Dim subscription As OpcSubscription = client.SubscribeNodes(commands)
...
Private Shared Sub HandleDataChanged( _
        ByVal sender As Object, _
        ByVal e As OpcDataChangeReceivedEventArgs)
    Dim item As OpcMonitoredItem = CType(sender, OpcMonitoredItem)
    Console.WriteLine("{0}: {1}", item.NodeId, e.Item.Value)
End Sub

Choose the right access method ​

SituationRecommendation
Values are needed once or rarelyReadNodes with all nodes in one call
The same nodes are read or written cyclicallyRegister the nodes and call ReadNodes or WriteNodes with the registered identifiers, see Register frequently read nodes
Values change rarely, the application reacts to changesSubscription with all nodes through SubscribeNodes, see Subscriptions
Very large single values (binary data)Read the values as a stream, see Large binary values as a stream
Nodes are only known by their pathResolve them once with TranslatePaths, see Find nodes by path