.NET Based OPC UA Client/Server SDK  3.1.0.500
Exposing Structured Data Type Members in the Address Space

Table of Contents

Overview

There are use cases where one might want to create VariableTypes that expose fields of a structure to the children of the VariableType:

  • Clients expected to connect to the server cannot deal with structured DataTypes.
  • Clients are only interested in accessing specific fields of the structure.

There are several VariableTypes in namespace 0 having children which expose fields of the structure. An example is ServerStatusType. The DataType of its Value attribute is a structured DataType, the ServerStatusDataType. This structure has the fields StartTime, CurrentTime, State, BuildInfo SecondsTillShutdown, and ShutdownReason. For each of these fields, the ServerStatusType has a corresponding child. The instance of this type in the Demo Server address space is the ServerStatus variable of the Server object.

This example describes a simplified case which is also shown in the Demo Server: The structured DataType WorkOrderType and the corresponding WorkOrderVariableType. The instances of the latter are WorkOrderVariable and WorkOrderVariable2 in the folder Objects → Demo → 015_WorkOrder in the Demo Server Address Space.

When using such a scenario, the developer has to ensure that when the value of the instance is changed, the value of the child is changed as well (and vice versa).

Implementation

The implementation can be found in the files Demo → DemoNodeManager.cs and Controllers → WorkOrderVariableModelController.cs

The Type nodes and the instance nodes are created when importing the Demo Server address space from the file demoserver.xml, which is not explained in this section.

We use the method LinkModelToNode to keep the values of the instances of WorkOrderVariableType and its children consistent (see DemoNodeManager.cs).

private void SetupWorkOrderModel()
{
NodeId WorkOrderId = new NodeId(Model.Variables.Demo_WorkOrder_WorkOrderVariable, DefaultNamespaceIndex);
Model.WorkOrderType value = new Model.WorkOrderType();
SetVariableDefaultValue(WorkOrderId, new Variant(value));
Model.WorkOrderVariableModel model = new Model.WorkOrderVariableModel()
{
Value = value
};
LinkModelToNode(WorkOrderId, model, null, null, 0);
WorkOrderId = new NodeId(Model.Variables.Demo_WorkOrder_WorkOrderVariable2, DefaultNamespaceIndex);
value = new Model.WorkOrderType();
SetVariableDefaultValue(WorkOrderId, new Variant(value));
model = new Model.WorkOrderVariableModel()
{
Value = value
};
LinkModelToNode(WorkOrderId, model, null, null, 0);
}

The remaining parts of the implementation can be found in the file WorkOrderVariableModelController.cs.

We have to extend the auto-generated class WorkOrderVariableModel by overriding GetModelHandle to assign custom getters and setters.

Check arguments:

private WorkOrderVariableModel m_model;
m_model = instance as WorkOrderVariableModel;
if (m_model == null)
{
return null;
}

Get a default model handle

ModelHandle handle = ModelMapper.GetModelHandle(namespaceUris, m_model);

Assign custom handlers to each field

ushort nsIdx = (ushort)namespaceUris.IndexOf(Model.Namespaces.Model);
foreach (ModelMapping mapping in handle.Mappings)
{
if (mapping.BrowsePath.Length == 1)
{
if (mapping.BrowsePath[0] == new QualifiedName(BrowseNames.AssetID, nsIdx))
{
mapping.MappingData = new Delegate[] {
// the ModelMappingGetValueHandler shall be the first handler in the array
new ModelMappingGetValueHandler(GetAssetID),
// the ModelMappingSetValueHandler shall be the second handler in the array
};
}
private DataValue GetAssetID(IMapperContext context)
{
return new DataValue()
{
WrappedValue = new Variant(m_model.Value.AssetID, TypeInfo.Scalars.String),
SourceTimestamp = DateTime.UtcNow,
ServerTimestamp = DateTime.UtcNow
};
}
private void SetAssetID(IMapperContext context, DataValue AssetId)
{
m_model.Value.AssetID = AssetId.WrappedValue.ToString();
}