/**********************************************************************************************************
 * Copyright(C) 2017, Heilongjiang TYW Co.,Ltd.
 *
 * @Filename:       Popup_Scheduler.c
 *
 * @Description:    报警调度
 *
 * @Functions List:
 *
 *
 * @History:        Version:            V0.0.1
                                        Date:           2018?1?29? 16?13?
                    Author:             myliu
                    Modification: creat
 **********************************************************************************************************/

/**********************************************************************************************************
 * Include
*/
#include "Popup_Lib.h"
#include "Popup_List.h"
#include "Sound_List.h"
#include "Popup_Message_List.h"
#include "Popup_Scheduler.h"

#include "rh850_macros.h"

/**********************************************************************************************************
 * Constants and defines
 */
#define MAX_MEM_NUM             128u      /*512bytes*/

/**********************************************************************************************************
 * Local variables
 */
#pragma alignvar(8)
static CommMemBuf_t CommBuf[MAX_MEM_NUM];
static struct PopupList *FatalListHead = POPUPLIB_NULL;
static struct PopupList *HintListHead = POPUPLIB_NULL;
static struct PopupList *WarningListHead = POPUPLIB_NULL;
static struct PopupList *ExceptionListHead = POPUPLIB_NULL;
static PopupSchedulingStruct s_PopupScheduling;

/**********************************************************************************************************
 * Local functions
 */
static struct PopupList *Popup_Polling_Next ( struct PopupList *CurPopup );
static void Popup_Update_Status ( uint16_t PopupID );
static void Popup_Fatal_Request ( uint16_t PopupID );
static void Popup_Hint_Request ( uint16_t PopupID );
static void Popup_Warning_Request ( uint16_t PopupID );
static void Popup_Exception_Request ( uint16_t PopupID );
static struct PopupList *Popup_Select ( uint16_t PopupID );
static void Popup_Delete_Request ( uint16_t PopupID );
static void Popup_Cal_CutGrade ( uint16_t NewPopupID );
static void Popup_Cal_CutGrade_loc ( uint16_t NewPopupID );
static uint8_t Popup_Compare_Priority ( uint16_t PopupID1, uint16_t PopupID2 );
static uint16_t Popup_Get_Last_Masked_Warning ( void );
static uint16_t Popup_Get_Previous_Masked_Warning ( uint16_t PopupID );
static uint16_t Popup_Get_Next_Masked_Warning ( uint16_t PopupID );
static void Popup_IRQ_Enable(void);
static void Popup_IRQ_Disable(void);

/**********************************************************************************************************
 * Global variables
 */

/**********************************************************************************************************
 * Global functions
 */
/**********************************************************************************************************
 *@Function:        Popup_Scheduler_Init
 *@Description:     初始化弹出信息调度器
 *@Input:           MY_MemInit / CommMemInit
 *@Output:          none.
 *@Calls:           none.
 *@Return:          none.
 *@Note:            存在内存管理函数，要求必须在硬件初始化之后，应用开始之前，进行初始化操作，且只能上电初始化一次
 **********************************************************************************************************/
void Popup_Scheduler_Init ( void )
{
    PopupList_Opt_t PopupListOpt = {0};

    PopupListOpt.CommMemBuf = CommBuf;
    PopupListOpt.MaxCommMenSize = MAX_MEM_NUM;
    PopupListOpt.pPopupAttributeTable = PopupAttributeTable;
    PopupListOpt.IRQ_Disable = Popup_IRQ_Disable;
    PopupListOpt.IRQ_Enable = Popup_IRQ_Enable;
    PopupList_Init(&PopupListOpt);
    /*初始化参数值*/
    s_PopupScheduling.Timer        = 0u;
    s_PopupScheduling.NewPopupID   = POPUP_NONE;
    s_PopupScheduling.CurPopupID   = POPUP_NONE;
    s_PopupScheduling.PollPopupID  = POPUP_NONE;
    s_PopupScheduling.CurStatus    = POPUP_STATUS_OFF;
    s_PopupScheduling.CutGrade     = POPUP_CUT_NOT;
    s_PopupScheduling.SndReqCode   = 1u;
    s_PopupScheduling.CurMaskedID  = POPUP_NONE;
    s_PopupScheduling.WarningExist = FALSE;
}

/**********************************************************************************************************
 *@Function:        Popup_Scheduler_OFF
 *@Description:     PopupList_Destroy
 *@Input:           MY_MemInit / CommMemInit
 *@Output:          none.
 *@Calls:           none.
 *@Return:          none.
 *@Note:            进入休眠状态的时候，要关闭调度器，清掉所有触发的信息
 **********************************************************************************************************/
void Popup_Scheduler_OFF ( void )
{
    /*1、删除全部重要类信息*/
    FatalListHead = PopupList_Destroy ( FatalListHead );
    /*2、删除全部提示类信息*/
    HintListHead = PopupList_Destroy ( HintListHead );
    /*3、删除全部报警类信息*/
    WarningListHead = PopupList_Destroy ( WarningListHead );
    /*4、删除全部特殊类信息*/
    ExceptionListHead = PopupList_Destroy ( ExceptionListHead );

    /*5、初始化参数值*/
    s_PopupScheduling.Timer        = 0u;
    s_PopupScheduling.NewPopupID   = POPUP_NONE;
    s_PopupScheduling.CurPopupID   = POPUP_NONE;
    s_PopupScheduling.PollPopupID  = POPUP_NONE;
    s_PopupScheduling.CurStatus    = POPUP_STATUS_OFF;
    s_PopupScheduling.CutGrade     = POPUP_CUT_NOT;
    s_PopupScheduling.SndReqCode   = 1u;
    s_PopupScheduling.CurMaskedID  = POPUP_NONE;
    s_PopupScheduling.WarningExist = FALSE;
}

/**********************************************************************************************************
 *@Function:        Popup_Scheduling_Service
 *@Description:   调度弹出信息，更新当前显示的信息名称,接口函数
 *                        可能发生切换的条件：
 *                        1、当前显示信息被删除/屏蔽
 *                        2、产生优先级高的新消息
 *                        3、显示时间到
 *@Input:               CommMemInit
 *@Output:            none.
 *@Calls:             none
 *@Return:            none
 *@Note:              该服务函数必须每100ms被调用一次
 **********************************************************************************************************/
