Promotic

PmaSequencer - Szczegółowy opis obiektu

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ć 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ć w następujący sposób:
W karcie "Sequencer" obiektu obierz Typ uruchamiania elementów=0 = pozycje są uruchamiane niezależnie, każdy według swego timeouta.
- 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");

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:
JavaScriptVBScriptWybierz oraz skopiuj do schowka

switch (pEvent.Val1)
{
case "A1":
// Algorytm operacji A1
pMe.Add(t2, "A2");
break;
case "A2":
// Algorytm operacji A2
pMe.Add(t3, "A3");
break;
case "A3":
// ...
break;
}

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");

i w zdarzeniu onStep może się wtedy znajdować prostszy skrypt:
JavaScriptVBScriptWybierz oraz skopiuj do schowka

switch (pEvent.Val1)
{
case "A1":
// Algorytm operacji A1
break;
case "A2":
// Algorytm operacji A2
break;
case "A3":
// ...
break;
}

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");

i w zdarzeniu onStep może się wtedy znajdować prostszy skrypt:
JavaScriptVBScriptWybierz oraz skopiuj do schowka

switch (pEvent.Val1)
{
case "A1":
// Algorytm operacji A1
pEvent.ReleaseCancel = true;
break;
case "A2":
// Algorytm operacji A2
break;
}

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 wołana metoda Release:
JavaScriptVBScriptWybierz oraz skopiuj do schowka

// 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ć w następujący sposób: 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");

W zdarzeniu onStep wtedy moży znajdować się na przykład następujący skrypt:
JavaScriptVBScriptWybierz oraz skopiuj do schowka

switch (pEvent.Val1)
{
case "A1":
// Algorytm operacji A1
break;
case "A2":
// Algorytm operacji A2
break;
}

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 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ć w następujący sposób:
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);

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);

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;
}

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")
- zapis uzyskanych obliczonych danych w aplikacji (faza "write")
JavaScriptVBScriptWybierz oraz skopiuj do schowka

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 iRow;
for (iRow = 0; iRow < nFiles; iRow++)
{
aDataWrite.SetItem(Pm.IniFileRead("#cfg:" + aFiles.GetItem(iRow), "MySettings", "value", 0, 4), iRow);
}

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;
}

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.
Nawigacja:
 
 
- Add
- PmaSequencer - Szczegółowy opis obiektu
 
 
© MICROSYS, spol. s r.o.