614 lines
18 KiB
C
614 lines
18 KiB
C
/*
|
|
*********************************************************************************
|
|
* *
|
|
* NOTICE: *
|
|
* *
|
|
*********************************************************************************
|
|
* mgcPhyPort <---------- mgcChnls <-----------mgcConnections
|
|
*/
|
|
|
|
|
|
/*********************************************************************************
|
|
* <mgc_chnl_info.c>
|
|
* This file should provide CHNL_INFO APIs used in MGC module. Every chnl should be attached to
|
|
*a phyport if used, and could contain servral connections
|
|
*
|
|
* Author Date
|
|
* ------ ------
|
|
* Sam Yao Aug 2008
|
|
*********************************************************************************/
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/* INCLUDE HEADER FILES */
|
|
/*-----------------------------------------------------------------------*/
|
|
#include "./include/mgc_chnl_info.h"
|
|
#include "./include/mgc_debug.h"
|
|
#include "./include/mgc_conn_info.h"
|
|
#include "./include/mgc_phy_port.h"
|
|
#include "./include/mgc_mg_info.h"
|
|
#include "./include/mgc_mgcp.h"
|
|
|
|
static CHNL_INFO mgcChnl[MGC_MAX_NUM_OF_CHNL];
|
|
/*-----------------------------------------------------------------------
|
|
CHNL_INFO M A N A G E R
|
|
------------------------------------------------------------------------*/
|
|
|
|
/***************************************************************************
|
|
* mgc_chnl_info_init
|
|
* ------------------------------------------------------------------------
|
|
* General: init CHNL_INFO structure objcet
|
|
* Return Value: void.
|
|
* ------------------------------------------------------------------------
|
|
* Arguments:
|
|
* Input: pChnlInfo- the pointer of the CHNL_INFO object which should be init
|
|
* id - the id of the CHNL_INFO object
|
|
***************************************************************************/
|
|
void mgc_chnl_info_init(CHNL_INFO *pChnlInfo , int id)
|
|
{
|
|
int i;
|
|
if((pChnlInfo == NULL)||(id < 0))
|
|
return;
|
|
|
|
pChnlInfo->id = id;
|
|
pChnlInfo->status = MGC_CHNL_INFO_STATUS_UNDEF;
|
|
pChnlInfo->pPhyPort = NULL;
|
|
pChnlInfo->connectNum = 0;
|
|
pChnlInfo->monFlag = 0;
|
|
pChnlInfo->maxConnectNum = 0;
|
|
for(i=0 ; i<MGC_MAX_NUM_OF_CHNL_CON ; i++)
|
|
{
|
|
pChnlInfo->pConnection[i] = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void mgc_chnl_info_setup(void)
|
|
{
|
|
int i;
|
|
CHNL_INFO *pChnlInfo = NULL;
|
|
|
|
for(i=0 ; i<MGC_MAX_NUM_OF_CHNL ; i++)
|
|
{
|
|
pChnlInfo = &mgcChnl[i];
|
|
mgc_chnl_info_init(pChnlInfo, i);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* mgc_chnl_info_clear
|
|
* ------------------------------------------------------------------------
|
|
* General: clear and init CHNL_INFO structure objcet , if any connection is created in this chnl,
|
|
* DLCX will be sent out
|
|
* Return Value: void.
|
|
* ------------------------------------------------------------------------
|
|
* Arguments:
|
|
* Input: pChnlInfo- the pointer of the CHNL_INFO object which should be init
|
|
***************************************************************************/
|
|
void mgc_chnl_info_clear(CHNL_INFO *pChnlInfo)
|
|
{
|
|
int i;
|
|
CONNECT_INFO *pConnect = NULL;
|
|
MGCP_PARA mgcpPara;
|
|
if(pChnlInfo == NULL)
|
|
return;
|
|
|
|
for(i=0 ; i<MGC_MAX_NUM_OF_CHNL_CON ; i++)
|
|
{
|
|
pConnect = pChnlInfo->pConnection[i];
|
|
|
|
if(pConnect == NULL)
|
|
continue;
|
|
|
|
if((mgc_connect_get_status(pConnect) == MGC_CONNECT_STATUS_CREATED)||(mgc_connect_get_status(pConnect) == MGC_CONNECT_STATUS_CREATE))
|
|
{
|
|
if(mgc_connect_dlcx_para_create(pConnect , &mgcpPara) == TRUE)
|
|
MGCP_req(mgc_mgcp_get_sap_index(), MGCP_CMD_DLCX , 0xFFFF , &mgcpPara);
|
|
}
|
|
mgc_connect_init(pConnect , pConnect->id);
|
|
}
|
|
|
|
mgc_chnl_info_init(pChnlInfo , pChnlInfo->id);
|
|
return;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* mgc_chnl_info_get_unused_chnl
|
|
* ------------------------------------------------------------------------
|
|
* General: get a unused CHNL_INFO structure
|
|
* Return Value: if failure return NULL , else return a pointer
|
|
* ------------------------------------------------------------------------
|
|
* Arguments:
|
|
* Input: void
|
|
***************************************************************************/
|
|
CHNL_INFO *mgc_chnl_info_get_unused_chnl(void)
|
|
{
|
|
int i;
|
|
CHNL_INFO *pChnlInfo = NULL;
|
|
|
|
for(i=0 ; i<MGC_MAX_NUM_OF_CHNL ; i++)
|
|
{
|
|
pChnlInfo = &mgcChnl[i];
|
|
|
|
if(pChnlInfo->pPhyPort != NULL)
|
|
continue;
|
|
|
|
return pChnlInfo;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* mgc_chnl_info_attach_to_phy_port
|
|
* ------------------------------------------------------------------------
|
|
* General: record the owner phyPort of the chnl
|
|
* Return Value: if failure return FALSE , else return TRUE
|
|
* ------------------------------------------------------------------------
|
|
* Arguments:
|
|
* Input: pChnlInfo - the pointer of the CHNL_INFO object which need to record it's own phyPort
|
|
* pPhyPort - the pointer of the PHY_PORT_INFO object which is the owner of the chnl
|
|
***************************************************************************/
|
|
BOOL mgc_chnl_info_attach_to_phy_port(CHNL_INFO *pChnlInfo , PHY_PORT_INFO *pPhyPort)
|
|
{
|
|
if((pChnlInfo == NULL) || (pPhyPort == NULL))
|
|
return FALSE;
|
|
|
|
if(pChnlInfo->status != MGC_CHNL_INFO_STATUS_UNDEF)
|
|
return FALSE;
|
|
|
|
switch(pPhyPort->portType)
|
|
{
|
|
case MGC_PHY_PORT_TYPE_E1:
|
|
case MGC_PHY_PORT_TYPE_T1:
|
|
case MGC_PHY_PORT_TYPE_ANALOG:
|
|
case MGC_PHY_VIRTUAL_TYPE_INTERNAL:
|
|
pChnlInfo->maxConnectNum = 1;
|
|
break;
|
|
case MGC_PHY_VIRTUAL_TYPE_TANDEM:
|
|
pChnlInfo->maxConnectNum = 2;
|
|
break;
|
|
case MGC_PHY_VIRTUAL_TYPE_SURVEILLANCE:
|
|
pChnlInfo->maxConnectNum = 1;
|
|
break;
|
|
case MGC_PHY_VIRTUAL_TYPE_ANN:
|
|
pChnlInfo->maxConnectNum = MGC_MAX_NUM_OF_CHNL_CON;
|
|
break;
|
|
default:
|
|
pChnlInfo->maxConnectNum = 0;
|
|
break;
|
|
}
|
|
|
|
pChnlInfo->pPhyPort = pPhyPort;
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* mgc_chnl_info_status_set
|
|
* ------------------------------------------------------------------------
|
|
* General: set the status of the CHNL_INFO object
|
|
* Return Value: void
|
|
* ------------------------------------------------------------------------
|
|
* Arguments:
|
|
* Input: pChnlInfo - the pointer of the CHNL_INFO object which need to be set
|
|
* status - the status of the CHNL_INFO object
|
|
***************************************************************************/
|
|
void mgc_chnl_info_status_set(CHNL_INFO *pChnlInfo , MGC_CHNL_INFO_STATUS status)
|
|
{
|
|
if(pChnlInfo == NULL)
|
|
return;
|
|
|
|
pChnlInfo->status = status;
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
* mgc_chnl_info_find_chnl_of_phy
|
|
* ------------------------------------------------------------------------
|
|
* General: get the CHNL_INFO object in the phyPort according to the chnl id
|
|
* Return Value: if failure return NULL , else return the pointor of the object
|
|
* ------------------------------------------------------------------------
|
|
* Arguments:
|
|
* Input: pPhyPort - the pointer of the phyPort which contain the CHNL_INFO objects pointers
|
|
* chnlNo - the CHNL_INFO object id
|
|
***************************************************************************/
|
|
CHNL_INFO *mgc_chnl_info_find_chnl_of_phy(PHY_PORT_INFO *pPhyPort , int chnlNo)
|
|
{
|
|
|
|
if(pPhyPort == NULL)
|
|
return NULL;
|
|
|
|
if(chnlNo > MGC_MAX_CHNL_NUM_PER_PHY)
|
|
return NULL;
|
|
|
|
if(chnlNo >= pPhyPort->chnlNum)
|
|
{
|
|
MGC_WARN("chnlNo is out of suppose to be ");
|
|
}
|
|
|
|
return pPhyPort->pChnlInfo[chnlNo];
|
|
}
|
|
|
|
/***************************************************************************
|
|
* mgc_chnl_info_get_chnl
|
|
* ------------------------------------------------------------------------
|
|
* General: get the CHNL_INFO object according to the CHNL parameters
|
|
* Return Value: if failure return NULL , else return the pointor of the object
|
|
* ------------------------------------------------------------------------
|
|
* Arguments:
|
|
* Input: chnl - CHNL object which contain mgNo , chnlNo, connectNo and phyPort No
|
|
***************************************************************************/
|
|
CHNL_INFO *mgc_chnl_info_get_chnl(CHNL chnl)
|
|
{
|
|
MG_INFO *pMgInfo = NULL;
|
|
PHY_PORT_INFO *pPhyPort = NULL;
|
|
CHNL_INFO *pChnlInfo = NULL;
|
|
|
|
pMgInfo = mgc_mg_info_get_index_mg(chnl.mgNo);
|
|
if(pMgInfo == NULL)
|
|
return NULL;
|
|
|
|
pPhyPort = mgc_phy_port_find_port_of_mg(pMgInfo , chnl.portNo);
|
|
if(pPhyPort == NULL)
|
|
return NULL;
|
|
|
|
pChnlInfo = mgc_chnl_info_find_chnl_of_phy(pPhyPort , chnl.chlNo);
|
|
if(pChnlInfo == NULL)
|
|
return NULL;
|
|
|
|
return pChnlInfo;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* mgc_chnl_info_attach_connect
|
|
* ------------------------------------------------------------------------
|
|
* General: record a connection in the assgined place
|
|
* Return Value: if failure return FALSE , else return TRUE
|
|
* ------------------------------------------------------------------------
|
|
* Arguments:
|
|
* Input: pChnlInfo - the pointer of the CHNL_INFO object which need to record the connect
|
|
* pConnect - the pointer of the CONNECT_INFO object
|
|
* connNo - the place where the CONNECT_INFO should be record in
|
|
***************************************************************************/
|
|
BOOL mgc_chnl_info_attach_connect(CHNL_INFO *pChnlInfo , CONNECT_INFO *pConnect , int connNo)
|
|
{
|
|
if((pChnlInfo == NULL)||(pConnect == NULL))
|
|
return FALSE;
|
|
|
|
if(connNo >= MGC_MAX_NUM_OF_CHNL_CON)
|
|
return FALSE;
|
|
|
|
if(mgc_connect_get_status(pConnect)!= MGC_CONNECT_STATUS_UNDEF)
|
|
return FALSE;
|
|
|
|
if(pChnlInfo->pConnection[connNo] != NULL)
|
|
return FALSE;
|
|
|
|
if(pChnlInfo->connectNum >= pChnlInfo->maxConnectNum)
|
|
return FALSE;
|
|
|
|
if(mgc_connect_attach_connect_to_chnl(pConnect , pChnlInfo) == FALSE)
|
|
return FALSE;
|
|
|
|
pChnlInfo->pConnection[connNo] = pConnect;
|
|
pChnlInfo->connectNum++;
|
|
mgc_connect_set_status(pConnect , MGC_CONNECT_STATUS_IDLE);
|
|
mgc_chnl_info_status_set(pChnlInfo , MGC_CHNL_INFO_STATUS_USED);
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* mgc_chnl_info_detach_connect
|
|
* ------------------------------------------------------------------------
|
|
* General: remove a connect info from the CHNL_INFO object
|
|
* Return Value: if failure return FALSE , else return TRUE
|
|
* ------------------------------------------------------------------------
|
|
* Arguments:
|
|
* Input: pChnlInfo - the pointer of the CHNL_INFO object which need to remove connect info
|
|
* pConnect - the pointer of the CONNECT_INFO object which need to be remove
|
|
***************************************************************************/
|
|
BOOL mgc_chnl_info_detach_connect(CHNL_INFO *pChnlInfo , CONNECT_INFO *pConnect)
|
|
{
|
|
int i;
|
|
|
|
if((pChnlInfo == NULL)||(pConnect == NULL))
|
|
return FALSE;
|
|
|
|
for(i=0 ; i<MGC_MAX_NUM_OF_CHNL_CON ; i++)
|
|
{
|
|
if(pConnect != pChnlInfo->pConnection[i])
|
|
continue;
|
|
|
|
pChnlInfo->pConnection[i] = NULL;
|
|
pChnlInfo->connectNum--;
|
|
mgc_connect_init(pConnect, pConnect->id);
|
|
// MGC_DEBUG("conn[%d] detach from chnlInfo[%d], as connNo %d" , pConnect->id , pChnlInfo->id , i);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* mgc_chnl_info_get_chnl_no_of_phy
|
|
* ------------------------------------------------------------------------
|
|
* General: find the record place of the CHNL_INFO object in it's owner phyPort
|
|
* Return Value: if failure return -1 , else return the place number
|
|
* ------------------------------------------------------------------------
|
|
* Arguments:
|
|
* Input: pChnlInfo - the pointer of the CHNL_INFO object which need to be find
|
|
* pPhyPort - the pointer of the PHY_PORT_INFO object which contain the CHNL_INFO
|
|
***************************************************************************/
|
|
int mgc_chnl_info_get_chnl_no_of_phy(CHNL_INFO *pChnlInfo , PHY_PORT_INFO *pPhyPort)
|
|
{
|
|
int i;
|
|
if((pChnlInfo == NULL)||(pPhyPort == NULL))
|
|
return -1;
|
|
|
|
for(i=0 ; i<MGC_MAX_CHNL_NUM_PER_PHY ; i++)
|
|
{
|
|
if(pChnlInfo != pPhyPort->pChnlInfo[i])
|
|
continue;
|
|
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* mgc_chnl_info_assign_connect
|
|
* ------------------------------------------------------------------------
|
|
* General: assgin a connection on the assigned place
|
|
* Return Value: if failure return NULL, else return the pointer of the connect
|
|
* ------------------------------------------------------------------------
|
|
* Arguments:
|
|
* Input: pChnlInfo - the pointer of the CHNL_INFO object which need to assign
|
|
* connNo - the assgined place NO
|
|
***************************************************************************/
|
|
CONNECT_INFO *mgc_chnl_info_assign_connect(CHNL_INFO *pChnlInfo , int connNo)
|
|
{
|
|
CONNECT_INFO *pConnect = NULL;
|
|
|
|
if(pChnlInfo == NULL)
|
|
return NULL;
|
|
|
|
pConnect = mgc_connect_find_connect_of_chnl(pChnlInfo, connNo);
|
|
if(pConnect != NULL)
|
|
{
|
|
//MGC_DEBUG("chnlInfo[%d] already have conn[%d], as connNo %d" , pChnlInfo->id , pConnect->id , connNo);
|
|
return pConnect;
|
|
}
|
|
|
|
if(pChnlInfo->connectNum >= pChnlInfo->maxConnectNum)
|
|
return FALSE;
|
|
|
|
pConnect = mgc_connect_get_unused();
|
|
if(pConnect == NULL)
|
|
return NULL;
|
|
|
|
if(mgc_chnl_info_attach_connect(pChnlInfo , pConnect , connNo) == FALSE)
|
|
return NULL;
|
|
|
|
//MGC_DEBUG("assign unused conn[%d] to chnlInfo[%d]connNo[%d]" , pConnect->id , pChnlInfo->id, connNo);
|
|
return pConnect;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
* mgc_chnl_info_assign_idle_connect
|
|
* ------------------------------------------------------------------------
|
|
* General: assgin a connection on the CHNL_INFO object
|
|
* Return Value: if failure return NULL, else return the pointer of the connect
|
|
* ------------------------------------------------------------------------
|
|
* Arguments:
|
|
* Input: pChnlInfo - the pointer of the CHNL_INFO object which need to assign
|
|
***************************************************************************/
|
|
CONNECT_INFO *mgc_chnl_info_assign_idle_connect(CHNL_INFO *pChnlInfo)
|
|
{
|
|
int i;
|
|
CONNECT_INFO *pConnect = NULL;
|
|
CONNECT_INFO *pConnectTmp = NULL;
|
|
|
|
if(pChnlInfo == NULL)
|
|
return NULL;
|
|
|
|
//MGC_DEBUG("func %s " , __FUNCTION__);
|
|
for(i=0 ; i<MGC_MAX_NUM_OF_CHNL_CON ; i++)
|
|
{
|
|
pConnect = pChnlInfo->pConnection[i];
|
|
|
|
if(pConnect != NULL)
|
|
{
|
|
if(mgc_connect_get_status(pConnect) != MGC_CONNECT_STATUS_IDLE)
|
|
continue;
|
|
|
|
return pConnect;
|
|
}
|
|
|
|
pConnectTmp = mgc_chnl_info_assign_connect(pChnlInfo , i);
|
|
|
|
if(pConnectTmp == NULL)
|
|
continue;
|
|
|
|
return pConnectTmp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void mgc_chnl_info_set_mon(int id, BOOL enable)
|
|
{
|
|
if((id < 0) || (id > MGC_MAX_NUM_OF_CHNL))
|
|
return;
|
|
|
|
mgcChnl[id].monFlag = enable;
|
|
}
|
|
|
|
BYTE *mgc_chnl_info_get_res_addr(void)
|
|
{
|
|
return (BYTE *)mgcChnl;
|
|
}
|
|
|
|
|
|
char *mgc_chnl_info_print_codec(CHNL_INFO *pChnlInfo)
|
|
{
|
|
int i;
|
|
CONNECT_INFO *pConnect = NULL;
|
|
|
|
if(pChnlInfo == NULL)
|
|
return "UNKNOWN";
|
|
|
|
if(pChnlInfo->maxConnectNum > 1)
|
|
return "UNKNOWN";
|
|
|
|
for(i=0 ; i<pChnlInfo->maxConnectNum ; i++)
|
|
{
|
|
pConnect = pChnlInfo->pConnection[i];
|
|
if(pConnect != NULL)
|
|
break;
|
|
}
|
|
|
|
if(pConnect == NULL)
|
|
return "UNKNOWN";
|
|
|
|
return mgc_mgcp_print_vc_codec(pConnect->mediaAttr.vocoderType);
|
|
}
|
|
|
|
|
|
|
|
char *mgc_chnl_info_print_remote(CHNL_INFO *pChnlInfo)
|
|
{
|
|
int i;
|
|
CONNECT_INFO *pConnect = NULL;
|
|
PUB_SDP_MEDIA *pMedia = NULL;
|
|
PUB_SDP_MSG *pSdp = NULL;
|
|
static char buf[32];
|
|
|
|
sprintf(buf , "0.0.0.0:0");
|
|
|
|
if(pChnlInfo == NULL)
|
|
return buf;
|
|
|
|
if(pChnlInfo->maxConnectNum > 1)
|
|
return buf;
|
|
|
|
for(i=0 ; i<pChnlInfo->maxConnectNum ; i++)
|
|
{
|
|
pConnect = pChnlInfo->pConnection[i];
|
|
if(pConnect != NULL)
|
|
break;
|
|
}
|
|
|
|
if(pConnect == NULL)
|
|
return buf;
|
|
|
|
pSdp = &pConnect->mediaAttr.sdp;
|
|
pMedia = &pSdp->medias.medias[0];
|
|
if((pMedia->m.port != 0)||(strlen(pSdp->c.addr) != 0))
|
|
sprintf(buf , "%s:%d" , pSdp->c.addr, pMedia->m.port);
|
|
return buf;
|
|
}
|
|
|
|
char *mgc_chnl_info_print_mode(CHNL_INFO *pChnlInfo)
|
|
{
|
|
int i;
|
|
CONNECT_INFO *pConnect = NULL;
|
|
|
|
if(pChnlInfo == NULL)
|
|
return "INACTIVE";
|
|
|
|
if(pChnlInfo->maxConnectNum > 1)
|
|
return "INACTIVE";
|
|
|
|
for(i=0 ; i<pChnlInfo->maxConnectNum ; i++)
|
|
{
|
|
pConnect = pChnlInfo->pConnection[i];
|
|
if(pConnect != NULL)
|
|
break;
|
|
}
|
|
|
|
if(pConnect == NULL)
|
|
return "INACTIVE";
|
|
|
|
switch(mgc_connect_get_conn_mode(pConnect))
|
|
{
|
|
case MGC_CON_MODE_INACTIVE:
|
|
return "INACTIVE";
|
|
case MGC_CON_MODE_RECVONLY:
|
|
return "RECVONLY";
|
|
case MGC_CON_MODE_SENDONLY:
|
|
return "SENDONLY";
|
|
case MGC_CON_MODE_SENDRECV:
|
|
return "SENDRECV";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "INACTIVE";
|
|
}
|
|
|
|
char *mgc_chnl_info_print_status(CHNL_INFO *pChnlInfo)
|
|
{
|
|
int i;
|
|
CONNECT_INFO *pConnect = NULL;
|
|
|
|
if(pChnlInfo == NULL)
|
|
return "UNDEF";
|
|
|
|
if(pChnlInfo->maxConnectNum > 1)
|
|
return "UNDEF";
|
|
|
|
for(i=0 ; i<pChnlInfo->maxConnectNum ; i++)
|
|
{
|
|
pConnect = pChnlInfo->pConnection[i];
|
|
if(pConnect != NULL)
|
|
break;
|
|
}
|
|
|
|
if(pConnect == NULL)
|
|
return "IDLE";
|
|
|
|
switch(mgc_connect_get_status(pConnect))
|
|
{
|
|
case MGC_CONNECT_STATUS_IDLE:
|
|
return "IDLE";
|
|
case MGC_CONNECT_STATUS_CREATE:
|
|
return "CREATE";
|
|
case MGC_CONNECT_STATUS_CREATED:
|
|
return "CONNECT";
|
|
case MGC_CONNECT_STATUS_CREATING:
|
|
return "CREATING";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "UNDEF";
|
|
}
|
|
|
|
#ifdef MGC_TEST_ENABLE
|
|
int mgc_chnl_info_assigned_num(void)
|
|
{
|
|
int i , num;
|
|
CHNL_INFO *pChnlInfo = NULL;
|
|
|
|
num = 0;
|
|
for(i=0 ; i<MGC_MAX_NUM_OF_CHNL ; i++)
|
|
{
|
|
pChnlInfo = &mgcChnl[i];
|
|
|
|
if(pChnlInfo->pPhyPort != NULL)
|
|
num++;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
#endif
|