OPC Custom Interface

Overview of the OPC Custom Interface
 

General Information
 

OPCGroup Object

The OPCGroup object is the object that an OPC server delivers to manage a collection of items. The interfaces that this object provides include: The functionality provided by each of these interfaces is defined in this section.

This section also identifies the interfaces required to be implemented to support the OLE mechanism for delivering a COM interface.
 
 

General Properties The OPCGroup has certain general properties and behaviors which affect the operation of the Interfaces and Methods. These are discussed here in order to minimize duplication. Name Each group has a name. For private groups the name must be unique among the other private groups that belong to that client. For public groups the name must be unique among all of the public groups. While a client can change the name of a private group, the name of a public group cannot be changed.

A private Group and a public group may have the same name as long as the client is not connected to the public group with the same name.

Group names are Case Sensitive. Group1 would be different from group1.

Cached data The methods below allow the client to specify that some operations can be performed on ‘CACHE’ or ‘DEVICE’. It is expected that most servers will implement some sort of CACHE. As discussed earlier these terms are simply part of the interface definition. The way the functions described below behave differs slightly based on which source is specified. The actual details of the implementation of this functionality is up to the server vendor. In most cases, access to CACHE data is expected to be ‘fast’ while access to the ‘DEVICE is expected to be ‘slow’ but more accurate. CACHE data is affected by the Active state of the group and the items in the group while DEVICE data is not. Note again that although we sometimes make suggestions, this specification does not dictate any particular implementation or performance. Active Groups and Items within Groups have an Active Flag. The active state of the group is maintained separately from the active state of the items. Changing the state of the group does not change the state of the items.

For the most part the Active flag is treated as ‘abstract’ within this specification. The state of these flags affects the described behavior of various interfaces in a well defined way. The implementation details of these capabilities is not dictated by this specification.

In practice it is expected that most servers will make use of this flag to optimize their use of communications and CPU resources. Items and Groups which are not active do not need to be maintained in the CACHE.

It is also expected that clients will simply set and clear active flags of groups and items as a more efficient alternative to adding and removing entire groups and items. For example if an operator display is minimized, its items might be set to inactive.

Refer to the Data Acquisition and Active State Behavior summary earlier in this document for a quick overview of the behavior of a client and server with respect to the active state of a group and items.

OnDataChange within the client's address space can be called whenever any active item data in a active group changes, where "change" is defined as a change in value (from the last value sent to this client), or a change in the Quality of the value. The server can return values and quality flags for those items within the group that changed.(this will be discussed more in later sections)

Update Rate The client can specify an ‘update rate’ for each group. This determines the time between when the exception limit is checked. If the exception limit is exceeded, the CACHE is updated. The server should make a ‘best effort’ to keep the data fresh. This also affects the maximum rate at which notifications will be sent to the IAdvise sink. The server should never send data to a client at a rate faster than the client requests.

IMPORTANT:

Note that this is NOT necessarily related to the server's underlying processing rate. For example if a device is performing PID control at 0.05 second rate the an MMI requests updates at a 5 second rate via OPC, the device would of course continue to control at a 0.05 second rate.

In addition, the server implementation would also be allowed to update the cached data available to sync or async read at a higher rate than 5 seconds if it wished to do so. All the update rate indicates is that (a) callbacks should happen no faster than this and (b) the cache should be updated at at least this rate.

The update rate is a ‘request’ from the client. The server should respond with an update rate that is as close as possible to that requested.

Time Zone (Time Bias) 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.

This time zone information may rarely be used and the device providing the data may not know its local time zone, therefore it was not prudent to add this overhead to all data transactions. Instead, the OPCGroup provides a place to store a time zone which can be set and read by the client. The default value for this is the time zone of the host computer. The OPCServer will not make use of this value. It is there only for the convenience of the client.

Percent Deadband Deadband will only apply to items in the group that have a dwEUType of Analog available. If the dwEUType is Analog, then the EU Low and EU High values for the item can be used to calculate the range for the item. This range will be multiplied with the Deadband to generate an exception limit. An exception is determined as follows:

Exception if (absolute value of (last cached value - current value) > pPercentDeadband * (EU High - EU Low) )

If the exception limit is exceeded, then the last cached value is updated with the new value and a notification will be sent to the IAdviseSink (if any). The pPercentDeadband is an optional behavior for the server. If the client does not specify this value on a server that does support the behavior, the default value of 0 (zero) will be assumed, and all value changes will update the CACHE. Note that the timestamp will be updated regardless of wether the cached value is updated.

The UpdateRate for a group determines time between when a value is checked to see if the exception limit has been exceeded. The PercentDeadband is used to keep noisy signals from updating the client unnecessarily.

ClientHandle This handle will be returned in the data stream to IAdviseSink. This allows the client to identify the group to which the data packet belongs.

It is expected that a client will assign unique value to the client handle if it intends to use any of the asynchronous functions of the OPC interfaces, including IOPCAsyncIO, IOPCAsyncIOs, and IDataObject/IAdviseSink or IConnectionPoint/IOPCDataCallback interfaces.

Reading and Writing Data There are basically three ways to get data into a client (ignoring the 'old' IDataObject/IAdviseSink). In general the three methods operate independently without ‘side effects’ on each other.

There are two ways to write data out:

Public Groups It is required that the server track each client's group properties (update rate, deadband, active status, timezone, lcid) for a public group. For example, if two clients with different LCIDs want data from a public group, they can change the state of the group to reflect their LCID and the server must keep track of both.
 
 
 
IOPCItemMgt IOPCItemMgt allows a client to add, remove and control the behavior of items is a group. IOPCItemMgt::AddItems

HRESULT AddItems(

[in] DWORD dwCount,

[in, size_is(dwCount)] OPCITEMDEF * pItemArray,

[out, size_is(,dwCount)] OPCITEMRESULT ** ppAddResults,

[out, size_is(,dwCount)] HRESULT ** ppErrors

);
 
 

Description

Add one or more items to a group.
Parameters Description
dwCount The number if items to be added
pItemArray Array of OPCITEMDEFs. These tell the server everything it needs to know about the item including the access path, definition and requested datatype
ppAddResults Array of OPCITEMRESULTs. This tells the client additional information about the item including the server assigned item handle and the canonical datatype.
ppErrors Array of HRESULTs. This tells the client which of the items was successfully added. For any item which failed it provides a reason.

HRESULT Return Codes
Return Code Description
E_FAIL The operation failed.
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.
S_OK The operation succeeded.
S_FALSE The operation completed with partial success. Refer to individual error returns for failure analysis.
OPC_E_PUBLIC Cannot add items to a public group

 

ppErrors Return Codes
Return Code Description
S_OK The function was successful for this item.
OPC_E_INVALIDITEMID The ItemID is not syntactically valid
OPC_E_UNKNOWNITEMID The ItemID is not in the server address space
OPC_E_BADTYPE The requested data type cannot be returned for this item (See comment)
E_FAIL The function was unsuccessful. 
OPC_E_UNKNOWNPATH The item's access path is not known to the server.

Comments

It is acceptable to add the same item to the group more than once. This will generate a 2nd item with a unique ServerHandle.

Any FAILED code in ppErrors indicates that the corresponding item was NOT added to the group and that the corresponding OPCITEMRESULT will not contain useful information.

As an alternative to OPC_E_BADTPYE it is acceptable for the server to return any FAILED error returned by VariantChangeType or VariantChangeTypeEx.

The server provided item handle will be unique within the group, but may not be unique across groups. The server is allowed to ‘reuse’ the handles of deleted items.

Items cannot be added to public groups.

The client needs to free all of the memory associated with the OPCITEMRESULTs including the BLOB.

If the server supports the BLOB it will return an updated BLOB in the OPCITEMRESULTs. This BLOB may differ in both content and size from the one passed by the client in OPCITEMDEF.

Note that if an Advise is active, the client will begin receiving callbacks for active items. This can occur very quickly, perhaps even before the client has time to process the returned results. The client must be designed to deal with this. One simple solution is for the client to clear the Active state of the group while doing AddItems and to restore it after the AddItems is completed and the results are processed.
 
 
 
 

IOPCItemMgt::ValidateItems

HRESULT ValidateItems(

[in] DWORD dwCount,

[in, size_is(dwCount)] OPCITEMDEF * pItemArray,

[in] BOOL bBlobUpdate,

[out, size_is(,dwCount)] OPCITEMRESULT ** ppValidationResults,

[out, size_is(,dwCount)] HRESULT ** ppErrors

);
 
 

Description

Determines if an item is valid (could it be added without error). Also returns information about the item such as canonical datatype. Does not affect the group in any way.
Parameters Description
dwCount The number if items to be validated
pItemArray Array of OPCITEMDEFs. These tell the server everything it needs to know about the item including the access path, definition and requested datatype
bBlobUpdate If non-zero (and the server supports Blobs) the server should return updated Blobs in OPCITEMRESULTs. If zero (False) the server will not return Blobs in OPCITEMRESULTs.
ppValidationResults Array of OPCITEMRESULTs. This tells the client additional information about the item including the canonical datatype.
ppErrors Array of HRESULTs. This tells the client which of the items was successfully validated. For any item which failed it provides a reason.

HRESULT Return Codes
Return Code Description
E_FAIL The operation failed.
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.
S_OK The operation succeeded.
S_FALSE The operation completed with partial success. Refer to individual error returns for failure analysis.

 
 
 

ppErrors Codes
Return Code Description
S_OK The function was successful for this item.
OPC_E_INVALIDITEMID The ItemID is not syntactically valid
OPC_E_UNKNOWNITEMID The ItemID is not in the server address space
OPC_E_BADTYPE The requested data type cannot be returned for this item (See comment)
E_FAIL The function was unsuccessful for this item. 
OPC_E_UNKNOWNPATH The item's access path is not known to the server.

 

Comments

The client needs to free all of the memory associated with the OPCITEMRESULTs including the BLOB.

As an alternative to OPC_E_BADTPYE it is acceptable for the server to return any FAILED error returned by VariantChangeType or VariantChangeTypeEx.
 
 

IOPCItemMgt::RemoveItems

HRESULT RemoveItems(

[in] DWORD dwCount,

[in, size_is(dwCount)] OPCHANDLE * phServer,

[out, size_is(,dwCount)] HRESULT ** ppErrors

);
 
 

Description

Removes (deletes) items from a group. Basically this is the reverse of AddItems.
Parameters Description
dwCount Number of items to be removed
phServer Array of server items handles. These were returned from AddItem.
ppErrors Array of HRESULTs. Indicates which items were successfully removed.

HRESULT Return Codes
Return Code Description
S_OK The function was successful.
S_FALSE The function was partially successful. See the ppErrors to determine what happened
E_FAIL The function was unsuccessful. 
OPC_E_PUBLIC Cannot remove items from a public group

ppError Codes
Return Code Description
S_OK The corresponding item was removed.
OPC_E_INVALIDHANDLE The corresponding Item handle was invalid.

Comments

Adding and removing items from a group does not affect the address space of the server or physical device. It simply indicates whether or not the client is interested in those particular items.

Items are not really objects in the custom interface (do not have interfaces), and there is no concept of a reference count for items. The client should insure that no further references are made to deleted items.

Items cannot be removed from a public group.
 
 

IOPCItemMgt::SetActiveState

HRESULT SetActiveState(

[in] DWORD dwCount,

[in, size_is(dwCount)] OPCHANDLE * phServer,

[in] BOOL bActive,

[out, size_is(,dwCount)] HRESULT ** ppErrors

);
 
 

Description

Sets one or more items in a group to active or inactive. This controls whether or not valid data can be obtained from Read CACHE for those items and whether or not they are included in the IAdvise subscription to the group.
Parameters Description
dwCount The number of items to be affected
phServer Array of Server items handles.
bActive TRUE if items are to be activated. FALSE if items are to be deactivated.
ppErrors Array of HRESULTs. Indicates which items were successfully affected.

HRESULT Return Codes
Return Code Description
S_OK The function was successful.
S_FALSE The function was partially successful. See the ppErrors to determine what happened
E_FAIL The function was unsuccessful. 

ppError Codes
Return Code Description
S_OK The function was successful.
OPC_E_INVALIDHANDLE The corresponding Item handle was invalid.

Comments

Deactivating items will not result in a callback (since by definition callbacks do not occur for inactive items). Activating items will generally result in an IAdvise callback at the next UpdateRate period.
 
IOPCItemMgt::SetClientHandles

HRESULT SetClientHandles(

[in] DWORD dwCount,

[in, size_is(dwCount)] OPCHANDLE * phServer,

[in, size_is(dwCount)] OPCHANDLE * phClient,

[out, size_is(,dwCount)] HRESULT ** ppErrors

);
 
 

Description

Changes the client handle for one or more items in a group.
Parameters Description
dwCount The number of items to be affected
phServer Array of Server items handles.
phClient Array of new Client item handles to be stored. The Client handles do not need to be unique.
ppErrors Array of HRESULTs. Indicates which items were successfully affected.

HRESULT Return Codes
Return Code Description
S_OK The function was successful.
S_FALSE The function was partially successful. See the itemResults to determine what happened
E_FAIL The function was unsuccessful. 

itemResults Codes
Return CodeDescription
S_OK The function was successful.
OPC_E_INVALIDHANDLE The corresponding Item handle was invalid.

Comments

In general, it is expected that clients will set the client handle when the item is added and not change it later. This function is most useful for setting the client handles for items in a public group to which the client has connected.
 
IOPCItemMgt::SetDatatypes

HRESULT SetDatatypes(

[in] DWORD dwCount,

[in, size_is(dwCount)] OPCHANDLE * phServer,

[in, size_is(dwCount)] VARTYPE * pRequestedDatatypes,

[out, size_is(,dwCount)] HRESULT ** ppErrors

);
 
 

Description

Changes the requested data type for one or more items in a group.
Parameters Description
dwCount The number of items to be affected
phServer Array of Server items handles.
pRequestedDatatypes Array of new Requested Datatypes to be stored.
ppErrors Array of HRESULT’s. Indicates which items were successfully affected.

HRESULT Return Codes
Return Code Description
S_OK The function was successful.
S_FALSE The function was partially successful. See the itemResults to determine what happened
E_FAIL The function was unsuccessful. 

itemResults Codes
Return Code Description
S_OK The function was successful.
OPC_E_INVALIDHANDLE The corresponding Item handle was invalid.
OPC_E_BADTYPE The requested datatype cannot be supported for this item. (See comment). The previous requested type is left unchanged.

Comments

In general, it is expected that clients will set the requested datatype when the item is added and not change it later. This function is most useful for setting the datatype for items in a public group to which the client has connected.

As an alternative to OPC_E_BADTPYE it is acceptable for the server to return any FAILED error returned by VariantChangeType or VariantChangeTypeEx.
 
 

IOPCItemMgt::CreateEnumerator

HRESULT CreateEnumerator(

[in] REFIID riid,

[out, iid_is(riid)] LPUNKNOWN* ppUnk

);
 
 

Description

Create an enumerator for the items in the group.
Parameters Description
riid The interface requested. Normally this is IID_IEnumOPCItemAttributes.
ppUnk Where to return the interface. NULL is returned for any HRESULT other than S_OK

HRESULT Return Codes
Return Code Description
S_OK The function was successful.
S_FALSE There is nothing to enumerate (There are no items in the group).
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid (e.g. a bad riid parameter was passed.)
E_FAIL The function was unsuccessful. 

Comments

The client must release the returned interface pointer when it is done with it.

IOPCGroupStateMgt

IOPCGroupStateMgt allows the client to manage the overall state of the group. Primarily this allows changes to the update rate and active state of the group. IOPCGroupStateMgt::GetState HRESULT GetState(

[out] DWORD * pUpdateRate,

[out] BOOL * pActive,

[out, string] LPWSTR * ppName,

[out] LONG * pTimeBias,

[out] FLOAT * pPercentDeadband,

[out] DWORD * pLCID,

[out] OPCHANDLE * phClientGroup,

[out] OPCHANDLE * phServerGroup

);
 
 

Description Get the current state of the group.
Parameters Description
pUpdateRate The current update rate. The Update Rate is in milliseconds
pActive The current active state of the group.
ppName The current name of the group
pTimeBias The TimeZone Bias of the group (in minutes)
pPercentDeadband The percent change in an item value that will cause an exception report of that value to a client. This parameter only applies to items in the group that have dwEUType of Analog. [See discussion of Percent Deadband in Section 0]
pLCID The current LCID for the group.
phClientGroup The client supplied group handle
phServerGroup The server generated group handle

HRESULT Return Codes
Return Code Description
E_FAIL The operation failed.
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.
S_OK The operation succeeded.

Comments

This function is typically called to obtain the current values of this information prior to calling SetState. This information was all supplied by or returned to the client when the group was created. This function is also useful for debugging.

All out arguments must be valid pointers. The marshaling mechanism requires valid pointers for proper behavior. NULL pointers will throw an RPC exception.

The client must free the returned ppName string.
 
 

IOPCGroupStateMgt::SetState

HRESULT SetState(

[unique, in] DWORD * pRequestedUpdateRate,

[out] DWORD * pRevisedUpdateRate,

[unique, in] BOOL *pActive,

[unique, in] LONG * pTimeBias,

[unique, in] FLOAT * pPercentDeadband

[unique, in] DWORD * pLCID,

[unique, in] OPCHANDLE *phClientGroup

);

Description

Client can set various properties of the group. Pointers to ‘in’ items are used so that the client can omit properties he does not want to change by passing a NULL pointer.

The pRevisedUpdateRate argument must contain a valid pointer.
 
 
 
 

Parameters Description
pRequestedUpdateRate New update rate requested for the group by the client
pRevisedUpdateRate Closest update rate the server is able to provide for this group. 
pActive TRUE (non-zero) to active the group. FALSE (0) to deactivate the group. 
pTimeBias TimeZone Bias if Group (in minutes)
pPercentDeadband The percent change in an item value that will cause an exception report of that value to a client. This parameter only applies to items in the group that have dwEUType of Analog. [See discussion of Percent Deadband in Section 0]
pLCID The Localization ID to be used by the group.
phClientGroup New client supplied handle for the group. This handle is returned in the data stream provided to the client’s IAdvise by the Groups IDataObject.

HRESULT Return Codes
Return Code Description
E_FAIL The operation failed.
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.
S_OK The operation succeeded.
OPC_S_UNSUPPORTEDRATE  The server does not support the requested data rate but will use the closest available rate.

 

Comments

For public groups, the server maintains unique state information for each client for Active, pUpdateRate, TimeZone. That is, the public groups behave as if each client had it’s own private copy.

Refer to Data Acquistion Section for details on the behavior of an OPC server with respect to the Synchronous and Asynchronous interfaces and Active state of groups.
 
 

IOPCGroupStateMgt::SetName

HRESULT SetName(

[in, string] LPCWSTR szName,

);
 
 

Description

Change the name of a private group. The name must be unique. The name cannot be changed for public groups.
Parameters Description
szName New name for group.

HRESULT Return Codes
Return Code Description
E_FAIL The operation failed.
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.
S_OK The operation succeeded.
OPC_E_DUPLICATENAME Duplicate name not allowed.

 

Comments

Group names are required to be unique with respect to an individual client to server connection.
 
IOPCGroupStateMgt::CloneGroup

HRESULT CloneGroup(

[in, string] LPCWSTR szName,

[in] REFIID riid,

[out, iid_is(riid)] LPUNKNOWN * ppUnk

);

Description

Creates a second copy of a group with a unique name. This works for both public and private groups. However, the new group is always a private group. All of the group and item properties are duplicated (as if the same set of AddItems calls had been made for the new group). That is, the new group contains the same update rate, items, group and item clienthandles, requested data types, etc as the original group. Once the new group is created it is entirely independent of the old group. You can add and delete items from it without affecting the old group.

Properties NOT copied to the new group are

Parameters Description
szName Name of the group. The name must be unique among the other groups created by this client. If no name is provided (szName is a pointer to a NUL string) the server will generate a unique name. The server generated name will also be unique relative to any existing public groups.
riid requested interface type
ppUnk place to return interface pointer. NULL is returned for any HRESULT other than S_OK

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded.
E_FAIL The operation failed.
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.
OPC_E_DUPLICATENAME Duplicate name not allowed.
E_NOINTERFACE The interface(riid) asked for is not supported by the server.

 

Comments

This represents a new group which is independent of the original group. See AddGroup for a discussion of Group object lifetime issues. As with AddGroup the group must be deleted with RemoveGroup when the client is done with it.

The client must also release the returned interface when it is no longer needed.

The primary use or intent of this function is to create a private duplicate of a public group which can then be modified by the client.
 
 

IOPCPublicGroupStateMgt This optional interface is used to convert a private group to a public group. Servers optionally provide this interface on group objects. A group created by a client is always created initially as a private group. This interface can be obtained from that private group in order to convert the group to a public group. IOPCPublicGroupStateMgt::GetState HRESULT GetState(

[out] BOOL * pPublic

);
 
 

