REXLANG – Volně programovatelný blok

Symbol blokuPotřebná licence: REXLANG
PIC

Popis funkce
V některých případech se může stát, že je do řídícího algoritmu nutné implementovat funkci, kterou nelze efektivně vytvořit z dostupné množiny bloků. Pro takový účel byl vyvinut blok REXLANG, který implementuje algoritmus definovaný skriptovacím jazykem. Je použit skriptovací jazyk velice podobný jazyku C (nebo Java).

Skriptovací jazyk
Jak již bylo řečeno, skriptovací jazyk vychází z jazyka C a je mu velmi podobný, nicméně existují určité rozdíly a omezení:

  • Jsou podpořeny datové typy double, long a string (lze použít i int, short, bool, které se interně zpracovávají jako long, a typ float, který se interně zpracovává jako double). Není implementován typedef.
  • Nejsou implementovány pointery a struktury. V kontextu bloku REXLANG lze však definovat pole a používat indexy (operátor [ ]). Vstupy, výstupy a parametry bloku nemohou být pole.
  • Není zaveden operátor ’,’.
  • Z preprocesoru jsou podpořeny příkazy #include, #define, #ifdef .. [#else .. ] #endif, #ifndef .. [#else .. ] #endif (tzn. není podpořeno #pragma a zejména #if .. [#else .. ] #endif).
  • Nejsou implementovány standardní knihovny ANSI C, je však definována většina funkcí z math.h a potom některé další (viz dále).
  • Jsou definována klíčová slova input, output a parameter pro napojení na vstupy, výstupy a parametry bloku. Dále jsou definovány systémové funkce pro řízení běhu a diagnostiku (viz dále).
  • Kromě funkce main(), která se volá periodicky při běhu řídícího systému mohou být implementovány funkce init() (volá se při startu), exit() (volá se při ukončení řídícího algoritmu) a parchange() (volá se v systému REXYGEN při změně jakéhokoliv z parametrů).
  • Ve funkcích a procedurách bez parametrů musí být v deklaraci explicitně uvedeno void.
  • Nelze přetěžovat identifikátory, tj. nelze používat klíčová slova a názvy vestavěných funkcí jako identifikátor, nelze pojmenovat stejně globální a lokální proměnnou.
  • Nelze inicializovat pole, ať už globální nebo lokální.
  • Uživatelské návratové hodnoty funkcí main(), init() a exit() jsou zapsány na výstup iE. Hodnoty < -99 zastaví běh algoritmu bloku REXLANG (pro další běh je nutná reinicializace vstupem RESET). Uživatelské návratové hodnoty:

    iE >= 0 …Bez chyby

    0 > iE >= -99 …Warning, provádění algoritmu bloku beze změny

    iE < -99 …Chybový stav, provádění algoritmu bloku zastaveno

Syntaxe skriptovacího jazyka
Syntaxe skriptovacího jazyka vychází z jazyka C, přičemž nejsou podpořeny pointery a jiné typy než long a double. Navíc jsou definována klíčová slova input, output a parameter, která slouží pro odkazování na vstupy, výstupy a parametry bloku. Syntaxe je následující:

  • <typ> input(<číslo vstupu>) <jméno proměnné>;
  • <typ> output(<číslo výstupu>) <jméno proměnné>;
  • <typ> parameter(<číslo parametru>) <jméno proměnné>;

Proměnné typu input a parameter lze pouze číst a do proměnných typu output lze pouze přiřazovat. Například:

double input(1) vstup; /* deklarace proměnné vstup typu double, která  
                          představuje hodnotu vstupu bloku u1 */  
long output(2) vystup; /* deklarace proměnné vystup typu long, která  
                          představuje hodnotu výstupu bloku y2 */  
 
vstup=3;               //nedovolený příkaz - do vstupu nelze přiřazovat  
sum=vystup+1;          //nedovolený příkaz - z výstupu nelze číst hodnotu  
 
if (vstup>1) vystup=3+vstup;  //správné použití

