/*****************************************************************************
 * file    lib/process/ca_db.c  
 * author  YuLiang
 * version 1.0.0
 * date    29-Nov-2023
 * brief   This file provides all the database related operation functions.
 ******************************************************************************
 * Attention
 *
 * <h2><center>&copy; COPYRIGHT(c) 2023 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 LandPower 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 ------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef CFG_DEV_TYPE_LAND_CA
/* 标准 C 库头文件 */
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <execinfo.h>

/* 私有头文件 */
#include <sqlite3.h>

#include "cmd.h"
#include "fifo.h"
#include "ca_db.h"
#include "ca_collect.h"

/* Private define ------------------------------------------------------------*/
#define CA_DB_NAME_LEN 64
#define CA_DB_FIFO "CA_DB_FIFO"
#define CA_DB_BUF_LEN 2048
#define CA_DB_WR_CNT 500                // 写 500 次数据库进行一次数据库清理

#define CA_DB_LOCK pthread_mutex_lock(&ca_db_ctrl.mutex)
#define CA_DB_UNLOCK pthread_mutex_unlock(&ca_db_ctrl.mutex)

/* Private typedef -----------------------------------------------------------*/
/* fifo 数据结构 */
typedef struct
{
    uint32_t type;                      // 数据类型
    void *data;                         // 数据
} ca_db_msg_t;

/* 数据库模块属性结构体 */
typedef struct
{
    char filename[CA_DB_NAME_LEN];      // 数据库名字
    sqlite3 *db;                        // 数据库指针
    int32_t fifo_id;                    // fifo id
    int32_t cnt;                        // 写入计数
    char str[CA_DB_BUF_LEN];            // 操作 buf
    pthread_mutex_t mutex;              // 数据库读写互斥锁
} ca_db_ctrl_t;

/* Private function prototypes -----------------------------------------------*/

/* Private macro -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
ca_db_ctrl_t ca_db_ctrl;                // 数据库全局控制结构体

/* Internal functions --------------------------------------------------------*/
/* description: 显示历史环流数据
   param:
   return: CMD_XXX */
CMD(ca_db_show_data,
    ca_db_show_data_cmd,
    "show db offset <1-5000> num <1-5000>",
    "Show\n"
    "Database\n"
    "Offset\n"
    "Offset index\n"
    "Number\n"
    "Number\n")
{
    uint16_t index = strtol(argv[0], NULL, 10) - 1;
    uint16_t num = strtol(argv[1], NULL, 10);
    
    ca_db_show_data_by_index(index, num);
    
    return CMD_SUCCESS;
}

/* description: 将数据按照格式串口打印
   param: argv - 数据内容
   return: E_XXX */
int _ca_db_show_data(void *data, int argc, char **argv, char **azColName)
{
    ca_coll_dev_data_t d;
    float temp = 0;
    uint8_t i = 0;
    uint8_t k = 0;

    k = 2;
    d.vin = strtol(argv[k++], NULL, 10);
    d.vbat = strtol(argv[k++], NULL, 10);
    d.vsc = strtol(argv[k++], NULL, 10);
    d.temperature = strtol(argv[k++], NULL, 10);
    d.run_time = strtol(argv[k++], NULL, 10);
    d.energy_mode = strtol(argv[k++], NULL, 10);
    d.CSQ = strtol(argv[k++], NULL, 10);

    for(i = 0; i < CA_COLL_ADC_CH_SUM; i++)
    {
        d.elec[i] = strtol(argv[k++], NULL, 10);
    }

    d.sen_short = strtol(argv[k++], NULL, 10);

    for(i = 0; i < CA_COLL_SENSOR_SUM; i++)
    {
        d.sen_temp[i] = strtol(argv[k++], NULL, 10);
        d.sen_x[i] = strtol(argv[k++], NULL, 10);
        d.sen_y[i] = strtol(argv[k++], NULL, 10);
        d.sen_z[i] = strtol(argv[k++], NULL, 10);
    }
    
    printh("ID %s UTC %s\r\n", argv[0], argv[1]);

    printh("VCC %-0.3fv VBAT %-0.3fv VSC %-0.3fv ", d.vin / 1000.0, d.vbat / 1000.0, d.vsc / 1000.0);
    printh("Temperature %-0.1f℃ Run %ds Mode %d ", d.temperature / 10.0, d.run_time,d.energy_mode);
    printh("Short %s\r\n", d.sen_short ? "TRUE" : "FALSE");
    
    printh("Electricity(v): ");
    for(i = 0; i < CA_COLL_ADC_CH_SUM - 2; i++)
    {
        printh("%-8.3f ", d.elec[i] / 1000.0);
    }
    printh("\r\n");
    
    printh("Sensor temperature(℃): ");
    for(i = 0; i < CA_COLL_SENSOR_SUM; i++)
    {
        temp = d.sen_temp[i] / 10.0;
        printh("%-5.1f ", temp);
    }
    printh("\r\n");

    printh("Sensor x: ");
    for(i = 0; i < CA_COLL_SENSOR_SUM; i++)
    {
        printh("%-5d ", d.sen_x[i]);
    }
    printh("\r\n");

    printh("Sensor y: ");
    for(i = 0; i < CA_COLL_SENSOR_SUM; i++)
    {
        printh("%-5d ", d.sen_y[i]);
    }
    printh("\r\n");

    printh("Sensor z: ");
    for(i = 0; i < CA_COLL_SENSOR_SUM; i++)
    {
        printh("%-5d ", d.sen_z[i]);
    }
    printh("\r\n\n");
    
    return 0;
}