Description Used to determine if a particular group is public or not. If the interface is missing then all groups in the server are private.
Parameters Description
pPublic TRUE if the group is public, FALSE if it is private

HRESULT Return Codes
Return Code Description
E_FAIL The operation failed.
E_INVALIDARG An argument to the function was invalid.
S_OK The operation succeeded.

 

Comments

A server which supports public groups can provide this interface for any group (private or public). In practice a client will generally know for each group whether it is private or public. However, this method is useful for debugging.
 
IOPCPublicGroupStateMgt::MoveToPublic HRESULT MoveToPublic(

void

);
 
 

Description Converts a private group to a public group. The group must have a name which must be unique among all existing public groups. The state of the group (active, UpdateRate, IAdvise connections, etc.) for the calling client is not affected.
Parameters Description
void  

HRESULT Return Codes
Return Code Description
S_OK The function was successful.
E_OUTOFMEMORY Not enough memory
E_FAIL The function was unsuccessful. 
OPC_E_DUPLICATENAME Duplicate Name not allowed

Comments

A public group cannot be converted back to a private group. However it can be ‘cloned’ into a new private group.

For public groups, the update rate, client group handle and active status are maintained as ‘instance’ data for each client.

The client is required to set the client groupHandle before any asynchronous functions are performed on a public group. After the group is made public other clients can connect to the group. Generally, they must set their client instance information (e.g. group and item handles) prior to using the other standard group interfaces associated with a group.

Once a group is made public, items cannot be added or deleted.

For the items in the group, the client handle, active status and requested data type are maintained as ‘instance’ data for each client.
 
 

IOPCSyncIO IOPCSyncIO allows a client to perform synchronous read and write operations to a server. The operations will run to completion.

Refer to the Data Acquisition and Active State Behavior table for an overview of the server data acquisition behavior and it’s affect on functionality within this interface.

Also refer to the Serialization and Syncronization issues section earlier in this document.

IOPCSyncIO::Read

HRESULT Read(

[in] OPCDATASOURCE dwSource,

[in] DWORD dwCount,

[in, size_is(dwCount)] OPCHANDLE * phServer,

[out, size_is(,dwCount)] OPCITEMSTATE ** ppItemValues,

[out, size_is(,dwCount)] HRESULT ** ppErrors

);
 
 

Description

This function reads the value, quality and timestamp information for one or more items in a group. The function runs to completion before returning. The data can be read from CACHE in which case it should be accurate to within the ‘UpdateRate’ and percent deadband of the group. The data can be read from the DEVICE in which case an actual read of the physical device is to be performed. The exact implementation of CACHE and DEVICE reads is not defined by this specification.

When reading from CACHE, the data is only valid if both the group and the item are active. If either the group or the item is inactive, then the Quality will indicate out of service (OPC_QUALITY_OUT_OF_SERVICE). Refer to the discussion of the quality bits later in this document for further information.

DEVICE reads are not affected by the ACTIVE state of the group or item.

Refer to the Data Acquisition and Active State Behavior table earlier in this document for an overview of the server data acquisition behavior and it’s affect on functionality within this interface.

Parameters Description
dwSource The ‘data source’; OPC_DS_CACHE or OPC_DS_DEVICE 
dwCount The number of items to be read.
phServer The list of server item handles for the items to be read
ppItemValues Array of structures in which the item values are returned.
ppErrors Array of HRESULTs indicating the success of the individual item reads. The errors correspond to the handles passed in phServer. This indicates whether the read succeeded in obtaining a defined value, quality and timestamp. NOTE any FAILED error code indicates that the corresponding Value, Quality and Time stamp are UNDEFINED.

 

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded. 
S_FALSE The operation succeeded but there are one or more errors in ppErrors. Refer to individual error returns for more infomation.
E_FAIL The operation failed.
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.

 

ppError Codes
Return Code Description
S_OK Successful Read.
E_FAIL The Read failed for this item
OPC_E_BADRIGHTS The item is not readable
OPC_E_INVALIDHANDLE The passed item handle was invalid. 
OPC_E_UNKNOWNITEMID The item is no longer available in the server address space. 
S_xxx

E_xxx

S_xxx - Vendor specific information can be provided if this item quality is other than GOOD.

E_xxx - Vendor specific error if this item cannot be accessed.

These vendor specific codes can be passed to GetErrorString().

Comments

If the HRESULT is S_OK, then ppError can be ignored (all results in it are guaranteed to be S_OK).

If the HRESULT is S_FALSE, then ppError will indicate which the status of each individual Item Read.

If the HRESULT is any FAILED code then (as noted earlier) the server should return NULL pointers for all OUT parameters including ppErrors.

For any S_xxx ppError code the client should assume the curresponding ITEMSTATE is well defined although the Quality may be UNCERTAIN or BAD. It is recommended (but not required) that server vendors provide additional information here regarding UNCERTAIN or BAD items.

For any FAILED ppError code the client should assume the curresponding ITEMSTATE is undefined. In fact the Server must set the corresponding ITEMSTATE VARIANT to VT_EMPTY so that it can be marshalled properly and so that the client can execute VariantClear on it.

Note that here (as in the OPCItemMgt methods) OPC_E_INVALIDHANDLE on one item will not affect the processing of other items and will cause the main HRESULT to return as S_FALSE

Expected behavior is that a CACHE read should be completed very quickly (within milliseconds). A DEVICE read may take a very long time (many seconds or more). Depending on the details of the implementation (e.g. which threading model is used) the DEVICE read may also prevent any other operations from being performed on the server by any other clients.

For this reason Clients are expected to use CACHE reads in most cases. DEVICE reads are intended for ‘special’ circumstances such as diagnostics.

The ppItemValues and ppErrors arrays are allocated by the server and must be freed by the client. Be sure to call VariantClear() on the variant in the ITEMRESULT.
 
 

IOPCSyncIO::Write

HRESULT Write(

[in] DWORD dwCount,

[in, size_is(dwCount)] OPCHANDLE * phServer,

[in, size_is(dwCount)] VARIANT * pItemValues,

[out, size_is(,dwCount)] HRESULT ** ppErrors

);

Description

Writes values to one or more items in a group. The function runs to completion. The values are written to the DEVICE. That is, the function should not return until it verifies that the device has actually accepted (or rejected) the data.

Writes are not affected by the ACTIVE state of the group or item.
 
 
 
 

Parameters Description
dwCount Number of items to be written
phServer The list of server item handles for the items to be read
pItemValues List of values to be written to the items. The datatypes of the values do not need to match the datatypes of the target items. However an error will be returned if a conversion cannot be done.
ppErrors Array of HRESULTs indicating the success of the individual item Writes. The errors correspond to the handles passed in phServer. This indicates whether the target device or system accepted the value. NOTE any FAILED error code indicates that the value was rejected by the device. 

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded.
S_FALSE The operation succeeded but there are one or more errors in ppErrors. Refer to individual error returns for more infomation.
E_FAIL The operation failed.
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.

ppError Codes
Return Code Description
S_OK The function was successful.
E_FAIL The function was unsuccessful. 
OPC_S_CLAMP The value was accepted but was clamped.
OPC_E_RANGE The value was out of range.
OPC_E_BADTYPE The passed data type cannot be accepted for this item (See comment)
OPC_E_BADRIGHTS The item is not writeable
OPC_E_INVALIDHANDLE The passed item handle was invalid. 
OPC_E_UNKNOWNITEMID The item is no longer available in the server address space
E_xxx

S_xxx

Vendor specific errors may also be returned. Descriptive information for such errors can be obtained from GetErrorString.

 
 
 

Comments

If the HRESULT is S_OK, then ppError can be ignored (all results in it are guaranteed to be S_OK).

If the HRESULT is any FAILED code then (as noted earlier) the server should return NULL pointers for all OUT parameters.

Note that here (as in the OPCItemMgt methods) OPC_E_INVALIDHANDLE on one item will not affect the processing of other items and will cause the main HRESULT to return as S_FALSE

As an alternative to OPC_E_BADTPYE it is acceptable for the server to return any FAILED error returned by VariantChangeType or VariantChangeTypeEx.

A DEVICE write may take a very long time (many seconds or more). Depending on the details of the implementation (e.g. which threading model is used) the DEVICE write may also prevent any other operations from being performed on the server by any other clients.

For this reason Clients are expected to use ASYNC write rather than SYNC write in most cases.

The ppErrors array is allocated by the server and must be freed by the client.
 
 
 
 

IOPCAsyncIO2 This interface is similar to IOPCAsync. This interface is intended to replace IOPCAsyncIO.

It differs from AsyncIO as follows;

IOPCAsyncIO2 allows a client to perform asynchronous read and write operations to a server. The operations will be ‘queued’ and the function will return immediately so that the client can continue to run. Each operation is treated as a ‘transaction’ and is associated with a transaction ID. As the operations are completed, a callback will be made to the IOPCDataCallback in the client. The information in the callback will indicate the transaction ID and the results of the operation.

Also the expected behavior is that for any one transaction to Async Read, Write and Refresh, ALL of the results of that transaction will be returned in a single call to appropriate function in IOPCDataCallback.

A server must be able to ‘queue’ at least one transaction of each type (read, write, refresh) for each group. It is acceptable for a server to return an error (CONNECT_E_ADVISELIMIT) if more than one transaction of the same type is performed on the same group by the same client. Server vendors may of course support queueing of additional transactions if they wish.

All operations that are successfully started are expected to complete even if they complete with an error. The concept of ‘time-out’ is not explicitly addressed in this specification however it is expected that where appropriate the server will internally implement any needed time-out logic and return a server specific error to the caller if this occurs.

Client Implementation Note:

The Unique Transaction ID passed to Read, Write and Refresh is generated by the Client and is returned to the client in the callback. This ID must be non-zero and unique to this particular client/server conversation. It does not need to be unique relative to other conversations by this or other clients. Note that the Group's Clienthandle is also returned in the callback and is generally sufficient to identify the returned data.

IMPORTANT NOTE: depending on the mix of client and server threading models used, it has been found in practice that the IOPCDataCallback can occur within the same thread as the Refresh, Read or Write and in fact can occur before the Read, Write or Refresh method returns to the caller.

Thus, if the client wants to save a record of the transaction in some list of ‘outstanding transactions’ in order to verify completion of a transaction it will need to generate the Transaction ID and save it BEFORE making the method call.

In practice most clients will probably not need to maintain such a list and so do not actually need to record the transaction ID.
 
 
 
 

IOPCAsyncIO2::Read