Dostupné funkce
Ve skriptovacím jazyce je možné používat následující funkce:

  • Matematické (viz ANSI C, soubor math.h):
    atan, sin, cos, exp, log, sqrt, tan, asin, acos, fabs, fmod, sinh, cosh, tanh, pow, atan2, ceil, floor a abs Význam funkcí by měl být zřejmý, jen je potřeba zmínit, že funkce abs pracuje s celými čísly. Pro výpočet absolutní hodnoty desetinného čísla slouží funkce fabs.
  • Vektorové (v ANSI C nejsou)
    double max([n,]val1,…,valn)

    Vrací hodnotu největšího prvku. Funkce má nepovinný první parametr, který určuje počet prvků.

    double max(n,vec)

    Vrací hodnotu největšího prvku z vektoru vec.

    double min([n,]val1,…,valn)

    Vrací hodnotu nejmenšího prvku. Funkce má nepovinný první parametr, který určuje počet prvků.

    double min(n,vec)

    Vrací hodnotu nejmenšího prvku z vektoru vec.

    double poly([n,]x,an,…,a1,a0)

    Vypočte hodnotu polynomu y = anxn + + a1x + a0. Funkce má nepovinný první parametr, který určuje počet prvků.

    double poly(n,x,vec)

    Vypočte hodnotu polynomu y = vec[n]xn + + vec[1] + x + vec[0].

    double scal(n,vec1,vec2)

    Vypočte skalární součin y = vec1[0]vec2[0] + + vec1[n-1]vec2[n-1].

    double scal(n,vec1,vec2,skip1,skip2)

    Vypočte skalární součin y = vec1[0]vec2[0]+vec1[skip1]vec2[skip2]++vec1[(n-1)*skip1]vec2[(n-1)*skip2]. Toto je výhodné, pokud vektory představují matice a potřebujeme vynásobit sloupce (resp. řádky, pokud je matice uložena po sloupcích).

    double conv(n,vec1,vec2)

    Vypočte konvolutorní součin y = vec1[0]vec2[n-1] + vec1[1]vec2[n-2] + + vec1[n-1]vec2[0].

    double sum(n,vec)

    Sečte prvky vektoru, tj. y = vec[0] + vec[1] + + vec[n-1].

    double sum([n,]val1,…,valn)

    Sečte prvky, tj. y = val1 + val2 + + valn. Funkce má nepovinný první parametr, který určuje počet prvků.

    []array([n,],an-1,…,a1,a0)

    Vrací pole/vektor, které obsahuje hodnoty parametrů. Funkce/operátor má nepovinný první parametr, který určuje počet prvků. Návratový typ se volí automaticky podle typu parametrů (musí být všechny stejného typu).

    []subarray(idx,vec)

    Vrací pole/vektor, které představuje pole vec od indexu idx. Návratový typ se volí automaticky podle typu vstupního pole.

    copyarray(count,vecSource,idxSource,vecTarget,idxTarget)

    Kopíruje count hodnot z pole vecSource od indexu idxSource do pole vecTarget od indexu idxTarget. Obě pole musí být stejného typu.

    void fillarray(vector, value, count)

    Kopíruje hodnotu value do count prvků pole vector (vždy od indexu 0).

  • Funkce pro práci s textem (v ANSI C jsou analogické funkce v souboru string.h)
    string strsub(str, index, len)

    Vrací textový podřetězec.

    long strlen(str)

    Vrací délku stringu (počet znaků).

    long strfind(str,substr) nebo long strfind(str,substr,offset)

    Vrací polohu začátku (kolikátý znak počítáno od 0) substringu (parametr substr) ve stringu str. Prohledávání začíná od znaku s indexem offset (pokud není zadán, tak od začátku). Parametr substr může být i znak.

    long strrfind(str,substr)

    Stejně jako předchozí funkce, ale prohledává od konce.

    strreplace(str,pattern,substr)

    Najde všechny výskyty stringu pattern ve stringu str a nahradí je stringem substr. Nový string je uložen do str.

    strupr(str)

    Převede text na velká písmena.

    strlwr(str)

    Převede text na malá písmena.

    strtrim(str)

    Odstraní bílé znaky (mezery) na začátku a konci textu.

    long str2long(str [, default])

    Převede text na celé číslo. Uvažuje jen tolik znaků od začátku, dokud text odpovídá číslu, zbylé znaky jsou ignorovány. Druhý (nepovinný) parametr určuje vrácené číslo pokud převod selže. Pokud parametr není uveden, při chybě funkce vrací 0.

    double str2double(str [, default])

    Převede text na desetiné číslo. Uvažuje jen tolik znaků od začátku, dokud text odpovídá číslu, zbylé znaky jsou ignorovány. Druhý (nepovinný) parametr určuje vrácené číslo pokud převod selže. Pokud parametr není uveden, při chybě funkce vrací 0.

    string long2str(num [, radix])

    Převede celé číslo num na text. Nepovinný parametr radix udává číselnou soustavu, ve které je převod proveden (typicky 10 nebo 16). Pokud není uveden, použije se radix = 10. Výstupní řetězec neobsahuje žádnou identifikaci použité číselné soustavy (např. předponu 0x u šestnáctkové soustavy).

    string double2str(num)

    Převede desetinné číslo num na text.

    strcpy(dest,src)

    Kopie řetězce. Funkce je zavedena z důvodu kompatibility s ANSI C. Lze použít konstrukci dest=src se stejným výsledkem.

    strcat(dest,src)

    Spojení řetězců. Funkce je zavedena z důvodu komplatibility s ANSI C. Lze použít konstrukci dest=dest+src se stejným výsledkem.

    strcmp(str1,str2)

    Porovnání stringů. Funkce je zavedena z důvodu komplatibility s ANSI C. Lze použít konstrukci str1==str2 se stejným výsledkem.

    float2buf(buf,x[,endian])

    Převádí číslo x na 4 prvky pole buf. Každý prvek představuje jeden byte čísla uloženého v single precision formátu dle IEEE 754 (známé jako float). Funkce se hodí pro přípravu dat při přenosu dat mezi zařízeními. Nepovinný třetí parametr má význam: 0 (default) = endian podle procesoru, 1 = nejnižší byte první, 2 = nejvyšší byte první.

    double2buf(buf,x[,endian])

    Podobná funkce jako float2buf, jen ukládá 8 byte, tzn. double precision formát.

    double buf2float(buf[,endian])

    Opačná funkce k funkci float2buf.

    double buf2double(buf[,endian])

    Opačná funkce k funkci double2buf.

    long RegExp(str,regexp,capture[])

    Porovnání stringu str s regulárním výrazem regexp. Pokud string vyhovuje, do pole capture se nastaví stringy odpovídající jednotlivým uzávorkovaným sekcím regulárního výrazu. capture[0] je vždy celý regulární výraz (první výskyt). Funkce vrací počet nastavených prvků v poli capture nebo záporné číslo v případě chyby. V regulárním výrazu jsou podporovány následující konstrukce:

    (?i) …Must be at the beginning of the regular expression. Makes the matching case-insensitive.

    ̂ …Match beginning of a string

    $ …Match end of a string

    () …Grouping and substring capturing

    \s …Match whitespace

    \S …Match non-whitespace

    \d …Match decimal digit

    \n …Match new line character

    \r …Match line feed character

    \f …Match vertical tab character

    \v …Match horizontal tab character

    \t …Match horizontal tab character

    \b …Match backspace character

    + …Match one or more times (greedy)

    +? …Match one or more times (non-greedy)

    * …Match zero or more times (greedy)

    *? …Match zero or more times (non-greedy)

    ? …Match zero or once (non-greedy)

    x|y …Match x or y (alternation operator)

    \meta …Match one of the meta characters: ^$().[]*+?|\

    \xHH …Match byte with hex value 0xHH, e.g. \x4a.

    [...] …] Match any character from the set. Ranges like [a-z are supported.

    [^...] …Match any character but the ones from the set.

    Příklad:
    RegExp("48,string1,string2","̂(\\d+),([̂,]+),",capture);
    Výsledek: capture=["48,string1","48","string1"]

    long ParseJson(json,cnt,names[],values[])

    Funkce předpokládá, že parametr json obsahuje text v JSON formátu. V poli names jsou názvy požadovaných objektů (k subpoložkám se přistupuje přes tečku, index pole se píše do [] - např. "cars[1].model"), do pole values funkce nastaví hodnoty těchto objektů. Parametr cnt udává počet požadovaných objektů (délku pole names i values). Funkce vrací počet skutečně nastavených hodnot (záporná čísla znamenají chybu).

    Poznámka: Textová proměnná se deklaruje stejně jako v ANSI C, tj. char <název proměnné>[<maximální počet znaků>];. Pro předání stringu do funkce se používá konstrukce char <název proměnné>[] nebo string <název proměnné>.

  • Systémové (v ANSI C nejsou)

    Archive(arc, type, id, lvl_cnt, value)

    Uloží hodnotu do archivního subsystému. arc je bitová maska archivů, do ktrých se má hodnota zapsat (např. pro zápis do archivů 3,5 nastavte arc=20 -> (BIN)10100 = (DEC)20). Archivy jsou číslovány od 1 a maximum je 15 (archiv s číslem 0 je interní systémový log). type

    1 …Bool

    2 …Byte (U8)

    3 …Short (I16)

    4 …Long (I32)

    5 …Word (U16)

    6 …DWord (U32)

    7 …Float (F32)

    8 …Double (F64)

    9 …Time

    10 …Large (I64)

    11 …Error

    12 …String

    17 …Bool Group

    18 …Byte Group (U8)

    19 …Short Group (I16)

    20 …Long Group (I32)

    21 …Word Group (U16)

    22 …DWord Group (U32)

    23 …Float Group (F32)

    24 …Double Group (F64)

    25 …Time Group

    26 …Large Group (I64)

    27 …Error Group

    id je unikátní identifikátor události v archivu. lvl_cnt je úroveň (závažnost) alarmu v případě zápisu alarmů nebo počet prvků v případě zápisu grupy. value je hodnota pro uložení do archivu nebo reference na pole v případě zápisu grupy.

    Trace(id,val)

    Výpis čísla id a hodnoty val. Funkce je určena pro odladění bloku. Číslo id je uživatelem definovaná konstanta v rozsahu 0 až 9999 pro snadnou identifikaci výpisu. Hodnota val může být libovolného datového typu včetně textových řetězců (string). Zprávy se vypisují do systémového logu systému REXYGEN.

    Pro zobrazení výpisů v systémovém logu je potřeba je aktivovat. Jděte do menu
    Target Diagnostic messages a zaškrtněte položku Information v poli Function block messages.

    Zároveň musí být povolen výpis zpráv z konkrétního bloku - zaškrtnutím checkboxu Enable logging na kartě Runtime v parametrech bloku. Ve výchozím stavu po vložení bloku z knihovny je toto povoleno. Teprve poté se zprávy objeví v systémovém logu.

    TraceError(id,val) TraceWarning(id,val) TraceVerbose(id,val)

    Tyto příkazy mají podobný význam jako příkaz Trace, avšak výpis se objeví v jiné skupině systémového logu (Error, Warning, Verbose). Výpisy úrovně Error se do logu zapisují vždy, nezávisle na zaškrtnutí checkboxu Enable logging daného bloku. Zprávy úrovně Warning a Verbose je potřeba nejprve povolit, stejně jako v případě příkazu Trace.

    Suspend(sec)

    Přeruší provádění kódu skriptu, pokud od jeho spuštění (v dané periodě) uplynulo více času (v sekundách), než je uvedeno. Při dalším spuštění bloku se pokračuje za tímto příkazem. Při Suspend(0) dojde k přerušení vždy.

    double GetPeriod()

    Vrací vlastní periodu spouštění daného bloku ve vteřinách.

    double CurrentTime()

    Vrací aktuální čas (v interním formátu). Používá se ve spojení s funkcí ElapsedTime().

    double ElapsedTime(new_time, old_time)

    Vrací uplynulý čas v sekundách (desetinné číslo), tj. rozdíl mezi časy určenými parametry new_time a old_time, získaným z předchozího volání funkce CurrentTime().

    double Random()

    Vrací pseudonáhodné číslo z intervalu 0,1). Před voláním funkce init() se automaticky inicializuje generátor pseudonáhodného čísla, takže sekvence je vždy stejná.

    long QGet(var)

    Vrací kvalitu proměnné var (tak jak s ní pracuje systém REXYGEN, viz bloky QFC, QFD, VIN, VOUT). Funkci je možno použít jen pro vstupy, parametry a výstupy - pro vnitřní proměnné vrací vždy 0.

    void QSet(var, value)

    Nastaví kvalitu proměnné var (tak jak s ní pracuje systém REXYGEN) na hodnotu val. Funkci je možno použít jen pro výstupy - pro ostatní se nic nenastaví.

    long QPropag([n,]val1,…,valn)

    Vrací kvalitu, která vznikne sloučením kvalit val1,…,valn. Základní pravidlo pro slučování je, že výsledná kvalita je nejhorší ze vstupních. Pokud nastavíme kvalitu výstupu bloku s použitím této funkce, tak že do parametrů dáme kvalitu všech vstupů bloku, které výstup ovlivňují, dostaneme stejné chování jako u ostatních bloků systému REXYGEN.

    double LoadValue(fileid, idx)

    Přečte hodnotu ze souboru. Předpokládá binární soubor, kde jsou za sebou uloženy hodnoty typu double nebo textový soubor, kde na každé řádce je jedno číslo. Pořadí hodnoty v tomto souboru, kterou chceme přečíst udává parametr idx (počítá se od 0). Soubor je identifikován parametrem fileid. V současnosti jsou podporovány následující hodnoty:

    0 …soubor na disku s názvem v parametru p0

    1 …soubor na disku s názvem stejným jako název bloku rozšířený o příponu .dat.

    2…soubor na disku s názvem v parametru srcname, ale s příponou .dat

    3 …soubor na disku s názvem rexlang.dat v aktuálním adresáři

    4-7 …stejné jako 0-3, ale soubor je textový. Každá řádka obsahuje jedno číslo. Číslo řádku je parametr idx (počítáno od 0). Hodnota idx=-1 znamená následující řádku (lze použít pro urychlení pro sekvenční čtení více hodnot).

    void SaveValue(fileid, idx, value)

    Uloží hodnotu value do souboru. Význam ostatních parametrů je stejný jako u funkce LoadValue.

    void GetSystemTime(time)

    Vrací hodnotu systémového času. Obvykle je to UTC, ale závisí na nastavení operačního systému. Parametr time musí být pole typu long o nejméně 8 prvcích. Funkce naplní toto pole hodnotami (po řadě) rok, měsíc, den (v měsíci), den v týdnu, hodina, minuta, sekunda, milisekunda. Na některých platformách milisekundy nejsou k dispozici (funkce vrací vždy 0ms) nebo mají jen omezenou přesnost.

    void Sleep(seconds)

    Pozastaví vykonávání skriptu na uvedenou dobu (zadává se v parametru jako desetinné číslo v sekundách). Funkci je nutné používat jen ve výjimečných případech, protože se tím pozastaví vykonávání celého tasku/schématu. Doba uspání by neměla přesáhnout 900 milisekund. Nejkratší doba, na kterou lze skript pozastavit, je zhruba 0.01 s. Přesná hodnota závisí na cílové platformě.

    long GetExtInt(ItemID)

    Vrací hodnotu celočíselného vstupu/výstupu/parametru libovolného bloku v REXu určeného parametrem ItemID. Tento parametr je textový a má stejný význam/strukuturu jako parametr sc bloku GETPI. Pokud hodnotu nelze získat (např. neexistující ItemID nebo není typu long) blok REXLANG skončí chybou a je potřeba ho resetovat.

    long GetExtLong(ItemID)

    Viz GetExtInt(ItemID).

    double GetExtReal(ItemID)

    Stejný význam jako předchozí příkazy, ale pro desetinné číslo.

    double GetExtDouble(ItemID)

    Viz GetExtReal(ItemID).

    string GetExtString(ItemID)

    Stejný význam jako předchozí příkazy, ale pro řetězce/text.

    void SetExt(ItemID, value)

    Nastaví hodnotu vstupu/výstupu/parametru libovolného bloku v REXu určeného parametrem ItemID. Tento parametr je textový a má stejný význam/strukuturu jako parametr sc bloku GETPI. Nastavuje se hodnota parametru value, přičemž typ nastavené hodnoty (long/double/string) je určen typem parametru value. Pokud hodnotu nelze nastavit (např. neexistující ItemID nebo neodpovídá datový typ) blok REXLANG skončí chybou a je potřeba ho resetovat.

    int BrowseExt(ItemID, first_subitem_index, max_count, subitems, kinds)

    Funkce umožňuje procházet bloky úlohy. Pokud je ItemID identifikátor bloku (cesta_k_bloku), subitems bude obsahovat jména vstupů, výstupů, parametrů a vnitřních stavů bloku. Návratová hodnota funkce je počet nastavených názvů nebo záporný chybový kod. Význam kinds je: executive = 0, module = 1, driver = 2, archive = 3, level = 4, task = 5, quicktask = 6, subsystem = 7, block = 8, input = 9, output = 10, internal state = 11, parameter or state array = 12, special = 13.

    long CallExt(ItemID)

    Spustí (jeden krok) libovolný bloku v REXu určeného parametrem ItemID. Tento parametr je textový a má stejný význam/strukuturu jako parametr sc bloku GETPI. Funkce vrací, to co vrátí volaná funkce (tj. chybový kód REXYGEN). Doporučje se spouštěný blok/subsystém pozastavit pro normální spouštění (zatržítko Halt v záložce Runtime parametrického dilaogu) a umístit do stejného tasku jako volající blok REXLANG.

    long GetInArrRows(input)

    Vrací počet řádek pole připojeného ke vstupu bloku REXLANG s indexem input.

    long GetInArrCols(input)

    Vrací počet sloupců pole připojeného ke vstupu bloku REXLANG s indexem input.

    long GetInArrMax(input)

    Vrací maximální (alokovanou) velikost pole připojeného ke vstupu bloku REXLANG s indexem input.

    double GetInArrDouble(input, row, col)

    Vrací prvek pole připojeného ke vstupu bloku REXLANG s indexem input.

    Void SetInArrValue(input, row, col, value)

    Nastaví prvek pole připojeného ke vstupu bloku REXLANG s indexem input.

    Void SetInArrDim(input, row, col)

    Nastaví rozměr pole připojeného ke vstupu bloku REXLANG s indexem input.

    long memrd32(hMem,offset)

    Čtení fyzické paměti. Handle se získá pomocí funkce OpenMemory.

    long memwr32(hMem, offset, value)

    Zápis do fyzické paměti. Handle se získá funkcí OpenMemory.

  • Komunikační (v ANSI C nejsou)

    Tato sada funkcí slouží pro práci se sériovou linkou (RS-232 nebo RS-485), sběrnicí I2C nebo SPI a komunikaci po TCP/IP nebo UDP/IP. Zde je uveden jen stručný popis funkcí, které se pro komunikaci používají. Součástí instalace systému REXYGEN jsou příklady, které názorně ukazují způsob použití.

    long Open(long type, long lclIP, long lclPort, long rmtIP, long rmtPort)

    Otevře socket nebo COM port - podle parametru type. Pro TCP klient provádí rovnou connect. Vrací identifikační číslo (tzv. handle) socketu nebo portu. Pokud je záporné, otevření/spojení se nezdařilo.

    long Open(long type, string comname, long baudrate, long parity)

    Modifikace příkazu Open() pro otevření sériové linky.

    long Open(long type, string filename)

    Modifikace příkazu Open() pro otevření souboru.

    long Open(long type, string localname, long locPort, string remotename, long remPort)

    Modifikace příkazu Open() pro otevření TCP nebo UDP socketu.

    long OpenFile(string filename)

    Modifikace příkazu Open() pro otevření souboru.

    long OpenCom(string comname, long baudrate, long parity)

    Modifikace příkazu Open() pro otevření sériové linky. Nastavení parity: 0=žádná, 1=lichá, 2=sudá.

    long OpenUDP(string localname, long lclPort, string remotename, long remPort)

    Modifikace příkazu Open() pro otevření UDP socketu.

    long OpenTCPsvr(string localname, long lclPort)

    Modifikace příkazu Open() pro otevření TCP socketu - server, naslouchání.

    long OpenTCPcli(string remotename, long remPort)

    Modifikace příkazu Open() pro otevření TCP socketu - klient. POZOR funkce nečeká na navázání spojení To nějakou dobu trvá (na lokální síti jednotky milisekund) a pokud se zavolá Write() nebo Read() ještě před navázáním spojení vrací funkce chybu -307 (chyba otevření souboru). Je potřeba buď chvíli počkat (např. použít funkci Sleep() ) nebo zápis/čtení dělat až v dalším tiku.

    long OpenI2C(string devicename)

    Modifikace příkazu Open() pro otevření I2C zařízení.

    long OpenSPI(string devicename)

    Modifikace příkazu Open() pro otevření SPI zařízení.

    long OpenDevice(string devicename)

    Modifikace příkazu Open() pro otevření zařízení. V podstatě stejné jako OpenFile(), ale následné Read() a Write() je nečekací (tj. vrací -1, pokud není co číst nebo nelze zapsat).

    long OpenMemory(string devicename, long baseaddr, long size)

    Modifikace příkazu Open() pro mapování fyzické paměti.

    long OpenSHM(string devicename, long deviceid, long size, long flags)

    Modifikace příkazu Open() pro mapování sdílené paměti (jen na linuxu, volá ftok() a shmget() ). První a druhý parametr slouží pro identifikaci oblasti paměti (tj. musí být stejné u všech spolupracujících zařízení); size je velikost sdílené oblasti paměti v bajtech; flags jsou standartní linuxové flagy/práva (pokud je 0=default, tak se nastaví: vytvořit oblast, pokud neexistuje, všichni mohou číst i zapisovat)

    void Close(long handle)

    Zavře socket, sériovou linku, soubor nebo jiné zařízení otevřené pomocí funkce Open (nebo její varianty).

    void GetOptions(long handle, long params[])

    Přečte parametry - nastaví aktuální hodnoty do pole params; pole musí být dostatečně dlouhé - viz SetOptions

    void SetOptions(long handle, long params[])

    Nastaví parametry sériové linky nebo socketu. Pole musí být dostatečně dlouhé - aktuálně je

    • 22 parametrů pro sériovou linku (na Windows parametery for SetCommState() a SetCommTimeouts() v pořadí: BaudRate, fParity, Parity, StopBits, ByteSize, fDtrControl, fRtsControl, fAbortOnError, fBinary, fErrorChar, fNull, fDsrSensitivity, fInX, fOutX, fOutxCtsFlow, fOutxDsrFlow, fTXContinueOnXoff, ReadIntervalTimeout, ReadTotalTimeoutConstant, ReadTotalTimeoutMultiplier, WriteTotalTimeoutConstant, WriteTotalTimeoutMultiplier; na linuxu je jiný interface, ale význam parametrů je stejný, pokud lze implementovat)
    • 2 pro soubor (1. prvek je režim: 1=seek begin, 2=seek current, 3=seek end, 4=set file end, 2. prvek je offset pro seek),
    • 3 pro SPI (1. prvek je SPI mode, 2. prvek je bits per word, 3. prvek je max speed Hz),
    • 5 pro I2C (1. prvek je slave address, 2. prvek je 10bits address flag, 3. prvek je Packet Error Checking flag, 4. prvek je nuber of retries, 5. prvek je timeout)
    long Accept(long hListen)

    Přijme spojení navázané klientem na naslouchací socket hListen; vrací handle komunikačního socketu nebo chybu.

    long Read(long handle, long buffer[], long count)

    Přijme data z linky nebo socketu nebo přečte ze souboru ; v parametru count je maximální počet byte, které se mají přečíst; funkce vrací počet skutečně přečtených byte nebo chybový kód; data jsou čtena tak, ze jeden byte z linky odpovídá jednomu prvku typu long v poli buffer. Ve starších verzích se funkce jmenovala Recv, což lze z důvodu zpětné kompatibility stále použít. Funkci lze použít také ve tvaru long Read(long handle, string data[], long count) (tj. místo pole na data se použije string; jeden byte ve vstupním souboru odpovídá jednomu znaku; binární soubory takto číst nelze) Chybové kódy jsou:

    -1

    je třeba počkat na dokončení operace (funkce je totiž tzv. „neblokující“

    -309

    čtení selhalo; chybový kód operačního systému se objevuje v logu (pokud je zapnuto logování u bloku)

    -307

    soubor/socket není otevřen

    Funkce může mít ještě jeden (poslední, nepovinný) parametr offset, což lze použít pokud je handle vytvořen OpenSHM() nebo OpenMemory().
    long Write(long handle, long buffer[], long count)

    Odešle data na linku nebo socket; v parametru count je počet byte, které se mají poslat; funkce vrací počet skutečně vyslaných byte nebo chybový kód; data jsou zapisována tak, že jeden byte z linky se odpovídá jednomu prvku typu long v poli buffer. Ve starších verzích se funkce jmenovala Send, což lze z důvodu zpětné kompatibility stále použít. Funkci lze použít také ve tvaru long Write(long handle, string data) (tj. místo pole dat se použije string; jeden byte ve výstupním souboru odpovídá jednomu znaku; binární soubory takto zapisovat nelze) Chybové kody jsou:

    -1 je třeba počkat na dokončení operace (funkce je totiž tzv. „neblokující“
    -310 zápis selhal; chybový kód operačního systému se objevuje v logu (pokud je zapnuto logování u bloku)
    -307 soubor/socket není otevřen
    Funkce může mít ještě jeden (poslední, nepovinný) parametr offset, což lze použít pokud je handle vytvořen OpenSHM() nebo OpenMemory().
    long ReadLine(long handle, string data)

    Přečte jednu řádku z (textového) souboru sériové linky nebo socketu; přečtené znaky jsou v proměnné data až do velikosti stringu; vrací skutečnou délku řádky nebo chybový kód.

    long DeleteFile(string filename)

    Smaže soubor. Vrací 0 pokud je soubor smazán; záporné číslo znamená chybu.

    long RenameFile(string filename, string newfilename)

    Přejmenuje soubor. Vrací 0 pokud je soubor přejmenován; záporné číslo znamená chybu.

    bool ExistFile(string filename)

    Vrací 1 pokud soubor nebo zařízení existuje (lze jej otevřít pro čtení).

    long I2C(long handle, long addr, long bufW[], long cntW, long bufR[], long cntR)

    Příkaz pro komunikaci po sběrnici I2C. Funguje jen na zařízeních s operačním systémem Linux, která mají toto rozhraní (např. Raspberry Pi). Provádí současně odeslání i příjem dat na/ze slave zařízení s adresou addr. Handle se získá funkcí OpenI2C, kde parametr funkce je jméno zařízení (dle operačního systému). Parametr bufW je buffer (pole) pro odchozí data, cntW je počet odeslaných byte, bufR je buffer (pole) pro příchozí data a cntR je počet přijímaných byte. Funkce vrací 0 nebo chybový kód.

    long SPI(long handle, 0, long bufW[], long cntW, long bufR[], long cntR)

    Příkaz pro provedení transakce na sběrnici SPI. Funguje jen na zařízeních s operačním systémem Linux, která mají toto rozhraní (např. Raspberry Pi). Handle se získá funkcí OpenSPI, kde parametr funkce je jméno zařízení (dle operačního systému). Druhý parametr je vždy 0 (rezervován pro interní použití). Parametr bufW je buffer (pole) pro odchozí data, cntW je počet odeslaných byte, bufR je buffer (pole) pro příchozí data a cntR je počet přijímaných byte. Pamatujte, že SPI komunikace probíhá současně v obou směrech (full-duplex), takže výsledná délka SPI transakce je dána maximem parametrů cntW a cntR, nikoliv jejich součtem. Funkce vrací 0 nebo chybový kód.

    long Seek(long handle, long mode[], long offset)

    Příkaz pro nastavení čtecí/zapisovací pozice. Parametr mode má význam: 1=offset od začátku souboru, 2= offset od aktualní pozice, 3=offset od konce souboru.

    long Recv(long handle, long buffer[], long count)

    Pouze pro zajištění zpětné kompatibility. Funkce nahrazena funkcí Read.

    long Send(long handle, long buffer[], long count)

    Pouze pro zajištění zpětné kompatibility. Funkce nahrazena funkcí Write.

    long crc16(data,length,init,poly,flags,offset)

    Vypočte kontrolní součet, tak jak jej definují různé komunikační protokoly. data pole (long, ale v každém prvku se předpoklád jeden byte) nebo text nad kterým se dělá kontrolní součet. length počet platných byte v poly/textu data (pro text je možné zadat -1 a pak se uvažuje celý text). init počáteční hodnota kontrolního součtu (tzv. inicializační vektor) poly tzv. řídící polynom flags 1...obrací se pořadí bitů (ve vstupních byte i výsledném crc), 2...na výsledném crc se udělá ještě xor 0xFFFF, 4...zpracovavají se všechny 4 bajty z longu v poli dat (delka i offset je v bajtech), 8... jako 4, ale data v longu se čtou od nejvyššího bajtu offset index prvního zpracovávaného bajtu z dat (tj. obvykle 0, ale někdy je potřeba pár bajtů na začátku vynechat, tak sem se napíše kolik) Poznámka: pro jiné délky kodu existuje analogická funkce, např. pro 32-bitové crc použijeme long crc32(data,length,init,poly,flags), pro 8-bitové long crc8(data,length,init,poly,flags), atd. Příklady volání pro časté protokoly: MODBUS: crc16("123456789",-1,0xFFFF,0x8005,1)); DECT-X: crc16("123456789",-1,0,0x0589,0));

