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").
测试结果:
本人查找的一些资料加上个人的理解,如果有错误,请务必指出来,十分感激,谢谢...