HRESULT Read(

[in] DWORD dwCount,

[in, size_is(dwCount)] OPCHANDLE * phServer,

[in] DWORD dwTransactionID,

[out] DWORD *pdwCancelID,

[out, size_is(,dwCount)] HRESULT ** ppErrors

);
 
 

Description

Read one or more items in a group. The results are returned via the client’s IOPCDataCallback connection established through the server’s IConnectionPointContainer.

Reads are from ‘DEVICE’ and are not affected by the ACTIVE state of the group or item.

Parameters Description
dwCount Number of items to be read.
phServer Array of server item handles of the items to be read
dwTransactionID The Client generated transaction ID. This is included in the ‘completion’ information provided to the OnReadComplete.
pdwCancelID Place to return a Server generated ID to be used in case the operation needs to be canceled.
ppErrors Array of errors for each item - returned by the server. See below.

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded. The read was successfully initiated
E_FAIL The operation failed.
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.
S_FALSE One or more of the passed items could not be read The ppError array indicates which items in phServer could not be read. Any items which do not return errors (E) here will be read and results will be returned to OnReadComplete. Items which do return errors here will not be returned in the callback.
CONNECT_E_NOCONNECTION The client has not registered a callback through IConnectionPoint::Advise.

 

ppError Codes
Return Code Description
S_OK The corresponding Item handle was valid and the item information will be returned on OnReadComplete.
E_FAIL The Read failed for this item
OPC_E_BADRIGHTS The item is not readable
OPC_E_INVALIDHANDLE The passed item handle was invalid. 
OPC_E_UNKNOWNITEMID The item is no longer available in the server address space. 
E_xxx

S_xxx

Vendor specific errors may also be returned. Descriptive information for such errors can be obtained from GetErrorString.

 

Comments

Some servers will be ‘smarter’ at read time and return ‘early’ errors, others may simply queue the request with minimal checking and return ‘late’ errors in the callback. The client should be prepared to deal with this.

If the HRESULT is S_OK, then ppError can be ignored (all results in it are guaranteed to be S_OK).

If the HRESULT is any FAILED code then (as noted earlier) the server should return NULL pointers for all OUT parameters. Note that in this case no Callback will occur.

If ALL errors in ppError are Failure codes then No callback will take place.

Items for which ppError returns any success code (including S_xxx) will be returned in the OnReadComplete callback. Note that the error result for an item returned in the callback may differ from that returned from Read.

NOTE: the server must return all of the results in a single callback. Thus, if the items in the group require multiple physical transactions to one or more physical devices then the server must wait until all of them are complete before invoking OnReadComplete.

The Client must free the returned ppError array.
 
 

IOPCAsyncIO2::Write

HRESULT Write(

[in] DWORD dwCount,

[in, size_is(dwCount)] OPCHANDLE * phServer,

[in, size_is(dwCount)] VARIANT * pItemValues,

[in] DWORD dwTransactionID,

[out] DWORD *pdwCancelID,

[out, size_is(,dwCount)] HRESULT ** ppErrors

);
 
 

Description

Write one or more items in a group. The results are returned via the client’s IOPCDataCallback connection established through the server’s IConnectionPointContainer.
Parameters Description
dwCount Number of items to be written
phServer List of server items handles for the items to be written
pItemValues List of values to be written. The value data types do not match the requested or canonical item datatype but must be ‘convertible’ to the canonical type.
dwTransactionID The Client generated transaction ID. This is included in the ‘completion’ information provided to the OnWriteComplete.
pdwCancelID Place to return a Server generated ID to be used in case the operation needs to be canceled.
ppErrors Array of errors for each item - returned by the server. See below.

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded.
E_FAIL The operation failed.
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.
S_FALSE One or more of the passed items could not be written The ppError array indicates which items in phServer could not be write. Any items which do not return errors (E) here will be written and results will be returned to OnWriteComplete. Items which do return errors here will not be returned in the callback.
CONNECT_E_NOCONNECTION The client has not registered a callback through IConnectionPoint::Advise.

 

ppError Codes
Return Code Description
S_OK The corresponding Item handle was valid. The write will be attempted and the results will be returned on OnWriteComplete
E_FAIL The function was unsuccessful. 
OPC_E_BADRIGHTS The item is not writeable
OPC_E_INVALIDHANDLE The passed item handle was invalid. 
OPC_E_UNKNOWNITEMID The item is no longer available in the server address space
E_xxx

S_xxx

Vendor specific errors may also be returned. Descriptive information for such errors can be obtained from GetErrorString.

 

Comments

Some servers will be ‘smarter’ at write time and return ‘early’ errors, others may simply queue the request with minimal checking and return ‘late’ errors in the callback. The client should be prepared to deal with this.

If the HRESULT is S_OK, then ppError can be ignored (all results in it are guaranteed to be S_OK).

If the HRESULT is any FAILED code then (as noted earlier) the server should return NULL pointers for all OUT parameters. Note that in this case no Callback will occur.

If ALL errors in ppError are Failure codes then No callback will take place.

Items for which ppError returns any success code (including S_xxx) will also have a result returned in the OnWriteComplete callback. Note that the error result for an item returned in the callback may differ from that returned from Write.

NOTE: all of the results must be returned by the server in a single callback. Thus if the items in the group require multiple physical transactions to one or more physical devices then the server must wait until all of them are complete before invoking the callback.

Client must free the returned ppError array.
 
 

IOPCAsyncIO2::Refresh2

HRESULT Refresh2(

[in] OPCDATASOURCE dwSource,

[in] DWORD dwTransactionID,

[out] DWORD *pdwCancelID

);
 
 

Description

Force a callback to IOPCDataCallback::OnDataChange for all active items in the group (whether they have changed or not). Inactive items are not included in the callback.
Parameters Description
dwSource Data source CACHE or DEVICE. If the DEVICE, then all active items in the CACHE are refreshed from the device BEFORE the callback.
dwTransactionID The Client generated transaction ID. This is included in the ‘completion’ information provided to the OnDataChange.
pdwCancelID Place to return a Server generated ID to be used in case the operation needs to be canceled.

 

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded.
E_FAIL The operation failed. (See notes below)
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.
CONNECT_E_NOCONNECTION The client has not registered a callback through IConnectionPoint::Advise.

 

Comments

If the HRESULT is any FAILED code then no Callback will occur.

Calling Refresh for an InActive Group will return E_FAIL. Calling refresh for an Active Group, where all the items in the group are InActive also returns E_FAIL.

The behavior of this function is identical to what happens when Advise is called initially except that the Callback will include a non-zero transaction ID.

Functionally it is also similar to what could be achieved by doing a READ of all of the active items in a group.

NOTE: all of the results must be returned in a single callback. Thus if the items in the group require multiple physical transactions to one or more physical devices then the server must wait until all of them are complete before invoking OnDataChange.

The expected behavior is that this Refresh will not affect the timing of normal OnDataChange callbacks which are based on the UpdateRate. For example, if the update rate is 1 hour and this method is called after 45 minutes then the server should still do its internal ‘checking’ at the end of the hour (15 minutes after the Refresh call). Calling this method may affect the contents of that next callback (15 minutes later) since only items where the value or status changed during that 15 minutes would be included. Items which had changed during the 45 minutes preceding the Refresh will be sent (along with all other values) as part of the Refresh Transaction. They would not be sent a second time at the end of the hour. The value sent in response to the Refresh becomes the ‘last value sent’ to the client when performing the normal subscription logic.
 
 

IOPCAsyncIO2::Cancel2

HRESULT Cancel2(

[in] DWORD dwCancelID

);
 
 

Description

Request that the server cancel an outstanding transaction.
Parameters Description
dwCancelID The Server generated Cancel ID which was associated with the operation when it was initiated.

 

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded.
E_FAIL The operation failed. Either the Cancel ID was invalid or it was ‘too late’ to cancel the transaction.

 

Comments

The exact behavior (for example whether an operation that has actually started will be aborted) will be server specific and will also depend on the timing of the cancel request. Also, depending on the timing, a Callback for the transaction may or may not occur. This method is intended for use during shutdown of a task.

In general, if this operation succeeds then a OnCancelComplete callback will occur. If this operation fails then a read, write or datachange callback may occur (or may already have occurred).
 
 

IOPCAsyncIO2::SetEnable

HRESULT SetEnable(

[in] BOOL bEnable

);
 
 

Description

Controls the operation of OnDataChange. Basically setting Enable to FALSE will disable any OnDataChange callbacks with a transaction ID of 0 (those which are not the result of a Refresh).
Parameters Description
bEnable TRUE enables OnDataChange callbacks, FALSE disables OnDataChange callbacks.

 

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded.
CONNECT_E_NOCONNECTION The client has not registered a callback through IConnectionPoint::Advise.
E_FAIL The operation failed.

 

Comments

The initial value of this variable when the group is created is TRUE and thus OnDataChange callbacks are enabled by default.

The purpose of this function is to allow a Connection to be established to an active group without necessarily enabling the OnDataChange notifications. An example might be a client doing an occasional Refresh from cache.

Even if a client does not intend to use the OnDataChange, it should still be prepared to deal with one or more OnDataChange callbacks which might occur before the client has time to disable them (i.e. at least free the memory associated with the 'out' parameters).

If the client really needs to prevent these initial unwanted callbacks then the following procedure can be used. Client creates and populates the group. Client sets the group Active state to FALSE. Client creates connection to group. Client uses this function to disable OnDataChange. sets the group Active state back to TRUE.

This does NOT affect operation of Refresh2(). I.e. calling Refresh2 will still result in an OnDataChange callback (with a non-zero transaction ID). Note that this allows Refresh to be used as essentially an Async read from Cache.
 
 
 
 

IOPCAsyncIO2::GetEnable

HRESULT GetEnable(

[out] BOOL *pbEnable

);
 
 

Description

Retrieves the last Callback Enable value set with SetEnable.
Parameters Description
pbEnable Where to save the returned result.

 

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded.
CONNECT_E_NOCONNECTION The client has not registered a callback through IConnectionPoint::Advise.
E_FAIL The operation failed.

 

Comments

See IOPCAsyncIO2::SetEnable() for additional information.
 
IConnectionPointContainer (on OPCGroup) This interface provides functionality similar to the IDataObject but is easier to implement and to understand and also provides some functionality which was missing from the IDataObject Interface. The client must use the new IOPCAsyncIO2 interface to communicate via connections established with this interface. IOPCAsyncIO2 is described elsewhere. The ‘old’ IOPCAsnyc will continue to communicate via IDataObject connections as in the past.

