How do I pass an array of an extended type in codesys/twincat3?

Viewed 707

Say I have an abstract function block AValve that I extend for various types of valve. I extend that AValve in order to implement it as a BasicValve. Also I have a function block that takes an array of AValve, which looks like this

FUNCTION_BLOCK ValveDispatch
VAR_IN_OUT
   valves : ARRAY[*] OF AVALVE;
END_VAR

If I try to pass an array of BasicValve into this function block, I'm met with:

Cannot convert type 'ARRAY [0..5] OF BasicValve' to type 'ARRAY [*] OF AVALVE' of VAR_IN_OUT 'valves'

Thinking that maybe codesys just couldn't handle both extended types AND variable length arrays at the same time, I've tried doing a set length array as an input, just for testing as I need the variable length. Doing so gives a slightly different error that seems to mean the same thing:

Type `ARRAY[0..5] of BasicValve' is not equal to type 'ARRAY [0..5] OF AVALVE' of VAR_IN_OUT 'valves'

Is there a way I can make this work? Passing a single extended object into an input expecting its base type works fine, but doing so with arrays seems to be unsupported.

3 Answers

The issue you are seeing actually comes down to a pretty simple point, arrays are value datatypes.

That is, when you are trying to pass ARRAY [*] OF FB_Ext through the VAR_IN_OUT what you are actually doing is creating a whole new datatype ARRAY [*] OF FB_Ext which does not extend ARRAY [*] OF FB_Base.

IF you look at an array at the bit level in ST the issue becomes pretty clear, an array itself has a size of ElementSize * ElementCount, So trying to force a bigger datatype FB_Ext into the smaller dataspace of FB_Base is not going to work without messing everything up.

So you will have to do this handling with either pointers or interfaces.


TLDR: What you want to do is not possible in ST.

So this was originally going to be a comment, but it got a bit beyond the size limit. Posted as a wiki instead.

The short answer: With Var_In_Out it is not possible. Var_in_out is a kind of call by reference. And a reference has always a strict typebinding in TwinCAT.

My first approach would be to solve this with a Interface-Pointer as simple var_input. And the interface contains size and type of the field you handover (as Array[*] also does). But I am not sure if derived interface pointers are allowed to handover. Maybe you have to handover the base interface and do a interface conversion with __TCQUERYINTERFACE somewhere manually.

I'm going to add an answer to my own question here, but not accept it for a bit in case someone has a secret left to divulge, haha.

Essentially, as far as I can tell, the pattern of passing an Array of Derived Type to an Array of BasicType is just simply unsupported. Either through a VAR_IN_OUT or other input type. You can't even just assign one to the other. Best I can tell Codesys doesn't respect inheritence when comparing arrays to each other, but WILL if you are comparing a particular index of the array to an object. I've come up with two work arounds that I don't like, but should work.

If you're passing interfaces, you can build the array in the header of the calling function block like this:

VAR
aFB : ARRAY[0..2] of ExtendedFB;//implements IExtended, which extends IBasic
aInterface : ARRAY[0..2] OF IBasic := [aFB[0], aFB[1],aFB[2]];
END_VAR

When it comes to extended fb's instead of interfaces, this won't quite work. In that scenario I had to use pointers, like so:

VAR
aFB: ARRAY[0..2] of ExtendedFB;//extends AbstractFB
apFB : Array[0..2] of Pointer to AbstractFB := [ADR(afb[0]), ADR(afb[1]), ADR(afb[2])];
END_VAR

TL;DR instead of passing Array of ExtendedType into an input expecting Array of BasicType or Reference to Array of BasicType, you can manually build an Array of BasicType one index at a time with ExtendedType objects and pass that instead, without the compiler complaining. Unfortunately, you're stuck with pointers if not using interfaces, since you can't create an array of function blocks without instantiating them.

Related