/**
* @file SFM_2oo2.c
* @brief Safe Function Module:2oo2功能
* @details
* 2oo2上电初始化：初始化、同步、校验
* 2oo2周期同步和2oo2数据交换
* @author GT-BU
* @date 2021.02.20
* @version V1.0
* @copyright TCT
* @par 修改日志
* <table>
* <tr><th>Date        <th>Version  <th>Author     <th>Description
* <tr><td>2021.02.01  <td>V1.0     <td>GT-BU      <td>创建初始版本
* </table>
*/
#include "SFM_2oo2.h"
#include "SFM_2x2.h"
#include "CM_Convert.h"
#include "CM_Memory.h"
#include "CM_CRC32.h"
#include "CM_RingBuf.h"
#include "CM_Time.h"
#include "CM_MD5.h"
#include "DFM_Time.h"
#include "CFM_Net.h"
#include "SFM_package.h"
#include "PFM_Cfg.h"
#include "PFM_Log.h"

#define SFM_SYNC_CHECK_NUM (10u)  /**<上电同步检查总次数 */
#define SFM_SYNC_CHECK_MIN (10u) /**<上电同步检查通过的最少次数 */


#define SFM_2oo2_TM_RING_SIZE (1000u / (3u * ((MSCP_CYC_TIME) / 100u)) * 2u) /**<400是周期时间,3是没晶振误差  */
#define SFM_2oo2_TM_FIX_VAL  (3) /**< 2oo2校时门限，单位:ms*/

static STRU_CM_RING_T s_2oo2_TmRing;

STRU_2OO2_CTRL_T s_2oo2_Ctrl; /**< 平台通信控制 */

STRU_2OO2_CTRL_T s_2oo2_Ctrl_App; /**< 应用通信控制 */


/* 2oo2发送 */
static void s_2oo2_Send(IN const CM_UINT8 *Data, IN CM_INT32 DataLen, STRU_2OO2_CTRL_T *p_2oo2_Ctrl,IN ENUM_2oo2_MSG_T msgType,CM_UINT16 FrameStart);
/* 2oo2重传请求与接收ack发送*/
static void s_2oo2_ARQ_Send(STRU_2OO2_CTRL_T *p_2oo2_Ctrl,IN ENUM_2oo2_MSG_T msgType,CM_UINT16 FrameIndex);
/* 2oo2数据接收，支持重传*/
static CM_INT32 s_2oo2_ARQ_Recv(OUT CM_UINT8 *RxData, IN CM_UINT32 RxSize, OUT ENUM_2oo2_MSG_T *pMsgType,
								IN const CM_UINT8 *Send_Data, IN CM_INT32 Send_DataLen, STRU_2OO2_CTRL_T *p_2oo2_Ctrl,IN ENUM_2oo2_MSG_T Send_msgType);

/* 维护模式2oo2发送 */
static void s_2oo2_Send_Crash(IN const CM_UINT8 *Data, IN CM_INT32 DataLen, STRU_2OO2_CTRL_T *p_2oo2_Ctrl,IN ENUM_2oo2_MSG_T msgType);
/* 维护模式2oo2接收 */
static CM_INT32 s_2oo2_Recv_Crash(OUT CM_UINT8 *RxData, IN CM_UINT32 RxSize, OUT ENUM_2oo2_MSG_T *pMsgType,STRU_2OO2_CTRL_T *p_2oo2_Ctrl);
static ENUM_CM_RETURN s_isClac2oo2Crc(ENUM_2oo2_MSG_T type);
/**
* @brief 2oo2上电初始化
* @param vitalBuf 关键数据数组
* @param vitalLen 关键数据长度
* @param p2oo2SyncTick 2oo2同步成功时的本机Tick
* @details
* 执行初始上电同步，成功后连续进行10次定期同步以校验延迟时间
* 校验不通过时将重新执行初始上电同步...
* 完成2oo2同步后，进行2oo2上电关键数据比较，比较不一致时宕机。
*/
void SFM_2oo2_Init(IN const CM_UINT8 *vitalBuf, IN CM_UINT16 vitalLen, OUT CM_UINT64 *p2oo2SyncTick)
{
	/*- 变量初始化 */
	CM_UINT8 myData[1000] = "sync";   /* 临时缓冲区 */
	CM_UINT8 peerData[1000] = { 0u }; /* 临时缓冲区 */
	CM_INT32 len = 0;                /* 临时变量 */
	CM_INT32 ret = 0;                /* 临时变量 */
	CM_UINT32 i = 0u;                /* 临时变量 */
	CM_INT32 delayTime = 0;          /* 延迟时间 */
	CM_UINT8 myGoodCount = 0u;       /* 本机校验通过次数 */
	CM_UINT64 cycTick = 0u;          /* 周期Tick */
	CM_UINT64 curTick = 0u;          /* 当前Tick */
	CM_INT32 checkCount = 0;         /* 同步检查次数 */
	CM_UINT32 baseLen = 0u;           /* 平台关键信息长度 */
	CM_UINT8 tmepIndex = 0u;
    /*- 循环执行2oo2上电同步*/
    while (1)
    {
        /*- 2oo2初次同步*/
        MSCP_LOG_MSG("2oo2 sync start...\n");
        (void)CFM_2oo2_InitSync();
        cycTick = CM_GetTick();
        myGoodCount = 0u;    
        ++checkCount;
        
        /*- @alias 连续进行10次定期同步，校验延迟时间 */
        for (i = 0u; i < SFM_SYNC_CHECK_NUM; i++)
        {
            /*- 2oo2同步 */
            (void)SFM_2oo2_Exchange_Currency(myData, 4, peerData, 4u, ENUM_2oo2_SYNC);
            curTick = CM_GetTick();
            delayTime = (CM_INT32)CM_UNSIGNED_LONG_INT_EXP((curTick - cycTick) / 1000u);
            /*- @alias 检查同步结果(延迟时间) */
#ifdef WIN32
            if (delayTime < 20000)
#else
            if (delayTime <= 3000)
#endif
            {
                myGoodCount++;
                MSCP_LOG_MSG(".");
            }
            else
            {
                MSCP_LOG_MSG("?");
            }
            /*- 延时200ms */
            cycTick += (CM_UINT64)CM_UNSIGNED_INT_EXP(200u * CM_UNSIGNED_MILLION);
            (void)DFM_WaitUntill(cycTick);
        }

        /*- 2oo2交换校验结果 */
        myData[0u] = myGoodCount;
        (void)SFM_2oo2_Exchange_Currency(myData, (CM_INT32)1, peerData, (CM_UINT32)1, ENUM_2oo2_SYNC);

        /*- 双机校验结果是否都通过 */
        if ((myGoodCount >= SFM_SYNC_CHECK_MIN) && (peerData[0u] >= SFM_SYNC_CHECK_MIN))
        {
            /*- 退出循环 */
            break;
        }
        /*- 校验次数是否超过10次 */
        else if (checkCount >= 30)
        {
            /*- 宕机 */
            CM_ASSERT(0, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_1, TSCP_DARK_ASSERT_1);
        }
        else
        {
            /*- 记录延迟时间 */
            MSCP_LOG_MSG(" fail %d\n", delayTime);
            /*- @alias 根据校验结果调整延迟时间 */
            if (myGoodCount < 3u) /*我的结果太差说明我周期开始时间过早*/
            {
                if (delayTime > (1000 * 20))
                {
                    cycTick += (CM_UINT64)CM_UNSIGNED_INT_EXP(210u * CM_UNSIGNED_MILLION);
                    MSCP_LOG_MSG("_");
                }
                else
                {
                    cycTick += (CM_UINT64)CM_UNSIGNED_INT_EXP(201u * CM_UNSIGNED_MILLION);
                    MSCP_LOG_MSG("-");
                }
                (void)DFM_WaitUntill(cycTick);
            }
            else
            {
                cycTick += (CM_UINT64)CM_UNSIGNED_INT_EXP(200u * CM_UNSIGNED_MILLION);
                (void)DFM_WaitUntill(cycTick);
            }
        }
    }
    /*- 记录校验通过的时间 */
    *p2oo2SyncTick = cycTick;
    MSCP_LOG_MSG(" success\n");

    /*- 2oo2上电比较，组上电比较校验帧 */
    /* ID信息 */
    myData[baseLen++] = g_PFM_Cfg.CPU_ID;
    myData[baseLen++] = g_PFM_Cfg.BOARD_ID;
    /*交互自动重启次数*/
    myData[baseLen++] = MSCP_Get_AutoRbootCut();
    /*交互软重启次数*/
    myData[baseLen++] = RM_Get_FaultRbootCut();
    /* 平台软件版本 */
    myData[baseLen++] = MSCP_SW_VERION[0];
    myData[baseLen++] = MSCP_SW_VERION[1];
    myData[baseLen++] = MSCP_SW_VERION[2];
    myData[baseLen++] = MSCP_SW_VERION[3];
    /* 平台配置数据MD5 */
    (void)CM_Memcpy((void*)&myData[baseLen], (CM_SIZE_T)CM_MD5_SIZE_BYTE, (void*)g_PFM_Cfg.CFG_MD5, (CM_SIZE_T)CM_MD5_SIZE_BYTE);
    baseLen += CM_MD5_SIZE_BYTE;

    CM_ASSERT(vitalLen <= 200u, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_1, TSCP_DARK_ASSERT_2);
    (void)CM_Memcpy((void*)(myData + baseLen), (CM_SIZE_T)(sizeof(myData) - baseLen), (const void*)vitalBuf, (CM_SIZE_T)vitalLen);
    len = (CM_INT32)CM_UNSIGNED_INT_EXP(baseLen + (CM_UINT32)vitalLen);
    /*TODO 软件完整性，数据版本、完整性检查，应用一致性*/

    /*- 与邻机交换校验帧 */
    ret = SFM_2oo2_Exchange_Currency(myData, len, peerData, (CM_UINT32)sizeof(peerData), ENUM_2oo2_INIT_CHECK);
    /*- 校验帧长度是否相同 */
    if (ret == len)
    {
        /*- 检查收到的校验帧内容 */
        CM_ASSERT((myData[0] + peerData[0]) == 3u, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_1, TSCP_DARK_ASSERT_3);
        ret = CM_Memcmp((void*)(myData + 1u), (void*)(peerData + 1u), (CM_SIZE_T)CM_SIGNED_INT_EXP(len - 1));
        CM_ASSERT(ret == 0, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_1, TSCP_DARK_ASSERT_4);
        MSCP_LOG_MSG("2oo2 Init Cmp Success\n");
        if(0u < MSCP_Get_AutoRbootCut())
        {
			/*- 比较宕机日志与实际版本号*/
			for(i = 0u; i < 3u; i++)
			{
				(1u == g_App_Verson.registerVerson[i].versonName)?tmepIndex = i:tmepIndex;
			}
			ret = CM_Memcmp(g_AutoRebootVersion,g_App_Verson.registerVerson[tmepIndex].verson,4u);
			CM_ASSERT(ret == 0, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_1, TSCP_DARK_ASSERT_6);
			MSCP_LOG_MSG("Init Version Cmp Success\n");
        }
        else
        {
        	/*- 不是自动重启*/
        }
    }
    else
    {
        /*- 宕机 */
        CM_ASSERT(0, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_1, TSCP_DARK_ASSERT_5);
    }
}
/**
* @brief 2oo2关键数据初始化
*/
void SFM_2oo2_DataInit(void)
{
	/*- 2取全局数据初始化 */
	SFM_2oo2_CtrlInit();

    /*- 2取环形队列初始化  */
    (void)CM_RB_Init(&s_2oo2_TmRing, CM_UNSIGNED_INT_EXP(SFM_2oo2_TM_RING_SIZE));

    /*- 2取初次同步 */
    (void)CFM_2oo2_InitSync();
}

/**
* @brief 2oo2发送
* @param Data    发送报文指针
* @param DataLen 发送报文长度
* @param msgType 发送报文类型
* @param p_2oo2_Ctrl 2取控制参数结构体
* @param FrameStart 发送起始标志，用于重传，首次发送数据输入1
*/
static void s_2oo2_Send(IN const CM_UINT8 *Data, IN CM_INT32 DataLen, STRU_2OO2_CTRL_T *p_2oo2_Ctrl,IN ENUM_2oo2_MSG_T msgType,CM_UINT16 FrameStart)
{
    /*- 变量初始化 */
    CM_UINT16 FrameNum = 0u;     /* 帧总数 */
    CM_UINT16 FrameIndex = 0u;   /* 帧序号 */
    CM_UINT32 FrameLen = 0u;     /* 帧长度 */
    CM_UINT32 SysCyc = SFM_2x2_GetSysCount(); /*系统周期号*/
    CM_UINT32 crc = 0u;
    ENUM_CM_RETURN clacCrcFlag = ENUM_CM_TRUE;         /* 判断是否计算CRC*/
    clacCrcFlag = s_isClac2oo2Crc(msgType);
    PFM_LOG_SET_STOPTIMER(TSCP_DARK_2OO2);
    /*- 入口检查 */
    CM_ASSERT(((DataLen >= 0) && (NULL != Data) && (NULL != p_2oo2_Ctrl) && (NULL != p_2oo2_Ctrl->DataSend)), SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_5, TSCP_DARK_ASSERT_1);
    /*- 计算帧数 */
    FrameNum = (CM_UINT16)(((CM_UINT32)DataLen <= NET_20K_SIZE) ? (1u) : (((CM_UINT32)DataLen + (NET_20K_SIZE - 1u)) / NET_20K_SIZE));
    if(FrameStart <= FrameNum)
    {

		/*
		2oo2通信帧格式：
		4字节 长度(不含自身)
		4字节 源ID
		4字节 目的ID
		4字节 周期号
		4字节 序列号
		1字节 类型
		2字节 帧总数
		2字节 帧序号
		n字节 数据
		4字节 CRC(含长度)
		*/
		/*- 填充报文头 */
		CM_LongToChar(NET_20K_SIZE + 21u, p_2oo2_Ctrl->UDP_Cache);        /* 长度（暂时填写满帧） */
		CM_LongToChar(p_2oo2_Ctrl->LocalID, p_2oo2_Ctrl->UDP_Cache + 4);  /* 源ID */
		CM_LongToChar(p_2oo2_Ctrl->PeerID, p_2oo2_Ctrl->UDP_Cache + 8);   /* 目的ID */
		CM_LongToChar(SysCyc, p_2oo2_Ctrl->UDP_Cache + 12);              /* 周期号 */
		CM_LongToChar(p_2oo2_Ctrl->SN, p_2oo2_Ctrl->UDP_Cache + 16);      /* 序列号 */
		p_2oo2_Ctrl->UDP_Cache[20] = (CM_UINT8)msgType;                  /* 类型 */
		CM_ShortToChar(FrameNum, p_2oo2_Ctrl->UDP_Cache + 21);           /* 帧总数 */
		/* p_2oo2_Ctrl->UDP_Cache + 23 */                                /* 帧序号（后面填写） */

		/*- @alias 循环发送完整帧 */
		for (FrameIndex = FrameStart; FrameIndex < FrameNum; FrameIndex++)
		{
			CM_ShortToChar(FrameIndex, p_2oo2_Ctrl->UDP_Cache + 23);         /* 帧序号 */
			(void)CM_Memcpy((void*)(p_2oo2_Ctrl->UDP_Cache + 25),(CM_SIZE_T)NET_20K_SIZE, (const void*)(Data + ((FrameIndex - 1u)*NET_20K_SIZE)), (CM_SIZE_T)NET_20K_SIZE); /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
            if(ENUM_CM_TRUE == clacCrcFlag)
            {
                CM_LongToChar(NET_20K_SIZE + 25u, p_2oo2_Ctrl->UDP_Cache);        /* 长度（填写满帧） */
                crc = CM_Crc32(p_2oo2_Ctrl->UDP_Cache, NET_20K_SIZE + 25);
                CM_LongToChar(crc, &p_2oo2_Ctrl->UDP_Cache[NET_20K_SIZE + 25]);
                (void)p_2oo2_Ctrl->DataSend(p_2oo2_Ctrl->UDP_Cache, (CM_INT32)(29u + NET_20K_SIZE));  /* 交给通信层发送 */
            }
            else
            {
			(void)p_2oo2_Ctrl->DataSend(p_2oo2_Ctrl->UDP_Cache, (CM_INT32)(25u + NET_20K_SIZE));  /* 交给通信层发送 */
            }
		}

		/*- 发送尾帧 */
		CM_ShortToChar(FrameIndex, p_2oo2_Ctrl->UDP_Cache + 23);    /*帧序号*/
		FrameLen = (CM_UINT32)DataLen - ((FrameIndex - 1u)*NET_20K_SIZE);
		(void)CM_Memcpy((void*)(p_2oo2_Ctrl->UDP_Cache + 25), (CM_SIZE_T)NET_20K_SIZE, (const void*)(Data + ((FrameIndex - 1u)*NET_20K_SIZE)), (CM_SIZE_T)FrameLen); /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */
		CM_LongToChar(21u + FrameLen, p_2oo2_Ctrl->UDP_Cache);       /*长度*/
        if(ENUM_CM_TRUE == clacCrcFlag)
        {
            CM_LongToChar(25u + FrameLen, p_2oo2_Ctrl->UDP_Cache);       /*长度*/
            crc = CM_Crc32(p_2oo2_Ctrl->UDP_Cache, FrameLen + 25);
            CM_LongToChar(crc, &p_2oo2_Ctrl->UDP_Cache[FrameLen + 25]);
            (void)p_2oo2_Ctrl->DataSend(p_2oo2_Ctrl->UDP_Cache, (CM_INT32)CM_UNSIGNED_INT_EXP(29u + FrameLen));   /* 交给通信层发送 */
        }
        else
        {
		(void)p_2oo2_Ctrl->DataSend(p_2oo2_Ctrl->UDP_Cache, (CM_INT32)CM_UNSIGNED_INT_EXP(25u + FrameLen));   /* 交给通信层发送 */
        }
		PFM_LOG_SET_STOPTIMER(TSCP_DARK_NULL);
    }
    else
    {
    	MSCP_LOG_MSG("s_2oo2_Send err!!!\n");
    	/*设置警告日志*/
    	(void)CFM_DarkLog_Storage(DARK_LOG_HEX, TSCP_DARK_WARNING, 4U, DARK_FAULT_CODE_MERGE(SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_5, TSCP_DARK_ASSERT_1));
    }
}