The general principles of ConnectionPoints are not discussed here as they are covered very clearly in the Microsoft Documentation. The reader is assumed to be familiar with this technology. OPC 2.0 Compliant Servers are REQUIRED to support this interface.

Likewise the details of the IEnumConnectionPoints, IConnectionPoint and IEnumConnections interfaces are well defined by Microsoft and are not discussed here.

Note that IConnectionPointContainer is implemented on the OPCGROUP rather than on the individual items. This is to allow the creation of a Callback connection between the client and the group using the IOPCDataCallback Interface for the most efficient possible transfer of data (many items per tranaction).

One callback object implemented by the client application can be used to service multiple groups. Therefore, information about the group and the particular transaction must be provided to the client application for it to be able to successfully interpret the items that are contained in the callback. Each callback will contain only items defined within the specified group.

Note: OPC Compliant servers are not required to support more than one connection between each Group and the Client. Given that groups are client specific entities it is expected that a single connection (to each group) will be sufficient for virtually all applications. For this reason (as per the COM Specification) the EnumConnections method for IConnectionPoint interface for the IOPCDataCallback is allowed to return E_NOTIMPL.
 
 

IConnectionPointContainer::EnumConnectionPoints HRESULT EnumConnectionPoints(

IEnumConnectionPoints **ppEnum

);
 
 

Description Create an enumerator for the Connection Points supported between the OPC Group and the Client.
Parameters Description
ppEnum Where to save the pointer to the connection point enumerator. See the Microsoft documentation for a discussion of IEnumConnectionPoints.
   

HRESULT Return Codes
Return Code Description
S_OK The function was successful.
For other codes see the OLE programmers reference  

Comments

OPCServers must return an enumerator that includes IOPCDataCallback. Additional vendor specific callbacks are also allowed.
 
IConnectionPointContainer:: FindConnectionPoint HRESULT FindConnectionPoint(

REFIID riid,

IConnectionPoint **ppCP

);
 
 

Description Find a particular connection point between the OPC Group and the Client.
Parameters Description
ppCP Where to store the Connection Point. See the Microsoft documentation for a discussion of IConnectionPoint.
riid The IID of the Connection Point. (e.g. IID_IOPCDataCallBack)

HRESULT Return Codes
Return Code Description
S_OK The function was successful.
For other codes see the OLE programmers reference  

Comments

OPCServers must support IID_IOPCDataCallback. Additional vendor specific callbacks are also allowed.
 
 
 
IEnumOPCItemAttributes IEnumOPCItemAttributes allows a client to find out the contents (items) of a group and the attributes of those items.

NOTE: most of the returned information was either supplied by or returned to the client at the time it called AddItem.

The optional EU information (see the OPCITEMATTRIBUTES discussion) may be very useful to some clients. This interface is also useful for debugging or for enumerating the contents of a public group.

This interface is returned only by IOPCItemMgt::CreateEnumerator. It is not available through query interface.

Since enumeration is a standard interface this is described only briefly.

See the OLE Programmer’s reference for Enumerators for a list and discussion of error codes.

IEnumOPCItemAttributes::Next

HRESULT Next(

[in] ULONG celt,

[out, size_is(,*pceltFetched)] OPCITEMATTRIBUTES ** ppItemArray,

[out] ULONG * pceltFetched

);
 
 

Description

Fetch the next ‘celt’ items from the group.
Parameters Description
celt number of items to be fetched.
ppItemArray Array of OPCITEMATTRIBUTES. Returned by the server.
pceltFetched Number of items actually returned.

Comments

The client must free the returned OPCITEMATTRIBUTES structure including the contained items; szItemID, szAccessPath, pBlob, vEUInfo.
 
IEnumOPCItemAttributes::Skip

HRESULT Skip(

[in] ULONG celt

);
 
 

Description

Skip over the next ‘celt’ attributes.
Parameters Description
celt Number of items to skip

Comments

Skip is probably not useful in the context of OPC.
 
IEnumOPCItemAttributes::Reset

HRESULT Reset(

void

);
 
 

Description

Reset the enumerator back to the first item.
Parametersvoid  

Comments
 
 
 
 

IEnumOPCItemAttributes::Clone

HRESULT Clone(

[out] IEnumOPCItemAttributes** ppEnumItemAttributes

);
 
 

Description

Create a 2nd copy of the enumerator. The new enumerator will initially be in the same ‘state’ as the current enumerator.
Parameters Description
ppEnumItemAttributes Place to return the new interface

Comments

The client must release the returned interface pointer when it is done with it.
 
 
 
IOPCAsyncIO (old) IOPCAsyncIO allows a client to perform asynchronous read and write operations to a server. The operations will be ‘queued’ and the function will return immediately so that the client can continue to run. Each operation is treated as a ‘transaction’ and is associated with a transaction ID. As the operations are completed, a callback will be made to the IAdvise Sink in the client (if one has been established). The information in the callback will indicate the transaction ID and the error results. By convention, 0 is an invalid transaction id.

Also the expected behavior is that for any one transaction to Async Read, Write and Refresh, ALL of the results of that transaction will be returned in a single call to OnDataChange.

A server must be able to ‘queue’ at least one transaction of each type (read, write, refresh) for each group. It is acceptable for a server to return an error (CONNECT_E_ADVISELIMIT) if more than one transaction of the same type is performed on the same group by the same client. Server vendors may of course support queueing of additional transactions if they wish.

All operations are expected to complete even if they complete with an error. The concept of ‘time-out’ is not explicitly addressed in this specification however it is expected that where appropriate the server will internally implement any needed time-out logic.

Client Implementation Note:

The Transaction ID is generated by the Server and returned to the client in the callback. Some clients may want to save the ID returned by the server in some list of ‘outstanding transactions’ in order to verify completion of a transaction. This could be complicated if the OnDataChange callback occurs before the client has saved the returned ID.

Note: Version 1.0 of this specification suggested an approach involving critical sections. However, depending on the mix of client and server threading models used, it has been found in practice that the OnDataChange callback can occur within the same thread as the Read or Write and in fact can occur before the Read or Write returns to the caller. Clearly, critical sections cannot resolve this case.

Although it has also been found in practice that many clients do not actually need to record the transaction ID (the Group’s ClientHandle is generally sufficient to identify the returned data), the following possible approach is suggested for those cases where this is needed.

Mainline Code

START CRITICAL SECTION

RECORD ALL NEEDED INFO ABOUT TRANSACTION EXCEPT TID.

CLEAR ‘TID COMPLETED’

SET A SPECIAL FLAG: ‘TID PENDING’

IOPCAsyncIO::Read or Write or Refresh

CHECK ‘TID COMPLETED’

IF SET AND EQUAL TO RETURNED TID THEN TRANSACTION IS COMPLETE

ELSE SAVE TRANSACTION ID IN LIST OF PENDING TRANSACTIONS

CLEAR ‘TID PENDING’

END CRITICAL SECTION

OnDataChange Code

START CRITICAL SECTION

READ DATA STREAM AND LOCATE TRANSACTION ID

LOCATE TRANSACTION ID IN LIST OF PENDING TRANSACTIONS

IF NOT FOUND, CHECK ‘TID PENDING’

IF ‘TID PENDING’ SET THEN RECORD THIS TID IN ‘TID COMPLETED’

END CRITICAL SECTION


 
 
 
 

IOPCAsyncIO::Read

HRESULT Read(

[in] DWORD dwConnection,

[in] OPCDATASOURCE dwSource,

[in] DWORD dwCount,

[in, size_is(dwCount)] OPCHANDLE * phServer,

[out] DWORD *pTransactionID,

[out, size_is(,dwCount)] HRESULT ** ppErrors

);
 
 

Description

Read one or more items in a group. The results are returned via the IAdvise Sink connection established through the IDataObject.

For CACHE reads the data is only valid if both the group and the item are active.

DEVICE reads are not affected by the ACTIVE state of the group or item.

Parameters Description
dwConnection The OLE Connection number returned from IDataObject::DAdvise. This is passed to help the server determine which advise sink to call when the request completes.
dwSource The data source; OPC_DS_CACHE or OPC_DS_DEVICE 
dwCount Number of items to be read.
phServer Array of server item handles of the items to be read
pTransactionID Place to return a Server generated transaction ID. This is included in the ‘completion’ information provided to the IAdvise.
ppErrors Array of errors for each item - returned by the server. Indicates only if the corresponding server handle was valid. Any other errors (communications time-out, access rights, etc.) will be returned in the callback. Note that at this time the only item level status information available in the callback is the QUALITY field.

 

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded. The read was successfully initiated
E_FAIL The operation failed.
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.
S_FALSE One or more of the passed handles was invalid. The ppError array indicates which handles in phServer were invalid. NOTE if any handle is invalid this error is returned and the entire ASYNC Read operation is rejected. No callback will occur. 
CONNECT_E_NOCONNECTION The client has not registered a callback of type OPCSTMFORMATDATA or OPCSTMFORMATDATATIME through IDataObject:DAdvise.

ppError Codes
Return Code Description
S_OK The corresponding Item handle was valid.
OPC_E_INVALIDHANDLE The corresponding Item handle was invalid

Comments

If the HRESULT is S_OK, then ppError can be ignored (all results in it are guaranteed to be S_OK).

If the HRESULT is any FAILED code then (as noted earlier) the server should return NULL pointers for all OUT parameters. Note that in this case no Callback will occur.

Note that there is a difference in the handling of OPE_E_INVALIDHANDLE between SYNC read and ASYNC read. In this case (ASYNC read) an INVALIDHANDLE on one item will cause the entire request to be rejected and will cause the main HRESULT to return as S_FALSE. In this case the ppErrors will contain one or more OPC_E_INVALIDHANDLE errors and no callback will occur.

The only item specific error checking done by this call is to validate the passed handles. Thus ppErrors always contains values of either S_OK or OPC_E_INVALIDHANDLE. If all of the passed handles are valid and the operation is performed then all item level error returns will be via OnDataChange. Note that at this time the only item level status information available in the Callback is the QUALITY field.

NOTE: all of the results must be returned by the server in a single callback.

If the items in the group require multiple physical transactions to one or more physical devices then the server must wait until all of them are complete before invoking OnDataChange.

The Client must free the returned ppError array.

The transaction ID generated by the server should be globally unique and non-zero.

The transaction ID is used to identify the results that are returned in the OnDataChange. The client may also use the transactionID when attempting to cancel an in progress asynchronous function
 
 

IOPCAsyncIO::Write

HRESULT Write(

[in] DWORD dwConnection,

[in] DWORD dwCount,

[in, size_is(dwCount)] OPCHANDLE * phServer,

[in, size_is(dwCount)] VARIANT * pItemValues,

[out] DWORD *pTransactionID,

[out, size_is(,dwCount)] HRESULT ** ppErrors

);
 
 

