Deklarując tablice do niedawna konieczne było podawanie od razu dokładnego rozmiaru. Od kiedy wyszła trzecia edycja standardu IEC61131-3 (wykorzystywana m.in. w e!COCKPIT) możliwe jest deklarowanie tablic o zmiennych wymiarach. Dzięki temu można tworzyć bardziej uniwersalne bloki funkcyjne i funkcje.
Oczywiście można wykorzystywać zmienne, aby określić granice tablicy. Muszą być one jednak zadeklarowane jako stałe (CONSTANTS). Nie zmienia to więc podstawowego ograniczenia nałożonego na tablice w poprzednich wersjach standardu.
PROGRAM MAIN VAR aTablica : ARRAY[1..UPPER_LIMIT] OF INT; END_VAR VAR CONSTANT UPPER_LIMIT : INT := 10; END_VAR
Ta właściwość jest szczególnie niewygodna, kiedy tablice przekazywane są jako parametry do bloków funkcyjnych. W artykule „Wskaźniki – najbardziej uniwersalne narzędzie programisty” opisałem możliwe rozwiązanie takiego problemu. Wykorzystanie wskaźników często bywa jednak kłopotliwe. Tworzony w taki sposób kod nie jest za bardzo czytelny przez co łatwo o błąd, który w tym przypadku może prowadzić do zatrzymania całego programu.
W e!COCKPIT można zadeklarować tablicę w nowy sposób:
aTablica : ARRAY[*] OF INT;
Z tej opcji można jednak korzystać tylko w przypadku deklarowania zmiennych VAR_IN_OUT dla bloków funkcyjnych i funkcji (w e!COCKPIT jest już możliwość korzystania ze zmiennych VAR_IN_OUT w funkcjach). Dalej w momencie kompilacji tablica musi mieć określony rozmiar. W tym przypadku tak zadeklarowana tablica przyjmie zawsze rozmiar tablicy jaką podłączymy do danego bloczka.
FUNCTION FunSumArray :INT VAR_IN_OUT aTablica:ARRAY[*]OF INT; END_VAR VAR i:INT; iSuma:DINT; END_VAR (*kod funkcji*) FOR i:= LOWER_BOUND(aTablica, 1) TO UPPER_BOUND(aTablica, 1) DO diSuma:=iSum+ aTablica[i]; END_FOR FunSumArray:=diSuma;
Funkcja LOWER_BOUND zwraca liczbę będącą dolnym zakresem tablicy. Jeśli więc zadeklarowaliśmy tablicę tak jak poniżej, funkcja powinna zwrócić wartość 11. Analogicznie funkcja UPPER_BOUND zwróci wartość 99.
VAR aTablica1:ARRAY[11..99]OF INT; END_VAR (*kod programu*) iDolnyZakres:= LOWER_BOUND(aTablica1,1); iGornyZakres:= UPPER_BOUND(aTablica1,1);
Korzystanie z bloków tworzonych w ten sposób jest o wiele wygodniejsze niż w wersji opartej na wskaźnikach.
PROGRAM PLC_PRG VAR aTablica1:ARRAY[0..99]OF INT; aTablica2:ARRAY[8..17]OF INT; aTablica3:ARRAY[-10..20]OF INT; aTablica4:ARRAY[300..800]OF INT; iWynik1:INT; iWynik2:INT; iWynik3:INT; iWynik4:INT; END_VAR (*kod programu*) IWynik1:= FunSumArray(aTablica1); IWynik2:= FunSumArray(aTablica2); IWynik3:= FunSumArray(aTablica3); IWynik4:= FunSumArray(aTablica4);
Co ważniejsze możemy przekazywać tablice z dowolnie zadeklarowanymi granicami dolnymi i górnymi. Zwiększa to możliwości wykorzystania takich bloków. Idealnym przykładem jest bloczek FbMbSimpleServerTcp z biblioteki WagoAppPlcModbus. Trzeba do niego podłączyć tablice, których granice mogą być dowolnie zadeklarowane. Dzięki temu zmienne mogą być udostępniane na dowolnym zakresie adresów modbus.
FUNCTION FunSumArray2D :INT VAR_IN_OUT aTablica:ARRAY[*,*]OF INT; END_VAR VAR i:INT; j:INT; iSuma:DINT; END_VAR (*kod funkcji*) FOR i:= LOWER_BOUND(aTablica, 1) TO UPPER_BOUND(aTablica, 1) DO FOR j:= LOWER_BOUND(aTablica, 2) TO UPPER_BOUND(aTablica, 2) DO diSuma:=iSum+ aTablica[i,j]; END_FOR END_FOR FunSumArray2D:=diSuma;
Podsumowanie
Dzięki takim rozwiązaniom możliwe jest tworzenie jeszcze bardziej elastycznych bloków funkcyjnych. W przeciwieństwie do bloków opartych o wskaźniki, kompilator dba o poprawność kodu. Efekt końcowy jest też o wiele bardziej czytelny. Z tak napisanych bloków mogą później bez problemów korzystać nawet niedoświadczeni programiści, bez konieczności zmiany dawnych przyzwyczajeń.
Krzysztof Nosal, WAGO.PL