Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

State diagrams

A state machine diagram starts with diagram state, and may have a title. Notation follows clause 14 of UML 2.5.1. Flow runs down the page by default, because transition labels sit beside vertical lines. direction right is available, and places labels above horizontal lines at the cost of wide rank gaps. The layout reserves room for every label.

diagram state "Door"
[*] --> Closed
Closed --> Opening : open [unlocked] / start motor
Opening --> Open : fully open / stop motor
Open --> Closing : close
Closing --> Closed : fully closed
Closing --> Opening : obstruction / reverse motor
Closed --> Locked : lock
Locked --> Closed : unlock [valid code]
Door Closed Closing Locked Open Opening open [unlocked] / start motor fully open / stop motor close fully closed obstruction / reverse motor lock unlock [valid code]

A name used in a transition declares a state in the deepest block that mentions it. A name that appears only once and is at most two edits from another name is reported as a typo. To declare such a state on purpose, write state X first. Use state "Display name" as alias when a state needs a longer name or a body.

Transition labels

A label follows : and has the UML form, in which every part is optional:

trigger {, trigger} [guard] / effect

A long label breaks before the effect, then wraps. Transitions between the same pair of states run side by side with their labels on the outside. A self-transition is a loop on the side of its state, with its label beside it.

diagram state "Transition labels"
direction right
[*] --> Waiting
Waiting --> Armed : arm, rearm [sensors ready] / log("armed")
Armed --> Waiting : disarm
Armed --> Armed : tick / count()
Armed --> Armed : drift [abs(x) > limit] / correct()
Armed --> Firing : trigger [armed and not locked] / ignite(); start_timer()
Firing --> [*] : done
Transition labels Armed Firing Waiting arm, rearm [sensors ready] / log("armed") disarm trigger [armed and not locked] / ignite(); start_timer() done tick / count() drift [abs(x) > limit] / correct()

UML restricts some labels, and drumlin reports a violation as an error:

  • A transition from a nested initial pseudostate is unlabeled. At the top level it may name the creating event.
  • A transition leaving a pseudostate has no trigger. It may have [guard] / effect.
  • The segments into a join and out of a fork have no trigger or guard.

Internal behaviours

Inside a state block, entry, do and exit lines go in a compartment under the name. The expression after / is optional. Any other line with a / and no arrow is an internal transition, in the same form as a transition label. Each of these takes a whole line.

diagram state "Password entry"
[*] --> Typing
state Typing {
  entry / set echo invisible
  exit / set echo normal
  character / handle character
  help / display help
}
Typing --> [*] : enter
Password entry Typing entry / set echo invisible exit / set echo normal character / handle character help / display help enter

Composite states and history

A state block with vertices inside is a composite state, and its contents form its region. [*] --> X starts the enclosing region at the initial pseudostate, which sits first in the flow. X --> [*] ends it at the final state. Each region has one of each, scoped to the block they are written in.

[H] is shallow history and [H*] is deep history. Written alone or in a transition inside a block, they belong to that block. Name[H] and Name[H*] reach the history of a composite state from outside it. A transition may also start at, end at or cross the border of a composite state.

diagram state "Composite states and history"
[*] --> Off
Off --> Playing : power
state Playing {
  entry / spin up
  do / stream
  exit / spin down
  [*] --> Track
  Track --> Track : next / advance
  Track --> Seeking : seek
  Seeking --> Track : found
  [H]
}
Playing --> Paused : pause
Paused --> Playing[H] : resume
Playing --> Off : power
Paused --> Off : power
Composite states and history Playing entry / spin up do / stream exit / spin down H Seeking Track Off Paused power seek found power power next / advance pause resume

Orthogonal regions

region "name" { ... } divides a state into regions, drawn tiled and separated by dashed lines. The name is optional. Regions sit across the flow: side by side when flowing down, stacked when flowing right. A transition cannot join two regions of one state.

diagram state "Elevator controller"
[*] --> Idle
Idle --> Operating : call received / plan route
Operating --> Idle : queue empty
Operating --> Fault : motor overcurrent / cut power
Fault --> Idle : technician reset
state Operating {
  entry / release brake
  exit / engage brake
  region "motion" {
    [*] --> Moving
    Moving --> Stopping : near target floor
    Stopping --> Levelled : speed zero
    Levelled --> Moving : next call
  }
  region "doors" {
    [*] --> Closed
    Closed --> Opening : levelled at floor
    Opening --> Open : fully open
    Open --> Closing : door timer
    Closing --> Closed : fully closed
    Closing --> Opening : obstruction
  }
}
Elevator controller Operating entry / release brake exit / engage brake motion Levelled Moving Stopping doors Closed Closing Open Opening Fault Idle call received / plan route queue empty technician reset near target floor speed zero next call levelled at floor fully open door timer fully closed obstruction motor overcurrent / cut power

Pseudostates

choice c         a diamond
junction j       a small filled circle
fork f           a thick bar across the flow; one line in, at least two out
join j           a thick bar across the flow; at least two lines in, one out
terminate t      a cross

A pseudostate is declared with a name, and is used like a state. Lines fan out from the corners of a choice or junction. A bar widens over the states it connects.

diagram state "Every pseudostate"
[*] --> Ready
Ready --> c : request
choice c
c --> Worker : [workers free] / assign
c --> Queued : [all busy]
Queued --> j : worker free
Worker --> j : done
junction j
j --> f : / prepare
fork f
f --> A
f --> B
state Tasks {
  region "left" {
    A --> [*]
  }
  region "right" {
    B --> [*]
  }
}
Tasks --> T : fatal
terminate T
Every pseudostate Tasks left A right B Queued Ready Worker request [workers free] / assign [all busy] worker free done / prepare fatal

Entry and exit points

entrypoint name and exitpoint name, written directly in a state block, place a small circle or a crossed circle on the state’s border, with its name beside it. Transitions from outside attach on the outer side and transitions from inside on the inner side.

diagram state "Entry and exit points"
[*] --> Idle
Idle --> again : retry
state Login {
  entrypoint again
  exitpoint aborted
  again --> Prompt
  Prompt --> Check : submit
  Check --> aborted : [locked out]
  Check --> [*] : [ok]
}
aborted --> Idle
Login --> Done
Entry and exit points Login Check Prompt Done Idle again aborted retry submit [locked out] [ok]

Local transitions

A transition between a composite state and a vertex inside it is a local transition, and is drawn to or from the inside of the state’s border, on its right side.

diagram state "Local transitions"
[*] --> Active
state Active {
  [*] --> One
  One --> Two : next
  Two --> Active : reset
}
Active --> [*]
Local transitions Active One Two next reset

Notes and hints

note <target>: text attaches a note to any state or pseudostate. left-of, right-of, above, below and align work as in package diagrams, and adjust layout when the automatic result needs help.