6 Finite State Machines
This section describes the general principles of the Finite State Machine (FSM) behaviour and shows how to make FSM based applications. Refer to the Reference Manual
stdlib, the modulegen_fsmfor additional information about this behaviour.Many applications can be modeled as FSMs and be programmed using the
gen_fsmbehaviour. Protocol stacks are such an example.A FSM can be described as a set of relations of the form:
State(S) x Event(E) -> Actions (A), State(S') ...These relations are interpreted as meaning:
If we are in state
Sand the eventEoccurs, we should perform the actionsAand make a transition to the stateS'.If you program an FSM using the
gen_fsmbehaviour, then the state transition rules should be written as a number of Erlang functions which conform to the following convention:StateName(Event, StateData) -> .. code for actions here ... {next_state, StateName', StateData'}The figure below is a simple FSM describing "Plain Ordinary Telephony Service" (POTS).
FSM ExampleThe POTS FSM can be described by the following
gen_fsmbehaviour:init(A) -> {ok, idle, A}. idle({off_hook, A}, A) -> {next_state, getting_number, {A,[]}}; idle({seize, A}, B) when A /= B -> {next_state, ringing_b_side, {B, A}); idle(_, A) -> {next_state, idle, A}. getting_number({digit, D}, {A, Seq}) -> case ... of ... -> ... {next_state, ringing_a_side, {A, B}}; ... -> ... {next_state, getting_number, {A, Seq1}}; ... -> ... {next_state, wait_on_hook, A} end; getting_number({on_hook, A}, {A,_}) -> {next_state, idle, A}. ringing_a_side({on_hook, A}, {A, B}) -> {next_state, idle, A}; ringing_a_side({answered, B}, {A, B}) -> {next_state, speech, {A,B}}. ringing_b_side({on_hook, A}, {B, A}) -> {next_state, idle, B}; ringing_b_side({off_hook, B}, {B, A}) -> {next_state, speech, {B, A}}. speech({on_hook, A}, {A, B}) -> {next_state, idle, A}; speech({on_hook, B}, {A, B}) -> {next_state, wait_on_hook, A}. wait_on_hook({on_hook, A}, A) -> {next_state, idle, A}.The code shown above only describes the state transitions. To add the actions, we might add the following code:
getting_number({digit, D}, {A, Seq}) -> Seq1 = Seq ++ [D], case number_analyser:analyse(Seq1) of {user, B} -> hw:seize(B, A), {next_state, ringing_a_side, {A, B}}; get_more_digits -> {next_state, getting_number, {A, Seq1}}; invalid_number -> hw:send_nasty_tone(A, bad_number_tone), {next_state, wait_on_hook, A} end; getting_number({on_hook, A}, {A,_}) -> hw:stop_codec(A), {next_state, idle, A}.To complete this example, we have to package the FSM and the event generation routines in a behaviour module, as shown in the following example, and then add the code for the FSM.
-module(pots). -behaviour(gen_fsm). -export([...]). start() -> gen_fsm:start(...) stop() -> gen_fsm:send_all_state_event(...) on_hook(A) -> gen_fsm:send_event(..., {off_hook, A}). ...6.1 An FSM Example
The simple POTS example shown above does not include all required details. The following example is complete and hopefully also self explanatory.
-module(test1_fsm). -behaviour(gen_fsm). %% interface routines %% start us up start() -> gen_fsm:start({local, hello}, test1_fsm, [], []). %% send a hello -- this will end up in the FSM routines hello() -> gen_fsm:send_event(hello, {hello, self()}). %% send a stop this will end up in "handle_event" stop() -> gen_fsm:send_all_state_event(hello, stopit). %% -- interface end %% This initialisation routine gets called init(_) -> {ok, idle, []}. %% The state machine idle({hello, A}, []) -> {next_state, one, A}. one({hello, B}, A) -> A ! {hello, B}, B ! {hello, A}, {next_state, idle, []}. %% this handles events in *any* state handle_event(stopit, AnyState, TheStateData) -> {stop, i_shall_stop, []}. %% tell it to stop %% This is the finalisation routine - it gets called %% When we have to stop terminate(i_shall_stop, TheStateIwasIn, TheStateData) -> ok.6.2 Other Ways of Programming FSMs
This section has focused on the
gen_fsmbehaviour. A very large FSM, or an FSM which has a very regular structure, can be built another way. It is possible to automatically generate the code which describes the machine from the FSM specification.