/**
* @brief 2oo2重传请求与ACK发送
* @param p_2oo2_Ctrl  	2取控制参数结构体
* @param msgType    	发送报文类型
* @param FrameIndex 	重传请求起始序号
*/
static void s_2oo2_ARQ_Send(STRU_2OO2_CTRL_T *p_2oo2_Ctrl,IN ENUM_2oo2_MSG_T msgType,CM_UINT16 FrameIndex)
{
    /*- 变量初始化 */
    CM_UINT32 SysCyc = SFM_2x2_GetSysCount(); /*系统周期号*/
    CM_UINT32 crc = 0u;
	/*- 入口检查 */
    CM_ASSERT(((NULL != p_2oo2_Ctrl) && (NULL != p_2oo2_Ctrl->DataSend)), SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_13, TSCP_DARK_ASSERT_1);
    /*
    2oo2通信帧格式：
    4字节 长度(不含自身)
    4字节 源ID
    4字节 目的ID
    4字节 周期号
    4字节 序列号
    1字节 类型
    2字节 需要重传的帧序号
    4字节 CRC(含长度)
    */
    /*- 填充报文头 */
    CM_LongToChar(23u, p_2oo2_Ctrl->UDP_Cache);        /* 长度（暂时填写满帧） */
    CM_LongToChar(p_2oo2_Ctrl->LocalID, p_2oo2_Ctrl->UDP_Cache + 4);  /* 源ID */
    CM_LongToChar(p_2oo2_Ctrl->PeerID, p_2oo2_Ctrl->UDP_Cache + 8);   /* 目的ID */
    CM_LongToChar(SysCyc, p_2oo2_Ctrl->UDP_Cache + 12);              /* 周期号 */
    CM_LongToChar(p_2oo2_Ctrl->SN, p_2oo2_Ctrl->UDP_Cache + 16);      /* 序列号 */
    p_2oo2_Ctrl->UDP_Cache[20] = (CM_UINT8)msgType;                  /* 类型 */
    CM_ShortToChar(FrameIndex, p_2oo2_Ctrl->UDP_Cache + 21);         /* 重传序号 */
    crc = CM_Crc32(p_2oo2_Ctrl->UDP_Cache, 23);
    CM_LongToChar(crc, p_2oo2_Ctrl->UDP_Cache + 23);

    (void)p_2oo2_Ctrl->DataSend(p_2oo2_Ctrl->UDP_Cache, (CM_INT32)27);   /* 交给通信层发送 */
}
/**
* @brief 2oo2接收支持重传
* @param RxData 		接收报文指针
* @param RxSize 		接收缓冲区大小
* @param pMsgType 		收到的报文类型
* @param Send_Data 		发送报文指针，用于重传
* @param Send_DataLen 	发送报文长度，用于重传
* @param p_2oo2_Ctrl 	2取控制参数结构体与临时缓冲区
* @param Send_msgType 	发送的数据类型，用于重传
* @return 收到的报文长度
* @details
*/
static CM_INT32 s_2oo2_ARQ_Recv(OUT CM_UINT8 *RxData, IN CM_UINT32 RxSize, OUT ENUM_2oo2_MSG_T *pMsgType,\
								IN const CM_UINT8 *Send_Data, IN CM_INT32 Send_DataLen, STRU_2OO2_CTRL_T *p_2oo2_Ctrl,IN ENUM_2oo2_MSG_T Send_msgType)
{
    /*- 变量初始化 */
    CM_INT32 rxLen = 0;       /* 网络接收长度 */
    CM_UINT32 msgLen = 0u;    /* 报文长度 */
    CM_UINT32 SrcID = 0u;     /* 源ID */
    CM_UINT32 DstID = 0u;     /* 目的ID */
    CM_UINT32 msgCyc = 0u;    /* 报文周期号 */
    CM_UINT32 msgSN = 0u;     /* 报文序列号 */
    CM_UINT16 FrameNum = 0u;  /* 帧数目 */
    CM_UINT16 FrameIndex = 0u;/* 帧序号 */
    CM_UINT8 *pData = NULL;   /* 指向帧数据的指针 */
    CM_INT32 DataLen = 0;     /* 帧数据长度 */
    CM_INT32 allLen = 0;      /* 收到的报文总长度 */
    CM_UINT8 type = 0u;       /* 报文类型 */
    CM_UINT16 ARQ_Cut = 0u;   /* 记录重传请求次数 */
    CM_UINT32 msgCrc = 0u;    /* 报文CRC */
    CM_UINT32 calcCrc = 0u;   /* 本地计算CRC */
    ENUM_CM_RETURN clacCrcFlag = ENUM_CM_TRUE;         /* 判断是否计算CRC*/
    CM_UINT16 ARQ_FrameStart = 0u;/* 重传帧序号 */
    CM_UINT8 MineRcvSuccessFlag = CM_FALSE;/*本机接收完成标志*/
    CM_UINT8 PeerRcvSuccessFlag = CM_FALSE;/*对系接收完成标志*/
    CM_UINT64 curTick = 0u; /* 当前时刻 */
    CM_UINT64 sTick = 0u; /* 起始Tick */
    CM_UINT32 sysCyc = SFM_2x2_GetSysCount();    /* 系统周期号 */
    ENUM_CM_RETURN ret = ENUM_CM_FALSE;          /* 临时变量 */
    PFM_LOG_SET_STOPTIMER(TSCP_DARK_2OO2);
    /*- 入参检查     */
    CM_ASSERT((NULL != RxData) && (NULL != pMsgType) && (NULL != Send_Data) && (NULL != p_2oo2_Ctrl) && (NULL != p_2oo2_Ctrl->DataRecv), SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_6, TSCP_DARK_ASSERT_1);
    /*- 记录起始时间*/
    sTick = CM_GetTick();
    /*- 循环接收2oo2数据 */
    while(1)
    {
        /*- 从通信层接收2oo2报文 */
        rxLen = p_2oo2_Ctrl->DataRecv(p_2oo2_Ctrl->UDP_Cache, (CM_UINT32)sizeof(p_2oo2_Ctrl->UDP_Cache));
        /*- 收到的报文长度是否大于23 */
        if (25 <= rxLen)
        {
            
            /*- 解析2oo2报文头 */
            /*
            2oo2通信帧格式：
            4字节 长度（不含自身）
            4字节 源ID
            4字节 目的ID
            4字节 周期号
            4字节 序列号
            1字节 类型
            2字节 帧总数
            2字节 帧序号
            n字节 数据
            4字节 CRC（含长度）
            */
            msgLen = CM_LongFromChar(p_2oo2_Ctrl->UDP_Cache);
            SrcID = CM_LongFromChar(p_2oo2_Ctrl->UDP_Cache + 4);
            DstID = CM_LongFromChar(p_2oo2_Ctrl->UDP_Cache + 8);
            msgCyc = CM_LongFromChar(p_2oo2_Ctrl->UDP_Cache + 12);
            msgSN = CM_LongFromChar(p_2oo2_Ctrl->UDP_Cache + 16);
            type = p_2oo2_Ctrl->UDP_Cache[20];
            clacCrcFlag = s_isClac2oo2Crc(type);
            if(ENUM_CM_TRUE == clacCrcFlag)
            {
				msgCrc = CM_LongFromChar(p_2oo2_Ctrl->UDP_Cache + msgLen);
				calcCrc = CM_Crc32(p_2oo2_Ctrl->UDP_Cache, msgLen);
            }
            else
            {
                calcCrc = msgCrc;
            }
			if (((msgLen + 4u) == (CM_UINT32)rxLen)
				&& (SrcID == p_2oo2_Ctrl->PeerID)
				&& (DstID == p_2oo2_Ctrl->LocalID)
				&& (msgCyc == sysCyc)
				&& (msgSN == p_2oo2_Ctrl->SN)
                && (msgCrc == calcCrc))
			{
				switch(type)
				{
				case ENUM_2oo2_ARQ:
					/*- 执行重新发送*/
					if(CM_FALSE == PeerRcvSuccessFlag)
					{
						ARQ_FrameStart = CM_ShortFromChar(p_2oo2_Ctrl->UDP_Cache + 21);
						s_2oo2_Send(Send_Data,Send_DataLen,p_2oo2_Ctrl,Send_msgType,ARQ_FrameStart);
					}
					else
					{
						/*无处理*/
					}
					if (CM_TRUE == MineRcvSuccessFlag)
					{
						s_2oo2_ARQ_Send(p_2oo2_Ctrl,ENUM_2oo2_RCV_SUCCESS,0);
					}
					break;
				case ENUM_2oo2_RCV_SUCCESS:
					/*- 记录接收完成标志,不响应重传请求*/
					PeerRcvSuccessFlag = CM_TRUE;
					break;
				default:
					FrameNum = CM_ShortFromChar(p_2oo2_Ctrl->UDP_Cache + 21);
					FrameIndex = CM_ShortFromChar(p_2oo2_Ctrl->UDP_Cache + 23);

					/*- 是否接收成功 */
					if (CM_FALSE == MineRcvSuccessFlag)
					{
						pData = p_2oo2_Ctrl->UDP_Cache + 25;
                        if(ENUM_CM_TRUE == clacCrcFlag)
                        {
                            DataLen = (CM_INT32)msgLen - 25;
                        }
                        else
                        {
						DataLen = (CM_INT32)msgLen - 21;
                        }
						/*- 分帧接收2oo2报文 */
						ret = SFM_FrameSave(&p_2oo2_Ctrl->FrameCtrl, type, FrameNum, FrameIndex, pData, DataLen, RxData, &allLen, RxSize);
						/*- 是否收齐了完整2oo2报文 */
						if (ENUM_CM_TRUE == ret)
						{
							/*- 输出报文类型，退出循环 */							
							*pMsgType = (ENUM_2oo2_MSG_T)type;
							/*- 接收成功发送成功标志，此处记录跳出标志 */
							MineRcvSuccessFlag = CM_TRUE;
							s_2oo2_ARQ_Send(p_2oo2_Ctrl,ENUM_2oo2_RCV_SUCCESS,0);
							s_2oo2_ARQ_Send(p_2oo2_Ctrl,ENUM_2oo2_RCV_SUCCESS,0);
							s_2oo2_ARQ_Send(p_2oo2_Ctrl,ENUM_2oo2_RCV_SUCCESS,0);
						}
						else
						{
							/*- 无处理，为防止乱序错误，此处不发送重传请求 */
						}
						sTick = CM_GetTick();
					}
					else
					{
						/*- 接收成功，重复数据无处理 */
					}
					break;
				}
			}
			else if(type == ENUM_2oo2_RCV_SUCCESS)
			{
				/*是接收成功回复 不做处理*/;
			}
			else
			{
				MSCP_LOG_MSG("len=[%d,%d]src=[%d,%d]dst=[%d,%d]cyc=[%d,%d]sn=[%d,%d],type=%d, crc:%d, calcrc:%d\n",
							rxLen, msgLen, SrcID, p_2oo2_Ctrl->PeerID, DstID, p_2oo2_Ctrl->LocalID,
							msgCyc, sysCyc, msgSN, p_2oo2_Ctrl->SN,type, msgCrc, calcCrc);
				(void)CFM_DarkLog_Multiple(DARK_LOG_HEX, TSCP_DARK_2OO2_RCV_ERR,11u,
										sizeof(CM_INT32),rxLen,
										sizeof(CM_UINT32),msgLen,
										sizeof(CM_UINT32),SrcID,
										sizeof(CM_UINT32),p_2oo2_Ctrl->PeerID,
										sizeof(CM_UINT32),DstID,
										sizeof(CM_UINT32),p_2oo2_Ctrl->LocalID,
										sizeof(CM_UINT32),msgCyc,
										sizeof(CM_UINT32),sysCyc,
										sizeof(CM_UINT32),msgSN,
										sizeof(CM_UINT32),p_2oo2_Ctrl->SN,
										sizeof(CM_UINT8),type);
			}

        }
        else
		{
        	/*- 记录当前时间*/
        	curTick = CM_GetTick();
        	/*- 超时重传，接收成功标志不响应*/
        	if((CM_UNSIGNED_MILLION*10u) < (curTick-sTick))
        	{
        		/*- 超时发送请求*/
        		s_2oo2_ARQ_Send(p_2oo2_Ctrl,ENUM_2oo2_ARQ,p_2oo2_Ctrl->FrameCtrl.FramIndex + 1u);
        		MSCP_LOG_MSG("[%d]s_2oo2_ARQ_Send:%d\n",p_2oo2_Ctrl->SN,p_2oo2_Ctrl->FrameCtrl.FramIndex + 1);
        		/*- 重置起始时间*/
        		sTick = CM_GetTick();
                ARQ_Cut++;
                (void)CFM_DarkLog_Multiple(DARK_LOG_HEX, TSCP_DARK_2OO2_RCV_ARQ,3u,
                						sizeof(CM_UINT32),p_2oo2_Ctrl->SN,
                						sizeof(CM_UINT16),(p_2oo2_Ctrl->FrameCtrl.FramIndex + 1u),
                						sizeof(CM_UINT16),ARQ_Cut);
        	}
        	else
        	{
        		/*- 无处理 */
        	}
		}
        /*本机与邻机接收成功或重传次数超过1分钟（6000次）退出重传 */
        if(((CM_TRUE == MineRcvSuccessFlag) && (CM_TRUE == PeerRcvSuccessFlag)) || (ARQ_Cut >= 6000))
        {
        	/*- 结束接收 */
        	/*- 结束接收 重传超过6000次日志记录 */
        	if(ARQ_Cut >= 6000)
        	{
        		MSCP_LOG_MSG("2oo2 failed break ARQ_Cut = %d, MineRcvSuccessFlag = %d, PeerRcvSuccessFlag = %d\n", ARQ_Cut, MineRcvSuccessFlag, PeerRcvSuccessFlag);
        		CM_ASSERT(0,SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_6, TSCP_DARK_ASSERT_2);
        	}
        	else
        	{
        		;
        	}
        	break;
        }
    }
    PFM_LOG_SET_STOPTIMER(TSCP_DARK_NULL);
    /*- 返回报文总长度 */
    return allLen;
}
/**
* @breif 全双工通用2oo2数据交换，支持重传
* @param pMyData 本机数据指针
* @param DataLen 本剧数据长度
* @param pPeerData 邻机数据指针
* @param DataSize 邻机数据缓冲区大小
* @param msgTye 2oo2交换数据类型
* @return 收到的邻机数据长度
*/
CM_INT32 SFM_2oo2_Exchange_Currency(IN const CM_UINT8 * pMyData, IN CM_INT32 DataLen, OUT CM_UINT8 * pPeerData, IN CM_UINT32 DataSize, IN ENUM_2oo2_MSG_T msgType)
{
    /*- 变量初始化 */
    CM_INT32 rxLen = -1;
    ENUM_2oo2_MSG_T rxType = ENUM_2oo2_ERROR;

    /*- 入口检查 */
    CM_ASSERT(DataLen >= 0, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_8, TSCP_DARK_ASSERT_1);
    
    /*- 更新2oo2通信序列号 */
    s_2oo2_Ctrl.SN++;

	/*- 发送2oo2数据 */
	s_2oo2_Send(pMyData, DataLen,&s_2oo2_Ctrl, msgType,1u);
	/*等待接收端应答*/
	rxLen = s_2oo2_ARQ_Recv(pPeerData, DataSize, &rxType,pMyData, DataLen, &s_2oo2_Ctrl,msgType);

    CM_RE_ASSERT(msgType == rxType, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_8, TSCP_DARK_ASSERT_2);

    CM_RE_ASSERT(0 <= rxLen, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_8, TSCP_DARK_ASSERT_3);

    /*- 返回收到的报文长度 */
    return rxLen;
}
/**
* @breif 应用全双工2oo2数据交换支持重传
* @param pMyData 本机数据指针 取值范围：非空
* @param DataLen 本剧数据长度 取值范围：0~0x7FFFFFFF
* @param pPeerData 邻机数据指针 取值范围：非空
* @param DataSize 邻机数据缓冲区大小 取值范围：0~0xFFFFFFFF
* @return 收到的邻机数据长度 取值范围：0~0x7FFFFFFF
*/
CM_INT32 SFM_2oo2_Currency_App(IN const CM_UINT8 pMyData[], IN CM_INT32 DataLen, OUT CM_UINT8 pPeerData[], IN CM_UINT32 DataSize)
{
    /*- 变量初始化 */
    CM_INT32 rxLen = -1;
    ENUM_2oo2_MSG_T rxType = ENUM_2oo2_ERROR;

    /*- 入口检查 */
    CM_ASSERT(DataLen >= 0, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_7, TSCP_DARK_ASSERT_1);
    CM_ASSERT((NULL != pMyData) && (NULL != pPeerData), SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_7, TSCP_DARK_ASSERT_3);

    /*- 更新2oo2通信序列号 */
    s_2oo2_Ctrl_App.SN++;

	/*- 发送2oo2数据 */
	s_2oo2_Send(pMyData, DataLen,&s_2oo2_Ctrl_App, ENUM_2oo2_APP,1u);
	/*等待接收端应答*/
	rxLen = s_2oo2_ARQ_Recv(pPeerData, DataSize, &rxType,pMyData, DataLen, &s_2oo2_Ctrl_App,ENUM_2oo2_APP);

    CM_ASSERT(0 <= rxLen, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_7, TSCP_DARK_ASSERT_2);

    /*- 返回收到的报文长度 */
    return rxLen;
}

