/**
* @file CM_UDP.c
* @brief UDP通信的封装
* @author GT-BU
* @date 2021.02.01
* @version V1.1
* @copyright TCT
* @par 修改日志
* <table>
* <tr><th>Date        <th>Version  <th>Author     <th>Description
* <tr><td>2017.12.20  <td>V1.0     <td>MSCP项目组 <td>创建初始版本
* <tr><td>2021.02.01  <td>V1.1     <td>GT-BU      <td>TSCP项目集成、更新
* </table>
*/
#include "CM_UDP.h"
#include "CM_Memory.h"
#include "PFM_Log.h"
#ifdef WIN32
#include <ws2tcpip.h> /* 组播 */
#endif

/** UDP服务已初始化标志 */
static CM_BOOL s_UDP_Initialized = CM_FALSE;

/**
* @brief 初始化UDP服务
* @details 仅需初始化一次
*/
ENUM_CM_RETURN CM_UDP_Init(void)
{
    /*- 变量初始化 */
    ENUM_CM_RETURN rt = ENUM_CM_FALSE; /* 函数返回值 */

    /*- 初始化socket服务 */
    rt = CM_Socket_Init();

    /*- 是否车初始化成功 */
    if (ENUM_CM_TRUE == rt)
    {
        /*- UDP初始化成功 */
        s_UDP_Initialized = CM_TRUE;
    }
    else
	{
		/*- 无处理 */
	}

    /*- 返回初始化结果 */
    return rt;
}

/**
* @brief socket是否可读
* @param sock soket
* @preturn 可读状态
* - ENUM_CM_TRUE 可读
* - ENUM_CM_FALSE 不可读
*/
ENUM_CM_RETURN CM_UDP_Is_Read_Ready(IN CM_SOCKET sock)
{
    /*- 变量初始化 */
    CM_INT32 sFd = 0;                    /* 文件描述符 */
    CM_INT32 sFlag = 0;                  /* 临时变量 */
    struct timeval timeover1 = { 0, 0 }; /* 时间 */
    fd_set readok;                       /* 临时变量 */
    ENUM_CM_RETURN rt = ENUM_CM_FALSE;   /* 函数返回值 */
    ENUM_CM_RETURN ret = ENUM_CM_FALSE;   /* 函数返回值 */
    /*- UDP服务是否已经初始化 */
    if (CM_TRUE == s_UDP_Initialized)
    {
        /*- select查询sock的读状态 */
    	ret = CM_Memset((void*) &readok, (CM_UINT8)0U, (size_t)(sizeof(fd_set)), (size_t)(sizeof(fd_set)));
        timeover1.tv_sec = 0;

        timeover1.tv_usec = 0;

        FD_ZERO(&readok);
        FD_SET(sock, &readok);

        sFd = (CM_INT32)select((sock + 1), &readok, NULL, NULL, &timeover1);

        /*- @alias 判断sock是否可读*/
        if ((sFd > 0) && (ENUM_CM_TRUE == ret))
        {
        	sFlag = FD_ISSET(sock, &readok);
            if (0 != sFlag)
            {
                rt = ENUM_CM_TRUE;
            }
            else
            {
                rt = ENUM_CM_FALSE;
            }
        }
        else
        {
            rt = ENUM_CM_FALSE;
        }
    }
    else
	{
		/*- 无处理 */
	}
    /*- 返回sock的可读状态 */
    return rt;
}

/**
* @brief socket是否可写
* @param sock soket
* @preturn 可写状态
* - ENUM_CM_TRUE 可写
* - ENUM_CM_FALSE 不可写
*/
ENUM_CM_RETURN CM_UDP_Is_Write_Ready(IN CM_SOCKET sock)
{
    /*- 变量初始化 */
    CM_INT32 sFd = 0;                    /* 文件描述符 */
    CM_INT32 sFlag = 0;                  /* 临时变量 */
    struct timeval timeover1 = { 0, 0 }; /* 时间 */
    fd_set writeok;                      /* 临时变量 */
    ENUM_CM_RETURN rt = ENUM_CM_FALSE;   /* 函数返回值 */
    ENUM_CM_RETURN ret = ENUM_CM_FALSE;   /* 函数返回值 */
    /*- UDP服务是否已经初始化 */
    if (CM_TRUE == s_UDP_Initialized)
    {
        /*- select查询sock的写状态 */
        ret = CM_Memset((void*) &writeok, (CM_UINT8)0U, (size_t)(sizeof(fd_set)), (size_t)(sizeof(fd_set)));
        timeover1.tv_sec = 0;
        timeover1.tv_usec = 0;
        FD_ZERO(&writeok);
        FD_SET(sock, &writeok);

        sFd = (CM_INT32)select((CM_INT32)(sock + 1), NULL, &writeok, NULL, &timeover1);

        /*- @alias 判断sock是否可写 */
        if ((sFd > 0) && (ENUM_CM_TRUE == ret))
        {
        	sFlag = FD_ISSET(sock, &writeok);
            if (0 != sFlag)
            {
                rt = ENUM_CM_TRUE;
            }
            else
            {
                rt = ENUM_CM_FALSE;
            }
        }
        else
        {
            rt = ENUM_CM_FALSE;
        }
    }
    else
	{
		/*- 无处理 */
	}
    /*- 返回sock的可写状态 */
    return rt;
}

