将学习一些从gen_fsm到gen_statem的Erlang教程

将学习一些从gen_fsm到gen_statem的Erlang教程,erlang,otp,gen-fsm,gen-statem,Erlang,Otp,Gen Fsm,Gen Statem,我已经阅读了本教程的一章,它使用了gen_fsm,而gen_fsm已经被弃用,取而代之的是gen_statem。运行测试时,我总是被卡住,因为其中一个客户端处于协商状态,并收到一个接受协商事件。(可能还有其他错误,但现在我无法理解为什么会发生这种情况) 贸易州 -module(trade_statem). -behaviour(gen_statem). % Public API -export([start/1, start_link/1, trade/2, accept_trade/1, m

我已经阅读了本教程的一章,它使用了gen_fsm,而gen_fsm已经被弃用,取而代之的是gen_statem。运行测试时,我总是被卡住,因为其中一个客户端处于协商状态,并收到一个接受协商事件。(可能还有其他错误,但现在我无法理解为什么会发生这种情况)

贸易州

-module(trade_statem).
-behaviour(gen_statem).

% Public API
-export([start/1, start_link/1, trade/2, accept_trade/1, make_offer/2, retract_offer/2, ready/1, cancel/1]).
% gen_statem callbacks
-export([init/1, callback_mode/0, code_change/4, terminate/3]).
% Custom states
-export([idle/3, idle_wait/3, negotiate/3, wait/3, ready/3]).

% Data record
-record(data, {name = "", other, own_items = [], other_items = [], monitor, from}).

% Public API
start(Name) -> gen_statem:start(?MODULE, [Name], []).

start_link(Name) -> gen_statem:start_link(?MODULE, [Name], []).

trade(OwnPid, OtherPid) -> gen_statem:call(OwnPid, {negotiate, OtherPid}, 30000).

accept_trade(OwnPid) -> gen_statem:call(OwnPid, accept_negotiate).

make_offer(OwnPid, Item) -> gen_statem:cast(OwnPid, {make_offer, Item}).

retract_offer(OwnPid, Item) -> gen_statem:cast(OwnPid, {retract_offer, Item}).

ready(OwnPid) -> gen_statem:call(OwnPid, ready, infinity).

cancel(OwnPid) -> gen_statem:stop(OwnPid).

% Client-To-Client API
ask_negotiate(OtherPid, OwnPid) -> gen_statem:cast(OtherPid, {ask_negotiate, OwnPid}).

accept_negotiate(OtherPid, OwnPid) -> gen_statem:cast(OtherPid, {accept_negotiate, OwnPid}).

do_offer(OtherPid, Item) -> gen_statem:cast(OtherPid, {do_offer, Item}).

undo_offer(OtherPid, Item) -> gen_statem:cast(OtherPid, {undo_offer, Item}).

are_you_ready(OtherPid) -> gen_statem:cast(OtherPid, are_you_ready).

not_yet(OtherPid) -> gen_statem:cast(OtherPid, not_yet).

am_ready(OtherPid) -> gen_statem:cast(OtherPid, ready).

ack_trans(OtherPid) -> gen_statem:cast(OtherPid, ack).

ask_commit(OtherPid) -> gen_statem:call(OtherPid, ask_commit).

do_commit(OtherPid) -> gen_statem:call(OtherPid, do_commit).

% gen_statem API
init(Name) -> {ok, idle, #data{name = Name}}.

callback_mode() -> state_functions.

code_change(_, StateName, Data, _) -> {ok, StateName, Data}.

terminate(normal, ready, D = #data{}) -> notice(D, "FSM leaving.", []);
terminate(_, _, _) -> ok.

% Custom states
idle(cast, {ask_negotiate, OtherPid}, D = #data{}) ->
    Ref = monitor(process, OtherPid),
    notice(D, "~p asked for a trade negotiation", [OtherPid]),
    {next_state, idle_wait, D#data{other = OtherPid, monitor = Ref}};
idle({call, From}, {negotiate, OtherPid}, D = #data{}) ->
    ask_negotiate(OtherPid, self()),
    notice(D, "asking user ~p for a trade", [OtherPid]),
    Ref = monitor(process, OtherPid),
    {next_state, idle_wait, D#data{other = OtherPid, monitor = Ref, from = From}};
idle(_, Event, _) ->
    unexpected(Event, idle),
    keep_state_and_data.

idle_wait(cast, {ask_negotiate, OtherPid}, D = #data{other = OtherPid}) ->
    gen_statem:reply(D#data.from, ok),
    notice(D, "starting negotiation", []),
    {next_state, negotiate, D};
idle_wait(cast, {accept_negotiate, OtherPid}, D = #data{other = OtherPid}) ->
    accept_negotiate(OtherPid, self()),
    notice(D, "accepting negotiation", []),
    {next_state, negotiate, D};
idle_wait({call, From}, accept_negotiate, D = #data{other = OtherPid}) ->
    accept_negotiate(OtherPid, self()),
    notice(D, "accepting negotiation", []),
    {next_state, negotiate, D, {reply, From, ok}};
idle_wait(_, Event, _) ->
    unexpected(Event, idle_wait),
    keep_state_and_data.

negotiate(cast, {make_offer, Item}, D = #data{own_items = OwnItems}) ->
    do_offer(D#data.other, Item),
    notice(D, "offering ~p", [Item]),
    {keep_state, D#data{own_items = add(Item, OwnItems)}};
negotiate(cast, {retract_offer, Item}, D = #data{own_items = OwnItems}) ->
    undo_offer(D#data.other, Item),
    notice(D, "cancelling offer on ~p", [Item]),
    {keep_state, D#data{own_items = remove(Item, OwnItems)}};
negotiate(cast, {do_offer, Item}, D = #data{other_items = OtherItems}) ->
    notice(D, "other player offering ~p", [Item]),
    {keep_state, D#data{other_items = add(Item, OtherItems)}};
negotiate(cast, {undo_offer, Item}, D = #data{other_items = OtherItems}) ->
    notice(D, "Other player cancelling offer on ~p", [Item]),
    {keep_state, D#data{other_items = remove(Item, OtherItems)}};
negotiate(cast, are_you_ready, D = #data{other = OtherPid}) ->
    io:format("Other user ready to trade~n"),
    notice(D, "Other user ready to transfer goods:~nYou get ~p, the other side gets ~p", [D#data.other_items, D#data.own_items]),
    not_yet(OtherPid),
    keep_state_and_data;
negotiate({call, From}, ready, D = #data{other = OtherPid}) ->
    are_you_ready(OtherPid),
    notice(D, "asking if ready, waiting", []),
    {next_state, wait, D#data{from = From}};
negotiate(EventType, Event, Data) ->
    unexpected({EventType, Event, Data}, negotiate),
    keep_state_and_data.

wait(cast, are_you_ready, D = #data{}) ->
    am_ready(D#data.other),
    notice(D, "asked if ready, and I am. Waiting for same reply", []),
    keep_state_and_data;
wait(cast, not_yet, D = #data{}) ->
    notice(D, "Other not ready yet", []),
    keep_state_and_data;
wait(cast, ready, D = #data{}) ->
    am_ready(D#data.other),
    ack_trans(D#data.other),
    gen_statem:reply(D#data.from, ok),
    notice(D, "other side is ready. Moving to ready state", []),
    {next_state, ready, D};
wait(_, Event, _) ->
    unexpected(Event, wait),
    keep_state_and_data.

ready({call, _}, ack, D = #data{}) ->
    case priority(self(), D#data.other) of
        true ->
            try
                notice(D, "asking for commit", []),
                ready_commit = ask_commit(D#data.other),
                notice(D, "ordering commit", []),
                ok = do_commit(D#data.other),
                notice(D, "committing...", []),
                commit(D),
                % Sus
                {stop, normal, D}
            catch Class:Reason ->
                notice(D, "commit failed", []),
                % Sus
                {stop, {Class, Reason}, D}
            end;
        false ->
            keep_state_and_data
    end;
ready({call, From}, ask_commit, D) ->
    notice(D, "replying to ask commit", []),
    % Sus
    {keep_state_and_data, {reply, From, ready_commit}};
ready({call, _}, do_commit, D) ->
    notice(D, "committing...", []),
    commit(D),
    % Sus
    {stop, normal, ok, D};
ready(_, Event, _) ->
    unexpected(Event, ready),
    keep_state_and_data.

% Private functions
add (Item, Items) -> [Item | Items].

remove(Item, Items) -> Items -- [Item].

notice(#data{name = N}, Str, Args) -> io:format("~s: " ++ Str ++ "~n", [N | Args]).

unexpected(Msg, State) -> io:format("~p received unknown event ~p while in state ~p~n", [self(), Msg, State]).

priority(OwnPid, OtherPid) when OwnPid > OtherPid -> true;
priority(OwnPid, OtherPid) when OwnPid < OtherPid -> false.

commit(D = #data{}) -> io:format("Transaction completed for ~s. Items sent are:~n~p,~n received are:~n~p.~nThis operation should have some atomic save in a database.~n", [D#data.name, D#data.own_items, D#data.other_items]).
-module(trade_calls).
-export([main_ab/0, main_cd/0, main_ef/0]).

%% test a little bit of everything and also deadlocks on ready state
%% -- leftover messages possible on race conditions on ready state
main_ab() ->
    S = self(),
    PidCliA = spawn(fun() -> a(S) end),
    receive PidA -> PidA end,
    spawn(fun() -> b(PidA, PidCliA) end).

a(Parent) ->
    {ok, Pid} = trade_statem:start_link("Carl"),
    Parent ! Pid,
    io:format("Spawned Carl: ~p~n", [Pid]),
    %sys:trace(Pid,true),
    timer:sleep(800),
    trade_statem:accept_trade(Pid),
    timer:sleep(400),
    io:format("~p~n",[trade_statem:ready(Pid)]),
    timer:sleep(1000),
    trade_statem:make_offer(Pid, "horse"),
    trade_statem:make_offer(Pid, "sword"),
    timer:sleep(1000),
    io:format("a synchronizing~n"),
    sync2(),
    trade_statem:ready(Pid),
    timer:sleep(200),
    trade_statem:ready(Pid),
    timer:sleep(1000).

b(PidA, PidCliA) ->
    {ok, Pid} = trade_statem:start_link("Jim"),
    io:format("Spawned Jim: ~p~n", [Pid]),
    %sys:trace(Pid,true),
    timer:sleep(500),
    trade_statem:trade(Pid, PidA),
    trade_statem:make_offer(Pid, "boots"),
    timer:sleep(200),
    trade_statem:retract_offer(Pid, "boots"),
    timer:sleep(500),
    trade_statem:make_offer(Pid, "shotgun"),
    timer:sleep(1000),
    io:format("b synchronizing~n"),
    sync1(PidCliA),
    trade_statem:make_offer(Pid, "horse"), %% race condition!
    trade_statem:ready(Pid),
    timer:sleep(200),
    timer:sleep(1000).

%% force a race condition on cd trade negotiation
main_cd() ->
    S = self(),
    PidCliC = spawn(fun() -> c(S) end),
    receive PidC -> PidC end,
    spawn(fun() -> d(S, PidC, PidCliC) end),
    receive PidD -> PidD end,
    PidCliC ! PidD.
    
c(Parent) ->
    {ok, Pid} = trade_statem:start_link("Marc"),
    Parent ! Pid,
    receive PidD -> PidD end,
    io:format("Spawned Marc: ~p~n", [Pid]),
    %sys:trace(Pid, true),
    sync2(),
    trade_statem:trade(Pid, PidD),
    %% no need to accept_trade thanks to the race condition
    timer:sleep(200),
    trade_statem:retract_offer(Pid, "car"),
    trade_statem:make_offer(Pid, "horse"),
    timer:sleep(600),
    trade_statem:cancel(Pid),
    timer:sleep(1000).

d(Parent, PidC, PidCliC) ->
    {ok, Pid} = trade_statem:start_link("Pete"),
    Parent ! Pid,
    io:format("Spawned Jim: ~p~n", [Pid]),
    %sys:trace(Pid,true),
    sync1(PidCliC),
    trade_statem:trade(Pid, PidC),
    %% no need to accept_trade thanks to the race condition
    timer:sleep(200),
    trade_statem:retract_offer(Pid, "car"),
    trade_statem:make_offer(Pid, "manatee"),
    timer:sleep(100),
    trade_statem:ready(Pid),
    timer:sleep(1000).

main_ef() ->
    S = self(),
    PidCliE = spawn(fun() -> e(S) end),
    receive PidE -> PidE end,
    spawn(fun() -> f(PidE, PidCliE) end).

e(Parent) ->
    {ok, Pid} = trade_statem:start_link("Carl"),
    Parent ! Pid,
    io:format("Spawned Carl: ~p~n", [Pid]),
    %sys:trace(Pid,true),
    timer:sleep(800),
    trade_statem:accept_trade(Pid),
    timer:sleep(400),
    io:format("~p~n",[trade_statem:ready(Pid)]),
    timer:sleep(1000),
    trade_statem:make_offer(Pid, "horse"),
    trade_statem:make_offer(Pid, "sword"),
    timer:sleep(1000),
    io:format("a synchronizing~n"),
    sync2(),
    trade_statem:ready(Pid),
    timer:sleep(200),
    trade_statem:ready(Pid),
    timer:sleep(1000).

f(PidE, PidCliE) ->
    {ok, Pid} = trade_statem:start_link("Jim"),
    io:format("Spawned Jim: ~p~n", [Pid]),
    %sys:trace(Pid,true),
    timer:sleep(500),
    trade_statem:trade(Pid, PidE),
    trade_statem:make_offer(Pid, "boots"),
    timer:sleep(200),
    trade_statem:retract_offer(Pid, "boots"),
    timer:sleep(500),
    trade_statem:make_offer(Pid, "shotgun"),
    timer:sleep(1000),
    io:format("b synchronizing~n"),
    sync1(PidCliE),
    trade_statem:make_offer(Pid, "horse"),
    timer:sleep(200),
    trade_statem:ready(Pid),
    timer:sleep(1000).

%%% Utils
sync1(Pid) ->
    Pid ! self(),
    receive ack -> ok end.

sync2() ->
    receive
        From -> From ! ack
    end.

谢谢你抽出时间

出现了一些错误,您可以对它们运行一个测试以获得差异。在交易结束时,您可能会看到一些otp报告,但这完全是意料之中的

贸易州erl

-module(trade_statem).
-behaviour(gen_statem).

% Public API
-export([start/1, start_link/1, trade/2, accept_trade/1, make_offer/2, retract_offer/2, ready/1, cancel/1]).
% gen_statem callbacks
-export([init/1, callback_mode/0, code_change/4, terminate/3]).
% Custom states
-export([idle/3, idle_wait/3, negotiate/3, wait/3, ready/3]).

% Data record
-record(data, {name = "", other, own_items = [], other_items = [], monitor, from}).

% Public API
start(Name) -> gen_statem:start(?MODULE, [Name], []).

start_link(Name) -> gen_statem:start_link(?MODULE, [Name], []).

trade(OwnPid, OtherPid) -> gen_statem:call(OwnPid, {negotiate, OtherPid}, 30000).

accept_trade(OwnPid) -> gen_statem:call(OwnPid, accept_negotiate).

make_offer(OwnPid, Item) -> gen_statem:cast(OwnPid, {make_offer, Item}).

retract_offer(OwnPid, Item) -> gen_statem:cast(OwnPid, {retract_offer, Item}).

ready(OwnPid) -> gen_statem:call(OwnPid, ready, infinity).

cancel(OwnPid) -> gen_statem:stop(OwnPid).

% Client-To-Client API
ask_negotiate(OtherPid, OwnPid) -> gen_statem:cast(OtherPid, {ask_negotiate, OwnPid}).

accept_negotiate(OtherPid, OwnPid) -> gen_statem:cast(OtherPid, {accept_negotiate, OwnPid}).

do_offer(OtherPid, Item) -> gen_statem:cast(OtherPid, {do_offer, Item}).

undo_offer(OtherPid, Item) -> gen_statem:cast(OtherPid, {undo_offer, Item}).

are_you_ready(OtherPid) -> gen_statem:cast(OtherPid, are_you_ready).

not_yet(OtherPid) -> gen_statem:cast(OtherPid, not_yet).

am_ready(OtherPid) -> gen_statem:cast(OtherPid, ready).

ack_trans(OtherPid) -> gen_statem:cast(OtherPid, ack).

ask_commit(OtherPid) -> gen_statem:call(OtherPid, ask_commit).

do_commit(OtherPid) -> gen_statem:call(OtherPid, do_commit).

% gen_statem API
init(Name) -> {ok, idle, #data{name = Name}}.

callback_mode() -> state_functions.

code_change(_, StateName, Data, _) -> {ok, StateName, Data}.

terminate(normal, ready, D = #data{}) -> notice(D, "FSM leaving.", []);
terminate(_, _, _) -> ok.

% Custom states
idle(cast, {ask_negotiate, OtherPid}, D = #data{}) ->
    Ref = monitor(process, OtherPid),
    notice(D, "~p asked for a trade negotiation", [OtherPid]),
    {next_state, idle_wait, D#data{other = OtherPid, monitor = Ref}};
idle({call, From}, {negotiate, OtherPid}, D = #data{}) ->
    ask_negotiate(OtherPid, self()),
    notice(D, "asking user ~p for a trade", [OtherPid]),
    Ref = monitor(process, OtherPid),
    {next_state, idle_wait, D#data{other = OtherPid, monitor = Ref, from = From}};
idle(_, Event, _) ->
    unexpected(Event, idle),
    keep_state_and_data.

idle_wait(cast, {ask_negotiate, OtherPid}, D = #data{other = OtherPid}) ->
    gen_statem:reply(D#data.from, ok),
    notice(D, "starting negotiation", []),
    {next_state, negotiate, D};
idle_wait(cast, {accept_negotiate, OtherPid}, D = #data{other = OtherPid}) ->
    gen_statem:reply(D#data.from, ok),
    notice(D, "starting negotiation", []),
    {next_state, negotiate, D};
idle_wait({call, From}, accept_negotiate, D = #data{other = OtherPid}) ->
    accept_negotiate(OtherPid, self()),
    notice(D, "accepting negotiation", []),
    {next_state, negotiate, D, {reply, From, ok}};
idle_wait(_, Event, _) ->
    unexpected(Event, idle_wait),
    keep_state_and_data.

negotiate(cast, {make_offer, Item}, D = #data{own_items = OwnItems}) ->
    do_offer(D#data.other, Item),
    notice(D, "offering ~p", [Item]),
    {keep_state, D#data{own_items = add(Item, OwnItems)}};
negotiate(cast, {retract_offer, Item}, D = #data{own_items = OwnItems}) ->
    undo_offer(D#data.other, Item),
    notice(D, "cancelling offer on ~p", [Item]),
    {keep_state, D#data{own_items = remove(Item, OwnItems)}};
negotiate(cast, {do_offer, Item}, D = #data{other_items = OtherItems}) ->
    notice(D, "other player offering ~p", [Item]),
    {keep_state, D#data{other_items = add(Item, OtherItems)}};
negotiate(cast, {undo_offer, Item}, D = #data{other_items = OtherItems}) ->
    notice(D, "Other player cancelling offer on ~p", [Item]),
    {keep_state, D#data{other_items = remove(Item, OtherItems)}};
negotiate(cast, are_you_ready, D = #data{other = OtherPid}) ->
    io:format("Other user ready to trade~n"),
    notice(D, "Other user ready to transfer goods:~nYou get ~p, the other side gets ~p", [D#data.other_items, D#data.own_items]),
    not_yet(OtherPid),
    keep_state_and_data;
negotiate({call, From}, ready, D = #data{other = OtherPid}) ->
    are_you_ready(OtherPid),
    notice(D, "asking if ready, waiting", []),
    {next_state, wait, D#data{from = From}};
negotiate(EventType, Event, Data) ->
    unexpected({EventType, Event, Data}, negotiate),
    keep_state_and_data.

wait(cast, {do_offer, Item}, D = #data{other_items = OtherItems}) ->
    gen_statem:reply(D#data.from, offer_changed),
    notice(D, "Other side offering ~p", [Item]),
    {next_state, negotiate, D#data{other_items = add(Item, OtherItems)}};
wait(cast, {undo_offer, Item}, D = #data{other_items = OtherItems}) ->
    gen_statem:reply(D#data.from, offer_changed),
    notice(D, "other side cancelling offer ~p", [Item]),
    {next_state, negotiate, D#data{other_items = remove(Item, OtherItems)}};
wait(cast, are_you_ready, D = #data{}) ->
    am_ready(D#data.other),
    notice(D, "asked if ready, and I am. Waiting for same reply", []),
    keep_state_and_data;
wait(cast, not_yet, D = #data{}) ->
    notice(D, "Other not ready yet", []),
    keep_state_and_data;
wait(cast, ready, D = #data{}) ->
    am_ready(D#data.other),
    ack_trans(D#data.other),
    gen_statem:reply(D#data.from, ok),
    notice(D, "other side is ready. Moving to ready state", []),
    {next_state, ready, D};
wait(_, Event, _) ->
    unexpected(Event, wait),
    keep_state_and_data.

ready(cast, ack, D = #data{}) ->
    case priority(self(), D#data.other) of
        true ->
            try
                notice(D, "asking for commit", []),
                ready_commit = ask_commit(D#data.other),
                notice(D, "ordering commit", []),
                ok = do_commit(D#data.other),
                notice(D, "committing...", []),
                commit(D),
                % Sus
                {stop, normal, D}
            catch Class:Reason ->
                notice(D, "commit failed", []),
                % Sus
                {stop, {Class, Reason}, D}
            end;
        false ->
            keep_state_and_data
    end;
ready({call, From}, ask_commit, D) ->
    notice(D, "replying to ask commit", []),
    % Sus
    {keep_state_and_data, {reply, From, ready_commit}};
ready({call, _}, do_commit, D) ->
    notice(D, "committing...", []),
    commit(D),
    % Sus
    {stop, normal, ok, D};
ready(_, Event, _) ->
    unexpected(Event, ready),
    keep_state_and_data.

% Private functions
add (Item, Items) -> [Item | Items].

remove(Item, Items) -> Items -- [Item].

notice(#data{name = N}, Str, Args) -> io:format("~s: " ++ Str ++ "~n", [N | Args]).

unexpected(Msg, State) -> io:format("~p received unknown event ~p while in state ~p~n", [self(), Msg, State]).

priority(OwnPid, OtherPid) when OwnPid > OtherPid -> true;
priority(OwnPid, OtherPid) when OwnPid < OtherPid -> false.

commit(D = #data{}) -> io:format("Transaction completed for ~s. Items sent are:~n~p,~n received are:~n~p.~nThis operation should have some atomic save in a database.~n", [D#data.name, D#data.own_items, D#data.other_items]).
-模块(贸易状态)。
-行为(gen_statem)。
%公共API
-导出([开始/1、开始链接/1、交易/2、接受交易/1、发出要约/2、撤回要约/2、准备就绪/1、取消/1])。
%gen_statem回调
-导出([init/1,回调模式/0,代码更改/4,终止/3])。
%海关国家
-导出([idle/3,idle\U wait/3,协商/3,等待/3,就绪/3])。
%数据记录
-记录(数据,{name=”“,其他,自有项目=[],其他项目=[],监视器,来自})。
%公共API
start(Name)->gen_statem:start(?模块,[名称],])。
开始链接(名称)->gen\u statem:开始链接(?模块,[名称],])。
trade(OwnPid,OtherPid)->gen_statem:call(OwnPid,{协商,OtherPid},30000)。
接受交易(OwnPid)->gen\u statem:call(OwnPid,接受协商)。
make_offer(OwnPid,Item)->gen_statem:cast(OwnPid,{make_offer,Item})。
撤回要约(OwnPid,Item)->gen\u statem:cast(OwnPid,{retract\u要约,Item})。
ready(OwnPid)->gen_statem:call(OwnPid,ready,infinity)。
取消(OwnPid)->gen_statem:stop(OwnPid)。
%客户端到客户端API
ask_协商(OtherPid,OwnPid)->gen_statem:cast(OtherPid,{ask_协商,OwnPid})。
接受协商(OtherPid,OwnPid)->gen\u statem:cast(OtherPid,{accept\u协商,OwnPid})。
dou-offer(OtherPid,Item)->gen-statem:cast(OtherPid,{dou-offer,Item})。
撤销要约(OtherPid,Item)->gen_statem:cast(OtherPid,{undo_要约,Item})。
你准备好了吗(OtherPid)->gen\u statem:cast(OtherPid,你准备好了吗)。
尚未(OtherPid)->gen\u statem:cast(OtherPid,尚未)。
am_ready(OtherPid)->gen_statem:cast(OtherPid,ready)。
ack\U trans(其他PID)->gen\U statem:cast(其他PID,ack)。
ask_commit(OtherPid)->gen_statem:call(OtherPid,ask_commit)。
do_commit(OtherPid)->gen_statem:call(OtherPid,do_commit)。
%gen_statem API
init(Name)->{ok,idle,#data{Name=Name}}。
回调\模式()->状态\函数。
代码更改(u,StateName,Data,u)->{ok,StateName,Data}。
终止(正常,就绪,D=#数据{})->通知(D,“FSM离开。”,[]);
终止(u,u,u)->正常。
%海关国家
空闲(cast,{ask_negotiate,OtherPid},D=#data{})->
Ref=监视器(过程,其他PID),
通知(D,“~p要求进行贸易谈判,[OtherPid]),
{next_state,idle_wait,D#data{other=OtherPid,monitor=Ref};
空闲({call,From},{negotiate,OtherPid},D=#data{})->
要求协商(其他PID,self()),
注意(D,“要求用户~p进行交易”[OtherPid]),
Ref=监视器(过程,其他PID),
{next_state,idle_wait,D#data{other=OtherPid,monitor=Ref,from=from}};
空闲(u,事件,u)->
意外(事件、空闲),
保存_state_和_数据。
空闲等待(cast,{ask_negotiate,OtherPid},D=#data{other=OtherPid})->
gen#u statem:答复(数据来源,ok),
通知(D,“开始谈判”[]),
{下一个州,谈判,D};
空闲等待(cast,{accept_negotiate,OtherPid},D=#data{other=OtherPid})->
gen#u statem:答复(数据来源,ok),
通知(D,“开始谈判”[]),
{下一个州,谈判,D};
空闲等待({call,From},accept_negotiate,D=#data{other=OtherPid})->
接受协商(OtherPid,self()),
通知(D,“接受谈判”,[]),
{下一个州,协商,D,{答复,发件人,ok};
空闲等待(u,事件,u)->
意外(事件、空闲等待),
保存_state_和_数据。
协商(cast,{make_offer,Item},D=#data{own_items=own items})->
是否提供(数据、其他、项目),
通知(D,“报价~p”,[项目],
{keep_state,D#data{own_items=add(Item,own items)};
协商(cast,{retract_offer,Item},D=#data{own_items=own items})->
撤销报价(数据、其他、项目),
通知(D,“取消~p”的报价,[项目],
{keep_state,D#data{own_items=remove(Item,own items)};
协商(cast,{do_offer,Item},D=#data{other_items=OtherItems})->
通知(D,“其他玩家提供~p”,[项目],
{keep_state,D#data{other_items=add(Item,OtherItems)};
协商(cast,{undo_offer,Item},D=#data{other_items=OtherItems})->
通知(D,“其他玩家取消~p”上的报价,[项目],
{keep_state,D#data{other_items=remove(Item,OtherItems)};
协商(cast,你准备好了吗,D=#data{other=OtherPid})->
io:格式(“其他用户准备交易~n”),
注意(D,“其他用户准备转移货物:~n您获得~p,另一方获得~p”,[D#data.Other_items,D#data.own_items]),
还没有(其他PID),
保存_state_和_数据;
协商({call,From},ready,D=#data{other=OtherPid})->
你准备好了吗,
通知(D,“询问是否准备就绪,等待”,[]),
{next_state,wait,D#data{from=from}};
协商(事件类型、事件、数据)->
意外({EventType,Event,Data},协商),
保存_state_和_数据。
等待(cast,{do_offer,Item},D=#data{other_items=OtherItems})->
gen#U statem:回复(数据来源,报价变更),
通知(D,“另一方报价”【项目】,
{下一个状态,协商,数据{other_items=add(Item,OtherItems)};
等待(cast,{undo_offer,Item},D=#data{other_items=OtherItems})->
gen#U statem:回复(数据来源,报价变更),
通知(D,“另一方取消报价”~p“,[项目],
{next_state,negotiate,D#data{other_items=remove(Item,OtherItems)};
等待(cast,你准备好了吗,D=#data{})->
am#U就绪(D#数据。其他),
通知(D,“询问是否准备好,我正在等待相同的答复”,[]),
K