Knowledge base

OPC

OLE for Process Control. More information about the standard itself can be found at the OPC foundation. PRODBX supports the 'Data access' part of the standard. The interface is implemented in opcobj.dll which you can find in your system32 folder. This API dll communicates directly with your OPC server, and is not dependant on any other dll to be installed such as the activeX opcdaauto.dll implementing the OPC.Automation object.

We describe the functions of the opcobj.dll here below with extra information on what it does and what it doesn't.

ErrorHandling

All boolean functions return FALSE when the function was successful, and TRUE when the function failed. If the function failed you can query for the errornumber and errortext.

int OpcError(BSTR *pbstrMessage)

The function returns an integer holding the PRODBX error number, which is translated by the Logging manager, and returns a string parameter holding the Windows errortext.

Error number Description
5327 OPC server is not registered. Please install your OPC server first.
5328 Unable to connect to OPC server
5329 The OPC server does not support the standard OPC server interface.
5330 Unable to create an OPC group.
5331 OPC server does not support synchronous IO.
5332 OPC server does not support item management.
5333 Unable to create the OPC tag.
5345 Wrong tag definition or tag does not exist in OPC server.
5346 Unable to do a synchronous read on tag.
5347 Synchronous read failed. Probably you don't have a connection to the device.
5348 The branch you are querying does not exist or the OPC server does not support browsing.
5349 Problem browsing to the branch. The OPC server may not support browsing, or you lost connection.
5350 Problem getting the leafs of the selected branch.
5351 Problem getting the next branch or leaf from the OPC server.
5352 Could not connect to the HKEY_CLASSES_ROOT of the computer specified. You may not have enough rights to access either the computer or its registry.
5363 Date/Time advise failed when configuring OPC asynchronous support
5364 WriteComplete Advise Failed when configuring OPC asynchronous support
5367 Unable to do a synchronous write on tag.

Getting the OPC serverlist

bool OpcGetServerList(BSTR *bstrMachineName,BSTR *bstrServerList)

MachineName: IP-address or machine name you want the function to return a server list from. Empty string means local machine.

ServerList: returns a string holding all registered OPC servers. Server names are separated by '#'.

To find the serverlist, the dll is querying the HKEY_CLASSES_ROOT registry entries. This means you should have access rights to that registry folder. If the function fails, then most of the times it will be because you don't have access to the registry you are querying.

Connecting to the OPC server

bool OpcConnect(BSTR *pbstrServerMachine,BSTR *pbstrServerName,BSTR *pbstrLicenseKey)

ServerMachine: IP address or machine name holding the OPC server. An empty string must be provided if the OPC server is installed on the same machine as your application. Connecting to a remote machine is done through DCOM. Follow the guidelines of your OPC supplier to configure DCOM. Make sure the OPC server is registered on the calling machine (running your application). Your OPC supplier will provide you the registration file for remote connections.

ServerName: The name of the OPC server. If your OPC supplier follows the naming conventions then this entry looks like '<Supplier name>.<Product name>.<Version>'. Like in previous remark, make sure your OPC server is registered on the machine you want to use your application on.

LicenseKey: You know where this stands for. It gives you weird behaviour if you don't provide this.

int OpcGetServerState()

Function returns 1 when your application is connected with the OPC server

Creating an OPC group

bool OpcAddGroup(BSTR *pbstrGroupName,int iLocaleId,int iUpdateRate,long lTimeBias,float fDeadBand)

GroupName: Any literal holding the name you assign the group.

LocaleId: Is the Windows language Id. (English = 1033). When you configure your OPC server through PRODBX, your Locale Id is defaulted to the active Locale Id on the machine you do the configuring. If you change it, it will be checked against the available Locale Id's. BUT: a lot of OPC suppliers do not follow the rules of Locale Ids. For example: if your system is set to display floating point values with a ',' (Comma) as decimal point, you most likely will not be able to overrule this setting by supplying a different Locale Id. If in this example you provide a value with a '.' (dot) your OPC server will ignore the decimal point!

UpdateRate: time in milliseconds the cache memory of your OPC server is updated. This value is only important is you use the asynchronous triggers.

TimeBias: Value in minutes. In some cases the data may have been collected by a device operating in a time zone other than that of the client. Then it will be useful to know what the time of the device was at the time the data was collected (e.g. to determine what ‘shift’ was on duty at the time). The Time zone Bias provides the information needed to convert the time stamp on the data back to the local time of the device.

DeadBand: Value in %. Only relevant for asynchronous triggers. The trigger is only fired when the value has changed deadband % of the previous value.

The dll supports only one group per object (connection). If you want more groups, with different parameters, you must create a new object in your application to hold a new connection and therefore new group.

Adding a tag (item)

bool OpcAddItem(BSTR *pbstrItem, BSTR *pbstrAccessPath, BSTR *pbstrDataType, INT Position, INT AsyncSupport)

Item: The tag name.

AccessPath: the access path to the tag.

