Zum Inhalt springen

Anleitung

Strukturierte Daten

Auf strukturierte Datentypen zugreifen, einfach, namenbasiert, dynamisch oder typisiert, und Datentypen generieren und definieren.

Anleitung

Strukturierte Daten ​

Die folgenden Abschnitte gehen davon aus, dass der Server einen Node mit der NodeId ns=2;s=Machine/Operator bereitstellt. Dieser Node verwendet den fiktiven Datentyp StaffType. Der Datentyp hat die folgende Struktur:

StaffType
  .Name : string
  .ID : int
  .Shift : ShiftInfoType
    .Name : string
    .Elapsed : DateTime
    .Remaining : byte

Einfachster Zugriff mit dynamic ​

Beteiligte TypenOpcClientOpcNodeIdOpcValueOpcDataObject

Für den einfachen Zugriff auf Werte von Variablen-Nodes mit strukturierten Daten unterstützt das OPC UA .NET SDK das Schlüsselwort dynamic. Zugriffe auf Variablen, die mit dynamic deklariert sind, wertet .NET zur Laufzeit aus. Sie greifen damit auf die Daten eines strukturierten Datentyps zu, ohne vorher einen .NET-Typ zu implementieren. So sieht ein solcher Zugriff aus:

csharp
client.UseDynamic = true;
client.Connect();
 
dynamic staff = client.ReadNode("ns=2;s=Machine/Operator").Value;
 
// Access the 'Name' and 'ID' field of the data without to declare the data type itself.
// Just use the field names known as they would be defined in a .NET Type.
Console.WriteLine("Name: {0}", staff.Name);
Console.WriteLine("Staff ID: {0}", staff.ID);
 
// Continue accessing subsequently used data types.
Console.WriteLine("Shift: {0}", staff.Shift.Name);
Console.WriteLine("- Time Elapsed: {0}", staff.Shift.Elapsed);
Console.WriteLine("- Jobs Remaining: {0}", staff.Shift.Remaining);
 
// Change Shift
staff.Name = "John";
staff.ID = 4242;
staff.Shift.Name = "Swing Shift";
 
client.WriteNode("ns=2;s=Machine/Operator", staff);
vbnet
client.UseDynamic = True
client.Connect()

Dim staff = client.ReadNode("ns=2;s=Machine/Operator").Value

'Access the 'Name' and 'ID' field of the data without to declare the data type itself.
'Just use the field names known as they would be defined in a .NET Type.
Console.WriteLine("Name: {0}", staff.Name)
Console.WriteLine("Staff ID: {0}", staff.ID)

'Continue accessing subsequently used data types.
Console.WriteLine("Shift: {0}", staff.Shift.Name)
Console.WriteLine("- Time Elapsed: {0}", staff.Shift.Elapsed)
Console.WriteLine("- Jobs Remaining: {0}", staff.Shift.Remaining)

'Change Shift
staff.Name = "John"
staff.ID = 4242
staff.Shift.Name = "Swing Shift"

client.WriteNode("ns=2;s=Machine/Operator", staff)

Namen-basierter Zugriff mit OpcDataObject ​

Beteiligte TypenOpcClientOpcNodeIdOpcValueOpcDataObjectOpcDataField

Für den Namen-basierten Zugriff auf Werte von Variablen-Nodes mit strukturierten Daten verwenden Sie direkt die Klasse OpcDataObject. Wenn Sie die Namen der Felder des Datentyps kennen, greifen Sie so auf die Felder zu:

csharp
client.UseDynamic = true;
client.Connect();
 
OpcDataObject staff = client.ReadNode("ns=2;s=Machine/Operator").As<OpcDataObject>();
 
// Access the 'Name' and 'ID' field of the data without to declare the data type itself.
// Just use the field names known as the 'key' to access the according field value.
Console.WriteLine("Name: {0}", staff["Name"].Value);
Console.WriteLine("Staff ID: {0}", staff["ID"].Value);
 
// Continue accessing subsequently used data types using the OpcDataObject as before.
OpcDataObject shift = (OpcDataObject)staff["Shift"].Value;
 
Console.WriteLine("Shift: {0}", shift["Name"].Value);
Console.WriteLine("- Time Elapsed: {0}", shift["Elapsed"].Value);
Console.WriteLine("- Jobs Remaining: {0}", shift["Remaining"].Value);
 
// Change Shift
staff["Name"].Value = "John";
staff["ID"].Value = 4242;
shift["Name"].Value = "Swing Shift";
 
