QuickOPC User's Guide and Reference
UAPubSubObjectDescriptor Class
Members  Example 



OpcLabs.EasyOpcUA Assembly > OpcLabs.EasyOpc.UA.PubSub Namespace : UAPubSubObjectDescriptor Class
Descriptor for an object in OPC UA PubSub.
Object Model
UAPubSubObjectDescriptor ClassUAKeyValueDictionary Class
Syntax
'Declaration
 
<ComVisibleAttribute(False)>
<GuidAttribute("BA9AE201-B0D9-4826-B68C-84D6668F7A99")>
<ValueControlAttribute("OpcLabs.EasyOpc.UA.Forms.Internal.PubSub.UAPubSubObjectDescriptorAdvancedControl, OpcLabs.EasyOpcForms, Version=5.72.465.1, Culture=neutral, PublicKeyToken=6faddca41dacb409", 
   Export=True, 
   PageId=900)>
<CLSCompliantAttribute(True)>
<TypeConverterAttribute(System.ComponentModel.ExpandableObjectConverter)>
<SerializableAttribute()>
Public MustInherit Class UAPubSubObjectDescriptor 
   Inherits OpcLabs.BaseLib.Info
   Implements LINQPad.ICustomMemberProvider, OpcLabs.BaseLib.ComTypes._Info, OpcLabs.BaseLib.ComTypes._Object2, OpcLabs.EasyOpc.UA.PubSub.ComTypes._UAPubSubObjectDescriptor, System.ICloneable, System.IFormattable, System.Runtime.Serialization.ISerializable, System.Xml.Serialization.IXmlSerializable 
'Usage
 
Dim instance As UAPubSubObjectDescriptor
[ComVisible(false)]
[Guid("BA9AE201-B0D9-4826-B68C-84D6668F7A99")]
[ValueControl("OpcLabs.EasyOpc.UA.Forms.Internal.PubSub.UAPubSubObjectDescriptorAdvancedControl, OpcLabs.EasyOpcForms, Version=5.72.465.1, Culture=neutral, PublicKeyToken=6faddca41dacb409", 
   Export=true, 
   PageId=900)]
[CLSCompliant(true)]
[TypeConverter(System.ComponentModel.ExpandableObjectConverter)]
[Serializable()]
public abstract class UAPubSubObjectDescriptor : OpcLabs.BaseLib.Info, LINQPad.ICustomMemberProvider, OpcLabs.BaseLib.ComTypes._Info, OpcLabs.BaseLib.ComTypes._Object2, OpcLabs.EasyOpc.UA.PubSub.ComTypes._UAPubSubObjectDescriptor, System.ICloneable, System.IFormattable, System.Runtime.Serialization.ISerializable, System.Xml.Serialization.IXmlSerializable  
[ComVisible(false)]
[Guid("BA9AE201-B0D9-4826-B68C-84D6668F7A99")]
[ValueControl("OpcLabs.EasyOpc.UA.Forms.Internal.PubSub.UAPubSubObjectDescriptorAdvancedControl, OpcLabs.EasyOpcForms, Version=5.72.465.1, Culture=neutral, PublicKeyToken=6faddca41dacb409", 
   Export=true, 
   PageId=900)]
[CLSCompliant(true)]
[TypeConverter(System.ComponentModel.ExpandableObjectConverter)]
[Serializable()]
public ref class UAPubSubObjectDescriptor abstract : public OpcLabs.BaseLib.Info, LINQPad.ICustomMemberProvider, OpcLabs.BaseLib.ComTypes._Info, OpcLabs.BaseLib.ComTypes._Object2, OpcLabs.EasyOpc.UA.PubSub.ComTypes._UAPubSubObjectDescriptor, System.ICloneable, System.IFormattable, System.Runtime.Serialization.ISerializable, System.Xml.Serialization.IXmlSerializable  
Remarks

 

This article describes the descriptors used in OPC UA PubSub communication. For a relation between physical and logical identifiers and descriptors, see Identifying information in OPC UA PubSub.

Explanation of Terms

In order to be able to describe how QuickOPC handles the logical and physical identifiers, especially in relation to OPC UA PubSub logical resolution, we need to define some terms first. There are also properties and methods that use these terms in their names, so knowing what they mean is beneficial to understand their purpose.

We say that a descriptor or filter strictly physical when it does not have a logical identifier in itself - that is, the name of the PubSub object is empty - and all its constituent descriptors (if there are any) are also strictly physical.