Description

Write one or more items in a group. The results are returned via the IAdviseSink connection established through the IDataObject.
Parameters Description
dwConnection The OLE Connection number returned from IDataObject::DAdvise. This is passed to help the server determine which advise sink to call when the request completes.
dwCount Number of items to be written
phServer List of server items handles for the items to be written
pItemValues List of values to be written. The value data types do not match the requested or canonical item datatype but must be ‘convertible’ to the canonical type.
pTransactionID Place to return a Server generated transaction ID. This is included in the ‘completion’ information provided to the IAdvise.
ppErrors Array of errors for each item - returned by the server. Indicates only if the corresponding server handle was valid. Any other errors (communications time-out, access rights, etc.) will be returned in the callback.

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded.
E_FAIL The operation failed.
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.
S_FALSE One or more of the passed handles was invalid. The ppError array indicates which handles in phServer were invalid. NOTE that if any handle is invalid this error is returned and the entire operation is rejected. No callback will occur. 
CONNECT_E_NOCONNECTION The client has not registered a callback of type OPCSTMFORMATWRITECOMPLETE through IDataObject:DAdvise.

ppError Codes
Return Code Description
S_OK The corresponding Item handle was valid.
OPC_E_INVALIDHANDLE The corresponding Item handle was invalid

Comments

If the HRESULT is S_OK, then ppError can be ignored (all results in it are guaranteed to be S_OK).

If the HRESULT is any FAILED code then (as noted earlier) the server should return NULL pointers for all OUT parameters. Note that in this case no Callback will occur.

Note that there is a difference in the handling of OPE_E_INVALIDHANDLE between SYNC write and ASYNC write. In this case (ASYNC write) an INVALIDHANDLE on one item will cause the entire request to be rejected and will cause the main HRESULT to return as S_FALSE. In this case the ppErrors will contain one or more OPC_E_INVALIDHANDLE errors and no callback will occur.

The only item specific error checking done by this call is to validate the passed handles. . Thus ppErrors always contains values of either S_OK or OPC_E_INVALIDHANDLE. If all of the passed handles are valid and the operation is performed then all item level error returns will be via OnDataChange. These error codes have the same values as those returned by IOPCSyncIO::Write.

NOTE: all of the results must be returned by the server in a single callback.

If the items in the group require multiple physical transactions to one or more physical devices then the server must wait until all of them are complete before invoking OnDataChange.

Client must free the returned ppError array.

See the notes under ‘Read’ regarding the transaction ID.
 
 

IOPCAsyncIO::Refresh

HRESULT Refresh(

[in] DWORD dwConnection,

[in] OPCDATASOURCE dwSource,

[out] DWORD *pTransactionID

);
 
 

Description

Force a callback for all active items in the group (whether they have changed or not). Inactive items are not included in the callback.
Parameters Description
dwConnection The OLE Connection number returned from IDataObject::DAdvise. This is passed to help the server determine which advise sync to call when the request completes.
dwSource Data source CACHE or DEVICE
pTransactionID Place to return a Server generated transaction ID. This is included in the ‘completion’ information provided to the IAdvise.

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded.
E_FAIL The operation failed. (See notes below)
E_OUTOFMEMORY Not enough memory
E_INVALIDARG An argument to the function was invalid.
CONNECT_E_NOCONNECTION The client has not registered a callback of type OPCSTMFORMATDATA or OPCSTMFORMATDATATIME through IDataObject:DAdvise.

Comments

If the HRESULT is any FAILED code then no Callback will occur.

Calling refresh for an InActive Group will return E_FAIL. Calling refresh for an Active Group, where all the items in the group are InActive also returns E_FAIL.

The behavior of this function is identical to what happens when DAdvise is called using ADVF_PRIMEFIRST except that the Callback will include a non-zero transaction ID.

Functionally it is also similar to what could be achieved by doing a READ from CACHE of all of the active items in a group.

NOTE: all of the results must be returned in a single callback.

If the items in the group require multiple physical transactions to one or more physical devices then the server must wait until all of them are complete before invoking OnDataChange.

The expected behavior is that this Refresh will not affect the timing of normal OnDataChange callbacks which are based on the UpdateRate. For example, if the update rate is 1 hour and this method is called after 45 minutes then the server should still do its internal ‘checking’ at the end of the hour (15 minutes after the Refresh call). Calling this method may affect the contents of that next callback (15 minutes later) since only items where the value or status changed during that 15 minutes would be included. Items which had changed during the 45 minutes preceding the Refresh will be sent (along with all other values) as part of the Refresh Transaction. They would not be sent a second time at the end of the hour. The value sent in response to the Refresh becomes the ‘last value sent’ to the client when performing the normal subscription logic.

See the notes under ‘Read’ regarding the transaction ID.
 
 

IOPCAsyncIO::Cancel

HRESULT Cancel(

[in] DWORD dwTransactionID

);
 
 

Description

Request that the server cancel an outstanding transaction.
Parameters Description
dwTransactionID The transaction ID which was associated with the operation to be canceled.

HRESULT Return Codes
Return Code Description
S_OK The operation succeeded.
E_FAIL The operation failed. Either the transaction ID was invalid or it was ‘too late’ to cancel the transaction.

 

Comments

The exact behavior (for example whether an operation that has actually started will be aborted) will be server specific and will also depend on the timing of the cancel request. Also, depending on the timing, a Callback for the transaction may or may not occur. This method is intended to be used during shutdown of a task.

In general, if this operation succeeds then no callback will occur. If this operation fails then a callback may occur (or may already have occured).

IDataObject (old) The OPC Specification requires the IDataObject to be implemented for the OPC servers.

IDataObject is implemented on the OPCGroup rather than on the individual items. This allows the creation of an Advise connection between the client and the group using the OPC Data Stream Formats for the efficient data transfer.

It is required that the following methods be supported.

DAdvise

DUnadvise

