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.
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.
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.
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.
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.
Returns the quality of the reading of the last synchronous read. The returned value is integer and can be explained as follows:
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.
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.
Sets the Head=Tail, and therefore resets the circular buffer.
Returns the current value of the Head pointer
Returns the current value of the Tail pointer
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.
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.
PROMES BVBA - Laarsebeekdreef 14 2900 Schoten Belgium
Tel: +32 475 87 69 38 - E-mail: firstname.lastname@example.org