void Popup_Scheduling_Service ( void )
{
    struct PopupList *CurPopup = POPUPLIB_NULL;
    struct PopupList *NextPopup = POPUPLIB_NULL;
    uint8_t IsReturn = 0u;

    /*2、当前无显示，且无新信息，直接返回,否则，显示时间累加*/
    if ( s_PopupScheduling.CurPopupID >= POPUP_MAX )
    {
        if ( s_PopupScheduling.NewPopupID < POPUP_MAX )
        {
            s_PopupScheduling.CutGrade = POPUP_CUT_IMMEDIATELY;
        }
    }
    else
    {
        CurPopup = Popup_Select ( s_PopupScheduling.CurPopupID );
        if ( s_PopupScheduling.Timer < 0xFFu )
        {
            s_PopupScheduling.Timer++;
        }
    }

    /*3、根据切换等级，做相应的处理*/
    switch ( s_PopupScheduling.CutGrade )
    {
    case POPUP_CUT_IMMEDIATELY:
        break;

    case POPUP_CUT_WAIT_TIME_MIN:
#if POPUP_DEFINE_FIRST_DISPLAY
        if ( s_PopupScheduling.CurStatus == POPUP_STATUS_FIRST )
        {
            if ( s_PopupScheduling.Timer < POPUP_DISP_TIME_MIN )
            {
                IsReturn = 1u;
            }
        }
#else
        if ( s_PopupScheduling.Timer < POPUP_DISP_TIME_MIN )
        {
            IsReturn = 1u;
        }
#endif
        break;

    case POPUP_CUT_WAIT_TIME_NORMAL:
#if POPUP_DEFINE_FIRST_DISPLAY
        if ( s_PopupScheduling.CurStatus == POPUP_STATUS_FIRST )
        {
            if ( s_PopupScheduling.Timer < POPUP_DISP_TIME_NORMAL )
            {
                IsReturn = 1u;
            }
            s_PopupScheduling.NewPopupID = POPUP_NONE;
        }
#else
        if ( s_PopupScheduling.Timer < POPUP_DISP_TIME_MIN )
        {
            IsReturn = 1u;
        }
#endif
        break;

    case POPUP_CUT_NOT:
        //第四组报警0.8秒交互
        if (PopupAttributeTable[s_PopupScheduling.CurPopupID].Priority == 2)
        {
            if (s_PopupScheduling.Timer < POPUP_DISP_TIME_NORMAL_FLAG)
            {
                IsReturn = 1u;
            }
        }
        else
        {
            if ( s_PopupScheduling.Timer < POPUP_DISP_TIME_NORMAL )
            {
                IsReturn = 1u;
            }
        }
        break;

    default:
        break;
    }

    if (IsReturn == 0u)
    {
        /*4 清除切换等级*/
        s_PopupScheduling.CutGrade = POPUP_CUT_NOT;

        /*5 切换报警信息*/
        if ( s_PopupScheduling.NewPopupID < POPUP_MAX )
        {
            NextPopup = Popup_Select ( s_PopupScheduling.NewPopupID );
            if ( NextPopup == POPUPLIB_NULL )
            {
                NextPopup = Popup_Polling_Next ( CurPopup );
            }
            s_PopupScheduling.NewPopupID = POPUP_NONE;
            s_PopupScheduling.Timer = 0u;
        }
        else
        {
            NextPopup = Popup_Polling_Next ( CurPopup );
        }

        /*6、更新当前显示的信息*/
        if ( NextPopup != POPUPLIB_NULL )
        {
            if ( NextPopup->PopupID != s_PopupScheduling.CurPopupID )
            {
                if ( PopupAttributeTable[NextPopup->PopupID].Class != PopupAttributeTable[s_PopupScheduling.CurPopupID].Class )
                {
                    s_PopupScheduling.Timer = 0u;
                }
                s_PopupScheduling.CurStatus = NextPopup->Status;
                s_PopupScheduling.CurPopupID = NextPopup->PopupID;
            }
        }
        else
        {
            s_PopupScheduling.CurPopupID = POPUP_NONE;
            s_PopupScheduling.Timer = 0u;
        }

        /*7. 更新上一个报警的状态*/
        if ( CurPopup != POPUPLIB_NULL )
        {
            Popup_Update_Status ( CurPopup->PopupID );
        }
    }

    return;
}

/**********************************************************************************************************
 *@Function:        Popup_Request
 *@Description:     信息插入请求，包括全部类型，接口函数
 *@Input:           uint8_t PopupID -- 弹出信息的ID.
 *@Output:          none.
 *@Calls:           none.
 *@Return:          none.
 *@Note:            none.
 **********************************************************************************************************/
void Popup_Request ( uint16_t PopupID )
{
    switch ( PopupAttributeTable[PopupID].Type )
    {
    case POPUP_TYPE_FATAL:
        Popup_Fatal_Request ( PopupID );
        break;

    case POPUP_TYPE_HINT:
        Popup_Hint_Request ( PopupID );
        break;

    case POPUP_TYPE_WARNING:
        Popup_Warning_Request ( PopupID );
        break;

    case POPUP_TYPE_EXCEPTION:
        Popup_Exception_Request ( PopupID );
        break;

    default:
        break;
    }

    return;
}

/**********************************************************************************************************
 *@Function:    Popup_Mask_Request
 *@Description: 屏蔽第三类（报警类）信息，只有显示过的才可以被屏蔽
 *              ***********屏蔽全部显示过的***************
 *@Input:       none.
 *@Output:      none.
 *@Calls:       none.
 *@Return:      none.
 *@Note:        none.
 **********************************************************************************************************/
void Popup_Mask_Request ( void )
{
    struct PopupList *p1 = POPUPLIB_NULL;

    if ( PopupAttributeTable[s_PopupScheduling.CurPopupID].Type == POPUP_TYPE_WARNING )
    {
        s_PopupScheduling.CurMaskedID = s_PopupScheduling.CurPopupID;
        p1 = WarningListHead;
        while ( p1 != POPUPLIB_NULL )
        {
            if ( p1->Status == POPUP_STATUS_LOOP )
            {
                p1->Status = POPUP_STATUS_MASKED;
            }
            p1 = p1->next;
        }
    }

    return;
}