client.WriteNode("ns=2;s=Machine/Operator", staff);
vbnet
client.UseDynamic = True
client.Connect()

Dim staff As OpcDataObject = client.ReadNode("ns=2;s=Machine/Operator").As(Of OpcDataObject)()

'Access the 'Name' and 'ID' field of the data without to declare the data type itself.
'Just use the field names known as the 'key' to access the according field value.
Console.WriteLine("Name: {0}", staff("Name").Value)
Console.WriteLine("Staff ID: {0}", staff("ID").Value)

'Continue accessing subsequently used data types using the OpcDataObject as before.
Dim shift As OpcDataObject = CType(staff("Shift").Value, OpcDataObject)

Console.WriteLine("Shift: {0}", shift("Name").Value)
Console.WriteLine("- Time Elapsed: {0}", shift("Elapsed").Value)
Console.WriteLine("- Jobs Remaining: {0}", shift("Remaining").Value)

'Change Shift
staff("Name").Value = "John"
staff("ID").Value = 4242
shift("Name").Value = "Swing Shift"

client.WriteNode("ns=2;s=Machine/Operator", staff)

Typinformationen für den dynamischen Zugriff laden ​

Beteiligte TypenOpcClientOpcNodeSetOpcAutomatismOpcDataTypeSystemOpcRawObjectOpcData

Für den Zugriff mit dynamic (Abschnitt Einfachster Zugriff mit dynamic) und den Namen-basierten Zugriff (Abschnitt Namen-basierter Zugriff mit OpcDataObject) benötigt der Client Informationen über die Datentypen des Servers. In Visual Basic entspricht dynamic einer nicht typisierten Deklaration mit Dim. Die Klasse OpcClient ruft diese Informationen beim Aufruf von Connect() automatisch vom Server ab, wenn Sie zuvor die Eigenschaft UseDynamic auf true setzen. Diese Eigenschaft gibt es auf der Instanz von OpcClient und auf der statischen Klasse OpcAutomatism.

Mit dieser Einstellung lädt der Client die notwendigen Typinformationen vom Server. Nach dem Aufruf von Connect() greifen Sie dann ohne eigenen Code für die Typen auf die Daten zu, wie in den vorherigen Abschnitten gezeigt.

Wenn eine UANodeSet.xml oder eine andere XML-Datei mit der Beschreibung des Servers vorliegt, können Sie diese Datei auch in die Clientanwendung laden. Der Inhalt einer solchen Datei beginnt mit UANodeSet. Der Client bezieht die Informationen dann aus dem NodeSet und muss die Typinformationen nicht beim Verbindungsaufbau abrufen. So laden Sie das NodeSet:

csharp
using (var client = new OpcClient("opc.tcp://localhost:4840")) {
    client.NodeSet = OpcNodeSet.Load(@"..\Resources\MyServersNodeSet.xml");
    client.UseDynamic = true;
 
    client.Connect();
    dynamic staff = client.ReadNode("ns=2;s=Machine/Operator").Value;
 
    Console.WriteLine("Name: {0}", staff.Name);
    Console.WriteLine("Staff ID: {0}", staff.ID);
}
vbnet
Using client = New OpcClient("opc.tcp://localhost:4840")
    client.NodeSet = OpcNodeSet.Load("..\Resources\MyServersNodeSet.xml")
    client.UseDynamic = True

    client.Connect()
    Dim staff = client.ReadNode("ns=2;s=Machine/Operator").Value

    Console.WriteLine("Name: {0}", staff.Name)
    Console.WriteLine("Staff ID: {0}", staff.ID)
End Using

Wenn ein NodeSet gesetzt ist, liefert die Methode GetDataTypeSystem() der Klasse OpcClient eine Instanz von OpcDataTypeSystem, die das Typsystem aus dem NodeSet beschreibt. Wenn Sie kein NodeSet über die Eigenschaft NodeSet festlegen, beschreibt die gelieferte Instanz das Typsystem des Servers. Die Informationen dazu fragt der Client bei Bedarf vom Server ab. In beiden Fällen setzt der Aufruf eine bestehende Verbindung zum Server voraus. Umfangreiche Typsysteme können die Grenzen der Datenübertragung überschreiten (Abschnitt Grenzen der Datenübertragung).

Unbekannte Strukturen: Wenn der Client den Datentyp einer Struktur nicht kennt oder ein Feld keinen festen ValueRank hat, kann er den Wert nicht dekodieren. Er liefert ihn dann als OpcRawObject. Dessen Eigenschaft Encoding nennt die Kodierung (binär oder XML), die Eigenschaft RawData enthält den kodierten Inhalt. Mit der Methode Decode<T> dekodieren Sie den Inhalt, sobald der Typ bekannt ist. Umgekehrt kodiert OpcData.Encode eine Struktur als OpcRawObject.

Typisierter Zugriff mit eigenen .NET-Typen ​

Beteiligte TypenOpcClientOpcNodeIdOpcDataTypeAttributeOpcDataTypeEncodingAttribute

Für den typisierten Zugriff definieren Sie .NET-Typen in der Clientanwendung so, wie sie im Server definiert sind. Alle nötigen Metadaten stellen Attribute bereit. Typen mit diesen Attributen registriert das SDK standardmäßig automatisch beim Verbindungsaufbau. Abschalten lässt sich das über die Eigenschaft OpcAutomatism.UseDynamicTypeRegistration. So definieren Sie .NET-Typen für strukturierte Datentypen:

csharp
[OpcDataType("ns=2;s=StaffType")]
[OpcDataTypeEncoding("ns=2;s=StaffType-Binary")]
public class Staff
{
    public string Name { get; set; }
    public int ID { get; set; }
    public ShiftInfo Shift { get; set; }
}
 
[OpcDataType("ns=2;s=ShiftInfoType")]
[OpcDataTypeEncoding("ns=2;s=ShiftInfoType-Binary")]
public class ShiftInfo
{
    public string Name { get; set; }
    public DateTime Elapsed { get; set; }
    public byte Remaining { get; set; }
}
vbnet
<OpcDataType("ns=2;s=StaffType")>
<OpcDataTypeEncoding("ns=2;s=StaffType-Binary")>
Public Class Staff
    Public Property Name As String
    Public Property ID As Integer
    Public Property Shift As ShiftInfo
End Class


<OpcDataType("ns=2;s=ShiftInfoType")>
<OpcDataTypeEncoding("ns=2;s=ShiftInfoType-Binary")>
Public Class ShiftInfo
    Public Property Name As String
    Public Property Elapsed As DateTime
    Public Property Remaining As Byte
End Class

Wenn Sie clientseitig einen .NET-Typ für einen serverseitig definierten strukturierten Datentyp definiert haben, verwenden Sie ihn so:

csharp
client.Connect();
Staff staff = client.ReadNode("ns=2;s=Machine/Operator").As<Staff>();
 
// Access the 'Name' and 'ID' field of the data with the declared the data type.
Console.WriteLine("Name: {0}", staff.Name);
Console.WriteLine("Staff ID: {0}", staff.ID);
 
// Continue accessing subsequently used data types.
Console.WriteLine("Shift: {0}", staff.Shift.Name);
Console.WriteLine("- Time Elapsed: {0}", staff.Shift.Elapsed);
Console.WriteLine("- Jobs Remaining: {0}", staff.Shift.Remaining);
 
// Change Shift
staff.Name = "John";
staff.ID = 4242;
staff.Shift.Name = "Swing Shift";
 
client.WriteNode("ns=2;s=Machine/Operator", staff);
vbnet
client.Connect()
Dim staff As Staff = client.ReadNode("ns=2;s=Machine/Operator").As(Of Staff)()

'Access the 'Name' and 'ID' field of the data with the declared the data type.
Console.WriteLine("Name: {0}", staff.Name)
Console.WriteLine("Staff ID: {0}", staff.ID)

'Continue accessing subsequently used data types.
Console.WriteLine("Shift: {0}", staff.Shift.Name)
Console.WriteLine("- Time Elapsed: {0}", staff.Shift.Elapsed)
Console.WriteLine("- Jobs Remaining: {0}", staff.Shift.Remaining)

'Change Shift
staff.Name = "John"
staff.ID = 4242
staff.Shift.Name = "Swing Shift"

client.WriteNode("ns=2;s=Machine/Operator", staff)

Datentypen generieren ​

Für den typisierten Zugriff (Abschnitt Typisierter Zugriff mit eigenen .NET-Typen) können Sie einzelne oder alle Datentypen eines OPC UA Servers mit OPC Watch generieren (Seite Downloads).

Über den Server generieren