Ladění kódu, debugging
Pro ladění kódu je k dispozici příkaz Trace, viz výše. Dále lze použít výstupy bloku, které se nouužívají pro vlastní algoritmus a zapisovat do nich hodnoty různých mezivýpočtů. V závislosti na povaze algoritmu může být vhodné tyto ladící hodnoty připojit do trendu. Pokud je potřeba sledovat hodnot více, je možné do tasku přidat blok CNA (připojený na TRNDV nebo VTOR) a do hodnot v jeho poli nastavovat opět růyné mezivýsledky pomocí funkce SetExt.

Poznámky

  • Typ vstupů u0..u15, výstupů y0..y15 a parametrů p0..p15 se určuje až při překladu zdrojového souboru bloku, podle specifikací input, output a parameter.
  • Všechny chybové kódy < -99 vyžadují reset bloku REXLANG vstupem RESET. Je samozřejmě nutné napřed odstranit příčinu, která chybu způsobila.
  • POZOR!!! Ve funkci init() je sice možné číst vstupy, ale protože ostatní bloky obvykle nenastavují v init fázi výstupy, bude tam vždy 0. Nastavovat výstupy lze, ale obvykle se to nedělá.
  • Parametr srcname je možné udávat s celou cestou. V opačném případě se soubor hledá na aktuálním adresáři a adresářích specifikovaných volbou -I v parametrech příkazové řádky programu REXYGEN Compiler.
  • Všechny parametry vektorových funkcí jsou typu double (popřípadě vektor typu double) kromě parametru n, který je typu long. Také si všimněme, že funkce, které mají jen jeden vektorový parametr existují ve třech variantách:

    double funkce(val1,…,valn)

    Vektor se zadává jako posloupnost parametrů typu double.

    double funkce(n,val1,…,valn)

    Vektor se zadává jako v předchozím případě, ale navíc první parametr udává počet čísel – délku vektoru. Na rozdíl od předchozí varianty, lze v této variantě překládat zdrojový kód bez úprav překladačem jazyka C. Parametr n musí být přímo číslo (nikoliv tzv. const proměnná) a musí odpovídat počtu následujících parametrů tvořících vektor.

    double funkce(n,vec)

    Parametr n je libovolný výraz typu long a udává počet prvků vektoru, se kterými funkce počítá.

  • Nepovinný parametr n u vektorových funkcí se musí uvádět, pokud chceme stejný kód beze změn použít v překladači C/C++. Takové použití vyžaduje implementovat všechny nestandardní funkce, což není velký problém, ale funkce s variantním počtem parametrů musí nějak poznat jejich počet.
  • Ve všech případech je třeba mít na paměti, že všechny vektory začínají prvkem s indexem 0 a dále, že program (stejně jako jazyk C) nekontroluje meze polí. Např. při definování double vec[10], x; (vektor s deseti prvky s indexy 0 9) není zápis x=vec[10]; ani syntaktická ani runtime chyba, ale hodnota je nedefinovaná. Dokonce lze i napsat vec[11]=x;, což je obzvláště nebezpečné, protože se tím přepíše nějaká jiná proměnná a program nefunguje správně, případně se může i „zaseknout“.
  • Při překladu skriptu se často hlásí jen chyba syntax error a číslo řádky, kde nastává. Znamená to chybu v syntaxi. Pokud se zdá vše v pořádku, může to být tím, že použitý identifikátor je klíčové slovo jazyka nebo jméno vestavěné funkce.
  • Všechny skoky se překládají relativně, tj. příslušný kus kódu je omezen na 32767 instrukcí (v přenositelném formátu na různé platformy).
  • Do zásobníku se ukládají všechny aktuálně platné proměnné a mezivýsledky, tj.:
    • Globální proměnné a lokální static proměnné (trvale na začátku zásobníku)
    • Návratové adresy funkcí
    • Parametry předávané funkcím (včetně „vestavěných“)
    • Lokální proměnné funkcí
    • Návratová hodnota funkce
    • Mezivýsledky operací (například výraz a=b+c; se provádí tak, že se do zásobníku uloží hodnota b, pak (na další místo) hodnota c pak se provede součet, obě hodnoty se ze zásobníku zruší a vloží se tam hodnota součtu).

    Každá jednoduchá proměnná (tedy long i double se počítá za jednu položku v zásobníku. Pole se tedy počítá podle jeho délky opět bez ohledu na typ.

  • Pole se do funkcí předávají odkazem. To znamená, že v zásobníku se počítá parametr jako jedna hodnota a hlavně se nepracuje s lokální kopií, ale vždy přímo s předaným polem.
  • Pokud je zadána nedostatečná velikost zásobníku (méně než potřeba pro globální proměnné plus 10), volí se automaticky jako dvojnásobek potřeby pro globální proměnné (tj. předpoklad, že aktivních lokálních proměnných nebude více než globálních) plus 100 rezerva (na výpočty, parametry funkcí, lokální proměnné, pokud by globálních bylo málo).
  • Při základním debug módu se kontroluje (za běhu skriptu), zda jsou všechny čtené hodnoty inicializované, zda index pole nepřesahuje deklarovanou velikost pole, přidává se několik neinicializovaných hodnot před a za každé deklarované pole (další ochrana proti nesprávnému indexu v poli), do kódu se přidávají instrukce NOP s argumentem číslo řádku ve zdrojovém souboru (usnadňuje dohledání v *.ill souboru). Pokud je zvolen úplný debug mód tak se navíc kontroluje, zda se program nepokouší přistupovat mimo platnou oblast dat (což je zatím nad SP ve stacku – neplatné hodnoty v zásobníku).
  • Pod pojmem instrukce se u tohoto bloku nemyslí instrukce procesoru, ale instrukce mezikódu nezávislého na procesoru. Zdrojový kód přeložený do tohoto mezikódu je v souboru *.ill (mnemokódy pro jednotlivé instrukce, co řádka to jedna instrukce).
  • Při použití sériové linky funkce Open() vždy nastaví binární neblokující režim bez timeoutů, 8 datových bitů, jeden stopbit, bez parity, 19200Bd. Bitovou rychlost a paritu lze nastavit přímo ve funkci Open() pomocí nepovinného druhého (bitrate) a třetího (parita) parametru.
  • Při čtení a zápisu dat do textového souboru je potřeba počítat s tím, že se musí přečíst/zapsat celý při každém přístupu. Naproti tomu binární soubor má pevnou strukturu, takže přístup je rychlejší. Výhoda textových souborů spočívá v tom, že je lze zobrazovat i měnit bez speciálního programu.
  • Na rozdíl od standardních bloků systému REXYGEN se automaticky nevolá funkce parchange() v inicializační fázi. Pokud je to potřeba, je nutné ji explicitně zavolat ve funkci init().
  • Protože operační systémy na bázi windows i linuxu přistupují k sériové lince stejně jako k souboru, je možné pomocí funkcí Recv() a Send() číst a zapisovat soubory sekvenčně po bajtech. V tomto případě se ve funkci Open() jako parametr type používají stejné hodnoty, jako ve funkci LoadValue() (parametr fileid).
  • Ve funkci WriteRead() pro případ SPI je potřeba počítat s tím, že se data čtou i během zápisu. Pokud tedy potřebujeme zapsat do zařízení 2byte (např. číslo příkazu) a po jeho předání zařízení posílá 4byte dat, je potřeba číst 6byte, přičemž první a druhý byte (přijatý při zápisu čísla příkazu) neobsahuje platná data. Obecně nelze ale byte přečtené při zápisu vypustit, protože u některých zařízení obsahují platná data.
  • Funkce OpenUDP(), OpenTCPsvr(), OpenTCPcli(), podporují i IPv6 socket. Zda použít IPv4 nebo IPv6 se určí automaticky podle formátu adresy popřípadě podle toho co vrátí DNS.
  • Funkce OpenFile() otevírá soubory v datovém adresáři systému REXYGEN(tj. v Linuxu implicitně v \rex\data, na Windows
    C:\ProgramData\REX Controls\REX_<verze>\RexCore). Jsou dovoleny podadresáře, ale není dovoleno ..\. Linky se následují.

Vstupy

HLD

Pozastavení – kód bloku se nevykonává, je-li hodnota rovna on.

Bool

RESET

Resetování chyby při náběžné hraně; blok se znovu inicializuje (vynulují se všechny globální proměnné a zavolá se funkce Init())

Bool

u0..u15

Vstupní signály, jejichž hodnoty jsou přístupné ve skriptu

Any

Výstupy

iE

Kód chyby při běhu skriptu. Pro chybové < -99 je provádění algoritmu bloku zastaveno do reinicializace vstupem RESET nebo novým spuštěním exekutivy)