uint8_t Popup_WarningRequested ( void );
uint8_t Popup_WarningRequested ( void )
{
    struct PopupList *p1 = POPUPLIB_NULL;
    uint8_t retVal = 0u;

    p1 = FatalListHead;
    if ( p1 == POPUPLIB_NULL )
    {
        p1 = HintListHead;
        while ( p1 != POPUPLIB_NULL )
        {
            if ( ( p1->Status == POPUP_STATUS_NEW ) || ( p1->Status == POPUP_STATUS_FIRST ) || ( p1->Status == POPUP_STATUS_LOOP ) )
            {
                retVal = 1u;
            }
            p1 = p1->next;
        }

        p1 = WarningListHead;
        while ( p1 != POPUPLIB_NULL )
        {
            if ( ( p1->Status == POPUP_STATUS_NEW ) || ( p1->Status == POPUP_STATUS_FIRST ) || ( p1->Status == POPUP_STATUS_LOOP ) )
            {
                retVal = 1u;
            }
            p1 = p1->next;
        }
    }
    else
    {
        retVal = 1u;
    }

    return retVal;
}

/**********************************************************************************************************
 *@Function:    Popup_Del_Masked_Request
 *@Description: 在IGN_OFF时，需要将屏蔽过的报警信息显示出来，但不允许再次显示报警声音
 *@Input:       none.
 *@Output:      none.
 *@Calls:       none.
 *@Return:      none.
 *@Note:        目前将报警信息的状态切换为循环显示状态.
 **********************************************************************************************************/
void Popup_Del_Masked_Request ( void )
{
    struct PopupList *p1 = POPUPLIB_NULL;

    p1 = WarningListHead;
    while ( p1 != POPUPLIB_NULL )
    {
        if ( p1->Status == POPUP_STATUS_MASKED )
        {
            p1->Status = POPUP_STATUS_LOOP;
            Popup_Cal_CutGrade ( p1->PopupID );
        }
        p1 = p1->next;
    }
}

/**********************************************************************************************************
 *@Function:        Popup_Get_Status
 *@Description:   获取查询信息的状态
 *@Input:               uint8_t PopupID -- 弹出信息的ID
 *@Output:            none.
 *@Calls:             PopupList_Select
 *@Return:            POPUP_STATUS_OFF -- 此信息不在需要显示
 *                POPUP_STATUS_FIRST -- 此信息第一次显示
 *                POPUP_STATUS_LOOP -- 此信息处于轮询显示中
 *                POPUP_STATUS_MASKED -- 此信息被屏蔽
 *                POPUP_STATUS_DELETE -- 此信息等待被删除
 *                POPUP_STATUS_CLEAR -- 此信息马上被删除
 *@Note:              none.
 **********************************************************************************************************/
uint8_t Popup_Get_Status ( uint16_t PopupID )
{
    struct PopupList *p1 = POPUPLIB_NULL;
    uint8_t retStatus = POPUP_STATUS_OFF;

    switch ( PopupAttributeTable[PopupID].Type )
    {
    case POPUP_TYPE_FATAL:
        p1 = PopupList_Select ( FatalListHead, &PopupID );
        break;

    case POPUP_TYPE_HINT:
        p1 = PopupList_Select ( HintListHead, &PopupID );
        break;

    case POPUP_TYPE_WARNING:
        p1 = PopupList_Select ( WarningListHead, &PopupID );
        break;

    case POPUP_TYPE_EXCEPTION:
        p1 = PopupList_Select ( ExceptionListHead, &PopupID );
        break;

    default:
        break;
    }

    if ( p1 != POPUPLIB_NULL )
    {
        retStatus = p1->Status;
    }

    return retStatus;
}

/**********************************************************************************************************
 *@Function:        Popup_Get_Current_Message
 *@Description:   获取当前信息的文字编号
 *@Input:               none.
 *@Output:            none.
 *@Calls:             none.
 *@Return:            none.
 *@Note:              none.
 **********************************************************************************************************/
uint16_t Popup_Get_Current_Message ( void )
{
    uint16_t retPopupMsgID = POPUP_MSG_NONE;

    if ( s_PopupScheduling.CurPopupID < POPUP_MAX )
    {
        retPopupMsgID = PopupAttributeTable[s_PopupScheduling.CurPopupID].Message;
    }

    return retPopupMsgID;
}

/**********************************************************************************************************
 *@Function:        Popup_Get_Current_Sound
 *@Description:   获取当前信息的声音编号
 *@Input:               none.
 *@Output:            none.
 *@Calls:             none.
 *@Return:            none.
 *@Note:              声音请求码影响声音的持续性及是否打断
 **********************************************************************************************************/
uint8_t Popup_Get_Current_Sound ( void )
{
    uint8_t retSndID = SND_NONE;

    if ( s_PopupScheduling.CurPopupID < POPUP_MAX )
    {
        retSndID = PopupAttributeTable[s_PopupScheduling.CurPopupID].Snd;
    }

    return retSndID;
}

/**********************************************************************************************************
 *@Function:        Popup_Get_Current_Sound_Req_Code
 *@Description:   获取当前信息的声音请求码
 *@Input:               none.
 *@Output:            none.
 *@Calls:             none.
 *@Return:            none.
 *@Note:              声音请求码影响声音的持续性及是否打断
 **********************************************************************************************************/
uint8_t Popup_Get_Current_Sound_Req_Code ( void )
{
    struct PopupList *p1 = POPUPLIB_NULL;
    uint8_t retReqCode = 0u;

    if ( s_PopupScheduling.CurPopupID < POPUP_MAX )
    {
        if ( PopupAttributeTable[s_PopupScheduling.CurPopupID].SndMode == POPUP_MODE_MULTI )
        {
            if ( s_PopupScheduling.SndReqCode == 0xFFu )
            {
                s_PopupScheduling.SndReqCode = 0x01u;
            }
            else
            {
                s_PopupScheduling.SndReqCode++;
            }
            retReqCode = s_PopupScheduling.SndReqCode;
        }
        else
        {
            p1 = Popup_Select ( s_PopupScheduling.CurPopupID );
            if ( p1 != POPUPLIB_NULL )
            {
                if ( p1->Status == POPUP_STATUS_FIRST )
                {
                    retReqCode = s_PopupScheduling.SndReqCode;
                }
            }
        }
    }

    return retReqCode;
}

/**********************************************************************************************************
 *@Function:        Popup_Delete
 *@Description:   清除当前显示信息，如果是第一次显示，要等待Tmin（第二类信息（提示类）除外）
 *@Input:               uint8_t PopupID -- 弹出信息的ID
 *@Output:            FatalListHead/HintListHead/WarningListHead/ExceptionListHead -- 新链表
 *@Calls:             PopupList_Select / PopupList_Delete
 *@Return:            none.
 *@Note:              none.
 **********************************************************************************************************/
