Table of Contents

Supported Data Types

The Ometa framework makes connections with many different back-end systems, for example SQL databases, ERP systems, REST services and more. Just like those systems have their own data types, the OPC UA protocol defines its own data types to model the world as closely as possible in relation to IoT devices.

It is needless to say that it is impossible for the framework to support every single data type that is used by those external systems and databases. This doesn't need to be an issue because the framework is flexible enough to handle those differences and usually those types are largely compatible with each other so that converting data between types can be done pretty easily. Unfortunately, this doesn't really apply to OPC UA regarding its data types. Yes, some types are compatible but definitely not all.

Due to the nature of the OPC UA protocol and IoT, specific data types exist within it to make it easier for working with devices directly. However, those data types do not necessarily map gracefully to the framework data types. This article will explain which data types are supported and how they are supported, what the caveats are, and importantly how the interface will behave in specific scenarios regarding data conversion.

Important to know, is that when the article refers to data types, it means the core data types. The OPC UA typing of objects and folders is handled differently because the OPC UA interface will transform those items into records. The core data types can be seen as similar to the field data types in the Ometa framework.

Basic Core Type Mapping

The list of defined OPC UA Core data types is quite long, a full list and additional information can be found on the links below:

Note

For the moment, the Ometa OPC UA interface and monitoring service only fully supports the data types described in the table below. Any other OPC UA type may or may not work but those types aren't officially supported (yet).

In the table below, the first column lists the supported OPC UA data types and the second column the default and best matching framework type corresponding to that OPC UA data type. It will be that data type the OPC UA Wizard will propose during the generation of a method. It is still possible to change proposed framework data type to another one but there is no guarantee that the conversion of the data to the newly selected data type will work.

Meanings of the icons:

  • ✔️: A conversion will most likely work as long the value fits in the destination type format.
  • ❌: Conversion is not supported.
OPC UA Data Type Framework Default Type YesNo Text Media DateTime Char Numeric Data (Number, Currency, Integer ) Link
Boolean YesNo ✔️ ✔️ ✔️ ✔️
ByteString Media ✔️ ✔️ ✔️
Image Media ✔️ ✔️
ImageBMP Media ✔️ ✔️
ImageGIF Media ✔️ ✔️
ImageJPG Media ✔️ ✔️
ImagePNG Media ✔️ ✔️
DateTime DateTime ✔️ ✔️ ✔️ (only Number, Currency)
Date DateTime ✔️ ✔️ ✔️ (only Number, Currency)
UtcTime DateTime ✔️ ✔️ ✔️ (only Number, Currency)
Enumeration Integer ✔️ (only Integer)
Guid Text ✔️
LocalizedText Text ✔️ ✔️ ✔️ ✔️ ✔️ ✔️
Number Integer ✔️ ✔️ ✔️ ✔️
Decimal Currency ✔️ ✔️ ✔️ ✔️ ✔️
Double Number ✔️ ✔️ ✔️ ✔️ ✔️
Duration Number ✔️ ✔️ ✔️ ✔️ ✔️
Float Number ✔️ ✔️ ✔️ ✔️ ✔️
Integer Integer ✔️ ✔️ ✔️ ✔️
Int16 Integer ✔️ ✔️ ✔️ ✔️
Int32 Integer ✔️ ✔️ ✔️ ✔️
Int64 Number ✔️ ✔️ ✔️ ✔️ ✔️
SByte Integer ✔️ ✔️ ✔️ ✔️
UInteger Number ✔️ ✔️ ✔️ ✔️
Byte Integer ✔️ ✔️ ✔️ ✔️
UInt16 Integer ✔️ ✔️ ✔️ ✔️
UInt32 Number ✔️ ✔️ ✔️ ✔️
Counter Number ✔️ ✔️ ✔️ ✔️
UInt64 Number ✔️ ✔️ ✔️ ✔️ ✔️
QualifiedName Text ✔️ ✔️ ✔️ ✔️ ✔️ ✔️
String Text ✔️ ✔️ ✔️ ✔️ ✔️ ✔️
XmlElement Text ✔️
Warning

When trying to read from to a variable with an unknown (unsupported, as not present in the table above) OPC UA type, the interface will still try to do some data conversion according a fall back scenario but there is no warranty that the conversion will succeed and return the expected result.

Writing is a bit stricter and that means that a conversion in that case is more likely to fail.

The support as stated in the table above works in both directions, e.g. an OPC UA Double value can be converted into a framework Char type and vice versa.

Specific behaviour and details about the data conversions will be explained in the paragraphs below.

Beware of Information Loss

Important

Data conversion is not without its issues. This is known when dealing with numeric values, e.g. converting a floating point number to an integer will result in information loss due to rounding of the decimal part.

Information loss due to conversion can go much further. DateTime ticks are 64-bit integers, whole numbers. The OPC UA interface allows writing floating point numbers to a date type. In that case, any decimals are first rounded to the nearest integer values and then the conversion to a date is performed. Again, the resulting date won't match the initial value written to the field. See From OPC UA to DateTime for more information about ticks.

Another example would be reading a number and letting it convert by the interface to a boolean. The only value the interface can write back from a boolean to a number, is either a zero (for false) or one (for true). And that means the original value read from the field, is lost due to the conversion requested during reading.

Reading and Data Conversion

As stated in the paragraph above, the framework OPC UA interface acts very liberal regarding the incoming values from an OPC UA server. This means that it will try to convert the values coming from the OPC UA server to the data types defined in the framework fields as closely as it possibly can.

For example, an OPC UA Int32 value is by default mapped to an Integer field. Yet, it is possible to change the destination framework type to another type and the framework interface will try to do its best to convert the incoming value to that particular requested type, even when the field is set to YesNo or Char. However, taking a String value and converting it into an Integer field will only work when the initial value coming from the server indeed is numeric. In any other case that conversion will fail.

From OPC UA to YesNo

Converting a numerical value to a boolean, the interface will only map a real zero (= 0) value to false. Any non-zero value is mapped to true.

For strings and string-base types, a conversion will only occur when the string contains some form of the text "true" or "false". Casing of the text is ignored, so "TRUE", "True", "trUe" will all result in a boolean value true. Similarly, the same logic is valid for false as "FALSE", "False", "falSE" will be recognised as correct input. Additionally, empty text, or any other text is converted to the value false.

From OPC UA to Text

The following table describes how the values are interpreted by the interface and converted into strings:

Source Type Expected Result
Boolean Either True or False.
Any numeric type, except Decimal The data is converted into text using an invariant culture format. The decimal separator is a decimal point.
Decimal numeric type The conversion is done like other numeric values but there is at least one trailing digit after the decimal point. E.g. -100.0
Byte, SByte Represented in hexadecimal values. The 'letters' are set in lower case. E.g. 0x15, 0x00, 0xff
ByteString and image types Same representation as with single bytes, only will every byte just added to the full string. E.g. 0x125e97f410
Any date/time based type ISO 8601 representation. E.g. 2021-05-28T11:17:30+00:00
XmlElement The full XML information is returned in a single string.

Keep in mind that when a variable of ByteString or any image type, is empty, an empty string will be returned.

Note

When reading from an XmlElement variable, disable the HTML processing on the framework Text field. Otherwise, the framework will replace certain characters like '<' and '>' by '&lt;' and '&gt;' respectively.

Disable Process HTML Tag

From OPC UA to Media

Data sent to or read from a media field, is handled within the framework as Base64 strings. In all cases, data coming from the types ByteString, Image, ImageBMP, ImageGIF, ImageJPG and ImagePNG is converted to a Base64 string and returned with additional prefix header.

All returned data starts with an ADM prefix with the following format: "file:opcuadata.;value:" The placeholder will contain a proper file name extension depending on the OPC UA type.

