Knowledge Base

MultiTrack (Intermediate): Synchronizing tasks

When you write a MultiTrack program containing several tasks, each task will normally be designed to take care of some fairly well defined aspect of the program. However, you will frequently need the various tasks to interact with each other, to coordinate their activities.

A simple but powerful way of coordinating the activities of multiple task is to use semaphores. Semaphores are simple on/off flags that can be set, cleared and tested by your program. Think of the semaphore signals used along railroad tracks, coordinating the activities of trains running on multiple tracks. Just as railroad semaphores synchronize the activities of the various trains to produce good outcomes rather than bad ones, semaphores can be used to coordinate the activities of tasks in a MultiTrack system.

Example

Imagine you are writing a program to control a batch processing vessel in the food industry. The vessel has an inlet valve, a heater and a pump to pump out the processed liquid. The heater is controlled by a simple thermostat but must only be turned on while the vessel is full. The fluid level in the vessel is controlled by low and high level probes. In addition there is a dispensing pump that must run for 10 seconds to dose the liquid with an additive during filling. The process is started by an input called iStartButton.

The program will have 3 tasks:

  1. The main sequencer. It controls the inlet valve, the processing time (3 minutes, timed from when the temperature reaches the setpoint) and the pump-out action. It also uses semaphores to “master control” the heater and the additive pump.
  2. The heater control. It controls the heater using the thermostat input and a semaphore from the main sequencer. It signals back to the main sequencer when the temperature gets to the setpoint, using a different semaphore.
  3. The additive pump control. It runs the additive pump for 10 seconds when commanded by the main sequencer via a semaphore.

The complete program file is located in the directory path Examples>MultTrk under SPLatPC in Program Files. The file name is MulTrk01.SPT.

Apart from the MultiTrack instructions that have been introduced earlier in this tutorial, it uses one other new MultiTrack-related instruction, WaitForST.

WaitForST (Wait For Semaphore True) will simply sit and wait for the nominated semaphore to become True. It will continually yield until the condition is met. Once the condition is met is lets control pass to the next instruction in the task.There is also a complimentary instruction, WaitForSF, which waits for the semaphore to become false.

Let’s dissect the main features of the program, one task at a time…

The main sequencer

Roll your mouse over each line of code in turn to display an explanation.

MainSequenceWaitOnKiStartButtonOnce the whole sequence is complete the main sequencer loops back to its beginning, ready for the next cycle
OnoFillValveOnce the sequence starts, it fills the vessel via the fill valve, using the full probe to detect the full condition.
WaitOniFullProbeOnce the sequence starts, it fills the vessel via the fill valve, using the full probe to detect the full condition.
OffoFillValveOnce the sequence starts, it fills the vessel via the fill valve, using the full probe to detect the full condition.
SetSsHeaterEnableOnce the vessel is full it sets the semaphore sHeaterEnable. This will be detected by the heater task and used to trigger its actions. Similarly, semaphore sAdditiveEnable is used to trigger the additive pump task.
SetSsAdditiveEnableOnce the vessel is full it sets the semaphore sHeaterEnable. This will be detected by the heater task and used to trigger its actions. Similarly, semaphore sAdditiveEnable is used to trigger the additive pump task.
WaitForSTsAtTempSetPointOnce the main sequencer has triggered the heater and additive actions, it has nothing to do until until the temperature hits the setpoint. It finds out about this by waiting for the heater task to set the semaphore sAtTempSetPoint
Pause18000The Pause instruction then times 3 minutes (18000 x 10mS) of cook time. While the main sequencer is “stuck” in the Pause instruction the heater task will continue controlling the temperature
ClrSsHeaterEnableAt the end of the cook time the Pause instruction expires and the following instruction executes. This turns off the heater task via sHeaterEnable
OnoDrainPumpThe main sequencer then drains the vessel using the drain pump and low level probe
WaitOffiEmptyProbeThe main sequencer then drains the vessel using the drain pump and low level probe
OffoDrainPumpThe main sequencer then drains the vessel using the drain pump and low level probe
GoToMainSequenceOnce the whole sequence is complete the main sequencer loops back to its beginning, ready for the next cycle
Heater control

The heater control task is best explained in terms of small blocks of code, each corresponding to a different phase in the life and times of a cycle.

