Reputation: 337
I'm trying to build a function that sums a single dimension array of any type in TwinCAT.
I'm relatively inexperienced with pointers so the answer may be obvious, but I could not find any solutions. I read through all these and they helped a bit.
T_ARG Convert Byte arrays The Wonders of ANY
My conceptual code works for an INT array, but I realized I would have to write a FOR loop for each datatype to make it work and that seems wrong. I also wanted to avoid having a CASE in a FOR loop as that seemed inefficient.
Is it possible to have a pointer (of unset type) to a pointer (of a specific type)? It looks possible in C, but not sure about IEC-61131.
FUNCTION fn_SumArray : DINT
VAR_INPUT
inArr : ANY;
inElem : ANY;
END_VAR
VAR
i : DINT := 0;
stepsize : DINT :=1;
pLREAL : POINTER TO LREAL; //64 bit
pREAL : POINTER TO REAL; //32 bit
pBYTE : POINTER TO BYTE; //8 bit
pWORD : POINTER TO WORD; //16 bit
pDWORD : POINTER TO DWORD; //32 bit
pLWORD : POINTER TO LWORD; //64 bit
pSINT : POINTER TO SINT; //8 bit
pUSINT : POINTER TO USINT; //8 bit
pINT : POINTER TO INT; //16 bit
pUINT : POINTER TO UINT; //16 bit
pDINT : POINTER TO DINT; //32 bit
pUDINT : POINTER TO UDINT; //32 bit
pLINT : POINTER TO LINT; //64 bit
pULINT : POINTER TO ULINT; //64 bit
END_VAR
--------------------------------------------------
CASE inElem.TypeClass OF
__SYSTEM.TYPE_CLASS.TYPE_LREAL:
stepsize := 8;
//set generic pointer here
__SYSTEM.TYPE_CLASS.TYPE_INT:
stepsize := 2;
//set generic pointer here
ELSE
fn_SumArray :=0;
END_CASE;
FOR i := 0 TO inArr.diSize-1 BY stepsize DO
genericPointer:= ADR(inarr.pValue[i]);
fn_SumArray := fn_SumArray +genericPointer^;
END_FOR;
Upvotes: 3
Views: 1523
Reputation: 1734
As far as I know, you'll have to repeat the code for all types you want to cover. CODESYS doesn't have true generic programming, so it is what it is:
(* DECLARATION *)
METHOD fn_SumArray : BOOL // if TRUE, then error
VAR_INPUT
arr_first : ANY_NUM;
arr_size : DINT;
sum_out : ANY_NUM;
END_VAR
VAR
i : DINT;
ptr_arr: NUMBER_POINTER; // just a UNION with all number type pointers
ptr_sum: NUMBER_POINTER;
END_VAR
(* IMPLEMENTATION *)
IF (arr_first.TypeClass <> sum_out.TypeClass) THEN
fn_SumArray := TRUE;
// set all bytes of sum_out to 0
FOR i := 0 TO sum_out.diSize - 1 DO
sum_out.pValue[i] := 0;
END_FOR
RETURN;
END_IF
CASE arr_first.TypeClass OF
__SYSTEM.TYPE_CLASS.TYPE_LREAL:
ptr_arr._LREAL := arr_first.pValue;
ptr_sum._LREAL := sum_out.pValue;
ptr_sum._LREAL^ := 0;
FOR i := 0 TO arr_size - 1 DO
ptr_sum._LREAL^ := ptr_sum._LREAL^ + ptr_arr._LREAL[i];
END_FOR
// Repeat the above for all ...
ELSE
fn_SumArray := TRUE;
// set all bytes of sum_out to 0
FOR i := 0 TO sum_out.diSize - 1 DO
sum_out.pValue[i] := 0;
END_FOR
END_CASE;
// A POINTER for every data type of ANY_NUM
// PS. A pointer has the exact same size for any type
TYPE NUMBER_POINTER :
UNION
// floating
_REAL: POINTER TO REAL;
_LREAL: POINTER TO LREAL;
// unsigned
_USINT: POINTER TO USINT;
_UINT: POINTER TO UINT;
_UDINT: POINTER TO UDINT;
_ULINT: POINTER TO ULINT;
// signed
_SINT: POINTER TO SINT;
_INT: POINTER TO INT;
_DINT: POINTER TO DINT;
_LINT: POINTER TO LINT;
END_UNION
END_TYPE
(* DECLARATION *)
PROGRAM PLC_PRG
VAR CONSTANT
SIZE: DINT := 50;
END_VAR
VAR
i: DINT;
arr_int: ARRAY [0..(SIZE - 1)] OF INT;
sum_int: INT;
arr_dint: ARRAY [0..(SIZE - 1)] OF DINT;
sum_dint: DINT;
arr_real: ARRAY [0..(SIZE - 1)] OF REAL;
sum_real: REAL;
first_run: BOOL := TRUE;
sum_test: REAL;
test_failed: BOOL;
END_VAR
(* IMPLEMENTATION *)
IF (first_run) THEN
first_run := FALSE;
FOR i := 0 TO SIZE - 1 DO
arr_int[i] := DINT_TO_INT(i);
arr_dint[i] := i * 10;
arr_real[i] := DINT_TO_REAL(i) / 10;
END_FOR
END_IF
fn_SumArray(arr_first := arr_int[0], arr_size := SIZE, sum_out := sum_int);
sum_test := 0;
FOR i := 0 TO SIZE - 1 DO
sum_test := sum_test + arr_int[i];
END_FOR
IF (sum_int <> REAL_TO_INT(sum_test)) THEN
test_failed := TRUE;
END_IF
fn_SumArray(arr_first := arr_dint[0], arr_size := SIZE, sum_out := sum_dint);
sum_test := 0;
FOR i := 0 TO SIZE - 1 DO
sum_test := sum_test + DINT_TO_REAL(arr_dint[i]);
END_FOR
IF (sum_dint <> REAL_TO_DINT(sum_test)) THEN
test_failed := TRUE;
END_IF
fn_SumArray(arr_first := arr_real[0], arr_size := SIZE, sum_out := sum_real);
sum_test := 0;
FOR i := 0 TO SIZE - 1 DO
sum_test := sum_test + arr_real[i];
END_FOR
IF (sum_real <> sum_test) THEN
test_failed := TRUE;
END_IF
The result of the program above:
If writing the same code for every type is too tedious, you can always use a script to automate this:
#!/usr/bin/env python3
code_template = \
"""CASE arr_first.TypeClass OF
{0}
ELSE
\tfn_SumArray := TRUE;
\tFOR i := 0 TO sum_out.diSize - 1 DO
\t\tsum_out.pValue[i] := 0;
\tEND_FOR
END_CASE;
"""
case_template = \
"""\t__SYSTEM.TYPE_CLASS.TYPE{0}:
\t\tptr_arr.{0} := arr_first.pValue;
\t\tptr_sum.{0} := sum_out.pValue;
\t\tptr_sum.{0}^ := 0;
\t\tFOR i := 0 TO arr_size - 1 DO
\t\t\tptr_sum.{0}^ := ptr_sum.{0}^ + ptr_arr.{0}[i];
\t\tEND_FOR
"""
types = ['_REAL', '_LREAL', '_USINT', '_UINT', '_UDINT', '_ULINT', '_SINT', '_INT', '_DINT', '_LINT']
cases = [case_template.format(t) for t in types]
code = code_template.format(''.join(cases))
print(code)
Just run the above script with python 3.x and you'll get the case statements code for the types listed in the script.
Upvotes: 4
Reputation: 337
What I ended up going with was a case statement, with for loops inside.
Not as elegant as I had hoped, but still functional.
FUNCTION fn_SumArray : REAL
VAR_INPUT
inArr : ANY; //Array to size
inElem : ANY; //Any element in the array
END_VAR
VAR
i : DINT := 0;
pBYTE : POINTER TO BYTE; //8 bit
pSINT : POINTER TO SINT; //8 bit
pUSINT : POINTER TO USINT; //8 bit
pWORD : POINTER TO WORD; //16 bit
pINT : POINTER TO INT; //16 bit
pUINT : POINTER TO UINT; //16 bit
pDINT : POINTER TO DINT; //32 bit
pUDINT : POINTER TO UDINT; //32 bit
pDWORD : POINTER TO DWORD; //32 bit
pREAL : POINTER TO REAL; //32 bit
pLREAL : POINTER TO LREAL; //64 bit
pLWORD : POINTER TO LWORD; //64 bit
END_VAR
//---------------------------------
CASE inElem.TypeClass OF
__SYSTEM.TYPE_CLASS.TYPE_SINT:
FOR i := 0 TO inArr.diSize-1 BY 1 DO
pSINT := ADR(inarr.pValue[i]);
fn_SumArray := fn_SumArray + pSINT^;
END_FOR;
__SYSTEM.TYPE_CLASS.TYPE_USINT:
FOR i := 0 TO inArr.diSize-1 BY 1 DO
pUSINT := ADR(inarr.pValue[i]);
fn_SumArray := fn_SumArray + pUSINT^;
END_FOR;
__SYSTEM.TYPE_CLASS.TYPE_WORD:
FOR i := 0 TO inArr.diSize-1 BY 2 DO
pWORD := ADR(inarr.pValue[i]);
fn_SumArray := fn_SumArray + pWORD^;
END_FOR;
__SYSTEM.TYPE_CLASS.TYPE_INT:
FOR i := 0 TO inArr.diSize-1 BY 2 DO
pINT := ADR(inarr.pValue[i]);
fn_SumArray := fn_SumArray + pINT^;
END_FOR;
__SYSTEM.TYPE_CLASS.TYPE_UINT:
FOR i := 0 TO inArr.diSize-1 BY 2 DO
pUINT := ADR(inarr.pValue[i]);
fn_SumArray := fn_SumArray + pUINT^;
END_FOR;
__SYSTEM.TYPE_CLASS.TYPE_DINT:
FOR i := 0 TO inArr.diSize-1 BY 4 DO
pDINT := ADR(inarr.pValue[i]);
fn_SumArray := fn_SumArray + pDINT^;
END_FOR;
__SYSTEM.TYPE_CLASS.TYPE_UDINT:
FOR i := 0 TO inArr.diSize-1 BY 4 DO
pUDINT := ADR(inarr.pValue[i]);
fn_SumArray := fn_SumArray + pUDINT^;
END_FOR;
__SYSTEM.TYPE_CLASS.TYPE_DWORD:
FOR i := 0 TO inArr.diSize-1 BY 4 DO
pDWORD := ADR(inarr.pValue[i]);
fn_SumArray := fn_SumArray + pDWORD^;
END_FOR;
__SYSTEM.TYPE_CLASS.TYPE_REAL:
FOR i := 0 TO inArr.diSize-1 BY 8 DO
pREAL := ADR(inarr.pValue[i]);
fn_SumArray := fn_SumArray + pREAL^;
END_FOR;
__SYSTEM.TYPE_CLASS.TYPE_LREAL:
FOR i := 0 TO inArr.diSize-1 BY 8 DO
pLREAL := ADR(inarr.pValue[i]);
fn_SumArray := fn_SumArray + pLREAL^;
END_FOR;
ELSE
fn_SumArray :=0;
END_CASE;
Upvotes: 1