cz
en

Cykly

Cykly jsou přesně ten důvod, proč použít jazyk ST. Použití jednoduchého cyklu si ukážeme na výpočtu průměrné hodnoty za posledních 15 minut. Jde o průběžný nevážený průměr.

Proměnná MINUTY je typu Analogový posuvný registr (B110_SHIFT_REGISTER), deklarací na řádku 6 se vlastně vkládá a pojmenovává funkční blok tohoto typu. Pokud nastane celá minuta, tedy ukazatel vteřin SINT1 = 0, náběžná hrana proměnné SHIFTNOW posune hodnoty v posuvném registru o jednu dál a na vstup IN vloží aktuální hodnotu výkonu. V posuvném registru jsou tedy vzorky hodnot po minutách za posledních (alespoň) 15 minut (posuvný registr má 24 pozic, ale budeme potřebovat jen prvních 15). V cyklu FOR…END_FOR (viz řádky 20 až 22) se posledních 15 hodnot sečte a v následujícím řádku se vydělením spočítá průměr.

Další typy cyklů, tedy WHILE a REPEAT, mají podobnou syntaxi:

WHILE J<10 DO
J:=J+2;
END_WHILE;

REPEAT
J:=J+2;
UNTIL J>10;

Liší se zejména tím, že u REPEAT se podmínka vyhodnocuje až na konci, tedy cyklus proběhne alespoň jednou. Podmínka by měla být napsána „bezpečně“, aby se nemohlo stát, že bude přeskočena a cyklus se bude opakovat do nekonečna.

Následující příklad by bez použití cyklů šel realizovat jen velmi obtížně:

Stupňové spínání se záskokem a rotací
Pro řízení skupiny agregátů, jako je kaskáda kotlů, sada tepelných čerpadel, nebo jen vícestupňový el. ohřev, se hodí blok se střídáním výstupů pro pravidelnější opotřebení a záskokem v případě, že jsou některé z agregátů nefunkční. V příkladu níže je upravený text standardního bloku T14. Vidíme v něm i práci s poli, ačkoli pole nejsou deklarována klasicky

test : ARRAY [0..15] OF BOOL;

Ale využívají knihovního typu MultiIoBool, což je typ v knihovně Lib.Core.V1_0  deklarovaný takto:

TYPE
       MultiIoBool : ARRAY[0..15] OF BOOL;
END_TYPE

Celý kód pak vypadá takto:

FUNCTION_BLOCK t14_light
    VAR
        BxPrev : BOOL;
        BxfPrev : BOOL;
        i, j : INT;
        Set : INT;
        InOKRot, OutDerot : Lib.Core.V1_0.MultiIoBool := [16(false)];
    END_VAR
    VAR_INPUT
        Bx  : BOOL := FALSE;
        Bxf : BOOL := FALSE;
// seznam agregátů schopných funkce (odvozeno např. z alarmových bloků)
        InOK : Lib.Core.V1_0.MultiIoBool := [true, 15(false)];
        NumOfActive, NumOfOutputs : INT := 1;                    
// NumOfActive: kolik výstupů má být aktivních (např. požadavek z PI regulátoru)
// NumOfOutputs: celkový instalovaný počet agregátů (konstanta)
    END_VAR                                                       
    VAR_OUTPUT
        Out : Lib.Core.V1_0.MultiIoBool;
    END_VAR
    VAR RETAIN
        Shift : INT; // O kolik jsou výstupy rotovány kvůli střídání
    END_VAR

    METHOD IncrementShift
        Shift := Shift + 1;
        IF Shift > NumOfOutputs - 1 THEN
            Shift := 0;
        END_IF;
    END_METHOD
    
    IF NOT BxPrev AND Bx THEN  //detekce náběžné hrany pro rotaci
        IncrementShift();
    END_IF;
    IF BxfPrev AND NOT Bxf THEN
        IncrementShift();
    END_IF;
    
    FOR i := 1 TO 16 DO   // vynulování výstupního pole
        Out[i] := False;
    END_FOR;

//    derotace seznamu připravených vstupů o Shift, takže výstupy mohou být aktivovány počínaje 1
    FOR j := 1 TO NumOfOutputs DO
        IF (j - Shift < 1) THEN
            InOKRot[(j - Shift + NumOfOutputs)] := InOK[j]
        ELSE
            InOKRot[(j - Shift)] := InOK[j]
        END_IF;
    END_FOR;
    