void Popup_Delete ( uint16_t PopupID )
{
    struct PopupList *p1 = POPUPLIB_NULL;

    p1 = Popup_Select ( PopupID );
    if ( p1 != POPUPLIB_NULL )
    {
        if ( p1->PopupID == s_PopupScheduling.CurPopupID )
        {
            p1->Status = POPUP_STATUS_DELETE;
            if ( PopupAttributeTable[PopupID].Type == POPUP_TYPE_HINT )
            {
                s_PopupScheduling.CutGrade = POPUP_CUT_IMMEDIATELY;
            }
            else
            {
                s_PopupScheduling.CutGrade = POPUP_CUT_WAIT_TIME_MIN;
            }
        }
        else
        {
            Popup_Delete_Request ( PopupID );
        }
    }

    return;
}

/**********************************************************************************************************
 *@Function:        Popup_Clear
 *@Description:   清除当前显示信息，不需要等待Tmin
 *@Input:               uint8_t PopupID -- 弹出信息的ID
 *@Output:            FatalListHead/HintListHead/WarningListHead/ExceptionListHead -- 新链表
 *@Calls:             PopupList_Select / PopupList_Delete
 *@Return:            none.
 *@Note:              none.
 **********************************************************************************************************/
void Popup_Clear ( uint16_t PopupID )
{
    struct PopupList *p1 = POPUPLIB_NULL;

    p1 = Popup_Select ( PopupID );
    if ( p1 != POPUPLIB_NULL )
    {
        if ( p1->PopupID == s_PopupScheduling.CurPopupID )
        {
            p1->Status = POPUP_STATUS_CLEAR;
            s_PopupScheduling.CutGrade = POPUP_CUT_IMMEDIATELY;
        }
        else
        {
            Popup_Delete_Request ( PopupID );
        }
    }

    return;
}

/**********************************************************************************************************
 *@Function:        Popup_Get_Warning_Status
 *@Description:   查询是否存在报警类信息/或者是获取显示致命信息下，是否存在报警类信息
 *@Input:               none
 *@Output:            none
 *@Calls:             none
 *@Return:            TRUE --存在报警类信息
 *                FALSE -- 不存在报警类信息
 *@Note:              none.
 **********************************************************************************************************/
uint8_t Popup_Get_Warning_Status ( void )
{
    uint8_t retVal = 0u;

    if ( WarningListHead != POPUPLIB_NULL )
    {
        retVal = 1u;
    }

    return retVal;
}

/**********************************************************************************************************
 *@Function:        Popup_Get_Masked_Warning_Msg
 *@Description:   获取当前显示的屏蔽信息的文字/图片编号
 *@Input:               none
 *@Output:            none
 *@Calls:             none
 *@Return:            文字/图片编号
 *@Note:              none.
 **********************************************************************************************************/
uint16_t Popup_Get_Masked_Warning_Msg ( void )
{
    uint16_t retMaskedID = POPUP_MSG_NONE;

    if ( s_PopupScheduling.CurMaskedID < POPUP_MAX )
    {
        retMaskedID = s_PopupScheduling.CurMaskedID;
    }
    return retMaskedID;
}

/**********************************************************************************************************
 *@Function:        Popup_Masked_Warning_Polling_Reset
 *@Description:   复位屏蔽信息当前显示的指针，向前，则指向最后一个；向后，则指向第一个
 *@Input:               none.
 *@Output:            none.
 *@Calls:             none.
 *@Return:            none.
 *@Note:              none.
 **********************************************************************************************************/
void Popup_Masked_Warning_Polling_Reset ( uint8_t Mode )
{
    uint16_t PopupID;

    if ( Mode == POPUP_POLLING_FORWARD )
    {
        PopupID = Popup_Get_First_Masked_Warning();
    }
    else
    {
        PopupID = Popup_Get_Last_Masked_Warning();
    }

    s_PopupScheduling.CurMaskedID = PopupID;

    return;
}

/**********************************************************************************************************
函数名：Popup_Masked_Warning_Polling
功  能：查询已屏蔽的报警

        每调用一次此函数,已屏蔽报警指针会按指定方向移动一位,当指针已移指定方向
        上的最后一个报警信息时,再次向同一方向查询,当前的已屏蔽报警信息会变为
        POPUP_NONE 表示报警查询已结束

        例如:当前有已屏蔽报警 A、B、C、D,调用Popup_Masked_Warning_Polling_Reset
        函数,设定方向为 POPUP_POLLING_FORWARD 时,已屏蔽报警指针会指向报警A,而后,
        调用该函数设定方向为 POPUP_POLLING_FORWARD 时已屏蔽报警指针即指向报警B.
        重复调用此函数且方向不变至已屏蔽报警指针指向报警D后,再以相同方向调用此函
        数则返回 POPUP_NONE,表示查询已结束

        通过调用Popup_Get_Masked_Warning可读出当前已屏蔽报警指针指向的报警

参  数：Mode：查询模式 POPUP_POLLING_FORWARD - 正向查找模式
                       POPUP_POLLING_REVERSE - 逆向查找模式
返回值：无
 **********************************************************************************************************/
void Popup_Masked_Warning_Polling ( uint8_t Mode )
{
    uint16_t PopupEnd;
    uint16_t PopupID;

    PopupID  = s_PopupScheduling.CurMaskedID;

    if ( PopupID != POPUP_NONE )
    {
        if ( Mode == POPUP_POLLING_FORWARD )
        {
            PopupEnd = Popup_Get_Last_Masked_Warning();
            if ( PopupEnd != POPUP_NONE )
            {
                if ( PopupID == PopupEnd )
                {
                    s_PopupScheduling.CurMaskedID = POPUP_NONE;
                }
                else
                {
                    s_PopupScheduling.CurMaskedID = Popup_Get_Next_Masked_Warning ( PopupID );
                }
            }
        }
        else
        {
            PopupEnd = Popup_Get_First_Masked_Warning();
            if ( PopupEnd != POPUP_NONE )
            {
                if ( PopupID == PopupEnd )
                {
                    s_PopupScheduling.CurMaskedID = POPUP_NONE;
                }
                else
                {
                    s_PopupScheduling.CurMaskedID = Popup_Get_Previous_Masked_Warning ( PopupID );
                }
            }
        }
    }

    return;
}

