W poprzednim artykule (Wskaźniki – najbardziej uniwersalne narzędzie programisty) opisałem korzyści i zagrożenia wynikające z zastosowania wskaźników. W e!COCKPIT zaimplementowano także obsługę referencji, która umożliwia korzystanie z wielu zalet wskaźników, eliminując jednocześnie związane z nimi problemy.
PROGRAM PLC_PRG
VAR (*kod programu*) iZmienna:=123; |
Powyższy przykład pokazuje tylko ogólną zasadę działania. W tym przypadku, aby uzyskać wartość refZmienna, nie trzeba korzystać z operatora ^ jak to miało miejsce we wskaźnikach.
Różnice pomiędzy referencją a wskaźnikiem
Zasada działania referencji jest podobna do wskaźników. Dzięki nim możliwe jest odwoływanie się do zmiennej bez konieczności kopiowania jej. W powyższym przykładzie zarówno zmienna iZmienna, jak i refZmienna mają taką samą wartość. Co ważne, zmiana wartości którejkolwiek z nich będzie mieć wpływ na drugą. Mamy tu więc do czynienia z podobnym połączeniem jak w przypadku zmiennych VAR_IN_OUT w blokach funkcyjnych.
W przeciwieństwie do wskaźników, kompilator chroni nas przed popełnieniem podstawowych błędów. Na przykład nie jest możliwe skompilowanie programu, jeśli chcemy połączyć referencję ze zmienną nieodpowiedniego typu. Kompilator zwróciłby więc błąd, jeśli próbowalibyśmy wykonać następujący kod:
PROGRAM PLC_PRG
VAR (*kod programu*) wZmienna:=123; |
Wiąże się to jednak z pewnymi ograniczeniami. Referencji nie można wykorzystywać do konwersji typów zmiennych. Nie jest też możliwe przetwarzanie danych w tablicach o dowolnych rozmiarach.
Zastosowanie referencji
Ze względu na powyższe właściwości zastosowanie ogranicza się przede wszystkim do bezpośredniego odwoływania się do zmiennych. Dalej dzięki temu możliwe jest uzyskiwanie funkcjonalności zmiennych VAR_IN_OUT nie tylko w blokach funkcyjnych, ale także w funkcjach.
W przeciwieństwie do wskaźników nie ma za to ryzyka odwoływania się do nieistniejących obszarów pamięci. Dla potencjalnego użytkownika takiej aplikacji dużą zaletą jest łatwiejsze odwoływanie się do wartości referencji oraz intuicyjne podłączanie zmiennych pod bloki funkcyjne, w których wykorzystano referencje.
FUNCTION FunMnozenie2 :BOOL
VAR_INPUT (*kod funkcji*) iTemp:=refZmiennaWejsciowa; FunMnozenie2:=true; |
PROGRAM PLC_PRG
VAR (*kod programu*) FunMnozenie2(iZmienna); |
Jak widać, podłączenie zmiennej pod wejście zadeklarowane jako REFERENCE TO INT dla użytkownika nie różni się niczym od podłączenia do wejścia zadeklarowanego jako INT.
Kolejną zaletą jest możliwość inicjalizacji referencji w oknie deklaracji zmiennych. Dzięki temu połącznie pomiędzy referencją a zmienną źródłową wykonywane jest tylko raz, a nie ponawiane w każdym cyklu sterownika, co może wpływać na wydajność programu.
PROGRAM PLC_PRG
VAR (*kod programu*) (*niepotrzebny fragment kodu : refZmienna REF=iZmienna; *) |
Dodatkowo referencje umożliwiają tworzenie uniwersalnych bloków funkcyjnych dzięki zastosowaniu funkcji „__ISVALIDREF()”, która określa, czy pod referencję prawidłowo została podpięta zmienna.
FUNCTION_BLOCK FbSterowanieOswietleniem VAR_INPUT xPrzelacznik : BOOL; refSwiatlo1 : REFERENCE TO BOOL; refSwiatlo2 : REFERENCE TO BOOL; END_VAR(*kod programu*) IF xPrzelacznik THEN IF __ISVALIDREF(refSwiatlo1) THEN refSwiatlo1 := TRUE; END_IF IF __ISVALIDREF(refSwiatlo2) THEN refSwiatlo2 := TRUE; END_IFELSE IF __ISVALIDREF(refSwiatlo1) THEN refSwiatlo1 := FALSE; END_IF IF __ISVALIDREF(refSwiatlo2) THEN refSwiatlo2 := FALSE; END_IF END_IF |
W ten sposób możemy na przykład sterować oświetleniem podłączając pod wejścia bloku funkcyjnego dostępne w pomieszczeniu obwody świetlne.
Częste błędy
Wskaźnik jest po prostu zmienną typu DWORD. W związku z tym nieprzypisanie mu żadnej wartości sprawi, że zostanie zainicjowany z wartością 0. W programie warto sprawdzać, czy nie występuje taka sytuacja, ponieważ kompilator nie będzie zgłaszał żadnych błędów.
W przypadku referencji programista jest zobowiązany do przypisania referencji do zmiennej źródłowej. Często może okazać się, że przypisanie wykonywane jest dynamicznie, w trakcie działania aplikacji. W takim przypadku należy zadeklarować „pustą” zmienną, która na etapie kompilacji zostanie połączona z referencją. Poniżej przykład takiego rozwiązania.
PROGRAM PLC_PRG
VAR (*kod programu*) iZmienna:=123; IF xPrzypisz THEN END_IF |
Dopiero kiedy zmienna xPrzypisz zostanie ustawiona na TRUE, referencja zacznie działać prawidłowo.
VAR_IN_OUT – czyli referencja i wskaźnik, który znamy i lubimy
Tak naprawdę każdy programista ma na co dzień styczność z referencjami, nawet jeśli nie jest tego świadomy. Zmienne VAR_IN_OUT działają bowiem dokładnie tak samo jak opisane tu referencje. Jeśli w bloku funkcyjnym korzystamy ze zmiennej VAR_IN_OUT, to w przeciwieństwie do zmiennych VAR_IN i VAR_OUT nie jest tworzona wewnętrzna kopia podłączonej zmiennej, a tylko przekazana referencja do zmiennej źródłowej.
Podsumowanie
Mam nadzieję, że ten i poprzedni artykuł oswoiły trochę zagadnienia wskaźników i referencji. Jednocześnie zachęcam do samodzielnego eksperymentowania i dalszej nauki. Jest to podstawa przed poznaniem bardziej zaawansowanych technik programistycznych.
Krzysztof Nosal, WAGO.PL