/*
*********************************************************************
* Includes
*********************************************************************
*/

#include "kwp2000_interface.h"
#include "kwp2000_tp.h"

/*
*********************************************************************
* defines
*********************************************************************
*/
/* Possible initialization formats */
#define KWP2000_FORMAT_NOADDRESS 0u            /* 00 */
#define KWP2000_FORMAT_CARB 1u                 /* 01 Exception mode */
#define KWP2000_FORMAT_PHYSICALADDRESSING 2u   /* 10 */
#define KWP2000_FORMAT_FUNCTIONALADDRESSING 3u /* 11 */

#define KWP2000_WUP_TI_IDLE_DU 5ul /* idle time of RX pins [us] */

#define KWP2000_WUP_TI_MIN_DU 24ul /* duration of WUP, min value (low time/hi time) [ms]  */
#define KWP2000_WUP_TI_MAX_DU 26ul /* duration of WUP, max value (low time/hi time) [ms]  */

/*
*********************************************************************
* structure
*********************************************************************
*/

typedef enum
{
    KWP2000_WUP_WAIT4IDLE_E = 0x00,
    KWP2000_WUP_WAIT4SCANLO_E = 0x10,
    KWP2000_WUP_SCANLO_E,
    KWP2000_WUP_WAIT4SCANHI_E = 0x20,
    KWP2000_WUP_SCANHI_E,
    KWP2000_WUP_SCANASC_E = 0x30,
    KWP2000_WUP_DETECTED_E = 0x80,
} Kwp2000_ScanStates_t;

typedef struct
{
    unsigned long xPortIn_pu32;
    unsigned long xPortInMsk_u32;
    unsigned long tiStartSample_u32;
    Kwp2000_ScanStates_t xScanState_u32;
    unsigned char xBusType_u32;
    unsigned long tiCom_u32;
} Kwp2000_ScanWup_t;

/*
*********************************************************************
* variable
*********************************************************************
*/

/* Carb address and format */
unsigned char kwp2000_CARB_Address;
unsigned char kwp2000_CARB_TgtRequest;
unsigned char kwp2000_CARB_TgtResponse;
unsigned char kwp2000_CARB_Fmt;

unsigned long Kwp2000_TimeCounter;
unsigned long Kwp2000_P2_TimeCounter;
unsigned long Kwp2000_ComMode;
Kwp2000_ScanWup_t Kwp2000_ScanWup;
Kwp2000_Modify_t Kwp2000_Modify;
Kwp2000_ComState_t Kwp2000_ComState;

/*
*********************************************************************
* function
*********************************************************************
*/

void Kwp2000_ProtocolInit(void);

/*-------------------------------------------------------------------------
* Function Name  : Kwp2000_Init
* Description    : Kwp2000初始化
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void Kwp2000_Init(void)
{
    Kwp2000_AscInit();
    Kwp2000_ProtocolInit();
}

/*-------------------------------------------------------------------------
* Function Name  : Kwp2000_ProtocolInit
* Description    : KWP2000协议初始化
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void Kwp2000_ProtocolInit(void)
{
    /* Initial time base */
    Kwp2000_TimeCounter = 0;
    Kwp2000_P2_TimeCounter = 0;

    /* Prepare Wup scan */
    Kwp2000_ComMode = COM_INIT;

    Kwp2000_ScanWup.xScanState_u32 = KWP2000_WUP_WAIT4IDLE_E;
    Kwp2000_ScanWup.tiStartSample_u32 = Kwp2000_TimeCounter;

    //Uart_DisableReceiveIsrReq(); /*----20220309----*/

    /*Set RX as GPIO-IN*/
    Uart_SetRxGpio();

    Kwp2000_Modify.typeOfModif = KWP2000_MODIFY_NULL;

    Kwp2000_ComState.Rx_len = 0;

    Kwp2000_SetAddress();
    Kwp2000_SetTiming();

    kwp2000_CARB_TgtRequest = 0x6A;
    kwp2000_CARB_TgtResponse = 0x6B;

    Kwp2000_ComState.ResponsePending = 0;
}

