State Machine Fundamentals
This page has interactive examples to help you learn about StateSmith state machines. The examples use real code generated by StateSmith from the svg diagrams below.
The same diagrams can generate code for any supported language.
Scroll on down!
Simple On Off
This simple state machine controls the below lightbulb's color.
Try using the buttons below to send events to the Ex01 state machine.
When the OFF
state is entered, it runs the code light_off();
.
When the ON1
state is entered, it runs the code light_blue();
.
Actions on Transitions
Transitions can also execute action code.
The transition from state OFF
to state ON1
runs the code
count++;
. Action code can call a function, modify a state machine or global variable, or do
almost anything.
count: 0
Add More States
Let's add some more states and colors!
This example is building up for the next one.
Adding An OFF
Event?
A cleaner solution follows in Ex05...
What if we add an OFF
event that shuts off the lightbulb no matter which state it is in?
We can have a transition from each ON
state to the OFF
state. This is OK for very
small designs like this one, but doesn't scale well.
Nested States
Add a parent state, and a single transition.
First, we nest all the ON
states inside of ON_GROUP
.
Then, we add a single transition from ON_GROUP
to OFF
for the OFF
event.
If none of the ON
states handle the OFF
event, then their parent
ON_GROUP
state will handle the event and transition to OFF
.
In the next section, we will see how sharing transitions like this can make a HUGE improvement in our designs.
Hierarchical State Machines (HSMs)
A Hierarchical State Machine (HSM) is a Finite State Machine (FSM), but with super powers!
Let's add a few more states to our design to make it a smart lightbulb and see the MASSIVE difference a HSM can make.
Regular Flat FSM
This comes from a real life example. Transition spaghetti mess.
Don't laugh. I've seen things much worse than this. I once received a design that looked like this spaghetti FSM mess except much bigger.
There were many hidden bugs and we eventually reimplemented the state machine with StateSmith utilizing a nested HSM design.
HSM To The Rescue!
Clear. Maintainable. Intuitive. Productive. Non-Repetitive.
When I reimplemented the real life FSM spaghetti mess with StateSmith and HSMs, a ton of problems and bugs instantly vanished.
We no longer wasted time playing whack-a-mole with bugs and oversights in the super messy, ultra repetitive design specifications.
The diagram was also simple enough that we shared it with the client. We would actually modify the StateSmith diagram live with the client over video calls. It was an incredibly powerful communication tool.
Exit Actions
Run action code when a state is exited.
In this example, if the REGULAR_OPERATION
state is ever exited, we can be sure that the light
is turned off with exit / light_off();
.
Exit actions are great for ensuring safety, and clean up code.
This example also shows how a single transition or general state behavior can act on multiple events
(DIM, INCREASE)
.
Choice Pseudo States
Decisions, Decisions, Decisions.
You can get fancier than what is shown here (more choices, chaining choice pseudo states), but this shows the main functionality well.
It's a bit unorthodox to have the initial state in the middle of a state, but it helps the design fit a phone screen.
count: 0
Please Consider Contributing
StateSmith needs more user feedback before it can hit version 1.0.
Please consider contributing (especially if you have web skills).
Thanks! - Adam
Scroll on...
Guards & Variables
State behaviors can also have guard conditions.
The transition from ON2
to ON_HOT
has a guard condition
[count >= 3]
. This transition will only be taken on the INCREASE
event if
count >= 3
.
Transition guards can be any code that evaluate to a boolean result. In this example, the guard tests a state machine variable, but it could call a function, a bunch of functions, do some math...
Another interesting thing in this example is that we specify the order of the ON2
behaviors
for the INCREASE
event. We want to run count++
before testing if we should
transition with [count >= 3]
.
You can read more about state behaviors here.
count: 0
Polled State Machines
Event driven vs. polled state machines. Choose 2.
All the state machines that we have seen so far have been event driven.
When you press a DIM
button on this page, the state machine's event handler function is
invoked with the
proper event ID. StateSmith state machines are event driven. They just sit waiting for you to
feed them the next event (no thread, or background CPU usage).
If you need to poll certain conditions, simply send the DO
event to your state machine at the
rate you want. You have full control over how and when the state machine runs. Very helpful for embedded systems and game development.
Any transition without an event specified will use the DO
event. This is different than UML,
but
we found the UML way caused problems for most users.
This example "polls" its state machine by dispatching a DO
event at the rate specified below.
When the state machine runs, it "polls" (checks) its Switch input to determine whether it should transition.
DO
event count: 0
Acting on Time
State machines can react to any time source you choose.
You can use the current system time, a game time (that can be paused), a tick counter...
This example uses a timer t1
to transition back to state ON1
after 2.1 seconds.
In the future, we will make it easier to track time spent in a state.
Put It All Together!
Try using the buttons below to send events to the Ex10 state machine.
count: 0
StateSmith Can Handle 300+ States
Its balanced algorithm efficiently supports both small and huge designs.
Large deeply nested designs perform well and are easy to debug.
There is no actual limit StateSmith can handle.
300+ is just the most I've used it with.
History Pseudo States
Deep history and custom history also supported.
Try getting to ON2
, then send DIM
event to exit ON
group. Then send INCREASE
event and you will see it return directly back to ON2
.
Consuming Events
A child state can consume an event (deny ancestor state).
This example is very similar to Ex10, but this time we want to stay in the ON_HOT
state for 4.2 seconds no matter what. Sending events OFF
or DIM
to the ON_HOT
state should do nothing.
We simply make ON_HOT
consume the OFF
and DIM
events so that its parent ON_GROUP
doesn't get a chance to see the events.
Have a lot of events to consume? Check out TriggerMaps.
This example may feel a bit contrived, but this ability for child states to consume an event is crucial for many applications like user interfaces.
Another fun feature of draw.io support is that you can place images anywhere and StateSmith will ignore them (the fire image).
Action Order
Exit -> Transition -> Enter
StateSmith follows the UML specification which states that a transition's action code is run after states have been exited and before states are entered.
Assume you are in the WATER
state and the EV1
event is dispatched. You will see the following order of actions:
log("water exited");
log("beverage exited");
log("tran action 1");
log("food entered");
log("tran action 2");
log("sandwich entered");
🕹️ You can play with an actual example here (requires a desktop).
Exit Points
Handy Drawing Tool
Exit points are just a diagramming aid. The `exit_pt` state machine example could be drawn with state ON1
transitioning directly to
the HALTED
state when the OFF
event is dispatched. It's the exact same thing.
Exit points are exceptionally helpful though because they allow you to visually collapse the ON_GROUP
state in draw.io, but still
have state ON1
transition to HALTED
when the OFF
event is dispatched.
Visually collapsing states
is a great way of managing large and complex state machines.
You can find the behavior syntax for exit points here.
🕹️ You can play with an actual example here (requires a desktop).
Entry Points
Like Exit Points
Entry points are just like exit points (see above section). They are great for working with visually collapsed states in draw.io.
The `entry_pt` state machine example could be drawn with state SHUT_OFF
transitioning directly to
the ON1
state when the ON
event is dispatched. It's the exact same thing.
You can find the behavior syntax for entry points here.
🕹️ You can play with an actual example here (requires a desktop).
DO
Events Are Special
They aren't normally consumed
When an event is dispatched to a state machine, the active state first gets a chance to handle it. If it doesn't handle the event, then its parent gets a chance... all the way up to the state machine root.
There is one exception - the do
event. The do
event is special in that state behaviors don't normally consume it. This allows child and parent states to do
some work all the way up the chain.
However, if a transition occurs, no other behaviors will be checked for any state. This also applies to the do
event.
Assume you are in the WAIT
state, the DO
event is dispatched, and wait_count < X
:
- transition guard
[wait_count > X]
is evaluated to false wait_count++;
menu_count++;
Still in the WAIT
state, the DO
event is dispatched, and wait_count > X
:
- transition guard
[wait_count > X]
is evaluated to true. - transition occurs (no other state behaviors are checked).
Assume you are now in the WATER
state, the DO
event is dispatched, and water_count < Y
:
water_count++;
- transition guard
[water_count > Y]
is evaluated to false bev_count++;
menu_count++;
🕹️ You can play with an actual example here (requires a desktop).
There is More
StateSmith has more features than are shown here.
Things like
Deep History
,
Trigger Maps
,
Parent Alias
...
You can find more info in the examples repo, and diagram-features.md.
More interactive samples are in the works.
Please Consider Contributing
StateSmith needs more user feedback before it can hit version 1.0.
Please consider contributing (especially if you have web skills).
Thanks! - Adam