/**
* @brief 2oo2同步
* @param pOffset 本机时钟调整量
* @return 2oo2同步完成的时刻
* @details
* 通过互发时钟同步报文并检查接收的方式实现2oo2同步
* 互发的同步报文同时交换了上一次时钟同步的结果
* 每次2oo2同步之后都会发起一次新的时钟同步（由时钟同步线程接收、处理）
*/
CM_UINT64 SFM_2oo2_Sync(OUT CM_INT64 *pOffset)
{
    /*- 变量初始化 */
	static CM_INT64 s_tm_mysum = 0;
	static CM_INT64 s_tm_peersum = 0;
	static CM_UINT32 s_tm_count = 0u;
	static CM_UINT32 s_tm_nadd = 0u;
	static CM_UINT32 s_tm_nsub = 0u;
    CM_INT32 rxLen = 0;            /* 接收数据长度 */
    CM_UINT8 myBuf[100] = { 0u };   /* 本机报文缓冲区 */
    CM_UINT8 peerBuf[100] = { 0u }; /* 邻机报文缓冲区 */
    CM_INT64 myDelay = 0;          /* 本机延迟时间 */
    CM_INT64 myOffset = 0;         /* 本机偏移时间 */
    CM_INT64 peerDelay = 0;
    CM_INT64 peerOffset = 0;
    CM_INT64 val = 0;
    CM_INT64 aveOffset = 0;
    CM_INT64 tempval = 0;       /*临时变量*/
    CM_UINT64 rxTick = 0u;
    CM_UINT64 costlog[20];    /* 耗时记录 */
    ENUM_CM_RETURN ret = ENUM_CM_FALSE;
    if(NULL != pOffset)
    {
    	costlog[0] = CM_GetTick();
		/*- 取得上一次校时结果 */
		ret = CFM_2oo2_TimeGet(&myDelay, &myOffset);
		/*- 是否成功取得校时结果 */
		if (ENUM_CM_TRUE == ret)
		{
			/*- 无处理 */
		}
		else
		{
			/*- 校时结果置为无效值 */
			myDelay = (CM_INT64)CM_SIGNED_INT_EXP(100 * CM_MILLION);
			myOffset = (CM_INT64)CM_SIGNED_INT_EXP(100 * CM_MILLION);
		}

		/*- 2oo2同步，并交换校时结果 */
		CM_64ToChar(myDelay, myBuf);
		CM_64ToChar(myOffset, myBuf + 8);
		costlog[1] = CM_GetTick();
		MSCP_LOG_MSG("SFM_2oo2_Sync1 = %d us\n", (costlog[1] - costlog[0]) / 1000u);
		rxLen = SFM_2oo2_Exchange_Currency(myBuf, (CM_INT32)16, peerBuf, (CM_UINT32)16, ENUM_2oo2_SYNC);
		costlog[2] = CM_GetTick();
		MSCP_LOG_MSG("SFM_2oo2_Sync2 = %d us\n", (costlog[2] - costlog[1]) / 1000u);
		/*- 记录同步完成时间 */
		rxTick = CM_GetTick();

		/*- 检查同步数据长度是否一致 */
		CM_ASSERT(16 == rxLen, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_10, TSCP_DARK_ASSERT_1);

		/*- 发起下一次校时备用*/
		CFM_2oo2_TimeReq();
		costlog[3] = CM_GetTick();
		MSCP_LOG_MSG("SFM_2oo2_Sync3 = %d us\n", (costlog[3] - costlog[2]) / 1000u);
		(void)CFM_DarkLog_Multiple(DARK_LOG_HEX, TSCP_DARK_2OO2_SYNC_TIME,3u,
								sizeof(CM_UINT64),(costlog[1] - costlog[0]) / 1000u,
								sizeof(CM_UINT64),(costlog[2] - costlog[1]) / 1000u,
								sizeof(CM_UINT64),(costlog[3] - costlog[2]) / 1000u);
		peerDelay = (CM_INT64)CM_64FromChar(peerBuf);
		peerOffset = (CM_INT64)CM_64FromChar(peerBuf + 8);
		*pOffset = 0;
		/*- 本次测得的偏移量是否在合理范围内 */
		if ((CM_ABS(myOffset) < (10 * CM_MILLION)) && (CM_ABS(peerOffset) < (10 * CM_MILLION)))
		{
			/*- 更新环形队列和累计数据 */
			(void)CM_RB_Get(&s_2oo2_TmRing, (CM_UINT16)SFM_2oo2_TM_RING_SIZE, &val);
			s_tm_mysum -= val;
			(void)CM_RB_Write(&s_2oo2_TmRing, myOffset);
			s_tm_mysum += myOffset;
			(void)CM_RB_Get(&s_2oo2_TmRing, (CM_UINT16)SFM_2oo2_TM_RING_SIZE, &val);
			s_tm_peersum -= val;
			(void)CM_RB_Write(&s_2oo2_TmRing, peerOffset);
			s_tm_peersum += peerOffset;
			/*- 累加同步次数 */
			s_tm_count += 2u;
			/*- 是否累计了足够多的数据 */
			if (s_tm_count >= SFM_2oo2_TM_RING_SIZE)
			{
				/*- 计算平均偏移量 */
				aveOffset = (s_tm_mysum - s_tm_peersum) / (CM_INT64)CM_UNSIGNED_INT_EXP(SFM_2oo2_TM_RING_SIZE);
				/*- 本机时钟是否过慢 */
				if (aveOffset > (SFM_2oo2_TM_FIX_VAL * CM_MILLION))
				{
					/*- 缩短本周期时间1ms */
					*pOffset = -CM_MILLION;
					s_tm_nsub++;
					s_tm_count = 0u;
				}
				/*- 本机时钟是否过快 */
				else if (aveOffset < (-SFM_2oo2_TM_FIX_VAL * CM_MILLION))
				{
					/*- 延长本周期时间 1ms */
					*pOffset = CM_MILLION;
					s_tm_nadd++;
					s_tm_count = 0u;
				}
				else
				{
					MSCP_LOG_MSG("aveOffset = %d\n", aveOffset);
				}
			}
			else
			{
				MSCP_LOG_MSG("s_tm_count = %d\n", s_tm_count);
			}
			/*- 记录同步结果 */
            tempval = (CM_INT64)CM_UNSIGNED_INT_EXP(SFM_2oo2_TM_RING_SIZE / 2u);
			MSCP_LOG_MSG("2oo2 sync:%lld,%lld,%lld,%lld(us)\n", myDelay / tempval, myOffset / tempval, peerDelay / tempval, peerOffset / tempval);
			MSCP_LOG_MSG("2oo2 offset_sum:%lld,%lld(us),%d,%d\n", s_tm_mysum / tempval, s_tm_peersum / tempval ,s_tm_nadd,s_tm_nsub);
			(void)CFM_DarkLog_Multiple(DARK_LOG_HEX, TSCP_DARK_2OO2_ALIGN_TIME,8u,
			                						sizeof(CM_INT64),myDelay / tempval,
			                						sizeof(CM_INT64),myOffset / tempval,
			                						sizeof(CM_INT64),peerDelay / tempval,
													sizeof(CM_INT64),peerOffset / tempval,
													sizeof(CM_INT64),s_tm_mysum / tempval,
													sizeof(CM_INT64),s_tm_peersum / tempval,
													sizeof(CM_UINT32),s_tm_nadd,
													sizeof(CM_UINT32),s_tm_nsub);
		}
		else
		{
			/*- 无处理 */
			/* 上电初始化阶段尚未校准周期定时器，此时获取的offset无效 */
			MSCP_LOG_MSG("tm sync fail:%lld,%lld\n", myOffset,peerOffset);
		}
    }
    else
    {
    	MSCP_LOG_MSG("pOffset = NULL\n");
    }
    /*- 返回同步完成时间 */
    return rxTick;
}