We say that a descriptor or filter requires resolution when an access to PubSub configuration is necessary in order to interpret the information in it for PubSub communication purposes. For example, a PubSub connection descriptor requires resolution when it has no (physical) network address specified, but it has a (logical) non-empty connection name.  Similarly, a writer group descriptor requires resolution when its dataset writer Id is set to zero (i.e. "unknown"), but it has a (logical) non-empty dataset writer name, and so on.

A physicalization of a descriptor or filter is an operation that makes it strictly physical. A physicalization is only possible on descriptor or filters that currently do not require resolution. Physicalization is performed by setting the logical name of the object to an empty string, and physicalizing its constituent descriptors (if there are any).

A logicalization of a descriptor or filter is an operation that removes the physical information from it.

If you construct the descriptor with only the physical information, this physical information will be used to make the subscription. Analogically, if you construct the descriptor with only the logical information, this logical information will be used (resolved first, and if successful, the subscription will be made with the physical information yielded by the resolution). If, however, the descriptor contains both the physical and logical information (which is the case e.g. when the descriptor is obtained by browsing the PubSub configuration), the software is, in general, allowed to choose whether it will use the physical or logical information (the default behavior is that the physical information will be used). If you have a descriptor that can potentially contain both the physical and logical information, and you want to make sure that either one will be used for making the subscription, you can use the physicalization or logicalization operations to achieve that.

PubSub Connection Descriptor

A PubSub connection descriptor combines together:

In order to specify a PubSub connection, you can set the PubSub connection name, or its network address, or both.

The PubSub connection can be "redirected" so that it uses a packet capture file, instead of true connection to the message oriented middleware (network). This is useful in all kinds of troubleshooting and development tasks. Such redirection is performed by setting up the PubSub connection descriptor in a specific way. For more information, see Using packet capture files with OPC UA PubSub.

Similar feature exists for broker-based communication. You can, for example, capture the received MQTT messages into a directory structure in the file system, and replay them later. For more information, see File-based MQTT emulation.

When possible, the transport profile (given by transport profile URI or transport profile name) is determined by QuickOPC, and you do not need to specify it explicitly. For more information on transport profiles, see OPC UA PubSub Transport Profiles.

In addition, for transport protocol mappings that are used with multiple message mappings (such as MQTT, which is used with JSON or UADP), QuickOPC supports special custom transport profiles that do not require you to know the message mapping upfront. For more information, see OPC UA PubSub Automatic Message Mapping Recognition.

The network address (in the NetworkAddress Property ) usually refers to the message oriented middleware. For example, for UDP, it can be an address of a UDP multicast group. For MQTT, it can be an address of an MQTT broker. There are, however, cases when the network address is set differently. For example, with UDP unicast, the network address is set to one of the local IP address of the computer that is receiving the messages, or to a local loopback address (using the "localhost").

Writer Group Descriptor

A writer group descriptor combines together an optional logical writer group name with an optional writer group Id. That is, in order to specify a writer group, you can set the writer group name, or its Id, or both.

Dataset Writer Descriptor

A dataset writer descriptor combines together an optional logical dataset writer name with an optional dataset writer Id. That is, in order to specify a dataset writer, you can set the dataset writer name, or its Id, or both.

Subscribe Dataset Filter

The subscribe dataset filter is a kind of compound descriptor. It is documented separately in Message Filtering (OPC UA PubSub).

DataSet Subscription Descriptor

The dataset subscription descriptor combines the connection information (and communication parameters) with the subscribe dataset filter. Its structure is conceptually shown in the picture below.

The dataset subscription descriptor allows resolution if it has enough logical information specified to perform (or at least attempt) the resolution to the physical information necessary for actually making the subscription. This means that at least the published dataset name or the PubSub connection name must be given in the dataset subscription descriptor, for it to allow resolution.

 

 

Example

.NET

COM

// This example shows how to subscribe to dataset messages and specify a filter, resolving logical parameters to physical
// from an OPC-UA PubSub configuration file in binary format. The metadata obtained through the resolution is used to decode
// fixed layout messages with RawData field encoding.
//
// In order to produce network messages for this example, run the UADemoPublisher tool. For documentation, see
// http://kb.opclabs.com/UADemoPublisher_Basics . In some cases, you may have to specify the interface name to be used.

