Patrz: obiekt
PmaSequencer.
Zasada działania obiektu:
Do obiektu można dodawać tzw. pozycje, którymi są struktury danych o 3 zmiennych użytkownika
Val1,
Val2 oraz
Val3. Dodanie pozycji jest wykonywane przy pomocy metody
Add. Pozycja, które jest dodana do obiektu, jest zaszeregowana do kolejki pozycji tego obiektu i w kolejce czeka przez określony czas (patrz parametr
Timeout w metodzie
Add, wymagany czas może być zerowy lub nieskończenie długi). Po upływie określonego czasu pozycja zostanie ucunięta z kolejki i dla tej pozycji zostanie wywołane zdarzenie
onStep. W parametrach tego zdarzenia można stwierdzić (ze zmiennych
pEvent.Val1,
pEvent.Val2 oraz
pEvent.Val3), dla której pozycji zostało zdarzenie wywołane. W ten sposób dana pozycja jest zakończona.
Ponadto w konfiguratorze "
Typ uruchamiania elementów" można ustawić znaczenie timeoutu, który określa się w metodzie
Add. Można określić, czy:
- timeout jest czasem od dodania pozycji do kolejki do jej uruchomienia (tzn. Pozycje są uruchamiane niezależnie) lub
- timeout jest opóźnieniem czasowym pomiędzy daną i poprzednio uruchomioną pozycją (tzn. Pozycje są uruchamiane kolejno).
Pozycję opczekującą w kolejce można zakończyć przedwcześnie (tzn. przed upływem jej czasu) przy pomocy metody
Release lub
Remove:
-
Metoda Release spowoduje, że pozycja zostanie usunięta z kolejki i zostanie wywołane zdarzenie
onStep (tzn. tak, jakby doszło do przedwczesnego upływu czasu).
W zdarzeniu
onStep można wtedy ustawić
pEvent.ReleaseCancel=true a tym zkasować usunięcie z kolejki - w taki sposób można jedną pozycję kolejki wołać kilkakrotnie.
-
Metoda Remove spowoduje, że pozycja zostanie usunięta z kolejki bez wywołania zdarzenia (tzn. tak, jakby żadnej pozycji nie było).
Zastosowanie innego wątku (threadu):
W konfiguratorze "
Zastosowany wątek (thread)" można
ustawić wątek (thread), w którym zostanie wywołane zdarzenie
onStep. Można ustawić albo główny wątek lub wytworzenie nowego wątku roboczego dla tego obiektu, patrz
Jak uruchamiać skrypty w innym wątku (threadu).
Uwaga! Jeżeli zostanie użyty inny
wątek (thread), wtedy dostęp do pozostałych obiektów w skrypcie przebiega w innym niż w wątku głównym i jest konieczne brać pod uwagę ewentualne problemy synchronizacji przy odczycie i zapisie do
Pma obiektów. Opcja ta jest stosowna zwłaszcza do czasochłonnych czynności obliczeniowych wykonywanych w tle.
Problem z synchronizacją (spójnością) odczytu i zapisu danych (przy zastosowaniu innego wątku) można rozwiązać na kilka sposobów.
Jedny z nich jest rozdzielenie całej operacji na 3 faze. Mianowicie odczyt danych z aplikacji, obróbka danych oraz zapis rezultatu do aplikacji (każda faza jest zaimplementowany przez wywołanie zdarzenia
onStep). Fazę odczytu i zapisu można wywołać w głównym wątku (nie dochodzi do problemów z synchronizacją danych) natomiast fazę obróbki danych można wywołać w wątku roboczym. Określenie, czy faza ma zostać wykonana w głównym czy też w roboczym wątku można określić w parametrze
Params metody
PmaSequencer.Add. Odczyt danych z aplikacji i zapis danych z powrotem do aplikacji wtedy przebiega w wątku głównym a właściwa obróbka danych przebiega w osobnym wątku roboczym i nie wstrzymuje głównego wątku. Poszczególne fazy muszą przekazywać sobie odczytane i opracowane dane. Najprostszym do przekazania jest wytworzenie w skrypcie danej fazy (zdarzenie
onStep) tablicy pomocniczej i przekazanie jej w parametrze
Val2 metody
PmaSequencer.Add. Oczywiście istnieje również możliwość przekazywania danych w obiekcie
PmaData. Zastosowanie patrz
Przykład czasochłonnej obróbki w wątku roboczym.
Na podstawie wyżej opisanej zasady można wytworzyć kilka konstrukcji, które można stosować do obróbki sekwencyjnej, czasowej lub asynchronicznej. Z nazwy obiektu (
PmaSequencer) wypływa, że jest przeznaczony przede wszystkim do obróbki sekwencyjnej, lecz przedstawimy także jego następne możliwości:
1 Przykład obróbki sekwencyjnej:
Istnieje wymóg wykonania następującego algorytmu:
- odczekać t1 sekund, po czym wykonać czynność A1
- odczekać t2 sekund, po czym wykonać czynność A2, itd.
To można przy pomocy obiektu
PmaSequencer wykonać następująco:
- Po czym wywołaj metodę
Add z parametrem
Timeout=t1 a pozycji przekażemy dane do identyfikacji na przykład
"A1" do zmiennej
Val1. To znaczy, że zostanie wykonane:
JavaScriptVBScriptWybierz oraz skopiuj do schowka
oSequencer.Add(t1, "A1");
oSequencer.Add t1, "A1"
Po
t1 sekundach zostanie wywołane zdarzenie
onStep, w którym zostanie wykonana czynność
A1 po czym zostanie dodana nowa pozycja czasowa do wykonania czynności
A2. To znaczy, że w zdarzeniu
onStep może być na przykład następujący skrypt:
W ten sposób można wytworzyć dowolnie długą sekwencyjną kolejkę czynności, która może być także uwarunkowana (np. w przypadku bieżących warunków po czynności
A1 nie jest wykonana czynność
A2 lecz bezpośrednio czynność
A3). Zaletą jest fakt, że wszystkie czynności znajdują się w jednym skrypcie (w zdarzeniu
onStep) i jest więc w przejrzysty sposób widoczna cała sekwencja czynności. W przejściach pomiędzy czynnościami można zapisywać wewnętrzne stany do zmiennych
Val2 oraz
Val3 (w przykładzie nie są zastosowane).
2 Przykład obróbki sekwencyjnej:
Istnieje wymóg wykonania 4 czynności (
A1,A2,A3,A4) w taki sposób, żeby czas pomiędzy ich obróbką zawsze wynosił
5 sekund.
Ten przypadek jest podobny do poprzedniego, lecz można to wytworzyć z typem uruchamiania:
1 = pozycje są uruchamiane stopniowo, timeout jest opóźnieniem pomiędzy nimi.
W
Add wywołane zostaną wszystkie pozycje jednocześnie:
JavaScriptVBScriptWybierz oraz skopiuj do schowka
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"
i w zdarzeniu
onStep może się wtedy znajdować prostszy skrypt:
W tym przypadku 1. czynność
A1 jest uruchomiona bezpośrednio (lub
5 sekund po uruchomieniu aplikacji), ponieważ poprzednia pozycja nie istniała i czas uruchomienia poprzedniej pozycji jest ustawiony na czas uruchomienia aplikacji. Następne pozycje są jednak uruchamiane w 5 sekundowych odcinkach czasu po sobie.
3 Przykład obróbki sekwencyjnej wraz z kontrolowanym zwalnianiem pozycji:
Istnieje wymóg wykonania 2 czynności (
A1,A2) w taki sposób, żeby czas pomiędzy ich obróbką wynosił aż
10 sekund. Ten zostanie skrócony w chwili zakończenia pierwszej obróbki.
Typ uruchomienia będzie:
1 = pozycje są uruchamiane stopniowo, timeout jest opóźnieniem pomiędzy nimi.
Wywołanie metody
Add dla obu pozycji jednocześnie:
JavaScriptVBScriptWybierz oraz skopiuj do schowka
oSequencer.Add(0, "A1");
oSequencer.Add(10, "A2");
oSequencer.Add 0, "A1"
oSequencer.Add 10, "A2"
i w zdarzeniu
onStep może się wtedy znajdować prostszy skrypt:
W tym przypadku 1. czynność
A1 jest uruchomiona bezpośrednio. Następna pozycja zostanie uruchomiona za
10 sekund, ale może zostać uruchomiona wcześniej, jeżeli po poprawnym zakończeniu pierwszej obróbki jest wywołana metoda
Release:
JavaScriptVBScriptWybierz oraz skopiuj do schowka
// Algorytm operacji A1
oSeq.Release(10, 0);
' Algorytm operacji A1
oSeq.Release 10, 0
4 Przykład obróbki czasowej:
Istnieje wymóg wykonania jednej czynności
A1 za
t1 sekund a drugiej czynności
A2 za
t2 sekund, itd. To można wykonać następująco: Typ uruchamiania będzie
0 = pozycje są uruchamiane niezależnie, każdy według swego timeouta i do obiektu
PmaSequencer jednocześnie zaszeregujemy obie czynności:
JavaScriptVBScriptWybierz oraz skopiuj do schowka
oSequencer.Add(t1, "A1");
oSequencer.Add(t2, "A2");
oSequencer.Add t1, "A1"
oSequencer.Add t2, "A2"
W zdarzeniu
onStep wtedy moży znajdować się na przykład następujący skrypt:
Sytuacja ta jest analogiczna do obróbki sekwencyjnej z tą różnicą, że czynności nie są zależne od siebie lecz każda jest wykonywana samodzielnie. W przykładzie ponownie nie są zastosowane zmienne
Val2 oraz
Val3, które można wykorzystać do przekazania dodatkowych parametrów czynności.
5 Przykład obróbki asynchronicznej:
Istnieje wymóg wykonania czynności
A1 na przykład dopiero po odbiorze danych z komunikacji lub kiedy użytkownik naciśnie dany przycisk, lub ..., po prostu w chwili kiedy dojdzie do danej operacji asynchronicznej. Jednak nie można czekać nieskończenie długo do powstania takiej operacji asynchronicznej lecz tylko najwyżej
t1 sekund. W czynności
A1 wtedy chcemy rozeznać, z jakiego powodu zostanie wywołane - na podstawie odczytu danych z komunikacji (po naciśnięciu przycisku ..) lub z powodu upływu czasu. To można wykonać następująco:
Typ uruchomienia będzie ustawiony na
0 = pozycje są uruchamiane niezależnie, każdy według swego timeouta i do obiektu
PmaSequencer zaszeregujemy pozycję do obróbki czynności
A1:
JavaScriptVBScriptWybierz oraz skopiuj do schowka
oSequencer.Add(t1, "A1", 111);
oSequencer.Add t1, "A1", 111
W ten sposób do obiektu zostanie zaszeregowana pozycja, której zmienna
Val1 będzie tekstem
"A1" a zmienna
Val2 będzie liczbą
111.
Do skryptu asynchronicznego zdarzenia od końca transmisji (po naciśnięciu przycisku ...) wpiszmy następujące polecenie:
JavaScriptVBScriptWybierz oraz skopiuj do schowka
oSequencer.Release(1, 2, "A1", 222);
oSequencer.Release 1, 2, "A1", 222
To polecenie oznacza, że w danym obiekcie
PmaSequencer ma zostać zwolniona pozycja, której zmienna
Val1 (= parametr 1) jest równa tekstu
"A1" a przy tym zwolnieniu do tej pozycji ma zostać przyszeregowana do zmiennej
Val2 (= parametr 2) liczba
222 (która przepisze pierwotnie ustawioną liczbę
111).
W zdarzeniu
onStep może wtedy znajdować się na przykład następujący skrypt:
JavaScriptVBScriptWybierz oraz skopiuj do schowka
switch (pEvent.Val2)
{
case 111:
// Algorytm operacji A1 - jeżeli doszło do upływu timeoutu
break;
case 222:
// Algorytm operacji A1 - jeżeli doszło do zdarzenia asynchronicznego
break;
}
Select Case pEvent.Val2
Case 111
' Algorytm operacji A1 - jeżeli doszło do upływu timeoutu
Case 222
' Algorytm operacji A1 - jeżeli doszło do zdarzenia asynchronicznego
End Select
W tym przykładzie w zasadzie nie było potrzebne wskazanie o czynności
A1 (tzn. tekst
"A1" w zmiennej
Val1). Przykład jednak można uogólnić w ten sposób, że dany obiekt
PmaSequencer będzie potrafić opracowywać wiele asynchronicznych czynności i wtedy w zdarzeniu
onStep było by konieczne rozróżnianie według wartości zmiennej
Val1.
6 Przykład czasochłonnej obróbki w wątku roboczym:
Istnieje wymóg wykonania następującego algorytmu:
- przygotowanie oraz przekazanie danych (faza "read")
- czasochłonne obliczenie danych (faza "work") - na przykład: operacje plikowe
- zapis uzyskanych obliczonych danych w aplikacji (faza "write")
JavaScriptVBScriptWybierz oraz skopiuj do schowka
oSequencer.Add(0, "read", 0, 0, "thread:main;");
oSequencer.Add 0, "read", 0, 0, "thread:main;"
Zdarzenie
onStep dla wszystkich 3 faz wygląda następująco:
JavaScriptVBScriptWybierz oraz skopiuj do schowka
switch (pEvent.Val1)
{
case "read":
// Zostanie wykonane w głównym wątku main
// Przygotowanie oraz wytworzenie tablicy plików aDataRead do opracowania w następnej fazie
var aDataRead = Pm.CreatePmArray().Array1("Glob.ini", "Cfg1.ini", "Cfg2.ini", "Cfg3.ini");
pMe.Add(0, "work", aDataRead, 0, "thread:work;");
break;
case "work":
// Zostanie wykonane w wątku roboczym work
// Opracowanie danych wejściowych w pEvent.Val2 na dane wyjściowe w tablicy aDataWrite i przekazanie danych wyjściowych do dalszej fazy
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":
// Zostanie wykonane w głównym wątku main
// Zapis danych wyjściowych w pEvent.Val2 do aplikacji
var aValues = pEvent.Val2;
var val1 = aValues.GetItem(0);
// ...
break;
}
Select Case pEvent.Val1
Case "read"
' Zostanie wykonane w głównym wątku main
' Przygotowanie oraz wytworzenie tablicy plików aDataRead do opracowania w następnej fazie
Dim aDataRead
aDataRead = Pm.CreatePmArray().Array1("Glob.ini", "Cfg1.ini", "Cfg2.ini", "Cfg3.ini")
pMe.Add 0, "work", aDataRead, 0, "thread:work;"
Case "work"
' Zostanie wykonane w wątku roboczym work
' Opracowanie danych wejściowych w pEvent.Val2 na dane wyjściowe w tablicy aDataWrite i przekazanie danych wyjściowych do dalszej fazy
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"
' Zostanie wykonane w głównym wątku main
' Zapis danych wyjściowych w pEvent.Val2 do aplikacji
Dim aValues
aValues = pEvent.Val2
Dim val1
val1 = aValues(0)
' ...
End Select
Odczyt i zapis danych aplikacji ma być wykonany w bezpieczny sposób z punktu widzenia synchronizacji danych (przy pomocy głównego wątku), w odróżnieniu od czasochłonnej obróbki, które ma być wykonana na tle w innym wątku (optymalizacja aplikacji). W karcie "
Sequencer" obiektu trzeba ustawić
Typ uruchamiania elementów=1 = pozycje są uruchamiane stopniowo, timeout jest opóźnieniem pomiędzy nimi, następnie trzeba ustawić
Zastosowany wątek (thread)=Normal = nowy wątek roboczy o normalnym pryorytecie. Wymóg pierwszej fazy (główny wątek) jest wytworzony przez wywołanie metody
Add z identyfikatorem fazy
"read" w parametrze
Val1.
W ten sposób można wytworzyć dowolnie długą sekwencję faz, gdzie poszczególne fazy mogą być wywoływane zarówno w głównym jak również w roboczym wątku. Zaletą jest, że wszystkie fazy znajdują się w jednym skrypcie (w zdarzeniu
onStep) i jest w przejrzysty sposób widoczna cała sekwencja czynności. W przejściach pomiędzy fazami można przechowywać wewnętrzne stany do zmiennych
Val2 oraz
Val3.
Powyższe przykłady ilustrują w jaki sposób można stosować obiekt
PmaSequencer. Można wytwarzać również opracowywanie mieszane, na przykład pomiędzy czynnościami
A1 oraz
A2 odczekać bezpośrednio
t1 sekund, pomiędzy czynnościami
A2 oraz
A3 odczekać na odbiór danych i następnie uruchomić dwie równoległe czynności
A3 oraz
A4 za
t3 oraz
t4 sekund, itd.