#include "common_include.h"

volatile    uint8_t           OWN_ID;
volatile    _NMPDU            NM_RPDU;              //receive PDU buf
volatile    _SleepStatus      NM_Sleep;
volatile    _MarkerStatus     NM_Marker;
volatile    uint16_t          NM_rx_err_count;
volatile    uint16_t          NM_tx_err_count;

volatile    uint8_t           NM_RX_STATE;
volatile    uint8_t           NMReset_main_Cnt;  

volatile    uint8_t           NM_IGN_State;
volatile    uint8_t           NM_TX_STATE;
volatile    uint8_t           NM_ER_STATE;
volatile    uint16_t          NM_CAN_CHL_ID; //NM_CAN_ID_BASE + OWN_NODE_CHL0              
volatile    uint16_t          NM_LAST_ID;
volatile    uint8_t           NM_LimpHomeTxCnt;

volatile    uint8_t           NM_Task_Lock;
    
volatile    _NM_TimeInfo      NM_Time;

volatile    uint8_t           NM_LastSend_PDU_Type; //Last PDU Type
volatile    uint8_t           NM_Destination;
volatile    uint16_t          NM_ID_BUF;

volatile    _NMPDU            NM_SPDU;              //send PDU buf
volatile    _NMPDU            NM_PDU_BUF;
volatile    _NetWorkStatus    NetWorkStatus;
volatile    uint16_t          NM_tSleepRequestMin;

//
void    NM_State_Transformation(void (* pProgram)(void));
void    (* NM_Main_Program)(void);
void    (* NMInitReset_Program)(void);
void    (* NMReset_Program)(void);

//
void    NM_ReceiveMsg_handle(void);
uint8_t  NM_CheckSkip(uint8_t Source,uint8_t Destination,uint8_t Own_Node);
void    NM_ConfigLogicalFun(uint8_t SourceID);
    
void     CancelAlarm(uint16_t timetype); //Cansel Ttyp /Tmax /Terror count
void     NM_SetTmaxTimer(uint16_t Second,_NM_TimeInfo *T);
uint8_t   NM_TmaxTimerOver(_NM_TimeInfo *T);

void     NM_SetTwbsTimer(uint16_t Second,_NM_TimeInfo *T);
uint8_t   NM_TwbsTimerOver(_NM_TimeInfo *T);

void     NM_SetSleepTimer(uint16_t Second,_NM_TimeInfo *T);
uint8_t   NM_SleepTimerOver(_NM_TimeInfo *T);

void    NM_SetTSendTimer(uint16_t Second,_NM_TimeInfo *T);
uint8_t  NM_TSendTimerOver(_NM_TimeInfo *T);
    
void    GotoMode(uint8_t Mode);

void    NM_Do_Nothing(void);

void    Transmit_NMPDU(uint8_t PDU_Type);

void    NMInitReset(void);
void    NMReset(void);

void    NMReset_main(void);
void    NMReset_tx(void);
void    NMReset_rx(void);
void    NMReset_error(void);

void    NMNormal_main(void);
void    NMNormal_rx(void);
void    NMNormal_tx(void);
void    NMNormal_error(void);

void    NMNormalPrepSleep_main(void);
void    NMNormalPrep_rx(void);
void    NMNormalPrep_tx(void);
void    NMNormalPrep_error(void);

void    NMLimpHome_rx(void);
void    NMLimpHome_tx(void);
void    NMLimpHome_error(void);

void    NMLimpHomePrepSleep_main(void);
void    NMLimpHomePrepSleep_rx(void);

void    NMTwbsNormal_main(void);
void    NMTwbsNormal_rx(void);

void    NMTwbsLimpHome_main(void);
void    NMTwbsLimpHome_rx(void);

void    NM_CheckBusOff_Fun(void);
    
