How-to guide
Data types at runtime
Define structures and enumerations at runtime, publish them in the address space, and provide their values without your own .NET types.
How-to guide
Data types at runtime
Types usedOpcServerOpcStructOpcStructFieldOpcEnumOpcEnumFieldOpcDataTypeNodeOpcDataObject
You usually define custom data types through .NET types and attributes. The Provide data types section describes this. If the data types are only known at runtime, for example because they come from a configuration or a connected PLC, define them through the OpcStruct (structures) and OpcEnum (enumerations) classes instead. The server provides the values of such data types as instances of OpcDataObject, without any .NET types of your own.
Enable dynamic data types
To let the server encode values of dynamically defined data types, enable the server's UseDynamic property. By default, it follows OpcAutomatism.UseDynamic, which is disabled:
server.UseDynamic = true;server.UseDynamic = TrueDefine structures
To define a structure, call the DefineType method of the OpcStruct class. It takes the OpcNodeId and the name of the data type as well as its fields as instances of OpcStructField. Each field consists of its name, the OpcNodeId of its data type (for example from the OpcDataTypes class), and an optional description. The AsArray method turns a field into an array, the AsOptional method into an optional field:
var machineDataTypeId = new OpcNodeId("MachineData", namespaceIndex: 2);
var machineDataType = OpcStruct.DefineType(
machineDataTypeId,
new OpcName("MachineData", namespaceIndex: 2),
new OpcStructField("Speed", OpcDataTypes.Int32),
new OpcStructField("JobName", OpcDataTypes.String),
new OpcStructField("Temperatures", OpcDataTypes.Double).AsArray(),
new OpcStructField("Comment", OpcDataTypes.String, description: "Optional comment.")
.AsOptional());Dim machineDataTypeId = New OpcNodeId("MachineData", namespaceIndex:=2)
Dim machineDataType = OpcStruct.DefineType( _
machineDataTypeId, _
New OpcName("MachineData", namespaceIndex:=2), _
New OpcStructField("Speed", OpcDataTypes.Int32), _
New OpcStructField("JobName", OpcDataTypes.String), _
New OpcStructField("Temperatures", OpcDataTypes.Double).AsArray(), _
New OpcStructField("Comment", OpcDataTypes.String, description:="Optional comment.") _
.AsOptional())If a structure should extend another one, define it through the DefineSubType method and specify the base type:
var extendedDataType = OpcStruct.DefineSubType(
machineDataTypeId,
new OpcNodeId("ExtendedMachineData", namespaceIndex: 2),
new OpcName("ExtendedMachineData", namespaceIndex: 2),
new OpcStructField("Operator", OpcDataTypes.String));Dim extendedDataType = OpcStruct.DefineSubType( _
machineDataTypeId, _
New OpcNodeId("ExtendedMachineData", namespaceIndex:=2), _
New OpcName("ExtendedMachineData", namespaceIndex:=2), _
New OpcStructField("Operator", OpcDataTypes.String))Define enumerations
You define an enumeration through the OpcEnum class in one of two ways:
- With the
DefineStringsmethod as a list of names (EnumStrings). The value of an entry is its index. - With the
DefineValuesmethod as a list of name-value pairs (EnumValues). The values do not have to be consecutive, and each entry can have a description.
var jobKindId = new OpcNodeId("JobKind", namespaceIndex: 2);
var jobKind = OpcEnum.DefineStrings(
jobKindId,
new OpcName("JobKind", namespaceIndex: 2),
new OpcEnumField("Production"),
new OpcEnumField("Maintenance"));
var errorCode = OpcEnum.DefineValues(
new OpcNodeId("ErrorCode", namespaceIndex: 2),
new OpcName("ErrorCode", namespaceIndex: 2),
new OpcEnumField("None", value: 0),
new OpcEnumField("Overheated", value: 10, description: "The temperature exceeded the limit."),
new OpcEnumField("OutOfMaterial", value: 20, description: "The material supply is empty."));Dim jobKindId = New OpcNodeId("JobKind", namespaceIndex:=2)
Dim jobKind = OpcEnum.DefineStrings( _
jobKindId, _
New OpcName("JobKind", namespaceIndex:=2), _
New OpcEnumField("Production"), _
New OpcEnumField("Maintenance"))
Dim errorCode = OpcEnum.DefineValues( _
New OpcNodeId("ErrorCode", namespaceIndex:=2), _
New OpcName("ErrorCode", namespaceIndex:=2), _
New OpcEnumField("None", value:=0), _
New OpcEnumField("Overheated", value:=10, description:="The temperature exceeded the limit."), _
New OpcEnumField("OutOfMaterial", value:=20, description:="The material supply is empty."))Create variables with dynamic data types
You create variables that use a dynamically defined data type in the node manager as usual. The DataTypeId property sets their data type:
// MyNodeManager.CreateNodes(...)
var machineDataNode = new OpcDataVariableNode(machineNode, "Data");
machineDataNode.DataTypeId = machineDataTypeId;
var jobKindNode = new OpcDataVariableNode(machineNode, "JobKind");
jobKindNode.DataTypeId = jobKindId;'MyNodeManager.CreateNodes(...)
Dim machineDataNode = New OpcDataVariableNode(machineNode, "Data")
machineDataNode.DataTypeId = machineDataTypeId
Dim jobKindNode = New OpcDataVariableNode(machineNode, "JobKind")
jobKindNode.DataTypeId = jobKindIdPublish data types
The server publishes the data types through data type nodes. The Create method of the OpcDataTypeNode class creates these nodes from the respective definition. Because the server resolves the data types of the fields to create these nodes, add them only after the server has started, through the AddNode method of the node manager:
server.Start();
// The data type nodes are added after the server has been started.
nodeManager.AddNode(OpcDataTypeNode.Create(machineDataType));
nodeManager.AddNode(OpcDataTypeNode.Create(jobKind));
nodeManager.AddNode(OpcDataTypeNode.Create(errorCode));server.Start()
'The data type nodes are added after the server has been started.
nodeManager.AddNode(OpcDataTypeNode.Create(machineDataType))
nodeManager.AddNode(OpcDataTypeNode.Create(jobKind))
nodeManager.AddNode(OpcDataTypeNode.Create(errorCode))Provide values
You create a value of a structure as an OpcDataObject. The server's GetDataType method returns the data type it needs as soon as the data type node is published. You access the fields by their name. Optional fields to which you assign no value stay empty. The value of an enumeration is a number: the index for EnumStrings, the value of the entry for EnumValues:
var dataType = server.GetDataType(machineDataTypeId);
var data = new OpcDataObject(dataType);
data["Speed"].Value = 1200;
data["JobName"].Value = "JOB0815";
data["Temperatures"].Value = new double[] { 20.5, 21.0, 21.3 };
machineDataNode.Value = data;
machineDataNode.ApplyChanges(server.SystemContext);
// Enumeration values are numbers (index or value of the field), here: Maintenance.
jobKindNode.Value = 1;
jobKindNode.ApplyChanges(server.SystemContext);Dim dataType = server.GetDataType(machineDataTypeId)
Dim data = New OpcDataObject(dataType)
data("Speed").Value = 1200
data("JobName").Value = "JOB0815"
data("Temperatures").Value = New Double() {20.5, 21.0, 21.3}
machineDataNode.Value = data
machineDataNode.ApplyChanges(server.SystemContext)
'Enumeration values are numbers (index or value of the field), here: Maintenance.
jobKindNode.Value = 1
jobKindNode.ApplyChanges(server.SystemContext)A client that resolves the data types of the server at runtime (for example through OpcClient.UseDynamic) also receives the values of the structure as OpcDataObject. The Structured data page describes this.

