Erlang OTP学习(1)gen_fsm

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_lock
19     % 第二个参数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/2
30 %% Module为设置的code_lock回调模块,StateName为回调函数,即code_lock:locked
31 -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/4
77 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]).  
92
93 %% 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"). 

 

测试结果:

 

本人查找的一些资料加上个人的理解,如果有错误,请务必指出来,十分感激,谢谢...

posted @ 2016-08-19 01:15  Shay_黄  阅读(503)  评论(0编辑  收藏  举报