Reusing charts
Using one chart inside another with state S is Other(...), wiring up its inputs and outputs, and keeping several charts in one file or in imported files.
A building has dozens of light switches, and every one of them works the same way. You would not want to draw the same two states and two transitions for each lamp. SCCharts lets you write the switch once, as a chart of its own, and use it wherever you need a switch. Each use is a reference: a state that says “I behave like that chart, wired to these variables”.
A chart inside a state
Section titled “A chart inside a state”PushSwitch is an ordinary chart: one press switches its lamp on, the next switches it off.
LampPanel uses it twice, once for each lamp, in two concurrent regions:
Diagram
Code
scchart LampPanel { input bool left, right output bool leftLamp = false, rightLamp = false
region { initial state Left is PushSwitch(left to press, leftLamp to lit) } region { initial state Right is PushSwitch(right to press, rightLamp to lit) }}
scchart PushSwitch { input bool press output bool lit = false
initial state Off if press do lit = true go to On
state On if press do lit = false go to Off}| Tick | input bool left | input bool right | output bool leftLamp | output bool rightLamp | Active state of region 1 of the chart | Active state of region 2 of the chart |
|---|
Guided tour, 5 ticks:
- left = false, right = falseTick 1 enters
LeftandRight. Each is a fresh copy ofPushSwitch, starting in its ownOff. - left = true
leftis wired to the first copy’spress, andleftLampto itslit: the left lamp comes on. The right copy is unaffected. - left = false, right = trueThe right copy switches on
rightLamp. The two copies run side by side, each in its own region. - right = false, left = trueThe left copy is in its
Onstate and switches off. The right copy staysOn. - left = falseLeft off, right on.
left and right each drive their own copy of PushSwitch, drawn inside Left and Right: pressing one never affects the other lamp.The line that does the work is
initial state Left is PushSwitch(left to press, leftLamp to lit)state Left is PushSwitch(...) makes Left a referencing state: it behaves exactly like the chart
PushSwitch. The list in brackets is the binding, the wiring between the two charts. left to press
means “my left is your press”, and leftLamp to lit means “my leftLamp is your lit”. Every
input and output of the referenced chart must be wired to something; bindings below
shows the ways to do it.
In the diagram, a referencing state shows the name of the chart it uses after an @ sign, followed by
the actuals it is bound to. KIELER draws it collapsed, as a box that you can expand to see the chart
inside. The simulations on this page show the copies expanded, so that you can watch their states.
What is really does: a copy
Section titled “What is really does: a copy”KIELER treats a reference like a macro. Before compiling, it replaces Left with a copy of
PushSwitch’s inside: its regions, its states and transitions, its local declarations and its own
entry, during and exit actions. The copy keeps the name Left, its initial or final flag, and
its incoming and outgoing transitions. Everywhere in the copy, press is replaced by left and lit by
leftLamp.
So a referencing state is simply a superstate that you did not have to write out, and everything you know about superstates applies to it: it can be left by a weak or strong abort, it can be suspended, and it can be the target of a history transition. A few consequences are worth spelling out:
- Every reference is a separate copy.
LeftandRighteach have their ownOffandOn. If the referenced chart has local variables, each copy has its own set. (A variable declaredglobalin the referenced chart is the exception: all copies share it.) - The referenced chart’s own actions become the referencing state’s. An
entry do ...written at the top ofPushSwitchwould run every timeLeftis entered; itsexitactions every timeLeftis left. - Aborts see the copy’s writes like any inner behaviour. A weak abort
if leftLamp go to ...onLeftsees what the copy wrote in that tick and fires at once; a strong abortabort toreads the value from before the copy ran, so it fires a tick later.
Two copies of one counter show the first point. Tally counts clicks with a self-loop; DoorCounters
uses it for the front door and for the back door:
Diagram
Code
scchart DoorCounters { input bool front, back output int frontCount = 0, backCount = 0
region { initial state Front is Tally(front to click, frontCount to count) } region { initial state Back is Tally(back to click, backCount to count) }}
scchart Tally { input bool click output int count = 0
initial state Counting if click do count++ go to Counting}| Tick | input bool front | input bool back | output int frontCount | output int backCount | Active state of region 1 of the chart | Active state of region 2 of the chart |
|---|
Guided tour, 6 ticks:
- front = false, back = falseTick 1 enters
FrontandBack, two copies ofTally, each in its ownCountingstate. - front = trueSomeone comes in at the front: the self-loop of
Front’s copy fires and adds one tofrontCount.Back’s copy does nothing. frontis still true (inputs keep their value), soFront’s copy counts again: 2.- front = false, back = trueNow the back door: only
Back’s copy counts. - front = trueBoth doors at once: both copies take their self-loop in the same tick, each adding one to its own output.
- front = false, back = falseNobody: 3 at the front, 2 at the back. Each copy has counted only its own door.
Tally, drawn inside Front and Back. In tick 5 both doors click, and both copies count, each into its own output.The two copies share nothing but their source: Front’s Counting state and self-loop are not
Back’s, and count is frontCount in one copy and backCount in the other.
Several charts in one file
Section titled “Several charts in one file”A file may contain any number of scchart definitions, in any order: a reference may name a chart that
is defined further down. But only one chart in a file is simulated, and it is the first one. The
others are only there to be referenced.
Here is the lamp panel again, with the two charts the other way round:
Diagram
Code
scchart PushSwitchFirst { input bool press output bool lit = false
initial state Off if press do lit = true go to On
state On if press do lit = false go to Off}
scchart PanelSecond { input bool left, right output bool leftLamp = false, rightLamp = false
region { initial state Left is PushSwitchFirst(left to press, leftLamp to lit) } region { initial state Right is PushSwitchFirst(right to press, rightLamp to lit) }}| Tick | input bool press | output bool lit | Active state of the chart |
|---|
Guided tour, 3 ticks:
- press = false
PushSwitchFirstcomes first in the file, so it is the chart that is simulated: its interface ispressandlit.PanelSecond, with itsleftandright, never runs. - press = true
presstoggleslit, exactly as if the file held onlyPushSwitchFirst. - press = falseIn
On.
press and lit, and the panel never starts.The panel, PanelSecond, is not broken. It is simply not the chart that runs. Put the chart you want to
run at the top of the file and the charts it uses below it. SCCharts Lab’s Simulate command and this
site’s simulator both follow KIELER here.
Two more rules apply to files with several charts:
-
The whole file is checked. A misspelt variable in a chart that nothing uses still rejects the file, with the usual
Couldn't resolve reference to ValuedObject 'q'.Problems that only the scheduler finds, such as a causality cycle, are found only in the simulated chart and the charts it references. -
Give every chart its own name. With two charts of the same name, KIELER warns
There are multiple root states with the same name. 'Twin'and simulates the first; a reference to the name may pick either. This happens easily when you copy a chart within a file and forget to rename it.
Charts in other files: import
Section titled “Charts in other files: import”To use a chart that lives in another file, import the file. Its charts can then be referenced by name.
HallLights imports the file push-switch.sctx, shown below it, and uses its chart ImportedSwitch:
Diagram
Code
import "push-switch"
scchart HallLights { input bool switchA output bool hall = false
initial state Hall is ImportedSwitch(switchA to press, hall to lit)}| Tick | input bool switchA | output bool hall | Active state of the chart |
|---|
Guided tour, 3 ticks:
- switchA = false
import "push-switch"loadspush-switch.sctxfrom the same folder, so its chartImportedSwitchcan be referenced. This file’s first chart,HallLights, is the one simulated. - switchA = trueThe imported switch turns
hallon. - switchA = falseOn.
import "push-switch", makes the chart ImportedSwitch from the other file available.scchart ImportedSwitch { input bool press output bool lit = false
initial state Off if press do lit = true go to On
state On if press do lit = false go to Off}import "push-switch"loadspush-switch.sctxfrom the folder of the importing file. The.sctxmay be left out or written. A path relative to that folder works too, andimport "lib/*"loads every.sctxfile in the folderlib.- Imports come first in the file, before the first
scchart. - Imported charts are only for referencing. The chart that runs is still the first chart of the file you
simulate, here
HallLights. importnames a file;isnames a chart. The two names need not match, and a file may hold several charts.
Three mistakes, and what KIELER says about them:
-
No
import. A chart in another file is invisible, even if it sits in the same folder:Couldn't resolve reference to NamedObject 'ImportedSwitch'. -
A file that does not exist. KIELER only warns,
Broken Import: There is no SCCharts model with the given name.and then reports the references it cannot resolve, as above.
-
The old pragma
#import "beep", which older examples use. Current KIELER does not accept it (no viable alternative at input 'import'): writeimport.
Wiring: bindings
Section titled “Wiring: bindings”The formals of a referenced chart are its inputs and outputs, in the order they are declared. A binding connects each formal to an actual: a variable or signal of the referencing chart, or for an input, a constant. There are three ways to write it.
Explicit bindings
Section titled “Explicit bindings”actual to formal, as in left to press: “my left to your press”. Explicit bindings say exactly what
is meant, they survive changes to the referenced chart, and they can be written in any order. Prefer them.
Bindings by order
Section titled “Bindings by order”You may leave out the names: PushSwitch(left, leftLamp) binds left to the first formal and leftLamp
to the second, in the order the formals are declared in PushSwitch. This is shorter, but it breaks
silently as soon as someone reorders or adds declarations in the referenced chart, and KIELER does not
check that the types match. The exercise at the end of this chapter shows how badly that can go.
You can mix the two styles, but the order bindings then follow a rule that few people guess. Here
ThreeDigits has the formals a, b, c (inputs) and result (an output), and computes
result = a * 100 + b * 10 + c. The reference is ThreeDigits(1 to b, 2, number, seven to a), where
seven is a local with the value 7. (If you switch to the diagram, note that it lists only the actuals,
not the formals they are bound to.) Predict number in tick 2, then open the explanation:
Diagram
Code
scchart PointerPuzzle { output int number = 0 int seven = 7
initial state R is ThreeDigits(1 to b, 2, number, seven to a)}
scchart ThreeDigits { input int a, b, c output int result
initial state Compute do result = a * 100 + b * 10 + c go to Compute}1 to b comes first. Which formal does the 2 after it bind to?
Explanation
number is 712. KIELER binds the arguments left to right with a pointer into the list of formals. An
explicit binding x to f moves the pointer to f and then one past it; an order binding takes the formal
at the pointer and moves on. So after 1 to b, the 2 binds to c, the formal declared after b, and
number binds to result. Then seven to a binds a. If the pointer runs past the last formal, the
binding is an error.
Implicit bindings
Section titled “Implicit bindings”A formal that is not bound by any argument is bound by name to a variable or signal of the same name
that is visible at the referencing state (declared in it, in a state around it, or in the chart).
is ImplicitSwitch(), or just is ImplicitSwitch, binds every formal this way:
Diagram
Code
scchart ImplicitPanel { input bool press output bool lit = false
initial state Switch is ImplicitSwitch()}
scchart ImplicitSwitch { input bool press output bool lit = false
initial state Off if press do lit = true go to On
state On if press do lit = false go to Off}| Tick | input bool press | output bool lit | Active state of the chart |
|---|
Guided tour, 3 ticks:
- press = false
ImplicitSwitch()binds nothing explicitly, sopressandlitare bound by name to the parent’spressandlit. The editor warnsValued Objects are bound implicitly!, followed by the names. - press = trueThe implicitly bound
pressis the parent’s input:litcomes on. - press = falseOn.
Warning: press, lit are bound by name
- Warning, line 5, column 27KIELER says:
Valued Objects are bound implicitly! press, lit,
press,litofImplicitSwitchare connected to the variables of the same name here. Write them in the parameter list (press to press) to make it explicit.About this message
press and lit are wired to the parent’s press and lit because the names match. The editor warns about it.The editor warns Valued Objects are bound implicitly!, followed on the next line by the names, each
followed by a comma (here press, lit, ). Implicit binding saves typing when a big interface is passed
straight through, but it is easy to bind something you did not mean to. If no variable of that name is
visible, the binding is an error.
What can be bound
Section titled “What can be bound”A variable or a signal, of course, and for an input also a literal such as true or 3: the input is
then that constant inside the copy. Any other expression, such as a && b, cannot be bound: compute it
into a variable of the referencing chart first and bind the variable.
Arrays, signals and types
| Actual | to an input | to an output |
|---|---|---|
| a variable or signal | yes | yes |
| a whole array | yes, to an array formal | yes, to an array formal |
one array element, a[1] |
yes, to a scalar formal | yes, to a scalar formal |
a literal, true or 3 |
yes: the input is that constant inside the copy | no |
any other expression, a && b |
no | no |
Signals bind to signals, valued signals included. Types are not checked: KIELER will happily bind a
float to a bool input.
Inside the copy, the formal is your variable
Section titled “Inside the copy, the formal is your variable”Binding is not copying values in and out. Inside the copy, the formal is replaced by the actual, so the copy works directly on your variables:
- When the copy writes an output, the bound variable changes at that moment, and the regions next to the referencing state see the new value in the same tick, as with any write.
- When the copy writes one of its inputs, it writes the variable you bound to it. A referenced chart
that does
level -= 1on its inputlevelchanges your variable. - Bind one variable to two outputs and both writes go to that variable.
Initial values follow the same logic, with a result that surprises most people, so they get a section of their own.
Output initial values are applied on every entry
Section titled “Output initial values are applied on every entry”In the chart below, remaining starts at 0, but the referenced Countdown declares its output as
output int left = 3. KIELER turns that initial value into an entry action of the referencing state: the
bound variable is set to 3 every time Timing is entered.
Diagram
Code
scchart OvenTimer { input bool start, stop output int remaining = 0
initial state Idle if start go to Timing
state Timing is Countdown(remaining to left) if stop go to Idle}
scchart Countdown { output int left = 3
initial state Counting if left > 0 do left-- go to Counting}| Tick | input bool start | input bool stop | output int remaining | Active state of the chart |
|---|
Guided tour, 9 ticks:
- start = false, stop = falseTick 1 starts in
Idle.remaininghas the parent’s initial value, 0. - start = trueEntering
TimingappliesCountdown’s initial value for its outputleftto the bound variable:remaining= 3, not 0. - start = falseCounting down: 2.
- 1.
- 0.
- The countdown has finished;
remainingstays 0. - stop = trueBack to
Idle. - stop = false, start = trueEvery entry of
Timingapplies the initial value again:remaining= 3. - start = false2.
Note: remaining gets the initial value of Countdown's output left whenever Timing is entered, not its own
- Note, line 8, column 19KIELER turns the referenced chart's output initial values into entry actions of the referencing state, so they win over the initial value declared here and are applied again on every entry.
remaining is 0 in tick 1, becomes 3 when Timing is entered in tick 2, and is set to 3 again on the next entry in tick 8.Here that is convenient: every start restarts the countdown. But it also means that the referenced chart’s initial value wins over yours, and that a value you wanted to keep across visits is overwritten whenever the state is entered. Leave the initial value off the referenced chart’s output if the referencing chart should decide. Initial values of the referenced chart’s inputs, on the other hand, are ignored: the input is your variable, with your value.
Waiting for a referenced chart to finish
Section titled “Waiting for a referenced chart to finish”A referenced chart terminates when all its regions reach final states, and then the referencing state
terminates: a join to on it fires in the same tick, as for any superstate. This combination lock uses
one small chart, Await, twice: once for a, then once for b.
Diagram
Code
scchart Combination { input bool a, b output bool open = false
initial state First is Await(a to key) join to Second
state Second is Await(b to key) join to Opened
state Opened { entry do open = true }}
scchart Await { input bool key
initial state Waiting if key go to Got
final state Got}| Tick | input bool a | input bool b | output bool open | Active state of the chart |
|---|
Guided tour, 6 ticks:
- a = false, b = falseTick 1 enters
First, a copy ofAwaitwaiting fora. - b = true
bis the wrong button now:Firstwaits fora. - b = false, a = true
a: the copy reaches its final stateGot, soFirstterminates andjoin to Secondfires in the same tick. - a = false
Second, a new copy ofAwait, waits forb. - b = true
b:Secondterminates and the lock opens. - b = falseOpen.
b first (tick 2) does nothing; a (tick 3) finishes First, and b (tick 5) finishes Second and opens the lock.If the referenced chart has no final state in some region, it can never finish. KIELER then warns
The referenced SCChart does not terminate, but you are using a termination to proceed.and the join to never fires.
Other forms you may meet
Section titled “Other forms you may meet”-
refdeclarations.ref C g(X, Y, O)among a state’s declarations also makes a copy ofC, which runs alongside the state’s regions. They are mostly used in dataflow regions; elsewhere, an extra region withinitial state G is C(X, Y, O)says the same more visibly. -
region R is C(...)is not a reference. KIELER rejects it withCouldn't resolve reference to NamedObject 'C'.The form belongs to inheritance (extends,override), which Enums, structs and classes touches on and the KIELER wiki’s Object Orientation page describes. To run a chart as a region, writeregion { initial state R is C(...) }. -
state S calls C(...)is accepted by KIELER’s grammar, but its C simulation cannot compile it. -
A chart cannot contain itself, directly or through other charts: the copies would never end. Official KIELER’s compiler runs out of stack on such a chart, without a useful message; SCCharts Lab reports
Recursive reference: chart Main uses itself (Main -> Main).Compiler messages explains it.
Try it
Section titled “Try it”The silent doorbells
DoorbellPanel uses the chart Bell for its front and back doors, but neither bell ever rings: press
front or back, and frontRings and backRings stay 0. KIELER reports no error and no warning. Find
out why, and fix the panel without changing Bell.
Show the starter chart
scchart DoorbellPanel { input bool front, back output int frontRings = 0, backRings = 0
region { initial state Front is Bell(front, frontRings) } region { initial state Back is Bell(back, backRings) }}
scchart Bell { output int rings = 0 input bool button
initial state Quiet if button do rings++ go to Ringing
state Ringing if !button go to Quiet}Show a hint
Bell(front, frontRings) binds by order. In which order does Bell declare its input and its output?
Show a solution
scchart DoorbellPanel { input bool front, back output int frontRings = 0, backRings = 0
region { initial state Front is Bell(front to button, frontRings to rings) } region { initial state Back is Bell(back to button, backRings to rings) }}
scchart Bell { output int rings = 0 input bool button
initial state Quiet if button do rings++ go to Ringing
state Ringing if !button go to Quiet}Check your understanding
Section titled “Check your understanding”Show the answer
Answer D: Helper runs, because it is the first chart in the file
KIELER always simulates the first chart in a file, and nothing chooses another. Move Main to the top. The
order of definitions does not matter for references: a reference may name a chart defined further down.
Show the answer
Answer B: 10
The initial value of Counter’s output becomes an entry action of C, applied to the bound variable on
every entry: total = 10. The counting is delayed, so it starts in tick 4.
Show the answer
Answer C: Two: each reference is a separate copy of Count
A reference is expanded into a copy of the referenced chart, locals included, so each instance counts on its own. To share a value, bind the same variable of the referencing chart to both.
Key ideas
state S is C(bindings)makesSa copy of chartC, wired to your variables; it then behaves like any superstate.- Only the first chart in a file is simulated. Put the main chart first; other charts, and imported files, are there to be referenced.
import "file"makes another file’s charts available;isnames the chart.- Bind explicitly (
actual to formal). Order bindings and implicit bindings work, but break silently. - Inside the copy, a formal is your variable: writes go straight to it, and an output’s initial value is assigned to it on every entry.
join toon a referencing state fires when the referenced chart reaches its final states.
Next, Arrays and for regions: a region written once and copied for every index of an array.