This document defines three DataEncodings: OPC UA Binary, OPC UA XML and OPC UA JSON. It describes how to construct Messages using each of these encodings.
All OPC UA DataEncodings are based on rules that are defined for a standard set of built-in types. These built-in types are then used to construct structures, arrays and Messages. The built-in types are described in Table 1.
ID |
Name |
Nullable |
Default |
Description |
1 |
Boolean |
No |
false |
A two-state logical value (true or false). |
2 |
SByte |
No |
0 |
An integer value between −128 and 127 inclusive. |
3 |
Byte |
No |
0 |
An integer value between 0 and 255 inclusive. |
4 |
Int16 |
No |
0 |
An integer value between −32 768 and 32 767 inclusive. |
5 |
UInt16 |
No |
0 |
An integer value between 0 and 65 535 inclusive. |
6 |
Int32 |
No |
0 |
An integer value between −2 147 483 648 and 2 147 483 647 inclusive. |
7 |
UInt32 |
No |
0 |
An integer value between 0 and 4 294 967 295 inclusive. |
8 |
Int64 |
No |
0 |
An integer value between −9 223 372 036 854 775 808 and 9 223 372 036 854 775 807 inclusive. |
9 |
UInt64 |
No |
0 |
An integer value between 0 and 18 446 744 073 709 551 615 inclusive. |
10 |
Float |
No |
0 |
An IEEE single precision (32 bit) floating point value. |
11 |
Double |
No |
0 |
An IEEE double precision (64 bit) floating point value. |
12 |
String |
Yes |
null |
A sequence of Unicode characters. |
13 |
DateTime |
Yes |
DateTime.MinValue (see 5.1.4) |
An instance in time. |
14 |
Guid |
Yes |
All zeros |
A 16-byte value that can be used as a globally unique identifier. |
15 |
ByteString |
Yes |
null |
A sequence of octets. |
16 |
XmlElement |
Yes |
null |
A sequence of Unicode characters that is an XML element. |
17 |
NodeId |
Yes |
All fields set to default. |
An identifier for a node in the address space of an OPC UA Server. |
18 |
ExpandedNodeId |
Yes |
All fields set to default. |
A NodeId that allows the namespace URI to be specified instead of an index. |
19 |
StatusCode |
No |
Good |
A numeric identifier for an error or condition that is associated with a value or an operation. |
20 |
QualifiedName |
Yes |
All fields set to default. |
A name qualified by a namespace. |
21 |
LocalizedText |
Yes |
All fields set to default. |
Human readable text with an optional locale identifier. |
22 |
ExtensionObject |
Yes |
All fields set to default. |
A structure that contains an application specific data type that may not be recognized by the receiver. |
23 |
DataValue |
Yes |
All fields set to default. |
A data value with an associated status code and timestamps. |
24 |
Variant |
Yes |
Null |
A union of all of the types specified above. |
25 |
DiagnosticInfo |
Yes |
No fields specified. |
A structure that contains detailed error and diagnostic information associated with a StatusCode. |
Most of these data types are the same as the abstract types defined in OPC 10000-3 and OPC 10000-4. However, the ExtensionObject and Variant types are defined in this document. In addition, this document defines a representation for the Guid type defined in OPC 10000-3.
The Nullable column indicates whether a ‘null’ value exists for the DataType in all DataEncodings. A ‘null’ value is a value that is equavalent ‘no value specified’. A nullable type with a default value means the default value shall be interpreted equivalent to a null.
The Default column specifies the default value for the type if a default value is needed. The default value for all arrays is ‘null’.
A Guid is a 16-byte globally unique identifier with the layout shown in Table 2.
Component |
Data Type |
Data1 |
UInt32 |
Data2 |
UInt16 |
Data3 |
UInt16 |
Data4 |
Byte [8] |
Guid values may be represented as a string in this form:
<Data1>-<Data2>-<Data3>-<Data4[0:1]>-<Data4[2:7]>
where Data1 is 8 characters wide, Data2 and Data3 are 4 characters wide and each Byte in Data4 is 2 characters wide. Each value is formatted as a hexadecimal number with padded zeros. A typical Guid value would look like this when formatted as a string:
C496578A-0DFE-4B8F-870A-745238C6AEAE
DateTime values have different ranges on different DevelopmentPlatforms. To ensure interoperablity two named values are defined:
“DateTime.MinValue” is the earliest value that can be represented;
“DateTime.MaxValue” is the latest value that can be represented.
If the range supported by the DataEncoding is outside of the range supported by a DevelopmentPlatform then decoders shall replace any below range values with DateTime.MinValue and any above range values with DateTime.MaxValue for the DevelopmentPlatform.
If the range supported by a DevelopmentPlatform is outside of the range supported by a DataEncoding then encoders shall replace any below range values with DateTime.MinValue and any above range values with DateTime.MaxValue for the DataEncoding.
The representation of a DateTime on a DevelopmentPlatform also has a maximum precision. Decoders shall truncate DateTime values that exceed the supported precision. All DevelopmentPlatforms shall support a precision of at least 1ms.
The DataValue built-in type adds additional fields for Picoseconds. If a DevelopmentPlatform cannot support the full precision of DateTime values allowed by the DataEncoding then it should expand the size of its internal representation of Picoseconds field to preserve the full precision of the DateTime. If it does not do this it shall set the Picoseconds to 0.
The Picoseconds shall be set to 0 when the DateTime value is DateTime.MinValue or DateTime.MaxValue.
Concrete examples can be found in 5.2.2.5.
A ByteString is structurally the same as a one-dimensional array of Byte. It is represented as a distinct built-in data type because it allows encoders to optimize the transmission of the value. However, some DevelopmentPlatforms will not be able to preserve the distinction between a ByteString and a one-dimensional array of Byte.
If a decoder for DevelopmentPlatform cannot preserve the distinction it shall convert all one-dimensional arrays of Byte to ByteStrings.
Each element in a one-dimensional array of ByteString can have a different length which means is structurally different from a two-dimensional array of Byte where the length of each dimension is the same. This means decoders shall preserve the distinction between two or more dimension arrays of Byte and one or more dimension arrays of ByteString.
If a DevelopmentPlatform does not support unsigned integers, then it will have to represent ByteStrings as arrays of SByte. In this case, the requirements for Byte would then apply to SByte.
Number, Integer and UInteger are abstract simple types defined in OPC 10000-3. When these types are used in Structure fields, the field value is encoded as a Variant.
Structures are sequences of name value pairs defined by the DataTypeDefinition Attribute in OPC 10000-3. Each DataEncoding describes how to use the DataTypeDefinition to serialize Structures. Subtypes of Structure extend the parent by adding additional name-value pairs to the sequence.
If a DataTypeDefinition sets the StructureType to StructureWithSubtypedValues then any field with a subtype of Structure DataType and IsOptional =TRUE allows the type and any subtype of the field’s DataType to be present in the field. In these cases, the values are serialized as an ExtensionObject. If IsOptional =FALSE then the field value is encoded directly according to the rules for the DataEncoding.
A field is serialized as an ExtensionObject if a field has a DataType set explicitly to Structure.
Unions are special subtypes of Structure where only one field value is encoded. All subtypes of Union are concrete. The remainder of the rules for Structure also apply to Unions.
An ExtensionObject is a container for any Structure and Union DataTypes.
The ExtensionObject contains a complex value serialized as a sequence of other DataTypes. It also contains an identifier which indicates what data it contains and how it is encoded.
There are four primary use cases where ExtensionObjects appear:
- When encoding a top-level DataType as a Service Request or Response;
- When encoding a Structure value inside a Variant;
- When encoding a field value in a Structure where the field DataType is Structure.
- When encoding a field value in a Structure where AllowSubTypes =TRUE (see F.12).
In all of these cases, the ExtensionObject provides an identifier that allows a decoder to know if it understands the Structure contained with it and a length that allows the Structure to be skipped if it is not recognized.
Structured DataTypes are represented in a Server address space as sub-types of the Structure DataType. The DataEncodings available for any given Structured DataTypes are represented as a DataTypeEncoding Object in the Server AddressSpace. The NodeId for the DataTypeEncoding Object is the identifier stored in the ExtensionObject. OPC 10000-3 describes how DataTypeEncoding Nodes are related to other Nodes of the AddressSpace.
Elements of an array of ExtensionObjects may have different DataTypeEncoding NodeIds specified. In some cases, this will be invalid, however, it is the responsibility of the application layer to enforce whatever constraints are imposed by the Information Model on a given array. Decoders shall accept any valid ExtensionObject as an array element.
Server implementers should use namespace qualified numeric NodeIds for any DataTypeEncoding Objects they define. This will minimize the overhead introduced by packing Structured DataType values into an ExtensionObject.
ExtensionObjects and Variants allow unlimited nesting which could result in stack overflow errors even if the message size is less than the maximum allowed. Decoders shall support at least 100 nesting levels. Decoders shall report an error if the number of nesting levels exceeds what it supports.
A Variant is a union of all built-in data types including an ExtensionObject. Variants can also contain arrays of any of these built-in types. Variants are used to store any value or parameter with a data type of BaseDataType or one of its subtypes.
Variants can be empty. An empty Variant is described as having a null value and should be treated like a null column in a SQL database. A null value in a Variant may not be the same as a null value for data types that support nulls such as Strings. Some DevelopmentPlatforms may not be able to preserve the distinction between a null for a DataType and a null for a Variant, therefore, applications shall not rely on this distinction. This requirement also means that if an Attribute supports the writing of a null value it shall also support writing of an empty Variant and vice versa.
Variants can contain arrays of Variants but they cannot directly contain another Variant.
DiagnosticInfo types only have meaning when returned in a response message with an associated StatusCode and table of strings. As a result, Variants cannot contain instances of DiagnosticInfo.
Values of Attributes are always returned in instances of DataValues. Therefore, the DataType of an Attribute cannot be a DataValue. Variants can contain DataValue when used in other contexts such as Method Arguments or PubSub Messages. The Variant in a DataValue cannot, directly or indirectly, contain another DataValue.
ExtensionObjects and Variants allow unlimited nesting which could result in stack overflow errors even if the message size is less than the maximum allowed. Decoders shall support at least 100 nesting levels. Decoders shall report an error if the number of nesting levels exceeds what it supports.
A Decimal is a high-precision signed decimal number. It consists of an arbitrary precision integer unscaled value and an integer scale. The scale is the inverse power of ten that is applied to the unscaled value.
A Decimal has the fields described in Table 3.
Field |
Type |
Description |
TypeId |
NodeId |
|
Encoding |
Byte |
This value is always 1. |
Length |
Int32 |
The length of the Scale and Value fields in bytes.If the length is less than or equal to 2 then the Decimal is an invalid value that cannot be used. |
Scale |
Int16 |
A signed integer representing scale which is the inverse power of ten that is applied to the unscaled value. i.e., the decimal number of the value multiplied by 10-scale The integer is encoded starting with the least significant bit. |
Value |
OctetString |
A 2-complement signed integer representing the unscaled value. The number of bytes is the value of the Length field minus size of the Scale field. The integer is encoded with the least significant byte first. |
When a Decimal is encoded in a Variant the built-in type is set to ExtensionObject. Decoders that do not understand the Decimal type shall treat it like any other unknown Structure and pass it on to the application. Decoders that do understand the Decimal can parse the value and use any construct that is suitable for the DevelopmentPlatform. Note that a Decimal is like a built-in type and a DevelopmentPlatform has to have hardcoded knowledge of the type. No Structure metadata is published for this type.
If a Decimal is embedded in another Structure then the DataTypeDefinition for the field shall specify the NodeId of the Decimal Node as the DataType. If a Server publishes an OPC Binary type description for the Structure then the type description shall set the DataType for the field to ExtensionObject.
The terms null, empty and zero-length are used to describe array values (Strings are arrays of characters and ByteStrings are arrays of Bytes for purposes of this discussion). A null array has no value. A zero-length or empty array is an array with 0 elements. Some DataEncodings will allow the distinction to be preserved on the wire, however, not all DevelopmentPlatforms will be able to preserve the distinction. For this reason, null, empty and zero length arrays are semantically the same for all DataEncodings. Decoders shall be able to handle all variations supported by the DataEncoding, however, decoders are not required to preserve the distinction. When testing for equality, applications shall treat null and empty arrays as equal. When a DevelopmentPlatform supports the distinction, writing and reading back an array value may result in null array becoming an empty array or vice versa.
QualifiedNames, NodeIds and ExpandedNodeIds optimize representation of NamespaceUris by using a NamespaceIndex to reference the location of the URI in a NamespaceTable. The NamespaceTable to use is known from the context. For example, when a Client establishes a Session with a Server, the Server supplies the NamespaceTable that is used for all exchanges within that Session. Another example, is when a Publisher publishes DataSetMetadata messages, the NamespaceTable is provided as part of the message.
However, there are other scenarios where there is no obvious context that can be used to store the NamespaceTable, such as column in a database table, so it is necessary to provide a self-contained representation of these DataTypes. This clause defines a normative String representation using the ABNF notation (see RFC 5234). Table 4 defines additional core rules used in these definitions.
Table 4 – Additional ABNF Core Rules
UNICODE |
Any Unicode character other than a Control character. |
CONTROL |
Any Unicode Control character (includes nulls, carriage returns, tabs and new lines). |
URI |
A string that conforms to RFC 3986. |
ENCODEDURI |
A URI which has the RFC 3986 Percent-Encoding applied to it. |
BASE64 |
A Base64 encoded binary value (see Base64). |
The ABNF description for a NodeId is found in Table 5.
Table 5 – ABNF Description for a NodeId
<node-id> |
= <identifier> |
<node-id> |
=/ <namespace-index> ";" <identifier> |
<node-id> |
=/ <namespace-uri> ";" <identifier> |
<namespace-index> |
= "ns=" *DIGIT |
<namespace-uri> |
= "nsu=" ENCODEDURI |
<identifier> |
= <numericid> / <stringid> / <guidid> / <opaqueid> |
<numericid> |
= "i=" *DIGIT |
<stringid> |
= "s=" *(UNICODE) |
<guidid> |
= "g=" 8HEXDIG 3("-" 4HEXDIG) "-" 12HEXDIG |
<opaqueid> |
= "b=" BASE64 |
<reserved> |
= ";" / "%" / CONTROL |
<escaped> |
= "%" 2HEXDIG |
NodeIds with a NamespaceIndex of 0 or a NamespaceUri of http://opcfoundation.org/UA/ shall only use the <identifier> form.
The <escaped> form of a Unicode character is the UTF-8 encoding of the Unicode character represented as a “%” followed by a hexadecimal number.Examples of NodeIds:
i=13
ns=10;i=12345
nsu=http://widgets.com/schemas/hello;s=水%09World
g=09087e75-8e5e-499b-954f-f2a9603db28a
nsu=tag:acme.com,2023:schemas:data#off%3B;b=M/RbKBsRVkePCePcx24oRA==
The ABNF description for a ExpandedNodeId is found in Table 6.
Table 6 – ABNF Description for a ExpandedNodeId
<expanded-node-id> |
= <node-id> |
<expanded-node-id> |
=/ <server-index> ";" <node-id> |
<expanded-node-id> |
=/ <server-uri> ";" <node-id> |
<server-index> |
= "svr=" *DIGIT |
<server-uri> |
= "svu=" ENCODEDURI |
ExpandedNodeIds that are not specific to a Server shall use the <node-id> form.
Examples of ExpandedNodeIds:
i=13
svr=1;nsu=http://widgets.com/schemas/hello;s=水%09World
svu=http://smith.com/east/factory;g=09087e75-8e5e-499b-954f-f2a9603db28a
svu=http://smith.com/west/factory;nsu=tag:acme.com,2023:schemas:data#off%3B;b=M/RbKBsRVkePCePcx24oRA==
The ABNF description for a QualifiedName is found in Table 7.
Table 7 – ABNF Description for a QualifiedName
<qualified-name> |
= <name> |
<qualified-name> |
=/ <namespace-index> ":" <name> |
<qualified-name> |
=/ <namespace-uri> ";" <name> |
<name> |
= *(UNICODE) |
QualifiedNames in the OPC UA Namespace shall not use the forms with a <namespace-index> or a <namespace-uri>.
Examples of QualifiedNames:
InputArguments
3:Hello%09World
nsu=http://widgets.com/schemas/hello;Hello%3BWorld
nsu=tag:acme.com,2023:schemas:data#off%3B;Boiler2
Text based DataEncodings such as UA XML (5.3) or UA JSON (5.4) make use of Names from the DataType and DataTypeDefinition to create the serialized data. XML and JSON formats impose restrictions on the Strings that may be used in serialized data so it is necessary to define a transformation that ensures any Names can be used in the DataEncoding. The following rules are used to convert a DataType Name or a Structure Field Name to a string supported by the Encoding:
- Any character that is not permitted by the DataEncoding is replaced by an underscore (U+005F);
- A name with text sequence that is not valid in the first position has an underscore (U+005F) added as a prefix.
The character restrictions for the XML DataEncoding are:
<allowed-name> |
= <letter> *(<allowed-char> / "_" / "-"/ ".") |
<allowed-char> |
= <letter> / DIGIT |
<letter> |
= UNICODE-LETTER |
UNICODE-LETTER |
Any Unicode character with a general category of ‘Letter’ |
In addition, XML names cannot start with the text ‘xml’ or any variation in case (i.e. ‘xMl’).
There are no restrictions for the JSON encoding other than the general restrictions defined in OPC 10000-3.
Table 8 has examples of XML encoded names.
Table 8 – Examples of XML Encoded Names
Hello |
Hello |
_Hello |
_Hello |
3DHello |
_3DHello |
冷水 |
冷水 |
3 (冷水。)-Hello |
_3__冷水___-Hello |