Event Pull Mechanism

From OPC Labs Knowledge Base

Event pull mechanism is an alternative to the traditional delivery mechanism of notifications through event handlers. Setting up event handlers, and properly handling events, is difficult or impossible in some (especially COM-based) tools (e.g. Python) or in some environments (e.g. applications running under Web server). In COM, unless taken care of already by the tool, the developer must also assure that (in an STA model) Windows messages are being pumped by the application.

Note: This feature should only be used when the traditional event delivery approach is not feasible. It is unlikely that you will need to use the event pull mechanism in C# or VB.NET (except maybe for server-side of certain Web apps and Web services).

Event pull mechanism replaces the event handlers by methods that allow the developer to obtain a next available notification from an internally provided queue. Events can then be handled by a loop that calls the “pull” method and processes the notifications. In order to maintain reasonable performance, a so-called long poll approach is used: The method only returns after a notification becomes available, or a specified timeout elapses. Note that the event pull concept is strictly confined to how the QuickOPC exchanges notifications with the enclosing application, and is not related to how QuickOPC communicates with OPC servers.

For each event XXXX, by convention, two methods for event pull mechanism are provided: A PullXXXX method, which allows to retrieve a single notification, and a PullMultipleXXXX method, which allows to retrieve multiple notifications, if they are available. Although currently is no significant performance difference between the two methods, it is recommended that your code uses the PullMultipleXXXX methods, because it may bring performance benefits in the future.

The following table shows the traditional events, and their corresponding methods for event pull:

Type Event Pull method PullMultiple method
EasyDAClient ItemChanged PullItemChanged PullMultipleItemChanges
EasyAEClient Notification PullNotification PullMultipleNotifications
EasyUAClient DataChangeNotification PullDataChangeNotification PullMultipleDataChangeNotifications
EventNotification PullEventNotification PullMultipleEventNotifications
ServerConditionChanged PullServerConditionChanged PullMultipleServerConditionChanges

Each PullXXXX or PullMultipleXXXX method takes an argument that specifies a timeout (in milliseconds). When a notification is available, the methods returns it immediately, and return a non-null object containing the event arguments (or an array of such event arguments, for PullMultipleXXXX). If no notifications are available during the specified period (timeout), the methods returns after the period elapses, but the return value is a null reference.

After your code receives a non-null event arguments object, it can process it as it would do in a traditional event handler.

You will probably be calling the PullXXXX or PullMultipleXXXX method in a loop with some termination condition. Specify a timeout that will allow the loop to react to the termination condition quickly enough. You should not, however, use very low (or zero) timeouts in such a loop, because that would lead to unnecessary high CPU consumption.

In order to use the event pull mechanism, your code needs to set up a queue with a fixed capacity upfront. This is done by setting a corresponding property in the EasyXXClient object. By convention, for an event named XXXX, the property name is PullXXXXQueueCapacity. For example, set the EasyUAClient.PullDataChangeNotificationQueueCapacity property to allocate a queue for OPC UA data change notifications.

If you do not set the queue capacity to a positive (non-zero) value, and attempt to use the event pull mechanism, the PullXXXX or PullMultipleXXXX method will throw an InvalidOperationException.

Besides the usual ArgumentException (when your code passes in an improper argument), the InvalidOperationException described above (which also indicates a bug in the code), and system exceptions such OutOfMemoryException, the PullXXXX and PullMultipleXXXX methods can only throw one type of exception, and that is ProcedureCallException. The current implementation does not actually throw this exception type, but future implementations may do so, and we recommend that your code is written in such a way that is ready for that. The ProcedureCallException indicates a failure in the event delivery mechanism (and a likely loss of one or more notifications).

Make sure you always specify a queue capacity that will provide a “buffer” of sufficient length for a difference between the rate of incoming notifications, and the rate your code can consume them. Besides an indication through the event log output (currently in OPC UA only), there is no way to detect queue overflows.