> ## 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 module `gen_fsm` for additional information about this behaviour.

> Many applications can be modeled as FSMs and be programmed using the `gen_fsm` behaviour. 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 `S` and the event `E` occurs, we should perform the actions `A` and make a transition to the state `S'`.

> If you program an FSM using the `gen_fsm` behaviour, 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 Example

> The POTS FSM can be described by the following `gen_fsm` behaviour:

>     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_fsm` behaviour. 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.

Copyright © 1991-2003 Ericsson Utvecklings AB