erlang gen_statem:错误 bad_return_from_state_function

Mr.*_*.OY 1 erlang fsm

我遇到了一个奇怪的问题。我得到了这个 FSM(代码的内容不是那么无能,所以我删除了它,现在你只能看结构):

start_link() ->
  gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []).


init([]) ->
  io:format ("init", []),
  Data={1},
  {ok, packetsDeliver, Data}.


callback_mode() ->
  [state_functions, state_enter].


packetsDeliver(enter, _OldState, Data) ->
  io:format ("Key1", []),
  {keep_state, Data};

packetsDeliver(info, _OldState, Data) ->
  io:format ("Key2", []),
  {keep_state, Data};


packetsDeliver(cast, _PacketData, Data) ->
  io:format ("Key3", []),
      {next_state, allPacketsDelivered, Data}.

allPacketsDelivered(enter, _OldState, Data) ->
  io:format ("Key4", []),
  {next_state, packetsDeliver , Data}.
Run Code Online (Sandbox Code Playgroud)

我尝试了几件事没有任何成功,我认为写作

{next_state, packetsDeliver , Data}.
Run Code Online (Sandbox Code Playgroud)

将为我提供状态:

packetsDeliver(enter, _OldState, Data)
Run Code Online (Sandbox Code Playgroud)

但是我收到了这个错误:

exception exit: {bad_return_from_state_function,
                       {next_state,packetsDeliver,{1}}}
     in function  gen_statem:loop_event_result/9 (gen_statem.erl, line 1165)
     in call from proc_lib:init_p_do_apply/3 (proc_lib.erl, line 247)
3> 
=ERROR REPORT==== 27-Jul-2019::23:53:35 ===
** State machine test terminating
** Last event = {cast,1}
** When server state  = {allPacketsDelivered,{1}}
** Reason for termination = error:{bad_return_from_state_function,
                                      {next_state,packetsDeliver,{1}}}
** Callback mode = [state_functions,state_enter]
** Stacktrace =
**  [{gen_statem,loop_event_result,9,[{file,"gen_statem.erl"},{line,1165}]},
     {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]
Run Code Online (Sandbox Code Playgroud)

另一个问题/问题是我不知道为什么状态

packetsDeliver(enter, _OldState, Data) ->
  io:format ("Key1", []),
  {keep_state, Data};
Run Code Online (Sandbox Code Playgroud)

当我执行 keep_state 时,是否将我传送到 packetDeliver(info, _OldState, Data)?

7st*_*tud 5

看看你的 Stateenter函数返回什么packetsDeliver

packetsDeliver(enter, _OldState, Data) ->
  io:format ("Key1", []),
  {keep_state, Data};   %% <====== HERE
Run Code Online (Sandbox Code Playgroud)

现在看看你的 Statecast函数返回什么packetsDeliver

packetsDeliver(cast, _PacketData, Data) ->
  io:format ("Key3", []),
  {next_state, allPacketsDelivered, Data}.  %% <===== HERE
Run Code Online (Sandbox Code Playgroud)

现在看看你的 Stateenter函数返回什么allPacketsDelivered

allPacketsDelivered(enter, _OldState, Data) ->
  io:format ("Key4", []),
  {next_state, packetsDeliver , Data}.  %% <==== HERE
Run Code Online (Sandbox Code Playgroud)

国家enter的职能allpacketsDelivered应该返回一个元组类似于国家enter的职能packetsDelivered,如{keep_state, ...}没有{next_state, ...}

docs,这些是状态enter函数允许的返回值:

state_callback_result(ActionType) = 
    {keep_state, NewData :: data()} |
    {keep_state,
     NewData :: data(),
     Actions :: [ActionType] | ActionType} |
    keep_state_and_data |
    {keep_state_and_data, Actions :: [ActionType] | ActionType} |
    {repeat_state, NewData :: data()} |
    {repeat_state,
     NewData :: data(),
     Actions :: [ActionType] | ActionType} |
    repeat_state_and_data |
    {repeat_state_and_data, Actions :: [ActionType] | ActionType} |
    stop |
    {stop, Reason :: term()} |
    {stop, Reason :: term(), NewData :: data()} |
    {stop_and_reply,
     Reason :: term(),
     Replies :: [reply_action()] | reply_action()} |
    {stop_and_reply,
     Reason :: term(),
     Replies :: [reply_action()] | reply_action(),
     NewData :: data()}
Run Code Online (Sandbox Code Playgroud)

请注意,这{next_state, ...}不是允许的返回值之一,这就是您收到错误的原因bad_return_from_state_function。该文件还称:

当 gen_statem 使用 state enter calls 运行时(enter, OldState, ...)在每次状态更改期间也会使用参数调用这些函数。在这种情况下,对可能返回的操作有一些限制:不允许延迟(),因为状态输入调用不是事件,因此没有要推迟的事件,并且 {next_event,_,_}不允许,因为使用状态输入调用不应影响事件是如何被消费和产生的。您也不能从此调用更改状态。你应该 {next_state,NextState, ...}带着NextState =/= State该gen_statem崩溃。请注意,实际上允许使用 {repeat_state, NewData, ...} 虽然它没有意义,因为您将立即被再次调用新状态 enter 调用,这只是一种奇怪的循环方式,并且有更好的方法在 Erlang 中循环。如果你不更新 NewData 并且有一些循环终止条件,或者如果你使用 {repeat_state_and_data, _} 或 repeat_state_and_data 你有一个无限循环!建议您使用 {keep_state,...}、{keep_state_and_data,_} 或 keep_state_and_data,因为无论如何都无法从 state enter 调用更改状态。

======

另一个问题/问题是我不知道为什么状态

packetsDeliver(enter, _OldState, Data) ->
  io:format ("Key1", []),
  {keep_state, Data};
Run Code Online (Sandbox Code Playgroud)

当我执行 keep_state 时,是否将我传送到 packetDeliver(info, _OldState, Data)?

什么when I do keep_state意思?我可以告诉你这对任何阅读你问题的人意味着什么:绝对没有。

当 I 时,该信息回调不执行do keep_state。这样就可以准确地告诉您如何修复代码,对吗?错误的。

我是这样做的:

send_event() ->
    gen_statem:cast(?MODULE, hello).
Run Code Online (Sandbox Code Playgroud)

如果你这样做:

Pid ! {blah, blah}
Run Code Online (Sandbox Code Playgroud)

然后info将执行当前状态的回调。

完整示例:

-module(packets).
-behavior(gen_statem).
-compile(export_all).

start_link() ->
    gen_statem:start_link({local, ?MODULE}, ?MODULE, [], []).

init([]) ->
    io:format ("init~n"),
    Data=1,
    {ok, packetsDeliver, Data}.  
    %The initial state will be packetsDeliver.
    %And, when you transition to a state, and you 
    %have specified state_enter, then the function
    %State(enter, OldState, Data) will execute, which
    %in this case is packetsDeliver(enter, OldState, Data)

callback_mode() ->
    [state_functions, state_enter].

packetsDeliver(enter, _OldState, Data) ->
    io:format("packetsDeliver enter~n"),
    {keep_state, Data};
packetsDeliver(cast, _Msg, Data) ->
    io:format ("packetsDeliver cast~n"),
    {next_state, allPacketsDelivered, Data};
packetsDeliver(info, _Msg, Data) ->
    io:format("packetsDeliver info~n"),
    {keep_state, Data}.

allPacketsDelivered(enter, _OldState, _Data) ->
    io:format("allPacketsDelivered enter~n"),
    %{next_state, packetsDeliver , Data}.
    {keep_state_and_data, []}.

send_event() ->
    gen_statem:cast(?MODULE, hello).
Run Code Online (Sandbox Code Playgroud)

在外壳中:

~/erlang_programs/gen_statem$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3  (abort with ^G)

1> c(packets).                  
packets.erl:3: Warning: export_all flag enabled - all functions will be exported
{ok,packets}

2> packets:start_link().                     
init
packetsDeliver enter
{ok,<0.71.0>}

3> Pid = whereis(packets).
<0.71.0>

4> Pid ! hello.
packetsDeliver info
hello

5> packets:send_event().
packetsDeliver cast
ok
allPacketsDelivered enter

6> 
Run Code Online (Sandbox Code Playgroud)