Visavi Visual OPC .NET  

OPC VARIANT Data Types and Interoperability

Excerpt from OPC DA 3.00 Specification
4.2.12 VARIANT Data Types and Interoperability, page 16

OPC Foundation (www.opcfoundation.org)

Data Types and Interoperability

In order to promote interoperability, the following rules and recommendations are presented.

General Recommendations:

General Rules:

Although the IEEE standard allows NANs to be stored in VT_R4 and VT_R8 format, such values can only be read and written using the exact native format of the target item. They will not be converted to/from other types. When such a value is read (as a native type) the QUALITY flag must be returned by the server as OPC_QUALITY_BAD. If such a value is written (as a native type) then the QUALITY flag provided by the client MUST be OPC_QUALITY_BAD. Whether or not a particular server ever returns or accepts such values is server specific.

Additional Rules regarding Data Conversion

OPC Servers must support at least the following conversions between Canonical and Requested datatypes. Reading and Writing should be symmetric. Note that the easiest way for most server implementers to provide this functionality is to use the VariantChangeTypeEx() function available in the COM libraries. In the table below, conversions marked OK can be expected to always work. Other conversions may or may not work depending on the specific value of the source.

As noted elsewhere in this specification the Client can specify a localeID to be used and the server should pass this to VariantChangeTypeEx() for all conversions. Note that it is possible for the end user to override some of the default Locale Settings in the Control Panel Regional Settings Dialog. For example in English it is possible to select date formats of either MM/DD/YY or YY/MM/DD as well other formats. Clearly a date of 03/02/01 is ambiguous in this case. It is the End User's responsibility to insure that the Regional Settings for a given localeID are compatible on different machines within his network.

From... I1 UI1 I2 UI2 I4 UI4 R4 R8 CY DATE BSTR BOOL
To...
I1 OK OK(7) (1) (1) (1) (1) (1) (1) (1) (1)(3) (4) OK
UI1 OK(7) OK (1) (1) (1) (1) (1) (1) (1) (1)(3) (4) OK
I2 OK OK OK OK (1) (1) (1) (1) (1) OK (4) OK
UI2 (1) OK OK(7) OK (1) (1) (1) (1) (1) OK (4) OK
I4 OK OK OK OK OK OK(7) (1) (1) (1) OK (4) OK
UI4 (1) OK (1) OK OK(7) OK (1) (1) (1) OK (4) OK
R4 OK OK OK OK OK OK OK (1) OK OK (4) OK
R8 OK OK OK OK OK OK OK OK OK OK (4) OK
CY OK OK OK OK OK OK (1) (1) OK OK (4) OK
DATE OK OK OK OK OK OK (1) (1) (1) OK (4) OK
BSTR OK OK OK OK OK OK OK OK OK OK OK OK
BOOL OK OK OK OK OK OK OK OK OK OK (4) OK

Notes:

(1) Conversion on `downcast' e.g. from I4 to I2 or R8 to R4 is allowed although Overflow is possible. If overflow occurs an error (DISP_E_OVERFLOW) is returned and in the case of Read the quality is set to BAD. In the case of Write the target value is not changed.

(2) Note that the internal storage type of VT_BOOL is `short' which must have the values VARIANT_TRUE (0xFFFF ­ i.e. `-1' when stored in a `short') and VARIANT_FALSE (0). When converting TO bool any non-zero value converts to VARIANT_TRUE. Converting FROM bool to any signed numeric type, VARIANT_TRUE converts to `-1' or `-1.0'. For unsigned types it converts to the maximum value. The recommended OPC standard for conversion of bool to BSTR is "0" or "-1" rather than `True" or "False". If a server chooses to convert to "True" or "False" is must account for the Locale (e.g. by passing VARIANT_LOCALBOOL to VariantChangeTypeEx). It should also be noted that the C++ keyword `true' is an abstract type which converts to `1' when assigned to any other value (e.g. to a short). Thus it is a coding error to assign `true' to `boolVal' which must always be set to VARIANT_TRUE or VARIANT_FALSE.

(3) Note that DATE is stored as a double where the integer part is the date and the fraction is the time. For the DATE, 0.0 is midnight Dec 30, 1899 (i.e. midnight Jan 1, 1900 is 2.0 and Dec 4, 2001 is 37229.0). For the TIME the fraction represents the time of day moving ahead from midnight (e.g. 0.2500 is 6:00 AM, 0.400 is 9:36:00 AM). This fraction is not affected by the sign of the date and always moves `ahead' from midnight for both positive and negative values. For example ­1.4 is Dec 29, 1899 9:36:00 AM. These conversions are supported by VariantChangeTypeEx(). Generally an OVERFLOW will occur if the TO type is UI1, I1, UI2 or I2. In addition the TIME (being a fraction) will be lost on any conversion of DATE to an Integer.

(4) BSTR conversions will give a DISP_E_TYPE error if the string does not make sense for conversion to the target type. For example "1234" converts to any numeric type (except it generates OVERFLOW for UI1), "12/04/2001" converts to DATE (depending on the Locale) but not to a numeric type and "ABCD" does not convert to any other type.

(5) Conversions from non Integers to Integers must round up according to the sign if the factional part exceeds 0.5. For example 1.6 would round up to 2 and -1.6 would round 'up' to -2. In addition, the generally accepted convention is that round up will occur if the fraction equals or exceeds 0.5 HOWEVER client and server writers should be aware that VariantChange does not reliably adhere to the 'equals' part of this rule. Experience has shown that some floats and doubles with a fraction exactly equal to .5 will round up while others will round down. For purposes of compliance either round up or round down is acceptable when the fraction exactly equals .5.

(6) Keep in mind that Currency (CY) is stored as a scaled (fixed point x 10,000) 8 byte integer (I.e. a `_huge') With 4 digits of precision to the right of the decimal point. For example $12.34 is stored as 123400.

(7) Conversion between signed and unsigned integers should generate an overflow if the requested type cannot hold the value (e.g. I1=-1 should overflow if converted to UI1 and UI1 of 255 should overflow if converted to I1). However some of these conversions behave improperly when performed by ChangeVariantTypeEx. Specifically, for conversions with the same number of bits the value is NOT checked for overflow. So an I1 of ­1 turns into a UI1 of 255. Similarly a UI1 value of 254 turns into an I1 value of ­2. The same applies for I2 and I4. This is an incorrect behavior by ChangeVariantTypeEx. Client programs and users should be aware that most servers will exhibit this behavior since most servers will use VariantChangeTypeEx. Correcting this is recommended but is NOT required for OPC Compliance. Conversions between types with different numbers of bits (e.g. I1, I2, I4) are properly checked for Overflow by VariantChangeTypeEx.

(8) Loss of Precision may occur when converting between various types (e.g. converting from R8 to R4 or from R4 to I4 or I2). However no error is reported as long as there is no OVERFLOW and Quality is returned as GOOD for Reads.

(9) Note that ChangeVariantTypeEx does not handle arrays. Servers which support arrays must implement conversion logic using additional code. For arrays the required behavior is that if any element of the array suffers a conversion error then the first error detected is returned (DISP_E_OVER or DISP_E_TYPE). For Read the Quality is set to BAD and an empty Variant is returned. For Write, if conversion of any element fails then none of the elements are written and the first error encountered is returned.

See Also

IOpcItemProperty | IOpcItemPropertyCollection | OPC Server