Advertisement
Guest User

Untitled

a guest
Jan 6th, 2020
155
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Erlang 14.95 KB | None | 0 0
  1. -module(consumer_agent).
  2. -behaviour(gen_fsm).
  3.  
  4. %% public API
  5. -export([start_link/1, negotiation/4]).
  6. %% gen_fsm callbacks
  7. -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,
  8.          terminate/3, code_change/4,
  9.          % custom state names
  10.          idle/3, idle_wait/2, negotiate/2,
  11.          wait/2, ready/2, ready/3]).
  12.  
  13. -record(state, {name="",
  14.                 other,
  15.                 item,
  16.                 price,
  17.                 initprice,
  18.                 maxprice,
  19.                 monitor,
  20.                 from}).
  21.  
  22.  
  23.  
  24. %%% PUBLIC API
  25. start_link(Name) ->
  26.     gen_fsm:start_link(?MODULE, [Name], []).
  27.  
  28. negotiation(OwnPid, OtherPid, Item, MaxPrice) ->
  29.     gen_fsm:sync_send_event(OwnPid, {negotiate, Item, OtherPid, MaxPrice}, 30000).
  30.  
  31.  
  32.  
  33. %%% CLIENT-TO-CLIENT API
  34. ask_negotiate(OtherPid, Item, OwnPid) ->
  35.     gen_fsm:send_event(OtherPid, {ask_negotiate, Item, OwnPid}).
  36.  
  37. do_offer(OtherPid, Price) ->
  38.     gen_fsm:send_event(OtherPid, {do_offer, Price}).
  39.  
  40. are_you_ready(OtherPid) ->
  41.     gen_fsm:send_event(OtherPid, are_you_ready).
  42.  
  43. not_yet(OtherPid) ->
  44.     gen_fsm:send_event(OtherPid, not_yet).
  45.  
  46. am_ready(OtherPid) ->
  47.     gen_fsm:send_event(OtherPid, 'ready!').
  48.  
  49. ack_trans(OtherPid) ->
  50.     gen_fsm:send_event(OtherPid, ack).
  51.  
  52. ask_commit(OtherPid) ->
  53.     gen_fsm:sync_send_event(OtherPid, ask_commit).
  54.  
  55. do_commit(OtherPid) ->
  56.     gen_fsm:sync_send_event(OtherPid, do_commit).
  57.  
  58. notify_cancel(OtherPid) ->
  59.     gen_fsm:send_all_state_event(OtherPid, cancel).
  60.  
  61. %%% GEN_FSM API
  62. init(Name) ->
  63.     {ok, idle, #state{name=Name}}.
  64.  
  65. idle({negotiate, Item, OtherPid, MaxPrice}, From, S=#state{}) ->
  66.     ask_negotiate(OtherPid, Item, self()),
  67.     notice(S, "asking producer ~p for a sale of item ~s", [OtherPid, Item]),
  68.     Ref = monitor(process, OtherPid),
  69.     {next_state, idle_wait, S#state{other=OtherPid,
  70.                                     monitor=Ref,
  71.                                     from=From,
  72.                                     item=Item,
  73.                                     maxprice=MaxPrice
  74.                                    }};
  75. idle(Event, _From, Data) ->
  76.     unexpected(Event, idle),
  77.     {next_state, idle, Data}.
  78.  
  79. idle_wait({accept_negotiate, InitPrice, OtherPid}, S=#state{other=OtherPid}) ->
  80.     gen_fsm:reply(S#state.from, ok),
  81.     notice(S, "starting negotiation, init price ~p", [InitPrice]),
  82.     NewPrice = S#state.maxprice * 0.75,
  83.     do_offer(S#state.other, NewPrice),
  84.     {next_state, negotiate, S#state{initprice=InitPrice, price=NewPrice}};
  85. idle_wait(Event, Data) ->
  86.     unexpected(Event, idle_wait),
  87.     {next_state, idle_wait, Data}.
  88.  
  89. negotiate({do_offer, Price}, S=#state{}) ->
  90.     notice(S, "Producer offering price ~p", [Price]),
  91.     NextPrice = calculate_next_price(S#state.price, Price, S#state.maxprice, S#state.initprice),
  92.     case NextPrice of
  93.         ok ->
  94.             are_you_ready(S#state.other),
  95.             {next_state, wait, S#state{price=Price}};
  96.         stop ->
  97.             notify_cancel(S#state.other),
  98.             {next_state, cancel, S};
  99.         _ ->
  100.             do_offer(S#state.other, NextPrice),
  101.             {next_state, negotiate, S#state{price=NextPrice}}
  102.     end;
  103. negotiate(are_you_ready, S=#state{}) ->
  104.     io:format("Producer ready to trade.~n"),
  105.     notice(S, "Producer ready to sell item ~p for ~p, initial price was ~p.",
  106.            [S#state.item, S#state.price, S#state.initprice]),
  107.     are_you_ready(S#state.other),
  108.     {next_state, wait, S};
  109. negotiate(Event, Data) ->
  110.     unexpected(Event, negotiate),
  111.     {next_state, negotiate, Data}.
  112.  
  113. wait({do_offer, Price}, S=#state{}) ->
  114.     gen_fsm:reply(S#state.from, offer_changed),
  115.     notice(S, "Producer offering price ~p", [Price]),
  116.     {next_state, negotiate, S#state{price=Price}};
  117. wait(are_you_ready, S=#state{}) ->
  118.     am_ready(S#state.other),
  119.     notice(S, "asked if ready, and I am. Waiting for same reply", []),
  120.     {next_state, wait, S};
  121. wait(not_yet, S = #state{}) ->
  122.     notice(S, "Producer not ready yet", []),
  123.     {next_state, wait, S};
  124. wait('ready!', S=#state{}) ->
  125.     am_ready(S#state.other),
  126.     ack_trans(S#state.other),
  127.     gen_fsm:reply(S#state.from, ok),
  128.     notice(S, "Producer is ready. Moving to ready state", []),
  129.     {next_state, ready, S};
  130. wait(Event, Data) ->
  131.     unexpected(Event, wait),
  132.     {next_state, wait, Data}.
  133.  
  134. ready(ack, S=#state{}) ->
  135.     case priority(self(), S#state.other) of
  136.         true ->
  137.             try
  138.                 notice(S, "asking for commit", []),
  139.                 ready_commit = ask_commit(S#state.other),
  140.                 notice(S, "ordering commit", []),
  141.                 ok = do_commit(S#state.other),
  142.                 notice(S, "committing...", []),
  143.                 commit(S),
  144.                 {stop, normal, S}
  145.             catch Class:Reason ->
  146.                 notice(S, "commit failed", []),
  147.                 {stop, {Class, Reason}, S}
  148.             end;
  149.         false ->
  150.             {next_state, ready, S}
  151.     end;
  152. %% BUG
  153. ready('ready!', S) ->
  154.     {next_state, ready, S};
  155. ready(Event, Data) ->
  156.     unexpected(Event, ready),
  157.     {next_state, ready, Data}.
  158. ready(ask_commit, _From, S) ->
  159.     notice(S, "replying to ask_commit", []),
  160.     {reply, ready_commit, ready, S};
  161. ready(do_commit, _From, S) ->
  162.     notice(S, "committing...", []),
  163.     commit(S),
  164.     {stop, normal, ok, S};
  165. ready(Event, _From, Data) ->
  166.     unexpected(Event, ready),
  167.     {next_state, ready, Data}.
  168.  
  169. handle_event(cancel, _StateName, S=#state{}) ->
  170.     notice(S, "received cancel event", []),
  171.     {stop, other_cancelled, S};
  172. handle_event(Event, StateName, Data) ->
  173.     unexpected(Event, StateName),
  174.     {next_state, StateName, Data}.
  175.  
  176. handle_sync_event(cancel, _From, _StateName, S = #state{}) ->
  177.     notify_cancel(S#state.other),
  178.     notice(S, "cancelling trade, sending cancel event", []),
  179.     {stop, cancelled, ok, S};
  180. handle_sync_event(Event, _From, StateName, Data) ->
  181.     unexpected(Event, StateName),
  182.     {next_state, StateName, Data}.
  183.  
  184. handle_info({'DOWN', Ref, process, Pid, Reason}, _, S=#state{other=Pid, monitor=Ref}) ->
  185.     notice(S, "Producer is dead", []),
  186.     {stop, {other_down, Reason}, S};
  187. handle_info(Info, StateName, Data) ->
  188.     unexpected(Info, StateName),
  189.     {next_state, StateName, Data}.
  190.  
  191. code_change(_OldVsn, StateName, Data, _Extra) ->
  192.  {ok, StateName, Data}.
  193.  
  194. terminate(normal, ready, S=#state{}) ->
  195.     notice(S, "FSM leaving.", []);
  196. terminate(_Reason, _StateName, _StateData) ->
  197.     ok.
  198.  
  199.  
  200.  
  201. %%% PRIVATE FUNCTIONS
  202. notice(#state{name=N}, Str, Args) ->
  203.     io:format("~s: "++Str++"~n", [N|Args]).
  204.  
  205. unexpected(Msg, State) ->
  206.     io:format("~p received unknown event ~p while in state ~p~n",
  207.               [self(), Msg, State]).
  208.  
  209. priority(OwnPid, OtherPid) when OwnPid > OtherPid -> true;
  210. priority(OwnPid, OtherPid) when OwnPid < OtherPid -> false.
  211.  
  212. commit(S = #state{}) ->
  213.     io:format("Transaction completed for ~p.~n"
  214.               "Item ~s was bought for ~p, initial price was ~p~n",
  215.               [S#state.name, S#state.item, S#state.price, S#state.initprice]).
  216.  
  217. calculate_next_price(PrevPrice, Price, MaxPrice, InitPrice) ->
  218.     case (Price > MaxPrice) or (Price > InitPrice) of
  219.         true -> stop;
  220.         _ -> case abs(PrevPrice - Price) < 0.1 * Price of
  221.                  true -> ok;
  222.                  _ -> 1.1*PrevPrice
  223.              end
  224.     end.
  225.  
  226.  
  227.  
  228.  
  229.  
  230.  
  231.  
  232.  
  233.  
  234. -module(producer_agent).
  235. -behaviour(gen_fsm).
  236.  
  237. %%% PUBLIC API
  238. -export([start_link/1, accept_negotiation/3]).
  239. %% gen_fsm callbacks
  240. -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,
  241.          terminate/3, code_change/4,
  242.          % custom state names
  243.          idle/2, idle_wait/3, negotiate/2,
  244.          wait/2, ready/2, ready/3]).
  245.  
  246. -record(state, {name="",
  247.                 other,
  248.                 item,
  249.                 price,
  250.                 initprice,
  251.                 minprice,
  252.                 monitor,
  253.                 from}).
  254.  
  255.  
  256.  
  257. %%% PUBLIC API
  258. start_link(Name) ->
  259.     gen_fsm:start_link(?MODULE, [Name], []).
  260.  
  261. accept_negotiation(OwnPid, InitPrice, MinPrice) ->
  262.     gen_fsm:sync_send_event(OwnPid, {accept_negotiate, InitPrice, MinPrice}).
  263.  
  264.  
  265.  
  266. %%% CLIENT-TO-CLIENT API
  267. accept_negotiate(OtherPid, InitPrice, OwnPid) ->
  268.     gen_fsm:send_event(OtherPid, {accept_negotiate, InitPrice, OwnPid}).
  269.  
  270. do_offer(OtherPid, Price) ->
  271.     gen_fsm:send_event(OtherPid, {do_offer, Price}).
  272.  
  273. are_you_ready(OtherPid) ->
  274.     gen_fsm:send_event(OtherPid, are_you_ready).
  275.  
  276. not_yet(OtherPid) ->
  277.     gen_fsm:send_event(OtherPid, not_yet).
  278.  
  279. am_ready(OtherPid) ->
  280.     gen_fsm:send_event(OtherPid, 'ready!').
  281.  
  282. ack_trans(OtherPid) ->
  283.     gen_fsm:send_event(OtherPid, ack).
  284.  
  285. ask_commit(OtherPid) ->
  286.     gen_fsm:sync_send_event(OtherPid, ask_commit).
  287.  
  288. do_commit(OtherPid) ->
  289.     gen_fsm:sync_send_event(OtherPid, do_commit).
  290.  
  291. notify_cancel(OtherPid) ->
  292.     gen_fsm:send_all_state_event(OtherPid, cancel).
  293.  
  294.  
  295.  
  296. %%% GEN_FSM API
  297. init(Name) ->
  298.     {ok, idle, #state{name=Name}}.
  299.  
  300. idle({ask_negotiate, Item, OtherPid}, S=#state{}) ->
  301.     Ref = monitor(process, OtherPid),
  302.     notice(S, "~p asked for a sale negotiation of item ~s", [OtherPid, Item]),
  303.     {next_state, idle_wait, S#state{other=OtherPid, monitor=Ref, item=Item}};
  304. idle(Event, Data) ->
  305.     unexpected(Event, idle),
  306.     {next_state, idle, Data}.
  307.  
  308. idle_wait({accept_negotiate, InitPrice, MinPrice}, From, S=#state{other=OtherPid}) ->
  309.     accept_negotiate(OtherPid, InitPrice, self()),
  310.     notice(S, "accepting negotiation with init price ~p", [InitPrice]),
  311.     {reply, ok, negotiate, S#state{initprice=InitPrice, minprice=MinPrice, from=From,
  312.                                   price=InitPrice}};
  313. idle_wait(Event, _From, Data) ->
  314.     unexpected(Event, idle_wait),
  315.     {next_state, idle_wait, Data}.
  316.  
  317. negotiate({do_offer, Price}, S=#state{}) ->
  318.     notice(S, "Consumer offering price ~p", [Price]),
  319.     NextPrice = calculate_next_price(Price, S#state.price, S#state.minprice),
  320.     case NextPrice of
  321.         ok ->
  322.             are_you_ready(S#state.other),
  323.             {next_state, wait, S#state{price=Price}};
  324.         stop ->
  325.            notify_cancel(S#state.other),
  326.             {next_state, cancel, S};
  327.         _ ->
  328.             do_offer(S#state.other, NextPrice),
  329.             {next_state, negotiate, S#state{price=NextPrice}}
  330.     end;
  331. negotiate(are_you_ready, S=#state{}) ->
  332.     notice(S, "Consumer ready to buy item ~p for ~p, initial price was ~p.",
  333.            [S#state.item, S#state.price, S#state.initprice]),
  334.     are_you_ready(S#state.other),
  335.     {next_state, wait, S};
  336. negotiate(Event, Data) ->
  337.     unexpected(Event, negotiate),
  338.     {next_state, negotiate, Data}.
  339.  
  340. wait({do_offer, Price}, S=#state{}) ->
  341.     gen_fsm:reply(S#state.from, offer_changed),
  342.     notice(S, "Consumer offering price ~p", [Price]),
  343.     {next_state, negotiate, S#state{price=Price}};
  344. wait(are_you_ready, S=#state{}) ->
  345.     am_ready(S#state.other),
  346.     notice(S, "asked if ready, and I am. Waiting for same reply", []),
  347.     {next_state, wait, S};
  348. wait(not_yet, S = #state{}) ->
  349.     notice(S, "Consumer not ready yet", []),
  350.     {next_state, wait, S};
  351. wait('ready!', S=#state{}) ->
  352.     am_ready(S#state.other),
  353.     ack_trans(S#state.other),
  354.     gen_fsm:reply(S#state.from, ok),
  355.     notice(S, "Consumer is ready. Moving to ready state", []),
  356.     {next_state, ready, S};
  357. wait(Event, Data) ->
  358.     unexpected(Event, wait),
  359.     {next_state, wait, Data}.
  360.  
  361. ready(ack, S=#state{}) ->
  362.     case priority(self(), S#state.other) of
  363.         true ->
  364.             try
  365.                 notice(S, "asking for commit", []),
  366.                 ready_commit = ask_commit(S#state.other),
  367.                 notice(S, "ordering commit", []),
  368.                 ok = do_commit(S#state.other),
  369.                 notice(S, "committing...", []),
  370.                 commit(S),
  371.                 {stop, normal, S}
  372.             catch Class:Reason ->
  373.                 notice(S, "commit failed", []),
  374.                 {stop, {Class, Reason}, S}
  375.             end;
  376.         false ->
  377.             {next_state, ready, S}
  378.     end;
  379. %% BUG
  380. ready('ready!', S) ->
  381.     {next_state, ready, S};
  382. ready(Event, Data) ->
  383.     unexpected(Event, ready),
  384.     {next_state, ready, Data}.
  385. ready(ask_commit, _From, S) ->
  386.     notice(S, "replying to ask_commit", []),
  387.     {reply, ready_commit, ready, S};
  388. ready(do_commit, _From, S) ->
  389.     notice(S, "committing...", []),
  390.     commit(S),
  391.     {stop, normal, ok, S};
  392. ready(Event, _From, Data) ->
  393.     unexpected(Event, ready),
  394.     {next_state, ready, Data}.
  395.  
  396. handle_event(cancel, _StateName, S=#state{}) ->
  397.     notice(S, "received cancel event", []),
  398.     {stop, other_cancelled, S};
  399. handle_event(Event, StateName, Data) ->
  400.     unexpected(Event, StateName),
  401.     {next_state, StateName, Data}.
  402.  
  403. handle_sync_event(cancel, _From, _StateName, S = #state{}) ->
  404.     notify_cancel(S#state.other),
  405.     notice(S, "cancelling trade, sending cancel event", []),
  406.     {stop, cancelled, ok, S};
  407. handle_sync_event(Event, _From, StateName, Data) ->
  408.     unexpected(Event, StateName),
  409.     {next_state, StateName, Data}.
  410.  
  411. handle_info({'DOWN', Ref, process, Pid, Reason}, _, S=#state{other=Pid, monitor=Ref}) ->
  412.     notice(S, "Consumer is dead", []),
  413.     {stop, {other_down, Reason}, S};
  414. handle_info(Info, StateName, Data) ->
  415.     unexpected(Info, StateName),
  416.     {next_state, StateName, Data}.
  417.  
  418. code_change(_OldVsn, StateName, Data, _Extra) ->
  419.  {ok, StateName, Data}.
  420.  
  421. terminate(normal, ready, S=#state{}) ->
  422.     notice(S, "FSM leaving.", []);
  423. terminate(_Reason, _StateName, _StateData) ->
  424.     ok.
  425.  
  426. %%% PRIVATE FUNCTIONS
  427. %% BUG
  428. notice(#state{name=N}, Str, Args) ->
  429.     io:format("~s: "++Str++"~n", [N|Args]).
  430.  
  431. unexpected(Msg, State) ->
  432.     io:format("~p received unknown event ~p while in state ~p~n",
  433.               [self(), Msg, State]).
  434.  
  435. priority(OwnPid, OtherPid) when OwnPid > OtherPid -> true;
  436. priority(OwnPid, OtherPid) when OwnPid < OtherPid -> false.
  437.  
  438. commit(S = #state{}) ->
  439.     io:format("Transaction completed for ~p.~n"
  440.               "Item ~s was sold for ~p, initial price was ~p~n",
  441.               [S#state.name, S#state.item, S#state.price, S#state.initprice]).
  442.  
  443. calculate_next_price(Price, PrevPrice, MinPrice) ->
  444.     case Price < MinPrice of
  445.         true -> stop;
  446.         _ -> case abs(PrevPrice - Price) < 0.1 * Price of
  447.                  true -> ok;
  448.                  _ -> 0.9 * PrevPrice
  449.              end
  450.     end.
  451.  
  452.  
  453.  
  454.  
  455.  
  456.  
  457.  
  458. -module(invokes).
  459. -compile(export_all).
  460.  
  461.  
  462. start() ->
  463.     S = self(),
  464.     spawn(fun() -> producer(S) end),
  465.     receive PidProducer -> PidProducer end,
  466.     spawn(fun() -> consumer(PidProducer) end),
  467.     timer:sleep(8000),
  468.     ok.
  469.  
  470. producer(Parent) ->
  471.     {ok, Pid} = producer_agent:start_link("Producer"),
  472.     Parent ! Pid,
  473.     io:format("Spawned producer: ~p~n", [Pid]),
  474.     timer:sleep(1000),
  475.     producer_agent:accept_negotiation(Pid, 1600, 960),
  476.     timer:sleep(1000).
  477.  
  478. consumer(Producer) ->
  479.     {ok, Pid} = consumer_agent:start_link("Consumer"),
  480.     io:format("Spawned consumer: ~p~n", [Pid]),
  481.     timer:sleep(100),
  482.     consumer_agent:negotiation(Pid, Producer, "Shoes", 1440),
  483.     timer:sleep(1000).
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement