%% #0.    BASIC INFORMATION
%% ----------------------------------------------------------------------
%% %CCaseFile:  sbgStSupport.erl 
%% Author:      etxkols
%% Description: IS hardware functions.
%%
%% ----------------------------------------------------------------------

%% @doc This the the API interface for module <code>sbgStSupport</code>.
%%
%% <font size="4">Follow this guide, feature team can run eNST easily.
%% <ol>
%% <li>prepare your sipp scenario file, and call <code>sbgEstSupport:prepare_sipp_cmd/3</code> to generate sipp commands.<br></br>
%% <strong>Note:</strong>  Some import parameters have default value, and there are configurable.</li>
%% <li>call <code>sbgEstSupport:prepare_systeminfo_filenames/1</code> to prepare the log files according which blades you want to monitor.</li>
%% <li>call <code>sbgEstSupport:start_collect_system_infos/1</code> to start collecting logs.</li>
%% <li>call <code>sbgEstSupport:run_scenario</code> to run your sipp traffic.<br></br>
%% <strong>Note:</strong>  the traffic run time is configurable and you also can configure monitoring the SIPp running status.</li>
%% <li>call <code>sbgEstSupport:generate_log_statistics/4</code> to analyze the log files and draw the picture.</li>
%% </ol>
%% You can refter the demo case in suite <code>sbg_eST_template.erl</code>, here is the <a href="https://a...content-available-to-author-only...n.se/jpT/elqstux/15-11-02/ct_run.ct_5@sekilxc0007.2015-11-02_12.04.49/auto.suite.sbg_eST_template.sbg_est_001.logs/run.2015-11-02_12.04.54/suite.log.html">log</a>
%% </font>
-module(sbgEstSupport).
-date('2015-11-02').
-author('elqstux').
-vsn('/main/R20A/R21A/R22A/R99A/2, checkout by elqstux in elqstux_ki_ppb').

%% ----------------------------------------------------------------------
%% %CCaseCopyrightBegin%
%% Copyright (c) Ericsson AB 2009-2013 All rights reserved.
%%
%% The program may be used and/or copied only with the written
%% permission from Ericsson Telecom AB, or in accordance with
%% the terms and conditions stipulated in the agreement/contract
%% under which the program has been supplied.
%%
%% All rights reserved
%%
%% ----------------------------------------------------------------------
%%
%% #1.    EXPORT LISTS
%% ----------------------------------------------------------------------
%% #1.1   EXPORTED INTERFACE FUNCTIONS
%% ----------------------------------------------------------------------

%% ----------------------------------------------------------------------
%% #2.         REVISION LOG
%% ----------------------------------------------------------------------
%% Rev         Date            Name            What
%% ----------------------------------------------------------------------
%% R21A/1      130731          elilige          Created
%% R21A/2      131016          elilige          Add draw_graph
%% ----------------------------------------------------------------------
%% ----------------------------------------------------------
%% #2.    EXPORT LISTS
%% ----------------------------------------------------------
%% #2.1   EXPORTED INTERFACE FUNCTIONS
%% ----------------------------------------------------------
-export([prepare_sipp_cmd/3,
         prepare_systeminfo_filenames/1,
         start_collect_system_infos/1,
stop_collect_system_infos/1,
generate_log_statistics/4,
         run_scenario/6]).

-define(DEBUG(Format, Args), ct:pal(Format, Args)).

-define(INFO_MSG(Format, Args), ct:pal(Format, Args)).
                  
-define(WARNING_MSG(Format, Args), ct:pal(Format, Args)).
                  
-define(ERROR_MSG(Format, Args), ct:pal(Format, Args)).

-define(CRITICAL_MSG(Format, Args), ct:pal(Format, Args)).

-define(STAT_FAIL, 10).
-define(CALL_LOSS_RATE, 0.0005).

-define(SIPP_PARAS, [
                     {service, [" -s ", ""]},
                     {scenarioFile, [" -sf ", ""]},
                     {infFile, [" -inf ", ""]},
                     {localIp, [" -i ", ""]},
                     {localPort, [" -p ", ""]},
                     {mediaIp, [" -mi ", ""]},
                     {mediaPort, [" -mp ", ""]},
                     {autoAnswer, [" -aa"]},
                     {background, [" -bg"]},
                     {callRate, [" -r ", ""]},
                     {maxCalls, [" -m ", ""]},
                     {callLength, [" -d ", ""]},
                     {parallelCalls, [" -l ", ""]},
                     {statFile, [" -stf ", ""]},
                     {errorFile, [" -error_file ", ""]},
                     {statFrequence, [" -fd ", ""]},
                     {openStat, [" -trace_stat"]},
                     {traceScreen, [" -trace_screen"]},
                     {traceErr, [" -trace_err"]},
                     {transport, [" -t ", ""]}
                    ]).
-define(SIPP_MANDATORY_PARAMETERS, [scenarioFile, localIp, localPort, mediaIp, statFile]).
-define(SIPP_DEFAULT_PARAMETERS, [autoAnswer, background, openStat, traceScreen, traceErr]).
-define(SIPP_OPTIONAL_PARAMETERS, [service, callRate, infFile, mediaPort, maxCalls, callLength,
                                         parallelCalls, statFrequence, transport, errorFile]).
                                                    
-define(SIPP_BIN, "/vobs/mgwblade/PPB/SBG_HSD10196_1/test/auto/suite/test/est_script/sipp").
-define(XML_DIR, "/vobs/mgwblade/PPB/SBG_HSD10196_1/test/auto/suite/test/sipp_xml/").
-define(EST_LOG_DIR, "/home/"++os:getenv("USER")++"/est_log_tmp/").
-define(EST_DATA_BIN, "/vobs/mgwblade/PPB/SBG_HSD10196_1/test/auto/suite/test/est_script/data_analysis/").

%%-define(SGCchNode,jpTstates:find_role(jpTnames:ty2na(sgc),ch)).
-define(SGCchNode,jpTstates:find_role(sbgFtSupport:get_bs_name(sgc,1),ch)).
-define(CH(M,F,A), jpTrpc:call(?SGCchNode, M, F, A)).

-define(SGComNode,jpTstates:find_role(sbgFtSupport:get_bs_name(sgc,1),om)).
-define(OM(M,F,A), jpTrpc:call(?SGComNode, M, F, A)).

-define(SBGchNode,jpTstates:find_role(sbgFtSupport:get_bs_name(ommp,1),ch)).
-define(SBGCH(M,F,A), jpTrpc:call(?SBGchNode, M, F, A)).

-define(SBGomNode,jpTstates:find_role(sbgFtSupport:get_bs_name(ommp,1),om)).
-define(SBGOM(M,F,A), jpTrpc:call(?SBGomNode, M, F, A)).

-define(REG_MATCH, {regKey, '_', '_', '_', '_', '_', '_', '_', '_'}).                                          
-define(REG_MATCH2, {regKey, '_', '_', '_'}).                                                                  
-define(DC_MATCH(APP), {APP, list_to_atom(atom_to_list(APP)++"DcMain"),'_'}).

-define(DEFAULT_MONITOR_INTERVAL, {0, 5, 0}).