/*-------------------------------------------------------------------------
* Function Name  : NM_Do_Nothing
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NM_Do_Nothing (void){;}

/*-------------------------------------------------------------------------
* Function Name  : NM_CheckBusOff_Fun
* Description    : handle Busoff interface function
* Input          : after busoff into limphome state
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NM_CheckBusOff_Fun(void)
{
    if(NetWorkStatus.BusOffEvent)
    {
        NetWorkStatus.BusOffEvent = 0;
        NM_D_Offline();

        if((NM_Main_Program != NMLimpHome_main) && (NM_Main_Program != NMLimpHomePrepSleep_main) && (NM_Main_Program != NMTwbsLimpHome_main) && (NM_Main_Program != NMBusSleep_main))
        {
             CancelAlarm(NM_Time_Max);
             CancelAlarm(NM_Time_Type);
             Transmit_NMPDU(LIMP_HOME_MSG);
             NM_SetTSendTimer(NM_Time_Error,(_NM_TimeInfo*)&NM_Time);
             NM_State_Transformation(NMLimpHome_main);
        }
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NM_Do_Nothing
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NM_State_Transformation(void (* pProgram)(void))
{
    NM_TX_STATE = 0;
    NM_RX_STATE = 0;
    NM_ER_STATE = 0;
    NM_Main_Program = pProgram;
    
    NMReset_main_Cnt = 0;
}

/*-------------------------------------------------------------------------
* Function Name  : NM_ReceiveMsg_handle
* Description    : handle receive PDU;
* Input          :
* Output         : None
* Return         : None
* onther         : propose : in interrupt function
--------------------------------------------------------------------------*/
void NM_ReceiveMsg_handle(void)
{
    if(NM_PDU_BUF.bits.ProceF == NM_BUF_FULL)
    {
        NM_RPDU.msg[ 0 ] = NM_PDU_BUF.msg[ 0 ];
        NM_RPDU.msg[ 1 ] = NM_PDU_BUF.msg[ 1 ];
        NM_RPDU.msg[ 2 ] = NM_PDU_BUF.msg[ 2 ];
        NM_RPDU.msg[ 3 ] = NM_PDU_BUF.msg[ 3 ];
        NM_RPDU.msg[ 4 ] = NM_PDU_BUF.msg[ 4 ];
        NM_RPDU.msg[ 5 ] = NM_PDU_BUF.msg[ 5 ];
        NM_RPDU.msg[ 6 ] = NM_PDU_BUF.msg[ 6 ];
        NM_RPDU.msg[ 7 ] = NM_PDU_BUF.msg[ 7 ];
        
        NM_RPDU.bits.SourceID = NM_ID_BUF;
        
        NM_RX_STATE = 1;
        
        NM_PDU_BUF.bits.ProceF = NM_BUF_EMPTY;
    }
}

/*-------------------------------------------------------------------------
* Function Name  : OsekNM_Manage_Main
* Description    : OSEK Main function
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void OsekNM_Manage_Main(uint16_t BatVolt)
{
    if(CurIgnSt == EVENT_IGN_ON)
    {
        NM_IGN_State = NM_IGN_ON; 
    }
    if(CurIgnSt == EVENT_IGN_OFF)
    {
        NM_IGN_State = NM_IGN_OFF; 
    }
    
    if(NM_Task_Lock == NM_LOCK)
    {
        return;
    }
    
    if(NM_Volt_Abnormal == 1)
    {
        return;
    }
    
    if(NM_Volt_Abnormal == 2)
    {
        NM_Volt_Abnormal = 0;
        
        NMInitReset();
        NM_State_Transformation(NMReset_main);
        NM_Destination = OWN_ID;
        NM_rx_err_count++;
        Transmit_NMPDU(ALIVE_MSG);
    }
    
    NM_ReceiveMsg_handle();
    
    NM_CheckSleep_Fun(  );
    
    if(NM_IGN_State == NM_IGN_ON)
    {
        if(NetWorkStatus.bussleep == NM_NO)
        {
            if(NM_Main_Program != NMBusSleep_main)
            {
                GotoMode(NMAwake);
            }
            TalkNM( );
        }
    }
    
    if(NM_Main_Program == NULL)
    {
        NM_Main_Program = NMReset_main;
    }
    
    NM_CheckBusOff_Fun(  );
    
    if(NetWorkStatus.NMactive == NM_Active)
    {
        (*NM_Main_Program)();
    }
    
    if(NetWorkStatus.APPactive == NM_Active)
    {
        NM_Send_App_Fun(  );
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NMInitReset
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NMInitReset(void)
{
    NM_rx_err_count = 0;
    NM_tx_err_count = 0;
    
    NM_D_Online();
}

/*-------------------------------------------------------------------------
* Function Name  : NMReset
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NMReset(void)
{
    NMReset_main_Cnt = 0;
    
    NM_Destination = OWN_ID;
    NM_rx_err_count++;
    NM_RPDU.bits.Opcode = 0;
    NM_TX_STATE = 0;
    NM_RX_STATE = 0;
    NM_ER_STATE = 0;
    
    NM_tSleepRequestMin = 0;
    NM_LimpHomeTxCnt = 0;
    
    NetWorkStatus.bussleep = NM_NO;
    
    Transmit_NMPDU(ALIVE_MSG);
}

/*-------------------------------------------------------------------------
* Function Name  : NMReset_main
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NMReset_main (void)
{
    if(NMInitReset_Program == NMInitReset)
    {
        (*NMInitReset_Program)();
        NMInitReset_Program = NM_Do_Nothing;
    }
    
    if(NMReset_Program == NMReset)
    {
        (*NMReset_Program)();
        NMReset_Program = NM_Do_Nothing;
    }
    
    NMReset_main_Cnt ++;
    
    if(NMReset_main_Cnt > 100)
    {
        NMReset_error();
        return;
    }
    if(NM_ER_STATE){NMReset_error();return;}
    if(NM_TX_STATE){NMReset_tx();}
}

void NMReset_error (void)
{
    NM_ER_STATE = 0;
    
    NM_tx_err_count++;
    if((NM_tx_err_count > NM_TX_LIMIT) || (NM_rx_err_count > NM_RX_LIMIT))
    {
        NM_SetTSendTimer(NM_Time_Error,(_NM_TimeInfo *)&NM_Time);
        NM_Marker.limphome = NM_NO;
        NM_State_Transformation(NMLimpHome_main);
    }
    else
    {
        NM_SetTSendTimer(NM_Time_Type,(_NM_TimeInfo *)&NM_Time);
        NM_State_Transformation(NMNormal_main);
    }
}

void NMReset_tx(void)
{
    NM_TX_STATE = 0;
    
    if((NM_tx_err_count <= NM_TX_LIMIT) && (NM_rx_err_count <= NM_RX_LIMIT))
    {
        NM_SetTSendTimer(NM_Time_Type,(_NM_TimeInfo*)&NM_Time);
        NM_State_Transformation(NMNormal_main);
    }
    else
    {
        NM_SetTSendTimer(NM_Time_Error,(_NM_TimeInfo*)&NM_Time);
        NM_Marker.limphome = NM_NO;
        NM_State_Transformation(NMLimpHome_main);
    }
}

void NMReset_rx(void)
{
    uint8_t OpCode = 0;
    uint8_t Source = 0;
    
    NM_RX_STATE = 0;
    
    NM_rx_err_count = 0;
    
    if(NM_LastSend_PDU_Type == ALIVE_MSG)
    {
        Source = NM_RPDU.bits.SourceID;
        
        OpCode = NM_RPDU.msg[ 1 ];
        
        if(OpCode & 0x03)
        {
            NM_ConfigLogicalFun(Source);
        }
    }
}

/*-------------------------------------------------------------------------
* Function Name  : StartNM
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void StartNM(void)
{
    OWN_ID = OWN_NODE;

    NM_CAN_CHL_ID = NM_CAN_ID_BASE + OWN_NODE;
    
    NM_Marker.stable = NM_Unstabitily;
    
    NM_Sleep.ind = NM_NO;
    NM_Sleep.ack = NM_NO;
    
    NM_D_Init(BusInit);
    
    NM_Task_Lock = NM_UNLOCK;
    
    TalkNM( );
    TalkAPP( );
      
    NMInitReset();
    NMReset();
    
    NM_Main_Program = NMReset_main;
    NMInitReset_Program = NM_Do_Nothing;
    NMReset_Program = NM_Do_Nothing;
    
    NetWorkStatus.BusOffEvent = 0;
    NetWorkStatus.WakeupEvent = NM_WaitWakeup;
    
    NM_tSleepRequestMin = 0;
}

/*-------------------------------------------------------------------------
* Function Name  : NMNormal_main
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NMNormal_main (void)
{
    if(NM_ER_STATE)
    {
        NMNormal_error();
        return;
    }
    else if(NM_TX_STATE)
    {
        NMNormal_tx();
        if(NM_Main_Program == NMNormalPrepSleep_main)
        {
            return;
        }
    }
    
    if(NM_RX_STATE)
    {
        NMNormal_rx();
        if(NM_Main_Program == NMTwbsNormal_main)
        {
            return;
        }
    }
    
    if(NM_TSendTimerOver((_NM_TimeInfo*)&NM_Time))
    {
        if(NetWorkStatus.NMactive == NM_Active)
        {
            NM_SetTmaxTimer(NM_Time_Max,(_NM_TimeInfo *)&NM_Time);

            Transmit_NMPDU(RING_MSG);
        }
    }
    
    if(NM_TmaxTimerOver((_NM_TimeInfo*)&NM_Time))
    {
        NMReset();
        NM_Main_Program = NMReset_main;
    }
}

void NMNormal_rx (void)
{
    uint8_t OpCode = 0;
    uint8_t Source = 0;
    uint8_t Destination = 0;
    uint8_t Own_Node_Staute = 0;
    uint8_t OwnID = 0;
    
    NM_RX_STATE = 0;
    NM_rx_err_count = 0;
    
    Source = NM_RPDU.bits.SourceID;
    Destination = NM_RPDU.bits.DestID;
    
    OpCode = NM_RPDU.msg[ 1 ];
    
    if(OpCode & 0x03)
    {
        NM_ConfigLogicalFun(Source);
    }
    
    if(OpCode & 0x10)
    {
        NM_Sleep.ind = NM_YES;
    }
    else
    {
        NM_Sleep.ind = NM_NO;
    }
    
    if(OpCode & 0x02)//RING
    {
        CancelAlarm(NM_Time_Type);
        CancelAlarm(NM_Time_Max);
        
        if(OpCode & 0x20)//NM_Sleep_ACK=1
        {
            if(NetWorkStatus.bussleep)
            {
                NM_SetTwbsTimer(NM_Time_WaitBusSleep,(_NM_TimeInfo *)&NM_Time);
                NM_State_Transformation(NMTwbsNormal_main);
                return;
            }
        }

        OwnID = OWN_ID;
        
        if((Destination == OwnID) || (Source == Destination))
        {
            NM_SetTSendTimer(NM_Time_Type,(_NM_TimeInfo *)&NM_Time);
        }
        else
        {
            NM_SetTmaxTimer(NM_Time_Max,(_NM_TimeInfo *)&NM_Time);
            
            Own_Node_Staute = NM_CheckSkip(Source,Destination,OwnID);
            
            if(Own_Node_Staute == NM_NODE_SKIP)
            {
                NM_Destination = OwnID;
                
                Transmit_NMPDU(ALIVE_MSG);
            }
        }
    }
    else if(OpCode & 0x01)
    {
        NM_Marker.stable = NM_Unstabitily;
    }
}

void NMNormal_tx (void)
{
    NM_TX_STATE = 0;
    
    NM_tx_err_count = 0;
    
    if((NM_Sleep.ind) && (NetWorkStatus.bussleep) && (NM_LastSend_PDU_Type == RING_MSG))
    {
        NM_SetTmaxTimer(NM_Time_Max,(_NM_TimeInfo *)&NM_Time);
        CancelAlarm(NM_Time_Type);
        NM_Sleep.ack = NM_YES;
        NM_State_Transformation(NMNormalPrepSleep_main);
    }
}

void NMNormal_error(void)
{
    NM_ER_STATE = 0;
    
    NM_tx_err_count++;
    
    if(NM_tx_err_count > NM_TX_LIMIT)
    {
        NM_SetTSendTimer(NM_Time_Error,(_NM_TimeInfo*)&NM_Time);
        
        NM_Marker.limphome = NM_NO;
        NM_State_Transformation(NMLimpHome_main);
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NMNormalPrepSleep_main
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NMNormalPrepSleep_main(void)
{
    if(NM_ER_STATE)
    {
        NMNormalPrep_error();
        return;
    }
    else if(NM_TX_STATE)
    {
        NMNormalPrep_tx();
        return;
    }
    
    if(NM_RX_STATE)
    {
        NMNormalPrep_rx();
        if((NM_Main_Program == NMTwbsNormal_main) || (NM_Main_Program == NMNormal_main))
        {
            return;
        }
    }
    
    if(NM_TmaxTimerOver((_NM_TimeInfo*)&NM_Time)) //
    {
        CancelAlarm(NM_Time_Error);
        CancelAlarm(NM_Time_Type);
        NM_Marker.limphome = NM_NO;
        NMReset();
        NM_Main_Program = NMReset_main;
        return;
    }
    
    if(NM_TSendTimerOver((_NM_TimeInfo*)&NM_Time))
    {
        if(NetWorkStatus.NMactive == NM_Active)
        {
            Transmit_NMPDU(RING_MSG);
        }
    }
}

void NMNormalPrep_rx(void)
{
    uint8_t  OpCode = 0;
    uint8_t  Source = 0;
    uint8_t  Destination = 0;
    uint8_t  Own_Node_Staute = 0;
    uint8_t  OwnID = 0;
    
    NM_RX_STATE = 0;
    NM_rx_err_count = 0;
    
    Source = NM_RPDU.bits.SourceID;
    Destination = NM_RPDU.bits.DestID;
    
    OpCode = NM_RPDU.msg[ 1 ];
    
    if(OpCode & 0x03)
    {
        NM_ConfigLogicalFun(Source);
    }
    
    if(OpCode & 0x10)
    {
        NM_Sleep.ind = NM_YES;
    }
    else
    {
        NM_Sleep.ind = NM_NO;
    }
    
    OwnID = OWN_ID;
    
    if(NM_Sleep.ind == NM_NO)
    {
        CancelAlarm(NM_Time_Max);
        CancelAlarm(NM_Time_Type);
        if((Destination == OwnID) || (Source == Destination))
        {
            NM_SetTSendTimer(NM_Time_Type,(_NM_TimeInfo *)&NM_Time);
        }
        else
        {
            NM_SetTmaxTimer(NM_Time_Max,(_NM_TimeInfo *)&NM_Time);
            Own_Node_Staute = NM_CheckSkip(Source,Destination,OwnID);
            
            if(Own_Node_Staute == NM_NODE_SKIP)
            {
                NM_Destination = OwnID;
                Transmit_NMPDU(ALIVE_MSG);
            }
        }
        NM_Main_Program = NMNormal_main;
    }
    else
    {
        if(OpCode & 0x02)//RING
        {
            if((OpCode & 0x20) == 0x20) //NM_Sleep_ACK=1
            {
                CancelAlarm(NM_Time_Type);
                CancelAlarm(NM_Time_Max);
                NM_SetTwbsTimer(NM_Time_WaitBusSleep,(_NM_TimeInfo *)&NM_Time);
                NM_State_Transformation(NMTwbsNormal_main);
                return;
            }
            
            CancelAlarm(NM_Time_Type);
            CancelAlarm(NM_Time_Max);
            
            if((Destination == OwnID) || (Source == Destination))
            {
                NM_SetTSendTimer(NM_Time_Type,(_NM_TimeInfo *)&NM_Time);
            }
            else
            {
                NM_SetTmaxTimer(NM_Time_Max,(_NM_TimeInfo *)&NM_Time);
                Own_Node_Staute = NM_CheckSkip(Source,Destination,OwnID);
                
                if(Own_Node_Staute == NM_NODE_SKIP)
                {
                    NM_Destination = OwnID;
                    Transmit_NMPDU(ALIVE_MSG);
                }
            }
        }
        else if(OpCode & 0x01)
        {
            NM_Marker.stable = NM_Unstabitily;
        }
    }
}

void NMNormalPrep_tx(void)
{
    NM_TX_STATE = 0;
    if(NM_LastSend_PDU_Type == RING_MSG)
    {
        NM_SetTwbsTimer(NM_Time_WaitBusSleep,(_NM_TimeInfo *)&NM_Time);
        NM_D_Offline();
        NM_State_Transformation(NMTwbsNormal_main);
    }
}

void NMNormalPrep_error(void)
{
    NM_ER_STATE = 0;
    if(NM_LastSend_PDU_Type == RING_MSG)
    {
        NM_SetTwbsTimer(NM_Time_WaitBusSleep,(_NM_TimeInfo *)&NM_Time);
        NM_D_Offline();
        NM_State_Transformation(NMTwbsNormal_main);
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NMTwbsNormal_main
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NMTwbsNormal_main(void)
{
    NM_D_Offline(  );
    
    if(NM_RX_STATE)
    {
        NMTwbsNormal_rx();
        if(NM_Main_Program == NMReset_main)
        {
            return;
        }
    }
    
    if(NM_TwbsTimerOver((_NM_TimeInfo *)&NM_Time))
    {
        NM_State_Transformation(NMBusSleep_main);
    }
}

void NMTwbsNormal_rx(void)
{
    NM_RX_STATE = 0;
    
    NM_SetTmaxTimer(NM_Time_Max,(_NM_TimeInfo *)&NM_Time);
    NM_Sleep.ind = NM_RPDU.msg[ 1 ] & 0x30;
    if(NM_Sleep.ind == NM_NO)
    {
        NMInitReset();
        NMReset();
        NM_Main_Program = NMReset_main;
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NMLimpHome_main
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NMLimpHome_main (void)
{
    if(NM_TX_STATE)
    {
        NMLimpHome_tx();
        return;
    }
    
    if(NM_ER_STATE)
    {
        NMLimpHome_error();
        return;
    }
    
    if(NM_RX_STATE)
    {
        NMLimpHome_rx();
        if((NM_Main_Program == NMReset_main) || (NM_Main_Program == NMTwbsLimpHome_main))
        {
            return;
        }
    }
    
    if(NM_TSendTimerOver((_NM_TimeInfo*)&NM_Time))
    {
        if(NetWorkStatus.bussleep)
        {
            NM_SetTmaxTimer(NM_Time_Max,(_NM_TimeInfo *)&NM_Time);
            
            NM_TX_STATE = 0;
            NM_RX_STATE = 0;
            NM_ER_STATE = 0;
            NM_Destination = OWN_ID;
            Transmit_NMPDU(LIMP_HOME_MSG);
            if(NM_Sleep.ind)
            {
                NM_State_Transformation(NMLimpHomePrepSleep_main);
            }
        }
        else
        {
            NM_SetTSendTimer(NM_Time_Error,(_NM_TimeInfo*)&NM_Time);
            Transmit_NMPDU(LIMP_HOME_MSG);
        }
    }
}

void NMLimpHome_rx(void)
{
    uint8_t   OpCode = 0;
    
    NM_RX_STATE = 0;
    
    OpCode = NM_RPDU.msg[ 1 ];
    
    if(OpCode & 0x20)
    {
        NM_Sleep.ack = NM_YES;
    }
    else
    {
        NM_Sleep.ack = NM_NO;
    }
    
    if(OpCode & 0x10)
    {
        NM_Sleep.ind = NM_YES;
    }
    else
    {
        NM_Sleep.ind = NM_NO;
    }
    
    if(NetWorkStatus.NMactive == NM_Active)
    {
        if(NM_Marker.limphome == NM_YES)
        {
            if(NetWorkStatus.bussleep)
            {
                if(NM_Sleep.ack)
                {
                    NM_SetTwbsTimer(NM_Time_WaitBusSleep,(_NM_TimeInfo*)&NM_Time);
                    NM_State_Transformation(NMTwbsLimpHome_main);
                }
                else //5
                {
                    CancelAlarm(NM_Time_Error);
                    NM_Marker.limphome = NM_NO;
                    NMInitReset();
                    NMReset();
                    NM_Main_Program = NMReset_main;
                }
            }
            else //if(NetWorkStatus.bussleep == NM_NO) //5
            {
                CancelAlarm(NM_Time_Error);
                NM_Marker.limphome = NM_NO;
                
                NMInitReset();
                NMReset();
                
                NM_Main_Program = NMReset_main;
            }
        }
        else
        {
            if(NM_Sleep.ack)
            {
                NM_SetTwbsTimer(NM_Time_WaitBusSleep,(_NM_TimeInfo*)&NM_Time);
                NM_State_Transformation(NMTwbsLimpHome_main);
            }
            else //5
            {
                CancelAlarm(NM_Time_Error);
                NM_Marker.limphome = NM_NO;
                NMInitReset();
                NMReset();
                NM_Main_Program = NMReset_main;
            }
        }
    }
}

void NMLimpHome_tx (void)
{
    NM_TX_STATE = 0;
    if(NM_LastSend_PDU_Type == LIMP_HOME_MSG)
    {
        NM_Marker.limphome = NM_YES;
        NM_SetTSendTimer(NM_Time_Error,(_NM_TimeInfo*)&NM_Time);
    }
}

void NMLimpHome_error(void)
{
    NM_ER_STATE = 0;
    if(NM_LastSend_PDU_Type == LIMP_HOME_MSG)
    {
        if(!NM_Time.TtypEnable)
        {
            NM_SetTSendTimer(NM_Time_Error,(_NM_TimeInfo*)&NM_Time);
        }
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NMLimpHomePrepSleep_main
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NMLimpHomePrepSleep_main(void)
{
    if(NM_RX_STATE)
    {
        NMLimpHomePrepSleep_rx();
        if((NM_Main_Program == NMLimpHome_main) || (NM_Main_Program == NMTwbsLimpHome_main))
        {
            return;
        }
    }
    if(NM_TmaxTimerOver((_NM_TimeInfo*)&NM_Time))//P39
    {
        NM_D_Offline( );
        NM_SetTwbsTimer(NM_Time_WaitBusSleep,(_NM_TimeInfo*)&NM_Time);
        NM_State_Transformation(NMTwbsLimpHome_main);
    }
}

void NMLimpHomePrepSleep_rx(void)
{
    uint8_t   OpCode = 0;
    
    NM_RX_STATE = 0;
    
    OpCode = NM_RPDU.msg[ 1 ];
    
    if(OpCode & 0x20) //P39
    {
        NM_SetTwbsTimer(NM_Time_WaitBusSleep,(_NM_TimeInfo*)&NM_Time);
        NM_State_Transformation(NMTwbsLimpHome_main);
    }
    else if(OpCode & 0x10)
    {
        
    }
    else
    {
        NM_SetTSendTimer(NM_Time_Error,(_NM_TimeInfo*)&NM_Time);
        NM_State_Transformation(NMLimpHome_main);
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NMTwbsLimpHome_main
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NMTwbsLimpHome_main(void)
{
    NM_D_Offline( );
    
    if(NM_RX_STATE)
    {
        NMTwbsLimpHome_rx();
        if(NM_Main_Program == NMLimpHome_main)
        {
            return;
        }
    }
    
    if(NM_TwbsTimerOver((_NM_TimeInfo*)&NM_Time))
    {
        NM_State_Transformation(NMBusSleep_main);
    }
}

void NMTwbsLimpHome_rx(void)
{
    uint8_t OpCode = 0;
    
    NM_RX_STATE  = 0;
    
    OpCode = NM_RPDU.msg[ 1 ];
    if((OpCode & 0x30) == 0)
    {
        NM_SetTSendTimer(NM_Time_Error,(_NM_TimeInfo*)&NM_Time);
        NM_State_Transformation(NMLimpHome_main);
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NMBusSleep_main
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NMBusSleep_main (void)
{
    NM_D_Offline( );
    NM_rx_err_count = 0;
    NM_tx_err_count = 0;
    
    if(NetWorkStatus.WakeupEvent == NM_LocalWakeup)
    {
        NetWorkStatus.WakeupEvent = NM_WaitWakeup;
        GotoMode(NMAwake);
    }
    else if(NetWorkStatus.WakeupEvent == NM_RemoteWakeup)
    {
        NetWorkStatus.WakeupEvent = NM_WaitWakeup;
        GotoMode(NMAwake);
    }
    else
    {
        GotoMode(NMBusSleep);
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NM_CheckSkip
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
uint8_t NM_CheckSkip (uint8_t Source,uint8_t Destination,uint8_t Own_Node)
{
    uint8_t S = 0;
    uint8_t R = 0;
    uint8_t D = 0;
    uint8_t skip;
    
    S = Source;
    R = Own_Node;
    D = Destination;
    
    skip = 0;
    
    if(D < R)
    {
        if(S < D)
        {
            
        }
        else
        {
            if(S < R)
            {
                skip = NM_NODE_SKIP;
            }
        }
    }
    else
    {
        if(S < D)
        {
            if(S < R)
            {
                skip = NM_NODE_SKIP;
            }
        }
        else
        {
            skip = NM_NODE_SKIP;
        }
    }
    return  skip;
}

/*-------------------------------------------------------------------------
* Function Name  : NM_ConfigLogicalFun
* Description    : determine logical successor
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NM_ConfigLogicalFun(uint8_t SourceID)
{
    uint8_t S = 0;
    uint8_t R = 0;
    uint8_t L = 0;
    
    S = SourceID;
    R = OWN_ID;
    L = NM_Destination;
    
    if(L == R)
    {
        L = S;
    }
    else
    {
        if(L < R)
        {
            if(S < L)
            {
                L = S;
            }
            else
            {
                if(S < R)
                {
                    
                }
                else
                {
                    L = S;
                }
            }
        }
        else
        {
            if(S < L)
            {
                if(S < R)
                {
                    
                }
                else
                {
                    L = S;
                }
            }
        }
    }
    
    NM_Destination = L;
}

/*-------------------------------------------------------------------------
* Function Name  : GotoMode
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void GotoMode (uint8_t Mode)
{
    switch(Mode)
    {
        case NMBusSleep:
            NM_TX_STATE = 0;
            NM_RX_STATE = 0;
            NM_ER_STATE = 0;

            NM_D_Init(BusSleep);
            SilentNM( );
            break;
        case NMAwake:
            if((NM_Main_Program == NMNormal_main) || (NM_Main_Program == NMLimpHome_main))
            {
                break;
            }
            TalkNM( );
            if(NM_Main_Program == NMTwbsNormal_main)
            {
                CancelAlarm(NM_Time_WaitBusSleep);
                NMInitReset();
                NM_State_Transformation(NMReset_main);
                NM_Destination = OWN_ID;
                NM_rx_err_count++;
                Transmit_NMPDU(ALIVE_MSG);
                
                break;
            }
            
            if(NM_Main_Program == NMLimpHomePrepSleep_main)
            {
                NM_Marker.limphome = NM_NO;
                
                NM_State_Transformation(NMLimpHome_main);
                
                NM_D_Online( );
                Transmit_NMPDU(LIMP_HOME_MSG);
                NM_SetTSendTimer(NM_Time_Error,(_NM_TimeInfo*)&NM_Time);
                break;
            }
            
            if(NM_Main_Program == NMTwbsLimpHome_main)
            {
                CancelAlarm(NM_Time_WaitBusSleep);
                
                NM_Marker.limphome = NM_NO;
                
                NM_State_Transformation(NMLimpHome_main);
                
                NM_D_Online( );
                Transmit_NMPDU(LIMP_HOME_MSG);
                NM_SetTSendTimer(NM_Time_Error,(_NM_TimeInfo*)&NM_Time);
                
                break;
            }
            
            if(NM_Main_Program == NMBusSleep_main)
            {
                NM_D_Init(BusInit);           //initialize hardware by D_Init(...;BusInit)//NMInit

                NM_Marker.limphome = NM_NO;//NMInit
                NMInitReset();
                NM_Destination = OWN_ID; //P26 reset the system specific default configuration
                NM_rx_err_count++;         //P25
                
                NM_State_Transformation(NMReset_main);
                
                NM_SPDU.bits.Opcode &= 0xCF;
                Transmit_NMPDU(ALIVE_MSG);
                break;
            }
            break;
        default:
            NM_State_Transformation(NM_Do_Nothing);
    }
}

/*-------------------------------------------------------------------------
* Function Name  : Transmit_NMPDU
* Description    : PDU Message handle
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/ 
void Transmit_NMPDU(uint8_t PDU_Type)
{
    NM_LastSend_PDU_Type = PDU_Type;
    
    NM_Sleep.ind = NetWorkStatus.bussleep;
            
    switch(PDU_Type)
    {
        case ALIVE_MSG:
            NM_SPDU.bits.Opcode = 0x01;

            NM_Sleep.ack = 0;
            NM_Marker.stable = NM_Unstabitily;
            break;
        case RING_MSG:
            NM_SPDU.bits.Opcode = 0x02;
            
            NM_Marker.stable = NM_Stabilize;
            break;
        case LIMP_HOME_MSG:
            NM_SPDU.bits.Opcode = 0x04;
            
            NM_Sleep.ack = 0;
            NM_Marker.stable = NM_Unstabitily;
            break;
        default:
            ;
    }
    
    if((NM_SPDU.bits.Opcode & 0x04) == 0x04)
    {
        if(NM_LimpHomeTxCnt < 255)
        {
            NM_LimpHomeTxCnt ++;
        }
    }
    else
    {
        NM_LimpHomeTxCnt = 0;
    }
    
    if(NM_Sleep.ind == NM_YES)
    {
        NM_SPDU.bits.Opcode |= 0x10;
    }
    else
    {
        NM_SPDU.bits.Opcode &= 0xCF;
    }
    
    if(NM_Sleep.ind == NM_NO)
    {
        NM_Sleep.ack = 0;
    }
    
    if(NM_Main_Program != NMNormalPrepSleep_main)
    {
        NM_Sleep.ack = NM_NO;
    }
    
    if(NM_Sleep.ack)
    {
        NM_SPDU.bits.Opcode |= 0x20;
    }
    else
    {
        NM_SPDU.bits.Opcode &= 0xDF;
    }
    
    NM_SPDU.bits.NM_Data0 = 0x00;
    NM_SPDU.bits.NM_Data1 = 0x00;
    NM_SPDU.bits.NM_Data2 = 0x00;
    NM_SPDU.bits.NM_Data3 = 0x00;
    NM_SPDU.bits.Reserved0 = 0xFF;
    NM_SPDU.bits.Reserved1 = 0xFF;
    
    NM_ER_STATE = 0;
    NM_TX_STATE = 0;
    
    NM_Send_PDU_Fun( );
}

/*-------------------------------------------------------------------------
* Function Name  : CancelAlarm
* Description    : Cansel Ttyp /Tmax /Terror count
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void CancelAlarm(uint16_t timetype)
{
    switch(timetype)
    {
        case NM_Time_Error:
        case NM_Time_Type:
            NM_Time.TtypEnable = 0;
            NM_Time.TtypTime = 0;
            break;
        case NM_Time_Max:
            NM_Time.TmaxEnable = 0;
            NM_Time.TmaxTime = 0;
            break;
        case NM_Time_WaitBusSleep:
            NM_Time.SleepEnable = 0;
            break;
        default:
            ;
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NM_SetTmaxTimer
* Description    : set Tmax count start
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NM_SetTmaxTimer(uint16_t Second,_NM_TimeInfo *T)
{
    T->TtypEnable = 0;
    T->TtypTime = 0;
    T->TmaxEnable = 1;
    T->OverTmaxTime = Second;
    T->TmaxTime = 0;
}

/*-------------------------------------------------------------------------
* Function Name  : NM_TmaxTimerOver
* Description    : check Tmax out
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
uint8_t NM_TmaxTimerOver(_NM_TimeInfo *T)
{
    if((T->TmaxTime >= T->OverTmaxTime)&&(T->TmaxEnable))
    {
        T->TmaxEnable = 0;
        T->TmaxTime = 0;
        return 1;
    }
    else
    {
        return 0;
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NM_SetTSendTimer
* Description    : set Ttyp or Terror count
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NM_SetTSendTimer(uint16_t Second,_NM_TimeInfo *T)
{
    T->TmaxEnable = 0;
    T->TmaxTime = 0;
    T->TtypEnable = 1;
    T->OverTtypTime = Second;
    T->TtypTime = 0;
}

/*-------------------------------------------------------------------------
* Function Name  : NM_TSendTimerOver
* Description    : Ttyp or Terror out
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
uint8_t NM_TSendTimerOver(_NM_TimeInfo *T)
{
    if((T->TtypTime >= T->OverTtypTime)&&(T->TtypEnable))
    {
        T->TtypEnable = 0;
        T->TtypTime = 0;
        return 1;
    }
    else
    {
        return 0;
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NM_SetTwbsTimer
* Description    : set Twbs count
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NM_SetTwbsTimer(uint16_t Second,_NM_TimeInfo *T)
{
    T->TwbsEnable = 1;
    T->OverTwbsTime = Second;
    T->TwbsTime = 0;
}

/*-------------------------------------------------------------------------
* Function Name  : NM_TwbsTimerOver
* Description    : check Twbs out
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
uint8_t NM_TwbsTimerOver(_NM_TimeInfo *T)
{
    if((T->TwbsTime >= T->OverTwbsTime)&&(T->TwbsEnable))
    {
        T->TwbsEnable = 0;
        return 1;
    }
    else
    {
        return 0;
    }
}

/*-------------------------------------------------------------------------
* Function Name  : NM_SetSleepTimer
* Description    : set sleep wait timer
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void NM_SetSleepTimer(uint16_t Second,_NM_TimeInfo *T)
{
    T->SleepEnable = 1;
    T->OverSleepTime = Second;
    T->SleepTime = 0;
}

/*-------------------------------------------------------------------------
* Function Name  : NM_SleepTimerOver
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
uint8_t NM_SleepTimerOver(_NM_TimeInfo *T)
{
    if((T->SleepTime >= T->OverSleepTime)&&(T->SleepEnable))
    {
        T->SleepEnable = 0;
        T->SleepTime = 0;
        return 1;
    }
    else
    {
        return 0;
    }
}