Przykładowa aplikacja Codesys w języku ST i LD: napełnianie i opróżnianie zbiorników wraz z alarmowaniem
Kontakt w sprawie artykułu: Mateusz Pytel - 2023-08-30
Z tego artykułu dowiesz się:
- jak wgrać gotowy program w Codesys,
- jak może wyglądać wizualizacja przykładowego procesu technologicznego (napełnianie i opróżnianie zbiorników),
- na jakiej zasadzie funkcjonują stany alarmowe.
W tym artykule prezentujemy gotowy przykład programu, napisanego w środowisku Codesys, w języku ST oraz LD.
Zanim jednak go wgramy, najpierw musimy przygotować nasze środowisko programistyczne. Należy to wykonać tak, jak opisaliśmy w pierwszym odcinku naszego kursu programowania w Codesys:
Plik źródłowy opisywanej w tym artykule aplikacji możesz ściągnąć,
Tworzenie podstawy programu w Codesys z wykorzystaniem symulatora
Krok 1: tworzymy nowy standardowy projekt Zbiorniki_ST. Pisany program będzie w języku ST, natomiast w tym artykule umieszczony został również kod w języku drabinkowym LD. Nasz komputer ma być wirtualnym sterownikiem, dlatego w polu Device wybieramy CODESYS Control Win V3.
Krok 2: dodajemy nową bibliotekę. Za pomocą Library Manager zarządzamy dostępnymi bibliotekami w naszej aplikacji. Standardowo do projektu dołączane są podstawowe biblioteki. Rozszerzamy ją o nową bibliotekę Util 3.5.17.0. Jest ona konieczna ze względu na wykorzystanie m.in. gotowego bloku migania BLINK, który został użyty w napisanym programie.
Krok 3: Wprowadzamy kod programu. Zmienne w oknie zmiennych, kod w edytorze programu.
Następnie kompilujemy program poprzez wybranie z menu kontekstowego Build -> Generate Code lub wyboru skrótu klawiszowego F11. Jeżeli uzyskamy poniższy widok bez żadnych błędów czy ostrzeżeń, oznacza to że prawidłowo został on skompilowany.
Przygotowanie środowiska wizualizacji do projektu w Codesys
Sposób przygotowania środowiska również został opisany w naszym kursie programowania Codesys. Zachęcam do zapoznania się z poniższym artykułem, gdzie od podstaw jest wyjaśniona konfiguracja środowiska Codesys do tworzenia wizualizacji:
Wracając do naszego programu, najpierw dodajemy pole wizualizacji i nazywamy ją jako Ekran_1, który będzie naszym widokiem startowym. Aby dodać wizualizację do projektu, należy w drzewku projektowym kliknąć prawym klawiszem myszy na Application, a następnie wybrać Add Object -> Visualization.
Od tego momentu pojawiły nam się nowe zakładki drzewka projektowego:
Visualization Manager – obiekt dodawany automatycznie po dodaniu pierwszej wizualizacji; konfiguracja ogólnych ustawień wizualizacji, takich jak np. używany styl obiektów wizualizacji lub zdefiniowanie poziomów dostępu do danych okien wizualizacji.
TargetVisu – ustawienia dotyczące wizualizacji wyświetlanej na terminalach czy monitorach. Gdy chcemy mieć stałą wielkość ekranu, który nie zmienia swojej wielkości, zaznaczamy opcję Use specified visualization size i wpisujemy rzeczywiste wymiary wielkości ekranu.
WebVisu – ustawienia dotyczące wizualizacji wyświetlanej w przeglądarce internetowej. W tym przypadku wszystko zależy od urządzenia, na którym wyświetlimy naszą stronę internetową. Możemy narzucić stałą rozdzielczość jak wyżej, ale możemy również użyć automatycznych skalowań (Use automatic detected visualization size).
W Codesys jest również opcja stworzenia aplikacji Responsive Web Design, która polega na możliwości stworzenia wielu ekranów wizualizacyjnych w różnych rozdzielczościach, tak aby układ menu, wielkość przycisków, ilość wykresów i grafik był ściśle dopasowany do wielkości urządzenia, na którym uruchomiono HMI.
Po więcej informacji na temat Responsive Web Design, wraz z instrukcją i przykładowym programem, zapraszam do naszego informatora technicznego dostępnego pod adresem https://www.astor.com.pl/wsparcie/dokumentacja-techniczna/zobacz/89183
W celu łatwiejszego podglądu naszego Ekran_1, wejdźmy w jego właściwości i ustawmy stały rozmiar ekranu na 800×600.
Chcemy aby nowo stworzona wizualizacja była wyświetlona w przeglądarce internetowej, więc najpierw wpisujemy naszą nazwę w polu Name of .htm file, a następnie wpisujemy w naszej przeglądarce poniższą składnię:
localhost:8080/webvisu.htm
UWAGA: Gdy nie chcemy korzystać z symulatora CODESYS Win V3, to zamiast localhost wpisujemy adres IP sterownika, np.: 192.168.1.6:8080/webvisu.htm
Praca z edytorem wizualizacji
Mamy przygotowane miejsce, gdzie będzie wyświetlona nasza wizualizacja. Teraz czas na stworzenie pierwszego ekranu, w którym chcemy zademonstrować nasze zbiorniki oraz postęp ich ładowania oraz opróżniania. Klikając dwukrotnie w drzewku projektowym na okno wizualizacji, otwiera się okno, gdzie w centralnym punkcie budujemy wizualizację, po prawej stronie z menu narzędziowego dodajemy elementy wizualizacji:
Możemy sobie zmienić białe tło na jakikolwiek inny kolor, naciskając prawym klawiszem myszy na nasz ekran, następnie wybierając Background – w nowym okienku wybieramy kolor, jaki nas interesuje. Ja wybrałem jeden z odcieni szarości:
Kolejnym krokiem będzie dodanie symulacji naszego zbiornika w postaci paska postępu (BarDisplayImage), oraz przypisanie do niej zmiennej iZbiorniki1 we właściwościach grafiki, w polu Property -> Value:
Musimy również zmienić skalę naszego paska, ponieważ zmienna Zbiornik jest w zakresie od 0 do 200. W związku z tym musimy zmienić to w polu Scale. Jako przedziałkę ustawimy wartość 25.
Dodamy również pole z informacją o aktualnym stanie zbiornika. Trzeba tutaj pamiętać o specjalnej procedurze wywołania tej funkcji, poprzez dodanie %i w opisie:
Dodamy też lampkę, która będzie nam sygnalizować napełnianie/opróżnianie zbiornika poprzez przypisanie zmiennej xLamp1. Dodatkowo zmienię jej kolor na zielony:
Tym o to sposobem mamy przedstawione wszystkie najważniejsze informacje dla zbiornika nr 1. To samo musimy powtórzyć dla zbiorników nr 2 oraz 3. Dodatkowo na podobnej zasadzie dodajemy przełączniki z menu narzędziowego, które będą odpowiedzialne za napełnianie (zmienna xNapelnianie w polu Variable) oraz za opróżnianie (zmienna xOproznianie w polu Variable).
Konfiguracja alarmów
W środowisku Codesys rozróżniamy 2 główne składowe alarmów:
Klasy alarmów
Alarmy przynależą do klas alarmowych. W klasach alarmów definiujemy priorytet danej klasy, czy dana klasa alarmów ma być archiwizowana, sposób potwierdzenia alarmów, możliwość definicji potwierdzeń wywołania alarmu z danej klasy oraz kolory wyświetlania alarmów z danej klasy.
Poniżej przedstawiłem metody potwierdzania alarmów w poszczególnych klasach alarmowych:
REP – alarm staje się nieaktywny po usunięciu przyczyny.
ACK – alarm jest nieaktywny po zatwierdzeniu zdarzenia.
REP_ACK – alarm nieaktywny po usunięciu przyczyny i potwierdzeniu.
ACK_REP – alarm nieaktywny po przejściu w tryb normalny oraz po potwierdzeniu + możliwość potwierdzenia aktywnego alarmu i w ten sposób dezaktywacja go z listy alarmów.
ACK_REP_ACK – alarm nieaktywny po opcjonalnym potwierdzeniu, że alarm został odebrany, poprawieniu przyczyny i potwierdzenie, że sytuacja alarmowa została zakończona.
Grupy alarmów
Grupy alarmowe służą do definicji alarmów. Alarm może być przypisany tylko do jednej grupy alarmowej. Istnieje możliwość tworzenia wielu grup alarmowych w zależności od potrzeb. Każdy alarm ma swoją określoną budowę:
ID – numer identyfikujący alarm.
Observation type – określenie rodzaju alarmu:
- Digital – zmienna cyfrowa,
- Upper/Lower limit – alarm powyżej/poniżej pewnej wartości,
- Outside/Inside range – alarm gdy wartość przekroczy ustalone progi,
- Change – alarm przy zmianie wartości ustalonej zmiennej.
- Event – w tym przypadku aplikacja wyzwala alarm (alarm zdarzenia), korzystając z funkcji z biblioteki
W celu dodania obiektu zarządzającego alarmami klikamy prawym klawiszem myszy na Application w drzewku projektowym i następnie wybieramy Add Object -> Alarm Configuration:
Automatycznie w drzewku projektowym pojawia się domyślna konfiguracja z klasami alarmów oraz opcją dla pamięci alarmów.
Następnie dodajemy nową grupę alarmów, nazwijmy ją Alarm_Zbiorniki:
W nowym oknie ustawiamy przykładowe stany alarmowe:
Lower limit – Gdy poziom pierwszego zbiornika będzie poniżej 20, pojawi się komunikat Error z komentarzem NISKI STAN ZB
Upper limit – Gdy poziom trzeciego zbiornika będzie powyżej 180, pojawi się komunikat Error z komentarzem WYSOKI STAN ZB
Aby wyświetlić stan alarmów, musimy stworzyć nowy ekran, na którym zostanie przedstawiona tabela alarmów:
Po zasymulowaniu jednego procesu napełniania i opróżniania zbiorników jesteśmy w stanie zobaczyć nasze zadeklarowane stany alarmowe z niskim lub wysokim poziomem.
Na potrzeby tego artykułu zagadnienie zostało przedstawione na poziomie podstawowym, natomiast jest możliwe rozszerzenie funkcjonalności alarmów np. poprzez rozszerzenie liczby komunikatów, archiwizację, zmianę kolorów lub czcionki czy dezaktywację wszystkich za pomocą jednej zmiennej.
Kod programu w języku ST
Plik źródłowy opisywanej w tym artykule aplikacji możesz ściągnąć,
//deklaracja zmiennych
PROGRAM Zbiorniki_ST
VAR
xZawor1in: BOOL;
iZbiornik1: INT;
xZawor2in: BOOL;
iZbiornik2: INT;
xZawor3in: BOOL;
iZbiornik3: INT;
xNapelnianie: BOOL;
xOproznianie: BOOL;
xZawor3out: BOOL;
xZawor2out: BOOL;
xZawor1out: BOOL;
xLamp1, xLamp2, xLamp3 : BOOL;
fbBlink : BLINK;
xInit: BOOL:=TRUE;
fbLicznik1 : CTU;
fbLicznik2 : CTU;
xSerwis : BOOL;
END_VAR
//stan poczatkowy - inicjalizacja
IF xInit THEN
xZawor1in:=TRUE;
// xZawor3out:=TRUE;
fbBlink.ENABLE:=FALSE;
xNapelnianie:=FALSE;
xOproznianie:=FALSE;
xInit:=FALSE;
END_IF
//start napelniania
IF xNapelnianie THEN
//napelnianie zbiornik1
IF xZawor1in THEN
iZbiornik1:=iZbiornik1+1;
IF iZbiornik1=200 THEN
xZawor1in:=FALSE;
xZawor2in:=TRUE;
END_IF
END_IF
//napelnianie zbiornik2
IF xZawor2in THEN
iZbiornik2:=iZbiornik2+1;
IF iZbiornik2=200 THEN
xZawor2in:=FALSE;
xZawor3in:=TRUE;
END_IF
END_IF
//napelnianie zbiornik3
IF xZawor3in THEN
iZbiornik3:=iZbiornik3+1;
IF iZbiornik3=200 THEN
xZawor3in:=FALSE;
xNapelnianie:=FALSE;
xZawor3out:=TRUE;
END_IF
END_IF
//sygnalizacja lamp1
IF xZawor1in AND iZbiornik1>0 AND iZbiornik1<200 THEN
fbBlink(ENABLE:=TRUE, TIMELOW:=T#1S, TIMEHIGH:=T#1S);
xLamp1:=fbBlink.OUT;
ELSIF iZbiornik1=200 THEN
xLamp1:=TRUE;
fbBlink.ENABLE:=FALSE;
ELSE
xLamp1:=FALSE;
END_IF
//sygnalizacja lamp2
IF xZawor2in AND iZbiornik2>0 AND iZbiornik2<200 THEN
fbBlink(ENABLE:=TRUE, TIMELOW:=T#1S, TIMEHIGH:=T#1S);
xLamp2:=fbBlink.OUT;
ELSIF iZbiornik2=200 THEN
xLamp2:=TRUE;
fbBlink.ENABLE:=FALSE;
ELSE
xLamp2:=FALSE;
END_IF
//sygnalizacja lamp3
IF xZawor3in AND iZbiornik3>0 AND iZbiornik3<200 THEN
fbBlink(ENABLE:=TRUE, TIMELOW:=T#1S, TIMEHIGH:=T#1S);
xLamp3:=fbBlink.OUT;
ELSIF iZbiornik3=200 THEN
xLamp3:=TRUE;
fbBlink.ENABLE:=FALSE;
ELSE
xLamp3:=FALSE;
END_IF
END_IF
//start oproznianie
IF xOproznianie THEN
//oproznianie zbiornik3
IF xZawor3out THEN
iZbiornik3:=iZbiornik3-1;
IF iZbiornik3=0 THEN
xZawor3out:=FALSE;
xZawor2out:=TRUE;
END_IF
END_IF
//oproznianie zbiornik2
IF xZawor2out THEN
iZbiornik2:=iZbiornik2-1;
IF iZbiornik2=0 THEN
xZawor2out:=FALSE;
xZawor1out:=TRUE;
END_IF
END_IF
//oproznianie zbiornik1
IF xZawor1out THEN
iZbiornik1:=iZbiornik1-1;
IF iZbiornik1=0 THEN
xZawor1out:=FALSE;
xOproznianie:=FALSE;
xInit:=TRUE;
END_IF
END_IF
//sygnalizacja lamp1
IF xZawor1out AND iZbiornik1>0 AND iZbiornik1<200 THEN
fbBlink(ENABLE:=TRUE, TIMELOW:=T#1S, TIMEHIGH:=T#1S);
xLamp1:=fbBlink.OUT;
ELSIF iZbiornik1=0 THEN
xLamp1:=FALSE;
ELSE
xLamp1:=TRUE;
END_IF
//sygnalizacja lamp2
IF xZawor2out AND iZbiornik2>0 AND iZbiornik2<200 THEN
fbBlink(ENABLE:=TRUE, TIMELOW:=T#1S, TIMEHIGH:=T#1S);
xLamp2:=fbBlink.OUT;
ELSIF iZbiornik2=0 THEN
xLamp2:=FALSE;
ELSE
xLamp2:=TRUE;
END_IF
//sygnalizacja lamp3
IF xZawor3out AND iZbiornik3>0 AND iZbiornik3<200 THEN
fbBlink(ENABLE:=TRUE, TIMELOW:=T#1S, TIMEHIGH:=T#1S);
xLamp3:=fbBlink.OUT;
ELSIF iZbiornik3=0 THEN
xLamp3:=FALSE;
ELSE
xLamp3:=TRUE;
END_IF
END_IF
//ilosc napelnien zbiornikow
fbLicznik1(CU:=xNapelnianie, RESET:=xSerwis);
//ilosc oproznien zbiornikow
fbLicznik2(CU:=xOproznianie, RESET:=xSerwis);
Kod programu w języku LD
PROGRAM Zbiorniki_LD
VAR
xZawor1in: BOOL;
iZbiornik1: INT;
xZawor2in: BOOL;
iZbiornik2: INT;
xZawor3in: BOOL;
iZbiornik3: INT;
xNapelnianie: BOOL;
xOproznianie: BOOL;
xZawor3out: BOOL;
xZawor2out: BOOL;
xZawor1out: BOOL;
xLamp1 : BOOL;
xLamp2 : BOOL;
xLamp3 : BOOL;
fbBlink : BLINK;
xInit: BOOL:=TRUE;
fbLicznik1 : CTU;
fbLicznik2 : CTU;
xSerwis : BOOL;
xPom1_1, xPom1_2, xPom1_3: BOOL;
xPom2_1, xPom2_2, xPom2_3: BOOL;
END_VAR