Error

0 ....

vše v pořádku, proběhla celá funkce main() popř. init()

-1 ...

provádění programu skončilo příkazem Suspend(), tj. vypršel čas pro výpočet; výpočet bude při dalším spuštění bloku pokračovat tam, kde skončil

< -1 .

chybový kód xxx systému REXYGEN, více viz příloha C

> 0 ..

uživatelské návratové hodnoty, běh algoritmu beze změny

y0..y15

Výstupní signály, jejichž hodnoty jsou definovány ve skriptu

Any

Parametry

srcname

Jméno souboru se skriptem  srcfile.c

String

srctype

Typ zdrojového souboru  1

Long (I32)

1: C-like 

Textový soubor s výše popisovanou syntaxí obdobnou jazyku C.

2: STL 

Textový soubor se syntaxí dle IEC61131-3. Norma je implementována se stejnými omezeními jako C-like skript (tj. žádné struktury, z typů jen INT a REAL a STRING, vstupy bloku jsou globální VAR_INPUT, výstupy bloku jsou globální VAR_OUTPUT, parametry bloku jsou globální VAR_PARAMETER, standardní funkce dle specifikace, systémové a komunikační funkce jako v C-like)

3: RLB 

Soubor v binárním formátu, který vzniká při překladu z formátu STL i C-like. Tento formát použijeme, pokud chceme předat fungující blok někomu jinému a nechceme mu dát zdrojové soubory.

