/**
* @file PFM_ControlFlowCheck.c
* @brief Platform Function Module:控制流状态监督
* @details
* 采用改进的CFCSS算法，静态签名为大质数，校验运算为大质数因式分解
* @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 "PFM_ControlFlowCheck.h"
#include "CM_Time.h"
#include "DFM_Time.h"
#include "PFM_Log.h"
#include "CM_Atomic.h"
#include "PFM_Cfg.h"
#include "DFM_DRV.h"
#include "CFM_Net.h"

#if QNX
#include <atomic.h>
#include <sys/neutrino.h> /* ThreadCtl BMP绑定CPU*/

#define CM_ATOMIC_CLR(loc,bits) (atomic_clr((loc),(bits)))
#define CM_ATOMIC_SET(loc,bits) (atomic_set((loc),(bits)))
#else
#define CM_ATOMIC_CLR(loc,bits) ((*(loc)) &= (~(bits)))
#define CM_ATOMIC_SET(loc,bits) ((*(loc)) |= (bits))
#endif

/* 记录控制流步骤 */
CM_INT32 g_PFM_Check = 0;
/** 静态签名 */
static const CM_INT32 s_key_S[61] =
{ 0,
10007,10039,10061,10067,10079,10091,10099,10103,10111,10133,
10139,10141,14151,10159,10163,10177,10181,10193,10211,10223,
10243,10247,10253,10267,10271,10289,10301,10313,10333,10337,
10343,10357,10369,10391,10399,10427,10429,10433,10453,10457,
10463,10477,10487,10499,10501,10513,10529,10531,10599,10567,
10589,10597,10601,10607,10613,10627,10631,10639,10651,10657
};

/** 调整码 */
static CM_INT32 s_key_d[61];

/** 监督码 */
static CM_INT32 s_key_G;

/** 运行标记 */
static CM_ATOMIC CM_UINT32 s_RunFlag;

/** 主看门狗运行标记 */
static CM_ATOMIC CM_UINT32 s_RunFlag_MainDog;

/** 备看门狗运行标记 */
static CM_ATOMIC CM_UINT32 s_RunFlag_PrepareDog;

/**
* @brief 控制流检查算法初始化
*/
void PFM_CFC_Init(void)
{
    /*- 变量初始化 */
    CM_INT32 i = 0; /* 循环变量 */

#if (PFM_CFC_PROC_NUM >= 60)
    CM_ASSERT(0u, CONTROL_FLOW_CHECK_MODE, DARK_FAULT_CODE_FUN_ORDER_1, TSCP_DARK_ASSERT_1);
#endif

    s_key_d[1] = s_key_S[PFM_CFC_PROC_NUM] * s_key_S[1];
    /*- @alias 初始化调整码*/
    for (i = 2; i <= PFM_CFC_PROC_NUM; i++)
    {
        s_key_d[i] = s_key_S[i - 1] * s_key_S[i];
    }

    /*- 监督码初值为循环最后一个代码块的静态签名*/
    s_key_G = s_key_S[PFM_CFC_PROC_NUM];
}

/**
* @brief 控制流校验
* @param step 当前执行阶段
*/
void PFM_CFC_Check(CM_INT32 step)
{
	g_PFM_Check = step;
    /*- 变量初始化 */
    CM_INT32 res = 0; /* 临时变量 */

    /*- 入口检查 */
    CM_ASSERT((0 < step) && (step <= PFM_CFC_PROC_NUM), CONTROL_FLOW_CHECK_MODE, DARK_FAULT_CODE_FUN_ORDER_2, TSCP_DARK_ASSERT_1);

    /*- 校验运算1 */
    res = s_key_d[step] % s_key_G;
    CM_ASSERT(res == 0, CONTROL_FLOW_CHECK_MODE, DARK_FAULT_CODE_FUN_ORDER_2, TSCP_DARK_ASSERT_2);
    /*- 校验运算2，并更新监督码 */
    s_key_G = s_key_d[step] / s_key_G;
    CM_ASSERT(s_key_G == s_key_S[step], CONTROL_FLOW_CHECK_MODE, DARK_FAULT_CODE_FUN_ORDER_2, TSCP_DARK_ASSERT_3);
}

/**
* @brief 获取当前的执行阶段
* @return 当前执行阶段
*/
CM_INT32 PFM_CFC_GetCurStep(void)
{
    /*- 变量初始化 */
    CM_INT32 i = 0;          /* 循环变量 */
    CM_INT32 keyG = s_key_G; /* 临时变量 */
    /*- @alias 遍历静态签名查找监督码对应执行阶段 */
    for (i = PFM_CFC_PROC_NUM; i > 0; i--)
    {
        if (s_key_S[i] == keyG)
        {
            /*- 返回 */
            break;
        }
        else
		{
			/*- 无处理 */
		}
    }
    /* 输出当前执行阶段 */
    return i;
}