After the ADM prefix, only for the types ImageBMP, ImageGIF, ImageJPG and ImagePNG, an extra data URI scheme prefix is added. That prefix will always the format: "data:image/;base64," The real value of the placeholder depends on the source data type.

Source Type ADM prefix Data URI scheme
ByteString and Image file:opcuadata.bin;value: N/A
ImageBMP file:opcuadata.bmp;value: data:image/bmp;base64,
ImageGIF file:opcuadata.gif;value: data:image/gif;base64,
ImageJPG file:opcuadata.jpg;value: data:image/jpg;base64,
ImagePNG file:opcuadata.png;value: data:image/png;base64,

Then the full data in Base64 format is attached.

Some examples:

Source Type Example Data
ByteString file:opcuadata.bin;value:iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAABAElEQVR4Xu3RI...Jggg==
ImagePNG file:opcuadata.png;value:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAABAElEQVR4Xu3RI...Jggg==

From OPC UA to DateTime

Real date/time values are mapped as-is, without any conversion needed.

When the source type is a numeric value, the interface will regard it as ticks, the time elapsed since 12:00:00 midnight, January the 1st, 0001. Read these remarks (Microsoft Docs) on how to interpret those tick values.

In the case of string input, the OPC UA framework interface tries to parse the text into a date/time value. When it fails, an error will be triggered.

However, there are some caveats to keep in mind. For example, an ISO date like "2021-05-21T00:00:00.0000000" will parse properly in the date 21st of May, 2021, and even a date like "2021-03-08" is interpreted like the 8th of March, 2021. But for dates like "08-05-2020" there could be some confusion depending on the culture settings of the OS.

Therefore it is hardcoded in the interface that such dates will always be parsed as American style dates, meaning "08-05-2020" becomes the 5th of August, 2020.

Note

When no timezone is present in a date string or during conversion of ticks to data, the timezone of the final date will be set to the local timezone.

From OPC UA to Char

Char is a specific type and used to represent Unicode characters. As such some additional checks are done to have some reasonable conversions in cases like multi-character strings or multi-byte arrays. In the table below provides more detailed information on the possibilities and limits to converting data to a character.

Source Type Expected Result
Boolean Either T or F.
Any numeric type and (s)byte Any positive number larger than 31 that fits within the maximum value of a character, will be converted. Any negative number or overflow will trigger an error.
ByteString (including images) The first byte of the array is taken and converted in a character, as long the value fits within the range as defined for converting numeric values.
Any string type The first character of the string is taken and returned.
Note

Be aware of the fact that any incoming numeric value (and first byte of a ByteString) between 0 and 31 (ends included) may not always return character data. This is due to the fact of internal handling of character data within the framework, some characters have special meanings, and by default that range consists of the ASCII control characters which usually don't have a visual 'component' on the screen.

Therefore it is strongly advised to only read numeric/byte data as a character when those characters can be displayed on the screen (ASCII code 32 and higher).

From OPC UA to Number, Currency and Integer

When converting data to a numeric value, an additional step might be involved than just handling the digits.

Parsing to Numeric Value From String/Date

Depending on the source data type, an extra step of interpreting or parsing the data might be needed, especially for any date/time and string type data.

The interpretation is done as follows:

  • Date/time type: the value is converted to the total number of ticks, quote from Microsoft Docs: The value of this property represents the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001 in the Gregorian calendar. Any timezone information is ignored.
  • String type: the incoming string is parsed according the destination framework type. This means that it is impossible to convert a floating point number in a string to a framework Integer because the parsing will fail as the interface expects a integer value in the string data, not a floating point number.

Converting to Destination Type

During this phase the incoming value, or result from string/date parsing, is assigned to the requested destination type. There is one caveat here: the input value must fit in the destination type, meaning the value must be within the range of possible values of the destination type.

All numeric types come in their own size/shape and if the input value is too large for the destination type, the OPC UA interface will throw an error.

The table below lists some ranges from numeric OPC UA types:

Source Type Value Range
Boolean 0/1 (true or false)
SByte -128 to 127
Byte 0 to 255
Int16 -32768 to 32767
UInt16 0 to 65535
Int32 -2147483648 to 2147483647
UInt32 0 to 4294967295
Int64 -9223372036854775808 to 9223372036854775807
UInt64 0 to 18446744073709551615
Float IEEE single precision (32 bit) floating point value
Double IEEE double precision (64 bit) floating point value

And the table below lists the ranges of the numeric type in the framework:

Framework Type Value Range
Integer -2147483648 to 2147483647
Number IEEE double precision (64 bit) floating point value
Currency -922337203685477.5808 to 922337203685477.5807

Based on these tables it is clear that the interface can convert Boolean, SByte, Byte, Int16, UInt16 and Int32 to the framework Integer. And it seems that for values from UInt32, the minimum type needed is the Number. However, that is incorrect. As long as the incoming value is between -2147483648 and 2147483647, the Integer type can be used as the receiving type.

Warning

Beware of Information Loss due to Rounding!

The interface also allows converting float and double values (= real numbers with decimals) to Integer values. However, keep in mind in those cases, rounding will occur. E.g. 123.45 becomes 123, 75.59 becomes 76. This rounding process means information loss in comparison to the original value.

See Beware of Information Loss for a more general explanation to which extent this might happen.

Special Cases

Reading from the OPC UA type Enumeration, is only supported by the framework field type Integer. Any other framework type will fail.

Needless to say that the OPC UA interface is only capable of creating a link (= URI) from string data. Keep in mind that the URI itself must be a proper well-formed URI or an error will be thrown, e.g.: https://www.ometa.net/

Writing and Data Conversion

Writing data to an OPC UA server is a bit more complex than reading from it and as such the OPC UA interface can be a little bit stricter during data conversion. As seen from the mapping table above, the data type mappings are not a perfect one-on-one mappings and the end-user may have selected a different framework data type of a field than initially proposed during the method generation.

Just before writing data to the OPC UA server, the interface will prepare the data to write by converting everything to the OPC UA data types. This is required as some data formats used in the framework need a transformation into a format that can be understood by the server. When at least one preparatory conversion fails, the interface considers the entire write action having failed even though no data has been sent to the OPC UA server. Only when that step is completed, data is sent for action writing by the server.

For example, a ByteString is mapped by default to a Media type but both types are fundamentally different internally, and therefore to be able to write a byte string value in the Media format coming from the framework, it must be converted into a real OPC UA ByteString before the actual write action is performed.

Important

Make sure that when writing to an OPC UA variable, only that variable name is set in the input fields' external name. The OPC UA interface does not support writing a single input value to multiple variables of the same device. In that case, a mapping must be made of one input field for each variable to write to.

From YesNo to OPC UA

When writing from a YesNo field to OPC UA, only the values 0 (false) and 1 (true) are passed on. This means that an OPC UA string field will only receive the text "False" and "True" respectively. For number or byte fields, the corresponding numeric values of false and true are stored.

From Text to OPC UA

Converting string to any other data type depends on the destination data type.

Note

Basically the same rules apply as those used when reading string data from OPC UA and converting those values to the specialised framework data types.

Some caveats to keep in mind when writing string data is listed below.

Boolean textual data must be either True, true, False or false, or the parsing to a real boolean value will fail.

When converting text to an OPC UA DateTime type, the string to write must be in ISO 8601 representation. When that string has no timezone at the end, e.g. 2021-05-28T11:17:30.123, the interface assumes the local timezone during conversion. This requirement is to remain consistent with the OPC UA DateTime to framework Text conversions. The result of that process is also an ISO date string.

Make sure in case of writing to a byte or byte string, the initial string has the proper format. For a single byte the expected format is '0xYY' where '0x' is a fixed prefix and 'YY' is the real byte value. And for byte strings, the expected format is '0xZZZZZZZZZZ...ZZ'. Again, '0x' is a fixed prefix, and the 'ZZZZ...ZZ' is the text that will be interpreted as bytes.