using System;
using System.Collections.Generic;
using System.Threading;
using OpcLabs.EasyOpc.UA.PubSub;
using OpcLabs.EasyOpc.UA.PubSub.OperationModel;

namespace UADocExamples.PubSub._EasyUASubscriber
{
    partial class SubscribeDataSet
    {
        public static void ResolveFromFile()
        {
            // Define the PubSub resolver. We want the information be resolved from a PubSub binary configuration file that
            // we have. The file itself is at the root of the project, and we have specified that it has to be copied to the
            // project's output directory.
            var pubSubResolverDescriptor = UAPubSubResolverDescriptor.File("UADemoPublisher-Default.uabinary");

            // Define the PubSub connection we will work with, using its logical name in the PubSub configuration.
            var pubSubConnectionDescriptor = new UAPubSubConnectionDescriptor { Name = "FixedLayoutConnection" };
            // In some cases you may have to set the interface (network adapter) name that needs to be used, similarly to
            // the statement below. Your actual interface name may differ, of course.
            //pubSubConnectionDescriptor.ResourceAddress.InterfaceName = "Ethernet";

            // Define the filter. The writer group and the dataset writer are specified using their logical names in the
            // PubSub configuration. The publisher Id in the filter will be taken from the logical PubSub connection.
            var filter = new UASubscribeDataSetFilter("FixedLayoutGroup", "SimpleWriter");

            // Instantiate the subscriber object and hook events.
            var subscriber = new EasyUASubscriber();
            subscriber.DataSetMessage += subscriber_DataSetMessage_ResolveFromFile;
            subscriber.ResolverAccess += subscriber_ResolverAccess_ResolveFromFile;

            Console.WriteLine("Subscribing...");
            subscriber.SubscribeDataSet(pubSubResolverDescriptor, pubSubConnectionDescriptor, filter);

            Console.WriteLine("Processing dataset message events for 20 seconds...");
            Thread.Sleep(20 * 1000);

            Console.WriteLine("Unsubscribing...");
            subscriber.UnsubscribeAllDataSets();

            Console.WriteLine("Waiting for 1 second...");
            // Unsubscribe operation is asynchronous, messages may still come for a short while.
            Thread.Sleep(1 * 1000);

            Console.WriteLine("Finished.");
        }

        static void subscriber_DataSetMessage_ResolveFromFile(object sender, EasyUADataSetMessageEventArgs e)
        {
            // Display the dataset.
            if (e.Succeeded)
            {
                // An event with null DataSetData just indicates a successful connection.
                if (!(e.DataSetData is null))
                {
                    Console.WriteLine();
                    Console.WriteLine($"Dataset data: {e.DataSetData}");
                    foreach (KeyValuePair<string, UADataSetFieldData> pair in e.DataSetData.FieldDataDictionary)
                        Console.WriteLine(pair);
                }
            }
            else
            {
                Console.WriteLine();
                Console.WriteLine($"*** Failure: {e.ErrorMessageBrief}");
            }
        }

        private static void subscriber_ResolverAccess_ResolveFromFile(object sender, EasyUAResolverAccessEventArgs e)
        {
            // Display resolution information.
            Console.WriteLine(e);
        }

        // Example output:
        //
        //Subscribing...
        //Processing dataset message events for 20 seconds...
        //[PublisherFile: UADemoPublisher-Default.uabinary] (no exception)
        //
        //Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
        //[BoolToggle, False {System.Boolean}; Good]
        //[Int32, 3072 {System.Int32}; Good]
        //[Int32Fast, 894 {System.Int32}; Good]
        //[DateTime, 10/1/2019 12:21:14 PM {System.DateTime}; Good]
        //
        //Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
        //[BoolToggle, False {System.Boolean}; Good]
        //[Int32, 3072 {System.Int32}; Good]
        //[Int32Fast, 920 {System.Int32}; Good]
        //[DateTime, 10/1/2019 12:21:14 PM {System.DateTime}; Good]
        //
        //Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
        //[BoolToggle, False {System.Boolean}; Good]
        //[Int32, 3073 {System.Int32}; Good]
        //[Int32Fast, 1003 {System.Int32}; Good]
        //[DateTime, 10/1/2019 12:21:15 PM {System.DateTime}; Good]
        //
        //Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
        //[BoolToggle, False {System.Boolean}; Good]
        //[Int32, 3073 {System.Int32}; Good]
        //[Int32Fast, 1074 {System.Int32}; Good]
        //[DateTime, 10/1/2019 12:21:15 PM {System.DateTime}; Good]
        //
        //Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
        //[BoolToggle, True {System.Boolean}; Good]
        //[Int32, 3074 {System.Int32}; Good]
        //[Int32Fast, 1140 {System.Int32}; Good]
        //[DateTime, 10/1/2019 12:21:16 PM {System.DateTime}; Good]
        //
        //...
    }
}
# This example shows how to subscribe to dataset messages and specify a filter, resolving logical parameters to physical
# from an OPC-UA PubSub configuration file in binary format. The metadata obtained through the resolution is used to
# decode fixed layout messages with RawData field encoding.
#
# In order to produce network messages for this example, run the UADemoPublisher tool. For documentation, see
# http://kb.opclabs.com/UADemoPublisher_Basics . In some cases, you may have to specify the interface name to be used.

