Dobre praktyki programowania sterowników PLC

Jest wiele środowisk programowania, a co za tym idzie? Jest wiele metod na ułatwienie programistom życia, ale także kolejnym specjalistom, którzy będą “maczać” palce w programie.

W tym temacie chciałbym abyśmy zebrali wskazówki, dzięki którym nasze programy będą lepsze.

Na przykład:

  • Ogólne porady i tricki, które można zastosować niezależnie od oprogramowania.
  • Tip&Tricks w konkretnym oprogramowaniu.
  • Sposoby podpatrzone u innych. Na przykład programy producentów maszyn niemieckich :sunglasses:

Na początek mogę zacytować kawałek z artykułu. Dobra praktyka to ścisłe powiązania nazewnictwa pomiędzy dokumentacją, a programami PLC i HMI (np. w nazwie alarmu jest tag + symbol w dokumentacji). To na prawdę ułatwia życie na Utrzymaniu Ruchu.

Poczynając od komunikacji pomiędzy projektantem kreślącym schemat po osobę składającą rozdzielnicę. Na tym etapie należy wyłapać jak najwięcej błędów w dokumentacji i poprawienie ich. Dobrym zwyczajem jest zachowanie standardów jakie ma zwierać dobra dokumentacja i trzymanie się tego przy każdej kolejnej realizacji. Opisy, schematy elektryczne i DTR (Dokumentacja Techniczno Ruchowa) powinny być przygotowane szczegółowo, starannie i zgodnie z obowiązującymi normami. Najważniejsze to zachowanie spójności pomiędzy schematem, instalacją elektryczną a najlepiej jeszcze z programem PLC w następującym wzorze:
oznaczenie w instalacji = oznaczenie w dokumentacji = symbol/tag w PLC i alarmach HMI


Edit: Niedawno pojawił się ciekawy artykuł w ramach pracy konkursowej na iAutomatyka.pl

1lajk

Podstawowa zasaday w programowaniu nie ważne czego czy to PLC czy IT to DRY ( Don’t Repeat Yourself) niestosowanie się do tego robi masakryczny syf w kodzie. Lepiej napisać raz dobrego FB’ka czy FC’ka i użyć go sto razy niż klepać do wszystkiego nowe funkcje. A funkcjonalności konfigurować na poziomie parametrów wejściowych. Drugą kwestią jest jeden TAG/Zmienna jedna cewka (no chyba, że mamy przerzutnik SR RS wtedy wiadomo jedna cewka SET jedna RESET). Z takich rzeczy ktore na szybko przeychodzą mi do głowy to pisząc kod najlepiej nie przypisywać wartości bezpośrednio do tagów tylko wysterowywać je z poziomu parametrów wyjściowych. Zaletą takiego rozwiązania jest to, że kod jest bardziej elastyczny i przy “Cross refernce” mamy w jednym miejscu tego co szukamy.

2lajki

Nie za bardzo rozumiem co masz na myśli. Jeden tag jedna cewka?

Niezależnie od platformy na jakiej pracujemy, ważne jest żeby opracować schemat nazewnictwa który będzie coś mówił o zmiennej, funkcji, każdej osobie która otworzy program.
Przy tworzeniu programu należy stworzyć sobie legendę struktury nazewnictwa. Pracowałem jako kontraktor dla dużego międzynarodowego koncernu, na dzień dobry dostałem 55 stronicowy przewodnik, jak tworzyć programy, jak je nazywać, jak nazywać zmienne plus opis całej struktury programu jak wyglądać program, jakie ma zawierać podprogramy łąnie z ich nazewnictwem. Główny wątkiem to były przedrostki które określały typ zmiennej - Ao_, Ai_ [analog out/in], C_ [zmienna globalna kontrolerowa], W_ [warninng], A_ [alarm] itd. Doskonałym pomysłem jest, jeśli zmienna jest przypisana do fizycznego wyjścia/wejścia, żeby zawierał symbol ze schematu elektrycznego - ułatwia to lokalizację i w programie i w szafie/maszynie.
Ważne jest opracowanie standardu i trzymanie się go.

2lajki

Jeśli integrujemy kilka urządzeń to oczywiście to samo nazewnictwo. Tworzę po stronie plc zmienną i przekazuje do robota. Po stronie robota ta sama nazwa. Jeśli przekazuje przykładowo do systemu wizyjnego czy innego plc to tak samo nazwa zostaje. Zmienia sie tylko przedrostek jak już wyżej wspomniał @zalewski.jacek.waw

