See: the
PmaSequencer object.
The principle of the object:
Into the object it is possible to add so-called items, which are data structures with 3 user variables
Val1,
Val2 and
Val3. Adding the item is carried out by the
Add method. The item that is added into the object is inserted into the item queue of this object and it waits for the required time in this queue (see the
Timeout parameter parameter in the
Add method, the required time can be zero or infinite long). After the required time expiration the item is removed from the queue and the
onStep event is triggered for this item. In parameters of this event it is possible to find out (from variables
pEvent.Val1,
pEvent.Val2 and
pEvent.Val3) for which item the event has been triggered. By this the item ends.
Further in the "
Type of item start" configurator it can be set the meaning of timeout that is entered in the
Add method. It can be defined:
- if the timeout is the time between inserting the item and its starting (i.e. Items starts independently) or
- if the timeout is the delay between starting this item and previously started items (i.e. Items starts sequentially).
The item waiting in the queue can be prematurely terminated (i.e. before its time expires) by the
Release or
Remove method:
-
The Release method removes the item from the queue and then the
onStep event is triggered (i.e. the same behavior as if the time expired early).
In the
onStep event it is possible to set
pEvent.ReleaseCancel=true and this way prevent removal from the queue - this way it is possible to call one item of the queue multiple times.
-
The Remove method causes that the item is removed from the queue without triggering the event (i.e. it behaves as if no item existed).
The usage of another thread:
In the "
Used thread" configurator it is possible to
set the thread where the
onStep event is triggered. It is possible to set either the main thread or to create new working thread for this object, see
How to start selected scripts in another thread.
Caution! If another
thread is used, then an access to other objects in the script proceeds in other than in the main thread and it is necessary to take into account possible problems with the synchronization on reading and writing into
Pma objects. This option is suitable first of all for time consuming calculations on the background.
The problem with synchronization (consistency) of read and write of the data (when using a single thread) can be solved by several ways.
One of the basic procedures is to divide the whole procedure into three steps. That is reading the data from the application, process the data and write the output data into the application (each step is initialized by triggering the
onStep event). The read and write step may be invoked in the main thread (preventing the data synchronization problem) and the data processing step can be invoked in the worker thread. The assigning of processes to threads can be defined in the
Params parameter of the
PmaSequencer.Add method. The data read and write then runs in the main thread and data procesing runs in the work thread not delaying the main thread. The processed data must be transmitted between each step. The easiest way of data sharing is to create in the script of the step (the
onStep event) a complementary array and transmit it as a
Val2 parameter of the
PmaSequencer.Add method. It is also possible to transmit the data in the
PmaData object. The usage see
Example of time consuming process in the worker thread.
From the above described simple principle it is possible to construct a few structures that are preferably used for the sequential, time or asynchronous control. From the object name (
PmaSequencer) implies that it is determined for the sequential control first of all but we'll show other its possibilities:
1 Example of the sequential control:
The request is to execute the following algorithm:
- wait for t1 seconds, then perform the action A1
- wait for t2 seconds, then perform the action A2, etc.
This can be done by means of the
PmaSequencer object as follows:
- Then we call the
Add method with the
Timeout=t1 parameter and give the item the identification for example
"A1" into the
Val1 variable. It means that we realize:
JavaScriptVBScriptSelect and copy to clipboard
oSequencer.Add(t1, "A1");
oSequencer.Add t1, "A1"
After
t1 seconds the
onStep event is triggered in which we perform the action
A1 and then insert new time request for the action
A2. It means that in the
onStep event can be for example the following script:
By this can be created a sequence of any length by the action that can be even conditional (e.g. under certain conditions the action
A2 isn't performed after the action
A1 but immediately the action
A3 is performed). The advantage is that we have all actions in one script (in the
onStep event) and thus the whole overview sequence can be seen. In transitions between actions it is possible to store internal states into variables
Val2 and
Val3 (they are not used in the example).
2 Example of the sequential control:
The request is to perform 4 actions (
A1,A2,A3,A4) but in the midst of their execution must be
5 seconds delay.
It is similar as in the previous example but we create it with the Type of item start:
1 = items start chronologically, timeout is delay between them.
We call the
Add method for all items together:
JavaScriptVBScriptSelect and copy to clipboard
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"
and then there can be simpler script in the
onStep event:
In this case the first action
A1 is started immediately (or
5 seconds after the application is launched) because the previous item doesn't exist and thus starting time of the previous item is set to runtime starting time. But the next items are started in 5 seconds periods in sequence.
3 Example of the sequential control with managed item release:
It is requested to perform 2 actions (
A1,A2) but there must be a security delay between them up to
10 seconds. The pause will be shortened once the first action is completed.
Type of execution will be:
1 = items start chronologically, timeout is delay between them.
We call the
Add method for all items together:
JavaScriptVBScriptSelect and copy to clipboard
oSequencer.Add(0, "A1");
oSequencer.Add(10, "A2");
oSequencer.Add 0, "A1"
oSequencer.Add 10, "A2"
and then there can be simpler script in the
onStep event:
In this case the 1st action
A1 is started immediately. The next item is executed after
10 seconds, but it can be executed sooner, if the
Release method is called after the first item is finished successfully:
JavaScriptVBScriptSelect and copy to clipboard
// Algorithm of the action A1
oSeq.Release(10, 0);
' Algorithm of the action A1
oSeq.Release 10, 0
4 Example of the time control:
The request is to perform the first action
A1 after
t1 seconds and the seconds action
A2 after
t2 seconds, etc. We do it as follows: The type of item start is set to
0 = items start independently, each according to its timeout and we insert both actions into the
PmaSequencer object together:
JavaScriptVBScriptSelect and copy to clipboard
oSequencer.Add(t1, "A1");
oSequencer.Add(t2, "A2");
oSequencer.Add t1, "A1"
oSequencer.Add t2, "A2"
Then in the
onStep event for example the following script can be:
It is similar to the sequential control with the difference that the actions are independent of each other but each one is performed separately. Again the variables
Val2 and
Val3 that could be used as additional action parameters, were not used in the example.
5 Example of the asynchronous control:
The request is to perform the action
A1 only when for example data from the communication arrive or when the user presses the specified key, or ..., in short when an asynchronous operation happens. We don't want to wait for the asynchronous operation for infinite time but only for
t1 seconds at the most. In action
A1 we want to distinguish by what reason it is triggered - whether of the communication (after pressing the button ..) or of the time expiration. We do it as follows:
The type of item start is set to
0 = items start independently, each according to its timeout and we insert the item for the action
A1 into the
PmaSequencer object:
JavaScriptVBScriptSelect and copy to clipboard
oSequencer.Add(t1, "A1", 111);
oSequencer.Add t1, "A1", 111
By this the item, whose the
Val1 variable is the text
"A1" and the
Val2 variable is the number
111, is inserted into the object.
We include the following command into the script of the asynchronous event at the end of data transfer (after pressing the button ...):
JavaScriptVBScriptSelect and copy to clipboard
oSequencer.Release(1, 2, "A1", 222);
oSequencer.Release 1, 2, "A1", 222
This command means that in the
PmaSequencer object the item, whose the
Val1 variable (= parameter 1) equals the text
"A1", have to be released and by this releasing the number
222 (which re-writes the number
111 set previously) has to be assigned into the
Val2 variable (= parameter 2) of this item.
Then in the
onStep event for example the following script can be:
JavaScriptVBScriptSelect and copy to clipboard
switch (pEvent.Val2)
{
case 111:
// Algorithm of the action A1 - if the timeout expired
break;
case 222:
// Algorithm of the action A1 - if the asynchronous event occurred
break;
}
Select Case pEvent.Val2
Case 111
' Algorithm of the action A1 - if the timeout expired
Case 222
' Algorithm of the action A1 - if the asynchronous event occurred
End Select
In this example we basically didn't need the indication about the action
A1 (i.e. the text
"A1" in the
Val1 variable). The example can be generalized so that the
PmaSequencer object could process more asynchronous actions and then we would have to distinguish in the
onStep event even according to the value of the
Val1 variable.
6 Example of time consuming process in the worker thread:
There is a request to run the following algorithm:
- prepare and pass the data (step "read")
- time consuming processing of the data (step "work") - for example: file operations
- use (write) the processed data in the application (step "write")
JavaScriptVBScriptSelect and copy to clipboard
oSequencer.Add(0, "read", 0, 0, "thread:main;");
oSequencer.Add 0, "read", 0, 0, "thread:main;"
The
onStep event for all three steps is as follows:
JavaScriptVBScriptSelect and copy to clipboard
switch (pEvent.Val1)
{
case "read":
// Executed in the main theread
// Preparation and creation of array of files aDataRead and passing it for processing to the next step
var aDataRead = Pm.CreatePmArray().Array1("Glob.ini", "Cfg1.ini", "Cfg2.ini", "Cfg3.ini");
pMe.Add(0, "work", aDataRead, 0, "thread:work;");
break;
case "work":
// Executed in the work theread
// Processing the input data in pEvent.Val2 to the output data in the aDataWrite array and transfering the input data to another step
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":
// Executed in the main theread
// Writing the input data in pEvent.Val2 into the application
var aValues = pEvent.Val2;
var val1 = aValues.GetItem(0);
// ...
break;
}
Select Case pEvent.Val1
Case "read"
' Executed in the main theread
' Preparation and creation of array of files aDataRead and passing it for processing to the next step
Dim aDataRead
aDataRead = Pm.CreatePmArray().Array1("Glob.ini", "Cfg1.ini", "Cfg2.ini", "Cfg3.ini")
pMe.Add 0, "work", aDataRead, 0, "thread:work;"
Case "work"
' Executed in the work theread
' Processing the input data in pEvent.Val2 to the output data in the aDataWrite array and transfering the input data to another step
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"
' Executed in the main theread
' Writing the input data in pEvent.Val2 into the application
Dim aValues
aValues = pEvent.Val2
Dim val1
val1 = aValues(0)
' ...
End Select
The read and write is to be done in a secure way from the data synchronization point of view (using the main thread), while the complex data processing is to be completed on the background in another thread (application optimization). On the "
Sequencer" tab of the object you select
Type of item start=1 = items start chronologically, timeout is delay between them, and then select
Used thread=Normal = new working thread of the normal priority. The request for the first step (main thread) is created by calling the
Add method, with the
"read" step identification in the
Val1 parameter.
This way can be created a sequence of any number of steps where each step can be assigned to main or to work thread. The main advantage is that all the steps are included into a single script (in the
onStep event) and the whole sequence is therefore seen on one place. When passing from one step to another, it is possible to save the inner states into variables
Val2 and
Val3.
The above mentioned examples show how to use the
PmaSequencer object. It is possible to create even mixed controls, for example to wait hard between actions
A1 and
A2 for
t1 seconds, to wait between actions
A2 and
A3 for data receive and consequently run the parallel actions
A3 and
A4 after
t3 and
t4 seconds, etc.