Only in the case of writing to byte string variables, it is possible to pass in an empty string (= ""). Then the interface will write an empty value to the variable.

Also, it is impossible to write an empty string (= "") to variables of the OCP UA XmlElement type. The string value requires at least a root element, e.g.

From Media to OPC UA

Data coming from a framework media field is always in Base64 format.

If present, so called ADM and data URI scheme prefixes will always be removed before writing. The Base64 string is converted into a byte array to write the data to the OPC UA variable.

From DateTime to OPC UA

Obviously, writing dates to OPC UA data type variables works without any conversion.

When writing to OPC UA numeric types, just like a date value is read from OPC UA and transformed into ticks to get a numeric output in the framework, the same will happen when writing a DateTime to an OPC UA numeric data type.

Keep in mind that writing to a numeric type other than Decimal, Double, Float, Int64 or UInt64 isn't very useful as that will most likely result in overflow errors (e.g. Int32, UInt32 and smaller).

Note

For more information about ticks (Microsoft Docs).

From Char to OPC UA

Mostly no specific conversion rules apply here, except for writing characters in the ASCII control character range, and for writing to the Boolean type.

ASCII control characters don't have a visual 'component' by default and some of those characters also have a specific meaning within the framework for particular purposes. Therefore, for writing values between 0 and 31 (ends included) it is better to write those values via a numeric framework field because there is no guarantee that those character values will be sent to the OPC UA server as intended.

When writing to a Boolean field, the character must be either 't' or 'T' and 'f' or 'F' to respectively being converted into true and false. Any other character will result in an error message.

From Number, Currency and Integer to OPC UA

Writing numbers to OPC UA strings works exactly the same way as reading numeric values and storing them into a framework Text or Media field. One has to pay attention that converting currency values to OPC UA strings will always give at least 1 decimal in the resulting string, e.g. 123.0 becomes "123.0". This does not apply to the Number and Integer framework types.

Another thing to keep in mind, again, is the potential loss of information. Due to rounding issues when writing to numeric data types in OPC UA that aren't immediately compatible, e.g. framework Number to OPC UA Int64, a different value may be written to the server. But when source values remain within the ranges of the destination type, writing by the interface is possible. For example, writing a framework Integer to an OPC UA UInt16 field, is possible as long as the Integer value is between 0 and 65535. Similarly, writing a framework Integer to an OPC UA Byte works when the value lies between 0 and 255 (range ends included).

When writing to the OPC UA type Enumeration, use only fields with the framework field type Integer. Any other framework type will fail and trigger error messages when sending the data to the OPC UA server.

Monitoring and Data Conversion

It's not only the framework OPC UA interface that attempts data conversions, the framework Windows OPC UA service does this as well for any incoming data. The exact same conversion routines are used as those when reading data, so please consult the paragraph Reading and Data Conversion for the details.

Subscriptions with Method Handling

However, there is an important limitation and that is: there is NO data conversion present when a subscription is used with a method handler to process the incoming data.

This is a consequence of how this is internally processed within the framework. Some internal data conversion is happening but that conversion is limited to what is needed to execute a method. The full data conversion routines as defined above are, obviously, not used as their use is limited to the OPC UA interface and Windows service.

Any errors will thrown by the BCM process, usually have a following format, e.g. "Exception triggered during method execution System.Exception: NACK Converting the string 2200302000 to the value type of field type Integer failed"

Important

Therefore it is very important to select the proper data of each field defined in the handler method of a subscription. Especially, the field mapped for receiving the incoming value needs to have a proper data type.

It might be a good idea to define that field with a type that can receive a multitude of value types, e.g. string, and do some basic data conversion within the handler method when needed.

Subscription with Direct Case Writing

When the OPC UA service writes values directly to a case, the data conversion routines as defined above, are used and applied on the incoming data.

This is possible due to the fact that the service can retrieve the case property data types and do a proper conversion from the OPC UA data type to the framework case property data type.