Search code examples
c#.net-standardopc-ua

Writing dynamic structs to an OPC UA Server (c#, .NetStandard)


I'm new to OPC UA and might be going about this totally wrong but maybe someone here can help me. I've got an OPC UA server running on a Siemens S7-1500 PLC and connect to it with my C# application. My application is a control software that feeds configuration data to the PLC. Writing basic data types works like a charm, same goes for arrays of basic types. The problem I face is that the PLC will have custom structs that aren't known at compile time of my application. That's why I've implemented a system to dynamically create objects that reflect the custom structs of the PLC. That way I don't have to release a new version of the application when the PLC side introduces a new custom struct. When writing to the custom struct I encode the date in my application to a byte array. This works fine for a single struct. But when I try to write an array of such custom structs I get a BadTypeMismatch error.

Here's how my code for writing the struct looks like:

var nodeId = GetNodeId(path);
Dictionary<uint, DataValue> nodeAttributes = ReadNodeAttributes(nodeId.ToString());
DataTypeNode dataTypeNode = (DataTypeNode)ReadNode(nodeAttributes[Attributes.DataType].ToString());

//Get the structure definition
StructureDefinition structureDefinition = (StructureDefinition)dataTypeNode.DataTypeDefinition.Body;
ExtensionObject ExtensionObject = new ExtensionObject();
ExtensionObject.TypeId = structureDefinition.DefaultEncodingId;
ExtensionObject.Body = ConvertParameterToByteArray(parameter);
DataValue dataValue = new DataValue(ExtensionObject);

//Setup for the WriteValue
WriteValue writeValue = new WriteValue();
writeValue.NodeId = nodeId;
writeValue.Value = dataValue;
writeValue.AttributeId = Attributes.Value;
//Add the created value to the collection
collection.Add(writeValue);

I figured to write an array of such structs I'd simply have to create a byte array starting with the number of elements and then the concatenated byte arrays of the individual structs. Where am I going wrong?


Solution

  • I finally figured out how to solve my problem. I'm now reading the structure information of the first element of the array and use this information to tell the OPC UA server how to interpret each element of the array. I still don't know why I need to tell the OPC UA server how to interpret the data in the first place as it already knows what it should get. Below is my code in case someone else has a similar issues.

    var innerNode = GetNodeId(path+"[0]");
    Dictionary<uint, DataValue> nodeAttributes = ReadNodeAttributes(nodeId.ToString());
    DataTypeNode dataTypeNode = (DataTypeNode)ReadNode(nodeAttributes[Attributes.DataType].ToString());
    
    //Get the structure definition
    StructureDefinition structureDefinition = (StructureDefinition)dataTypeNode.DataTypeDefinition.Body;
    
    ExtensionObject[] values = new ExtensionObject[customTable.Count];
    int i = 0;
    foreach (IStructParameter param in customTable)
    {
        values[i] = new ExtensionObject();
        values[i].TypeId = structureDefinition.DefaultEncodingId; ;
        values[i].Body = ConvertParameterToByteArray(param);
        i++;
    }
    WriteValue writeValue = new WriteValue();
    writeValue.NodeId = nodeId;
    DataValue dataValue = new DataValue(values);
    writeValue.Value = dataValue;
    writeValue.AttributeId = Attributes.Value;
    //Add the created value to the collection
    collection.Add(writeValue);