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) is encoded as a 32-bit integer in the stream.

image005.png

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 9 lists the bit ranges for the supported floating-point types.

Table 9 – Supported Floating Point Types

Name

Width (bits)

Fraction

Exponent

Sign

Float

32

0-22

23-30

31

Double

64

0-51

52-62

63

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) is 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 (Hex: 000000000000F8FF) or (Hex: 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.

image006.png

Figure 3 – Encoding Floating Points in a binary stream

All String values are encoded as a sequence of UTF-8 characters 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.

Strings with embedded nulls (‘\0’) can lead to unpredictable application behaviour because embedded nulls have special meaning to some DevelopmentPlatforms. For this reason, embedded nulls are not recommended and ByteString should be used instead. That said, Encoders may encode Strings with embedded nulls. Decoders shall use the length to read all bytes in String.

Figure 4 illustrates how the multilingual string ‘水Boy’ is encoded in a byte stream.

image007.png

Figure 4 – Encoding Strings in a binary stream

A DateTime value shall be encoded as a 64-bit signed integer (see 5.2.2.2) 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:

  1. A date/time value is encoded as 0 if either
  2. The value is equal to or earlier than 1601-01-01 12:00AM UTC.
  3. The value is the earliest date that can be represented with the DevelopmentPlatform’s encoding.
  4. A date/time is encoded as the maximum value for an Int64 if either
  5. The value is equal to or greater than 9999-12-31 11:59:59PM UTC,
  6. The value is the latest date that can be represented with the DevelopmentPlatform’s encoding.
  7. A date/time is decoded as the earliest time that can be represented on the platform if either
  8. The encoded value is 0,
  9. The encoded value represents a time earlier than the earliest time that can be represented with the DevelopmentPlatform’s encoding.
  10. A date/time is decoded as the latest time that can be represented on the platform if either
  11. The encoded value is the maximum value for an Int64,
  12. 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” is encoded in a byte stream.

image008.png

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 element serialized as UTF-8 string and then encoded as ByteString.

Figure 6 illustrates how the XmlElement “<A>Hot水</A>” is encoded in a byte stream.

. image009.png

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 10.

Table 10 – NodeId components

Name

Data Type

Description

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

UInt32 or String or Guid or ByteString

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 11. Table 11 through Table 14 describe the structure of each possible format (they exclude the byte which indicates the format).

Table 11 – NodeId DataEncoding values

Name

Value

Description

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 5.2.2.10.

ServerIndex Flag

0x40

See discussion of ExpandedNodeId in 5.2.2.10.

The standard NodeId DataEncoding has the structure shown in Table 12. The standard DataEncoding is used for all formats that do not have an explicit format defined.

Table 12 – Standard NodeId Binary DataEncoding

Name

Data Type

Description

Namespace

UInt16

The NamespaceIndex.

Identifier

*

The identifier which is encoded according to the following rules:

NUMERIC

UInt32

STRING

String

GUID

Guid

OPAQUE

ByteString

An example of a String NodeId with Namespace = 1 and Identifier = “Hot水” is shown in Figure 7.

image010.png

Figure 7 – A String NodeId

The Two Byte NodeId DataEncoding has the structure shown in Table 13.

Table 13 – Two Byte NodeId Binary DataEncoding

Name

Data Type

Description

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.

image011.png

Figure 8 – A Two Byte NodeId

The Four Byte NodeId DataEncoding has the structure shown in Table 14.

Table 14 – Four Byte NodeId Binary DataEncoding

Name

Data Type

Description

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.

image012.png

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 5.2.2.9 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 15.

Table 15 – ExpandedNodeId Binary DataEncoding

Name

Data Type

Description

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 16 – DiagnosticInfo Binary DataEncoding

Name

Data Type

Description

Encoding Mask

Byte

A bit mask that indicates which fields are present in the stream.

The mask has the following bits:

0x01

Symbolic Id

0x02

Namespace

0x04

LocalizedText

0x08

Locale

0x10

Additional Info

0x20

InnerStatusCode

0x40

InnerDiagnosticInfo

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 17.

The abstract QualifiedName structure is defined in OPC 10000-3.

Table 17 – QualifiedName Binary DataEncoding

Name

Data Type

Description

NamespaceIndex

UInt16

The namespace index.

Name

String

The name.

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 18 – LocalizedText Binary DataEncoding

Name

Data Type

Description

EncodingMask

Byte

A bit mask that indicates which fields are present in the stream.

The mask has the following bits:

0x01

Locale

0x02

Text

Locale

String

The locale.

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, the DataEncoding used and the number of bytes encoded.

An ExtensionObject may be serialized as a ByteString or an XmlElement by the application and then passed 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 19.

Table 19 – Extension Object Binary DataEncoding

Name

Data Type

Description

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 an XmlElement.

Length

Int32

The length of the object body.

The length shall be specified if the body is encoded.

Body

OctetString

The encoded object.

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.

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 the 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 20.

Table 20 – Variant Binary DataEncoding

Name

Data Type

Description

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 ArrayDimensions 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 or an array of ByteStrings and pass both onto the application. Encoders shall not use these IDs.

The ArrayDimensions field shall only be present if the number of dimensions is 2 or greater and all dimensions have a length greater than 0.

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]

If one or more dimensions has a length <= 0 then the ArrayLength is 0.

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.

ArrayDimensionsLength

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 21.

Table 21 – Data Value Binary DataEncoding

Name

Data Type

Description

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 SourceTimestamp is DateTime.MinValue.

0x08

False if the ServerTimestamp is DateTime.MinValue.

0x10

False if the SourcePicoseconds is not present.

0x20

False if the ServerPicoseconds is not present.

Value

Variant

The value.

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’.