diff options
Diffstat (limited to 'lib/megaco/examples/simple/megaco_simple_mg.erl')
-rw-r--r-- | lib/megaco/examples/simple/megaco_simple_mg.erl | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/lib/megaco/examples/simple/megaco_simple_mg.erl b/lib/megaco/examples/simple/megaco_simple_mg.erl new file mode 100644 index 0000000000..95efaf5df3 --- /dev/null +++ b/lib/megaco/examples/simple/megaco_simple_mg.erl @@ -0,0 +1,437 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-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: Simple example of an MG +%% +%% Example usage: +%% +%% cd megaco/examples/simple +%% erl -pa ../../../megaco/ebin -s megaco_filter -s megaco +%% megaco_simple_mg:start(). +%%---------------------------------------------------------------------- + +-module(megaco_simple_mg). + +-behaviour(megaco_user). + +-export([ + start_batch/0, start_batch/1, init_batch/4, + start/0, start/3, + start/4, %% ???????????????????????? + stop/0, stop/1, + start_tcp_text/2, start_tcp_binary/2, + start_udp_text/2, start_udp_binary/2 + ]). + +-export([ + handle_connect/2, + handle_disconnect/3, + handle_syntax_error/3, + handle_message_error/3, + handle_trans_request/3, + handle_trans_long_request/3, + handle_trans_reply/4, + handle_trans_ack/4, + handle_unexpected_trans/3, + handle_trans_request_abort/4 + ]). + +-include_lib("megaco/include/megaco.hrl"). +-include_lib("megaco/include/megaco_message_v1.hrl"). + + + +%%---------------------------------------------------------------------- +%% To be used at command line: erl -s ?MODULE start_batch +%%---------------------------------------------------------------------- + +start_batch() -> + start_batch([]). + +start_batch(Args0) when is_list(Args0) -> + {ok, LocalHost} = inet:gethostname(), + Defs = [{mgc_host, LocalHost}, {trace,false}, {debug, false}], + Args = parse_args(Args0, Defs), + MgcHost = get_arg(mgc_host, Args), + Trace = get_arg(trace, Args), + Debug = get_arg(debug, Args), + Pid = spawn(?MODULE, init_batch, [self(), MgcHost, Trace, Debug]), + receive + {init_batch, Pid, Res} -> + io:format("~p(~p): ~p~n", [?MODULE, ?LINE, Res]), + Res + end. + +parse_args([], Acc) -> + Acc; +parse_args([Arg|Args], Acc) when is_atom(Arg) -> + case string:tokens(atom_to_list(Arg),"{},") of + ["mgc_host", Host] when is_list(Host) -> + parse_args(Args, parse_args(mgc_host, Host, Acc)); + ["trace",Trace] -> + parse_args(Args, parse_args(trace, list_to_atom(Trace), Acc)); + ["debug",Debug] -> + parse_args(Args, parse_args(debug, list_to_atom(Debug), Acc)); + _Invalid -> + parse_args(Args, Acc) + end. + +parse_args(Key, Val, Args) -> + Entry = {Key, Val}, + case lists:keyreplace(Key, 1, Args, {Key, Val}) of + Args -> + [Entry|Args]; + Args2 -> + Args2 + end. + +get_arg(Key, Args) -> + {value, {Key, Val}} = lists:keysearch(Key, 1, Args), + Val. + +init_batch(ReplyTo, MgcHost, Trace, Debug) -> + register(?MODULE, self()), + Res = start(MgcHost, Trace, Debug), + ReplyTo ! {init_batch, self(), Res}, + receive + after infinity -> Res + end. + + +%%---------------------------------------------------------------------- +%% Starting the MG +%%---------------------------------------------------------------------- + +%% ----------------------------------------------------------------------- + +init_inline_trace(true) -> + megaco:enable_trace(max, io); +init_inline_trace(_) -> + ok. + +%% ----------------------------------------------------------------------- + + +start() -> + {ok, LocalHost} = inet:gethostname(), + start(LocalHost, false, false). + +%% Used when calling from the erlang shell: +start(MgcHost, Trace, Debug) + when is_atom(MgcHost) andalso is_atom(Trace) andalso is_atom(Debug) -> + start(atom_to_list(MgcHost), Trace, Debug); + +start(MgcHost, Trace, Debug) + when is_list(MgcHost) andalso is_atom(Trace) andalso is_atom(Debug) -> + put(debug, Debug), + d("start -> entry with" + "~n MgcHost: ~s" + "~n Trace: ~p", [MgcHost, Trace]), + init_inline_trace(Trace), + Starters = [fun start_tcp_text/2, + fun start_tcp_binary/2, + fun start_udp_text/2, + fun start_udp_binary/2], + [Fun(MgcHost, []) || Fun <- Starters]. + +start_tcp_text(MgcHost, Default) -> + d("start_tcp_text -> entry with" + "~n MgcHost: ~p", [MgcHost]), + Config = [{encoding_mod, megaco_pretty_text_encoder}, + {encoding_config, []}, + {send_mod, megaco_tcp} | Default], + Mid = {deviceName, "gateway_tt"}, + {Mid, start(MgcHost, ?megaco_ip_port_text, Mid, Config)}. + +start_tcp_binary(MgcHost, Default) -> + d("start_tcp_binary -> entry with" + "~n MgcHost: ~p", [MgcHost]), + Config = [{encoding_mod, megaco_binary_encoder}, + {encoding_config, []}, + {send_mod, megaco_tcp} | Default], + Mid = {deviceName, "gateway_tb"}, + {Mid, start(MgcHost, ?megaco_ip_port_binary, Mid, Config)}. + +start_udp_text(MgcHost, Default) -> + d("start_udp_text -> entry with" + "~n MgcHost: ~p", [MgcHost]), + Config = [{encoding_mod, megaco_pretty_text_encoder}, + {encoding_config, []}, + {send_mod, megaco_udp} | Default], + Mid = {deviceName, "gateway_ut"}, + {Mid, start(MgcHost, ?megaco_ip_port_text, Mid, Config)}. + +start_udp_binary(MgcHost, Default) -> + d("start_udp_binary -> entry with" + "~n MgcHost: ~p", [MgcHost]), + Config = [{encoding_mod, megaco_binary_encoder}, + {encoding_config, []}, + {send_mod, megaco_udp} | Default], + Mid = {deviceName, "gateway_ub"}, + {Mid, start(MgcHost, ?megaco_ip_port_binary, Mid, Config)}. + +start(MgcHost, MgcPort, Mid, Config) -> + case megaco:start_user(Mid, [{user_mod, ?MODULE} | Config]) of + ok -> + case start_transport(MgcHost, MgcPort, Mid) of + {ok, ConnHandle} -> + service_change(ConnHandle); + {error, Reason} -> + {error, Reason} + end; + {error, Reason} -> + {error, {start_user, Reason}} + end. + +start_transport(MgcHost, MgcPort, Mid) -> + RecHandle = megaco:user_info(Mid, receive_handle), + case RecHandle#megaco_receive_handle.send_mod of + megaco_tcp -> start_tcp(MgcHost, MgcPort, RecHandle); + megaco_udp -> start_udp(MgcHost, MgcPort, RecHandle); + SendMod -> {error, {bad_send_mod, SendMod}} + end. + +start_tcp(MgcHost, MgcPort, RecHandle) -> + d("start_tcp -> start transport"), + case megaco_tcp:start_transport() of + {ok, Pid} -> + d("start_tcp -> transport started: ~p", [Pid]), + Options = [{host, MgcHost}, + {port, MgcPort}, + {receive_handle, RecHandle}], + case megaco_tcp:connect(Pid, Options) of + {ok, SendHandle, ControlPid} -> + d("start_tcp -> connected: ~p", [ControlPid]), + MgcMid = preliminary_mid, + megaco:connect(RecHandle, MgcMid, SendHandle, ControlPid); + {error, Reason} -> + d("start_tcp -> connection failed: ~p", [Reason]), + {error, {megaco_tcp_connect, Reason}} + end; + {error, Reason} -> + d("start_tcp -> failed starting transport: ~p", [Reason]), + {error, {megaco_tcp_start_transport, Reason}} + end. + +start_udp(MgcHost, MgcPort, RecHandle) -> + d("start_udp -> start transport"), + case megaco_udp:start_transport() of + {ok, SupPid} -> + d("start_udp -> transport started: ~p", [SupPid]), + Options = [{port, 0}, {receive_handle, RecHandle}], + case megaco_udp:open(SupPid, Options) of + {ok, Handle, ControlPid} -> + d("start_udp -> port opened: ~p", [ControlPid]), + %% Socket = megaco_udp:socket(Handle), + %% MgPort = inet:port(Socket), BUGBUG BUGBUG + MgcMid = preliminary_mid, + SendHandle = megaco_udp:create_send_handle(Handle, + MgcHost, % BUGBUG BUGBUG + MgcPort), + megaco:connect(RecHandle, MgcMid, SendHandle, ControlPid); + {error, Reason} -> + d("start_udp -> failed open port: ~p", [Reason]), + {error, {megaco_udp_open, Reason}} + end; + {error, Reason} -> + d("start_udp -> failed starting transport: ~p", [Reason]), + {error, {megaco_udp_start_transport, Reason}} + end. + +service_change(ConnHandle) -> + service_change(ConnHandle, restart, ?megaco_cold_boot). + +service_change(ConnHandle, Method, Reason) -> + SCP = #'ServiceChangeParm'{serviceChangeMethod = Method, + serviceChangeReason = [Reason]}, + TermId = [?megaco_root_termination_id], + SCR = #'ServiceChangeRequest'{terminationID = TermId, + serviceChangeParms = SCP}, + CR = #'CommandRequest'{command = {serviceChangeReq, SCR}}, + AR = #'ActionRequest'{contextId = ?megaco_null_context_id, + commandRequests = [CR]}, + megaco:call(ConnHandle, [AR], []). + +%%---------------------------------------------------------------------- +%% Stopping the MG +%%---------------------------------------------------------------------- + +stop() -> + [{Mid, stop(Mid)} || Mid <- megaco:system_info(users)]. + +stop(Mid) -> + Reason = stopped_by_user, + Disco = fun(CH) -> + Pid = megaco:conn_info(CH, control_pid), + megaco:disconnect(CH, Reason), + megaco:cancel(CH, Reason), + exit(Pid, Reason) + end, + lists:map(Disco, megaco:user_info(Mid, connections)), + megaco:stop_user(Mid). + +%%---------------------------------------------------------------------- +%% Invoked when a new connection is established +%%---------------------------------------------------------------------- + +handle_connect(ConnHandle, ProtocolVersion) -> + d("handle_connect -> entry with" + "~n ConnHandle: ~p" + "~n ProtocolVersion: ~p", [ConnHandle, ProtocolVersion]), + ok. + +%%---------------------------------------------------------------------- +%% Invoked when a connection is teared down +%%---------------------------------------------------------------------- + +handle_disconnect(ConnHandle, ProtocolVersion, Reason) -> + d("handle_disconnect -> entry with" + "~n ConnHandle: ~p" + "~n ProtocolVersion: ~p" + "~n Reason: ~p", [ConnHandle, ProtocolVersion, Reason]), + megaco:cancel(ConnHandle, Reason), % Cancel the outstanding messages + d("handle_disconnect -> done", []), + ok. + +%%---------------------------------------------------------------------- +%% Invoked when a received message had syntax errors +%%---------------------------------------------------------------------- + +handle_syntax_error(ReceiveHandle, ProtocolVersion, ErrorDescriptor) -> + d("handle_syntax_error -> entry with" + "~n ReceiveHandle: ~p" + "~n ProtocolVersion: ~p" + "~n ErrorDescriptor: ~p", + [ReceiveHandle, ProtocolVersion, ErrorDescriptor]), + reply. + +%%---------------------------------------------------------------------- +%% Invoked when a received message contained no transactions +%%---------------------------------------------------------------------- + +handle_message_error(ConnHandle, ProtocolVersion, ErrorDescriptor) -> + d("handle_message_error -> entry with" + "~n ConnHandle: ~p" + "~n ProtocolVersion: ~p" + "~n ErrorDescriptor: ~p", + [ConnHandle, ProtocolVersion, ErrorDescriptor]), + no_reply. + +%%---------------------------------------------------------------------- +%% Invoked for each transaction request +%%---------------------------------------------------------------------- + +handle_trans_request(ConnHandle, ProtocolVersion, ActionRequests) -> + d("handle_trans_request -> entry with" + "~n ConnHandle: ~p" + "~n ProtocolVersion: ~p" + "~n ActionRequests: ~p", + [ConnHandle, ProtocolVersion, ActionRequests]), + ED = #'ErrorDescriptor'{errorCode = ?megaco_not_implemented, + errorText = "Transaction requests not handled"}, + {discard_ack, ED}. + +%%---------------------------------------------------------------------- +%% Optionally invoked for a time consuming transaction request +%%---------------------------------------------------------------------- + +handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData) -> + d("handle_trans_long_request -> entry with" + "~n ConnHandle: ~p" + "~n ProtocolVersion: ~p" + "~n ReqData: ~p", [ConnHandle, ProtocolVersion, ReqData]), + ED = #'ErrorDescriptor'{errorCode = ?megaco_not_implemented, + errorText = "Long transaction requests not handled"}, + {discard_ack, ED}. + +%%---------------------------------------------------------------------- +%% Optionally invoked for a transaction reply +%%---------------------------------------------------------------------- + +handle_trans_reply(ConnHandle, ProtocolVersion, ActualReply, ReplyData) -> + d("handle_trans_reply -> entry with" + "~n ConnHandle: ~p" + "~n ProtocolVersion: ~p" + "~n ActualReply: ~p" + "~n ReplyData: ~p", + [ConnHandle, ProtocolVersion, ActualReply, ReplyData]), + ok. + +%%---------------------------------------------------------------------- +%% Optionally invoked for a transaction acknowledgement +%%---------------------------------------------------------------------- + +handle_trans_ack(ConnHandle, ProtocolVersion, AckStatus, AckData) -> + d("handle_trans_ack -> entry with" + "~n ConnHandle: ~p" + "~n ProtocolVersion: ~p" + "~n AckStatus: ~p" + "~n AckData: ~p", + [ConnHandle, ProtocolVersion, AckStatus, AckData]), + ok. + + +%%---------------------------------------------------------------------- +%% Invoked when an unexpected message has been received +%%---------------------------------------------------------------------- + +handle_unexpected_trans(ConnHandle, ProtocolVersion, Trans) -> + d("handle_unexpected_trans -> entry with" + "~n ConnHandle: ~p" + "~n ProtocolVersion: ~p" + "~n AckStatus: ~p" + "~n AckData: ~p", + [ConnHandle, ProtocolVersion, Trans]), + ok. + + +%%---------------------------------------------------------------------- +%% Invoked when an unexpected message has been received +%%---------------------------------------------------------------------- + +handle_trans_request_abort(ConnHandle, ProtocolVersion, TransId, Pid) -> + d("handle_trans_request_abort -> entry with" + "~n ConnHandle: ~p" + "~n ProtocolVersion: ~p" + "~n TransId: ~p" + "~n Pid: ~p", + [ConnHandle, ProtocolVersion, TransId, Pid]), + ok. + + +%%---------------------------------------------------------------------- +%% DEBUGGING +%%---------------------------------------------------------------------- + +d(F) -> + d(F, []). + +d(F,A) -> + d(get(debug),F,A). + +d(true,F,A) -> + io:format("SIMPLE_MG: " ++ F ++ "~n", A); +d(_, _F, _A) -> + ok. + + + + |