/**
* @brief 创建UDP socket
* @param pSock socket指针
* @param isNonBlockMode 是否是非阻塞模式 CM_TRUE:非阻塞，CM_FALSE:阻塞
* @param addrIP IP地址
* @param portNumber 端口号
* @param maddr 所属组播地址，0为不加入组播
* @return 创建结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
*/
ENUM_CM_RETURN CM_UDP_Socket_Create(OUT CM_SOCKET* pSock, IN CM_BOOL isNonBlockMode, IN CM_UINT32 addrIP, IN CM_UINT16 portNumber, IN CM_UINT32 maddr, IN CM_UINT8 broadFlag)
{
    /*- 变量初始化 */
    CM_SOCKADDR_T addrHost;           /* 主机地址 */
    CM_INT32 ret = 0;                 /* 临时结果 */
    CM_CHAR* tmpstr = NULL;           /* 临时结果 */
    ENUM_CM_RETURN rt = ENUM_CM_FALSE; /* 函数返回值 */
    ENUM_CM_RETURN retError = ENUM_CM_FALSE; /* 临时返回值 */
    struct ip_mreq multiCast;         /* 组播配置 */           
    CM_INT32 bCast = 1;
    /*- 是否UDP服务未初始化或输入非法 */
    if ((CM_TRUE != s_UDP_Initialized) || (NULL == pSock))
    {
        /*- 创建失败 */
        rt = ENUM_CM_FALSE;
    }
    else
    {
        /*- 创建UDP socket*/
    	retError = CM_Memset((void*) &addrHost, (CM_UINT8)0U, (size_t)(sizeof(CM_SOCKADDR_T)), (size_t)(sizeof(CM_SOCKADDR_T)));
        rt = CM_Socket_Create(pSock, CM_TRUE);
        /*- 创建socket是否成功 */
        if ((ENUM_CM_TRUE == rt) && (ENUM_CM_TRUE == retError))
        {
            /*- 绑定IP端口 */
        	retError = CM_Memset((void*) &addrHost, (CM_UINT8)0U, (size_t)(sizeof(CM_SOCKADDR_T)), (size_t)(sizeof(CM_SOCKADDR_T)));
            addrHost.sin_family = (CM_INT16)AF_INET;             
            addrHost.sin_port = htons(portNumber);
#ifdef WIN32
            addrHost.sin_addr.s_addr = addrIP;
            if(1 == broadFlag)
            {
				/*广播*/
				setsockopt(*pSock, SOL_SOCKET, SO_BROADCAST, (void*)(&bCast), (unsigned int)sizeof(bCast));
            }
			else
			{
				/*单播-组播*/
			}
#else
            if(1u == broadFlag)
            {
            	/*广播*/
            	addrHost.sin_addr.s_addr = INADDR_ANY;
            	(void)setsockopt(*pSock, SOL_SOCKET, SO_BROADCAST, (void*)(&bCast), (unsigned int)sizeof(bCast));
            }
			else
			{
				if (0u != maddr)
				{
					/*组播*/
					addrHost.sin_addr.s_addr = INADDR_ANY;
					/*设置端口复用*/
					(void)setsockopt(*pSock,SOL_SOCKET,SO_REUSEPORT ,(void*)(&bCast), (unsigned int)sizeof(bCast));
					/*- 配置组播地址 */
					multiCast.imr_interface.s_addr = addrIP;
					multiCast.imr_multiaddr.s_addr = maddr;
					ret = setsockopt(*pSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void*)(&multiCast), (unsigned int)sizeof(multiCast));
				}
				else
				{
					/*单播*/
					addrHost.sin_addr.s_addr = addrIP;
				}
			}
#endif
            ret = (CM_INT32)bind(*pSock, (CM_SOCKADDR*) &addrHost, (unsigned int)sizeof(CM_SOCKADDR));
            /*- 绑定是否成功 */
            if ((ret != CM_SOCKET_ERROR) && (ENUM_CM_TRUE == retError))
            {
            	/*- 设置socket的阻塞模式 */
#ifdef WIN32
            	ret = ioctlsocket(*pSock, (long)FIONBIO, (unsigned long *)&isNonBlockMode);
#else/*Linux Vxworks常用PIOX标准*/
            	ret = ioctl(*pSock, FIONBIO, &isNonBlockMode);
#endif
                /*- 设置socket的阻塞模式是否失败 */
                if (CM_SOCKET_ERROR == ret)
                {
                    /*- 创建失败 */
                    rt = ENUM_CM_FALSE;
                }
                else
                {
					/*- 创建成功 */
                    rt = ENUM_CM_TRUE;
                }
            }
            else
            {
                /*- 创建失败 */
                tmpstr = inet_ntoa(addrHost.sin_addr);
                CM_NM_ASSERT(NULL != tmpstr, UDP_MODE,DARK_FAULT_CODE_FUN_ORDER_1,TSCP_DARK_ASSERT_1);
                MSCP_LOG_MSG("band IP:%s:%u fail\n", tmpstr, portNumber); /* parasoft-suppress BD-PB-NOTINIT "WIN API 导致的误报" */
                rt = ENUM_CM_FALSE;
            }
        }
        else
		{
			/*- 无处理 */
		}
    }
    /*- 返回创建结果 */
    return rt;
}