Because the IDataObject deals with a STREAM rather than individual items, the following methods do not need to be supported (they can be implemented as stubs which return E_NOTIMPL.

GetData

GetDataHere

GetCanonicalFormatEtc

The server vendor may chose to implement additional methods on the IDataObject. It is the intent of this design that data items be transferred to applications primarily via the Advise connection or via the Synchronous or Asynchronous Read methods.

The data returned to the Advise connection is returned via a IAdviseSink which receives data in a Global Memory Section also referred to here as the ‘stream’. These streams can be in several formats. They are used to provide exception data as well as completion information for Async Reads and Writes. The stream formats are

"OPCSTMFORMATDATA"

"OPCSTMFORMATDATATIME"

"OPCSTMFORMATWRITECOMPLETE"

Use the function

RegisterClipboardFormat()

to obtain the format value (cfFormat) to be used for data transfers between OPC client applications and OPC server applications.

The registered callback function (OnDataChange in the client’s IAdviseSink) may be specified by the client application so that it spans multiple groups. Information about the group (the Group’s ClientHandle) must be provided to the client application as part of the stream so that the client can successfully interpret the items that are contained in the data stream. Each data stream will only contain the items defined within the specified group.

Because of the nature of the asynchronous calls, OLE requires that no synchronous calls are made from a method that has been called asynchronously (as all of the IAdviseSink methods are) which would cause the asynchronous function to be blocked. It is very important that the methods that are called asynchronously (the IAdviseSink methods) have limited processing, and return quickly. Lengthy processing should be done outside of the context of the asynchronous method that has been invoked.

It is the client application’s responsibility to keep up with the data changes that the server (configured by the client app) sends. The client should assume that the server may send data at the update rate specified in the group, and that for each group that identical throughput may occur. Various Windows and OLE related internal errors can result if the server sends data faster than the client can receive it. The performance of the OPC servers and OPC clients is highly tied to the developers implementation of these critical interfaces.

The server should be implemented to optimize the acquisition of the data items for multiple clients wherever possible. This means that it is best for the server to read data from devices at the fastest rate possible: (a) to support the needs of multiple clients configured for the same item or (b), if a single client has configured the same item in different groups at different update rates.

Refer to the OLE programming manual for a tutorial and guide to implementing the required functionality.
 
 
 
 

IDataObject::DAdvise HRESULT DAdvise(

FORMATETC *pFmt,

DWORD adv,

LPADVISESINK pSnk,

DWORD * pConnection

);
 
 

Description Create a connection for a particular ‘stream’ format between the OPC Group and the Client.
Parameters Description
pFmt The format in which the client is interested. This will always be one of the three supported OPC formats as described below.
adv Data Advise Flags specifier. Not used by OPC.
pSnk Pointer to the Client’s IAdviseSink
pConnection OLE Connection key for use with IOPCAsyncIO and UnAdvise

HRESULT Return Codes
Return Code Description
S_OK The function was successful.
CONNECT_E_ADVISELIMIT The group cannot support additional connections of this type.
For other codes see the OLE programmers reference  

Comments

Since groups are specific to a client, it is sufficient for OPC Compliance that a group support only a single ‘connection point’ for each stream format. A second attempt by the same client to subscribe to the same stream format on the same group may return CONNECT_E_ADVISELIMIT.

The Advise Flags Parameter (adv) is not used by OPC. Servers should ignore this parameter and should always send a copy of all data items when a connection is made. Note that this is equivalent to the behavior associated with ADVF_PRIMEFIRST.

It is expected that a client will assign unique values to the group and item client handles if they intend to use any of the asynchronous functions of the OPC interfaces, including IOPCAsyncIO, and IDataObject/IAdviseSink interfaces, since this is the only key to the information that the server provides back to the client with the OnDataChange stream.

The ‘formats’ really represent different types of events rather than different formats for the same data.

The FORMATETC must be filled in as follows;

fe.cfFormat = OPCSTMFORMATDATA or

OPCSTMFORMATDATATIME or

OPCSTMFORMATWRITECOMPLETE.

(See RegisterClipboardFormat())

fe.dwAspect = DVASPECT_CONTENT;

fe.ptd = NULL;

fe.tymed = TYMED_HGLOBAL;

fe.lindex = -1;

The storage medium will always be TYMED_HGLOBAL (for computability with DCOM).
 
 

IDataObject::DUnadvise HRESULT DUnadvise(

DWORD Connection

);
 
 

Description Terminate a connection between the OPC Group and the Client.
Parameters Description
Connection The connection to be terminated

HRESULT Return Codes
Return Code Description
S_OK The function was successful.
For other codes see the OLE programmers reference  

Comments
 
 
 
 

Client Side Interfaces

IOPCDataCallback

In order to use connection points, the client must create an object that supports both the IUnknown and IOPCDataCallback Interface. The client would pass a pointer to the IUnknown interface (NOT the IOPCDataCallback) to the Advise method of the proper IConnectionPoint in the server (as obtained from IConnectionPointContainer:: FindConnectionPoint or EnumConnectionPoints). The Server will call QueryInterface on the client object to obtain the IOPCDataCallback interface. Note that the transaction must be performed in this way in order for the interface marshalling to work properly for Local or Remote servers.

All of the methods below must be implemented by the client.

This Interface will be called as a result of changes in the data of the group (OnDataChange) and also as a result of calls to the IOPCAsyncIO2 interface.

Note: although it is not recommended, the client could change the active status of the group or items while an Async call is outstanding. The server should be able to deal with this in a reasonable fashion (i.e. not crash) although the exact behavior is undefined.

Note: memory management follows the standard COM rules. That is, the server allocates 'in' parameters and frees them after the client returns. The client only frees 'out' parameters. In the case of these callbacks there are no 'out' parameters so all memory is owned by the server.

IOPCDataCallback::OnDataChange

HRESULT OnDataChange(

[in] DWORD dwTransid,

[in] OPCHANDLE hGroup,

[in] HRESULT hrMasterquality,

[in] HRESULT hrMastererror,

[in] DWORD dwCount,

[in, sizeis(dwCount)] OPCHANDLE * phClientItems,

[in, sizeis(dwCount)] VARIANT * pvValues,

[in, sizeis(dwCount)] WORD * pwQualities,

[in, sizeis(dwCount)] FILETIME * pftTimeStamps,

[in, sizeis(dwCount)] HRESULT *pErrors

);
 
 

Description This method is provided by the client to handle notifications from the OPC Group for exception based data changes and Refreshes.
Parameters Description
dwTransid 0 if the call is the result of an ordinary subscription. non-0 if the call is the result of a Refresh.
hGroup The Client handle of the group
hrMasterquality S_OK if OPC_QUALITY_MASK for all ‘qualities’ are OPC_QUALITY_GOOD, S_FALSE otherwise.
hrMastererror S_OK if all ‘errors are S_OK, S_FALSE otherwise.
dwCount The number of items in the client handle list
phClientItems The list of client handles for the items which have changed.
pvValues A List of VARIANTS containing the values (in RequestedDataType) for the items which have changed.
pwQualities A List of Quality values for the items
pftTimeStamps A list of TimeStamps for the items
pErrors A list of HRESULTS for the items. If the quality of a data item has changed to UNCERTAIN or BAD., this field allows the server to return additional server specific errors which provide more useful information to the user. See below.

 

HRESULT Return Codes
Return Code Description
S_OK The client must always return S_OK.

‘pErrors’ Return Codes
Return Code Description
S_OK The returned data for this item quality is GOOD.
E_FAIL The Operation failed for this item.
OPC_E_BADRIGHTS The item is or has become not readable.
OPC_E_UNKNOWNITEMID The item is no longer available in the server address space.
S_xxx, E_xxx S_xxx - Vendor specific information can be provided if this item quality is other than GOOD.

E_xxx - Vendor specific error if this item cannot be accessed.

These vendor specific codes can be passed to GetErrorString().

Comments

For any S_xxx pErrors code the client should assume the curresponding Value, Quality and Timestamp are well defined although the Quality may be UNCERTAIN or BAD. It is recommended (but not required) that server vendors provide additional information here regarding UNCERTAIN or BAD items.

For any FAILED ppError code the client should assume the curresponding Value, Quality and Timestamp are undefined. In fact the Server must set the corresponding Value VARIANT to VT_EMPTY so that it can be marshalled properly.

This section will discuss the reasons why the client may receive callbacks.

Callbacks can occur for the following reasons;

The 'errors' array can return additional information in the case where the server is having problems obtaining data for an Item. These vendor specific errors could contain helpful information about communications errors or device status. E_FAIL, while allowed, is generally not a very helpful error to return.

Note: although it is not recommended, the client could change the active status of the group or items while an Async call is outstanding. The server should be able to deal with this in a reasonable fashion (i.e. not crash) although the exact behavior is undefined.

During cleanup after the callback the Server must be sure to do a VariantClear() on each of the value Variants.
 
 

IOPCDataCallback::OnReadComplete HRESULT OnReadComplete(

[in] DWORD dwTransid,

[in] OPCHANDLE hGroup,

[in] HRESULT hrMasterquality,

[in] HRESULT hrMastererror,

[in] DWORD dwCount,

[in, sizeis(dwCount)] OPCHANDLE * phClientItems,

[in, sizeis(dwCount)] VARIANT * pvValues,

[in, sizeis(dwCount)] WORD * pwQualities,

[in, sizeis(dwCount)] FILETIME * pftTimeStamps,

[in, sizeis(dwCount)] HRESULT *pErrors

);
 
 

Description This method is provided by the client to handle notifications from the OPC Group on completion of Async Reads.
Parameters Description
dwTransid The TransactionID returned to the client when the Read was initiated.
hGroup The Client handle of the group
hrMasterquality S_OK if OPC_QUALITY_MASK for all ‘qualities’ are OPC_QUALITY_GOOD, S_FALSE otherwise.
hrMastererror S_OK if all ‘errors are S_OK, S_FALSE otherwise.
dwCount The number of items in the client handle, values, qualities, times and errors lists. This may be less than the number of items passed to Read. Items for whic errors were detected and returned from Read are not included in the callback.
phClientItems The list of client handles for the items which were read. This is NOT guarenteed to be in any particular order although it will match the values, qualities, times and errors array.
pvValues A List of VARIANTS containing the values (in RequestedDataType) for the items.
pwQualities A List of Quality values for the items
pftTimeStamps A list of TimeStamps for the items
pErrors A list of HRESULTS for the items. If the system is unable to return data for an item, this field allows the server to return additional server specific errors which provide more useful information to the user.

HRESULT Return Codes
Return Code Description
S_OK The client must always return S_OK

‘pErrors’ Return Codes
Return Code Description
S_OK The returned data for this item quality is GOOD.
E_FAIL The Read failed for this item
OPC_E_BADRIGHTS The item is not readable
OPC_E_INVALIDHANDLE The passed item handle was invalid. (Generally this should already have been tested by AsyncIO2::Read).
OPC_E_UNKNOWNITEMID The item is no longer available in the server address space. 
S_xxx, E_xxx S_xxx - Vendor specific information can be provided if this item quality is other than GOOD.

E_xxx - Vendor specific error if this item cannot be accessed.

These vendor specific codes can be passed to GetErrorString().

Comments

For any S_xxx pErrors code the client should assume the curresponding Value, Quality and Timestamp are well defined although the Quality may be UNCERTAIN or BAD. It is recommended (but not required) that server vendors provide additional information here regarding UNCERTAIN or BAD items.

For any FAILED ppError code the client should assume the curresponding Value, Quality and Timestamp are undefined. In fact the Server must set the corresponding Value VARIANT to VT_EMPTY so that it can be marshalled properly.

Items for which an error (E_xxx) was returned in the initial AsyncIO2 Read request will NOT be returned here. I.e. the returned list may be ‘sparse’. Also the order of the returned list is not specified (it may not match the order of the list passed to read).

This Callback occurs only after an AsyncIO2 Read.

The 'pErrors' array can return additional information in the case where the server is having problems obtaining data for an Item. These vendor specific errors could contain helpful information about communications errors or device status. E_FAIL, while allowed, is generally not a very helpful error to return.
 
 

IOPCDataCallback::OnWriteComplete HRESULT OnWriteComplete(

[in] DWORD dwTransid,

[in] OPCHANDLE hGroup,

[in] HRESULT hrMasterError,

[in] DWORD dwCount,

[in, sizeis(dwCount)] OPCHANDLE * phClientItems,

[in, sizeis(dwCount)] HRESULT * pError

);
 
 

Description This method is provided by the client to handle notifications from the OPC Group on completion of AsyncIO2 Writes.
Parameters Description
dwTransid The TransactionID returned to the client when the Write was initiated.
hGroup The Client handle of the group
hrMasterError S_OK if all ‘errors are S_OK, S_FALSE otherwise.
dwCount The number of items in the client handle and errors list. This may be less than the number of items passed to Write. . Items for which errors were detected and returned from Write are not included in the callback.
phClientItems The list of client handles for the items which were written. This is NOT guarenteed to be in any particular order although it must match the ‘errors’ array.
pErrors A List of HRESULTs for the items. Note that Servers are allowed to define vendor specific error codes here. These codes can be passed to GetErrorString().

HRESULT Return Codes
Return Code Description
S_OK The client must always return S_OK

‘pErrors’ Return Codes
Return Code Description
S_OK The data item was written.
OPC_E_BADRIGHTS The item is not writable.
OPC_E_INVALIDHANDLE The passed item handle was invalid. (Generally this should already have been tested by AsyncIO2::Write).
OPC_E_UNKNOWNITEMID The item is no longer available in the server address space. 
S_xxx, E_xxx S_xxx - the data item was written but there is a vendor specific warning (for example the value was clamped).

E_xxx - the data item was NOT written and there is a vendor specific error which provides more information (for example the device is offline). These codes can be passed to GetErrorString().

Comments

Items for which an error (E_xxx) was returned in the initial AsyncIO2 Write request will NOT be returned here. I.e. the returned list may be ‘sparse’. Also the order of the returned list is not specified (it may not match the order of the list passed to write).

This Callback occurs only after an AsyncIO2 Write.

The 'errors' array can return additional information in the case where the server is having problems accessing data for an Item. These vendor specific errors could contain helpful information about communications errors or device status. E_FAIL, while allowed, is generally not a very helpful error to return.
 
 
 
 

IOPCDataCallback::OnCancelComplete HRESULT OnCancelComplete(

[in] DWORD dwTransid,

[in] OPCHANDLE hGroup

);
 
 

Description This method is provided by the client to handle notifications from the OPC Group on completion of Async Cancel.
Parameters Description
dwTransid The TransactionID provided by the client when the Read, Write or Refresh was initiated.
hGroup The Client handle of the group

HRESULT Return Codes
Return Code Description
S_OK The client must always return S_OK

Comments

This Callback occurs only after an AsyncIO2 Cancel. Note that if the Cancel Request returned S_OK then the client can expect to receive this callback. If the Cancel request Failed then the client should NOT receive this callback
 

IOPCShutdown

In order to use this connection point, the client must create an object that supports both the IUnknown and IOPCShutdown Interface. The client would pass a pointer to the IUnknown interface (NOT the IOPCShutdown) to the Advise method of the proper IConnectionPoint in the server (as obtained from IConnectionPointContainer:: FindConnectionPoint or EnumConnectionPoints). The Server will call QueryInterface on the client object to obtain the IOPCShutdown interface. Note that the transaction must be performed in this way in order for the interface marshalling to work properly for Local or Remote servers.

The ShutdownRequest method on this Interface will be called when the server needs to shutdown. The client should release all connections and interfaces for this server.

A client which is connected to multiple OPCServers (for example Data access and/or other servers such as Alarms and events servers from one or more vendors) should maintain separate shutdown callbacks for each object since any server can shut down independently of the others.

IOPCShutdown::ShutdownRequest HRESULT ShutdownRequest (

[in] LPWSTR szReason

);
 
 

Description This method is provided by the client so that the server can request that the client disconnect from the server. The client should UnAdvise all connections, Remove all groups and release all interfaces.
Parameters Description
szReason An optional text string provided by the server indicating the reason for the shutdown. The server may pass a pointer to a NUL string if no reason is provided.

 

HRESULT Return Codes
Return Code Description
S_OK The client must always return S_OK.

Comments

The shutdown connection point is on a ‘per COM object’ basis. That is, it relates to the object created by CoCreate… If a client connects to multiple COM objects then it should monitor each one separately for shutdown requests.
 
 
 
 

IAdviseSink (old)

The client need only provide a full implementation of OnDataChange. The other methods of IAdviseSink can be implemented as stubs since they will never be called. Callbacks can occur for several reasons; simple Subscription, Async Read, Async Write, Refresh. A client can be written such that it performs several of these operations in parallel. In this case the client can determine the ‘cause’ of a particular callback by examining first the data format as provided in the FORMATETC and second the Transaction ID as contained in the stream.

Because of the nature of the asynchronous calls, OLE requires that no synchronous calls are made from a method that has been called asynchronously (as all of the IAdviseSink methods are) which would cause the asynchronous function to be blocked. It is very important that the methods that are called asynchronously (the IAdviseSink methods) have limited processing, and return quickly. Lengthy processing should be done outside of the context of the asynchronous method that has been invoked.

It is client application responsibility to keep up with the data changes that the server has been configured by the client application to send. The client should assume that the server may send data at the update rate specified in the group, and that for each group that identical throughput may occur. Various Windows and OLE related internal errors can result if the server sends data faster than the client can receive it. The performance of the OPC servers and OPC clients is highly tied to the developers implementation of these critical interfaces.
 
 

IAdviseSink::OnDataChange

void OnDataChange (

[in] FORMATETC * pFE,

[in] STGMEDIUM * pSTM

);
 
 

Description This method is provided by the client to handle notifications from the OPC Group for exception based data changes, Async reads and Refreshes and Async Write Complete.
Parameters Description
pFE the format of the data being receive by the sink
pSTM the storage medium containing the data.

Comments

This section will discuss the reasons why the client may receive callbacks, the contents of FORMATETC and the contents of the STGMEDIUM.

Note that the caller (the server) owns and will free the storage since the parameters are all 'in's.

The client should NOT free the STGMEDIUM. Also note that the storage is valid only for the duration of the OnDataChange call.

Callbacks can occur for several reasons;

The FORMATETC will be filled in as follows;

fe.cfFormat = OPCSTMFORMATDATA or

OPCSTMFORMATDATATIME or

OPCSTMFORMATWRITECOMPLETE.

fe.ptd = NULL;

fe.dwAspect = DVASPECT_CONTENT;

fe.lindex = -1;

fe.tymed = TYMED_HGLOBAL;

The storage medium will always be TYMED_HGLOBAL (for computability with DCOM). The global memory handle can be found in pSTM.hGlobal. GlobalLock() can be used to convert this to a pointer.

The data stored in the global memory by the server will have one of several structures depending on the Format (which depends on the event that generated the data). Although the data resides in this structure in global memory, we refer to it as a ‘data stream’.

These three formats are summarized below and are described in detail later in the document.

OPCSTMFORMATDATATIME Data with TimeStamp

The data consists of a group header followed by one or more item headers followed by the data.

OPCGROUPHEADER

OPCITEMHEADER1[hdr.dwItemCount]

VARIANTS[hdr.dwItemCount]

OPCSTMFORMATDATA Data without TimeStamp

The data consists of a group header followed by one or more item headers followed by the data.

OPCGROUPHEADER

OPCITEMHEADER2[hdr.dwItemCount]

VARIANTS[hdr.dwItemCount]

OPCSTMFORMATWRITECOMPLETE Async Write Complete

The data consists of a group header followed by one or more item headers followed by the data.

OPCGROUPHEADERWRITE

OPCITEMHEADERWRITE[hdr.dwItemCount]
 
 

IAdviseSink - Data Stream Formats (old) This section describes the data structures associated with the three stream formats used in the IDataObject / IAdviseSink connection. It also discusses the critical issue of the Packing of these streams and structures. These formats are also discussed in the Client Side Custom Interface section.

The following table shows the clipboard format names.

"OPCSTMFORMATDATA" Used for On Data Change, Refresh and Async Read
"OPCSTMFORMATDATATIME" Used for On Data Change, Refresh and Async Read
"OPCSTMFORMATWRITECOMPLETE" Used for Async Write
Clients and servers must ‘Register’ these stream formats by calling the windows function RegisterClipboardFormat();
 
OPCGROUPHEADER

typedef struct {

DWORD dwSize;

DWORD dwItemCount;

OPCHANDLE hClientGroup;

DWORD dwTransactionID;

HRESULT hrStatus;

} OPCGROUPHEADER;

This structure can appear at the head of the OPCSTMFORMATDATA or OPCSTMFORMATDATATIME data stream. It is followed by an array of OPCITEMHEADER1s or OPCITEMHEADER2s.

Member Description
dwSize The Total size of the data stream (the header, all item headers and all data)
dwItemCount The number of Itemheaders which follow. This will vary depending on the number of values being reported.
hClientGroup The client provided handle for the group for which data is being reported. This allows a single OnDataChange handler to identify which of many possible groups are reporting data.
dwTransactionID For normal subscriptions this is 0

For Async operations Refresh or Read this is the transaction ID returned by the method.

hrStatus The status of the asynchronous request (including OnDataChange). This enables error codes (e.g. E_OUTOFMEMORY) to be returned in the case of an asynchronous request failing in the server. A status of S_FALSE should be returned when the read operation was successful, but one or more items has a quality status of BAD or UNCERTAIN.

Comment

If the hrStatus is any FAILED code then the server must return dwItemCount as 0.

There are no ITEM level HRESULT error codes returned at this time. The only item level status information available to the callback function is the Quality Field.

OPCITEMHEADER1

typedef struct {

OPCHANDLE hClient;

DWORD dwValueOffset;

WORD wQuality;

WORD wReserved;

FILETIME ftTimeStampItem;

} OPCITEMHEADER1;

An array of these structures appears in the stream following the GROUPHEADER for OPCSTMFORMATDATATIME. The serialized data (in the form of Variants) appears after this array.

Member Description
hClient The client provided handle associated with this item
dwValueOffset The offset in the data stream (the global memory section) of the serialized variant which contains the data.
wQuality The Quality bits for the data.
ftTimeStampItem The TimeStamp for the data.

 

OPCITEMHEADER2

typedef struct {

OPCHANDLE hClient;

DWORD dwValueOffset;

WORD wQuality;

WORD wReserved;

} OPCITEMHEADER2;

An array of these structures appears in the stream following the GROUPHEADER for OPCSTMFORMATDATA. The serialized data (in the form of Variants) appears after this array.
 
 

Member Description
hClient The client provided handle associated with this item
dwValueOffset The offset in the data stream (the global memory section) of the serialized variant which contains the data.
wQuality The Quality bits for the data.

 
 
 

OPCGROUPHEADERWRITE

typedef struct {

DWORD dwItemCount;

OPCHANDLE hClientGroup;

DWORD dwTransactionID;

HRESULT hrStatus;

} OPCGROUPHEADERWRITE;

This structure can appear at the head of the data stream. It is followed by an array of OPCITEMHEADERWRITEs.

Member Description
dwItemCount The number of Itemheaders which follow. This will vary depending on the number of values being reported.
hClientGroup The client provided handle for the group for which data is being reported. This allows a single OnDataChange handler to identify which of many possible groups are reporting data.
dwTransactionID This is the transaction ID returned by the IOPCAsyncIO::Write method.
hrStatus The status of the asynchronous write request. This enables error codes (e.g. E_OUTOFMEMORY) to be returned in the case of an asynchronous request failing in the server.

Comment

If the hrStatus is any FAILED code then the server must return dwItemCount as 0.
 
OPCITEMHEADERWRITE

typedef struct {

OPCHANDLE hClient;

HRESULT dwError;

} OPCITEMHEADERWRITE;

An array of these structures appears in the stream following the GROUPHEADERWRITE.
 
 

Member Description
hClient The client provided handle associated with this item
dwError The HRESULTs for each of the items that was written.

 

Comment

The item level HRESULTs for Write are the same as those returned for Sync Write.
 
 
 
Marshaling the Data (Variants) into the Stream It is important that all servers which use the IDataObject interface marshal the item data into the stream in exactly the same way since the stream itself is exposed to the client. As mentioned above, the various GROUPHEADERs are written first without padding into the stream followed by as many ITEMHEADERs as needed. The ITEMHEADERs must be followed by the data itself. Again the data must be written in exactly the same way without padding by all servers. This data is always in the form of one of the VARIANT types listed earlier. For variant types contained within the variant union itself these are written via:

memcpy(dest, source, sizeof(tagVARIANT));

For a BSTR the union is followed without padding by an image of the BSTR. The BSTR image will include the terminating NUL (WIDE char). Note that BSTRs contain WIDE chars which are 2 bytes each. The BSTR starts with a DWORD byte count followed by 'count' bytes of data followed by 2 bytes of 0. Thus the total space required for the BSTR is the number of bytes specified in count + 6 (4 for the DWORD count and 2 for the trailing NUL).

For VT_ARRAY the data is the VARIANT union followed by the SAFEARRAY structure (with one SAFEARRAYBOUND, pvData = NULL) followed by the data items themselves (the contents of the SAFEARRAY’s HGLOBAL). Again, everything including the data items is completely unpadded.

Currently OPC supports only a one dimensional SAFEARRAY.

Clearly any pointers in the SAFEARRAY and VARIANT unions need to be recreated by the receiver when the data is unmarshalled and stored locally.