erlang fsm 学习及用例

来源:互联网 发布:聊天软件遇见 编辑:程序博客网 时间:2024/05/17 15:21

1. 有限状态机

 

有限状态机可以用下面这个公式来表达

State(S) x Event(E) -> Actions(A), State(S')

表示的就是在S状态时如果有事件E发生,那么执行动作A后把状态调整到S’。理解很好理解,如果能够熟练应用必须得下苦功,多练习。

 

start_link跟gen-sever 类似,启动后进入init初始化,结果是 ok,StateName,State. 此例子中是初始化密码门的原始密码,置输入密码为空

2. 一个例子

 

erlang手册中用这个例子来解释的:开锁问题,有一个密码锁的门,它就可以看作一个状态机,初始状态门是锁着的,任何时候有人按一个密码键就会产生一个事件,这个键值和前面的按键组合后与密码相比较,看是否正确,如果输入的密码顺序是对的,那么将门打开10秒,如果输入密码不完全,则等待下次按钮按下,如果输入密码顺序是错的,则重新开始等待按键按下。

首先了解一些基础

接下来看代码 注释都在其中

复制代码
 1 %% gen_fsm 测试 2 -module(code_lock).   3 -behaviour(gen_fsm).   4    5 -export([start_link/1]).   6 -export([button/1]).   7    8 -export([init/1, locked/2, open/2, stop/0]).   9 -export([code_change/4, handle_event/3, handle_info/3, handle_sync_event/4, terminate/3]).  10   11 %% 如果进程注册成功,则新的gen_fsm进程调用code_lock:init(Code),12 %% 返回{ok, StateName, StateData}。StateName是gen_fsm的初始状态,在这里返回的是locked,表示初始状态下门是锁着的,13 %% 此处StateName(locked)也表示当调用gen_fsm:send_event/2时的回调函数14 %% StateData是gen_fsm的内部状态,在这里Statedata是当前的按键顺序(初始时为空)和正确的锁代码,是个列表15 -spec(start_link(Code::string()) -> {ok,pid()} | ignore | {error,term()}).  16 % start_link调用gen_fsm:start_link/4,启动一个新的gen_fsm进程并连接17 start_link(Code) ->18     % 第一个参数{local, code_lock}指定名字,在本地注册为code_lock19     % 第二个参数code_lock是回调模块20     % 第三个参数Code是传递给回调模块init函数的参数,就是密码锁的密码21     % 第四个[]是状态机的选项22     gen_fsm:start_link({local, code_lock}, code_lock, Code, []).  23 24 init(LockCode) ->  25     io:format("init: ~p~n", [LockCode]),    26     {ok, locked, {[], LockCode}}.  27 28 %% 使用gen_fsm:send_event/2来实现按建事件的通知29 %% gen_fsm:send_event -> Module:StateName/230 %% Module为设置的code_lock回调模块,StateName为回调函数,即code_lock:locked31 -spec(button(Digit::string()) -> ok).  32 button(Digit) ->  33     gen_fsm:send_event(code_lock, {button, Digit}).  34   35 locked({button, Digit}, {SoFar, Code}) ->  36     io:format("buttion: ~p, So far: ~p, Code: ~p~n", [Digit, SoFar, Code]),  37     % 将输入的值连接起来38     InputDigits = lists:append(SoFar, Digit),  39     case InputDigits of  40         Code ->     % 密码输入正确  41             do_unlock(),    % 解锁时要执行的代码可以放在do_unlock()方法中 42             {next_state, open, {[], Code}, 10000};      % 解锁后状态为open,也表示超时10秒后调用open函数43         Incomplete when length(Incomplete)<length(Code) ->  % 输入的密码长度小于真实密码的长度(即输入未完成) 44             {next_state, locked, {Incomplete, Code}, 5000}; % 超时5秒后调用locked(timeout,{SoFar, Code})方法 45         Wrong ->    % 密码输入错误 46             io:format("wrong passwd: ~p~n", [Wrong]),  47             {next_state, locked, {[], Code}}    % 输入错误则直接清空已经输入的密码48     end;  49 locked(timeout, {_SoFar, Code}) ->  50     io:format("timout when waiting button inputting, clean the input, button again plz~n"),  51     {next_state, locked, {[], Code}}.   % 超时清空已经输入的密码52   53 open(timeout, State) ->  54     do_lock(),  % 上锁时要执行的代码可以放在do_unlock()方法中55     {next_state, locked, State}.    % 解锁超时后则将状态该为上锁状态 56   57 code_change(_OldVsn, StateName, Data, _Extra) ->  58     {ok, StateName, Data}.  59   60 terminate(normal, _StateName, _Data) ->  61     ok.  62 63 %% gen_fsm:send_all_state_event(CallModule, Event) -> CallModule:handle_event(Event, StateName, Data)64 %% gen_fsm:send_all_state_event(code_lock, stop) -> code_lock:handle_event(stop, StateName, Date)65 stop() ->  66     gen_fsm:send_all_state_event(code_lock, stop).67 68 %% 有时候一个事件可以到达gen_fsm进程的任何状态,69 %% 取代用gen_fsm:send_event/2发送消息和写一段每个状态函数处理事件的代码,70 %% 这个消息我们可以用gen_fsm:send_all_state_event/2 发送,用Module:handle_event/3处理71 handle_event(Event, StateName, Data) ->  72     io:format("handle_event... ~n"),  73     unexpected(Event, StateName),  74     {next_state, StateName, Data}.  75 76 %% gen_fsm:sync_send_all_state_event -> Module:handle_sync_event/477 handle_sync_event(Event, From, StateName, Data) ->  78     io:format("handle_sync_event, for process: ~p... ~n", [From]),  79     unexpected(Event, StateName),  80     {next_state, StateName, Data}.  81   82 handle_info(Info, StateName, Data) ->  83     io:format("handle_info...~n"),  84     unexpected(Info, StateName),  85     {next_state, StateName, Data}.  86   87   88 %% Unexpected allows to log unexpected messages  89 unexpected(Msg, State) ->  90     io:format("~p RECEIVED UNKNOWN EVENT: ~p, while FSM process in state: ~p~n",  91               [self(), Msg, State]).  9293 %% actions  94 do_unlock() ->  95     io:format("passwd is right, open the DOOR.~n").  96   97 do_lock() ->  98     io:format("over, close the DOOR.~n"). 
复制代码

 

测试结果:

0 0
原创粉丝点击