My chart does not do what I expect
Troubleshooting a chart that compiles but misbehaves: the usual symptoms, their causes and fixes, problems setting up SCCharts Lab, and a checklist for testing a chart.
Your chart compiles, but it does something other than what you meant. Find the symptom below: each comes with its usual cause, the fix, and a link to the full explanation. If the compiler gives you a message instead, look it up in Compiler messages.
Nothing happens in tick 1
Section titled “Nothing happens in tick 1”Tick 1 only starts the chart: it gives every variable its initial value and enters the initial
states. An ordinary transition out of a state is checked from the tick after the state was entered,
so the first one can fire in tick 2, whatever the inputs are in tick 1. If something must happen in
tick 1, write it as an entry action of the initial state or make the transition immediate.
What happens in tick 1
My transition fires a tick late
Section titled “My transition fires a tick late”The same rule, one state further on: a transition is not checked in the tick its state is entered, so
every state on a path costs a tick. ChimeLate reaches Pressed in the tick of the press and chimes
in the next. In ChimeNow the transition out of Pressed is immediate, so it chimes at once:
Diagrams
ChimeLate
ChimeNow
Code
ChimeLate
scchart ChimeLate { input bool press output signal chime
initial state Idle if press go to Pressed
state Pressed do chime go to Idle}ChimeNow
scchart ChimeNow { input bool press output signal chime
initial state Idle if press go to Pressed
state Pressed immediate do chime go to Idle}| Tick | input bool press | ChimeLate: output signal chime | ChimeLate: Active state of the chart | ChimeNow: output signal chime | ChimeNow: Active state of the chart |
|---|
Guided tour, 4 ticks, the same inputs for both charts:
- press = falseTick 1 enters
Idlein both charts. - press = true
press: both charts go toPressed. InChimeNowthe immediate transition out ofPressedfires at once, so it chimes in this tick and is back inIdle. InChimeLatethat transition is delayed: it is not checked in the tickPressedis entered. - press = false
ChimeLatechimes now, one tick late. - Both charts wait in
Idle.
ChimeNow chimes in tick 2, ChimeLate in tick 3.immediate is the fix when the transition should fire on arrival, but a cycle made only of immediate
transitions is rejected, so at least one transition on every loop must stay delayed. Count delays
(if 3 A) and a join to with a trigger do not count the entry tick either.
Delayed and immediate transitions
My output stays true
Section titled “My output stays true”A variable keeps its value until something writes it again. Setting ding = true on a transition
does not set it back afterwards, however briefly you meant it to be true:
Diagrams
BellVariable
BellSignal
Code
BellVariable
scchart BellVariable { input bool press output bool ding = false
initial state Waiting if press do ding = true go to Waiting}BellSignal
scchart BellSignal { input bool press output signal ding
initial state Waiting if press do ding go to Waiting}| Tick | input bool press | BellVariable: output bool ding | BellVariable: Active state of the chart | BellSignal: output signal ding | BellSignal: Active state of the chart |
|---|
Guided tour, 6 ticks, the same inputs for both charts:
- press = falseTick 1 enters
Waitingin both charts. - press = true
press:BellVariablesetsdingto true,BellSignalemitsding. Both read true after this tick. - press = falseNothing fires. The variable keeps the value it was given; the signal is absent, because nothing emitted it in this tick.
- The same: the variable stays true.
- press = trueAnother press: the signal is present again. The variable was true all along.
- press = falseTo make the variable read like the signal, something would have to set it back to false in every tick after the press.
If the output marks an event (“a coin was inserted”, “beep now”), declare it as a signal: a signal is
present only in a tick that emits it. If it is a setting that lasts, keep the variable and write
false where the setting ends, on the transition that leaves the state or in its exit action.
Signals or variables? and
Entry, during and exit actions
A button press counts many times
Section titled “A button press counts many times”A chart sees values, not changes. While a button is held down, press is true in every tick, and an
input in the simulators keeps its value until you change it, so a button you switched on stays
pressed. To react once per press, react to the tick in which it becomes true,
press && !pre(press):
Diagrams
PressCounter
PressCounterEdge
Code
PressCounter
scchart PressCounter { input bool press output int count = 0
initial state Counting if press do count++ go to Counting}PressCounterEdge
scchart PressCounterEdge { input bool press output int count = 0
initial state Counting if press && !pre(press) do count++ go to Counting}| Tick | input bool press | PressCounter: output int count | PressCounter: Active state of the chart | PressCounterEdge: output int count | PressCounterEdge: Active state of the chart |
|---|
Guided tour, 7 ticks, the same inputs for both charts:
- press = falseTick 1 enters
Countingin both charts. - press = trueThe button goes down. Both charts count one.
- The button is still down: an input keeps its value.
PressCounterseespresstrue again and counts two.PressCounterEdgewaits forpressto become true, and it already was. - Still held:
PressCounteris at three,PressCounterEdgestill at one. - press = falseThe button is released.
- press = trueA second press.
PressCounterEdgecounts two presses,PressCounterfour ticks. - press = falseReleased.
PressCounter counts ticks, PressCounterEdge counts presses.To press a button for a single tick in this guide’s simulations, use the pulse button next to its switch; in SCCharts Lab, switch the input on, step, and switch it off again. Edges and pulses has the recipes, and Debouncing the cure for a real button that bounces.
My counter starts from 0 again
Section titled “My counter starts from 0 again”A variable declared inside a state is initialised again every time that state is entered: by a
self-loop, by a transition back into its superstate, even by a history transition. So a counter
declared there starts over on every visit, and an entry do n = 0 does the same on every self-loop.
Declare the counter one level further out, at the top of the chart or in a superstate that stays
active, or make it static.
Fresh on every entry,
Self-loops leave and come back and
History restores states, not data
Two regions set the same variable
Section titled “Two regions set the same variable”KIELER rejects a chart in which two concurrent regions could set one variable to different values, with
The SCG is NOT asc-schedulable! and a list of Can't schedule from … lines. It does so even when you
know that the two writes never happen in the same tick: the check looks at the chart’s structure, not
at its triggers. Give the variable one owner, a single region that does all the writing, and let the
other regions send it requests with signals. Increments such as count += 1 from several regions are
allowed: they add up. Write–write conflicts,
One writer per variable, and
Sharing a resource for an owner that hands a device to one region at a time.
If such a chart compiles but its result changes when you swap the order of its regions in the file,
look for updates written the old way, x = x + 1, in two regions.
A trap: old-style updates
I need a timeout in seconds
Section titled “I need a timeout in seconds”A chart counts ticks, not seconds. If whatever runs it ticks at a fixed rate, counting ticks is
measuring time: at one tick every 10 ms, if 300 true go to Off waits three seconds. If the hardware
has a timer that sets an input once a second, count that input instead, if 3 second, as the oven in
Short on time? does. And if the environment can say
how much time has passed, a clock measures it directly. Counters and timeouts
and Time
I cannot leave a superstate from inside it
Section titled “I cannot leave a superstate from inside it”A transition connects two states on the same level, so a state inside a superstate cannot go to a
state outside it: KIELER says Couldn't resolve reference to State 'Idle'., with your state’s name.
There are two ways out. A transition written on the superstate itself, after its closing brace, leaves
it whatever the inside is doing. Or the inside reaches a final state, and the superstate’s join to
fires. Leaving from anywhere and
Final states and join to
My join to never fires
Section titled “My join to never fires”A join to fires when every region of its superstate is in a final state at the same time. Look for
one of these:
- A region without a final state, or one whose final state is never reached. KIELER warns about
a missing one
(
Every region should have a final state…,All or none concurrent regions…). Give every region a final state that it can reach. - A region that leaves its final state again. A final state may have outgoing transitions; if a region moves on before the others arrive, the regions are never final together.
- A signal declared in the superstate, together with an action. In KIELER a superstate that declares a signal and also has an entry, during or exit action never terminates, even when all its regions are final. There is no warning:
Diagrams
PrintStuck
PrintFixed
Code
PrintStuck
scchart PrintStuck { input bool job output int pages = 0 output bool busy
initial state Idle if job go to Printing
state Printing { signal fed entry do busy = true region Feed: initial state Feeding do fed go to Fed final state Fed region Count: initial state Waiting if fed do pages++ go to Counted final state Counted } do busy = false join to Idle}PrintFixed
scchart PrintFixed { input bool job output int pages = 0 output bool busy signal fed
initial state Idle if job go to Printing
state Printing { entry do busy = true region Feed: initial state Feeding do fed go to Fed final state Fed region Count: initial state Waiting if fed do pages++ go to Counted final state Counted } do busy = false join to Idle}| Tick | input bool job | PrintStuck: output int pages | PrintStuck: output bool busy | PrintStuck: Active state of the chart | PrintStuck: Active state of region Feed (in Printing) | PrintStuck: Active state of region Count (in Printing) | PrintFixed: output int pages | PrintFixed: output bool busy | PrintFixed: Active state of the chart | PrintFixed: Active state of region Feed (in Printing) | PrintFixed: Active state of region Count (in Printing) |
|---|
Guided tour, 6 ticks, the same inputs for both charts:
- job = falseTick 1 enters
Idlein both charts. - job = true
job: both charts enterPrintingand setbusy. - job = falseIn both charts both regions reach their final states. In
PrintFixedthejoin tofires at once, andbusyis false again. InPrintStuck, whosePrintingdeclares the signalfedand has an entry action, it never fires. PrintStuckstays inPrintingfor good.- job = trueA second
job: onlyPrintFixedcan start again. - job = false
PrintFixedhas counted two pages,PrintStuckone.
Note: KIELER never takes the join of Printing: it declares signals (fed) and has state actions (PrintStuck)
- Note in
PrintStuck, line 9, column 9KIELER moves the regions of such a state into a helper state that never finishes. Declare the signals one level up, or move the actions out of the state.
Fed and Counted in tick 3, but only PrintFixed, which declares fed in the chart instead of in Printing, leaves through its join to.Declare the signal one level further out, as PrintFixed does, or move the action onto the
transition that enters the superstate.
Final states and join to ends with this
case.
History brings me back to the start
Section titled “History brings me back to the start”Only go to S history (or shallow history) resumes S where it was left; a plain go to S always
starts it again from its initial states. If you did write history and the state still starts over,
look at where it is nested. KIELER honours a history transition into a nested state only if every
superstate around that state is itself the target of some history transition, and silently treats
any other one as a plain go to:
Diagrams
MenuNested
MenuNestedFixed
Code
MenuNested
@layout[elk.direction] DOWNscchart MenuNested { input bool power, menu, home, right output int screen = 0
initial state Off { entry do screen = -1 } if power go to On
state On { initial state Home { entry do screen = 0 } if menu go to Settings history
state Settings { initial state Brightness { entry do screen = 11 } if right go to Contrast
state Contrast { entry do screen = 12 } } if home go to Home } if !power go to Off}MenuNestedFixed
@layout[elk.direction] DOWNscchart MenuNestedFixed { input bool power, menu, home, right output int screen = 0
initial state Off { entry do screen = -1 } if power go to On shallow history
state On { initial state Home { entry do screen = 0 } if menu go to Settings history
state Settings { initial state Brightness { entry do screen = 11 } if right go to Contrast
state Contrast { entry do screen = 12 } } if home go to Home } if !power go to Off}| Tick | input bool power | input bool menu | input bool home | input bool right | MenuNested: output int screen | MenuNested: Active state of the chart | MenuNested: Active state of the region of On | MenuNested: Active state of the region of On.Settings | MenuNestedFixed: output int screen | MenuNestedFixed: Active state of the chart | MenuNestedFixed: Active state of the region of On | MenuNestedFixed: Active state of the region of On.Settings |
|---|
Guided tour, 9 ticks, the same inputs for both charts:
- power = false, menu = false, home = false, right = falseTick 1 enters
Offin both (screen= −1). - power = truePower on: the first visit to
Onstarts inHome(0). - menu = trueThe first visit to
Settings:Brightness(11). - menu = false, right = true
Contrast(12). - right = false, home = true
Home(0). - home = false, menu = trueBack to
Settingswith history.MenuNested: surprise,Settingsstarts again inBrightness(11).Onis not the target of a history transition, so KIELER silently treatsgo to Settings historyas a plaingo to Settings.MenuNestedFixed:Onis a history target (fromOff), so the history inside it works:Contrast(12). - menu = false, power = falsePower off:
Off(−1). - power = truePower on.
MenuNestedstartsOninHome(0).MenuNestedFixedentersOnwithshallow history, so it resumes inSettings, which starts again inBrightness(11). The fix changed what switching on does. HomeinMenuNested,BrightnessinMenuNestedFixed.
Note: KIELER ignores this history: Settings restarts from its initial state (MenuNested)
- Note in
MenuNested, line 15, column 5KIELER only handles history on transitions into states of the chart's top-level regions, or inside a state that is itself the target of a history transition. HereSettingsis nested in a state without one, so this transition behaves like a normal transition. Move the mode switch to the top level, or give the enclosing state a history transition too.
Contrast in tick 4, go home and open the menu again in tick 6: MenuNested starts Settings again, MenuNestedFixed resumes it.A history transition that is silently ignored has the details and the fix.
My chart never terminates
Section titled “My chart never terminates”A chart terminates when every region at its top level is in a final state, and then it does nothing more. If yours runs on:
- Check the final states, as for a
join toabove: every top-level region needs one, and all of them must be final in the same tick. - A chart that declares any signal never terminates in KIELER’s simulator, even in its final states: an input, output or local signal is enough. The simulation adds an action of its own to every chart, so the rule of the previous section applies to the chart itself. This guide’s simulations do the same.
Most reactive systems never terminate, and do not need to. If yours should stop, letting it rest in final states without outgoing transitions is usually all you need: the chart then stays there. Signals and termination
The wrong transition fires
Section titled “The wrong transition fires”When several transitions could fire, the order decides:
- The first transition written wins. A state’s transitions are tried in the order they are
written (
1:,2:in the diagram), and only the first whose trigger holds fires. Put the one that must win first; a safety check goes at the top. Priority - A transition without a trigger hides the ones after it. It always holds, so a delayed
transition written after it can never fire, and KIELER warns
The transition is not reachable.Write it last. abort tobeatsgo to. Strong aborts must be written first, so they win over weak aborts andjoin toin the same tick. Several ways out- The inside of a superstate still fired in the tick you left it. That is a weak abort,
go to: the inside finishes its tick first. If it must not, useabort to. Weak and strong
An exit action ran although I used abort to
Section titled “An exit action ran although I used abort to”That is KIELER’s rule: exit actions run however a state is left, a strong abort included. abort to
skips the inner transitions and the during actions of the tick it fires in, never the exit actions
(the KIELER wiki says otherwise, and is out of date there). Write exit actions as clean-up that always
happens, and put what must not happen on an emergency stop on the transitions that end the state
normally. Actions and aborts
My outputs are wrong only in tick 1
Section titled “My outputs are wrong only in tick 1”Tick 1 has no previous tick, so in tick 1 pre(x) is the default of the type, 0 or false, not the
initial value of x. A test such as temp > pre(temp) is then true in tick 1 although nothing has
changed. A plain during action does not run in the tick its state is entered, so for the initial
state it skips tick 1:
Diagrams
Rising
RisingFromTick2
Code
Rising
scchart Rising { input int temp output bool rising
initial state Watch { immediate during do rising = temp > pre(temp) }}RisingFromTick2
scchart RisingFromTick2 { input int temp output bool rising
initial state Watch { during do rising = temp > pre(temp) }}| Tick | input int temp | Rising: output bool rising | Rising: Active state of the chart | RisingFromTick2: output bool rising | RisingFromTick2: Active state of the chart |
|---|
Guided tour, 5 ticks, the same inputs for both charts:
- temp = 20Tick 1. In
Risingtheimmediate duringaction runs at once and compares 20 withpre(temp), which is 0 in tick 1:risingis true. InRisingFromTick2the plainduringaction waits for the next tick, sorisingstays false. - From here on both charts compare
tempwith its real previous value, and agree. - temp = 2121 after 20: rising, in both.
- temp = 2323 after 21: rising.
- temp = 2222 after 23: not rising.
temp is 20 from tick 1, and only Rising, with immediate during, reports a rise in tick 1.So if a comparison with pre must skip tick 1, put it in a plain during action, as
RisingFromTick2 does, or on a delayed transition: neither runs in the tick its state is entered.
Another cause: a variable whose initial value is built on another one that reads an input can come
out wrong in tick 1, because of a bug in KIELER’s compiler; compute it on a transition instead
(Tick 1 runs input-free initial values first).
Tick 1 has no past has more on pre in tick 1.
The playground and SCCharts Lab disagree
Section titled “The playground and SCCharts Lab disagree”The playground runs this guide’s own engine, a reimplementation of KIELER’s compiler checks and simulator that runs in the browser. Every example in the guide has been checked against KIELER, but your own chart has not, and SCCharts Lab runs KIELER’s actual compiler. Before you suspect either of them, check that the two runs really are the same:
- The same inputs in the same ticks. Both keep an input’s value until you change it, so an input left on in one and switched off in the other gives different runs.
- The same simulation system. The guide follows Netlist-based Simulation (C). Others, such as Priority-based Simulation (C), differ in a few details, for example in tick 1.
- The same chart. Both run the first
scchartin the file. - The same KIELER. The official KIELER VS Code extension (0.3.0) has an older compiler than SCCharts Lab. They behave the same on the charts checked so far, but they do not accept quite the same charts. How this guide is checked lists the differences.
If they still disagree, trust SCCharts Lab, and please report it, with the playground’s Copy link for your chart and the inputs that show the difference.
Setup problems
Section titled “Setup problems”Getting SCCharts has the installation steps. When something still does not work:
The diagram does not open
Section titled “The diagram does not open”The file must end in .sctx: the status bar then shows the language mode SCTX. Open the diagram
with the preview button in the editor’s title bar or Open Diagram Preview (Ctrl+Alt+V). If no
SCCharts Lab command works at all, the extension has not started: look for one of the two messages
below, about KIELER VS Code or about Java. A diagram that stops updating comes back with Restart
diagram (Ctrl+Alt+D), or with Restart KIELER language server from the Command Palette.
The simulation never starts
Section titled “The simulation never starts”- Fix the errors first. A chart with errors in the Problems panel does not compile, so it cannot run.
- Choose a system. Simulate… in the preview’s toolbar asks for one: choose Netlist-based Simulation (C). The first run compiles the chart to C and takes a few seconds.
- A C compiler. The C simulation needs one. Without it SCCharts Lab says
C simulation needs a C compiler.On Windows it offers to download a portable GCC; on macOS runxcode-select --install; on Linux installgcc(sudo apt install build-essentialon Debian or Ubuntu). - In the KIELER VS Code extension, a chart with certain syntax errors never finishes compiling,
and no message says so: a keyword used as a name (
input bool reset,go,period), a number above 2147483647, orif 3 == level. Fix the errors the editor marks, and simulate again. Compiler messages explains them; SCCharts Lab stops at them with an error instead.
SCCharts Lab: Show Java runtime and C compiler in use in the Command Palette says what the extension found.
Java and C compiler problems
Section titled “Java and C compiler problems”On Windows (x64), Apple Silicon Macs and Linux (x64) the extension brings its own Java. On other platforms it needs Java 21 or newer, and says so if it finds none:
SCCharts Lab needs Java 21 or newer and found none.Install a Java 21 JDK, or point the setting keith-vscode.javaHome at one, then reload the window. To
use a C compiler that is not on your PATH, such as an existing MinGW or MSYS2 gcc.exe on Windows, set
keith-vscode.cCompilerPath to it; if that path is wrong, the message is
The configured C compiler was not found: followed by the path. You never need javac: it is only for the Java
simulations, and the C simulation is the one to use. KIELER’s own extension needs Java on your PATH
and a C compiler too.
SCCharts Lab and KIELER VS Code
Section titled “SCCharts Lab and KIELER VS Code”The two extensions register the same commands and languages, so they cannot run side by side. With KIELER VS Code enabled, SCCharts Lab does not start and says:
SCCharts Lab cannot run beside KIELER VS Code. Uninstall it, then reload the window.Uninstall and reload does exactly that; disabling KIELER VS Code in the Extensions view and
reloading works too. If your course needs the official extension, disable SCCharts Lab instead: your
.sctx files work in both.
Test your chart
Section titled “Test your chart”A chart is only as good as the runs you have tried. Before you hand one in:
- Set every input in tick 1, and remember that tick 1 only enters the initial states.
- Press and hold. Try each button for a single tick and held down for several. Inputs keep their value, so a held button is what you get if you forget to switch it off.
- Run the coincidences. Two inputs in the same tick, an input in the very tick a timeout fires, an input in the tick a state is entered. Each is decided by one detail of the chart, a priority or a delay, and each takes one tick to try.
- Run the failures, not only the expected sequence: a sensor that never reports, a door opened at the worst moment, two inputs that should never come together.
- Check the end of every phase, not only its start: does the output go off in the tick you expect, or one tick later?
SCCharts Lab helps with all of this:
- Save and Load, at the right end of the trace’s top line, write a run to a
.ktracefile and replay it. Save a test once and replay it after every change to the chart. - Breakpoints, in the preview’s toolbar, pause the simulation when a state is entered, or after a
tick in which a condition holds, such as
heating && door. TheCkey runs ticks until one fires. - Watch expressions, in the trace’s top line, are evaluated after every tick:
pre(count) != count, say. - Back (
Backspace) rewinds one tick, and clicking an earlier tick’s header in the trace rewinds to it, so you can try another input from there.
In the playground, Rebuild after an edit replays the inputs you have given so far on the new version of the chart, as long as its inputs and outputs are unchanged. Under Variables, Watch shows the value of an expression in the tick shown, and Run until steps until the watch holds or a state you choose is active.