/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2025-10-20 Administrator the first version */ #include #include #include #include #include "DB_SQLite.h" #include "sqlite3.h" #include #define DB_NAME "/SC828.db" int db_HelperInit; rt_mq_t db_mq = RT_NULL; // sqlite3 **db; // 消息队列对象与缓冲区(静态分配) //static struct rt_mq db_mq; //static char mq_pool[DB_QUEUE_SIZE * sizeof(struct db_command)]; // 全局数据库连接与状态 static sqlite3 *g_db = RT_NULL; static rt_bool_t db_enabled = RT_FALSE; // 是否允许数据库操作 static rt_mutex_t state_mutex = RT_NULL; // 状态访问锁 // 函数前向声明 static void return_timeout(struct db_command *cmd); static void return_disabled(struct db_command *cmd); static void handle_db_command(struct db_command *cmd); static int callback(void *result_str, int argc, char **argv, char **azColName); static const char *sql_upgrade_workorder_steps = "CREATE TABLE WorkorderSteps (WorkOrder VARCHAR,DYELOT VARCHAR,ProgramID VARCHAR,Program VARCHAR,ReDye INT DEFAULT (0)," "Mode VARCHAR,Step INT,StepID VARCHAR,StepName VARCHAR,ParameterName VARCHAR,P1 DOUBLE,P2 DOUBLE,P3 DOUBLE," "P4 DOUBLE,P5 DOUBLE,P6 INT,P7 INT,P8 INT,P9 INT,P10 INT,Remark VARCHAR,StepTime INT,StepID_S1 VARCHAR," "StepID_S2 VARCHAR,StepID_S3 VARCHAR,StepName_S1 VARCHAR,StepName_S2 VARCHAR,StepName_S3 VARCHAR,P1_S1 DOUBLE," "P1_S2 DOUBLE,P1_S3 DOUBLE,P2_S1 DOUBLE,P2_S2 DOUBLE,P2_S3 DOUBLE,P3_S1 DOUBLE,P3_S2 DOUBLE,P3_S3 DOUBLE," "P4_S1 DOUBLE,P4_S2 DOUBLE,P4_S3 DOUBLE,P5_S1 DOUBLE,P5_S2 DOUBLE,P5_S3 DOUBLE); "; static const char *sql_upgrade_workorder_set = "CREATE TABLE WorkOrderSet (WorkOrder VARCHAR,ReDye INT DEFAULT (0),PumpSpeed INT,Blower INT,Swing INT,ClothWheel INT,Nozzle INT); "; static const char *sql_upgrade_workorder = "CREATE TABLE WorkOrder (WorkOrder VARCHAR,Dyelot VARCHAR,ReDyeINT DEFAULT (0),ProgramNameVARCHAR,StartTime DATETIME,EndTime DATETIME," "Time TEXT,lock INT,State INT,ProgramID VARCHAR,Machines VARCHAR,color VARCHAR,ColorNumber VARCHAR,Client VARCHAR,ClothWeight VARCHAR," "ClothSpecies VARCHAR,BathRatio VARCHAR,Total VARCHAR,USER VARCHAR,ColorName VARCHAR,Remark TEXT); "; static const char *sql_upgrade_run_table = "CREATE TABLE RUN (WorkOrder VARCHAR,DYELOT VARCHAR,ProgramID VARCHAR,Program VARCHAR,ReDye INT DEFAULT (0)," "Mode VARCHAR,Step INT,StepID VARCHAR,StepName VARCHAR,ParameterName VARCHAR,P1 DOUBLE,P2 DOUBLE,P3 DOUBLE," "P4 DOUBLE,P5 DOUBLE,P6 INT,P7 INT,P8 INT,P9 INT,P10 INT,Remark VARCHAR,StepTime INT,StepID_S1 VARCHAR," "StepID_S2 VARCHAR,StepID_S3 VARCHAR,StepName_S1 VARCHAR,StepName_S2 VARCHAR,StepName_S3 VARCHAR,P1_S1 DOUBLE," "P1_S2 DOUBLE,P1_S3 DOUBLE,P2_S1 DOUBLE,P2_S2 DOUBLE,P2_S3 DOUBLE,P3_S1 DOUBLE,P3_S2 DOUBLE,P3_S3 DOUBLE," "P4_S1 DOUBLE,P4_S2 DOUBLE,P4_S3 DOUBLE,P5_S1 DOUBLE,P5_S2 DOUBLE,P5_S3 DOUBLE); "; static const char *sql_upgrade_dyelot_table = "CREATE TABLE Dyelot (WorkOrder VARCHAR,Dyelot VARCHAR,ReDye INT,Machine VARCHAR,Step INT,Tank INT,State INT,ProductCode VARCHAR," "ProductName VARCHAR,ProductType INT,Grams FLOAT,Amount FLOAT,CALL_TIME VARCHAR,DispenseEndTime VARCHAR,Type INT); "; static const char *sql_upgrade_iolog_table = "CREATE TABLE IOLog (ID varchar,IOName varchar,type varchar,Value DOUBLE DEFAULT (0),DIO BOOLEAN,AIO INTEGER DEFAULT (0),PLC varchar); "; static const char *sql_upgrade_chart_table = "CREATE TABLE Chart (WorkOrder varchar,DYELOT varchar,ReDye INTEGER,Name varchar,Time varchar,MTT DOUBLE DEFAULT (0),MTL DOUBLE DEFAULT (0)," "STTA DOUBLE DEFAULT (0),STLA DOUBLE DEFAULT (0),STTB DOUBLE DEFAULT (0),STLB DOUBLE DEFAULT (0),STTC DOUBLE DEFAULT (0)," "STLC DOUBLE DEFAULT (0),MTH DOUBLE DEFAULT (0),MST DOUBLE DEFAULT (0),MSL DOUBLE DEFAULT (0),MUT DOUBLE DEFAULT (0)); "; // ------------------------ // 内部工具函数 // ------------------------ /** * @brief 返回超时错误(排队时间过长) */ static void return_timeout(struct db_command *cmd) { cmd->result_code = -RT_ETIMEOUT; rt_strncpy(cmd->result, "Cmd timeout: waited too long in queue", MAX_RESULT_LEN - 1); if (cmd->reply_sem) { rt_sem_release(cmd->reply_sem); } } /** * @brief 返回“数据库已禁用”错误 */ static void return_disabled(struct db_command *cmd) { cmd->result_code = -RT_ENODEV; rt_strncpy(cmd->result, "Error: Database is disabled", MAX_RESULT_LEN - 1); if (cmd->reply_sem) { rt_sem_release(cmd->reply_sem); } } /** * @brief SELECT 查询回调函数 */ static int callback(void *result_str, int argc, char **argv, char **azColName) { char *str = (char *)result_str; int len = rt_strlen(str); for (int i = 0; i < argc; i++) { rt_snprintf(str + len, MAX_RESULT_LEN - len, "%s=%s|", azColName[i], argv[i] ? argv[i] : "NULL"); len = rt_strlen(str); } return 0; } /** * @brief 执行具体 SQL 操作 */ static void handle_db_command(struct db_command *cmd) { char *err_msg = 0; int rc; switch (cmd->type) { case DB_CMD_INSERT: case DB_CMD_DELETE: case DB_CMD_UPDATE: case DB_CMD_EXEC: rc = sqlite3_exec(g_db, cmd->sql, 0, 0, &err_msg); if (rc != SQLITE_OK) { cmd->result_code = rc; rt_strncpy(cmd->result, err_msg, MAX_RESULT_LEN - 1); sqlite3_free(err_msg); } else { cmd->result_code = 0; rt_strncpy(cmd->result, "OK", MAX_RESULT_LEN - 1); } break; case DB_CMD_SELECT: cmd->result[0] = '\0'; rc = sqlite3_exec(g_db, cmd->sql, callback, cmd->result, &err_msg); if (rc != SQLITE_OK) { cmd->result_code = rc; rt_strncpy(cmd->result, err_msg, MAX_RESULT_LEN - 1); sqlite3_free(err_msg); } else { cmd->result_code = 0; if (cmd->result[0] == '\0') rt_strncpy(cmd->result, "No data", MAX_RESULT_LEN - 1); } break; case DB_CMD_EXIT: rt_strncpy(cmd->result, "Exit", MAX_RESULT_LEN - 1); cmd->result_code = 0; break; default: cmd->result_code = -1; rt_strncpy(cmd->result, "Invalid command", MAX_RESULT_LEN - 1); break; } } // ------------------------ // 外部接口实现 // ------------------------ /** * @brief 外部调用:立即关闭数据库并禁用所有操作 */ void close_db_immediately(void) { if (!state_mutex) return; rt_mutex_take(state_mutex, RT_WAITING_FOREVER); { if (g_db) { sqlite3_close(g_db); g_db = RT_NULL; } db_enabled = RT_FALSE; } rt_mutex_release(state_mutex); rt_kprintf("Database disabled.\n"); } void db_sqlite_init_full(void) { if (access(DB_NAME, F_OK) == 0) { rt_kprintf("DB open\n"); if (db_connect(DB_NAME) == RT_EOK){ if(db_table_is_exist("WorkorderSteps")<=0){ if(db_create_database(sql_upgrade_workorder_steps)==0) {rt_kprintf("WorkorderSteps Created successfully \n");}else{rt_kprintf("WorkorderSteps Creation failed \n");} } if(db_table_is_exist("WorkOrderSet")<=0){ if(db_create_database(sql_upgrade_workorder_set)==0) {rt_kprintf("WorkOrderSet Created successfully \n");}else{rt_kprintf("WorkOrderSet Creation failed \n");} } if(db_table_is_exist("WorkOrder")<=0){ if(db_create_database(sql_upgrade_workorder)==0) {rt_kprintf("WorkOrder Created successfully \n");}else{rt_kprintf("WorkOrder Creation failed \n");} } if(db_table_is_exist("RUN")<=0){ if(db_create_database(sql_upgrade_run_table)==0) {rt_kprintf("RUN Created successfully \n");}else{rt_kprintf("RUN Creation failed \n");} } if(db_table_is_exist("Dyelot")<=0){ if(db_create_database(sql_upgrade_dyelot_table)==0) {rt_kprintf("Dyelot Created successfully \n");}else{rt_kprintf("Dyelot Creation failed \n");} } if(db_table_is_exist("IOLog")<=0){ if(db_create_database(sql_upgrade_iolog_table)==0) {rt_kprintf("IOLog Created successfully \n");}else{rt_kprintf("IOLog Creation failed \n");} } if(db_table_is_exist("Chart")<=0){ if(db_create_database(sql_upgrade_chart_table)==0) {rt_kprintf("Chart Created successfully \n");}else{rt_kprintf("Chart Creation failed \n");} } }else{ rt_kprintf("DB ok\n"); db_connect(DB_NAME); } }else{ rt_kprintf("DB open failed \n"); sqlite3_open(DB_NAME, db); //创建表 db_create_database(sql_upgrade_workorder_steps); db_create_database(sql_upgrade_workorder_set); db_create_database(sql_upgrade_workorder); db_create_database(sql_upgrade_run_table); db_create_database(sql_upgrade_dyelot_table); db_create_database(sql_upgrade_iolog_table); db_create_database(sql_upgrade_chart_table); } } /** * @brief 数据库处理线程入口函数 */ static void db_sqlite(void *parameter) { struct db_command cmd; rt_tick_t max_wait_ticks = rt_tick_from_millisecond(DB_MAX_WAITING_MS); // 检查并初始化数据库文件 db_sqlite_init_full(); // ✅ 使用 rt_mq_create 动态创建消息队列 db_mq = rt_mq_create("db_mq", sizeof(struct db_command), DB_QUEUE_SIZE, RT_IPC_FLAG_FIFO); if (db_mq == RT_NULL) { rt_kprintf("Failed to create message queue 'db_mq'!\n"); return; } // 创建状态锁 state_mutex = rt_mutex_create("db_lock", RT_IPC_FLAG_FIFO); if (!state_mutex) { rt_kprintf("Failed to create db_lock mutex!\n"); rt_mq_delete(db_mq); // 创建失败要清理 db_mq = RT_NULL; return; } // 默认启用数据库 db_enabled = RT_TRUE; rt_kprintf("Database handler started. Path: %s\n", DB_NAME); while (1) { // 接收命令 if (rt_mq_recv(db_mq, &cmd, sizeof(struct db_command), RT_WAITING_FOREVER) != RT_EOK) continue; // 1. 检查排队超时 rt_tick_t now = rt_tick_get(); if ((now - cmd.send_tick) > max_wait_ticks) { int wait_ms = (now - cmd.send_tick) * 1000 / RT_TICK_PER_SECOND; rt_kprintf("DROP cmd[type=%d]: waited %dms > limit(%dms)\n", cmd.type, wait_ms, DB_MAX_WAITING_MS); return_timeout(&cmd); continue; } // 2. 获取当前启用状态 rt_bool_t current_enabled; rt_mutex_take(state_mutex, RT_WAITING_FOREVER); current_enabled = db_enabled; rt_mutex_release(state_mutex); // 3. 若已禁用 → 返回错误 if (!current_enabled) { rt_kprintf("Rejected cmd[type=%d]: database is disabled\n", cmd.type); return_disabled(&cmd); if (cmd.type == DB_CMD_EXIT) break; continue; } // 4. 打开数据库(如果未打开) rt_mutex_take(state_mutex, RT_WAITING_FOREVER); { if (!g_db && current_enabled) { int rc = sqlite3_open(DB_NAME, &g_db); if (rc != SQLITE_OK) { rt_kprintf("Failed to open DB at %s: %s\n", DB_NAME, sqlite3_errmsg(g_db)); sqlite3_close(g_db); g_db = RT_NULL; rt_mutex_release(state_mutex); return_disabled(&cmd); continue; } else { rt_kprintf("Opened database: %s\n", DB_NAME); } } } rt_mutex_release(state_mutex); // 5. 执行命令 handle_db_command(&cmd); // 6. 返回结果 if (cmd.reply_sem) { rt_sem_release(cmd.reply_sem); } // 7. 退出命令 if (cmd.type == DB_CMD_EXIT) break; } // ✅ 清理资源 if (g_db) { sqlite3_close(g_db); g_db = RT_NULL; } db_enabled = RT_FALSE; if (state_mutex) { rt_mutex_delete(state_mutex); state_mutex = RT_NULL; } // ✅ 删除动态消息队列 if (db_mq) { rt_mq_delete(db_mq); db_mq = RT_NULL; } rt_kprintf("Database handler exited.\n"); } /** * @brief 向数据库发送命令并等待响应 * * @param type 命令类型(INSERT/SELECT等) * @param sql SQL 语句字符串 * @param timeout_ms 超时时间(毫秒),0 表示不等待 * @return RT_EOK 成功,RT_ETIMEOUT 超时,RT_ERROR 失败 */ rt_err_t db_send_command(enum db_cmd_type type, const char* sql, rt_int32_t timeout_ms) { struct db_command cmd; rt_sem_t sem = RT_NULL; rt_err_t result = RT_EOK; // 1. 创建临时信号量用于同步 sem = rt_sem_create("db_rep", 0, RT_IPC_FLAG_FIFO); if (sem == RT_NULL) { rt_kprintf("Failed to create reply semaphore!\n"); return -RT_ENOMEM; } // 2. 构造命令 rt_memset(&cmd, 0, sizeof(cmd)); cmd.type = type; rt_strncpy(cmd.sql, sql, MAX_SQL_LEN - 1); cmd.reply_sem = sem; cmd.send_tick = rt_tick_get(); // 3. 发送命令到消息队列 if (rt_mq_send(db_mq, &cmd, sizeof(cmd)) != RT_EOK) { rt_kprintf("Failed to send command to db_mq!\n"); rt_sem_delete(sem); return -RT_ERROR; } // 4. 等待数据库线程处理完成(超时保护) if (timeout_ms > 0) { rt_int32_t tick = rt_tick_from_millisecond(timeout_ms); result = rt_sem_take(sem, tick); } else { result = rt_sem_take(sem, RT_WAITING_FOREVER); // 永久等待(慎用) } // 5. 检查结果 if (result == RT_EOK) { rt_kprintf("SQL executed: code=%d\n", cmd.result_code); if (type == DB_CMD_SELECT || strlen(cmd.result) > 0) { rt_kprintf("Result: %s\n", cmd.result); } } else if (result == -RT_ETIMEOUT) { rt_kprintf("Database response timeout!\n"); } else { rt_kprintf("Wait failed: %d\n", result); } // 6. 删除信号量 rt_sem_delete(sem); return result; } /* 线程 */ void thread_DB_SQLite(void) { /* 初始化线程 1,名称是 thread1,入口是 thread1_entry*/ rt_thread_t tid; tid = rt_thread_create("db_sqlite", db_sqlite, RT_NULL, 1024*16, 3, 15); if (tid != RT_NULL) { rt_thread_startup(tid); } else { rt_kprintf("Failed to create sqlite_sys thread!\n"); } // return 0; }