/*-------------------------------------------------------------------------
* Function Name  : Kwp2000_Timeout
* Description    :
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void Kwp2000_Timeout(void)
{
    unsigned long tiSample_u32;

    if ((Kwp2000_ComMode & PROTOCOL) == KWP2000)
    {
        tiSample_u32 = Kwp2000_TimeCounter;

        /**/
        if (Kwp2000_ComMode == KWP2000_FAST_INIT)
        {

            if ((tiSample_u32 - Kwp2000_ComState.tiCom_u32) >= 20u)
            {
                Kwp2000_ProtocolInit();
            }
        }
        else
        {

            if ((tiSample_u32 - Kwp2000_ComState.tiCom_u32) >= kwp2000_P3_MAX_LIMIT)
            {
                Kwp2000_ProtocolInit();
            }
        }
        /*
        tiSample_u32 = Kwp2000_TimeCounter;

        if((tiSample_u32 - Kwp2000_ComState.tiCom_u32) >= kwp2000_P3_MAX_LIMIT)
        {
            Kwp2000_ProtocolInit();
        }
        */
    }
}

/*-------------------------------------------------------------------------
* Function Name  : Kwp2000_CommuniationDown
* Description    : 通讯底层与协议层初始化
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void Kwp2000_CommuniationDown(void)
{
    Kwp2000_AscInit();
    Kwp2000_ProtocolInit();
}

/*-------------------------------------------------------------------------
* Function Name  : Kwp2000_VerifyChecksum
* Description    : Verify the message checksum
* Input          :
* Output         : None
* Return         : TRUE or FALSE
* onther         :
--------------------------------------------------------------------------*/
unsigned char Kwp2000_VerifyChecksum(void)
{
    unsigned char answer;
    unsigned char CS;

    /* Calculates the message Checksum */
    CS = Kwp2000_ChecksumCalculate((unsigned char *) & (Kwp2000_ComState.RxBuffer[0]), (unsigned short)(Kwp2000_ComState.headerSize + Kwp2000_ComState.kwp2000_Len));
    /* Verifies it with the one that was in the message */
    if (CS == Kwp2000_ComState.kwp2000_Checksum)
    {
        /* it is ok */
        answer = 1;
    }
    else
    {
        /* it is not the same */
        answer = 0;
    }

    return (answer);
}

/*-------------------------------------------------------------------------
* Function Name  : Kwp2000_ChecksumCalculate
* Description    : calculates the checksum
* Input          : dataBuffer, numberOfBytes
* Output         : None
* Return         : sum
* onther         :
--------------------------------------------------------------------------*/
unsigned char Kwp2000_ChecksumCalculate(unsigned char *dataBuffer, unsigned short numberOfBytes)
{
    unsigned char sum;
    unsigned short i;

    /* Resets the sum */
    sum = 0;

    for (i = 0; i < numberOfBytes; i++)
    {
        /* add each byte to the sum */
        sum += dataBuffer[i];
    }

    return (sum);
}

