Advanced programming


First of all: The concept of virtual methods has nothing to do with the concept of virtual methods in object-oriented languages!

A virtual method in iPlus is basically a configurable, serializable, and persistable parameter list that is passed to a real .NET method. As with the concept of overloaded methods in .NET (that method names can be named equal, but the number of parameters can vary), there may be different parameter lists.

These parameter lists must have a unique name and can be called using ACUrlCommand with it's "virtual name" - hence the term "virtual method". However, the iPlus framework knows to which .NET method this parameter list must be passed.  This .NET method must then be implemented internally so that it can work with these different parameter sets.

Virtual methods are mostly used in the derivatives of the PAProcessFunction class.  PAProcessFunction's are classes that are implemented according to the ISA S88 standard and have a state. The state machine starts when the PAPocessFunction is started using the asynchronous Start method (Virtual methods can also be used for synchronous methods. In addition, asynchronous methods can also be used synchronously). The Start method can be started either directly from the GUI or from a workflow. The special thing about workflows is that they have these parameter lists (virtual methods, class ACMethod) in persistent form and can delegate them to the PAProcessFunction "Start"-method when the program flow comes in the appropriate place. The parameter values of these virtual methods can be edited by end users themselves and persisted in the workflow.

The Start-method has the following signature:

 [ACMethodAsync("Process", "en{'Start'}de{'Start'}", (short)MISort.Start, false)]
public virtual ACMethodEventArgs Start(ACMethod acMethod)

The ACMethod class consists essentially of three properties:

NameDescription
ACValueList ParameterValueList

This list is used to store the parameters that the Start method can handle (or another asynchronous method that has an ACMethod parameter).
The ACValueList type is a list of ACValue. ACValue has two important properties:

  1. string ACIdentifier: Unique Name (ID) of the parameter
  2. object Value: The actual value.
ACValueList ResultValueListThis list contains the variable return values when the Start method (or an asynchronous method) has done its job.
string ACIdentifier

The "virtual method name". The virtual method name must be unique to the Start method (or an asnychronous method). That is, if an asynchronous method should handle different parameter sets, then the ACIdentifiers must also be different.

How does the Start method or a . NET method with the ACMethod parameter know, which virtual methods exists and where are they declared?

For a virtual method to be known, it must be registered in the static constructor by calling the static method ACMethod.RegisterVirtualMethod:

ACMethod.RegisterVirtualMethod(typeof(PAFMixing), "Start", CreateVirtualMethod("Mixing", "en{'Mixing On/Off'}de{'Mischen Ein/Aus'}", typeof(PWMixing)));

 

The method RegisterVirtualMethod has three or four parameters:

NameDescription
Type typeOfACComponent

Type of class to which the virtual method belongs (where it was declared and is known).

string assemblyMethodName

Name of the .NET method that can handle the virtual method. Thus, the iPlus framework knows which .NET-method must be called in order to pass the virtual method (in the sense of the parameter list).

ACMethodWrapper acMethodWrapper

This class is basically a metadescription of the virtual method. On the one hand, it stores an instance of the ACMethod itself. This instance is used as a copy template when a new set of parameters is needed to make a new method call to the .NET method. On the other hand, it contains a translation table that is used when the parameter list is displayed on the surface, to describe the parameter in more detail in the language of the end user.

Type pwClassType of workflow class that also knows this virtual method with its parameter list.

In the previous code example, the instance of the ACMethodWrapper is passed by the function call CreateVirtualMethod(...), because this is declaratively difficult due to the complexity of the class and would make the code illegible on the other.

Now let's take a closer look at the CreateVirtualMethod method to better understand how the ACMethodWrapper instance is created:

 

If you've been careful, you'll be wondering what about the return values? Do they not have to be on the ResultValueList and also announced?

Correct, the ResultValueList was not declared because the Mixing method simply does not have return values.

 

What do you think - in which cases could a mixer have a return value if you simply imagine a food processor? What would happen if you stird or kneaded a dough in a food processor for a very long time?

That's right, it would warm up due to the mechanical friction - and we've already found a return value: "temperature".

 

Is there actually a technical difference (with regard to the control of a agitator) whether I switch on a food processor for a long or very short time?

No, in both cases the motor must be supplied with voltage. Therefore, the technical implementation is the same.

 

If you have been paying close attention, you have already found the solution and understood the actual principle and the advantage of the virtual method:

We simply define another virtual method, e.g. "MixingTemperature" with which the start method can also work, but in this case you still have to determine the temperature reached when switching off the mixer motor of the food processor and return it.

Let's add another call in the static constructor:

ACMethod.RegisterVirtualMethod(typeof(PAFMixing), "Start", CreateVirtualTemperatureMethod("MixingTemperature", "en{'Mixing Temp.'}de{'Mischen Temp.'}", typeof(PWMixing)));

The implementation of the CreateVirtualTemperatureMethod looks like this:

 

Now, when we compare the two methods CreateVirtualTemperatureMethod and CreateVirtualMethod, we notice the following commonality:

Both have the "Speed" parameter!

The Start method can now be programmed in such an abstract way that in both cases it treats the "Speed" parameter equally.