Current section
Files
Jump to
Current section
Files
src/ecrn_control.erl
%%% @copyright Erlware, LLC. All Rights Reserved.
%%%
%%% This file is provided to you under the BSD License; you may not use
%%% this file except in compliance with the License.
%%%-------------------------------------------------------------------
%%% @doc
%%% Provides testing/fast forward control for the system
-module(ecrn_control).
-behaviour(gen_server).
%% API
-export([start_link/0,
cancel/1,
datetime/0,
set_datetime/1,
multi_set_datetime/2]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-include("internal.hrl").
-record(state, {reference_datetime :: calendar:datetime(),
datetime_at_reference :: erlcron:seconds()}).
%%%===================================================================
%%% API
%%%===================================================================
%%-spec start_link/0 :: () -> {ok, pid()} | ignore | {error, Error::term()}.
-spec start_link() -> {ok, pid()} | ignore | {error, Error::term()}.
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%%-spec cancel/1 :: (erlcron:job_ref()) -> ok | undefined.
-spec cancel(AlarmRef) -> Result when
AlarmRef :: erlcron:job_ref(),
Result :: ok | undefined.
cancel(AlarmRef) ->
gen_server:call(?SERVER, {cancel, AlarmRef}).
%%-spec datetime/0 :: () -> {calendar:datetime(), erlcron:seconds()}.
-spec datetime() -> {calendar:datetime(), erlcron:seconds()}.
datetime() ->
gen_server:call(?SERVER, get_datetime).
%% @doc sets the date-time for the erlcron
%%-spec set_datetime/1 :: (calendar:datetime()) -> ok.
-spec set_datetime(DateTime) -> ok when
DateTime :: calendar:datetime().
set_datetime(DateTime={_,_}) ->
gen_server:call(?SERVER, {set_datetime, DateTime}, infinity).
%% @doc sets the date-time with the erlcron on all nodes
%%-spec multi_set_datetime/2 :: ([node()], calendar:datetime()) -> ok.
-spec multi_set_datetime(Nodes,DateTime) -> ok when
Nodes :: [node()],
DateTime :: calendar:datetime().
multi_set_datetime(Nodes, DateTime={_,_}) ->
gen_server:multi_call(Nodes, ?SERVER, {set_datetime, DateTime}).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
%% @private
init([]) ->
DateTime = erlang:localtime(),
{ok, #state{reference_datetime=DateTime,
datetime_at_reference=ecrn_util:epoch_seconds()}}.
%% @private
handle_call({cancel, AlarmRef}, _From, State) ->
{reply, internal_cancel(AlarmRef), State};
handle_call(get_datetime, _From, State = #state{reference_datetime = DateTime,
datetime_at_reference = Actual}) ->
{reply, {DateTime, Actual}, State};
handle_call({set_datetime, DateTime}, _From, State) ->
NewState = State#state{reference_datetime=DateTime,
datetime_at_reference=ecrn_util:epoch_seconds()},
case lists:foldl(fun({_, [Pid]}, Acc) ->
ecrn_agent:set_datetime(Pid, DateTime,
NewState#state.datetime_at_reference),
Acc;
({Ref, X}, Acc) when is_list(X) ->
[Ref | Acc]
end,
[],
ecrn_reg:get_all()) of
[] ->
{reply, ok, NewState};
ErrorRefs ->
{reply, {error, ErrorRefs}, NewState}
end.
%% @private
handle_cast(_Msg, State) ->
{noreply, State}.
%% @private
handle_info(_Info, State) ->
{noreply, State}.
%% @private
terminate(_Reason, _State) ->
ok.
%% @private
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
internal_cancel(AlarmRef) ->
case ecrn_reg:get(AlarmRef) of
undefined ->
undefined;
{ok, [Pid]} ->
ecrn_agent:cancel(Pid)
end.