/*-------------------------------------------------------------------------
* Function Name  : Kwp2000_PrepareHeader
* Description    : constructs the message header
* Input          : headerSize
* Output         : dataSize
* Return         : sum
* onther         :
--------------------------------------------------------------------------*/
unsigned char Kwp2000_PrepareHeader(unsigned char dataSize)
{
    unsigned char headerSize;
    unsigned char fmt;

    /* Resets the header size */
    headerSize = 1;
    /* Tests the data size */
    if (dataSize > 63)
    {
        /* Too big to be written in the format byte, needs additional length byte */
        fmt = Kwp2000_ComState.Format << 6;
        Kwp2000_ComState.TxBuffer[4 - (headerSize++)] = dataSize;
    }
    else
    {
        /* Format and size in the same byte */
        fmt = (Kwp2000_ComState.Format << 6) | dataSize;
    }
    /* Test the format used */
    switch (Kwp2000_ComState.Format)
    {
    case KWP2000_FORMAT_NOADDRESS:
        /* if no address then nothing to be added to the header */
        break;
    case KWP2000_FORMAT_CARB:
        //   /* CARB mode */
        //   fmt = kwp2000_CARB_Fmt;
        //   asc0_CommunicationBuf.b[4-(headerSize++)]= kwp2000_CARB_Address;
        //   asc0_CommunicationBuf.b[4-(headerSize++)]= kwp2000_CARB_TgtResponse;
        break;
    case KWP2000_FORMAT_FUNCTIONALADDRESSING:
    case KWP2000_FORMAT_PHYSICALADDRESSING:
        /* Physical addressing */
        Kwp2000_ComState.TxBuffer[4 - (headerSize++)] = kwp2000_PhysicalAddress;
        Kwp2000_ComState.TxBuffer[4 - (headerSize++)] = Kwp2000_ComState.SrcAddr; // kwp2000_ServerAddress;
        break;
    }
    Kwp2000_ComState.TxBuffer[4 - headerSize] = fmt;

    Kwp2000_ComState.Response_Datas = (unsigned char *)&Kwp2000_ComState.TxBuffer[4 - headerSize];

    return (headerSize);
}

/*-------------------------------------------------------------------------
* Function Name  : Kwp2000_FormatAnalyse
* Description    : Copies the datas from the buffer to the variables related to the header information
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void Kwp2000_HeaderAnalyse(void)
{
    /* Tests the header size */
    switch (Kwp2000_ComState.headerSize)
    {
    case 1:
        /* 1 byte  -> FMT */
        break;
    case 2:
        /* 2 bytes -> FMT LEN */
        Kwp2000_ComState.kwp2000_Len = Kwp2000_ComState.RxBuffer[1];
        break;
    case 3:
        /* 3 bytes -> FMT TGT SRC */
        Kwp2000_ComState.TgtAddr = Kwp2000_ComState.RxBuffer[1];
        Kwp2000_ComState.SrcAddr = Kwp2000_ComState.RxBuffer[2];
        break;
    case 4:
        /* 4 bytes -> FMT TGT SRC LEN */
        Kwp2000_ComState.TgtAddr = Kwp2000_ComState.RxBuffer[1];
        Kwp2000_ComState.SrcAddr = Kwp2000_ComState.RxBuffer[2];
        Kwp2000_ComState.kwp2000_Len = Kwp2000_ComState.RxBuffer[3];
        break;
    }
}

/*-------------------------------------------------------------------------
* Function Name  : Kwp2000_FormatAnalyse
* Description    : ����֡����
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
unsigned char Kwp2000_FormatAnalyse(void)
{
    unsigned char headerSize;

    /* Extracts the format information */
    Kwp2000_ComState.Format = (Kwp2000_ComState.RxBuffer[0] & 0xC0) >> 6;
    /* Extracts the message length */
    Kwp2000_ComState.kwp2000_Len = Kwp2000_ComState.RxBuffer[0] & 0x3F;
    /* Resets the headerSize */
    headerSize = 1;
    /* Tests the format */
    switch (Kwp2000_ComState.Format)
    {
    case KWP2000_FORMAT_NOADDRESS:
        /* There is no address */
        break;
    case KWP2000_FORMAT_CARB:
        /* There are source and target addresses */
        Kwp2000_ComState.kwp2000_Len = 0;
    case KWP2000_FORMAT_PHYSICALADDRESSING:
    case KWP2000_FORMAT_FUNCTIONALADDRESSING:
        /* There are source and target addresses */
        headerSize += 2;
        break;
    }
    /* Tests the message length */
    if (Kwp2000_ComState.kwp2000_Len == 0)
    {
        /* it was 0 so there is an additional length byte */
        headerSize++;
    }
    else
    {
    }
    /* Sets the pointer at the beginning of the datas */
    Kwp2000_ComState.Request_Datas = (unsigned char *) & (Kwp2000_ComState.RxBuffer[headerSize]);

    return (headerSize);
}