/**********************************************************************************************************
 *@Function:        Popup_Update_Status
 *@Description:   更新指定弹出信息的状态
 *@Input:               uint8_t PopupID -- 弹出信息的ID
 *@Output:            none
 *@Calls:             PopupList_Select / PopupList_Delete
 *@Return:            none.
 *@Note:              none.
 **********************************************************************************************************/
static void Popup_Update_Status ( uint16_t PopupID )
{
    struct PopupList *p1 = POPUPLIB_NULL;

    /*1、根据类型，查找ID位置*/
    p1 = Popup_Select ( PopupID );
    if ( p1 != POPUPLIB_NULL )
    {
        if ( ( p1->Status == POPUP_STATUS_CLEAR ) || ( p1->Status == POPUP_STATUS_DELETE ) )
        {
            ;
            Popup_Delete_Request ( p1->PopupID );
        }
        else if ( p1->Status != POPUP_STATUS_MASKED )
        {
            if ( PopupAttributeTable[p1->PopupID].DispMode == POPUP_MODE_SINGLE )
            {
                p1->Status = POPUP_STATUS_OFF;
            }
            else
            {
                p1->Status = POPUP_STATUS_LOOP;
            }
        }
        else
        {

        }
    }
}

/**********************************************************************************************************
 *@Function:        Popup_Polling_Next
 *@Description:   切换轮询的下个显示
 *@Input:               none
 *@Output:            none
 *@Calls:             PopupList_Select / PopupList_Malloc / PopupList_Insert / Popup_Cal_CutGrade
 *@Return:            none.
 *@Note:              none.
 **********************************************************************************************************/
uint8_t hyq_1, hyq_2;
static struct PopupList *Popup_Polling_Next ( struct PopupList *CurPopup )
{
    struct PopupList *NextPopup = POPUPLIB_NULL;

    if ( CurPopup != POPUPLIB_NULL )
    {
        NextPopup = CurPopup->next;

#if POPUP_DEFINE_FIRST_DISPLAY
        while ( NextPopup != POPUPLIB_NULL )
        {
            if ( NextPopup->Status == POPUP_STATUS_FIRST )
            {
                break;
            }
            NextPopup = NextPopup->next;
        }
#endif
        while ( NextPopup != POPUPLIB_NULL )
        {
            /*原始部分*/
            /*
            if ( ( NextPopup->Status == POPUP_STATUS_LOOP ) || ( NextPopup->Status == POPUP_STATUS_FIRST ) )
            {
                break;
            }
            NextPopup = NextPopup->next;
            */

            /*----test----*/
            if (PopupAttributeTable[NextPopup->PopupID].Type != POPUP_TYPE_WARNING)
            {
                if ((NextPopup->Status == POPUP_STATUS_LOOP) || (NextPopup->Status == POPUP_STATUS_FIRST))
                {
                    break;
                }
            }
            else
            {
                if (NextPopup->Status == POPUP_STATUS_FIRST)
                    break;

                else if (NextPopup->Status == POPUP_STATUS_LOOP)
                {
                    /*----test----*/
                    hyq_1 = CurPopup->PopupID ;
                    hyq_2 = NextPopup->PopupID;
                    /*----test----*/

                    if (PopupAttributeTable[NextPopup->PopupID].Priority == PopupAttributeTable[CurPopup->PopupID].Priority)
                        break;
                    else
                    {
                        NextPopup = WarningListHead;
                        return  NextPopup;
                    }
                }
            }
            NextPopup = NextPopup->next;
            /*----test----*/
        }
    }

    if ( NextPopup == POPUPLIB_NULL )
    {
        if ( FatalListHead != POPUPLIB_NULL )
        {
            NextPopup = FatalListHead;
            while ( NextPopup != POPUPLIB_NULL )
            {
                if ( ( NextPopup->Status == POPUP_STATUS_FIRST ) || ( NextPopup->Status == POPUP_STATUS_LOOP ) )
                {
                    break;
                }
                NextPopup = NextPopup->next;
            }
        }

        if ( ( HintListHead != POPUPLIB_NULL ) && ( NextPopup == POPUPLIB_NULL ) )
        {
            NextPopup = HintListHead;
            while ( NextPopup != POPUPLIB_NULL )
            {
                if ( ( NextPopup->Status == POPUP_STATUS_FIRST ) || ( NextPopup->Status == POPUP_STATUS_LOOP ) )
                {
                    break;
                }
                NextPopup = NextPopup->next;
            }
        }

        if ( ( WarningListHead != POPUPLIB_NULL ) && ( NextPopup == POPUPLIB_NULL ) )
        {
#if POPUP_CUT_RECOVER
            if ( s_PopupScheduling.PollPopupID < POPUP_MAX )
            {
                NextPopup = PopupList_Select ( WarningListHead, &s_PopupScheduling.PollPopupID );
                s_PopupScheduling.PollPopupID = POPUP_NONE;
            }
#endif
            if ( NextPopup == POPUPLIB_NULL )
            {
                NextPopup = WarningListHead;
                while ( NextPopup != POPUPLIB_NULL )
                {
                    if ( ( NextPopup->Status == POPUP_STATUS_FIRST ) || ( NextPopup->Status == POPUP_STATUS_LOOP ) )
                    {
                        break;
                    }
                    NextPopup = NextPopup->next;
                }
            }
        }

        if ( ( ExceptionListHead != POPUPLIB_NULL ) && ( NextPopup == POPUPLIB_NULL ) )
        {
            NextPopup = HintListHead;
            while ( NextPopup != POPUPLIB_NULL )
            {
                if ( ( NextPopup->Status == POPUP_STATUS_FIRST ) || ( NextPopup->Status == POPUP_STATUS_LOOP ) )
                {
                    break;
                }
                NextPopup = NextPopup->next;
            }
        }
    }

    return NextPopup;
}

/**********************************************************************************************************
 *@Function:        Popup_Fatal_Request
 *@Description:   插入第一类信息，并确定其切换等级
 *@Input:               uint8_t PopupID -- 弹出信息的ID
 *@Output:            ExceptionListHead -- 新链表
 *@Calls:             PopupList_Select / PopupList_Malloc / PopupList_Insert / Popup_Cal_CutGrade
 *@Return:            none.
 *@Note:              none.
 **********************************************************************************************************/