1lajk

Dysponujesz tym przewodnikiem jeszcze. Chetnie bym go obejrzał.

3lajki

Żeby nie używać wielu cewek do jednej zmiennej/tagu, bo suma summarum zadziała tylko ta ostatnio wywołana a robi zmyłkę przeciwniki. A już w ogóle robi się miszmasz jak używasz przeżutnika SR wywołanego parę razy względem jednej zmiennej.

1lajk

Długie nazwy mają ten plus, że są wygodne jeśli chodzi o wyszukiwanie, ale są conajmniej 2 minusy:

  • jeśli przesadzimy z długością to później możemy mieć problem jeśli przykładowo do aplikacji zostanie dołożona scada - słyszałem taką historię, że gościu zrobił sobie bardzo długie opisowe zmienne, a gdy pojawiła się scada nie widziała ich i musiał wszystko skracać
  • zmienne z notacją węgierską, PascalCase lub camelCase są jednak mniej czytelne.
    Od siebie mogę dodać, że polecam stosowanie komentarzy zmiennych - są często pomijane, a stanowią świetny sposób na dokumentowanie projektu. Moim zdaniem zmienne z dobrymi komentarzami odejmują sporo komentowania w networkach / kodzie.
    Odnośnie PascalCase / CamelCase - często ludzie zastanawiają się której opcji do czego używać. Wydane mi się naturalna używanie: „zmiennaTemporary” do zmiennych które nie pamiętają swojego stanu między cyklami.

Chciałbym również zapytać jakich ogólnie znanych standardów programowania się trzymacie? Czy stosujecie zalecenia PLC Open, a jeśli tak to jakie wg Was są najważniejsze?

Co do długości nazw to zawsze staram się tworzyć takie nazwy zmiennych aby dało się zrozumieć jej funkcjonalność bez opisów - np. czysty upload programu z CPU, program bez komentarzy. Dobrą praktyką jest tworzenie opisów nie tylko zmiennych ale też części programu:


STEROWANIE RĘCZNE


oraz komentarze poszczególnych linijek kodu, drabinki czy opisu bloków funkcyjnych


WARUNEK URUCHOMIENIA NAPĘDU


Wygląda to dość topornie ale siadając pierwszy raz do takie programu nie tracisz czasu na analizę co autor miał na myśli.
Oczywiście długość nazw zmiennych jest determinowana sprzętem - w nowych wersjach mamy praktycznie dowolność w długości nazwa w starych tak dobrze nie było i komentarze ratowały sytuację.
Jeśli chodzi o format to zdecydowanie PascalCase. Co do stosowanych norm to zalecenia klienta stanowią główną wytyczną jak pisać.

A odnośnie komentowania projektu - zmiennych oraz kodu:
Moim zdaniem główna zasada jakiej powinniśmy się zawsze trzymać to komentowanie na bieżąco. Odkładanie pisania komentarzy na później skutkuje tym, że albo się ich nie napisze albo będą dublować to, co znajduje się w kodzie.
Moim zdaniem komentarze powinny zawierać to, co programista miał na myśli pisząc daną linijkę kodu - co chciał dzięki niej osiągnąć, jakie ma zastosowanie i jaką funkcję pełni w programie, a nie co w danej linijce się znajduje.

4lajki

Ja również,chętnie zobaczę :slight_smile:

Przy zmianach programu stosuje marker oznaczony swoim imieniem przez co woem gdzie co ja zmienialem , usuwalem.

Dobra praktyka jest mapowanie IO w jednym miejscu podpinamy IO pod zmienne w efekcie pozniej jak mamy kolejny taki sam projekt to program jest ok tylkp przepinamy IO np w jednym FC

1lajk

Nie wymienione wyżej, a warte uwagi jest:

  • stworzenie osobnych zmiennych do wymiany danych z HMI

Dzięki temu mamy możliwość zmian w PLC nie wpływających na komunikację z HMI, oszczędza to klepanie tego samego w dwóch miejscach :slight_smile:

2lajki

