1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2000-2019. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21%% 22%%---------------------------------------------------------------------- 23%% Purpose: Test encoding/decoding of the sample call flows Megaco/H.248 24%%---------------------------------------------------------------------- 25%% megaco_call_flow_test:pretty_text(). 26%% megaco_call_flow_test:compact_text(). 27%% megaco_call_flow_test:bin(). 28%% megaco_call_flow_test:asn1_ber(). 29%% megaco_call_flow_test:asn1_per(). 30%% megaco_call_flow_test:erl_dist(). 31%% megaco_call_flow_test:compressed_erl_dist(). 32%% megaco_call_flow_test:gnuplot_gif(). 33%% megaco_call_flow_test:gnuplot_size_gif(). 34%% megaco_call_flow_test:gnuplot_enc_time_gif(). 35%% megaco_call_flow_test:gnuplot_dec_time_gif(). 36%% megaco_call_flow_test:gnuplot_code_time_gif(). 37%%---------------------------------------------------------------------- 38 39-module(megaco_call_flow_SUITE). 40 41-export([ 42 suite/0, all/0, groups/0, 43 init_per_suite/1, end_per_suite/1, 44 init_per_group/2, end_per_group/2, 45 init_per_testcase/2, end_per_testcase/2, 46 47 pretty/1, 48 compact/1, 49 pretty_flex/1, 50 compact_flex/1, 51 bin/1, 52 ber/1, 53 per/1, 54 standard_erl/1, 55 compressed_erl/1 56 ]). 57 58-export([ 59 msg1/0, msg1/1, 60 msg2/0, msg2/1, 61 msg3/0, msg3/1, 62 msg4/0, msg4/1, 63 msg5a/0, msg5a/1, 64 msg5b/0, msg5b/1, 65 msg6/0, msg6/1, 66 msg7/0, msg7/1, 67 msg9/0, msg9/1, 68 msg10/0, msg10/1, 69 msg11/0, msg11/1, 70 msg12/0, msg12/1, 71 msg13/0, msg13/1, 72 msg14/0, msg14/1, 73 msg15/0, msg15/1, 74 msg16/0, msg16/1, 75 msg17a/0, msg17a/1, 76 msg17b/0, msg17b/1, 77 msg18a/0, msg18a/1, 78 msg18b/0, msg18b/1, 79 msg18c/0, msg18c/1, 80 msg18d/0, msg18d/1, 81 msg19a/0, msg19a/1, 82 msg19b/0, msg19b/1, 83 msg20/0, msg20/1, 84 msg21/0, msg21/1, 85 msg22a/0, msg22a/1, 86 msg22b/0, msg22b/1, 87 msg23a/0, msg23a/1, 88 msg23b/0, msg23b/1 89 90 ]). 91 92-export([ 93 encoders/0, 94 msg_sizes/0, 95 coding_times/0, 96 encoding_times/0, 97 decoding_times/0, 98 coding_times_stat/0, 99 encoding_times_stat/0, 100 decoding_times_stat/0, 101 size_stat/0, 102 gnuplot_gif/0, 103 gnuplot_size_gif/0, 104 105 gen_byte_msg/2, 106 gen_header_file_binary/1, 107 gen_ber_header/0, 108 gen_ber_bin_header/0, 109 gen_per_header/0, 110 single_meter/4, 111 count/2 112 ]). 113 114-include_lib("megaco/include/megaco.hrl"). 115-include_lib("megaco/include/megaco_message_v1.hrl"). 116-include("megaco_test_lib.hrl"). 117 118 119%%====================================================================== 120%% Common Test interface functions 121%%====================================================================== 122 123suite() -> 124 [{ct_hooks, [ts_install_cth]}]. 125 126all() -> 127 [ 128 {group, text}, 129 {group, binary}, 130 {group, erl} 131 ]. 132 133groups() -> 134 [ 135 {text, [], text_cases()}, 136 {flex, [], flex_cases()}, 137 {binary, [], binary_cases()}, 138 {erl, [], erl_cases()} 139 ]. 140 141text_cases() -> 142 [ 143 pretty, 144 compact 145 ]. 146 147flex_cases() -> 148 [ 149 pretty_flex, 150 compact_flex 151 ]. 152 153binary_cases() -> 154 [ 155 bin, 156 ber, 157 per 158 ]. 159 160erl_cases() -> 161 [ 162 standard_erl, 163 compressed_erl 164 ]. 165 166 167 168%% 169%% ----- 170%% 171 172init_per_suite(suite) -> 173 []; 174init_per_suite(doc) -> 175 []; 176init_per_suite(Config0) when is_list(Config0) -> 177 178 ?ANNOUNCE_SUITE_INIT(), 179 180 p("init_per_suite -> entry with" 181 "~n Config: ~p" 182 "~n Nodes: ~p", [Config0, erlang:nodes()]), 183 184 case ?LIB:init_per_suite(Config0) of 185 {skip, _} = SKIP -> 186 SKIP; 187 188 Config1 when is_list(Config1) -> 189 190 %% We need a (local) monitor on this node also 191 megaco_test_sys_monitor:start(), 192 193 p("init_per_suite -> end when" 194 "~n Config: ~p" 195 "~n Nodes: ~p", [Config1, erlang:nodes()]), 196 197 Config1 198 end. 199 200end_per_suite(suite) -> []; 201end_per_suite(doc) -> []; 202end_per_suite(Config0) when is_list(Config0) -> 203 204 p("end_per_suite -> entry with" 205 "~n Config: ~p" 206 "~n Nodes: ~p", [Config0, erlang:nodes()]), 207 208 megaco_test_sys_monitor:stop(), 209 Config1 = ?LIB:end_per_suite(Config0), 210 211 p("end_per_suite -> end when" 212 "~n Nodes: ~p", [erlang:nodes()]), 213 214 Config1. 215 216 217%% 218%% ----- 219%% 220 221init_per_group(Group, Config) -> 222 ?ANNOUNCE_GROUP_INIT(Group), 223 Config. 224 225end_per_group(_Group, Config) -> 226 Config. 227 228 229%% 230%% ----- 231%% 232 233init_per_testcase(Case, Config) -> 234 %% We do *not* reset events with each test case 235 %% The test cases are so short we don't bother, 236 %% and also we would drown in mprintouts... 237 megaco_test_lib:init_per_testcase(Case, Config). 238 239end_per_testcase(Case, Config) -> 240 megaco_test_lib:end_per_testcase(Case, Config). 241 242 243 244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 245 246pretty(suite) -> 247 []; 248pretty(Config) when is_list(Config) -> 249 ?ACQUIRE_NODES(1, Config), 250 pretty_text(). 251 252compact(suite) -> 253 []; 254compact(Config) when is_list(Config) -> 255 ?ACQUIRE_NODES(1, Config), 256 compact_text(). 257 258pretty_flex(suite) -> 259 []; 260pretty_flex(Config) when is_list(Config) -> 261 ?ACQUIRE_NODES(1, Config), 262 pretty_flex(). 263 264compact_flex(suite) -> 265 []; 266compact_flex(Config) when is_list(Config) -> 267 ?ACQUIRE_NODES(1, Config), 268 compact_flex(). 269 270bin(suite) -> 271 []; 272bin(Config) when is_list(Config) -> 273 ?ACQUIRE_NODES(1, Config), 274 bin(). 275 276ber(suite) -> 277 []; 278ber(Config) when is_list(Config) -> 279 ?ACQUIRE_NODES(1, Config), 280 asn1_ber(). 281 282per(suite) -> 283 []; 284per(Config) when is_list(Config) -> 285 ?ACQUIRE_NODES(1, Config), 286 asn1_per(). 287 288standard_erl(suite) -> 289 []; 290standard_erl(Config) when is_list(Config) -> 291 ?ACQUIRE_NODES(1, Config), 292 standard_erl(). 293 294compressed_erl(suite) -> 295 []; 296compressed_erl(Config) when is_list(Config) -> 297 ?ACQUIRE_NODES(1, Config), 298 compressed_erl(). 299 300%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 301 302-define(PP(Name, Val), #'PropertyParm'{name = Name, value = [Val]}). 303-define(SP(Name, Val), #'StatisticsParameter'{statName = Name, statValue = [Val]}) . 304 305names() -> 306 [ 307 msg1, msg2, msg3, msg4, msg5a, msg5b, msg6, msg7, msg9, msg10, 308 msg11, msg12, msg13, msg14, msg15, msg16, msg17a, msg17b, 309 msg18a, msg18b, msg18c, msg18d, msg19a, msg19b, msg20, msg21, 310 msg22a, msg22b, msg23a, msg23b 311 ]. 312 313%% In this example, MG1 has the IP address 124.124.124.222, MG2 is 314%% 125.125.125.111, and the MGC is 123.123.123.4. The default Megaco port 315%% is 55555 for all three. 316 317-define(DEFAULT_PORT, 55555). 318-define(MG1_MID_NO_PORT, {ip4Address, 319 #'IP4Address'{address = [124, 124, 124, 222]}}). 320-define(MG1_MID, {ip4Address, #'IP4Address'{address = [124, 124, 124, 222], 321 portNumber = ?DEFAULT_PORT}}). 322-define(MG2_MID, {ip4Address, #'IP4Address'{address = [125, 125, 125, 111], 323 portNumber = ?DEFAULT_PORT}}). 324-define(MGC_MID, {ip4Address, #'IP4Address'{address = [123, 123, 123, 4], 325 portNumber = ?DEFAULT_PORT}}). 326 327-define(A4444, ["11111111", "00000000", "00000000"]). 328-define(A4445, ["11111111", "00000000", "11111111"]). 329-define(A5555, ["11111111", "11111111", "00000000"]). 330-define(A5556, ["11111111", "11111111", "11111111"]). 331 332%% -define(A4444, ["00000000"]). 333%% -define(A4445, ["00001111"]). 334%% -define(A5555, ["11110000"]). 335%% -define(A5556, ["11111111"]). 336 337request(Mid, TransId, ContextId, CmdReq) when is_list(CmdReq) -> 338 Actions = [#'ActionRequest'{contextId = ContextId, 339 commandRequests = CmdReq}], 340 Req = {transactions, 341 [{transactionRequest, 342 #'TransactionRequest'{transactionId = TransId, 343 actions = Actions}}]}, 344 #'MegacoMessage'{mess = #'Message'{version = 1, 345 mId = Mid, 346 messageBody = Req}}. 347 348reply(Mid, TransId, ContextId, CmdReply) when is_list(CmdReply) -> 349 Actions = [#'ActionReply'{contextId = ContextId, 350 commandReply = CmdReply}], 351 Req = {transactions, 352 [{transactionReply, 353 #'TransactionReply'{transactionId = TransId, 354 transactionResult = {actionReplies, Actions}}}]}, 355 #'MegacoMessage'{mess = #'Message'{version = 1, 356 mId = Mid, 357 messageBody = Req}}. 358 359%%---------------------------------------------------------------------- 360%% 1. An MG registers with an MGC using the ServiceChange command: 361%% 362%% MG1 to MGC: 363%% "MEGACO/1 [124.124.124.222] 364%% Transaction = 9998 { 365%% Context = - { 366%% ServiceChange = ROOT {Services { 367%% Method=Restart, 368%% ServiceChangeAddress=55555, Profile=ResGW/1} 369%% } 370%% } 371%% } 372%%---------------------------------------------------------------------- 373 374msg1() -> 375 msg1(?MG1_MID_NO_PORT). 376msg1(Mid) -> 377 Address = {portNumber, ?DEFAULT_PORT}, 378 Profile = #'ServiceChangeProfile'{profileName = "resgw", 379 version = 1}, 380 Parm = #'ServiceChangeParm'{serviceChangeMethod = restart, 381 serviceChangeAddress = Address, 382 serviceChangeReason = ["901 mg cold boot"], 383 %% BUGBUG: Mandatory reason missing in spec 384 serviceChangeProfile = Profile}, 385 Req = #'ServiceChangeRequest'{terminationID = [?megaco_root_termination_id], 386 serviceChangeParms = Parm}, 387 CmdReq = #'CommandRequest'{command = {serviceChangeReq, Req}}, 388 request(Mid, 9998, ?megaco_null_context_id, [CmdReq]). 389 390%%---------------------------------------------------------------------- 391%% 392%% 2. The MGC sends a reply: 393%% 394%% MGC to MG1: 395%% MEGACO/1 [123.123.123.4]:55555 396%% Reply = 9998 { 397%% Context = - {ServiceChange = ROOT { 398%% Services {ServiceChangeAddress=55555, Profile=ResGW/1} } } 399%% } 400%%---------------------------------------------------------------------- 401 402msg2() -> 403 msg2(?MGC_MID). 404msg2(Mid) -> 405 Address = {portNumber, ?DEFAULT_PORT}, 406 Profile = #'ServiceChangeProfile'{profileName = "resgw", 407 version = 1}, 408 Parm = #'ServiceChangeResParm'{serviceChangeAddress = Address, 409 serviceChangeProfile = Profile}, 410 Reply = #'ServiceChangeReply'{terminationID = [?megaco_root_termination_id], 411 serviceChangeResult = {serviceChangeResParms, 412 Parm}}, 413 reply(Mid, 9998, ?megaco_null_context_id, [{serviceChangeReply, Reply}]). 414 415%%---------------------------------------------------------------------- 416%% 3. The MGC programs a Termination in the NULL context. The 417%% terminationId is A4444, the streamId is 1, the requestId in the 418%% Events descriptor is 2222. The mId is the identifier of the sender 419%% of this message, in this case, it is the IP address and port 420%% [123.123.123.4]:55555. Mode for this stream is set to 421%% SendReceive. "al" is the analog line supervision package. 422%% 423%% MGC to MG1: 424%% MEGACO/1 [123.123.123.4]:55555 425%% Transaction = 9999 { 426%% Context = - { 427%% Modify = A4444 { 428%% Media { Stream = 1 { 429%% LocalControl { 430%% Mode = SendReceive, 431%% tdmc/gain=2, ; in dB, 432%% tdmc/ec=G165 433%% }, 434%% Local { 435%% v=0 436%% c=IN IP4 $ 437%% m=audio $ RTP/AVP 0 438%% a=fmtp:PCMU VAD=X-NNVAD ; special voice activity 439%% ; detection algorithm 440%% } 441%% } 442%% }, 443%% Events = 2222 {al/of} 444%% } 445%% } 446%% } 447%%---------------------------------------------------------------------- 448 449msg3() -> 450 msg3(?MGC_MID). 451msg3(Mid) -> 452 msg3(Mid, ?A4444). 453msg3(Mid, Tid) -> 454 Gain = ?PP("tdmc/gain", "2"), 455 %% Ec = ?PP("tdmc/ec", "G165"), 456 Ec = ?PP("tdmc/ec", "g165"), %% BUGBUG: should be case insensitive 457 LCD = #'LocalControlDescriptor'{streamMode = sendRecv, 458 propertyParms = [Gain, Ec]}, 459 V = ?PP("v", "0"), 460 C = ?PP("c", "IN IP4 $ "), 461 M = ?PP("m", "audio $ RTP/AVP 0"), 462 A = ?PP("a", "fmtp:PCMU VAD=X-NNVAD"), 463 LD = #'LocalRemoteDescriptor'{propGrps = [[V, C, M, A]]}, 464 Parms = #'StreamParms'{localControlDescriptor = LCD, 465 localDescriptor = LD}, 466 StreamDesc = #'StreamDescriptor'{streamID = 1, 467 streamParms = Parms}, 468 MediaDesc = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}}, 469 ReqEvent = #'RequestedEvent'{pkgdName = "al/of", 470 evParList = []}, 471 EventsDesc = #'EventsDescriptor'{requestID = 2222, 472 eventList = [ReqEvent]}, 473 AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = Tid}], 474 descriptors = [{mediaDescriptor, MediaDesc}, 475 {eventsDescriptor, EventsDesc}]}, 476 CmdReq = #'CommandRequest'{command = {modReq, AmmReq}}, 477 request(Mid, 9999, ?megaco_null_context_id, [CmdReq]). 478 479%%---------------------------------------------------------------------- 480%% The dialplan script could have been loaded into the MG previously. 481%% Its function would be to wait for the OffHook, turn on dialtone and 482%% start collecting DTMF digits. However in this example, we use the 483%% digit map, which is put into place after the offhook is detected (step 484%% 5 below). 485%% 486%% 487%% Note that the embedded EventsDescriptor could have been used to 488%% combine steps 3 and 4 with steps 8 and 9, eliminating steps 6 and 7. 489%% 490%% 491%% 4. The MG1 accepts the Modify with this reply: 492%% 493%% MG1 to MGC: 494%% MEGACO/1 [124.124.124.222]:55555 495%% Reply = 9999 { 496%% Context = - {Modify = A4444} 497%% } 498%%---------------------------------------------------------------------- 499 500msg4() -> 501 msg4(?MG1_MID). 502msg4(Mid) -> 503 msg4(Mid, ?A4444). 504msg4(Mid, Tid) -> 505 Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = Tid}]}, 506 reply(Mid, 9999, ?megaco_null_context_id, [{modReply, Reply}]). 507 508%%---------------------------------------------------------------------- 509%% 5. A similar exchange happens between MG2 and the MGC, resulting in an 510%% idle Termination called A5555. 511%%---------------------------------------------------------------------- 512 513msg5a() -> 514 msg5a(?MGC_MID). 515msg5a(Mid) -> 516 msg3(Mid, ?A4444). 517 518msg5b() -> 519 msg5b(?MG2_MID). 520msg5b(Mid) -> 521 msg4(Mid, ?A5555). 522 523%%---------------------------------------------------------------------- 524%% A.1.2 Collecting Originator Digits and Initiating Termination 525%% 526%% The following builds upon the previously shown conditions. It 527%% illustrates the transactions from the Media Gateway Controller and 528%% originating Media Gateway (MG1) to get the originating Termination 529%% (A4444) through the stages of digit collection required to initiate a 530%% connection to the terminating Media Gateway (MG2). 531%% 532%% 6. MG1 detects an offhook event from User 1 and reports it to the 533%% Media Gateway Controller via the Notify Command. 534%% 535%% MG1 to MGC: 536%% MEGACO/1 [124.124.124.222]:55555 537%% Transaction = 10000 { 538%% Context = - { 539%% Notify = A4444 {ObservedEvents =2222 { 540%% 19990729T22000000:al/of}} 541%% } 542%% } 543%%---------------------------------------------------------------------- 544 545msg6() -> 546 msg6(?MG1_MID). 547msg6(Mid) -> 548 TimeStamp = #'TimeNotation'{date = "19990729", 549 time = "22000000"}, 550 Event = #'ObservedEvent'{eventName = "al/of", 551 timeNotation = TimeStamp, 552 eventParList = []}, 553 Desc = #'ObservedEventsDescriptor'{requestId = 2222, 554 observedEventLst = [Event]}, 555 NotifyReq = #'NotifyRequest'{terminationID = [#megaco_term_id{id = ?A4444}], 556 observedEventsDescriptor = Desc}, 557 CmdReq = #'CommandRequest'{command = {notifyReq, NotifyReq}}, 558 request(Mid, 10000, ?megaco_null_context_id, [CmdReq]). 559 560%%---------------------------------------------------------------------- 561%% 7. And the Notify is acknowledged. 562%% 563%% MGC to MG1: 564%% MEGACO/1 [123.123.123.4]:55555 565%% Reply = 10000 { 566%% Context = - {Notify = A4444} 567%% } 568%%---------------------------------------------------------------------- 569 570msg7() -> 571 msg7(?MGC_MID). 572msg7(Mid) -> 573 Reply = #'NotifyReply'{terminationID = [#megaco_term_id{id = ?A4444}]}, 574 reply(Mid, 10000, ?megaco_null_context_id, [{notifyReply, Reply}]). 575 576%%---------------------------------------------------------------------- 577%% 8. The MGC Modifies the termination to play dial tone, and to look for 578%% digits now. There is also an embedded event to stop dialtone upon 579%% detection of the first digit. dd is the DTMF Detection package, and 580%% ce is the completion event. 581%%---------------------------------------------------------------------- 582 583%% BUGBUG: Example missing in spec 584 585%%---------------------------------------------------------------------- 586%% 9. 587%% 588%% MGC to MG1: 589%% MEGACO/1 [123.123.123.4]:55555 590%% Transaction = 10001 { 591%% Context = - { 592%% Modify = A4444 { 593%% Events = 2223 { 594%% al/on, dd/ce {DigitMap=Dialplan0} 595%% }, 596%% Signals {cg/dt}, 597%% DigitMap= Dialplan0{ 598%% (0S| 00S|[1-7]xLxx|8Lxxxxxxx|#xxxxxxx|*xx|9L1xxxxxxxxxx|9L011x.S)} 599%% } 600%% } 601%% } 602%%---------------------------------------------------------------------- 603 604msg9() -> 605 msg9(?MGC_MID). 606msg9(Mid) -> 607 Name = "dialplan00", 608 Body = "(0s| 00s|[1-7]xlxx|8lxxxxxxx|#xxxxxxx|*xx|9l1xxxxxxxxxx|9l011x.s)", 609 Value = #'DigitMapValue'{digitMapBody = Body}, 610 On = #'RequestedEvent'{pkgdName = "al/on", evParList = []}, 611 Action = #'RequestedActions'{eventDM = {digitMapName, Name}}, 612 Ce = #'RequestedEvent'{pkgdName = "dd/ce", 613 eventAction = Action, 614 evParList = []}, 615 EventsDesc = #'EventsDescriptor'{requestID = 2223, 616 eventList = [On, Ce]}, 617 Signal = #'Signal'{signalName = "cg/rt", sigParList = []}, 618 DigMapDesc = #'DigitMapDescriptor'{digitMapName = Name, 619 digitMapValue = Value}, 620 AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A4444}], 621 descriptors = [{eventsDescriptor, EventsDesc}, 622 {signalsDescriptor, [{signal, Signal}]}, 623 {digitMapDescriptor, DigMapDesc}]}, 624 CmdReq = #'CommandRequest'{command = {modReq, AmmReq}}, 625 request(Mid, 10001, ?megaco_null_context_id, [CmdReq]). 626 627%%---------------------------------------------------------------------- 628%% 10. And the Modify is acknowledged. 629%% 630%% MG1 to MGC: 631%% MEGACO/1 [124.124.124.222]:55555 632%% Reply = 10001 { 633%% Context = - {Modify = A4444} 634%% } 635%%---------------------------------------------------------------------- 636 637msg10() -> 638 msg10(?MG1_MID). 639msg10(Mid) -> 640 Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4444}]}, 641 reply(Mid, 10001, ?megaco_null_context_id, [{modReply, Reply}]). 642 643%%---------------------------------------------------------------------- 644%% 11. Next, digits are accumulated by MG1 as they are dialed by User 1. 645%% Dialtone is stopped upon detection of the first digit, using the 646%% embedded event in step 8. When an appropriate match is made of 647%% collected digits against the currently programmed Dialplan for 648%% A4444, another Notify is sent to the Media Gateway Controller. 649%% 650%% MG1 to MGC: 651%% MEGACO/1 [124.124.124.222]:55555 652%% Transaction = 10002 { 653%% Context = - { 654%% Notify = A4444 {ObservedEvents =2223 { 655%% 19990729T22010001:dd/ce{ds="916135551212"}}} 656%% } 657%% } 658%%---------------------------------------------------------------------- 659 660msg11() -> 661 msg11(?MG1_MID). 662msg11(Mid) -> 663 TimeStamp = #'TimeNotation'{date = "19990729", 664 time = "22010001"}, 665 Parm = #'EventParameter'{eventParameterName = "ds", 666 value = ["916135551212"]}, 667 %% BUGBUG: Quoted string or safe char? 668 Event = #'ObservedEvent'{eventName = "dd/ce", 669 timeNotation = TimeStamp, 670 eventParList = [Parm]}, 671 Desc = #'ObservedEventsDescriptor'{requestId = 2223, 672 observedEventLst = [Event]}, 673 NotifyReq = #'NotifyRequest'{terminationID = [#megaco_term_id{id = ?A4444}], 674 observedEventsDescriptor = Desc}, 675 CmdReq = #'CommandRequest'{command = {notifyReq, NotifyReq}}, 676 request(Mid, 10002, ?megaco_null_context_id, [CmdReq]). 677 678%%---------------------------------------------------------------------- 679%% 12. And the Notify is acknowledged. 680%% 681%% MGC to MG1: 682%% MEGACO/1 [123.123.123.4]:55555 683%% Reply = 10002 { 684%% Context = - {Notify = A4444} 685%% } 686%%---------------------------------------------------------------------- 687 688msg12() -> 689 msg12(?MGC_MID). 690msg12(Mid) -> 691 Reply = #'NotifyReply'{terminationID = [#megaco_term_id{id = ?A4444}]}, 692 reply(Mid, 10002, ?megaco_null_context_id, [{notifyReply, Reply}]). 693 694%%---------------------------------------------------------------------- 695%% 13. The controller then analyses the digits and determines that a 696%% connection needs to be made from MG1 to MG2. Both the TDM 697%% termination A4444, and an RTP termination are added to a new 698%% context in MG1. Mode is ReceiveOnly since Remote descriptor values 699%% are not yet specified. Preferred codecs are in the MGC's preferred 700%% order of choice. 701%% 702%% MGC to MG1: 703%% MEGACO/1 [123.123.123.4]:55555 704%% Transaction = 10003 { 705%% Context = $ { 706%% Add = A4444, 707%% Add = $ { 708%% Media { 709%% Stream = 1 { 710%% LocalControl { 711%% Mode = ReceiveOnly, 712%% 713%% nt/jit=40, ; in ms 714%% }, 715%% Local { 716%% v=0 717%% c=IN IP4 $ 718%% m=audio $ RTP/AVP 4 719%% a=ptime:30 720%% v=0 721%% c=IN IP4 $ 722%% m=audio $ RTP/AVP 0 723%% } 724%% } 725%% } 726%% } 727%% } 728%% } 729%% 730%% NOTE - The MGC states its prefrred parameter values as a series of 731%% sdp blocks in Local. The MG fills in the Local Descriptor in the 732%% Reply. 733%%---------------------------------------------------------------------- 734 735msg13() -> 736 msg13(?MGC_MID). 737msg13(Mid) -> 738 AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A4444}], 739 descriptors = []}, 740 CmdReq = #'CommandRequest'{command = {addReq, AmmReq}}, 741 Jit = ?PP("nt/jit", "40"), 742 LCD = #'LocalControlDescriptor'{streamMode = recvOnly, 743 propertyParms = [Jit]}, 744 V = ?PP("v", "0"), 745 C = ?PP("c", "IN IP4 $ "), 746 M = ?PP("m", "audio $ RTP/AVP 4"), 747 A = ?PP("a", "ptime:30"), 748 V2 = ?PP("v", "0"), 749 C2 = ?PP("c", "IN IP4 $ "), 750 M2 = ?PP("m", "audio $ RTP/AVP 0"), 751 LD = #'LocalRemoteDescriptor'{propGrps = [[V, C, M, A], [V2, C2, M2]]}, 752 Parms = #'StreamParms'{localControlDescriptor = LCD, 753 localDescriptor = LD}, 754 StreamDesc = #'StreamDescriptor'{streamID = 1, 755 streamParms = Parms}, 756 MediaDesc = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}}, 757 ChooseTid = #megaco_term_id{contains_wildcards = true, 758 id = [[?megaco_choose]]}, 759 AmmReq2 = #'AmmRequest'{terminationID = [ChooseTid], 760 descriptors = [{mediaDescriptor, MediaDesc}]}, 761 CmdReq2 = #'CommandRequest'{command = {addReq, AmmReq2}}, 762 request(Mid, 10003, ?megaco_choose_context_id, [CmdReq, CmdReq2]). 763 764%%---------------------------------------------------------------------- 765%% 14. MG1 acknowledges the new Termination and fills in the Local IP 766%% address and UDP port. It also makes a choice for the codec based 767%% on the MGC preferences in Local. MG1 sets the RTP port to 2222. 768%% 769%% MEGACO/1 [124.124.124.222]:55555 770%% Reply = 10003 { 771%% Context = 2000 { 772%% Add = A4444, 773%% Add=A4445{ 774%% Media { 775%% Stream = 1 { 776%% Local { 777%% v=0 778%% c=IN IP4 124.124.124.222 779%% m=audio 2222 RTP/AVP 4 780%% a=ptime:30 781%% a=recvonly 782%% } ; RTP profile for G.723 is 4 783%% } 784%% } 785%% } 786%% } 787%% } 788%%---------------------------------------------------------------------- 789 790msg14() -> 791 msg14(?MG1_MID). 792msg14(Mid) -> 793 V = ?PP("v", "0"), 794 C = ?PP("c", "IN IP4 124.124.124.222"), 795 M = ?PP("m", "audio 2222 RTP/AVP 4"), 796 A = ?PP("a", "a=ptime:30"), 797 A2= ?PP("a", "recvonly"), 798 LD = #'LocalRemoteDescriptor'{propGrps = [[V, C, M, A, A2]]}, 799 Parms = #'StreamParms'{localDescriptor = LD}, 800 StreamDesc = #'StreamDescriptor'{streamID = 1, 801 streamParms = Parms}, 802 MediaDesc = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}}, 803 Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4444}]}, 804 Reply2 = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4445}], 805 terminationAudit = [{mediaDescriptor, MediaDesc}]}, 806 reply(Mid, 10003, 2000, [{addReply, Reply}, {addReply, Reply2}]). 807 808%%---------------------------------------------------------------------- 809%% 15. The MGC will now associate A5555 with a new Context on MG2, and 810%% establish an RTP Stream (i.e, A5556 will be assigned), SendReceive 811%% connection through to the originating user, User 1. The MGC also 812%% sets ring on A5555. 813%% 814%% MGC to MG2: 815%% MEGACO/1 [123.123.123.4]:55555 816%% Transaction = 50003 { 817%% Context = $ { 818%% Add = A5555 { Media { 819%% Stream = 1 { 820%% LocalControl {Mode = SendReceive} }}, 821%% Signals {al/ri} 822%% }, 823%% Add = $ {Media { 824%% Stream = 1 { 825%% LocalControl { 826%% Mode = SendReceive, 827%% nt/jit=40 ; in ms 828%% }, 829%% Local { 830%% v=0 831%% c=IN IP4 $ 832%% m=audio $ RTP/AVP 4 833%% a=ptime:30 834%% }, 835%% Remote { 836%% v=0 837%% c=IN IP4 124.124.124.222 838%% m=audio 2222 RTP/AVP 4 839%% a=ptime:30 840%% } ; RTP profile for G.723 is 4 841%% } 842%% } 843%% } 844%% } 845%% } 846%%---------------------------------------------------------------------- 847 848msg15() -> 849 msg15(?MGC_MID). 850msg15(Mid) -> 851 LCD = #'LocalControlDescriptor'{streamMode = sendRecv, 852 propertyParms = []}, 853 Parms = #'StreamParms'{localControlDescriptor = LCD}, 854 StreamDesc = #'StreamDescriptor'{streamID = 1, 855 streamParms = Parms}, 856 MediaDesc = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}}, 857 Signal = #'Signal'{signalName = "al/ri", 858 sigParList = []}, 859 AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A5555}], 860 descriptors = [{mediaDescriptor, MediaDesc}, 861 {signalsDescriptor, [{signal, Signal}]}]}, 862 CmdReq = #'CommandRequest'{command = {addReq, AmmReq}}, 863 Jit = ?PP("nt/jit", "40"), 864 LCD2 = #'LocalControlDescriptor'{streamMode = sendRecv, 865 propertyParms = [Jit]}, 866 V = ?PP("v", "0"), 867 C = ?PP("c", "IN IP4 $ "), 868 M = ?PP("m", "audio $ RTP/AVP 4"), 869 A = ?PP("a", "ptime:30"), 870 LD2 = #'LocalRemoteDescriptor'{propGrps = [[V, C, M, A]]}, 871 V2 = ?PP("v", "0"), 872 C2 = ?PP("c", "IN IP4 124.124.124.222"), 873 M2 = ?PP("m", "audio 2222 RTP/AVP 4"), 874 RD2 = #'LocalRemoteDescriptor'{propGrps = [[V2, C2, M2]]}, 875 Parms2 = #'StreamParms'{localControlDescriptor = LCD2, 876 localDescriptor = LD2, 877 remoteDescriptor = RD2}, 878 StreamDesc2 = #'StreamDescriptor'{streamID = 1, 879 streamParms = Parms2}, 880 MediaDesc2 = #'MediaDescriptor'{streams = {multiStream, [StreamDesc2]}}, 881 ChooseTid = #megaco_term_id{contains_wildcards = true, 882 id = [[?megaco_choose]]}, 883 AmmReq2 = #'AmmRequest'{terminationID = [ChooseTid], 884 descriptors = [{mediaDescriptor, MediaDesc2}]}, 885 CmdReq2 = #'CommandRequest'{command = {addReq, AmmReq2}}, 886 request(Mid, 50003, ?megaco_choose_context_id, [CmdReq, CmdReq2]). 887 888%%---------------------------------------------------------------------- 889%% 16. This is acknowledged. The stream port number is different from the 890%% control port number. In this case it is 1111 (in the SDP). 891%% 892%% MG2 to MGC: 893%% MEGACO/1 [124.124.124.222]:55555 894%% Reply = 50003 { 895%% Context = 5000 { 896%% Add = A5556{ 897%% Media { 898%% Stream = 1 { 899%% Local { 900%% v=0 901%% c=IN IP4 125.125.125.111 902%% m=audio 1111 RTP/AVP 4 903%% } 904%% } ; RTP profile for G723 is 4 905%% } 906%% } 907%% } 908%% } 909%%---------------------------------------------------------------------- 910 911msg16() -> 912 msg16(?MG2_MID). 913msg16(Mid) -> 914 V = ?PP("v", "0"), 915 C = ?PP("c", "IN IP4 125.125.125.111"), 916 M = ?PP("m", "audio 1111 RTP/AVP 4"), 917 LD = #'LocalRemoteDescriptor'{propGrps = [[V, C, M]]}, 918 Parms = #'StreamParms'{localDescriptor = LD}, 919 StreamDesc = #'StreamDescriptor'{streamID = 1, 920 streamParms = Parms}, 921 MediaDesc = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}}, 922 Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A5556}], 923 terminationAudit = [{mediaDescriptor, MediaDesc}]}, 924 reply(Mid, 50003, 5000, [{addReply, Reply}]). 925 926%%---------------------------------------------------------------------- 927%% 17. The above IPAddr and UDPport need to be given to MG1 now. 928%% 929%% MGC to MG1: 930%% MEGACO/1 [123.123.123.4]:55555 931%% Transaction = 10005 { 932%% Context = 2000 { 933%% Modify = A4444 { 934%% Signals {cg/rt} 935%% }, 936%% Modify = A4445 { 937%% Media { 938%% Stream = 1 { 939%% Remote { 940%% v=0 941%% c=IN IP4 125.125.125.111 942%% m=audio 1111 RTP/AVP 4 943%% } 944%% } ; RTP profile for G723 is 4 945%% } 946%% } 947%% } 948%% } 949%%---------------------------------------------------------------------- 950 951msg17a() -> 952 msg17a(?MGC_MID). 953msg17a(Mid) -> 954 Signal = #'Signal'{signalName = "cg/rt", sigParList = []}, 955 AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A4444}], 956 descriptors = [{signalsDescriptor, [{signal, Signal}]}]}, 957 CmdReq = #'CommandRequest'{command = {modReq, AmmReq}}, 958 959 V = ?PP("v", "0"), 960 C = ?PP("c", "IN IP4 125.125.125.111"), 961 M = ?PP("m", "audio 1111 RTP/AVP 4"), 962 RD2 = #'LocalRemoteDescriptor'{propGrps = [[V, C, M]]}, 963 Parms2 = #'StreamParms'{remoteDescriptor = RD2}, 964 StreamDesc2 = #'StreamDescriptor'{streamID = 1, 965 streamParms = Parms2}, 966 MediaDesc2 = #'MediaDescriptor'{streams = {multiStream, [StreamDesc2]}}, 967 AmmReq2 = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A4445}], 968 descriptors = [{mediaDescriptor, MediaDesc2}]}, 969 CmdReq2 = #'CommandRequest'{command = {modReq, AmmReq2}}, 970 request(Mid, 10005, 2000, [CmdReq, CmdReq2]). 971 972%%---------------------------------------------------------------------- 973%% MG1 to MGC: 974%% MEGACO/1 [124.124.124.222]:55555 975%% Reply = 10005 { 976%% Context = 2000 {Modify = A4444, Modify = A4445} 977%% } 978%%---------------------------------------------------------------------- 979 980msg17b() -> 981 msg17b(?MG1_MID). 982msg17b(Mid) -> 983 Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4444}]}, 984 Reply2 = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4445}]}, 985 reply(Mid, 10005, 2000, [{modReply, Reply}, {modReply, Reply2}]). 986 987%%---------------------------------------------------------------------- 988%% 18. The two gateways are now connected and User 1 hears the 989%% RingBack. The MG2 now waits until User2 picks up the receiver and 990%% then the two-way call is established. 991%% 992%% MG2 to MGC: 993%% MEGACO/1 [125.125.125.111]:55555 994%% Transaction = 50005 { 995%% Context = 5000 { 996%% Notify = A5555 {ObservedEvents =1234 { 997%% 19990729T22020002:al/of}} 998%% } 999%% } 1000%%---------------------------------------------------------------------- 1001 1002msg18a() -> 1003 msg18a(?MG2_MID). 1004msg18a(Mid) -> 1005 TimeStamp = #'TimeNotation'{date = "19990729", 1006 time = "22020002"}, 1007 Event = #'ObservedEvent'{eventName = "al/of", 1008 timeNotation = TimeStamp, 1009 eventParList = []}, 1010 Desc = #'ObservedEventsDescriptor'{requestId = 1234, 1011 observedEventLst = [Event]}, 1012 NotifyReq = #'NotifyRequest'{terminationID = [#megaco_term_id{id = ?A5555}], 1013 observedEventsDescriptor = Desc}, 1014 CmdReq = #'CommandRequest'{command = {notifyReq, NotifyReq}}, 1015 request(Mid, 50005, 5000, [CmdReq]). 1016 1017%%---------------------------------------------------------------------- 1018%% MGC to MG2: 1019%% MEGACO/1 [123.123.123.4]:55555 1020%% Reply = 50005 { 1021%% Context = - {Notify = A5555} 1022%% } 1023%%---------------------------------------------------------------------- 1024 1025msg18b() -> 1026 msg18b(?MGC_MID). 1027msg18b(Mid) -> 1028 Reply = #'NotifyReply'{terminationID = [#megaco_term_id{id = ?A5555}]}, 1029 reply(Mid, 50005, ?megaco_null_context_id, [{notifyReply, Reply}]). 1030 1031%%---------------------------------------------------------------------- 1032%% MGC to MG2: 1033%% MEGACO/1 [123.123.123.4]:55555 1034%% Transaction = 50006 { 1035%% Context = 5000 { 1036%% Modify = A5555 { 1037%% Events = 1235 {al/on}, 1038%% Signals { } ; to turn off ringing 1039%% } 1040%% } 1041%% } 1042%%---------------------------------------------------------------------- 1043 1044msg18c() -> 1045 msg18c(?MGC_MID). 1046msg18c(Mid) -> 1047 On = #'RequestedEvent'{pkgdName = "al/on", evParList = []}, 1048 EventsDesc = #'EventsDescriptor'{requestID = 1235, 1049 eventList = [On]}, 1050 AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A5555}], 1051 descriptors = [{eventsDescriptor, EventsDesc}, 1052 {signalsDescriptor, []}]}, 1053 CmdReq = #'CommandRequest'{command = {modReq, AmmReq}}, 1054 request(Mid, 50006, 5000, [CmdReq]). 1055 1056%%---------------------------------------------------------------------- 1057%% MG2 to MGC: 1058%% MEGACO/1 [125.125.125.111]:55555 1059%% Reply = 50006 { 1060%% Context = 5000 {Modify = A4445} 1061%% } 1062%%---------------------------------------------------------------------- 1063 1064msg18d() -> 1065 msg18d(?MG2_MID). 1066msg18d(Mid) -> 1067 Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4445}]}, 1068 reply(Mid, 50006, 5000, [{modReply, Reply}]). 1069 1070%%---------------------------------------------------------------------- 1071%% 19. Change mode on MG1 to SendReceive, and stop the ringback. 1072%% 1073%% MGC to MG1: 1074%% MEGACO/1 [123.123.123.4]:55555 1075%% Transaction = 10006 { 1076%% Context = 2000 { 1077%% Modify = A4445 { 1078%% Media { 1079%% Stream = 1 { 1080%% LocalControl { 1081%% Mode=SendReceive 1082%% } 1083%% } 1084%% } 1085%% }, 1086%% Modify = A4444 { 1087%% Signals { } 1088%% } 1089%% } 1090%% } 1091%%---------------------------------------------------------------------- 1092 1093msg19a() -> 1094 msg19a(?MGC_MID). 1095msg19a(Mid) -> 1096 LCD = #'LocalControlDescriptor'{streamMode = sendRecv, 1097 propertyParms = []}, 1098 Parms = #'StreamParms'{localControlDescriptor = LCD}, 1099 StreamDesc = #'StreamDescriptor'{streamID = 1, 1100 streamParms = Parms}, 1101 MediaDesc = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}}, 1102 AmmReq = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A4445}], 1103 descriptors = [{mediaDescriptor, MediaDesc}]}, 1104 CmdReq = #'CommandRequest'{command = {modReq, AmmReq}}, 1105 AmmReq2 = #'AmmRequest'{terminationID = [#megaco_term_id{id = ?A4444}], 1106 descriptors = [{signalsDescriptor, []}]}, 1107 CmdReq2 = #'CommandRequest'{command = {modReq, AmmReq2}}, 1108 request(Mid, 10006, 2000, [CmdReq, CmdReq2]). 1109 1110%%---------------------------------------------------------------------- 1111%% MG1 to MGC: 1112%% MEGACO/1 [124.124.124.222]:55555 1113%% Reply = 10006 { 1114%% Context = 2000 {Modify = A4445, Modify = A4444}} 1115%%---------------------------------------------------------------------- 1116 1117msg19b() -> 1118 msg19b(?MG1_MID). 1119msg19b(Mid) -> 1120 Reply = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4445}]}, 1121 Reply2= #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A4444}]}, 1122 reply(Mid, 10006, 2000, [{modReply, Reply}, {modReply, Reply2}]). 1123 1124%%---------------------------------------------------------------------- 1125%% 20. The MGC decides to Audit the RTP termination on MG2. 1126%% 1127%% MEGACO/1 [123.123.123.4]:55555 1128%% Transaction = 50007 { 1129%% Context = - {AuditValue = A5556{ 1130%% Audit{Media, DigitMap, Events, Signals, Packages, Statistics }} 1131%% } 1132%% } 1133%%---------------------------------------------------------------------- 1134 1135msg20() -> 1136 msg20(?MGC_MID). 1137msg20(Mid) -> 1138 Tokens = [mediaToken, eventsToken, signalsToken, 1139 digitMapToken, statsToken, packagesToken], 1140 AuditDesc = #'AuditDescriptor'{auditToken = Tokens}, 1141 Req = #'AuditRequest'{terminationID = #megaco_term_id{id = ?A5556}, 1142 auditDescriptor = AuditDesc}, 1143 CmdReq = #'CommandRequest'{command = {auditValueRequest, Req}}, 1144 request(Mid, 50007, ?megaco_null_context_id, [CmdReq]). 1145 1146%%---------------------------------------------------------------------- 1147%% 21. The MG2 replies. An RTP termination has no events nor signals, so 1148%% these are left out in the reply . 1149%% 1150%% MEGACO/1 [125.125.125.111]:55555 1151%% Reply = 50007 { 1152%% Context = - { 1153%% AuditValue = A5556 { 1154%% Media { 1155%% Stream = 1 { 1156%% LocalControl { Mode = SendReceive, 1157%% nt/jit=40 }, 1158%% Local { 1159%% v=0 1160%% c=IN IP4 125.125.125.111 1161%% m=audio 1111 RTP/AVP 4 1162%% a=ptime:30 1163%% }, 1164%% Remote { 1165%% v=0 1166%% c=IN IP4 124.124.124.222 1167%% m=audio 2222 RTP/AVP 4 1168%% a=ptime:30 1169%% } } }, 1170%% Packages {nt-1, rtp-1}, 1171%% Statistics { rtp/ps=1200, ; packets sent 1172%% nt/os=62300, ; octets sent 1173%% rtp/pr=700, ; packets received 1174%% nt/or=45100, ; octets received 1175%% rtp/pl=0.2, ; % packet loss 1176%% rtp/jit=20, 1177%% rtp/delay=40 } ; avg latency 1178%% } 1179%% } 1180%% } 1181%%---------------------------------------------------------------------- 1182 1183msg21() -> 1184 msg21(?MG2_MID). 1185msg21(Mid) -> 1186 Jit = ?PP("nt/jit", "40"), 1187 LCD = #'LocalControlDescriptor'{streamMode = sendRecv, 1188 propertyParms = [Jit]}, 1189 LDV = ?PP("v", "0"), 1190 LDC = ?PP("c", "IN IP4 125.125.125.111"), 1191 LDM = ?PP("m", "audio 1111 RTP/AVP 4"), 1192 LDA = ?PP("a", "ptime:30"), 1193 LD = #'LocalRemoteDescriptor'{propGrps = [[LDV, LDC, LDM, LDA]]}, 1194 RDV = ?PP("v", "0"), 1195 RDC = ?PP("c", "IN IP4 124.124.124.222"), 1196 RDM = ?PP("m", "audio 2222 RTP/AVP 4"), 1197 RDA = ?PP("a", "ptime:30"), 1198 RD = #'LocalRemoteDescriptor'{propGrps = [[RDV, RDC, RDM, RDA]]}, 1199 StreamParms = #'StreamParms'{localControlDescriptor = LCD, 1200 localDescriptor = LD, 1201 remoteDescriptor = RD}, 1202 StreamDesc = #'StreamDescriptor'{streamID = 1, 1203 streamParms = StreamParms}, 1204 Media = #'MediaDescriptor'{streams = {multiStream, [StreamDesc]}}, 1205 PackagesItem = #'PackagesItem'{packageName = "nt", 1206 packageVersion = 1}, 1207 PackagesItem2 = #'PackagesItem'{packageName = "rtp", 1208 packageVersion = 1}, 1209 Stat = ?SP("rtp/ps","1200"), 1210 Stat2 = ?SP("nt/os","62300"), 1211 Stat3 = ?SP("rtp/pr","700"), 1212 Stat4 = ?SP("nt/or","45100"), 1213 Stat5 = ?SP("rtp/pl","0.2"), 1214 Stat6 = ?SP("rtp/jit","20"), 1215 Stat7 = ?SP("rtp/delay","40"), 1216 Statistics = [Stat, Stat2, Stat3, Stat4, Stat5, Stat6, Stat7], 1217 Audits = [{mediaDescriptor, Media}, 1218 {packagesDescriptor, [PackagesItem, PackagesItem2]}, 1219 {statisticsDescriptor, Statistics}], 1220 Reply = {auditResult, #'AuditResult'{terminationID = #megaco_term_id{id = ?A5556}, 1221 terminationAuditResult = Audits}}, 1222 reply(Mid, 50007, ?megaco_null_context_id, [{auditValueReply, Reply}]). 1223 1224%%---------------------------------------------------------------------- 1225%% 22. When the MGC receives an onhook signal from one of the MGs, it 1226%% brings down the call. In this example, the user at MG2 hangs up first. 1227%% 1228%% MG2 to MGC: 1229%% MEGACO/1 [125.125.125.111]:55555 1230%% Transaction = 50008 { 1231%% Context = 5000 { 1232%% Notify = A5555 {ObservedEvents =1235 { 1233%% 19990729T24020002:al/on} 1234%% } 1235%% } 1236%% } 1237%%---------------------------------------------------------------------- 1238 1239msg22a() -> 1240 msg22a(?MG2_MID). 1241msg22a(Mid) -> 1242 TimeStamp = #'TimeNotation'{date = "19990729", 1243 time = "24020002"}, 1244 Event = #'ObservedEvent'{eventName = "al/on", 1245 timeNotation = TimeStamp, 1246 eventParList = []}, 1247 Desc = #'ObservedEventsDescriptor'{requestId = 1235, 1248 observedEventLst = [Event]}, 1249 NotifyReq = #'NotifyRequest'{terminationID = [#megaco_term_id{id = ?A5555}], 1250 observedEventsDescriptor = Desc}, 1251 CmdReq = #'CommandRequest'{command = {notifyReq, NotifyReq}}, 1252 request(Mid, 50008, 5000, [CmdReq]). 1253 1254%%---------------------------------------------------------------------- 1255%% MGC to MG2: 1256%% MEGACO/1 [123.123.123.4]:55555 1257%% Reply = 50008 { 1258%% Context = - {Notify = A5555} 1259%% } 1260%%---------------------------------------------------------------------- 1261 1262msg22b() -> 1263 msg22b(?MGC_MID). 1264msg22b(Mid) -> 1265 Reply = #'NotifyReply'{terminationID = [#megaco_term_id{id = ?A5555}]}, 1266 reply(Mid, 50008, ?megaco_null_context_id, [{notifyReply, Reply}]). 1267 1268%%---------------------------------------------------------------------- 1269%% 23. The MGC now sends both MGs a Subtract to take down the call. Only 1270%% the subtracts to MG2 are shown here. Each termination has its own 1271%% set of statistics that it gathers. An MGC may not need to request 1272%% both to be returned. A5555 is a physical termination, and A5556 is 1273%% an RTP termination. 1274%% 1275%% MGC to MG2: 1276%% 1277%% MEGACO/1 [123.123.123.4]:55555 1278%% Transaction = 50009 { 1279%% Context = 5000 { 1280%% Subtract = A5555 {Audit{Statistics}}, 1281%% Subtract = A5556 {Audit{Statistics}} 1282%% } 1283%% } 1284%%---------------------------------------------------------------------- 1285 1286msg23a() -> 1287 msg23a(?MGC_MID). 1288msg23a(Mid) -> 1289 CommonAuditDesc = #'AuditDescriptor'{auditToken = [statsToken]}, 1290 SubReq = #'SubtractRequest'{terminationID = [#megaco_term_id{id = ?A5555}], 1291 auditDescriptor = CommonAuditDesc}, 1292 SubReq2 = #'SubtractRequest'{terminationID = [#megaco_term_id{id = ?A5556}], 1293 auditDescriptor = CommonAuditDesc}, 1294 CmdReq = #'CommandRequest'{command = {subtractReq, SubReq}}, 1295 CmdReq2 = #'CommandRequest'{command = {subtractReq, SubReq2}}, 1296 request(Mid, 50009, 5000, [CmdReq, CmdReq2]). 1297%% 1298%%---------------------------------------------------------------------- 1299%% MG2 to MGC: 1300%% 1301%% MEGACO/1 [125.125.125.111]:55555 1302%% Reply = 50009 { 1303%% Context = 5000 { 1304%% Subtract = A5555 { 1305%% Statistics { 1306%% nt/os=45123, ; Octets Sent 1307%% nt/dur=40 ; in seconds 1308%% } 1309%% }, 1310%% Subtract = A5556 { 1311%% Statistics { 1312%% rtp/ps=1245, ; packets sent 1313%% nt/os=62345, ; octets sent 1314%% rtp/pr=780, ; packets received 1315%% nt/or=45123, ; octets received 1316%% rtp/pl=10, ; % packets lost 1317%% rtp/jit=27, 1318%% rtp/delay=48 ; average latency 1319%% } 1320%% } 1321%% } 1322%% } 1323%%---------------------------------------------------------------------- 1324 1325msg23b() -> 1326 msg23b(?MG2_MID). 1327msg23b(Mid) -> 1328 Stat11 = ?SP("nt/os","45123"), 1329 Stat12 = ?SP("nt/dur", "40"), 1330 Stats1 = [Stat11, Stat12], 1331 Reply1 = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A5555}], 1332 terminationAudit = [{statisticsDescriptor, Stats1}]}, 1333 Stat21 = ?SP("rtp/ps","1245"), 1334 Stat22 = ?SP("nt/os", "62345"), 1335 Stat23 = ?SP("rtp/pr", "780"), 1336 Stat24 = ?SP("nt/or", "45123"), 1337 Stat25 = ?SP("rtp/pl", "10"), 1338 Stat26 = ?SP("rtp/jit", "27"), 1339 Stat27 = ?SP("rtp/delay","48"), 1340 Stats2 = [Stat21, Stat22, Stat23, Stat24, Stat25, Stat26, Stat27], 1341 Reply2 = #'AmmsReply'{terminationID = [#megaco_term_id{id = ?A5556}], 1342 terminationAudit = [{statisticsDescriptor, Stats2}]}, 1343 reply(Mid, 50009, 5000, [{subtractReply, Reply1}, {subtractReply, Reply2}]). 1344 1345%%---------------------------------------------------------------------- 1346%% 24. The MGC now sets up both MG1 and MG2 to be ready to detect the 1347%% next off-hook event. See step 1. Note that this could be the 1348%% default state of a termination in the null context, and if this 1349%% were the case, no message need be sent from the MGC to the 1350%% MG. Once a termination returns to the null context, it goes back 1351%% to the default termination values for that termination. 1352%%---------------------------------------------------------------------- 1353 1354%% BUGBUG: Example missing in spec 1355 1356%%---------------------------------------------------------------------- 1357%% Testing 1358%%---------------------------------------------------------------------- 1359 1360messages() -> 1361 [{Slogan, catch ?MODULE:Slogan()} || Slogan <- names()]. 1362 1363encoders() -> 1364 [ 1365 {megaco_pretty_text_encoder, [], []}, 1366 {megaco_compact_text_encoder, [], []}, 1367 {megaco_binary_encoder, [], [native]}, 1368 {megaco_ber_encoder, [], [native]}, 1369 {megaco_per_encoder, [], [native]}, 1370 {megaco_erl_dist_encoder, [], []}, 1371 {megaco_erl_dist_encoder, [compressed], [compressed]} 1372 ]. 1373 1374 1375pretty_mod({Mod, Opt, _Opt2}) -> 1376 case Mod of 1377 megaco_pretty_text_encoder when Opt == [flex] -> pretty_flex; 1378 megaco_compact_text_encoder when Opt == [flex] -> compact_flex; 1379 megaco_pretty_text_encoder -> pretty_text; 1380 megaco_compact_text_encoder -> compact_text; 1381 megaco_binary_encoder -> asn1_ber; 1382 megaco_ber_encoder -> asn1_ber_old; 1383 megaco_per_encoder -> asn1_per; 1384 megaco_erl_dist_encoder when Opt == [] -> standard_erl; 1385 megaco_erl_dist_encoder when Opt == [compressed] -> compressed_erl; 1386 Ugly -> Ugly 1387 end. 1388 1389%%---------------------------------------------------------------------- 1390%% Run specific encoder for all test cases 1391%%---------------------------------------------------------------------- 1392 1393pretty_text() -> 1394 Default = [], 1395 Encoder = {megaco_pretty_text_encoder, Default, Default}, 1396 All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()], 1397 compute_res(All). 1398 1399compact_text() -> 1400 Default = [], 1401 Encoder = {megaco_compact_text_encoder, Default, Default}, 1402 All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()], 1403 compute_res(All). 1404 1405pretty_flex() -> 1406 Default = [flex], 1407 Encoder = {megaco_pretty_text_encoder, Default, Default}, 1408 All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()], 1409 compute_res(All). 1410 1411compact_flex() -> 1412 Default = [flex], 1413 Encoder = {megaco_compact_text_encoder, Default, Default}, 1414 All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()], 1415 compute_res(All). 1416 1417bin() -> 1418 Default = [], 1419 Native = [native], 1420 Encoder = {megaco_binary_encoder, Default, Native}, 1421 All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()], 1422 compute_res(All). 1423 1424asn1_ber() -> 1425 Default = [], 1426 Native = [native], 1427 Encoder = {megaco_ber_encoder, Default, Native}, 1428 All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()], 1429 compute_res(All). 1430 1431asn1_per() -> 1432 Default = [], 1433 Native = [native], 1434 Encoder = {megaco_per_encoder, Default, Native}, 1435 All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()], 1436 compute_res(All). 1437 1438standard_erl() -> 1439 Config = [], 1440 Encoder = {megaco_erl_dist_encoder, Config, Config}, 1441 All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()], 1442 compute_res(All). 1443 1444compressed_erl() -> 1445 Config = [compressed], 1446 Encoder = {megaco_erl_dist_encoder, Config, Config}, 1447 All = [encode(Slogan, Msg, Encoder) || {Slogan, Msg} <- messages()], 1448 compute_res(All). 1449 1450encode(Slogan, DecodedMsg, {Mod, Opt, _Opt2} = _Encoder) -> 1451 Main = "==================================================", 1452 Sub = "--------------------------------------------------", 1453 case catch Mod:encode_message(Opt, DecodedMsg) of 1454 {ok, EncodedMsg} when is_binary(EncodedMsg) -> 1455 Sz = size(EncodedMsg), 1456 ok = io:format("~w ~s ~w bytes~n", 1457 [Slogan, Main, Sz]), 1458 catch io:format("~n~s~n", [(catch binary_to_list(EncodedMsg))]), 1459 case catch Mod:decode_message(Opt, EncodedMsg) of 1460 {ok, ReDecodedMsg} -> 1461 fmt(Slogan, DecodedMsg, ReDecodedMsg); 1462 {error, {Line, _, Reason}} when is_integer(Line)-> 1463 ?ERROR([{Slogan, Line, decode_failed}, {error, Reason}, {encoded, EncodedMsg}, DecodedMsg]), 1464 io:format("~n~w <ERROR> #~w: ~w~n", 1465 [Slogan, Line, Reason]); 1466 Other -> 1467 ?ERROR([{Slogan, 0, decode_failed}, Other, {encoded, EncodedMsg}, DecodedMsg]), 1468 io:format("~n~w <ERROR> ~w~n", [Slogan, Other]) 1469 end, 1470 Sz; 1471 Other -> 1472 ?ERROR([{Slogan, encode_failed}, Other, DecodedMsg]), 1473 ok = io:format("~w ~s~n~p~n<ERROR> ~s~n~p~n", 1474 [Slogan, Main, DecodedMsg, Sub, Other]), 1475 {Slogan, {encode_message, Other}} 1476 end. 1477 1478fmt(_Slogan, Msg, Msg) -> 1479 ok; 1480fmt(Slogan, {'MegacoMessage', A, {'Message', V, MID, {transactions, [{T, Old}]}}}, 1481 {'MegacoMessage', A, {'Message', V, MID, {transactions, [{T, New}]}}}) -> 1482 fmt(Slogan, Old, New); 1483fmt(Slogan, Old, New) -> 1484 PrettyOld = lists:flatten(io_lib:format("~p", [Old])), 1485 PrettyNew = lists:flatten(io_lib:format("~p", [New])), 1486 fmt_diff(Slogan, Old, New, PrettyOld, PrettyNew, []). 1487 1488fmt_diff(Slogan, Old, New, [H | OldRest], [H | NewRest], Common) -> 1489 fmt_diff(Slogan, Old, New, OldRest, NewRest, [H | Common]); 1490fmt_diff(Slogan, Old, New, OldRest, NewRest, Common) -> 1491 RevCommon = lists:reverse(Common), 1492 ?ERROR([{Slogan, decode_mismatch}, {old, Old}, {new, New}]), 1493 Sub = "--------------------------------------------------", 1494 io:format("~n~w COMMON ~s~n~s~n", [Slogan, Sub, RevCommon]), 1495 io:format("~n~w OLD ~s~n~s~n", [Slogan, Sub, OldRest]), 1496 io:format("~n~w NEW ~s~n~s~n", [Slogan, Sub, NewRest]). 1497 1498compute_res(All) -> 1499 compute_res(All, [], 0). 1500 1501compute_res([H | T], Bad, Sum) when is_integer(H) -> 1502 compute_res(T, Bad, Sum + H); 1503compute_res([H | T], Bad, Sum) -> 1504 compute_res(T, [H | Bad], Sum); 1505compute_res([], Bad, Sum) -> 1506 ok = io:format("#bytes: ~w; errors: ~p~n", [Sum, Bad]). 1507 1508%%---------------------------------------------------------------------- 1509%% Compute sizes of encoded messages 1510%%---------------------------------------------------------------------- 1511 1512msg_sizes() -> 1513 Encoders = encoders(), 1514 msg_sizes(Encoders). 1515 1516%% Returns a list of {MessageSlogan, MessageSizes} where 1517%% MessageSizes is the result from msg_sizes/2 1518msg_sizes(Encoders) -> 1519 [{S, msg_sizes(Msg, Encoders)} || {S, Msg} <- messages()]. 1520 1521%% Returns a list of {Encoder, Res} tuples 1522%% where Res either is the message size (integer) 1523%% or an error atom 1524msg_sizes(DecodedMsg, Encoders) -> 1525 [abs_msg_size(DecodedMsg, E) || E <- Encoders]. 1526 1527abs_msg_size(DecodedMsg, {Mod, Opt, _Opt2} = Encoder) -> 1528 case catch Mod:encode_message(Opt, DecodedMsg) of 1529 {ok, EncodedMsg} when is_binary(EncodedMsg) -> 1530 {Encoder, size(EncodedMsg)}; 1531 Error -> 1532 {Encoder, {bad_encoder, Error}} 1533 end. 1534 1535%%---------------------------------------------------------------------- 1536%% Compute time for encoding messages 1537%%---------------------------------------------------------------------- 1538 1539encoding_times() -> 1540 Encoders = encoders(), 1541 encoding_times(Encoders). 1542 1543%% Returns a list of {MessageSlogan, EncodingTimes} where 1544%% EncodingTimes is the result from encoding_times/2 1545encoding_times(Encoders) -> 1546 [{Slogan, encoding_times(Msg, Encoders)} || {Slogan, Msg} <- messages()]. 1547 1548%% Returns a list of {Encoder, Res} tuples 1549%% where Res either is the encoding time (integer) 1550%% or an error atom 1551encoding_times(DecodedMsg, Encoders) -> 1552 [{E, encoding_time(encoding_msg(DecodedMsg, E))} || E <- Encoders]. 1553 1554encoding_msg(DecodedMsg, {Mod, Opt, Opt2} = Encoder) -> 1555 {ok, EncodedMsg} = Mod:encode_message(Opt, DecodedMsg), 1556 {ok, DecodedMsg2} = Mod:decode_message(Opt2, EncodedMsg), 1557 {Encoder, DecodedMsg2}. 1558 1559encoding_time({{Mod, _Opt, Opt2} = Encoder, DecodedMsg}) -> 1560 meter(fun() -> {ok, _} = Mod:encode_message(Opt2, DecodedMsg) end, Encoder). 1561 1562%%---------------------------------------------------------------------- 1563%% Compute time for decoding messages 1564%%---------------------------------------------------------------------- 1565 1566decoding_times() -> 1567 Decoders = encoders(), 1568 decoding_times(Decoders). 1569 1570%% Returns a list of {MessageSlogan, DecodingTimes} where 1571%% DecodingTimes is the result from decoding_times/2 1572decoding_times(Decoders) -> 1573 [{Slogan, decoding_times(Msg, Decoders)} || {Slogan, Msg} <- messages()]. 1574 1575%% Returns a list of {Decoder, Res} tuples 1576%% where Res either is the decoding time (integer) 1577%% or an error atom 1578decoding_times(DecodedMsg, Encoders) -> 1579 [{E, decoding_time(decoding_msg(DecodedMsg, E))} || E <- Encoders]. 1580 1581decoding_msg(DecodedMsg, {Mod, Opt, _Opt2} = Encoder) -> 1582 {ok, EncodedMsg} = Mod:encode_message(Opt, DecodedMsg), 1583 {Encoder, EncodedMsg}. 1584 1585decoding_time({{Mod, _Opt, Opt2} = Encoder, EncodedMsg}) -> 1586 meter(fun() -> {ok, _} = Mod:decode_message(Opt2, EncodedMsg) end, Encoder). 1587 1588%%---------------------------------------------------------------------- 1589%%---------------------------------------------------------------------- 1590 1591coding_times() -> 1592 Coders = encoders(), 1593 coding_times(Coders). 1594 1595%% Returns a list of {MessageSlogan, DecodingTimes} where 1596%% DecodingTimes is the result from decoding_times/2 1597coding_times(Coders) -> 1598 [{Slogan, coding_times(Msg, Coders)} || {Slogan, Msg} <- messages()]. 1599 1600%% Returns a list of {Decoder, Res} tuples 1601%% where Res either is the decoding time (integer) 1602%% or an error atom 1603coding_times(DecodedMsg, Coders) -> 1604 [{E, coding_time(coding_msg(DecodedMsg, E))} || E <- Coders]. 1605 1606coding_msg(DecodedMsg, {Mod, Opt, _Opt2} = Encoder) -> 1607 {ok, EncodedMsg} = Mod:encode_message(Opt, DecodedMsg), 1608 {Encoder, EncodedMsg}. 1609 1610coding_time({{Mod, _Opt, Opt2} = Encoder, EncodedMsg}) -> 1611 Fun = fun() -> 1612 {ok, DecodedMsg} = Mod:decode_message(Opt2, EncodedMsg), 1613 {ok, _} = Mod:encode_message(Opt2, DecodedMsg) 1614 end, 1615 meter(Fun, Encoder). 1616 1617%%---------------------------------------------------------------------- 1618%% Return size statistics as term 1619%%---------------------------------------------------------------------- 1620 1621size_stat() -> 1622 Encoders = encoders(), 1623 MsgSizes = msg_sizes(Encoders), 1624 stat(Encoders, MsgSizes). 1625 1626%%---------------------------------------------------------------------- 1627%%---------------------------------------------------------------------- 1628 1629gnuplot_gif() -> 1630 [ 1631 {size, gnuplot_size_gif()}, 1632 {encoding, gnuplot_enc_time_gif()}, 1633 {decoding, gnuplot_dec_time_gif()}, 1634 {coding, gnuplot_code_time_gif()} 1635 ]. 1636 1637%%---------------------------------------------------------------------- 1638%% Generate GIF picture from size statistics with gnuplot 1639%%---------------------------------------------------------------------- 1640 1641gnuplot_size_gif() -> 1642 {ok, _Cwd} = file:get_cwd(), 1643 TmpDir = "megaco_encoded_size.tmp", 1644 GifFile = "megaco_encoded_size.gif", 1645 Header = 1646 ["set title \"Size comparison of Megaco/H.248 encoding formats\"\n", 1647 "set timestamp top\n", 1648 "set terminal gif\n", 1649 "set xlabel \"Test cases from Appendix A\"\n", 1650 "set ylabel \"Message size in bytes\"\n", 1651 "set rmargin 10\n", 1652 "set key left top Left\n", 1653 "set output \"", GifFile, "\"\n\n"], 1654 Encoders = encoders(), 1655 Stat = msg_sizes(Encoders), 1656 BatchFile = gnuplot_dir(TmpDir, Header, Encoders, Stat), 1657 Cmd = "cd " ++ TmpDir ++ "; gnuplot " ++ BatchFile, 1658 os:cmd(Cmd), 1659 ok = io:format("~n~s~nxv ~s~n", [Cmd, filename:join([TmpDir, GifFile])]), 1660 stat(Encoders, Stat). 1661 1662%%---------------------------------------------------------------------- 1663%% Return encoding time statistics as term 1664%%---------------------------------------------------------------------- 1665 1666encoding_times_stat() -> 1667 Encoders = encoders(), 1668 EncodingTimes = encoding_times(Encoders), 1669 stat(Encoders, EncodingTimes). 1670 1671%%---------------------------------------------------------------------- 1672%% Return encoding time statistics as term 1673%%---------------------------------------------------------------------- 1674 1675decoding_times_stat() -> 1676 Encoders = encoders(), 1677 DecodingTimes = decoding_times(Encoders), 1678 stat(Encoders, DecodingTimes). 1679 1680%%---------------------------------------------------------------------- 1681%% Return encoding time statistics as term 1682%%---------------------------------------------------------------------- 1683 1684coding_times_stat() -> 1685 Encoders = encoders(), 1686 CodingTimes = coding_times(Encoders), 1687 stat(Encoders, CodingTimes). 1688 1689%%---------------------------------------------------------------------- 1690%% Generate GIF picture from encoding time statistics with gnuplot 1691%%---------------------------------------------------------------------- 1692 1693gnuplot_enc_time_gif() -> 1694 {ok, _Cwd} = file:get_cwd(), 1695 TmpDir = "megaco_encoding_time.tmp", 1696 GifFile = "megaco_encoding_time.gif", 1697 Header = 1698 ["set title \"Encoding time comparison of Megaco/H.248 encoding formats\"\n", 1699 "set timestamp top\n", 1700 "set terminal gif\n", 1701 "set xlabel \"Test cases from Appendix A\"\n", 1702 "set ylabel \"Time for encoding in micro seconds\"\n", 1703 "set rmargin 10\n", 1704 "set key left top Left\n", 1705 "set output \"", GifFile, "\"\n\n"], 1706 Encoders = encoders(), 1707 Stat = encoding_times(Encoders), 1708 BatchFile = gnuplot_dir(TmpDir, Header, Encoders, Stat), 1709 Cmd = "cd " ++ TmpDir ++ "; gnuplot " ++ BatchFile, 1710 os:cmd(Cmd), 1711 ok = io:format("~n~s~nxv ~s~n", [Cmd, filename:join([TmpDir, GifFile])]), 1712 stat(Encoders, Stat). 1713 1714%%---------------------------------------------------------------------- 1715%% Generate GIF picture from decoding time statistics with gnuplot 1716%%---------------------------------------------------------------------- 1717 1718gnuplot_dec_time_gif() -> 1719 {ok, _Cwd} = file:get_cwd(), 1720 TmpDir = "megaco_decoding_time.tmp", 1721 GifFile = "megaco_decoding_time.gif", 1722 Header = 1723 ["set title \"Decoding time comparison of Megaco/H.248 encoding formats\"\n", 1724 "set timestamp top\n", 1725 "set terminal gif\n", 1726 "set xlabel \"Test cases from Appendix A\"\n", 1727 "set ylabel \"Time for decoding in micro seconds\"\n", 1728 "set rmargin 10\n", 1729 "set key left top Left\n", 1730 "set output \"", GifFile, "\"\n\n"], 1731 Encoders = encoders(), 1732 Stat = decoding_times(Encoders), 1733 BatchFile = gnuplot_dir(TmpDir, Header, Encoders, Stat), 1734 Cmd = "cd " ++ TmpDir ++ "; gnuplot " ++ BatchFile, 1735 os:cmd(Cmd), 1736 ok = io:format("~n~s~nxv ~s~n", [Cmd, filename:join([TmpDir, GifFile])]), 1737 stat(Encoders, Stat). 1738 1739%%---------------------------------------------------------------------- 1740%% Generate GIF picture from decoding time statistics with gnuplot 1741%%---------------------------------------------------------------------- 1742 1743gnuplot_code_time_gif() -> 1744 {ok, _Cwd} = file:get_cwd(), 1745 TmpDir = "megaco_coding_time.tmp", 1746 GifFile = "megaco_coding_time.gif", 1747 Header = 1748 ["set title \"Encoding + decoding time comparison of Megaco/H.248 encoding formats\"\n", 1749 "set timestamp top\n", 1750 "set terminal gif\n", 1751 "set xlabel \"Test cases from Appendix A\"\n", 1752 "set ylabel \"Time for encoding+decoding in micro seconds\"\n", 1753 "set rmargin 10\n", 1754 "set key left top Left\n", 1755 "set output \"", GifFile, "\"\n\n"], 1756 Encoders = encoders(), 1757 Stat = coding_times(Encoders), 1758 BatchFile = gnuplot_dir(TmpDir, Header, Encoders, Stat), 1759 Cmd = "cd " ++ TmpDir ++ "; gnuplot " ++ BatchFile, 1760 os:cmd(Cmd), 1761 ok = io:format("~n~s~nxv ~s~n", [Cmd, filename:join([TmpDir, GifFile])]), 1762 stat(Encoders, Stat). 1763 1764%%---------------------------------------------------------------------- 1765%% Encode asn.1 messages 1766%%---------------------------------------------------------------------- 1767 1768gen_byte_msg(Msg, {Mod, Opt, _Opt2} = _Encoder) -> 1769 {ok, EncodedMsg} = Mod:encode_message(Opt, Msg), 1770 EncodedMsg. 1771 1772%%---------------------------------------------------------------------- 1773%% Gen the C header file content as a binary 1774%%---------------------------------------------------------------------- 1775 1776gen_header_file_binary([]) -> 1777 ok; 1778gen_header_file_binary([{S, B}| Rest]) -> 1779 file:write_file(atom_to_list(S), B), 1780 gen_header_file_binary(Rest). 1781 1782%%---------------------------------------------------------------------- 1783%% Generate headerfile for asn.1 BER test in C 1784%%---------------------------------------------------------------------- 1785 1786gen_ber_header() -> 1787 Encoder = {megaco_ber_encoder, [], []}, 1788 L = [{S, gen_byte_msg(Msg, Encoder)} || {S, Msg} <- messages()], 1789 gen_header_file_binary(L). 1790 1791%%---------------------------------------------------------------------- 1792%% Generate headerfile for asn.1 BER test in C 1793%%---------------------------------------------------------------------- 1794gen_ber_bin_header() -> 1795 Encoder = {megaco_ber_encoder, [], []}, 1796 L = [{S, gen_byte_msg(Msg, Encoder)} || {S, Msg} <- messages()], 1797 gen_header_file_binary(L). 1798 1799%%---------------------------------------------------------------------- 1800%% Generate headerfile for asn.1 PER test in C 1801%%---------------------------------------------------------------------- 1802gen_per_header() -> 1803 Encoder = {megaco_per_encoder, [], []}, 1804 L = [{S, gen_byte_msg(Msg, Encoder)} || {S, Msg} <- messages()], 1805 gen_header_file_binary(L). 1806 1807%%---------------------------------------------------------------------- 1808%% Execute a fun a number of times and return avg in millis 1809%%---------------------------------------------------------------------- 1810 1811meter(Fun, Encoder) -> 1812 MaxTime = timer:seconds(5), 1813 Rep = 2, 1814 M = pretty_mod(Encoder), 1815 io:format("~p:\t", [M]), 1816 Times = [single_meter(Fun, MaxTime, {M, N}) || N <- lists:seq(1, Rep)], 1817 Min = lists:min(Times), 1818 Max = lists:max(Times), 1819 Median = lists:nth((Rep + 1) div 2, lists:sort(Times)), 1820 io:format("(median=~p, diff=~p)~n", [Median, Max - Min]), 1821 Median. 1822 1823single_meter(Fun, MaxTime, Tag) -> 1824 Pid = spawn_link(?MODULE, single_meter, [self(), Fun, MaxTime, Tag]), 1825 receive 1826 {meter, Pid, Time} -> 1827 io:format("~p \t", [Time]), 1828 Time; 1829 {'EXIT', Pid, Reason} -> 1830 {bad_result, Reason} 1831 end. 1832 1833single_meter(Parent, Fun, Expected, _Tag) -> 1834 erlang:statistics(runtime), 1835 erlang:send_after(Expected, self(), return_count), 1836 Count = count(Fun, 1), 1837 {_, Actual} = erlang:statistics(runtime), 1838 %% Diff = Actual - Expected, 1839 Micros = trunc((Actual / Count) * 1000), 1840 Parent ! {meter, self(), Micros}, 1841 unlink(Parent), 1842 exit(normal). 1843 1844count(Fun, N) -> 1845 Fun(), 1846 receive 1847 return_count -> 1848 N 1849 after 0 -> 1850 count(Fun, N + 1) 1851 end. 1852 1853stat(Encoders, Stat) -> 1854 Fun = 1855 fun({_Mod, _Opt, _Opt2} = E) -> 1856 List = lists:flatten([[Val || {E2, Val} <- Info, E2 == E] || 1857 {_Slogan, Info} <- Stat]), 1858 Max = lists:max(List), 1859 Min = lists:min(List), 1860 case catch lists:sum(List) of 1861 {'EXIT', _} -> 1862 {E, bad, List}; 1863 Sum when is_integer(Sum) -> 1864 N = length(List), 1865 {E, [{min, Min}, {avg, Sum div N}, {max, Max}]} 1866 end 1867 end, 1868 lists:map(Fun, Encoders). 1869 1870gnuplot_dir(Dir, Header, Encoders, Stat) -> 1871 file:make_dir(Dir), 1872 {Names, Arrows} = gnuplot_data(Encoders, Dir, Stat, 1, [], []), 1873 [H | T] = [lists:concat(["\"", N, "\" with linespoints"]) || N <- Names], 1874 Plots = [[", ", Plot] || Plot <- T], 1875 IoList = [Header, Arrows, "\nplot ", H, Plots, "\n\nshow output\n"], 1876 Bin = list_to_binary(IoList), 1877 BatchFile = "gnuplot.batch", 1878 file:write_file(filename:join(Dir, BatchFile), Bin), 1879 BatchFile. 1880 1881gnuplot_data([], _Dir, _Stat, _Pos, Names, Arrows) -> 1882 {lists:reverse(Names), lists:reverse(Arrows)}; 1883gnuplot_data([{Mod, _Opt, _Opt2} = E | Encoders], Dir, Stat, Pos, Names, Arrows) -> 1884 Plot = fun({Msg, List}, {N, AccSz}) -> 1885 {value, {_, Sz}} = lists:keysearch(E, 1, List), 1886 ActualSz = 1887 if 1888 is_integer(Sz) -> 1889 Sz; 1890 true -> 1891 ok = io:format("<ERROR> ~p(~p) -> ~p~n", 1892 [Mod, Msg, Sz]), 1893 0 1894 end, 1895 Acc = {N + 1, [ActualSz | AccSz]}, 1896 {lists:concat([N + 1, " ", ActualSz, "\n"]), Acc} 1897 end, 1898 {Data, {N, Sizes}} = lists:mapfoldl(Plot, {0, []}, Stat), 1899 Min = lists:min(Sizes), 1900 Max = lists:max(Sizes), 1901 Sum = lists:sum(Sizes), 1902 Avg = Sum div N, 1903 Len = length(Stat), 1904 Pretty = pretty_mod(E), 1905 Name = lists:concat([Pretty, " (", Min, ",", Avg, ",", Max, ")"]), 1906 file:write_file(filename:join(Dir, Name), list_to_binary(Data)), 1907 %%"plot \"-\" title \"", E, "\" with linespoints\n", Data, "e\n", 1908 Arrow = 1909 lists:concat(["set arrow from 1,", Avg, 1910 " to ", Len, ", ", Avg, 1911 " nohead lt ", Pos, "\n", 1912 "set label \" ", Avg, " (avg)\" at ", Len, ",", Avg + 10, "\n"]), 1913 gnuplot_data(Encoders, Dir, Stat, Pos + 1, [Name | Names], [Arrow | Arrows]). 1914 1915 1916%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1917 1918p(F, A) -> 1919 io:format("*** [~s] ***" 1920 "~n " ++ F ++ "~n", 1921 [?FTS() | A]). 1922 1923