%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
%%----------------------------------------------------------------------
%% Purpose: Transaction sender process
%%----------------------------------------------------------------------
-module(megaco_trans_sender).
-export([start_link/5,
stop/1,
upgrade/2,
send_req/3,
send_reqs/3,
send_ack/2,
send_ack_now/2,
send_pending/2,
send_reply/2,
timeout/2,
ack_maxcount/2,
req_maxcount/2,
req_maxsize/2]).
-export([system_continue/3, system_terminate/4, system_code_change/4]).
-export([init/6]).
-include_lib("megaco/include/megaco.hrl").
-include("megaco_message_internal.hrl").
-include_lib("megaco/src/app/megaco_internal.hrl").
-record(state,
{
parent,
conn_handle,
timeout,
req_sz = 0,
req_maxsize, %% Max total size of all accumulated reqs
req_maxcount,
ack_maxcount,
reqs = [],
acks = []
}).
%%%-----------------------------------------------------------------
%%% Public API
%%%-----------------------------------------------------------------
start_link(CH, To, MaxSzReqs, MaxNoReqs, MaxNoAcks) ->
?d("start_link -> entry with"
"~n CH: ~p"
"~n To: ~p"
"~n MaxSzReqs: ~p"
"~n MaxNoReqs: ~p"
"~n MaxNoAcks: ~p", [CH, To, MaxSzReqs, MaxNoReqs, MaxNoAcks]),
Args = [self(), CH, To, MaxSzReqs, MaxNoReqs, MaxNoAcks],
proc_lib:start_link(?MODULE, init, Args).
stop(Pid) when is_pid(Pid) ->
Pid ! stop,
ok.
upgrade(Pid, CH) when is_pid(Pid) ->
Pid ! {upgrade, CH},
ok.
send_req(Pid, Tid, Req) when is_pid(Pid) andalso is_binary(Req) ->
Pid ! {send_req, Tid, Req},
ok.
send_reqs(Pid, Tids, Reqs)
when is_pid(Pid) andalso
is_list(Tids) andalso
is_list(Reqs) andalso
(length(Tids) =:= length(Reqs)) ->
Pid ! {send_reqs, Tids, Reqs},
ok.
send_ack(Pid, Serial) when is_pid(Pid) andalso is_integer(Serial) ->
Pid ! {send_ack, Serial},
ok.
send_ack_now(Pid, Serial) when is_pid(Pid) andalso is_integer(Serial) ->
Pid ! {send_ack_now, Serial},
ok.
send_pending(Pid, Serial) when is_pid(Pid) andalso is_integer(Serial) ->
Pid ! {send_pending, Serial},
ok.
send_reply(Pid, Reply) when is_pid(Pid) andalso is_binary(Reply) ->
Pid ! {send_reply, Reply}.
ack_maxcount(Pid, Max) when is_pid(Pid) andalso is_integer(Max) ->
Pid ! {ack_maxcount, Max},
ok.
req_maxcount(Pid, Max) when is_pid(Pid) andalso is_integer(Max) ->
Pid ! {req_maxcount, Max},
ok.
req_maxsize(Pid, Max) when is_pid(Pid) andalso is_integer(Max) ->
Pid ! {req_maxsize, Max},
ok.
timeout(Pid, Timeout) when is_pid(Pid) ->
Pid ! {timeout, Timeout},
ok.
%%%-----------------------------------------------------------------
%%% Internal exports
%%%-----------------------------------------------------------------
init(Parent, CH, To, MaxSzReqs, MaxNoReqs, MaxNoAcks) ->
?d("init -> entry with"
"~n Parent: ~p"
"~n CH: ~p"
"~n To: ~p"
"~n MaxSzReqs: ~p"
"~n MaxNoReqs: ~p"
"~n MaxNoAcks: ~p", [Parent, CH, To, MaxSzReqs, MaxNoReqs, MaxNoAcks]),
process_flag(trap_exit, true),
proc_lib:init_ack(Parent, {ok, self()}),
S = #state{parent = Parent,
conn_handle = CH,
timeout = To,
req_maxsize = MaxSzReqs,
req_maxcount = MaxNoReqs,
ack_maxcount = MaxNoAcks},
loop(S, To).
%%%-----------------------------------------------------------------
%%% Internal functions
%%%-----------------------------------------------------------------
%% idle (= empty)
loop(#state{reqs = [], acks = [], timeout = Timeout} = S, _) ->
receive
{send_ack, Serial} ->
?d("loop(empty) -> received send_ack [~w] request", [Serial]),
loop(S#state{acks = [Serial]}, Timeout);
{send_ack_now, Serial} ->
?d("loop(empty) -> received send_ack_now [~w] request", [Serial]),
send_msg(S#state.conn_handle, [], [Serial]),
loop(S, Timeout);
{send_req, Tid, Req} when size(Req) >= S#state.req_maxsize ->
?d("loop(empty) -> received (big) send_req request ~w", [Tid]),
send_msg(S#state.conn_handle, [{Tid, Req}], []),
loop(S, Timeout);
{send_req, Tid, Req} ->
?d("loop(empty) -> received send_req request ~w", [Tid]),
loop(S#state{req_sz = size(Req), reqs = [{Tid,Req}]}, Timeout);
{send_reqs, Tids, Reqs} ->
?d("loop(empty) -> received send_reqs request: ~w", [Tids]),
{NewS, _} = handle_send_reqs(Tids, Reqs, S),
loop(NewS, Timeout);
{send_pending, Serial} ->
?d("loop(empty) -> received send_pending [~w] request", [Serial]),
handle_send_result(
send_pending(S#state.conn_handle, Serial, [], [])
),
loop(S, Timeout);
{send_reply, Reply} ->
?d("loop(empty) -> received send_reply request", []),
#state{conn_handle = CH, req_maxsize = MaxSz} = S,
handle_send_result( send_reply(CH, Reply, MaxSz, 0, [], []) ),
loop(S, Timeout);
{upgrade, CH} ->
?d("loop(empty) -> received upgrade request:"
"~n CH: ~p", [CH]),
loop(S#state{conn_handle = CH}, Timeout);
{ack_maxcount, NewMax} ->
?d("loop(empty) -> received ack_maxcount request", []),
loop(S#state{ack_maxcount = NewMax}, Timeout);
{req_maxcount, NewMax} ->
?d("loop(empty) -> received req_maxcount request", []),
loop(S#state{req_maxcount = NewMax}, Timeout);
{req_maxsize, NewMax} ->
?d("loop(empty) -> received req_maxsize request", []),
loop(S#state{req_maxsize = NewMax}, Timeout);
{timeout, NewTimeout} ->
?d("loop(empty) -> received timeout request", []),
loop(S#state{timeout = NewTimeout}, NewTimeout);
stop ->
?d("loop(empty) -> received stop request", []),
exit(normal);
{system, From, Msg} ->
?d("loop(empty) -> received system message:"
"~n From: ~p"
"~n Msg: ~p", [From, Msg]),
Parent = S#state.parent,
sys:handle_system_msg(Msg, From, Parent,
?MODULE, [], {S, Timeout});
{'EXIT', Parent, Reason} when S#state.parent == Parent ->
?d("loop(empty) -> received upgrade request", []),
exit(Reason);
M ->
warning_msg("received unexpected message (ignoring): "
"~n~p", [M]),
loop(S, Timeout)
end;
%% active (= some acks or reqs waiting to to be sent)
loop(#state{reqs = Reqs, acks = Acks, ack_maxcount = MaxAcks,
timeout = Timeout} = S, To)
when To >= 0 ->
Start = t(),
receive
{send_ack, Serial} when length(Acks) + 1 >= MaxAcks ->
?d("loop(active,~w) -> "
"received [~w] send_ack [~w] request",
[To, length(Acks), Serial]),
handle_send_result(
send_msg(S#state.conn_handle, Reqs, [Serial|Acks])
),
loop(S#state{req_sz = 0, reqs = [], acks = []}, Timeout);
{send_ack, Serial} ->
?d("loop(active,~w) -> received send_ack [~w] request",
[To, Serial]),
loop(S#state{acks = [Serial|Acks]}, to(To, Start));
{send_ack_now, Serial} ->
?d("loop(active,~w) -> [~w,~w] "
"received send_ack_now [~w] request",
[To, length(Reqs), length(Acks), Serial]),
handle_send_result(
send_msg(S#state.conn_handle, Reqs, [Serial|Acks])
),
loop(S#state{req_sz = 0, reqs = [], acks = []}, Timeout);
%% We need to check that this is not a resend!!
%% In that case, send whatever we have in store
{send_req, Tid, Req} ->
?d("loop(active,~w) -> received send_req request ~w", [To,Tid]),
{NewS, NewT} =
case handle_send_req(Tid, Req, S) of
{S1, true} ->
{S1, Timeout};
{S1, false} ->
{S1, to(To, Start)}
end,
loop(NewS, NewT);
{send_reqs, Tids, NewReqs} ->
?d("loop(active,~w) -> received send_reqs request ~w", [To,Tids]),
{NewS, NewT} =
case handle_send_reqs(Tids, NewReqs, S) of
{S1, true} ->
{S1, Timeout};
{S1, false} ->
{S1, to(To, Start)}
end,
loop(NewS, NewT);
{send_pending, Serial} ->
?d("loop(active,~w) -> received send_pending [~w] request",
[To, Serial]),
handle_send_result(
send_pending(S#state.conn_handle, Serial, Reqs, Acks)
),
loop(S#state{req_sz = 0, reqs = [], acks = []}, Timeout);
{send_reply, Reply} ->
?d("loop(active,~w) -> received send_reply request", [To]),
#state{conn_handle = CH, req_maxsize = MaxSz, req_sz = ReqSz} = S,
handle_send_result(
send_reply(CH, Reply, MaxSz, ReqSz, Reqs, Acks)
),
loop(S#state{req_sz = 0, reqs = [], acks = []}, Timeout);
{upgrade, CH} ->
?d("loop(active,~w) -> received upgrade request", [To]),
loop(S#state{conn_handle = CH}, to(To, Start));
{req_maxsize, NewMax} ->
?d("loop(active,~w) -> received req_maxsize request", [To]),
loop(S#state{req_maxsize = NewMax}, to(To, Start));
{req_maxcount, NewMax} ->
?d("loop(active,~w) -> received req_maxcount request", [To]),
loop(S#state{req_maxcount = NewMax}, to(To, Start));
{ack_maxcount, NewMax} ->
?d("loop(active,~w) -> received ack_maxcount request", [To]),
loop(S#state{ack_maxcount = NewMax}, to(To, Start));
{timeout, NewTimeout} when NewTimeout > Timeout ->
?d("loop(active,~w) -> received timeout request: ~w",
[To, NewTimeout]),
%% We need to recalculate To
NewTo = NewTimeout - (Timeout - to(To, Start)),
loop(S#state{timeout = NewTimeout}, NewTo);
{timeout, NewTimeout} ->
?d("loop(active,~w) -> received timeout request: ~w",
[To, NewTimeout]),
%% We need to recalculate To
NewTo = to(To, Start) - (Timeout - NewTimeout),
loop(S#state{timeout = NewTimeout}, NewTo);
stop ->
?d("loop(active,~w) -> received stop request", [To]),
handle_send_result( send_msg(S#state.conn_handle, Reqs, Acks) ),
exit(normal);
{system, From, Msg} ->
?d("loop(active,~w) -> received system message:"
"~n From: ~p"
"~n Msg: ~p", [To, From, Msg]),
Parent = S#state.parent,
sys:handle_system_msg(Msg, From, Parent,
?MODULE, [], {S, to(To, Start)});
{'EXIT', Parent, Reason} when S#state.parent == Parent ->
?d("loop(active,~w) -> received exit request", [To]),
exit(Reason);
M ->
warning_msg("received unexpected message (ignoring): "
"~n~p", [M]),
loop(S, to(To, Start))
after To ->
?d("loop(active,~w) -> timeout - time to send", [To]),
handle_send_result( send_msg(S#state.conn_handle, Reqs, Acks) ),
loop(S#state{req_sz = 0, reqs = [], acks = []}, Timeout)
end;
loop(#state{reqs = Reqs, acks = Acks, timeout = Timeout} = S, _To) ->
?d("loop(active) -> timeout [~w, ~w]", [length(Reqs),length(Acks)]),
handle_send_result( send_msg(S#state.conn_handle, Reqs, Acks) ),
loop(S#state{req_sz = 0, reqs = [], acks = []}, Timeout).
%%%-----------------------------------------------------------------
%% The request is itself larger then the max size, so first send
%% everything we have stored in one message, and then the new request
%% in another.
%% Note that it does not matter if we with this request
%% passed the maxcount limit.
%% Note that this message cannot be a re-sent, since
%% such a request would have been stored, but sent immediatly.
handle_send_req(Tid, Req,
#state{conn_handle = CH,
req_maxsize = MaxSz, reqs = Reqs, acks = Acks} = S)
when size(Req) >= MaxSz ->
?d("handle_send_req -> request bigger then maxsize ~w", [MaxSz]),
handle_send_result( send_msg(CH, Reqs, Acks) ),
handle_send_result( send_msg(CH, [{Tid, Req}], []) ),
{S#state{req_sz = 0, reqs = [], acks = []}, true};
%% And handle all the other cases
handle_send_req(Tid, Req,
#state{conn_handle = CH, req_sz = ReqSz,
req_maxcount = MaxReqs, req_maxsize = MaxSz,
reqs = Reqs, acks = Acks} = S) ->
case lists:keymember(Tid, 1, Reqs) of
true ->
%% A re-send, time to send whatever we have in the store
?d("handle_send_req -> was a re-send, so flush",[]),
handle_send_result( send_msg(CH, Reqs, Acks) ),
{S#state{req_sz = 0, reqs = [], acks = []}, true};
false when length(Reqs) + 1 >= MaxReqs ->
%% We finally passed the req-maxcount limit
?d("handle_send_req -> maxcount ~w passed", [MaxReqs]),
handle_send_result(
send_msg(S#state.conn_handle, [{Tid, Req}|Reqs], Acks)
),
{S#state{req_sz = 0, reqs = [], acks = []}, true};
false when size(Req) + ReqSz >= MaxSz ->
%% We finally passed the req-maxsize limit
?d("handle_send_req -> maxsize ~w passed", [MaxSz]),
handle_send_result(
send_msg(S#state.conn_handle, [{Tid, Req}|Reqs], Acks)
),
{S#state{req_sz = 0, reqs = [], acks = []}, true};
false ->
%% Still not time to send
?d("handle_send_req -> nothing to be sent",[]),
{S#state{req_sz = ReqSz + size(Req), reqs = [{Tid, Req}|Reqs]},
false}
end.
%% We passed the req-maxcount limit: Time to send, atleast some of
%% the stuff...
handle_send_reqs(Tids, Reqs0,
#state{conn_handle = CH,
req_maxsize = MaxSz, req_sz = ReqSz,
req_maxcount = MaxReqs, reqs = Reqs, acks = Acks} = S)
when length(Reqs0) + length(Reqs) >= MaxReqs ->
?d("handle_send_reqs -> maxcount ~w: ~w, ~w",
[MaxSz,length(Reqs0),length(Reqs)]),
Reqs1 = merge_tids_and_reqs(Tids, Reqs0, []),
{NewReqs, NewReqSz} = send_reqs(CH, Reqs1, Acks, Reqs, ReqSz, MaxSz),
?d("handle_send_reqs -> sent:"
"~n NewReqSz: ~w"
"~n length(NewReqs): ~w", [NewReqSz, length(NewReqs)]),
{S#state{req_sz = NewReqSz, reqs = NewReqs, acks = []}, true};
%% We did not pass the req-maxcount limit, but we could have passed the
%% req-maxsize limit, so maybe send...
handle_send_reqs(Tids, Reqs0, #state{conn_handle = CH,
req_maxsize = MaxSz, req_sz = ReqSz,
reqs = Reqs, acks = Acks} = S) ->
?d("handle_send_reqs -> not maxcount - maybe maxsize (~w)", [MaxSz]),
Reqs1 = merge_tids_and_reqs(Tids, Reqs0, []),
case maybe_send_reqs(CH, Reqs1, Acks, Reqs, ReqSz, MaxSz, false) of
{NewReqs, NewReqSz, true} ->
?d("handle_send_reqs -> sent:"
"~n NewReqSz: ~w"
"~n length(NewReqs): ~w", [NewReqSz, length(NewReqs)]),
{S#state{req_sz = NewReqSz, reqs = NewReqs, acks = []}, true};
{NewReqs, NewReqSz, false} ->
?d("handle_send_reqs -> not sent:"
"~n NewReqSz: ~w"
"~n length(NewReqs): ~w", [NewReqSz, length(NewReqs)]),
{S#state{req_sz = NewReqSz, reqs = NewReqs}, false}
end.
merge_tids_and_reqs([], [], Reqs) ->
Reqs;
merge_tids_and_reqs([Tid|Tids], [Req|Reqs], Acc) ->
merge_tids_and_reqs(Tids, Reqs, [{Tid,Req}|Acc]).
%% We know that we shall send, so if maybe_send_reqs does not,
%% we send it our self...
send_reqs(CH, Reqs, Acks, Acc, AccSz, MaxSz) ->
?d("send_reqs -> entry when"
"~n length(Reqs): ~w"
"~n Acks: ~w"
"~n length(Acc): ~w"
"~n AccSz: ~w", [length(Reqs), Acks, length(Acc), AccSz]),
case maybe_send_reqs(CH, Reqs, Acks, Acc, AccSz, MaxSz, false) of
{NewReqs, _NewReqSz, false} ->
?d("send_reqs -> nothing sent yet"
"~n length(NewReqs): ~w", [length(NewReqs)]),
handle_send_result( send_msg(CH, NewReqs, Acks) ),
{[], 0};
{NewReqs, NewReqSz, true} ->
?d("send_reqs -> something sent"
"~n length(NewReqs): ~w"
"~n NewReqSz: ~w", [length(NewReqs), NewReqSz]),
{NewReqs, NewReqSz}
end.
maybe_send_reqs(_CH, [], _Acks, Acc, AccSz, _MaxSz, Sent) ->
?d("maybe_send_reqs -> done when"
"~n Sent: ~w"
"~n AccSz: ~w"
"~n length(Acc): ~w", [Sent, AccSz, length(Acc)]),
{Acc, AccSz, Sent};
maybe_send_reqs(CH, [{Tid, Req}|Reqs], Acks, Acc, _AccSz, MaxSz, _Sent)
when size(Req) >= MaxSz ->
%% The request was above the maxsize limit, so first send
%% what's in store and the the big request.
?d("maybe_send_reqs -> entry when request [~w] size (~w) > max size"
"~n Acks: ~w"
"~n length(Acc): ~w", [Tid, size(Req), Acks, length(Acc)]),
handle_send_result( send_msg(CH, Acc, Acks) ),
handle_send_result( send_msg(CH, [{Tid, Req}], []) ),
maybe_send_reqs(CH, Reqs, [], [], 0, MaxSz, true);
maybe_send_reqs(CH, [{Tid, Req}|Reqs], Acks, Acc, AccSz, MaxSz, _Sent)
when AccSz + size(Req) >= MaxSz ->
%% We _did_ pass the maxsize limit with this request, so send
?d("maybe_send_reqs -> entry when sum of requests (~w) > max size"
"~n Tid: ~w"
"~n Acks: ~w"
"~n length(Acc): ~w", [Tid, size(Req) + AccSz, Acks, length(Acc)]),
handle_send_result( send_msg(CH, [{Tid, Req}|Acc], Acks) ),
maybe_send_reqs(CH, Reqs, [], [], 0, MaxSz, true);
maybe_send_reqs(CH, [{Tid, Req}|Reqs], Acks, Acc, AccSz, MaxSz, Sent) ->
?d("maybe_send_reqs -> entry when"
"~n Tid: ~w"
"~n size(Req): ~w"
"~n Acks: ~w"
"~n length(Acc): ~w"
"~n AccSz: ~w", [Tid, size(Req), Acks, length(Acc), AccSz]),
NewAcc = [{Tid,Req}|Acc],
NewAccSz = AccSz + size(Req),
maybe_send_reqs(CH, Reqs, Acks, NewAcc, NewAccSz, MaxSz, Sent).
%%%-----------------------------------------------------------------
send_pending(CH, Serial, Reqs, Acks) ->
?d("send_pending -> entry with"
"~n Serial: ~w"
"~n length(Reqs): ~w"
"~n length(Acks): ~w", [Serial, length(Reqs), length(Acks)]),
case megaco_config:lookup_local_conn(CH) of
[CD] ->
TP = #'TransactionPending'{transactionId = Serial},
Pend = {transactionPending, TP},
do_send_msg(CD, Pend, lists:reverse(Reqs), Acks);
[] ->
ok
end.
%% We need to check the size of the reply. If the reply itself is
%% larger then the max limit, then it is sent in a separate message.
send_reply(CH, Reply, MaxSz, _ReqSz, Reqs, Acks) ->
?d("send_reply -> entry with"
"~n length(Reqs): ~w"
"~n length(Acks): ~w", [length(Reqs), length(Acks)]),
case megaco_config:lookup_local_conn(CH) of
[CD] when size(Reply) > MaxSz ->
handle_send_result( send_msg(CD, lists:reverse(Reqs), Acks) ),
Rep = {transactionReply, Reply},
do_send_msg(CD, Rep, [], []);
[CD] ->
Rep = {transactionReply, Reply},
do_send_msg(CD, Rep, lists:reverse(Reqs), Acks);
[] ->
ok
end.
do_send_msg(CD, Trans, [], []) ->
Body = {transactions, [Trans]},
Slogan = "send trans reply/pending",
?d("do_send_msg -> ~s", [Slogan]),
megaco_messenger_misc:send_body(CD, Slogan, Body);
do_send_msg(CD, Trans, Reqs0, []) ->
Reqs = [{transactionRequest, Req} || {_, Req} <- Reqs0],
Body = {transactions, [Trans|Reqs]},
Slogan = "send trans reply/pending and reqs",
?d("do_send_msg -> ~s", [Slogan]),
megaco_messenger_misc:send_body(CD, Slogan, Body);
do_send_msg(CD, Trans, [], SerialRanges) ->
Acks = make_acks(ranges(SerialRanges), []),
Body = {transactions, [Trans, {transactionResponseAck, Acks}]},
Slogan = "send trans reply/pending and acks",
?d("do_send_msg -> ~s", [Slogan]),
megaco_messenger_misc:send_body(CD, Slogan, Body);
do_send_msg(CD, Trans, Reqs0, SerialRanges) ->
Acks = make_acks(ranges(SerialRanges), []),
Reqs = [{transactionRequest, Req} || {_, Req} <- Reqs0],
Body = {transactions, [Trans, {transactionResponseAck, Acks}|Reqs]},
Slogan = "send trans reply/pending, reqs and acks",
?d("do_send_msg -> ~s", [Slogan]),
megaco_messenger_misc:send_body(CD, Slogan, Body).
send_msg(_, [], []) ->
ok;
send_msg(CH, Reqs, Serials) ->
case megaco_config:lookup_local_conn(CH) of
[ConnData] ->
do_send_msg(ConnData, lists:reverse(Reqs), Serials);
[] ->
ok
end.
do_send_msg(CD, Reqs0, []) ->
?d("do_send_msg -> entry with"
"~n length(Reqs0): ~p", [length(Reqs0)]),
Reqs = [{transactionRequest, Req} || {_, Req} <- Reqs0],
%% ?d("do_send_msg -> Reqs: ~n~p", [Reqs]),
Body = {transactions, Reqs},
megaco_messenger_misc:send_body(CD, "send trans reqs", Body);
do_send_msg(CD, [], SerialRanges) ->
?d("do_send_msg -> entry with"
"~n SerialRanges: ~p", [SerialRanges]),
Acks = make_acks(ranges(SerialRanges), []),
%% ?d("do_send_msg -> Reqs: ~n~p", [Reqs]),
Body = {transactions, [{transactionResponseAck, Acks}]},
megaco_messenger_misc:send_body(CD, "send trans acks", Body);
do_send_msg(CD, Reqs0, SerialRanges) ->
?d("do_send_msg -> entry with"
"~n length(Reqs0): ~p"
"~n SerialRanges: ~p", [length(Reqs0), SerialRanges]),
Acks = make_acks(ranges(SerialRanges), []),
Reqs = [{transactionRequest, Req} || {_, Req} <- Reqs0],
%% ?d("do_send_msg -> Reqs: ~n~p", [Reqs]),
Body = {transactions, [{transactionResponseAck, Acks}|Reqs]},
megaco_messenger_misc:send_body(CD, "send trans reqs and acks", Body).
handle_send_result(ok) ->
ok;
handle_send_result({ok, _}) ->
ok;
handle_send_result({error, {send_message_cancelled, _Reason}}) ->
ok;
handle_send_result({error, {send_message_failed, Reason}}) ->
error_msg("Failed sending message: ~n ~p", [Reason]),
error;
handle_send_result(Error) ->
error_msg("Failed sending message: ~n ~p", [Error]),
error.
ranges(L) ->
lists:reverse(ranges(lists:sort(L), [], [])).
ranges([], Range, Ranges) ->
ranges2(Range, Ranges);
ranges([S1|Sn], [S2|_] = Range, Ranges) when S1 == (S2+1) ->
ranges(Sn, [S1|Range], Ranges);
ranges([S|Sn], Range, Ranges) ->
ranges(Sn, [S], ranges2(Range, Ranges)).
ranges2([], Ranges) ->
Ranges;
ranges2([S], Ranges) ->
[{S,S}|Ranges];
ranges2(Range0, Ranges) ->
Range = lists:reverse(Range0),
[{hd(Range),lists:last(Range)}|Ranges].
make_acks([], Acks) ->
lists:reverse(Acks);
make_acks([{S,S}|SerialRanges], Acks) ->
TRA = #'TransactionAck'{firstAck = S},
make_acks(SerialRanges, [TRA|Acks]);
make_acks([{F,L}|SerialRanges], Acks) ->
TRA = #'TransactionAck'{firstAck = F, lastAck = L},
make_acks(SerialRanges, [TRA|Acks]).
%%%-----------------------------------------------------------------
to(To, Start) ->
To - (t() - Start).
%% Time in milli seconds
t() ->
erlang:monotonic_time(milli_seconds).
warning_msg(F, A) ->
?megaco_warning("Transaction sender: " ++ F, A).
error_msg(F, A) ->
?megaco_error("Transaction sender: " ++ F, A).
%%%-----------------------------------------------------------------
%%% System messages handled here
%%%-----------------------------------------------------------------
system_continue(_Parent, _Dbg, {S,To}) ->
loop(S, To).
system_terminate(Reason, _Parent, _Dbg, {S, _}) ->
#state{conn_handle = CH, reqs = Reqs, acks = Acks} = S,
send_msg(CH, Reqs, Acks),
exit(Reason).
system_code_change(S, _Module, _OLdVsn, _Extra) ->
?d("system_code_change -> entry", []),
{ok, S}.