Как назначить инициализированный массив массиву массивов в TwinCAT

#plc #twincat #codesys #st #iec61131-3

Вопрос:

Я пытаюсь назначить два инициализированных массива evenNumbers и oddNumbers массиву массивов integers :

 PROGRAM ArrayInit
VAR
    evenNumbers : ARRAY[1..3] OF INT := [2, 4, 6];
    oddNumbers: ARRAY[1..3] OF INT := [1, 3, 5];
    
    integers : ARRAY[1..2] OF ARRAY[1..3] OF INT := [evenNumbers, oddNumbers];
END_VAR
 

Этот код выдает мне ошибку компилятора

Ожидается инициализация массива

Конечно, я могу напрямую инициализировать integers числа, которые я хочу, вот так:

 PROGRAM ArrayInit
VAR
    integers: ARRAY[1..2] OF ARRAY[1..3] OF INT := [
        [2, 4, 6], [1, 3, 5]
    ];
END_VAR
 

или как упоминал Сергей

 PROGRAM ArrayInit
VAR
    integers: ARRAY[1..2, 1..3] OF INT := [
        2, 4, 6, 1, 3, 5
    ];
END_VAR
 

Однако, если исходные массивы очень большие и/или я хочу задокументировать, что представляют собой эти разные массивы, было бы неплохо использовать описательное имя. Т. Е. integers : ARRAY[1..2] OF ARRAY[1..3] OF INT := [evenNumbers, oddNumbers]; хорошо видно, что в integers нем есть два списка, один с четными и один с нечетными числами.

Я также пытался инициализировать integers как integers: ARRAY[1..2] OF ARRAY[1..3] OF INT := [[evenNumbers], [oddNumbers]]; , но это приводит к ошибке компилятора:

Не удается преобразовать тип «МАССИВ [1..3] INT» в тип «INT»

Теперь я задаюсь вопросом, возможно ли это вообще? Если да, то кто-нибудь знает, как я могу это сделать?

Ответ №1:

Чтобы назначить массив с несколькими уровнями, вы делаете это в одной строке.

 combinedSet : ARRAY[1..2, 1..2] OF INT := [1,2,3,4];
 

приведет к массиву

 [
  1 => [
     1 => 1, 
     2 => 2
  ],
  2 => [
     1 => 2, 
     2 => 4
  ]
]
 

ПОЭТОМУ сначала он назначает все элементы первого элемента [1, 1] , [1, 2] , [1, 3] … а потом гнездо одно [2, 1] , [2, 2] , [2, 3]

Дополнительная информация

Самый простой способ объединить 2 массива в один многомерный-это:

 PROGRAM PLC_PRG
    VAR
        arr1 : ARRAY[1..3] OF INT := [1,3,5];
        arr2 : ARRAY[1..3] OF INT := [2,4,6];
        combinedSet : ARRAY[1..2] OF ARRAY[1..3] OF INT;
    END_VAR
    
    combinedSet[1] := arr1;
    combinedSet[2] := arr2;
END_PROGRAM
 

Причина, по которой это не работает

 integers : ARRAY[1..2] OF ARRAY[1..3] OF INT := [evenNumbers, oddNumbers];
 

Потому evenNumbers что и oddNumbers не инициализируются в момент использования. Если бы вы объявили их в VAR CONSTANT нем, это, вероятно, сработало бы, но тогда вы не смогли бы изменить содержимое этих массивов в программе.

Комментарии:

1. Спасибо, Сергей. К сожалению, это не совсем то, что я искал. Я обновил вопрос, чтобы прояснить его, надеюсь. Мне было интересно , можно ли будет повторно использовать инициализированные массивы oddNumbers и evenNumbers , чтобы было понятнее, что хранится в новом массиве integers .

2. Я добавил несколько дополнительных ответов. Более сложный способ объединения массивов с помощью выделения памяти @Guiorgy уже опубликовал.

3. Ввод evenNumbers и oddNumbers ввод, к VAR_GLOBAL CONSTANT сожалению, не работает. Назначение их в самой программе ( combinedSet[1] := arr1; ) — это также то, к чему я пришел в определенный момент. Я надеялся, что это можно сделать непосредственно в декларации, но, к сожалению, похоже, что это не так.

Ответ №2:

Я не думаю, что вы можете присоединяться к массивам при инициализации третьего (см. правку ниже). Однако вы можете вызвать функцию в начале программы, как только она соединит эти два массива:

 PROGRAM PLC_PRG
