Skip to content

Mode switching

Switching between operating modes safely: request the switch, wait for a safe point, then commit.

Most devices have modes: a coffee machine serves coffee or cleans itself, a printer prints or runs maintenance, a robot works or charges. In SCCharts a mode is naturally a superstate with its own behaviour inside, and switching modes is a transition out of it. The hard part is not the transition. It is when to take it: a mode is often in the middle of something that must not be cut short, and whatever it switched on must be switched off.

This page develops a coffee machine with two modes, Serving and Cleaning, in three steps: a switch that cuts in at any time, the same with exit actions that clean up, and a switch that waits for a safe point. At the end it adds an emergency stop, which must not wait. It builds on superstates, aborts, actions and the latch pattern.

Serving makes coffee: a brew request switches the heater on, the machine heats for a tick, then pours for two ticks, then switches the heater off and counts the cup. Cleaning rinses for a tick, flushes for a tick, and then hands back to Serving with a join to. The first attempt switches to cleaning whenever clean is pressed:

initial state Serving {
...
}
if clean go to Cleaning

if clean go to Cleaning leaves Serving from whatever inner state it is in. In CoffeeAbrupt, a press in the middle of a brew (tick 3) abandons the cup and leaves the heater on:

CoffeeAbrupt and CoffeeExitActionComparison

Diagrams

CoffeeAbrupt

Diagram of CoffeeAbruptStates: Serving (initial, superstate), Ready (initial), Heating, Pouring (superstate), Cleaning (superstate), Rinse (initial), Flush, Rinsed (final). Transitions: Ready to Heating: brew / heater = true; Heating to Pouring; Pouring to Ready: 2 true / heater = false; cups++; Rinse to Flush; Flush to Rinsed; Serving to Cleaning: clean; Cleaning to Serving.CoffeeAbruptinput boolbrew, cleanoutput boolheater, pump, rinsingoutput intcups = 0ServingReadyHeatingPouringentry/ pump = trueexit/ pump = falsebrew/ heater = true2 true/ heater = false;cups++Cleaningentry/ rinsing = trueexit/ rinsing = falseRinseFlushRinsedclean

CoffeeExitAction

Diagram of CoffeeExitActionStates: Serving (initial, superstate), Ready (initial), Heating, Pouring (superstate), Cleaning (superstate), Rinse (initial), Flush, Rinsed (final). Transitions: Ready to Heating: brew / heater = true; Heating to Pouring; Pouring to Ready: 2 true / heater = false; cups++; Rinse to Flush; Flush to Rinsed; Serving to Cleaning: clean; Cleaning to Serving.CoffeeExitActioninput boolbrew, cleanoutput boolheater, pump, rinsingoutput intcups = 0Servingexit/ heater = falseReadyHeatingPouringentry/ pump = trueexit/ pump = falsebrew/ heater = true2 true/ heater = false;cups++Cleaningentry/ rinsing = trueexit/ rinsing = falseRinseFlushRinsedclean

Code

CoffeeAbrupt

scchart CoffeeAbrupt {
input bool brew, clean
output bool heater, pump, rinsing
output int cups = 0
initial state Serving {
initial state Ready
if brew do heater = true go to Heating
state Heating
go to Pouring
state Pouring {
entry do pump = true
exit do pump = false
}
if 2 true do heater = false; cups++ go to Ready
}
if clean go to Cleaning
state Cleaning {
entry do rinsing = true
exit do rinsing = false
initial state Rinse
go to Flush
state Flush
go to Rinsed
final state Rinsed
}
join to Serving
}

CoffeeExitAction

scchart CoffeeExitAction {
input bool brew, clean
output bool heater, pump, rinsing
output int cups = 0
initial state Serving {
exit do heater = false
initial state Ready
if brew do heater = true go to Heating
state Heating
go to Pouring
state Pouring {
entry do pump = true
exit do pump = false
}
if 2 true do heater = false; cups++ go to Ready
}
if clean go to Cleaning
state Cleaning {
entry do rinsing = true
exit do rinsing = false
initial state Rinse
go to Flush
state Flush
go to Rinsed
final state Rinsed
}
join to Serving
}
Tick 0 of 15
Inputs for tick 1, for both charts Boolean input: true while on. It keeps its value until you change it. Boolean input: true while on. It keeps its value until you change it.
Timing diagram of CoffeeAbrupt and CoffeeExitAction, no ticks yet
Tickinput bool brewinput bool cleanCoffeeAbrupt: output bool heaterCoffeeAbrupt: output bool pumpCoffeeAbrupt: output bool rinsingCoffeeAbrupt: output int cupsCoffeeAbrupt: Active state of the chartCoffeeAbrupt: Active state of the region of ServingCoffeeAbrupt: Active state of the region of CleaningCoffeeExitAction: output bool heaterCoffeeExitAction: output bool pumpCoffeeExitAction: output bool rinsingCoffeeExitAction: output int cupsCoffeeExitAction: Active state of the chartCoffeeExitAction: Active state of the region of ServingCoffeeExitAction: Active state of the region of Cleaning
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 15 ticks Both charts get the same inputs. Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 15 ticks, the same inputs for both charts:

  1. brew = false, clean = falseTick 1: both machines enter Serving and wait in Ready.
  2. brew = truebrew: in both, the heater goes on and the machine moves to Heating.
  3. brew = false, clean = trueclean in the middle of the brew. if clean go to Cleaning is a weak abort, so the inside of Serving finishes its tick first: Heating moves on to Pouring, whose entry action starts the pump. Then Serving is left, and Pouring’s exit action stops the pump again. Only CoffeeExitAction has an exit action on Serving that switches the heater off. In CoffeeAbrupt the heater stays on.
  4. clean = falseBoth machines are cleaning, and the half-made cup has been abandoned. CoffeeAbrupt’s heater is still on.
  5. Cleaning finishes and Serving starts again. cups is still 0 in both.
  6. Back in Ready. CoffeeAbrupt’s heater is still on, with nothing to heat.
  7. brew = trueA proper brew in both.
  8. brew = falsePouring.
  9. Pouring.
  10. The cup is done in both: cups is 1. CoffeeAbrupt’s heater is off at last, switched off by the end of this brew.
  11. Ready.
  12. brew = true, clean = truebrew and clean in the same tick. The weak abort’s last wish lets Ready start the brew in both machines (heater on), and only then is Serving left for Cleaning. CoffeeExitAction’s exit action switches the heater off again before the tick ends; CoffeeAbrupt’s stays on.
  13. brew = false, clean = falseCleaning.
  14. Back to Serving.
  15. Ready: the heater is on in CoffeeAbrupt and off in CoffeeExitAction.
After tick 3, heater stays on in CoffeeAbrupt until the next brew happens to switch it off. CoffeeExitAction switches it off on the way out.

Abrupt is heater in CoffeeAbrupt, Exit is heater in CoffeeExitAction, and cups is the same in both:

Tick Inputs Abrupt Exit cups
2 brew true true 0
3 clean true false 0
6 none true false 0
10 none false false 1
12 brew, clean true false 1
15 none true false 1

Two things went wrong in CoffeeAbrupt:

  • Nothing switched the heater off. The heater was switched on by a transition inside Serving, and only another transition inside Serving switches it off. When Serving is left early, that transition never runs. The pump is fine: it is switched by Pouring’s entry and exit actions, and exit actions run however a state is left.
  • The weak abort grants a last wish. go to is a weak abort, so the inside of Serving finishes its tick before the mode is left. In tick 12, brew and clean arrive together: Ready starts the brew (heater on), and only then does the machine switch to cleaning.

The fix for the first problem, in CoffeeExitAction, is one line: exit do heater = false on Serving. An exit action is the mode’s “finally” block. Use it for every output the mode may have switched on, and the next mode starts from a known state, however the old one ended.

That makes the switch safe for the hardware. It does not make it good: a press of clean still throws away a half-made cup. To fix that, the switch must wait.

A safe point is a moment at which the mode can be left without harm. For the coffee machine it is Ready: no cup in progress. A safe switch has three parts:

  1. Request. Remember that a switch was asked for, whenever it was asked for. That is a latch.
  2. Wait. Let the current mode carry on until it reaches a safe point.
  3. Commit. At the safe point, leave the mode.
CoffeeMachineSimulation

Diagram

Diagram of CoffeeMachineStates: Serving (initial, superstate), Ready (initial), Heating, Pouring (superstate), Stopped (final), Cleaning (superstate), Rinse (initial), Flush, Rinsed (final). Transitions: Ready to Stopped: 1: cleanWanted; Ready to Heating: 2: brew / heater = true; Heating to Pouring; Pouring to Ready: 2 true / heater = false; cups++; Rinse to Flush; Flush to Rinsed; Serving to Cleaning: / cleanWanted = false; Cleaning to Serving.CoffeeMachineinput boolbrew, cleanoutput boolheater, pump, rinsingoutput intcups = 0boolcleanWanted = falseServingduringclean/ cleanWanted = trueReadyHeatingPouringentry/ pump = trueexit/ pump = falseStopped1: cleanWanted2: brew/ heater = true2 true/ heater = false;cups++Cleaningentry/ rinsing = trueexit/ rinsing = falseRinseFlushRinsed/ cleanWanted = false

Code

scchart CoffeeMachine {
input bool brew, clean
output bool heater, pump, rinsing
output int cups = 0
bool cleanWanted = false
initial state Serving {
during if clean do cleanWanted = true
initial state Ready
if cleanWanted go to Stopped
if brew do heater = true go to Heating
state Heating
go to Pouring
state Pouring {
entry do pump = true
exit do pump = false
}
if 2 true do heater = false; cups++ go to Ready
final state Stopped
}
do cleanWanted = false join to Cleaning
state Cleaning {
entry do rinsing = true
exit do rinsing = false
initial state Rinse
go to Flush
state Flush
go to Rinsed
final state Rinsed
}
join to Serving
}
Tick 0 of 14
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it. Boolean input: true while on. It keeps its value until you change it.
Timing diagram of CoffeeMachine
Tickinput bool brewinput bool cleanoutput bool heateroutput bool pumpoutput bool rinsingoutput int cupsActive state of the chartActive state of the region of ServingActive state of the region of Cleaning
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 14 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 14 ticks:

  1. brew = false, clean = falseTick 1: Serving, Ready.
  2. brew = truebrew: the heater goes on.
  3. brew = false, clean = trueclean during the brew. Serving’s during action records the request in cleanWanted, and the brew carries on: Heating moves to Pouring.
  4. clean = falsePouring. The request is remembered although clean has been released.
  5. The cup is done (cups is 1) and the chart is back in Ready, the safe point. Ready was entered in this tick, so its delayed transitions wait for the next one.
  6. Commit: if cleanWanted go to Stopped fires, Stopped is final, so Serving terminates and its join clears cleanWanted and enters Cleaning, all in this tick.
  7. Rinse, flush ...
  8. ... done: Cleaning terminates and Serving starts again.
  9. Ready.
  10. brew = true, clean = truebrew and clean in the same tick, in Ready. The during action writes cleanWanted before Ready reads it, and Ready tries if cleanWanted first: cleaning wins, the brew request is ignored.
  11. brew = false, clean = falseCleaning.
  12. Cleaning.
  13. Back to Serving.
  14. Ready.
clean in tick 3 is remembered; the cup is finished in tick 5, and cleaning starts in tick 6.

Each part is one or two lines:

  • Request: during if clean do cleanWanted = true on Serving records a request in every tick in which Serving is active, except the tick in which it was entered. A during action of a superstate runs alongside its inner states, so the brew is not disturbed.
  • Wait: only Ready looks at the request. Heating and Pouring do not, so a cup that has been started is finished.
  • Commit: in Ready, if cleanWanted go to Stopped enters a final state, so Serving terminates, and its join to Cleaning fires in the same tick. The join’s effect clears the request.

CoffeeMachine has no exit action for the heater, and needs none: it leaves Serving only from Ready, where the heater is already off. Add one anyway as soon as a mode can be left in another way, as the emergency stop below shows.

The request and the commit both write cleanWanted: the during action sets it, the join clears it. KIELER accepts that, because the two are not concurrent. The join is a transition of the region around Serving, and it is taken only after Serving has finished its tick. The during action, on the other hand, is concurrent with Serving’s inner states: KIELER runs a superstate’s during action like an extra region alongside them. So, as between any concurrent regions, its write of cleanWanted comes before Ready’s read of it in the same tick. In tick 10, when brew and clean arrive together, Ready already sees the request.

Here is the switch, tick by tick. State is the state after the tick, and Request is cleanWanted:

Tick Inputs State Request
2 brew Serving, Heating false
3 clean Serving, Pouring true
4 none Serving, Pouring true
5 none Serving, Ready (the cup is done) true
6 none Cleaning, Rinse false
8 none Serving, Ready false
10 brew, clean Cleaning, Rinse false

Tick 5 to tick 6 is worth a second look. The cup is finished in tick 5, which enters Ready, and the switch is committed one tick later: Ready’s transitions are delayed, so they are not checked in the tick Ready is entered. If you want the commit in the very tick the safe point is reached, make the transition immediate if cleanWanted go to Stopped.

Notice the policy decisions this chart makes, and that you should make on purpose in your own:

  • Who wins at the safe point? Ready tries if cleanWanted before if brew, so a pending clean beats a new brew request, also when both arrive in the same tick (tick 10).
  • What happens to requests that arrive in the wrong mode? A brew request during cleaning is ignored. The exercise below remembers it instead.

Some switches must happen at once, safe point or not: a fault, a lid opened, an emergency stop. That is a strong abort from a superstate around all the normal modes:

CoffeeEmergencySimulation

Diagram

Diagram of CoffeeEmergencyStates: Operating (initial, superstate), Serving (initial, superstate), Ready (initial), Heating, Pouring (superstate), Stopped (final), Cleaning (superstate), Rinse (initial), Flush, Rinsed (final), Halted. Transitions: Ready to Stopped: 1: cleanWanted; Ready to Heating: 2: brew / heater = true; Heating to Pouring; Pouring to Ready: 2 true / heater = false; cups++; Rinse to Flush; Flush to Rinsed; Serving to Cleaning: / cleanWanted = false; Cleaning to Serving; Operating to Halted: fault.CoffeeEmergencyinput boolbrew, clean, faultoutput boolheater, pump, rinsingoutput intcups = 0boolcleanWanted = falseOperatingServingexit/ heater = falseduringclean/ cleanWanted = trueReadyHeatingPouringentry/ pump = trueexit/ pump = falseStopped1: cleanWanted2: brew/ heater = true2 true/ heater = false;cups++Cleaningentry/ rinsing = trueexit/ rinsing = falseRinseFlushRinsed/ cleanWanted = falseHaltedfault

Code

scchart CoffeeEmergency {
input bool brew, clean, fault
output bool heater, pump, rinsing
output int cups = 0
bool cleanWanted = false
initial state Operating {
initial state Serving {
exit do heater = false
during if clean do cleanWanted = true
initial state Ready
if cleanWanted go to Stopped
if brew do heater = true go to Heating
state Heating
go to Pouring
state Pouring {
entry do pump = true
exit do pump = false
}
if 2 true do heater = false; cups++ go to Ready
final state Stopped
}
do cleanWanted = false join to Cleaning
state Cleaning {
entry do rinsing = true
exit do rinsing = false
initial state Rinse
go to Flush
state Flush
go to Rinsed
final state Rinsed
}
join to Serving
}
if fault abort to Halted
state Halted
}
Tick 0 of 7
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it. Boolean input: true while on. It keeps its value until you change it. Boolean input: true while on. It keeps its value until you change it.
Timing diagram of CoffeeEmergency
Tickinput bool brewinput bool cleaninput bool faultoutput bool heateroutput bool pumpoutput bool rinsingoutput int cupsActive state of the chartActive state of the region of OperatingActive state of the region of Operating.ServingActive state of the region of Operating.Cleaning
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 7 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 7 ticks:

  1. brew = false, clean = false, fault = falseTick 1: Operating, Serving, Ready.
  2. brew = truebrew: heater on.
  3. brew = falsePouring: pump on.
  4. fault = trueA fault in the middle of the cup. The strong abort if fault abort to Halted stops everything inside Operating before it reacts. But the exit actions still run, innermost first: Pouring’s stops the pump, Serving’s switches the heater off.
  5. fault = falseHalted, everything off.
  6. brew = trueNothing gets the machine out of Halted: a brew request is ignored.
  7. brew = falseHalted.
Tick 4: a fault in the middle of a cup. Everything stops, and the exit actions switch the heater and the pump off.

if fault abort to Halted is checked before anything inside Operating reacts, so in tick 4 nothing inside it moves: no inner transition fires and no during action runs. Yet the heater and pump do go off: exit actions run on strong aborts too, innermost first (Pouring’s, then Serving’s). This surprises people who have read the KIELER wiki, which says that a strong abort suppresses exit actions. KIELER’s compiler does run them, and that is what makes exit actions useful for safety: whatever ends a mode, its outputs are put into a safe state.

If Operating also had weak aborts or a join to, the strong abort would have to be written first; otherwise KIELER rejects the chart with Causality problem! and:

Strong abort transitions must have a higher priority than weak abort or termination transitions.

For more on supervising a chart and forcing a safe state, see watchdogs.

A brew request during cleaning

In the starter, a brew request during cleaning is lost: press clean in tick 2 and brew in tick 3, while the machine is cleaning, and no cup is ever made. Change it so that the request is remembered and the cup is made as soon as Serving is back in Ready: with these inputs, the heater should go on in tick 5 and cups should be 1 in tick 8. Use the same request-and-commit idea as for cleaning, in the other direction, and clear the request when the brew starts. If KIELER warns Instantaneous loop detected! about your chart (SCCharts Lab: Potential instantaneous loop, followed by the variables involved), that is a false alarm here.

Show the starter chart
scchart CoffeeQueue {
input bool brew, clean
output bool heater, pump, rinsing
output int cups = 0
bool cleanWanted = false
initial state Serving {
during if clean do cleanWanted = true
initial state Ready
if cleanWanted go to Stopped
if brew do heater = true go to Heating
state Heating
go to Pouring
state Pouring {
entry do pump = true
exit do pump = false
}
if 2 true do heater = false; cups++ go to Ready
final state Stopped
}
do cleanWanted = false join to Cleaning
state Cleaning {
entry do rinsing = true
exit do rinsing = false
initial state Rinse
go to Flush
state Flush
go to Rinsed
final state Rinsed
}
join to Serving
}
Show a solution
scchart CoffeeQueue {
input bool brew, clean
output bool heater, pump, rinsing
output int cups = 0
bool cleanWanted = false
bool brewWanted = false
initial state Serving {
during if clean do cleanWanted = true
initial state Ready
if cleanWanted go to Stopped
if brew || brewWanted do brewWanted = false; heater = true go to Heating
state Heating
go to Pouring
state Pouring {
entry do pump = true
exit do pump = false
}
if 2 true do heater = false; cups++ go to Ready
final state Stopped
}
do cleanWanted = false join to Cleaning
state Cleaning {
entry do rinsing = true
exit do rinsing = false
during if brew do brewWanted = true
initial state Rinse
go to Flush
state Flush
go to Rinsed
final state Rinsed
}
join to Serving
}
CoffeeMachinePrediction

Diagram

Diagram of CoffeeMachineStates: Serving (initial, superstate), Ready (initial), Heating, Pouring (superstate), Stopped (final), Cleaning (superstate), Rinse (initial), Flush, Rinsed (final). Transitions: Ready to Stopped: 1: cleanWanted; Ready to Heating: 2: brew / heater = true; Heating to Pouring; Pouring to Ready: 2 true / heater = false; cups++; Rinse to Flush; Flush to Rinsed; Serving to Cleaning: / cleanWanted = false; Cleaning to Serving.CoffeeMachineinput boolbrew, cleanoutput boolheater, pump, rinsingoutput intcups = 0boolcleanWanted = falseServingduringclean/ cleanWanted = trueReadyHeatingPouringentry/ pump = trueexit/ pump = falseStopped1: cleanWanted2: brew/ heater = true2 true/ heater = false;cups++Cleaningentry/ rinsing = trueexit/ rinsing = falseRinseFlushRinsed/ cleanWanted = false

Code

scchart CoffeeMachine {
input bool brew, clean
output bool heater, pump, rinsing
output int cups = 0
bool cleanWanted = false
initial state Serving {
during if clean do cleanWanted = true
initial state Ready
if cleanWanted go to Stopped
if brew do heater = true go to Heating
state Heating
go to Pouring
state Pouring {
entry do pump = true
exit do pump = false
}
if 2 true do heater = false; cups++ go to Ready
final state Stopped
}
do cleanWanted = false join to Cleaning
state Cleaning {
entry do rinsing = true
exit do rinsing = false
initial state Rinse
go to Flush
state Flush
go to Rinsed
final state Rinsed
}
join to Serving
}

The safe coffee machine, CoffeeMachine, with new inputs. What are rinsing and heater after these ticks?

Inputs per tick. A value in grey was not set in that tick: it holds from an earlier one.

Tickbrewclean
1falsefalse
2falsetrue
3truefalse
4falsefalse
5truefalse
6falsetrue
7falsefalse
8falsefalse
9falsefalse
Plays the scenario on the diagram, then marks each answer.
Explanation

rinsing is true after tick 3, heater is true after tick 5, and rinsing is false after tick 8 and true after tick 9. The clean of tick 2 found the machine in Ready, so cleaning started at once, and the brew of tick 3 is ignored: Cleaning has no transition for it. Back in Ready, the brew of tick 5 switches the heater on. The clean of tick 6 comes during the brew: it is recorded, the cup is finished in tick 8, and the switch is committed in tick 9, because Ready’s transitions are delayed.

Serving is left by a strong abort, if fault abort to Halted on a superstate around it, while Pouring is active. Which actions run in that tick?
Show the answer

Answer D: The exit actions of Pouring and Serving, innermost first

A strong abort prevents the inner behaviour of the tick: no transitions inside fire and no during actions run. But KIELER runs the exit actions of every state being left, the innermost first, also on a strong abort. That is why an exit action is the right place to switch outputs off.

Why does the safe coffee machine record the clean request with a during action on Serving, instead of a transition if clean go to … from Heating and Pouring?
Show the answer

Answer C: A transition would leave the state and cut the brew short

The point is to let the brew continue. A during action of a superstate runs alongside its inner states in every tick after the entry tick, so it can note the request while Heating and Pouring carry on. Only Ready, the safe point, acts on it.

Key ideas

  • A mode is a superstate; switching modes is a transition out of it.
  • Give every mode exit actions that switch off what it may have switched on. Exit actions run on every kind of exit, strong aborts included.
  • A weak abort (go to) lets the mode finish its tick first; that last wish can start things you did not want started.
  • Safe switching: latch the request, act on it only at a safe point, and commit by terminating the mode (final state and join to).
  • Switches that must not wait (faults) are strong aborts on a superstate around all normal modes, written before any weak abort.

Next, Handshakes: a request and an acknowledgement between two regions.

Report a mistake on this page