Event-Driven Execution and Efficiency

Statements and action triggers do not follow a strict sequential execution. Action triggers and statements in a particular state are executed once when that state first becomes active. After the first execution, a statement or action trigger is executed again if the value of any variable in the statement changes.

It is important to watch for possible race conditions. For example, if a state called Monitor contains two actions:

If HighAlarm Shutdown;

If HighAlarm StartPump;

Both of these actions change to a new state on the same condition. It cannot be determined which will execute first, and the final state will be either Shutdown or StartPump. This is called a "race condition", because the module depends on which action trigger wins the race. Imagine that all statements and action triggers are executing simultaneously when analyzing a design. Although this is not strictly what happens, it is a good design strategy.

Action script statements are always executed in the order they appear, but only when an action is triggered (becomes true). Action triggers are checked whenever a value they depend on changes. For example, consider three actions:

If HighAlarm { Check variable };

If TimeOut(1, 0.1) { True every 0.1 seconds };

If MatchKeys(2,"A") { Look for "A" key };

None of these actions have a destination state. The first action trigger is checked whenever the variable HighAlarm changes. The second is not checked immediately, but when the state containing this statement starts, the time it is expected to execute is computed and the action will be executed at that time. If the second parameter of the TimeOut function ever changes (the length of time to wait), the time/frequency that that statement executes will change. The third action is not checked unless VTS receives a key press.

Changes propagate through the system. For example, consider the statement:

X = Y + 4;

Every time the variable Y changes, all statements that depend on the variable Y will be updated, including the assignment to X. Then, because X changed, all statements depending on the value of X will be updated. 

Note: the above rule may affect how you write seemingly simple statements.  For example, if the following were to appear in steady state code, X would continuously increment.

X = X + 1;

Presumably, you would only want X to increment in response to some specific condition or at a particular time. For this purpose, you could use a script block like so:

IF  <reason_to_increment_X_is_true>;

[

  X = X + 1;

  {Ensure trigger condition is set to false unless you want }

  { to continue incrementing X                              }

]

Some functions are not triggered by changes in values (or other events), but by changes in the internal time clock, for example the TimeOut function, or the PID loop.

It is interesting from a performance perspective to know when each statement will execute, to gauge how much CPU time will be spent on each activity. VTS applications can be made to achieve very high performance if the number of active statements actually executing is limited - however, due to this event-driven structure, that does not mean loss of features. For example, an active state could contain hundreds of output statements that display 1 variable each. If all statements were to execute, it would take a certain length of time. If only one variable changes, the total CPU need only process one statement instead of hundreds. This means that the response time for the page is cut to 1 percent or less of the time it would take to update all statements..

Another example is a state with many actions, each looking at the same variable:

If Stage == 1 Alarms;

If Stage == 2 Mixer;

...

If Stage == 100 TankFarm;

In this example, every time the variable Stage changes, VTS attempts to execute each of the action triggers once, in an unknown order. This does not necessarily mean that all 100 actions are checked -as soon as one is found to be true and it contains a state change, it will stop this state and start a new one. When the state is stopped, the remaining actions are no longer checked. It could be possible that all 100 actions would have to be checked, however, it is more likely that an average of 50 actions would be checked based on a uniform random distribution of Stage.

When designing a module, it is a good idea to sort activities that take place continuously, such as a flashing lamp or PID loop, and keep them separate from activities which start on a trigger, such as starting a pump when a level is too high, or switching the graphics screen at the press of a button.