%% @spec prepare_sipp_cmd(SIPp_Config, Role, IpVsn) -> Res :: tuple()
%% @doc  Shows <a href="http://s...content-available-to-author-only...e.net/">SIPp</a> online help documentation of what config parameters there are.<br></br>
%% Prepare sipp cmd for starting sipp process, default value is used if cannot get from user input.<br></br>
%% <pre>
%% <strong>Node:</strong>
%% SIPp_Config     proplists()
%% Role            access | core4access
%% IpVsm           ipv4 | ipv6
%% Res             tuple, just like {ok, {UacScenarioFile, UacStatFile, UacSippCmd}=UacParam}
%% </pre>
%% <dl>
%% <dt>======================================================================== </dt>
%% <dt>       The key for parameter(SIPp_Config)                                </dt>
%% <dt>======================================================================== </dt>
%% <dt><pre><strong>Parameter</strong>                         <strong>Description</strong>        </pre></dt>
%% <dt><pre>localIp                           IP address of source, mandatory but has default value</pre></dt>
%% <dt><pre>mediaIp                           Media address of source, mandatory but has default value</pre></dt>
%% <dt><pre>localPort                         Source port, mandatory but has default value</pre></dt>
%% <dt><pre>remoteIp                          IP address of destination, mandatory but has default value</pre></dt>
%% <dt><pre>remotePort                        Destination port, mandatory but has default value</pre></dt>
%% <dt><pre>scenarioFile                      Path of scenario file, mandatory</pre></dt>
%% <dt><pre>statFile                          Path of statistics file, mandatory but has default value</pre></dt>
%% <dt><pre>infFile                           Path of inject parameter file, optional</pre></dt>
%% <dt><pre>callrate                          Number of calls/s, optional</pre></dt>
%% <dt><pre>maxcalls                          Maximum number of calls setup, optional</pre></dt>
%% <dt><pre>calllength                        Length in seconds of calls, optional</pre></dt>
%% <dt><pre>parallelCalls                     Total number of calls in parallel, optional</pre></dt>
%% <dt><pre>statFrequence                     The statistics dump log report frequency, optional</pre></dt>
%% <dt><pre>transport                         The transport mode, optional</pre></dt>
%% <dt>======================================================================== </dt>
%% </dl>
%% === Example usage ===
%% ```jpTsipp:prepare_sipp_cmd([{localIp, "10.10.121.7"},
%%                              {mediaIp, "10.10.121.7"},
%%                              {localPort, "8004"},
%%                              {remoteIp, "10.21.0.4"},
%%                              {remotePort, "10004"},
%%                              {scenarioFile, "./sipp/st/uac_register.xml"},
%%                              {infFile, "./sipp/st/database.csv"},
%%                              {statFile, "./sipp/st/sipp_regtister_uac.csv"},
%%                              {callRate, "5"}], access, ipv4).'''

prepare_sipp_cmd(SIPp_Config, Role, IpVsn) ->
    
    SippParameters = prepare_sipp_param(SIPp_Config, Role, IpVsn),
    
    RemoteAddr = proplists:get_value(remoteIp, SippParameters),
    RemotePort = proplists:get_value(remotePort, SippParameters),
    RemoteUri  = build_remote_uri(RemoteAddr, RemotePort), 
    StatFile   = proplists:get_value(statFile, SippParameters),
    ScenarioFile = proplists:get_value(scenarioFile, SippParameters),
    
    MandatoryBool= check_sipp_param(SippParameters,
				    ?SIPP_MANDATORY_PARAMETERS, mandatory),
    OptionalBool = check_sipp_param(SippParameters,
				    ?SIPP_OPTIONAL_PARAMETERS, optional),
    
    if 
        (MandatoryBool =:= true) and (OptionalBool =:= true) ->
            NewSippParameters = set_default_sipp_param(SippParameters,
						       ?SIPP_DEFAULT_PARAMETERS),
            SippParaString = build_sipp_parameters_string(NewSippParameters,
							  ""),
            if
                RemoteUri =:= "" -> 
                    ?ERROR_MSG("Error:~nRemote Address Information is invalid: ~p, ~p~n",
			       [RemoteAddr, RemotePort]),
                    {error, RemoteAddr};
                true ->
                    SippCmd = ?SIPP_BIN ++ " " ++ RemoteUri ++ " " ++ SippParaString,
                    ?DEBUG("Generate Sipp command:~n~p~nGet StatFile:~n~p~n",
			   [SippCmd, StatFile]),
                    {ok, {ScenarioFile, StatFile, SippCmd}}
            end;
        true ->
            ?WARNING_MSG("Warning:~nThe mandatory parameters check result: ~p~n the optional check result: ~p.~n",
			 [MandatoryBool, OptionalBool]),
            {error, {MandatoryBool, OptionalBool}}
    end.

%% -----------------------------------------------------------------------
%% prepare_sipp_param(SIPp_Config, Role, IpVsn)
%% @spec prepare_sipp_param(SIPp_Config::list(), Role::atom(), IpVsn::atom()) -> ResList::list()
%% @doc Update sipp parameters, combine path with file
%% SIPp_Config::list() is a list of parameters to sipp. More info about parameters
%% in jpTsipp:prepare_sipp_cmd/1
%% <pre>
%% <b>Input:</b>
%% SIPp_Config      List    Argument list of tuples in form {TypeTag,Param} 
%% Role     Atom     access|core4access|foreign|core4foreign  
%% IpVsn      Atom     ipv4|ipv6    
%%
%% <b>Output:</b>
%% ResList   List     Argument list of tuples
%%
%% <i>Example:</i>
%% jpTsipp:prepare_sipp_param([{localIp, "10.10.121.7"},
%%                              {mediaIp, "10.10.121.7"},
%%                              {localPort, "8004"},
%%                              {remoteIp, "10.21.0.4"},
%%                              {remotePort, "10004"}
%%                              {scenarioFile, "./sipp/st/uac_register.xml"},
%%                              {infFile, "./sipp/st/database.csv"},
%%                              {statFile, "./sipp/st/sipp_regtister_uac.csv"},
%%                              {errorFile, "./sipp/st/sipp-error.log"},
%%                              {callRate, "5"}], access, ipv4).
%% </pre>
%% @end
%% -----------------------------------------------------------------------
prepare_sipp_param(SIPp_Config, Role, IpVsn)->
    PreLocalIp = proplists:get_value(localIp, SIPp_Config),
    PreLocalPort = proplists:get_value(localPort, SIPp_Config),
    PreRemoteIp = proplists:get_value(remoteIp, SIPp_Config),
    PreRemotePort = proplists:get_value(remotePort, SIPp_Config),
    
    LocalIp = handle_local_ip(PreLocalIp, Role, IpVsn),
    LocalPort = handle_local_port(PreLocalPort, Role),
    RemoteIp = handle_remote_ip(PreRemoteIp, Role, IpVsn),
    RemotePort = handle_remote_port(PreRemotePort, Role),

    PreMediaIp = proplists:get_value(mediaIp, SIPp_Config),
    MediaIp = choose(PreMediaIp=:=undefined, LocalIp, PreMediaIp),
    
    ErrorFile = case proplists:get_value(errorFile, SIPp_Config) of
                    undefined ->
                        ?EST_LOG_DIR ++ to_string(Role) ++ "_" ++ write_time(now) ++ "_" ++ "sipp-error.log";
                    TmpErrorFile ->
                        filename:dirname(TmpErrorFile) ++ "/" ++ write_time(now) ++ "_" ++ filename:basename(TmpErrorFile)
                end,
    
    StatFile = case proplists:get_value(statFile, SIPp_Config) of
                   undefined ->
                       ?EST_LOG_DIR ++ to_string(Role) ++ "_" ++ write_time(now) ++ "_" ++ "sipp-statistics.csv";
                   TmpStatFile ->
                       filename:dirname(TmpStatFile) ++ "/" ++ write_time(now) ++ "_" ++ filename:basename(TmpStatFile)
               end,
   
    update_sipp_param([{localIp, LocalIp}, {localPort, LocalPort}, {remoteIp, RemoteIp}, 
                       {remotePort, RemotePort}, {mediaIp, MediaIp}, {statFile, StatFile},
                       {errorFile, ErrorFile}], SIPp_Config).


update_sipp_param([], []) ->
    [];
update_sipp_param([], SIPp_Config) ->
    SIPp_Config;
update_sipp_param([{Attr, Value}|Rest]=_Param_List, SIPp_Config) ->
    case {Attr, Value} of
        {_, undefined} ->
            update_sipp_param(Rest, SIPp_Config);
        {_, _} ->
            NewSIPp_Config = proplists:delete(Attr, SIPp_Config) ++ [{Attr, Value}],
            update_sipp_param(Rest, NewSIPp_Config)
    end.        

%% -----------------------------------------------------------------------
%% check_sipp_param(SippParameters, _ParameterList, Type)
%% @spec check_sipp_param(SippParameters::list(), _ParameterList::list(), Type::atom()) -> Res::atom()
%% @doc Check whether parameters are lost and duplicated in parameter list
%% SippParameters::list() is a list of parameters to sipp. More info about parameters
%% in jpTsipp:prepare_sipp_cmd/1
%% <pre>
%% <b>Input:</b>
%% SippParameters      List    Argument list of tuples in form {TypeTag,Param}
%% _ParameterList      List    Parameter list
%% Type                Atom    mandatory|optional       
%%
%% <b>Output:</b>
%% Res   Atom     true|false
%%
%% <i>Example:</i>
%% jpTsipp:check_sipp_param([{localIp, "10.10.121.7"},
%%                           {mediaIp, "10.10.121.7"},
%%                           {localPort, "8004"},
%%                           {remoteIp, "10.21.0.4"},
%%                           {remotePort, "10004"},
%%                           {scenarioFile, "./sipp/st/uac_register.xml"},
%%                           {infFile, "./sipp/st/database.csv"},
%%                           {statFile, "./sipp/st/sipp_regtister_uac.csv"},
%%                           {callRate, "5"}], 
%%                          [scenarioFile, localIp, localPort, mediaIp, statFile],
%%                          mandatory).
%% </pre>
%% @end
%% -----------------------------------------------------------------------
check_sipp_param(SippParameters, [Parameter|Rest]=_ParameterList, mandatory) ->
    case get_parame_quantity(Parameter, SippParameters) of
        1 ->
            Cont=proplists:get_value(Parameter, SippParameters),
            case is_string(Cont) of
                true -> 
                    check_sipp_param(SippParameters, Rest, mandatory);
                false ->
                    ?WARNING_MSG("Wrong Parameter ~p Content: ~p~n",[Parameter, Cont]),
                    false
            end;
        0 ->
            ?WARNING_MSG("the mandatory parameter(~p) is not included.~n ",[Parameter]),
            false;
        _ -> 
            ?WARNING_MSG("the mandatory parameter(~p) is more than one.~n", [Parameter]),
            false
    end;
check_sipp_param(SippParameters, [Parameter|Rest]=_ParameterList, optional) ->
    case get_parame_quantity(Parameter, SippParameters) of
        1 -> 
            case is_string(proplists:get_value(Parameter, SippParameters)) of
                true -> 
                    check_sipp_param(SippParameters, Rest, optional);
                false ->
                    false
            end;
        0 -> 
            check_sipp_param(SippParameters, Rest, optional);
        _ ->
            ?WARNING_MSG("the optional parameter(~p) is more than one.~n", [Parameter]),
            false 
    end;
check_sipp_param(_SippParameters, [], _) -> 
    true.

%% -----------------------------------------------------------------------
%% set_default_sipp_param(SippParameters, _DefaultParameterList)
%% @spec set_default_sipp_param(SippParameters::list(), _DefaultParameterList::list()) -> ResList::list()
%% @doc Add default sipp parameter in SippParameters
%% SippParameters::list() is a list of parameters to sipp. More info about parameters
%% in jpTsipp:prepare_sipp_cmd/1
%% <pre>
%% <b>Input:</b>
%% SippParameters      List    Argument list of tuples in form {TypeTag,Param}
%% _ParameterList      List    Parameter list     
%%
%% <b>Output:</b>
%% ResList  List     Argument list of tuples in form {TypeTag, Param}
%%
%% <i>Example:</i>
%% jpTsipp:set_default_sipp_param([{localIp, "10.10.121.7"},
%%                           {mediaIp, "10.10.121.7"},
%%                           {localPort, "8004"},
%%                           {remoteIp, "10.21.0.4"},
%%                           {remotePort, "10004"},
%%                           {scenarioFile, "./sipp/st/uac_register.xml"},
%%                           {infFile, "./sipp/st/database.csv"},
%%                           {statFile, "./sipp/st/sipp_regtister_uac.csv"},
%%                           {callRate, "5"},
%%                           {maxCalls, "20000"}], 
%%                          [autoAnswer, background, openStat]).
%% </pre>
%% @end
%% -----------------------------------------------------------------------
set_default_sipp_param(SippParameters, [Parameter|Rest] = _DefaultParameterList) ->
    case get_parame_quantity(Parameter, SippParameters) of 
        0 -> 
            set_default_sipp_param([{Parameter, true}|SippParameters], Rest);
        _ -> 
            NewSippParameters = proplists:delete(Parameter, SippParameters),
            set_default_sipp_param([{Parameter, true}|NewSippParameters], Rest)
    end;
set_default_sipp_param(SippParameters, []) ->   
    SippParameters.


%% -----------------------------------------------------------------------
%% get_parame_quantity(Key, Parameters)
%% @spec get_parame_quantity(Key::atom(), Parameters::list()) -> Res::atom()
%% @doc Calculate the number of Key in Parameters list
%% Parameters::list() is a list of parameters to sipp. More info about parameters
%% in jpTsipp:prepare_sipp_cmd/1
%% <pre>
%% <b>Input:</b>
%% Key             Atom    key in Parameters list
%% Parameters      List    Argument list of tuples in form {TypeTag,Param}  
%%
%% <b>Output:</b>
%% Res   Atom      Number of keys
%%
%% <i>Example:</i>
%% jpTsipp:get_parame_quantity(localIp, [{localIp, "10.10.121.7"},
%%                                       {mediaIp, "10.10.121.7"},
%%                                       {localPort, "8004"},
%%                                       {callRate, "5"}]).
%% </pre>
%% @end
%% -----------------------------------------------------------------------
get_parame_quantity(Key, Parameters) ->
    length(proplists:get_all_values(Key, Parameters)).

%% -----------------------------------------------------------------------
%% build_sipp_parameters_string(_CmdList, CmdStr)
%% @spec build_sipp_parameters_string(_CmdList::list(), CmdStr::string()) -> Res::string()
%% @doc Generate parameters part of sipp cmd
%% _CmdList::list() is a list of parameters to sipp
%% <pre>
%% <b>Input:</b>
%% _CmdList        List    Parameters list
%% CmdStr          String  connector  
%%
%% <b>Output:</b>
%% Res   String      Parameters part of sipp cmd
%%
%% </pre>
%% @end
%% -----------------------------------------------------------------------
build_sipp_parameters_string([{Attr, Cont}|Rest]=_CmdList, CmdStr) -> 
    case proplists:get_value(Attr, ?SIPP_PARAS) of
        [Parameter] -> 
            if 
                Cont =:= true ->
                    build_sipp_parameters_string(Rest, CmdStr ++ Parameter);
                Cont =:= false ->
                    build_sipp_parameters_string(Rest, CmdStr)
            end;
        [Parameter, ""] -> 
            build_sipp_parameters_string(Rest, CmdStr ++ Parameter ++ to_string(Cont));
        undefined -> 
            ?WARNING_MSG("Warning:~nbuild_sipp_parameters_string recv unexpected input: ~p, skip the parameter and go ahead.~n",[Attr]),
            build_sipp_parameters_string(Rest, CmdStr)
    end;
build_sipp_parameters_string([], CmdStr) ->
    CmdStr.

%% -----------------------------------------------------------------------
%% build_remote_uri(RemoteIp, RemoterPort)
%% @spec build_remote_uri(RemoteIp::string(), RemoterPort::string()) -> Res::string()
%% @doc Generate remoter uri
%% <pre>
%% <b>Input:</b>
%% RemoterIp        String
%% RemoterPort      String 
%%
%% <b>Output:</b>
%% Res   String      Remtoe URI part of sipp cmd
%%
%% </pre>
%% @end
%% -----------------------------------------------------------------------
build_remote_uri(RemoteIp, RemotePort) ->
    if 
        (RemoteIp =:= undefined) or (RemotePort =:=undefined) ->
            ?ERROR_MSG("Error:~nbuild_remote_uri unknow remote parameters: remote ip=~p; remote port=~p~n",[RemoteIp, RemotePort]),
            "";
        true -> 
            to_string(RemoteIp) ++ ":" ++ to_string(RemotePort)
    end.

run_scenario(UacParam, UasParam, CallDuration, RunTime) ->
    run_scenario(UacParam, UasParam, CallDuration, RunTime, ?DEFAULT_MONITOR_INTERVAL, false).
%% -----------------------------------------------------------------------
%% run_scenario(_UacParam, _UasParam,  CallDuration, RunTime, MonitorInterval, StatMonitorFlag)
%% @spec run_scenario(_UacParam::tuple(), _UasParam::tuple(), CallDuration::integer(), 
%%                    RunTime::tuple(), MonitorInterval::tuple(),
%%                    StatMonitorFlag::atom()) -> Res::atom()
%% @doc Run the SIPp scenario.
%% <pre>
%% <b>Input:</b>
%%        _UacParam       Tuple     {UacSnrFile, UacStatFile, UacCmd}
%%        UacSnrFile      String    The scenario file for UAC
%%        UacStatFile     String    The Statistic file for UAC
%%        UacCmd          String    The SIPp command string for UAC
%%
%%        _UasParam       Tuple     {UasSnrFile, UasStatFile, UasCmd}
%%        UasSnrFile      String    The scenario file for UAS
%%        UasStatFile     String    The Statistic file for UAS
%%        UasCmd          String    The SIPp command string for UAS
%%
%%        CallDuration    Integer   Duration time for each call
%%        RunTime         Tuple     Total runtime for SIPp
%%        MonitorInterval Tuple     The monitor interval for SIPp
%%        StatMonitorFlag Atom      true|false   whether to monitor pass rate during call ongoing
%%
%% <b>Output:</b>
%%        Res             Atom      ok|error
%% </pre>
%% @end
%% ----------------------------------------------------------------------
run_scenario({UacSnrFile, UacStatFile, UacCmd} = _UacParam, 
	     {UasSnrFile, UasStatFile, UasCmd} = _UasParam, 
	     CallDuration, RunTime, MonitorInterval, StatMonitorFlag) ->

    %%  Start SIPp
    case start_sipp(UasCmd) of
        {ok, UasPid} ->
            ct:pal("###  UAS started, UasPid=~p  ###~n", [UasPid]),
            timer:sleep(1000), % Sometimes UAS needs more time to open TCP port etc.

            case start_sipp(UacCmd) of
                {ok, UacPid} -> 
		    ct:pal("###  UAC started, UacPid=~p  ###~n", [UacPid]),
		    do_handle_scenario({UasPid, UasSnrFile, UasStatFile}, 
				       {UacPid, UacSnrFile, UacStatFile},
				       CallDuration, RunTime, MonitorInterval, 
				       StatMonitorFlag);

                _ -> 
		    ct:pal("### UAC start failed, kill the started UAS process and quit the function!"),
		    os:cmd("kill -9" ++ to_string(UasPid)),
		    timer:sleep(1000),
		    UasScreenFile = filename:dirname(UasSnrFile) ++
			"/" ++ filename:basename(UasSnrFile, ".xml") ++
			"_" ++ to_string(UasPid) ++ "_screen.log",
		    ct:pal("UasScreenFile is:~p~n", [UasScreenFile]),
		    os:cmd("mv "++UasScreenFile++" "++?EST_LOG_DIR),
		    error
            end;
        _ ->
	    ct:pal("### UAS start failed, quit the function!"),
	    error
    end.
    
%% -----------------------------------------------------------------------
%% do_handle_scenario(_UasParam, _UacParam, CallDuration,
%%                    RunTime, MonitorInterval, StatMonitorFlag)
%% @spec do_handle_scenario(_UasParam       :: tuple(), 
%%                          _UacParam       :: tuple(), 
%%                          CallDuration    :: integer(), 
%%                          RunTime         :: tuple(), 
%%                          MonitorInterval :: tuple(), 
%%                          StatMonitorFlag :: atom()) -> Res::atom()
%%                          
%% @doc monitor SIPp running status and returns the result of scenario executing
%% <pre>
%% <b>Input:</b>
%%        _UasParam       Tuple     {UasPid,  UasSnrFile, UasStatFile}
%%        UasPid          String    The SIPp process id for UAS
%%        UaSSnrFile      String    The scenario file for UAS
%%        UaSStatFile     String    The Statistic file for UAS
%%
%%        _UacParam       Tuple     {UacPid, UacSnrFile, UacStatFile}
%%        UacPid          String    The SIPp process id for UAC
%%        UacSnrFile      String    The scenario file for UAC
%%        UacStatFile     String    The Statistic file for UAC
%% UasPid          Pid       The SIPp process id for UAS
%% UacPid          Pid       The SIPp process id for UAC
%% CallDuration    Integer   Call duration for each call
%% RunTime         Tuple     Total runtime for SIPp
%% MonitorInterval Tuple     The monitor interval for SIPp
%% StatMonitorFlag Atom      true|false   whether to monitor pass rate during call ongoing
%%
%% <b>Output:</b>
%% Res   Atom      ok|error
%%
%% </pre>
%% @end
%% ----------------------------------------------------------------------
do_handle_scenario({UasPid, UasSnrFile, UasStatFile} = _UasParam, 
		   {UacPid, UacSnrFile, UacStatFile} = _UacParam,
		   CallDuration, RunTime, MonitorInterval,
		   StatMonitorFlag) ->
    %%  Start Interval Check Timer
    {{RunHour, RunMinute, RunSecond}, 
     {MonitorHour, MonitorMinute, MonitorSecond}} = {RunTime, MonitorInterval},

    %% check the status of sipp process
    {ok, MonitorSippStatusRef} =
	timer:apply_interval(timer:hms(MonitorHour, MonitorMinute, MonitorSecond), 
			     ?MODULE, 
			     check_sipp_process_status,
			     [self(), [UasPid, UacPid]]),

    MonitorRefs =
        case StatMonitorFlag of
	    true ->
                %% check the call status of sipp traffic
		{ok, MonitorCallStatusRef}
		    = timer:apply_interval(timer:hms(MonitorHour, MonitorMinute, MonitorSecond), 
					   ?MODULE, 
					   check_sipp_call_status,
					   [self(), [UasStatFile, UacStatFile]]),
		[MonitorSippStatusRef, MonitorCallStatusRef];
	    _ ->
		[MonitorSippStatusRef]
	end,

    {ok, TRefClose} = 
	timer:apply_after(timer:hms(RunHour, RunMinute, RunSecond), 
			  ?MODULE, 
			  close_monitor, 
			  [self(), MonitorRefs]),

    TRefList = MonitorRefs ++ [TRefClose],

    %%  Interval Check
    IntervalCheckResult = interval_loop_check([{UacStatFile, 0}, {UasStatFile, 0}]),
    case IntervalCheckResult of
        ok ->
            ct:pal("Interval check ok~n"),
            ok;
        {failed, FailedResult} ->
            ?WARNING_MSG("Warning: interval_loop_check recv failed(~p)~nto cancel all timers~n", [FailedResult]),
            lists:foreach(fun(P) -> timer:cancel(P) end, TRefList)
    end,

    stop_uac_uas_sipp_processes([UacPid, UasPid], CallDuration),

    %%  Final Check
    {ok, UacStatData} = ?MODULE:get_stat_tail(UacStatFile),
    UacStatResult = check_sipp_stat(final, UacStatData),
    case UacStatResult of
        true ->
            ?DEBUG("Final UAC call failed rate is less than 0.05%.~n", []);
        false ->
            ?WARNING_MSG("Warning: final UAC call failed rate is more than 0.05%.~n", [])
    end,

    {ok, UasStatData} = ?MODULE:get_stat_tail(UasStatFile),
    UasStatResult = check_sipp_stat(final, UasStatData),
    case UasStatResult of
        true ->
            ?DEBUG("Final UAS call failed rate is less than 0.05%.~n", []);
        false ->
            ?WARNING_MSG("Warning: final UAS call failed rate is more than 0.05%.~n", [])
    end,

    UasScreenFile = filename:dirname(UasSnrFile) ++ "/" ++ filename:basename(UasSnrFile, ".xml") ++
	"_" ++ to_string(list_to_integer(UasPid)-1) ++ "_screen.log", 

    UacScreenFile = filename:dirname(UacSnrFile) ++ "/" ++ filename:basename(UacSnrFile, ".xml") ++
	"_" ++ to_string(list_to_integer(UacPid)-1) ++ "_screen.log",

    ct:pal("UasScreenFile is:~p~n", [UasScreenFile]),
    ct:pal("UacScreenFile is:~p~n", [UacScreenFile]),

    os:cmd("mv "++UasScreenFile++" "++?EST_LOG_DIR),
    os:cmd("mv "++UacScreenFile++" "++?EST_LOG_DIR),

    if 
        IntervalCheckResult =:= ok, UacStatResult =:=true, UasStatResult =:= true -> ok;
        true -> error
    end.    

prepare_env() ->
    %% Kill any lingering SIPp processes to avoid that the scenario fails
    os:cmd("pkill -9 sipp"),
    os:cmd("mkdir "++ ?EST_LOG_DIR),
    {ok, _} = ?CH(b2bDbg, start_error_trace, []),
    ok = ?CH(oabDbg, log, [start]),
    true = ?CH(regDbg, err_trace, []),
    true = ?CH(sipDbg, err_trace, []).

fallback_env() ->
    ok = ?CH(dbg, stop_clear, []).


prepare_systeminfo_filename(CurrentTime, sgc1, ch) ->
    Filename_suffix = write_time(CurrentTime) ++ ".log",
    
    Vmstat_Filename = string:concat("/blade/homedir/sgc1-ch-vmstat-", Filename_suffix),
    SysInfo_Filename = ?EST_LOG_DIR ++ "sgc1-ch-system-info-" ++ Filename_suffix,
    
    PlcRes_Filename = ?EST_LOG_DIR ++ "sgc1-ch-plc-res-" ++ Filename_suffix,
    
    {SysInfo_Filename, Vmstat_Filename, PlcRes_Filename};

prepare_systeminfo_filename(CurrentTime, sgc1, om) ->
    Filename_suffix = write_time(CurrentTime) ++ ".log",
    
    Vmstat_Filename = string:concat("/blade/homedir/sgc1-om-vmstat-", Filename_suffix),
    SysInfo_Filename = ?EST_LOG_DIR ++ "sgc1-om-system-info-" ++ Filename_suffix,
    
    PlcRes_Filename = ?EST_LOG_DIR ++ "sgc1-om-plc-res-" ++ Filename_suffix,
    
    {SysInfo_Filename, Vmstat_Filename, PlcRes_Filename};

prepare_systeminfo_filename(CurrentTime, ommp, ch) ->
    Filename_suffix = write_time(CurrentTime) ++ ".log",
    
    Vmstat_Filename = string:concat("/blade/homedir/ommp-ch-vmstat-", Filename_suffix),
    SysInfo_Filename = ?EST_LOG_DIR ++ "ommp-ch-system-info-" ++ Filename_suffix,
    
    PlcRes_Filename = ?EST_LOG_DIR ++ "ommp-ch-plc-res-" ++ Filename_suffix,
    
    {SysInfo_Filename, Vmstat_Filename, PlcRes_Filename};

prepare_systeminfo_filename(CurrentTime, ommp, om) ->
    Filename_suffix = write_time(CurrentTime) ++ ".log",
    
    Vmstat_Filename = string:concat("/blade/homedir/ommp-om-vmstat-", Filename_suffix),
    SysInfo_Filename = ?EST_LOG_DIR ++ "ommp-om-system-info-" ++ Filename_suffix,
    
    PlcRes_Filename = ?EST_LOG_DIR ++ "ommp-om-plc-res-" ++ Filename_suffix,
    
    {SysInfo_Filename, Vmstat_Filename, PlcRes_Filename}.

collect_system_info(SysInfo_Filename, Vmstat_Filename,sgc1,ch) ->
    Mem_result = ?CH(ets, tab2list, [plcInfo]),
    ProcessNum = length(?CH(erlang, processes, [])),
    Result = Mem_result ++ [{process_Num, ProcessNum}],
    
    {ok, File_handler} = file:open(SysInfo_Filename, [append]),
    io:format(File_handler, "~p~n", [Result]),
    ok = file:close(File_handler),
    
    VmCmd = "vmstat 5 5 >>" ++ Vmstat_Filename,
    ?CH(os, cmd, [VmCmd]);

collect_system_info(SysInfo_Filename, Vmstat_Filename,sgc1,om) ->
    Mem_result = ?OM(ets, tab2list, [plcInfo]),
    ProcessNum = length(?OM(erlang, processes, [])),
    Result = Mem_result ++ [{process_Num, ProcessNum}],
    
    {ok, File_handler} = file:open(SysInfo_Filename, [append]),
    io:format(File_handler, "~p~n", [Result]),
    ok = file:close(File_handler),
    
    VmCmd = "vmstat 5 5 >>" ++ Vmstat_Filename,
    ?OM(os, cmd, [VmCmd]);

collect_system_info(SysInfo_Filename, Vmstat_Filename,ommp,ch) ->
    Mem_result = ?SBGCH(ets, tab2list, [plcInfo]),
    ProcessNum = length(?SBGCH(erlang, processes, [])),
    Result = Mem_result ++ [{process_Num, ProcessNum}],
    
    {ok, File_handler} = file:open(SysInfo_Filename, [append]),
    io:format(File_handler, "~p~n", [Result]),
    ok = file:close(File_handler),
    
    VmCmd = "vmstat 5 5 >>" ++ Vmstat_Filename,
    ?SBGCH(os, cmd, [VmCmd]);

collect_system_info(SysInfo_Filename, Vmstat_Filename,ommp,om) ->
    Mem_result = ?SBGOM(ets, tab2list, [plcInfo]),
    ProcessNum = length(?SBGOM(erlang, processes, [])),
    Result = Mem_result ++ [{process_Num, ProcessNum}],
    
    {ok, File_handler} = file:open(SysInfo_Filename, [append]),
    io:format(File_handler, "~p~n", [Result]),
    ok = file:close(File_handler),
    
    VmCmd = "vmstat 5 5 >>" ++ Vmstat_Filename,
    ?SBGOM(os, cmd, [VmCmd]).

collect_plc_res(PlcRes_Filename) ->
    PlcRes_result = ?CH(plcScheduler, plc_res, [1, 1000]),
    {ok, File_handler} = file:open(PlcRes_Filename, [append]),
    io:format(File_handler, "~p~n", [PlcRes_result]),
    ok = file:close(File_handler).

transfer_vmstat_file(Filename,Type, Role) ->
    BladeNum = get_blade_no(Type, Role),
    File = "/private" ++ re:replace(Filename, "blade", BladeNum, [{return, list}]),

    LocalFile = filename:join([?EST_LOG_DIR,  filename:basename(File)]),
    SisRootPswd = sbgFtSupport:get_sis_root_pw(),
    ok = scp_from("root", SisRootPswd, "sis1", File, LocalFile),
    LocalFile.

get_blade_no(sgc1, ch) ->
    [_, BladeNum] = re:split(to_string(?SGCchNode), "[\@]", [{return, list}]),
    BladeNum;
get_blade_no(sgc1, om) ->
    [_, BladeNum] = re:split(to_string(?SGComNode), "[\@]", [{return, list}]),
    BladeNum;
get_blade_no(ommp, ch) ->
    [_, BladeNum] = re:split(to_string(?SBGchNode), "[\@]", [{return, list}]),
    BladeNum;
get_blade_no(ommp, om) ->
    [_, BladeNum] = re:split(to_string(?SBGomNode), "[\@]", [{return, list}]),
    BladeNum.

scp_from(User, Passwd, RemoteHost, RemoteFile, LocalFile) ->
    H = case is_atom(RemoteHost) of
        true -> atom_to_list(RemoteHost);
        false -> RemoteHost
    end,

    RemoteIp = jpTutil:get_ip_from_hostname(H),
    Options =     [{user,User},
                   {password,Passwd},
                   {user_interaction,false},
                   {silently_accept_hosts,true}],
    
    application:start(crypto),
    application:start(ssh),    
    
    case ssh_sftp:start_channel(RemoteIp, Options) of
        {ok, Pid, ConnRef} ->
            case ssh_sftp:read_file(Pid, RemoteFile) of
                {ok, Data} ->
                    case file:write_file(LocalFile, Data) of
                        ok ->
                            ssh:close(ConnRef);
                        {error, Error} ->
                            ct:pal("scp_from remote host ~p Error ~p~n", [RemoteHost, Error]),
                            ssh:close(ConnRef),
                            {nok, Error}
                    end;
                ErrorReadingFile ->
                    ssh:close(ConnRef),
                    {nok, {error_reading_source_file, ErrorReadingFile}}  
            end;
        {error, Error} ->
            {nok, Error};
        ErrorFromConnect ->
            {nok, ErrorFromConnect}
    end.  

get_all_counter() ->
    RegCount = count(reg),
    B2bCount = count(b2b),
    HiwCount = count(hiw),
    OabCount = count(oab),
    [RegCount, B2bCount, HiwCount, OabCount].

count(reg) ->                                                                                                  
    Count1 = ?CH(sysProc, select_count_names, [[{{?REG_MATCH, '_'},[],[true]}]]),                                      
    Count2 = ?CH(sysProc, select_count_names, [[{{?REG_MATCH2, '_'},[],[true]}]]),                                     
    Count  = reg_count(Count1, Count2),
    ResultCount = count(Count),
    io:format("\t Number of REG processes in the system are ~p~n", [ResultCount]),
    ResultCount;
count(b2b) ->                                                                                                  
    Count = ?CH(sysProc, select_count_names, [[{{?DC_MATCH(b2b), '_'},[],[true]}]]), 
    ResultCount = count(Count),
    io:format("\t Number of B2B processes in the system are ~p~n", [ResultCount]),
    ResultCount;
count(hiw) ->                                                                                                  
    Count = ?CH(sysProc, select_count_names, [[{{?DC_MATCH(hiw),'_'},[],[true]}]]),
    ResultCount = count(Count),
    io:format("\t Number of HIW processes in the system are ~p~n", [ResultCount]),
    ResultCount;
count(oab) ->                                                                                                  
    Count = ?CH(sysProc, select_count_names, [[{{?DC_MATCH(oab),'_'},[],[true]}]]),
    ResultCount = count(Count),
    io:format("\t Number of OAB processes in the system are ~p~n", [ResultCount]),
    ResultCount;
count(Int) when is_integer(Int)->                                                                              
    Int;                                                                                                       
count(Other) ->                                                                                                
    [Other].                                                                                                   
                                                                                                                
reg_count( C1,  C2) when is_integer(C1), is_integer(C2) -> C1+C2;                                              
reg_count( C1, _C2) when is_integer(C1)                 -> C1;                                                 
reg_count(_C1,  C2) when is_integer(C2)                 -> C2;                                                 
reg_count(_C1, _C2)                                     -> 0. 

draw_graph(InputFile, sysinfo) ->
    OutputFile = ?EST_LOG_DIR ++ filename:rootname(filename:basename(InputFile)) ++ ".svg",
    Cmd = "java -jar " ++ ?EST_DATA_BIN ++ "sysinfo.jar " ++ "\<" ++ " " ++ 
              InputFile ++ " \| java -jar " ++ ?EST_DATA_BIN ++ "xml2svg.jar " ++
              "\>" ++ " " ++ OutputFile,
    ct:pal("The graph draw sysinfo command:~n~p~n", [Cmd]),
    os:cmd(Cmd),
    OutputFile;

draw_graph(InputFile, vmstat) ->
    OutputFile = ?EST_LOG_DIR ++ filename:rootname(filename:basename(InputFile)) ++ ".svg",
    Cmd = "java -jar " ++ ?EST_DATA_BIN ++ "vmstat.jar " ++ "\<" ++ " " ++
              InputFile ++ " \| java -jar " ++ ?EST_DATA_BIN ++ "xml2svg.jar " ++
              "\>" ++ " " ++ OutputFile,
    ct:pal("The graph draw vmstat command:~n~p~n", [Cmd]),
    os:cmd(Cmd),
    OutputFile;

draw_graph(InputFile, passrate) ->
    OutputFile = ?EST_LOG_DIR ++ filename:rootname(filename:basename(InputFile)) ++ ".svg",
    Cmd = "java -jar " ++ ?EST_DATA_BIN ++ "passrate.jar " ++ "\<" ++ " " ++ 
              InputFile ++ " \| java -jar " ++ ?EST_DATA_BIN ++ "xml2svg.jar " ++
              "\>" ++ " " ++ OutputFile,
    ct:pal("The graph draw passrate command:~n~p~n", [Cmd]),
    os:cmd(Cmd),
    OutputFile.

generate_html(SysInfoSvg, VmstatSvg, UacPassrateSvg, UasPassrateSvg) ->
    Cmd = ?EST_DATA_BIN ++ "html_generate.sh" ++ " " ++ SysInfoSvg ++ " " ++
              VmstatSvg ++ " " ++ UacPassrateSvg ++ " " ++ UasPassrateSvg ++ " " ++ ?EST_LOG_DIR,
    os:cmd(Cmd).

generate_graph_on_log_html(SysInfoSvg, VmstatSvg, UacPassrateSvg, UasPassrateSvg) ->
    {ok, Cwd} = file:get_cwd(),
    Cmd = "cp " ++ SysInfoSvg ++ " " ++ VmstatSvg ++ " " ++ UacPassrateSvg ++
              " " ++ UasPassrateSvg ++ " " ++ Cwd ++ "/",
    os:cmd(Cmd),
    FileInfo = [{"Erlang VM Information", filename:basename(SysInfoSvg)},
                {"Vmstat Information", filename:basename(VmstatSvg)},
                {"UacPass Rate", filename:basename(UacPassrateSvg)},
                {"UasPass Rate", filename:basename(UasPassrateSvg)}],
    Res = gen_html_format(FileInfo),
    io:format(Res).

gen_html_format(FileInfo) ->
    F = fun({Desp, FileName}) ->
            "<h1>" ++ Desp ++ "</h1>" ++ "<embed type=\"image/svg+xml\" src=\"../../" ++ FileName ++ "\"/>"
        end,
    List = lists:map(F, FileInfo),
    lists:flatten(["<div>" | List], "</div>").


%% #---------------------------------------------------------
%% #3.2   CODE FOR INTERNAL FUNCTIONS
%% #---------------------------------------------------------

%% -----------------------------------------------------------------------
%% start_sipp(SIPpCommand)
%% @spec start_sipp(SIPpCommand) -> Res::tuple()
%% @doc Start SIPP command and returns the process id of SIPp
%% <pre>
%% <b>Input:</b>
%% SIPpCommand        String
%%
%% <b>Output:</b>
%% Res   Tuple      {ok|error, PID::string()}
%%
%% </pre>
%% @end
%% -----------------------------------------------------------------------
start_sipp(SIPpCommand) ->
    SIPpShellPrintout = os:cmd(SIPpCommand),
    ct:pal("startsipp result: ##### ~p~n", [SIPpShellPrintout]),
    get_pid_str(SIPpShellPrintout).


%% -----------------------------------------------------------------------
%% interval_loop_check(Counters)
%% @spec interval_loop_check(Counters::integer()) -> Res::atom()
%% @doc Check the sipp process status and sipp call status, return failed if
%% sipp process killed or call fail rate is more than expect in a certain time
%% <pre>
%% <b>Input:</b>
%% Counters     Integer   The number of times for check
%%
%% <b>Output:</b>
%% Res   Tuple
%%
%% </pre>
%% @end
%% -----------------------------------------------------------------------
interval_loop_check(Counters)->
    receive
        {ua, SippPid, SippStatus} -> 
            case SippStatus of 
                true ->
                    ct:pal("Sipp(PID=~p) is running~n", [SippPid]),
                    interval_loop_check(Counters);
                false -> 
                    ?WARNING_MSG("the SIPp process(~p) doesn't work", [SippPid]),
                    {failed, {sippStatus, SippPid}}
            end;
        {uaStat, StatFile, StatData} ->
            CounterValue = proplists:get_value(StatFile, Counters),
            RestCounters = proplists:delete(StatFile, Counters),
            case check_sipp_stat(interval, StatData) of
                true -> 
                    ct:pal("Statistics file(~p) interval loop check ok~n", [StatFile]),
                    interval_loop_check([{StatFile, 0}|RestCounters]);
                false ->                    
                    ?WARNING_MSG("the SIPp(~p) has some failed calls in the past ~p interval times~n",
                                 [StatFile, CounterValue]),
                    if CounterValue < ?STAT_FAIL ->
                           interval_loop_check([{StatFile, CounterValue + 1}|RestCounters]);
                       true ->
                           {failed, {sippStat, StatFile}}
                    end
            end;
        stop ->
            ok
    end.

%% -----------------------------------------------------------------------
%% close_monitor(Pid, TRefs)
%% @spec close_monitor(Pid, TRefs)->Res
%% @doc Close the monitor for SIPp
%% <pre>
%% <b>Input:</b>
%% TRefs        Time Reference of monitor
%%
%% </pre>
%% @end
%% -----------------------------------------------------------------------
close_monitor(Pid, TRefs) ->
    [timer:cancel(TRef)||TRef<-TRefs],
    ct:pal("close_monitor: TRefs: ~p~n", [TRefs]),
    Pid ! stop.

choose(true, __True, _) -> __True;
choose(false, _, __False) -> __False.


handle_local_ip(undefined, Role, IpVsn) ->
    local_ip_string(IpVsn, Role);
handle_local_ip(LocalIp, _, _) ->
    LocalIp.


local_ip_string(IpVsn, Role) ->
    case ?CH(sysEnv, is_ssit, []) of
        true ->
            ip_string_ssit(IpVsn);
        _ ->
            local_ip_string_stp(IpVsn, Role)
    end.

local_ip_string_stp(ipv4, access) ->
    "10.10.121.7";
local_ip_string_stp(ipv6, access) ->
    "3001:10:121::7";
local_ip_string_stp(ipv4, core4access) ->
    "10.10.240.7";
local_ip_string_stp(ipv6, core4access) ->
    "3001:10:240::7";
local_ip_string_stp(ipv4, foreign) ->
    "10.10.141.7";
local_ip_string_stp(ipv6, foreign) ->
    "3001:10:141::7";
local_ip_string_stp(ipv4, core4foreign) ->
    "10.10.240.7";
local_ip_string_stp(ipv6, core4foreign) ->
    "3001:10:240::7";
local_ip_string_stp(_IpVsn, Role) ->
    local_ip_string(ipv4, Role).

ip_string_ssit(IpVsn) ->
    IPtuple = case IpVsn of
                  ipv6 ->
                      get_local_ip_tuple(inet6);
                  _ ->
                      get_local_ip_tuple(inet)
              end,
    inet_parse:ntoa(IPtuple).

get_local_ip_tuple(inet6) ->
    %% The IPv6 linc local address that exists on the linux machine
    %% can not be used by erlang. The address must be a global address or
    %% loopback. Only loopback address is available in simulated environment.
    {0,0,0,0,0,0,0,1};
get_local_ip_tuple(InetFamily) ->
    {ok, Host}  = inet:gethostname(),
    {ok, IP}    = inet:getaddr(Host, InetFamily),
    IP. 

handle_local_port(undefined, Role) ->
    local_port_string(Role);
handle_local_port(LocalPort, _Role) ->
    LocalPort.

local_port_string(access) ->
    "8004";
local_port_string(core4access) ->
    "8003";
local_port_string(foreign) ->
    "8002";
local_port_string(core4foreign) ->
    "8003".


handle_remote_ip(undefined, Role, IpVsn) ->
    remote_ip_string(IpVsn, Role);
handle_remote_ip(RemoteIp, _, _) ->
    RemoteIp.

remote_ip_string(IpVsn, Role) ->
    case ?CH(sysEnv, is_ssit, []) of
        true ->
            ip_string_ssit(IpVsn);
        _ ->
            remote_ip_string_stp(IpVsn, Role)
    end.

remote_ip_string_stp(ipv4, access) ->
    "10.21.0.4";
remote_ip_string_stp(ipv6, access) ->
    "3001:21::4";
remote_ip_string_stp(ipv4, core4access) ->
    "10.10.230.4";
remote_ip_string_stp(ipv6, core4access) ->
    "3001:10:230::4";
remote_ip_string_stp(ipv4, foreign) ->
    "10.41.0.4";
remote_ip_string_stp(ipv6, foreign) ->
    "3001:41::4";
remote_ip_string_stp(ipv4, core4foreign) ->
    "10.10.230.5";
remote_ip_string_stp(ipv6, core4foreign) ->
    "3001:10:230::5";
remote_ip_string_stp(_IpVsn, Role) ->
    remote_ip_string_stp(ipv4, Role).


handle_remote_port(undefined, Role) ->
    remote_port_string(Role);
handle_remote_port(RemotePort, _) ->
    RemotePort.

remote_port_string(access) ->
    "10004";
remote_port_string(core4access) ->
    "10003";
remote_port_string(foreign) ->
    "10002";
remote_port_string(core4foreign) ->
    "10005".

stop_sipp_process(Pid) ->
    stop_sipp_process(Pid, graceful).

stop_sipp_process(Pid, graceful) ->
    Cmd = "kill -USR1 " ++ to_string(Pid),
    ?DEBUG("the command is ~p~n",[Cmd]),
    Res = os:cmd(Cmd),
    ?DEBUG("the command result is ~p~n",[Res]);
stop_sipp_process(Pid, enforced) ->
    os:cmd("kill -9 " ++ to_string(Pid)).

stop_uac_uas_sipp_processes([Pid|T] = _Pids, CallDuration) ->
    stop_sipp_process(Pid),
    timer:sleep(CallDuration+180000),
    case check_pid_alive(Pid) of
        false -> 
            ok;
        _ ->
            ?WARNING_MSG("Failed to stop the PID(~p) gracefully.~n",[Pid]),
            ?DEBUG("the PID(~p) status ~p~n", [Pid, check_pid_alive(Pid)]),
            stop_sipp_process(Pid, enforced)
    end,
    timer:sleep(120000),
    stop_uac_uas_sipp_processes(T, 0);

stop_uac_uas_sipp_processes([], _CallDuration) ->
  ok.

close_sipp()->
    Res = os:cmd("pgrep sipp"),
    case length(Res) of 
        0 -> ok;
        _ -> os:cmd("pkill sipp")
    end.

check_sipp_process_status(Pid, [SippPid|T]=_SippPids) ->
    Pid ! {ua, SippPid, check_pid_alive(SippPid)},
    check_sipp_process_status(Pid, T);
check_sipp_process_status(_Pid, []) ->
    ok.

check_sipp_call_status(Pid, [StatFile|T]=_StatFiles)->
    {ok, StatData} = get_stat_tail(StatFile),
    Pid ! {uaStat, StatFile, StatData},
    check_sipp_call_status(Pid, T);
check_sipp_call_status(_Pid, []) ->
    ok.

check_pid_alive(Pid) ->
    IsString=is_string(Pid),
    if 
        IsString -> 
            filelib:is_dir("/proc/" ++ Pid);
        is_integer(Pid) -> 
            filelib:is_dir("/proc/" ++ to_string(Pid));
        true ->
            ?WARNING_MSG("Wrong Pid Content:~p~n",[Pid]),
            false
    end.

check_sipp_stat(interval, StatData)->
    FailedCall_P = lists:nth(17, StatData),
    if
        FailedCall_P > 0 ->
            false;
        FailedCall_P == 0 ->
            true
    end;
check_sipp_stat(final, StatData)->
    TotalCreatedCall=lists:nth(13, StatData),
    FailedCall_C = lists:nth(18, StatData),
    
    ct:pal("Total number of calls is:~p~n", [TotalCreatedCall]),
    ct:pal("Number of failed calls is:~p~n", [FailedCall_C]),
    
    if 
        TotalCreatedCall > 0 ->
            CallLossRate = FailedCall_C / TotalCreatedCall;
        true ->
            CallLossRate = 0
    end,
    
    PrintInfo = [{"Total calls", to_list(TotalCreatedCall)},
                 {"Total failed calls", to_list(FailedCall_C)},
                 {"Error rate", to_list(CallLossRate*100) ++ "%"}],
    print(PrintInfo), 
    
    SIPpResult = if 
                     CallLossRate > ?CALL_LOSS_RATE ->
                         false;
                     true ->
                         true
                 end,

    SIPpResult.

get_stat_tail(StatFile) ->
    case filelib:is_regular(StatFile) of
        true ->
            TailStat = os:cmd("tail -1 " ++ StatFile),
            StrippedTailStat=string:strip(TailStat, right, $\n),
            RowList=convert_datatype(split_statistics(StrippedTailStat, ";")),
            {ok, RowList};
        false ->
            ?WARNING_MSG("the StatFile ~p doesn't exist~n", [StatFile]),
            {error, StatFile}
    end.

get_stat_head(StatFile) ->
    case filelib:is_regular(StatFile) of
        true ->
            HeadStat = os:cmd("head -1 " ++ StatFile),
            StrippedHeadStat=string:strip(HeadStat, right, $\n),
            RowList=split_statistics(StrippedHeadStat, ";"),
            {ok, RowList};
        false ->
            ?WARNING_MSG("the StatFile ~p doesn't exist~n", [StatFile]),
            {error, StatFile}
    end.

get_pid_str(Str)->
    {ok, Mp} = re:compile("PID=\\\[([0-9]+)\\\]"),
    case re:run(Str, Mp, [{capture, all_but_first, list}]) of
        {match, [PID]} -> {ok, PID};
        _ -> {error, Str}
    end.

convert_datatype(List) ->
    %% the first 5 items are date
    %% the next 13 items are counters 
    lists:sublist(List, 5)++[to_number(X)||X<-lists:sublist(List, 6, 13)].


write_time(now)->
    write_time(calendar:local_time());

write_time({{Year, Month, Day}, {Hour, Minute, Second}}=_Time)->
    lists:flatten(io_lib:format("~w-~.2.0w-~.2.0w_~.2.0w:~.2.0w:~.2.0w", [Year, Month, Day, Hour, Minute, Second])).

%%18 is for SIPp stat File 
split_statistics(String, StatDelimiter) ->
    split_statistics(String, StatDelimiter, 18).

split_statistics(String, StatDelimiter, Length) ->
    RowList = re:split(String, StatDelimiter, [{return, list}]),
    lists:sublist(RowList, Length).

print(PrintInfo) ->
    erlang:yield(),
    Subject = "Early ST test report",
    L1 = length(Subject),
    L = integer_to_list(trunc((69 - L1)/2 + L1)),
    io:format("+~69.0.-~+"),
    io:format("|~69."++ L ++". s|", [Subject]),
    io:format("+~69.35.-s+", ["+"]),
    lists:foreach(fun({Desp, Num}) ->
                       io:format("|~34.33. s|~34.20. s|", [Desp, Num]),
                       io:format("+~69.35.-s+", ["+"])
                  end, PrintInfo),

    io:format("+~69.0.-~+~n").

to_list(Integer) when is_integer(Integer)->
    erlang:integer_to_list(Integer);
to_list(Atom) when is_atom(Atom) ->
    erlang:atom_to_list(Atom);
to_list(Float) when is_float(Float) ->
    N = math:pow(10, 3),
    Result = round(Float*N)/N,
    lists:flatten(io_lib:print(Result));
to_list(0) ->
    "0";
to_list(0.0) ->
    "0".

is_string(Input)->
    io_lib:printable_unicode_list(Input).

to_string(Input) when is_integer(Input) ->
    erlang:integer_to_list(Input);
to_string(Input) when is_float(Input) ->
    erlang:float_to_list(Input);
to_string(Input) when is_atom(Input) ->
    erlang:atom_to_list(Input);
to_string(Input) ->
    case is_string(Input) of
        true -> Input;
        false -> lists:flatten(io_lib:print(Input)) 
    end. %% Is String already

to_number(Str) ->
    case string:to_float(Str) of
        {error, _} -> 
	    case string:to_integer(Str) of
		{error, _} -> 
		    ?WARNING_MSG("the String(~p) is not integer and float.~n", [Str]),
		    Str;
		{Int, _} -> 
		    Int
	    end;
        {Float, _} -> 
            Float
    end.





















-define(FILE_NAMES, [
		     {sgc1_ch, ["/blade/homedir/sgc1-ch-vmstat-", ?EST_LOG_DIR ++ "sgc1-ch-system-info-", ?EST_LOG_DIR ++ "sgc1-ch-plc-res-"]},
		     {sgc1_om, ["/blade/homedir/sgc1-om-vmstat-", ?EST_LOG_DIR ++ "sgc1-om-system-info-", ?EST_LOG_DIR ++ "sgc1-om-plc-res-"]},
		     {ommp_ch, ["/blade/homedir/ommp-ch-vmstat-", ?EST_LOG_DIR ++ "ommp-ch-system-info-", ?EST_LOG_DIR ++ "ommp-ch-plc-res-"]},
		     {ommp_om, ["/blade/homedir/ommp-om-vmstat-", ?EST_LOG_DIR ++ "ommp-om-system-info-", ?EST_LOG_DIR ++ "ommp-om-plc-res-"]}
		    ]).
%% -----------------------------------------------------------------------
%% @spec prepare_systeminfo_filenames(Input :: lists()) -> lists()
%% @doc Prepare the log files according which blades you want to monitor.
%% <pre>
%%   <strong>Example</strong>:
%%      prepare_systeminfo_filenames([sgc1_ch])
%%      prepare_systeminfo_filenames([sgc1_ch, sgc1_om, ommp_ch, ommp_om])
%% </pre>
%% @end
%% -----------------------------------------------------------------------
prepare_systeminfo_filenames(Input) when is_list(Input) ->
    CurrentTime = calendar:local_time(),
    Filename_suffix = write_time(CurrentTime) ++ ".log",
    prepare_systeminfo_filenames_help(Input, Filename_suffix, []);
prepare_systeminfo_filenames(_) ->
    ct:pal("prepare_systeminfo_filenames: wrong input, just like [sgc1_ch, sgc1_om, ommp_ch, ommp_om]~n"),
    [].

prepare_systeminfo_filenames_help([], _, Res) ->
    lists:reverse(Res);
prepare_systeminfo_filenames_help([H | T], Filename_suffix, Res) ->
    case proplists:get_value(H, ?FILE_NAMES) of
        [VmStat, SysInfo, Plc] ->
	    TempRes =  {H, {SysInfo ++ Filename_suffix, VmStat ++ Filename_suffix, Plc ++ Filename_suffix}},
            print_systeminfo_file(TempRes),
            prepare_systeminfo_filenames_help(T, Filename_suffix, [TempRes | Res]);
        undefined ->
            ct:pal("There have wrong input parmeters, please select from (sgc1_ch, sgc1_om, ommp_ch, ommp_om)~n"),
            prepare_systeminfo_filenames_help(T, Filename_suffix, Res)
    end.

print_systeminfo_file({Blade, {SysInfo_Filename, Vmstat_Filename, PlcRes_Filename}}) ->
    ct:pal("Blade: ~p~nSysInfo_Filename:~p~nVmstat_Filename:~p~nPlcRes_Filename:~p~n",
	   [Blade, SysInfo_Filename, Vmstat_Filename, PlcRes_Filename]).



%% -----------------------------------------------------------------------
%% @spec start_collect_system_infos(Input :: lists()) -> lists()
%% @doc Start collecting the logs.<br></br>
%% <strong>Note</strong>: The input parameter of this function is the result of <code>sbgEstSupport:prepare_systeminfo_filenames/1</code>.
%% @end
%% -----------------------------------------------------------------------
start_collect_system_infos(FileNames) ->
    start_collect_system_infos(FileNames, []).

start_collect_system_infos([], Res) ->
    lists:reverse(Res);
start_collect_system_infos([{sgc1_ch, {Sys, VmStat, Plc}} | T], Res) ->
    {ok, SysInfoTimerRef} = timer:apply_interval(timer:hms(0, 2, 0),
						 ?MODULE, collect_system_info, [sgc1_ch, Sys, VmStat]),
    {ok, PlcResTimerRef}  = timer:apply_interval(timer:hms(0, 5, 0),
						 sbgEstSupport, collect_plc_res, [Plc]),
    start_collect_system_infos(T,
			       [{sgc1_ch, SysInfoTimerRef, PlcResTimerRef} | Res]);

start_collect_system_infos([{sgc1_om, {Sys, VmStat, Plc}} | T], Res) ->
    {ok, SysInfoTimerRef} = timer:apply_interval(timer:hms(0, 2, 0),
						 ?MODULE, collect_system_info, [sgc1_om, Sys, VmStat]),
    {ok, PlcResTimerRef} = timer:apply_interval(timer:hms(0, 5, 0),
						sbgEstSupport, collect_plc_res, [Plc]),
    start_collect_system_infos(T, [{sgc1_om, SysInfoTimerRef, PlcResTimerRef} | Res]);

start_collect_system_infos([{ommp_ch, {Sys, VmStat, Plc}} | T], Res) ->
    {ok, SysInfoTimerRef} = timer:apply_interval(timer:hms(0, 2, 0),
						 ?MODULE, collect_system_info, [ommp_ch, Sys, VmStat]),
    {ok, PlcResTimerRef} = timer:apply_interval(timer:hms(0, 5, 0),
						sbgEstSupport, collect_plc_res, [Plc]),
    start_collect_system_infos(T, [{ommp_ch, SysInfoTimerRef, PlcResTimerRef} | Res]);

start_collect_system_infos([{ommp_om, {Sys, VmStat, Plc}} | T], Res) ->
    {ok, SysInfoTimerRef} = timer:apply_interval(timer:hms(0, 2, 0),
						 ?MODULE, collect_system_info, [ommp_om, Sys, VmStat]),
    {ok, PlcResTimerRef} = timer:apply_interval(timer:hms(0, 5, 0),
						sbgEstSupport, collect_plc_res, [Plc]),
    start_collect_system_infos(T, [{ommp_om, SysInfoTimerRef, PlcResTimerRef} | Res]).


-define(BLADES, [
		 {sgc1_ch, ?SGCchNode},
		 {sgc1_om, ?SGComNode},
		 {ommp_ch, ?SBGchNode},
		 {ommp_om, ?SBGomNode}
		]).
collect_system_info(BladeType, SysInfo_Filename, Vmstat_Filename) ->
    case proplists:get_value(BladeType, ?BLADES) of
        undefined ->
            ct:pal("The BladeType is wrong, pleaea refer: ~p~n", [?BLADES]);
        Node ->
	    io:format("data is collecting, node:~p~n", [Node]),
            Mem_result = jpTrpc:call(Node, ets, tab2list, [plcInfo]),
            ProcessNum = length(jpTrpc:call(Node, erlang, processes, [])),
            Result = lists:sort(Mem_result ++ [{process_Num, ProcessNum}]),

            {ok, File_handler} = file:open(SysInfo_Filename, [append]),
            io:format(File_handler, "~p~n", [Result]),
            ok = file:close(File_handler),

            VmCmd = "vmstat 5 5 >>" ++ Vmstat_Filename,
            jpTrpc:call(Node, os, cmd, [VmCmd])
    end.



%% -----------------------------------------------------------------------
%% @spec stop_collect_system_infos(Input :: lists()) -> lists()
%% @doc Stop collecting the logs.<br></br>
%% <strong>Note</strong>: The input parameter of this function is the result of <code>sbgEstSupport:start_collect_system_infos/1</code>.
%% @end
%% -----------------------------------------------------------------------
stop_collect_system_infos(TimerRefs) ->
    Fun = fun({BladeType, SysTimerRef, PlcTimerRef}) ->
		  ct:pal("stop_collect_system_infos: ~p ~p ~p~n",
			 [BladeType, SysTimerRef, PlcTimerRef]),
		  timer:cancel(SysTimerRef),
		  timer:cancel(PlcTimerRef)
	  end,
    lists:foreach(Fun, TimerRefs).


%% -----------------------------------------------------------------------
%% @spec generate_log_statistics(FileInfos :: lists(), UacStatFile :: string(), UasStatFile :: string(), BladeType :: atom()) -> ok
%% @doc Analyse the log files and draw graph.<br></br>
%% <pre>
%% <strong>Note</strong>:
%%   FileInfos    is the result of sbgEstSupport:prepare_systeminfo_filenames/1.
%%   UacStatFile  can get from the result of sbgEstSupport:prepare_sipp_cmd/3.
%%   UasStatFile  can get from the result of sbgEstSupport:prepare_sipp_cmd/3.
%%   BladeType    sgc1_ch | sgc1_om | ommp_ch | ommp_om
%% </pre>
%% @end
%% -----------------------------------------------------------------------
generate_log_statistics(FileInfos, UacStatFile, UasStatFile, BladeType)
        when is_atom(BladeType) andalso is_list(FileInfos) ->
    case proplists:get_value(BladeType, FileInfos) of
        {SysInfo, VmState, _PlcInfo} ->
            ct:pal("generate_log_statistics for blade: ~p~n", [BladeType]),
            %% --- transfer the collected vmstat file to LMWP ---
            Local_Vmstat = sbgEstSupport:transfer_vmstat_file(VmState, sgc1, ch),

            timer:sleep(60000),

            %% ------ script to draw graph need to be updated-----
            SysInfo_Svg = sbgEstSupport:draw_graph(SysInfo, sysinfo),
            Vmstat_Svg = sbgEstSupport:draw_graph(Local_Vmstat, vmstat),
            Uac_Svg = sbgEstSupport:draw_graph(UacStatFile, passrate),
            Uas_Svg = sbgEstSupport:draw_graph(UasStatFile, passrate),
            timer:sleep(60000),

            %% --- generate graph on jpt log page
            sbgEstSupport:generate_graph_on_log_html(SysInfo_Svg, Vmstat_Svg,
                                                     Uac_Svg, Uas_Svg);
        undefined ->
            ct:pal("generate_log_statistics: The fourth parameter should be atom: sgc1_ch, sgc1_om, ommp_ch, ommp_om.~n")
    end;
generate_log_statistics(_, _, _, _) ->
    ct:pal("generate_log_statistics: The input parameters are wrong, the fourth should be atom: sgc1_ch, sgc1_om, ommp_ch, ommp_om.~n").