// vynulování výstupního pole a dalších pomocných proměnných
    FOR i := 1 TO 16 DO   
        OutDerot[i] := False;    
    END_FOR;
    Set := 0;                                        
    i := 0;

 // nastavení počtu aktivních výstupů při přeskočení nefunkčních - díky derotaci začínáme od 1
    WHILE NOT (Set >= NumOfActive OR i > NumOfOutputs) DO
          IF InOKRot[i] THEN                      
            OutDerot[i] := True;
            Set := Set + 1;                
          END_IF;
            i := i + 1;
    END_WHILE;

// Rotace výstupů zpět k fyzicky správnému pořadí
    FOR j := 1 TO NumOfOutputs DO
        IF (j - Shift < 1) THEN
            Out[j] := OutDerot[(j - Shift + NumOfOutputs)]
        ELSE
            Out[j] := OutDerot[(j - Shift)]
        END_IF;
    END_FOR;
    
    BxPrev := Bx;    // pro detekci náběžné či sestupné hrany pro Shift
    BxfPrev := Bxf;
        
END_FUNCTION_BLOCK

Princip je následující:

  • Při náběžné hraně na vstupu Bx (nebo sestupné na vstupu Bxf) zvýšíme o jednu posuv (Shift), a to až do max. počtu použitých výstupů, načež hodnota přetéká do 0. Na vstup jsou přiváděny impulzy v pravidelných intervalech nebo na základě ručního požadavku pro střídání. To zajišťuje pravidelnou rotaci agregátů a tím jejich rovnoměrné opotřebení.
  • Vektor vstupů připravených k provozu je rotován proti směru o Shift, takže první vstup, který se má sepnout, je na první pozici.
  • Aktivujeme postupně výstupy, dokud není aktivní požadovaný počet, přičemž nefunkční agregáty jsou přeskakovány (funkce záskok).
  • Vektor výstupu je následně rotován po směru, takže výstupy, které se mají sepnout, jsou posunuty o Posuv.

V programu pak použití bloku vypadá takto:

Proměnná ActiveOutputs obsahuje požadavek na počet aktivních výstupů. Celkový počet agregátů je konstantou a proto není tento vstup vyveden. Pomocný blok Bool_to_MultiIoBool převádí příslušný počet proměnných typu bool (zde 8) na pole binárních proměnných, což je datový typ, který je využit ve standardním bloku T14, viz deklarace

InOKRot, OutDerot : Lib.Core.V1_0.MultiIoBool := [16(false)];

Zdrojový text pomocného bloku je velmi jednoduchý:

FUNCTION_BLOCK Bool_to_MultiIoBool
    VAR
        i: int;
    END_VAR
    VAR_INPUT
        in1, in2, in3, in4, in5, in6, in7, in8: bool; // může být i více
    END_VAR
    VAR_OUTPUT
        out : Lib.Core.V1_0.MultiIoBool;
    END_VAR
out[1] := in1;
out[2] := in2;
out[3] := in3;
out[4] := in4;
out[5] := in5;
out[6] := in6;
out[7] := in7;
out[8] := in8; // atd.
END_FUNCTION_BLOCK

Určitě by šlo se v bloku t14_light obejít i bez pole a deklarovat vstupy „natvrdo“, ale stejně bychom je museli uvnitř vložit do nějakého pole kvůli rotačním operacím a cyklům. Navíc datový typ MultiIoBool umožňuje v bloku použít dynamické výstupy:

a tím snadno modifikovat počet viditelných výstupů bloku v grafice FUPLA.

Na závěr ještě poznámka k nastavení výchozích hodnot pole při deklaraci:

test : ARRAY [0..15] OF BOOL := [16(false)];

znamená, že všech 16 pozic pole má výchozí hodnotu False. Výchozí hodnoty lze zapsat buď každou zvlášť, nebo více najednou. Oba způsoby lze kombinovat:

test : ARRAY [0..15] OF BOOL := [true, 15(false)];

nastaví první pozici na True a ostatních 15 na False.

 Všechny díly