Viz: objekt
PmaSequencer.
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 pro tuto položku je vyvolána 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 vyvolala. Tímto položka končí.
Dále v konfigurátoru "
Typ spouštění položek" lze nastavit smysl timeoutu, který se zadává v metodě
Add. Lze určit, zda:
- 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
- timeout je zpoždění 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 je vyvolána 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 lze jednu položku fronty volat vícenásobně.
-
Metoda Remove způsobí, že se položka odstraní z fronty bez vyvolání události (tzn. chová se jako by žádná položka nebyla).
Použití jiného vlákna (threadu):
V konfigurátoru "
Použité vlákno (thread)" lze
nastavit vlákno (thread), ve kterém je vyvolá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í! Pokud se použije jiné
vlákno (thread), pak přístup k ostatním objektům ve skriptu probíhá v jiném než v hlavním vlákně a je nutno vzít v úvahu případné problémy se synchronizací při čtení a zápisu do
Pma 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 vyvolat v hlavním vlákně (nedojde k problémům se synchronizací dat) a krok zpracování dat pak lze 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
PmaSequencer.Add. Čtení dat 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
PmaSequencer.Add. Existuje samozřejmě také možnost předávat si data v objektu
PmaData. 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 (
PmaSequencer) 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
PmaSequencer provést následovně:
- Pak zavoláme metodu
Add s parametrem
Timeout=t1 a položce dáme identifikaci například
"A1" do proměnné
Val1. To znamená, že provedeme:
JavaScriptVBScriptVyber a zkopíruj do schránky
oSequencer.Add(t1, "A1");
oSequencer.Add t1, "A1"
Po
t1 sekundách je vyvolána událost
onStep, ve které provedeme akci
A1 a poté zařadíme nový časový požadavek pro akci
A2. To znamená, že v události
onStep může být například následující skript:
Takovým způsobem lze vytvořit libovolně dlouhá 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í bylo vždy zpoždění
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 zpoždění mezi nimi.
Zavoláme
Add pro všechny položky najednou:
JavaScriptVBScriptVyber a zkopíruj do schránky
oSequencer.Add(5, "A1");
oSequencer.Add(5, "A2");
oSequencer.Add(5, "A3");
oSequencer.Add(5, "A4");
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šší skript:
V tom případě se 1.akce
A1 spustí okamžitě (nebo
5 sekund po spuštění aplikace), protože 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ě.
3 Příklad sekvenčního řízení s řízeným uvolňováním položek:
Je požadavek provést 2 akce (
A1,A2) ale aby mezi nimi bylo bezpečnostní zpoždění až
10 sekund. Ta bude zkrácena ve chvíli úspěšného ukončení první akce.
Typ spouštění bude:
1 = položky se spouští postupně, timeout je zpoždění mezi nimi.
Zavoláme metodu
Add pro obě položky najednou:
JavaScriptVBScriptVyber a zkopíruj do schránky
oSequencer.Add(0, "A1");
oSequencer.Add(10, "A2");
oSequencer.Add 0, "A1"
oSequencer.Add 10, "A2"
a v události
onStep pak může být jednodušší skript:
V tom případě se 1. akce
A1 spustí okamžitě. Další položka se spustí za
10 sekund, ale může být spuštěna i dříve, pokud po úspěšném ukončení první akce je volána metoda
Release:
JavaScriptVBScriptVyber a zkopíruj do schránky
// Algoritmus akce A1
oSeq.Release(10, 0);
' Algoritmus akce A1
oSeq.Release 10, 0
4 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
PmaSequencer zařadíme najednou obě akce:
JavaScriptVBScriptVyber a zkopíruj do schránky
oSequencer.Add(t1, "A1");
oSequencer.Add(t2, "A2");
oSequencer.Add t1, "A1"
oSequencer.Add t2, "A2"
V události
onStep pak může být například následující skript:
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říkladu nejsou využity proměnné
Val2 a
Val3, které by šly použít pro dodatečné parametry akce.
5 Příklad asynchronního řízení:
Je požadavek provést akci
A1 až když například příjdou data z komunikace nebo až uživatel 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 pouze maximálně
t1 sekund. V akci
A1 pak chceme rozeznat z jakého důvodu je vyvolána - zda od komunikace (po stisknutí tlačítka ..) 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
PmaSequencer zařadíme položku pro akci
A1:
JavaScriptVBScriptVyber a zkopíruj do schránky
oSequencer.Add(t1, "A1", 111);
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 (po stisknutí tlačítka ...) vložíme následující příkaz:
JavaScriptVBScriptVyber a zkopíruj do schránky
oSequencer.Release(1, 2, "A1", 222);
oSequencer.Release 1, 2, "A1", 222
Tento příkaz znamená, že se má v daném objektu
PmaSequencer 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 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:
V tomto příkladu jsme v podstatě ani nepotřebovali indikaci o akci
A1 (tzn. text
"A1" v proměnné
Val1). Příklad však lze zobecnit tak, že daný objekt
PmaSequencer 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.
6 Příklad časově náročného zpracování v pracovním vlákně:
Je požadavek provést následující algoritmus:
- příprava a předání dat (krok "read")
- časově náročné zpracování dat (krok "work") - například: souborové operace
- získaná zpracovaná data použít (uložit) v aplikaci (krok "write")
JavaScriptVBScriptVyber a zkopíruj do schránky
oSequencer.Add(0, "read", 0, 0, "thread:main;");
oSequencer.Add 0, "read", 0, 0, "thread:main;"
Událost
onStep pro všechny 3 kroky má následující tvar:
JavaScriptVBScriptVyber a zkopíruj do schránky
switch (pEvent.Val1)
{
case "read":
// Vykoná se v hlavním threadu main
// Příprava a vytvoření pole souborů aDataRead ke zpracování v dalšímu kroku
var aDataRead = Pm.CreatePmArray().Array1("Glob.ini", "Cfg1.ini", "Cfg2.ini", "Cfg3.ini");
pMe.Add(0, "work", aDataRead, 0, "thread:work;");
break;
case "work":
// Vykoná se v pracovním threadu work
// 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
var aFiles = pEvent.Val2;
var nFiles = aFiles.GetSize(1);
var aDataWrite = Pm.CreatePmArray(nFiles);
var iFile;
for (iFile = 0; iFile < nFiles; iFile++)
{
aDataWrite.SetItem(Pm.IniFileRead("#cfg:" + aFiles.GetItem(iFile), "MySettings", "value", 0, 4), iFile);
}
pMe.Add(0, "write", aDataWrite, 0, "thread:main;");
break;
case "write":
// Vykoná se v hlavním threadu main
// Zápis výstupních dat v pEvent.Val2 do aplikace
var aValues = pEvent.Val2;
var val1 = aValues.GetItem(0);
// ...
break;
}
Select Case pEvent.Val1
Case "read"
' Vykoná se v hlavním threadu main
' Příprava a vytvoření pole souborů aDataRead ke zpracování v dalšímu kroku
Dim aDataRead
aDataRead = Pm.CreatePmArray().Array1("Glob.ini", "Cfg1.ini", "Cfg2.ini", "Cfg3.ini")
pMe.Add 0, "work", aDataRead, 0, "thread:work;"
Case "work"
' Vykoná se v pracovním threadu work
' 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
Dim aFiles
aFiles = pEvent.Val2
Dim nFiles
nFiles = aFiles.GetSize(1)
Dim aDataWrite
aDataWrite = Pm.CreatePmArray(nFiles)
Dim iFile
For iFile = 0 To nFiles - 1
aDataWrite.SetItem Pm.IniFileRead("#cfg:" & aFiles(iFile), "MySettings", "value", 0, 4), iFile
Next
pMe.Add 0, "write", aDataWrite, 0, "thread:main;"
Case "write"
' Vykoná se v hlavním threadu main
' Zápis výstupních dat v pEvent.Val2 do aplikace
Dim aValues
aValues = pEvent.Val2
Dim val1
val1 = aValues(0)
' ...
End Select
Čtení i zápis dat aplikace má být provedeno bezpečným způsobem z hlediska synchronizace dat (pomocí hlavního vlákna), zatímco náročné zpracování dat má být provedeno na pozadí v jiném vlákně (optimalizace aplikace). V kartě "
Sequencer" objektu zvolíme
Typ spouštění položek=1 = položky se spouští postupně, timeout je zpoždění 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ří voláním metody
Add s identifikací kroku
"read" v parametru
Val1.
Takovým způsobem lze vytvořit libovolně dlouhá 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
PmaSequencer 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.