I have 3 modules: calculadora
, log_calculadora
and supervisor_calculadora
. Calculadora
is just a simple calculator that makes sum, subtraction, multiplication and division using gen_fsm and supervisor implements supervisor behaviour. Calculadora works well but when I try the supervisor, who must restart the calculadora
module when you make a division 0/0 or an exception, it doesn't work. Why?
PD:The module log_calculadora
just writes the operations that I made in calculadora
in a log.txt file. The TEST module is the one who gives me the exception exit.
-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.
proper:quickcheck(calculadora_supervisada_statem:prop_calculadora()).
Triggers the following
prop_calculadora() ->
?FORALL(Cmds, commands(?MODULE),
begin
supervisor_calculadora:start(),
In the above code I see that supervisor_calculadora is started for each test case (quickchecl ?FORALL executes the block for each Cmd). It will try to start the supervisor and register it again with the same name and fails.