/**
* @brief 2oo2发送(用于维护模式下的2取发送)
* @param Data    发送报文指针
* @param DataLen 发送报文长度
* @param msgType 发送报文类型
* @details
* 维护模式下的2取数据量小,不分包,无周期号
*/
static void s_2oo2_Send_Crash(IN const CM_UINT8 *Data, IN CM_INT32 DataLen, STRU_2OO2_CTRL_T *p_2oo2_Ctrl,IN ENUM_2oo2_MSG_T msgType)
{
    /*- 变量初始化 */
    CM_UINT32 crc = 0u;          /* CRC */
    /*- 入口检查 */
    CM_ASSERT_MAINTAIN((DataLen >= 0) && (NULL != Data));

    /*
         维护模式下2oo2通信帧格式：
    4字节 长度(不含自身)
    4字节 源ID
    4字节 目的ID
    4字节 序列号
    1字节 类型
    n字节 数据
    4字节 CRC(含长度)
    */
    /*- 填充报文头 */
    CM_LongToChar(17u + DataLen, p_2oo2_Ctrl->UDP_Cache);        /* 长度  */
    CM_LongToChar(p_2oo2_Ctrl->LocalID, p_2oo2_Ctrl->UDP_Cache + 4);  /* 源ID */
    CM_LongToChar(p_2oo2_Ctrl->PeerID, p_2oo2_Ctrl->UDP_Cache + 8);   /* 目的ID */
    CM_LongToChar(p_2oo2_Ctrl->SN, p_2oo2_Ctrl->UDP_Cache + 12);      /* 序列号 */
    p_2oo2_Ctrl->UDP_Cache[16] = (CM_UINT8)msgType;                  /* 类型 */

    (void)CM_Memcpy((void*)(p_2oo2_Ctrl->UDP_Cache + 17), (CM_SIZE_T)NET_20K_SIZE, (const void*)Data, (CM_SIZE_T)DataLen); /* parasoft-suppress MISRA2004-17_1 MISRA2004-17_4 "指针运算提高代码执行效率" */

    crc = CM_Crc32(p_2oo2_Ctrl->UDP_Cache, 17u + DataLen);
    CM_LongToChar(crc, p_2oo2_Ctrl->UDP_Cache + 17u + DataLen);  /*CRC*/

    (void)CFM_2oo2_DataSend_Crash(p_2oo2_Ctrl->UDP_Cache, (CM_INT32)CM_UNSIGNED_INT_EXP(21u + DataLen));   /* 交给通信层发送 */
}

