High Performance OPC UA Server SDK
1.7.1.383
|
Structures are stored in Variant values as extension objects. On the wire an ExtensionObject is encoded as TypeId, followed by the Length of the body in bytes and then the body itself, which is the encoded structure data itself. The structure body must be encoded as a series of structure fields, where each field is encoded according to the base types defined in Part6 of the OPC UA Specification. Also nested structures are allowed.
Example:
Encoding Table:
Field | Bytes | Value |
---|---|---|
Type Id | 4 | The encoding id of the Type |
Encoding | 1 | 0x1 for Binary encoded data |
Length | 4 | 9 |
Open | 1 | The value of 'open' |
Flow | 4 | The value of 'flow' |
Temperature | 4 | The value if 'temperature' |
In the SDK the extension object can be in two different states, UA_EXTENSIONOBJECT_BINARY or UA_EXTENSIONOBJECT_ENCODEABLEOBJECT. Normally when the SDK decodes a UA Message it tries to decode Variants and its contained ExtensionObjects. When the extension objects's encoding_id is known, it gets decoded and the ExtensionObject contains a pointer to the decoded object (UA_EXTENSIONOBJECT_ENCODEABLEOBJECT). This requires that the types was known at compile time and a C struct was generated for it.
If the type is not known the struct is stored as byte blob in the extension object's body (UA_EXTENSIONOBJECT_BINARY).
See also ua_extensionobject for details on the ExtensionObject.
In this case you can decode the value yourself on application level. A typical use case for this scenario are PLCs where the types are created at runtime by downloading a new PLC program, so the SDK does not know the types at compile time. This also means that there exist no C structs where the decoder could decode to, but some other some other generic data model, which is application specific.
The SDK offers a simple API to create well-known extensobjects which are know at compile-time.
The workflow is a follows:
The SDK API will take ownership of the memory of the given encodable object, to avoid memcpy calls, and takes care of creating the ExtensionObject with correct encoding_id and setting the Variant data type.
The following example demonstrate how to create an ExtensionObject which contains an ua_range.
When processing Variant values you need to be able to extract the encodable objects from Variant/ExtensionObjects. This is even easier than creating extension objects.
For well-known types the encodable objects of Variant/ExtensionObjects get serialized when the network message gets serialized. But this works only for known types.
For other types you can encode your struct yourself on application level, and fill the extension object with a pre-encoded ua_bytestring value. When the network message gets serialized, this ua_variant encoder can write the structure to the network stream by using a simple ua_memcpy operation.
Don't worry, you can leverage the existing encoder infrastructure to implement the encoding of custom types.
The following example shows how to encode the custom struct example from the beginning of this section:
ExtensionObjects that are not known by the SDK will be stored as ua_bytestring in the ua_extensionobject. You can decode this data in the same way as we have created it in the previous section on application level.
The following example shows how to decode the custom struct example from the beginning of this section: