/*
 * @Author: mbw
 * @Date: 2024-11-14 10:21:04
 * @LastEditors: mbw && 1600520629@qq.com
 * @LastEditTime: 2025-01-17 10:46:51
 * @FilePath: \JT-DT-YD4N02A_RTT_MRS-NT26K\bsp\src\bsp_h308.c
 * @Description:
 *
 * Copyright (c) 2024 by ${git_name_email}, All Rights Reserved.
 */
#include "bsp_h308.h"
// #include "drv_usart.h"
#include "lwrb.h"

// 用到了atoi
#include <stdlib.h>
#include "bsp_flash.h"
#include "bsp_rtc.h"
#include "user_sys.h"

#define LOG_TAG "bsp_h308"
#define LOG_LVL LOG_LVL_DBG
#include <ulog.h>

// 用了浮点打印
ALIGN(8)
static char h308_thread_stack[H308_THREAD_STACK_SIZE];
static struct rt_thread h308_thread;

#define UART4_RX_RB_LENGTH (128U)

static lwrb_t uart4_rx_rb;
static rt_uint8_t uart4_rx_rb_data[UART4_RX_RB_LENGTH];

static rt_sem_t uart4_rx_ok_sem;

static rt_timer_t uart4_rx_timer;

uint8_t sensor_rx_count = 0; // 接收缓冲区中，已经收到的数据包数量

TsH308 H308 = {0};

void _UART4_RxTimeout(void *parameter)
{
    rt_sem_release(uart4_rx_ok_sem);
    rt_timer_stop(uart4_rx_timer);
}

static uint8_t H308_XorChecksum(char *str, int len)
{
    uint8_t xorvalue = 0;

    if (str == RT_NULL || len < 2)
    {
        // 处理错误或返回
        return 0; // 或者选择抛出异常
    }
    for (int i = 0; i < len - 4; i++) // 校验值前面的空格要，
    {
        xorvalue ^= str[i];
    }

    return xorvalue;
}

// float打印问题
// https://www.wch.cn/bbs/thread-108241-1.html
// https://www.wch.cn/bbs/thread-91909-1.html
// https://club.rt-thread.org/ask/article/1d040664a33a7b46.html
// https://club.rt-thread.org/ask/article/1d040664a33a7b46.html

/**
 * @brief  打印数据
 * @param  data:使用8字节对齐就好着，4字节对齐就出现问题，问题解决，在cpuport.c中将RT_ALIGN_DOWN((rt_ubase_t)stk, REGBYTES)改为RT_ALIGN_DOWN((rt_ubase_t)stk, 8);
 * @retval None
 */
void H308_ShowData(TsH308Data data)
{
    // A+000.00 +31.1 0657.80 +18.1 B+008.0 00 12
    LOG_D("H308Data: %6.2f, %5.1f, %7.2f, %5.1f, %5.1f, %02X, %02X", data.lel, data.temp, data.laser_temp, data.signal_strength, data.gain, data.fault_code, data.checksum);
}

#ifdef TEST_ENABLE
void Sensor_ShowData(void)
{
    H308_ShowData(H308.Data);
}
MSH_CMD_EXPORT(Sensor_ShowData, Sensor_ShowData);
#endif

uint8_t H308_GetFrameData(const char *p_src, const rt_uint8_t src_len, TsH308Data *pData)
{
    // A+000.00 +31.1 0657.80 +18.1 B+008.0 00 12

    rt_uint8_t xor  = 0;
    uint8_t ret     = 0;
    char *p_str     = strstr(p_src, "A+"); // 查找子字符串的位置
    uint32_t offset = p_str - p_src;       // 计算偏移量

    TsH308StrData _H308StrData = {0};

    if ((p_str != NULL) && (offset <= src_len - 44))
    {
        if (sscanf(p_str, "A+%6s %6s %8s %6s B+%6s %2s %2s",
                   _H308StrData.vol, _H308StrData.temp, _H308StrData.laser_temp, _H308StrData.signal_strength,
                   _H308StrData.gain, _H308StrData.fault_code, _H308StrData.checksum) == 7)
        {
            pData->checksum = (rt_uint8_t)strtol(_H308StrData.checksum, NULL, 16);
            xor             = H308_XorChecksum(p_str, 44); // 对数据进行异或校验
            if (xor == pData->checksum)
            {
                pData->lel             = (float)(atof(_H308StrData.vol) * 20);
                pData->temp            = (float)atof(_H308StrData.temp);
                pData->laser_temp      = (float)atof(_H308StrData.laser_temp);
                pData->signal_strength = (float)atof(_H308StrData.signal_strength);
                pData->gain            = (float)atof(_H308StrData.gain);
                pData->fault_code      = (rt_uint8_t)strtol(_H308StrData.fault_code, NULL, 16);
                pData->checksum        = (rt_uint8_t)strtol(_H308StrData.checksum, NULL, 16);
                ret                    = 0;
            }
            else
            {
                LOG_E("xor failed");
                ret = RT_ERROR;
            }
        }
        else
        {
            ret = RT_ERROR;
            LOG_E("H308_ExtractData failed");
        }
    }

    return ret;
}

static uint8_t H308_Count(const rt_uint8_t *buffer, uint8_t value, uint8_t size)
{
    uint8_t count = 0;
    for (uint8_t i = 0; i < size; i++)
    {
        if (buffer[i] == value)
        {
            count++;
        }
    }
    return count;
}

// static void H308_HandleAbnormal(uint8_t count, uint8_t *flag, uint8_t threshold)
//{
//     if (count > threshold && *flag == 0)
//     {
//         H308.detection_flag = kH308Abnormal;
//         Send_Laser_Alarm_Event(kAlarmExceptionEvent);
//         *flag = 1;
//     }
//     else if (*flag == 1 && count < threshold)
//     {
//         *flag = 0;
//         H308.detection_flag = kH308Normal;
//         Send_Laser_Alarm_Event(kNormalDetectionEvents);
//     }
// }

static void H308_HandleAlarm(uint8_t count, uint8_t *flag, uint8_t threshold)
{
    if (count >= threshold && *flag == 0)
    {
        H308.detection_flag = kH308Alarm;
        Send_Laser_Alarm_Event(kAlarmEvent);
        *flag = 1;
    }
    else if (*flag == 1 && count == 0)
    {
        *flag               = 0;
        H308.detection_flag = kH308Normal;
        Send_Laser_Alarm_Event(kAlarmRcyEvent);
    }
}

static void H308_HandleFault(uint8_t count, uint8_t *flag, uint8_t threshold)
{
    if (count >= threshold && *flag != 1)
    {
        *flag               = 1;
        H308.detection_flag = kH308Fault;
        Send_Laser_Alarm_Event(kFaultEvent);
    }
    else if (*flag == 1 && count == 0)
    {
        *flag               = 0;
        H308.detection_flag = kH308Normal;
        Send_Laser_Alarm_Event(kFaultRcyEvent);
    }
}

static uint8_t H308_CheckData(void)
{
    static rt_uint8_t alarm_flag = 0, fault_flag = 0;
    static rt_uint8_t alarm_count = 0, fault_count = 0;
    static rt_uint8_t index                                   = 0;
    static rt_uint8_t alarm_status_buffer[H308_SAMPLING_TIMS] = {0};
    static rt_uint8_t fault_buf[H308_SAMPLING_TIMS]           = {0};

    alarm_status_buffer[index] = (H308.Data.lel < (float)H308.alarm_value) ? kH308Normal : kH308Alarm;
    fault_buf[index]           = (H308.Data.fault_code == 0) ? kH308Normal : kH308Fault;
    index++;
    if (index >= H308_SAMPLING_TIMS)
    {
        index = 0;
    }

    alarm_count = H308_Count(alarm_status_buffer, kH308Alarm, H308_SAMPLING_TIMS);
    fault_count = H308_Count(fault_buf, kH308Fault, H308_SAMPLING_TIMS);
    H308_HandleAlarm(alarm_count, &alarm_flag, H308_SAMPLING_TIMS);
    H308_HandleFault(fault_count, &fault_flag, H308_SAMPLING_TIMS);

    return RT_EOK;
}

// TODO: 寿命检测
uint8_t IS_H308_EndOfLife(void)
{
    RTC_GetTime();
    if (RtcDateTime.year > 2035)//至少大于2035年再检测
    {
       if (RTC_GetCounter() >= H308.expiration_seconds)
        {
            H308.end_of_life = 1;
        }
    }
    return H308.end_of_life;
}

void UART4_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure   = {0};
    USART_InitTypeDef USART_InitStructure = {0};
    NVIC_InitTypeDef NVIC_InitStructure   = {0};

    lwrb_init(&uart4_rx_rb, uart4_rx_rb_data, sizeof(uart4_rx_rb_data));

    USART_DeInit(UART4); // 寄存器恢复默认值

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate   = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits   = USART_StopBits_1;
    USART_InitStructure.USART_Parity     = USART_Parity_No;

    USART_InitStructure.USART_Mode                = USART_Mode_Tx | USART_Mode_Rx;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(UART4, &USART_InitStructure);

    USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);

    NVIC_InitStructure.NVIC_IRQChannel                   = UART4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    USART_Cmd(UART4, ENABLE);
    LOG_I("UART4 Init");
}

static void h308_thread_entry(void *param)
{
     rt_err_t ret;
    static uint8_t str[UART4_RX_RB_LENGTH] = {0};
    static uint8_t h308_rx_timout_cnt      = 0;
    static uint8_t h308_err_cnt            = 0;
   
    H308.alarm_value = Flash_Get_SysCfg(kAlarmLValueId); // 获取系统报警阈值
    LOG_D("报警阈值:%d%LEL", H308.alarm_value);

    while (1)
    {
        ret = rt_sem_take(uart4_rx_ok_sem, 3000);
        if (ret == RT_EOK)
        {
            uint8_t len = lwrb_get_full(&uart4_rx_rb);
            lwrb_read(&uart4_rx_rb, str, len);
            rt_memset(&str[len - 2], 0, 2);

            if (len >= 44)
            {
                ret = H308_GetFrameData((const char *)str, len, &H308.Data);
                LOG_I("str:[%s]", str);
                if (ret == 0)
                {
                    H308_CheckData();
                }
                else
                {
                    h308_err_cnt++;
                }
            }
            else
            {
                h308_err_cnt++;
                LOG_E("(len = %d) < 44, error data:[%s]", len, str);
                if (h308_err_cnt >= 5)
                {
                    H308_RST_ON;
                    rt_thread_mdelay(100);
                    H308_RST_OFF;
                    rt_thread_mdelay(10000);
                    h308_err_cnt = 0;
                }
            }
            rt_memset(str, 0, len);

            if (h308_rx_timout_cnt)
            {
                h308_rx_timout_cnt = 0;
            }
        }
        else if (ret == -RT_ETIMEOUT)//这个主要是考虑传感器未焊接好，或者脱落的情况，国标要求30s内反应 3*5 = 15s，符合国标
        {
            h308_rx_timout_cnt++;
            if (h308_rx_timout_cnt >= 5)
            {
                H308_RST_ON;
                rt_thread_mdelay(100);
                H308_RST_OFF;//复位一下
                h308_rx_timout_cnt = 0;
                if (H308.detection_flag != kH308Fault)
                {
                    H308.detection_flag = kH308Fault;
                    Send_Laser_Alarm_Event(kFaultEvent);
                }
            }
        }
    }
}

int BSP_H308_Init(void)
{
    rt_pin_mode(H308_PWR_PIN, PIN_MODE_OUTPUT);
    rt_pin_mode(H308_RST_PIN, PIN_MODE_OUTPUT);
    H308_RST_OFF;
    H308_PWR_ON;
    
    uart4_rx_ok_sem = rt_sem_create("uart4_rx", 0, RT_IPC_FLAG_FIFO);
    if (uart4_rx_ok_sem == RT_NULL)
    {
        LOG_E("uart4_rx_ok_sem create failed");
    }

    uart4_rx_timer = rt_timer_create("_UART4_RxTimeout", _UART4_RxTimeout, RT_NULL, 200, RT_TIMER_FLAG_PERIODIC);
    if (uart4_rx_timer == RT_NULL)
    {
        LOG_E("uart4_rx_timer create failed");
    }
    UART4_Init();
    rt_err_t ret = rt_thread_init(&h308_thread,
                                  "h308_thread",
                                  h308_thread_entry,
                                  RT_NULL,
                                  &h308_thread_stack[0],
                                  sizeof(h308_thread_stack),
                                  H308_THREAD_PRIORITY, H308_THREAD_TIMESLICE);

    if (ret == RT_EOK)
    {
        rt_thread_startup(&h308_thread);
    }
    else
    {
        LOG_E("rt_thread_init h308_thread Failed");
        return ret;
    }

    return ret;
}
INIT_PREV_EXPORT(BSP_H308_Init);

void UART4_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void UART4_IRQHandler(void)
{
    GET_INT_SP();
    rt_interrupt_enter();

    if (USART_GetITStatus(UART4, USART_IT_RXNE) != RESET)
    {
        uint8_t temp = USART_ReceiveData(UART4);
        lwrb_write(&uart4_rx_rb, &temp, 1);
        // sensor_rx_count++;
        rt_timer_start(uart4_rx_timer);
    }

    rt_interrupt_leave();
    FREE_INT_SP();
}

#ifdef TEST_ENABLE
static void TEST_H308(int argc, char **argv)
{
    if (argc == 2)
    {
        int cmd = atoi(argv[1]);
        switch (cmd)
        {
            case 0:
                H308_PWR_OFF;
                LOG_D("H308_PWR_OFF");
                break;
            case 1:
                H308_PWR_ON;
                LOG_D("H308_PWR_ON");
                break;
            case 2:
                H308_RST_OFF;
                LOG_D("H308_RST_OFF");
                break;
            case 3:
                H308_RST_ON;
                LOG_D("H308_RST_ON");
                break;

            default:
                break;
        }
    }
    else
    {
        LOG_E("TEST_H308      --use _cmd_ [mode] 0:PWR_OFF  1:PWR_ON  2:RST_OFF  3:RST_ON");
    }
}
MSH_CMD_EXPORT(TEST_H308, "TEST_H308");

static void TEST_H308_EndOfLife(void)
{
    H308.end_of_life = 1;
}
MSH_CMD_EXPORT(TEST_H308_EndOfLife, TEST_H308_EndOfLife);

#endif