/**
* @brief UDP发送（指定IP端口）
* @param sock socket
* @param pBuf 发送报文指针
* @param bufSize 发送报文长度
* @param addrDest 目的IP
* @param nPortDest 目的端口
* @return 发送结果
* - ENUM_CM_TRUE 成功
* - ENUM_CM_FALSE 失败
*/
ENUM_CM_RETURN CM_UDP_Send_Msg(IN CM_SOCKET sock, IN const CM_UINT8* pBuf, IN CM_INT32 bufSize, IN CM_UINT32 addrDest, IN CM_UINT16 nPortDest)
{
    /*- 变量初始化 */
    CM_SOCKADDR_T addr;                /* 目的地址 */
#ifdef WIN32
    size_t ret = 0;                    /* 函数返回值 */
#else
    ssize_t ret = 0;                  /* 函数返回值 */
#endif
    ENUM_CM_RETURN rt = ENUM_CM_FALSE;  /* 函数返回值 */
    ENUM_CM_RETURN retError = ENUM_CM_FALSE;  /* 函数返回值 */
    ENUM_CM_RETURN isReady = ENUM_CM_FALSE; /* 可写标志 */
	struct in_addr addr1;

    /*- UDP协议是否已经初始化 */
    if (CM_TRUE == s_UDP_Initialized)
    {
    	/*- 获取socket可写状态 */
    	isReady = CM_UDP_Is_Write_Ready(sock);
        /*- socket是否可写 */
        if (ENUM_CM_TRUE == isReady)
        {
            /*- 生成目的地址 */
        	retError = CM_Memset((void *) &addr, (CM_UINT8)0U, (size_t)(sizeof(addr)), (size_t)(sizeof(addr)));

            addr.sin_family = (CM_INT16)AF_INET;
            addr.sin_port = htons(nPortDest);
            addr.sin_addr.s_addr = addrDest;

            /*- 向目的地址发送报文 */
            ret = sendto(sock, (const void *)pBuf, (size_t)bufSize, 0, (const struct sockaddr*) &addr, (unsigned int)sizeof(CM_SOCKADDR_T));

            /*- @alias 判断报文是否发送成功 */
            if (ret == CM_SOCKET_ERROR)
            {
                rt = ENUM_CM_FALSE;
                MSCP_LOG_MSG("Want to send dest ip = %s, port = %d,  UDP send errno = %d,%s\n",inet_ntoa(addr.sin_addr),nPortDest,errno,strerror(errno));
            }
            else if (ret != bufSize)
            {
                rt = ENUM_CM_FALSE;
                MSCP_LOG_MSG("s_UDP_Initialized != CM_TRUE\n");
            }
            else if(ENUM_CM_FALSE == retError)
            {
            	rt = ENUM_CM_FALSE;
            	MSCP_LOG_MSG("CM_Memset Error\n");
            }
            else
            {
                rt = ENUM_CM_TRUE;
            }
        }
        else
        {
            /*- 发送失败 */
            rt = ENUM_CM_FALSE;
            MSCP_LOG_MSG("ENUM_CM_TRUE != isReady\n");
        }
    }
    else
	{
    	MSCP_LOG_MSG("s_UDP_Initialized != CM_TRUE\n");
	}
    /*- 返回发送结果 */
    return rt;
}