/**
* @brief 2oo2接收(用于维护模式下的2取发送)
* @param RxData 接收报文指针
* @param RxSize 接收缓冲区大小
* @param pMsgType 收到的报文类型
* @return 收到的报文长度
* @details
* 2oo2报文是阻塞方式接收，也就是无报文时将阻塞在本函数内，有报文时立即接收并返回，
* 函数返回值0表示接收成功，但数据段长度为0
*/
static CM_INT32 s_2oo2_Recv_Crash(OUT CM_UINT8 *RxData, IN CM_UINT32 RxSize, OUT ENUM_2oo2_MSG_T *pMsgType,STRU_2OO2_CTRL_T *p_2oo2_Ctrl)
{
    /*- 变量初始化 */
    CM_INT32 rxLen = 0;       /* 网络接收长度 */
    CM_UINT32 msgLen = 0u;    /* 报文长度 */
    CM_UINT32 SrcID = 0u;     /* 源ID */
    CM_UINT32 DstID = 0u;     /* 目的ID */
    CM_UINT32 msgSN = 0u;     /* 报文序列号 */
    CM_UINT32 msgCrc = 0u;    /* 报文CRC */
    CM_UINT32 calcCrc = 0u;   /* 本地计算CRC */
    CM_INT32 allLen = 0;      /* 收到的报文总长度 */
    CM_UINT8 type = 0u;       /* 报文类型 */
    /*- 入参检查     */
    CM_ASSERT_MAINTAIN((NULL != RxData) && (NULL != pMsgType));
    /*- 循环接收2oo2数据 */
    while(1)
    {
        /*- 从通信层接收2oo2报文 */
        rxLen = CFM_2oo2_DataRecv_Crash(p_2oo2_Ctrl->UDP_Cache, (CM_UINT32)sizeof(p_2oo2_Ctrl->UDP_Cache));
        /*- 收到的报文长度是否大于0 */
        if (0 < rxLen)
        {
            /*- 解析2oo2报文头 */
        	CM_ASSERT_MAINTAIN(21 <= rxLen);
            /*
            2oo2通信帧格式：
            4字节 长度（不含自身）
            4字节 源ID
            4字节 目的ID
            4字节 序列号
            1字节 类型
            n字节 数据
            4字节 CRC（含长度）
            */
            msgLen = CM_LongFromChar(p_2oo2_Ctrl->UDP_Cache);
            SrcID = CM_LongFromChar(p_2oo2_Ctrl->UDP_Cache + 4);
            DstID = CM_LongFromChar(p_2oo2_Ctrl->UDP_Cache + 8);
            msgSN = CM_LongFromChar(p_2oo2_Ctrl->UDP_Cache + 12);
            type = p_2oo2_Ctrl->UDP_Cache[16];
            CM_ASSERT_MAINTAIN((msgLen + 4u) == (CM_UINT32)rxLen);

            msgCrc = CM_LongFromChar(p_2oo2_Ctrl->UDP_Cache + msgLen);
            calcCrc = CM_Crc32(p_2oo2_Ctrl->UDP_Cache, msgLen);
            /*- 校验报文头是否合法 */
            if ((SrcID == p_2oo2_Ctrl->PeerID)
                && (DstID == p_2oo2_Ctrl->LocalID)
                && (msgSN == p_2oo2_Ctrl->SN)
                && (msgCrc == calcCrc))
            {
            	/*RxData = p_2oo2_Ctrl->UDP_Cache + 17;*/
            	allLen = (CM_INT32)msgLen - 17;
            	(void)CM_Memcpy(RxData,RxSize,&p_2oo2_Ctrl->UDP_Cache[17],allLen);
                *pMsgType = (ENUM_2oo2_MSG_T)type;

                MSCP_LOG_MSG("msgLen = %d, SrcID = %d, DstID = %d, msgSN = %d, type = %d\n",msgLen,SrcID,DstID,msgSN,type);
    			(void)CFM_DarkLog_Multiple(DARK_LOG_HEX, TSCP_DARK_2OO2_CRASH_RCVOK,5u,
    			                						sizeof(CM_UINT32),msgLen,
    			                						sizeof(CM_UINT32),SrcID,
    			                						sizeof(CM_UINT32),DstID,
    													sizeof(CM_UINT32),msgSN,
    													sizeof(CM_UINT8),type);
                /*- 跳出 */
                break;
            }
            else
            {
                MSCP_LOG_MSG("len=[%d,%d]src=[%d,%d]dst=[%d,%d]cyc=[%d,%d]sn=[%d,%d],crc=[%d,%d]\n",
                    rxLen, msgLen, SrcID, p_2oo2_Ctrl->PeerID, DstID, p_2oo2_Ctrl->LocalID,
                    msgSN, p_2oo2_Ctrl->SN,msgCrc,calcCrc);
    			(void)CFM_DarkLog_Multiple(DARK_LOG_HEX, TSCP_DARK_2OO2_CRASH_RCVERR,10u,
    			                						sizeof(CM_INT32),rxLen,
    			                						sizeof(CM_UINT32),msgLen,
    			                						sizeof(CM_UINT32),SrcID,
    													sizeof(CM_UINT32),p_2oo2_Ctrl->PeerID,
    													sizeof(CM_UINT32),DstID,
    			                						sizeof(CM_UINT32),p_2oo2_Ctrl->LocalID,
    			                						sizeof(CM_UINT32),msgSN,
    			                						sizeof(CM_UINT32),p_2oo2_Ctrl->SN,
    			                						sizeof(CM_UINT32),msgCrc,
    			                						sizeof(CM_UINT32),calcCrc);
            }
        }
        else
		{
			/*- 无处理 */
		}
    }
    /* 返回报文总长度 */
    return allLen;
}

