calculadora
、log_calculadora
およびの3 つのモジュールがありsupervisor_calculadora
ます。Calculadora
gen_fsm を使用して合計、減算、乗算、および除算を行う単純な計算機であり、スーパーバイザーはスーパーバイザーの動作を実装します。Calculadora はうまく動作calculadora
しますが、除算 0/0 または例外を作成したときにモジュールを再起動する必要があるスーパーバイザーを試してみると、機能しません。なんで?
PD: モジュールlog_calculadora
は、私が行った操作をcalculadora
log.txt ファイルに書き込むだけです。TEST モジュールは、私に例外終了を与えるモジュールです。
電卓:
-module(calculadora).
-author("BreixoCF").
-behaviour(gen_fsm).
%% Public API
-export([on/0, off/0, modo/1, acumular/1]).
%% Internal API (gen_fsm)
-export([init/1, handle_event/3, terminate/3]).
%% Internal API (estados)
-export([suma/2, suma/3, resta/2, resta/3, producto/2, producto/3, division/2, division/3]).
-define(CALC, calculadora).
-define(LOG_MODULE, log_calculadora).
-define(LOG_FILE, "log.txt").
%%%===================================================================
%%% Public API
%%%===================================================================
-spec on() -> ok.
on() ->
gen_fsm:start_link({local, ?CALC}, ?CALC, 0, []).
-spec off() -> ok.
off() ->
gen_fsm:send_all_state_event(?CALC, stop).
-spec modo(O::atom())-> ok.
modo(suma) ->
gen_fsm:send_event(?CALC, {modoSuma});
modo(resta) ->
gen_fsm:send_event(?CALC, {modoResta});
modo(producto) ->
gen_fsm:send_event(?CALC, {modoProducto});
modo(division) ->
gen_fsm:send_event(?CALC, {modoDivision}).
-spec acumular(N::number()) -> number().
acumular(N) ->
gen_fsm:sync_send_event(?CALC, {numero, N}).
%%%===================================================================
%%% Internal API (gen_fsm)
%%%===================================================================
init(Ac) ->
{ok, _Pid} = gen_event:start_link({local, logcalc}),
gen_event:add_handler(logcalc, ?LOG_MODULE, ?LOG_FILE),
gen_event:notify(logcalc, {on, []}),
{ok, suma, Ac}.
handle_event(stop, _Estado, _DatosEstado) ->
{stop, normal, []}.
terminate(normal, _Estado, _DatosEstado) ->
gen_event:notify(logcalc, {off}),
gen_event:stop(logcalc),
ok.
%%%===================================================================
%%% Internal API (estados)
%%%===================================================================
% Cambio Modo SUMA
suma({modoSuma}, Ac) ->
gen_event:notify(logcalc, {cambio, suma, suma}),
{next_state, suma, Ac};
suma({modoResta}, Ac) ->
gen_event:notify(logcalc, {cambio, suma, resta}),
{next_state, resta, Ac};
suma({modoProducto}, Ac) ->
gen_event:notify(logcalc, {cambio, suma, producto}),
{next_state, producto, Ac};
suma({modoDivision}, Ac) ->
gen_event:notify(logcalc, {cambio, suma, division}),
{next_state, division, Ac}.
% Cálculo Modo SUMA
suma({numero, N}, _From, Ac) ->
gen_event:notify(logcalc, {operacion, suma, N, Ac, Ac+N}),
{reply, Ac+N, suma, Ac+N}.
% Cambio Modo RESTA
resta({modoSuma}, Ac) ->
gen_event:notify(logcalc, {cambio, resta, suma}),
{next_state, suma, Ac};
resta({modoResta}, Ac) ->
gen_event:notify(logcalc, {cambio, resta, resta}),
{next_state, resta, Ac};
resta({modoProducto}, Ac) ->
gen_event:notify(logcalc, {cambio, resta, producto}),
{next_state, producto, Ac};
resta({modoDivision}, Ac) ->
gen_event:notify(logcalc, {cambio, resta, division}),
{next_state, division, Ac}.
% Cálculo Modo RESTA
resta({numero, N}, _From, Ac) ->
gen_event:notify(logcalc, {operacion, resta, N, Ac, Ac-N}),
{reply, Ac-N, resta, Ac-N}.
% Cambio Modo PRODUCTO
producto({modoSuma}, Ac) ->
gen_event:notify(logcalc, {cambio, producto, suma}),
{next_state, suma, Ac};
producto({modoResta}, Ac) ->
gen_event:notify(logcalc, {cambio, producto, resta}),
{next_state, resta, Ac};
producto({modoProducto}, Ac) ->
gen_event:notify(logcalc, {cambio, producto, producto}),
{next_state, producto, Ac};
producto({modoDivision}, Ac) ->
gen_event:notify(logcalc, {cambio, producto, division}),
{next_state, division, Ac}.
% Cálculo Modo PRODUCTO
producto({numero, N}, _From, Ac) ->
gen_event:notify(logcalc, {operacion, producto, N, Ac, Ac*N}),
{reply, Ac*N, producto, Ac*N}.
% Cambio Modo DIVISION
division({modoSuma}, Ac) ->
gen_event:notify(logcalc, {cambio, division, suma}),
{next_state, suma, Ac};
division({modoResta}, Ac) ->
gen_event:notify(logcalc, {cambio, division, resta}),
{next_state, resta, Ac};
division({modoProducto}, Ac) ->
gen_event:notify(logcalc, {cambio, division, producto}),
{next_state, producto, Ac};
division({modoDivision}, Ac) ->
gen_event:notify(logcalc, {cambio, division, division}),
{next_state, division, Ac}.
% Cálculo Modo DIVISION
division({numero, N}, _From, Ac) ->
gen_event:notify(logcalc, {operacion, division, N, Ac, Ac/N}),
{reply, Ac/N, division, Ac/N}.
スーパーバイザー:
-module(supervisor_calculadora).
-author("BreixoCF").
-behaviour(supervisor).
%% API
-export([start/0]).
%% Supervisor callbacks
-export([init/1]).
%%%===================================================================
%%% API functions
%%%===================================================================
start() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%%%===================================================================
%%% Supervisor callbacks
%%%===================================================================
init([]) ->
RestartStrategy = one_for_one,
MaxRestarts = 10,
MaxSecondsBetweenRestarts = 5,
Flags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},
Restart = permanent,
Shutdown = 2000,
Type = worker,
Calculadora = {calculadora, {calculadora, on, []},
Restart, Shutdown, Type, [calculadora]},
{ok, {Flags, [Calculadora]}}.
テストモジュール
-module(calculadora_supervisada_statem).
-behaviour(proper_statem).
-include_lib("proper/include/proper.hrl").
%% CALLBACKS from proper_statem
-export([initial_state/0, command/1, precondition/2, postcondition/3, next_state/3]).
-export([suma/2, resta/2, producto/2, division/2]).
-export([acumular/1, modo/1]).
initial_state() ->
{suma, 0}.
command(_S) ->
frequency([{25, {call, ?MODULE, acumular, [number()]}},
{20, {call, ?MODULE, modo, [modo()]}}]).
modo() ->
elements([suma, resta, producto, division, unknown]).
next_state({division, _S}, _V, {call, ?MODULE, acumular, [0]}) ->
{suma, 0};
next_state({Op, S}, _V, {call, ?MODULE, acumular, [N]}) ->
{Op, erlang:apply(?MODULE, Op, [S, N])};
next_state({_Op, _S}, _V, {call, ?MODULE, modo, [unknown]}) ->
{suma, 0};
next_state({_Op, S}, _V, {call, ?MODULE, modo, [NewOp]}) ->
{NewOp, S};
next_state(S, _V, {call, _, _, _}) ->
S.
precondition(_S, {call, _, _, _}) ->
true.
postcondition({division, _S}, {call, ?MODULE, acumular, [0]}, {'EXIT',_}) ->
true;
postcondition({Op, S}, {call, ?MODULE, acumular, [N]}, Res) ->
Res == ?MODULE:Op(S, N);
postcondition({_Op, _S}, {call, ?MODULE, modo, [_NewOp]}, _Res) ->
true;
postcondition(_S, {call, _, _, _}, _Res) ->
false.
prop_calculadora() ->
?FORALL(Cmds, commands(?MODULE),
begin
supervisor_calculadora:start(),
{H, S, Res} = run_commands(?MODULE,Cmds),
cleanup(),
?WHENFAIL(io:format("History: ~p\nState: ~p\nRes: ~p\n", [H, S, Res]),
aggregate(command_names(Cmds), Res == ok))
end).
%%--------------------------------------------------------------------
%% Internal wrappers and auxiliary functions
%%--------------------------------------------------------------------
acumular(N) ->
Acc = (catch calculadora:acumular(N)),
timer:sleep(100),
Acc.
modo(O) ->
Acc = (catch calculadora:modo(O)),
timer:sleep(100),
Acc.
cleanup() ->
catch calculadora:off(),
timer:sleep(100).
suma(A, B) ->
A + B.
resta(A, B) ->
A - B.
producto(A, B) ->
A * B.
division(_A, 0) ->
0;
division(A, B) ->
A / B.