# The QuickOPC package is needed. Install it using "pip install opclabs_quickopc".
import opclabs_quickopc
import time

# Import .NET namespaces.
from OpcLabs.BaseLib import *
from OpcLabs.EasyOpc.UA.PubSub import *
from OpcLabs.EasyOpc.UA.PubSub.OperationModel import *


def dataSetMessage(sender, e):
    # Display the dataset.
    if e.Succeeded:
        # An event with null DataSetData just indicates a successful connection.
        if e.DataSetData is not None:
            print('')
            print('Dataset data: ', e.DataSetData, sep='')
            for pair in e.DataSetData.FieldDataDictionary:
                print(pair)
    else:
        print('')
        print('*** Failure: ', e.ErrorMessageBrief, sep='')


def resolverAccess(sender, e):
    # Display resolution information.
    print(e)


# Define the PubSub resolver. We want the information be resolved from a PubSub binary configuration file that
# we have. The file itself is in this script's directory.
pubSubResolverDescriptor = UAPubSubResolverDescriptor.File(ResourceDescriptor('UADemoPublisher-Default.uabinary'))

# Define the PubSub connection we will work with, using its logical name in the PubSub configuration.
pubSubConnectionDescriptor = UAPubSubConnectionDescriptor()
pubSubConnectionDescriptor.Name = 'FixedLayoutConnection'
# In some cases you may have to set the interface (network adapter) name that needs to be used, similarly to
# the statement below. Your actual interface name may differ, of course.
#pubSubConnectionDescriptor.ResourceAddress.InterfaceName = 'Ethernet'

# Define the filter. The writer group and the dataset writer are specified using their logical names in the
# PubSub configuration. The publisher Id in the filter will be taken from the logical PubSub connection.
filter = UASubscribeDataSetFilter(
    UAWriterGroupDescriptor('FixedLayoutGroup'),
    UADataSetWriterDescriptor('SimpleWriter'))

# Instantiate the subscriber object and hook events.
subscriber = EasyUASubscriber()
subscriber.DataSetMessage += dataSetMessage
subscriber.ResolverAccess += resolverAccess

print('Subscribing...')
IEasyUASubscriberExtension.SubscribeDataSet(subscriber,
                                            pubSubResolverDescriptor,
                                            pubSubConnectionDescriptor,
                                            filter)

print('Processing dataset message events for 20 seconds...')
time.sleep(20)

print('Unsubscribing...')
subscriber.UnsubscribeAllDataSets()

print('Waiting for 1 second...')
# Unsubscribe operation is asynchronous, messages may still come for a short while.
time.sleep(1)

subscriber.DataSetMessage -= dataSetMessage
subscriber.ResolverAccess -= resolverAccess

print('Finished.')
' This example shows how to subscribe to dataset messages and specify a filter, resolving logical parameters to physical
' from an OPC-UA PubSub configuration file in binary format. The metadata obtained through the resolution is used to decode
' fixed layout messages with RawData field encoding.
'
' In order to produce network messages for this example, run the UADemoPublisher tool. For documentation, see
' http://kb.opclabs.com/UADemoPublisher_Basics . In some cases, you may have to specify the interface name to be used.

Imports OpcLabs.EasyOpc.UA.PubSub
Imports OpcLabs.EasyOpc.UA.PubSub.OperationModel

