.NET Based OPC UA Client/Server SDK  3.1.0.500
Read – Read Structure

Prerequisites

A session with the server must be established.

Description

This example demonstrates how to read values having structured data types that are unknown at compile time. Press the button “Show Code” to display the corresponding code, and the button “Help” to show this documentation page.

clienttutorials_read_structure.png

Fill in a Node ID to the respective input field. Press the button “…” to open a tree view for browsing the server’s address space. When clicking the button “Read”, the value is displayed in the box at “Read Response”. Check the box in front of “Use Asynchronous Pattern” to call “BeginRead” instead of “Read”.

The example contains two variants for displaying the value. The text box only supports simple data types and structured data types without optional fields.

clienttutorials_read_structure3.png

In the second variant, values are shown in a tree view. This more complicated example can additionally be used for unions, enumerations, and structured data types with optional fields.

clienttutorials_read_structure2.png

Sample Code

For using SDK functionality to parse value with a (at compile time) unknown structured DataType it is necessary to create an instance of the DataTypeManager first.

m_dataTypeManager = new DataTypeManager(m_parent.Session);

Then we extract the ExtensionObject from the DataValue and pass it to the DataTypeManager

DataValue dv = results[0];
Variant value = dv.WrappedValue;
// Only ExtensionObjects are allowed for this example
if (value.DataType != BuiltInType.ExtensionObject || value.ValueRank != ValueRanks.Scalar)
{
throw new Exception("Only scalar values of structured DataTypes can be displayed in this example");
}
ExtensionObject e = value.ToExtensionObject();
if (e.Encoding == ExtensionObjectEncoding.EncodeableObject)
{
throw new Exception("Only values of structured DataTypes that are not registered at the stack can be shown in this example.");
}
// The DataManager parses the encoded data
GenericEncodeableObject encodeable = m_dataTypeManager.ParseValue(e);

The following paragraphs only show how to print the value to a text box. See the complete example code for the more elaborate tree view.

Structures may be nested, so a recursive method is needed to show a structure’s content.

private void PrintValue(GenericEncodeableObject genericValue)
{
m_text.Text = "";
// Extract the data type description
GenericStructureDataType structuredDataType = genericValue.TypeDefinition;
// We have a structure, process structure elements
if (structuredDataType.TypeClass == GenericDataTypeClass.Structured)
{
m_text.Text = structuredDataType.Name.Name;
// Use a recursive method, because structures can be nested.
PrintValueRec(genericValue, 1);
}
}
private void PrintValueRec(GenericEncodeableObject genericValue, int depth)
{

We get the fields of the structure and iterate over these fields.

GenericStructureDataType structuredDataType = genericValue.TypeDefinition;
for (int i = 0; i < depth; i++)
{
intend += " ";
}
for (int i = 0; i < structuredDataType.Count; i++)
{
// Get the description of the field (name, data type etc.)
GenericStructureDataTypeField fieldDescription = structuredDataType[i];
// Get the value of the field
Variant fieldValue = genericValue[i];
string fieldName = fieldDescription.Name;

Then we have to check the TypeClass and ValueRank of each field. If a field contains a structured DataType, the recursive method is called again.

if (fieldDescription.ValueRank == -1)
{
if (fieldDescription.TypeDescription.TypeClass == GenericDataTypeClass.Simple)
{
// Print the name and the value
string fieldValueString = fieldValue.ToString();
string outputString = "\r\n" + intend + fieldName + " = " + fieldValueString;
m_text.Text = m_text.Text += outputString;
}
else if (fieldDescription.TypeDescription.TypeClass == GenericDataTypeClass.Structured)
{
// Print the name and call this method again the child
string outputString = "\r\n" + intend + fieldName;
m_text.Text = m_text.Text += outputString;
PrintValueRec((GenericEncodeableObject)fieldValue.GetValue<GenericEncodeableObject>(null), depth + 1);
}
}
else if (fieldDescription.ValueRank == 1)
{
// Print the fields name
string outputString = "\r\n" + intend + fieldName;
m_text.Text = m_text.Text += outputString;
if (fieldDescription.TypeDescription.TypeClass == GenericDataTypeClass.Simple)
{
// Print each element
Array array = fieldValue.Value as Array;
int counter = 0;
foreach (object element in array)
{
outputString = "\r\n" + intend + " " + fieldDescription.TypeDescription.Name.Name + "[" + counter++ + "]: " + element.ToString();
m_text.Text = m_text.Text += outputString;
}
}
else if (fieldDescription.TypeDescription.TypeClass == GenericDataTypeClass.Structured)
{
// Call this method again foreach element
ExtensionObjectCollection extensionObjects = fieldValue.ToExtensionObjectArray();
int counter = 0;
foreach (ExtensionObject e in extensionObjects)
{
outputString = "\r\n" + intend + " " + fieldDescription.TypeDescription.Name.Name + "[" + counter++ + "]";
m_text.Text = m_text.Text += outputString;
PrintValueRec((GenericEncodeableObject)e.Body, depth + 2);
}
}
}