├── ch12 ├── sys.config ├── coffee-1.0.rel ├── coffee-1.1.rel ├── coffee-1.1 │ ├── ebin │ │ ├── coffee.appup │ │ └── coffee.app │ └── src │ │ ├── coffee_app.erl │ │ ├── hw.erl │ │ ├── coffee_sup.erl │ │ └── coffee_fsm.erl ├── coffee-1.0 │ ├── ebin │ │ └── coffee.app │ └── src │ │ ├── coffee_app.erl │ │ ├── hw.erl │ │ ├── coffee_sup.erl │ │ └── coffee_fsm.erl ├── relup └── erlang │ ├── hw.erl │ ├── coffee.erl.original │ ├── coffee.erl │ └── patches │ └── coffee.erl ├── .gitignore ├── ch9 ├── myconfig.config ├── bsc.config ├── logtofile.config ├── saslfile.config ├── rb.config ├── dist.config ├── top_app.app ├── top_app │ ├── top_app.app │ └── top_app.erl ├── top_app.erl ├── start_phases │ ├── bsc.erl │ └── bsc.app ├── bsc-1.0 │ ├── src │ │ ├── frequency_sup.erl │ │ ├── phone_test.erl │ │ ├── bsc_sup.erl │ │ ├── bsc.erl │ │ ├── freq_overload.erl │ │ ├── counters.erl │ │ ├── hlr.erl │ │ ├── simple_phone_sup.erl │ │ ├── phone_sup.erl │ │ ├── logger.erl │ │ ├── phone.erl │ │ ├── frequency.erl │ │ └── phone_fsm.erl │ └── ebin │ │ └── bsc.app ├── combining │ └── bsc.erl └── bsc.erl ├── ch11 ├── bsc.config ├── bsc_heart ├── basestation.rel └── bsc │ ├── src │ ├── bsc.erl │ ├── frequency_sup.erl │ ├── bsc_sup.erl │ ├── freq_overload.erl │ ├── counters.erl │ ├── simple_phone_sup.erl │ ├── phone_sup.erl │ ├── hlr.erl │ ├── logger.erl │ ├── phone.erl │ ├── frequency.erl │ └── phone_fsm.erl │ └── ebin │ └── bsc.app ├── ch2 ├── addr.erl ├── ex1.erl ├── ex3.erl ├── ex2.erl ├── echo.erl ├── echo_link.erl ├── queens.erl └── hlr.erl ├── ch10 ├── tcp_wrapper_sup.erl ├── mutex_sup.erl ├── tcp_print.erl ├── mutex0.erl ├── mutex1.erl ├── mutex2.erl ├── mutex.erl └── tcp_wrapper.erl ├── ch4 ├── timeout.erl ├── from.erl ├── ping.erl └── frequency.erl ├── ch7 ├── crash_example.erl ├── freq_overload.erl ├── counters.erl ├── logger.erl └── frequency.erl ├── ch6 ├── erlang │ ├── hw.erl │ ├── earth.erl │ └── coffee_fsm.erl ├── otp │ ├── hw.erl │ ├── test_fsm.erl │ ├── coffee_fsm.erl │ └── coffee_fsm_timeout.erl ├── earth.erl └── exercise │ ├── hlr.erl │ ├── phone.erl │ └── phone_fsm.erl ├── ch8 ├── erlang │ ├── hw.erl │ ├── my_supervisor.erl │ └── coffee.erl └── otp │ ├── phone_test.erl │ ├── bsc_sup.erl │ ├── freq_overload.erl │ ├── counters.erl │ ├── hlr.erl │ ├── simple_phone_sup.erl │ ├── phone_sup.erl │ ├── frequency_sup.erl │ ├── logger.erl │ ├── frequency_sup2.erl │ ├── phone.erl │ ├── frequency.erl │ └── phone_fsm.erl ├── ch3 ├── behavior │ ├── server.erl │ └── frequency.erl ├── pattern.erl ├── call │ └── server.erl └── frequency.erl ├── README.md └── ch5 └── frequency.erl /ch12/sys.config: -------------------------------------------------------------------------------- 1 | []. 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.beam 3 | -------------------------------------------------------------------------------- /ch9/myconfig.config: -------------------------------------------------------------------------------- 1 | [{sasl, [{errlog_type, error}]}, 2 | {bsc, [{frequencies, [1,2,3,4,5,6]}]}]. 3 | -------------------------------------------------------------------------------- /ch11/bsc.config: -------------------------------------------------------------------------------- 1 | [{sasl, [{errlog_type, error}, {sasl_error_logger, tty}]}, 2 | {bsc, [{frequencies, [1,2,3,4,5,6]}]}]. 3 | -------------------------------------------------------------------------------- /ch9/bsc.config: -------------------------------------------------------------------------------- 1 | [{sasl, [{errlog_type, error}, {sasl_error_logger, tty}]}, 2 | {bsc, [{frequencies, [1,2,3,4,5,6]}]}]. 3 | -------------------------------------------------------------------------------- /ch9/logtofile.config: -------------------------------------------------------------------------------- 1 | [{sasl, [{sasl_error_logger, {file, "SASLlogs"}}, 2 | {utc_log, true}]}, 3 | {bsc, [{frequencies, [1,2,3,4,5,6]}]}]. 4 | -------------------------------------------------------------------------------- /ch9/saslfile.config: -------------------------------------------------------------------------------- 1 | [{sasl, [{sasl_error_logger, {file, "SASLlogs"}}, 2 | {utc_log, true}]}, 3 | {bsc, [{frequencies, [1,2,3,4,5,6]}]}]. 4 | -------------------------------------------------------------------------------- /ch11/bsc_heart: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #Basic Heart Script for the Base Station Controller 3 | 4 | ROOTDIR=/Users/francescoc/ernie 5 | 6 | $ROOTDIR/bin/start 7 | -------------------------------------------------------------------------------- /ch12/coffee-1.0.rel: -------------------------------------------------------------------------------- 1 | {release, 2 | {"coffee","1.0"}, 3 | {erts, "7.2"}, 4 | [{kernel, "4.1.1"}, 5 | {stdlib, "2.7"}, 6 | {sasl, "2.6.1"}, 7 | {coffee, "1.0"}]}. 8 | -------------------------------------------------------------------------------- /ch11/basestation.rel: -------------------------------------------------------------------------------- 1 | {release, 2 | {"basestation","1.0"}, 3 | {erts, "7.2"}, 4 | [{kernel, "4.1.1"}, 5 | {stdlib, "2.7"}, 6 | {sasl, "2.6.1"}, 7 | {bsc, "1.0"}]}. 8 | -------------------------------------------------------------------------------- /ch12/coffee-1.1.rel: -------------------------------------------------------------------------------- 1 | {release, 2 | {"coffee","1.1"}, 3 | {erts, "7.2"}, 4 | [{kernel, "4.1.1"}, 5 | {stdlib, "2.7"}, 6 | {sasl, "2.6.1"}, 7 | {coffee, "1.1"}]}. 8 | -------------------------------------------------------------------------------- /ch9/rb.config: -------------------------------------------------------------------------------- 1 | [{sasl, [{sasl_error_logger, false}, 2 | {error_logger_mf_dir, "."}, 3 | {error_logger_mf_maxbytes, 20000}, 4 | {error_logger_mf_maxfiles, 5}]}, 5 | {bsc, [{frequencies, crash}]}]. 6 | -------------------------------------------------------------------------------- /ch2/addr.erl: -------------------------------------------------------------------------------- 1 | -module(addr). 2 | -export([type/1]). 3 | 4 | -include_lib("kernel/include/inet.hrl"). 5 | 6 | type(Addr) -> 7 | {ok, HostEnt} = inet:gethostbyaddr(Addr), 8 | HostEnt#hostent.h_addrtype. 9 | -------------------------------------------------------------------------------- /ch2/ex1.erl: -------------------------------------------------------------------------------- 1 | -module(ex1). 2 | -export([factorial/1]). 3 | 4 | factorial(0) -> 5 | 1; 6 | factorial(N) when N > 0 -> 7 | N * factorial(N-1). 8 | 9 | %% factorial(_) -> 10 | %% {error,bad_argument}. 11 | -------------------------------------------------------------------------------- /ch12/coffee-1.1/ebin/coffee.appup: -------------------------------------------------------------------------------- 1 | %% File:coffee.appup 2 | {"1.1", % Current version 3 | [{"1.0", [{update, coffee_fsm, {advanced, {}}}]}], % Upgrade from 4 | [{"1.0", [{update, coffee_fsm, {advanced, {}}}]}] % Downgrade to 5 | }. 6 | -------------------------------------------------------------------------------- /ch10/tcp_wrapper_sup.erl: -------------------------------------------------------------------------------- 1 | -module(mutex_sup). 2 | -export(start_link/0, init/1]). 3 | 4 | 5 | start_link() -> 6 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 7 | 8 | init(_) -> 9 | {ok, {one_for_one, 5, 3600}, []}. 10 | -------------------------------------------------------------------------------- /ch11/bsc/src/bsc.erl: -------------------------------------------------------------------------------- 1 | -module(bsc). 2 | 3 | -behaviour(application). 4 | 5 | %% Application callbacks 6 | -export([start/2, stop/1]). 7 | 8 | start(_StartType, _StartArgs) -> 9 | bsc_sup:start_link(). 10 | 11 | stop(_Data) -> 12 | ok. 13 | -------------------------------------------------------------------------------- /ch4/timeout.erl: -------------------------------------------------------------------------------- 1 | -module(timeout). 2 | -behaviour(gen_server). 3 | 4 | -export([init/1, handle_call/3]). 5 | 6 | init(_Args) -> 7 | {ok, undefined}. 8 | 9 | handle_call({sleep, Ms}, _From, LoopData) -> 10 | timer:sleep(Ms), 11 | {reply, ok, LoopData}. 12 | -------------------------------------------------------------------------------- /ch2/ex3.erl: -------------------------------------------------------------------------------- 1 | -module(ex3). 2 | -export([filter/2, is_even/1]). 3 | 4 | filter(P,[]) -> []; 5 | filter(P,[X|Xs]) -> 6 | case P(X) of 7 | true -> 8 | [X| filter(P,Xs)]; 9 | _ -> 10 | filter(P,Xs) 11 | end. 12 | 13 | is_even(X) -> 14 | X rem 2 == 0. 15 | -------------------------------------------------------------------------------- /ch9/dist.config: -------------------------------------------------------------------------------- 1 | [{kernel, [{distributed, [{bsc, 1000, [n1@localhost, {n2@localhost, n3@localhost}, n4@localhost]}]}, 2 | {sync_nodes_mandatory, [n1@localhost]}, 3 | {sync_nodes_optional, [n2@localhost, n3@localhost, n4@localhost]}, 4 | {sync_nodes_timeout, 15000}]}, 5 | {bsc, [{frequencies, [1,2,3,4,5,6]}]}]. 6 | -------------------------------------------------------------------------------- /ch9/top_app.app: -------------------------------------------------------------------------------- 1 | {application, top_app, 2 | [{description, "Included Application Example"}, 3 | {vsn, "1.0"}, 4 | {modules, [top_app]}, 5 | {applications, [kernel, stdlib, sasl]}, 6 | {included_applications, [bsc]}, 7 | {start_phases, [{start, []}, {admin, []}, {stop, []}]}, 8 | {mod, {application_starter, [top_app, []]}} 9 | ] 10 | }. 11 | -------------------------------------------------------------------------------- /ch9/top_app/top_app.app: -------------------------------------------------------------------------------- 1 | {application, top_app, 2 | [{description, "Included Application Example"}, 3 | {vsn, "1.0"}, 4 | {modules, [top_app]}, 5 | {applications, [kernel, stdlib, sasl]}, 6 | {included_applications, [bsc]}, 7 | {start_phases, [{start, []}, {admin, []}, {stop, []}]}, 8 | {mod, {application_starter, [top_app, []]}} 9 | ] 10 | }. 11 | -------------------------------------------------------------------------------- /ch2/ex2.erl: -------------------------------------------------------------------------------- 1 | -module(ex2). 2 | -export([print_all/1,all_print/1]). 3 | 4 | print_all([]) -> 5 | io:format("\n"); 6 | print_all([X|Xs]) -> 7 | io:format("~p\t",[X]), 8 | print_all(Xs). 9 | 10 | all_print(Ys) -> 11 | case Ys of 12 | [] -> 13 | io:format("~n"); 14 | [X|Xs] -> 15 | io:format("~p\t",[X]), 16 | all_print(Xs) 17 | end. 18 | -------------------------------------------------------------------------------- /ch12/coffee-1.0/ebin/coffee.app: -------------------------------------------------------------------------------- 1 | {application, coffee, 2 | [{description, "Coffee Machine Controller"}, 3 | {vsn, "1.0"}, 4 | {modules, [coffee_fsm, 5 | coffee_sup, 6 | coffee_app, 7 | hw]}, 8 | {registered, [coffee_fsm, coffee_Sup]}, 9 | {applications, [kernel, stdlib, sasl]}, 10 | {env, []}, 11 | {mod, {coffee_app, []}}]}. -------------------------------------------------------------------------------- /ch12/coffee-1.1/ebin/coffee.app: -------------------------------------------------------------------------------- 1 | {application, coffee, 2 | [{description, "Coffee Machine Controller"}, 3 | {vsn, "1.1"}, 4 | {modules, [coffee_fsm, 5 | coffee_sup, 6 | coffee_app, 7 | hw]}, 8 | {registered, [coffee_fsm, coffee_Sup]}, 9 | {applications, [kernel, stdlib, sasl]}, 10 | {env, []}, 11 | {mod, {coffee_app, []}}]}. -------------------------------------------------------------------------------- /ch7/crash_example.erl: -------------------------------------------------------------------------------- 1 | -module(crash_example). 2 | -behaviour(gen_event). 3 | -export([init/1, terminate/2, handle_event/2]). 4 | 5 | init(normal) -> {ok, []}; 6 | init(return) -> error; 7 | init(ok) -> ok; 8 | init(crash) -> exit(crash). 9 | 10 | terminate(_Reason, _LoopData) -> ok. 11 | 12 | handle_event(crash, _LoopData) -> 1/0; 13 | handle_event(return, _LoopData) -> error. 14 | -------------------------------------------------------------------------------- /ch9/top_app/top_app.erl: -------------------------------------------------------------------------------- 1 | -module(top_app). 2 | -behaviour(application). 3 | -export([start/2, start_phase/3, stop/1]). 4 | 5 | start(_Type, _Args) -> 6 | {ok, _Pid} = bsc_sup:start_link(). 7 | 8 | start_phase(StartPhase, StartType, Args) -> 9 | io:format("top_app:start_phase(~p,~p,~p).~n", 10 | [StartPhase, StartType, Args]). 11 | 12 | stop(_Data) -> 13 | ok. 14 | -------------------------------------------------------------------------------- /ch10/mutex_sup.erl: -------------------------------------------------------------------------------- 1 | -module(mutex_sup). 2 | -export([start_link/0, init/1]). 3 | 4 | 5 | start_link() -> 6 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 7 | 8 | init(_) -> 9 | {ok, {{one_for_one, 5, 3600}, []}}. 10 | 11 | % MutexChildSpec = {mutex, 12 | % {mutex, start_link, [printer]}, 13 | % transient, 14 | % 5000, 15 | % [mutex]}, 16 | -------------------------------------------------------------------------------- /ch2/echo.erl: -------------------------------------------------------------------------------- 1 | -module(echo). 2 | -export([go/0, loop/0]). 3 | 4 | go() -> 5 | Pid = spawn(echo, loop, []), 6 | Pid ! {self(), hello}, 7 | receive 8 | {Pid, Msg} -> 9 | io:format("~w~n",[Msg]) 10 | end, 11 | Pid ! stop. 12 | 13 | loop() -> 14 | receive 15 | {From, Msg} -> 16 | From ! {self(), Msg}, 17 | loop(); 18 | stop -> 19 | ok 20 | end. 21 | -------------------------------------------------------------------------------- /ch9/top_app.erl: -------------------------------------------------------------------------------- 1 | -module(top_app). 2 | -behaviour(application). 3 | -export([start/2, start_phase/3, stop/1]). 4 | 5 | start(_StartType, _StartArgs) -> 6 | {ok, _Pid} = bsc_sup:start_link(). 7 | 8 | start_phase(StartPhase, StartType, Args) -> 9 | io:format("top_app:start_phase(~p,~p,~p).~n", 10 | [StartPhase, StartType, Args]). 11 | 12 | stop(_Data) -> 13 | ok. 14 | -------------------------------------------------------------------------------- /ch2/echo_link.erl: -------------------------------------------------------------------------------- 1 | -module(echo_link). 2 | -export([go/0, loop/0]). 3 | 4 | go() -> 5 | Pid = spawn_link(echo, loop, []), 6 | Pid ! {self(), hello}, 7 | receive 8 | {Pid, Msg} -> 9 | io:format("~w~n",[Msg]) 10 | end, 11 | Pid ! stop. 12 | 13 | loop() -> 14 | receive 15 | {From, Msg} -> 16 | From ! {self(), Msg}, 17 | loop(); 18 | stop -> 19 | ok 20 | end. 21 | -------------------------------------------------------------------------------- /ch4/from.erl: -------------------------------------------------------------------------------- 1 | -module(from). 2 | -behaviour(gen_server). 3 | -export([init/1, handle_call/3]). 4 | 5 | init(Sum) -> 6 | {ok, Sum}. 7 | 8 | handle_call({add, Data}, From, Sum) -> 9 | gen_server:reply(From, ok), 10 | timer:sleep(1000), 11 | NewSum = add(Data, Sum), 12 | io:format("From:~p, Sum:~p~n",[From, NewSum]), 13 | {noreply, NewSum}. 14 | 15 | add(Data, Sum) -> 16 | Data + Sum. 17 | -------------------------------------------------------------------------------- /ch9/start_phases/bsc.erl: -------------------------------------------------------------------------------- 1 | -module(bsc). 2 | -behaviour(application). 3 | 4 | %% Application callbacks 5 | -export([start/2, start_phase/3, stop/1]). 6 | 7 | start(_StartType, _StartArgs) -> 8 | bsc_sup:start_link(). 9 | 10 | start_phase(StartPhase, StartType, Args) -> 11 | io:format("bsc:start_phase(~p,~p,~p).~n", 12 | [StartPhase, StartType, Args]). 13 | 14 | stop(_Data) -> 15 | ok. 16 | -------------------------------------------------------------------------------- /ch6/erlang/hw.erl: -------------------------------------------------------------------------------- 1 | -module(hw). 2 | -compile(export_all). 3 | 4 | display(Str, Arg) -> io:format("Display:" ++ Str ++ "~n", Arg). 5 | return_change(Payment) -> io:format("Machine:Returned ~w in change~n",[Payment]). 6 | drop_cup() -> io:format("Machine:Dropped Cup.~n"). 7 | prepare(Type) -> io:format("Machine:Preparing ~p.~n",[Type]). 8 | reboot() -> io:format("Machine:Rebooted Hardware~n"). 9 | -------------------------------------------------------------------------------- /ch6/otp/hw.erl: -------------------------------------------------------------------------------- 1 | -module(hw). 2 | -compile(export_all). 3 | 4 | display(Str, Arg) -> io:format("Display:" ++ Str ++ "~n", Arg). 5 | return_change(Payment) -> io:format("Machine:Returned ~w in change~n",[Payment]). 6 | drop_cup() -> io:format("Machine:Dropped Cup.~n"). 7 | prepare(Type) -> io:format("Machine:Preparing ~p.~n",[Type]). 8 | reboot() -> io:format("Machine:Rebooted Hardware~n"). 9 | -------------------------------------------------------------------------------- /ch8/erlang/hw.erl: -------------------------------------------------------------------------------- 1 | -module(hw). 2 | -compile(export_all). 3 | 4 | display(Str, Arg) -> io:format("Display:" ++ Str ++ "~n", Arg). 5 | return_change(Payment) -> io:format("Machine:Returned ~w in change~n",[Payment]). 6 | drop_cup() -> io:format("Machine:Dropped Cup.~n"). 7 | prepare(Type) -> io:format("Machine:Preparing ~p.~n",[Type]). 8 | reboot() -> io:format("Machine:Rebooted Hardware~n"). 9 | -------------------------------------------------------------------------------- /ch11/bsc/ebin/bsc.app: -------------------------------------------------------------------------------- 1 | {application, bsc, 2 | [{description, "Base Station Controller"}, 3 | {vsn, "1.0"}, 4 | {modules, [bsc, bsc_sup, frequency, freq_overload, logger, 5 | % hlr, 6 | simple_phone_sup, phone_fsm]}, 7 | {registered, [bsc_sup, frequency, frequency_sup, freq_overload, simple_phone_sup]}, 8 | {applications, [kernel, stdlib, sasl]}, 9 | {env, []}, 10 | {mod, {bsc, []}}]}. 11 | -------------------------------------------------------------------------------- /ch2/queens.erl: -------------------------------------------------------------------------------- 1 | -module(queens). 2 | -export([queens/1]). 3 | 4 | queens(0) -> [[]]; 5 | queens(N) -> 6 | [[Row | Columns] || 7 | Columns <- queens(N-1), 8 | Row <- [1,2,3,4,5,6,7,8] -- Columns, % -- is the list difference operator. 9 | safe(Row, Columns, 1)]. 10 | 11 | safe(_Row, [], _N) -> true; 12 | safe(Row, [Column|Columns], N) -> 13 | (Row /= Column + N) andalso (Row /= Column - N) andalso 14 | safe(Row, Columns, (N+1)). 15 | -------------------------------------------------------------------------------- /ch9/start_phases/bsc.app: -------------------------------------------------------------------------------- 1 | {application, bsc, 2 | [{description, "Base Station Controller"}, 3 | {vsn, "1.0"}, 4 | {modules, [bsc, bsc_sup, frequency, freq_overload, logger, simple_phone_sup, phone_fsm]}, 5 | {registered, [bsc_sup, frequency, frequency_sup, overload, simple_phone_sup]}, 6 | {applications, [kernel, stdlib, sasl]}, 7 | {start_phases, [{init, []}, {admin, []}, {oper, []}]}, 8 | {env, []}, 9 | {mod, {bsc, []}}]}. 10 | -------------------------------------------------------------------------------- /ch10/tcp_print.erl: -------------------------------------------------------------------------------- 1 | -module(tcp_print). 2 | -export([init_request/0, get_request/2, stop_request/2]). 3 | -behaviour(tcp_wrapper). 4 | 5 | 6 | init_request() -> 7 | io:format("Receiving Data~n."), 8 | {ok,[]}. 9 | get_request(Data, Buffer)-> 10 | io:format("."), 11 | {ok, [Data|Buffer]}. 12 | stop_request(_Reason, Buffer) -> 13 | io:format("~n"), 14 | io:format(lists:reverse(Buffer)), 15 | io:format("~n"). 16 | 17 | -------------------------------------------------------------------------------- /ch6/earth.erl: -------------------------------------------------------------------------------- 1 | -module(earth). 2 | -export([start/0, init/0]). 3 | 4 | start() -> 5 | spawn(?MODULE, init, []). 6 | 7 | init() -> 8 | create_earth(), 9 | day(). 10 | 11 | day() -> 12 | receive 13 | eclipse -> day(); 14 | sunset -> night() 15 | end. 16 | 17 | night() -> 18 | receive 19 | sunrise -> 20 | make_roosters_crow(), 21 | day() 22 | end. 23 | 24 | create_earth() -> ok. 25 | make_roosters_crow() -> ok. 26 | -------------------------------------------------------------------------------- /ch6/erlang/earth.erl: -------------------------------------------------------------------------------- 1 | -module(earth). 2 | -export([start/0, init/0]). 3 | 4 | start() -> 5 | spawn(?MODULE, init, []). 6 | 7 | init() -> 8 | create_earth(), 9 | day(). 10 | 11 | day() -> 12 | receive 13 | eclipse -> day(); 14 | sunset -> night() 15 | end. 16 | 17 | night() -> 18 | receive 19 | sunrise -> 20 | make_roosters_crow(), 21 | day() 22 | end. 23 | 24 | create_earth() -> ok. 25 | make_roosters_crow() -> ok. 26 | -------------------------------------------------------------------------------- /ch12/coffee-1.0/src/coffee_app.erl: -------------------------------------------------------------------------------- 1 | -module(coffee_app). 2 | 3 | -behaviour(application). 4 | 5 | %% Application callbacks 6 | -export([start/2, stop/1]). 7 | -vsn('1.0'). 8 | 9 | %% =================================================================== 10 | %% Application callbacks 11 | %% =================================================================== 12 | 13 | start(_StartType, _StartArgs) -> 14 | coffee_sup:start_link(). 15 | 16 | stop(_State) -> 17 | ok. 18 | -------------------------------------------------------------------------------- /ch12/coffee-1.1/src/coffee_app.erl: -------------------------------------------------------------------------------- 1 | -module(coffee_app). 2 | 3 | -behaviour(application). 4 | 5 | %% Application callbacks 6 | -export([start/2, stop/1]). 7 | -vsn('1.0'). 8 | 9 | %% =================================================================== 10 | %% Application callbacks 11 | %% =================================================================== 12 | 13 | start(_StartType, _StartArgs) -> 14 | coffee_sup:start_link(). 15 | 16 | stop(_State) -> 17 | ok. 18 | -------------------------------------------------------------------------------- /ch4/ping.erl: -------------------------------------------------------------------------------- 1 | -module(ping). 2 | -behaviour(gen_server). 3 | 4 | -export([init/1, handle_call/3, handle_info/2]). 5 | -define(TIMEOUT, 5000). 6 | 7 | init(_Args) -> 8 | {ok, undefined, ?TIMEOUT}. 9 | 10 | handle_call(start, _From, LoopData) -> 11 | {reply, started, LoopData, ?TIMEOUT}; 12 | handle_call(pause, _From, LoopData) -> 13 | {reply, paused, LoopData}. 14 | 15 | handle_info(timeout, LoopData) -> 16 | {_Hour,_Min,Sec} = time(), 17 | io:format("~2.w~n",[Sec]), 18 | {noreply, LoopData, ?TIMEOUT}. 19 | -------------------------------------------------------------------------------- /ch11/bsc/src/frequency_sup.erl: -------------------------------------------------------------------------------- 1 | -module(frequency_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, init/1]). 5 | -export([stop/0]). 6 | 7 | start_link() -> 8 | supervisor:start_link({local,?MODULE},?MODULE, []). 9 | 10 | stop() -> exit(whereis(?MODULE), shutdown). 11 | 12 | init(_) -> 13 | ChildSpecList = [child(freq_overload), child(frequency)], 14 | {ok,{{rest_for_one, 2, 3600}, ChildSpecList}}. 15 | 16 | child(Module) -> 17 | {Module, {Module, start_link, []}, 18 | permanent, 2000, worker, [Module]}. 19 | -------------------------------------------------------------------------------- /ch8/otp/phone_test.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2013,2016 Francesco Cesarini 2 | -module(phone_test). 3 | -compile(export_all). 4 | 5 | start(Num, Calls) -> 6 | catch frequency_sup2:start_link(), 7 | [phone_fsm:start_link(X) || X <- lists:seq(1,Num)], 8 | call(Calls, Num). 9 | 10 | call(0,_) -> ok; 11 | call(X, Num) -> 12 | MSISDN1 = random:uniform(Num), 13 | MSISDN2 = random:uniform(Num), 14 | {ok, Pid1} = hlr:lookup_id(MSISDN1), 15 | phone:outbound(Pid1, MSISDN2), 16 | timer:sleep(100), 17 | call(X-1, Num). 18 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/frequency_sup.erl: -------------------------------------------------------------------------------- 1 | -module(frequency_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, init/1]). 5 | -export([stop/0]). 6 | 7 | start_link() -> 8 | supervisor:start_link({local,?MODULE},?MODULE, []). 9 | 10 | stop() -> exit(whereis(?MODULE), shutdown). 11 | 12 | init(_) -> 13 | ChildSpecList = [child(overload), child(frequency)], 14 | {ok,{{rest_for_one, 2, 3600}, ChildSpecList}}. 15 | 16 | child(Module) -> 17 | {Module, {Module, start_link, []}, 18 | permanent, 2000, worker, [Module]}. 19 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/phone_test.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2013,2016 Francesco Cesarini 2 | -module(phone_test). 3 | -compile(export_all). 4 | 5 | start(Num, Calls) -> 6 | catch frequency_sup2:start_link(), 7 | [phone_fsm:start_link(X) || X <- lists:seq(1,Num)], 8 | call(Calls, Num). 9 | 10 | call(0,_) -> ok; 11 | call(X, Num) -> 12 | MSISDN1 = random:uniform(Num), 13 | MSISDN2 = random:uniform(Num), 14 | {ok, Pid1} = hlr:lookup_id(MSISDN1), 15 | phone:outbound(Pid1, MSISDN2), 16 | timer:sleep(100), 17 | call(X-1, Num). 18 | -------------------------------------------------------------------------------- /ch12/relup: -------------------------------------------------------------------------------- 1 | {"1.1", 2 | [{"1.0",[], 3 | [{load_object_code,{coffee,"1.1",[coffee_fsm]}}, 4 | point_of_no_return, 5 | {suspend,[coffee_fsm]}, 6 | {load,{coffee_fsm,brutal_purge,brutal_purge}}, 7 | {code_change,up,[{coffee_fsm,{}}]}, 8 | {resume,[coffee_fsm]}]}], 9 | [{"1.0",[], 10 | [{load_object_code,{coffee,"1.0",[coffee_fsm]}}, 11 | point_of_no_return, 12 | {suspend,[coffee_fsm]}, 13 | {code_change,down,[{coffee_fsm,{}}]}, 14 | {load,{coffee_fsm,brutal_purge,brutal_purge}}, 15 | {resume,[coffee_fsm]}]}]}. 16 | -------------------------------------------------------------------------------- /ch8/otp/bsc_sup.erl: -------------------------------------------------------------------------------- 1 | -module(bsc_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, init/1]). 5 | -export([stop/0]). 6 | 7 | start_link() -> 8 | supervisor:start_link({local,?MODULE}, ?MODULE, []). 9 | 10 | stop() -> exit(whereis(?MODULE), shutdown). 11 | 12 | init(_) -> 13 | ChildSpecList = [child(freq_overload), child(frequency), child(simple_phone_sup)], 14 | {ok,{{rest_for_one, 2, 3600}, ChildSpecList}}. 15 | 16 | child(Module) -> 17 | {Module, {Module, start_link, []}, 18 | permanent, 2000, worker, [Module]}. 19 | -------------------------------------------------------------------------------- /ch11/bsc/src/bsc_sup.erl: -------------------------------------------------------------------------------- 1 | -module(bsc_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, init/1]). 5 | -export([stop/0]). 6 | 7 | start_link() -> 8 | supervisor:start_link({local,?MODULE}, ?MODULE, []). 9 | 10 | stop() -> exit(whereis(?MODULE), shutdown). 11 | 12 | init(_) -> 13 | ChildSpecList = [child(freq_overload), child(frequency), child(simple_phone_sup)], 14 | {ok,{{rest_for_one, 2, 3600}, ChildSpecList}}. 15 | 16 | child(Module) -> 17 | {Module, {Module, start_link, []}, 18 | permanent, 2000, worker, [Module]}. 19 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/ebin/bsc.app: -------------------------------------------------------------------------------- 1 | {application, bsc, 2 | [{description, "Base Station Controller"}, 3 | {vsn, "1.0"}, 4 | {modules, [bsc, 5 | bsc_sup, 6 | frequency, 7 | freq_overload, 8 | logger, 9 | simple_phone_sup, 10 | phone_fsm 11 | ]}, 12 | {registered, [bsc_sup, frequency, frequency_sup, overload, simple_phone_sup]}, 13 | {applications, [kernel, stdlib, sasl]}, 14 | %% {start_phases, [{init, []}, {admin, []}, {oper, []}]}, 15 | {env, []}, 16 | {mod, {bsc, []}}]}. 17 | -------------------------------------------------------------------------------- /ch8/otp/freq_overload.erl: -------------------------------------------------------------------------------- 1 | -module(freq_overload). 2 | -export([start_link/0, add/2, delete/2]). 3 | -export([no_frequency/0, frequency_available/0, frequency_denied/0]). 4 | 5 | start_link() -> gen_event:start_link({local, ?MODULE}). 6 | 7 | no_frequency() -> gen_event:notify(?MODULE, {set_alarm, {no_frequency, self()}}). 8 | frequency_available() -> gen_event:notify(?MODULE, {clear_alarm, no_frequency}). 9 | frequency_denied() -> gen_event:notify(?MODULE, {event, {frequency_denied, self()}}). 10 | 11 | add(M,A) -> gen_event:add_sup_handler(?MODULE, M, A). 12 | delete(M,A) -> gen_event:delete_handler(?MODULE,M,A). 13 | -------------------------------------------------------------------------------- /ch9/combining/bsc.erl: -------------------------------------------------------------------------------- 1 | -module(bsc). 2 | -behaviour(application). 3 | -behaviour(supervisor). 4 | 5 | -export([start/2, stop/1, init/1]). 6 | 7 | %% Application callbacks 8 | 9 | start(_Type, _Args) -> 10 | {ok, _Pid} = supervisor:start_link({local,?MODULE}, ?MODULE, []). 11 | 12 | stop(_Data) -> 13 | ok. 14 | 15 | %% Supervisor callbacks 16 | 17 | init(_) -> 18 | ChildSpecList = [child(freq_overload), child(frequency), child(simple_phone_sup)], 19 | {ok,{{rest_for_one, 2, 3600}, ChildSpecList}}. 20 | 21 | child(Module) -> 22 | {Module, {Module, start_link, []}, permanent, 2000, worker, [Module]}. 23 | -------------------------------------------------------------------------------- /ch12/erlang/hw.erl: -------------------------------------------------------------------------------- 1 | %%% File : hardware.erl 2 | %%% Author : 3 | %%% Description : Coffee Automoata stub 4 | %%% Created : 21 Mar 2004 by 5 | 6 | -module(hw). 7 | -compile(export_all). 8 | 9 | display(Str, Arg) -> io:format("Display:" ++ Str ++ "~n", Arg). 10 | return_change(Payment) -> io:format("Machine:Returned ~w in change~n",[Payment]). 11 | drop_cup() -> io:format("Machine:Dropped Cup.~n"). 12 | prepare(Type) -> io:format("Machine:Preparing ~p.~n",[Type]). 13 | reboot() -> io:format("Machine:Rebooted Hardware~n"). 14 | 15 | -------------------------------------------------------------------------------- /ch12/coffee-1.0/src/hw.erl: -------------------------------------------------------------------------------- 1 | %%% File : hardware.erl 2 | %%% Author : 3 | %%% Description : Coffee Automoata stub 4 | %%% Created : 21 Mar 2004 by 5 | 6 | -module(hw). 7 | -compile(export_all). 8 | 9 | display(Str, Arg) -> io:format("Display:" ++ Str ++ "~n", Arg). 10 | return_change(Payment) -> io:format("Machine:Returned ~w in change~n",[Payment]). 11 | drop_cup() -> io:format("Machine:Dropped Cup.~n"). 12 | prepare(Type) -> io:format("Machine:Preparing ~p.~n",[Type]). 13 | reboot() -> io:format("Machine:Rebooted Hardware~n"). 14 | 15 | -------------------------------------------------------------------------------- /ch12/coffee-1.1/src/hw.erl: -------------------------------------------------------------------------------- 1 | %%% File : hardware.erl 2 | %%% Author : 3 | %%% Description : Coffee Automoata stub 4 | %%% Created : 21 Mar 2004 by 5 | 6 | -module(hw). 7 | -compile(export_all). 8 | 9 | display(Str, Arg) -> io:format("Display:" ++ Str ++ "~n", Arg). 10 | return_change(Payment) -> io:format("Machine:Returned ~w in change~n",[Payment]). 11 | drop_cup() -> io:format("Machine:Dropped Cup.~n"). 12 | prepare(Type) -> io:format("Machine:Preparing ~p.~n",[Type]). 13 | reboot() -> io:format("Machine:Rebooted Hardware~n"). 14 | 15 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/bsc_sup.erl: -------------------------------------------------------------------------------- 1 | -module(bsc_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, init/1]). 5 | -export([stop/0]). 6 | 7 | start_link() -> 8 | {ok, Pid} = supervisor:start_link({local,?MODULE},?MODULE, []), 9 | freq_overload:add(counters, {}), 10 | freq_overload:add(logger, {file, "log"}), 11 | {ok, Pid}. 12 | 13 | stop() -> exit(whereis(?MODULE), shutdown). 14 | 15 | init(_) -> 16 | ChildSpecList = [child(freq_overload), child(frequency), child(simple_phone_sup)], 17 | {ok,{{rest_for_one, 2, 3600}, ChildSpecList}}. 18 | 19 | child(Module) -> 20 | {Module, {Module, start_link, []}, 21 | permanent, 2000, worker, [Module]}. 22 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/bsc.erl: -------------------------------------------------------------------------------- 1 | -module(bsc). 2 | -behaviour(application). 3 | -behaviour(supervisor). 4 | 5 | -export([start/2, stop/1, init/1]). 6 | 7 | start(_StartType, _StartArgs) -> 8 | {ok, Pid} = supervisor:start_link({local,?MODULE}, ?MODULE, []), 9 | freq_overload:add(counters, {}), 10 | freq_overload:add(logger, {file, "log"}), 11 | {ok, Pid}. 12 | 13 | stop(_Data) -> 14 | ok. 15 | 16 | %% Supervisor callbacks. 17 | 18 | init(_) -> 19 | ChildSpecList = [child(freq_overload), child(frequency), child(simple_phone_sup)], 20 | {ok,{{rest_for_one, 2, 3600}, ChildSpecList}}. 21 | 22 | child(Module) -> 23 | {Module, {Module, start_link, []}, 24 | permanent, 2000, worker, [Module]}. 25 | -------------------------------------------------------------------------------- /ch12/coffee-1.0/src/coffee_sup.erl: -------------------------------------------------------------------------------- 1 | -module(coffee_sup). 2 | -behaviour(supervisor). 3 | -export([start_link/0, init/1]). 4 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 5 | -vsn('1.0'). 6 | 7 | %% =================================================================== 8 | %% API functions 9 | %% =================================================================== 10 | 11 | start_link() -> 12 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 13 | 14 | %% =================================================================== 15 | %% Supervisor callbacks 16 | %% =================================================================== 17 | 18 | init([]) -> 19 | {ok, { {one_for_one, 5, 10}, [?CHILD(coffee_fsm, worker)]}}. 20 | 21 | 22 | -------------------------------------------------------------------------------- /ch12/coffee-1.1/src/coffee_sup.erl: -------------------------------------------------------------------------------- 1 | -module(coffee_sup). 2 | -behaviour(supervisor). 3 | -export([start_link/0, init/1]). 4 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 5 | -vsn('1.0'). 6 | 7 | %% =================================================================== 8 | %% API functions 9 | %% =================================================================== 10 | 11 | start_link() -> 12 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 13 | 14 | %% =================================================================== 15 | %% Supervisor callbacks 16 | %% =================================================================== 17 | 18 | init([]) -> 19 | {ok, { {one_for_one, 5, 10}, [?CHILD(coffee_fsm, worker)]}}. 20 | 21 | 22 | -------------------------------------------------------------------------------- /ch9/bsc.erl: -------------------------------------------------------------------------------- 1 | -module(bsc). 2 | -behaviour(application). 3 | -behaviour(supervisor). 4 | 5 | -export([start/2, start_phase/3, stop/1, init/1]). 6 | 7 | start(_Type, _Args) -> 8 | {ok, Pid} = supervisor:start_link({local,?MODULE},?MODULE, []). 9 | 10 | start_phase(Phase, Type, Args) -> 11 | io:format("bsc:start_phase(~p,~p,~p).",[Phase, Type, Args]). 12 | 13 | stop(_Data) -> 14 | ok. 15 | 16 | %% Supervisor callbacks. 17 | 18 | init(_) -> 19 | ChildSpecList = [child(freq_overload), 20 | child(frequency), 21 | child(simple_phone_sup)], 22 | {ok,{{rest_for_one, 2, 3600}, ChildSpecList}}. 23 | 24 | child(Module) -> 25 | {Module, {Module, start_link, []}, 26 | permanent, 2000, worker, [Module]}. 27 | -------------------------------------------------------------------------------- /ch7/freq_overload.erl: -------------------------------------------------------------------------------- 1 | -module(freq_overload). 2 | -export([start_link/0, add/2, delete/2]). 3 | -export([no_frequency/0, frequency_available/0, frequency_denied/0]). 4 | 5 | start_link() -> 6 | case gen_event:start_link({local, ?MODULE}) of 7 | {ok, Pid} -> 8 | add(counters, {}), 9 | add(logger, {file, "log"}), 10 | {ok, Pid}; 11 | Error -> 12 | Error 13 | end. 14 | 15 | no_frequency() -> gen_event:notify(?MODULE, {set_alarm, {no_frequency, self()}}). 16 | frequency_available() -> gen_event:notify(?MODULE, {clear_alarm, no_frequency}). 17 | frequency_denied() -> gen_event:notify(?MODULE, {event, {frequency_denied, self()}}). 18 | 19 | add(M,A) -> gen_event:add_sup_handler(?MODULE, M, A). 20 | delete(M,A) -> gen_event:delete_handler(?MODULE,M,A). 21 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/freq_overload.erl: -------------------------------------------------------------------------------- 1 | -module(freq_overload). 2 | -export([start_link/0, add/2, delete/2]). 3 | -export([no_frequency/0, frequency_available/0, frequency_denied/0]). 4 | 5 | start_link() -> 6 | case gen_event:start_link({local, ?MODULE}) of 7 | {ok, Pid} -> 8 | add(counters, {}), 9 | add(logger, {file, "log"}), 10 | {ok, Pid}; 11 | Error -> 12 | Error 13 | end. 14 | 15 | no_frequency() -> gen_event:notify(?MODULE, {set_alarm, {no_frequency, self()}}). 16 | frequency_available() -> gen_event:notify(?MODULE, {clear_alarm, no_frequency}). 17 | frequency_denied() -> gen_event:notify(?MODULE, {event, {frequency_denied, self()}}). 18 | 19 | add(M,A) -> gen_event:add_sup_handler(?MODULE, M, A). 20 | delete(M,A) -> gen_event:delete_handler(?MODULE,M,A). 21 | -------------------------------------------------------------------------------- /ch11/bsc/src/freq_overload.erl: -------------------------------------------------------------------------------- 1 | -module(freq_overload). 2 | -export([start_link/0, add/2, delete/2]). 3 | -export([no_frequency/0, frequency_available/0, frequency_denied/0]). 4 | 5 | start_link() -> 6 | case gen_event:start_link({local, ?MODULE}) of 7 | {ok, Pid} -> 8 | add(counters, {}), 9 | add(logger, {file, "log"}), 10 | {ok, Pid}; 11 | Error -> 12 | Error 13 | end. 14 | 15 | 16 | no_frequency() -> gen_event:notify(?MODULE, {set_alarm, {no_frequency, self()}}). 17 | frequency_available() -> gen_event:notify(?MODULE, {clear_alarm, no_frequency}). 18 | frequency_denied() -> gen_event:notify(?MODULE, {event, {frequency_denied, self()}}). 19 | 20 | add(M,A) -> gen_event:add_sup_handler(?MODULE, M, A). 21 | delete(M,A) -> gen_event:delete_handler(?MODULE,M,A). 22 | -------------------------------------------------------------------------------- /ch10/mutex0.erl: -------------------------------------------------------------------------------- 1 | -module(mutex0). 2 | 3 | -export([start_link/1, init/1, stop/1]). 4 | -export([wait/1, signal/1]). 5 | 6 | 7 | wait(Name) -> 8 | Name ! {wait,self()}, 9 | Mutex = whereis(Name), 10 | receive 11 | {Mutex,ok} -> ok 12 | end. 13 | 14 | signal(Name) -> 15 | Name ! {signal,self()}, 16 | ok. 17 | 18 | start_link(Name) -> 19 | spawn_link(?MODULE, init, [Name]). 20 | 21 | stop(Name) -> Name ! stop. 22 | 23 | init(Name) -> 24 | register(Name, self()), 25 | process_flag(trap_exit, true), 26 | free(Name). 27 | 28 | 29 | free(Name) -> 30 | receive 31 | {wait,Pid} -> %% The user requests. 32 | Pid ! {self(),ok}, 33 | busy(Pid, Name); 34 | stop -> 35 | ok 36 | end. 37 | 38 | busy(Pid, Name) -> 39 | receive 40 | {signal,Pid} -> 41 | free(Name) 42 | end. 43 | -------------------------------------------------------------------------------- /ch3/behavior/server.erl: -------------------------------------------------------------------------------- 1 | -module(server). 2 | -export([start/2, stop/1, call/2]). 3 | -export([init/2]). 4 | 5 | start(Name, Args) -> 6 | register(Name, spawn(server, init, [Name, Args])). 7 | 8 | stop(Name) -> 9 | Name ! {stop, self()}, 10 | receive {reply, Reply} -> Reply end. 11 | 12 | init(Mod, Args) -> 13 | State = Mod:init(Args), 14 | loop(Mod, State). 15 | 16 | 17 | call(Name, Msg) -> 18 | Name ! {request, self(), Msg}, 19 | receive {reply, Reply} -> Reply end. 20 | 21 | reply(To, Reply) -> 22 | To ! {reply, Reply}. 23 | 24 | loop(Mod, State) -> 25 | receive 26 | {request, From, Msg} -> 27 | {NewState, Reply} = Mod:handle(Msg, State), 28 | reply(From, Reply), 29 | loop(Mod, NewState); 30 | {stop, From} -> 31 | Reply = Mod:terminate(State), 32 | reply(From, Reply) 33 | end. 34 | -------------------------------------------------------------------------------- /ch3/pattern.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2015-2016 Francesco Cesarini 2 | 3 | -module(pattern). 4 | -export([start/1, init/1]). 5 | 6 | start(Args) -> % Start the server. 7 | spawn(server, init, [Args]). 8 | 9 | init(Args) -> % Initialize the internal process state 10 | State = initialize_state(Args), 11 | loop(State). 12 | 13 | loop(State) -> % Receive and handle messages. 14 | receive 15 | {handle, Msg} -> 16 | NewState = handle(Msg, State), 17 | loop(NewState); 18 | stop -> 19 | terminate(State) % Stop the process. 20 | end. 21 | 22 | terminate(State) -> % Cleanup prior to termination. 23 | clean_up(State). 24 | 25 | %% TODO: Fill in specific server code 26 | handle(_, _) -> ok. 27 | initialize_state(_) -> ok. 28 | clean_up(_) -> ok. 29 | -------------------------------------------------------------------------------- /ch7/counters.erl: -------------------------------------------------------------------------------- 1 | -module(counters). 2 | -behaviour(gen_event). 3 | -export([init/1, terminate/2, handle_event/2, handle_info/2]). 4 | -export([get_counters/1, handle_call/2]). 5 | 6 | 7 | get_counters(Pid) -> 8 | gen_event:call(Pid, counters, get_counters). 9 | 10 | init(_) -> 11 | TableId = ets:new(counters, []), 12 | {ok, TableId}. 13 | 14 | terminate(_Reason, TableId) -> 15 | Counters = ets:tab2list(TableId), 16 | ets:delete(TableId), 17 | {counters, Counters}. 18 | 19 | handle_event(Event, TableId) -> 20 | try ets:update_counter(TableId, Event, 1) of 21 | _ok -> {ok, TableId} 22 | catch 23 | error:_ -> ets:insert(TableId, {Event, 1}), 24 | {ok, TableId} 25 | end. 26 | 27 | handle_call(get_counters, TableId) -> 28 | {ok, {counters, ets:tab2list(TableId)}, TableId}. 29 | 30 | handle_info(_, TableId) -> 31 | {ok, TableId}. 32 | -------------------------------------------------------------------------------- /ch11/bsc/src/counters.erl: -------------------------------------------------------------------------------- 1 | -module(counters). 2 | -behaviour(gen_event). 3 | -export([init/1, terminate/2, handle_event/2, handle_info/2]). 4 | -export([get_counters/1, handle_call/2]). 5 | 6 | get_counters(Pid) -> 7 | gen_event:call(Pid, counters, get_counters). 8 | 9 | init(_) -> 10 | TableId = ets:new(counters, []), 11 | {ok, TableId}. 12 | 13 | terminate(_Reason, TableId) -> 14 | Counters = ets:tab2list(TableId), 15 | ets:delete(TableId), 16 | {counters, Counters}. 17 | 18 | handle_event(Event, TableId) -> 19 | try ets:update_counter(TableId, Event, 1) of 20 | _ok -> {ok, TableId} 21 | catch 22 | error:_ -> ets:insert(TableId, {Event, 1}), 23 | {ok, TableId} 24 | end. 25 | 26 | handle_call(get_counters, TableId) -> 27 | {ok, {counters, ets:tab2list(TableId)}, TableId}. 28 | 29 | handle_info(_, TableId) -> 30 | {ok, TableId}. 31 | -------------------------------------------------------------------------------- /ch8/otp/counters.erl: -------------------------------------------------------------------------------- 1 | -module(counters). 2 | -behaviour(gen_event). 3 | -export([init/1, terminate/2, handle_event/2, handle_info/2]). 4 | -export([get_counters/1, handle_call/2]). 5 | 6 | 7 | get_counters(Pid) -> 8 | gen_event:call(Pid, counters, get_counters). 9 | 10 | init(_) -> 11 | TableId = ets:new(counters, []), 12 | {ok, TableId}. 13 | 14 | terminate(_Reason, TableId) -> 15 | Counters = ets:tab2list(TableId), 16 | ets:delete(TableId), 17 | {counters, Counters}. 18 | 19 | handle_event(Event, TableId) -> 20 | try ets:update_counter(TableId, Event, 1) of 21 | _ok -> {ok, TableId} 22 | catch 23 | error:_ -> ets:insert(TableId, {Event, 1}), 24 | {ok, TableId} 25 | end. 26 | 27 | handle_call(get_counters, TableId) -> 28 | {ok, {counters, ets:tab2list(TableId)}, TableId}. 29 | 30 | handle_info(_, TableId) -> 31 | {ok, TableId}. 32 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/counters.erl: -------------------------------------------------------------------------------- 1 | -module(counters). 2 | -behaviour(gen_event). 3 | -export([init/1, terminate/2, handle_event/2, handle_info/2]). 4 | -export([get_counters/1, handle_call/2]). 5 | 6 | get_counters(Pid) -> 7 | gen_event:call(Pid, counters, get_counters). 8 | 9 | init(_) -> 10 | TableId = ets:new(counters, []), 11 | {ok, TableId}. 12 | 13 | terminate(_Reason, TableId) -> 14 | Counters = ets:tab2list(TableId), 15 | ets:delete(TableId), 16 | {counters, Counters}. 17 | 18 | handle_event(Event, TableId) -> 19 | try ets:update_counter(TableId, Event, 1) of 20 | _ok -> {ok, TableId} 21 | catch 22 | error:_ -> ets:insert(TableId, {Event, 1}), 23 | {ok, TableId} 24 | end. 25 | 26 | handle_call(get_counters, TableId) -> 27 | {ok, {counters, ets:tab2list(TableId)}, TableId}. 28 | 29 | handle_info(_, TableId) -> 30 | {ok, TableId}. 31 | -------------------------------------------------------------------------------- /ch2/hlr.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2013-2016 Francesco Cesarini 2 | 3 | -module(hlr). 4 | -export([new/0, attach/1, detach/0, lookup_id/1, lookup_ms/1]). 5 | 6 | new() -> 7 | ets:new(msisdn2pid, [public, named_table]), 8 | ets:new(pid2msisdn, [public, named_table]), 9 | ok. 10 | 11 | attach(Ms) -> 12 | ets:insert(msisdn2pid, {Ms, self()}), 13 | ets:insert(pid2msisdn, {self(), Ms}). 14 | 15 | detach() -> 16 | case ets:lookup(pid2msisdn, self()) of 17 | [{Pid, Ms}] -> 18 | ets:delete(pid2msisdn, Pid), 19 | ets:delete(msisdn2pid, Ms); 20 | [] -> 21 | ok 22 | end. 23 | 24 | lookup_id(Ms) -> 25 | case ets:lookup(msisdn2pid, Ms) of 26 | [] -> {error, invalid}; 27 | [{Ms, Pid}] -> {ok, Pid} 28 | end. 29 | 30 | lookup_ms(Pid) -> 31 | case ets:lookup(pid2msisdn, Pid) of 32 | [] -> {error, invalid}; 33 | [{Pid, Ms}] -> {ok, Ms} 34 | end. 35 | -------------------------------------------------------------------------------- /ch6/exercise/hlr.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2013,2016 Francesco Cesarini 2 | -module(hlr). 3 | -export([new/0, attach/1, detach/0, lookup_id/1, lookup_ms/1]). 4 | 5 | new() -> 6 | ets:new(msisdn2pid, [public, named_table]), 7 | ets:new(pid2msisdn, [public, named_table]), 8 | ok. 9 | 10 | attach(Ms) -> 11 | ets:insert(msisdn2pid, {Ms, self()}), 12 | ets:insert(pid2msisdn, {self(), Ms}). 13 | 14 | detach() -> 15 | case ets:lookup(pid2msisdn, self()) of 16 | [{Pid, Ms}] -> 17 | ets:delete(pid2msisdn, Pid), 18 | ets:delete(msisdn2pid, Ms); 19 | [] -> 20 | ok 21 | end. 22 | 23 | 24 | lookup_id(Ms) -> 25 | case ets:lookup(msisdn2pid, Ms) of 26 | [] -> {error, invalid}; 27 | [{Ms, Pid}] -> {ok, Pid} 28 | end. 29 | 30 | lookup_ms(Pid) -> 31 | case ets:lookup(pid2msisdn, Pid) of 32 | [] -> {error, invalid}; 33 | [{Pid, Ms}] -> {ok, Ms} 34 | end. 35 | -------------------------------------------------------------------------------- /ch8/otp/hlr.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2013,2016 Francesco Cesarini 2 | -module(hlr). 3 | -export([new/0, attach/1, detach/0, lookup_id/1, lookup_ms/1]). 4 | 5 | new() -> 6 | ets:new(msisdn2pid, [public, named_table]), 7 | ets:new(pid2msisdn, [public, named_table]), 8 | ok. 9 | 10 | attach(Ms) -> 11 | detach(), 12 | ets:insert(msisdn2pid, {Ms, self()}), 13 | ets:insert(pid2msisdn, {self(), Ms}). 14 | 15 | detach() -> 16 | case ets:lookup(pid2msisdn, self()) of 17 | [{Pid, Ms}] -> 18 | ets:delete(pid2msisdn, Pid), 19 | ets:delete(msisdn2pid, Ms); 20 | [] -> 21 | ok 22 | end. 23 | 24 | lookup_id(Ms) -> 25 | case ets:lookup(msisdn2pid, Ms) of 26 | [] -> {error, invalid}; 27 | [{Ms, Pid}] -> {ok, Pid} 28 | end. 29 | 30 | lookup_ms(Pid) -> 31 | case ets:lookup(pid2msisdn, Pid) of 32 | [] -> {error, invalid}; 33 | [{Pid, Ms}] -> {ok, Ms} 34 | end. 35 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/hlr.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2013,2016 Francesco Cesarini 2 | -module(hlr). 3 | -export([new/0, attach/1, detach/0, lookup_id/1, lookup_ms/1]). 4 | 5 | new() -> 6 | ets:new(msisdn2pid, [public, named_table]), 7 | ets:new(pid2msisdn, [public, named_table]), 8 | ok. 9 | 10 | attach(Ms) -> 11 | detach(), 12 | ets:insert(msisdn2pid, {Ms, self()}), 13 | ets:insert(pid2msisdn, {self(), Ms}). 14 | 15 | detach() -> 16 | case ets:lookup(pid2msisdn, self()) of 17 | [{Pid, Ms}] -> 18 | ets:delete(pid2msisdn, Pid), 19 | ets:delete(msisdn2pid, Ms); 20 | [] -> 21 | ok 22 | end. 23 | 24 | lookup_id(Ms) -> 25 | case ets:lookup(msisdn2pid, Ms) of 26 | [] -> {error, invalid}; 27 | [{Ms, Pid}] -> {ok, Pid} 28 | end. 29 | 30 | lookup_ms(Pid) -> 31 | case ets:lookup(pid2msisdn, Pid) of 32 | [] -> {error, invalid}; 33 | [{Pid, Ms}] -> {ok, Ms} 34 | end. 35 | -------------------------------------------------------------------------------- /ch8/otp/simple_phone_sup.erl: -------------------------------------------------------------------------------- 1 | -module(simple_phone_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, attach_phone/1, detach_phone/1]). 5 | -export([init/1]). 6 | 7 | start_link() -> 8 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 9 | 10 | init([]) -> 11 | hlr:new(), 12 | {ok, {{simple_one_for_one, 10, 3600}, 13 | [{ms, {phone_fsm, start_link, []}, 14 | transient, 2000, worker, [phone_fsm]}]}}. 15 | 16 | 17 | %%Race condiiton risk. Device attached after we check but before we start 18 | 19 | attach_phone(Ms) -> 20 | case hlr:lookup_id(Ms) of 21 | {ok, _Pid} -> 22 | {error, attached}; 23 | _NotAttached -> 24 | supervisor:start_child(?MODULE, [Ms]) 25 | end. 26 | 27 | detach_phone(Ms) -> 28 | case hlr:lookup_id(Ms) of 29 | {ok, Pid} -> 30 | supervisor:terminate_child(?MODULE, Pid); 31 | _NotAttached -> 32 | {error, detached} 33 | end. 34 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/simple_phone_sup.erl: -------------------------------------------------------------------------------- 1 | -module(simple_phone_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, attach_phone/1, detach_phone/1]). 5 | -export([init/1]). 6 | 7 | start_link() -> 8 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 9 | 10 | init([]) -> 11 | hlr:new(), 12 | {ok, {{simple_one_for_one, 10, 3600}, 13 | [{ms, {phone_fsm, start_link, []}, 14 | transient, 2000, worker, [phone_fsm]}]}}. 15 | 16 | %%Race condiiton risk. Device attached after we check but before we start 17 | 18 | attach_phone(Ms) -> 19 | case hlr:lookup_id(Ms) of 20 | {ok, _Pid} -> 21 | {error, attached}; 22 | _NotAttached -> 23 | supervisor:start_child(?MODULE, [Ms]) 24 | end. 25 | 26 | detach_phone(Ms) -> 27 | case hlr:lookup_id(Ms) of 28 | {ok, Pid} -> 29 | supervisor:terminate_child(?MODULE, Pid); 30 | _NotAttached -> 31 | {error, detached} 32 | end. 33 | -------------------------------------------------------------------------------- /ch11/bsc/src/simple_phone_sup.erl: -------------------------------------------------------------------------------- 1 | -module(simple_phone_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, attach_phone/1, detach_phone/1]). 5 | -export([init/1]). 6 | 7 | start_link() -> 8 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 9 | 10 | init([]) -> 11 | hlr:new(), 12 | {ok, {{simple_one_for_one, 10, 3600}, 13 | [{ms, {phone_fsm, start_link, []}, 14 | transient, 2000, worker, [phone_fsm]}]}}. 15 | 16 | 17 | %%Race condiiton risk. Device attached after we check but before we start 18 | 19 | attach_phone(Ms) -> 20 | case hlr:lookup_id(Ms) of 21 | {ok, _Pid} -> 22 | {error, attached}; 23 | _NotAttached -> 24 | supervisor:start_child(?MODULE, [Ms]) 25 | end. 26 | 27 | detach_phone(Ms) -> 28 | case hlr:lookup_id(Ms) of 29 | {ok, Pid} -> 30 | supervisor:terminate_child(?MODULE, Pid); 31 | _NotAttached -> 32 | {error, detached} 33 | end. 34 | -------------------------------------------------------------------------------- /ch8/otp/phone_sup.erl: -------------------------------------------------------------------------------- 1 | -module(phone_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, attach_phone/1, detach_phone/1]). 5 | -export([init/1]). 6 | 7 | start_link() -> 8 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 9 | 10 | init([]) -> 11 | {ok, {{one_for_one, 10, 3600}, []}}. 12 | 13 | 14 | %%Race condition risk. Device attached after we check but before we start 15 | 16 | attach_phone(Ms) -> 17 | case hlr:lookup_id(Ms) of 18 | {ok, _Pid} -> 19 | {error, attached}; 20 | _NotAttached -> 21 | ChildSpec = {Ms, {phone_fsm, start_link, [Ms]}, 22 | transient, 2000, worker, [phone_fsm]}, 23 | supervisor:start_child(?MODULE, ChildSpec) 24 | end. 25 | 26 | detach_phone(Ms) -> 27 | case hlr:lookup_id(Ms) of 28 | {ok, _Pid} -> 29 | supervisor:terminate_child(?MODULE, Ms), 30 | supervisor:delete_child(?MODULE, Ms); 31 | _NotAttached -> 32 | {error, detached} 33 | end. 34 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/phone_sup.erl: -------------------------------------------------------------------------------- 1 | -module(phone_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, attach_phone/1, detach_phone/1]). 5 | -export([init/1]). 6 | 7 | start_link() -> 8 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 9 | 10 | init([]) -> 11 | {ok, {{one_for_one, 10, 3600}, []}}. 12 | 13 | 14 | %%Race condition risk. Device attached after we check but before we start 15 | 16 | attach_phone(Ms) -> 17 | case hlr:lookup_id(Ms) of 18 | {ok, _Pid} -> 19 | {error, attached}; 20 | _NotAttached -> 21 | ChildSpec = {Ms, {phone_fsm, start_link, [Ms]}, 22 | transient, 2000, worker, [phone_fsm]}, 23 | supervisor:start_child(?MODULE, ChildSpec) 24 | end. 25 | 26 | detach_phone(Ms) -> 27 | case hlr:lookup_id(Ms) of 28 | {ok, _Pid} -> 29 | supervisor:terminate_child(?MODULE, Ms), 30 | supervisor:delete_child(?MODULE, Ms); 31 | _NotAttached -> 32 | {error, detached} 33 | end. 34 | -------------------------------------------------------------------------------- /ch11/bsc/src/phone_sup.erl: -------------------------------------------------------------------------------- 1 | -module(phone_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, attach_phone/1, detatch_phone/1]). 5 | -export([init/1]). 6 | 7 | start_link() -> 8 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 9 | 10 | init([]) -> 11 | {ok, {{one_for_one, 10, 3600}, []}}. 12 | 13 | 14 | %%Race condiiton risk. Device attached after we check but before we start 15 | 16 | attach_phone(Ms) -> 17 | case hlr:lookup_id(Ms) of 18 | {ok, _Pid} -> 19 | {error, attached}; 20 | _NotAttached -> 21 | ChildSpec = {Ms, {phone_fsm, start_link, [Ms]}, 22 | transient, 2000, worker, [phone_fsm]}, 23 | supervisor:start_child(?MODULE, ChildSpec) 24 | end. 25 | 26 | detatch_phone(Ms) -> 27 | case hlr:lookup_id(Ms) of 28 | {ok, _Pid} -> 29 | supervisor:terminate_child(?MODULE, Ms), 30 | supervisor:delete_child(?MODULE, Ms); 31 | _NotAttached -> 32 | {error, detached} 33 | end. 34 | -------------------------------------------------------------------------------- /ch3/call/server.erl: -------------------------------------------------------------------------------- 1 | -module(server). 2 | -export([start/2, stop/1, call/2]). 3 | -export([init/2]). 4 | 5 | start(Name, Args) -> 6 | register(Name, spawn(server, init, [Name, Args])). 7 | 8 | stop(Name) -> 9 | Name ! {stop, self()}, 10 | receive {reply, Reply} -> Reply end. 11 | 12 | init(Mod, Args) -> 13 | State = Mod:init(Args), 14 | loop(Mod, State). 15 | 16 | 17 | call(Name, Msg) -> 18 | Ref = erlang:monitor(process, Name), 19 | catch Name ! {request, {Ref, self()}, Msg}, 20 | receive 21 | {reply, Ref, Reply} -> 22 | erlang:demonitor(Ref, [flush]), 23 | Reply; 24 | {'DOWN', Ref, process, _Name, _Reason} -> 25 | {error, no_proc} 26 | end. 27 | 28 | reply({Ref, To}, Reply) -> 29 | To ! {reply, Ref, Reply}. 30 | 31 | loop(Mod, State) -> 32 | receive 33 | {request, From, Msg} -> 34 | {NewState, Reply} = Mod:handle(Msg, State), 35 | reply(From, Reply), 36 | loop(Mod, NewState); 37 | {stop, From} -> 38 | Reply = Mod:terminate(State), 39 | From ! {reply, Reply} 40 | end. 41 | -------------------------------------------------------------------------------- /ch10/mutex1.erl: -------------------------------------------------------------------------------- 1 | -module(mutex1). 2 | 3 | -export([start_link/1, start_link/2, init/3, stop/1]). 4 | -export([wait/1, signal/1]). 5 | 6 | wait(Name) -> 7 | Name ! {wait,self()}, 8 | Mutex = whereis(Name), 9 | receive 10 | {Mutex,ok} -> ok 11 | end. 12 | 13 | signal(Name) -> 14 | Name ! {signal,self()}, 15 | ok. 16 | 17 | start_link(Name) -> 18 | start_link(Name, []). 19 | 20 | start_link(Name, DbgOpts) -> 21 | proc_lib:start_link(?MODULE, init, [self(), Name, DbgOpts]). 22 | 23 | stop(Name) -> Name ! stop. 24 | 25 | init(Parent, Name, DbgOpts) -> 26 | register(Name, self()), 27 | process_flag(trap_exit, true), 28 | Debug = sys:debug_options(DbgOpts), 29 | proc_lib:init_ack({ok,self()}), 30 | free(Name, Parent, Debug). 31 | 32 | free(Name, Parent, Debug) -> 33 | receive 34 | {wait,Pid} -> 35 | Pid ! {self(),ok}, 36 | busy(Pid, Name, Parent, Debug); 37 | stop -> 38 | ok 39 | end. 40 | 41 | busy(Pid, Name, Parent, Debug) -> 42 | receive 43 | {signal,Pid} -> 44 | free(Name, Parent, Debug) 45 | end. 46 | -------------------------------------------------------------------------------- /ch8/otp/frequency_sup.erl: -------------------------------------------------------------------------------- 1 | -module(frequency_sup). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, init/1]). 5 | -export([stop/0]). 6 | 7 | start_link() -> 8 | supervisor:start_link({local,?MODULE},?MODULE, []). 9 | 10 | stop() -> exit(whereis(?MODULE), shutdown). 11 | 12 | init(_) -> 13 | ChildSpecList = [child(freq_overload), child(frequency)], 14 | {ok,{{rest_for_one, 2, 3600}, ChildSpecList}}. 15 | 16 | child(Module) -> 17 | {Module, {Module, start_link, []}, 18 | permanent, 2000, worker, [Module]}. 19 | 20 | %% init/1 and child/1 returning SupervisorSpec as a map rather than a 21 | %% tuple, for Erlang 18.0 or newer: 22 | %%init(_) -> 23 | %% ChildSpecList = [child(freq_overload), child(frequency)], 24 | %% SupFlags = #{strategy => rest_for_one, 25 | %% intensity => 2, period => 3600}, 26 | %% {ok, {SupFlags, ChildSpecList}}. 27 | %% 28 | %%child(Module) -> 29 | %% #{id => Module, 30 | %% start => {Module, start_link, []}, 31 | %% restart => permanent, 32 | %% shutdown => 2000, 33 | %% type => worker, 34 | %% modules => [Module]}. 35 | -------------------------------------------------------------------------------- /ch11/bsc/src/hlr.erl: -------------------------------------------------------------------------------- 1 | %%% @author Francesco Cesarini 2 | %%% @copyright (C) 2013, Francesco Cesarini 3 | %%% @doc 4 | %%% 5 | %%% @end 6 | %%% Created : 9 Jun 2013 by Francesco Cesarini 7 | 8 | -module(hlr). 9 | -export([new/0, attach/1, detach/0, lookup_id/1, lookup_ms/1]). 10 | 11 | 12 | new() -> 13 | ets:new(msisdn2pid, [public, named_table]), 14 | ets:new(pid2msisdn, [public, named_table]), 15 | ok. 16 | 17 | attach(Ms) -> 18 | detach(), 19 | ets:insert(msisdn2pid, {Ms, self()}), 20 | ets:insert(pid2msisdn, {self(), Ms}). 21 | 22 | detach() -> 23 | case ets:lookup(pid2msisdn, self()) of 24 | [{Pid, Ms}] -> 25 | ets:delete(pid2msisdn, Pid), 26 | ets:delete(msisdn2pid, Ms); 27 | [] -> 28 | ok 29 | end. 30 | 31 | 32 | lookup_id(Ms) -> 33 | case ets:lookup(msisdn2pid, Ms) of 34 | [] -> {error, invalid}; 35 | [{Ms, Pid}] -> {ok, Pid} 36 | end. 37 | 38 | lookup_ms(Pid) -> 39 | case ets:lookup(pid2msisdn, Pid) of 40 | [] -> {error, invalid}; 41 | [{Pid, Ms}] -> {ok, Ms} 42 | end. 43 | 44 | -------------------------------------------------------------------------------- /ch8/otp/logger.erl: -------------------------------------------------------------------------------- 1 | -module(logger). 2 | -behaviour(gen_event). 3 | -export([init/1, terminate/2, handle_event/2, handle_info/2]). 4 | 5 | init({standard_io, {Fd, Count}}) when is_pid(Fd) -> 6 | file:close(Fd), 7 | {ok, {standard_io, Count}}; 8 | init({File, {standard_io, Count}}) when is_list(File) -> 9 | {ok, Fd} = file:open(File, write), 10 | {ok, {Fd, Count}}; 11 | init(standard_io) -> 12 | {ok, {standard_io, 1}}; 13 | init({file, File}) -> 14 | {ok, Fd} = file:open(File, write), 15 | {ok, {Fd, 1}}; 16 | init(Args) -> 17 | {error, {args, Args}}. 18 | 19 | terminate(swap, {Type, Count}) -> 20 | {Type, Count}; 21 | terminate(_Reason, {standard_io, Count}) -> 22 | {count, Count}; 23 | terminate(_Reason, {Fd, Count}) -> 24 | file:close(Fd), 25 | {count, Count}. 26 | 27 | handle_event(Event, {Fd, Count}) -> 28 | print(Fd, Count, Event, "Event"), 29 | {ok, {Fd, Count+1}}. 30 | 31 | handle_info(Event, {Fd, Count}) -> 32 | print(Fd, Count, Event, "Unknown"), 33 | {ok, {Fd, Count+1}}. 34 | 35 | print(Fd, Count, Event, Tag) -> 36 | io:format(Fd, "Id:~w Time:~w Date:~w~n"++Tag++":~w~n", [Count, time(),date(),Event]). 37 | -------------------------------------------------------------------------------- /ch7/logger.erl: -------------------------------------------------------------------------------- 1 | -module(logger). 2 | -behaviour(gen_event). 3 | -export([init/1, terminate/2, handle_event/2, handle_info/2]). 4 | 5 | init({standard_io, {Fd, Count}}) when is_pid(Fd) -> 6 | file:close(Fd), 7 | {ok, {standard_io, Count}}; 8 | init({File, {standard_io, Count}}) when is_list(File) -> 9 | {ok, Fd} = file:open(File, write), 10 | {ok, {Fd, Count}}; 11 | 12 | init(standard_io) -> 13 | {ok, {standard_io, 1}}; 14 | init({file, File}) -> 15 | {ok, Fd} = file:open(File, write), 16 | {ok, {Fd, 1}}; 17 | init(Args) -> 18 | {error, {args, Args}}. 19 | 20 | terminate(swap, {Type, Count}) -> 21 | {Type, Count}; 22 | terminate(_Reason, {standard_io, Count}) -> 23 | {count, Count}; 24 | terminate(_Reason, {Fd, Count}) -> 25 | file:close(Fd), 26 | {count, Count}. 27 | 28 | handle_event(Event, {Fd, Count}) -> 29 | print(Fd, Count, Event, "Event"), 30 | {ok, {Fd, Count+1}}. 31 | 32 | handle_info(Event, {Fd, Count}) -> 33 | print(Fd, Count, Event, "Unknown"), 34 | {ok, {Fd, Count+1}}. 35 | 36 | print(Fd, Count, Event, Tag) -> 37 | io:format(Fd, "Id:~w Time:~w Date:~w~n"++Tag++":~w~n", 38 | [Count, time(),date(),Event]). 39 | -------------------------------------------------------------------------------- /ch8/erlang/my_supervisor.erl: -------------------------------------------------------------------------------- 1 | -module(my_supervisor). 2 | -export([start/2, init/1, stop/1]). 3 | 4 | start(Name, ChildSpecList) -> 5 | register(Name, Pid = spawn(?MODULE, init, [ChildSpecList])), 6 | {ok, Pid}. 7 | 8 | stop(Name) -> Name ! stop. 9 | 10 | init(ChildSpecList) -> 11 | process_flag(trap_exit, true), 12 | loop(start_children(ChildSpecList)). 13 | 14 | start_children(ChildSpecList) -> 15 | [{element(2, apply(M,F,A)), {M,F,A}} || {M,F,A} <- ChildSpecList]. 16 | 17 | 18 | loop(ChildList) -> 19 | receive 20 | {'EXIT', Pid, normal} -> 21 | loop(lists:keydelete(Pid,1,ChildList)); 22 | {'EXIT', Pid, _Reason} -> 23 | NewChildList = restart_child(Pid, ChildList), 24 | loop(NewChildList); 25 | stop -> 26 | terminate(ChildList) 27 | end. 28 | 29 | restart_child(Pid, ChildList) -> 30 | {Pid, {M,F,A}} = lists:keyfind(Pid, 1, ChildList), 31 | {ok, NewPid} = apply(M,F,A), 32 | lists:keyreplace(Pid,1,ChildList,{NewPid, {M,F,A}}). 33 | 34 | terminate(ChildList) -> 35 | lists:foreach(fun({Pid, _}) -> exit(Pid, kill) end, ChildList). 36 | 37 | %%terminate([{Pid, _} | ChildList]) -> 38 | %% exit(Pid, kill), 39 | %% terminate(ChildList). 40 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/logger.erl: -------------------------------------------------------------------------------- 1 | -module(logger). 2 | -behaviour(gen_event). 3 | -export([init/1, terminate/2, handle_event/2, handle_info/2]). 4 | 5 | init({standard_io, {Fd, Count}}) when is_pid(Fd) -> 6 | file:close(Fd), 7 | {ok, {standard_io, Count}}; 8 | init({File, {standard_io, Count}}) when is_list(File) -> 9 | {ok, Fd} = file:open(File, write), 10 | {ok, {Fd, Count}}; 11 | init(standard_io) -> 12 | {ok, {standard_io, 1}}; 13 | init({file, File}) -> 14 | {ok, Fd} = file:open(File, write), 15 | {ok, {Fd, 1}}; 16 | init(Args) -> 17 | {error, {args, Args}}. 18 | 19 | terminate(swap, {Type, Count}) -> 20 | {Type, Count}; 21 | terminate(_Reason, {standard_io, Count}) -> 22 | {count, Count}; 23 | terminate(_Reason, {Fd, Count}) -> 24 | file:close(Fd), 25 | {count, Count}. 26 | 27 | handle_event(Event, {Fd, Count}) -> 28 | print(Fd, Count, Event, "Event"), 29 | {ok, {Fd, Count+1}}. 30 | 31 | handle_info(Event, {Fd, Count}) -> 32 | print(Fd, Count, Event, "Unknown"), 33 | {ok, {Fd, Count+1}}. 34 | 35 | print(Fd, Count, Event, Tag) -> 36 | io:format(Fd, "Id:~w Time:~w Date:~w~n"++Tag++":~w~n", 37 | [Count, time(),date(),Event]). 38 | -------------------------------------------------------------------------------- /ch11/bsc/src/logger.erl: -------------------------------------------------------------------------------- 1 | -module(logger). 2 | -behaviour(gen_event). 3 | -export([init/1, terminate/2, handle_event/2, handle_info/2]). 4 | 5 | init({standard_io, {Fd, Count}}) when is_pid(Fd) -> 6 | file:close(Fd), 7 | {ok, {standard_io, Count}}; 8 | init({File, {standard_io, Count}}) when is_list(File) -> 9 | {ok, Fd} = file:open(File, write), 10 | {ok, {Fd, Count}}; 11 | 12 | init(standard_io) -> 13 | {ok, {standard_io, 1}}; 14 | init({file, File}) -> 15 | {ok, Fd} = file:open(File, write), 16 | {ok, {Fd, 1}}; 17 | init(Args) -> 18 | {error, {args, Args}}. 19 | 20 | 21 | terminate(swap, {Type, Count}) -> 22 | {Type, Count}; 23 | terminate(_Reason, {standard_io, Count}) -> 24 | {count, Count}; 25 | terminate(_Reason, {Fd, Count}) -> 26 | file:close(Fd), 27 | {count, Count}. 28 | 29 | handle_event(Event, {Fd, Count}) -> 30 | print(Fd, Count, Event, "Event"), 31 | {ok, {Fd, Count+1}}. 32 | 33 | handle_info(Event, {Fd, Count}) -> 34 | print(Fd, Count, Event, "Unknown"), 35 | {ok, {Fd, Count+1}}. 36 | 37 | 38 | print(Fd, Count, Event, Tag) -> 39 | io:format(Fd, "Id:~w Time:~w Date:~w~n"++Tag++":~w~n", [Count, time(),date(),Event]). 40 | -------------------------------------------------------------------------------- /ch3/behavior/frequency.erl: -------------------------------------------------------------------------------- 1 | -module(frequency). 2 | -export([start/0, stop/0, allocate/0, deallocate/1]). 3 | -export([init/1, terminate/1, handle/2]). 4 | 5 | %% These are the start functions used to create and 6 | %% initialize the server. 7 | start() -> server:start(frequency, []). 8 | 9 | init(_Args) -> 10 | {get_frequencies(), []}. 11 | 12 | %% Hard-coded 13 | get_frequencies() -> [10,11,12,13,14,15]. 14 | 15 | stop() -> server:stop(frequency). 16 | allocate() -> server:call(frequency, {allocate, self()}). 17 | deallocate(Freq) -> server:call(frequency, {deallocate, Freq}). 18 | 19 | handle({allocate, Pid}, Frequencies) -> 20 | allocate(Frequencies, Pid); 21 | handle({deallocate, Freq}, Frequencies) -> 22 | {deallocate(Frequencies, Freq), ok}. 23 | terminate(_Frequencies) -> 24 | ok. 25 | 26 | %% The Internal Helper Functions used to allocate and 27 | %% deallocate frequencies. 28 | allocate({[], Allocated}, _Pid) -> 29 | {{[], Allocated}, {error, no_frequency}}; 30 | allocate({[Freq|Free], Allocated}, Pid) -> 31 | {{Free, [{Freq, Pid}|Allocated]}, {ok, Freq}}. 32 | 33 | deallocate({Free, Allocated}, Freq) -> 34 | NewAllocated=lists:keydelete(Freq, 1, Allocated), 35 | {[Freq|Free], NewAllocated}. 36 | -------------------------------------------------------------------------------- /ch6/otp/test_fsm.erl: -------------------------------------------------------------------------------- 1 | -module(test_fsm). 2 | -behaviour(gen_fsm). 3 | 4 | -export([start_link/2, start/2]). 5 | 6 | -export([init/1, state_name/2, state_name/3, handle_event/3, 7 | handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). 8 | 9 | -record(state, {}). 10 | 11 | start_link(TimerMs, Options) -> 12 | gen_fsm:start_link(?MODULE, TimerMs, Options). 13 | start(TimerMs, Options) -> 14 | gen_fsm:start(?MODULE, TimerMs, Options). 15 | 16 | init(0) -> 17 | {stop, stopped}; 18 | init(1) -> 19 | {next_state, selection, []}; 20 | init(TimerMs) -> 21 | timer:sleep(TimerMs), 22 | ignore. 23 | 24 | state_name(_Event, State) -> 25 | {next_state, state_name, State}. 26 | 27 | state_name(_Event, _From, State) -> 28 | Reply = ok, 29 | {reply, Reply, state_name, State}. 30 | 31 | handle_event(_Event, StateName, State) -> 32 | {next_state, StateName, State}. 33 | 34 | handle_sync_event(_Event, _From, StateName, State) -> 35 | Reply = ok, 36 | {reply, Reply, StateName, State}. 37 | 38 | handle_info(_Info, StateName, State) -> 39 | {next_state, StateName, State}. 40 | 41 | terminate(_Reason, _StateName, _State) -> 42 | ok. 43 | 44 | code_change(_OldVsn, StateName, State, _Extra) -> 45 | {ok, StateName, State}. 46 | -------------------------------------------------------------------------------- /ch8/otp/frequency_sup2.erl: -------------------------------------------------------------------------------- 1 | -module(frequency_sup2). 2 | -behaviour(supervisor). 3 | 4 | -export([start_link/0, init/1]). 5 | -export([stop/0]). 6 | 7 | stop() -> exit(whereis(?MODULE), shutdown). 8 | 9 | start_link() -> 10 | {ok, Pid} = supervisor:start_link({local,?MODULE},?MODULE, []), 11 | freq_overload:add(counters, {}), 12 | freq_overload:add(logger, {file, "log"}), 13 | {ok, Pid}. 14 | 15 | init(_) -> 16 | hlr:new(), 17 | ChildSpecList = [child(freq_overload), child(frequency)], 18 | {ok,{{rest_for_one, 2, 3600}, ChildSpecList}}. 19 | 20 | child(Module) -> 21 | {Module, {Module, start_link, []}, 22 | permanent, 2000, worker, [Module]}. 23 | 24 | %% init/1 and child/1 returning SupervisorSpec as a map rather than a 25 | %% tuple, for Erlang 18.0 or newer: 26 | %%init(_) -> 27 | %% hlr:new(), 28 | %% ChildSpecList = [child(freq_overload), child(frequency)], 29 | %% SupFlags = #{strategy => rest_for_one, 30 | %% intensity => 2, period => 3600}, 31 | %% {ok, {SupFlags, ChildSpecList}}. 32 | %% 33 | %%child(Module) -> 34 | %% #{id => Module, 35 | %% start => {Module, start_link, []}, 36 | %% restart => permanent, 37 | %% shutdown => 2000, 38 | %% type => worker, 39 | %% modules => [Module]}. 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Code for *Designing for Scalability with Erlang/OTP* 2 | 3 | In this repository you'll find the code of the examples in the book 4 | [*Designing for Scalability with 5 | Erlang/OTP*](http://shop.oreilly.com/product/0636920024149.do). 6 | 7 | The following excerpt from the book explains the terms for using the code 8 | in this repository: 9 | 10 | > This book is here to help you get your job done. In general, if example 11 | > code is offered with this book, you may use it in your programs and 12 | > documentation. You do not need to contact us for permission unless you're 13 | > reproducing a significant portion of the code. For example, writing a 14 | > program that uses several chunks of code from this book does not require 15 | > permission. Selling or distributing a CD-ROM of examples from O'Reilly 16 | > books does require permission. Answering a question by citing this book and 17 | > quoting example code does not require permission. Incorporating a 18 | > significant amount of example code from this book into your product's 19 | > documentation does require permission. 20 | 21 | > We appreciate, but do not require, attribution. An attribution usually 22 | > includes the title, author, publisher, and ISBN. For example: "*Book 23 | > Title* by Some Author (O'Reilly). Copyright 2016 Some Copyright Holder, 24 | > 978-0-596-xxxx-x." 25 | 26 | > If you feel your use of code examples falls outside fair use or the 27 | > permission given above, feel free to contact us at 28 | > [permissions@oreilly.com](mailto:permissions@oreilly.com). 29 | -------------------------------------------------------------------------------- /ch10/mutex2.erl: -------------------------------------------------------------------------------- 1 | -module(mutex2). 2 | 3 | -export([start_link/1, start_link/2, init/3, stop/1]). 4 | 5 | -export([wait/1, signal/1]). 6 | 7 | wait(Name) -> 8 | Name ! {wait,self()}, 9 | Mutex = whereis(Name), 10 | receive 11 | {Mutex,ok} -> ok 12 | end. 13 | 14 | signal(Name) -> 15 | Name ! {signal,self()}, 16 | ok. 17 | 18 | start_link(Name) -> 19 | start_link(Name, []). 20 | 21 | start_link(Name, DbgOpts) -> 22 | proc_lib:start_link(?MODULE, init, [self(), Name, DbgOpts]). 23 | 24 | stop(Name) -> Name ! stop. 25 | 26 | init(Parent, Name, DbgOpts) -> 27 | register(Name, self()), 28 | process_flag(trap_exit, true), 29 | Debug = sys:debug_options(DbgOpts), 30 | proc_lib:init_ack({ok,self()}), 31 | free(Name, Parent, Debug). 32 | 33 | free(Name, Parent, Debug) -> 34 | receive 35 | {wait,Pid} -> 36 | link(Pid), 37 | Pid ! {self(),ok}, 38 | busy(Pid, Name, Parent, Debug); 39 | stop -> 40 | terminate(shutdown, Name); 41 | {'EXIT',Parent,Reason} -> 42 | terminate(Reason, Name) 43 | end. 44 | 45 | busy(Pid, Name, Parent, Debug) -> 46 | receive 47 | {signal,Pid} -> 48 | free(Name, Parent, Debug); 49 | {'EXIT',Parent,Reason} -> 50 | exit(Pid, Reason), 51 | terminate(Reason, Name) 52 | end. 53 | 54 | terminate(Reason, Name) -> 55 | unregister(Name), 56 | terminate(Reason). 57 | terminate(Reason) -> 58 | receive 59 | {wait,Pid} -> 60 | exit(Pid, Reason), 61 | terminate(Reason) 62 | after 0 -> 63 | exit(Reason) 64 | end. 65 | -------------------------------------------------------------------------------- /ch3/frequency.erl: -------------------------------------------------------------------------------- 1 | -module(frequency). 2 | -export([start/0, stop/0, allocate/0, deallocate/1]). 3 | -export([init/0]). 4 | 5 | %% These are the start functions used to create and 6 | %% initialize the server. 7 | start() -> register(frequency, spawn(frequency, init, [])). 8 | 9 | init() -> 10 | Frequencies = {get_frequencies(), []}, 11 | loop(Frequencies). 12 | 13 | %% Hard-coded 14 | get_frequencies() -> [10,11,12,13,14,15]. 15 | 16 | stop() -> call(stop). 17 | allocate() -> call(allocate). 18 | deallocate(Freq) -> call({deallocate, Freq}). 19 | 20 | call(Message) -> 21 | frequency ! {request, self(), Message}, 22 | receive 23 | {reply, Reply} -> Reply 24 | end. 25 | 26 | reply(Pid, Reply) -> 27 | Pid ! {reply, Reply}. 28 | 29 | loop(Frequencies) -> 30 | receive 31 | {request, Pid, allocate} -> 32 | {NewFrequencies, Reply} = allocate(Frequencies, Pid), 33 | reply(Pid, Reply), 34 | loop(NewFrequencies); 35 | {request, Pid , {deallocate, Freq}} -> 36 | NewFrequencies = deallocate(Frequencies, Freq), 37 | reply(Pid, ok), 38 | loop(NewFrequencies); 39 | {request, Pid, stop} -> 40 | reply(Pid, ok) 41 | end. 42 | 43 | 44 | %% The Internal Helper Functions used to allocate and 45 | %% deallocate frequencies. 46 | allocate({[], Allocated}, _Pid) -> 47 | {{[], Allocated}, {error, no_frequency}}; 48 | allocate({[Freq|Free], Allocated}, Pid) -> 49 | {{Free, [{Freq, Pid}|Allocated]}, {ok, Freq}}. 50 | 51 | deallocate({Free, Allocated}, Freq) -> 52 | NewAllocated=lists:keydelete(Freq, 1, Allocated), 53 | {[Freq|Free], NewAllocated}. 54 | -------------------------------------------------------------------------------- /ch8/erlang/coffee.erl: -------------------------------------------------------------------------------- 1 | -module(coffee). 2 | -export([tea/0, espresso/0, americano/0, cappuccino/0, 3 | pay/1, cup_removed/0, cancel/0]). 4 | -export([start_link/0, init/0]). 5 | 6 | start_link() -> 7 | {ok, spawn_link(?MODULE, init, [])}. 8 | 9 | init() -> 10 | register(?MODULE, self()), 11 | hw:reboot(), 12 | hw:display("Make Your Selection", []), 13 | selection(). 14 | 15 | %% Client Functions for Drink Selections 16 | tea() -> ?MODULE ! {selection, tea, 100}. 17 | espresso() -> ?MODULE ! {selection, espresso, 150}. 18 | americano() -> ?MODULE ! {selection, americano, 100}. 19 | cappuccino() -> ?MODULE ! {selection, cappuccino,150}. 20 | 21 | %% Client Functions for Actions 22 | cup_removed() -> ?MODULE ! cup_removed. 23 | pay(Coin) -> ?MODULE ! {pay, Coin}. 24 | cancel() -> ?MODULE ! cancel. 25 | 26 | %% State: drink selection
 27 | selection() -> 28 | receive 29 | {selection, Type, Price} -> 30 | hw:display("Please pay:~w",[Price]), 31 | payment(Type, Price, 0); 32 | {pay, Coin} -> 33 | hw:return_change(Coin), 34 | selection(); 35 | _Other -> % cancel 36 | selection() 37 | end. 38 | 39 | %% State: payment 40 | payment(Type, Price, Paid) -> 41 | receive 42 | {pay, Coin} -> 43 | if 44 | Coin + Paid >= Price -> 45 | hw:display("Preparing Drink.",[]), 46 | hw:return_change(Coin + Paid - Price), 47 | hw:drop_cup(), hw:prepare(Type), 48 | hw:display("Remove Drink.", []), 49 | remove(); 50 | true -> 51 | ToPay = Price - (Coin + Paid), 52 | hw:display("Please pay:~w",[ToPay]), 53 | payment(Type, Price, Coin + Paid) 54 | end; 55 | cancel -> 56 | hw:display("Make Your Selection", []), 57 | hw:return_change(Paid), 58 | selection(); 59 | _Other -> %selection 60 | payment(Type, Price, Paid) 61 | end. 62 | 63 | %% State: remove cup
 64 | remove() -> 65 | receive 66 | cup_removed -> 67 | hw:display("Make Your Selection", []), 68 | selection(); 69 | {pay, Coin} -> 70 | hw:return_change(Coin), 71 | remove(); 72 | _Other -> % cancel/selection 73 | remove() 74 | end. 75 | -------------------------------------------------------------------------------- /ch12/erlang/coffee.erl.original: -------------------------------------------------------------------------------- 1 | -module(coffee). 2 | -export([tea/0, espresso/0, americano/0, cappuccino/0, 3 | pay/1, cup_removed/0, cancel/0]). 4 | -export([start_link/0, init/0]). 5 | 6 | start_link() -> 7 | {ok, spawn_link(?MODULE, init, [])}. 8 | 9 | init() -> 10 | register(?MODULE, self()), 11 | hw:reboot(), 12 | hw:display("Make Your Selection", []), 13 | selection(). 14 | 15 | %% Client Functions for Drink Selections 16 | tea() -> ?MODULE ! {selection, tea, 100}. 17 | espresso() -> ?MODULE ! {selection, espresso, 150}. 18 | americano() -> ?MODULE ! {selection, americano, 100}. 19 | cappuccino() -> ?MODULE ! {selection, cappuccino,150}. 20 | 21 | %% Client Functions for Actions 22 | cup_removed() -> ?MODULE ! cup_removed. 23 | pay(Coin) -> ?MODULE ! {pay, Coin}. 24 | cancel() -> ?MODULE ! cancel. 25 | 26 | %% State: drink selection 27 | selection() -> 28 | receive 29 | {selection, Type, Price} -> 30 | hw:display("Please pay:~w",[Price]), 31 | payment(Type, Price, 0); 32 | {pay, Coin} -> 33 | hw:return_change(Coin), 34 | selection(); 35 | _Other -> % cancel 36 | selection() 37 | end. 38 | 39 | %% State: payment 40 | payment(Type, Price, Paid) -> 41 | receive 42 | {pay, Coin} -> 43 | if Coin + Paid >= Price -> 44 | hw:display("Preparing Drink.",[]), 45 | hw:return_change(Coin + Paid - Price), 46 | hw:drop_cup(), hw:prepare(Type), 47 | hw:display("Remove Drink.", []), 48 | remove(); 49 | true -> 50 | ToPay = Price - (Coin + Paid), 51 | hw:display("Please pay:~w",[ToPay]), 52 | payment(Type, Price, Coin + Paid) 53 | end; 54 | cancel -> 55 | hw:display("Make Your Selection", []), 56 | hw:return_change(Paid), 57 | selection(); 58 | _Other -> %selection 59 | payment(Type, Price, Paid) 60 | end. 61 | 62 | %% State: remove cup 63 | remove() -> 64 | receive 65 | cup_removed -> 66 | hw:display("Make Your Selection", []), 67 | selection(); 68 | {pay, Coin} -> 69 | hw:return_change(Coin), 70 | remove(); 71 | _Other -> % cancel/selection 72 | remove() 73 | end. 74 | -------------------------------------------------------------------------------- /ch6/erlang/coffee_fsm.erl: -------------------------------------------------------------------------------- 1 | -module(coffee_fsm). 2 | -export([tea/0, espresso/0, americano/0, cappuccino/0, 3 | pay/1, cup_removed/0, cancel/0]). 4 | -export([start_link/0, init/0]). 5 | 6 | start_link() -> 7 | {ok, spawn_link(?MODULE, init, [])}. 8 | 9 | init() -> 10 | register(?MODULE, self()), 11 | hw:reboot(), 12 | hw:display("Make Your Selection", []), 13 | selection(). 14 | 15 | %% Client Functions for Drink Selections 16 | 17 | tea() -> ?MODULE ! {selection, tea, 100}. 18 | espresso() -> ?MODULE ! {selection, espresso, 150}. 19 | americano() -> ?MODULE ! {selection, americano, 100}. 20 | cappuccino() -> ?MODULE ! {selection, cappuccino,150}. 21 | 22 | %% Client Functions for Actions 23 | 24 | cup_removed() -> ?MODULE ! cup_removed. 25 | pay(Coin) -> ?MODULE ! {pay, Coin}. 26 | cancel() -> ?MODULE ! cancel. 27 | 28 | %% State: drink selection
 29 | selection() -> 30 | receive 31 | {selection, Type, Price} -> 32 | hw:display("Please pay:~w",[Price]), 33 | payment(Type, Price, 0); 34 | {pay, Coin} -> 35 | hw:return_change(Coin), 36 | selection(); 37 | _Other -> % cancel 38 | selection() 39 | end. 40 | 41 | %% State: payment 42 | payment(Type, Price, Paid) -> 43 | receive 44 | {pay, Coin} -> 45 | if 46 | Coin + Paid >= Price -> 47 | hw:display("Preparing Drink.",[]), 48 | hw:return_change(Coin + Paid - Price), 49 | hw:drop_cup(), hw:prepare(Type), 50 | hw:display("Remove Drink.", []), 51 | remove(); 52 | true -> 53 | ToPay = Price - (Coin + Paid), 54 | hw:display("Please pay:~w",[ToPay]), 55 | payment(Type, Price, Coin + Paid) 56 | end; 57 | cancel -> 58 | hw:display("Make Your Selection", []), 59 | hw:return_change(Paid), 60 | selection(); 61 | _Other -> %selection 62 | payment(Type, Price, Paid) 63 | end. 64 | 65 | %% State: remove cup
 66 | remove() -> 67 | receive 68 | cup_removed -> 69 | hw:display("Make Your Selection", []), 70 | selection(); 71 | {pay, Coin} -> 72 | hw:return_change(Coin), 73 | remove(); 74 | _Other -> % cancel/selection 75 | remove() 76 | end. 77 | -------------------------------------------------------------------------------- /ch8/otp/phone.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2013,2016 Francesco Cesarini 2 | %%% @doc phone.erl is a mobile phone simulator which can be used to test the 3 | %%% phone_fsm.erl finite state machine, handling the phone state on the server side. 4 | -module(phone). 5 | -compile(export_all). 6 | -define(TIMEOUT, 60000). 7 | 8 | start_test(Num, Calls) -> 9 | [phone_fsm:start_link(X) || X <- lists:seq(1,Num)], 10 | call(Calls, Num). 11 | 12 | call(0,_) -> ok; 13 | call(X, Num) -> 14 | %% timer:sleep(100), 15 | FromMs = random:uniform(Num), 16 | ToMs = random:uniform(Num), 17 | {ok, FromMsId} = hlr:lookup_id(FromMs), 18 | case phone_fsm:action({outbound, ToMs}, FromMsId) of 19 | ok -> 20 | call(X-1, Num); 21 | _error -> 22 | call(X, Num) 23 | end. 24 | 25 | reply(outbound, _ToMsId, Ms) -> 26 | clear(), 27 | FromMsId = self(), 28 | io:format("~p dialing ~p~n",[FromMsId, _ToMsId]), 29 | F = fun() -> 30 | random:seed(now()), 31 | timer:sleep(random:uniform(3000)), 32 | io:format("~p hanging up ~p~n",[FromMsId, Ms]), 33 | phone_fsm:action(hangup, FromMsId) 34 | end, 35 | put(pid, spawn(F)); 36 | 37 | reply(connected, OtherMsId, _Ms) -> 38 | clear(), 39 | FromMsId = self(), 40 | io:format("~p connected to ~p~n",[FromMsId, OtherMsId]), 41 | F = fun() -> 42 | random:seed(now()), 43 | timer:sleep(random:uniform(3000)), 44 | io:format("~p hanging up ~p~n",[FromMsId, OtherMsId]), 45 | phone_fsm:action(hangup, FromMsId) 46 | end, 47 | put(pid, spawn(F)); 48 | reply(invalid, _ToMs, _Ms) -> 49 | io:format("~p connecting to ~p failed:invalid number~n",[_ToMs, _Ms]), 50 | clear(); 51 | reply(inbound, _FromMsId, _Ms) -> 52 | clear(), 53 | ToMsId = self(), 54 | F = fun() -> 55 | random:seed(now()), 56 | timer:sleep(random:uniform(1500)), 57 | case random:uniform(2) of 58 | 1 -> 59 | io:format("accept(~p,~p)~n",[ToMsId, _FromMsId]), 60 | phone_fsm:action(accept, ToMsId), 61 | timer:sleep(random:uniform(3000)), 62 | phone_fsm:action(hangup, ToMsId); 63 | 2 -> 64 | phone_fsm:action(reject, ToMsId) 65 | end 66 | end, 67 | put(pid, spawn(F)); 68 | reply(hangup, _FromMsId, _Ms) -> 69 | clear(); 70 | reply(_Reason, FromMsId, _Ms) -> 71 | io:format("~p connecting to ~p failed:~w~n", 72 | [element(2,hlr:lookup_ms(FromMsId)), _Ms, _Reason]), 73 | clear(). 74 | 75 | clear() -> 76 | case get(pid) of 77 | undefined -> ok; 78 | Pid -> 79 | exit(Pid, kill), erase(pid), 80 | io:format("~p cleared~n",[self()]) 81 | end. 82 | -------------------------------------------------------------------------------- /ch6/exercise/phone.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2013,2016 Francesco Cesarini 2 | %%% @doc phone.erl is a mobile phone simulator which can be used to test the 3 | %%% phone_fsm.erl finite state machine, handling the phone state on the server side. 4 | -module(phone). 5 | -compile(export_all). 6 | 7 | -define(TIMEOUT, 60000). 8 | 9 | start_test(Num, Calls) -> 10 | [phone_fsm:start_link(X) || X <- lists:seq(1,Num)], 11 | call(Calls, Num). 12 | 13 | call(0,_) -> ok; 14 | call(X, Num) -> 15 | %% timer:sleep(100), 16 | FromMs = random:uniform(Num), 17 | ToMs = random:uniform(Num), 18 | {ok, FromMsId} = hlr:lookup_id(FromMs), 19 | case phone_fsm:action({outbound, ToMs}, FromMsId) of 20 | ok -> 21 | call(X-1, Num); 22 | _error -> 23 | call(X, Num) 24 | end. 25 | 26 | reply(outbound, _ToMsId, Ms) -> 27 | clear(), 28 | FromMsId = self(), 29 | io:format("~p dialing ~p~n",[FromMsId, _ToMsId]), 30 | F = fun() -> 31 | random:seed(now()), 32 | timer:sleep(random:uniform(3000)), 33 | io:format("~p hanging up ~p~n",[FromMsId, Ms]), 34 | phone_fsm:action(hangup, FromMsId) 35 | end, 36 | put(pid, spawn(F)); 37 | 38 | reply(connected, OtherMsId, _Ms) -> 39 | clear(), 40 | FromMsId = self(), 41 | io:format("~p connected to ~p~n",[FromMsId, OtherMsId]), 42 | F = fun() -> 43 | random:seed(now()), 44 | timer:sleep(random:uniform(3000)), 45 | io:format("~p hanging up ~p~n",[FromMsId, OtherMsId]), 46 | phone_fsm:action(hangup, FromMsId) 47 | end, 48 | put(pid, spawn(F)); 49 | reply(invalid, _ToMs, _Ms) -> 50 | io:format("~p connecting to ~p failed:invalid number~n",[_ToMs, _Ms]), 51 | clear(); 52 | reply(inbound, _FromMsId, _Ms) -> 53 | clear(), 54 | ToMsId = self(), 55 | F = fun() -> 56 | random:seed(now()), 57 | timer:sleep(random:uniform(1500)), 58 | case random:uniform(2) of 59 | 1 -> 60 | io:format("accept(~p,~p)~n",[ToMsId, _FromMsId]), 61 | phone_fsm:action(accept, ToMsId), 62 | timer:sleep(random:uniform(3000)), 63 | phone_fsm:action(hangup, ToMsId); 64 | 2 -> 65 | phone_fsm:action(reject, ToMsId) 66 | end 67 | end, 68 | put(pid, spawn(F)); 69 | reply(hangup, _FromMsId, _Ms) -> 70 | clear(); 71 | reply(_Reason, FromMsId, _Ms) -> 72 | io:format("~p connecting to ~p failed:~w~n", 73 | [element(2,hlr:lookup_ms(FromMsId)), _Ms, _Reason]), 74 | clear(). 75 | 76 | 77 | clear() -> 78 | case get(pid) of 79 | undefined -> ok; 80 | Pid -> 81 | exit(Pid, kill), erase(pid), 82 | io:format("~p cleared~n",[self()]) 83 | end. 84 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/phone.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2013,2016 Francesco Cesarini 2 | %%% @doc phone.erl is a mobile phone simulator which can be used to test the 3 | %%% phone_fsm.erl finite state machine, handling the phone state on the server side. 4 | -module(phone). 5 | -compile(export_all). 6 | 7 | -define(TIMEOUT, 60000). 8 | 9 | start_test(Num, Calls) -> 10 | [phone_fsm:start_link(X) || X <- lists:seq(1,Num)], 11 | call(Calls, Num). 12 | 13 | call(0,_) -> ok; 14 | call(X, Num) -> 15 | %% timer:sleep(100), 16 | FromMs = random:uniform(Num), 17 | ToMs = random:uniform(Num), 18 | {ok, FromMsId} = hlr:lookup_id(FromMs), 19 | case phone_fsm:action({outbound, ToMs}, FromMsId) of 20 | ok -> 21 | call(X-1, Num); 22 | _error -> 23 | call(X, Num) 24 | end. 25 | 26 | reply(outbound, _ToMsId, Ms) -> 27 | clear(), 28 | FromMsId = self(), 29 | io:format("~p dialing ~p~n",[FromMsId, _ToMsId]), 30 | F = fun() -> 31 | random:seed(now()), 32 | timer:sleep(random:uniform(3000)), 33 | io:format("~p hanging up ~p~n",[FromMsId, Ms]), 34 | phone_fsm:action(hangup, FromMsId) 35 | end, 36 | put(pid, spawn(F)); 37 | 38 | reply(connected, OtherMsId, _Ms) -> 39 | clear(), 40 | FromMsId = self(), 41 | io:format("~p connected to ~p~n",[FromMsId, OtherMsId]), 42 | F = fun() -> 43 | random:seed(now()), 44 | timer:sleep(random:uniform(3000)), 45 | io:format("~p hanging up ~p~n",[FromMsId, OtherMsId]), 46 | phone_fsm:action(hangup, FromMsId) 47 | end, 48 | put(pid, spawn(F)); 49 | reply(invalid, _ToMs, _Ms) -> 50 | io:format("~p connecting to ~p failed:invalid number~n",[_ToMs, _Ms]), 51 | clear(); 52 | reply(inbound, _FromMsId, _Ms) -> 53 | clear(), 54 | ToMsId = self(), 55 | F = fun() -> 56 | random:seed(now()), 57 | timer:sleep(random:uniform(1500)), 58 | case random:uniform(2) of 59 | 1 -> 60 | io:format("accept(~p,~p)~n",[ToMsId, _FromMsId]), 61 | phone_fsm:action(accept, ToMsId), 62 | timer:sleep(random:uniform(3000)), 63 | phone_fsm:action(hangup, ToMsId); 64 | 2 -> 65 | phone_fsm:action(reject, ToMsId) 66 | end 67 | end, 68 | put(pid, spawn(F)); 69 | reply(hangup, _FromMsId, _Ms) -> 70 | clear(); 71 | reply(_Reason, FromMsId, _Ms) -> 72 | io:format("~p connecting to ~p failed:~w~n",[element(2,hlr:lookup_ms(FromMsId)), 73 | _Ms, _Reason]), 74 | clear(). 75 | 76 | clear() -> 77 | case get(pid) of 78 | undefined -> ok; 79 | Pid -> 80 | exit(Pid, kill), erase(pid), 81 | io:format("~p cleared~n",[self()]) 82 | end. 83 | -------------------------------------------------------------------------------- /ch12/erlang/coffee.erl: -------------------------------------------------------------------------------- 1 | -module(coffee). 2 | -export([tea/0, espresso/0, americano/0, cappuccino/0, 3 | pay/1, cup_removed/0, cancel/0]). 4 | -export([start_link/0, init/0,code_change/2]). 5 | -vsn(1.0). 6 | 7 | start_link() -> 8 | {ok, spawn_link(?MODULE, init, [])}. 9 | 10 | init() -> 11 | register(?MODULE, self()), 12 | hw:reboot(), 13 | hw:display("Make Your Selection", []), 14 | selection(). 15 | 16 | %% Client Functions for Drink Selections 17 | tea() -> ?MODULE ! {selection, tea, 100}. 18 | espresso() -> ?MODULE ! {selection, espresso, 150}. 19 | americano() -> ?MODULE ! {selection, americano, 100}. 20 | cappuccino() -> ?MODULE ! {selection, cappuccino,150}. 21 | 22 | %% Client Functions for Actions 23 | cup_removed() -> ?MODULE ! cup_removed. 24 | pay(Coin) -> ?MODULE ! {pay, Coin}. 25 | cancel() -> ?MODULE ! cancel. 26 | 27 | %% State: drink selection 28 | selection() -> 29 | receive 30 | {selection, Type, Price} -> 31 | hw:display("Please pay:~w",[Price]), 32 | payment(Type, Price, 0); 33 | {pay, Coin} -> 34 | hw:return_change(Coin), 35 | selection(); 36 | {upgrade, Data} -> 37 | ?MODULE:code_change(fun selection/0, Data); 38 | _Other -> % cancel 39 | selection() 40 | end. 41 | 42 | %% State: payment 43 | payment(Type, Price, Paid) -> 44 | receive 45 | {pay, Coin} -> 46 | if Coin + Paid >= Price -> 47 | hw:display("Preparing Drink.",[]), 48 | hw:return_change(Coin + Paid - Price), 49 | hw:drop_cup(), hw:prepare(Type), 50 | hw:display("Remove Drink.", []), 51 | remove(); 52 | true -> 53 | ToPay = Price - (Coin + Paid), 54 | hw:display("Please pay:~w",[ToPay]), 55 | payment(Type, Price, Coin + Paid) 56 | end; 57 | cancel -> 58 | hw:display("Make Your Selection", []), 59 | hw:return_change(Paid), 60 | selection(); 61 | {upgrade, Data} -> 62 | ?MODULE:code_change({payment, Type, Price, Paid}, Data); 63 | _Other -> %selection 64 | payment(Type, Price, Paid) 65 | end. 66 | 67 | %% State: remove cup 68 | remove() -> 69 | receive 70 | cup_removed -> 71 | hw:display("Make Your Selection", []), 72 | selection(); 73 | {pay, Coin} -> 74 | hw:return_change(Coin), 75 | remove(); 76 | {upgrade, Data} -> 77 | ?MODULE:code_change(fun remove/0, Data); 78 | _Other -> % cancel/selection 79 | remove() 80 | end. 81 | 82 | code_change({payment, Type, Price, Paid}, _) -> 83 | payment(Type, Price, Paid); 84 | code_change(State, _) -> 85 | State(). 86 | -------------------------------------------------------------------------------- /ch10/mutex.erl: -------------------------------------------------------------------------------- 1 | -module(mutex). 2 | 3 | -export([start_link/1, start_link/2, init/3, stop/1]). 4 | -export([wait/1, signal/1]). 5 | -export([system_continue/3, system_terminate/4]). 6 | 7 | wait(Name) -> 8 | Name ! {wait,self()}, 9 | Mutex = whereis(Name), 10 | receive 11 | {Mutex,ok} -> ok 12 | end. 13 | 14 | signal(Name) -> 15 | Name ! {signal,self()}, 16 | ok. 17 | 18 | start_link(Name) -> 19 | start_link(Name, []). 20 | 21 | start_link(Name, DbgOpts) -> 22 | proc_lib:start_link(?MODULE, init, [self(), Name, DbgOpts]). 23 | 24 | stop(Name) -> Name ! stop. 25 | 26 | init(Parent, Name, DbgOpts) -> 27 | register(Name, self()), 28 | process_flag(trap_exit, true), 29 | Debug = sys:debug_options(DbgOpts), 30 | proc_lib:init_ack({ok,self()}), 31 | NewDebug = sys:handle_debug(Debug, fun debug/3, Name, init), 32 | free(Name, Parent, NewDebug). 33 | 34 | 35 | free(Name, Parent, Debug) -> 36 | receive 37 | {wait,Pid} -> %% The user requests. 38 | NewDebug = sys:handle_debug(Debug, fun debug/3, Name, {wait,Pid}), 39 | Pid ! {self(),ok}, 40 | busy(Pid, Name, Parent, NewDebug); 41 | {system,From,Msg} -> %% The system messages. 42 | sys:handle_system_msg(Msg, From, Parent, ?MODULE, Debug, {free, Name}); 43 | stop -> 44 | terminate(stopped, Name, Debug); 45 | {'EXIT',Parent,Reason} -> 46 | terminate(Reason, Name, Debug) 47 | end. 48 | 49 | busy(Pid, Name, Parent, Debug) -> 50 | receive 51 | {signal,Pid} -> 52 | NewDebug = sys:handle_debug(Debug, fun debug/3, Name, {signal,Pid}), 53 | free(Name, Parent, NewDebug); 54 | {system,From,Msg} -> %% The system messages. 55 | sys:handle_system_msg(Msg, From, Parent, ?MODULE, Debug, {busy,Name,Pid}); 56 | {'EXIT',Parent,Reason} -> 57 | exit(Pid, Reason), 58 | terminate(Reason, Name, Debug) 59 | end. 60 | 61 | debug(Dev, Event, Name) -> 62 | io:format(Dev, "mutex ~w: ~w~n", [Name,Event]). 63 | 64 | system_continue(Parent, Debug, {busy,Name,Pid}) -> 65 | busy(Pid, Name, Parent, Debug); 66 | system_continue(Parent, Debug, {free,Name}) -> 67 | free(Name, Parent, Debug). 68 | 69 | system_terminate(Reason, _Parent, Debug, {busy,Name,Pid}) -> 70 | exit(Pid, Reason), 71 | terminate(Reason, Name, Debug); 72 | system_terminate(Reason, _Parent, Debug, {free,Name}) -> 73 | terminate(Reason, Name, Debug). 74 | 75 | terminate(Reason, Name, Debug) -> 76 | unregister(Name), 77 | sys:handle_debug(Debug, fun debug/3, Name, {terminate, Reason}), 78 | terminate(Reason). 79 | terminate(Reason) -> 80 | receive 81 | {wait,Pid} -> 82 | exit(Pid, Reason), 83 | terminate(Reason) 84 | after 0 -> 85 | exit(Reason) 86 | end. 87 | -------------------------------------------------------------------------------- /ch11/bsc/src/phone.erl: -------------------------------------------------------------------------------- 1 | %%% @author Francesco Cesarini 2 | %%% @copyright (C) 2013, Francesco Cesarini 3 | %%% @doc phone.erl is a mobile phone simulator which can be used to test the 4 | %%% phone_fsm.erl finite state machine, handling the phone state on the server side. 5 | %%% 6 | %%% @end 7 | %%% Created : 9 Jun 2013 by Francesco Cesarini 8 | 9 | -module(phone). 10 | 11 | -compile(export_all). 12 | -define(TIMEOUT, 60000). 13 | 14 | 15 | start_test(Num, Calls) -> 16 | [phone_fsm:start_link(X) || X <- lists:seq(1,Num)], 17 | call(Calls, Num). 18 | 19 | 20 | 21 | call(0,_) -> ok; 22 | call(X, Num) -> 23 | FromMs = random:uniform(Num), 24 | ToMs = random:uniform(Num), 25 | {ok, FromMsId} = hlr:lookup_id(FromMs), 26 | case phone_fsm:action({outbound, ToMs}, FromMsId) of 27 | ok -> 28 | call(X-1, Num); 29 | _error -> 30 | call(X, Num) 31 | end. 32 | 33 | reply(outbound, _ToMsId, Ms) -> 34 | clear(), 35 | FromMsId = self(), 36 | io:format("~p dialing ~p~n",[FromMsId, _ToMsId]), 37 | F = fun() -> 38 | random:seed(erlang:system_time()), 39 | timer:sleep(random:uniform(3000)), 40 | io:format("~p hanging up ~p~n",[FromMsId, Ms]), 41 | phone_fsm:action(hangup, FromMsId) 42 | end, 43 | put(pid, spawn(F)); 44 | 45 | reply(connected, OtherMsId, _Ms) -> 46 | clear(), 47 | FromMsId = self(), 48 | io:format("~p connected to ~p~n",[FromMsId, OtherMsId]), 49 | F = fun() -> 50 | random:seed(erlang:system_time()), 51 | timer:sleep(random:uniform(3000)), 52 | io:format("~p hanging up ~p~n",[FromMsId, OtherMsId]), 53 | phone_fsm:action(hangup, FromMsId) 54 | end, 55 | put(pid, spawn(F)); 56 | reply(invalid, _ToMs, _Ms) -> 57 | io:format("~p connecting to ~p failed:invalid number~n",[_ToMs, _Ms]), 58 | clear(); 59 | reply(inbound, _FromMsId, _Ms) -> 60 | clear(), 61 | ToMsId = self(), 62 | F = fun() -> 63 | random:seed(erlang:system_time()), 64 | timer:sleep(random:uniform(1500)), 65 | case random:uniform(2) of 66 | 1 -> 67 | io:format("accept(~p,~p)~n",[ToMsId, _FromMsId]), 68 | phone_fsm:action(accept, ToMsId), 69 | timer:sleep(random:uniform(3000)), 70 | phone_fsm:action(hangup, ToMsId); 71 | 2 -> 72 | phone_fsm:action(reject, ToMsId) 73 | end 74 | end, 75 | put(pid, spawn(F)); 76 | reply(hangup, _FromMsId, _Ms) -> 77 | clear(); 78 | reply(_Reason, FromMsId, _Ms) -> 79 | io:format("~p connecting to ~p failed:~w~n",[element(2,hlr:lookup_ms(FromMsId)), 80 | _Ms, _Reason]), 81 | clear(). 82 | 83 | 84 | clear() -> 85 | case get(pid) of 86 | undefined -> ok; 87 | Pid -> 88 | exit(Pid, kill), erase(pid), 89 | io:format("~p cleared~n",[self()]) 90 | end. 91 | 92 | 93 | -------------------------------------------------------------------------------- /ch6/otp/coffee_fsm.erl: -------------------------------------------------------------------------------- 1 | -module(coffee_fsm). 2 | -behaviour(gen_fsm). 3 | 4 | -export([start_link/0, stop/0]). 5 | -export([init/1, terminate/3, handle_event/3]). 6 | -export([selection/2, payment/2, remove/2]). 7 | -export([americano/0, cappuccino/0, tea/0, espresso/0, 8 | pay/1,cancel/0, cup_removed/0]). 9 | 10 | 11 | start_link() -> 12 | gen_fsm:start_link({local, ?MODULE}, ?MODULE, [], []). 13 | 14 | init([]) -> 15 | hw:reboot(), 16 | hw:display("Make Your Selection", []), 17 | process_flag(trap_exit, true), 18 | {ok, selection, []}. 19 | 20 | %% Client Functions for Drink Selections 21 | tea() -> gen_fsm:send_event(?MODULE,{selection,tea,100}). 22 | espresso() ->gen_fsm:send_event(?MODULE,{selection,espresso,150}). 23 | americano() -> gen_fsm:send_event(?MODULE,{selection,americano,150}). 24 | cappuccino() -> gen_fsm:send_event(?MODULE,{selection,cappuccino,150}). 25 | 26 | 27 | 28 | %% Client Functions for Actions 29 | pay(Coin) -> gen_fsm:send_event(?MODULE,{pay, Coin}). 30 | cancel() -> gen_fsm:send_event(?MODULE,cancel). 31 | cup_removed() -> gen_fsm:send_event(?MODULE,cup_removed). 32 | 33 | 34 | 35 | %% State: drink selection 36 | selection({selection,Type,Price}, _LoopData) -> 37 | hw:display("Please pay:~w",[Price]), 38 | {next_state, payment, {Type, Price, 0}}; 39 | selection({pay, Coin}, LoopData) -> 40 | hw:return_change(Coin), 41 | {next_state, selection, LoopData}; 42 | selection(_Other, LoopData) -> 43 | {next_state, selection, LoopData}. 44 | 45 | 46 | payment({pay, Coin}, {Type,Price,Paid}) 47 | when Coin+Paid >= Price -> 48 | NewPaid = Coin + Paid, 49 | hw:display("Preparing Drink.",[]), 50 | hw:return_change(NewPaid - Price), 51 | hw:drop_cup(), hw:prepare(Type), 52 | hw:display("Remove Drink.", []), 53 | {next_state, remove, []}; 54 | payment({pay, Coin}, {Type,Price,Paid}) 55 | when Coin+Paid < Price -> 56 | NewPaid = Coin + Paid, 57 | hw:display("Please pay:~w",[Price - NewPaid]), 58 | {next_state, payment, {Type, Price, NewPaid}}; 59 | payment(cancel, {_Type, _Price, Paid}) -> 60 | hw:display("Make Your Selection", []), 61 | hw:return_change(Paid), 62 | {next_state, selection, []}; 63 | payment(_Other, LoopData) -> 64 | {next_state, payment, LoopData}. 65 | 66 | 67 | %% State: remove cup
 68 | remove(cup_removed, LoopData) -> 69 | hw:display("Make Your Selection", []), 70 | {next_state, selection, LoopData}; 71 | remove({pay, Coin}, LoopData) -> 72 | hw:return_change(Coin), 73 | {next_state, remove, LoopData}; 74 | remove(_Other, LoopData) -> 75 | {next_state, remove, LoopData}. 76 | 77 | 78 | stop() -> gen_fsm:sync_send_all_state_event(?MODULE, stop). 79 | 80 | handle_event(stop, State, LoopData) -> 81 | {stop, normal, LoopData}. 82 | 83 | terminate(_Reason, payment, {_Type,_Price,Paid}) -> 84 | hw:return_change(Paid); 85 | terminate(_Reason, _StateName, _LoopData) -> 86 | ok. 87 | 88 | -------------------------------------------------------------------------------- /ch12/coffee-1.0/src/coffee_fsm.erl: -------------------------------------------------------------------------------- 1 | -module(coffee_fsm). 2 | -behaviour(gen_fsm). 3 | -export([start_link/0, init/1]). 4 | -export([selection/2, payment/2, remove/2]). 5 | -export([americano/0, cappuccino/0, tea/0, espresso/0, 6 | pay/1,cancel/0, cup_removed/0]). 7 | 8 | -export([stop/0, selection/3, payment/3, remove/3]). 9 | -export([terminate/3]). 10 | -vsn('1.0'). 11 | 12 | start_link() -> 13 | gen_fsm:start_link({local, ?MODULE}, ?MODULE, [], []). 14 | 15 | init([]) -> 16 | hw:reboot(), 17 | hw:display("Make Your Selection", []), 18 | {ok, selection, []}. 19 | 20 | %% Client Functions for Drink Selections 21 | tea() -> gen_fsm:send_event(?MODULE,{selection,tea,100}). 22 | espresso() ->gen_fsm:send_event(?MODULE,{selection,espresso,150}). 23 | americano() -> gen_fsm:send_event(?MODULE,{selection,americano,100}). 24 | cappuccino() -> gen_fsm:send_event(?MODULE,{selection,cappuccino,150}). 25 | 26 | 27 | 28 | %% Client Functions for Actions 29 | pay(Coin) -> gen_fsm:send_event(?MODULE,{pay, Coin}). 30 | cancel() -> gen_fsm:send_event(?MODULE,cancel). 31 | cup_removed() -> gen_fsm:send_event(?MODULE,cup_removed). 32 | 33 | 34 | 35 | %% State: drink selection 36 | selection({selection,Type,Price}, _LoopData) -> 37 | hw:display("Please pay:~w",[Price]), 38 | {next_state, payment, {Type, Price, 0}}; 39 | selection({pay, Coin}, LoopData) -> 40 | hw:return_change(Coin), 41 | {next_state, selection, LoopData}; 42 | selection(_Other, LoopData) -> 43 | {next_state, selection, LoopData}. 44 | 45 | 46 | payment({pay, Coin}, {Type,Price,Paid}) 47 | when Coin+Paid >= Price -> 48 | NewPaid = Coin + Paid, 49 | hw:display("Preparing Drink.",[]), 50 | hw:return_change(NewPaid - Price), 51 | hw:drop_cup(), hw:prepare(Type), 52 | hw:display("Remove Drink.", []), 53 | {next_state, remove, []}; 54 | payment({pay, Coin}, {Type,Price,Paid}) 55 | when Coin+Paid < Price -> 56 | NewPaid = Coin + Paid, 57 | hw:display("Please pay:~w",[Price - NewPaid]), 58 | {next_state, payment, {Type, Price, NewPaid}}; 59 | payment(cancel, {_Type, _Price, Paid}) -> 60 | hw:display("Make Your Selection", []), 61 | hw:return_change(Paid), 62 | {next_state, selection, []}; 63 | payment(_Other, LoopData) -> 64 | {next_state, payment, LoopData}. 65 | 66 | 67 | %% State: remove cup 68 | remove(cup_removed, LoopData) -> 69 | hw:display("Make Your Selection", []), 70 | {next_state, selection, LoopData}; 71 | remove({pay, Coin}, LoopData) -> 72 | hw:return_change(Coin), 73 | {next_state, remove, LoopData}; 74 | remove(_Other, LoopData) -> 75 | {next_state, remove, LoopData}. 76 | 77 | 78 | stop() -> gen_fsm:sync_send_event(?MODULE, stop). 79 | 80 | remove(stop, _From, LoopData) -> 81 | {stop, normal, ok, LoopData}. 82 | selection(stop, _From, LoopData) -> 83 | {stop, normal, ok, LoopData}. 84 | payment(stop, _From, Paid) -> 85 | hw:return_change(Paid), 86 | {stop, normal, ok, 0}. 87 | 88 | terminate(_Reason, _StateName, _LoopData) -> 89 | ok. 90 | -------------------------------------------------------------------------------- /ch6/otp/coffee_fsm_timeout.erl: -------------------------------------------------------------------------------- 1 | -module(coffee_fsm_timeout). 2 | -behaviour(gen_fsm). 3 | 4 | -export([start_link/0, stop/0]). 5 | -export([init/1, terminate/3, handle_event/3]). %% Callback functions 6 | -export([selection/2, payment/2, remove/2]). %% States 7 | -export([americano/0, cappuccino/0, tea/0, %% Client Functions 8 | espresso/0, pay/1, cancel/0, cup_removed/0]). 9 | 10 | -define(TIMEOUT, 10000). 11 | 12 | start_link() -> 13 | gen_fsm:start_link({local, ?MODULE}, ?MODULE, [], []). 14 | 15 | init([]) -> 16 | hw:reboot(), 17 | hw:display("Make Your Selection", []), 18 | process_flag(trap_exit, true), 19 | {ok, selection, []}. 20 | 21 | %% Client Functions for Drink Selections 22 | tea() -> gen_fsm:send_event(?MODULE,{selection,tea,100}). 23 | espresso() -> gen_fsm:send_event(?MODULE,{selection,espresso,150}). 24 | americano() -> gen_fsm:send_event(?MODULE,{selection,americano,150}). 25 | cappuccino() -> gen_fsm:send_event(?MODULE,{selection,cappuccino,150}). 26 | 27 | %% Client Functions for Actions 28 | pay(Coin) -> gen_fsm:send_event(?MODULE,{pay, Coin}). 29 | cancel() -> gen_fsm:send_event(?MODULE,cancel). 30 | cup_removed() -> gen_fsm:send_event(?MODULE,cup_removed). 31 | 32 | %% State: drink selection 33 | selection({selection,Type,Price}, _LoopData) -> 34 | hw:display("Please pay:~w",[Price]), 35 | {next_state, payment, {Type, Price, 0}, ?TIMEOUT}; 36 | selection({pay, Coin}, LoopData) -> 37 | hw:return_change(Coin), 38 | {next_state, selection, LoopData}; 39 | selection(_Other, LoopData) -> 40 | {next_state, selection, LoopData}. 41 | 42 | 43 | payment({pay, Coin}, {Type,Price,Paid}) when Coin+Paid >= Price -> 44 | NewPaid = Coin + Paid, 45 | hw:display("Preparing Drink.",[]), 46 | hw:return_change(NewPaid - Price), 47 | hw:drop_cup(), hw:prepare(Type), 48 | hw:display("Remove Drink.", []), 49 | {next_state, remove, []}; 50 | payment({pay, Coin}, {Type,Price,Paid}) when Coin+Paid < Price -> 51 | NewPaid = Coin + Paid, 52 | hw:display("Please pay:~w",[Price - NewPaid]), 53 | {next_state, payment, {Type, Price, NewPaid}, ?TIMEOUT}; 54 | payment(cancel, {_Type, _Price, Paid}) -> 55 | hw:display("Make Your Selection", []), 56 | hw:return_change(Paid), 57 | {next_state, selection, []}; 58 | payment(timeout, {_Type, _Price, Paid}) -> 59 | hw:display("Make Your Selection", []), 60 | hw:return_change(Paid), 61 | {next_state, selection, []}; 62 | payment(_Other, LoopData) -> 63 | {next_state, payment, LoopData, ?TIMEOUT}. 64 | 65 | 66 | %% State: remove cup
 67 | remove(cup_removed, LoopData) -> 68 | hw:display("Make Your Selection", []), 69 | {next_state, selection, LoopData}; 70 | remove({pay, Coin}, LoopData) -> 71 | hw:return_change(Coin), 72 | {next_state, remove, LoopData}; 73 | remove(_Other, LoopData) -> 74 | {next_state, remove, LoopData}. 75 | 76 | 77 | stop() -> gen_fsm:sync_send_all_state_event(?MODULE, stop). 78 | 79 | handle_event(stop, _State, LoopData) -> 80 | {stop, normal, LoopData}. 81 | 82 | terminate(_Reason, payment, {_Type,_Price,Paid}) -> 83 | hw:return_change(Paid); 84 | terminate(_Reason, _StateName, _LoopData) -> 85 | ok. 86 | -------------------------------------------------------------------------------- /ch4/frequency.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2015-2016 Francesco Cesarini 2 | 3 | -module(frequency). 4 | -behaviour(gen_server). 5 | 6 | -export([start/0, stop/0, allocate/0, deallocate/1]). 7 | -export([init/1, handle_call/3, handle_cast/2, terminate/2, handle_info/2]). 8 | -export([format_status/2]). 9 | 10 | %% CLIENT FUNCTIONS 11 | 12 | %% start() -> {ok, pid()} | {error, Reason} 13 | %% Starts the frequency server. Called by supervisor 14 | 15 | start() -> 16 | gen_server:start_link({local, frequency}, frequency, [], []). 17 | 18 | %% stop() -> ok. 19 | %% Stops the frequency server. 20 | 21 | stop() -> 22 | gen_server:cast(frequency, stop). 23 | 24 | 25 | %% allocate() -> {ok, Frequency} | {error, no_resource} 26 | %% If available, it returns a frequency used to make a call. 27 | %% Frequency must be deallocated on termination. 28 | 29 | allocate() -> 30 | gen_server:call(frequency, {allocate, self()}). 31 | 32 | %% deallocate() -> ok 33 | %% Frees a frequency so it can be used by another client. 34 | 35 | deallocate(Frequency) -> 36 | gen_server:cast(frequency, {deallocate, Frequency}). 37 | 38 | 39 | %% CALLBACK FUNCTIONS 40 | 41 | %% init(_) -> {ok, State} 42 | %% Initialises the generic server by getting the list of 43 | %% available frequencies. [] is the list of allocated ones. 44 | 45 | init(_Args) -> 46 | Frequencies = {get_frequencies(), []}, 47 | {ok, Frequencies}. 48 | 49 | %% Dummy function. To be replaced with call to BSC. 50 | 51 | get_frequencies() -> [10,11,12,13,14, 15]. 52 | 53 | 54 | %% handle_call({allocate, Pid}, _, {Available, Allocated}) -> 55 | %% {reply, {ok, Frequency} | {error, no_resource}, {Available, Allocated}} 56 | %% Callback for allocating resources. 57 | 58 | handle_call({allocate, Pid}, _From, Frequencies) -> 59 | {NewFrequencies, Reply} = allocate(Frequencies, Pid), 60 | {reply, Reply, NewFrequencies}. 61 | 62 | 63 | %% handle_cast({deallocate, Freq}, Frequencies) -> {noreply, NewFrequencies}; 64 | %% Callback for deallocating resources 65 | 66 | handle_cast({deallocate, Freq}, Frequencies) -> 67 | NewFrequencies = deallocate(Frequencies, Freq), 68 | {noreply, NewFrequencies}; 69 | 70 | %% handle_cast(stop, LoopData) -> {stop, normal, LoopData}. 71 | %% callback to stop the gen_server. 72 | 73 | handle_cast(stop, LoopData) -> 74 | {stop, normal, LoopData}. 75 | 76 | handle_info(_Msg, LoopData) -> 77 | {noreply, LoopData}. 78 | 79 | %% terminate(Reason, LoopData) -> ok. 80 | %% Termination callback. Does nothing, but should instead kill clients. 81 | 82 | terminate(_Reason, _LoopData) -> 83 | ok. 84 | 85 | format_status(_Opt, [_ProcDict, {Available, Allocated}]) -> 86 | {data, [{"State", {{available, Available}, {allocated, Allocated}}}]}. 87 | 88 | %% INTERNAL FUNCTIONS 89 | 90 | %% Helper functions used to allocate and deallocate resources. 91 | 92 | allocate({[], Allocated}, _Pid) -> 93 | {{[], Allocated}, {error, no_frequency}}; 94 | allocate({[Res|Resources], Allocated}, Pid) -> 95 | {{Resources, [{Res, Pid}|Allocated]}, {ok, Res}}. 96 | 97 | deallocate({Free, Allocated}, Res) -> 98 | NewAllocated = lists:keydelete(Res, 1, Allocated), 99 | {[Res|Free], NewAllocated}. 100 | -------------------------------------------------------------------------------- /ch5/frequency.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (C) 2015-2016 Francesco Cesarini 2 | 3 | -module(frequency). 4 | -behaviour(gen_server). 5 | 6 | -export([start/0, stop/0, allocate/0, deallocate/1]). 7 | -export([init/1, handle_call/3, handle_cast/2, terminate/2, handle_info/2]). 8 | -export([format_status/2]). 9 | 10 | %% CLIENT FUNCTIONS 11 | 12 | %% start() -> {ok, pid()} | {error, Reason} 13 | %% Starts the frequency server. Called by supervisor 14 | 15 | start() -> 16 | gen_server:start_link({local, frequency}, frequency, [], []). 17 | 18 | %% stop() -> ok. 19 | %% Stops the frequency server. 20 | 21 | stop() -> 22 | gen_server:cast(frequency, stop). 23 | 24 | 25 | %% allocate() -> {ok, Frequency} | {error, no_resource} 26 | %% If available, it returns a frequency used to make a call. 27 | %% Frequency must be deallocated on termination. 28 | 29 | allocate() -> 30 | gen_server:call(frequency, {allocate, self()}). 31 | 32 | %% deallocate() -> ok 33 | %% Frees a frequency so it can be used by another client. 34 | 35 | deallocate(Frequency) -> 36 | gen_server:cast(frequency, {deallocate, Frequency}). 37 | 38 | 39 | %% CALLBACK FUNCTIONS 40 | 41 | %% init(_) -> {ok, State} 42 | %% Initialises the generic server by getting the list of 43 | %% available frequencies. [] is the list of allocated ones. 44 | 45 | init(_Args) -> 46 | Frequencies = {get_frequencies(), []}, 47 | {ok, Frequencies}. 48 | 49 | %% Dummy function. To be replaced with call to BSC. 50 | 51 | get_frequencies() -> [10,11,12,13,14, 15]. 52 | 53 | 54 | %% handle_call({allocate, Pid}, _, {Available, Allocated}) -> 55 | %% {reply, {ok, Frequency} | {error, no_resource}, {Available, Allocated}} 56 | %% Callback for allocating resources. 57 | 58 | handle_call({allocate, Pid}, _From, Frequencies) -> 59 | {NewFrequencies, Reply} = allocate(Frequencies, Pid), 60 | {reply, Reply, NewFrequencies}. 61 | 62 | 63 | %% handle_cast({deallocate, Freq}, Frequencies) -> {noreply, NewFrequencies}; 64 | %% Callback for deallocating resources 65 | 66 | handle_cast({deallocate, Freq}, Frequencies) -> 67 | NewFrequencies = deallocate(Frequencies, Freq), 68 | {noreply, NewFrequencies}; 69 | 70 | %% handle_cast(stop, LoopData) -> {stop, normal, LoopData}. 71 | %% callback to stop the gen_server. 72 | 73 | handle_cast(stop, LoopData) -> 74 | {stop, normal, LoopData}. 75 | 76 | handle_info(_Msg, LoopData) -> 77 | {noreply, LoopData}. 78 | 79 | %% terminate(Reason, LoopData) -> ok. 80 | %% Termination callback. Does nothing, but should instead kill clients. 81 | 82 | terminate(_Reason, _LoopData) -> 83 | ok. 84 | 85 | format_status(_Opt, [_ProcDict, {Available, Allocated}]) -> 86 | {data, [{"State", {{available, Available}, {allocated, Allocated}}}]}. 87 | 88 | %% INTERNAL FUNCTIONS 89 | 90 | %% Helper functions used to allocate and deallocate resources. 91 | 92 | allocate({[], Allocated}, _Pid) -> 93 | {{[], Allocated}, {error, no_frequency}}; 94 | allocate({[Res|Resources], Allocated}, Pid) -> 95 | {{Resources, [{Res, Pid}|Allocated]}, {ok, Res}}. 96 | 97 | deallocate({Free, Allocated}, Res) -> 98 | NewAllocated = lists:keydelete(Res, 1, Allocated), 99 | {[Res|Free], NewAllocated}. 100 | -------------------------------------------------------------------------------- /ch7/frequency.erl: -------------------------------------------------------------------------------- 1 | -module(frequency). 2 | -behaviour(gen_server). 3 | 4 | -export([start_link/0, stop/0, allocate/0, deallocate/1]). 5 | -export([init/1, handle_call/3, handle_cast/2, terminate/2, handle_info/2]). 6 | -export([format_status/2]). 7 | 8 | %% CLIENT FUNCTIONS 9 | 10 | %% start() -> {ok, pid()} | {error, Reason} 11 | %% Starts the frequency server. Called by supervisor 12 | 13 | start_link() -> 14 | gen_server:start_link({local, frequency}, frequency, [], []). 15 | 16 | %% stop() -> ok. 17 | %% Stops the frequency server. 18 | 19 | stop() -> 20 | gen_server:cast(frequency, stop). 21 | 22 | 23 | %% allocate() -> {ok, Frequency} | {error, no_resource} 24 | %% If available, it returns a frequency used to make a call. 25 | %% Frequency must be deallocated on termination. 26 | 27 | allocate() -> 28 | gen_server:call(frequency, {allocate, self()}). 29 | 30 | %% deallocate() -> ok 31 | %% Frees a frequency so it can be used by another client. 32 | 33 | deallocate(Frequency) -> 34 | gen_server:cast(frequency, {deallocate, Frequency }). 35 | 36 | 37 | %% CALLBACK FUNCTIONS 38 | 39 | %% init(_) -> {ok, State} 40 | %% Initialises the generic server by getting the list of 41 | %% available frequencies. [] is the list of allocated ones. 42 | 43 | init(_Args) -> 44 | Frequencies = {get_frequencies(), []}, 45 | {ok, Frequencies}. 46 | 47 | %% Dummy function. To be replaced with call to BSC. 48 | 49 | get_frequencies() -> [10,11,12,13,14, 15]. 50 | 51 | 52 | %% handle_call({allocate, Pid}, _, {Available, Allocated}) -> 53 | %% {reply, {ok, Frequency} | {error, no_resource}, {Available, Allocated}} 54 | %% Callback for allocating resources. 55 | 56 | handle_call({allocate, Pid}, _From, Frequencies) -> 57 | {NewFrequencies, Reply} = allocate(Frequencies, Pid), 58 | {reply, Reply, NewFrequencies}. 59 | 60 | 61 | %% handle_cast({deallocate, Freq}, Frequencies) -> {noreply, NewFrequencies}; 62 | %% Callback for deallocating resources 63 | 64 | handle_cast({deallocate, Freq}, Frequencies) -> 65 | NewFrequencies = deallocate(Frequencies, Freq), 66 | {noreply, NewFrequencies}; 67 | 68 | %% handle_cast(stop, LoopData) -> {stop, normal, LoopData}. 69 | %% callback to stop the gen_server. 70 | 71 | handle_cast(stop, LoopData) -> 72 | {stop, normal, LoopData}. 73 | 74 | handle_info(_Msg, LoopData) -> 75 | {noreply, LoopData}. 76 | 77 | %% terminate(Reason, LoopData) -> ok. 78 | %% Termination callback. Does nothing, but should instead kill clients. 79 | 80 | terminate(_Reason, _LoopData) -> 81 | ok. 82 | 83 | format_status(_Opt, [_ProcDict, {Available, Allocated}]) -> 84 | {data, [{"State", {{available, Available}, {allocated, Allocated}}}]}. 85 | 86 | %% INTERNAL FUNCTIONS 87 | 88 | %% Helper functions used to allocate and deallocate resources. 89 | 90 | allocate({[], Allocated}, _Pid) -> 91 | freq_overload:frequency_denied(), 92 | {{[], Allocated}, {error, no_frequency}}; 93 | allocate({[Res|Resources], Allocated}, Pid) -> 94 | case Resources of 95 | [] -> freq_overload:no_frequency(); 96 | _ -> ok 97 | end, 98 | {{Resources, [{Res, Pid}|Allocated]}, {ok, Res}}. 99 | 100 | deallocate({Free, Allocated}, Res) -> 101 | case Free of 102 | [] -> freq_overload:frequency_available(); 103 | _ -> ok 104 | end, 105 | NewAllocated = lists:keydelete(Res, 1, Allocated), 106 | {[Res|Free], NewAllocated}. 107 | -------------------------------------------------------------------------------- /ch8/otp/frequency.erl: -------------------------------------------------------------------------------- 1 | -module(frequency). 2 | -behaviour(gen_server). 3 | 4 | -export([start_link/0, stop/0, allocate/0, deallocate/1]). 5 | -export([init/1, handle_call/3, handle_cast/2, terminate/2, handle_info/2]). 6 | -export([format_status/2]). 7 | 8 | %% CLIENT FUNCTIONS 9 | 10 | %% start() -> {ok, pid()} | {error, Reason} 11 | %% Starts the frequency server. Called by supervisor 12 | 13 | start_link() -> 14 | gen_server:start_link({local, frequency}, frequency, [], []). 15 | 16 | %% stop() -> ok. 17 | %% Stops the frequency server. 18 | 19 | stop() -> 20 | gen_server:cast(frequency, stop). 21 | 22 | 23 | %% allocate() -> {ok, Frequency} | {error, no_resource} 24 | %% If available, it returns a frequency used to make a call. 25 | %% Frequency must be deallocated on termination. 26 | 27 | allocate() -> 28 | gen_server:call(frequency, {allocate, self()}). 29 | 30 | %% deallocate() -> ok 31 | %% Frees a frequency so it can be used by another client. 32 | 33 | deallocate(Frequency) -> 34 | gen_server:cast(frequency, {deallocate, Frequency }). 35 | 36 | 37 | %% CALLBACK FUNCTIONS 38 | 39 | %% init(_) -> {ok, State} 40 | %% Initialises the generic server by getting the list of 41 | %% available frequencies. [] is the list of allocated ones. 42 | 43 | init(_Args) -> 44 | Frequencies = {get_frequencies(), []}, 45 | {ok, Frequencies}. 46 | 47 | %% Dummy function. To be replaced with call to BSC. 48 | 49 | get_frequencies() -> [10,11,12,13,14, 15]. 50 | 51 | 52 | %% handle_call({allocate, Pid}, _, {Available, Allocated}) -> 53 | %% {reply, {ok, Frequency} | {error, no_resource}, {Available, Allocated}} 54 | %% Callback for allocating resources. 55 | 56 | handle_call({allocate, Pid}, _From, Frequencies) -> 57 | {NewFrequencies, Reply} = allocate(Frequencies, Pid), 58 | {reply, Reply, NewFrequencies}. 59 | 60 | 61 | %% handle_cast({deallocate, Freq}, Frequencies) -> {noreply, NewFrequencies}; 62 | %% Callback for deallocating resources 63 | 64 | handle_cast({deallocate, Freq}, Frequencies) -> 65 | NewFrequencies = deallocate(Frequencies, Freq), 66 | {noreply, NewFrequencies}; 67 | 68 | %% handle_cast(stop, LoopData) -> {stop, normal, LoopData}. 69 | %% callback to stop the gen_server. 70 | 71 | handle_cast(stop, LoopData) -> 72 | {stop, normal, LoopData}. 73 | 74 | handle_info(_Msg, LoopData) -> 75 | {noreply, LoopData}. 76 | 77 | %% terminate(Reason, LoopData) -> ok. 78 | %% Termination callback. Does nothing, but should instead kill clients. 79 | 80 | terminate(_Reason, _LoopData) -> 81 | ok. 82 | 83 | format_status(_Opt, [_ProcDict, {Available, Allocated}]) -> 84 | {data, [{"State", {{available, Available}, {allocated, Allocated}}}]}. 85 | 86 | %% INTERNAL FUNCTIONS 87 | 88 | %% Helper functions used to allocate and deallocate resources. 89 | 90 | allocate({[], Allocated}, _Pid) -> 91 | freq_overload:frequency_denied(), 92 | {{[], Allocated}, {error, no_frequency}}; 93 | allocate({[Res|Resources], Allocated}, Pid) -> 94 | case Resources of 95 | [] -> freq_overload:no_frequency(); 96 | _ -> ok 97 | end, 98 | {{Resources, [{Res, Pid}|Allocated]}, {ok, Res}}. 99 | 100 | deallocate({Free, Allocated}, Res) -> 101 | case Free of 102 | [] -> freq_overload:frequency_available(); 103 | _ -> ok 104 | end, 105 | NewAllocated = lists:keydelete(Res, 1, Allocated), 106 | {[Res|Free], NewAllocated}. 107 | -------------------------------------------------------------------------------- /ch10/tcp_wrapper.erl: -------------------------------------------------------------------------------- 1 | -module(tcp_wrapper). 2 | -export([start_link/2, cast/3]). 3 | -export([init/3, system_continue/3, system_terminate/4, init_request/2]). 4 | %-export([behaviour_info/1]). 5 | 6 | -callback init_request() -> {'ok', Reply :: term()}. 7 | -callback get_request(Data :: term(), LoopData :: term()) -> {'ok', Reply :: term()} | 8 | {'stop', Reason :: atom(), LoopData :: term()}. 9 | -callback stop_request(Reason :: term(), LoopData :: term()) -> term(). 10 | 11 | %behaviour_info(callbacks) -> 12 | % [{init_request, 0},{get_request, 2},{stop_request, 2}]. 13 | 14 | start_link(Mod, Port) -> 15 | proc_lib:start_link(?MODULE, init, [Mod, Port, self()]). 16 | 17 | cast(Host, Port, Data) -> 18 | {ok, Socket} = gen_tcp:connect(Host, Port, [binary, {active, false}, {reuseaddr, true}]), 19 | send(Socket, Data), 20 | ok = gen_tcp:close(Socket). 21 | 22 | send(Socket, <>) -> 23 | gen_tcp:send(Socket, [Chunk]), 24 | send(Socket, Rest); 25 | send(Socket, <>) -> 26 | gen_tcp:send(Socket, Rest). 27 | 28 | init(Mod, Port, Parent) -> 29 | {ok, Listener} = gen_tcp:listen(Port, [{active, false}]), 30 | proc_lib:init_ack({ok, self()}), 31 | loop(Mod, Listener, Parent, sys:debug_options([])). 32 | 33 | loop(Mod, Listener, Parent, Debug) -> 34 | receive 35 | {system,From,Msg} -> 36 | sys:handle_system_msg(Msg, From, Parent, ?MODULE, Debug, {Listener, Mod}); 37 | {'EXIT', Parent, Reason} -> 38 | terminate(Reason, Listener, Debug); 39 | {'EXIT', Child, _Reason} -> 40 | NewDebug = sys:handle_debug(Debug, fun debug/3, stop_request, Child), 41 | loop(Mod, Listener, Parent, NewDebug) 42 | after 0 -> 43 | accept(Mod, Listener, Parent, Debug) 44 | end. 45 | 46 | accept(Mod, Listener, Parent, Debug) -> 47 | case gen_tcp:accept(Listener, 1000) of 48 | {ok, Socket} -> 49 | Pid = proc_lib:spawn_link(?MODULE, init_request, [Mod, Socket]), 50 | gen_tcp:controlling_process(Socket, Pid), 51 | NewDebug = sys:handle_debug(Debug, fun debug/3, init_request, Pid), 52 | loop(Mod, Listener, Parent, NewDebug); 53 | {error, timeout} -> 54 | loop(Mod, Listener, Parent, Debug); 55 | {error, Reason} -> 56 | NewDebug = sys:handle_debug(Debug, fun debug/3, error, Reason), 57 | terminate(Reason, Listener, NewDebug) 58 | end. 59 | 60 | system_continue(Parent, Debug, {Listener, Mod}) -> 61 | loop(Mod, Listener, Parent, Debug). 62 | 63 | system_terminate(Reason, _Parent, Debug, {Listener, _Mod}) -> 64 | terminate(Reason, Listener, Debug). 65 | 66 | terminate(Reason, Listener, Debug) -> 67 | sys:handle_debug(Debug, fun debug/3, terminating, Reason), 68 | gen_tcp:close(Listener), 69 | exit(Reason). 70 | 71 | debug(Dev, Event, Data) -> 72 | io:format(Dev, "Listener ~w:~w~n", [Event,Data]). 73 | 74 | init_request(Mod, Socket) -> 75 | {ok, LoopData} = Mod:init_request(), 76 | get_request(Mod, Socket, LoopData). 77 | 78 | get_request(Mod, Socket, LoopData) -> 79 | case gen_tcp:recv(Socket, 0) of 80 | {ok, Data} -> 81 | case Mod:get_request(Data, LoopData) of 82 | {ok, NewLoopData} -> 83 | get_request(Mod, Socket, NewLoopData); 84 | {stop, Reason, NewLoopData} -> 85 | gen_tcp:close(Socket), 86 | stop_request(Mod, Reason, NewLoopData) 87 | end; 88 | {error, Reason} -> 89 | stop_request(Mod, Reason, LoopData) 90 | end. 91 | 92 | stop_request(Mod, Reason, LoopData) -> 93 | Mod:stop_request(Reason, LoopData). 94 | -------------------------------------------------------------------------------- /ch12/erlang/patches/coffee.erl: -------------------------------------------------------------------------------- 1 | -module(coffee). 2 | -export([tea/0, espresso/0, americano/0, cappuccino/0, 3 | pay/1, cup_removed/0, cancel/0, open/0, close/0]). 4 | -export([start_link/0, init/0, code_change/2]). 5 | -vsn(1.1). 6 | 7 | start_link() -> 8 | {ok, spawn_link(?MODULE, init, [])}. 9 | 10 | init() -> 11 | register(?MODULE, self()), 12 | hw:reboot(), 13 | hw:display("Make Your Selection", []), 14 | selection(). 15 | 16 | %% Client Functions for Drink Selections 17 | tea() -> ?MODULE ! {selection, tea, 100}. 18 | espresso() -> ?MODULE ! {selection, espresso, 150}. 19 | americano() -> ?MODULE ! {selection, americano, 100}. 20 | cappuccino() -> ?MODULE ! {selection, cappuccino,150}. 21 | 22 | %% Client Functions for Actions 23 | cup_removed() -> ?MODULE ! cup_removed. 24 | pay(Coin) -> ?MODULE ! {pay, Coin}. 25 | cancel() -> ?MODULE ! cancel. 26 | open() -> ?MODULE ! open. 27 | close() -> ?MODULE ! close. 28 | 29 | %% State: drink selection 30 | selection() -> 31 | receive 32 | {selection, Type, Price} -> 33 | hw:display("Please pay:~w",[Price]), 34 | payment(Type, Price, 0); 35 | {pay, Coin} -> 36 | hw:return_change(Coin), 37 | selection(); 38 | {upgrade, Extra} -> 39 | ?MODULE:code_change(fun selection/0, Extra); 40 | open -> 41 | hw:display("Open", []), 42 | service(); 43 | _Other -> % cancel 44 | selection() 45 | end. 46 | 47 | %% State: payment 48 | payment(Type, Price, Paid) -> 49 | receive 50 | {pay, Coin} -> 51 | if Coin + Paid >= Price -> 52 | hw:display("Preparing Drink.",[]), 53 | hw:return_change(Coin + Paid - Price), 54 | hw:drop_cup(), hw:prepare(Type), 55 | hw:display("Remove Drink.", []), 56 | remove(); 57 | true -> 58 | ToPay = Price - (Coin + Paid), 59 | hw:display("Please pay:~w",[ToPay]), 60 | payment(Type, Price, Coin + Paid) 61 | end; 62 | cancel -> 63 | hw:display("Make Your Selection", []), 64 | hw:return_change(Paid), 65 | selection(); 66 | {upgrade, Extra} -> 67 | ?MODULE:code_change({payment, Type, 68 | Price, Paid}, Extra); 69 | _Other -> % selection 70 | payment(Type, Price, Paid) 71 | end. 72 | 73 | %% State: remove cup 74 | remove() -> 75 | receive 76 | cup_removed -> 77 | hw:display("Make Your Selection", []), 78 | selection(); 79 | {pay, Coin} -> 80 | hw:return_change(Coin), 81 | remove(); 82 | {upgrade, Extra} -> 83 | ?MODULE:code_change(fun remove/0, Extra); 84 | _Other -> % cancel/selection 85 | remove() 86 | end. 87 | 88 | service() -> 89 | receive 90 | close -> 91 | hw:reboot(), 92 | hw:display("Make Your Selection", []), 93 | service(); 94 | {pay, Coin} -> 95 | hw:return_change(Coin), 96 | service(); 97 | _Other -> 98 | service() 99 | end. 100 | 101 | code_change({payment, _Type, _Price, Paid}, _Extra) -> 102 | hw:return_change(Paid), 103 | hw:display("Make Your Selection", []), 104 | selection(); 105 | code_change(State, _) -> 106 | State(). 107 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/frequency.erl: -------------------------------------------------------------------------------- 1 | -module(frequency). 2 | -behaviour(gen_server). 3 | -export([start_link/0, stop/0, allocate/0, deallocate/1]). 4 | -export([init/1, handle_call/3, handle_cast/2, terminate/2, handle_info/2]). 5 | -export([format_status/2]). 6 | 7 | %% CLIENT FUNCTIONS 8 | 9 | %% start() -> {ok, pid()} | {error, Reason} 10 | %% Starts the frequency server. Called by supervisor 11 | 12 | start_link() -> 13 | gen_server:start_link({local, frequency}, frequency, [], []). 14 | 15 | %% stop() -> ok. 16 | %% Stops the frequency server. 17 | 18 | stop() -> 19 | gen_server:cast(frequency, stop). 20 | 21 | 22 | %% allocate() -> {ok, Frequency} | {error, no_resource} 23 | %% If available, it returns a frequency used to make a call. 24 | %% Frequency must be deallocated on termination. 25 | 26 | allocate() -> 27 | gen_server:call(frequency, {allocate, self()}). 28 | 29 | %% deallocate() -> ok 30 | %% Frees a frequency so it can be used by another client. 31 | 32 | deallocate(Frequency) -> 33 | gen_server:cast(frequency, {deallocate, Frequency }). 34 | 35 | 36 | %% CALLBACK FUNCTIONS 37 | 38 | %% init(_) -> {ok, State} 39 | %% Initialises the generic server by getting the list of 40 | %% available frequencies. [] is the list of allocated ones. 41 | 42 | init(_Args) -> 43 | Frequencies = {get_frequencies(), []}, 44 | {ok, Frequencies}. 45 | 46 | %% Dummy function. To be replaced with call to BSC. 47 | get_frequencies() -> [10,11,12,13,14,15]. 48 | 49 | %% This version of this function needs to be used with the 50 | %% bsc.config and rb.config files 51 | %get_frequencies() -> 52 | % {ok, FreqList} = application:get_env(frequencies), 53 | % FreqList. 54 | 55 | 56 | %% handle_call({allocate, Pid}, _, {Available, Allocated}) -> 57 | %% {reply, {ok, Frequency} | {error, no_resource}, {Available, Allocated}} 58 | %% Callback for allocating resources. 59 | 60 | handle_call({allocate, Pid}, _From, Frequencies) -> 61 | {NewFrequencies, Reply} = allocate(Frequencies, Pid), 62 | {reply, Reply, NewFrequencies}. 63 | 64 | 65 | %% handle_cast({deallocate, Freq}, Frequencies) -> {noreply, NewFrequencies}; 66 | %% Callback for deallocating resources 67 | 68 | handle_cast({deallocate, Freq}, Frequencies) -> 69 | NewFrequencies = deallocate(Frequencies, Freq), 70 | {noreply, NewFrequencies}; 71 | 72 | %% handle_cast(stop, LoopData) -> {stop, normal, LoopData}. 73 | %% callback to stop the gen_server. 74 | 75 | handle_cast(stop, LoopData) -> 76 | {stop, normal, LoopData}. 77 | 78 | handle_info(_Msg, LoopData) -> 79 | {noreply, LoopData}. 80 | 81 | %% terminate(Reason, LoopData) -> ok. 82 | %% Termination callback. Does nothing, but should instead kill clients. 83 | 84 | terminate(_Reason, _LoopData) -> 85 | ok. 86 | 87 | format_status(_Opt, [_ProcDict, {Available, Allocated}]) -> 88 | {data, [{"State", {{available, Available}, {allocated, Allocated}}}]}. 89 | 90 | %% INTERNAL FUNCTIONS 91 | 92 | %% Helper functions used to allocate and deallocate resources. 93 | 94 | allocate({[], Allocated}, _Pid) -> 95 | freq_overload:frequency_denied(), 96 | {{[], Allocated}, {error, no_frequency}}; 97 | allocate({[Res|Resources], Allocated}, Pid) -> 98 | case Resources of 99 | [] -> freq_overload:no_frequency(); 100 | _ -> ok 101 | end, 102 | {{Resources, [{Res, Pid}|Allocated]}, {ok, Res}}. 103 | 104 | deallocate({Free, Allocated}, Res) -> 105 | case Free of 106 | [] -> freq_overload:frequency_available(); 107 | _ -> ok 108 | end, 109 | NewAllocated = lists:keydelete(Res, 1, Allocated), 110 | {[Res|Free], NewAllocated}. 111 | -------------------------------------------------------------------------------- /ch11/bsc/src/frequency.erl: -------------------------------------------------------------------------------- 1 | %%% File : frequency.erl 2 | %%% Author : 3 | %%% Description : Server example from lecture notes 4 | %%% Created : 25 Mar 2003 by 5 | 6 | -module(frequency). 7 | -behaviour(gen_server). 8 | 9 | -export([start_link/0, stop/0, allocate/0, deallocate/1]). 10 | -export([init/1, handle_call/3, handle_cast/2, terminate/2, handle_info/2]). 11 | -export([format_status/2]). 12 | 13 | %% CLIENT FUNCTIONS 14 | 15 | %% start() -> {ok, pid()} | {error, Reason} 16 | %% Starts the frequency server. Called by supervisor 17 | 18 | start_link() -> 19 | gen_server:start_link({local, frequency}, frequency, [], []). 20 | 21 | %% stop() -> ok. 22 | %% Stops the frequency server. 23 | 24 | stop() -> 25 | gen_server:cast(frequency, stop). 26 | 27 | 28 | %% allocate() -> {ok, Frequency} | {error, no_resource} 29 | %% If available, it returns a frequency used to make a call. 30 | %% Frequency must be deallocated on termination. 31 | 32 | allocate() -> 33 | gen_server:call(frequency, {allocate, self()}). 34 | 35 | %% deallocate() -> ok 36 | %% Frees a frequency so it can be used by another client. 37 | 38 | deallocate(Frequency) -> 39 | gen_server:cast(frequency, {deallocate, Frequency }). 40 | 41 | 42 | %% CALLBACK FUNCTIONS 43 | 44 | %% init(_) -> {ok, State} 45 | %% Initialises the generic server by getting the list of 46 | %% available frequencies. [] is the list of allocated ones. 47 | 48 | init(_Args) -> 49 | Frequencies = {get_frequencies(), []}, 50 | {ok, Frequencies}. 51 | 52 | %% Dummy function. To be replaced with call to BSC. 53 | 54 | get_frequencies() -> 55 | case application:get_env(frequencies) of 56 | {ok, FreqList} -> FreqList; 57 | undefined -> [10,11,12,13,14, 15] 58 | end. 59 | 60 | %% handle_call({allocate, Pid}, _, {Available, Allocated}) -> 61 | %% {reply, {ok, Frequency} | {error, no_resource}, {Available, Allocated}} 62 | %% Callback for allocating resources. 63 | 64 | handle_call({allocate, Pid}, _From, Frequencies) -> 65 | {NewFrequencies, Reply} = allocate(Frequencies, Pid), 66 | {reply, Reply, NewFrequencies}. 67 | 68 | 69 | %% handle_cast({deallocate, Freq}, Frequencies) -> {noreply, NewFrequencies}; 70 | %% Callback for deallocating resources 71 | 72 | handle_cast({deallocate, Freq}, Frequencies) -> 73 | NewFrequencies = deallocate(Frequencies, Freq), 74 | {noreply, NewFrequencies}; 75 | 76 | %% handle_cast(stop, LoopData) -> {stop, normal, LoopData}. 77 | %% callback to stop the gen_server. 78 | 79 | handle_cast(stop, LoopData) -> 80 | {stop, normal, LoopData}. 81 | 82 | handle_info(_Msg, LoopData) -> 83 | {noreply, LoopData}. 84 | 85 | %% terminate(Reason, LoopData) -> ok. 86 | %% Termination callback. Does nothing, but should instead kill clients. 87 | 88 | terminate(_Reason, _LoopData) -> 89 | ok. 90 | 91 | format_status(_Opt, [_ProcDict, {Available, Allocated}]) -> 92 | {data, [{"State", {{available, Available}, {allocated, Allocated}}}]}. 93 | 94 | %% INTERNAL FUNCTIONS 95 | 96 | %% Help functions used to allocate and deallocate resources. 97 | 98 | allocate({[], Allocated}, _Pid) -> 99 | freq_overload:frequency_denied(), 100 | {{[], Allocated}, {error, no_frequency}}; 101 | allocate({[Res|Resources], Allocated}, Pid) -> 102 | case Resources of 103 | [] -> freq_overload:no_frequency(); 104 | _ -> ok 105 | end, 106 | {{Resources, [{Res, Pid}|Allocated]}, {ok, Res}}. 107 | 108 | deallocate({Free, Allocated}, Res) -> 109 | case Free of 110 | [] -> freq_overload:frequency_available(); 111 | _ -> ok 112 | end, 113 | NewAllocated = lists:keydelete(Res, 1, Allocated), 114 | {[Res|Free], NewAllocated}. 115 | 116 | -------------------------------------------------------------------------------- /ch12/coffee-1.1/src/coffee_fsm.erl: -------------------------------------------------------------------------------- 1 | -module(coffee_fsm). 2 | -behaviour(gen_fsm). 3 | -vsn('1.1'). 4 | -export([start_link/0, init/1]). 5 | -export([selection/2, payment/2, remove/2, service/2]). 6 | -export([americano/0, cappuccino/0, tea/0, espresso/0, 7 | pay/1, cancel/0, cup_removed/0, open/0, close/0]). 8 | 9 | -export([stop/0, selection/3, payment/3, remove/3, service/3]). 10 | -export([terminate/3, code_change/4]). 11 | 12 | start_link() -> 13 | gen_fsm:start_link({local, ?MODULE}, ?MODULE, [], []). 14 | 15 | init([]) -> 16 | hw:reboot(), 17 | hw:display("Make Your Selection", []), 18 | {ok, selection, []}. 19 | 20 | %% Client Functions for Drink Selections 21 | tea() -> gen_fsm:send_event(?MODULE,{selection,tea,100}). 22 | espresso() ->gen_fsm:send_event(?MODULE,{selection,espresso,150}). 23 | americano() -> gen_fsm:send_event(?MODULE,{selection,americano,100}). 24 | cappuccino() -> gen_fsm:send_event(?MODULE,{selection,cappuccino,150}). 25 | 26 | 27 | %% Client Functions for Actions 28 | pay(Coin) -> gen_fsm:send_event(?MODULE,{pay, Coin}). 29 | cancel() -> gen_fsm:send_event(?MODULE,cancel). 30 | cup_removed() -> gen_fsm:send_event(?MODULE,cup_removed). 31 | open() -> gen_fsm:send_event(?MODULE, open). 32 | close() -> gen_fsm:send_event(?MODULE, close). 33 | 34 | 35 | 36 | %% State: drink selection 37 | selection({selection,Type,Price}, _LoopData) -> 38 | hw:display("Please pay:~w",[Price]), 39 | {next_state, payment, {Type, Price, 0}}; 40 | selection({pay, Coin}, LoopData) -> 41 | hw:return_change(Coin), 42 | {next_state, selection, LoopData}; 43 | selection(open, LoopData) -> 44 | hw:display("Open", [ ]), 45 | {next_state, service, LoopData}; 46 | selection(_Other, LoopData) -> 47 | {next_state, selection, LoopData}. 48 | 49 | %% State: service machine 50 | service(close, LoopData) -> 51 | hw:reboot(), 52 | hw:display("Make Your Selection", []), 53 | {next_state, selection, LoopData}; 54 | service({pay, Coin}, LoopData) -> 55 | hw:return_change(Coin), 56 | {next_state, service, LoopData}; 57 | service(_Other, LoopData) -> 58 | {next_state, service, LoopData}. 59 | 60 | payment({pay, Coin}, {Type,Price,Paid}) 61 | when Coin+Paid >= Price -> 62 | NewPaid = Coin + Paid, 63 | hw:display("Preparing Drink.",[]), 64 | hw:return_change(NewPaid - Price), 65 | hw:drop_cup(), hw:prepare(Type), 66 | hw:display("Remove Drink.", []), 67 | {next_state, remove, []}; 68 | payment({pay, Coin}, {Type,Price,Paid}) 69 | when Coin+Paid < Price -> 70 | NewPaid = Coin + Paid, 71 | hw:display("Please pay:~w",[Price - NewPaid]), 72 | {next_state, payment, {Type, Price, NewPaid}}; 73 | payment(cancel, {_Type, _Price, Paid}) -> 74 | hw:display("Make Your Selection", []), 75 | hw:return_change(Paid), 76 | {next_state, selection, []}; 77 | payment(_Other, LoopData) -> 78 | {next_state, payment, LoopData}. 79 | 80 | 81 | %% State: remove cup
 82 | remove(cup_removed, LoopData) -> 83 | hw:display("Make Your Selection", []), 84 | {next_state, selection, LoopData}; 85 | remove({pay, Coin}, LoopData) -> 86 | hw:return_change(Coin), 87 | {next_state, remove, LoopData}; 88 | remove(_Other, LoopData) -> 89 | {next_state, remove, LoopData}. 90 | 91 | 92 | stop() -> gen_fsm:sync_send_event(?MODULE, stop). 93 | 94 | selection(stop, _From, LoopData) -> 95 | {stop, normal, ok, LoopData}. 96 | service(stop, _From, LoopData) -> 97 | {stop, normal, ok, LoopData}. 98 | payment(stop, _From, Paid) -> 99 | hw:return_change(Paid), 100 | {stop, normal, ok, 0}. 101 | remove(stop, _From, LoopData) -> 102 | {stop, normal, ok, LoopData}. 103 | 104 | 105 | terminate(_Reason, _StateName, _LoopData) -> 106 | ok. 107 | 108 | code_change('1.0', payment, {_Type, _Price, Paid}, _Extra) -> 109 | io:format("code_change('1.0', payment, ~p, ~p).",[{_Type, _Price, Paid}, _Extra]), 110 | hw:return_change(Paid), 111 | hw:display("Make Your Selection", []), 112 | {ok, selection, {}}; 113 | code_change('1.0', State, LoopData, _Extra) -> 114 | io:format("code_change('1.0', ~p, ~p, ~p).",[State, LoopData, _Extra]), 115 | {ok, State, LoopData}; 116 | code_change({down, '1.0'}, service, LoopData, _Extra) -> 117 | io:format("code_change({down, '1.0'}, service, ~p, ~p).",[LoopData, _Extra]), 118 | hw:reboot(), 119 | hw:display("Make Your Selection", []), 120 | {ok, selection, LoopData}; 121 | code_change({down, '1.0'}, payment, {_Type, _Price, Paid}, _Extra) -> 122 | io:format("code_change({down, '1.0'}, payment, ~p, ~p).",[{_Type, _Price, Paid}, _Extra]), 123 | hw:return_change(Paid), 124 | hw:display("Make Your Selection", []), 125 | {ok, selection, {}}; 126 | code_change({down, '1.0'}, State, LoopData, _Extra) -> 127 | io:format("code_change({down, '1.0'}, ~p, ~p, ~p).",[State, LoopData, _Extra]), 128 | {ok, State, LoopData}; 129 | code_change(Other, State, LoopData, _Extra) -> 130 | io:format("Other: code_change(~p, ~p, ~p, ~p).",[Other, State, LoopData, _Extra]), 131 | {ok, State, LoopData}. 132 | -------------------------------------------------------------------------------- /ch9/bsc-1.0/src/phone_fsm.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2013,2016 Francesco Cesarini 2 | -module(phone_fsm). 3 | -behaviour(gen_fsm). 4 | 5 | -export([start_link/1]). 6 | 7 | -export([init/1, terminate/3, handle_sync_event/4]). 8 | 9 | %% Client Functions 10 | -export([inbound/1, action/2, busy/1, reject/1, accept/1, hangup/1]). 11 | 12 | %% States 13 | -export([idle/2, calling/2, connected/2, receiving/2]). 14 | 15 | start_link(Ms) -> 16 | gen_fsm:start_link(?MODULE, Ms, [{debug, [trace]}]). 17 | %% gen_fsm:start_link(?MODULE, Ms, []). 18 | 19 | init(Ms) -> 20 | process_flag(trap_exit, true), 21 | hlr:attach(Ms), 22 | {ok, idle, Ms}. 23 | 24 | terminate(_Reason, idle, _Ms) -> 25 | hlr:detach(); 26 | terminate(_Reason, calling, {_Ms, CallingMsId}) -> 27 | phone_fsm:hangup(CallingMsId), 28 | hlr:detach(); 29 | terminate(_Reason, connected, {_Ms, OtherMsId, _Freq}) -> %% We hang up, We initated call 30 | phone_fsm:hangup(OtherMsId), 31 | hlr:detach(); 32 | terminate(_Reason, receiving, {_Ms, FromMsId}) -> 33 | phone_fsm:reject(FromMsId), 34 | hlr:detach(). 35 | 36 | %% Events from the Phone 37 | action({outbound, ToMs}, MsId) -> 38 | gen_fsm:sync_send_all_state_event(MsId, {outbound, ToMs}); 39 | action(Action, MsId) -> %Action == hangup, reject, accept 40 | 41 | gen_fsm:send_event(MsId, {action,Action}). 42 | 43 | 44 | busy(ToMsId) -> 45 | gen_fsm:send_event(ToMsId, {busy, self()}). 46 | reject(ToMsId) -> 47 | gen_fsm:send_event(ToMsId, {reject, self()}). 48 | accept(ToMsId) -> 49 | gen_fsm:send_event(ToMsId, {accept, self()}). 50 | hangup(ToMsId) -> 51 | gen_fsm:send_event(ToMsId, {hangup, self()}). 52 | inbound(ToMsId) -> 53 | gen_fsm:send_event(ToMsId, {inbound, self()}). 54 | 55 | %% Event outbound in state idle is synchronous and handled in 56 | %% handle_sync_event 57 | 58 | idle({inbound, FromMsId}, Ms) -> 59 | phone:reply(inbound, FromMsId, Ms), 60 | {next_state, receiving, {Ms, FromMsId}}; 61 | idle(_Ignore, State) -> % , hangup, reject, accept 62 | io:format("~p in idle, ignored. State:~w, Event:~w~n",[self(), State, _Ignore]), 63 | {next_state, idle, State}. 64 | 65 | 66 | %% Beware of race conditions. This event could be received right after 67 | %% the other party accepts, meaning we would handle it not here but in state 68 | %% connected. 69 | 70 | calling({action, hangup}, {Ms, CallingMsId}) -> 71 | phone_fsm:hangup(CallingMsId), 72 | {next_state, idle, Ms}; 73 | calling({busy, Pid}, {Ms, Pid}) -> 74 | phone:reply(busy, Pid, Ms), 75 | {next_state, idle, Ms}; 76 | calling({reject, Pid}, {Ms, Pid}) -> 77 | phone:reply(rejected, Pid, Ms), 78 | {next_state, idle, Ms}; 79 | calling({accept, Pid}, {Ms, Pid}) -> 80 | case frequency:allocate() of 81 | {error, no_frequency} -> 82 | phone_fsm:reject(Pid), 83 | phone:reply(no_frequency, Pid, Ms), 84 | {next_state, idle, Ms}; 85 | {ok, Freq} -> 86 | phone:reply(connected, Pid, Ms), 87 | {next_state, connected, {Ms, Pid, Freq}} 88 | end; 89 | calling({inbound, Pid}, State) -> 90 | phone_fsm:busy(Pid), 91 | {next_state, calling, State}; 92 | calling(_Ignore, State) -> % {action, reject}, {action, accept} 93 | io:format("In calling, ignored. State:~w, Event:~w~n",[State, _Ignore]), 94 | {next_state, calling, State}. 95 | 96 | connected({inbound, FromMsId}, State) -> 97 | phone_fsm:busy(FromMsId), 98 | {next_state, connected, State}; 99 | connected({action, hangup}, {Ms, OtherMsId, Freq}) -> %% We hang up, We initated call 100 | phone_fsm:hangup(OtherMsId), 101 | frequency:deallocate(Freq), 102 | {next_state, idle, Ms}; 103 | connected({action, hangup}, {Ms, OtherMsId}) -> %% We hang up, Other initated call 104 | phone_fsm:hangup(OtherMsId), 105 | {next_state, idle, Ms}; 106 | 107 | connected({hangup, OtherMsId}, {Ms, OtherMsId}) -> %% they hang Up 108 | phone:reply(hangup, OtherMsId, Ms), 109 | {next_state, idle, Ms}; 110 | connected({hangup, OtherMsId}, {Ms, OtherMsId, Freq}) -> %% they hang Up 111 | phone:reply(hangup, OtherMsId, Ms), 112 | frequency:deallocate(Freq), 113 | {next_state, idle, Ms}; 114 | connected(_Ignore, State) -> 115 | io:format("In connected, ignored. State:~w, Event:~w~n",[State, _Ignore]), 116 | {next_state, connected, State}. 117 | 118 | receiving({action, accept}, {Ms, FromMsId}) -> 119 | phone_fsm:accept(FromMsId), 120 | {next_state, connected, {Ms, FromMsId}}; 121 | receiving({action, reject}, {Ms, FromMsId}) -> 122 | phone_fsm:reject(FromMsId), 123 | {next_state, idle, Ms}; 124 | receiving({hangup, FromMsId}, {Ms, FromMsId}) -> 125 | phone:reply(hangup, FromMsId, Ms), 126 | {next_state, idle, Ms}; 127 | receiving({inbound, FromMsId}, State) -> %Others 128 | phone_fsm:busy(FromMsId), 129 | {next_state, receiving, State}; 130 | receiving(_Ignore, State) -> % {action, hangup} 131 | io:format("In receiving, ignored. State:~w, Event:~w~n",[State, _Ignore]), 132 | {next_state, receiving, State}. 133 | 134 | handle_sync_event({outbound, ToMs}, _From, idle, Ms) -> 135 | case hlr:lookup_id(ToMs) of 136 | {error, invalid} -> 137 | io:format("ERROR, INVALID~n"), 138 | phone:reply(invalid, ToMs, Ms), 139 | {reply, {error, invalid}, idle, Ms}; 140 | {ok, ToMsId} when is_pid(ToMsId) -> 141 | phone:reply(outbound, ToMs, Ms), 142 | phone_fsm:inbound(ToMsId), 143 | {reply, ok, calling, {Ms, ToMsId}} 144 | end; 145 | handle_sync_event({outbound, _ToMSISDN}, _From, State, MSISDN) -> 146 | {reply, {error, busy}, State, MSISDN}. 147 | -------------------------------------------------------------------------------- /ch6/exercise/phone_fsm.erl: -------------------------------------------------------------------------------- 1 | %%% @copyright (c) 2013,2016 Francesco Cesarini 2 | -module(phone_fsm). 3 | -behaviour(gen_fsm). 4 | 5 | -export([start_link/1]). 6 | 7 | -export([init/1, terminate/3, handle_sync_event/4]). 8 | 9 | -export([inbound/1, action/2, busy/1, reject/1, accept/1, hangup/1]). 10 | 11 | -export([idle/2, calling/2, connected/2, receiving/2]). 12 | 13 | start_link(Ms) -> 14 | gen_fsm:start_link(?MODULE, Ms, [{debug, [trace]}]). 15 | %% gen_fsm:start_link(?MODULE, Ms, []). 16 | 17 | init(Ms) -> 18 | process_flag(trap_exit, true), 19 | hlr:attach(Ms), 20 | {ok, idle, Ms}. 21 | 22 | terminate(_Reason, idle, _Ms) -> 23 | hlr:detach(); 24 | terminate(_Reason, calling, {_Ms, CallingMsId}) -> 25 | phone_fsm:hangup(CallingMsId), 26 | hlr:detach(); 27 | terminate(_Reason, connected, {_Ms, OtherMsId, _Freq}) -> 28 | %% We hang up, We initated call 29 | phone_fsm:hangup(OtherMsId), 30 | hlr:detach(); 31 | terminate(_Reason, receiving, {_Ms, FromMsId}) -> 32 | phone_fsm:reject(FromMsId), 33 | hlr:detach(). 34 | 35 | %% Events from the Phone 36 | action({outbound, ToMs}, MsId) -> 37 | gen_fsm:sync_send_all_state_event(MsId, {outbound, ToMs}); 38 | action(Action, MsId) -> %Action == hangup, reject, accept 39 | gen_fsm:send_event(MsId, {action,Action}). 40 | 41 | 42 | busy(ToMsId) -> 43 | gen_fsm:send_event(ToMsId, {busy, self()}). 44 | reject(ToMsId) -> 45 | gen_fsm:send_event(ToMsId, {reject, self()}). 46 | accept(ToMsId) -> 47 | gen_fsm:send_event(ToMsId, {accept, self()}). 48 | hangup(ToMsId) -> 49 | gen_fsm:send_event(ToMsId, {hangup, self()}). 50 | inbound(ToMsId) -> 51 | gen_fsm:send_event(ToMsId, {inbound, self()}). 52 | %%-------------------------------------------------------------------- 53 | %%-------------------------------------------------------------------- 54 | 55 | %% Event outbound in state idle is synchronous and handled in 56 | %% handle_sync_event 57 | 58 | idle({inbound, FromMsId}, Ms) -> 59 | phone:reply(inbound, FromMsId, Ms), 60 | {next_state, receiving, {Ms, FromMsId}}; 61 | idle(_Ignore, State) -> % , hangup, reject, accept 62 | io:format("~p in idle, ignored. State:~w, Event:~w~n",[self(), State, _Ignore]), 63 | {next_state, idle, State}. 64 | 65 | 66 | %% Beware of race conditions. This event could be received right after 67 | %% the other party accepts, meaning we would handle it not here but in state 68 | %% connected. 69 | 70 | calling({action, hangup}, {Ms, CallingMsId}) -> 71 | phone_fsm:hangup(CallingMsId), 72 | {next_state, idle, Ms}; 73 | calling({busy, Pid}, {Ms, Pid}) -> 74 | phone:reply(busy, Pid, Ms), 75 | {next_state, idle, Ms}; 76 | calling({reject, Pid}, {Ms, Pid}) -> 77 | phone:reply(rejected, Pid, Ms), 78 | {next_state, idle, Ms}; 79 | calling({accept, Pid}, {Ms, Pid}) -> 80 | case frequency:allocate() of 81 | {error, no_frequency} -> 82 | phone_fsm:reject(Pid), 83 | phone:reply(no_frequency, Pid, Ms), 84 | {next_state, idle, Ms}; 85 | {ok, Freq} -> 86 | phone:reply(connected, Pid, Ms), 87 | {next_state, connected, {Ms, Pid, Freq}} 88 | end; 89 | calling({inbound, Pid}, State) -> 90 | phone_fsm:busy(Pid), 91 | {next_state, calling, State}; 92 | calling(_Ignore, State) -> % {action, reject}, {action, accept} 93 | io:format("In calling, ignored. State:~w, Event:~w~n",[State, _Ignore]), 94 | {next_state, calling, State}. 95 | 96 | connected({inbound, FromMsId}, State) -> 97 | phone_fsm:busy(FromMsId), 98 | {next_state, connected, State}; 99 | connected({action, hangup}, {Ms, OtherMsId, Freq}) -> %% We hang up, We initated call 100 | phone_fsm:hangup(OtherMsId), 101 | frequency:deallocate(Freq), 102 | {next_state, idle, Ms}; 103 | connected({action, hangup}, {Ms, OtherMsId}) -> %% We hang up, Other initated call 104 | phone_fsm:hangup(OtherMsId), 105 | {next_state, idle, Ms}; 106 | 107 | connected({hangup, OtherMsId}, {Ms, OtherMsId}) -> %% they hang Up 108 | phone:reply(hangup, OtherMsId, Ms), 109 | {next_state, idle, Ms}; 110 | connected({hangup, OtherMsId}, {Ms, OtherMsId, Freq}) -> %% they hang Up 111 | phone:reply(hangup, OtherMsId, Ms), 112 | frequency:deallocate(Freq), 113 | {next_state, idle, Ms}; 114 | connected(_Ignore, State) -> 115 | io:format("In connected, ignored. State:~w, Event:~w~n",[State, _Ignore]), 116 | {next_state, connected, State}. 117 | 118 | receiving({action, accept}, {Ms, FromMsId}) -> 119 | phone_fsm:accept(FromMsId), 120 | {next_state, connected, {Ms, FromMsId}}; 121 | receiving({action, reject}, {Ms, FromMsId}) -> 122 | phone_fsm:reject(FromMsId), 123 | {next_state, idle, Ms}; 124 | receiving({hangup, FromMsId}, {Ms, FromMsId}) -> 125 | phone:reply(hangup, FromMsId, Ms), 126 | {next_state, idle, Ms}; 127 | receiving({inbound, FromMsId}, State) -> %Others 128 | phone_fsm:busy(FromMsId), 129 | {next_state, receiving, State}; 130 | receiving(_Ignore, State) -> % {action, hangup} 131 | io:format("In receiving, ignored. State:~w, Event:~w~n",[State, _Ignore]), 132 | {next_state, receiving, State}. 133 | 134 | handle_sync_event({outbound, ToMs}, _From, idle, Ms) -> 135 | case hlr:lookup_id(ToMs) of 136 | {error, invalid} -> 137 | io:format("ERROR, INVALID~n"), 138 | phone:reply(invalid, ToMs, Ms), 139 | {reply, {error, invalid}, idle, Ms}; 140 | {ok, ToMsId} when is_pid(ToMsId) -> 141 | phone:reply(outbound, ToMs, Ms), 142 | phone_fsm:inbound(ToMsId), 143 | {reply, ok, calling, {Ms, ToMsId}} 144 | end; 145 | handle_sync_event({outbound, _ToMSISDN}, _From, State, MSISDN) -> 146 | {reply, {error, busy}, State, MSISDN}. 147 | -------------------------------------------------------------------------------- /ch8/otp/phone_fsm.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @author Francesco Cesarini 3 | %%% @copyright (C) 2013, Francesco Cesarini 4 | %%% @doc 5 | %%% 6 | %%% @end 7 | %%% Created : 8 Jun 2013 by Francesco Cesarini 8 | %%%------------------------------------------------------------------- 9 | -module(phone_fsm). 10 | 11 | -behaviour(gen_fsm). 12 | 13 | %% API 14 | -export([start_link/1]). 15 | 16 | %% gen_fsm callbacks 17 | -export([init/1, terminate/3, handle_sync_event/4]). 18 | %% Client Functions 19 | -export([inbound/1, action/2, busy/1, reject/1, accept/1, hangup/1]). 20 | %% States 21 | -export([idle/2, calling/2, connected/2, receiving/2]). 22 | 23 | 24 | start_link(Ms) -> 25 | gen_fsm:start_link(?MODULE, Ms, [{debug, [trace]}]). 26 | %% gen_fsm:start_link(?MODULE, Ms, []). 27 | 28 | init(Ms) -> 29 | process_flag(trap_exit, true), 30 | hlr:attach(Ms), 31 | {ok, idle, Ms}. 32 | 33 | 34 | %% Events from the Phone 35 | action({outbound, ToMs}, MsId) -> 36 | gen_fsm:sync_send_all_state_event(MsId, {outbound, ToMs}); 37 | action(Action, MsId) -> %Action == hangup, reject, accept 38 | 39 | gen_fsm:send_event(MsId, {action,Action}). 40 | 41 | 42 | busy(ToMsId) -> 43 | gen_fsm:send_event(ToMsId, {busy, self()}). 44 | reject(ToMsId) -> 45 | gen_fsm:send_event(ToMsId, {reject, self()}). 46 | accept(ToMsId) -> 47 | gen_fsm:send_event(ToMsId, {accept, self()}). 48 | hangup(ToMsId) -> 49 | gen_fsm:send_event(ToMsId, {hangup, self()}). 50 | inbound(ToMsId) -> 51 | gen_fsm:send_event(ToMsId, {inbound, self()}). 52 | %%-------------------------------------------------------------------- 53 | %%-------------------------------------------------------------------- 54 | 55 | %% Event outbound in state idle is synchronous and handled in 56 | %% handle_sync_event 57 | 58 | idle({inbound, FromMsId}, Ms) -> 59 | phone:reply(inbound, FromMsId, Ms), 60 | {next_state, receiving, {Ms, FromMsId}}; 61 | idle(_Ignore, State) -> % , hangup, reject, accept 62 | io:format("~p in idle, ignored. State:~w, Event:~w~n",[self(), State, _Ignore]), 63 | {next_state, idle, State}. 64 | 65 | 66 | %% Beware of race conditions. This event could be received right after 67 | %% the other party accepts, meaning we would handle it not here but in state 68 | %% connected. 69 | 70 | calling({action, hangup}, {Ms, CallingMsId}) -> 71 | phone_fsm:hangup(CallingMsId), 72 | {next_state, idle, Ms}; 73 | 74 | 75 | calling({busy, Pid}, {Ms, Pid}) -> 76 | phone:reply(busy, Pid, Ms), 77 | {next_state, idle, Ms}; 78 | calling({reject, Pid}, {Ms, Pid}) -> 79 | phone:reply(rejected, Pid, Ms), 80 | {next_state, idle, Ms}; 81 | calling({accept, Pid}, {Ms, Pid}) -> 82 | case frequency:allocate() of 83 | {error, no_frequency} -> 84 | phone_fsm:reject(Pid), 85 | phone:reply(no_frequency, Pid, Ms), 86 | {next_state, idle, Ms}; 87 | {ok, Freq} -> 88 | phone:reply(connected, Pid, Ms), 89 | {next_state, connected, {Ms, Pid, Freq}} 90 | end; 91 | calling({inbound, Pid}, State) -> 92 | phone_fsm:busy(Pid), 93 | {next_state, calling, State}; 94 | calling(_Ignore, State) -> % {action, reject}, {action, accept} 95 | io:format("In calling, ignored. State:~w, Event:~w~n",[State, _Ignore]), 96 | {next_state, calling, State}. 97 | 98 | connected({inbound, FromMsId}, State) -> 99 | phone_fsm:busy(FromMsId), 100 | {next_state, connected, State}; 101 | connected({action, hangup}, {Ms, OtherMsId, Freq}) -> %% We hang up, We initated call 102 | phone_fsm:hangup(OtherMsId), 103 | frequency:deallocate(Freq), 104 | {next_state, idle, Ms}; 105 | connected({action, hangup}, {Ms, OtherMsId}) -> %% We hang up, Other initated call 106 | phone_fsm:hangup(OtherMsId), 107 | {next_state, idle, Ms}; 108 | 109 | connected({hangup, OtherMsId}, {Ms, OtherMsId}) -> %% they hang Up 110 | phone:reply(hangup, OtherMsId, Ms), 111 | {next_state, idle, Ms}; 112 | connected({hangup, OtherMsId}, {Ms, OtherMsId, Freq}) -> %% they hang Up 113 | phone:reply(hangup, OtherMsId, Ms), 114 | frequency:deallocate(Freq), 115 | {next_state, idle, Ms}; 116 | connected(_Ignore, State) -> 117 | io:format("In connected, ignored. State:~w, Event:~w~n",[State, _Ignore]), 118 | {next_state, connected, State}. 119 | 120 | receiving({action, accept}, {Ms, FromMsId}) -> 121 | phone_fsm:accept(FromMsId), 122 | {next_state, connected, {Ms, FromMsId}}; 123 | receiving({action, reject}, {Ms, FromMsId}) -> 124 | phone_fsm:reject(FromMsId), 125 | {next_state, idle, Ms}; 126 | receiving({hangup, FromMsId}, {Ms, FromMsId}) -> 127 | phone:reply(hangup, FromMsId, Ms), 128 | {next_state, idle, Ms}; 129 | receiving({inbound, FromMsId}, State) -> %Others 130 | phone_fsm:busy(FromMsId), 131 | {next_state, receiving, State}; 132 | receiving(_Ignore, State) -> % {action, hangup} 133 | io:format("In receiving, ignored. State:~w, Event:~w~n",[State, _Ignore]), 134 | {next_state, receiving, State}. 135 | 136 | handle_sync_event({outbound, ToMs}, _From, idle, Ms) -> 137 | case hlr:lookup_id(ToMs) of 138 | {error, invalid} -> 139 | io:format("ERROR, INVALID~n"), 140 | phone:reply(invalid, ToMs, Ms), 141 | {reply, {error, invalid}, idle, Ms}; 142 | {ok, ToMsId} when is_pid(ToMsId) -> 143 | phone:reply(outbound, ToMs, Ms), 144 | phone_fsm:inbound(ToMsId), 145 | {reply, ok, calling, {Ms, ToMsId}} 146 | end; 147 | handle_sync_event({outbound, _ToMSISDN}, _From, State, MSISDN) -> 148 | {reply, {error, busy}, State, MSISDN}. 149 | 150 | -------------------------------------------------------------------------------- /ch11/bsc/src/phone_fsm.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @author Francesco Cesarini 3 | %%% @copyright (C) 2013, Francesco Cesarini 4 | %%% @doc 5 | %%% 6 | %%% @end 7 | %%% Created : 8 Jun 2013 by Francesco Cesarini 8 | %%%------------------------------------------------------------------- 9 | -module(phone_fsm). 10 | 11 | -behaviour(gen_fsm). 12 | 13 | %% API 14 | -export([start_link/1]). 15 | 16 | %% gen_fsm callbacks 17 | -export([init/1, terminate/3, handle_sync_event/4]). 18 | %% Client Functions 19 | -export([inbound/1, action/2, busy/1, reject/1, accept/1, hangup/1]). 20 | %% States 21 | -export([idle/2, calling/2, connected/2, receiving/2]). 22 | 23 | 24 | start_link(Ms) -> 25 | gen_fsm:start_link(?MODULE, Ms, [{debug, [trace]}]). 26 | %% gen_fsm:start_link(?MODULE, Ms, []). 27 | 28 | init(Ms) -> 29 | process_flag(trap_exit, true), 30 | hlr:attach(Ms), 31 | {ok, idle, Ms}. 32 | 33 | terminate(_Reason, idle, _Ms) -> 34 | hlr:detach(); 35 | terminate(_Reason, calling, {_Ms, CallingMsId}) -> 36 | phone_fsm:hangup(CallingMsId), 37 | hlr:detach(); 38 | terminate(_Reason, connected, {_Ms, OtherMsId, _Freq}) -> %% We hang up, We initated call 39 | phone_fsm:hangup(OtherMsId), 40 | hlr:detach(); 41 | terminate(_Reason, receiving, {_Ms, FromMsId}) -> 42 | phone_fsm:reject(FromMsId), 43 | hlr:detach(). 44 | 45 | %% Events from the Phone 46 | action({outbound, ToMs}, MsId) -> 47 | gen_fsm:sync_send_all_state_event(MsId, {outbound, ToMs}); 48 | action(Action, MsId) -> %Action == hangup, reject, accept 49 | 50 | gen_fsm:send_event(MsId, {action,Action}). 51 | 52 | 53 | busy(ToMsId) -> 54 | gen_fsm:send_event(ToMsId, {busy, self()}). 55 | reject(ToMsId) -> 56 | gen_fsm:send_event(ToMsId, {reject, self()}). 57 | accept(ToMsId) -> 58 | gen_fsm:send_event(ToMsId, {accept, self()}). 59 | hangup(ToMsId) -> 60 | gen_fsm:send_event(ToMsId, {hangup, self()}). 61 | inbound(ToMsId) -> 62 | gen_fsm:send_event(ToMsId, {inbound, self()}). 63 | %%-------------------------------------------------------------------- 64 | %%-------------------------------------------------------------------- 65 | 66 | %% Event outbound in state idle is synchronous and handled in 67 | %% handle_sync_event 68 | 69 | idle({inbound, FromMsId}, Ms) -> 70 | phone:reply(inbound, FromMsId, Ms), 71 | {next_state, receiving, {Ms, FromMsId}}; 72 | idle(_Ignore, State) -> % , hangup, reject, accept 73 | io:format("~p in idle, ignored. State:~w, Event:~w~n",[self(), State, _Ignore]), 74 | {next_state, idle, State}. 75 | 76 | 77 | %% Beware of race conditions. This event could be received right after 78 | %% the other party accepts, meaning we would handle it not here but in state 79 | %% connected. 80 | 81 | calling({action, hangup}, {Ms, CallingMsId}) -> 82 | phone_fsm:hangup(CallingMsId), 83 | {next_state, idle, Ms}; 84 | 85 | 86 | calling({busy, Pid}, {Ms, Pid}) -> 87 | phone:reply(busy, Pid, Ms), 88 | {next_state, idle, Ms}; 89 | calling({reject, Pid}, {Ms, Pid}) -> 90 | phone:reply(rejected, Pid, Ms), 91 | {next_state, idle, Ms}; 92 | calling({accept, Pid}, {Ms, Pid}) -> 93 | case frequency:allocate() of 94 | {error, no_frequency} -> 95 | phone_fsm:reject(Pid), 96 | phone:reply(no_frequency, Pid, Ms), 97 | {next_state, idle, Ms}; 98 | {ok, Freq} -> 99 | phone:reply(connected, Pid, Ms), 100 | {next_state, connected, {Ms, Pid, Freq}} 101 | end; 102 | calling({inbound, Pid}, State) -> 103 | phone_fsm:busy(Pid), 104 | {next_state, calling, State}; 105 | calling(_Ignore, State) -> % {action, reject}, {action, accept} 106 | io:format("In calling, ignored. State:~w, Event:~w~n",[State, _Ignore]), 107 | {next_state, calling, State}. 108 | 109 | connected({inbound, FromMsId}, State) -> 110 | phone_fsm:busy(FromMsId), 111 | {next_state, connected, State}; 112 | connected({action, hangup}, {Ms, OtherMsId, Freq}) -> %% We hang up, We initated call 113 | phone_fsm:hangup(OtherMsId), 114 | frequency:deallocate(Freq), 115 | {next_state, idle, Ms}; 116 | connected({action, hangup}, {Ms, OtherMsId}) -> %% We hang up, Other initated call 117 | phone_fsm:hangup(OtherMsId), 118 | {next_state, idle, Ms}; 119 | 120 | connected({hangup, OtherMsId}, {Ms, OtherMsId}) -> %% they hang Up 121 | phone:reply(hangup, OtherMsId, Ms), 122 | {next_state, idle, Ms}; 123 | connected({hangup, OtherMsId}, {Ms, OtherMsId, Freq}) -> %% they hang Up 124 | phone:reply(hangup, OtherMsId, Ms), 125 | frequency:deallocate(Freq), 126 | {next_state, idle, Ms}; 127 | connected(_Ignore, State) -> 128 | io:format("In connected, ignored. State:~w, Event:~w~n",[State, _Ignore]), 129 | {next_state, connected, State}. 130 | 131 | receiving({action, accept}, {Ms, FromMsId}) -> 132 | phone_fsm:accept(FromMsId), 133 | {next_state, connected, {Ms, FromMsId}}; 134 | receiving({action, reject}, {Ms, FromMsId}) -> 135 | phone_fsm:reject(FromMsId), 136 | {next_state, idle, Ms}; 137 | receiving({hangup, FromMsId}, {Ms, FromMsId}) -> 138 | phone:reply(hangup, FromMsId, Ms), 139 | {next_state, idle, Ms}; 140 | receiving({inbound, FromMsId}, State) -> %Others 141 | phone_fsm:busy(FromMsId), 142 | {next_state, receiving, State}; 143 | receiving(_Ignore, State) -> % {action, hangup} 144 | io:format("In receiving, ignored. State:~w, Event:~w~n",[State, _Ignore]), 145 | {next_state, receiving, State}. 146 | 147 | handle_sync_event({outbound, ToMs}, _From, idle, Ms) -> 148 | case hlr:lookup_id(ToMs) of 149 | {error, invalid} -> 150 | io:format("ERROR, INVALID~n"), 151 | phone:reply(invalid, ToMs, Ms), 152 | {reply, {error, invalid}, idle, Ms}; 153 | {ok, ToMsId} when is_pid(ToMsId) -> 154 | phone:reply(outbound, ToMs, Ms), 155 | phone_fsm:inbound(ToMsId), 156 | {reply, ok, calling, {Ms, ToMsId}} 157 | end; 158 | handle_sync_event({outbound, _ToMSISDN}, _From, State, MSISDN) -> 159 | {reply, {error, busy}, State, MSISDN}. 160 | 161 | --------------------------------------------------------------------------------