Promotic
WikipediaLinkedInYoutubeTwitterFacebook

PmSequencer - Podrobný popis objektu

Viz: Objekt PmSequencer
 
Princip objektu:

Do objektu lze přidávat tzv. položky, což jsou datové struktury o 3 uživatelských proměnných Val1, Val2 a Val3. Přidání položky se provádí metodou Add. Položka, která se přidá do objektu, se zařadí do fronty položek tohoto objektu a v této frontě čeká požadovanou dobu (viz parametr Timeout v metodě Add, požadovaná doba může být nulová nebo nekonečně dlouhá). Po vypršení požadované doby se položka odstraní z fronty a spustí se pro tuto položku událost onStep. V parametrech této události lze zjistit (z proměnných pEvent.Val1, pEvent.Val2 a pEvent.Val3), pro kterou položku se tato událost spustila. Tímto položka končí.

Dále lze v konfigurační položce Typ spouštění položek nastavit smysl timeoutu, který se zadává v metodě Add. Lze určit, zda požadovaný timeout je doba mezi zařazaním položky a jejím spuštěním (tzn. Položky se spouští nezávisle) nebo je timeout prodleva mezi spuštěním této položky a předchozí spuštěné položky (tzn. Položky se spouští postupně).

Položku čekající ve frontě lze předčasně ukončit (tzn. dřív než vyprší její čas) metodou Release nebo Remove:

- Metoda Release způsobí, že se položka odstraní z fronty a vyvolá se událost onStep (tzn. chová se jako by předčasně vypršel čas).

V události onStep lze tehdy nastavit pEvent.ReleaseCancel=true a tím odstranění z fronty zrušit - tak je možné jednu položku fronty volat vícenásobně.

- Metoda Remove způsobí, že se položka odstraní z fronty bez volání události (tzn. chová se jako by žádná položka nebyla).
 
Použití jiného vlákna:

V konfigurační položce Použité vlákno (thread) lze nastavit vlákno (thread), ve kterém bude spuštěna událost onStep. Lze nastavit buď hlavní vlákno nebo vytvoření nového pracovního vlákna pro tento objekt, viz Jak spouštět vybrané skripty v jiném vlákně (threadu).

Upozornění! Použije-li se jiné vlákno (thread), pak přístup k ostaním objektům ze skriptu probíhá v jiném než hlavním vlákně a je nutno vzít v úvahu případné problémy se synchronizací při čtení a zápisu do Promotic objektů. Tato volba je vhodná zejména pro časově náročné výpočetní úkony na pozadí.

Problém se synchronizací (konsistencí) čtení a zápisu dat (při použití jiného vlákna) lze řešit několika způsoby. Jedním ze základních způsobů je rozdělení celé činnosti na 3 kroky. A to čtení dat z aplikace, zpracování dat a zápis výsledných dat do aplikace (každý krok je implementován vyvoláním události onStep). Krok čtení a krok zápisu pak lze nechat vyvolat v hlavním vlákně (nedojde k problémům se synchronizací dat) a krok zpracování dat pak lze nechat vyvolat v pracovním vlákně. Určení, zda má být krok proveden v hlavním nebo pracovním vlákně lze určit v parametru Params metody PmSequencer.Add. Čtení dat z aplikace a zápis dat zpět do aplikace pak probíhají v hlavním vlákně a vlastní zpracování dat pak probíhá v samostatném pracovním vlákně a nezdržuje hlavní vlákno. Jednotlivé kroky si musí předávat vyčtená a zpracovávaná data. Nejjednodušší k předání je vytvořit ve skriptu daného kroku (událost onStep) pomocné pole a předat je jako parametr Val2 metody PmSequencer.Add. Existuje samozřejmě také možnost předávat si data v objektu PmData. Použití viz Příklad časově náročného zpracování v pracovním vlákně.

 
Z výše popsaného jednoduchého principu lze sestrojit několik konstrukcí, které se výhodně používají pro sekvenční, časové nebo asynchronní řízení. Z názvu objektu (PmSequencer) vyplývá, že je určen především pro řízení sekvenční ale ukážeme i další jeho možnosti:
 
1.příklad sekvenčního řízení:

Je požadavek provést následující algoritmus: počkat t1 sekund, pak udělat akci A1, počkat t2 sekund a pak udělat akci A2, atd. Toto lze pomocí objektu PmSequencer provést následovně: V záložce Sequencer objektu zvolíme Typ spouštění položek=0 = položky se spouští nezávisle, každá podle svého timeoutu. Pak zavoláme metodu Add s parametrem Timeout=t1 a položce dáme identifikaci například "A1" do proměnné Val1. Tj. provedeme:

oSequencer.Add t1, "A1"

Po t1 sekundách se zavolá událost onStep, ve které provedeme akci A1 a poté zařadíme nový časový požadavek pro akci A2. Tzn. v události onStep může být například následující skript:

  Case "A1"
    ... algoritmus akce A1...
    pMe.Add t2, "A2"
  Case "A2"
    ... algoritmus akce A2...
    pMe.Add t3, "A3"
    ...
End Select

Takovým způsobem můžeme tvořit libovolně dlouhou sekvenční posloupnost akcí, která může být i podmíněná (např. za určitých podmínek se po akci A1 neprovede akce A2 ale hned akce A3). Výhoda je, že všechny akce máme v jednom skriptu (v události onStep) a je tedy přehledně vidět celá sekvenční posloupnost. V přechodech mezi akcemi lze uchovávat vnitřní stavy do proměnných Val2 a Val3 (v příkladu jsou nevyužity).

 
2.příklad sekvenčního řízení:

Je požadavek provést 4 akce (A1,A2,A3,A4) ale aby mezi jejich provádění byla vždy prodleva 5 sekund. Je to podobné jako v předchozím případě, vytvoříme to však nyní s typem spouštění: 1 = položky se spouští postupně, timeout je prodleva mezi nimi. Zavoláme Add pro všechny položky najednou:

oSequencer.Add 5, "A1"
oSequencer.Add 5, "A2"
oSequencer.Add 5, "A3"
oSequencer.Add 5, "A4"

a v události onStep pak může být jednodušší script:

  Case "A1"
    ... algoritmus akce A1...
  Case "A2"
    ... algoritmus akce A2...
    .. atd. ..
End Select

V tomto případě se 1.akce A1 spustí okamžitě (nebo 5 sekund po spuštění runtime) neboť předchozí položka neexistovala a čas spuštění předchozí položky je tudíž nastaven na čas spuštění runtime. Další položky se však spouští v 5 sekundových intervalech po sobě.

 
Příklad časového řízení:

Je požadavek provést jednu akci A1 za t1 sekund a druhou akci A2 za t2 sekund, atd. Provedeme to následovně: Typ spouštění bude 0 = položky se spouští nezávisle, každá podle svého timeoutu a do objektu PmSequencer zařadíme najednou obě akce:

oSequencer.Add t1, "A1"
oSequencer.Add t2, "A2"

V události onStep pak může být například následující skript:

  Case "A1"
    ... algoritmus akce A1...
  Case "A2"
    ... algoritmus akce A2...
End Select

Je to podobné sekvenčnímu řízení s tím rozdílem, že akce na sobě nejsou závislé ale každá se provede samostatně. Opět v příkladě nejsou využity proměnné Val2 a Val3, které by šly použít pro dodatečné parametry akce.

 
Příklad asynchronního řízení:

Je požadavek provést akci A1 až když například příjdou data z komunikace nebo až operátor stiskne danou klávesu, nebo ..., zkrátka až když nastane nějaká asynchronní operace. Na tuto asynchronní operaci však nechceme čekat nekonečně dlouho ale jen maximálně t1 sekund. V akci A1 pak chceme rozeznat z jakého důvodu byla spuštěna - zda od komunikace (od stisku klávesy ..) nebo od vypršení timeoutu. Provedeme to následovně:

