Strona główna ASTOR
Automatyka w praktyce

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:

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.

Wybieranie symulatora i języka drabinkowego w Codesys. Źródło: ASTOR

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.

Dodanie nowych bibliotek. Źródło: ASTOR

Krok 3: Wprowadzamy kod programu. Zmienne w oknie zmiennych, kod w edytorze programu.

Ekran środowiska Codesys (kliknij, aby powiększyć). Źródło: ASTOR

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.

Pole komunikatów Codesys. Źródło: ASTOR

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.

Dodawanie ekranu wizualizacji. Źródło: ASTOR

Od tego momentu pojawiły nam się nowe zakładki drzewka projektowego:

Widok drzewka projektowego. Źródło: ASTOR

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.

HMI dopasowane do wielkości i położenia urządzenia. Źródło: ASTOR

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.

Okno właściwości ekranu. Źródło: ASTOR

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

Okno właściwości WebVisu. Źródło: ASTOR

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:

Widok podczas edycji wizualizacji (kliknij, aby powiększyć). Źródło: ASTOR

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:

Zmiana tła. Źródło: ASTOR

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:

Tworzenie zbiornika i przypisanie zmiennej (kliknij, aby powiększyć). Źródło: ASTOR

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.

Okno właściwości obiektu. Źródło: ASTOR

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:

Dodawanie aktualnego stanu sterownika (kliknij, aby powiększyć). Źródło: ASTOR

Dodamy też lampkę, która będzie nam sygnalizować napełnianie/opróżnianie zbiornika poprzez przypisanie zmiennej xLamp1. Dodatkowo zmienię jej kolor na zielony:

Dodawanie lampki pracy zbiornika (kliknij, aby powiększyć). Źródło: ASTOR

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).

Wizualizacja pracy programu (kliknij, aby powiększyć). Źródło: ASTOR

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:

Dodanie obiektu zarządzającego alarmami. Źródło: ASTOR

Automatycznie w drzewku projektowym pojawia się domyślna konfiguracja z klasami alarmów oraz opcją dla pamięci alarmów.

Alarmy w drzewku projektowym. Źródło: ASTOR

Następnie dodajemy nową grupę alarmów, nazwijmy ją Alarm_Zbiorniki:

Dodawanie nowej grupy alarmów. Źródło: ASTOR

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

Okno właściwości grupy alarmu (kliknij, aby powiększyć). Źródło: ASTOR

Aby wyświetlić stan alarmów, musimy stworzyć nowy ekran, na którym zostanie przedstawiona tabela alarmów:

Dodawanie tabeli powiadomień alarmu (kliknij, aby powiększyć). Źródło: ASTOR

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.

Tabela komunikatów alarmu. Źródło: ASTOR

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

//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

Newsletter Poradnika Automatyka

Czytaj trendy i inspiracje, podstawy automatyki, automatykę w praktyce

Please wait...

Dziękujemy za zapis do newslettera!

Czy ten artykuł był dla Ciebie przydatny?

Średnia ocena artykułu: 3.7 / 5. Ilość ocen: 7

Ten artykuł nie był jeszcze oceniony.

Zadaj pytanie

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *