Skip to content

Superstates

Put a state machine inside a state: nesting, the scope of declarations, and finishing with a termination transition.

A washing machine is more than on and off. Once you press start, it runs a programme: it fills the drum, washes, and drains. The phases of the programme belong together, and some things have to work whichever phase the machine is in. The stop button is one of them.

Every chart so far has been a single, flat state machine. A superstate expresses this grouping: it is a state with states inside it. From the outside it is one state (in the chart below it is called Programme); inside, it is a small state machine of its own. This chapter shows how a chart enters and leaves a superstate, how declarations inside one behave, and how a superstate finishes.

Here is the washing machine’s controller. It waits in Idle until start, then runs the programme. The inputs full and timeUp come from the machine’s sensors: the drum is full, the wash time is over.

WasherSimulation

Diagram

Diagram of WasherStates: Idle (initial), Programme (superstate), Filling (initial), Washing, Draining. Transitions: Filling to Washing: full / motor = true; Washing to Draining: timeUp / motor = false; Idle to Programme: start.Washerinput boolstart, full, timeUpoutput boolmotorIdleProgrammeFillingWashingDrainingfull/ motor = truetimeUp/ motor = falsestart

Code

scchart Washer {
input bool start, full, timeUp
output bool motor
initial state Idle
if start go to Programme
state Programme {
initial state Filling
if full do motor = true go to Washing
state Washing
if timeUp do motor = false go to Draining
state Draining
}
}
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 Washer
Tickinput bool startinput bool fullinput bool timeUpoutput bool motorActive state of the chartActive state of the region of Programme
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. start = false, full = false, timeUp = falseTick 1 enters the initial state, Idle. Its transition is delayed, so nothing else can happen in this tick.
  2. start = truestart is true, so Idle → Programme fires. Entering Programme enters its initial state, Filling, in the same tick: two states are active now, Programme and Filling inside it.
  3. start = falseFilling waits for full, which is false, so nothing fires. The chart stays in Programme, in Filling.
  4. full = truefull is true: Filling → Washing fires inside Programme and switches the motor on. Programme stays active: moving between its inner states does not leave it.
  5. full = falseNothing is enabled. The machine keeps washing, and motor keeps its value.
  6. timeUp = truetimeUp is true: Washing → Draining fires and switches the motor off.
  7. timeUp = falseDraining has no outgoing transition, so the machine stays here for ever. The rest of the chapter gives it ways out.
Step to tick 2 and look at the diagram: Programme and Filling are both active.

Programme is declared like any other state, followed by a block in braces. Inside the braces you write states and transitions exactly as you would at the top of a chart, with one of them marked initial. This is what KIELER does with it, tick by tick:

Tick start full timeUp State after motor after
1 false false false Idle false
2 true false (held) false (held) Programme (Filling) false
3 false false (held) false (held) Programme (Filling) false
4 false (held) true false (held) Programme (Washing) true
5 false (held) false false (held) Programme (Washing) true
6 false (held) false (held) true Programme (Draining) false
7 false (held) false (held) false Programme (Draining) false

Three things to notice:

  • Entering a superstate enters its initial state in the same tick. Tick 2 takes a single transition, Idle to Programme, and ends with two active states: Programme and, inside it, Filling. A superstate is never active on its own; one of its inner states is always active as well. When superstates are nested, entering the outer one enters every level, all the way down, in the same tick.
  • Inside, the usual rules hold. Filling was entered in tick 2, so its delayed transition can fire from tick 3 on. An immediate transition out of Filling could have fired in tick 2 already (see Delayed and immediate transitions).
  • Moving between inner states does not leave the superstate. The machine changes phase in ticks 4 and 6, but Programme stays active the whole time.

KIELER draws a superstate as a large rounded box with its name at the top and its inner states inside. Declarations of the superstate, if it has any, are listed under its name, the way the chart lists its inputs and outputs. In SCCharts Lab the diagram also follows the editor’s cursor: put the cursor inside Programme’s braces, and the diagram selects Programme and expands the regions around it, which in a large chart is the quickest way to see where you are.

The machine needs a stop button that works in every phase. You could give each of Filling, Washing and Draining its own if stop go to Idle. A superstate lets you write it once:

WasherSimulation

Diagram

Diagram of WasherStates: Idle (initial), Programme (superstate), Filling (initial), Washing, Draining. Transitions: Filling to Washing: full / motor = true; Washing to Draining: timeUp / motor = false; Idle to Programme: start; Programme to Idle: stop / motor = false.Washerinput boolstart, full, timeUp, stopoutput boolmotorIdleProgrammeFillingWashingDrainingfull/ motor = truetimeUp/ motor = falsestartstop/ motor = false

Code

scchart Washer {
input bool start, full, timeUp, stop
output bool motor
initial state Idle
if start go to Programme
state Programme {
initial state Filling
if full do motor = true go to Washing
state Washing
if timeUp do motor = false go to Draining
state Draining
}
if stop do motor = false go to Idle
}
Tick 0 of 6
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. Boolean input: true while on. It keeps its value until you change it.
Timing diagram of Washer
Tickinput bool startinput bool fullinput bool timeUpinput bool stopoutput bool motorActive state of the chartActive state of the region of Programme
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 6 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 6 ticks:

  1. start = false, full = false, timeUp = false, stop = falseTick 1 enters Idle.
  2. start = truestart is true: the machine enters Programme and, in the same tick, Filling.
  3. start = false, full = truefull is true: Filling → Washing, and the motor runs.
  4. full = false, stop = truestop is true. Programme’s own transition fires although the machine is in Washing: a superstate’s transition can leave it whichever inner state is active. Washing and Programme are both left, and the effect switches the motor off.
  5. stop = false, start = truestart again. The machine enters Programme and starts in Filling, not in Washing where it was stopped: entering a superstate always starts at its initial state.
  6. start = falseFilling waits for full as before.
In tick 4, stop leaves Programme while it is in Washing. In tick 5 the machine starts again at Filling.

The transition if stop do motor = false go to Idle is written after Programme’s closing brace, so it belongs to Programme itself, not to any state inside it. It can fire whichever inner state is active, and when it fires, the inner state is left along with Programme.

Two details matter in practice:

  • The superstate does not remember where it was. When Programme is entered again in tick 5, it starts at its initial state, Filling. A machine that should resume where it stopped needs a history transition, which comes much later, in History and deferred transitions.
  • Leaving undoes nothing. That is why the stop transition switches the motor off. Without do motor = false, a stop during Washing would leave Programme with motor still true, and it would stay true in Idle and after the next start: a variable keeps its value until something assigns it.

A transition written on a superstate, after its closing brace, is called an abort: it can cut short whatever the inside is doing. The go to you have used all along makes a weak abort: if a transition inside is enabled in the same tick as stop, the inside takes its step first, and only then is Programme left. Its counterpart, the strong abort abort to, leaves before anything inside moves. Aborts is about the difference.

A superstate can declare its own variables and signals. They go at the start of its block, before its first state, in the same way as the chart declares its inputs and outputs at the top.

A declaration inside a superstate is a local of that superstate. The transitions inside it can use the name; the rest of the chart cannot. A transition outside the superstate that used one of its locals would be rejected with Couldn't resolve reference to ValuedObject '…'., naming the variable.

Every superstate is a scope, and scopes nest. If the same name is declared in several of them, a use of the name means the nearest declaration, looking outwards from where it is used. The inner declaration hides, or shadows, the outer one:

ShadowingSimulation

Diagram

Diagram of ShadowingStates: Outer (initial, superstate), Waiting (initial), Done. Transitions: Waiting to Done: press / count++; seen = count.Shadowinginput boolpressoutput intcount = 1output intseenOuterintcount = 10WaitingDonepress/ count++;seen = count

Code

scchart Shadowing {
input bool press
output int count = 1
output int seen
initial state Outer {
int count = 10
initial state Waiting
if press do count++; seen = count go to Done
state Done
}
}
Tick 0 of 3
Inputs for tick 1 Boolean input: true while on. It keeps its value until you change it.
Timing diagram of Shadowing
Tickinput bool pressoutput int countoutput int seenlocal int count of state OuterActive state of the chartActive state of the region of Outer
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 3 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 3 ticks:

  1. press = falseTick 1 sets the output count to 1, enters Outer and sets Outer’s own count to 10. Two variables now have the same name.
  2. press = trueThe transition inside Outer fires. Its count means the nearest declaration, Outer’s local: 10 becomes 11, and seen copies it. The output count is untouched: still 1.
  3. press = falseNothing else happens. KIELER gave no warning about the second count.
Watch the two count lanes: the output count stays 1, because the transition updates Outer.count, Outer’s own, from 10 to 11. seen copies it.

KIELER does not warn about shadowing. The chart works, but whoever reads it (you, next week) can easily mistake one count for the other. Give locals names of their own.

Two more facts about scope, both of which surprise people:

  • input and output only mean something at the top of a chart. Written inside a superstate, output int z declares an ordinary local: it is not part of the chart’s interface, and nothing outside the chart can see it.
  • A superstate’s own outgoing transitions, the ones written after its closing brace, can read the superstate’s locals too. KIELER’s validator has a check that was meant to forbid this, but the check never fires, so such charts compile. Don’t build on it: if the way out of a superstate depends on a variable, declare the variable one level further out.

A rep counter for the gym shows the repetitions of the current set. A press on the button counts a rep; rest ends the set; the next press starts a new set.

RepCounterSimulation

Diagram

Diagram of RepCounterStates: Set (initial, superstate), Counting (initial), Resting. Transitions: Counting to Counting: press / reps++; shown = reps; Set to Resting: rest; Resting to Set: press.RepCounterinput boolpress, restoutput intshownSetintreps = 0Countingpress/ reps++;shown = repsRestingrestpress

Code

scchart RepCounter {
input bool press, rest
output int shown
initial state Set {
int reps = 0
initial state Counting
if press do reps++; shown = reps go to Counting
}
if rest go to Resting
state Resting
if press go to Set
}
Tick 0 of 10
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 RepCounter
Tickinput bool pressinput bool restoutput int shownActive state of the chartActive state of the region of Set
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 10 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 10 ticks:

  1. press = false, rest = falseTick 1 enters Set, which initialises its local reps to 0, and Counting inside it.
  2. press = trueA press: reps becomes 1 and the display shown copies it.
  3. press = falseNo press, nothing happens.
  4. press = trueAnother press: reps is 2.
  5. press = falseNo press.
  6. rest = truerest leaves Set for Resting. shown is an output and keeps its value, 2.
  7. rest = false, press = trueA press starts the next set: Set is entered again, so reps is initialised to 0 again. This press does not count as a rep: Counting was entered in this tick, and its transition is delayed.
  8. press = falseNo press.
  9. press = trueThe first rep of the new set: reps is 1, not 3. Every entry of Set starts from scratch.
  10. press = falseNo press.
In tick 9 the first rep of the second set shows 1, not 3: reps started again from 0 when Set was entered in tick 7.

reps is declared in Set, so it is initialised every time Set is entered: in tick 1, and again in tick 7. (The press of tick 7 only starts the set: Counting is entered in that tick, and its transition is delayed.) That is the rule for every local: a variable declared in a superstate gets its initial value on every entry of the superstate, or, if it has none, its type’s default (0 for int, false for bool). Think of a superstate as a function and its locals as the function’s local variables: every call starts from scratch.

For the reps of one set, that is what you want. A value that has to survive leaving and re-entering, such as the total over all sets, belongs one level further out, at the top of the chart. You will build it in the second exercise below.

So far the programme never ends: the machine drains for ever. Let the drain lead to a final state, and let Programme react when its inside has finished:

WasherSimulation

Diagram

Diagram of WasherStates: Idle (initial), Programme (superstate), Filling (initial), Washing, Draining, Done (final). Transitions: Filling to Washing: full / motor = true; Washing to Draining: timeUp / motor = false; Draining to Done: empty; Idle to Programme: start; Programme to Idle: 1: stop / motor = false; Programme to Idle: 2: / beep.Washerinput boolstart, full, timeUp, empty, stopoutput boolmotoroutput signalbeepIdleProgrammeFillingWashingDrainingDonefull/ motor = truetimeUp/ motor = falseemptystart1: stop/ motor = false2: / beep

Code

scchart Washer {
input bool start, full, timeUp, empty, stop
output bool motor
output signal beep
initial state Idle
if start go to Programme
state Programme {
initial state Filling
if full do motor = true go to Washing
state Washing
if timeUp do motor = false go to Draining
state Draining
if empty go to Done
final state Done
}
if stop do motor = false go to Idle
do beep join to Idle
}
Tick 0 of 6
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. 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 Washer
Tickinput bool startinput bool fullinput bool timeUpinput bool emptyinput bool stopoutput bool motoroutput signal beepActive state of the chartActive state of the region of Programme
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 6 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 6 ticks:

  1. start = false, full = false, timeUp = false, empty = false, stop = falseTick 1 enters Idle.
  2. start = truestart: the machine enters Programme and Filling.
  3. start = false, full = truefull: Filling → Washing, motor on.
  4. full = false, timeUp = truetimeUp: Washing → Draining, motor off.
  5. timeUp = false, empty = trueempty: Draining → Done. Done is a final state, so Programme has finished: it terminates. Its termination transition, join to Idle, fires in this same tick: beep is emitted and the machine is back in Idle.
  6. empty = falsebeep is a signal, so it was present for that one tick only. The machine waits in Idle for the next start.
In tick 5 three things happen at once: Draining moves to Done, Programme terminates, and the join takes the machine back to Idle with a beep.

Done is a final state, drawn with a double outline (see Delayed and immediate transitions). At the top level of a chart a final state ends the chart; inside a superstate it ends the superstate’s inner state machine. When the inside of a superstate reaches a final state, the superstate terminates.

Termination on its own does not move anything: a terminated superstate stays active until one of its transitions leaves it. The transition written join to is a termination transition: it fires when its superstate has terminated. KIELER draws it with a green triangle where it starts, as on the transition from Programme back to Idle in the diagram above. It is drawn dashed because it is immediate, which is the next point.

In detail:

  • The join fires in the tick the superstate terminates. In tick 5 the inner transition to Done and the join both fire, and the beep belongs to that same tick.
  • The join’s effects run after everything the inside did in that tick. If the inside writes a variable on its way to the final state, the join’s effect already sees the new value.
  • A join without a trigger is immediate. It is checked in every tick in which its superstate is active, including the tick in which the superstate was entered.

The diagram numbers Programme’s two outgoing transitions: 1: stop and 2: / beep. They are priorities, as for any state with several outgoing transitions (see Triggers, effects and priority). What happens when a stop and the end of the programme fall into the same tick is a question for Aborts.

Because a trigger-less join is immediate, a superstate whose inside finishes at once is left again in the tick it was entered. Predict what this chart does when start arrives, run it, then open the explanation:

QuickPrediction

Diagram

Diagram of QuickStates: Idle (initial), Work (superstate), Begin (initial), Done (final), Finished. Transitions: Begin to Done: / steps++; Idle to Work: start / steps = 1; Work to Finished: / steps++.Quickinput boolstartoutput intstepsIdleWorkBeginDone/ steps++Finishedstart/ steps = 1/ steps++

Code

scchart Quick {
input bool start
output int steps
initial state Idle
if start do steps = 1 go to Work
state Work {
initial state Begin
immediate do steps++ go to Done
final state Done
}
do steps++ join to Finished
state Finished
}

Begin’s transition is immediate, and the join has no trigger.

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

Tickstart
1false
2true
  1. After tick 2, is Finished active?
Plays the scenario on the diagram, then marks each answer.
Explanation

In tick 2, Idle to Work fires and sets steps to 1. Entering Work enters Begin, whose immediate transition fires in the same tick: steps is 2 and Work reaches its final state, Done. Work has terminated, and its join is immediate, so it fires too: steps is 3 and the chart is in Finished. Three transitions in one tick, and Work was active for no time at all.

A termination transition can have a trigger: if ready join to After fires once its superstate has terminated and ready holds. Like every transition with a trigger, it is then delayed: it is not checked in the tick its superstate was entered, even if the superstate terminates in that tick and the trigger is true. Compare the two versions:

PlainJoin and GuardedJoinComparison

Diagrams

PlainJoin

Diagram of PlainJoinStates: Idle (initial), Work (superstate), Begin (initial), Done (final), After. Transitions: Begin to Done; Idle to Work: start; Work to After: / left = true.PlainJoininput boolstart, readyoutput boolleftIdleWorkBeginDoneAfterstart/ left = true

GuardedJoin

Diagram of GuardedJoinStates: Idle (initial), Work (superstate), Begin (initial), Done (final), After. Transitions: Begin to Done; Idle to Work: start; Work to After: ready / left = true.GuardedJoininput boolstart, readyoutput boolleftIdleWorkBeginDoneAfterstartready/ left = true

Code

PlainJoin

scchart PlainJoin {
input bool start, ready
output bool left
initial state Idle
if start go to Work
state Work {
initial state Begin
immediate go to Done
final state Done
}
do left = true join to After
state After
}

GuardedJoin

scchart GuardedJoin {
input bool start, ready
output bool left
initial state Idle
if start go to Work
state Work {
initial state Begin
immediate go to Done
final state Done
}
if ready do left = true join to After
state After
}
Tick 0 of 3
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 PlainJoin and GuardedJoin, no ticks yet
Tickinput bool startinput bool readyPlainJoin: output bool leftPlainJoin: Active state of the chartPlainJoin: Active state of the region of WorkGuardedJoin: output bool leftGuardedJoin: Active state of the chartGuardedJoin: Active state of the region of Work
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 3 ticks Both charts get the same inputs. Press Next to run tick 1, or Take over to set the inputs yourself.

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

  1. start = false, ready = trueTick 1 enters Idle in both charts. ready is true from the start; only GuardedJoin’s join reads it.
  2. start = truestart: both charts enter Work, and Begin → Done fires at once, so Work terminates in its entry tick. PlainJoin’s join has no trigger, so it is immediate: it fires now, sets left and ends in After. GuardedJoin’s if ready makes its join delayed, so it is not checked in Work’s entry tick: left stays false.
  3. start = falseGuardedJoin’s join is checked for the first time: Work is still terminated and ready still true, so it fires and sets left. Both charts are now in After, one tick apart.
ready is true throughout and start comes in tick 2: PlainJoin reaches After in tick 2, GuardedJoin one tick later.

The diagrams show the difference as well: PlainJoin’s join is drawn dashed, because it is immediate, and GuardedJoin’s solid, because it is delayed. The four ways to write a join, and whether each is checked in the tick its superstate is entered:

Termination transition Checked in the entry tick?
join to After yes: no trigger, so immediate
if ready join to After no: a trigger makes it delayed
immediate if ready join to After yes
delayed join to After no

After the entry tick, a delayed join is checked in every tick for as long as its superstate stays terminated, so it fires in the first tick in which its trigger holds.

A superstate can contain superstates, which can contain superstates, to any depth. The rules are the same at every level:

  • Entering a superstate enters its initial state, and if that is a superstate, its initial state, and so on, all in one tick.
  • A transition of a superstate leaves it whichever state is active inside it, however deep.
  • Termination travels outwards. When an inner superstate terminates and its join leads to a final state of the outer superstate, the outer one terminates in that same tick, and its own join can fire as well.

A coffee machine shows the last rule. Brew sits inside Make, and both finish with a join:

CoffeeSimulation

Diagram

Diagram of CoffeeStates: Idle (initial), Make (superstate), Grinding (initial), Brew (superstate), Pouring (initial), Poured (final), Served (final). Transitions: Pouring to Poured: full; Grinding to Brew: ground; Brew to Served: / cups++; Idle to Make: start; Make to Idle: / ready.Coffeeinput boolstart, ground, fulloutput signalreadyoutput intcups = 0IdleMakeGrindingBrewPouringPouredfullServedground/ cups++start/ ready

Code

scchart Coffee {
input bool start, ground, full
output signal ready
output int cups = 0
initial state Idle
if start go to Make
state Make {
initial state Grinding
if ground go to Brew
state Brew {
initial state Pouring
if full go to Poured
final state Poured
}
do cups++ join to Served
final state Served
}
do ready join to Idle
}
Tick 0 of 6
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 Coffee
Tickinput bool startinput bool groundinput bool fulloutput signal readyoutput int cupsActive state of the chartActive state of the region of MakeActive state of the region of Make.Brew
Click a tick to show it, or use the Left and Right arrow keys to go back and step.
Guided tour, 6 ticks Press Next to run tick 1, or Take over to set the inputs yourself.

Guided tour, 6 ticks:

  1. start = false, ground = false, full = falseTick 1 enters Idle.
  2. start = truestart enters Make, and with it Make’s initial state Grinding.
  3. start = false, ground = trueThe beans are ground: Grinding → Brew. Entering Brew enters its initial state Pouring in the same tick. Three states are active now, one inside the other: Make, Brew and Pouring.
  4. ground = falsefull is still false: Pouring waits, and Make, Brew and Pouring stay active.
  5. full = trueThe cup is full, and termination travels outwards in one tick: Pouring → Poured makes Brew terminate; Brew’s join (cups is 1) leads to Served, a final state of Make, so Make terminates too; and Make’s join emits ready and returns to Idle.
  6. full = falseBack in Idle, ready for the next cup.
Tick 5: the cup is full, and Brew, then Make, terminate in the same tick. Watch cups and ready.

In tick 5 three transitions fire, from the inside out: Pouring to the final state Poured, which ends Brew; Brew’s join, which counts the cup and enters Served, a final state of Make; and, since Make has now terminated as well, Make’s join, which emits ready and returns to Idle. Both joins have no trigger, so both are immediate and fire in the tick their superstate terminates.

The first exercise builds a rinse cycle this way.

A rinse cycle

After draining, the machine should rinse before it beeps: fill the drum again (full), rinse with the motor running until timeUp, and drain again (empty). Put the rinse in a superstate of its own, Rinse, inside Programme, and let Programme finish only when the rinse has finished. Run your chart and check that the beep comes in the same tick as the second empty.

Show the starter chart
scchart Washer {
input bool start, full, timeUp, empty, stop
output bool motor
output signal beep
initial state Idle
if start go to Programme
state Programme {
initial state Filling
if full do motor = true go to Washing
state Washing
if timeUp do motor = false go to Draining
state Draining
if empty go to Done
final state Done
}
if stop do motor = false go to Idle
do beep join to Idle
}
Show a solution
scchart Washer {
input bool start, full, timeUp, empty, stop
output bool motor
output signal beep
initial state Idle
if start go to Programme
state Programme {
initial state Filling
if full do motor = true go to Washing
state Washing
if timeUp do motor = false go to Draining
state Draining
if empty go to Rinse
state Rinse {
initial state Refilling
if full do motor = true go to Rinsing
state Rinsing
if timeUp do motor = false go to Emptying
state Emptying
if empty go to Rinsed
final state Rinsed
}
join to Done
final state Done
}
if stop do motor = false go to Idle
do beep join to Idle
}

A running total

Give RepCounter a second output, total, that counts every rep of the session, across all sets, while shown keeps showing the reps of the current set only.

Show the starter chart
scchart RepCounter {
input bool press, rest
output int shown
initial state Set {
int reps = 0
initial state Counting
if press do reps++; shown = reps go to Counting
}
if rest go to Resting
state Resting
if press go to Set
}
Show a solution
scchart RepCounter {
input bool press, rest
output int shown, total
initial state Set {
int reps = 0
initial state Counting
if press do reps++; total++; shown = reps go to Counting
}
if rest go to Resting
state Resting
if press go to Set
}
A superstate S has the termination transition if c join to T. Its inside reaches a final state in the tick S is entered, and c is true in that tick and in the next. When does the join fire?
Show the answer

Answer B: In the tick after that

A join with a trigger is delayed, like every transition with a trigger, so it is not checked in the tick S is entered. In the next tick S is still terminated and c is still true, so the join fires. GuardedJoin above does exactly this. Without the trigger, join to T would have fired in the entry tick.

A chart declares output int n = 0. Its superstate S declares int n = 5, and a transition inside S does n++. What is the output n after that transition?
Show the answer

Answer A: 0

Inside S, the name n means the nearest declaration, S’s own local. That local goes from 5 to 6. The output n is shadowed inside S and nothing assigns it, so it is still 0.

Set declares int reps without an initial value. The chart leaves Set while reps is 4, and later enters Set again. What is reps right after the new entry?
Show the answer

Answer B: 0

Variables do keep their values, but a local of a superstate is initialised again on every entry of the superstate: to its initial value if it has one, otherwise to its type’s default, 0 for an int. RepCounter above shows the same re-initialisation, there to its initial value, 0: the first rep of the second set, in tick 9, shows 1.

Key ideas

  • A superstate is a state with states inside. Entering it enters its initial state in the same tick, all the way down.
  • A superstate’s own transitions, written after its closing brace, leave it whichever inner state is active. The next entry starts again at the initial state.
  • Declarations inside a superstate are local to it. The nearest declaration wins, and locals are initialised again on every entry.
  • When the inside reaches a final state the superstate terminates, and join to can fire in that same tick. A join without a trigger is immediate; if c join to waits at least one tick.

Next, Concurrent regions: several state machines side by side, in one chart or one superstate.

Report a mistake on this page