Typ spouštění bude 0 = položky se spouští nezávisle, každá podle svého timeoutu a do objektu PmSequencer zařadíme položku pro akci A1:

oSequencer.Add t1, "A1", 111

Tímto se do objektu zařadí položka, jejíž proměnná Val1 bude text "A1" a proměnná Val2 bude číslo 111.

Do skriptu asynchronní události od konce přenosu (od stisku klávesy ..) vložíme následující příkaz:

oSequencer.Release 1, 2, "A1", 222

Tento příkaz znamená, že se má v daném objektu PmSequencer uvolnit položka, jejíž proměnná Val1 (=parametr 1) je rovna textu "A1" a při tomto uvolňování se této položce má přiřadit do její proměnné Val2 (= parametr 2) číslo 222 (které přepíše původně nastavené číslo 111).

V události onStep pak může být například následující skript:

  Case 111
    ... algoritmus akce A1 pokud vypršel timeout
  Case 222
    ... algoritmus akce A1 pokud nastala asynchronní událost
End Select

V tomto příkladu jsme v podstatě ani nepotřebovali indikaci o akci A1 (tzn. text "A1" v proměnné Val1). Příklad je však možno zobecnit tak, že daný objekt PmSequencer bude umět zpracovávat více asynchronních akcí a pak bychom v události onStep ještě museli rozlišovat podle hodnot proměnné Val1.

 
Příklad časově náročného zpracování v pracovním vlákně:

Je požadavek provést následující algoritmus: načíst data z aplikace (object PmData)(krok read), přepočítat data (krok work) a zapsat je přepočtené zpět. Čtení i zápis dat aplikace má být provedeno bezpečným způsobem z hlediska synchronizace dat (použít hlavní vlákno), zatímco náročné zpracování dat má být provedeno na pozadí v jiném vlákně (optimalizace aplikace). V záložce Sequencer objektu zvolíme Typ spouštění položek=1 = položky se spouští postupně, timeout je prodleva mezi nimi, dále zvolíme Použité vlákno (thread)=Normal = nové pracovní vlákno normální priority. Požadavek na první krok (hlavní vlákno) se vytvoří zavoláním metody Add s identifikací kroku "read" v parametru Val1.

oSequencer.Add 0, "read", 0, 0, "thread:main;"

Událost onStep pro všechny 3 kroky má následující tvar:

  Case "read"
    Dim aDataRead(100)
    ... vyčtení dat z aplikace do pole aDataRead a předání vstupních dat dalšímu kroku
    pMe.Add 0, "work", aDataRead, 0, "thread:work;"
  Case "work"
    Dim aDataWrite(100)
    ... zpracovávání vstupních dat v pEvent.Val2 na výstupní data v poli aDataWrite a předání výstupních dat dalšímu kroku
    pMe.Add 0, "write", aDataWrite, 0, "thread:main;"
  Case "write"
    ... zápis výstupních dat v pEvent.Val2 do aplikace
End Select

Takovým způsobem můžeme tvořit libovolně dlouhou sekvenční posloupnost kroků, kde jednotlivé kroky mohou být vyvolávany jak v hlavním, tak pracovním vlákně. Výhoda je, že všechny kroky máme v jednom skriptu (v události onStep) a je tedy přehledně vidět celá sekvenční posloupnost. V přechodech mezi kroky lze uchovávat vnitřní stavy do proměnných Val2 a Val3.

 
Výše předložené příklady ukazují jak objekt PmSequencer používat. Lze vytvářet i smíšené řízení, například mezi akcemi A1 a A2 čekat natvrdo t1 sekund, mezi akcemi A2 a A3 čekat na příjem dat a následně pustit dvě paralelní akce A3 a A4 za t3 a t4 sekund, atd.
© MICROSYS, spol. s r. o.Tavičská 845/21 703 00 Ostrava-Vítkovice