Example: Assume you have a 'Device_5' defined with a group 'Alarms' holding a tag 'SteamPressure'. Your access path will be 'Device_5.Alarms' and the Item 'SteamPressure'. However, some OPC servers do not like this notation and want the accesspath to be empty and the Item defined as 'Device_5.Alarms.SteamPressure'. Do some tests to find out to what notation your OPC server is compatible with.

DataType: ="S" (string). See OpcReadItem.

Position: After the item is added, it will be referenced with this number. Make sure you have a unique number between 0 and 9999 for each item you add.

AsyncSupport: =1 if asynchronous support is required. When an item is defined 'with asynchronous support' it will send triggers to the dll holding the reference number of the item its value being changed. Set the value to 0 when asynchronous support is not required. If you define too many asynchronous items, you will the risk your application is not able to process the values in time, and therefore to clean up the memory being used in the asynchronous interface. This situation will ultimately result in an access violation error.

int OpcGetCanonicalDataType(INT iPosition)

After defining an item you can query for its data type, the data type defined in the OPC server. This might be different then the datatype you defined the item with. This is not an issue since all item reads are treated in variants.

Position: the reference number you defined when you added the item.

The function returns an integer value holding the canonical data type.

Canonical Data type
1 SQL style
2 2 byte signed integer
3 4 byte signed integer
4 4 byte float
5 8 byte float
6 Currency
7 Date
8 String
11 Boolean
14 16 byte fixed point
16 Byte
17 Byte
18 Double Byte
19 Double Word
20 8 byte signed integer
21 8 byte unsigned integer
22 Integer
23 Integer

int OpcGetAccessRights(INT iPosition)

Returns the accessrights of the item, as it is defined in the OPC server.

Position: the reference number you defined when you added the item.

Function returns 1 when the item is read-only, a 3 when the item is writeable.

Reading a tag (Item)

bool OpcSyncReadTag(BSTR *pbstrReturnValue,BSTR *pbstrTimeStamp,INT Position)

Position: the reference number you defined when you added the item. This references the item you want to read.

ReturnValue: Current value of the tag. OpcSyncReadTag is always reading the device and not the cache. The value returned is string type. Conversions should be done in your application. If you are using the dll from a VB-program you can treat this return value as a variant.

TimeStamp: Date and time the device returned when reading the OPC item.

int OpcGetQuality()

Returns the quality of the reading of the last synchronous read. The returned value is integer and can be explained as follows:

Quality Description
0 Bad
64 Uncertain
192 Good
4 Configuration Error
8 Device not connected
12 Device failure
16 Sensor failure
20 Last known value
24 Communication failure
28 Out of service
68 Last usable value
80 Sensor in calibration
84 EGU exceeded
216 Local override

Writing a tag (item)

bool OpcSyncWriteTag(BSTR *pbstrTagToWrite,INT Position)

Position: the reference number you defined when you added the item. This references the item you want to write a value to.

TagToWrite: value to be written to the item. Note that this is always a string value. Make sure you check the format conventions when writing a numeric value, such as the decimal point for float.

Asynchronous support

When you added items with the asynchronous support option, then each time the value of this item changes (in the boundaries defined in the group), an interrupt is triggered. The interrupt points to a routine in the dll that captures the reference of the tag that was triggered (Position). The reference is then stored in a circular buffer. This buffer of 1000 items is controlled by 2 pointers called 'Head' and 'Tail'. When an interrupt occurs, the asynchronous routine will increment the 'Head' value and store the reference of the tag at that position in the buffer. When the 'Head' value becomes 1000 it is reset to 0 to start writing in the beginning of the buffer again. Each time you read from the circular buffer, the 'Tail' value is incremented, and the routine will return the value on the new 'Tail' position, which is the reference of a triggered item-tag.

However, before writing to the circular buffer, the asynchronous routine will check if the reference of the triggered item already exists in the buffer between the 'Tail' and 'Head' pointer. If it exists, the routine will not write the same reference to the buffer again.

bool OpcResetBuffer()

Sets the Head=Tail, and therefore resets the circular buffer.

int OpcGetBufferHead()

Returns the current value of the Head pointer

int OpcGetBufferTail()

Returns the current value of the Tail pointer

int OpcGetAsyncTag()

Returns the next reference stored in the circular buffer.

After retrieving the reference you should call the OpcSyncReadItem to get the actual value of the item.

Browsing the OPC server

Browsing the OPC server is browsing through its branches and leafs. Branches may be compared with the access paths and leafs with the actual items.

bool OpcShowBranch(BSTR *bstrBranche)

Function prepares a new collection of branches in the bstrBranch. If you provided an empty string, the function will prepare a collection of all branches at the root.

bool OpcShowLeafs(BSTR *bstrBranche)

Function prepares a new collection of leafs in the bstrBranch.

int OpcGetItemCount()

After using the functions OpcShowBranch or OpcShowLeafs you can use this function to find out how many branches or leaves the collection has.

bool OpcGetNextItem(BSTR *pbstrNextItem)

Each time this function is called it will return the next item in the collection prepared by OpcShowBranch or OpcShowLeafs.