static void Popup_Fatal_Request ( uint16_t PopupID )
{
    struct PopupList *p1 = POPUPLIB_NULL;

    p1 = PopupList_Select ( FatalListHead, &PopupID );
    if ( p1 == POPUPLIB_NULL )
    {
        p1 = HintListHead;
        while ( p1 != POPUPLIB_NULL )
        {
            if ( p1->Status != POPUP_STATUS_OFF )
            {
                p1->Status = POPUP_STATUS_OFF;
            }
            p1 = p1->next;
        }

        p1 = PopupList_Malloc();
        if ( p1 != POPUPLIB_NULL )
        {
            p1->PopupID = PopupID;
            p1->Status = POPUP_STATUS_FIRST;
            p1->next = POPUPLIB_NULL;
            FatalListHead = PopupList_Insert ( FatalListHead, p1 );
            Popup_Cal_CutGrade ( PopupID );
        }
    }
    else
    {
        if ((p1->Status == POPUP_STATUS_DELETE) || (p1->Status == POPUP_STATUS_CLEAR))
        {
            p1->Status = POPUP_STATUS_FIRST;
        }
    }

    return;
}

/**********************************************************************************************************
 *@Function:        Popup_Hint_Request
 *@Description:   插入第二类信息，并确定其切换等级
 *@Input:               uint8_t PopupID -- 弹出信息的ID
 *@Output:            ExceptionListHead -- 新链表
 *@Calls:             PopupList_Select / PopupList_Malloc / PopupList_Insert / Popup_Cal_CutGrade
 *@Return:            none.
 *@Note:              none.
 **********************************************************************************************************/
static void Popup_Hint_Request ( uint16_t PopupID )
{
    struct PopupList *p1 = POPUPLIB_NULL;

    p1 = PopupList_Select ( HintListHead, &PopupID );
    if ( p1 == POPUPLIB_NULL )
    {
        p1 = PopupList_Malloc();
        if ( p1 != POPUPLIB_NULL )
        {
            p1->PopupID = PopupID;
            //if ( FatalListHead == POPUPLIB_NULL )
            //{
            p1->Status = POPUP_STATUS_FIRST;
            s_PopupScheduling.NewPopupID = PopupID;
#if POPUP_HINT_CUT_HINT_IMMEDIATELY
            s_PopupScheduling.CutGrade = POPUP_CUT_IMMEDIATELY;   /*提示信息可以打断提示类、报警类，所以如果不是特殊类信息，提示类都可以立即打断 */
#else
            s_PopupScheduling.CutGrade = POPUP_CUT_WAIT_TIME_MIN;   /*提示信息可以打断提示类、报警类，所以如果不是特殊类信息，提示类都可以立即打断 */
#endif
            s_PopupScheduling.PollPopupID = s_PopupScheduling.CurPopupID;

            //}
            //else
            //{
            //    p1->Status = POPUP_STATUS_OFF;
            //}
            p1->next = POPUPLIB_NULL;
            HintListHead = PopupList_Insert ( HintListHead, p1 );
        }
    }

    return;
}

/**********************************************************************************************************
 *@Function:        Popup_Warning_Request
 *@Description:   插入第三类信息，并确定其切换等级
 *@Input:               uint8_t PopupID -- 弹出信息的ID
 *@Output:            ExceptionListHead -- 新链表
 *@Calls:             PopupList_Select / PopupList_Malloc / PopupList_Insert / Popup_Cal_CutGrade
 *@Return:            none.
 *@Note:              none.
 **********************************************************************************************************/
static void Popup_Warning_Request ( uint16_t PopupID )
{
    struct PopupList *p1 = POPUPLIB_NULL;

    p1 = PopupList_Select ( WarningListHead, &PopupID );
    if ( p1 == POPUPLIB_NULL )
    {
        /*普通报警可以打断提示类信息，且未显示的提示类信息，不再进行显示，等同于特殊报警*/
#if POPUP_WARNING_CUT_HINT_MIN
        p1 = HintListHead;
        while ( p1 != POPUPLIB_NULL )
        {
            if ( p1->Status != POPUP_STATUS_OFF )
            {
                p1->Status = POPUP_STATUS_OFF;
            }
            p1 = p1->next;
        }
#endif

        p1 = PopupList_Malloc();
        if ( p1 != POPUPLIB_NULL )
        {
            p1->PopupID = PopupID;
            p1->Status = POPUP_STATUS_FIRST;
            p1->next = POPUPLIB_NULL;
            WarningListHead = PopupList_Insert ( WarningListHead, p1 );

            /*如果存在特殊报警，不更新新信息*/
            if ( FatalListHead == POPUPLIB_NULL )
            {
#if POPUP_WARNING_CUT_HINT_MIN
                /*如果存在提示类信息，等待最小时间后，打断*/
                if ( PopupAttributeTable[s_PopupScheduling.CurPopupID].Type == POPUP_TYPE_HINT )
                {
                    s_PopupScheduling.NewPopupID = PopupID;
                    s_PopupScheduling.CutGrade = POPUP_CUT_WAIT_TIME_MIN;
                }
                else
                {
                    Popup_Cal_CutGrade ( PopupID );
                }
#else
                Popup_Cal_CutGrade ( PopupID );
#endif
            }
        }
    }

    return;
}

/**********************************************************************************************************
 *@Function:        Popup_Exception_Request
 *@Description:   插入第四类信息，并确定其切换等级
 *@Input:               uint8_t PopupID -- 弹出信息的ID
 *@Output:            ExceptionListHead -- 新链表
 *@Calls:             PopupList_Select / PopupList_Malloc / PopupList_Insert / Popup_Cal_CutGrade
 *@Return:            none.
 *@Note:              none.
 **********************************************************************************************************/
static void Popup_Exception_Request ( uint16_t PopupID )
{
    struct PopupList *p1 = POPUPLIB_NULL;

    p1 = PopupList_Select ( ExceptionListHead, &PopupID );
    if ( p1 == POPUPLIB_NULL )
    {
        p1 = PopupList_Malloc();
        if ( p1 != POPUPLIB_NULL )
        {
            p1->PopupID = PopupID;
            p1->Status = POPUP_STATUS_FIRST;
            p1->next = POPUPLIB_NULL;
            ExceptionListHead = PopupList_Insert ( ExceptionListHead, p1 );

            if ( FatalListHead == POPUPLIB_NULL )
            {
                Popup_Cal_CutGrade ( PopupID );
            }
        }
    }

    return;
}


/**********************************************************************************************************
 *@Function:        Popup_Select
 *@Description:   在全部链表中查找指定ID的位置
 *@Input:               uint8_t PopupID -- 弹出信息的ID
 *@Output:            struct PopupList *head -- 链表头
 *                        struct PopupList *p -- 指定ID所在的位置
 *@Calls:             PopupList_Select
 *@Return:            struct PopupList *p -- 指定ID所在的位置
 *@Note:              none
 **********************************************************************************************************/
static struct PopupList *Popup_Select ( uint16_t PopupID )
{
    struct PopupList *head = POPUPLIB_NULL;
    struct PopupList *p1 = POPUPLIB_NULL;

    /*1、根据类型，获取链表头*/
    if ( PopupAttributeTable[PopupID].Type == POPUP_TYPE_FATAL )
    {
        head = FatalListHead;
    }
    else if ( PopupAttributeTable[PopupID].Type == POPUP_TYPE_HINT )
    {
        head = HintListHead;
    }
    else if ( PopupAttributeTable[PopupID].Type == POPUP_TYPE_WARNING )
    {
        head = WarningListHead;
    }
    else if ( PopupAttributeTable[PopupID].Type == POPUP_TYPE_EXCEPTION )
    {
        head = ExceptionListHead;
    }
    else
    {

    }

    /*2、查找ID位置*/
    if ( head != POPUPLIB_NULL )
    {
        p1 = PopupList_Select ( head, &PopupID );
    }

    return p1;
}

/**********************************************************************************************************
 *@Function:        Popup_Delete_Request
 *@Description:   在全部链表中查找指定ID的位置
 *@Input:               uint8_t PopupID -- 弹出信息的ID
 *@Output:            struct PopupList *head -- 链表头
 *                        struct PopupList *p -- 指定ID所在的位置
 *@Calls:             PopupList_Select
 *@Return:            struct PopupList *p -- 指定ID所在的位置
 *@Note:              none
 **********************************************************************************************************/
static void Popup_Delete_Request ( uint16_t PopupID )
{
    /*1、根据类型，获取链表头*/
    if ( PopupAttributeTable[PopupID].Type == POPUP_TYPE_FATAL )
    {
        FatalListHead = PopupList_Delete ( FatalListHead, PopupID );
    }
    else if ( PopupAttributeTable[PopupID].Type == POPUP_TYPE_HINT )
    {
        HintListHead = PopupList_Delete ( HintListHead, PopupID );
    }
    else if ( PopupAttributeTable[PopupID].Type == POPUP_TYPE_WARNING )
    {
        WarningListHead = PopupList_Delete ( WarningListHead, PopupID );
    }
    else if ( PopupAttributeTable[PopupID].Type == POPUP_TYPE_EXCEPTION )
    {
        ExceptionListHead = PopupList_Delete ( ExceptionListHead, PopupID );
    }
    else
    {

    }

    return;
}

/**********************************************************************************************************
 *@Function:    Popup_Cal_CutGrade
 *@Description: 计算新信息的切换等级
 *@Input:             uint8_t NewPopupID -- 新的弹出信息ID
 *@Output:          none.
 *@Calls:           Popup_Compare_Priority
 *@Return:          Grade0 -- 无切换请求
 *              Grade1 -- 低切换等级，当前显示信息，如果是第一次需要满足Tnormal(4s)；否则，立即打断.
 *              Grade2 -- 中切换等级，当前显示信息，如果是第一次需要满足Tmin(2s)；否则，立即打断.
 *              Grade3 -- 高切换等级，立即打断当前显示信息.
 *@Note:            none
 **********************************************************************************************************/
static void Popup_Cal_CutGrade ( uint16_t NewPopupID )
{
    uint8_t ret = 0u;

    /*已经存在切换等级，如果新信息的优先级低于未显示的信息，则切换等级维持不变*/
    if ( s_PopupScheduling.NewPopupID < POPUP_MAX )
    {
        ret = Popup_Compare_Priority ( NewPopupID, s_PopupScheduling.NewPopupID );
        if ( ret != 0u )
        {
            s_PopupScheduling.NewPopupID = NewPopupID;
            Popup_Cal_CutGrade_loc(NewPopupID);
        }
    }
    else
    {
        s_PopupScheduling.NewPopupID = NewPopupID;
        Popup_Cal_CutGrade_loc(NewPopupID);
    }

    return;
}

static void Popup_Cal_CutGrade_loc ( uint16_t NewPopupID )
{
    uint8_t ret = 0u;

    if ( s_PopupScheduling.CurPopupID < POPUP_MAX )
    {
        ret = Popup_Compare_Priority ( NewPopupID, s_PopupScheduling.CurPopupID );
        switch ( ret )
        {
        case 0u: /*新信息优先级低于/等于当前显示类型*/
            if ( PopupAttributeTable[NewPopupID].Class == PopupAttributeTable[s_PopupScheduling.CurPopupID].Class )
            {
#if POPUP_SAME_CLASS_CUT_IMMEDIATELY
                s_PopupScheduling.CutGrade = POPUP_CUT_IMMEDIATELY;       /*类型相同，立即打断当前*/
                Popup_Update_Status ( s_PopupScheduling.CurPopupID );
                s_PopupScheduling.SndReqCode++;
#endif
            }
            else
            {
                s_PopupScheduling.CutGrade = POPUP_CUT_WAIT_TIME_NORMAL;
            }
            break;

#if POPUP_HIGH_CUT_LOW_TYPE_IMMEDIATELY
        case 1u: /*新信息的类型高*/
            s_PopupScheduling.CutGrade = POPUP_CUT_IMMEDIATELY;
            s_PopupScheduling.PollPopupID = s_PopupScheduling.CurPopupID;
            break;
#endif

        case 2u:
        case 3u: /*新信息的优先级高（包括主、副优先级）*/
            s_PopupScheduling.CutGrade = POPUP_CUT_WAIT_TIME_MIN;
            break;

        default:
            s_PopupScheduling.CutGrade = POPUP_CUT_NOT;
            break;
        }
    }
    else
    {
        s_PopupScheduling.CutGrade = POPUP_CUT_IMMEDIATELY;
    }
}