/* description: 删除多余的数据保留 5000 条
   param: argv - 数据内容
   return: E_XXX */
int32_t _ca_db_clean(void)
{
    ca_db_ctrl_t *log = &ca_db_ctrl;
    char *zErrMsg = NULL;

    /* 删除小于比最大 id 小 5000 的数据, 即保留 id 最大的 5000 条数据 */
    snprintf(log->str, CA_DB_BUF_LEN, "delete from cable_data where id<(select Max(id) from cable_data)-5000;");
    if (sqlite3_exec(log->db, log->str, 0, 0, &zErrMsg) != SQLITE_OK)
    {
        DBG(DBG_M_CA_DB_ERR, "SQL DELETE ERR: %s\r\n", zErrMsg);
        sqlite3_free(zErrMsg);
        return E_SYS_CALL;
    }

    return E_NONE;
}

/* description: 将历史数据写入数据库
   param: data - 数据内容
   return: E_XXX */
int32_t _ca_db_data_write(ca_coll_dev_data_t *data)
{
    struct tm *day = NULL;
    char *zErrMsg = NULL;
    char *str = ca_db_ctrl.str;
    char time_str[TIME_STR_LEN] = {0};
    uint16_t len = 0;
    time_t utc = 0;

    utc = time(NULL);
    day = localtime(&utc);
    strftime(time_str, TIME_STR_LEN, "%Y/%m/%d %H:%M:%S", day);

    CA_DB_LOCK;

    len = snprintf(str, CA_DB_BUF_LEN, "INSERT INTO cable_data VALUES (NULL, \"%s\", %d, %d, %d, %d, %d, %d, %d, ", \
        time_str, data->vin, data->vbat, data->vsc, data->temperature, data->run_time, data->energy_mode, data->CSQ);
    len += snprintf(str + len, CA_DB_BUF_LEN - len, "%d, %d, %d, %d, %d, %d, %d, %d, ", \
        data->elec[0], data->elec[1], data->elec[2], data->elec[3], data->elec[4], data->elec[5], data->elec[6], data->elec[7]);
    len += snprintf(str + len, CA_DB_BUF_LEN - len, "%d, %d, %d, %d, %d, %d, %d, %d, %d, ", \
        data->sen_short, data->sen_temp[0], data->sen_x[0], data->sen_y[0], data->sen_z[0], data->sen_temp[1], data->sen_x[1], data->sen_y[1], data->sen_z[1]);
    len += snprintf(str + len, CA_DB_BUF_LEN - len, "%d, %d, %d, %d, %d, %d, %d, %d, ", \
        data->sen_temp[2], data->sen_x[2], data->sen_y[2], data->sen_z[2], data->sen_temp[3], data->sen_x[3], data->sen_y[3], data->sen_z[3]);
    len += snprintf(str + len, CA_DB_BUF_LEN - len, "%d, %d, %d, %d, %d, %d, %d, %d, ", \
        data->sen_temp[4], data->sen_x[4], data->sen_y[4], data->sen_z[4], data->sen_temp[5], data->sen_x[5], data->sen_y[5], data->sen_z[5]);
    len += snprintf(str + len, CA_DB_BUF_LEN - len, "%d, %d, %d, %d, %d, %d, %d, %d);", \
        data->sen_temp[6], data->sen_x[6], data->sen_y[6], data->sen_z[6], data->sen_temp[7], data->sen_x[7], data->sen_y[7], data->sen_z[7]);
    
    if (sqlite3_exec(ca_db_ctrl.db, str, 0, 0, &zErrMsg ) != SQLITE_OK)
    {
        DBG(DBG_M_CA_DB_ERR, "SQL INSERT error: %s\r\n", zErrMsg);
        sqlite3_free(zErrMsg);
        return E_SYS_CALL;
    }
        
    /* 保证数据库大小不超过 5000 */
    ca_db_ctrl.cnt++;
    if (ca_db_ctrl.cnt >= CA_DB_WR_CNT)
    {
        _ca_db_clean();
        ca_db_ctrl.cnt = 0;
    }

    CA_DB_UNLOCK;

    return E_NONE;
}

/* description: 写数据库线程
   param:
   return: */
void *_ca_db_handle_write(void *arg)
{
    ca_db_msg_t *recv_msg = NULL;

    while(1)
    {
        /* 等待数据 */
        if (fifo_read(ca_db_ctrl.fifo_id, (void **)&recv_msg) != 0)
        {
            DBG(DBG_M_CA_DB_ERR, "ERROR at fifo read!\r\n");
            continue;
        }

        /* 处理数据 */
        switch(recv_msg->type)
        {
        case CA_DB_T_DATA:
            _ca_db_data_write((ca_coll_dev_data_t*)recv_msg->data);
            break;
        default:
            break;
        }

        /* 释放数据 */
        XFREE(MTYPE_CA_DB, recv_msg->data);
        fifo_push(ca_db_ctrl.fifo_id);
    }
    return NULL;
}

/* Interface functions -------------------------------------------------------*/
/* description: 数据库处理预初始化.
   param:
   return: E_XXX */
int32_t ca_db_init(void)
{
    ca_db_ctrl_t *log = &ca_db_ctrl;
    int32_t rv = 0;
    char *zErrMsg = NULL;

    /* 打开数据库 */
    snprintf(log->filename, CA_DB_NAME_LEN, CA_DB_FILE);
    rv = sqlite3_open(log->filename, &log->db);
    if (rv)
    {
        log->db = NULL;
        log_err(LOG_CA_DB, "Can't open database!");
        return E_SYS_CALL;
    }

    /* 创建历史数据表 */
    snprintf(log->str, CA_DB_BUF_LEN, \
        "CREATE TABLE IF NOT EXISTS cable_data(ID INTEGER PRIMARY KEY AUTOINCREMENT," \
        "UTC TEXT, VIN INTEGER, VBAT INTEGER, VSC INTEGER, TEMP INTEGER, RUN INTEGER, MODE INTEGER, CSQ INTEGER," \
        "ELEC1 INTEGER, ELEC2 INTEGER, ELEC3 INTEGER, ELEC4 INTEGER, ELEC5 INTEGER, ELEC6 INTEGER, ELEC7 INTEGER, ELEC8 INTEGER," \
        "SEN_S INTEGER, SEN1_T INTEGER, SEN1_X INTEGER, SEN1_Y INTEGER, SEN1_Z INTEGER, SEN2_T INTEGER, SEN2_X INTEGER, SEN2_Y INTEGER, SEN2_Z INTEGER," \
        "SEN3_T INTEGER, SEN3_X INTEGER, SEN3_Y INTEGER, SEN3_Z INTEGER, SEN4_T INTEGER, SEN4_X INTEGER, SEN4_Y INTEGER, SEN4_Z INTEGER," \
        "SEN5_T INTEGER, SEN5_X INTEGER, SEN5_Y INTEGER, SEN5_Z INTEGER, SEN6_T INTEGER, SEN6_X INTEGER, SEN6_Y INTEGER, SEN6_Z INTEGER," \
        "SEN7_T INTEGER, SEN7_X INTEGER, SEN7_Y INTEGER, SEN7_Z INTEGER, SEN8_T INTEGER, SEN8_X INTEGER, SEN8_Y INTEGER, SEN8_Z INTEGER);");
    if (sqlite3_exec(log->db, log->str, 0, 0, &zErrMsg) != SQLITE_OK)
    {
        log_err(LOG_CA_DB, "SQL create error: %s\r\n", zErrMsg);
        sqlite3_free(zErrMsg);
        return E_SYS_CALL;
    }

    /* 维持数据库大小 */
    _ca_db_clean();

    /* 添加命令行 */
    cmd_install_element(COMMON_NODE, &ca_db_show_data_cmd);
    
    return E_NONE;
}

