/*****************************************************************************
 * file    Src/common.c 
 * author  YuLiang
 * version 1.0.0
 * date    10-Jun-2022
 * brief   This file provides all the common related operation functions.
 ******************************************************************************
 * Attention
 *
 * <h2><center>&copy; COPYRIGHT(c) 2022 LandPower</center></h2>
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of WTOE nor the names of its contributors may be used to 
 *      endorse or promote products derived from this software without specific
 *      prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os.h"
#include "rtc.h"

#include "common.h"
#include "dev_config.h"
#include "cli.h"
#include "flash_log.h"

/* Private define ------------------------------------------------------------*/
/* CRC32 使用. */
#define CRC32_CHAR_BIT 8

/* 中断中打印限制长度. */
#define USART_PRINT_BUF_LEN 128

/* 公共监控任务标志位长度. */
#define COMMON_SYS_FLAG_LEN 4

/* Private typedef -----------------------------------------------------------*/
/* 公共监控任务操作参数. */
typedef struct
{
    uint8_t flag[COMMON_SYS_FLAG_LEN];
    uint32_t reset_delay;
} common_sys_data_t;

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* 公共监控任务使用. */
const osThreadAttr_t COMMON_SYS_attributes = {
    .name = "COMMON_SYS",
    .stack_size = 128 * 4,
    .priority = (osPriority_t) osPriorityAboveNormal,
};
static common_sys_data_t common_sys_data;
static RTC_DateTypeDef common_date;
static RTC_TimeTypeDef common_time;

char *energy_str[ADC_ENERGY_CNT] = 
{
    "NONE",
    "NORMAL",
    "SLEEP",
    "FORCE"
};

