From f06f69068807168cf4cc731711ed82489cc5b99c Mon Sep 17 00:00:00 2001
From: Raimo Niskanen
The two
This mode fits well when you have a regular state diagram @@ -216,7 +216,7 @@ handle_event(EventType, EventContent, State, Data) -> it is is wrong, we start all over, waiting for a new button sequence.
-
@@ -842,9 +842,9 @@ open(cast, {button,_}, Data) ->
changed i.e
- Postponing is ordered by the
+ Postponing is ordered by the state transition
- The
+ The state transition
It may be beneficial in some cases to be able to generate events
- to your own state machine. This can be done with the
+ to your own state machine.
+ This can be done with the state transition
@@ -1200,6 +1201,71 @@ handle_event({call,From}, code_length, _State, #{code := Code}) ->
+ The example servers so far in this chapter will for example + when killed by an exit signal or due to an internal error + print out the full internal state in the error log. + This state contains both the code lock code + and which digits that remains to unlock. +
++ This state data can be regarded as sensitive, + and maybe not what you want in the error log + because of something unpredictable happening. +
++ Another reason to filter the state can be + that the state is too big to print out since it fills + the error log with uninteresting details. +
+
+ To avoid this you can format the internal state
+ that gets in the error log and gets returned from
+
+ StateData =
+ {State,
+ maps:filter(
+ fun (code, _) -> false;
+ (remaining, _) -> false;
+ (_, _) -> true
+ end,
+ Data)},
+ case Opt of
+ terminate ->
+ StateData;
+ normal ->
+ [{data,[{"State",StateData}]}]
+ end.
+ ]]>
+
+ It is not mandatory to implement a
+
-export([start_link/2,stop/0]).
-export([button/1,code_length/0,set_lock_button/1]).
--export([init/1,terminate/3,code_change/4]).
+-export([init/1,terminate/3,code_change/4,format_status/2]).
-export([handle_event/4]).
start_link(Code, LockButton) ->
@@ -1286,51 +1352,65 @@ set_lock_button(LockButton) ->
init({Code,LockButton}) ->
process_flag(trap_exit, true),
- Data = #{code => Code},
+ Data = #{code => Code, remaining => undefined, timer => undefined},
enter(?CALLBACK_MODE, {locked,LockButton}, Data, []).
-%% State: locked
-handle_event(internal, enter, {locked,_}, #{code := Code} = Data) ->
- do_lock(),
- {keep_state,Data#{remaining => Code}};
handle_event(
- {call,From}, {button,Digit}, {locked,LockButton},
- #{code := Code, remaining := Remaining} = Data) ->
- case Remaining of
- [Digit] -> % Complete
- enter(next_state, {open,LockButton}, Data, [{reply,From,ok}]);
- [Digit|Rest] -> % Incomplete
- {keep_state,Data#{remaining := Rest},[{reply,From,ok}]};
- [_|_] -> % Wrong
- {keep_state,Data#{remaining := Code},[{reply,From,ok}]}
- end;
-%%
-%% State: open
-handle_event(internal, enter, {open,_}, Data) ->
- Tref = erlang:start_timer(10000, self(), lock),
- do_unlock(),
- {keep_state,Data#{timer => Tref}};
+ {call,From}, {set_lock_button,NewLockButton},
+ {StateName,OldLockButton}, Data) ->
+ {next_state,{StateName,NewLockButton},Data,
+ [{reply,From,OldLockButton}]};
handle_event(
- info, {timeout,Tref,lock}, {open,LockButton},
- #{timer := Tref} = Data) ->
- enter(next_state, {locked,LockButton}, Data, []);
+ {call,From}, code_length,
+ {_StateName,_LockButton}, #{code := Code}) ->
+ {keep_state_and_data,
+ [{reply,From,length(Code)}]};
handle_event(
- {call,From}, {button,LockButton}, {open,LockButton},
- #{timer := Tref} = Data) ->
- erlang:cancel_timer(Tref),
- enter(next_state, {locked,LockButton}, Data, [{reply,From,locked}]);
-handle_event({call,_}, {button,_}, {open,_}, _) ->
- {keep_state_and_data,[postpone]};
-%%
-%% Any state
+ EventType, EventContent,
+ {locked,LockButton}, #{code := Code, remaining := Remaining} = Data) ->
+ case {EventType,EventContent} of
+ {internal,enter} ->
+ do_lock(),
+ {keep_state,Data#{remaining := Code}};
+ {{call,From},{button,Digit}} ->
+ case Remaining of
+ [Digit] -> % Complete
+ next_state(
+ {open,LockButton}, Data,
+ [{reply,From,ok}]);
+ [Digit|Rest] -> % Incomplete
+ {keep_state,Data#{remaining := Rest},
+ [{reply,From,ok}]};
+ [_|_] -> % Wrong
+ {keep_state,Data#{remaining := Code},
+ [{reply,From,ok}]}
+ end
+ end;
handle_event(
- {call,From}, {set_lock_button,NewLockButton}, {StateName,OldLockButton},
- Data) ->
- {next_state,{StateName,NewLockButton},Data,
- [{reply,From,OldLockButton}]};
-handle_event({call,From}, code_length, _State, #{code := Code}) ->
- {keep_state_and_data,[{reply,From,length(Code)}]}.
+ EventType, EventContent,
+ {open,LockButton}, #{timer := Timer} = Data) ->
+ case {EventType,EventContent} of
+ {internal,enter} ->
+ Tref = erlang:start_timer(10000, self(), lock),
+ do_unlock(),
+ {keep_state,Data#{timer := Tref}};
+ {info,{timeout,Timer,lock}} ->
+ next_state({locked,LockButton}, Data, []);
+ {{call,From},{button,Digit}} ->
+ if
+ Digit =:= LockButton ->
+ erlang:cancel_timer(Timer),
+ next_state(
+ {locked,LockButton}, Data,
+ [{reply,From,locked}]);
+ true ->
+ {keep_state_and_data,
+ [postpone]}
+ end
+ end.
+next_state(State, Data, Actions) ->
+ enter(next_state, State, Data, Actions).
enter(Tag, State, Data, Actions) ->
{Tag,State,Data,[{next_event,internal,enter}|Actions]}.
@@ -1344,13 +1424,28 @@ terminate(_Reason, State, _Data) ->
ok.
code_change(_Vsn, State, Data, _Extra) ->
{?CALLBACK_MODE,State,Data}.
- ]]>
-
- It may be an ill-fitting model for a physical code lock
- that the
+ It may be an ill-fitting model for a physical code lock
+ that the