Simulation tasks
Use this page when you already have an FCSTM model and need to run a concrete simulation task. For the first guided run, start with FCSTM simulation first run. For execution-order reasoning, see Execution semantics explanation. For exact command forms, settings, export formats and Python API boundaries, see Simulation reference.
Quick command reminder
This section is a task reminder. The complete lookup tables live in Simulation reference.
pyfcstm simulate currently exposes a small CLI surface:
Option |
Meaning |
|---|---|
|
Entry FCSTM file. This option is required. |
|
Run semicolon-separated batch commands instead of starting the REPL. |
|
Disable ANSI color in command output. |
|
Show the CLI help text. |
The batch string and the REPL share the same command names. The most useful commands are:
Command |
Use it for |
|---|---|
|
Execute one or more cycles. Omit |
|
Show the current cycle, current state and persistent variables. |
|
Show events available from the current state. |
|
Show recent history rows or the full retained history. |
|
Rebuild the runtime as a hot start at a state with explicit variables. |
|
Show or change display/history/logging settings. |
|
Export history; the format is inferred from the file extension. |
|
Reset the runtime using the current settings. |
|
Show command help. |
|
Leave the REPL. |
Run batch commands
Use -e when you want a reproducible transcript in CI, docs or bug reports.
Separate commands with semicolons:
pyfcstm simulate -i docs/source/tutorials/cli/simple_machine.fcstm \
-e "cycle; events; cycle Start; current; cycle Stop; history 3" \
--no-color
The generated transcript used by the short tutorial is kept here:
────────────────────────────────────────────────────────────
>>> cycle
────────────────────────────────────────────────────────────
Cycle: 1
Current State: SimpleMachine.Idle
Variables:
counter = 0
────────────────────────────────────────────────────────────
>>> events
────────────────────────────────────────────────────────────
Available Events:
• Start (SimpleMachine.Idle.Start)
────────────────────────────────────────────────────────────
>>> cycle Start
────────────────────────────────────────────────────────────
Cycle: 2
Current State: SimpleMachine.Running
Variables:
counter = 0
────────────────────────────────────────────────────────────
>>> current
────────────────────────────────────────────────────────────
Cycle: 2
Current State: SimpleMachine.Running
Variables:
counter = 0
────────────────────────────────────────────────────────────
>>> cycle Stop
────────────────────────────────────────────────────────────
Cycle: 3
Current State: SimpleMachine.Stopped
Variables:
counter = 1
────────────────────────────────────────────────────────────
>>> history 3
────────────────────────────────────────────────────────────
Cycle State counter
---------------------------------------
1 SimpleMachine.Idle 0
2 SimpleMachine.Running 0
3 SimpleMachine.Stopped 1
Use the interactive REPL
Omit -e to start the REPL:
pyfcstm simulate -i docs/source/tutorials/cli/simple_machine.fcstm --no-color
Then type commands one at a time:
cycle
events
cycle Start
current
history 3
quit
Interactive mode adds command history, completion and suggestions, but command semantics are the same as batch mode.
Inject events
A command such as cycle Start injects an event into that cycle. In Python,
pass the event list to SimulationRuntime.cycle:
#!/usr/bin/env python3
from pyfcstm.dsl import parse_with_grammar_entry
from pyfcstm.model import parse_dsl_node_to_state_machine
from pyfcstm.simulate import SimulationRuntime
dsl_code = """
def int counter = 0;
state System {
[*] -> Idle;
state Idle;
state Active;
Idle -> Active :: Start;
Active -> Idle :: Stop;
}
"""
# Parse and create state machine
ast = parse_with_grammar_entry(dsl_code, 'state_machine_dsl')
sm = parse_dsl_node_to_state_machine(ast)
# Create runtime
runtime = SimulationRuntime(sm)
# Initialize
runtime.cycle()
print(f"Initial: state={'.'.join(runtime.current_state.path)}")
# Trigger Start event
runtime.cycle(['Start'])
print(f"After 'Start': state={'.'.join(runtime.current_state.path)}")
# Trigger Stop event
runtime.cycle(['Stop'])
print(f"After 'Stop': state={'.'.join(runtime.current_state.path)}")
# Try Start again
runtime.cycle(['Start'])
print(f"After 'Start' again: state={'.'.join(runtime.current_state.path)}")
Output:
Initial: state=System.Idle
After 'Start': state=System.Active
After 'Stop': state=System.Idle
After 'Start' again: state=System.Active
Event names in examples rely on DSL event scoping. For syntax facts, use
DSL reference; this page only covers how the simulator
receives events. For supported runtime event input forms and event-accounting
fields in CycleResult, use Simulation reference.
Hot start at a state
Use the REPL init command when you need to inspect a later state without
replaying all earlier cycles:
init System.Active counter=10 flag=1
cycle
current
Use the Python API when embedding the same idea in tests:
runtime = SimulationRuntime(
sm,
initial_state="System.Active",
initial_vars={"counter": 10, "flag": 1},
)
Hot start has deliberate boundaries:
initial_varsmust provide every declared persistent variable.enter actions are skipped for the pre-built path.
during actions run normally after the first cycle starts.
a composite hot-start target must have a valid path to a stoppable leaf, or the runtime reports a DFS validation error.
Implement abstract handlers
Abstract lifecycle actions are implemented by registering Python handlers. Use
@abstract_handler on methods whose names can be arbitrary; the decorator
argument names the DSL action path:
#!/usr/bin/env python3
from pyfcstm.dsl import parse_with_grammar_entry
from pyfcstm.model import parse_dsl_node_to_state_machine
from pyfcstm.simulate import SimulationRuntime, abstract_handler
dsl_code = """
def int counter = 0;
state System {
[*] -> Active;
state Active {
enter abstract Init;
during abstract Monitor;
exit abstract Cleanup;
during {
counter = counter + 1;
}
}
state Done;
Active -> Done : if [counter >= 5];
}
"""
# Define handler class
class MyHandlers:
@abstract_handler('System.Active.Init')
def handle_init(self, ctx):
print(f"[Init] state={ctx.get_full_state_path()}, counter={ctx.get_var('counter')}")
@abstract_handler('System.Active.Monitor')
def handle_monitor(self, ctx):
print(f"[Monitor] counter={ctx.get_var('counter')}")
@abstract_handler('System.Active.Cleanup')
def handle_cleanup(self, ctx):
print(f"[Cleanup] final_counter={ctx.get_var('counter')}")
# Parse and create state machine
ast = parse_with_grammar_entry(dsl_code, 'state_machine_dsl')
sm = parse_dsl_node_to_state_machine(ast)
# Create runtime and register handlers
runtime = SimulationRuntime(sm)
handlers = MyHandlers()
runtime.register_handlers_from_object(handlers)
# Execute cycles
for i in range(1, 7):
runtime.cycle()
print(f"Cycle {i}: state={'.'.join(runtime.current_state.path)}")
Output:
[Init] state=System.Active, counter=0
[Monitor] counter=0
Cycle 1: state=System.Active
[Monitor] counter=1
Cycle 2: state=System.Active
[Monitor] counter=2
Cycle 3: state=System.Active
[Monitor] counter=3
Cycle 4: state=System.Active
[Monitor] counter=4
Cycle 5: state=System.Active
[Cleanup] final_counter=5
Cycle 6: state=System.Done
Handler context is read-only. Typical helpers are ctx.get_full_state_path(),
ctx.get_var(name), ctx.has_var(name), ctx.active_leaf and
ctx.action_stage.
Export history
Use history when you only need terminal output. Use export <path> when
you want a file for later analysis. The simulator infers the format from the
extension; current task docs should prefer small examples such as:
cycle
cycle Start
history 2
export run.json
Use JSON or JSONL for machine processing, CSV for spreadsheets, and YAML for human-readable snapshots. Keep exported files outside the source tree unless the file is a checked-in demo output regenerated by the docs build. The exact per-format fields are listed in Simulation reference.
Tune display settings
setting without arguments lists current settings. setting key value
changes one value for the current session. Common examples are:
setting table_max_rows 10
setting history_size 200
setting log_level info
setting color off
history_size controls retained rows. color is also affected by the CLI
--no-color option. The complete setting table, defaults and accepted value
forms are in Simulation reference.
Debug a failing model
A compact debugging loop is:
Run
pyfcstm inspectfirst if parsing or diagnostics are suspicious.Use
pyfcstm simulate -i docs/source/tutorials/cli/simple_machine.fcstm -e "cycle; events; current" --no-colorto get a stable transcript.Add only one event at a time.
Use
historyorexportafter the failure.If hot start fails, check that every variable is provided and that the target composite can reach a stoppable leaf without requiring a missing event.
Do not use simulation output as a substitute for target-runtime tests. It is a fast model-level check that should be paired with generated-runtime tests when you depend on target-language behavior.