aboutsummaryrefslogtreecommitdiffstats
path: root/lib/megaco/test/megaco_call_flow_test.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/megaco/test/megaco_call_flow_test.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/megaco/test/megaco_call_flow_test.erl')
-rw-r--r--lib/megaco/test/megaco_call_flow_test.erl1767
1 files changed, 1767 insertions, 0 deletions
diff --git a/lib/megaco/test/megaco_call_flow_test.erl b/lib/megaco/test/megaco_call_flow_test.erl
new file mode 100644
index 0000000000..a25a7924e8
--- /dev/null
+++ b/lib/megaco/test/megaco_call_flow_test.erl
@@ -0,0 +1,1767 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2009. 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%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Test encoding/decoding of the sample call flows Megaco/H.248
+%%----------------------------------------------------------------------
+%% megaco_call_flow_test:pretty_text().
+%% megaco_call_flow_test:compact_text().
+%% megaco_call_flow_test:bin().
+%% megaco_call_flow_test:asn1_ber().
+%% megaco_call_flow_test:asn1_ber_bin().
+%% megaco_call_flow_test:asn1_per().
+%% megaco_call_flow_test:erl_dist().
+%% megaco_call_flow_test:compressed_erl_dist().
+%% megaco_call_flow_test:gnuplot_gif().
+%% megaco_call_flow_test:gnuplot_size_gif().
+%% megaco_call_flow_test:gnuplot_enc_time_gif().
+%% megaco_call_flow_test:gnuplot_dec_time_gif().
+%% megaco_call_flow_test:gnuplot_code_time_gif().
+%%----------------------------------------------------------------------
+
+-module(megaco_call_flow_test).
+
+-compile(export_all).
+-include_lib("megaco/include/megaco.hrl").
+-include_lib("megaco/include/megaco_message_v1.hrl").
+-include("megaco_test_lib.hrl").
+
+t() -> megaco_test_lib:t(?MODULE).
+t(Case) -> megaco_test_lib:t({?MODULE, Case}).
+
+%% Test server callbacks
+init_per_testcase(Case, Config) ->
+ megaco_test_lib:init_per_testcase(Case, Config).
+
+fin_per_testcase(Case, Config) ->
+ megaco_test_lib:fin_per_testcase(Case, Config).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Top test case
+
+all(suite) ->
+ [
+ text,
+ binary
+ ].
+
+text(suite) ->
+ [
+ pretty,
+ compact
+ ].
+
+flex(suite) ->
+ [
+ pretty_flex,
+ compact_flex
+ ].
+
+binary(suite) ->
+ [
+ bin,
+ ber,
+ ber_bin,
+ per
+ ].
+
+pretty(suite) ->
+ [];
+pretty(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ pretty_text().
+
+compact(suite) ->
+ [];
+compact(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ compact_text().
+
+pretty_flex(suite) ->
+ [];
+pretty_flex(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ pretty_flex().
+
+compact_flex(suite) ->
+ [];
+compact_flex(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ compact_flex().
+
+bin(suite) ->
+ [];
+bin(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ bin().
+
+ber(suite) ->
+ [];
+ber(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ asn1_ber().
+
+ber_bin(suite) ->
+ [];
+ber_bin(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ asn1_ber_bin().
+
+per(suite) ->
+ [];
+per(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ asn1_per().
+
+standard_erl(suite) ->
+ [];
+standard_erl(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ standard_erl().
+
+compressed_erl(suite) ->
+ [];
+compressed_erl(Config) when is_list(Config) ->
+ ?ACQUIRE_NODES(1, Config),
+ compressed_erl().
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(PP(Name, Val), #'PropertyParm'{name = Name, value = [Val]}).
+-define(SP(Name, Val), #'StatisticsParameter'{statName = Name, statValue = [Val]}) .
+
+names() ->
+ [
+ msg1, msg2, msg3, msg4, msg5a, msg5b, msg6, msg7, msg9, msg10,
+ msg11, msg12, msg13, msg14, msg15, msg16, msg17a, msg17b,
+ msg18a, msg18b, msg18c, msg18d, msg19a, msg19b, msg20, msg21,
+ msg22a, msg22b, msg23a, msg23b
+ ].
+
+%% In this example, MG1 has the IP address 124.124.124.222, MG2 is
+%% 125.125.125.111, and the MGC is 123.123.123.4. The default Megaco port
+%% is 55555 for all three.
+
+-define(DEFAULT_PORT, 55555).
+-define(MG1_MID_NO_PORT, {ip4Address,
+ #'IP4Address'{address = [124, 124, 124, 222]}}).
+-define(MG1_MID, {ip4Address, #'IP4Address'{address = [124, 124, 124, 222],
+ portNumber = ?DEFAULT_PORT}}).
+-define(MG2_MID, {ip4Address, #'IP4Address'{address = [125, 125, 125, 111],
+ portNumber = ?DEFAULT_PORT}}).
+-define(MGC_MID, {ip4Address, #'IP4Address'{address = [123, 123, 123, 4],
+ portNumber = ?DEFAULT_PORT}}).
+
+-define(A4444, ["11111111", "00000000", "00000000"]).
+-define(A4445, ["11111111", "00000000", "11111111"]).
+-define(A5555, ["11111111", "11111111", "00000000"]).
+-define(A5556, ["11111111", "11111111", "11111111"]).
+
+%% -define(A4444, ["00000000"]).
+%% -define(A4445, ["00001111"]).
+%% -define(A5555, ["11110000"]).
+%% -define(A5556, ["11111111"]).
+
+request(Mid, TransId, ContextId, CmdReq) when is_list(CmdReq) ->
+ Actions = [#'ActionRequest'{contextId = ContextId,
+ commandRequests = CmdReq}],
+ Req = {transactions,
+ [{transactionRequest,
+ #'TransactionRequest'{transactionId = TransId,
+ actions = Actions}}]},
+ #'MegacoMessage'{mess = #'Message'{version = 1,
+ mId = Mid,
+ messageBody = Req}}.
+
+reply(Mid, TransId, ContextId, CmdReply) when is_list(CmdReply) ->
+ Actions = [#'ActionReply'{contextId = ContextId,
+ commandReply = CmdReply}],
+ Req = {transactions,
+ [{transactionReply,
+ #'TransactionReply'{transactionId = TransId,
+ transactionResult = {actionReplies, Actions}}}]},
+ #'MegacoMessage'{mess = #'Message'{version = 1,
+ mId = Mid,
+ messageBody = Req}}.
+
+%%----------------------------------------------------------------------
+%% 1. An MG registers with an MGC using the ServiceChange command:
+%%
+%% MG1 to MGC:
+%% "MEGACO/1 [124.124.124.222]
+%% Transaction = 9998 {
+%% Context = - {
+%% ServiceChange = ROOT {Services {
+%% Method=Restart,
+%% ServiceChangeAddress=55555, Profile=ResGW/1}
+%% }
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg1() ->
+ msg1(?MG1_MID_NO_PORT).
+msg1(Mid) ->
+ Address = {portNumber, ?DEFAULT_PORT},
+ Profile = #'ServiceChangeProfile'{profileName = "resgw",
+ version = 1},
+ Parm = #'ServiceChangeParm'{serviceChangeMethod = restart,
+ serviceChangeAddress = Address,
+ serviceChangeReason = ["901 mg cold boot"],
+ %% BUGBUG: Mandatory reason missing in spec
+ serviceChangeProfile = Profile},
+ Req = #'ServiceChangeRequest'{terminationID = [?megaco_root_termination_id],
+ serviceChangeParms = Parm},
+ CmdReq = #'CommandRequest'{command = {serviceChangeReq, Req}},
+ request(Mid, 9998, ?megaco_null_context_id, [CmdReq]).
+
+%%----------------------------------------------------------------------
+%%
+%% 2. The MGC sends a reply:
+%%
+%% MGC to MG1:
+%% MEGACO/1 [123.123.123.4]:55555
+%% Reply = 9998 {
+%% Context = - {ServiceChange = ROOT {
+%% Services {ServiceChangeAddress=55555, Profile=ResGW/1} } }
+%% }
+%%----------------------------------------------------------------------
+
+msg2() ->
+ msg2(?MGC_MID).
+msg2(Mid) ->
+ Address = {portNumber, ?DEFAULT_PORT},
+ Profile = #'ServiceChangeProfile'{profileName = "resgw",
+ version = 1},
+ Parm = #'ServiceChangeResParm'{serviceChangeAddress = Address,
+ serviceChangeProfile = Profile},
+ Reply = #'ServiceChangeReply'{terminationID = [?megaco_root_termination_id],
+ serviceChangeResult = {serviceChangeResParms,
+ Parm}},
+ reply(Mid, 9998, ?megaco_null_context_id, [{serviceChangeReply, Reply}]).
+
+%%----------------------------------------------------------------------
+%% 3. The MGC programs a Termination in the NULL context. The
+%% terminationId is A4444, the streamId is 1, the requestId in the
+%% Events descriptor is 2222. The mId is the identifier of the sender
+%% of this message, in this case, it is the IP address and port
+%% [123.123.123.4]:55555. Mode for this stream is set to
+%% SendReceive. "al" is the analog line supervision package.
+%%
+%% MGC to MG1:
+%% MEGACO/1 [123.123.123.4]:55555
+%% Transaction = 9999 {
+%% Context = - {
+%% Modify = A4444 {
+%% Media { Stream = 1 {
+%% LocalControl {
+%% Mode = SendReceive,
+%% tdmc/gain=2, ; in dB,
+%% tdmc/ec=G165
+%% },
+%% Local {
+%% v=0
+%% c=IN IP4 $
+%% m=audio $ RTP/AVP 0
+%% a=fmtp:PCMU VAD=X-NNVAD ; special voice activity
+%% ; detection algorithm
+%% }
+%% }
+%% },
+%% Events = 2222 {al/of}
+%% }
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg3() ->
+ msg3(?MGC_MID).
+msg3(Mid) ->
+ msg3(Mid, ?A4444).
+msg3(Mid, Tid) ->
+ Gain = ?PP("tdmc/gain", "2"),
+ %% Ec = ?PP("tdmc/ec", "G165"),
+ Ec = ?PP("tdmc/ec", "g165"), %% BUGBUG: should be case insensitive
+ LCD = #'LocalControlDescriptor'{streamMode = sendRecv,
+ propertyParms = [Gain, Ec]},
+ V = ?PP("v", "0"),
+ C = ?PP("c", "IN IP4 $ "),
+ M = ?PP("m", "audio $ RTP/AVP 0"),
+ A = ?PP("a", "fmtp:PCMU VAD=X-NNVAD"),
+ LD = #'LocalRemoteDescriptor'{propGrps = [[V, C, M, A]]},
+ Parms = #'StreamParms'{localControlDescriptor = LCD,
+ localDescriptor = LD},
+ StreamDesc = #'StreamDescriptor'{streamID = 1,
+ streamParms = Parms},
+ MediaDesc = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}},
+ ReqEvent = #'RequestedEvent'{pkgdName = "al/of",
+ evParList = []},
+ EventsDesc = #'EventsDescriptor'{requestID = 2222,
+ eventList = [ReqEvent]},
+ AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = Tid}],
+ descriptors = [{mediaDescriptor, MediaDesc},
+ {eventsDescriptor, EventsDesc}]},
+ CmdReq = #'CommandRequest'{command = {modReq, AmmReq}},
+ request(Mid, 9999, ?megaco_null_context_id, [CmdReq]).
+
+%%----------------------------------------------------------------------
+%% The dialplan script could have been loaded into the MG previously.
+%% Its function would be to wait for the OffHook, turn on dialtone and
+%% start collecting DTMF digits. However in this example, we use the
+%% digit map, which is put into place after the offhook is detected (step
+%% 5 below).
+%%
+%%
+%% Note that the embedded EventsDescriptor could have been used to
+%% combine steps 3 and 4 with steps 8 and 9, eliminating steps 6 and 7.
+%%
+%%
+%% 4. The MG1 accepts the Modify with this reply:
+%%
+%% MG1 to MGC:
+%% MEGACO/1 [124.124.124.222]:55555
+%% Reply = 9999 {
+%% Context = - {Modify = A4444}
+%% }
+%%----------------------------------------------------------------------
+
+msg4() ->
+ msg4(?MG1_MID).
+msg4(Mid) ->
+ msg4(Mid, ?A4444).
+msg4(Mid, Tid) ->
+ Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = Tid}]},
+ reply(Mid, 9999, ?megaco_null_context_id, [{modReply, Reply}]).
+
+%%----------------------------------------------------------------------
+%% 5. A similar exchange happens between MG2 and the MGC, resulting in an
+%% idle Termination called A5555.
+%%----------------------------------------------------------------------
+
+msg5a() ->
+ msg5a(?MGC_MID).
+msg5a(Mid) ->
+ msg3(Mid, ?A4444).
+
+msg5b() ->
+ msg5b(?MG2_MID).
+msg5b(Mid) ->
+ msg4(Mid, ?A5555).
+
+%%----------------------------------------------------------------------
+%% A.1.2 Collecting Originator Digits and Initiating Termination
+%%
+%% The following builds upon the previously shown conditions. It
+%% illustrates the transactions from the Media Gateway Controller and
+%% originating Media Gateway (MG1) to get the originating Termination
+%% (A4444) through the stages of digit collection required to initiate a
+%% connection to the terminating Media Gateway (MG2).
+%%
+%% 6. MG1 detects an offhook event from User 1 and reports it to the
+%% Media Gateway Controller via the Notify Command.
+%%
+%% MG1 to MGC:
+%% MEGACO/1 [124.124.124.222]:55555
+%% Transaction = 10000 {
+%% Context = - {
+%% Notify = A4444 {ObservedEvents =2222 {
+%% 19990729T22000000:al/of}}
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg6() ->
+ msg6(?MG1_MID).
+msg6(Mid) ->
+ TimeStamp = #'TimeNotation'{date = "19990729",
+ time = "22000000"},
+ Event = #'ObservedEvent'{eventName = "al/of",
+ timeNotation = TimeStamp,
+ eventParList = []},
+ Desc = #'ObservedEventsDescriptor'{requestId = 2222,
+ observedEventLst = [Event]},
+ NotifyReq = #'NotifyRequest'{terminationID = [#megaco_term_id{id = ?A4444}],
+ observedEventsDescriptor = Desc},
+ CmdReq = #'CommandRequest'{command = {notifyReq, NotifyReq}},
+ request(Mid, 10000, ?megaco_null_context_id, [CmdReq]).
+
+%%----------------------------------------------------------------------
+%% 7. And the Notify is acknowledged.
+%%
+%% MGC to MG1:
+%% MEGACO/1 [123.123.123.4]:55555
+%% Reply = 10000 {
+%% Context = - {Notify = A4444}
+%% }
+%%----------------------------------------------------------------------
+
+msg7() ->
+ msg7(?MGC_MID).
+msg7(Mid) ->
+ Reply = #'NotifyReply'{terminationID = [#megaco_term_id{id = ?A4444}]},
+ reply(Mid, 10000, ?megaco_null_context_id, [{notifyReply, Reply}]).
+
+%%----------------------------------------------------------------------
+%% 8. The MGC Modifies the termination to play dial tone, and to look for
+%% digits now. There is also an embedded event to stop dialtone upon
+%% detection of the first digit. dd is the DTMF Detection package, and
+%% ce is the completion event.
+%%----------------------------------------------------------------------
+
+%% BUGBUG: Example missing in spec
+
+%%----------------------------------------------------------------------
+%% 9.
+%%
+%% MGC to MG1:
+%% MEGACO/1 [123.123.123.4]:55555
+%% Transaction = 10001 {
+%% Context = - {
+%% Modify = A4444 {
+%% Events = 2223 {
+%% al/on, dd/ce {DigitMap=Dialplan0}
+%% },
+%% Signals {cg/dt},
+%% DigitMap= Dialplan0{
+%% (0S| 00S|[1-7]xLxx|8Lxxxxxxx|#xxxxxxx|*xx|9L1xxxxxxxxxx|9L011x.S)}
+%% }
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg9() ->
+ msg9(?MGC_MID).
+msg9(Mid) ->
+ Name = "dialplan00",
+ Body = "(0s| 00s|[1-7]xlxx|8lxxxxxxx|#xxxxxxx|*xx|9l1xxxxxxxxxx|9l011x.s)",
+ Value = #'DigitMapValue'{digitMapBody = Body},
+ On = #'RequestedEvent'{pkgdName = "al/on", evParList = []},
+ Action = #'RequestedActions'{eventDM = {digitMapName, Name}},
+ Ce = #'RequestedEvent'{pkgdName = "dd/ce",
+ eventAction = Action,
+ evParList = []},
+ EventsDesc = #'EventsDescriptor'{requestID = 2223,
+ eventList = [On, Ce]},
+ Signal = #'Signal'{signalName = "cg/rt", sigParList = []},
+ DigMapDesc = #'DigitMapDescriptor'{digitMapName = Name,
+ digitMapValue = Value},
+ AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A4444}],
+ descriptors = [{eventsDescriptor, EventsDesc},
+ {signalsDescriptor, [{signal, Signal}]},
+ {digitMapDescriptor, DigMapDesc}]},
+ CmdReq = #'CommandRequest'{command = {modReq, AmmReq}},
+ request(Mid, 10001, ?megaco_null_context_id, [CmdReq]).
+
+%%----------------------------------------------------------------------
+%% 10. And the Modify is acknowledged.
+%%
+%% MG1 to MGC:
+%% MEGACO/1 [124.124.124.222]:55555
+%% Reply = 10001 {
+%% Context = - {Modify = A4444}
+%% }
+%%----------------------------------------------------------------------
+
+msg10() ->
+ msg10(?MG1_MID).
+msg10(Mid) ->
+ Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4444}]},
+ reply(Mid, 10001, ?megaco_null_context_id, [{modReply, Reply}]).
+
+%%----------------------------------------------------------------------
+%% 11. Next, digits are accumulated by MG1 as they are dialed by User 1.
+%% Dialtone is stopped upon detection of the first digit, using the
+%% embedded event in step 8. When an appropriate match is made of
+%% collected digits against the currently programmed Dialplan for
+%% A4444, another Notify is sent to the Media Gateway Controller.
+%%
+%% MG1 to MGC:
+%% MEGACO/1 [124.124.124.222]:55555
+%% Transaction = 10002 {
+%% Context = - {
+%% Notify = A4444 {ObservedEvents =2223 {
+%% 19990729T22010001:dd/ce{ds="916135551212"}}}
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg11() ->
+ msg11(?MG1_MID).
+msg11(Mid) ->
+ TimeStamp = #'TimeNotation'{date = "19990729",
+ time = "22010001"},
+ Parm = #'EventParameter'{eventParameterName = "ds",
+ value = ["916135551212"]},
+ %% BUGBUG: Quoted string or safe char?
+ Event = #'ObservedEvent'{eventName = "dd/ce",
+ timeNotation = TimeStamp,
+ eventParList = [Parm]},
+ Desc = #'ObservedEventsDescriptor'{requestId = 2223,
+ observedEventLst = [Event]},
+ NotifyReq = #'NotifyRequest'{terminationID = [#megaco_term_id{id = ?A4444}],
+ observedEventsDescriptor = Desc},
+ CmdReq = #'CommandRequest'{command = {notifyReq, NotifyReq}},
+ request(Mid, 10002, ?megaco_null_context_id, [CmdReq]).
+
+%%----------------------------------------------------------------------
+%% 12. And the Notify is acknowledged.
+%%
+%% MGC to MG1:
+%% MEGACO/1 [123.123.123.4]:55555
+%% Reply = 10002 {
+%% Context = - {Notify = A4444}
+%% }
+%%----------------------------------------------------------------------
+
+msg12() ->
+ msg12(?MGC_MID).
+msg12(Mid) ->
+ Reply = #'NotifyReply'{terminationID = [#megaco_term_id{id = ?A4444}]},
+ reply(Mid, 10002, ?megaco_null_context_id, [{notifyReply, Reply}]).
+
+%%----------------------------------------------------------------------
+%% 13. The controller then analyses the digits and determines that a
+%% connection needs to be made from MG1 to MG2. Both the TDM
+%% termination A4444, and an RTP termination are added to a new
+%% context in MG1. Mode is ReceiveOnly since Remote descriptor values
+%% are not yet specified. Preferred codecs are in the MGC's preferred
+%% order of choice.
+%%
+%% MGC to MG1:
+%% MEGACO/1 [123.123.123.4]:55555
+%% Transaction = 10003 {
+%% Context = $ {
+%% Add = A4444,
+%% Add = $ {
+%% Media {
+%% Stream = 1 {
+%% LocalControl {
+%% Mode = ReceiveOnly,
+%%
+%% nt/jit=40, ; in ms
+%% },
+%% Local {
+%% v=0
+%% c=IN IP4 $
+%% m=audio $ RTP/AVP 4
+%% a=ptime:30
+%% v=0
+%% c=IN IP4 $
+%% m=audio $ RTP/AVP 0
+%% }
+%% }
+%% }
+%% }
+%% }
+%% }
+%%
+%% NOTE - The MGC states its prefrred parameter values as a series of
+%% sdp blocks in Local. The MG fills in the Local Descriptor in the
+%% Reply.
+%%----------------------------------------------------------------------
+
+msg13() ->
+ msg13(?MGC_MID).
+msg13(Mid) ->
+ AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A4444}],
+ descriptors = []},
+ CmdReq = #'CommandRequest'{command = {addReq, AmmReq}},
+ Jit = ?PP("nt/jit", "40"),
+ LCD = #'LocalControlDescriptor'{streamMode = recvOnly,
+ propertyParms = [Jit]},
+ V = ?PP("v", "0"),
+ C = ?PP("c", "IN IP4 $ "),
+ M = ?PP("m", "audio $ RTP/AVP 4"),
+ A = ?PP("a", "ptime:30"),
+ V2 = ?PP("v", "0"),
+ C2 = ?PP("c", "IN IP4 $ "),
+ M2 = ?PP("m", "audio $ RTP/AVP 0"),
+ LD = #'LocalRemoteDescriptor'{propGrps = [[V, C, M, A], [V2, C2, M2]]},
+ Parms = #'StreamParms'{localControlDescriptor = LCD,
+ localDescriptor = LD},
+ StreamDesc = #'StreamDescriptor'{streamID = 1,
+ streamParms = Parms},
+ MediaDesc = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}},
+ ChooseTid = #megaco_term_id{contains_wildcards = true,
+ id = [[?megaco_choose]]},
+ AmmReq2 = #'AmmRequest'{terminationID = [ChooseTid],
+ descriptors = [{mediaDescriptor, MediaDesc}]},
+ CmdReq2 = #'CommandRequest'{command = {addReq, AmmReq2}},
+ request(Mid, 10003, ?megaco_choose_context_id, [CmdReq, CmdReq2]).
+
+%%----------------------------------------------------------------------
+%% 14. MG1 acknowledges the new Termination and fills in the Local IP
+%% address and UDP port. It also makes a choice for the codec based
+%% on the MGC preferences in Local. MG1 sets the RTP port to 2222.
+%%
+%% MEGACO/1 [124.124.124.222]:55555
+%% Reply = 10003 {
+%% Context = 2000 {
+%% Add = A4444,
+%% Add=A4445{
+%% Media {
+%% Stream = 1 {
+%% Local {
+%% v=0
+%% c=IN IP4 124.124.124.222
+%% m=audio 2222 RTP/AVP 4
+%% a=ptime:30
+%% a=recvonly
+%% } ; RTP profile for G.723 is 4
+%% }
+%% }
+%% }
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg14() ->
+ msg14(?MG1_MID).
+msg14(Mid) ->
+ V = ?PP("v", "0"),
+ C = ?PP("c", "IN IP4 124.124.124.222"),
+ M = ?PP("m", "audio 2222 RTP/AVP 4"),
+ A = ?PP("a", "a=ptime:30"),
+ A2= ?PP("a", "recvonly"),
+ LD = #'LocalRemoteDescriptor'{propGrps = [[V, C, M, A, A2]]},
+ Parms = #'StreamParms'{localDescriptor = LD},
+ StreamDesc = #'StreamDescriptor'{streamID = 1,
+ streamParms = Parms},
+ MediaDesc = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}},
+ Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4444}]},
+ Reply2 = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4445}],
+ terminationAudit = [{mediaDescriptor, MediaDesc}]},
+ reply(Mid, 10003, 2000, [{addReply, Reply}, {addReply, Reply2}]).
+
+%%----------------------------------------------------------------------
+%% 15. The MGC will now associate A5555 with a new Context on MG2, and
+%% establish an RTP Stream (i.e, A5556 will be assigned), SendReceive
+%% connection through to the originating user, User 1. The MGC also
+%% sets ring on A5555.
+%%
+%% MGC to MG2:
+%% MEGACO/1 [123.123.123.4]:55555
+%% Transaction = 50003 {
+%% Context = $ {
+%% Add = A5555 { Media {
+%% Stream = 1 {
+%% LocalControl {Mode = SendReceive} }},
+%% Signals {al/ri}
+%% },
+%% Add = $ {Media {
+%% Stream = 1 {
+%% LocalControl {
+%% Mode = SendReceive,
+%% nt/jit=40 ; in ms
+%% },
+%% Local {
+%% v=0
+%% c=IN IP4 $
+%% m=audio $ RTP/AVP 4
+%% a=ptime:30
+%% },
+%% Remote {
+%% v=0
+%% c=IN IP4 124.124.124.222
+%% m=audio 2222 RTP/AVP 4
+%% a=ptime:30
+%% } ; RTP profile for G.723 is 4
+%% }
+%% }
+%% }
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg15() ->
+ msg15(?MGC_MID).
+msg15(Mid) ->
+ LCD = #'LocalControlDescriptor'{streamMode = sendRecv,
+ propertyParms = []},
+ Parms = #'StreamParms'{localControlDescriptor = LCD},
+ StreamDesc = #'StreamDescriptor'{streamID = 1,
+ streamParms = Parms},
+ MediaDesc = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}},
+ Signal = #'Signal'{signalName = "al/ri",
+ sigParList = []},
+ AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A5555}],
+ descriptors = [{mediaDescriptor, MediaDesc},
+ {signalsDescriptor, [{signal, Signal}]}]},
+ CmdReq = #'CommandRequest'{command = {addReq, AmmReq}},
+ Jit = ?PP("nt/jit", "40"),
+ LCD2 = #'LocalControlDescriptor'{streamMode = sendRecv,
+ propertyParms = [Jit]},
+ V = ?PP("v", "0"),
+ C = ?PP("c", "IN IP4 $ "),
+ M = ?PP("m", "audio $ RTP/AVP 4"),
+ A = ?PP("a", "ptime:30"),
+ LD2 = #'LocalRemoteDescriptor'{propGrps = [[V, C, M, A]]},
+ V2 = ?PP("v", "0"),
+ C2 = ?PP("c", "IN IP4 124.124.124.222"),
+ M2 = ?PP("m", "audio 2222 RTP/AVP 4"),
+ RD2 = #'LocalRemoteDescriptor'{propGrps = [[V2, C2, M2]]},
+ Parms2 = #'StreamParms'{localControlDescriptor = LCD2,
+ localDescriptor = LD2,
+ remoteDescriptor = RD2},
+ StreamDesc2 = #'StreamDescriptor'{streamID = 1,
+ streamParms = Parms2},
+ MediaDesc2 = #'MediaDescriptor'{streams = {multiStream, [StreamDesc2]}},
+ ChooseTid = #megaco_term_id{contains_wildcards = true,
+ id = [[?megaco_choose]]},
+ AmmReq2 = #'AmmRequest'{terminationID = [ChooseTid],
+ descriptors = [{mediaDescriptor, MediaDesc2}]},
+ CmdReq2 = #'CommandRequest'{command = {addReq, AmmReq2}},
+ request(Mid, 50003, ?megaco_choose_context_id, [CmdReq, CmdReq2]).
+
+%%----------------------------------------------------------------------
+%% 16. This is acknowledged. The stream port number is different from the
+%% control port number. In this case it is 1111 (in the SDP).
+%%
+%% MG2 to MGC:
+%% MEGACO/1 [124.124.124.222]:55555
+%% Reply = 50003 {
+%% Context = 5000 {
+%% Add = A5556{
+%% Media {
+%% Stream = 1 {
+%% Local {
+%% v=0
+%% c=IN IP4 125.125.125.111
+%% m=audio 1111 RTP/AVP 4
+%% }
+%% } ; RTP profile for G723 is 4
+%% }
+%% }
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg16() ->
+ msg16(?MG2_MID).
+msg16(Mid) ->
+ V = ?PP("v", "0"),
+ C = ?PP("c", "IN IP4 125.125.125.111"),
+ M = ?PP("m", "audio 1111 RTP/AVP 4"),
+ LD = #'LocalRemoteDescriptor'{propGrps = [[V, C, M]]},
+ Parms = #'StreamParms'{localDescriptor = LD},
+ StreamDesc = #'StreamDescriptor'{streamID = 1,
+ streamParms = Parms},
+ MediaDesc = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}},
+ Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A5556}],
+ terminationAudit = [{mediaDescriptor, MediaDesc}]},
+ reply(Mid, 50003, 5000, [{addReply, Reply}]).
+
+%%----------------------------------------------------------------------
+%% 17. The above IPAddr and UDPport need to be given to MG1 now.
+%%
+%% MGC to MG1:
+%% MEGACO/1 [123.123.123.4]:55555
+%% Transaction = 10005 {
+%% Context = 2000 {
+%% Modify = A4444 {
+%% Signals {cg/rt}
+%% },
+%% Modify = A4445 {
+%% Media {
+%% Stream = 1 {
+%% Remote {
+%% v=0
+%% c=IN IP4 125.125.125.111
+%% m=audio 1111 RTP/AVP 4
+%% }
+%% } ; RTP profile for G723 is 4
+%% }
+%% }
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg17a() ->
+ msg17a(?MGC_MID).
+msg17a(Mid) ->
+ Signal = #'Signal'{signalName = "cg/rt", sigParList = []},
+ AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A4444}],
+ descriptors = [{signalsDescriptor, [{signal, Signal}]}]},
+ CmdReq = #'CommandRequest'{command = {modReq, AmmReq}},
+
+ V = ?PP("v", "0"),
+ C = ?PP("c", "IN IP4 125.125.125.111"),
+ M = ?PP("m", "audio 1111 RTP/AVP 4"),
+ RD2 = #'LocalRemoteDescriptor'{propGrps = [[V, C, M]]},
+ Parms2 = #'StreamParms'{remoteDescriptor = RD2},
+ StreamDesc2 = #'StreamDescriptor'{streamID = 1,
+ streamParms = Parms2},
+ MediaDesc2 = #'MediaDescriptor'{streams = {multiStream, [StreamDesc2]}},
+ AmmReq2 = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A4445}],
+ descriptors = [{mediaDescriptor, MediaDesc2}]},
+ CmdReq2 = #'CommandRequest'{command = {modReq, AmmReq2}},
+ request(Mid, 10005, 2000, [CmdReq, CmdReq2]).
+
+%%----------------------------------------------------------------------
+%% MG1 to MGC:
+%% MEGACO/1 [124.124.124.222]:55555
+%% Reply = 10005 {
+%% Context = 2000 {Modify = A4444, Modify = A4445}
+%% }
+%%----------------------------------------------------------------------
+
+msg17b() ->
+ msg17b(?MG1_MID).
+msg17b(Mid) ->
+ Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4444}]},
+ Reply2 = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4445}]},
+ reply(Mid, 10005, 2000, [{modReply, Reply}, {modReply, Reply2}]).
+
+%%----------------------------------------------------------------------
+%% 18. The two gateways are now connected and User 1 hears the
+%% RingBack. The MG2 now waits until User2 picks up the receiver and
+%% then the two-way call is established.
+%%
+%% MG2 to MGC:
+%% MEGACO/1 [125.125.125.111]:55555
+%% Transaction = 50005 {
+%% Context = 5000 {
+%% Notify = A5555 {ObservedEvents =1234 {
+%% 19990729T22020002:al/of}}
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg18a() ->
+ msg18a(?MG2_MID).
+msg18a(Mid) ->
+ TimeStamp = #'TimeNotation'{date = "19990729",
+ time = "22020002"},
+ Event = #'ObservedEvent'{eventName = "al/of",
+ timeNotation = TimeStamp,
+ eventParList = []},
+ Desc = #'ObservedEventsDescriptor'{requestId = 1234,
+ observedEventLst = [Event]},
+ NotifyReq = #'NotifyRequest'{terminationID = [#megaco_term_id{id = ?A5555}],
+ observedEventsDescriptor = Desc},
+ CmdReq = #'CommandRequest'{command = {notifyReq, NotifyReq}},
+ request(Mid, 50005, 5000, [CmdReq]).
+
+%%----------------------------------------------------------------------
+%% MGC to MG2:
+%% MEGACO/1 [123.123.123.4]:55555
+%% Reply = 50005 {
+%% Context = - {Notify = A5555}
+%% }
+%%----------------------------------------------------------------------
+
+msg18b() ->
+ msg18b(?MGC_MID).
+msg18b(Mid) ->
+ Reply = #'NotifyReply'{terminationID = [#megaco_term_id{id = ?A5555}]},
+ reply(Mid, 50005, ?megaco_null_context_id, [{notifyReply, Reply}]).
+
+%%----------------------------------------------------------------------
+%% MGC to MG2:
+%% MEGACO/1 [123.123.123.4]:55555
+%% Transaction = 50006 {
+%% Context = 5000 {
+%% Modify = A5555 {
+%% Events = 1235 {al/on},
+%% Signals { } ; to turn off ringing
+%% }
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg18c() ->
+ msg18c(?MGC_MID).
+msg18c(Mid) ->
+ On = #'RequestedEvent'{pkgdName = "al/on", evParList = []},
+ EventsDesc = #'EventsDescriptor'{requestID = 1235,
+ eventList = [On]},
+ AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A5555}],
+ descriptors = [{eventsDescriptor, EventsDesc},
+ {signalsDescriptor, []}]},
+ CmdReq = #'CommandRequest'{command = {modReq, AmmReq}},
+ request(Mid, 50006, 5000, [CmdReq]).
+
+%%----------------------------------------------------------------------
+%% MG2 to MGC:
+%% MEGACO/1 [125.125.125.111]:55555
+%% Reply = 50006 {
+%% Context = 5000 {Modify = A4445}
+%% }
+%%----------------------------------------------------------------------
+
+msg18d() ->
+ msg18d(?MG2_MID).
+msg18d(Mid) ->
+ Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4445}]},
+ reply(Mid, 50006, 5000, [{modReply, Reply}]).
+
+%%----------------------------------------------------------------------
+%% 19. Change mode on MG1 to SendReceive, and stop the ringback.
+%%
+%% MGC to MG1:
+%% MEGACO/1 [123.123.123.4]:55555
+%% Transaction = 10006 {
+%% Context = 2000 {
+%% Modify = A4445 {
+%% Media {
+%% Stream = 1 {
+%% LocalControl {
+%% Mode=SendReceive
+%% }
+%% }
+%% }
+%% },
+%% Modify = A4444 {
+%% Signals { }
+%% }
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg19a() ->
+ msg19a(?MGC_MID).
+msg19a(Mid) ->
+ LCD = #'LocalControlDescriptor'{streamMode = sendRecv,
+ propertyParms = []},
+ Parms = #'StreamParms'{localControlDescriptor = LCD},
+ StreamDesc = #'StreamDescriptor'{streamID = 1,
+ streamParms = Parms},
+ MediaDesc = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}},
+ AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A4445}],
+ descriptors = [{mediaDescriptor, MediaDesc}]},
+ CmdReq = #'CommandRequest'{command = {modReq, AmmReq}},
+ AmmReq2 = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A4444}],
+ descriptors = [{signalsDescriptor, []}]},
+ CmdReq2 = #'CommandRequest'{command = {modReq, AmmReq2}},
+ request(Mid, 10006, 2000, [CmdReq, CmdReq2]).
+
+%%----------------------------------------------------------------------
+%% MG1 to MGC:
+%% MEGACO/1 [124.124.124.222]:55555
+%% Reply = 10006 {
+%% Context = 2000 {Modify = A4445, Modify = A4444}}
+%%----------------------------------------------------------------------
+
+msg19b() ->
+ msg19b(?MG1_MID).
+msg19b(Mid) ->
+ Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4445}]},
+ Reply2= #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4444}]},
+ reply(Mid, 10006, 2000, [{modReply, Reply}, {modReply, Reply2}]).
+
+%%----------------------------------------------------------------------
+%% 20. The MGC decides to Audit the RTP termination on MG2.
+%%
+%% MEGACO/1 [123.123.123.4]:55555
+%% Transaction = 50007 {
+%% Context = - {AuditValue = A5556{
+%% Audit{Media, DigitMap, Events, Signals, Packages, Statistics }}
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg20() ->
+ msg20(?MGC_MID).
+msg20(Mid) ->
+ Tokens = [mediaToken, eventsToken, signalsToken,
+ digitMapToken, statsToken, packagesToken],
+ AuditDesc = #'AuditDescriptor'{auditToken = Tokens},
+ Req = #'AuditRequest'{terminationID = #megaco_term_id{id = ?A5556},
+ auditDescriptor = AuditDesc},
+ CmdReq = #'CommandRequest'{command = {auditValueRequest, Req}},
+ request(Mid, 50007, ?megaco_null_context_id, [CmdReq]).
+
+%%----------------------------------------------------------------------
+%% 21. The MG2 replies. An RTP termination has no events nor signals, so
+%% these are left out in the reply .
+%%
+%% MEGACO/1 [125.125.125.111]:55555
+%% Reply = 50007 {
+%% Context = - {
+%% AuditValue = A5556 {
+%% Media {
+%% Stream = 1 {
+%% LocalControl { Mode = SendReceive,
+%% nt/jit=40 },
+%% Local {
+%% v=0
+%% c=IN IP4 125.125.125.111
+%% m=audio 1111 RTP/AVP 4
+%% a=ptime:30
+%% },
+%% Remote {
+%% v=0
+%% c=IN IP4 124.124.124.222
+%% m=audio 2222 RTP/AVP 4
+%% a=ptime:30
+%% } } },
+%% Packages {nt-1, rtp-1},
+%% Statistics { rtp/ps=1200, ; packets sent
+%% nt/os=62300, ; octets sent
+%% rtp/pr=700, ; packets received
+%% nt/or=45100, ; octets received
+%% rtp/pl=0.2, ; % packet loss
+%% rtp/jit=20,
+%% rtp/delay=40 } ; avg latency
+%% }
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg21() ->
+ msg21(?MG2_MID).
+msg21(Mid) ->
+ Jit = ?PP("nt/jit", "40"),
+ LCD = #'LocalControlDescriptor'{streamMode = sendRecv,
+ propertyParms = [Jit]},
+ LDV = ?PP("v", "0"),
+ LDC = ?PP("c", "IN IP4 125.125.125.111"),
+ LDM = ?PP("m", "audio 1111 RTP/AVP 4"),
+ LDA = ?PP("a", "ptime:30"),
+ LD = #'LocalRemoteDescriptor'{propGrps = [[LDV, LDC, LDM, LDA]]},
+ RDV = ?PP("v", "0"),
+ RDC = ?PP("c", "IN IP4 124.124.124.222"),
+ RDM = ?PP("m", "audio 2222 RTP/AVP 4"),
+ RDA = ?PP("a", "ptime:30"),
+ RD = #'LocalRemoteDescriptor'{propGrps = [[RDV, RDC, RDM, RDA]]},
+ StreamParms = #'StreamParms'{localControlDescriptor = LCD,
+ localDescriptor = LD,
+ remoteDescriptor = RD},
+ StreamDesc = #'StreamDescriptor'{streamID = 1,
+ streamParms = StreamParms},
+ Media = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}},
+ PackagesItem = #'PackagesItem'{packageName = "nt",
+ packageVersion = 1},
+ PackagesItem2 = #'PackagesItem'{packageName = "rtp",
+ packageVersion = 1},
+ Stat = ?SP("rtp/ps","1200"),
+ Stat2 = ?SP("nt/os","62300"),
+ Stat3 = ?SP("rtp/pr","700"),
+ Stat4 = ?SP("nt/or","45100"),
+ Stat5 = ?SP("rtp/pl","0.2"),
+ Stat6 = ?SP("rtp/jit","20"),
+ Stat7 = ?SP("rtp/delay","40"),
+ Statistics = [Stat, Stat2, Stat3, Stat4, Stat5, Stat6, Stat7],
+ Audits = [{mediaDescriptor, Media},
+ {packagesDescriptor, [PackagesItem, PackagesItem2]},
+ {statisticsDescriptor, Statistics}],
+ Reply = {auditResult, #'AuditResult'{terminationID = #megaco_term_id{id = ?A5556},
+ terminationAuditResult = Audits}},
+ reply(Mid, 50007, ?megaco_null_context_id, [{auditValueReply, Reply}]).
+
+%%----------------------------------------------------------------------
+%% 22. When the MGC receives an onhook signal from one of the MGs, it
+%% brings down the call. In this example, the user at MG2 hangs up first.
+%%
+%% MG2 to MGC:
+%% MEGACO/1 [125.125.125.111]:55555
+%% Transaction = 50008 {
+%% Context = 5000 {
+%% Notify = A5555 {ObservedEvents =1235 {
+%% 19990729T24020002:al/on}
+%% }
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg22a() ->
+ msg22a(?MG2_MID).
+msg22a(Mid) ->
+ TimeStamp = #'TimeNotation'{date = "19990729",
+ time = "24020002"},
+ Event = #'ObservedEvent'{eventName = "al/on",
+ timeNotation = TimeStamp,
+ eventParList = []},
+ Desc = #'ObservedEventsDescriptor'{requestId = 1235,
+ observedEventLst = [Event]},
+ NotifyReq = #'NotifyRequest'{terminationID = [#megaco_term_id{id = ?A5555}],
+ observedEventsDescriptor = Desc},
+ CmdReq = #'CommandRequest'{command = {notifyReq, NotifyReq}},
+ request(Mid, 50008, 5000, [CmdReq]).
+
+%%----------------------------------------------------------------------
+%% MGC to MG2:
+%% MEGACO/1 [123.123.123.4]:55555
+%% Reply = 50008 {
+%% Context = - {Notify = A5555}
+%% }
+%%----------------------------------------------------------------------
+
+msg22b() ->
+ msg22b(?MGC_MID).
+msg22b(Mid) ->
+ Reply = #'NotifyReply'{terminationID = [#megaco_term_id{id = ?A5555}]},
+ reply(Mid, 50008, ?megaco_null_context_id, [{notifyReply, Reply}]).
+
+%%----------------------------------------------------------------------
+%% 23. The MGC now sends both MGs a Subtract to take down the call. Only
+%% the subtracts to MG2 are shown here. Each termination has its own
+%% set of statistics that it gathers. An MGC may not need to request
+%% both to be returned. A5555 is a physical termination, and A5556 is
+%% an RTP termination.
+%%
+%% MGC to MG2:
+%%
+%% MEGACO/1 [123.123.123.4]:55555
+%% Transaction = 50009 {
+%% Context = 5000 {
+%% Subtract = A5555 {Audit{Statistics}},
+%% Subtract = A5556 {Audit{Statistics}}
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg23a() ->
+ msg23a(?MGC_MID).
+msg23a(Mid) ->
+ CommonAuditDesc = #'AuditDescriptor'{auditToken = [statsToken]},
+ SubReq = #'SubtractRequest'{terminationID = [#megaco_term_id{id = ?A5555}],
+ auditDescriptor = CommonAuditDesc},
+ SubReq2 = #'SubtractRequest'{terminationID = [#megaco_term_id{id = ?A5556}],
+ auditDescriptor = CommonAuditDesc},
+ CmdReq = #'CommandRequest'{command = {subtractReq, SubReq}},
+ CmdReq2 = #'CommandRequest'{command = {subtractReq, SubReq2}},
+ request(Mid, 50009, 5000, [CmdReq, CmdReq2]).
+%%
+%%----------------------------------------------------------------------
+%% MG2 to MGC:
+%%
+%% MEGACO/1 [125.125.125.111]:55555
+%% Reply = 50009 {
+%% Context = 5000 {
+%% Subtract = A5555 {
+%% Statistics {
+%% nt/os=45123, ; Octets Sent
+%% nt/dur=40 ; in seconds
+%% }
+%% },
+%% Subtract = A5556 {
+%% Statistics {
+%% rtp/ps=1245, ; packets sent
+%% nt/os=62345, ; octets sent
+%% rtp/pr=780, ; packets received
+%% nt/or=45123, ; octets received
+%% rtp/pl=10, ; % packets lost
+%% rtp/jit=27,
+%% rtp/delay=48 ; average latency
+%% }
+%% }
+%% }
+%% }
+%%----------------------------------------------------------------------
+
+msg23b() ->
+ msg23b(?MG2_MID).
+msg23b(Mid) ->
+ Stat11 = ?SP("nt/os","45123"),
+ Stat12 = ?SP("nt/dur", "40"),
+ Stats1 = [Stat11, Stat12],
+ Reply1 = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A5555}],
+ terminationAudit = [{statisticsDescriptor, Stats1}]},
+ Stat21 = ?SP("rtp/ps","1245"),
+ Stat22 = ?SP("nt/os", "62345"),
+ Stat23 = ?SP("rtp/pr", "780"),
+ Stat24 = ?SP("nt/or", "45123"),
+ Stat25 = ?SP("rtp/pl", "10"),
+ Stat26 = ?SP("rtp/jit", "27"),
+ Stat27 = ?SP("rtp/delay","48"),
+ Stats2 = [Stat21, Stat22, Stat23, Stat24, Stat25, Stat26, Stat27],
+ Reply2 = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A5556}],
+ terminationAudit = [{statisticsDescriptor, Stats2}]},
+ reply(Mid, 50009, 5000, [{subtractReply, Reply1}, {subtractReply, Reply2}]).
+
+%%----------------------------------------------------------------------
+%% 24. The MGC now sets up both MG1 and MG2 to be ready to detect the
+%% next off-hook event. See step 1. Note that this could be the
+%% default state of a termination in the null context, and if this
+%% were the case, no message need be sent from the MGC to the
+%% MG. Once a termination returns to the null context, it goes back
+%% to the default termination values for that termination.
+%%----------------------------------------------------------------------
+
+%% BUGBUG: Example missing in spec
+
+%%----------------------------------------------------------------------
+%% Testing
+%%----------------------------------------------------------------------
+
+messages() ->
+ [{Slogan, catch ?MODULE:Slogan()} || Slogan <- names()].
+
+encoders() ->
+ [
+ {megaco_pretty_text_encoder, [], []},
+ {megaco_compact_text_encoder, [], []},
+ {megaco_binary_encoder, [], [native]},
+ %% {megaco_ber_encoder, [], [native]},
+ %% {megaco_ber_bin_encoder, [], [native]},
+ {megaco_per_encoder, [], [native]},
+ {megaco_erl_dist_encoder, [], []},
+ {megaco_erl_dist_encoder, [compressed], [compressed]}
+ ].
+
+
+pretty_mod({Mod, Opt, _Opt2}) ->
+ case Mod of
+ megaco_pretty_text_encoder when Opt == [flex] -> pretty_flex;
+ megaco_compact_text_encoder when Opt == [flex] -> compact_flex;
+ megaco_pretty_text_encoder -> pretty_text;
+ megaco_compact_text_encoder -> compact_text;
+ megaco_binary_encoder -> asn1_ber;
+ megaco_ber_encoder -> asn1_ber_old;
+ megaco_ber_bin_encoder -> asn1_ber_bin;
+ megaco_per_encoder -> asn1_per;
+ megaco_erl_dist_encoder when Opt == [] -> standard_erl;
+ megaco_erl_dist_encoder when Opt == [compressed] -> compressed_erl;
+ Ugly -> Ugly
+ end.
+
+%%----------------------------------------------------------------------
+%% Run specific encoder for all test cases
+%%----------------------------------------------------------------------
+
+pretty_text() ->
+ Default = [],
+ Encoder = {megaco_pretty_text_encoder, Default, Default},
+ All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()],
+ compute_res(All).
+
+compact_text() ->
+ Default = [],
+ Encoder = {megaco_compact_text_encoder, Default, Default},
+ All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()],
+ compute_res(All).
+
+pretty_flex() ->
+ Default = [flex],
+ Encoder = {megaco_pretty_text_encoder, Default, Default},
+ All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()],
+ compute_res(All).
+
+compact_flex() ->
+ Default = [flex],
+ Encoder = {megaco_compact_text_encoder, Default, Default},
+ All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()],
+ compute_res(All).
+
+bin() ->
+ Default = [],
+ Native = [native],
+ Encoder = {megaco_binary_encoder, Default, Native},
+ All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()],
+ compute_res(All).
+
+asn1_ber() ->
+ Default = [],
+ Native = [native],
+ Encoder = {megaco_ber_encoder, Default, Native},
+ All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()],
+ compute_res(All).
+
+asn1_ber_bin() ->
+ Default = [],
+ Native = [native],
+ Encoder = {megaco_ber_bin_encoder, Default, Native},
+ All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()],
+ compute_res(All).
+
+asn1_per() ->
+ Default = [],
+ Native = [native],
+ Encoder = {megaco_per_encoder, Default, Native},
+ All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()],
+ compute_res(All).
+
+standard_erl() ->
+ Config = [],
+ Encoder = {megaco_erl_dist_encoder, Config, Config},
+ All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()],
+ compute_res(All).
+
+compressed_erl() ->
+ Config = [compressed],
+ Encoder = {megaco_erl_dist_encoder, Config, Config},
+ All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()],
+ compute_res(All).
+
+encode(Slogan, DecodedMsg, {Mod, Opt, _Opt2} = _Encoder) ->
+ Main = "==================================================",
+ Sub = "--------------------------------------------------",
+ case catch Mod:encode_message(Opt, DecodedMsg) of
+ {ok, EncodedMsg} when is_binary(EncodedMsg) ->
+ Sz = size(EncodedMsg),
+ ok = io:format("~w ~s ~w bytes~n",
+ [Slogan, Main, Sz]),
+ catch io:format("~n~s~n", [(catch binary_to_list(EncodedMsg))]),
+ case catch Mod:decode_message(Opt, EncodedMsg) of
+ {ok, ReDecodedMsg} ->
+ fmt(Slogan, DecodedMsg, ReDecodedMsg);
+ {error, {Line, _, Reason}} when is_integer(Line)->
+ ?ERROR([{Slogan, Line, decode_failed}, {error, Reason}, {encoded, EncodedMsg}, DecodedMsg]),
+ io:format("~n~w <ERROR> #~w: ~w~n",
+ [Slogan, Line, Reason]);
+ Other ->
+ ?ERROR([{Slogan, 0, decode_failed}, Other, {encoded, EncodedMsg}, DecodedMsg]),
+ io:format("~n~w <ERROR> ~w~n", [Slogan, Other])
+ end,
+ Sz;
+ Other ->
+ ?ERROR([{Slogan, encode_failed}, Other, DecodedMsg]),
+ ok = io:format("~w ~s~n~p~n<ERROR> ~s~n~p~n",
+ [Slogan, Main, DecodedMsg, Sub, Other]),
+ {Slogan, {encode_message, Other}}
+ end.
+
+fmt(_Slogan, Msg, Msg) ->
+ ok;
+fmt(Slogan, {'MegacoMessage', A, {'Message', V, MID, {transactions, [{T, Old}]}}},
+ {'MegacoMessage', A, {'Message', V, MID, {transactions, [{T, New}]}}}) ->
+ fmt(Slogan, Old, New);
+fmt(Slogan, Old, New) ->
+ PrettyOld = lists:flatten(io_lib:format("~p", [Old])),
+ PrettyNew = lists:flatten(io_lib:format("~p", [New])),
+ fmt_diff(Slogan, Old, New, PrettyOld, PrettyNew, []).
+
+fmt_diff(Slogan, Old, New, [H | OldRest], [H | NewRest], Common) ->
+ fmt_diff(Slogan, Old, New, OldRest, NewRest, [H | Common]);
+fmt_diff(Slogan, Old, New, OldRest, NewRest, Common) ->
+ RevCommon = lists:reverse(Common),
+ ?ERROR([{Slogan, decode_mismatch}, {old, Old}, {new, New}]),
+ Sub = "--------------------------------------------------",
+ io:format("~n~w COMMON ~s~n~s~n", [Slogan, Sub, RevCommon]),
+ io:format("~n~w OLD ~s~n~s~n", [Slogan, Sub, OldRest]),
+ io:format("~n~w NEW ~s~n~s~n", [Slogan, Sub, NewRest]).
+
+compute_res(All) ->
+ compute_res(All, [], 0).
+
+compute_res([H | T], Bad, Sum) when is_integer(H) ->
+ compute_res(T, Bad, Sum + H);
+compute_res([H | T], Bad, Sum) ->
+ compute_res(T, [H | Bad], Sum);
+compute_res([], Bad, Sum) ->
+ ok = io:format("#bytes: ~w; errors: ~p~n", [Sum, Bad]).
+
+%%----------------------------------------------------------------------
+%% Compute sizes of encoded messages
+%%----------------------------------------------------------------------
+
+msg_sizes() ->
+ Encoders = encoders(),
+ msg_sizes(Encoders).
+
+%% Returns a list of {MessageSlogan, MessageSizes} where
+%% MessageSizes is the result from msg_sizes/2
+msg_sizes(Encoders) ->
+ [{S, msg_sizes(Msg, Encoders)} || {S, Msg} <- messages()].
+
+%% Returns a list of {Encoder, Res} tuples
+%% where Res either is the message size (integer)
+%% or an error atom
+msg_sizes(DecodedMsg, Encoders) ->
+ [abs_msg_size(DecodedMsg, E) || E <- Encoders].
+
+abs_msg_size(DecodedMsg, {Mod, Opt, _Opt2} = Encoder) ->
+ case catch Mod:encode_message(Opt, DecodedMsg) of
+ {ok, EncodedMsg} when is_binary(EncodedMsg) ->
+ {Encoder, size(EncodedMsg)};
+ Error ->
+ {Encoder, {bad_encoder, Error}}
+ end.
+
+%%----------------------------------------------------------------------
+%% Compute time for encoding messages
+%%----------------------------------------------------------------------
+
+encoding_times() ->
+ Encoders = encoders(),
+ encoding_times(Encoders).
+
+%% Returns a list of {MessageSlogan, EncodingTimes} where
+%% EncodingTimes is the result from encoding_times/2
+encoding_times(Encoders) ->
+ [{Slogan, encoding_times(Msg, Encoders)} || {Slogan, Msg} <- messages()].
+
+%% Returns a list of {Encoder, Res} tuples
+%% where Res either is the encoding time (integer)
+%% or an error atom
+encoding_times(DecodedMsg, Encoders) ->
+ [{E, encoding_time(encoding_msg(DecodedMsg, E))} || E <- Encoders].
+
+encoding_msg(DecodedMsg, {Mod, Opt, Opt2} = Encoder) ->
+ {ok, EncodedMsg} = Mod:encode_message(Opt, DecodedMsg),
+ {ok, DecodedMsg2} = Mod:decode_message(Opt2, EncodedMsg),
+ {Encoder, DecodedMsg2}.
+
+encoding_time({{Mod, _Opt, Opt2} = Encoder, DecodedMsg}) ->
+ meter(fun() -> {ok, _} = Mod:encode_message(Opt2, DecodedMsg) end, Encoder).
+
+%%----------------------------------------------------------------------
+%% Compute time for decoding messages
+%%----------------------------------------------------------------------
+
+decoding_times() ->
+ Decoders = encoders(),
+ decoding_times(Decoders).
+
+%% Returns a list of {MessageSlogan, DecodingTimes} where
+%% DecodingTimes is the result from decoding_times/2
+decoding_times(Decoders) ->
+ [{Slogan, decoding_times(Msg, Decoders)} || {Slogan, Msg} <- messages()].
+
+%% Returns a list of {Decoder, Res} tuples
+%% where Res either is the decoding time (integer)
+%% or an error atom
+decoding_times(DecodedMsg, Encoders) ->
+ [{E, decoding_time(decoding_msg(DecodedMsg, E))} || E <- Encoders].
+
+decoding_msg(DecodedMsg, {Mod, Opt, _Opt2} = Encoder) ->
+ {ok, EncodedMsg} = Mod:encode_message(Opt, DecodedMsg),
+ {Encoder, EncodedMsg}.
+
+decoding_time({{Mod, _Opt, Opt2} = Encoder, EncodedMsg}) ->
+ meter(fun() -> {ok, _} = Mod:decode_message(Opt2, EncodedMsg) end, Encoder).
+
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+
+coding_times() ->
+ Coders = encoders(),
+ coding_times(Coders).
+
+%% Returns a list of {MessageSlogan, DecodingTimes} where
+%% DecodingTimes is the result from decoding_times/2
+coding_times(Coders) ->
+ [{Slogan, coding_times(Msg, Coders)} || {Slogan, Msg} <- messages()].
+
+%% Returns a list of {Decoder, Res} tuples
+%% where Res either is the decoding time (integer)
+%% or an error atom
+coding_times(DecodedMsg, Coders) ->
+ [{E, coding_time(coding_msg(DecodedMsg, E))} || E <- Coders].
+
+coding_msg(DecodedMsg, {Mod, Opt, _Opt2} = Encoder) ->
+ {ok, EncodedMsg} = Mod:encode_message(Opt, DecodedMsg),
+ {Encoder, EncodedMsg}.
+
+coding_time({{Mod, _Opt, Opt2} = Encoder, EncodedMsg}) ->
+ Fun = fun() ->
+ {ok, DecodedMsg} = Mod:decode_message(Opt2, EncodedMsg),
+ {ok, _} = Mod:encode_message(Opt2, DecodedMsg)
+ end,
+ meter(Fun, Encoder).
+
+%%----------------------------------------------------------------------
+%% Return size statistics as term
+%%----------------------------------------------------------------------
+
+size_stat() ->
+ Encoders = encoders(),
+ MsgSizes = msg_sizes(Encoders),
+ stat(Encoders, MsgSizes).
+
+%%----------------------------------------------------------------------
+%%----------------------------------------------------------------------
+
+gnuplot_gif() ->
+ [
+ {size, gnuplot_size_gif()},
+ {encoding, gnuplot_enc_time_gif()},
+ {decoding, gnuplot_dec_time_gif()},
+ {coding, gnuplot_code_time_gif()}
+ ].
+
+%%----------------------------------------------------------------------
+%% Generate GIF picture from size statistics with gnuplot
+%%----------------------------------------------------------------------
+
+gnuplot_size_gif() ->
+ {ok, _Cwd} = file:get_cwd(),
+ TmpDir = "megaco_encoded_size.tmp",
+ GifFile = "megaco_encoded_size.gif",
+ Header =
+ ["set title \"Size comparison of Megaco/H.248 encoding formats\"\n",
+ "set timestamp top\n",
+ "set terminal gif\n",
+ "set xlabel \"Test cases from Appendix A\"\n",
+ "set ylabel \"Message size in bytes\"\n",
+ "set rmargin 10\n",
+ "set key left top Left\n",
+ "set output \"", GifFile, "\"\n\n"],
+ Encoders = encoders(),
+ Stat = msg_sizes(Encoders),
+ BatchFile = gnuplot_dir(TmpDir, Header, Encoders, Stat),
+ Cmd = "cd " ++ TmpDir ++ "; gnuplot " ++ BatchFile,
+ os:cmd(Cmd),
+ ok = io:format("~n~s~nxv ~s~n", [Cmd, filename:join([TmpDir, GifFile])]),
+ stat(Encoders, Stat).
+
+%%----------------------------------------------------------------------
+%% Return encoding time statistics as term
+%%----------------------------------------------------------------------
+
+encoding_times_stat() ->
+ Encoders = encoders(),
+ EncodingTimes = encoding_times(Encoders),
+ stat(Encoders, EncodingTimes).
+
+%%----------------------------------------------------------------------
+%% Return encoding time statistics as term
+%%----------------------------------------------------------------------
+
+decoding_times_stat() ->
+ Encoders = encoders(),
+ DecodingTimes = decoding_times(Encoders),
+ stat(Encoders, DecodingTimes).
+
+%%----------------------------------------------------------------------
+%% Return encoding time statistics as term
+%%----------------------------------------------------------------------
+
+coding_times_stat() ->
+ Encoders = encoders(),
+ CodingTimes = coding_times(Encoders),
+ stat(Encoders, CodingTimes).
+
+%%----------------------------------------------------------------------
+%% Generate GIF picture from encoding time statistics with gnuplot
+%%----------------------------------------------------------------------
+
+gnuplot_enc_time_gif() ->
+ {ok, _Cwd} = file:get_cwd(),
+ TmpDir = "megaco_encoding_time.tmp",
+ GifFile = "megaco_encoding_time.gif",
+ Header =
+ ["set title \"Encoding time comparison of Megaco/H.248 encoding formats\"\n",
+ "set timestamp top\n",
+ "set terminal gif\n",
+ "set xlabel \"Test cases from Appendix A\"\n",
+ "set ylabel \"Time for encoding in micro seconds\"\n",
+ "set rmargin 10\n",
+ "set key left top Left\n",
+ "set output \"", GifFile, "\"\n\n"],
+ Encoders = encoders(),
+ Stat = encoding_times(Encoders),
+ BatchFile = gnuplot_dir(TmpDir, Header, Encoders, Stat),
+ Cmd = "cd " ++ TmpDir ++ "; gnuplot " ++ BatchFile,
+ os:cmd(Cmd),
+ ok = io:format("~n~s~nxv ~s~n", [Cmd, filename:join([TmpDir, GifFile])]),
+ stat(Encoders, Stat).
+
+%%----------------------------------------------------------------------
+%% Generate GIF picture from decoding time statistics with gnuplot
+%%----------------------------------------------------------------------
+
+gnuplot_dec_time_gif() ->
+ {ok, _Cwd} = file:get_cwd(),
+ TmpDir = "megaco_decoding_time.tmp",
+ GifFile = "megaco_decoding_time.gif",
+ Header =
+ ["set title \"Decoding time comparison of Megaco/H.248 encoding formats\"\n",
+ "set timestamp top\n",
+ "set terminal gif\n",
+ "set xlabel \"Test cases from Appendix A\"\n",
+ "set ylabel \"Time for decoding in micro seconds\"\n",
+ "set rmargin 10\n",
+ "set key left top Left\n",
+ "set output \"", GifFile, "\"\n\n"],
+ Encoders = encoders(),
+ Stat = decoding_times(Encoders),
+ BatchFile = gnuplot_dir(TmpDir, Header, Encoders, Stat),
+ Cmd = "cd " ++ TmpDir ++ "; gnuplot " ++ BatchFile,
+ os:cmd(Cmd),
+ ok = io:format("~n~s~nxv ~s~n", [Cmd, filename:join([TmpDir, GifFile])]),
+ stat(Encoders, Stat).
+
+%%----------------------------------------------------------------------
+%% Generate GIF picture from decoding time statistics with gnuplot
+%%----------------------------------------------------------------------
+
+gnuplot_code_time_gif() ->
+ {ok, _Cwd} = file:get_cwd(),
+ TmpDir = "megaco_coding_time.tmp",
+ GifFile = "megaco_coding_time.gif",
+ Header =
+ ["set title \"Encoding + decoding time comparison of Megaco/H.248 encoding formats\"\n",
+ "set timestamp top\n",
+ "set terminal gif\n",
+ "set xlabel \"Test cases from Appendix A\"\n",
+ "set ylabel \"Time for encoding+decoding in micro seconds\"\n",
+ "set rmargin 10\n",
+ "set key left top Left\n",
+ "set output \"", GifFile, "\"\n\n"],
+ Encoders = encoders(),
+ Stat = coding_times(Encoders),
+ BatchFile = gnuplot_dir(TmpDir, Header, Encoders, Stat),
+ Cmd = "cd " ++ TmpDir ++ "; gnuplot " ++ BatchFile,
+ os:cmd(Cmd),
+ ok = io:format("~n~s~nxv ~s~n", [Cmd, filename:join([TmpDir, GifFile])]),
+ stat(Encoders, Stat).
+
+%%----------------------------------------------------------------------
+%% Encode asn.1 messages
+%%----------------------------------------------------------------------
+
+gen_byte_msg(Msg, {Mod, Opt, _Opt2} = _Encoder) ->
+ {ok, EncodedMsg} = Mod:encode_message(Opt, Msg),
+ EncodedMsg.
+
+%%----------------------------------------------------------------------
+%% Gen the C header file content as a binary
+%%----------------------------------------------------------------------
+
+gen_header_file_binary([]) ->
+ ok;
+gen_header_file_binary([{S, B}| Rest]) ->
+ file:write_file(atom_to_list(S), B),
+ gen_header_file_binary(Rest).
+
+%%----------------------------------------------------------------------
+%% Generate headerfile for asn.1 BER test in C
+%%----------------------------------------------------------------------
+
+gen_ber_header() ->
+ Encoder = {megaco_ber_encoder, [], []},
+ L = [{S, gen_byte_msg(Msg, Encoder)} || {S, Msg} <- messages()],
+ gen_header_file_binary(L).
+
+%%----------------------------------------------------------------------
+%% Generate headerfile for asn.1 BER test in C
+%%----------------------------------------------------------------------
+gen_ber_bin_header() ->
+ Encoder = {megaco_ber_bin_encoder, [], []},
+ L = [{S, gen_byte_msg(Msg, Encoder)} || {S, Msg} <- messages()],
+ gen_header_file_binary(L).
+
+%%----------------------------------------------------------------------
+%% Generate headerfile for asn.1 PER test in C
+%%----------------------------------------------------------------------
+gen_per_header() ->
+ Encoder = {megaco_per_encoder, [], []},
+ L = [{S, gen_byte_msg(Msg, Encoder)} || {S, Msg} <- messages()],
+ gen_header_file_binary(L).
+
+%%----------------------------------------------------------------------
+%% Execute a fun a number of times and return avg in millis
+%%----------------------------------------------------------------------
+
+meter(Fun, Encoder) ->
+ MaxTime = timer:seconds(5),
+ Rep = 2,
+ M = pretty_mod(Encoder),
+ io:format("~p:\t", [M]),
+ Times = [single_meter(Fun, MaxTime, {M, N}) || N <- lists:seq(1, Rep)],
+ Min = lists:min(Times),
+ Max = lists:max(Times),
+ Median = lists:nth((Rep + 1) div 2, lists:sort(Times)),
+ io:format("(median=~p, diff=~p)~n", [Median, Max - Min]),
+ Median.
+
+single_meter(Fun, MaxTime, Tag) ->
+ Pid = spawn_link(?MODULE, single_meter, [self(), Fun, MaxTime, Tag]),
+ receive
+ {meter, Pid, Time} ->
+ io:format("~p \t", [Time]),
+ Time;
+ {'EXIT', Pid, Reason} ->
+ {bad_result, Reason}
+ end.
+
+single_meter(Parent, Fun, Expected, _Tag) ->
+ erlang:statistics(runtime),
+ erlang:send_after(Expected, self(), return_count),
+ Count = count(Fun, 1),
+ {_, Actual} = erlang:statistics(runtime),
+ %% Diff = Actual - Expected,
+ Micros = trunc((Actual / Count) * 1000),
+ Parent ! {meter, self(), Micros},
+ unlink(Parent),
+ exit(normal).
+
+count(Fun, N) ->
+ Fun(),
+ receive
+ return_count ->
+ N
+ after 0 ->
+ count(Fun, N + 1)
+ end.
+
+stat(Encoders, Stat) ->
+ Fun =
+ fun({_Mod, _Opt, _Opt2} = E) ->
+ List = lists:flatten([[Val || {E2, Val} <- Info, E2 == E] ||
+ {_Slogan, Info} <- Stat]),
+ Max = lists:max(List),
+ Min = lists:min(List),
+ case catch lists:sum(List) of
+ {'EXIT', _} ->
+ {E, bad, List};
+ Sum when is_integer(Sum) ->
+ N = length(List),
+ {E, [{min, Min}, {avg, Sum div N}, {max, Max}]}
+ end
+ end,
+ lists:map(Fun, Encoders).
+
+gnuplot_dir(Dir, Header, Encoders, Stat) ->
+ file:make_dir(Dir),
+ {Names, Arrows} = gnuplot_data(Encoders, Dir, Stat, 1, [], []),
+ [H | T] = [lists:concat(["\"", N, "\" with linespoints"]) || N <- Names],
+ Plots = [[", ", Plot] || Plot <- T],
+ IoList = [Header, Arrows, "\nplot ", H, Plots, "\n\nshow output\n"],
+ Bin = list_to_binary(IoList),
+ BatchFile = "gnuplot.batch",
+ file:write_file(filename:join(Dir, BatchFile), Bin),
+ BatchFile.
+
+gnuplot_data([], _Dir, _Stat, _Pos, Names, Arrows) ->
+ {lists:reverse(Names), lists:reverse(Arrows)};
+gnuplot_data([{Mod, _Opt, _Opt2} = E | Encoders], Dir, Stat, Pos, Names, Arrows) ->
+ Plot = fun({Msg, List}, {N, AccSz}) ->
+ {value, {_, Sz}} = lists:keysearch(E, 1, List),
+ ActualSz =
+ if
+ is_integer(Sz) ->
+ Sz;
+ true ->
+ ok = io:format("<ERROR> ~p(~p) -> ~p~n",
+ [Mod, Msg, Sz]),
+ 0
+ end,
+ Acc = {N + 1, [ActualSz | AccSz]},
+ {lists:concat([N + 1, " ", ActualSz, "\n"]), Acc}
+ end,
+ {Data, {N, Sizes}} = lists:mapfoldl(Plot, {0, []}, Stat),
+ Min = lists:min(Sizes),
+ Max = lists:max(Sizes),
+ Sum = lists:sum(Sizes),
+ Avg = Sum div N,
+ Len = length(Stat),
+ Pretty = pretty_mod(E),
+ Name = lists:concat([Pretty, " (", Min, ",", Avg, ",", Max, ")"]),
+ file:write_file(filename:join(Dir, Name), list_to_binary(Data)),
+ %%"plot \"-\" title \"", E, "\" with linespoints\n", Data, "e\n",
+ Arrow =
+ lists:concat(["set arrow from 1,", Avg,
+ " to ", Len, ", ", Avg,
+ " nohead lt ", Pos, "\n",
+ "set label \" ", Avg, " (avg)\" at ", Len, ",", Avg + 10, "\n"]),
+ gnuplot_data(Encoders, Dir, Stat, Pos + 1, [Name | Names], [Arrow | Arrows]).