A Boolean value shall be encoded as a single byte where a value of 0 (zero) is false and any non-zero value is true.
Encoders shall use the value of 1 to indicate a true value; however, decoders shall treat any non-zero value as true.
All integer types shall be encoded as little endian values where the least significant byte appears first in the stream.
Figure 2 illustrates how value 1 000 000 000 (Hex: 3B9ACA00) should be encoded as a 32-bit integer in the stream.
Figure 2 – Encoding Integers in a binary stream
All floating-point values shall be encoded with the appropriate IEEE-754 binary representation which has three basic components: the sign, the exponent, and the fraction. The bit ranges assigned to each component depend on the width of the type. Table 4 lists the bit ranges for the supported floating point types.
Table 4 – Supported Floating Point Types
In addition, the order of bytes in the stream is significant. All floating point values shall be encoded with the least significant byte appearing first (i.e. little endian).
Figure 3 illustrates how the value −6,5 (Hex: C0D00000) should be encoded as a Float.
The floating-point type supports positive and negative infinity and not-a-number (NaN). The IEEE specification allows for multiple NaN variants; however, the encoders/decoders may not preserve the distinction. Encoders shall encode a NaN value as an IEEE quiet-NAN (000000000000F8FF) or (0000C0FF). Any unsupported types such as denormalized numbers shall also be encoded as an IEEE quiet-NAN. Any test for equality between NaN values always fails.
Figure 3 – Encoding Floating Points in a binary stream
All String values are encoded as a sequence of UTF-8 characters without a null terminator and preceded by the length in bytes.
The length in bytes is encoded as Int32. A value of −1 is used to indicate a ‘null’ string.
Figure 4 illustrates how the multilingual string “Boy” should be encoded in a byte stream.
Figure 4 – Encoding Strings in a binary stream
A DateTime value shall be encoded as a 64-bit signed integer (see Clause 18.104.22.168) which represents the number of 100 nanosecond intervals since January 1, 1601 (UTC).
Not all DevelopmentPlatforms will be able to represent the full range of dates and times that can be represented with this DataEncoding. For example, the UNIX time_t structure only has a 1 second resolution and cannot represent dates prior to 1970. For this reason, a number of rules shall be applied when dealing with date/time values that exceed the dynamic range of a DevelopmentPlatform. These rules are:
- A date/time value is encoded as 0 if either
- The value is equal to or earlier than 1601-01-01 12:00AM UTC.
- The value is the earliest date that can be represented with the DevelopmentPlatform’s encoding.
- A date/time is encoded as the maximum value for an Int64 if either
- The value is equal to or greater than 9999-12-31 11:59:59PM UTC,
- The value is the latest date that can be represented with the DevelopmentPlatform’s encoding.
- A date/time is decoded as the earliest time that can be represented on the platform if either
- The encoded value is 0,
- The encoded value represents a time earlier than the earliest time that can be represented with the DevelopmentPlatform’s encoding.
- A date/time is decoded as the latest time that can be represented on the platform if either
- The encoded value is the maximum value for an Int64,
- The encoded value represents a time later than the latest time that can be represented with the DevelopmentPlatform’s encoding. These rules imply that the earliest and latest times that can be represented on a given platform are invalid date/time values and should be treated that way by applications.
A decoder shall truncate the value if a decoder encounters a DateTime value with a resolution that is greater than the resolution supported on the DevelopmentPlatform.
A Guid is encoded in a structure as shown in Table 2. Fields are encoded sequentially according to the data type for field.
Figure 5 illustrates how the Guid “72962B91-FA75-4AE6-8D28-B404DC7DAF63” should be encoded in a byte stream.
Figure 5 – Encoding Guids in a binary stream
A ByteString is encoded as sequence of bytes preceded by its length in bytes. The length is encoded as a 32-bit signed integer as described above.
If the length of the byte string is −1 then the byte string is ‘null’.
An XmlElement is an XML fragment serialized as UTF-8 string and then encoded as ByteString.
Figure 6 illustrates how the XmlElement “<A>Hot</A>” should be encoded in a byte stream.
Figure 6 – Encoding XmlElement in a binary stream
A decoder may choose to parse the XML after decoding; if an unrecoverable parsing error occurs then the decoder should try to continue processing the stream. For example, if the XmlElement is the body of a Variant or an element in an array which is the body of a Variant then this error can be reported by setting value of the Variant to the StatusCode Bad_DecodingError.
The components of a NodeId are described the Table 5.
Table 5 – NodeId components
|Namespace||UInt16||The index for a namespace URI.
An index of 0 is used for OPC UA defined NodeIds.
|IdentifierType||Enumeration||The format and data type of the identifier.
The value may be one of the following:
NUMERIC - the value is an UInteger;
STRING - the value is String;
GUID - the value is a Guid;
OPAQUE - the value is a ByteString;
|Value||*||The identifier for a node in the address space of an OPC UA Server.|
The DataEncoding of a NodeId varies according to the contents of the instance. For that reason, the first byte of the encoded form indicates the format of the rest of the encoded NodeId. The possible DataEncoding formats are shown in Table 6. The tables that follow describe the structure of each possible format (they exclude the byte which indicates the format).
Table 6 – NodeId DataEncoding values
|Two Byte||0x00||A numeric value that fits into the two-byte representation.|
|Four Byte||0x01||A numeric value that fits into the four-byte representation.|
|Numeric||0x02||A numeric value that does not fit into the two or four byte representations.|
|String||0x03||A String value.|
|Guid||0x04||A Guid value.|
|ByteString||0x05||An opaque (ByteString) value.|
|NamespaceUri Flag||0x80||See discussion of ExpandedNodeId in 22.214.171.124.|
|ServerIndex Flag||0x40||See discussion of ExpandedNodeId in 126.96.36.199.|
The standard NodeId DataEncoding has the structure shown in Table 7. The standard DataEncoding is used for all formats that do not have an explicit format defined.
Table 7 – Standard NodeId Binary DataEncoding
|Identifier||*||The identifier which is encoded according to the following rules:
An example of a String NodeId with Namespace = 1 and Identifier = “Hot” is shown in Figure 7.
Figure 7 – A String NodeId
The Two Byte NodeId DataEncoding has the structure shown in Table 8.
Table 8 – Two Byte NodeId Binary DataEncoding
|Identifier||Byte||The Namespace is the default OPC UA namespace (i.e. 0).
The Identifier Type is ‘Numeric’.
The Identifier shall be in the range 0 to 255.
An example of a Two Byte NodeId with Identifier = 72 is shown in Figure 8.
Figure 8 – A Two Byte NodeId
The Four Byte NodeId DataEncoding has the structure shown in Table 9.
Table 9 – Four Byte NodeId Binary DataEncoding
|Namespace||Byte||The Namespace shall be in the range 0 to 255.|
|Identifier||UInt16||The Identifier Type is ‘Numeric’.
The Identifier shall be an integer in the range 0 to 65 535.
An example of a Four Byte NodeId with Namespace = 5 and Identifier = 1 025 is shown in Figure 9.
Figure 9 – A Four Byte NodeId
An ExpandedNodeId extends the NodeId structure by allowing the NamespaceUri to be explicitly specified instead of using the NamespaceIndex. The NamespaceUri is optional. If it is specified, then the NamespaceIndex inside the NodeId shall be ignored.
The ExpandedNodeId is encoded by first encoding a NodeId as described in 188.8.131.52 and then encoding NamespaceUri as a String.
An instance of an ExpandedNodeId may still use the NamespaceIndex instead of the NamespaceUri. In this case, the NamespaceUri is not encoded in the stream. The presence of the NamespaceUri in the stream is indicated by setting the NamespaceUri flag in the encoding format byte for the NodeId.
If the NamespaceUri is present, then the encoder shall encode the NamespaceIndex as 0 in the stream when the NodeId portion is encoded. The unused NamespaceIndex is included in the stream for consistency.
An ExpandedNodeId may also have a ServerIndex which is encoded as a UInt32 after the NamespaceUri. The ServerIndex flag in the NodeId encoding byte indicates whether the ServerIndex is present in the stream. The ServerIndex is omitted if it is equal to zero.
The ExpandedNodeId encoding has the structure shown in Table 10.
Table 10 – ExpandedNodeId Binary DataEncoding
|NodeId||NodeId||The NamespaceUri and ServerIndex flags in the NodeId encoding indicate whether those fields are present in the stream.|
|NamespaceUri||String||Not present if null or Empty.|
|ServerIndex||UInt32||Not present if 0.|
A StatusCode is encoded as a UInt32.
A DiagnosticInfo structure is described in OPC 10000-4. It specifies a number of fields that could be missing. For that reason, the encoding uses a bit mask to indicate which fields are actually present in the encoded form.
As described in OPC 10000-4, the SymbolicId, NamespaceUri, LocalizedText and Locale fields are indexes in a string table which is returned in the response header. Only the index of the corresponding string in the string table is encoded. An index of −1 indicates that there is no value for the string.
DiagnosticInfo allows 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.
Table 11 – DiagnosticInfo Binary DataEncoding
|Encoding Mask||Byte||A bit mask that indicates which fields are present in the stream.
The mask has the following bits:
0x01 Symbolic Id
0x10 Additional Info
|SymbolicId||Int32||A symbolic name for the status code.|
|NamespaceUri||Int32||A namespace that qualifies the symbolic id.|
|Locale||Int32||The locale used for the localized text.|
|LocalizedText||Int32||A human readable summary of the status code.|
|Additional Info||String||Detailed application specific diagnostic information.|
|Inner StatusCode||StatusCode||A status code provided by an underlying system.|
|Inner DiagnosticInfo||DiagnosticInfo||Diagnostic info associated with the inner status code.|
A QualifiedName structure is encoded as shown in Table 12.
The abstract QualifiedName structure is defined in OPC 10000-3.
Table 12 – QualifiedName Binary DataEncoding
|NamespaceIndex||UInt16||The namespace index.|
A LocalizedText structure contains two fields that could be missing. For that reason, the encoding uses a bit mask to indicate which fields are actually present in the encoded form.
The abstract LocalizedText structure is defined in OPC 10000-3.
Table 13 – LocalizedText Binary DataEncoding
|EncodingMask||Byte||A bit mask that indicates which fields are present in the stream.
The mask has the following bits:
Omitted is null or empty.
|Text||String||The text in the specified locale.
Omitted is null or empty.
An ExtensionObject is encoded as sequence of bytes prefixed by the NodeId of its DataTypeEncoding and the number of bytes encoded.
An ExtensionObject may be encoded by the application which means it is passed as a ByteString or an XmlElement to the encoder. In this case, the encoder will be able to write the number of bytes in the object before it encodes the bytes. However, an ExtensionObject may know how to encode/decode itself which means the encoder shall calculate the number of bytes before it encodes the object or it shall be able to seek backwards in the stream and update the length after encoding the body.
When a decoder encounters an ExtensionObject it shall check if it recognizes the DataTypeEncoding identifier. If it does, then it can call the appropriate function to decode the object body. If the decoder does not recognize the type it shall use the Encoding to determine if the body is a ByteString or an XmlElement and then decode the object body or treat it as opaque data and skip over it.
The serialized form of an ExtensionObject is shown in Table 14.
Table 14 – Extension Object Binary DataEncoding
|TypeId||NodeId||The identifier for the DataTypeEncoding node in the Server’s AddressSpace. ExtensionObjects defined by the OPC UA specification have a numeric node identifier assigned to them with a NamespaceIndex of 0. The numeric identifiers are defined in A.3.
Decoders use this field to determine the syntax of the Body. For example, if this field is the NodeId of the JSON Encoding Object for a DataType then the Body is a ByteString containing a JSON document encoded as a UTF-8 string.
|Encoding||Byte||An enumeration that indicates how the body is encoded.
The parameter may have the following values:
0x00 No body is encoded.
0x01 The body is encoded as a ByteString.
0x02 The body is encoded as a XmlElement.
|Length||Int32||The length of the object body.
The length shall be specified if the body is encoded.
|Body||Byte [*]||The object body.
This field contains the raw bytes for ByteString bodies.
For XmlElement bodies this field contains the XML encoded as a UTF-8 string without any null terminator.
Some binary encoded structures may have a serialized length that is not a multiple of 8 bits. Encoders shall append 0 bits to ensure the serialized length is a multiple of 8 bits. Decoders that understand the serialized format shall ignore the padding bits.
ExtensionObjects are used in two contexts: as values contained in Variant structures or as parameters in OPC UA Messages.
A decoder may choose to parse an XmlElement body after decoding; if an unrecoverable parsing error occurs then the decoder should try to continue processing the stream. For example, if the ExtensionObject is the body of a Variant or an element in an array that is the body of Variant then this error can be reported by setting value of the Variant to the StatusCode Bad_DecodingError.
A Variant is a union of the built-in types.
The structure of a Variant is shown in Table 15.
Table 15 – Variant Binary DataEncoding
|EncodingMask||Byte||The type of data encoded in the stream. A value of 0 specifies a NULL and that no other fields are encoded. The mask has the following bits assigned:
0:5 Built-in Type Id (see Table 1).
6 True if the Array Dimensions field is encoded.
7 True if an array of values is encoded.
The Built-in Type Ids 26 through 31 are not currently assigned but may be used in the future. Decoders shall accept these IDs, assume the Value contains a ByteString and pass both onto the application. Encoders shall not use these IDs.
|ArrayLength||Int32||The number of elements in the array.
This field is only present if the array bit is set in the encoding mask.
Multi-dimensional arrays are encoded as a one-dimensional array and this field specifies the total number of elements. The original array can be reconstructed from the dimensions that are encoded after the value field.
Higher rank dimensions are serialized first. For example, an array with dimensions [2,2,2] is written in this order:
[0,0,0], [0,0,1], [0,1,0], [0,1,1], [1,0,0], [1,0,1], [1,1,0], [1,1,1]
|Value||*||The value encoded according to its built-in data type.
If the array bit is set in the encoding mask, then each element in the array is encoded sequentially. Since many types have variable length encoding each element shall be decoded in order.
The value shall not be a Variant but it could be an array of Variants.
Many implementation platforms do not distinguish between one dimensional Arrays of Bytes and ByteStrings. For this reason, decoders are allowed to automatically convert an Array of Bytes to a ByteString.
|ArrayDimensions Length||Int32||The number of dimensions.
This field is only present if the ArrayDimensions flag is set in the encoding mask.
|ArrayDimensions||Int32[*]||The length of each dimension encoded as a sequence of Int32 values
This field is only present if the ArrayDimensions flag is set in the encoding mask. The lower rank dimensions appear first in the array.
All dimensions shall be specified and shall be greater than zero.
If ArrayDimensions are inconsistent with the ArrayLength then the decoder shall stop and raise a Bad_DecodingError.
The types and their identifiers that can be encoded in a Variant are shown in Table 1.
A DataValue is always preceded by a mask that indicates which fields are present in the stream.
The fields of a DataValue are described in Table 16.
Table 16 – Data Value Binary DataEncoding
|Encoding Mask||Byte||A bit mask that indicates which fields are present in the stream.
The mask has the following bits:
0x01 False if the Value is Null.
0x02 False if the StatusCode is Good.
0x04 False if the Source Timestamp is DateTime.MinValue.
0x08 False if the Server Timestamp is DateTime.MinValue.
0x10 False if the Source Picoseconds is 0.
0x20 False if the Server Picoseconds is 0.
Not present if the Value bit in the EncodingMask is False.
|Status||StatusCode||The status associated with the value.
Not present if the StatusCode bit in the EncodingMask is False.
|SourceTimestamp||DateTime||The source timestamp associated with the value.
Not present if the SourceTimestamp bit in the EncodingMask is False.
|SourcePicoSeconds||UInt16||The number of 10 picosecond intervals for the SourceTimestamp.
Not present if the SourcePicoSeconds bit in the EncodingMask is False.
If the source timestamp is missing the picoseconds are ignored.
|ServerTimestamp||DateTime||The Server timestamp associated with the value.
Not present if the ServerTimestamp bit in the EncodingMask is False.
|ServerPicoSeconds||UInt16||The number of 10 picosecond intervals for the ServerTimestamp.
Not present if the ServerPicoSeconds bit in the EncodingMask is False.
If the Server timestamp is missing the picoseconds are ignored.
The Picoseconds fields store the difference between a high-resolution timestamp with a resolution of 10 picoseconds and the Timestamp field value which only has a 100 ns resolution. The Picoseconds fields shall contain values less than 10 000. The decoder shall treat values greater than or equal to 10 000 as the value ‘9999’.