sHeaterEnabledefSEMWe start by declaring the two semaphores. They are declared in the heater code because they are considered to belong to the heater task.
sAtTempSetPointdefSEMWe start by declaring the two semaphores. They are declared in the heater code because they are considered to belong to the heater task.
HeaterWaitForSTsHeaterEnableThe heater task initially just sits waiting to be commanded to start heating. The main sequencer will set sHeaterEnable when it’s time to heat the vessel. The heater task simply has to wait for the semaphore
OnoHeaterOnce the heater is activated, its initial job is to heat the vessel up to the operating temperature and then signal back to the main sequencer. So, it turns on the heater, waits for the thermostat to trip, turns off the heater and sets the semaphore sAtTempSetPoint. Once that is all done it can ‘fall through’ to the next phase, which is continuously maintaining the temperature.
WaitOffiThermostatOnce the heater is activated, its initial job is to heat the vessel up to the operating temperature and then signal back to the main sequencer. So, it turns on the heater, waits for the thermostat to trip, turns off the heater and sets the semaphore sAtTempSetPoint. Once that is all done it can ‘fall through’ to the next phase, which is continuously maintaining the temperature.
OffoHeaterOnce the heater is activated, its initial job is to heat the vessel up to the operating temperature and then signal back to the main sequencer. So, it turns on the heater, waits for the thermostat to trip, turns off the heater and sets the semaphore sAtTempSetPoint. Once that is all done it can ‘fall through’ to the next phase, which is continuously maintaining the temperature.
SetSsAtTempSetPointOnce the heater is activated, its initial job is to heat the vessel up to the operating temperature and then signal back to the main sequencer. So, it turns on the heater, waits for the thermostat to trip, turns off the heater and sets the semaphore sAtTempSetPoint. Once that is all done it can ‘fall through’ to the next phase, which is continuously maintaining the temperature.
HeatLoopYieldTaskThe heater task now sits in a ‘loop’ maintaining the temperature and watching out for sHeaterEnable to go false. It tests sHeaterEnable with the GoIfSF instruction. When the semaphore turns off the task will jump out of the loop to HeaterDone. The Input and Output instructions simply make the output echo the thermostat. The YieldTask is required because there is no other yielding instruction inside the loop.
GoIfSFThe heater task now sits in a ‘loop’ maintaining the temperature and watching out for sHeaterEnable to go false. It tests sHeaterEnable with the GoIfSF instruction. When the semaphore turns off the task will jump out of the loop to HeaterDone. The Input and Output instructions simply make the output echo the thermostat. The YieldTask is required because there is no other yielding instruction inside the loop.
sHeaterEnable,HeaterDoneThe heater task now sits in a ‘loop’ maintaining the temperature and watching out for sHeaterEnable to go false. It tests sHeaterEnable with the GoIfSF instruction. When the semaphore turns off the task will jump out of the loop to HeaterDone. The Input and Output instructions simply make the output echo the thermostat. The YieldTask is required because there is no other yielding instruction inside the loop.
InputiThermostatThe heater task now sits in a ‘loop’ maintaining the temperature and watching out for sHeaterEnable to go false. It tests sHeaterEnable with the GoIfSF instruction. When the semaphore turns off the task will jump out of the loop to HeaterDone. The Input and Output instructions simply make the output echo the thermostat. The YieldTask is required because there is no other yielding instruction inside the loop.
OutputoHeaterThe heater task now sits in a ‘loop’ maintaining the temperature and watching out for sHeaterEnable to go false. It tests sHeaterEnable with the GoIfSF instruction. When the semaphore turns off the task will jump out of the loop to HeaterDone. The Input and Output instructions simply make the output echo the thermostat. The YieldTask is required because there is no other yielding instruction inside the loop.
GoToHeatLoopThe heater task now sits in a ‘loop’ maintaining the temperature and watching out for sHeaterEnable to go false. It tests sHeaterEnable with the GoIfSF instruction. When the semaphore turns off the task will jump out of the loop to HeaterDone. The Input and Output instructions simply make the output echo the thermostat. The YieldTask is required because there is no other yielding instruction inside the loop.
HeaterDoneOffoHeaterOnce the heater task has done its job of maintaining temperature during cooking, it can clean up. It turns off the heater for the last time, clears the sAtTempSetPoint semaphore and then goes back to waiting for the next cycle to commence.
ClrSsAtTempSetPointOnce the heater task has done its job of maintaining temperature during cooking, it can clean up. It turns off the heater for the last time, clears the sAtTempSetPoint semaphore and then goes back to waiting for the next cycle to commence.
GoToHeaterOnce the heater task has done its job of maintaining temperature during cooking, it can clean up. It turns off the heater for the last time, clears the sAtTempSetPoint semaphore and then goes back to waiting for the next cycle to commence.
Additive pump
sAdditiveEnabledefSEMTry working this one out for yourself!
AdditivePumpWaitForSTsAdditiveEnableTry working this one out for yourself!
OnoAdditivePumpTry working this one out for yourself!
Pause1000Try working this one out for yourself!
OffoAdditivePumpTry working this one out for yourself!
ClrSsAdditiveEnableTry working this one out for yourself!
GoToAdditivePumpTry working this one out for yourself!
Exercise 1:

If the start button is pressed in the middle of a cycle, when the cycle completes a new cycle will start immediately. Work out why this is so, and change the program so the button must be pressed after the end of the cycle to have any effect.

Exercise 2:

The code in the heating loop in the heater control could be a little smaller. The loop contains one conditional GoTo and one unconditional GoTo. Rewrite the loop to eliminate the unconditional GoTo.

Exercise 3:

Run the program in the SPLat/PC simulator. You can get the complete program in the directory path described above. If it’s not there you may need to download and install the latest SPLat/PC. You will probably discover that testing the complete program in toto is quite impractical. Break the program into individual tasks and test each task independently, as best you can. You will have to write some special code to aid testing.