我遇到了一个奇怪的问题。我得到了这个 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)?
看看你的 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 调用更改状态。
======
另一个问题/问题是我不知道为什么状态
Run Code Online (Sandbox Code Playgroud)packetsDeliver(enter, _OldState, Data) -> io:format ("Key1", []), {keep_state, Data};当我执行 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)