How to decode all extension fields of an AnalogItemType (C++ client)?

Unified Architecture topics related to OPC UA Specification, compliant behavior and any technical issues of OPC UA, like Security, Information Model, Companion Specs DI, PLCopen, ADI, ...

Moderator: Support Team

Post Reply
Bergsteiger
Sr. Member
Sr. Member
Posts: 18
Joined: 07 Jul 2014, 16:11

How to decode all extension fields of an AnalogItemType (C++ client)?

Post by Bergsteiger »

1. In my server I am using the methods like (with the Unified Automation SDK)

Code: Select all

variableNode->setEngineeringUnits(uaUnit);
to code the EURange, EngineeeringUnits and InstrumentRange.
This produces automatically stringified node ids like
ns=2;s=6044.EngineeringUnits

When browsing the AnalogItemType nodes I am using

Code: Select all

    UaNodeId unitNodeId(variableNodeId.toString()+".EngineeringUnits", _nsIndex);
    // The magic converting the extension object to UAVariant happens inside readNodeIdAsUaVariant
    UaVariant unitAsUaVariant(readNodeIdAsUaVariant(unitNodeId));
    return UaEUInformation(unitAsUaVariant);

This is already rather complex but works nicely.

2. Currently I need to decode the extension objects from analog items that are generated by the https://www.arburg.com/WaterManifoldDevices/ model namespace. There also all sensor data is beeing stored as AnalogItemType, but the node ids of the extension objects are just numerical like
ns=4,i=6074
Most of the time the numerical node Id just is one higher than its corresponding AnalogItemType node, but this is not allways the case. This model does not carry the InstrumentRange but if I would need to decode the extensions objects now the code is rather complicated as below. Specifically I need to rely on the browsename to decode the items.