Namespace PubSub._EasyUASubscriber
    Partial Friend Class SubscribeDataSet
        Public Shared Sub ResolveFromFile()

            ' Define the PubSub resolver. We want the information be resolved from a PubSub binary configuration file that
            ' we have. The file itself is at the root of the project, and we have specified that it has to be copied to the
            ' project's output directory.
            Dim pubSubResolverDescriptor = UAPubSubResolverDescriptor.File("UADemoPublisher-Default.uabinary")

            ' Define the PubSub connection we will work with, using its logical name in the PubSub configuration.
            Dim pubSubConnectionDescriptor = New UAPubSubConnectionDescriptor
            pubSubConnectionDescriptor.Name = "FixedLayoutConnection"
            ' In some cases you may have to set the interface (network adapter) name that needs to be used, similarly to
            ' the statement below. Your actual interface name may differ, of course.
            ' pubSubConnectionDescriptor.ResourceAddress.InterfaceName = "Ethernet"

            ' Define the filter. The writer group And the dataset writer are specified using their logical names in the
            ' PubSub configuration. The publisher Id in the filter will be taken from the logical PubSub connection.
            Dim filter = New UASubscribeDataSetFilter("FixedLayoutGroup", "SimpleWriter")

            ' Instantiate the subscriber object and hook events.
            Dim subscriber = New EasyUASubscriber()
            AddHandler subscriber.DataSetMessage, AddressOf subscriber_DataSetMessage_ResolveFromFile
            AddHandler subscriber.ResolverAccess, AddressOf subscriber_ResolverAccess_ResolveFromFile

            Console.WriteLine("Subscribing...")
            subscriber.SubscribeDataSet(pubSubResolverDescriptor, pubSubConnectionDescriptor, filter)

            Console.WriteLine("Processing dataset message events for 20 seconds...")
            Threading.Thread.Sleep(20 * 1000)

            Console.WriteLine("Unsubscribing...")
            subscriber.UnsubscribeAllDataSets()

            Console.WriteLine("Waiting for 1 second...")
            ' Unsubscribe operation is asynchronous, messages may still come for a short while.
            Threading.Thread.Sleep(1 * 1000)

            Console.WriteLine("Finished...")
        End Sub

        Private Shared Sub subscriber_DataSetMessage_ResolveFromFile(ByVal sender As Object, ByVal e As EasyUADataSetMessageEventArgs)
            ' Display the dataset.
            If e.Succeeded Then
                ' An event with null DataSetData just indicates a successful connection.
                If e.DataSetData IsNot Nothing Then
                    Console.WriteLine()
                    Console.WriteLine($"Dataset data: {e.DataSetData}")
                    For Each pair As KeyValuePair(Of String, UADataSetFieldData) In e.DataSetData.FieldDataDictionary
                        Console.WriteLine(pair)
                    Next
                End If
            Else
                Console.WriteLine()
                Console.WriteLine($"*** Failure: {e.ErrorMessageBrief}")
            End If
        End Sub

        Private Shared Sub subscriber_ResolverAccess_ResolveFromFile(ByVal sender As Object, ByVal e As EasyUAResolverAccessEventArgs)
            ' Display resolution information.
            Console.WriteLine(e)
        End Sub
    End Class



    ' Example output
    '
    'Subscribing...
    'Processing dataset message events for 20 seconds...
    '
    '[PublisherFile: UADemoPublisher-Default.uabinary] (no exception)
    '
    'Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
    '[BoolToggle, False {System.Boolean}; Good]
    '[Int32, 3072 {System.Int32}; Good]
    '[Int32Fast, 894 {System.Int32}; Good]
    '[DateTime, 10/1/2019 12:21:14 PM {System.DateTime}; Good]
    '
    'Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
    '[BoolToggle, False {System.Boolean}; Good]
    '[Int32, 3072 {System.Int32}; Good]
    '[Int32Fast, 920 {System.Int32}; Good]
    '[DateTime, 10/1/2019 12:21:14 PM {System.DateTime}; Good]
    '
    'Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
    '[BoolToggle, False {System.Boolean}; Good]
    '[Int32, 3073 {System.Int32}; Good]
    '[Int32Fast, 1003 {System.Int32}; Good]
    '[DateTime, 10/1/2019 12:21:15 PM {System.DateTime}; Good]
    '
    'Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
    '[BoolToggle, False {System.Boolean}; Good]
    '[Int32, 3073 {System.Int32}; Good]
    '[Int32Fast, 1074 {System.Int32}; Good]
    '[DateTime, 10/1/2019 12:21:15 PM {System.DateTime}; Good]
    '
    'Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
    '[BoolToggle, True {System.Boolean}; Good]
    '[Int32, 3074 {System.Int32}; Good]
    '[Int32Fast, 1140 {System.Int32}; Good]
    '[DateTime, 10/1/2019 12:21:16 PM {System.DateTime}; Good]
    '...