/*-------------------------------------------------------------------------
* Function Name  : kwp2000_AddressTest
* Description    : verifies the target address to know if this ECU is the target
* Input          :
* Output         : None
* Return         : TRUE or FALSE
* onther         :
--------------------------------------------------------------------------*/
unsigned char kwp2000_AddressTest(void)
{
    unsigned char answer = 0;

    /* Tests the format */
    if (Kwp2000_ComState.Format == KWP2000_FORMAT_NOADDRESS)
    {
        /* No addressing bytes */
        /* The message is for this ECU */
        answer = 1;
    }
    else
    {
        switch (Kwp2000_ComState.Format)
        {
        case KWP2000_FORMAT_CARB:
            /* CARB addressing mode */
            if (Kwp2000_ComState.TgtAddr == kwp2000_CARB_TgtRequest)
            {
                /* it is the correct address */
                answer = 1;
            }
            else
            {
                /* it is the false address */
                answer = 0;
            }
            break;
        case KWP2000_FORMAT_PHYSICALADDRESSING:
            /* Physical addressing mode */
            if (Kwp2000_ComState.TgtAddr == kwp2000_PhysicalAddress)
            {
                /* it is the correct address */
                answer = 1;
            }
            else
            {
                /* it is the false address */
                answer = 0;
            }
            break;
        case KWP2000_FORMAT_FUNCTIONALADDRESSING:
            /* Functional addressing mode */
            if ((Kwp2000_ComState.TgtAddr == kwp2000_FunctionalAddress) || (Kwp2000_ComState.TgtAddr == kwp2000_PhysicalAddress))
            {
                /* it is the correct address */
                answer = 1;
            }
            else
            {
                /* it is the false address */
                answer = 0;
            }
            break;
        default:
            break;
        }
    }

    return (answer);
}

/*-------------------------------------------------------------------------
* Function Name  : Kwp2000_ComInit_Handle
* Description    : 测量Twup
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void Kwp2000_ComInit_Handle(void)
{
    unsigned char RxPortState;

    /* scan all RX-pins, which are configured, for WUP */
    RxPortState = Uart_GetRxLevel();

    /* scan WUP (100ms) for each ASC available */

    /* wait Tidle */
    if (Kwp2000_ScanWup.xScanState_u32 == KWP2000_WUP_WAIT4IDLE_E)
    {
        if (RxPortState != 0)
        {
            if ((Kwp2000_TimeCounter - Kwp2000_ScanWup.tiStartSample_u32) >= KWP2000_WUP_TI_IDLE_DU)
            {
                Kwp2000_ScanWup.xScanState_u32 = KWP2000_WUP_WAIT4SCANLO_E;
            }
        }
        else
        {
            Kwp2000_ScanWup.tiStartSample_u32 = Kwp2000_TimeCounter;
        }
    }

    /* wait for low level on RX-pin and start time measurement */
    if (Kwp2000_ScanWup.xScanState_u32 == KWP2000_WUP_WAIT4SCANLO_E)
    {
        if (RxPortState == 0)
        {
            Kwp2000_ScanWup.xScanState_u32 = KWP2000_WUP_SCANLO_E;
            Kwp2000_ScanWup.tiStartSample_u32 = Kwp2000_TimeCounter;
        }
    }
    // measure low level time on RX-pin, if level changes to high during the first 25ms
    // abort measurement
    if (Kwp2000_ScanWup.xScanState_u32 == KWP2000_WUP_SCANLO_E)
    {
        if (RxPortState != 0)
        {
            if (((Kwp2000_TimeCounter - Kwp2000_ScanWup.tiStartSample_u32) >= KWP2000_WUP_TI_MIN_DU) && ((Kwp2000_TimeCounter - Kwp2000_ScanWup.tiStartSample_u32) <= KWP2000_WUP_TI_MAX_DU))
            {
                Kwp2000_ScanWup.tiStartSample_u32 = Kwp2000_TimeCounter;
                Kwp2000_ScanWup.xScanState_u32 = KWP2000_WUP_WAIT4SCANHI_E;
            }
            else
            {
                Kwp2000_ScanWup.tiStartSample_u32 = Kwp2000_TimeCounter;
                Kwp2000_ScanWup.xScanState_u32 = KWP2000_WUP_WAIT4IDLE_E;
            }
        }
    }
    // After 25ms - 4%, wait until RX-pin level goes hi
    // If level stays low, abort measurement
    if (Kwp2000_ScanWup.xScanState_u32 == KWP2000_WUP_WAIT4SCANHI_E)
    {
        if (RxPortState != 0)
        {
            if ((Kwp2000_TimeCounter - Kwp2000_ScanWup.tiStartSample_u32) >= KWP2000_WUP_TI_MIN_DU)
            {
                Kwp2000_ScanWup.xScanState_u32 = KWP2000_WUP_DETECTED_E;

                Uart_SetRxGpio(); // RX
                Kwp2000_AscEnableRx();

                /* reset variables, which are used by the receive interrupt function */
                Kwp2000_ComState.Rx_len = 0;

                /* enable irq */
                Uart_EnableReceiveIsrReq();

                Kwp2000_ComMode = KWP2000_FAST_INIT;

                Kwp2000_ComState.tiCom_u32 = Kwp2000_TimeCounter;
            }
        }
        else
        {
            if (RxPortState == 0)
            {
                Kwp2000_ScanWup.tiStartSample_u32 = Kwp2000_TimeCounter;
                Kwp2000_ScanWup.xScanState_u32 = KWP2000_WUP_WAIT4IDLE_E;
            }
        }
    }

    Kwp2000_TimeCounter += 1;
    Kwp2000_P2_TimeCounter += 1;
}