Code: Select all

    UaEUInformation unit;
    UaRange	    range;
    UaRange         instrumentRange;

    UaStatus result = uaSession->browse(
                serviceSettings,
                nodeToBrowse,
                browseContext,
                continuationPoint,
                variableReferenceDescriptions
             );

    if (result.isGood()) {

        for (unsigned int i = 0; i < variableReferenceDescriptions.length(); i++) {
            OpcUa_ReferenceDescription rd = variableReferenceDescriptions[i];


            AnalogItemTypeExtensionHashes selector = hash(rd.BrowseName.Name.strContent);


            UaVariant extensionAsUaVariant(readNodeId(rd.NodeId.NodeId));
            UaExtensionObject extensionObject;
            extensionAsUaVariant.toExtensionObject(extensionObject);
            switch (extensionObject.encoding()) {
                case UaExtensionObject::EncodeableObject:
                    switch (extensionObject.dataTypeId().identifierNumeric()) {
                        case OpcUaId_EUInformation:
                            switch (selector) {
                                case EngineeringUnits:
                                    unit = UaEUInformation(extensionObject);
                                    break;
                            }

                            break;
                        case OpcUaId_Range:
                            switch (selector) {
                                case EURange:
                                    range = UaRange(extensionObject);
                                    break;
                                case InstrumentRange:
                                    instrumentRange = UaRange(extensionObject);
                                    break;
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
	}
    }

Is this correct or I am doing something wrong ? The printExtensionObject from the example files is even more complicated, but still does not distinguish whether the item is a EURange, EngineeringUnits or InstrumentRange and comment says it should not be used in productive code.
ATTENTION: Code will not compile directly as it uses some pseudo code

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

Re: How to decode all extension fields of an AnalogItemType (C++ client)?

Post by Support Team »

Hello,

Is the NodeId ns=4,i=6074 the NodeId of the Property Node or the encoding NodeId in the ExtensionObject?

The following documentation describes how DataTypes work in OPC UA:
http://documentation.unified-automation.com/uasdkc/1.9.1/html/L2DataTypes.html

The DataType NodeId of a Property (e.g. EngineeringUnits Property) is provided by the DataType Attribute of the Property.
The EncodingNodeId is contained in the ExtensionObject if you read the Value of the Property and the value is a Structure DataType.

If the DataType or the EncodingNodeId is not in the OPC UA namespace (index 0), it is not one of the OPC UA defined DataTypes.
The OPC UA defined DataTypes are already decoded by the stack. In this case the encoding is UaExtensionObject::EncodeableObject and the structure can be accessed directly.
If the encoding is UaExtensionObject::Binary, you need the dictionary described in the documentation above. This dictionary is provided by the C++ SDK. The example is shown in printExtensionObject.

The printExtensionObject is just sample code on how to decode unknown structures using the dictionary.
If you have a known DataType, the handling is shown in printExtensionObjectKnownType().

Another thing to consider is that you have two NodeIds for a Structure:
EUInformation
DatatType: OpcUaId_EUInformation
Encoding NodeId: OpcUaId_EUInformation_Encoding_DefaultBinary
Best regards
Unified Automation Support Team

Bergsteiger
Sr. Member
Sr. Member
Posts: 18
Joined: 07 Jul 2014, 16:11

Re: How to decode all extension fields of an AnalogItemType (C++ client)?

Post by Bergsteiger »

Complex question, obviously a complicated answer. Sorry, but I could not understand all of your reply.
Therefore I will try to make the question a bit more clear (Your answer behind >):

> Is the NodeId ns=4,i=6074 the NodeId of the Property Node or the encoding NodeId in the ExtensionObject?

The node ID refers to the node ID of the analog item itself, that has corresponding properties as:

This is the structure of my model to be browsed with corresponding node IDs and types

Code: Select all

DeviceSet 				ns=2;i=5001
 WMD_Name 				ns=4;i=5104
  Channels:                           	ns=4;i=5103
      Channel_1:                      	ns=4;i=5111
		...
          ReturnTemperature          	ns=4;6074
              ActualValue             	ns=4;8141    Variable    AnalogItemType              
		EngineeringUnits      	ns=4;i=8172  OpcUaId_EUInformation
		EURange		      	ns=4;i=8173  OpcUaId_Range
		InstrumentRange       	ns=4;i=8174  OpcUaId_Range
              SetValue                	ns=4;8168    Variable    AnalogItemType  
		EngineeringUnits      	ns=4;i=8172  OpcUaId_EUInformation
		EURange		      	ns=4;i=8173  OpcUaId_Range
		InstrumentRange       	ns=4;i=8174  OpcUaId_Range
                ...
> The following documentation describes how DataTypes work in OPC UA:
> http://documentation.unified-automation.com/uasdkc/1.9.1/html/L2DataTypes.html
If I understand correctly only the last paragraph(Client side handling of structure DataTypes) explains the client situation, and I guess I followed these rules or do I?

> The DataType NodeId of a Property (e.g. EngineeringUnits Property) is provided by the DataType Attribute of the Property.
> The EncodingNodeId is contained in the ExtensionObject if you read the Value of the Property and the value is a Structure DataType.
Yes, but how to destinguish these InstrumentRange and EURange, both have the same DataType Range = 884:
Image

Both are poperties of the same AnalogItem with nodeId ns=4;i=6074 (Sorry, but I could not upload the corresponding picture). As you saw earlier in my code (2) I was using the browsename to distinguish those. - This is what is puzzeling me most -

Currently I would like just to ignore binary encodings to avoid additional complexity.

> The printExtensionObject is ...
Yes, but at least my version, that I got from the training, can't distinguish between InstrumenRange and EURange as I wrote before.

Best regards

Bergsteiger
Sr. Member
Sr. Member
Posts: 18
Joined: 07 Jul 2014, 16:11

Re: How to decode all extension fields of an AnalogItemType (C++ client)?

Post by Bergsteiger »

Typo:

Code: Select all

ReturnTemperature           	ns=4;5106
              ActualValue             	ns=4;6074    Variable    AnalogItemType              
		EngineeringUnits      	ns=4;i=8172  OpcUaId_EUInformation
		EURange		      	ns=4;i=8173  OpcUaId_Range
		InstrumentRange       	ns=4;i=8174  OpcUaId_Range
              SetValue                	ns=4;6075    Variable    AnalogItemType  
		EngineeringUnits      	ns=4;i=8172  OpcUaId_EUInformation
		EURange		      	ns=4;i=8173  OpcUaId_Range
		InstrumentRange       	ns=4;i=8174  OpcUaId_Range
                ...
Copying info from the picture failed!

Post Reply