End Namespace
// This example shows how to subscribe to dataset messages and specify a filter, resolving logical parameters to physical
// from an OPC-UA PubSub configuration file in binary format. The metadata obtained through the resolution is used to decode
// fixed layout messages with RawData field encoding.
//
// In order to produce network messages for this example, run the UADemoPublisher tool. For documentation, see
// http://kb.opclabs.com/UADemoPublisher_Basics . In some cases, you may have to specify the interface name to be used.

type
  TSubscriberEventHandlers81 = class
    procedure OnDataSetMessage(
      ASender: TObject;
      sender: OleVariant;
      const eventArgs: _EasyUADataSetMessageEventArgs);
    procedure OnResolverAccess(
      ASender: TObject;
      sender: OleVariant;
      const eventArgs: _EasyUAResolverAccessEventArgs);
  end;

class procedure SubscribeDataSet.ResolveFromFile;
var
  SubscribeDataSetArguments: _EasyUASubscribeDataSetArguments;
  Subscriber: TEasyUASubscriber;
  SubscriberEventHandlers: TSubscriberEventHandlers81;
begin
  SubscribeDataSetArguments := CoEasyUASubscribeDataSetArguments.Create;

  // Define the PubSub connection we will work with, using its logical name in the PubSub configuration.
  SubscribeDataSetArguments.DataSetSubscriptionDescriptor.ConnectionDescriptor.Name := 'FixedLayoutConnection';
  // In some cases you may have to set the interface (network adapter) name that needs to be used, similarly to
  // the statement below. Your actual interface name may differ, of course.
  SubscribeDataSetArguments.DataSetSubscriptionDescriptor.ConnectionDescriptor.ResourceAddress.InterfaceName := 'Ethernet';

  // Define the filter. The writer group and the dataset writer are specified using their logical names in the
  // PubSub configuration. The publisher Id in the filter will be taken from the logical PubSub connection.
  SubscribeDataSetArguments.DataSetSubscriptionDescriptor.Filter.WriterGroupDescriptor.Name := 'FixedLayoutGroup';
  SubscribeDataSetArguments.DataSetSubscriptionDescriptor.Filter.DataSetWriterDescriptor.Name := 'SimpleWriter';

  // Define the PubSub resolver. We want the information be resolved from a PubSub binary configuration file that
  // we have. The file itself is included alongside the script.
  SubscribeDataSetArguments.DataSetSubscriptionDescriptor.ResolverDescriptor.PublisherFileResourceDescriptor.UrlString := 'UADemoPublisher-Default.uabinary';
  SubscribeDataSetArguments.DataSetSubscriptionDescriptor.ResolverDescriptor.ResolverKind := UAPubSubResolverKind_PublisherFile;

  // Instantiate the subscriber object and hook events.
  Subscriber := TEasyUASubscriber.Create(nil);
  SubscriberEventHandlers := TSubscriberEventHandlers81.Create;
  Subscriber.OnDataSetMessage := SubscriberEventHandlers.OnDataSetMessage;
  Subscriber.OnResolverAccess := SubscriberEventHandlers.OnResolverAccess;

  WriteLn('Subscribing...');
  Subscriber.SubscribeDataSet(SubscribeDataSetArguments);

  WriteLn('Processing dataset message for 20 seconds...');
  PumpSleep(20*1000);

  WriteLn('Unsubscribing...');
  Subscriber.UnsubscribeAllDataSets;

  WriteLn('Waiting for 1 second...');
  // Unsubscribe operation is asynchronous, messages may still come for a short while.
  PumpSleep(1*1000);

  WriteLn('Finished.');
  FreeAndNil(Subscriber);
  FreeAndNil(SubscriberEventHandlers);
end;

procedure TSubscriberEventHandlers81.OnDataSetMessage(
  ASender: TObject;
  sender: OleVariant;
  const eventArgs: _EasyUADataSetMessageEventArgs);
var
  Count: Cardinal;
  DictionaryEntry2: _DictionaryEntry2;
  Element: OleVariant;
  FieldDataDictionaryEnumerator: IEnumVariant;
begin
  // Display the dataset.
  if eventArgs.Succeeded then
  begin
    // An event with null DataSetData just indicates a successful connection.
    if eventArgs.DataSetData <> nil then
    begin
      WriteLn;
      WriteLn('Dataset data: ', eventArgs.DataSetData.ToString);
      FieldDataDictionaryEnumerator := eventArgs.DataSetData.FieldDataDictionary.GetEnumerator;
      while (FieldDataDictionaryEnumerator.Next(1, Element, Count) = S_OK) do
      begin
        DictionaryEntry2 := IUnknown(Element) as _DictionaryEntry2;
        WriteLn(DictionaryEntry2.ToString);
      end;
    end;
  end
  else begin
    WriteLn;
      WriteLn('*** Failure: ', eventArgs.ErrorMessageBrief);
  end;
end;

procedure TSubscriberEventHandlers81.OnResolverAccess(
  ASender: TObject;
  sender: OleVariant;
  const eventArgs: _EasyUAResolverAccessEventArgs);
begin
  // Display resolution information.
  WriteLn(eventArgs.ToString);
end;

// Example output:
//
//Subscribing...
//Processing dataset message events for 20 seconds...
//[PublisherFile: UADemoPublisher-Default.uabinary] (no exception)
//
//Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
//[BoolToggle, False {System.Boolean}; Good]
//[Int32, 3072 {System.Int32}; Good]
//[Int32Fast, 894 {System.Int32}; Good]
//[DateTime, 10/1/2019 12:21:14 PM {System.DateTime}; Good]
//
//Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
//[BoolToggle, False {System.Boolean}; Good]
//[Int32, 3072 {System.Int32}; Good]
//[Int32Fast, 920 {System.Int32}; Good]
//[DateTime, 10/1/2019 12:21:14 PM {System.DateTime}; Good]
//
//Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
//[BoolToggle, False {System.Boolean}; Good]
//[Int32, 3073 {System.Int32}; Good]
//[Int32Fast, 1003 {System.Int32}; Good]
//[DateTime, 10/1/2019 12:21:15 PM {System.DateTime}; Good]
//
//Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
//[BoolToggle, False {System.Boolean}; Good]
//[Int32, 3073 {System.Int32}; Good]
//[Int32Fast, 1074 {System.Int32}; Good]
//[DateTime, 10/1/2019 12:21:15 PM {System.DateTime}; Good]
//
//Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
//[BoolToggle, True {System.Boolean}; Good]
//[Int32, 3074 {System.Int32}; Good]
//[Int32Fast, 1140 {System.Int32}; Good]
//[DateTime, 10/1/2019 12:21:16 PM {System.DateTime}; Good]
//
//...
Rem This example shows how to subscribe to dataset messages and specify a filter, resolving logical parameters to physical
Rem from an OPC-UA PubSub configuration file in binary format. The metadata obtained through the resolution is used to decode
Rem fixed layout messages with RawData field encoding.
Rem
Rem In order to produce network messages for this example, run the UADemoPublisher tool. For documentation, see
Rem http://kb.opclabs.com/UADemoPublisher_Basics . In some cases, you may have to specify the interface name to be used.

Option Explicit

Const UAPubSubResolverKind_PublisherFile = 3

Dim SubscribeDataSetArguments: Set SubscribeDataSetArguments = CreateObject("OpcLabs.EasyOpc.UA.PubSub.OperationModel.EasyUASubscribeDataSetArguments")

' Define the PubSub connection we will work with, using its logical name in the PubSub configuration.
SubscribeDataSetArguments.DataSetSubscriptionDescriptor.ConnectionDescriptor.Name = "FixedLayoutConnection"
' In some cases you may have to set the interface (network adapter) name that needs to be used, similarly to
' the statement below. Your actual interface name may differ, of course.
SubscribeDataSetArguments.DataSetSubscriptionDescriptor.ConnectionDescriptor.ResourceAddress.InterfaceName = "Ethernet"

' Define the filter. The writer group and the dataset writer are specified using their logical names in the
' PubSub configuration. The publisher Id in the filter will be taken from the logical PubSub connection.
SubscribeDataSetArguments.DataSetSubscriptionDescriptor.Filter.WriterGroupDescriptor.Name = "FixedLayoutGroup"
SubscribeDataSetArguments.DataSetSubscriptionDescriptor.Filter.DataSetWriterDescriptor.Name = "SimpleWriter"