/* CRC使用. */
static const unsigned char bit_mask[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
static uint32_t crc32Table[256];

/* 软件看门狗标志. */
static uint32_t common_watchdog;
static uint8_t is_watchdog_hw_reset = TRUE;

/* 串口打印使用的端口. */
extern UART_HandleTypeDef *cli_uart;

/* Private function prototypes -----------------------------------------------*/
/* Internal functions --------------------------------------------------------*/
/* 判断当情是否在中断中. */
static int _is_handler_mode(void)
{
    return __get_IPSR() != 0;
}

/* 公共监控任务操作处理函数. */
static void _common_sys_handle(void)
{
    if (bit_list_tst(common_sys_data.flag, COM_SYS_SAVE_CONFIG))
    {
        bit_list_clr(common_sys_data.flag, COM_SYS_SAVE_CONFIG);
        dev_config_save();
    }
    if (bit_list_tst(common_sys_data.flag, COM_SYS_SAVE_RECORD))
    {
        bit_list_clr(common_sys_data.flag, COM_SYS_SAVE_RECORD);
        dev_record_save();
    }
    if (bit_list_tst(common_sys_data.flag, COM_SYS_SAVE_INFO))
    {
        bit_list_clr(common_sys_data.flag, COM_SYS_SAVE_INFO);
        dev_info_save();
    }
    if (bit_list_tst(common_sys_data.flag, COM_SYS_RESET))
    {
        if (0 == common_sys_data.reset_delay)
        {
            HAL_NVIC_SystemReset();
        }
        else
            common_sys_data.reset_delay--;
    }
    if (bit_list_tst(common_sys_data.flag, COM_SYS_SHUTDOWN))
    {
        bit_list_clr(common_sys_data.flag, COM_SYS_SHUTDOWN);
        system_shtudown(common_sys_data.reset_delay);
    }
}

/* 公共监控主任务循环. */
static void _common_sys_start(void *argument)
{
    uint8_t count = 0;
    
    for (;;)
    {
        osDelay(250);

        /* 每 250ms 发送一次串口数据. */
        cli_data_buf_send_hw();

        /* 每 1s 处理一次配置和重启操作. */
        if (0 == (count & 0x03))
        {
            _common_sys_handle();
            HAL_GPIO_TogglePin(LED_RUN_GPIO_Port, LED_RUN_Pin);
            common_watchdog_set(COM_WDG_COM);
        }

        if (is_watchdog_hw_reset)
        {
            is_watchdog_hw_reset = FALSE;
            HAL_GPIO_WritePin(WDG_GPIO_Port, WDG_Pin, GPIO_PIN_SET);
            osDelay(1);
            HAL_GPIO_WritePin(WDG_GPIO_Port, WDG_Pin, GPIO_PIN_RESET);
            vty_print("WDG\r\n");
        }

        count++;
    }
}

/* Interface functions -------------------------------------------------------*/
void bit_list_set(uint8_t *bit_list, uint32_t bit)
{
    bit_list[bit / 8] |= bit_mask[bit % 8];
}

int bit_list_tst(uint8_t *bit_list, uint32_t bit)
{
    return (bit_list[bit / 8] & bit_mask[bit % 8]) ? 1 : 0;
}

void bit_list_clr(uint8_t *bit_list, uint32_t bit)
{
    bit_list[bit / 8] &= ~bit_mask[bit % 8];
}

/* 在中断中发送通知. */
void notify_to_task(TaskHandle_t task)
{
    BaseType_t higher_task_woken;
 
    /* xHigherPriorityTaskWoken 必须被初始化为 pdFALSE. 如果调用vTaskNotifyGiveFromISR()
       会解除 vHandlingTask 任务的阻塞状, 并且 vHandlingTask 任务的优先级高于当前处于运行
       状的任务, 则 xHigherPriorityTaskWoken 将会自动被设置为 pdTRUE. */
    higher_task_woken = pdFALSE;
 
    /* 向一个任务发送通知, xHandlingTask 是该任务的句柄. */
    vTaskNotifyGiveFromISR(task, &higher_task_woken);
 
    /* 如果 xHigherPriorityTaskWoken 为 pdTRUE, 则强制上下文切换. 这个宏的实现取决于移植层,
       可能会调用 portEND_SWITCHING_ISR. */
    portYIELD_FROM_ISR(higher_task_woken);
}

/* 在中断中发送通知, 带通知值. */
void notify_to_task_form_val(TaskHandle_t task, uint32_t val)
{
    BaseType_t higher_task_woken;
 
    /* xHigherPriorityTaskWoken 必须被初始化为 pdFALSE. 如果调用 vTaskNotifyGiveFromISR()
       会解除 vHandlingTask 任务的阻塞状, 并且 vHandlingTask 任务的优先级高于当前处于运行
       状的任务, 则 xHigherPriorityTaskWoken 将会自动被设置为 pdTRUE. */
    higher_task_woken = pdFALSE;
 
    /* 向一个任务发送通知,xHandlingTask是该任务的句柄. */
    xTaskNotifyFromISR(task, val, eSetValueWithOverwrite, &higher_task_woken);
 
    /* 如果 xHigherPriorityTaskWoken 为 pdTRUE, 则强制上下文切换. 这个宏的实现取决于移植层,
       可能会调用 portEND_SWITCHING_ISR. */
    portYIELD_FROM_ISR(higher_task_woken);
}

/* 芯片进入睡眠模式, time 为设置的睡眠时间, 必须大于 1. */
void system_shtudown(uint32_t time)
{
    /* 设置时间必须大于 1. */
    if (0 == time)
    {
        time = 1;
    }

    vty_print_usart("SD %d\r\n", time);

    /* 设置为 RTC 唤醒. */
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, time - 1, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
    /* 关闭系统时钟, 避免被系统时钟唤醒. */
    HAL_SuspendTick();
    /* 进入 shutdown 模式. */
    HAL_PWR_EnterSTANDBYMode();
}

uint8_t crc8(uint8_t *p, uint8_t len)
{  
    unsigned char crc = 0;
    unsigned char i, crc_1byte, abyte;  
    
    while(len--) //len为总共要校验的字节数   
    {  
        crc_1byte = 0;   //设定crc_1byte初值为0 
        abyte = crc ^ *p++;
        for(i = 0; i < 8; i++)    
        {    
            if (((crc_1byte ^ abyte) & 0x01))    
            {    
                crc_1byte ^= 0x18;     
                crc_1byte >>= 1;    
                crc_1byte |= 0x80;    
            }          
            else    
            {
                crc_1byte >>= 1;
            }
            abyte >>= 1;          
        }   
        crc = crc_1byte;
    }
    
    return crc;   
}

/* 计算CRC16/MODBUS. */
uint16_t crc16(uint8_t *data, uint16_t size)
{
    uint16_t crc = 0xFFFF;
    uint8_t i = 0;
    
    while(size--)
    {
        crc = crc ^ *data++;
        for(i = 0; i < 8; i++)
        {
            if ((crc & 0x0001) > 0)
            {
                crc = crc >> 1;
                crc = crc ^ 0xa001;
            }
            else
                crc = crc >> 1;
        }
    }
    
    return crc;
}

uint32_t crc32_reflect(uint32_t value, uint8_t bits)
{
    uint32_t reflection = 0;
    
    for(uint8_t i = 0; i < bits ; i++)
    {
        if (value & (1u << i))
            reflection |= 1 << (bits - 1 - i);
    }

    return reflection;
}

/* crc32 初始化. */
void crc32_table_init(void)
{
    uint32_t int1 = 1; 
    uint32_t const value_high_bit = int1 << 31u;
    uint8_t dividend = 0;
    
    do
    {
        uint32_t remainder = 0;
        for(uint8_t mask = 1u << (CRC32_CHAR_BIT - 1u); mask; mask >>= 1)
        {
            if (dividend & mask)
                remainder ^= value_high_bit;

            if (remainder & value_high_bit)
            {
                remainder <<= 1;
                remainder ^= 0x04C11DB7u;
            }
            else
                remainder <<= 1;
        }

        crc32Table[crc32_reflect(dividend, CRC32_CHAR_BIT)] = crc32_reflect(remainder, 32);
    }
    while(++dividend);
}

uint32_t crc32(void const *buf, uint16_t byte_count)
{
    uint32_t rem = 0xFFFFFFFF;
    uint8_t const * const b_begin = (uint8_t*)buf;
    uint8_t const * const b_end = b_begin + byte_count;
    
    for(uint8_t const *p = b_begin; p < b_end; p++)
    {
        uint8_t const byte_index = *p ^ rem;
        rem >>= CRC32_CHAR_BIT;
        rem ^= crc32Table[ byte_index ];
    }

    return ~rem;
}

uint32_t crc32_update(uint32_t crc, char* buf, uint32_t len)
{
    uint32_t i = 0;

    for(i = 0; i < len; i++)
        crc = crc32Table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8);

    return crc;
}

/* description: 在终端上输出格式化字符.
 *              注意: 不能在中断中使用.
 * param: 无.
 * return: 无. */
void vty_print(char *format, ...)
{
    va_list ap;
    va_start(ap, format);

    if (IS_MONITOR_BIT_SET(dev_config.flag, DEV_FLAG_CLI))
    {
        if (IS_MONITOR_BIT_SET(system_init_flag, SYS_INIT_OS))
            cli_usart_data_buf_send(format, ap);
        else
            cli_usart_data_buf_send_without_os(format, ap);
    }

    va_end(ap);
}

/* 打印错误信息vty. */
void vty_print_error(char const *func, int line, int rv)
{
    vty_print("%s(%d): Error generation %d.\r\n", func, line, rv);
}

/* 通过 usart1 输出格式化字符. 原地等待发送, 与操作系统无关,
 * 可以在中断中使用, 尽量不要在中断中使用, 太耗时间. */
void vty_print_usart(char *format, ...)
{
    static char buf[USART_PRINT_BUF_LEN];

    if (IS_MONITOR_BIT_SET(dev_config.flag, DEV_FLAG_CLI))
    {
        va_list ap;
        va_start(ap, format);

        /* 中断只能在串口输出. */
        if (_is_handler_mode() != 0)
        {
            taskDISABLE_INTERRUPTS();

            if (vsnprintf(buf, USART_PRINT_BUF_LEN, format, ap) > 0)
            {
                HAL_UART_AbortTransmit(cli_uart);
                HAL_GPIO_WritePin(RS485_DBG_EN_GPIO_Port, RS485_DBG_EN_Pin, GPIO_PIN_SET);
                HAL_UART_Transmit(cli_uart, (uint8_t*)buf, strlen(buf), 1000);
                HAL_GPIO_WritePin(RS485_DBG_EN_GPIO_Port, RS485_DBG_EN_Pin, GPIO_PIN_RESET);
            }

            taskENABLE_INTERRUPTS();
        }
        else
        {
            while(HAL_UART_STATE_BUSY_TX == cli_uart->gState)
            {
                continue;
            }

            if (vsnprintf(buf, USART_PRINT_BUF_LEN, format, ap) > 0)
            {
                HAL_GPIO_WritePin(RS485_DBG_EN_GPIO_Port, RS485_DBG_EN_Pin, GPIO_PIN_SET);
                HAL_UART_Transmit(cli_uart, (uint8_t*)buf, strlen(buf), 1000);
                HAL_GPIO_WritePin(RS485_DBG_EN_GPIO_Port, RS485_DBG_EN_Pin, GPIO_PIN_RESET);
            }
        }

        va_end(ap);
    }
}

/* 打印错误信息串口, 可以在中断中使用. */
void vty_print_error_usart(char const *func, int line, int rv)
{
    vty_print_usart("Error generation %s(%d) is %d.\r\n", func, line, rv);
}

/* buf 16进制打印 */
void buf_print(uint8_t *buf, int32_t len)
{
    int32_t i = 0;
    
    for(i = 0; i < len;)
    {
        vty_print("%02x ", buf[i++]);
        if(0 == i % 32)
        {
            vty_print("\r\n");
        }
    }
    if(i % 32 != 0)
    {
        vty_print("\r\n");
    }

    return;
}

/* buf 16进制打印 */
void buf_print_division(uint8_t *buf, int32_t len, uint16_t division)
{
    int32_t i = 0;
    
    for(i = 0; i < len;)
    {
        vty_print("%02x ", buf[i++]);
        if(0 == i % division)
        {
            vty_print("\r\n");
        }
    }
    if (i % division != 0)
    {
        vty_print("\r\n");
    }

    return;
}

/* 公共监控任务初始化. */
void common_sys_init_os(void)
{
    osThreadNew(_common_sys_start, NULL, &COMMON_SYS_attributes);
}

/* 公共监控任务操作接口. */
void common_sys_set(uint8_t flag, void *arg)
{
    bit_list_set(common_sys_data.flag, flag);

    switch(flag)
    {
    case COM_SYS_RESET:
        MONITOR_BITMAP_SET(system_init_flag, SYS_INIT_RESET);
        common_sys_data.reset_delay = *(uint32_t*)arg;
        break;
    case COM_SYS_SHUTDOWN:
        if (!IS_MONITOR_BIT_SET(system_init_flag, SYS_INIT_SHUTDOWN))
        {
            MONITOR_BITMAP_SET(system_init_flag, SYS_INIT_SHUTDOWN);
            common_sys_data.reset_delay = *(uint32_t*)arg;
        }
        break;
    default:
        break;
    }
}

/* 设置看门狗标记. */
void common_watchdog_set(uint32_t id)
{
    common_watchdog |= id;
}

/* 清除看门狗标记. */
void common_watchdog_clr(void)
{
    common_watchdog = 0;
}

/* 查询看门狗标记.0-没有置位,1-置位. */
int32_t common_watchdog_tst(uint32_t id)
{
    return common_watchdog & id;
}

/* 查询看门狗标记.0-没有置位,1-置位. */
int32_t common_watchdog_tst_all(void)
{
    uint32_t temp = COM_WDG_COM | COM_WDG_ADC | COM_WDG_CLI | COM_WDG_WIR;

    return ((common_watchdog & temp) == temp);
}

/* 通知程序喂硬件看门狗. */
void common_watchdog_hw_refresh(void)
{
    is_watchdog_hw_reset = TRUE;
}

/* 计算绝对值. */
uint32_t abs_cal(int32_t a, int32_t b)
{
    return (a > b ? a - b : b - a);
}

/* 计算绝对值. */
uint32_t abs_cal_u(uint32_t a, uint32_t b)
{
    return (a > b ? a - b : b - a);
}

void rtc_time_set(uint32_t utc)
{
    utc += 8 * 3600;
    struct tm *day = localtime(&utc);
    time_t temp = 0;

    HAL_RTC_GetTime(&hrtc, &time_structure, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &date_structure, RTC_FORMAT_BIN);
    tm_structure.tm_year = date_structure.Year + 100;
    tm_structure.tm_mon = date_structure.Month - 1;
    tm_structure.tm_mday = date_structure.Date;
    tm_structure.tm_hour = time_structure.Hours;
    tm_structure.tm_min = time_structure.Minutes;
    tm_structure.tm_sec = time_structure.Seconds;
    
    temp = mktime(&tm_structure);
    if (abs_cal_u(temp,  utc) >= 10)
    {
        vty_print("%ld %d\r\n", temp, utc);
        memset(&time_structure,0,sizeof(RTC_TimeTypeDef));
        memset(&date_structure,0,sizeof(RTC_DateTypeDef));

        time_structure.Hours = day->tm_hour;
        time_structure.Minutes = day->tm_min;
        time_structure.Seconds = day->tm_sec;
        HAL_RTC_SetTime(&hrtc, &time_structure, RTC_FORMAT_BIN);
        
        if (day->tm_year >= 100)
        {
            date_structure.Year = day->tm_year - 100;
        }
        date_structure.Month = day->tm_mon + 1;
        date_structure.Date = day->tm_mday;
        HAL_RTC_SetDate(&hrtc, &date_structure, RTC_FORMAT_BIN);
    }
    vty_print("%d/%d/%d %d:%d:%d\r\n", date_structure.Year, date_structure.Month, date_structure.Date,
        time_structure.Hours, time_structure.Minutes, time_structure.Seconds);
}

void restart_ontime(void)
{
    uint32_t sleep_time = 65;

    HAL_RTC_GetTime(&hrtc, &common_time, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &common_date, RTC_FORMAT_BIN);
    
    if (0 == dev_config.csg_reset_time[0])
    {
        if (common_time.Hours == dev_config.csg_reset_time[1]
            && common_time.Minutes == dev_config.csg_reset_time[2])
        {
            flash_log_write(FLASH_LOG_TYPE_WARNING, "Ontime restart %ds\r\n", sleep_time);

            common_sys_set(COM_SYS_SHUTDOWN, (void*)&sleep_time);
        }
    }
    else
    {
        if (common_date.Date == dev_config.csg_reset_time[0]
            && common_time.Hours == dev_config.csg_reset_time[1]
            && common_time.Minutes == dev_config.csg_reset_time[2])
        {
            flash_log_write(FLASH_LOG_TYPE_WARNING, "Ontime restart %ds\r\n", sleep_time);
            common_sys_set(COM_SYS_SHUTDOWN, (void*)&sleep_time);
        }
    }
}

#if LD_DEBUG
/* debug 模块使能 bitmap. */
static uint8_t dbg_module[DBG_BYTE_LEN];

/* 开指定模块的 debug. */
static void _dbg_on(uint8_t module)
{
    bit_list_set(dbg_module, module);
}

/* 关指定模块的debug */
static void _dbg_off(uint8_t module)
{
    bit_list_clr(dbg_module, module);
}

/* 关所有模块的debug */
static void _dbg_all_off(void)
{
    memset(dbg_module, 0, DBG_BYTE_LEN);
}

/* 获取当前模块debug状态 */
int32_t dbg_stat_get(uint8_t module)
{
    return bit_list_tst(dbg_module, module);
}

/* debug模块函数分发,flag=1代表是否是开启lwip debug. */
int32_t dbg_cmd_hander(uint8_t cmd, uint8_t data)
{
    switch(cmd)
    {
        case DBG_CMD_ON:
            _dbg_on(data);
            break;
        case DBG_CMD_OFF:
            _dbg_off(data);
            break;
        case DBG_CMD_OFF_ALL:
            _dbg_all_off();
            break;
        default:
            break;
    }

    return HAL_OK;
}
#endif

#if BYTE_ORDER == LITTLE_ENDIAN
#if !defined(iap_htons)
/**
 * Convert an u16_t from host- to network byte order.
 *
 * @param n u16_t in host byte order
 * @return n in network byte order
 */
uint16_t iap_htons(uint16_t n)
{
    return (uint16_t)PP_HTONS(n);
}
#endif /* iap_htons */

#if !defined(iap_htonl)
/**
 * Convert an u32_t from host- to network byte order.
 *
 * @param n u32_t in host byte order
 * @return n in network byte order
 */
uint32_t iap_htonl(uint32_t n)
{
    return (uint32_t)PP_HTONL(n);
}
#endif /* iap_htonl */
#endif /* BYTE_ORDER == LITTLE_ENDIAN */
/******************* (C) COPYRIGHT LandPower ***** END OF FILE ****************/
