ST znamená Structured Text. U Siemense se jmenuje SCL jako Structured Code Language. Vychází z pascalu, liší se od něj jiným způsobem zakončování příkazů (nepoužívá se slovo Begin a místo univerzálního End má každý příkaz svůj speciální End_něco), céčkovským stylem středníků, jiným systémem předávání parametrů podprogramům a odstraněním celé řady syntaktických nepravidelností a složitostí. Definuje ho norma IEC 61131-3. V praxi se používá překvapivě málo. Jednak proto, že první generace programátorů začínala jako elektrikáři a vyrostla na grafických jazycích simulujících elektrická nebo logická schémata či vývojové diagramy (LAD, FBD, SFC), a jednak proto, že výrobci vývojových prostředí ještě pro ST nevytvořili žádné opravdu přehledné a názorné online zobrazení stavu programu za chodu stroje (vrcholem techniky je výpis aktuálních hodnot proměnných, ale třeba barevné odlišení aktivních a neaktivních částí zdrojáku jsem ještě neviděl). (psáno 2019, vývoj už možná trochu pokročil)
Hlavní výhodou ST je, že v něm nic není problém. Věci, které byste v LADu pracně obcházeli opičárnami přes celou obrazovku, jsou tady trivialita na pár řádků. Místo desítek grafických "spínačů", "cívek" a speciálních bloků stačí znát jenom šest příkazů a umět je vhodně kombinovat. Zdroják můžete normálně psát, nemusíte myší tahat symboly z knihoven. Přesouvání, kopírování a mazání delších úseků nepřipomíná klikací adventuru. Nepotřebné, nedodělané nebo nefunkční úseky programu stačí zakomentovat, nemusíte je před ukládáním mazat nebo uměle doklepávat do nesmyslné, ale zkompilovatelné podoby. Textové zdrojáky si můžete kopírovat do textových souborů nebo z jednoho vývojového prostředí do úplně jiného, grafické byste museli ručně překreslovat. Celkově ušetříte asi tak dvě třetiny času na programování (nepřeháním, vyzkoušel jsem na vlastní kůži). A také velkou část výpočetního výkonu PLC, protože nepoužité větve ifů a jiných podmínek se přeskakují, zatímco prvky grafického programu se musí projít a vyhodnotit všechny.
Slouží k psaní poznámek do zdrojového kódu nebo k deaktivaci částí kódu, které nechceme kompilovat ani spouštět.
(*Tohle je komentář. Může pokračovat přes libovolný počet řádků. Standardní, použitelný ve vývojových prostředích všech výrobců.*) //Tohle je taky komentář, končí na konci řádku. //Nestandardní, funguje jenom u některých výrobců (např. Siemens).
Jména proměnných, podprogramů atd.. Můžou se skládat z písmen, číslic a podtržítek (A..Z, a..z, 0..9, _) a nesmí začínat číslicí. Některá vývojová prostředí dovolují i jiné znaky (např. diakritiku nebo mezery), ale v zájmu přenositelnosti kódu doporučuji se jim vyhýbat. Když někde v následujícím textu uvidíte identifikátor s háčky a čárkami, tak je to jenom zástupný symbol, ne použitelný příklad. Na velikosti písmen nezáleží.
kam := co;
Do proměnné na levé straně přiřazovacího operátoru zkopíruje hodnotu výrazu na pravé straně. Lze přiřazovat libovolné datové typy od nejjednodušších boolů po složité struktury, jedinou podmínkou je stejný nebo aspoň kompatibilní datový typ (kdyby to nešlo, použijte systémové konverzní funkce něco_TO_něco). Operátor se píše dohromady, mezi ":" a "=" nedávejte žádné mezery. Příklady:
cislo:=cislo+1; x1 := (-b + SQRT(b*b-4*a*c)) / (2*a); q2:=(i1 OR i2) AND i3; q3:=true; cislo_typu_dint := WORD_TO_DINT(cislo_typu_word);
Větvení podle logické podmínky typu ano/ne. Možné varianty:
IF podmínka THEN příkazy; END_IF;
Podmínka je jakýkoli logický výraz, tj. něco, co dává výsledek typu BOOL (podrobněji to probereme dále v odstavci Datové typy). Je-li splněna, provedou se dané příkazy, jinak se neprovede nic. Příkazů mezi THEN a END_IF může být libovolný počet.
IF podmínka THEN příkazy1; ELSE příkazy2; END_IF;
Je-li splněna podmínka, provedou se příkazy1, jinak se provedou příkazy2.
IF podmínka1 THEN příkazy1; ELSIF podmínka2 THEN příkazy2; ELSE příkazy3; END_IF;
Je-li splněna podmínka1, provedou se příkazy1. Když ne, ale je splněna podmínka2, provedou se příkazy2. Když není splněna ani ta, provedou se příkazy3. ELSIF má stejný efekt jako kdyby se napsalo ELSE IF, jediný bonus je v tom, že se celá konstrukce uzavře jedním end_ifem, místo aby se psaly end_ify pro každý if samostatně. Elsifů může být mezi jedním ifem a end_ifem libovolný počet, else jedno nebo žádné. Příklady:
IF leve_tlacitko_stisknuto AND prave_tlacitko_stisknuto THEN lis_nahoru:=false; lis_dolu:=true; krok_cyklu:=20; END_IF; IF něco THEN proměnná:=true; END_IF; (*ekvivalent cívky (S) v jazyce LAD*) IF něco THEN proměnná:=false; END_IF; (*ekvivalent cívky (R)*) proměnná:=něco; (*ekvivalent cívky ( ) je prosté přiřazení*) IF NOT cidlo1sviti THEN chyba:=1; ELSIF cidlo2sviti OR chyba_motoru THEN chyba:=2; napajeni_motoru:=false; ELSE chyba:=0; krok:=30; END_IF;
Větvení do více variant podle číselné podmínky. Typická aplikace: členění pracovních cyklů stroje do jednotlivých kroků.
CASE číslo OF hodnota1:příkazy1; hodnota2:příkazy2; END_CASE;
Číslo je celočíselný výraz, tj. cokoli (konstanta, proměnná nebo složitější výraz), co dá výsledek typu INT, UINT, SINT, USINT, DINT, UDINT apod. (ne REAL). Hodnoty jsou celočíselné konstanty nebo složitější výrazy, ale složené výhradně z konstant (tedy ne proměnné). Na pořadí nezáleží, nemusí být seřazené podle velikosti nebo něco takového. Počet hodnot v jednom casu je libovolný, počet příkazů u každé hodnoty taky. Provede se sada příkazů za tou hodnotou, která odpovídá danému číslu, všechny ostatní se přeskočí (i kdyby součástí provedených příkazů bylo přepsání čísla na hodnotu odpovídající některému jinému řádku). Pokud neodpovídá žádná hodnota, neprovede se nic.
CASE číslo OF hodnota1:příkazy1; hodnota2:příkazy2; ELSE příkazy3; END_CASE;
Odpovídá-li některá hodnota danému číselnému výrazu, provedou se příkazy za ní a všechno ostatní včetně příkazů za ELSE se přeskočí. Neodpovídá-li žádná hodnota, provedou se příkazy za ELSE.
CASE číslo OF hodnota1,hodnota2,hodnota3:příkazy1; hodnota4..hodnota5:příkazy2; hodnota6..hodnota7,hodnota8:příkazy3; END_CASE;
Hodnot u jednoho řádku může být víc než jedna. Buď se vypíšou všechny možnosti oddělené čárkami, nebo se použije operátor "až" (..) pro určení rozsahu od - do. Dá se to libovolně kombinovat.
Příklad:
CASE krok OF 0:kontrolka_start:=Clock_1Hz; IF stisknuto_tlacitko_start THEN kontrolka_start:=false; krok:=10; END_IF; 10:lis_nahoru:=false; lis_dolu:=true; IF NOT lis_nahore AND lis_dole THEN krok:=20; END_IF; 30:kontrolka_zalisovani:=true; krok:=40; 40: ...atd... ... ELSE chyba:=100; (*chyba programu: špatné číslo kroku*) END_CASE;
Pozn.: CASE se vždycky dá nahradit konstrukcí IF...ELSIF...ELSIF...END_IF. Rozdíl je jenom v přehlednosti a v množství písmenek: "10:" je kratší než "ELSIF krok=10 THEN".
Cyklus, který se opakuje tak dlouho, dokud není splněna podmínka. Cyklení probíhá v rámci jednoho scanu, takže pozor, aby netrvalo moc dlouho - PLC by se zaseklo.
REPEAT příkazy; UNTIL podmínka END_REPEAT;
Podmínka je na konci cyklu, příkazy se tedy vždycky alespoň jednou provedou, i kdyby byla splněná hned od začátku. Repeat je vhodný pro různé zpracovávání dat (třídění apod.), kde nějakou operaci děláme jednou nebo případně na několik dalších pokusů. Časové prodlevy nebo čekání na stisk tlačítka se musí řešit jinak, protože běží dlouhodobě přes více scanů.
Cyklus, který se opakuje tak dlouho, dokud podmínka platí. Opět vše v rámci jednoho scanu.
WHILE podmínka DO příkazy; END_WHILE;
Podmínka je na začátku, takže jestli hned od začátku neplatí, neprovedou se příkazy v cyklu ani jednou. Vhodné pro operace, kdy předem nevíme, jestli nějaký úkon vůbec potřebujeme a pokud ano, tak kolikrát (třeba zkracování řetězce znak po znaku na danou délku). Opět nepoužitelné pro čekání na nějakou událost.
Cyklus, který proběhne přesně tolikrát, kolikrát určíme. Opět vše v rámci jednoho scanu.
FOR řídicí_proměnná:=počáteční_hodnota TO koncová_hodnota DO příkazy; END_FOR;
Řídicí proměnná je celočíselná proměnná (INT, UINT, SINT apod.). Pozor, že někdy bývá omezeno, kde může být definována (např. nesmí být deklarována v jiném funčním bloku než v tom, kde se nachází ten cyklus) a jakého může být typu (např. u Siemense výhradně INT) - záleží na konkrétním PLC. Počáteční a koncová hodnota jsou libovolné celočíselné výrazy.
Začíná se s řídicí proměnnou rovnou počáteční hodnotě. V každé další iteraci se automaticky zvyšuje o 1, poslední iterace proběhne na koncové hodnotě. Je-li konec menší než počátek, cyklus neproběhne ani jednou. Řídicí proměnnou můžeme uvnitř cyklu číst (je v ní vždy hodnota odpovídající aktuální iteraci), ale nesmíme ji přepisovat.
Potřebujeme-li řídicí proměnnou posouvat jinak než o 1 nahoru, zadáme krok pomocí slova BY:
FOR řídicí_proměnná:=počáteční_hodnota TO koncová_hodnota BY krok DO příkazy; END_FOR;
Krok je celočíselná konstanta. Může být i záporný, v takovém případě se jede sestupně od větší počáteční hodnoty do menší koncové (používá se vždycky slovo TO, pascalské DOWNTO v ST neexistuje).
Typická aplikace foru je práce s poli: řídicí proměnná představuje index a projíždíme jednu položku po druhé. Příklad: dejme tomu, že máme nadeklarováno tohle:
VAR pole:ARRAY[1..100] OF INT; index:INT; END_VAR;
Potom si můžeme hrát třeba takhle:
(*vynulování všech prvků pole:*) FOR index:=1 TO 100 DO pole[index]:=0; END_FOR; (*vyplnění pole čísly od 100 na první pozici do 1 na sté:*) FOR index:=1 TO 100 DO pole[index]:=101-index; END_FOR; (*posun všech prvků pole o jednu pozici doprava:*) FOR index:=100 TO 2 BY -1 DO pole[index]:=pole[index-1]; END_FOR; pole[1]:=0; (*opačný posun - doleva:*) FOR index:=1 TO 99 DO pole[index]:=pole[index+1]; END_FOR; pole[100]:=0;
Podprogramů jsou tři druhy: programy, funkce a funkční bloky.
Bloky kódu bez parametrů. Existují vždy jenom v jedné instanci. Standardně s vlastní statickou pamětí pro lokální proměnné (VAR), u Siemense bez (ten jim navíc neříká programy, ale organizační bloky - OB). Dají se volat buď pomocí systémových událostí (hlavní pracovní cyklus (scan), chyba, přerušení atd., možnosti se liší podle výrobce PLC) nebo z jiných programů:
muj_program;
Siemens vyžaduje za jménem OB prázdnou závorku:
muj_program();
V každém PLC musí existovat minimálně jeden hlavní program, který operační systém automaticky v každém scanu zavolá. Záleží jenom na vás, jestli všechen užitečný kód nacpete přímo do něj, nebo si ho rozdělíte do několika podprogramů, které z toho hlavního budete volat.
Bloky kódu bez statické paměti, lokální proměnné existují jenom dočasně v rámci jednoho volání funkce (VAR_TEMP). Dají se volat odkudkoli. Můžou (ale nemusí) mít vstupní parametry (VAR_INPUT) a návratovou hodnotu. Bez návratové hodnoty (když je Return value typu VOID) se volají jako příkaz, stejně jako programy:
moje_funkce(parametry);
Závorka za jménem funkce je nutná vždy, i kdyby funkce neměla žádné parametry. S návratovou hodnotou (Return value má určený datový typ) se funkce volají jako výraz:
proměnná:=moje_funkce(parametry);
Z funkcí se dají skládat i složitější výrazy, stejně jako z konstant nebo proměnných.
Parametry je při volání potřeba vypsat jménem a přiřazovacím operátorem jim přiřadit hodnoty:
moje_funkce(parametr1:=hodnota1, parametr2:=hodnota2);
Hodnotou předávanou do vstupních parametrů může být proměnná, konstanta nebo jakýkoli výraz, ze kterého vypadne výsledek odpovídajícího datového typu. Parametry funkce musíme vždy uvést všechny, žádný nesmí chybět. Na pořadí parametrů nezáleží, rozlišují se jménem. Vývojové prostředí Codesys dovoluje i zkrácený zápis bez jmen parametrů (stejně jako to funguje třeba v pascalu), v takovém případě se pořadí musí dodržet:
moje_funkce(hodnota1, hodnota2);
Kromě funkcí, které si napíšete sami, existuje i celá řada předdefinovaných systémových.
Bloky kódu s vlastní statickou pamětí (VAR_cokoli). Každý FB může existovat v libovolném počtu instancí. Instance se deklaruje úplně stejně jako jakákoli jiná proměnná, jako typ se jí uvede jméno příslušného funkčního bloku. Jestli jste zvyklí na objektové programování, můžete si funkční blok představit jako takovou hodně jednoduchou třídu s libovolným počtem atributů a jednou metodou. Poznámka ke speciální siemensí terminologii: když je instance FB uložená v prostoru pro globální proměnné a datové bloky, říká se tomu "single instance", když je uložená lokálně v paměťovém prostoru uvnitř jiných bloků, je to "multiple instance". Funkční blok se volá jako příkaz, napsáním jména instance (jde to odkudkoli, odkud na tu instanci vidíme):
instance_bloku(parametry);
Závorka je u Siemense povinná vždy, v Codesysu jenom když zrovna nějaké parametry předáváme.
Vstupní parametry (VAR_INPUT) předáváme podobně jako u funkcí (jenom zkrácený zápis bez jmen parametrů tentokrát nefunguje):
instance_bloku(parametr:=hodnota);
Máme-li u některých vstupních parametrů nadefinované počáteční hodnoty (Default values), můžeme je při volání bloku vynechat a tím tyto hodnoty implicitně dostanou. Vstupní parametry se přepisují při každém zavolání FB, nezůstávají uložené v paměti bloku a nejsou přístupné zvenku.
Výstupní parametry (VAR_OUTPUT) se předávají speciálním operátorem "=>":
instance_bloku(parametr1=>proměnná1, parametr2=>proměnná2);
Data tentokrát tečou zleva doprava: z parametru do zadané proměnné. Proto to taky musí být vždycky proměnná, ne konstanta nebo výraz. Výstupní parametr je fyzicky vzato statická proměnná sídlící v instanci funkčního bloku, svoji posledně nastavenou hodnotu udrží libovolně dlouho. Není povinné vypisovat do závorky všechny výstupní parametry - když je nepřečteme hned v okamžiku volání bloku, nevadí, můžeme se na ně podívat kdykoli jindy:
proměnná:=instance_bloku.jméno_výstupního_parametru;
Nesmíme do nich ale zvenčí zapisovat.
Vstupně/výstupní parametry (VAR_IN_OUT) jsou takový kříženec předchozích dvou typů. Stejně jako výstupní parametry existují v paměti instance a udržují si posledně nastavenou hodnotu. Zapisují se stejným operátorem jako vstupní, ale napojit se na ně dá jedině proměnná, ne konstanta:
instance_bloku(parametr:=proměnná);
Není povinné vypsat všechny, ty vynechané budou mít stejnou hodnotu, jaká v nich zůstala od minula. Když už nějaký parametr použijeme, zkopíruje se do něj na začátku volání FB hodnota připojené proměnné a na konci se do ní zkopíruje jeho výsledná hodnota. S I/O parametry si z míst mimo blok můžeme dělat co chceme, číst i zapisovat:
proměnná:=instance_bloku.jméno_io_parametru; instance_bloku.jméno_io_parametru:=hodnota;
V některých vývojových prostředích (např. TIA Portal) můžeme obdobným způsobem odkudkoli přímo přistupovat ke všem interním proměnným programů a funkčních bloků (VAR) a libovolně je číst nebo přepisovat. Technicky je to v pořádku, z hlediska přehlednosti kódu je ale lepší veškeré zvenku používané proměnné deklarovat jako parametry, aby se to nepletlo.
K čemu jsou funkční bloky dobré? Typický příklad jsou třeba časovače (systémové bloky TON, TOFF, TP a další), které si ve svojí paměti udržují údaj o uběhlém čase, přes vstupní parametry dostávají povely a výstupní parametr rozsvěcí nebo zhasínají, když uběhne zadaný čas. Typická aplikace vlastních FB je třeba řízení několika stejných dopravníků nebo robotů (mají stejný pracovní cyklus, ale fungují nezávisle a přes parametry se jim předávají signály z jiných fyzických čidel a motorů), detekce zaseknutí pneuválců (blok, který na vstupech dostane informaci o poloze ventilu a signály z koncových čidel, uvnitř používá časovač a na výstupu hlásí, že válec nějak podezřele dlouho nedojel do požadované polohy) nebo komunikace s jinými zařízeními.
Pokud se náhodou setkáte s vývojovým prostředím, které vás nechá všechno psát volným textem (vzpomínám si jenom na starý Simotion Scout), vypadala by kompletní definice funkčního bloku nějak takhle:
FUNCTION_BLOCK jméno; VAR_INPUT ...deklarace vstupních parametrů... END_VAR; VAR_OUTPUT ...deklarace výstupních parametrů... END_VAR; VAR_IN_OUT ...deklarace obousměrných parametrů... END_VAR; VAR ...deklarace lokálních statických proměnných... END_VAR; VAR_TEMP ...deklarace lokálních proměnných použitelných jenom po dobu jednoho volání bloku... END_VAR; BEGIN příkazy; END_FUNCTION_BLOCK;
Jak vidíte, parametry se nedeklarují v závorce za jménem bloku, jak je zvykem třeba v pascalu nebo céčku, ale v blocích VAR_něco...END_VAR, úplně stejně, jako kdyby šlo o klasické lokální proměnné. Za deklarační částí následuje slovo BEGIN (to je jediný případ, kdy se v ST používá), potom libovolný počet příkazů a nakonec ukončovací slovo END_to_samé_jako_na_začátku.
Program se definuje obdobně s klíčovým slovem PROGRAM, za hlavičkou může mít jenom VAR a VAR_TEMP, parametry žádné.
Funkce používá úvodní slovo FUNCTION, snese jenom VAR_INPUT a VAR_TEMP, v hlavičce potřebuje navíc deklaraci návratového typu:
FUNCION jméno_funkce:typ_návratové_hodnoty;
a někde v příkazové části je potřeba tu návratovou hodnotu vyplnit:
jméno_funkce:=nějaká_hodnota;
V praxi se volný zápis definic moc často nevidí, protože vývojová prostředí většinou používají různé průvodce a dialogová okna, takže místo vzpomínání, jak se píše které klíčové slovo, jenom vyplňujete kolonky a tabulky.
Kromě lokálních proměnných v podprogramech existují v PLC i globální proměnné. Ty se zapisují do úseku VAR_GLOBAL...END_VAR. Jsou vždy přístupné odkudkoli, v některých případech (Codesys + Designer studio) jsou to jediné, co se dá exportovat do grafického ovládacího panelu.
Dočasné proměnné (VAR_TEMP) a vstupní parametry (VAR_INPUT) existují jenom v rámci jednoho volání funkce nebo funkčního bloku, po jeho skončení zmizí. VAR_TEMP nedostávají žádné výchozí hodnoty, jejich obsah je před prvním použitím víceméně náhodný. Statické proměnné (VAR) a výstupní parametry (VAR_OUT a VAR_IN_OUT) vydrží libovolně dlouho a co si do nich při jednom zavolání bloku uložíte, to tam příště zase najdete. Při vypnutí PLC se ale zapomenou a při příštím zapnutí se nastaví na výchozí hodnoty (nuly, pokud si je nepředefinujete). Pro dlouhodobé ukládání dat (například uživatelské nastavení nebo různé statistiky a počítadla) slouží speciální odrůda globálních proměnných značky VAR_RETAIN, VAR_PERSISTENT nebo VAR_RETAIN_PERSISTENT. Jedny přežijí vypnutí PLC, druhé restart systému a ty třetí oboje - a právě ty jsou na tyhle věci vhodné. Životnost dat v nich je omezená jenom kapacitou zálohovacího kondenzátoru nebo baterie. Místo klíčových slov stačí v některých vývojových prostředích (třeba TIA portal) jenom zaškrtnout čtvereček "retain", případně je v některých starších PLC (např. Simatic S7-200) retentivní paměť umístěná na určité pevně dané absolutní adrese.
Mezi VAR a END_VAR píšeme deklarace úplně stejně jako v pascalu:
jméno1:typ1; jméno2,jméno3,jméno4:typ2;
Po zapnutí PLC se celá operační paměť nuluje, takže v číslech budeme mít nuly a v boolech false. Jinou výchozí hodnotu si nadefinujeme takhle (pascalisti a céčkaři, pozor: používá se přiřazovací operátor, ne rovnítko):
jméno:typ:=hodnota;
Umístění proměnné na absolutní adresu se dělá takhle:
jméno AT adresa:typ;
Absolutní adresování využijeme především pro přístup k fyzickým vstupům a výstupům PLC. Přímý zápis adresy se skládá z následujících znaků:
Příklady:
TlacitkoStart AT %I0.2:BOOL; (*digitální vstup*) CervenaKontrolka AT %Q1.0:BOOL; (*digitální výstup*) PolohaPojezdu AT %IW1:INT; (*16b analogový vstup*) FazeProgramu AT %MB19:USINT; (*8b číslo v běžné paměti*)
Digitálním vstupům a výstupům přímo odpovídají jednotlivé šroubky na krabičce PLC. Analogové vstupy a výstupy mají taky každý jednu svorku, ale interně mají víc bitů podle toho, jaké rozlišení má příslušný převodník. Absolutně adresovaná paměť M představuje v některých starších nebo jednodušších PLC jedinou možnost vytváření proměnných, ale jestli vaše vývojové prostředí zvládá jazyk ST, určitě umí s pamětí pracovat tak, abyste se o absolutní adresy proměnných nemuseli starat, pokud k tomu nemáte nějaký specifický důvod (třeba když se na určitých adresách nacházejí užitečné systémové věci, jako třeba časovač v Simatiku S7-1200).
Neboli to, co píšeme za dvojtečku při deklaraci proměnných. Typová kontrola je v ST poměrně silná (silnější než třeba v pascalu) a přetypovávání podle prováděné operace se většinou musí dělat ručně.
Může nabývat hodnot True (logická 1) nebo False (logická 0). Používá se na digitální vstupy a výstupy, do podmínek v příkazech IF, REPEAT a WHILE a obecně pro pamatování čehokoli ve stylu ano/ne. V paměti zabírá jeden bit a pokud se jich deklaruje několik za sebou, naskládají se do jednoho bytu (proto se při absolutním adresování musí používat dvě čísla: byte.bit). Všechny ostatní typy se zarovnávají na celé byty. S boolem se dají dělat logické (bitové) operace NOT, AND, OR a XOR.
Čísla o velikosti 8, 16 a 32 bitů (1, 2 a 4 byty). Považují se za bitová pole. Fungují na ně stejné operace jako na bool (aplikují se na odpovídající dvojice bitů jednotlivých operandů) a bitové posuny SHL a SHR. K aritmetickým operacím (sčítání, odčítání apod.) použít nejdou. Používají se např. k hromadnému přístupu k digitálním vstupům a výstupům nebo na ukládání většího množství boolů, když z nějakého důvodu nemůžeme použít pole (typicky třeba alarmy v HMI Simatic, ty nedovolují nic jiného než word).
16bitové celé číslo se znaménkem určené pro aritmetické výpočty +, -, *, / (celočíselné dělení) a MOD (zbytek po dělení) a do příkazů Case a For. Bitové operace na něj nefungují.
Různé variace na INT, se stejným polem působnosti. U jako Unsigned neboli bez znaménka, S jako Short (8 bitů) a D jako Double (32 bitů). Například USINT je vnitřně totéž co BYTE, liší se jenom tím, jaké operace na nich překladač dovolí.
Všechna celá čísla se dají zapisovat desítkově (např. 15), šestnáctkově neboli hexadecimálně (16#0F) a dvojkově neboli binárně (2#00001111). Syntaxe je jednoduchá: základ soustavy, pak křížek a nakonec to číslo.
Reálné číslo. Umí reálnou aritmetiku (+, -, *, /). V praxi se používá málokdy, protože PLC s okolními zařízeními komunikuje většinou binárně nebo celočíselně, ale může se hodit k výpočtům a předvádění čísel uživatelům. Zapisuje se desítkově, desetinná část se odděluje tečkou.
Jeden znak - písmeno, číslice, cokoli: 'A', 'ž', ' ' apod.. Praktické využití jsem snad ještě neviděl (PLC nemá klávesnici jako počítač a na všechno ostatní se lépe hodí stringy).
Řetězec znaků, zapisuje se po pascalsku: 'Bla bla', '' (prázdný řetězec o nulové délce), 'McDonald''s' (apostrofy v textu je potřeba zdvojit), 'c:\adresar\soubor.txt' (zpětná lomítka nic zvláštního nedělají), 'První řádek$RDruhý řádek' ($R je kód znaku CR (ASCII 13), který PLC obvykle používají na ukončování řádků). Na spojování řetězců neexistuje žádný operátor, je potřeba použít funkci Concat.
Jestli s řetězci plánujete nějaké divočiny jako třeba rozklad na byty a posílání po drátech mezi různými zařízeními, dejte si pozor, že se jejich interní implementace může lišit: například Festo CPX používá zakončování nulou, v Simatiku řetězce začínají údajem o délce. Taky pozor, že string není pole, takže nefunguje přímé indexování jednotlivých znaků přes hranaté závorky jako v pascalu - je potřeba použít systémové funkce na kopírování podřetězců (Left, Right, Middle, nebo jak se jmenují).
Časové hodnoty. Konstanty se zapisují stylem T#12h30min, T#1.5s, T#1500ms a podobně (obecně T#, pak číslo a nakonec jednotky, bez mezer a apostrofů nebo jiných znaků okolo). Používají se hlavně do časovačů (standardní funkční blok TON a podobné), které se využívají každou chvíli.
Datum. Ve zdrojáku se píše jako D#2021-01-20, při zadávání přes uživatelský ovládací panel může mít jiný formát (třeba 20.1.2021, když je v systému nastavená čeština).
Datum a čas. Zápis DT# a něco, přesně nevím, nikdy jsem to nepoužil.
Pole několika prvků stejného typu, v hranaté závorce je uveden první a poslední index (třeba 1..10). Hlavní přínos polí je v tom, že za index můžete dosadit proměnnou a tím se dynamicky odkázat na libovolný prvek (snad s jedinou výjimkou Simatiku S7-300 s vývojovým prostředím Step7 5.5, který indexovat proměnnou neumí).
Datová struktura odpovídající céčkovskému typu struct nebo pascalskému record. Místo tří teček si dosaďte deklaraci libovolného množství položek (proměnných) libovolných typů. Jestli se nemýlím, strukturu není možné takhle vypsat rovnou při deklaraci proměnné, musíte si ji napřed nadefinovat jako uživatelský datový typ (viz dále). K jednotlivým položkám struktury přistupujeme přes tečku:
proměnná_typu_struct.položka:=nějaká_hodnota; nějaká_proměnná:=proměnná_typu_struct.položka;
Příkaz With, jak ho známe z pascalu, v ST neexistuje. Přiřazovacím operátorem jdou zkopírovat i celé struktury najednou, pokud jsou obě stejného typu.
Struktury používám hlavně jako popis vnitřního uspořádání datových bloků pro komunikaci s externími zařízeními (např. rozhraní Profinet aj.) nebo v kombinaci s polem (array of struct) pro ukládání dat ve formě tabulek, jiné smysluplné využití mě nenapadá.
Většina vývojových prostředí je nepoužívá a nevím, jestli je norma vůbec nějak definuje. Potkal jsem je v Codesysu a tam se proměnná typu ukazatel deklaruje takhle:
VAR ukazatel:POINTER TO nějaký_typ; END_VAR
Zbytek funguje jako v pascalu: ukazatel^ jsou data, na která ten ukazatel ukazuje, a jestli se nemýlím, tak @proměnná je adresa proměnné a Nil je hodnota ukazatele, který neukazuje na nic.
TYPE jméno_typu:definice; END_TYPE;
V praxi většinou přesnou syntaxi nemusíte řešit, protože vás vývojové prostředí buď nechá psát do tabulky, ve které se nedá udělat chyba, nebo vám aspoň předvyplní všechny hlavičky. Víceméně jediné datové typy, které má smysl takhle definovat, jsou struktury (STRUCT). Některá vývojová prostředí (třeba TIA Portal) je proto rovnou považují za synonyma a jiné typy definovat ani neumožňují.
Umožňují definovat si nějakou hodnotu na jednom místě a pak ji využívat v různých místech programu. Až ji jednou budete potřebovat změnit, stačí přepsat jenom tu definici, nemusíte procházet celý program a přepisovat jedno číslo po druhém (a na některé zapomenout a pak se divit, proč to najednou nefunguje).
CONST jméno1:=hodnota1; jméno2:=hodnota2; END_CONST;
Stejně jako v případě proměnných, i pro konstanty mají různá vývojová prostředí různé způsoby zadávání. Někdy je můžete psát takhle volně, jindy se jména a hodnoty (a případně i datové typy) vyplňují do tabulek.