@@ 1,47 1,49 @@
-% Timing functions.
-% See we need to have a single clock that everything uses, but it has to
-% be able to be simulated as well. So this clock is going to stop and
-% start, potentially run at a different rate than realtime, etc. It
-% will NOT run backwards, quite, but there needs to be a `reset` message
-% that informs clients "the clock has jumped a significant amount, throw
-% away your state and start over", which *could* involve moving
-% backwards.
-%
-% We are going to use the Erlang Term Storage to store a "global
-% mutable" value and have a little node to update it continuously.
-% Then a client can either get the latest time, or have a process
-% running that will keep track of the last time it was asked for
-% time and check for jumps, reset's, etc.
-%
-% Time is Hard. See
-% https://www.erlang.org/doc/apps/erts/time_correction.html
-% for some of the details around it.
-%
-% The timer runs at a fixed rate. For now we will call it 100 Hz, so
-% this is the maximum resolution it can manage. It uses Erlang
-% monotonic time as it stime source, so it may warp or skew, but only
-% forwards. Thus we assume we have a Good OS time source exists before
-% we start anything and that it won't skew around too much. It also
-% ONLY works on the local node; clients on other nodes will not get any
-% messages from this. Timing across distributed systems is NOT
-% something I am going to try to do right now.
-%
-% It sends out time from some arbitrary point in the past, in
-% milliseconds. The user may not assume that they know what the rate or
-% resolution of the timer is. Just that, as far as the robot is
-% concerned, each message is "exact enough".
+%% @doc Timing functions.
+%%
+%% See we need to have a single clock that everything uses, but it has to
+%% be able to be simulated as well. So this clock is going to stop and
+%% start, potentially run at a different rate than realtime, etc. It
+%% will NOT run backwards, quite, but there needs to be a `reset` message
+%% that informs clients "the clock has jumped a significant amount, throw
+%% away your state and start over", which *could* involve moving
+%% backwards.
+%%
+%% We are going to use the Erlang Term Storage to store a "global
+%% mutable" value and have a little node to update it continuously.
+%% Then a client can either get the latest time, or have a process
+%% running that will keep track of the last time it was asked for
+%% time and check for jumps, reset's, etc.
+%%
+%% Time is Hard. See
+%% [https://www.erlang.org/doc/apps/erts/time_correction.html]
+%% for some of the details around it.
+%%
+%% The timer runs at a fixed rate. For now we will call it 100 Hz, so
+%% this is the maximum resolution it can manage. It uses Erlang
+%% monotonic time as it stime source, so it may warp or skew, but only
+%% forwards. Thus we assume we have a Good OS time source exists before
+%% we start anything and that it won't skew around too much. It also
+%% ONLY works on the local node; clients on other nodes will not get any
+%% messages from this. Timing across distributed systems is NOT
+%% something I am going to try to do right now.
+%%
+%% It sends out time from some arbitrary point in the past, in
+%% milliseconds. The user may not assume that they know what the rate or
+%% resolution of the timer is. Just that, as far as the robot is
+%% concerned, each message is "exact enough".
-module(gh_time).
-export([init/0, start/0, start_link/0, raw_time/0, start_timeserver/0,
timeserver/1, timeserver_time/1]).
-% Start the timer service
+%% @doc Start the timer service
start() ->
spawn_link(?MODULE, init, []).
-% Start the timer service
+%% @doc Start the timer service
start_link() ->
spawn_link(?MODULE, init, []).
+%% @private
init() ->
register(gh_time, self()),
Now = erlang:monotonic_time(milli_seconds),
@@ 50,24 52,37 @@ init() ->
ets:insert(Table, {now, Now}),
loop(Table, Now).
-% Call this function to get the raw time.
-% Users should do start_timeserver instead.
+%% @private
+%% @doc Call this function to get the raw time.
+%% Users should do start_timeserver instead.
raw_time() ->
[{now, Now}] = ets:lookup(gh_time, now),
Now.
-% Actually it should start a time tracker thingy for the user.
-% It should run one per user process and keep track of its local
-% time info, so it can report dt and whether the time has jumped,
-% reset, or something else.
-%
-% TODO: Start loop if necessary???
-% Link this to the loop so we restart these if the loop screws up?
-% Ponder more later.
+%% @doc Start a time tracker thingy for the user
+%%
+%% It should run one per user process and keep track of its local
+%% time info, so it can report dt and whether the time has jumped,
+%% reset, or something else.
+%%
+%% @returns The PID of the time server process
+%%
+%% TODO: Start loop if necessary???
+%% Link this to the loop so we restart these if the loop screws up?
+%% Or at least detect it somehow, since raw_time() should be monotonic
+%% and thus never return the exact same time twice?
+%% Ponder more later.
+-spec start_timeserver() -> pid().
start_timeserver() ->
Now = raw_time(),
spawn(?MODULE, timeserver, [Now]).
+%% @doc Get the time from the given time server. Units are
+%% milliseconds. Start time is arbitrary.
+%%
+%% @returns {status, absolute time, dt since last query to the
+%% time server.
+-spec timeserver_time(pid()) -> {atom(), integer(), integer()}.
timeserver_time(Timeserver) ->
Timeserver ! self(),
receive
@@ 75,17 90,19 @@ timeserver_time(Timeserver) ->
% TODO: Timeout?
end.
-% This is the main loop that uses the Erlang Term Storage to
-% store what the last known time is. ETS is node-local, so
-% time is node-local.
-%
-% Time is monotonic. FOR NOW we do not do anything
-% Time 0 point is arbitrary???
-% Units??? -- selectable, milliseconds for now.
-% See https://www.erlang.org/doc/apps/erts/time_correction.html
-%
-% To consider later: alternate time sources such as a specific
-% chrony server, GPS time source, etc.
+%% @private
+%%
+%% @doc This is the main loop that uses the Erlang Term Storage to
+%% store what the last known time is. ETS is node-local, so
+%% time is node-local.
+%%
+%% Time is monotonic. FOR NOW we do not do anything
+%% Time 0 point is arbitrary???
+%% Units??? -- selectable, milliseconds for now.
+%% See https://www.erlang.org/doc/apps/erts/time_correction.html
+%%
+%% To consider later: alternate time sources such as a specific
+%% chrony server, GPS time source, etc.
loop(Table, LastUpdate) ->
% Milliseconds per tick
Ms = 10,
@@ 107,13 124,17 @@ loop(Table, LastUpdate) ->
loop(Table, LastUpdate)
end.
+%% @private
+%%
+%% @doc Main loop of the client-side time server.
timeserver(LastUpdate) ->
% Hmmm, what should the max error be?
% Basically we want to warn the client if
% it hasn't called this frequently enough.
% Maybe that's the client's problem?
% Hmmmm. This is intended to keep track of
- % it *for* the client. Maybe have a hz param.
+ % it *for* the client. Maybe have a hz param
+ % the client provides.
MaxError = 10,
Now = raw_time(),
Dt = Now - LastUpdate,