aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tv/src/tv_etsread.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tv/src/tv_etsread.erl')
-rw-r--r--lib/tv/src/tv_etsread.erl770
1 files changed, 0 insertions, 770 deletions
diff --git a/lib/tv/src/tv_etsread.erl b/lib/tv/src/tv_etsread.erl
deleted file mode 100644
index 32f111c9a1..0000000000
--- a/lib/tv/src/tv_etsread.erl
+++ /dev/null
@@ -1,770 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%%*********************************************************************
-%%%
-%%% Description: Module containing the interface towards ETS tables,
-%%% i.e., handling the polling and thereafter sending the
-%%% result to the database part of the table tool.
-%%%
-%%%*********************************************************************
-
-
--module(tv_etsread).
--compile([{nowarn_deprecated_function,{gs,destroy,1}},
- {nowarn_deprecated_function,{gs,start,0}},
- {nowarn_deprecated_function,{gs,window,3}}]).
-
-
-
--export([etsread/2]).
-
-
-
--include("tv_int_def.hrl").
--include("tv_int_msg.hrl").
-
-
-
-
-
-
-%%%*********************************************************************
-%%% EXTERNAL FUNCTIONS
-%%%*********************************************************************
-
-
-
-
-etsread(MasterPid, ErrorMsgMode) ->
- process_flag(trap_exit, true),
- put(error_msg_mode, ErrorMsgMode),
- blocked(MasterPid).
-
-
-
-
-
-%%%*********************************************************************
-%%% INTERNAL FUNCTIONS
-%%%*********************************************************************
-
-
-
-blocked(MasterPid) ->
- receive
- Msg ->
- case Msg of
-
- #etsread_deblock{} ->
- deblock(Msg, MasterPid);
-
- {'EXIT', Pid, Reason} ->
- exit_signals({Pid, Reason}, MasterPid),
- blocked(MasterPid);
-
- {error_msg_mode, Mode} ->
- put(error_msg_mode, Mode),
- blocked(MasterPid);
-
- _Other ->
- %% io:format("Received signal ~p~n", [_Other]),
- blocked(MasterPid)
- end
- end.
-
-
-
-
-
-
-
-deblock(Msg, MasterPid) ->
- #etsread_deblock{dbs_pid = DbsPid,
- table_type = KindOfTable,
- node = Node,
- local_node = LocalNode,
- table_id = TableId,
- poll_interval = PollInt} = Msg,
- PollInterval = case PollInt of
- infinity ->
- PollInt;
- _Other ->
- PollInt * 1000
- end,
- %% Get table info!
- case catch get_table_info(Node, LocalNode, TableId, KindOfTable) of
- nodedown ->
- MasterPid ! #pc_nodedown{sender = self(),
- automatic_polling = false},
- blocked(MasterPid);
- no_table ->
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = false},
- blocked(MasterPid);
- mnesia_not_started ->
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = false},
- blocked(MasterPid);
- {unexpected_error,_Reason} ->
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = false},
- blocked(MasterPid);
- {Type, Pos, Protection} ->
- MasterPid ! #etsread_deblock_cfm{sender = self(),
- type = Type,
- keypos = Pos,
- protection = Protection
- },
-
- timer:sleep(500),
- case catch read_table(Node, LocalNode, TableId, KindOfTable, DbsPid) of
- nodedown ->
- MasterPid ! #pc_nodedown{sender = self(),
- automatic_polling = false},
- blocked(MasterPid);
- no_table ->
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = false},
- blocked(MasterPid);
- mnesia_not_started ->
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = false},
- blocked(MasterPid);
- {unexpected_error,_Reason} ->
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = false},
- blocked(MasterPid);
- _ElapsedTime ->
- deblocked_loop(MasterPid, DbsPid, Node, LocalNode, TableId,
- KindOfTable, PollInterval)
- end
- end.
-
-
-
-
-
-
-get_table_info(Node, LocalNode, TableId, KindOfTable) ->
- case KindOfTable of
- ets ->
- % Check whether table is 'bag' or 'set' type.
- Type = tv_ets_rpc:info(Node, LocalNode, TableId, type),
- % Get position for the key.
- Pos = tv_ets_rpc:info(Node, LocalNode, TableId, keypos),
- Protection = tv_ets_rpc:info(Node, LocalNode, TableId, protection),
- {Type, Pos, Protection};
- mnesia ->
- Type = tv_mnesia_rpc:table_info(Node, LocalNode, TableId, type),
- Pos = 2,
- %% All Mnesia tables are regarded as being public!
- {Type, Pos, public}
- end.
-
-
-
-
-
-
-deblocked_loop(MasterPid,DbsPid,Node,LocalNode,TableId,KindOfTable,PollInterval) ->
- receive
- Msg ->
-
- case Msg of
-
- #etsread_poll_table{} ->
- case catch read_table(Node, LocalNode, TableId, KindOfTable, DbsPid) of
- %% No automatic polling here!
- nodedown ->
- MasterPid ! #pc_nodedown{sender = self(),
- automatic_polling = false};
- no_table ->
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = false};
- mnesia_not_started ->
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = false};
- {unexpected_error,_Reason} ->
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = false};
- _ElapsedTime ->
- done
- end,
- deblocked_loop(MasterPid, DbsPid, Node, LocalNode,
- TableId, KindOfTable, PollInterval);
-
-
- #etsread_set_poll_interval{interval = PollInt} ->
- NewPollInterval = case PollInt of
- infinity ->
- PollInt;
- _Other ->
- PollInt * 1000
- end,
- deblocked_loop(MasterPid, DbsPid, Node, LocalNode,
- TableId, KindOfTable, NewPollInterval);
-
-
- #etsread_deblock{} ->
- deblock(Msg, MasterPid);
-
-
- #etsread_update_object{key_no=KeyNo, object=Obj, old_object=OldObj} ->
- update_object(KindOfTable, Node, LocalNode, TableId, DbsPid,
- KeyNo, Obj, OldObj, MasterPid, PollInterval),
- deblocked_loop(MasterPid, DbsPid, Node, LocalNode, TableId, KindOfTable,
- PollInterval);
-
-
- #etsread_new_object{object=Obj} ->
- new_object(KindOfTable, Node, LocalNode, TableId, DbsPid,
- Obj, MasterPid, PollInterval),
- deblocked_loop(MasterPid, DbsPid, Node, LocalNode, TableId, KindOfTable,
- PollInterval);
-
-
- #etsread_delete_object{object=Obj} ->
- delete_object(KindOfTable, Node, LocalNode, TableId, DbsPid,
- Obj, MasterPid, PollInterval),
- deblocked_loop(MasterPid, DbsPid, Node, LocalNode, TableId, KindOfTable,
- PollInterval);
-
-
- #ip_dead_table{} ->
- AutoPoll = case PollInterval of
- infinity ->
- false;
- _Other ->
- true
- end,
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = AutoPoll},
- deblocked_loop(MasterPid, DbsPid, Node, LocalNode, TableId,
- KindOfTable, infinity);
-
-
- #etsread_nodedown{} ->
- AutoPoll = case PollInterval of
- infinity ->
- false;
- _Other ->
- true
- end,
- MasterPid ! #pc_nodedown{sender = self(),
- automatic_polling = AutoPoll},
- deblocked_loop(MasterPid, DbsPid, Node, LocalNode, TableId,
- KindOfTable, infinity);
-
-
- {error_msg_mode, Mode} ->
- put(error_msg_mode, Mode),
- deblocked_loop(MasterPid, DbsPid, Node, LocalNode, TableId, KindOfTable,
- PollInterval);
-
-
- {'EXIT', Pid, Reason} ->
- exit_signals({Pid, Reason}, MasterPid),
- deblocked_loop(MasterPid, DbsPid, Node, LocalNode,
- TableId, KindOfTable, PollInterval)
- end
-
- after PollInterval ->
- %% Automatic polling must be on, otherwise these
- %% lines would never be executed!
- NewPollInterval =
- case catch read_table(Node,LocalNode,TableId,KindOfTable,DbsPid) of
- nodedown ->
- MasterPid ! #pc_nodedown{sender = self(),
- automatic_polling = true},
- infinity;
- no_table ->
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = true},
- infinity;
- mnesia_not_started ->
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = true},
- infinity;
- {unexpected_error,_Reason} ->
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = true},
- infinity;
- ElapsedMilliseconds ->
- if
- (ElapsedMilliseconds * 1000) >= PollInterval ->
- infinity;
- true ->
- PollInterval
- end
- end,
- deblocked_loop(MasterPid, DbsPid, Node, LocalNode,
- TableId, KindOfTable, NewPollInterval)
- end.
-
-
-
-
-
-exit_signals(ExitInfo, MasterPid) ->
- case ExitInfo of
- {MasterPid, _Reason} ->
- exit(normal);
- _Other ->
- done
- end.
-
-
-
-
-update_object(KindOfTable, Node, LocalNode, TableId, DbsPid, KeyNo, Obj, OldObj, MasterPid, PollInterval) ->
- AutoPoll =
- case PollInterval of
- infinity ->
- false;
- _Other ->
- true
- end,
- case check_record_format(KindOfTable, Node, LocalNode, TableId, Obj) of
- bad_format ->
- DbsPid ! #etsread_update_object_cfm{sender = self(),
- success = false};
- ok ->
- %% Check that we are allowed to edit the table!
- case catch update_object2(KindOfTable, Node, LocalNode, TableId, DbsPid, KeyNo,
- Obj, OldObj) of
-
- nodedown ->
- DbsPid ! #etsread_update_object_cfm{sender = self(),
- success = false},
- MasterPid ! #pc_nodedown{sender = self(),
- automatic_polling = AutoPoll};
-
- no_table ->
- DbsPid ! #etsread_update_object_cfm{sender = self(),
- success = false},
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = AutoPoll};
-
- mnesia_not_started ->
- DbsPid ! #etsread_update_object_cfm{sender = self(),
- success = false},
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = AutoPoll};
-
-
- {unexpected_error,_Reason} ->
- DbsPid ! #etsread_update_object_cfm{sender = self(),
- success = false},
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = AutoPoll};
-
- ok ->
- DbsPid ! #etsread_update_object_cfm{sender = self(),
- success = true}
- end
- end.
-
-
-
-
-
-update_object2(ets, Node, LocalNode, Tab, _DbsPid, KeyNo, Obj, OldObj) ->
- %% We shall update a specific object! If the table is a 'set' table,
- %% it is just to insert the altered object. However, if the table
- %% is a 'bag', or a 'duplicate_bag', we first have to remove the
- %% old object, and then insert the altered one.
- %% But, we aren't finished with that... we also want to preserve
- %% the time order, meaning we have to delete *ALL* objects having the
- %% very same key, and then insert them again! (Actually we would have
- %% to do this anyhow, due to limitations in the interface functions,
- %% but this remark has to be noted!)
- OldKey = element(KeyNo, OldObj),
- InsertList =
- case tv_ets_rpc:info(Node, LocalNode, Tab, type) of
- set ->
- %% Have to remove old object, because the key may be what's changed.
- tv_ets_rpc:delete(Node, LocalNode, Tab, OldKey),
- [Obj];
- ordered_set ->
- %% Have to remove old object, because the key may be what's changed.
- tv_ets_rpc:delete(Node, LocalNode, Tab, OldKey),
- [Obj];
- _Other -> %% 'bag' or 'duplicate_bag'
- OldList = tv_ets_rpc:lookup(Node, LocalNode, Tab, OldKey),
- tv_ets_rpc:delete(Node, LocalNode, Tab, OldKey),
- %% Have to beware of duplicate_bag tables,
- %% i.e., the same object may occur more than
- %% once, but we only want to replace it once!
- {_Replaced, TmpList} =
- lists:foldl(
- fun(Data, {Replaced,Acc}) when Data =/= OldObj ->
- {Replaced, [Data | Acc]};
- (_Data, {Replaced,Acc}) when not Replaced ->
- {true, [Obj | Acc]};
- (Data, {Replaced,Acc}) ->
- {Replaced, [Data | Acc]}
- end,
- {false, []},
- OldList),
- lists:reverse(TmpList)
- end,
- lists:foreach(fun(H) ->
- tv_ets_rpc:insert(Node, LocalNode, Tab, H)
- end,
- InsertList),
- ok;
-update_object2(mnesia, Node, LocalNode, Tab, _DbsPid, KeyNo, Obj, OldObj) ->
- OldKey = element(KeyNo, OldObj),
- InsertList =
- case tv_mnesia_rpc:table_info(Node, LocalNode, Tab, type) of
- set ->
- tv_mnesia_rpc:transaction(
- Node,
- LocalNode,
- fun() ->
- mnesia:delete(Tab,OldKey,write)
- end),
- [Obj];
- ordered_set ->
- tv_mnesia_rpc:transaction(
- Node,
- LocalNode,
- fun() ->
- mnesia:delete(Tab,OldKey,write)
- end),
- [Obj];
- _Other -> %% 'bag' or 'duplicate_bag'
- {atomic, OldList} =
- tv_mnesia_rpc:transaction(
- Node,
- LocalNode,
- fun() ->
- mnesia:read(Tab,OldKey,read)
- end),
- %% We can't use mnesia:delete_object here, because
- %% time order wouldn't be preserved then!!!
- tv_mnesia_rpc:transaction(
- Node,
- LocalNode,
- fun() ->
- mnesia:delete(Tab,OldKey,write)
- end),
- ChangeFun =
- fun(H) when H =:= OldObj ->
- Obj;
- (H) ->
- H
- end,
- [ChangeFun(X) || X <- OldList]
- end,
- lists:foreach(fun(H) ->
- tv_mnesia_rpc:transaction(
- Node,
- LocalNode,
- fun() ->
- %% This mnesia call shall not be distributed,
- %% since the transaction sees to that it is
- %% executed on the right node!!!
- mnesia:write(Tab,H,write)
- end)
- end,
- InsertList),
- ok.
-
-
-
-
-
-
-delete_object(KindOfTable, Node, LocalNode, TableId, DbsPid, Obj, MasterPid, PollInterval) ->
- AutoPoll =
- case PollInterval of
- infinity ->
- false;
- _Other ->
- true
- end,
- case catch delete_object2(KindOfTable, Node, LocalNode, TableId, DbsPid, Obj) of
-
- nodedown ->
- DbsPid ! #etsread_delete_object_cfm{sender = self(),
- success = false},
- MasterPid ! #pc_nodedown{sender = self(),
- automatic_polling = AutoPoll};
-
- no_table ->
- DbsPid ! #etsread_delete_object_cfm{sender = self(),
- success = false},
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = AutoPoll};
-
- mnesia_not_started ->
- DbsPid ! #etsread_delete_object_cfm{sender = self(),
- success = false},
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = AutoPoll};
-
- {unexpected_error,_Reason} ->
- DbsPid ! #etsread_delete_object_cfm{sender = self(),
- success = false},
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = AutoPoll};
-
- ok ->
- DbsPid ! #etsread_delete_object_cfm{sender = self(),
- success = true}
- end.
-
-
-
-
-delete_object2(ets, Node, LocalNode, Tab, _DbsPid, Obj) ->
- KeyNo = tv_ets_rpc:info(Node, LocalNode, Tab, keypos),
- Key = element(KeyNo, Obj),
- InsertList =
- case tv_ets_rpc:info(Node, LocalNode, Tab, type) of
- set ->
- %% Have to remove old object, because the key may be what's changed.
- tv_ets_rpc:delete(Node, LocalNode, Tab, Key),
- [];
- ordered_set ->
- %% Have to remove old object, because the key may be what's changed.
- tv_ets_rpc:delete(Node, LocalNode, Tab, Key),
- [];
- _Other -> %% 'bag' or 'duplicate_bag'
- OldList = tv_ets_rpc:lookup(Node, LocalNode, Tab, Key),
- tv_ets_rpc:delete(Node, LocalNode, Tab, Key),
- OldList -- [Obj]
- end,
-
- lists:foreach(fun(H) ->
- tv_ets_rpc:insert(Node, LocalNode, Tab, H)
- end,
- InsertList),
- ok;
-delete_object2(mnesia, Node, LocalNode, Tab, _DbsPid, Obj) ->
- tv_mnesia_rpc:transaction(
- Node,
- LocalNode,
- fun() ->
- %% This mnesia call shall not be distributed,
- %% since the transaction sees to that it is
- %% executed on the right node!!!
- mnesia:delete_object(Tab,Obj,write)
- end),
- ok.
-
-
-
-
-
-new_object(KindOfTable, Node, LocalNode, TableId, DbsPid, Obj, MasterPid, PollInterval) ->
- AutoPoll =
- case PollInterval of
- infinity ->
- false;
- _Other ->
- true
- end,
- case check_record_format(KindOfTable, Node, LocalNode, TableId, Obj) of
- bad_format ->
- DbsPid ! #etsread_new_object_cfm{sender = self(),
- success = false};
- ok ->
- %% Check that we are allowed to edit the table!
- case catch new_object2(KindOfTable, Node, LocalNode, TableId, DbsPid, Obj) of
-
- nodedown ->
- DbsPid ! #etsread_new_object_cfm{sender = self(),
- success = false},
- MasterPid ! #pc_nodedown{sender = self(),
- automatic_polling = AutoPoll};
-
- no_table ->
- DbsPid ! #etsread_new_object_cfm{sender = self(),
- success = false},
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = AutoPoll};
-
- mnesia_not_started ->
- DbsPid ! #etsread_new_object_cfm{sender = self(),
- success = false},
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = AutoPoll};
-
- {unexpected_error,_Reason} ->
- DbsPid ! #etsread_new_object_cfm{sender = self(),
- success = false},
- MasterPid ! #pc_dead_table{sender = self(),
- automatic_polling = AutoPoll};
-
- ok ->
- DbsPid ! #etsread_new_object_cfm{sender = self(),
- success = true}
- end
- end.
-
-
-
-
-
-new_object2(ets, Node, LocalNode, Tab, _DbsPid, Obj) ->
- tv_ets_rpc:insert(Node, LocalNode, Tab, Obj),
- ok;
-new_object2(mnesia, Node, LocalNode, Tab, _DbsPid, Obj) ->
- tv_mnesia_rpc:transaction(
- Node,
- LocalNode,
- fun() ->
- %% This mnesia call shall not be distributed,
- %% since the transaction sees to that it is
- %% executed on the right node!!!
- mnesia:write(Tab,Obj,write)
- end),
- ok.
-
-
-
-
-
-check_record_format(mnesia, Node, LocalNode, Tab, Obj) ->
- Arity = tv_mnesia_rpc:table_info(Node, LocalNode, Tab, arity),
- case size(Obj) of
- Arity ->
- ok;
- _Other ->
- gs:window(etsreadwin, gs:start(), []),
- case get(error_msg_mode) of
- normal ->
- tv_utils:notify(etsreadwin, "TV Notification",
- ["The record is not complete,",
- "too few fields are specified!"]);
- haiku ->
- tv_utils:notify(etsreadwin, "TV Notification",
- ["The attempt to change",
- "The specified record size",
- "Is simply ignored."])
- end,
- gs:destroy(etsreadwin),
- bad_format
- end;
-check_record_format(ets, _Node, _LocalNode, _Tab, _Obj) ->
- ok.
-
-
-
-
-
-
-
-read_table(Node, LocalNode, Tab, KindOfTable, DbsPid) ->
- T1 = time(),
-
- {TableContent, ListOfKeys} =
- case KindOfTable of
- ets ->
- {tv_ets_rpc:tab2list(Node, LocalNode, Tab),
- [tv_ets_rpc:info(Node, LocalNode, Tab, keypos)]
- };
- mnesia ->
- %% It may be tempting to use Mnesia event subscription,
- %% but will this really save the day? The main drawback
- %% is that we will then have to update the table copy we
- %% store internally in two different ways: one for the
- %% Mnesia tables, and one for the ETS tables. Also, if
- %% the Mnesia tables are frequently updated, this will
- %% cause TV to work all the time too (either updating the
- %% table copy for each inserted/deleted object, or storing
- %% these objects until polling is ordered). To make this
- %% work smoothly requires a bit of work...
- %% The second drawback is that it doesn't seem clear in all
- %% circumstances how the subscription actually works - i.e.,
- %% if we only use subscriptions, can we actually be sure that
- %% the *real* state of the table is the same as the one kept
- %% in TV? For example, imagine the scenario that Mnesia is
- %% stopped, all Mnesia directories are removed (from the UNIX
- %% shell), and then Mnesia once again is started. The first
- %% problem is that we have to check for start/stop of Mnesia,
- %% the second is that we then have to rescan the actual table.
- %% The logic for this may require som effort to write!
- %% Also, what will happen if the table is killed/dies?
- %% Will we get messages for each element in the table?
- %% (I havent't checked this last issue, this is just som thoughts.)
- %% And generally, there is always a risk that a message is lost,
- %% which will result in TV showing an erroneous table content.
- %%
- %% All in all, using Mnesia subscriptions *may* be a sub-optimization.
- %% The current solution works fine, is also easy to control, and is
- %% mainly the same for both ETS and Mnesia tables.
- %% My suggestion is that it is used until someone actually complains
- %% about the polling time being too long for huge tables! :-)
- %% (However, it shall be emphasized that it is this module that
- %% actually polls the Mnesia/ETS tables, meaning that it is
- %% mainly this module that has to be modified, should the usage of
- %% subscriptions be desired. The other module that has to be modified
- %% is the one maintaining the internal copy of the table.)
- WildPattern = tv_mnesia_rpc:table_info(Node,LocalNode,Tab,wild_pattern),
- {atomic, Content} =
- tv_mnesia_rpc:transaction(
- Node,
- LocalNode,
- fun() ->
- %% This mnesia call shall not be distributed,
- %% since the transaction sees to that it is
- %% executed on the right node!!!
- mnesia:match_object(Tab, WildPattern, read)
- end),
- {Content, [2 | tv_mnesia_rpc:table_info(Node, LocalNode,Tab, index)]}
- end,
-
- T2 = time(),
-
- ElapsedTime = compute_elapsed_seconds(T1, T2),
-
- DbsPid ! #dbs_new_data{sender = self(),
- data = TableContent,
- keys = ListOfKeys,
- time_to_read_table = ElapsedTime
- },
-
- ElapsedTime.
-
-
-
-
-
-
-
-compute_elapsed_seconds({H1, M1, S1}, {H2, M2, S2}) ->
- ElapsedHours = get_time_diff(hours, H1, H2),
- ElapsedMinutes = get_time_diff(minutes, M1, M2),
- ElapsedSeconds = get_time_diff(seconds, S1, S2),
- (ElapsedHours * 3600) + (ElapsedMinutes * 60) + ElapsedSeconds + 1.
-
-
-
-
-
-get_time_diff(_Type, T1, T2) when T1 =< T2 ->
- T2 - T1;
-get_time_diff(hours, T1, T2) ->
- T2 + 24 - T1;
-get_time_diff(minutes, T1, T2) ->
- T2 + 60 - T1;
-get_time_diff(seconds, T1, T2) ->
- T2 + 60 - T1.