0

I have 4 arrays, each contains 10 words. The goal is to merge these 4 arrays into one bigger array (40 words) in codesys.

I'm able to copy the content of one array with help of a pointer in following way:

declaration:

array1: ARRAY [0..9] OF WORD;
array2: ARRAY [0..9] OF WORD;
array3: ARRAY [0..9] OF WORD;
array4: ARRAY [0..9] OF WORD;
masterarray: ARRAY [0..39] OF WORD;
pt: POINTER TO ARRAY [0..39] OF WORD;

code:

pt := ADR(array1);
masterarray := pt^;

I haven't figured out, how to continue from here to merge the rest of the arrays. I tied to use SIZEOF -function to figure out the size of the pointer, and use that as an offset... but since I haven't used pointers before, I'm not quite sure what I really should do.

Guiorgy
  • 1,405
  • 9
  • 26
MrHUU
  • 3
  • 3
  • `masterarray := pt^;`, this doesn't only copy the `array1` content to the start of `masterarray`, but also anything that is in the memory after `array1` at the time – Guiorgy Feb 17 '21 at 14:36
  • Hi, and thanks for the clarification. I did notice this, and this worked just fine, but only if all 4 arrays are one after the other on registers. But if I changed the declaration order, I still got all the values moved to the new array, but in the wrong order. That's why I'm looking for a solution, where moving values function every time, no matter where in the register they are located. – MrHUU Feb 18 '21 at 06:10

1 Answers1

0

Your first choice is to do simple loops:

i, j: USINT;
len: DINT := UPPER_BOUND(array1, 1); // 9 in your case
FOR i := 0 TO len DO
    masterarray[j] := array1[i];
    j := j + 1;
END_FOR
FOR i := 0 TO len DO
    masterarray[j] := array2[i];
    j := j + 1;
END_FOR
FOR i := 0 TO len DO
    masterarray[j] := array3[i];
    j := j + 1;
END_FOR
FOR i := 0 TO len DO
    masterarray[j] := array4[i];
    j := j + 1;
END_FOR

Another option is using the MemoryUtils library, specifically the MemCpy function:

MEMUtils.MemCpy(pbySrc := ADR(array1), pbyDest := ADR(masterarray), dwSize := SIZEOF(array1));
i := UPPER_BOUND(array1, 1) + 1;
MEMUtils.MemCpy(pbySrc := ADR(array2), pbyDest := ADR(masterarray[i]), dwSize := SIZEOF(array2)); // shift the destination by i (10 in this case)
i := i + UPPER_BOUND(array2, 1) + 1;
MEMUtils.MemCpy(pbySrc := ADR(array3), pbyDest := ADR(masterarray[i]), dwSize  := SIZEOF(array3)); // shift the destination by i (20 in this case)
i := i + UPPER_BOUND(array3, 1) + 1;
MEMUtils.MemCpy(pbySrc := ADR(array4), pbyDest  := ADR(masterarray[i]), dwSize := SIZEOF(array4)); // shift the destination by i (30 in this case)

EDIT: Here's a little function I wrote:

// Assume that:
//      - all ARRAYs start at index 0
//      - the destination ARRAY size can fit all source ARRAYs
//      - all ARRAYs store WORDs
FUNCTION MERGE
VAR CONSTANT
    SIZEOF_of_word: USINT := SIZEOF(WORD);
END_VAR
VAR_IN_OUT
    pointers: ARRAY [*] OF POINTER TO WORD;
    sizes: ARRAY [*] OF DWORD;
END_VAR
VAR_INPUT
    destinationPtr: POINTER TO WORD;
END_VAR
VAR
    i: DINT;
    len: DINT := UPPER_BOUND(pointers, 1);
    j: DWORD;
END_VAR

FOR i := 0 TO len DO
    MEMUtils.MemCpy(pbySrc := pointers[i], pbyDest := ADR(destinationPtr[j]), dwSize := sizes[i]);
    j := j + sizes[i] / SIZEOF_of_word;
END_FOR
PROGRAM PLC_PRG
VAR
    array1: ARRAY [0..9] OF WORD := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    array2: ARRAY [0..9] OF WORD := [11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
    array3: ARRAY [0..9] OF WORD := [21, 22, 23, 24, 25, 26, 27, 28, 29, 30];
    array4: ARRAY [0..9] OF WORD := [31, 32, 33, 34, 35, 36, 37, 38, 39, 40];
    masterarray: ARRAY [0..39] OF WORD;
    pts: ARRAY[0..3] OF POINTER TO WORD := [ADR(array1), ADR(array2), ADR(array3), ADR(array4)];
    ss: ARRAY[0..3] OF DWORD := [SIZEOF(array1), SIZEOF(array2), SIZEOF(array3), SIZEOF(array4)];
END_VAR

MERGE(pointers := pts, sizes := ss, destinationPtr := ADR(masterarray));

PS: I don't know if this is the most efficient way to do so

Guiorgy
  • 1,405
  • 9
  • 26
  • Tell me about `ARRAY [*]` What is this? – Sergey Romanov Feb 18 '21 at 05:05
  • Hi, and thank you for your help! This example works flawlessly, with a small change: MEMUtils.MemCpy(pbySrc := ADR(array1), pbyDest := ADR(masterarray), dwSize := SIZEOF(array1)); The change concerns the UPPER_BOUND, since in reality I don't have a true array, but several structs (with arrays inside). These structs are fixed length, therefore UPPER_BOUND is not needed, and I just defined i as an 'offset', which would be the same as result from UPPER_BOUND, in case if I had array instead of struct. ADR(masterarray[i] => i = 10, 20 and 30 – MrHUU Feb 18 '21 at 08:30
  • @MrHUU, true if you have fixed length arrays. I just generalized for future reference ;) – Guiorgy Feb 18 '21 at 08:52
  • @SergeyRomanov, [Variable length arrays](https://help.codesys.com/api-content/2/codesys/3.5.16.0/en/_cds_datatype_array/#array-of-variable-length). Basically, there're 3 ways to pass an array to func/fb: 1. Predefined fixed length (not flexible). 2. Pass a pointer and the length of the array (flexible, but some don't like pointers) 3. Pass the array in VAR_IN_OUT as a variable length array (basically, it can be of any size, and to get the [x..y] bounds you use LOWER_BOUND(arr, 1) for x, and UPPER_BOUND(arr, 1) for y). In reality, it's like option 2, but you don't need to pass the length – Guiorgy Feb 18 '21 at 08:58
  • @Guiorgy does it work only for `VAR_IN_OUT` scope? – Sergey Romanov Feb 19 '21 at 11:42
  • @SergeyRomanov, what do you mean? I used `VAR_IN_OUT` just for convinience. – Guiorgy Feb 19 '21 at 16:36
  • @Guiorgy the question was can I use `ARRAY [*]` only in `VAR_IN_OUT` scope or in `VAR_INPUT` too? Because in docs it uses the same scope. But anyway it is ok, I can just test it. – Sergey Romanov Feb 21 '21 at 07:00
  • @SergeyRomanov, arrays of variable length can only be used in the `VAR_IN_OUT` scope. This means that, if `ARRAY [*]` is used, a valid array must be passed to the func/fb. You may not want to use that, if the array in your func/fb is optional. – Guiorgy Feb 21 '21 at 12:01