/**
* @brief UDP发送（源IP、端口）
* @param sock socket
* @param pBuf 接收报文指针
* @param BufSize 接收报文缓冲区大小
* @param pAddrSrc 源IP
* @param pPortSrc 源端口
* @return 收到报文长度
* - 正整数 收到的报文长度
* - 0 未收到报文
* - -1 接收异常
*/
CM_INT32 CM_UDP_Recv_Msg(IN CM_SOCKET sock, OUT CM_UINT8 pBuf[], IN CM_INT32 BufSize, OUT CM_UINT32* pAddrSrc, OUT CM_UINT16* pPortSrc)
{
    /*- 变量初始化 */
    CM_SOCKADDR_T addr;   /* 临时变量 */
#ifdef WIN32
    int nFromLen = (int)sizeof(CM_SOCKADDR_T); /* 临时变量 */
#else
    CM_UINT32 nFromLen = (CM_UINT32)sizeof(CM_SOCKADDR_T); /* 临时变量 */
#endif
    CM_INT32 recvNum = 0; /* 接收长度 */
	CM_CHAR addrTmp[INET_ADDRSTRLEN] = { 0 };/*地址信息buff*/
    ENUM_CM_RETURN retError = ENUM_CM_FALSE;  /* 函数返回值 */
    /*- 是否UDP协议未初始化或输入非法 */
    if ((CM_TRUE != s_UDP_Initialized) || (NULL == pBuf) || (0 >= BufSize))
    {
        /*- 接收异常 */
        recvNum = -1;
        CM_RE_ASSERT(0, NET_MODE, DARK_FAULT_CODE_FUN_ORDER_11, TSCP_DARK_ASSERT_3);
    }
    else
    {
    	retError = CM_Memset((void*) &addr, (CM_UINT8)0U, (unsigned long)(sizeof(addr)), (unsigned long)(sizeof(addr)));
        /*- UDP接收 */
        recvNum = (CM_INT32)recvfrom(sock, (void *)pBuf, (unsigned long)BufSize, 0, (struct sockaddr*) &addr, &nFromLen);

        /*- 接收长度是否大于0，并且小于缓冲区的长度*/
        if ((recvNum > 0) && (recvNum < BufSize))
        {
            /*- @alias 输出源IP */
            if (NULL != pAddrSrc)
            {
                *pAddrSrc = addr.sin_addr.s_addr;
            }
            else
    		{
    			/* 无处理 */
    		}

            /*- @alias 出书源端口 */
            if (NULL != pPortSrc)
            {
                *pPortSrc = ntohs(addr.sin_port);
            }
            else
    		{
				/* 无处理 */
    		}
        }
        else
		{
			if (recvNum > 0)
			{
				/* 转换服务端ip地址 */	
				if (NULL != inet_ntop(AF_INET, &addr.sin_addr, addrTmp, (size_t)INET_ADDRSTRLEN))
				{
					MSCP_LOG_MSG("errno: %d , Error: %s , recvNum : %d\n", errno, strerror(errno), recvNum);
					MSCP_LOG_MSG("Warning:udp recv ip %s port %u !!! The packet length was too long.\nerrno: %d , Error: %s , recvNum : %d\n" \
							,addrTmp, ntohs(addr.sin_port), errno, strerror(errno), recvNum);
				}
				else
				{			
					MSCP_LOG_MSG("Warning: Couldn't convert  address to string!!! The packet length was too long.\nerrno: %d , Error: %s , recvNum : %d\n" \
							, errno, strerror(errno), recvNum);
				}
			}
			else
			{
				/* 无处理 */
			}
			/*- 接收异常 */
        	recvNum = -1;
		}
    }
    /*- 返回接收长度 */
    return recvNum;
}
