One of the great things in OPC UA is that when the base information models don’t fit your needs quite well enough, there are many options to extend the specifications. One common case where it’s nice to be able to extend the base spec is with Complex DataTypes.
Complex DataTypes are in effect data types with some sort of structure (like C structs). OPC UA Part 3 defines Standard DataTypes, such as Double, String, and ImageJPG. For many applications though, there are data types that aren’t covered by the base DataTypes. Of course, any data can be transferred as ByteStrings, but this approach doesn’t give a client application clues about how to interpret those ByteStrings.
Fortunately, OPC UA contains mechanisms for defining custom Complex DataTypes. Using them involves creating an encoder/decoder for the data that the stack will use when communicating with other OPC UA applications. In our SDK, an example of creating such an encoder/decoder is found in the package com.prosysopc.ua.tests.
The class to be sent over OPC UA is called ExtensionObjectTestStructure, which is just a simple wrapper for one string. ExtensionObjectTestStructure implements the interface Structure, which defines getters for XmlEncodeId, BinaryEncodeId and TypeId. You must ensure that these NodeId’s (which are up to you) are known to both the client and server stacks in order for successful communication. In the example, this is achieved by using an arbitrary namespaceIndex for the NodeId’s (so that they match on server and client), but in real life namespaceUris are used for the task.
The encoder/decoder is implemented as an AbstractSerializer, added to both the client and server stacks in their initialization. It must implement methods for encoding and decoding the class, as well as calculating the encoded length. In each method, you are handed the appropiate classes to use, so you simply need to work through the fields/properties of the object in the implementation. This AbstractSerializer is then registered with the Java stack by calling EncodeableSerializer.getInstance().addSerializer(), and after that the type can be sent over OPC UA.
Other stacks can be extended in a similar way, so using Complex DataTypes instead of ad hoc string/byte encodings will enable interoperability with your own data types. The sample zip file (ComplexDataTypeExample) contains the example server and structure classes and a code snippet to add on the client side (for example in SampleConsoleClient.initialize())Â We will be adding better support for handling the namespaceIndex/namespaceUri issue in future releases.