这玩意比gen_fsm好用的多,不错!
这篇写的好 https://blog.csdn.net/linhaibo/article/details/52072764 总结的详细,不过关于 handle_event_result返回这里没表达清楚,没有这个 {keep_state_and_data} 用法 只找到了{keep_state,Data}
和 {repeate_state} {repeate_state_and_data}
查资料http://erlang.org/doc/man/gen_statem.html弄懂了
用gen_statem完成gen_fsm一样的功能,代码简洁了很多。
%%%-------------------------------------------------------------------
%%% @author
%%% @copyright (C) 2020
%%% @doc
%%%
%%% @end
%%% Created : 2021/3/23-19-52
%%%-------------------------------------------------------------------
-module(statem_test).
-description("statem_test").
-vsn(1).
-behaviour(gen_statem).
%% API
-export([start_link/1, init/1, callback_mode/0, handle_event/4, terminate/3, code_change/4]).
start_link(Pwd) ->
gen_statem:start_link(?MODULE, Pwd, []).
init(Pwd) ->
process_flag(trap_exit, true),
{ok, lock, {[], Pwd}}.
callback_mode() ->
%% 返回 handle_event_function 或者 state_functions
handle_event_function.
%% gen_statem 支持两种回调模式:
%% 1. state_functions : 和gen_fsm类似,自定义函数来完成状态传递。
%% 2. handle_event_function : 使用handle_event完成状态传递,个人觉得这种模式比较好,不用定义一些乱七八糟的函数,让代码更整齐。
%% call=同步 cast=异步
%% gen_statem:call(Pid,{start}) .
%% 对应回调 handle_event(call,{start},StateName,StateData)
%% gen_statem:cast(Pid,{start}).
%% 对应回调 handle_event(cast,{start},StateName,StateData)
%% Pid ! {start} (常见socket收到消息)
%% 对应回调 handle_event(info,{start},StateName,StateData)
handle_event(info, InputPwd, lock, {OldInput,Pwd}) ->
if
InputPwd =:= Pwd ->
io:format("密码正确! ~n", []),
{next_state, open, {[], Pwd}}; %% 密码正确开门
length(OldInput) >= 2 ->
io:format("密码已经输错三次了,正确密码=~p. ~n", [Pwd]),
{next_state, lock, {[], Pwd}}; %% 状态和数据都不变
true ->
%% 密码不正确没满足三次,历史输入存进数据
io:format("密码错误! ~n", []),
{next_state, lock, {[InputPwd | OldInput], Pwd}}
end;
%%如果状态保持不变,可以使用 {keep_state,StateData}
%%这个比gen_fsm每次都调用 next_state 方便多了
handle_event(info, _Event, open, {OldInput,Pwd}) ->
io:format("门开了,你输入的密码总共是~p ,正确密码是~p ~n",[OldInput,Pwd]),
{keep_state,{OldInput,Pwd}}; %% 状态和数据都不变
handle_event(info, _Event, _, State) ->
{stop, normal, State}.
terminate(_Reason, _StateName, _State) ->
ok.
code_change(_OldVsn, OldState, StateData, _Extra) ->
{ok, OldState, StateData}.
gen_statem的 handle_event_function 模式,这么简单谁不爱呢
9>{_,Pid}=statem_test:start_link(banana).
{ok,<0.18398.0>}
10> Pid ! hasaki.
密码错误!
hasaki
11> Pid ! hasaki.
密码错误!
hasaki
12> Pid ! hasaki.
密码已经输错三次了,正确密码=banana.
hasaki
13> Pid ! hasaki.
密码错误!
hasaki
14> Pid ! hasaki.
密码错误!
hasaki
15> Pid ! banana.
密码正确!
banana
16> Pid ! banana.
门开了,你输入的密码总共是[] ,正确密码是banana
banana
17>