/**
* @brief 平台状态监督/主狗
*/
void PFM_CFC_Monitor(void)
{
    /*- 变量初始化 */
	CM_UINT64 startTimer = 0u;
	CM_UINT64 nowTimer = 0u;
	CM_UINT64 leftTimer = 0u;

	CM_UINT64 startTimer1 = 0u;
	CM_UINT64 leftTimer1 = 0u;

    CM_INT32 step = 0; /* 程序步骤 */
    CM_UINT32 cycTime = g_PFM_Cfg.CYC_TIME + 21u;
	CM_UINT32 cycTime1 = g_PFM_Cfg.CYC_TIME + 41u;
    CM_UINT32 cycCount = g_PFM_Cfg.CYC_TIME/2u + 1u;
	CM_UINT8  loop_count = 0u;
    /*- @alias 等待主任务启动 */
    while (0u == CM_ATOMIC_LOAD(&s_RunFlag))
    {
    	/*- 休眠等待进入主周期并且以喂狗 */
        CM_Sleep(2u);
    }
	
	/*- 主狗喂狗 */
	(void)PFM_CFC_WatchDog_Main();

	/*- @alias 等待备看门狗启动 */
    while (0u == CM_ATOMIC_LOAD(&s_RunFlag_PrepareDog))
    {
    	/*- 休眠等待备狗以喂狗 */
        CM_Sleep(2u);
		loop_count++;
		if(cycCount < loop_count )
		{
			/*- 如果轮询了周期时间/2次备狗还未启动  ，已经出现异常，宕机处理 */
			MSCP_LOG_MSG("prepare watchdog start timeout. \n");
#ifdef QNX
			(void)DFM_ledMonitor(2u);
#endif
			CM_RE_ASSERT(0, CONTROL_FLOW_CHECK_MODE, DARK_FAULT_CODE_FUN_ORDER_3, TSCP_DARK_ASSERT_1);

		}
		else
		{
			/*- 无处理 */
		}
    }

    /*- 循环等待软中断 */
    while (1)
    {
		/*- 主狗喂狗 */
    	(void)PFM_CFC_WatchDog_Main();
    	/*- 平台主线程以喂狗，并且备狗以喂狗，并且喂狗时间合法*/
		if(0x55u == CM_ATOMIC_LOAD(&s_RunFlag))
		{
			/*- 清除运行标志 */
			(void)CM_ATOMIC_STORE(&s_RunFlag, 0x00u);
			startTimer = CM_GetTick();
#ifdef QNX
			(void)DFM_ledMonitor(1u);
#endif
		}
		/*- 只要leftTimer大于等于cycTime就宕机*/
		else if(leftTimer >= cycTime)
		{
			/*- 宕机并输出平台所处步骤 */
			step = PFM_CFC_GetCurStep();
            (void)CFM_DarkLog_Multiple(DARK_LOG_HEX, TSCP_DARK_PFMCONTROFLOWCHECK_MONITOR1, 3u, \
            					sizeof(CM_INT32),step,
            					sizeof(CM_UINT64),leftTimer,
								sizeof(CM_UINT32),cycTime);
            MSCP_LOG_MSG("timeout at step %d,s_RunFlag = 0x%.2x,leftTimer = %lu,cycTime = %lu\n",step,s_RunFlag,leftTimer,cycTime);
			(void)printf("timeout at step %d,s_RunFlag = 0x%.2x,leftTimer = %lu,cycTime = %u\n",step,s_RunFlag,leftTimer,cycTime);
#ifdef QNX
			(void)DFM_ledMonitor(2u);
#endif
			CM_RE_ASSERT(0, CONTROL_FLOW_CHECK_MODE, DARK_FAULT_CODE_FUN_ORDER_3, TSCP_DARK_ASSERT_2);
		}
		else
		{
			/*- 无处理 */
		}

		/*- 备狗以喂狗，并且喂狗时间合法*/
		if(0x55u == CM_ATOMIC_LOAD(&s_RunFlag_PrepareDog))
		{
			/*- 清除运行标志 */
			(void)CM_ATOMIC_STORE(&s_RunFlag_PrepareDog, 0x00u);
			startTimer1 = CM_GetTick();
		}
		/*- 只要leftTimer1大于等于cycTime就宕机*/
		else if(leftTimer1 >= cycTime1)
		{
            (void)CFM_DarkLog_Multiple(DARK_LOG_HEX, TSCP_DARK_PFMCONTROFLOWCHECK_MONITOR2, 2u, \
            					sizeof(CM_UINT64),leftTimer1,
								sizeof(CM_UINT32),cycTime1);
			MSCP_LOG_MSG("prepare watchdog timeout,s_RunFlag_PrepareDog = 0x%.2x,leftTimer = %lu,leftTimer1 = %lu,cycTime1 = %lu\n",s_RunFlag_PrepareDog,leftTimer,leftTimer1,cycTime1);
#ifdef QNX
			(void)DFM_ledMonitor(2u);
#endif
			CM_RE_ASSERT(0, CONTROL_FLOW_CHECK_MODE, DARK_FAULT_CODE_FUN_ORDER_3, TSCP_DARK_ASSERT_3);
		}
		else
		{
			/*- 无处理 */
		}

		/*- 计算喂狗至当前的时间 */
		CM_Sleep(WATCH_DOG_POLL_TIME);
		nowTimer = CM_GetTick();
	    leftTimer = (nowTimer - startTimer) / 1000000u;
		leftTimer1 = (nowTimer - startTimer1) / 1000000u;

    }
}
/**
* @brief 主看门狗状态监督/备狗
*/
CM_THREAD_FUNC_T MSCP_WatchDog(void)
{
	/*- 变量初始化 */
	CM_UINT64 startTimer = 0u;
	CM_UINT64 nowTimer = 0u;
	CM_UINT64 leftTimer = 0u;
	CM_UINT32 cycTime = g_PFM_Cfg.CYC_TIME/2u;
	CM_UINT32 cycCount = g_PFM_Cfg.CYC_TIME/2u + 1u;
	CM_UINT8  loop_count = 0u;
#if QNX
	void* pmask = NULL;

	/*- 本线程锁定在 A53 core 1 */
	pmask = (void*)0x02u;
	(void)ThreadCtl(_NTO_TCTL_RUNMASK, pmask);
	
#endif

    /*- @alias 等待主任务启动 */
    while (0u == CM_ATOMIC_LOAD(&s_RunFlag))
    {
    	/*- 休眠等待进入主周期并且以喂狗 */
        CM_Sleep(2u);
    }
	
	/*- 备狗喂狗 */
	(void)PFM_CFC_WatchDog_Prepare();

	/*- @alias 等待主看门狗启动 */
    while (0u == CM_ATOMIC_LOAD(&s_RunFlag_MainDog))
    {
    	/*- 休眠等待主狗以喂狗 */
        CM_Sleep(2u);
		loop_count++;
		if(cycCount < loop_count )
		{
			/*- 如果轮询了周期时间/2次主狗还未启动  ，已经出现异常，宕机处理 */
			MSCP_LOG_MSG("main watchdog start timeout. \n");
#ifdef QNX
			(void)DFM_ledMonitor(2u);
#endif
			CM_RE_ASSERT(0, CONTROL_FLOW_CHECK_MODE, DARK_FAULT_CODE_FUN_ORDER_4, TSCP_DARK_ASSERT_1);

		}
		else
		{
			/*- 无处理 */
		}
    }
	
	/*- 循环等待软中断 */
	while (1)
	{

		/*- 备狗喂狗 */
    	(void)PFM_CFC_WatchDog_Prepare();
		/*- 主狗以喂狗，并且喂狗时间合法*/
		if(0x55u == CM_ATOMIC_LOAD(&s_RunFlag_MainDog))
		{
			/*- 清除运行标志 */
			(void)CM_ATOMIC_STORE(&s_RunFlag_MainDog, 0x00u);
			startTimer = CM_GetTick();
		}
		/*- 喂狗后，超过合法时间未进行喂狗操作*/
		else if(leftTimer >= cycTime)
		{
			/*- 宕机并输出平台所处步骤 */
            (void)CFM_DarkLog_Multiple(DARK_LOG_HEX, TSCP_DARK_PFMCONTROFLOWCHECK_WATCHDOG, 2, \
            					sizeof(CM_UINT64),leftTimer,
            					sizeof(CM_UINT32),cycTime);
			MSCP_LOG_MSG("main watchdog timeout,s_RunFlag_MainDog = 0x%.2x,leftTimer = %lu,cycTime = %lu\n",s_RunFlag_MainDog,leftTimer,cycTime);
#ifdef QNX
			(void)DFM_ledMonitor(2u);
#endif
			CM_RE_ASSERT(0, CONTROL_FLOW_CHECK_MODE, DARK_FAULT_CODE_FUN_ORDER_4, TSCP_DARK_ASSERT_2);
		}
		else
		{
			/*- 无处理 */
		}
			/*- 计算喂狗至当前的时间 */
		CM_Sleep(WATCH_DOG_POLL_TIME);
		nowTimer = CM_GetTick();

		leftTimer = (nowTimer - startTimer) / 1000000u;

	}
    return NULL;
}

/**
* @brief 喂狗
*/
void PFM_CFC_WatchDog(void)
{
	/*- 设置运行标记 */
	(void)CM_ATOMIC_STORE(&s_RunFlag, 0x55u); /* parasoft-suppress GJB8114-R_1_7_2 "QNX宏不符合编码规范" */
	MSCP_LOG_MSG("-- dog eat food ---\n");
}
/**
* @brief 主狗喂狗
*/
void PFM_CFC_WatchDog_Main(void)
{
	/*- 设置运行标记 */
	(void)CM_ATOMIC_STORE(&s_RunFlag_MainDog, 0x55u); /* parasoft-suppress GJB8114-R_1_7_2 "QNX宏不符合编码规范" */
}

/**
* @brief 备狗喂狗
*/
void PFM_CFC_WatchDog_Prepare(void)
{
	/*- 设置运行标记 */
	(void)CM_ATOMIC_STORE(&s_RunFlag_PrepareDog, 0x55u); /* parasoft-suppress GJB8114-R_1_7_2 "QNX宏不符合编码规范" */
}


