A door with a code lock could be viewed as an FSM. Initially,
the door is locked. Anytime someone presses a button, this
generates an event. Depending on what buttons have been pressed
before, the sequence so far may be correct, incomplete or wrong.

If it is correct, the door is unlocked for 30 seconds (30000 ms).
If it is incomplete, we wait for another button to be pressed. If
it is is wrong, we start all over, waiting for a new button
sequence.

Implementing the code lock FSM using gen_fsm results in
this callback module:

start_link calls the function gen_fsm:start_link/4.
This function spawns and links to a new process, a gen_fsm.

The first argument {local, code_lock} specifies
the name. In this case, the gen_fsm will be locally registered
as code_lock.

If the name is omitted, the gen_fsm is not registered.
Instead its pid must be used. The name could also be given as
{global, Name}, in which case the gen_fsm is registered
using global:register_name/2.

The second argument, code_lock, is the name of
the callback module, that is the module where the callback
functions are located.

In this case, the interface functions (start_link and
button) are located in the same module as the callback
functions (init, locked and open). This
is normally good programming practice, to have the code
corresponding to one process contained in one module.

The third argument, Code, is a term which is passed
as-is to the callback function init. Here, init
gets the correct code for the lock as indata.

The fourth argument, [], is a list of options. See
gen_fsm(3) for available options.

If name registration succeeds, the new gen_fsm process calls
the callback function code_lock:init(Code). This function
is expected to return {ok, StateName, StateData}, where
StateName is the name of the initial state of the gen_fsm.
In this case locked, assuming the door is locked to begin
with. StateData is the internal state of the gen_fsm. (For
gen_fsms, the internal state is often referred to 'state data' to
distinguish it from the state as in states of a state machine.)
In this case, the state data is the button sequence so far (empty
to begin with) and the correct code of the lock.

init(Code) ->
{ok, locked, {[], Code}}.

Note that gen_fsm:start_link is synchronous. It does not
return until the gen_fsm has been initialized and is ready to
receive notifications.

gen_fsm:start_link must be used if the gen_fsm is part of
a supervision tree, i.e. is started by a supervisor. There is
another function gen_fsm:start to start a stand-alone
gen_fsm, i.e. a gen_fsm which is not part of a supervision tree.

The function notifying the code lock about a button event is
implemented using gen_fsm:send_event/2:

button(Digit) ->
gen_fsm:send_event(code_lock, {button, Digit}).

code_lock is the name of the gen_fsm and must agree with
the name used to start it. {button, Digit} is the actual
event.

The event is made into a message and sent to the gen_fsm. When
the event is received, the gen_fsm calls
StateName(Event, StateData) which is expected to return a
tuple {next_state, StateName1, StateData1}.
StateName is the name of the current state and
StateName1 is the name of the next state to go to.
StateData1 is a new value for the state data of
the gen_fsm.

If the door is locked and a button is pressed, the complete
button sequence so far is compared with the correct code for
the lock and, depending on the result, the door is either unlocked
and the gen_fsm goes to state open, or the door remains in
state locked.

When a correct code has been givened, the door is unlocked and
the following tuple is returned from locked/2:

{next_state, open, {[], Code}, 30000};

30000 is a timeout value in milliseconds. After 30000 ms, i.e.
30 seconds, a timeout occurs. Then StateName(timeout, StateData) is called. In this case, the timeout occurs when
the door has been in state open for 30 seconds. After that
the door is locked again:

Sometimes an event can arrive at any state of the gen_fsm.
Instead of sending the message with gen_fsm:send_event/2
and writing one clause handling the event for each state function,
the message can be sent with gen_fsm:send_all_state_event/2
and handled with Module:handle_event/3:

In a Supervision Tree

If the gen_fsm is part of a supervision tree, no stop function
is needed. The gen_fsm will automatically be terminated by its
supervisor. Exactly how this is done is defined by a
shutdown strategy
set in the supervisor.

If it is necessary to clean up before termination, the shutdown
strategy must be a timeout value and the gen_fsm must be set to
trap exit signals in the init function. When ordered
to shutdown, the gen_fsm will then call the callback function
terminate(shutdown, StateName, StateData):

The callback function handling the stop event returns a
tuple {stop,normal,StateData1}, where normal
specifies that it is a normal termination and StateData1
is a new value for the state data of the gen_fsm. This will
cause the gen_fsm to call
terminate(normal,StateName,StateData1) and then
terminate gracefully:

If the gen_fsm should be able to receive other messages than
events, the callback function handle_info(Info, StateName, StateData) must be implemented to handle them. Examples of
other messages are exit messages, if the gen_fsm is linked to
other processes (than the supervisor) and trapping exit signals.