gen_statem
gen_statem (OTP 19+) implements finite-state machines; it replaced gen_fsm.
gen_statem (OTP 19+) implements finite-state machines; it replaced gen_fsm.
30.1 Door lock example (state_functions mode)
-module(door).
-behaviour(gen_statem).
-export([start_link/1, button/1, stop/0]).
-export([init/1, callback_mode/0, locked/3, open/3, terminate/3]).
start_link(Code) -> gen_statem:start_link({local, ?MODULE}, ?MODULE, Code, []).
button(Digit) -> gen_statem:cast(?MODULE, {button, Digit}).
stop() -> gen_statem:stop(?MODULE).
init(Code) -> {ok, locked, #{code => Code, buttons => []}}.
callback_mode() -> state_functions.
locked(cast, {button, Digit}, #{code := Code, buttons := Buttons} = Data) ->
NewButtons = lists:sublist(Buttons ++ [Digit], length(Code)),
case NewButtons =:= Code of
true ->
io:format("Unlocked!~n"),
{next_state, open, Data#{buttons := []}, [{state_timeout, 10000, lock}]};
false ->
{keep_state, Data#{buttons := NewButtons}}
end.
open(state_timeout, lock, Data) ->
io:format("Locked again~n"),
{next_state, locked, Data};
open(cast, {button, _}, _Data) ->
{keep_state_and_data, [postpone]}. % handle again after next state change
terminate(_Reason, _State, _Data) -> ok.30.2 handle_event_function mode
State can be any term (e.g., {connected, Retries}).
callback_mode() -> [handle_event_function, state_enter].
handle_event(enter, _OldState, State, _Data) -> io:format("entering ~p~n", [State]), keep_state_and_data;
handle_event({call, From}, get_state, State, _Data) -> {keep_state_and_data, [{reply, From, State}]};
handle_event(cast, go, idle, Data) -> {next_state, running, Data};
handle_event(cast, stop, running, Data) -> {next_state, idle, Data}.30.3 Features
Event types: {call, From}, cast, info, timeout, state_timeout, {timeout, Name}, internal, enter.
Actions: {reply, From, R}, postpone, {next_event, Type, Content} (internal events), hibernate, timeouts.
State enter calls: run code on every state entry.
Timeouts: event timeout, state timeout (cancelled on state change), generic named timeouts.