4: ILL 

Textový soubor, ale zapisují se mnemonické kódy instrukcí, do kterých je překládán formát STL. Dalo by se to přirovnat k assembleru. V současnosti není tento formát podporován.

stack

Velikost zásobníku pro všechny výpočty a proměnné. Zadává se jako počet proměnných, které se mají do zásobníku vejít. Výchozí hodnota 0 znamená, že velikost zásobníku se zvolí automaticky, což ve většině případů vyhovuje.

Long (I32)

debug

Úroveň/množství ladicích kontrol a informací; větší číslo znamená více kontrol a tím pomalejší běh algoritmu; volbu bez kontroly se doporučuje nepoužívat (může vést až k pádu aplikace na cílové platformě při nesprávně napsaném kódu).  3

Long (I32)

1 ....

bez kontroly

2 ....

základní kontrola

3 ....

úplná kontrola

strs

Velikost paměti (zásobníku) pro všechny texty. Zadává se jako počet byte/znaků, které se mají do zásobníku vejít. Výchozí hodnota 0 znamená, že velikost zásobníku se zvolí automaticky, což ve většině případů vyhovuje.

Long (I32)

p0..p15

Parametry, jejichž hodnoty jsou přístupné ze skriptu

Any

Příklad
Následující příklad implementuje lineární model procesu definovaný vahovou funkcí (filtr typu FIR) doplněný o saturaci na vstupu. Příklad je napsán tak, aby ukazoval různé konstrukce použitého skriptovacího jazyka. Algoritmus by bylo možné realizovat i jednodušším postupem.

