OPC Studio User's Guide and Reference
Multiple-operation Methods
Client and Subscriber Development > Development Models > Imperative Programming Model > Operational Methods Overview > Multiple-operation Methods
In This Topic

As described in Operational Methods Overview, OPC operations are much more effective if performed together - multiple operations at once. Multiple-operation methods allow you to perform such simultaneous operations, and you should use whenever possible, over  single-operation methods. 

By convention, multiple-operation methods always contain the word "Multiple" in their name.

Arguments and return values as arrays

In its basic form, the multiple-operation method takes an array (of multiple arrays) of arguments as an input, and produces (as an output) a return value which is an array of results.

The size of the output array is the same as the size of the input array(s), and an element at each index in the result array contains a result that corresponds to the arguments at the same index in the input array(s).

Arguments and return values as lists

This functionality is not available under (or the text does not apply to) .NET development platform (.NET Framework, .NET 6+).

Some COM tools and languages (e.g. PowerScript in PowerBuilder) lack the ability to properly provide or process arrays of COM objects. In such case, using multiple-operation methods would be impossible using their standard array-based form.

In order to make the use of multiple-operations methods in such tools and language possible, QuickOPC introduces an alternative form of these methods, where arrays are replaced by lists (objects). You can then use methods and properties on the list object in order to access its element, instead of indexing an array. By convention, the list-based methods always contain the word "List" in their name. For correspondence between the array-based and list-based methods, see Operational Methods Overview. Of course, if you prefer, you can use the list-based methods from other tools and languages as well.

The order of elements in the return value corresponds with the order of elements in the method arguments. List elements can also be accessed by index, and therefore an element of the returned list corresponds at certain index corresponds to the element of the arguments list at the same index.

There are also single-operation methods that accepts list instead of array, in case that the argument or return value is an array already, even for a single-operation method. In such case, most of the discussion in this topic can also be applied, except for the correspondence of the order and/or indexes between the arguments and the return value.

Creating and accessing the list

A list object that is passed to a list-based multiple-operation method can be any object that implement the IList Interface. A list object returned from the method also derived from the same interface; in fact, it may provide even broader guarantees as to what interfaces are supported.

You will typically use following members of the list object in order to meaningfully create the list and acess its elements:

In principle, a list can also be enumerated using standard COM collection enumeration approach, although various COM tools and languages also fail implementing it properly. You can, however, use a standard "for" loop, available in practically all languages, with an integer counter, and access the list's element by index. List indexes are zero-based, i.e. the range of valid indexes (for already existing elements of the list) is between 0 and (Count - 1).

The ElasticVector object

QuickOPC provides an ElasticVector Class, which you can conveniently use to provide list-based arguments to multiple-operation methods. The ElasticVector implements the IList Interface, and has a handful of additional useful members as well.

Above that, it is truly "elastic", in the sense that it can grow automatically with certain operations. It does not shrink by itself. The growing constitutes of lowering the lower bound or increasing the upper bound as necessary for the operation. The absolute element indexes remain unchanged, even when the lower bound changes.

Setting an element at specified index is, for example, always a valid operation with the ElasticVector, regardless of whether the index falls between the current lower and upper bounds of the vector. If it falls outside the range, the bounds are automatically adjusted as needed. This allows the object be used without having to explicitly set the vector dimensions first.

Following examples shows usage of the ElasticVector Class for passing a list of arguments into a multiple-operation method. The example also shows how the return value from the multiple-operation method, which is a list again, can be processed and its elements extracted.

// This example shows how to read the Value attributes of 3 different nodes at once. Using the same method, it is also possible 
// to read multiple attributes of the same node.

mle_outputtext.Text = ""

// Instantiate the client object
OLEObject client
client = CREATE OLEObject
client.ConnectToNewObject("OpcLabs.EasyOpc.UA.EasyUAClient")

// Prepare arguments. By default, the Value attributes of the nodes will be read.

OLEObject readArguments1
readArguments1 = CREATE OLEObject
readArguments1.ConnectToNewObject("OpcLabs.EasyOpc.UA.OperationModel.UAReadArguments")
readArguments1.EndpointDescriptor.UrlString = "http://opcua.demo-this.com:51211/UA/SampleServer"
readArguments1.NodeDescriptor.NodeId.ExpandedText = "nsu=http://test.org/UA/Data/ ;i=10845"

OLEObject readArguments2
readArguments2 = CREATE OLEObject
readArguments2.ConnectToNewObject("OpcLabs.EasyOpc.UA.OperationModel.UAReadArguments")
readArguments2.EndpointDescriptor.UrlString = "http://opcua.demo-this.com:51211/UA/SampleServer"
readArguments2.NodeDescriptor.NodeId.ExpandedText = "nsu=http://test.org/UA/Data/ ;i=10853"

OLEObject readArguments3
readArguments3 = CREATE OLEObject
readArguments3.ConnectToNewObject("OpcLabs.EasyOpc.UA.OperationModel.UAReadArguments")
readArguments3.EndpointDescriptor.UrlString = "http://opcua.demo-this.com:51211/UA/SampleServer"
readArguments3.NodeDescriptor.NodeId.ExpandedText = "nsu=http://test.org/UA/Data/ ;i=10855"

OLEObject readArgumentsList
readArgumentsList = CREATE OLEObject
readArgumentsList.ConnectToNewObject("OpcLabs.BaseLib.Collections.ElasticVector")
readArgumentsList.Add(readArguments1)
readArgumentsList.Add(readArguments2)
readArgumentsList.Add(readArguments3)

// Obtain values. 
OLEObject valueResultList
valueResultList = client.ReadValueList(readArgumentsList)

// Display results
Int i
FOR i = 0 TO valueResultList.Count - 1
    OLEObject valueResult
    valueResult = valueResultList.Item[i]
    IF valueResult.Succeeded THEN
        mle_outputtext.Text = mle_outputtext.Text + "Value: " + String(valueResult.Value) + "~r~n"
    ELSE
        mle_outputtext.Text = mle_outputtext.Text + "*** Failure: " + valueResult.ErrorMessageBrief + "~r~n"
    END IF    
NEXT

// Example output:
//
//Value: 8
//Value: -8.06803E+21
//Value: Strawberry Pig Banana Snake Mango Purple Grape Monkey Purple? Blueberry Lemon^            

 

 

See Also