///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000-2023 Ericsson Telecom AB
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v2.0
// which accompanies this distribution, and is available at
// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
///////////////////////////////////////////////////////////////////////////////
//
//  File: 		    SCCP_selftest.ttcn
//  Description:  SS7 SCCP basic test
//                according to specification ITU-T SS7 SCCP, ANSI ..., TCC ... 
//  References:   ITU-T: Recommendation Q.711-Q.714, 
//                ANSI  , 
//                TCC 
//  Rev:          R7A
//  Updated:	    2007-04.12
//  Contact:      http://ttcn.ericsson.se
//

module SCCP_selftest
{//startmodule

modulepar 
{
  hexstring tsp_own_GT := '0614377760'H;
  hexstring tsp_remote_GT := '0614375760'H;
  integer tsp_SSN := 2; //8:MSC 5:MAP see 3.4.2.2/Q.713
  octetstring tsp_SIO := '83'O;//SCCP national
  integer tsp_own_SPC := 461087; // =0x07091E 
  integer tsp_remote_SPC := 461086; // =0x07091D
  integer tsp_SLS := 0;
  charstring tsp_sccp_serviceType := "mtp3_itu"
}//modulepar

import from General_Types all;

import from MTP3asp_Types all;
import from MTP3asp_PortType all;

import from SCCPasp_Types  all;
import from SCCP_Types  all;
import from SCCP_Emulation all;


//==================================================================
// MTPsim component
// Description: Simulates two MTP stacks for two MTP3-User
//              to implement this configuration:
//              MTPsim includes MTP3/1 and MTP3/2
//              MTPsim only receives TRANSFER_req and sends TRANSFER_ind
//              with the same content
//       +----------+       +----------+
//       |SCCP-userA| <--->|SCCP-userB|          = MTC
//       +----------+      +----------+
//            | A               | B
//       +----------+       +----------+
//       | SCCP   A | <--->| SCCP B   |
//       +----------+      +----------+
//            | A               | B
//       +-----------------------------+
//       | MTP3  1.        | MTP3   2.|         = MTP3sim
//       +----------------------------+
//
//==================================================================

group MTPsim 
{


type component MTPsim_CT {
  port MTP3asp_SP_PT MTP_A_PORT
  port MTP3asp_SP_PT MTP_B_PORT
}

function MTPsim_EventHandler(  ) runs on MTPsim_CT
{
  var ASP_MTP3_TRANSFERreq vl_MTP3_TRANSFERreq;
  var ASP_MTP3_TRANSFERind vl_MTP3_TRANSFERind
  alt{
    [] MTP_A_PORT.receive( ASP_MTP3_TRANSFERreq:? ) -> value vl_MTP3_TRANSFERreq 
          
       { 
          MTP_B_PORT.send( t_ASP_MTP3_TRANSFERind( 
                                            vl_MTP3_TRANSFERreq.sio,   
                                            vl_MTP3_TRANSFERreq.opc,
                                            vl_MTP3_TRANSFERreq.dpc,
                                            vl_MTP3_TRANSFERreq.sls,
                                            vl_MTP3_TRANSFERreq.data ) ) ;
          repeat;
       }//A.receive
    [] MTP_B_PORT.receive( ASP_MTP3_TRANSFERreq:? ) -> value vl_MTP3_TRANSFERreq
          
      { 
        MTP_A_PORT.send( t_ASP_MTP3_TRANSFERind (
                                            vl_MTP3_TRANSFERreq.sio,   
                                            vl_MTP3_TRANSFERreq.opc,
                                            vl_MTP3_TRANSFERreq.dpc,
                                            vl_MTP3_TRANSFERreq.sls,
                                            vl_MTP3_TRANSFERreq.data ));
        repeat;
      }//B.receive
              
  }//alt

} //MTPsim_EventHandler

}//group MTPsim

// Main test component with behaviour SCCPuserA andSCCPuserB
type component MTC_CT {
  var SCCP_PAR_Address v_CalledAddress, v_CallingAddress;
  var integer v_testvalue;
  var MTPsim_CT vc_MTPsim;
  var SCCP_CT vc_SCCP_A, vc_SCCP_B;
  var MSC_SCCP_MTP3_parameters v_BootA;  
  var MSC_SCCP_MTP3_parameters v_BootB;
  var SCCP_PAR_Connection_Id v_cid_A, v_cid_B;
  
  port SCCPasp_PT A_PORT; //SCCPuserA  
  port SCCPasp_PT B_PORT  //SCCPuserB
  
}

function initBootParams() runs on MTC_CT
{
  v_BootA :=
  { sio:=
    { ni:= substr(oct2bit(tsp_SIO),0,2),
      prio:= substr(oct2bit(tsp_SIO),2,2),
      si:= substr(oct2bit(tsp_SIO),4,4)
    },
    opc:=tsp_own_SPC,
    dpc:=tsp_remote_SPC, 
    sls:=tsp_SLS, 
    sccp_serviceType:=tsp_sccp_serviceType,
    ssn:= tsp_SSN
  };
 
  v_BootB :=
  { sio:=
    { ni:= substr(oct2bit(tsp_SIO),0,2),
      prio:= substr(oct2bit(tsp_SIO),2,2),
      si:= substr(oct2bit(tsp_SIO),4,4)
    },
    opc:=tsp_remote_SPC,
    dpc:=tsp_own_SPC, 
    sls:=tsp_SLS, 
    sccp_serviceType:=tsp_sccp_serviceType,
    ssn:= tsp_SSN
  };
  return;
} //initBootParams
function init() runs on MTC_CT
{

  
  initBootParams();
  log("v_BootA:",v_BootA);
  log("v_BootB: ",v_BootB);
  vc_MTPsim:= MTPsim_CT.create; 
  
  // Protocol Stack A creation & connections:
  vc_SCCP_A:=SCCP_CT.create;
  connect(vc_SCCP_A:MTP3_SCCP_PORT,vc_MTPsim:MTP_A_PORT); 
  connect(self:A_PORT,vc_SCCP_A:SCCP_SP_PORT);
  
  // Protocol Stack B creation & connections:
  vc_SCCP_B:=SCCP_CT.create;
  connect(vc_SCCP_B:MTP3_SCCP_PORT,vc_MTPsim:MTP_B_PORT);
  connect(self:B_PORT,vc_SCCP_B:SCCP_SP_PORT);

  // Start stacks: 
  vc_MTPsim.start( MTPsim_EventHandler() ); 

  vc_SCCP_A.start( SCCPStart( v_BootA ) ); // Bootparameters !!! cont here!!!
  
  vc_SCCP_B.start( SCCPStart(v_BootB));
  log( "init() is done");
  
}// init

function terminate( ) runs on MTC_CT
{
  log( "termitate() started");
  /*while( all component.running != true )
  {
    //waits
  }*/
    all component.stop;
    disconnect(vc_SCCP_A:MTP3_SCCP_PORT, vc_MTPsim:MTP_A_PORT); 
    disconnect(self:A_PORT,vc_SCCP_A:SCCP_SP_PORT);
  
    disconnect(vc_SCCP_B:MTP3_SCCP_PORT,vc_MTPsim:MTP_B_PORT);
    disconnect(self:B_PORT,vc_SCCP_B:SCCP_SP_PORT);
    log(" all components stopped");
    self.stop;
} //terminate

// function getOddEven returns '0' 
// if even number of dec digit can be found in GT see Q.713
function getOddEven( in hexstring pl_GT) return bitstring
{
   return int2bit( (lengthof(pl_GT) mod 2) ,1);
}

function getOddEvenEnc( in hexstring pl_GT) return bitstring
{
   if( (lengthof(pl_GT) mod 2) == 0 ) { return '0010'B;} //even
   else { return '0001'B;} //odd
}

//******************************************************************
//function setAddresses_gti0001() runs on MTC_CT
// Sets CalledAddress and CallingAddress as a gti001-type address
// according to the cfg file.
//******************************************************************

function setAddresses_gti0001() runs on MTC_CT
{
    if( (tsp_sccp_serviceType == "mtp3_itu") or 
        (tsp_sccp_serviceType == "mtp3b_itu") or
        (tsp_sccp_serviceType == "mtp3_ttc") or
        (tsp_sccp_serviceType == "mtp3b_ttc")
      ) {
      v_CalledAddress :={ 
        addressIndicator  := {
          pointCodeIndic := '1'B,
          ssnIndicator := '1'B,
          globalTitleIndic := '0001'B,
          routingIndicator := '0'B
        },//addressIndicator
        signPointCode     := SCCP_SPC_int2bit(tsp_remote_SPC, tsp_sccp_serviceType, tsp_SIO), // see SCCP_Emulation.ttcn 
        subsystemNumber   := tsp_SSN,
        globalTitle := {
          gti0001:= {
            natureOfAddress := '0000011'B,
            oddeven := getOddEven( tsp_remote_GT ),
            globalTitleAddress := tsp_remote_GT
          }
        }//globalTitle
      } // v_CalledAddress
    
      v_CallingAddress :={ 
        addressIndicator  := {
          pointCodeIndic := '1'B,
          ssnIndicator := '1'B,
          globalTitleIndic := '0001'B,
          routingIndicator := '0'B
        },//addressIndicator
        signPointCode     := SCCP_SPC_int2bit(tsp_own_SPC, tsp_sccp_serviceType, tsp_SIO), // see SCCP_Emulation.ttcn 
        subsystemNumber   := tsp_SSN,
        globalTitle:= { 
          gti0001 := {
            natureOfAddress := '0000011'B,
            oddeven := getOddEven( tsp_own_GT ), 
            globalTitleAddress := tsp_own_GT
          }
        }//globalTitle
      } // v_CallingAddress
      
    } else if(
        (tsp_sccp_serviceType == "mtp3_ansi") or
        (tsp_sccp_serviceType == "mtp3b_ansi")  )        
    {
      
      v_CalledAddress :={ 
        addressIndicator  := {
          pointCodeIndic := '1'B,
          ssnIndicator := '1'B,
          globalTitleIndic := '0001'B,
          routingIndicator := '0'B
        },//addressIndicator
        signPointCode     := SCCP_SPC_int2bit(tsp_remote_SPC, tsp_sccp_serviceType, tsp_SIO), // see SCCP_Emulation.ttcn 
        subsystemNumber   := tsp_SSN,
        globalTitle := {
          gti0011:= {
            translationType   := int2oct(7,1),
            encodingScheme    := getOddEvenEnc( tsp_remote_GT ),        
            numberingPlan     := '0111'B,  //ISDN/mobile numbering plan, see T1.112.3-2001/3.4.2.3.1            
            globalTitleAddress:= tsp_remote_GT
          }
        }//globalTitle
      } // v_CalledAddress
      
      v_CallingAddress :={ 
        addressIndicator  := {
          pointCodeIndic := '1'B,
          ssnIndicator := '1'B,
          globalTitleIndic := '0001'B,
          routingIndicator := '0'B
        },//addressIndicator
        signPointCode     := SCCP_SPC_int2bit(tsp_remote_SPC, tsp_sccp_serviceType, tsp_SIO), // see SCCP_Emulation.ttcn 
        subsystemNumber   := tsp_SSN,
        globalTitle := {
          gti0011:= {
            translationType   := int2oct(7,1),
            encodingScheme    := getOddEvenEnc( tsp_own_GT ),        
            numberingPlan     := '0111'B,  //ISDN/mobile numbering plan, see T1.112.3-2001/3.4.2.3.1            
            globalTitleAddress:= tsp_own_GT
          }
        }//globalTitle
      } // v_CallingAddress
      
    }//if
    else  
    {
      log( "wrong tsp_sccp_serviceType ->exit ");
      setverdict( fail );
    } 
}//setAddresses_gti001

function f_SendAndReceive1N_UNITDATA(in octetstring pl_userdata) runs on MTC_CT 
{
  var ASP_SCCP_N_UNITDATA_ind vl_N_UNITDATA_ind;
  timer TL_timer:= 40.0;
  TL_timer.start;
  log("A_PORT.send follows");
  log("Addresses:",v_CalledAddress, v_CallingAddress);
  A_PORT.send( t_ASP_N_UNITDATA_req(  v_CalledAddress,
                                      v_CallingAddress,
                                      '00000001'B, //sequence control
                                      '00000001'B, //return option
                                      pl_userdata,
                                      omit ) );
  log("A_PORT.send executed");
  alt { 
    [] B_PORT.receive( tr_ASP_N_UNITDATA_ind ) -> value vl_N_UNITDATA_ind
      { 

        if( (vl_N_UNITDATA_ind.calledAddress == v_CalledAddress ) and
        (vl_N_UNITDATA_ind.callingAddress == v_CallingAddress) and 
        (vl_N_UNITDATA_ind.userData == pl_userdata) )
        {
          log("Correct  CalledAddress, CallingAddress and userData received, data are correct");
          setverdict(pass);
        }
        else
        {
          log("Some data corrupted");
          log("Original data:", v_CalledAddress, v_CallingAddress, pl_userdata);
          setverdict( fail );
        }
      };       
    [] TL_timer.timeout 
      { 
        setverdict( fail );
        log("Timeout....");
      };                                
  } //alt  
  TL_timer.stop;
}//f_SendAndReceive1N_UNITDATA

/****************************************************
Connection Oriented Part
****************************************************/

/****************************************************
function f_connect
Establishes a connection
(Sends an ASP_SCCP_N_CONNECT_req on A_PORT and waits for 
N_CONNECT_ind on B_PORT. If it is received, 
it sends back an ASP_SCCP_N_CONNECT_res on B_PORT and waits for
N_CONNECT_cfm on A_PORT)
****************************************************/
function f_connect() runs on MTC_CT return boolean
{
  var ASP_SCCP_N_CONNECT_ind vl_N_CONNECT_ind;
  var ASP_SCCP_N_CONNECT_cfm vl_N_CONNECT_cfm;
  setverdict(none);
  v_cid_A := 13;
  timer TL_timer:= 40.0;
  TL_timer.start; 
  // A Sends ASP_SCCP_N_CONNECT_req , receives
  A_PORT.send( t_ASP_N_CONNECT_req( v_CalledAddress,
                                    v_CallingAddress,
                                    omit, //expeditedDataSel
                                    omit, //QoS
                                    omit, //userData
                                    v_cid_A, 
                                    omit //importance
                                    ) );
  alt {
    [] B_PORT.receive( tr_ASP_N_CONNECT_ind ) -> value vl_N_CONNECT_ind
      {
        v_cid_B := vl_N_CONNECT_ind.connectionId;
        B_PORT.send( t_ASP_N_CONNECT_res( omit,// respondingAddress
                                          omit,//expeditedDataSel
                                          omit,//qualityOfService
                                          omit, //userData
                                          v_cid_B,
                                          omit //importance 
                                          ));
      } 
    [] B_PORT.receive 
      {
        log( "unexpected asp received for ASP_SCCP_N_CONNECT_req, failed");
        setverdict( fail );
        return false;
      }
    [] TL_timer.timeout 
      {
        setverdict( pass );
        log("Timeout....");
        return false;
      }
  }
  
  // receives ASP_SCCP_N_CONNECT_cfm  
  alt {
    [] A_PORT.receive( tr_ASP_N_CONNECT_cfm ) -> value vl_N_CONNECT_cfm
      {
        setverdict( pass );
        log("f_connect finished successfully");
        return true;
      }
    [] TL_timer.timeout 
      {
        setverdict( pass );
        log("Timeout....");
        return false;
      }
  }// alt
  log("f_connect finished");
  return false;
}//f_connect

/****************************************************
function f_send
Sends an ASP_SCCP_N_DATA_req on A_PORT and waits for answer in
B_PORT
****************************************************/
function f_send(in octetstring pl_userdata) runs on MTC_CT
{
  var ASP_SCCP_N_DATA_ind vl_N_DATA_ind;
  timer TL_timer:= 120.0;
  TL_timer.start;
  A_PORT.send( t_ASP_N_DATA_req ( pl_userdata, v_cid_A, omit) ) ;
  alt {
    [] B_PORT.receive( tr_ASP_N_DATA_ind ) -> value vl_N_DATA_ind
    {
      if( vl_N_DATA_ind.userData == pl_userdata )
      {
        log( "userData received correctly" );
        setverdict( pass );
      } 
      else
      {
        log("user data mismatch error in f_send()")
        setverdict(fail);
      }
      
    }//B_PORT.receive( tr_ASP_N_DATA_ind )
    
    [] B_PORT.receive 
      {
        log( "unexpected asp received for ASP_SCCP_N_DATA_req, failed");
        setverdict( fail );
      }
    [] TL_timer.timeout 
      {
        setverdict( pass );
        log("Timeout....");
      }
      
   } //alt
}//f_send

//f_disconnect with timeout

function f_disconnect( ) runs on MTC_CT 
{
  var ASP_SCCP_N_DISCONNECT_ind vl_N_DISCONNECT_ind;
  timer TL_timer:= 5.0;
  TL_timer.start;
  A_PORT.send(t_ASP_N_DISCONNECT_req( omit, // respondingAddress
                                      0,  //reason : end user originated, see 3.11/Q.713
                                      omit, //userData
                                      v_cid_A, 
                                      omit ))
  alt {
    [] B_PORT.receive(tr_ASP_N_DISCONNECT_ind) -> value vl_N_DISCONNECT_ind
      {
        setverdict( pass );
      }
    [] B_PORT.receive
      {
        log("unexpected asp received on B_PORT instead of ASP_SCCP_N_DISCONNECT_ind");
        //repeat;
        setverdict(fail);
      }
    [] TL_timer.timeout 
      { 
        setverdict( fail );
        log("Timeout....");
      };           
  }//alt
  
  //give time for inner release complete (rlc):
  alt {
    [] TL_timer.timeout 
      { 
        setverdict( pass );
        log("Stopped with expected timeout");
      };           
  }  
}//f_disconnect

//===================================================
// Testcases
//===================================================

/****************************************************
tc_ConnlessSendingShortASP
Sends a 300 octet long userdata in one ASP_SCCP_N_UNITDATA_req
and receives it in one ASP_SCCP_N_UNITDATA_req.
SCCP transfers information 
in udp or (forced) xudp packets.
****************************************************/
testcase tc_ConnlessSendingShortASP() runs on MTC_CT 
{
  var octetstring vl_userdata;
  init();
  setAddresses_gti0001();
  vl_userdata :='12345678901234567890'O;  
  f_SendAndReceive1N_UNITDATA( vl_userdata );
  terminate(); 
 } //tc_ConnlessSendingShortASP

/****************************************************
 tc_ConnlessSendingLongASP
 Sends a 300 octet long userdata in one ASP_SCCP_N_UNITDATA_req
 and receives it in one ASP_SCCP_N_UNITDATA_req.
 It is used for segmentation and reassembly.
 SCCP transfers information 
in xudp packets 
****************************************************/
testcase tc_ConnlessSendingLongASP() runs on MTC_CT 
{
  var octetstring vl_userdata;
  var integer vl_i;  
  init();
  setAddresses_gti0001();
  vl_userdata := ''O;
  for(vl_i:=0;vl_i<30;vl_i:=vl_i+1) {
    vl_userdata := vl_userdata &'12345678901234567890'O;  
  }
  f_SendAndReceive1N_UNITDATA( vl_userdata );
  terminate(); 
}//tc_ConnlessSendingLongASP

/****************************************************
tc_ConnOrientedShortASPSending
****************************************************/
testcase tc_ConnOrientedShortASPSending() runs on MTC_CT 
{
  var octetstring vl_userdata;
  init();
  setAddresses_gti0001();
  vl_userdata := '12345678901234567890'O;
  if(f_connect( ))
  {
    f_send(vl_userdata);
    f_disconnect();
  }
  terminate(); 
}
/****************************************************
tc_ConnOrientedLongASPSending
****************************************************/
testcase tc_ConnOrientedLongASPSending() runs on MTC_CT 
{
  var octetstring vl_userdata;
  var integer vl_i;  
  init();
  setAddresses_gti0001();
  vl_userdata := ''O;
  for(vl_i:=0;vl_i<30;vl_i:=vl_i+1) {
    vl_userdata := vl_userdata &'12345678901234567890'O;  
  }
  if(f_connect( ))
  {
    f_send(vl_userdata);
    //f_SendAndReceive1N_UNITDATA( vl_userdata );
    f_disconnect();
  }
  terminate(); 
}
/****************************************************
 CONTROL
****************************************************/
control 
{
  execute( tc_ConnlessSendingShortASP() );
  execute( tc_ConnlessSendingLongASP() );
  execute( tc_ConnOrientedShortASPSending());
  execute( tc_ConnOrientedLongASPSending());
}

}//module