/**********************************************************************************************************
 *@Function:        Popup_Compare_Priority
 *@Description:   比较两种弹出消息的优先级
 *@Input:               uint8_t PopupID1 -- 弹出信息1
 *                uint8_t PopupID2 -- 弹出信息2
 *@Output:          none.
 *@Calls:             PopupAttributeTable -- 属性表
 *@Return:            0 -- 两种优先级相同或PopupID1的优先级低
 *                1 -- PopupID1的类型高于PopupID2的类型
 *                2 -- PopupID1的主优先级高于PopupID2的主优先级
 *                3 -- PopupID1的副优先级高于PopupID2的副优先级
 *@Note:              none
 **********************************************************************************************************/
static uint8_t Popup_Compare_Priority ( uint16_t PopupID1, uint16_t PopupID2 )
{
    uint8_t ret = 0u;

    /*比较类型*/
    if ( PopupAttributeTable[PopupID1].Type < PopupAttributeTable[PopupID2].Type )
    {
        ret = 1u;
    }
    else if ( PopupAttributeTable[PopupID1].Type == PopupAttributeTable[PopupID2].Type )
    {
        /*类型相同，比较主优先级*/
        if ( PopupAttributeTable[PopupID1].Priority < PopupAttributeTable[PopupID2].Priority )
        {
            ret = 2u;
        }
        else if ( PopupAttributeTable[PopupID1].Priority == PopupAttributeTable[PopupID2].Priority )
        {
            /*类型相同，主优先级相同，比较副优先级*/
            if ( PopupAttributeTable[PopupID1].SubPriority < PopupAttributeTable[PopupID2].SubPriority )
            {
                ret = 3u;
            }
        }
        else
        {
        }
    }
    else
    {
    }

    return ret;
}


/**********************************************************************************************************
 *@Function:        Popup_Get_First_Masked_Warning
 *@Description:   获取第一个屏蔽信息
 *@Input:               none.
 *@Output:          none.
 *@Calls:             none.
 *@Return:            第一个屏蔽信息的ID，如果没有屏蔽信息，返回POPUP_NONE
 *@Note:              none
 **********************************************************************************************************/

uint16_t Popup_Get_First_Masked_Warning ( void )
{
    struct PopupList *p = POPUPLIB_NULL;
    uint16_t retVal = POPUP_NONE;

    p = WarningListHead;
    while ( p != POPUPLIB_NULL )
    {
        if ( p->Status == POPUP_STATUS_MASKED )
        {
            break;
        }
        p = p->next;
    }

    if ( p != POPUPLIB_NULL )
    {
        retVal = p->PopupID;
    }

    return retVal;
}

/**********************************************************************************************************
 *@Function:        Popup_Get_Last_Masked_Warning
 *@Description:   获取最后一个屏蔽信息
 *@Input:               none.
 *@Output:          none.
 *@Calls:             none.
 *@Return:            最后一个屏蔽信息的ID，如果没有屏蔽信息，返回POPUP_NONE
 *@Note:              none
 **********************************************************************************************************/
static uint16_t Popup_Get_Last_Masked_Warning ( void )
{
    struct PopupList *p1 = POPUPLIB_NULL;
    struct PopupList *p2 = POPUPLIB_NULL;
    uint16_t retVal = POPUP_NONE;

    p1 = WarningListHead;
    while ( p1 != POPUPLIB_NULL )
    {
        if ( p1->Status == POPUP_STATUS_MASKED )
        {
            p2 = p1;
        }
        p1 = p1->next;
    }

    if ( p2 != POPUPLIB_NULL )
    {
        retVal = p2->PopupID;
    }

    return retVal;
}

/**********************************************************************************************************
 *@Function:        Popup_Get_Previous_Masked_Warning
 *@Description:   获取当前显示的屏蔽信息的上一个屏蔽信息
 *@Input:               none.
 *@Output:          none.
 *@Calls:             none.
 *@Return:            上一个屏蔽信息的ID，如果上一个信息为空，返回POPUP_NONE
 *@Note:              none
 **********************************************************************************************************/
static uint16_t Popup_Get_Previous_Masked_Warning ( uint16_t PopupID )
{
    struct PopupList *p1 = POPUPLIB_NULL;
    struct PopupList *p2 = POPUPLIB_NULL;
    uint16_t retVal = POPUP_NONE;

    p1 = WarningListHead;
    while ( p1 != POPUPLIB_NULL )
    {
        if ( p1->PopupID == PopupID )
        {
            break;
        }

        if ( ( p1->Status == POPUP_STATUS_MASKED ) && ( PopupAttributeTable[PopupID].Class != PopupAttributeTable[p1->PopupID].Class ) )
        {
            p2 = p1;
        }
        p1 = p1->next;
    }

    if ( p2 != POPUPLIB_NULL )
    {
        retVal = p2->PopupID;
    }

    return retVal;
}

/**********************************************************************************************************
 *@Function:        Popup_Get_Previous_Masked_Warning
 *@Description:   获取当前显示的屏蔽信息的下一个屏蔽信息
 *@Input:               none.
 *@Output:          none.
 *@Calls:             none.
 *@Return:            下一个屏蔽信息的ID，如果下一个信息为空，返回POPUP_NONE
 *@Note:              none
 **********************************************************************************************************/
static uint16_t Popup_Get_Next_Masked_Warning ( uint16_t PopupID )
{
    struct PopupList *p1 = POPUPLIB_NULL;
    uint16_t retVal = POPUP_NONE;

    p1 = PopupList_Select ( WarningListHead, &PopupID );
    p1 = p1->next;

    while ( p1 != POPUPLIB_NULL )
    {
        if ( ( p1->Status == POPUP_STATUS_MASKED ) && ( PopupAttributeTable[PopupID].Class != PopupAttributeTable[p1->PopupID].Class ) )
        {
            break;
        }
        p1 = p1->next;
    }

    if ( p1 != POPUPLIB_NULL )
    {
        retVal = p1->PopupID;
    }

    return retVal;
}

static void Popup_IRQ_Enable(void)
{
    //IRQ_ENABLE();
    enable_interrupt();
}

static void Popup_IRQ_Disable(void)
{
    //IRQ_DISABLE();
    disable_interrupt();
}

/******************************************************************************
函数名：Popup_Mask_Request
功  能：请求屏蔽报警信息
        可以在任意时刻发出屏蔽请求,只有满足屏蔽条件时,报警信息才会被屏蔽
参  数：无
返回值：无
******************************************************************************/
void Popup_Hint_Mask_Request ( uint16_t PopupID )
{

}

/**********************************************************************************************************
 **********************************************************************************************************/