VAR
    arr1: ARRAY [0..2] OF INT := [1, 2, 3];
    arr2: ARRAY [0..2] OF INT := [4, 5, 6];
    arr3: ARRAY [0..5] OF INT;
    initialized: BOOL := FALSE;
END_VAR

IF (NOT initialized) THEN
    initialized := TRUE;
    JOIN_INT_ARRAYS(arr1 := arr1, arr2 := arr2, dest_arr := arr3);
END_IF
 
 // Assume that:
//      - the destination ARRAY size can fit all source ARRAYs
//      - all ARRAYs store INTs
FUNCTION JOIN_INT_ARRAYS
VAR CONSTANT
    size_of_int: DWORD := SIZEOF(INT);
END_VAR
VAR_IN_OUT
    arr1: ARRAY [*] OF INT;
    arr2: ARRAY [*] OF INT;
    dest_arr: ARRAY [*] OF INT;
END_VAR
VAR
    arr1_len: DWORD := DINT_TO_DWORD(UPPER_BOUND(arr1, 1) - LOWER_BOUND(arr1, 1)   1) * size_of_int;
END_VAR

MEMUtils.MemCpy(pbySrc := arr1, pbyDest := dest_arr, dwSize := arr1_len);
MEMUtils.MemCpy(pbySrc := arr2, pbyDest := dest_arr   arr1_len, dwSize := DINT_TO_DWORD(UPPER_BOUND(arr2, 1) - LOWER_BOUND(arr2, 1)   1) * size_of_int);
 

Результат:

результат.png

Несколько вещей, которые стоит отметить:

  • Я использовал MemCpy функцию из MEMUtils библиотеки. Если у вас его нет или вы не хотите добавлять его в свой проект, вы можете вручную скопировать значения из одного массива в другой с помощью FOR цикла.
  • Я опустил проверку дальности, которая может быть чрезвычайно опасной. Если вам нужна дополнительная защита, добавьте ее сами.
  • избегайте передачи arr_dest как arr1 или arr2 . Попытка копирования из массива в себя может привести к проблемам.

Редактировать:

На самом деле, похоже, это работает:

 integers: ARRAY [0..1] OF ARRAY [0..2] OF INT := [[2, 4, 6], [1, 3, 5]];
evenNumbers: ARRAY [0..2] OF INT := integers[0];
oddNumbers: ARRAY [0..2] OF INT := integers[1];
 

Результат:

результат1.png

Но я не знаю, является ли это вашим желаемым результатом. Если вам нужен непрерывный массив в качестве объединенного массива, то вы можете попробовать это:

 // in Program
evenNumbers: ARRAY [0..2] OF INT := [2, 4, 6];
oddNumbers: ARRAY [0..2] OF INT := [1, 3, 5];
integers: ARRAY [0..5] OF INT := JOIN_INT_ARRAYS_3_3_6(arr1 := evenNumbers, arr2 := oddNumbers);
 
 // (left)3   (right)3 = (result)6
FUNCTION JOIN_INT_ARRAYS_3_3_6 : ARRAY [0..5] OF INT
VAR_IN_OUT
    arr1: ARRAY [0..2] OF INT;
    arr2: ARRAY [0..2] OF INT;
END_VAR
VAR
    i: USINT;
END_VAR

FOR i := 0 TO 2 DO
    JOIN_INT_ARRAYS_3_3_6[i] := arr1[i];
END_FOR
FOR i := 0 TO 2 DO
    JOIN_INT_ARRAYS_3_3_6[i   3] := arr2[i];
END_FOR
 

и результат:

результат 2.png

Однако с помощью этого метода функция не может быть общей, поэтому вам придется изменять размеры входных и выходных массивов каждый раз, когда меняются условия, и создавать множество, если вы хотите использовать это в нескольких местах, поэтому это не элегантно, и лично я бы этого избегал, но если это работает для вас, то вот оно.

Кроме того, это, кажется, дает мне C0441: Access to uninitialized VAR_IN_OUT variable предупреждение, так что еще одна причина попытаться избежать этого.

Комментарии:

1. Хороший способ объединения массивов. Сложный, продвинутый и при этом красивый. Похоже на запутывание кода для большинства разработчиков ПЛК 🙂

2. Мило, но действительно немного сложно. К сожалению, присвоение значений непосредственно в объявлении кажется невозможным.

3. @Роальд, проверь правку 😉