Jeśli tworzymy osobne zmienne to jest to dublowanie, więc nie rozumiem jak dzięki temu możemy oszczędzić “klepanie tego samego w dwóch miejscach” :wink:
Zawsze mam kłopot z wymianą danych między HMI/SCADA, bo wpływa na to dużo czynników:

  • zmienne symboliczne (w Siemensie zoptymalizowane) czy stara szkoła i odnoszenie się do adresu zmiennej - część paneli kiepsko radzi sobie z adresowaniem symbolicznym,
  • jeśli zmienne symboliczne to nazwa musi być krótka, bo panele HMI łatwiej radzą sobie z takimi,
  • część połączeń HMI przy adresowaniu symbolicznym lepiej radzi sobie ze strukturami ( czyt. np. Rockwell), a inna część nie obsługuje struktur,
  • tak jak kolega powyżej poruszył temat: lepiej komunikację z HMI wydzielać na osobne zmienne i osobne miejsce w programie czy obsługiwać z resztą kodu obsługi urządzeń i algorytmu automatyki? Jeśli osobno to jest to uciążliwe, ale ładniejsze. Jeśli razem to prostsze, ale mniej czytelne i komunikacja z HMI ginie w reszcie kodu.
    Ogólnie podsumowując moim zdaniem prze… :grinning:

Wystarczy stworzyć osobny blok kodu (FC/Routine/POU) w którym zawarte jest mapowanie pomiędzy zmiennymi wykorzystanymi w programie, a zmiennymi dla HMI.
Dlaczego nam to oszczędza roboty?
Wiadomo jak jest ze zmianami i przykładowo: pozycja jest sygnalizowana przez styki typu NO to zmiana tego wymagana jest na wizualizacji i w programie, lecz jeżeli jest to rozdzielone to logika zmienia się tylko w programie i wizualizacja tego nie odczuwa

1lajk

Tak jak pisałem jeśli zrobimy to w osobnym bloku kodu to zajmuje to więcej pracy. Jeśli chcemy, żeby ktoś się w tym odnalazł to wypadałoby odwzorować szkielet programu jaki mamy w algorytmie sterowania automatycznego w tym osobnym bloku dla HMI - tak, żeby zachować czytelność programu, więc może powodować to niepotrzebne rozbudowanie programu.
Obie opcje mają wady i zalety. Moim zdaniem trzeba wybrać 1 z nich, konsekwentnie się trzymać i dbać o czytelność programu.
Poza tym z logiką dla HMI na sterowniku jest taki problem, że gdy sterownik nie pracuje - przykładowo po załączeniu maszyny, a HMI szybciej się odpali to panel pokazuje nam głupoty i źle to wygląda.
Moim zdaniem zawsze warto brać pod uwagę wartość domyślną, żeby później nie mieć przykładowo niepotrzebnych alarmów na HMI w buforze historycznym.

Owszem zajmuje więcej pracy, ale przy pierwszym podejściu, zmiany są łatwiejsze i tu nadrabiamy poprzedni naddatek.
Pamiętać też trzeba, że kod więcej czytamy niż piszemy - dlatego powinien być czytelny i prosty ponad optymalizacje

1lajk

A co najważniejsze wystarczy zrobić jednego snapshota zeby uchronić się przed reincjalizacja. Dawanie zmiennych które sa sterowane z panelu HMI do osobnego DB jest jak najlepsza praktyka. Kto dużo pracował z SIemensem to na pewno potwierdzi

3lajki

Pytanie bardziej konkretne:
Jak radzicie sobie z danymi IO w Siemensie S7-1200/S7-1500?
Ja dane trzymam tylko w DB. Markerów używam przy 2 okazjach: system and clock memory bits oraz dane IO. No i właśnie z danymi IO jest taki problem, że nie mogę ich w 100% ładnie uporządkować. Można zrobić tablice tag table - to pozwala na wygodne przeglądanie. Jednak jak chcę się odwołać w programie do konkretnej zmiennej to do tej pory miałem problem. Od pewnego czasu stosuję UDT jako typ danych zmiennych IO. Przykładowo jak komunikuję się z falownikiem to dzięki temu opisuję sobie poszczególne bity Status Word oraz Control Word, a jeśli zadana prędkość znajduje się na 2 wordach to mogę ją połączyć np. w 1 zmienną typu Dint. Podobnie robię ze zmiennymi IO modułów na sterowników oraz rozproszonych wejść/wyjść. Jeśli zmienne są dobrze zaplanowane na etapie projektu elektryki to fajnie to działa. Zamiast długich/dziwnych nazw zmiennych można stosować “miejsce na maszynie.sygnał”
Ktoś jeszcze tak robi? Są jakieś wady tej praktyki z których mogę sobie nie zdawać sprawy? :stuck_out_tongue: