/**
 * @file        Protocol_Lib.c
 * @brief       串口协议解析
 * @details     串口协议解析
 * @author      myliu
 * @date        2022.05.09
 * @version     V1.0
 * @copyright   myiu
 */

#include <stdio.h>
#include <string.h>
#include "Protocol_Lib.h"
static UARTOpen          UARTOpen_Cbk;
static UARTSend          UARTSend_Cbk;
static UARTRead          UARTRead_Cbk;
static ProcParse         ProcParseCbk;
static UARTClose         UARTClose_Cbk;
static ProtocolSetData   ProtocolSetData_Cbk;
static Protocol_uint8_t *mDataBufPtr   = Protocol_NULL;
static Protocol_uint16_t mDataBufLen   = 0;
static Protocol_uint32_t DataBufMaxLen = 0;
//#define DEBUG_PRO_DATA 0
/**
 * @brief 初始化函数
 * @param[in] pMemSpace 分配给协议库的内存空间，用来缓存串口数据
 * @param[in] MemLen    分配的内存空间大小
 * @param[in] pFunc     回调函数，包括串口打开、发送、读取、关闭，也包括解析后数据回传
 * @param[in] ProcParseCbk 此回调函数，返回数据从FrameNo开始到CRC16之前，和ProtocolSetData_Cbk二选一
 * @param[in] ProtocolSetData_Cbk 此回调函数，返回数据区分命令字、电源状态等，和ProcParseCbk二选一，详见结构体Protocol_Data_t
 *
 * @warning 此函数KL30和Wakeup都要调用，否则未分配内存，功能不好使，也有可能造成野指针复位
 *
 * @since 1.0.0
 */
void Protocol_Init(Protocol_uint8_t *pMemSpace, Protocol_uint32_t MemLen, Protocol_Func_t *pFunc)
{
    mDataBufPtr         = pMemSpace;
    DataBufMaxLen       = MemLen;
    UARTOpen_Cbk        = pFunc->UARTOpen_Cbk;
    UARTSend_Cbk        = pFunc->UARTSend_Cbk;
    UARTRead_Cbk        = pFunc->UARTRead_Cbk;
    ProcParseCbk        = pFunc->ProcParseCbk;
    UARTClose_Cbk       = pFunc->UARTClose_Cbk;
    ProtocolSetData_Cbk = pFunc->ProtocolSetData_Cbk;

    if ( UARTOpen_Cbk != Protocol_NULL )
    {
        UARTOpen_Cbk( );
    }

    return;
}

/**
 * @brief 串口协议服务函数，包括读取数据，解析数据，如在外部读取数据，可不调用此函数
 *
 * @warning 此函数可自定义周期调用，建议20ms周期调用，最大不可超过协议的最小发送周期
 *
 * @since 1.0.0
 */
void Protocol_Service(void)
{
    int               len;
    Protocol_uint32_t readNum = 0;

    if ( UARTRead_Cbk != Protocol_NULL )
    {
        readNum = UARTRead_Cbk(mDataBufPtr + mDataBufLen, 256 - mDataBufLen);
        if ( readNum > 0 )
        {
            mDataBufLen += readNum;
            // 解析协议
            len = Protocol_Parse(mDataBufPtr, mDataBufLen);
            if ( (len > 0) && (len < mDataBufLen) )
            {
                // 将未解析的数据移到头部
                // Move unparsed data to the head
                memcpy(mDataBufPtr, mDataBufPtr + len, mDataBufLen - len);
            }

            mDataBufLen -= len;
        }
    }
}

/**
 * @brief 协议解析函数，如外部获取数据（例如中断），可直接调用此函数解析数据
 * @param[in] pData     协议数据内容
 * @param[in] len       需要处理的协议数据长度
 *
 * @return    剩余已处理的数据长度
 *
 * @since 1.0.0
 */
Protocol_uint32_t Protocol_Parse(const Protocol_uint8_t *pData, Protocol_uint32_t len)
{
    Protocol_uint32_t remainLen = len;    // 剩余数据长度 Remaining data length
    Protocol_uint32_t dataLen;            // 数据包长度 Packet length
    Protocol_uint32_t frameLen;           // 帧长度 Frame length
    Protocol_uint32_t frameSum;
    Protocol_Data_t   ProcData;
    int               i = 0;
    int               dataBuf [ 256 ];

    /**
     * 以下部分需要根据协议格式进行相应的修改，解析出每一帧的数据
     */
    while ( remainLen >= DATA_PACKAGE_MIN_LEN )
    {
        // 找到一帧数据的数据头
        // Find the data header of a frame of data
        while ( (remainLen >= 2) && ((pData [ 0 ] != CMD_HEAD1) || (pData [ 1 ] != CMD_HEAD2)) )
        {
            pData++;
            remainLen--;
            continue;
        }

        if ( remainLen < DATA_PACKAGE_MIN_LEN )
        {
#ifdef DEBUG_PRO_DATA

            d_printf("too short!!!!!!\n");
#endif
            break;
        }

        dataLen  = pData [ 2 ];

        frameLen = dataLen + DATA_PACKAGE_FIXED_LEN;
				
        if ( frameLen > remainLen )
        {
            // 数据内容不全
#ifdef DEBUG_PRO_DATA

            d_printf("Incomplete data packet!!!!!!\n");
#endif
            break;
        }

        // 打印一帧数据，需要时在CommDef.h文件中打开DEBUG_PRO_DATA宏
#ifdef DEBUG_PRO_DATA
        for ( i = 0; i < frameLen; ++i )
        {
            d_printf("%x ", pData [ i ]);
        }
        d_printf("\n");
#endif

        // 检测校验码 Checksum 
        frameSum = (pData [ frameLen - 2 ] << 8) | (pData [ frameLen - 1 ]);
        if ( frameLen > 4 )
        {
            if ( getCheckSum(pData + 2, frameLen - 4) == frameSum )
            {
                // 解析一帧数据
                if ( ProcParseCbk != Protocol_NULL )
                {
                    ProcParseCbk(pData + 3, dataLen - 2);
                }

                if ( ProtocolSetData_Cbk != Protocol_NULL )
                {
                    ProcData.FrameNo  = pData [ 3 ];
                    ProcData.PowerSts = (pData [ 4 ] >> 6) & 0x03;
                    ProcData.CmdID    = pData [ 4 ] & 0x3F;
                    if ( ProcData.CmdID == 0x10 )
                    {
#ifdef DEBUG_PRO_DATA
                        for ( i = 0; i < frameLen; ++i )
                        {
                            d_printf("%x ", pData [ i ]);
                        }
                        d_printf("\n");
#endif
                    }
                    if ( ProcData.CmdID == 0x12 )
                    {
#ifdef DEBUG_PRO_DATA
                        for ( i = 0; i < frameLen; ++i )
                        {
                            d_printf("%x ", pData [ i ]);
                        }
                        d_printf("\n");
#endif
                    }
                    ProcData.DataLen = dataLen - 4;
                    memcpy(ProcData.Data, pData + 5, ProcData.DataLen);
                    ProtocolSetData_Cbk(&ProcData);
                }
            }
            else
            {
                for ( i = 0; i < frameLen; ++i )
                {
                 //   dataBuf [ i ] = pData [ i ];
                }
                i = 0;
#ifdef DEBUG_PRO_DATA

                d_printf("CheckSum error: new = %x, old = %x!!!!!!\n", getCheckSum(pData + 2, frameLen - 4), frameSum);
#endif
            }
        }

        pData += frameLen;
        remainLen -= frameLen;
    }

    return len - remainLen;
}

/**
 * 根据协议格式进行拼接
 */
/**
 * @brief 串口协议数据拼接，如初始化发送函数，调用此函数后，数据已通过串口发送
 * @param[in] cmdID     命令字
 * @param[in] pData     协议数据内容（不包括协议头、长度、帧序号、命令字、校验和，从数据域算起）
 * @param[in] len       数据域长度
 *
 * @return    已发送的数据长度
 *
 * @since 1.0.0
 */
Protocol_uint32_t Protocol_Send(const Protocol_uint16_t cmdID, const Protocol_uint8_t *pData, Protocol_uint8_t len)
{
    int               i        = 0;
    Protocol_uint16_t checksum = 0;
    Protocol_uint8_t  dataBuf [ 256 ];
    Protocol_uint32_t frameLen;

    if ( len + DATA_PACKAGE_MIN_LEN > 256 )
    {
        // printf("sendProtocol data is too len !!!\n");
        return 0;
    }

    dataBuf [ 0 ] = CMD_HEAD1;
    dataBuf [ 1 ] = CMD_HEAD2;    // 同步帧头 Sync frame header

    dataBuf [ 2 ] = len + 4;
    dataBuf [ 3 ] = 0;    // 命令字节 Command byte

    dataBuf [ 4 ] = ( Protocol_uint8_t )cmdID;

    frameLen = 5;

    // 数据 Data
    for ( i = 0; i < len; ++i )
    {
        dataBuf [ frameLen ] = pData [ i ];
        frameLen++;
    }

    // 校验码 Checksum
    checksum             = getCheckSum(dataBuf + 2, frameLen - 2);
    dataBuf [ frameLen ] = (checksum >> 8) & 0x00FF;
    frameLen++;
    dataBuf [ frameLen ] = checksum & 0x00FF;
    frameLen++;

    if ( UARTSend_Cbk != Protocol_NULL )
    {
        return UARTSend_Cbk(dataBuf, frameLen);
    }
    else
    {
        return 0;
    }
}
