You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

445 lines
15 KiB
C

/*****************************************************************************
* 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 ****/