/* description: 数据库处理初始化.
   param:
   return: E_XXX */
int32_t ca_db_init_after(void)
{
    struct sched_param param;
    pthread_attr_t attr;
    pthread_t pid;
    ca_db_ctrl_t *db = &ca_db_ctrl;
    int rv = 0;

    /* 舒适化线程锁, 保证只有数据库单线程操作 */
    pthread_mutex_init(&db->mutex, NULL);

    /* 初始化 fifo */
    db->fifo_id = fifo_create(CA_DB_FIFO, 32);
    if (db->fifo_id < 0)
    {
        log_err(LOG_CA_DB, "Open fifo " CA_DB_FIFO " error.");
        return E_ERROR;
    }

    /* 创建数据库造作线程, 配置线程RR调度，优先级25 */
    pthread_attr_init(&attr);
    param.sched_priority = 25;
    pthread_attr_setschedpolicy(&attr, SCHED_RR);
    pthread_attr_setschedparam(&attr, &param);
    pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
    rv = pthread_create(&pid, &attr, _ca_db_handle_write, NULL);
    if (rv != 0)
    {
        log_err(LOG_CA_DB, "Can't create cable db pthread %d.", rv);
        return E_SYS_CALL;
    }
    else
    {
        thread_m_add("CA_DB_THREAD", pid);
    }
    pthread_attr_destroy(&attr);

    return E_NONE;
}

/* description: 写数据库
   param: type - 操作类型
          data - 操作数据指针
   return: E_XXX */
int32_t ca_db_write(uint32_t type, void *data)
{
    ca_db_msg_t msg;
    void *pkt = NULL;

    /* 申请报文空间 */
    if (CA_DB_T_DATA == type)
    {
        pkt = XMALLOC_Q(MTYPE_CA_DB, sizeof(ca_coll_dev_data_t));
        if (!pkt)
        {
            DBG(DBG_M_CA_DB_ERR, "Malloc ERR!\r\n");
            return E_MEM;
        }

        /* 封装消息. */
        memcpy(pkt, data, sizeof(ca_coll_dev_data_t));
    }

    msg.type = type;
    msg.data = pkt;

    if (fifo_write(ca_db_ctrl.fifo_id, (void*)(&msg), sizeof(ca_db_msg_t)) != sizeof(ca_db_msg_t))
    {
        DBG(DBG_M_CA_DB_ERR, "Fifo write ERROR\r\n");
        XFREE(MTYPE_CA_DB, pkt);
        return E_ERROR;
    }

    return E_NONE;
}

/* description: 显示据库数据
   param: index - 偏移
          num - 条目
   return: */
void ca_db_show_data_by_index(uint16_t index, uint16_t num)
{
    ca_db_ctrl_t *log = &ca_db_ctrl;
    char *zErrMsg = NULL;

    CA_DB_LOCK;

    /* 按最新数据排序(id 递减), 读取偏移 index, 数量 num 条数据 */
    snprintf(log->str, CA_DB_BUF_LEN, "SELECT * FROM cable_data ORDER BY id DESC LIMIT %d OFFSET %d;", num, index);
    
    if (sqlite3_exec(log->db, log->str, _ca_db_show_data, 0, &zErrMsg) != SQLITE_OK)
    {
        DBG(DBG_M_CA_DB_ERR, "SQL show error: %s\r\n", zErrMsg);
        sqlite3_free(zErrMsg);
    }

    CA_DB_UNLOCK;
}
#endif
/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****/

