This article describes best practices that aid in developing applications and libraries that use the QuickOPC library.
All information described in this article is merely a set of guidelines to aid development. These guidelines do not constitute an absolute truth. They are patterns that we found helpful; not rules that should be followed blindly. There are situations where certain guidelines do not apply. It is up to each individual developer to decide if a certain guideline makes sense in each specific situation.
The guidelines in this article are listed in no particular order.
Do not write any code for OPC failure recovery
Failure Recovery, problems in communications with target OPC server are automatically recognized, and the component performs the recovery automatically. Attempts to duplicate this functionality in your code will interfere with this mechanism and won’t work.
It is fine (and indeed, suggested) to catch the errors and handle them appropriately. But the error handling should not include attempts to remedy the situation.
As described in the documentation, you can call a static method EasyUAClient.CloseAll to close all unused sessions that are open to OPC-UA servers.
With single-operand synchronous methods, only catch OpcException or UAException
Methods that operate on a single element, e.g. an OPC Data Access item, throw an OpcException exception for all OPC-related errors. In OPC-UA, UAException is used for the same purpose. The only other exceptions that can be thrown are program execution-related errors (such as OutOfMemoryException, or StackOverflowException), and argument validation exceptions, such as ArgumentException.
When you catch OpcException or UAException, you are completely safe that all OPC-related situations that you cannot control will be covered. This includes errors related to arguments that can only be discovered in run-time and depend on the state of environment. For example, an invalid OPC-DA item ID does not throw ArgumentException, but still an OpcException. So, even if something changes in the target OPC server, you can handle it by catching OpcException (or UAException). For more information, see Error Handling.
By catching other exceptions than OpcException (or UAException) in this case, you could hide programming or catastrophic errors that should not be handled in this way.
The SubscribeXXXX and UnsubscribeXXXX methods, such as EasyDAClient.SubscribeItem, EasyDAClient.UnsubscribeItem, or EasyAEClient.SubscribeEvents, work asynchronously. This rule therefore does not apply to them; instead, see Do not catch any exceptions with asynchronous or multiple-operand methods.
Do not catch any exceptions with asynchronous or multiple-operand methods
Asynchronous methods (such as SubscribeXXXX), or methods that work on multiple elements at once (such as EasyDAClient.ReadMultipleItems) only throw program execution-related errors (such as OutOfMemoryException, or StackOverflowException), and argument validation exceptions, such as ArgumentException. The OPC-related exceptions are reported in a different way: By event notifications or callbacks (for asynchronous methods), or in the OperationResult.Exception properties of the methods return value array (for multiple-operand methods).
By catching exceptions in this case, you could hide programming or catastrophic errors that should not be handled in this way. For more information, see Errors and Multiple-Element Operations.
Always test the Exception property before accessing the actual result (Value, Vtq, or AttributeData property)
Whenever QuickOPC return an OperationResult object, or an object derived from it, such as ValueResult, DAVtqResult or UAAttributeDataResult, the actual result properties are only valid when the result represents a success, i.e. its Extension property is a null reference. You should therefore always test the Extension property for null-ness first. Only the properties available in the base OperationResult class are always accessible.
The same applies to the Exception property in event notifications, for all event arguments that are derived from the OperationEventArgs class. For example, the Exception property must be checked in event handlers for the EasyDAClient.ItemChanged event (in EasyDAItemChangedEventArgs), and in event handlers for the EasyUAClient.MonitoredItemChanged event (in EasyUAMonitoredItemChangedEventArgs).
If you attempt to get other properties while Exception is not null, the outcome is undefined; most likely an exception will be thrown, but that would be a serious exception indicating a programming error. For more information, see Error Handling.
Always test the HasValue property before accessing DAVtq.Value or UAAttributeData.Value
The controlling member in the DAVtq class (OPC Data Access Value/Timestamp/Quality) is the Quality property. When the quality is not sufficient, the Value property is not available (and QuickOPC assures that is a null reference in this case). Getting the Value property when it is not available makes little sense.
The same applies to the UAAttributeData class (in OPC Unified Architecture) with regard to its StatusCode and Value properties.
It is commonly said that the value is available whenever the quality (or status code) is not Bad, but precisely speaking, this is not true, as there are some border cases (per OPC specifications) in which this rule does not apply. Your best choice is to test the HasValue property, which has the precise, OPC-compliant functionality built in. Only if HasValue is true, the Value property contains valid data.
For more information, see Data Objects
The data objects hold the actual data made available by the OPC technology.
Quality and Value, Timestamp and Quality (VTQ).
Use multiple-operand methods instead of looping
Due to way OPC internally works, it is significantly more efficient to perform more operations at once. Whenever your application logic allows it, use methods with the word Multiple in their name, i.e. methods that work on multiple elements (such as OPC items) at once, and try to group together a bigger number of operands. This approach gives much better performance.
Do not block inside OPC event handlers or callback methods
You should not block for excessive or significant time inside the OPC event handlers (such as an event handler for EasyDAClient.ItemChanged) or callback methods. Doing so delays further event processing, and in serious cases, can lead to excessive memory usage and eventually loss of events.
QuickOPC internally queues the events, and therefore any blocking in your code cannot have negative influence on the target OPC server. In the long-term, however, the average processing time for your event handlers or callback methods must be equal to or less than the average rate the OPC events are incoming.
Use generic or type-safe access wherever possible
OPC “Classic” represents data using OLE Automation VARIANT-s, which are transformed to .NET (CLR) objects. As such, a value of any type can appear in place of OPC data. Your code typically knows that certain piece of data (such as OPC item) is of specific type (such as 32-bit signed integer), and expects that the value indeed is of this type.
Instead of trying to put the type conversions and safety checks in your code, you should use the features already available in QuickOPC.NET.
If your language supports generics and you feel comfortable working with them, it is the best choice to use generic types and generic access (see e.g. Generic Types and Generic Access). The second best choice is type-safe access, which provides similar functionality, but without use of generics (see e.g. Type-safe Access).