fsm - Finite State Machine

Module Description

The fsm module implements a Finite State Machine. Use fsm-new-state to add states to the machine. Then use fsm-new-transition to add transitions between the states. fsm-new-transition returns the new transition. Use ftr-condition@ on this new transition to get a reference to the condition in the transition. This is actually a bit array [see bar]. Use the words of the bar module to set the condition. When the whole FSM is built, start using the machine by calling fsm-start. By default the first created state is the start state, but this can be changed by fsm-start!. After starting the machine, feed events to the machine by fsm-feed. This word returns the new, current state or nil if no transition matched. The machine can be converted to graphviz's dot files by fsm-to-dot. This word uses the labels of the states and transitions to build the dot representation. It also set the shape of the states [double circle for start and end states, circles for the others]. Use fst-attributes! and ftr-attributes! to set additional graphviz attributes. During the feeding of events, the optional actions are called. When a state is left, the exit action is called, when a state is entered the entry state is called. If a transition matched, the action of this transition is also called. The stack usage for those actions:

state entry action:  fst -- = State fst is entered
state exit action:   fst -- = State fst is left
transition action: n ftr -- = Transition fst matched for event n

Module Words

fsm structure

fsm% ( -- n )
Get the required space for a fsm variable

FSM creation, initialisation and destruction

fsm-init ( +n fsm -- )
Initialise the fsm with the number of events n

fsm-(free) ( fsm -- )
Free the internal, private variables from the heap

fsm-create ( "<spaces>name" +n -- ; -- fsm )
Create a named fsm in the dictionary with the number of events n

fsm-new ( +n -- fsm )
Create a new fsm on the heap with the number of events n

fsm-free ( fsm -- )
Free the fsm from the heap

Member words

fsm-start@ ( fsm -- fst )
Get the start state

fsm-start! ( fst fsm -- )
Set the start state

State words

fsm-new-state ( x xt1 xt2 c-addr1 u1 fsm -- fst )
Add a new state with label c-addr1 u1, entry action xt1, exit action xt2 and data x

fsm-start ( fsm -- )
Start the finite state machine

fsm-find-state ( c-addr u fsm -- fst | nil )
Find the state by its label c-addr u in the fsm

Transition words

fsm-new-transition ( x xt c-addr1 u1 fst1 fst2 fsm -- ftr )
Add a new transition from state fst1 to state fst2 with label c-addr1 u1, action xt and data x

fsm-any-transition ( x xt c-addr1 u1 fst1 fst2 fsm -- ftr )
Set the any transition for state fst1 to state fst2 with label c-addr1 u1, action xt and data x

Event words

fsm-feed ( n fsm -- fst | nil )
Feed the event to the current state, return the next state or nil if the event did not match any condition

fsm-try ( n fsm -- fst | nil )
Try the event for the current event, return the next state, but do not move to this state

Conversion words

fsm-to-dot ( c-addr u tos fsm -- )
Convert the fsm to a dot string using the stream, giving the graph the name c-addr u

Inspection

fsm-dump ( fsm - )
Dump the FSM

Examples

include ffl/fsm.fs
include ffl/enm.fs


\ Example: vending machine


\ Enumerate the events

begin-enumeration
enum: voilence
enum: coin
enum: choice
enum: refuse
enum: #events
end-enumeration


\ Create the finite state machine on the heap with #events events

#events fsm-new value machine


\ Create the entry and exit action words

: say-thank-you  ( fst -- = Say 'thank you' after a coin or choice )
  drop
  ." Thank you" cr
;

: say-choice?    ( fst -- = Ask for choice after the coin )
  drop
  ." Please make your choice" cr
;

: say-coin?      ( fst -- = Ask for coin after the choice )
  drop
  ." Please enter your coin" cr
;

: call-support   ( fst -- = Call support after voilence, states data contains the phone number )
  ." Voilence against the machine, calling: " fst-data@ . cr
;


\ Create the states in the state machine

0     nil            ' say-thank-you s" start"    machine fsm-new-state value start
0   ' say-choice?    ' say-thank-you s" choice?"  machine fsm-new-state value choice?
0   ' say-coin?      ' say-thank-you s" coin?"    machine fsm-new-state value coin?
112 ' call-support     nil           s" support"  machine fsm-new-state value support


\ Set extra dot attributes for the support state

s" color=red" support fst-attributes!


\ Create the transitions for state start, use the bit array to set the condition

0     nil             s" coin"      start choice? machine fsm-new-transition ftr-condition@ coin     swap bar-set-bit
0     nil             s" choice"    start coin?   machine fsm-new-transition ftr-condition@ choice   swap bar-set-bit
0     nil             s" voilence"  start support machine fsm-new-transition ftr-condition@ voilence swap bar-set-bit

s" voilence" start fst-find-transition s" color=yellow" rot ftr-attributes!


\ Create the transition actions for choice? and coin? states

: deliver-choice   ( n ftr -- = Deliver the choosen product )
  2drop
  ." Deliver choice" cr
;

: do-refund        ( n ftr -- = Refused the product, refund the coin )
  2drop
  ." Refund coin" cr
;


\ Create the transitions for state choice?

0   ' deliver-choice  s" choice"    choice? start   machine fsm-new-transition ftr-condition@ choice   swap bar-set-bit
0   ' do-refund       s" refuse"    choice? start   machine fsm-new-transition ftr-condition@ refuse   swap bar-set-bit
0     nil             s" voilence"  choice? support machine fsm-new-transition ftr-condition@ voilence swap bar-set-bit


\ Set extra dot attributes for the voilence transition

s" voilence" choice? fst-find-transition s" color=yellow" rot ftr-attributes!


\ Create the transitions for state coin?

0   ' deliver-choice  s" coin"      coin? start   machine fsm-new-transition ftr-condition@ coin     swap bar-set-bit
0     nil             s" refuse"    coin? start   machine fsm-new-transition ftr-condition@ refuse   swap bar-set-bit
0     nil             s" voilence"  coin? support machine fsm-new-transition ftr-condition@ voilence swap bar-set-bit

s" voilence" coin? fst-find-transition s" color=yellow" rot ftr-attributes!


\ Start the state machine and feed it events 

machine fsm-start

coin     machine fsm-feed drop
choice   machine fsm-feed drop
coin     machine fsm-feed drop
refuse   machine fsm-feed drop
choice   machine fsm-feed drop
coin     machine fsm-feed drop
voilence machine fsm-feed drop


\ Create a text output stream for writing the state machine to dot

tos-new value dot-tos


\ Create the writer word

: write-dot    ( c-addr u file-id -- flag = Write the string )
  write-line 
  0=
;


\ Create the output file

s" out.dot" w/o create-file throw value dot-file


\ Set the writer word and the file in the stream

dot-file  ' write-dot  dot-tos  tos-set-writer


\ Write the state machine to the dot-file with graph name "Machine"

s" Machine" dot-tos machine fsm-to-dot


\ Free the dot stream

dot-tos tos-free


\ Close the dot file

dot-file close-file throw


\ To generate an image, use for example: dot -Tpng -o fsm.png out.dot

\ ==============================================================================

generated 10-Apr-2008 by ofcfrth-0.5.0