double input(0) vstup;  //promenna ’vstup’ predstavuje hodnotu u0  
double output(0) vystup; //promenna ’vystup’ predstavuje prirazeni do y0  
double stav[20], param[20];  
const long count=20;  
 
long init(void)  
{  
    long i;  
    const double a=0.95;  
    param[0]=0.2;param[5]=0.2;param[10]=0.2;param[12]=0.2;param[15]=0.2;  
    for(i=0;i<count;i++)  
    {  
        param[i]=param[i]+exp(-i*a)/a;  
        Trace(1,param[i]);  
    }  
    return 0;  
}  
 
long main(void)  
{  
    long i;  
    double soucet=0.0;  
    for(i=0;i<count-1;i++)  
        stav[i]=stav[i+1];  
    if(fabs(vstup)>1)  
        stav[count-1]=(vstup>0)? 1 : -1;  
    else  
        stav[count-1]=vstup;  
    for(i=0;i<count;i++)  
    {  
        soucet+=stav[i]*param[count-1-i];  
        Suspend(0.1);  
    }  
    vystup=soucet;  
    return 0;  
}  
 
long exit(void){return 0;}

a tentýž příklad v STL syntaxi:

VAR_INPUT  
  vstup:REAL;  //promenna ’vstup’ predstavuje hodnotu u0  