/**
* @breif 全双工2oo2数据交换(进入运营模式后的2取交换,限制时间)
* @param pMyData 本机数据指针
* @param DataLen 本剧数据长度
* @param pPeerData 邻机数据指针
* @param DataSize 邻机数据缓冲区大小
* @param msgTye 2oo2交换数据类型
* @return 收到的邻机数据长度
* @details
* 全双工模式进行2oo2数据交换
* 每一机均在同一时间向对方发送自己的交换数据
* 然后立即接收来自邻机的交换数据
*/
CM_INT32 SFM_2oo2_Exchange_FullDuplex_Crash(
    IN const CM_UINT8* pMyData,
    IN CM_INT32 DataLen,
    OUT CM_UINT8* pPeerData,
    IN CM_UINT32 DataSize,
    IN ENUM_2oo2_MSG_T msgType)
{
	/*- 变量初始化 */
	CM_INT32 rxLen = -1;
	ENUM_2oo2_MSG_T rxType = ENUM_2oo2_ERROR;

	/*- 入口检查 */
	CM_ASSERT_MAINTAIN((NULL != pMyData) && (NULL != pPeerData));
	CM_ASSERT_MAINTAIN(DataLen >= 0);

	/*- 更新2oo2通信序列号 */
	s_2oo2_Ctrl.SN++;
	/*- 发送2oo2数据 */
	s_2oo2_Send_Crash(pMyData, DataLen,&s_2oo2_Ctrl ,msgType);
	/*- 接收2oo2数据 */
	rxLen = s_2oo2_Recv_Crash(pPeerData, DataSize, &rxType,&s_2oo2_Ctrl);
	CM_ASSERT_MAINTAIN(msgType == rxType);
	CM_ASSERT_MAINTAIN(0 <= rxLen);

	/*- 返回接收长度 */
	return rxLen;
}