So generieren Sie einen einzelnen Datentyp als .NET-Implementierung:

  1. OPC Watch öffnen
  2. Neue Verbindung anlegen (+) und bei Bedarf konfigurieren
  3. Verbindung zum Server herstellen (Stecker-Symbol)
  4. Einen Node auswählen:
    • entweder einen Variablen-Node mit einem strukturierten Datentyp als Wert
    • oder einen Datentyp-Node unter /Types/DataTypes/BaseDataType/Structure oder /Enumeration
  5. Rechtsklick auf diesen Node
  6. „Generate DataType“ klicken
  7. Den Code in die Anwendung einfügen

Alle Datentypen generieren Sie wahlweise in eine Code-Datei (*.cs) oder eine Assembly-Datei (*.dll). So gehen Sie vor:

  1. OPC Watch öffnen
  2. Neue Verbindung anlegen (+) und bei Bedarf konfigurieren
  3. Verbindung zum Server herstellen (Stecker-Symbol)
  4. Den Server-Node auswählen, dessen Name mit opc.tcp:// beginnt
  5. Rechtsklick auf diesen Node
  6. „Generate Models“ klicken
  7. Im Dialog den gewünschten Dateityp auswählen
  8. „Speichern“ klicken
  9. Die Datei dem Projekt hinzufügen

Über ein NodeSet generieren

  1. OPC Watch öffnen
  2. In der oberen rechten Ecke der Anwendung das erste Symbol anklicken
  3. NodeSet-Datei öffnen, meist eine XML-Datei einer Companion-Spezifikation
  4. OPC Watch lädt das NodeSet und zeigt anschließend den Dialog „Speichern unter“ an
  5. Im Dialog den gewünschten Dateityp auswählen
  6. „Speichern“ klicken
  7. Die Datei dem Projekt hinzufügen

Datentypen definieren ​

Beteiligte TypenOpcClientOpcNodeIdOpcDataOpcDataTypeAttributeOpcDataTypeEncodingAttributeOpcDataTypeSystemOpcDataTypeInfo

Statt den Datentyp als .NET-Code oder .NET-Assembly zu generieren (Abschnitt Datentypen generieren), können Sie ihn auch manuell definieren. Implementieren Sie dazu den Typ so, wie der Server ihn definiert. Der .NET-Typ muss also die Felder des strukturierten Datentyps in genau derselben Reihenfolge und mit denselben Typen bereitstellen, egal ob er eine Struktur oder eine Klasse ist. Alle weiteren Metadaten stellen Attribute bereit:

csharp
[OpcDataType("<NodeId of DataType Node>")]
[OpcDataTypeEncoding(
        "<NodeId of Binary Encoding Node>",
        NamespaceUri = "<NamespaceUri.Value of binary Dictionary-Node>")]
internal struct MyDataType
{
    public short FieldA;
    public int FieldB;
    public string FieldC;
    ...
}
vbnet
<OpcDataType("<NodeId of DataType Node>")>
<OpcDataTypeEncoding( _
        "<NodeId of Binary Encoding Node>", _
        NamespaceUri:="<NamespaceUri.Value of binary Dictionary-Node>")>
Friend Structure MyDataType
    Public FieldA As Short
    Public FieldB As Integer
    Public FieldC As String
    ...
End Structure

Die nötigen Informationen erhalten Sie aus dem Handbuch des OPC UA Servers, vom Team, das die SPS programmiert, oder über die Klasse OpcClient. Mit OpcClient untersuchen Sie dazu den Variablen-Node, der den strukturierten Datentyp verwendet:

csharp
OpcNodeInfo node = client.BrowseNode("ns=2;s=Machine/Operator");
 
if (node is OpcVariableNodeInfo variableNode) {
    OpcNodeId dataTypeId = variableNode.DataTypeId;
    OpcDataTypeInfo dataType = client.GetDataTypeSystem().GetType(dataTypeId);
 
    Console.WriteLine(dataType.TypeId);
    Console.WriteLine(dataType.Encoding);
 
    Console.WriteLine(dataType.Name);
 
    foreach (OpcDataFieldInfo field in dataType.GetFields())
        Console.WriteLine(".{0} : {1}", field.Name, field.FieldType);
 
    Console.WriteLine();        
    Console.WriteLine("Data Type Attributes:");
    Console.WriteLine(
            "\t[OpcDataType(\"{0}\")]",
            dataType.TypeId.ToString(OpcNodeIdFormat.Foundation));
    Console.WriteLine(
            "\t[OpcDataTypeEncoding(\"{0}\", NamespaceUri = \"{1}\")]",
            dataType.Encoding.Id.ToString(OpcNodeIdFormat.Foundation),
            dataType.Encoding.Namespace.Value);
}
vbnet
Dim node As OpcNodeInfo = client.BrowseNode("ns=2;s=Machine/Operator")
Dim variableNode As OpcVariableNodeInfo = TryCast(node, OpcVariableNodeInfo)