END_VAR  
 
VAR_OUTPUT  
  vystup:REAL; //promenna ’vystup’ predstavuje prirazeni do y0  
END_VAR  
 
VAR_PARAMETER  
  tt:REAL; //promenna ’tt’ predstavuje hodnotu parametru p0  
END_VAR  
 
VAR  
  param, stav : ARRAY[0 .. 19] OF REAL;  
END_VAR  
 
VAR CONSTANT  
  count:INT := 20;  
END_VAR  
 
FUNCTION init : INT;  
VAR  
  i:INT;  
END_VAR  
 
VAR CONSTANT  
    a:REAL := 0.95;  
END_VAR  
    param[0]:=0.2; param[5]:=0.2; param[10]:=0.2; param[12]:=0.2; param[15]:=0.2;  
    FOR i:=0 TO count-1 DO  
        param[i] := param[i] + EXP(-i*a)/a;  
        Trace(1,param[i]);  
    END_FOR  
 
    init := 0;  
END_FUNCTION  
 
 
FUNCTION main : INT;  
VAR  
  i:INT;  
  soucet:REAL := 0.0;  
END_VAR  
 
    FOR i:=0 TO count-2 DO  
        stav[i] := stav[i+1];  
    END_FOR  
 
    IF abs(vstup)>1 THEN  
IF vstup>0.0 THEN  
            stav[count-1] := 1;  
ELSE  
    stav[count-1] := -1;  
END_IF  
    ELSE  
        stav[count-1] := vstup;  
    END_IF  
 
    FOR i:=0 TO count-1 DO  
        soucet := soucet+stav[i]*param[count-1-i];  
     IF tt>0.0 THEN  
           Suspend(tt);  
        ELSE  
           Suspend(0.1);  
END_IF  
    END_FOR  
 
    vystup := soucet;  
    main := 0;  
END_FUNCTION  
 
FUNCTION exit : INT;  
    exit := 0;  
END_FUNCTION

2023 © REX Controls s.r.o., www.rexygen.com