/*-------------------------------------------------------------------------
* Function Name  : Kwp2000_AscRxInterrupt
* Description    : 串口接收中断
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
void Kwp2000_AscRxInterrupt(void)
{
    unsigned short rxUartData;

    Uart_ClearRxFullFlag(); /* clear service request flags */

    if ((Kwp2000_ComMode & PROTOCOL) == KWP2000)
    {
        if (Kwp2000_ComMode < KWP2000_BUILD_RESPONSE)
        {
            /* if receve on going */
            if (Kwp2000_ComMode == KWP2000_FAST_INIT)
            {
                Kwp2000_ComMode = KWP2000_WAIT_RECEPTION;
            }

            if (Kwp2000_ComState.Rx_len < KWP2000_RX_BUFFER_SIZE)
            {
                rxUartData = Uart_GetData(); /*读取串口数据 ----20220303----*/

                Kwp2000_ComState.RxBuffer[Kwp2000_ComState.Rx_len] = (unsigned char)rxUartData;

                Kwp2000_ComState.tiCom_u32 = Kwp2000_TimeCounter;

                Kwp2000_ComState.Rx_len++;
            }
        }
        else
        {
            /* if transmit on going, do nothing */
            rxUartData = Uart_GetData(); /* 读取串口数据----20220303----*/

            if (Kwp2000_ComState.Tx_len)
            {
                Kwp2000_ComState.Tx_len--;
            }

            if (Kwp2000_ComState.Tx_len > 0)
            {
                if (rxUartData == (*Kwp2000_ComState.Response_Datas))
                {
                    Kwp2000_P2_TimeCounter = 0;
                    Kwp2000_ComState.Response_Datas++;
                    Kwp2000_AscTx(Kwp2000_ComState.Response_Datas); /* 数据发送----20220303----*/
                }
                else
                {
                    Kwp2000_ComState.Tx_len = 0;
                    Kwp2000_ComMode = KWP2000_WAIT_RECEPTION;
                }
            }
            else
            {
                if (Kwp2000_ComState.ResponsePending)
                {
                    Kwp2000_ComMode = KWP2000_BUILD_RESPONSE;

                    /* Start a new timeout counter */
                    Kwp2000_ComState.tiCom_u32 = Kwp2000_TimeCounter;
                }
                else
                {
                    Kwp2000_ComMode = KWP2000_MODIFY_CONFIG;
                }
            }
        }
    }
}