' Define the PubSub resolver. We want the information be resolved from a PubSub binary configuration file that
' we have. The file itself is included alongside the script.
SubscribeDataSetArguments.DataSetSubscriptionDescriptor.ResolverDescriptor.PublisherFileResourceDescriptor.UrlString = "UADemoPublisher-Default.uabinary"
SubscribeDataSetArguments.DataSetSubscriptionDescriptor.ResolverDescriptor.ResolverKind = UAPubSubResolverKind_PublisherFile

' Instantiate the subscriber object and hook events.
Dim Subscriber: Set Subscriber = CreateObject("OpcLabs.EasyOpc.UA.PubSub.EasyUASubscriber")
WScript.ConnectObject Subscriber, "Subscriber_"

WScript.Echo "Subscribing..."
Subscriber.SubscribeDataSet SubscribeDataSetArguments

WScript.Echo "Processing dataset message events for 20 seconds..."
WScript.Sleep 20*1000

WScript.Echo "Unsubscribing..."
Subscriber.UnsubscribeAllDataSets

WScript.Echo "Waiting for 1 second..."
' Unsubscribe operation is asynchronous, messages may still come for a short while.
WScript.Sleep 1*1000

WScript.Echo "Finished."



Sub Subscriber_DataSetMessage(Sender, e)
    ' Display the dataset.
    If e.Succeeded Then
        ' An event with null DataSetData just indicates a successful connection.
        If Not (e.DataSetData Is Nothing) Then
            WScript.Echo
            WScript.Echo "Dataset data: " & e.DataSetData
            Dim Pair: For Each Pair in e.DataSetData.FieldDataDictionary
                WScript.Echo Pair
            Next
        End If
    Else
        WScript.Echo
        WScript.Echo "*** Failure: " & e.ErrorMessageBrief
    End If
End Sub

Sub Subscriber_ResolverAccess(Sender, e)
    ' Display resolution information.
    WScript.Echo e
End Sub



' Example output:
'
'Subscribing...
'Processing dataset message events for 20 seconds...
'[PublisherFile: UADemoPublisher-Default.uabinary] (no exception)
'
'Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
'[BoolToggle, False {System.Boolean}; Good]
'[Int32, 3072 {System.Int32}; Good]
'[Int32Fast, 894 {System.Int32}; Good]
'[DateTime, 10/1/2019 12:21:14 PM {System.DateTime}; Good]
'
'Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
'[BoolToggle, False {System.Boolean}; Good]
'[Int32, 3072 {System.Int32}; Good]
'[Int32Fast, 920 {System.Int32}; Good]
'[DateTime, 10/1/2019 12:21:14 PM {System.DateTime}; Good]
'
'Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
'[BoolToggle, False {System.Boolean}; Good]
'[Int32, 3073 {System.Int32}; Good]
'[Int32Fast, 1003 {System.Int32}; Good]
'[DateTime, 10/1/2019 12:21:15 PM {System.DateTime}; Good]
'
'Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
'[BoolToggle, False {System.Boolean}; Good]
'[Int32, 3073 {System.Int32}; Good]
'[Int32Fast, 1074 {System.Int32}; Good]
'[DateTime, 10/1/2019 12:21:15 PM {System.DateTime}; Good]
'
'Dataset data: Good; Data; publisher=(UInt16)30, group=101, fields: 4
'[BoolToggle, True {System.Boolean}; Good]
'[Int32, 3074 {System.Int32}; Good]
'[Int32Fast, 1140 {System.Int32}; Good]
'[DateTime, 10/1/2019 12:21:16 PM {System.DateTime}; Good]
'
'...
Inheritance Hierarchy

System.Object
   OpcLabs.BaseLib.Object2
      OpcLabs.BaseLib.Info
         OpcLabs.EasyOpc.UA.PubSub.UAPubSubObjectDescriptor
            OpcLabs.EasyOpc.UA.PubSub.UADataSetFieldDescriptor
            OpcLabs.EasyOpc.UA.PubSub.UADataSetWriterDescriptor
            OpcLabs.EasyOpc.UA.PubSub.UAPubSubConnectionDescriptor
            OpcLabs.EasyOpc.UA.PubSub.UAPubSubGroupDescriptor

Requirements

Target Platforms: .NET Framework: Windows 10 (selected versions), Windows 11 (selected versions), Windows Server 2016, Windows Server 2022; .NET: Linux, macOS, Microsoft Windows

See Also