Reading structured datatypes from server at runtime

Questions regarding the use of the .NET SDK 2.0 for Server or Client development or integration into customer products ...

Moderator: uasdknet

Post Reply
marklendering
Full Member
Full Member
Posts: 6
Joined: 03 Nov 2015, 13:43

Reading structured datatypes from server at runtime

Post by marklendering »

Hello,

I am trying to read a structured type from a server at runtime. From what i gathered from this topic: http://forum.unified-automation.com/post1549.html , it is possible to read the data into a Dictionary. However, I cant find any documentation or example on how to perform this.
I am using the SDK version 2.4.0

I use the following code to read and parse data from the server.

Code: Select all

DataTypeManager mgr = new DataTypeManager(session);

DataValue result = readNode(eQ.m_session, obj.Variables["measurements"].nodeId); //this is a custom method
ExtensionObject[] extObs = (ExtensionObject[])(result.Value);
if (extObs != null)
{
      List<KeyValue> keyValueList = new List<KeyValue>();
      foreach (ExtensionObject extOb in extObs)
      {
           KeyValue kv = new KeyValue(); //custom object ( string key, string[] value, enum type )
           GenericEncodeableObject tempObj = mgr.ParseValue(extOb);
           Variant readValue;
           if (tempObj.TryGetFieldValue("key", out readValue))
           {
                 kv.key = readValue.ToString();
                 if (!kv.key.Equals(""))
                 {
                      tempObj.TryGetFieldValue("value", out readValue);
                      kv.value = readValue.ToStringArray();
                      tempObj.TryGetFieldValue("typeId", out readValue);
                      kv.typeId = (KeyValueType)readValue.ToInt32();
                      keyValueList.Add(kv);
                }
           }
      }
}
this code is a workAround for getting the data from the server. However, i also need to write the data back to the server.

With the following code I am able to read the dictionary and when inspecting the actual data i can see that it contains what i need.
The 2.4.0 changenotes say something about a generic object type:
Generic structured DataType support.
Adds a class for creating structured DataTypes at runtime. This class registers
the new structured DataType at the OPC UA communication stack.
Adds a class for setting values where the fields of the structure can be
accessed by name or by index.
but i dont see how i can get there from where i am now :

Code: Select all

DataTypeManager mgr = new DataTypeManager(session);
DataTypeDictionary dict = mgr.GetDictionary(new ExpandedNodeId("PLC1Dictionary", 4));
BinarySchemaBuilder bd = new BinarySchemaBuilder(dict.SchemaData);
in short: I need to create and object from the structured object type i received from the server, edit the data/store it, and send it back when needed.

User avatar
Support Team
Hero Member
Hero Member
Posts: 3072
Joined: 18 Mar 2011, 15:09

Re: Reading structured datatypes from server at runtime

Post by Support Team »

Hello,

Please see the ReadStructure example in GettingStarted client: documentation.
You can get the necessary information about the DataType and the ValueRank for reading using the fields of the GenericStructuredDataType.

Best regards
Support Team

marklendering
Full Member
Full Member
Posts: 6
Joined: 03 Nov 2015, 13:43

Re: Reading structured datatypes from server at runtime

Post by marklendering »

I followed the ReadStructure from the GettingStarted example you linked and came up with this:

Code: Select all

private static DataValue parseStructuredObject(Session session, NodeId nodeId)
{
	DataTypeManager mgr = new DataTypeManager(session);
	DataValue result = readNode(session, nodeId);
	if (result.Value != null)
	{
		Variant[] value = result.WrappedValue.ToVariantArray();
		if (value != null)
		{
			if (value[0].DataType == BuiltInType.ExtensionObject || value[0].ValueRank == ValueRanks.Scalar)
			{
				ExtensionObject e = value[0].ToExtensionObject();
				if (e.Encoding != ExtensionObjectEncoding.EncodeableObject)
				{
					GenericEncodeableObject encodeable = mgr.ParseValue(e);
					GenericStructureDataType structuredDataType = encodeable.TypeDefinition;
					context.Factory.AddGenericDataTypeMapping(structuredDataType);
					result = readNode(session, nodeId); //read it again, now that we can parse the data
				}
			}
		}
	}
	return result; //return whatever the result is
}

however, this way I need to read data from the server first and add the typemapping before im able to write data back. When I need to write data to an object which is currently empty and i dont know the datatypemapping yet, I cannot use this method.


Is there a way that I can read the structure and generate the datatypemapping from the type dictionary the server provides( OPC Binary )?

marklendering
Full Member
Full Member
Posts: 6
Joined: 03 Nov 2015, 13:43

Re: Reading structured datatypes from server at runtime

Post by marklendering »

I figured it out in the meantime:

Code: Select all

        private static bool parseStructuredObjectTypes(Session session, NodeId nodeId)
        {
            DataTypeManager mgr = new DataTypeManager(session);
            DataValue result = readNodeDataType(session, nodeId); //returns the object Datatype nodeId 

            if (result.Value != null)
            {
                NodeId dataTypeId = result.WrappedValue.ToNodeId();
                GenericStructureDataType datatype = mgr.NewTypeFromDataType(dataTypeId, null, true); 
                context.Factory.AddGenericDataTypeMapping(datatype);
                return true;
            }
            return false;
        }
The last time i got stuck on this line, since it would keep telling me its couldnt find any default binaries:

Code: Select all

GenericStructureDataType datatype = mgr.NewTypeFromDataType(dataTypeId, null, true); 
The reason it works now is that i replaced the qualified name field with "null".

thanks for the support!


EDIT: i still had some issues.
the namespace of the variable i am reading and the datatype of this variable are different ( even though they are in the same namespace, they were read differently: nsu=urn:WIN-0FREJU7ELKC:BeckhoffAutomation:Ua:PLC1;s=<StructuredDataType>:Error__DefaultBinary vs ns=4;s=<StructuredDataType>:Error__DefaultBinary )

Since i was adding one and reading the other i still couldnt parse the objects correctly. So i came up with this:

Code: Select all

private static DataValue parseStructuredObjectTypes(Session session, NodeId nodeId)
        {
            DataTypeManager mgr = new DataTypeManager(session);
            DataValue result = readNodeDataType(session, nodeId);

            if (result.Value != null)
            {
                NodeId dataTypeId = result.WrappedValue.ToNodeId();
                GenericStructureDataType datatype = mgr.NewTypeFromDataType(dataTypeId, null, false);
                ExpandedNodeId tempNodeId = new ExpandedNodeId(datatype.BinaryEncodingNodeId.Identifier.ToString(), session.NamespaceUris[nodeId.NamespaceIndex]);
                datatype.BinaryEncodingNodeId = tempNodeId;
                context.Factory.AddGenericDataTypeMapping(datatype);
                return readNode(session, nodeId);
            }
            return null;
        }

Post Reply