/**
 * @brief 二取判断是否重置自动重启次数
 * @details 每达到阈值，双机同时清零
 */
void SFM_2oo2_Reset_AutoRbootCut(void)
{
	/*- 变量初始化 */
    CM_UINT64 tickcur = 0u; /* 当前时间 */
    CM_UINT8 mybuf[1] = {0};
    CM_UINT8 peerbuf[1] = {0};
    CM_INT32 peerlen = 0;
    CM_UINT8 cut = 0xFFu;
    /*- 获取当前时间 */
	if (0u != g_PFM_Cfg.M_AutoClear_Second)
	{
		/*- 获取上电时间 */
		tickcur = CM_GetSecTick();
		/*- 上电时间超过清零时间 */
		if (tickcur > g_PFM_Cfg.M_AutoClear_Second)
		{
			/*- 超过阈值*/
			mybuf[0] = 0x55;
		}
		else
		{
			/*- 没超阈值*/
			mybuf[0] = 0xAA;
		}
		/*- 是否开启2取2开关 */
		if(0x55u == g_PFM_Cfg.M_2oo2_Enable)
		{
			/*- 二取交互状态*/
			peerlen = SFM_2oo2_Exchange_Currency(mybuf,1,peerbuf,1,ENUM_2oo2_RESET_AUTOREBOOT);
			CM_RE_ASSERT(peerlen > 0, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_12, TSCP_DARK_ASSERT_1);
		}
		/*- 判断双cpu是否同时清零 */
		if((0x55u == mybuf[0]) && (0x55u == peerbuf[0]))
		{
			/*- 清空自动重启次数*/
			cut = MSCP_Reset_AutoRbootCut(0);
			/*- 清空故障远程重启次数*/
			RM_Reset_FaultRbootCut(0);
			CM_RE_ASSERT(cut == 0u, SFM_2oo2_MODE, DARK_FAULT_CODE_FUN_ORDER_12, TSCP_DARK_ASSERT_2);
			/*- 同时修改时间阈值*/
			g_PFM_Cfg.M_AutoClear_Second += g_PFM_Cfg.M_AutoClear_Days*24u*60u*60u;
		}
		else
		{
			/*- 无处理 */
		}
	}
}
static ENUM_CM_RETURN s_isClac2oo2Crc(ENUM_2oo2_MSG_T type){
    ENUM_CM_RETURN rt = ENUM_CM_TRUE;
    switch (type)
    {
    case ENUM_2oo2_INPUT_EX:
    case ENUM_2oo2_END_EX:
        rt = ENUM_CM_FALSE;
        break;
    default:
        break;
    }
    return rt;
}

/**
 * @brief 初始化2取控制数据以及清空2取缓冲区
 * @details 初始化和退出运营开始时调用
 */
void SFM_2oo2_CtrlInit(void)
{
	/*- 初始化2取控制数据 */
	s_2oo2_Ctrl.LocalID = (g_PFM_Cfg.BOARD_ID * 256u) + g_PFM_Cfg.CPU_ID;
	s_2oo2_Ctrl.PeerID = CM_UNSIGNED_INT_EXP(((CM_UINT32)g_PFM_Cfg.BOARD_ID * 256u) + (3u - (CM_UINT32)g_PFM_Cfg.CPU_ID));
	s_2oo2_Ctrl.SN = 0u;
	s_2oo2_Ctrl.FrameCtrl.FramIndex = 0u;
	s_2oo2_Ctrl.FrameCtrl.FrameNum = 0u;
	s_2oo2_Ctrl.FrameCtrl.RxLen = 0;
	s_2oo2_Ctrl.FrameCtrl.type = 0u;
    s_2oo2_Ctrl.DataRecv = CFM_2oo2_DataRecv;
    s_2oo2_Ctrl.DataSend = CFM_2oo2_DataSend;
    /*- 清空2取缓冲区 */
    (void)memset(s_2oo2_Ctrl.UDP_Cache, 0, sizeof(s_2oo2_Ctrl.UDP_Cache));
    /*- 初始化应用2取结构体参数*/
	s_2oo2_Ctrl_App.LocalID = s_2oo2_Ctrl.LocalID;
	s_2oo2_Ctrl_App.PeerID = s_2oo2_Ctrl.PeerID;
    s_2oo2_Ctrl_App.SN = 0u;
    s_2oo2_Ctrl_App.FrameCtrl.FramIndex = 0u;
    s_2oo2_Ctrl_App.FrameCtrl.FrameNum = 0u;
    s_2oo2_Ctrl_App.FrameCtrl.RxLen = 0;
    s_2oo2_Ctrl_App.FrameCtrl.type = 0u;
    s_2oo2_Ctrl_App.DataRecv = CFM_2oo2_DataRecv_App;
    s_2oo2_Ctrl_App.DataSend = CFM_2oo2_DataSend_App;
    /*- 清空2取缓冲区 */
    (void)memset(s_2oo2_Ctrl_App.UDP_Cache, 0, sizeof(s_2oo2_Ctrl_App.UDP_Cache));
}