If variableNode IsNot Nothing Then
    Dim dataTypeId As OpcNodeId = variableNode.DataTypeId
    Dim dataType As OpcDataTypeInfo = client.GetDataTypeSystem().GetType(dataTypeId)

    Console.WriteLine(dataType.TypeId)
    Console.WriteLine(dataType.Encoding)

    Console.WriteLine(dataType.Name)

    For Each field As OpcDataFieldInfo In dataType.GetFields()
        Console.WriteLine(".{0} : {1}", field.Name, field.FieldType)
    Next

    Console.WriteLine()
    Console.WriteLine("Data Type Attributes:")
    Console.WriteLine( _
            vbTab & "[OpcDataType(""{0}"")]", _
            dataType.TypeId.ToString(OpcNodeIdFormat.Foundation))
    Console.WriteLine( _
            vbTab & "[OpcDataTypeEncoding(""{0}"", NamespaceUri = ""{1}"")]", _
            dataType.Encoding.Id.ToString(OpcNodeIdFormat.Foundation), _
            dataType.Encoding.Namespace.Value)
End If

Union-Datentypen ​

Beteiligte TypenOpcClientOpcDataTypeAttributeOpcDataTypeEncodingAttributeIOpcUnion

Ein Union-Datentyp überträgt von mehreren Feldern immer nur eines. Welches Feld übertragen wird, legt ein vorangestellter Auswahlwert fest. Um einen solchen Datentyp zu definieren, implementiert der .NET-Typ die Schnittstelle IOpcUnion. Ihre Eigenschaft SwitchValue enthält den Auswahlwert. Der Wert 1 wählt das erste Feld, der Wert 2 das zweite und so weiter. Beim Wert 0 wird kein Feld übertragen:

csharp
[OpcDataType("<NodeId of DataType Node>")]
[OpcDataTypeEncoding(
        "<NodeId of Binary Encoding Node>",
        NamespaceUri = "<NamespaceUri.Value of binary Dictionary-Node>")]
internal class MyUnionDataType : IOpcUnion
{
    // 0 = no field, 1 = 'Number', 2 = 'Text'.
    public uint SwitchValue { get; set; }
 
    public int Number { get; set; }
 
    public string Text { get; set; }
}
vbnet
<OpcDataType("<NodeId of DataType Node>")>
<OpcDataTypeEncoding( _
        "<NodeId of Binary Encoding Node>", _
        NamespaceUri:="<NamespaceUri.Value of binary Dictionary-Node>")>
Friend Class MyUnionDataType
    Implements IOpcUnion

    '0 = no field, 1 = 'Number', 2 = 'Text'.
    Public Property SwitchValue As UInteger Implements IOpcUnion.SwitchValue

    Public Property Number As Integer

    Public Property Text As String
End Class

Datentypen mit optionalen Feldern ​

Beteiligte TypenOpcClientOpcNodeIdOpcDataOpcDataTypeAttributeOpcDataTypeEncodingAttributeOpcDataTypeEncodingMaskAttributeOpcEncodingMaskKindOpcDataTypeMemberSwitchAttributeOpcMemberSwitchOperator

Um die übertragene Datenmenge zu reduzieren, können Sie bestimmte Felder eines strukturierten Datentyps im Datenstrom als vorhanden oder fehlend kennzeichnen. Das hängt von bestimmten Bedingungen oder vom Wert eines anderen Felds in der Datenstruktur ab. Ob ein Feld vorhanden ist, zeigt also entweder der Wert eines anderen Felds oder ein einzelnes Bit in der EncodingMask. Die EncodingMask ist ein Feld, das als Präambel vor den Daten im Datenstrom kodiert ist. Ihre Größe in Bytes geben Sie im OpcDataTypeEncodingMaskAttribute an. Wenn Sie die Eigenschaft Size nicht explizit festlegen, erhält sie bei OpcEncodingMaskKind.Auto die kleinste nötige Anzahl Bytes, berechnet aus der Anzahl der optionalen Felder.

