我的 gen_statem fsm 实现有什么问题?

What is wrong with my gen_statem fsm implementation?

我正在使用 gen_statem 模块实现 gen_fsm,当我尝试将其状态检查为 handle_event_function 时,出现以下错误:

> ** exception error: {function_clause,
>                         {gen_statem,call,[{ok,<0.139.0>},state,0]}}
>      in function  gen:do_for_proc/2
>         called as gen:do_for_proc({ok,<0.139.0>},#Fun<gen.0.9801092>)
>      in call from gen_statem:'-call_clean/4-fun-0-'/5 (gen_statem.erl, line 637) 25> c("C:/Erlang/Genserv/fsm.erl").

下面是我的代码,分为:

-mandatory methods for the fsm to work
-api that the client can use (state change,get the state,start)
- generic handlers for when client demands something related to state
-state implementations

模块

-module(fsm).
-record(state,{
    current="None",
    intvCount=0,
    jobCount=0
}).
-export([init/1,terminate/3,callback_mode/0,code_change/4]).

-export([state/1,start/0,hire/2,fire/2,interview/2]).

-export([sitting_home/3,interviewing/3,working/3]).

-behaviour(gen_statem).

%API
start()->
    gen_statem:start_link(?MODULE,[],[]).
state(PID)->
    gen_statem:call(PID,state,0).
hire(PID,Company)->
    gen_statem:call(PID,{hire,Company},0).
fire(PID,Company)->
    gen_statem:call(PID,{fire,Company},0).
interview(PID,Company)->
    gen_state:call(PID,{intv,Company},0).

%mandatory
code_change(V,State,Data,Extra)->{ok,State,Data}.
callback_mode() ->
    [state_functions,handle_event_function].
init([])->
    {ok,sitting_home,#state{current="None",jobCount=0,intvCount=0}}.
terminate(Reasom,State,Data)->
    void.

% Generic handlers
handle_event({call,From},state,State)->
    {keep_state,State,[{reply,From,State}]};
handle_event(_,_,State)->
    {keep_state,State}.

% State implementations
sitting_home({call,From},{intv,Company},State=#state{intvCount=C})->
     {next_state,interviewing,State#state{intvCount=C+1},[{reply,From,"Interviewing by:"++Company}]};
sitting_home(EventType,Event,State)->
     handle_event(EventType,Event,State).
interviewing({call,From},{rejected,Company},State)->
    {next_state,sitting_home,State,[{reply,From,"rejected by:"++Company}]};
interviewing({call,From},{accepted,Company},State=#state{jobCount=J})->
    {next_state,
    working,
    State#state{jobCount=J+1,current=Company},
    [{reply,From,"accepted offer from:"++Company}]
};
interviewing(EventType,Event,State)->
    handle_event(EventType,Event,State).


working({call,From},{fire,Company},State=#state{current=C})->
    {next_state,working,State#state{current="None"},[{reply,From,"Got fired from"++Company}]};
working(EventType,Event,State)->
      handle_event(EventType,Event,State).

用法

FSM_PID=fsm:start().
fsm:state(PID). //crashes when demanding state
fsm:hire(PID,"Some Company").

fsm:start() return 元组 {ok,Pid}。您不能在下次调用时直接使用该函数的 return。相反,您必须执行以下操作:

{ok,P} = fsm:start().
fsm:state(P).

然后你会遇到其他麻烦,第一个是你指定超时为0,所以你会得到超时错误。您将不得不更改它,并继续调试 :o)

[编辑]

你不需要指定Pid,它是由gen_statem代码在后台完成的,gen_statem:call函数是在调用者上下文中执行的,所以它仍然有访问调用方 Pid.

实际上它有点复杂,取决于您是否指定超时,gen_statem:call/3 将生成一个进程来调用 gen:call/4 函数或直接调用它,因此Pid 发送到状态机的将是衍生函数的一个或调用者的一个。

gen:call/4 还在状态机的消息中添加 Reference 以 "sign" 回答和保证来自应用程序任何其他部分的传入消息不会被解释为答案。

这是erlang(和编程)中的一种通用模式,用于在接口函数中尽可能多地隐藏系统机制。正如您在函数 state/1

中所做的那样