Optionale Felder, deren Vorhandensein ein Bit der EncodingMask anzeigt, definieren Sie bei OpcEncodingMaskKind.Auto auf drei Arten. Sie verwenden Nullable-Typen (Nullable), das OpcDataTypeMemberSwitchAttribute ohne Parameter (das nächste freie Bit) oder das OpcDataTypeMemberSwitchAttribute mit der Nummer eines bestimmten Bits:

csharp
[OpcDataType("<NodeId of DataType Node>")]
[OpcDataTypeEncoding(
        "<NodeId of Binary Encoding Node>",
        NamespaceUri = "<NamespaceUri.Value of binary Dictionary-Node>")]
[OpcDataTypeEncodingMask(OpcEncodingMaskKind.Auto)]
internal struct MyDataTypeWithOptionalFields
{
    public short FieldA;
    public int FieldB;
    public string FieldC;
 
    // Nullables are treat as optional fields by default.
    // Existence-Indicator-Bit is Bit0 in the encoding mask.
    public uint? OptionalField1;
 
    // Existence-Indicator-Bit is Bit1 (the next unused bit) in the encoding mask.
    [OpcDataTypeMemberSwitch]
    public int OptionalField2;
 
    // Existence-Indicator-Bit is Bit3 (bit 2 is unused) in the encoding mask.
    [OpcDataTypeMemberSwitch(bit: 3)]
    public byte OptionalField3;
}
vbnet
<OpcDataType("<NodeId of DataType Node>")>
<OpcDataTypeEncoding( _
        "<NodeId of Binary Encoding Node>", _
        NamespaceUri:="<NamespaceUri.Value of binary Dictionary-Node>")>
<OpcDataTypeEncodingMask(OpcEncodingMaskKind.Auto)>
Friend Structure MyDataTypeWithOptionalFields
    Public FieldA As Short
    Public FieldB As Integer
    Public FieldC As String

    'Nullables are treat as optional fields by default.
    'Existence-Indicator-Bit is Bit0 in the encoding mask.
    Public OptionalField1 As UInteger?

    'Existence-Indicator-Bit is Bit1 (the next unused bit) in the encoding mask.
    <OpcDataTypeMemberSwitch>
    Public OptionalField2 As Integer

    'Existence-Indicator-Bit is Bit3 (bit 2 is unused) in the encoding mask.
    <OpcDataTypeMemberSwitch(3)>
    Public OptionalField3 As Byte
End Structure

Wenn das Vorhandensein eines Felds stattdessen vom Wert eines vorangehenden Felds abhängen soll, geben Sie beim OpcDataTypeMemberSwitchAttribute den Namen dieses Felds an, optional zusammen mit einem Vergleichswert und einem OpcMemberSwitchOperator. Ein solches Feld belegt kein Bit der EncodingMask:

csharp
[OpcDataType("<NodeId of DataType Node>")]
[OpcDataTypeEncoding(
        "<NodeId of Binary Encoding Node>",
        NamespaceUri = "<NamespaceUri.Value of binary Dictionary-Node>")]
internal struct MyDataTypeWithConditionalFields
{
    public bool FieldD;
 
    // 'OptionalField4' exists only if the value of 'FieldD' is equals 'true'.
    [OpcDataTypeMemberSwitch("FieldD")]
    public string OptionalField4;
 
    public int FieldE;
 
    // 'OptionalField5' exists only if the value of 'FieldE' is greater than '42'.
    [OpcDataTypeMemberSwitch("FieldE", value: 42, @operator: OpcMemberSwitchOperator.GreaterThan)]
    public string OptionalField5;
}
vbnet
<OpcDataType("<NodeId of DataType Node>")>
<OpcDataTypeEncoding( _
        "<NodeId of Binary Encoding Node>", _
        NamespaceUri:="<NamespaceUri.Value of binary Dictionary-Node>")>
Friend Structure MyDataTypeWithConditionalFields
    Public FieldD As Boolean

    ''OptionalField4' exists only if the value of 'FieldD' is equals 'true'.
    <OpcDataTypeMemberSwitch("FieldD")>
    Public OptionalField4 As String

    Public FieldE As Integer

    ''OptionalField5' exists only if the value of 'FieldE' is greater than '42'.
    <OpcDataTypeMemberSwitch("FieldE", 42, OpcMemberSwitchOperator.GreaterThan)>
    Public OptionalField5 As String
End Structure

In OPC UA beschreibt eine DataTypeDefinition optionale Felder nur über Bits der EncodingMask. Andere Clients, die einen solchen Datentyp nur über seine DataTypeDefinition kennen, können ihn deshalb unter Umständen nicht dekodieren.