#include "DbStore.h" #include "../protocol/Message.h" #include "sqlite3.h" #include #include #include #include namespace ccu { namespace { std::string toHex(const std::vector& data) { static const char* hex = "0123456789ABCDEF"; std::string s; s.reserve(data.size() * 2); for (uint8_t b : data) { s += hex[(b >> 4) & 0xF]; s += hex[b & 0xF]; } return s; } } // namespace DbStore::DbStore(const std::string& dbPath) : m_dbPath(dbPath) {} DbStore::~DbStore() { close(); } bool DbStore::exec(const char* sql) { char* err = nullptr; int rc = sqlite3_exec(m_db, sql, nullptr, nullptr, &err); if (rc != SQLITE_OK) { m_lastError = err ? err : "sqlite error"; sqlite3_free(err); return false; } return true; } bool DbStore::open() { if (m_db) return true; if (sqlite3_open(m_dbPath.c_str(), &m_db) != SQLITE_OK) { m_lastError = sqlite3_errmsg(m_db); sqlite3_close(m_db); m_db = nullptr; return false; } sqlite3_busy_timeout(m_db, 2000); exec("PRAGMA journal_mode=WAL;"); exec("PRAGMA synchronous=NORMAL;"); // comm_log:收发全部原始帧 if (!exec( "CREATE TABLE IF NOT EXISTS comm_log (" " id INTEGER PRIMARY KEY AUTOINCREMENT," " time REAL NOT NULL," " link TEXT NOT NULL," " direction INTEGER NOT NULL," // 0=收 1=发 " msg_id INTEGER NOT NULL," " msg_name TEXT NOT NULL," " length INTEGER NOT NULL," " checksum_ok INTEGER NOT NULL," " hex TEXT NOT NULL" ");")) return false; exec("CREATE INDEX IF NOT EXISTS idx_comm_log_time ON comm_log(time);"); // bcu_node:锂电池 BCU 节点解析数据(每收到一帧 BMS 报文落一行, // 记录该节点合并后的最新状态;BMS/CAN 协议字段固定故用结构化表) if (!exec( "CREATE TABLE IF NOT EXISTS bcu_node (" " id INTEGER PRIMARY KEY AUTOINCREMENT," " time REAL NOT NULL," " addr INTEGER NOT NULL," // BCU 节点地址 1~54 " func INTEGER NOT NULL," // 触发本行的报文功能码 " total_voltage REAL," // 累加电压 V " current REAL," // 回路电流 A(放电为正) " soc REAL," // SOC % " alarm_code INTEGER," // 告警码(原始值) " self_check INTEGER," // 自检状态(原始值) " max_cell_voltage REAL," // 最高单体电压 V " max_cell_voltage_no INTEGER," " min_cell_voltage REAL," // 最低单体电压 V " min_cell_voltage_no INTEGER," " avg_cell_voltage REAL," // 单体平均电压 V " max_cell_temp REAL," // 最高单体温度 ℃ " max_cell_temp_no INTEGER," " min_cell_temp REAL," // 最低单体温度 ℃ " min_cell_temp_no INTEGER," " avg_cell_temp REAL," // 单体平均温度 ℃ " pos_relay INTEGER," // 正极继电器 1闭合/0断开 " neg_relay INTEGER," // 负极继电器 1闭合/0断开 " pos_insulation REAL," // 正极绝缘阻抗 kΩ " neg_insulation REAL," // 负极绝缘阻抗 kΩ " port_voltage REAL," // 正负极端口电压 V " pos_relay_outer_voltage REAL," // 正极继电器外侧电压 V " alarm_bits TEXT," // 告警位 byte0~5 十六进制 " alarm_extra_byte6 INTEGER," " alarm_extra_byte7 INTEGER," " rx_mask INTEGER," // 收到过的功能码位图 " last_rx_time REAL" // 节点最近收帧时刻(MOOSTime) ");")) return false; exec("CREATE INDEX IF NOT EXISTS idx_bcu_node_time ON bcu_node(time);"); exec("CREATE INDEX IF NOT EXISTS idx_bcu_node_addr ON bcu_node(addr);"); return prepareInsert() && prepareBcuInsert(); } bool DbStore::prepareInsert() { const char* sql = "INSERT INTO comm_log (time, link, direction, msg_id, msg_name, length, checksum_ok, hex) " "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8);"; if (sqlite3_prepare_v2(m_db, sql, -1, &m_stmtInsert, nullptr) != SQLITE_OK) { m_lastError = sqlite3_errmsg(m_db); return false; } return true; } bool DbStore::prepareBcuInsert() { const char* sql = "INSERT INTO bcu_node (time, addr, func, total_voltage, current, soc, " "alarm_code, self_check, " "max_cell_voltage, max_cell_voltage_no, min_cell_voltage, min_cell_voltage_no, avg_cell_voltage, " "max_cell_temp, max_cell_temp_no, min_cell_temp, min_cell_temp_no, avg_cell_temp, " "pos_relay, neg_relay, pos_insulation, neg_insulation, port_voltage, pos_relay_outer_voltage, " "alarm_bits, alarm_extra_byte6, alarm_extra_byte7, rx_mask, last_rx_time) " "VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13,?14,?15,?16,?17,?18," "?19,?20,?21,?22,?23,?24,?25,?26,?27,?28,?29);"; if (sqlite3_prepare_v2(m_db, sql, -1, &m_stmtBcuInsert, nullptr) != SQLITE_OK) { m_lastError = sqlite3_errmsg(m_db); return false; } return true; } void DbStore::close() { std::lock_guard lock(m_mutex); if (m_stmtInsert) { sqlite3_finalize(m_stmtInsert); m_stmtInsert = nullptr; } if (m_stmtBcuInsert) { sqlite3_finalize(m_stmtBcuInsert); m_stmtBcuInsert = nullptr; } if (m_db) { sqlite3_close(m_db); m_db = nullptr; } } void DbStore::onRawFrame(int direction, uint16_t msgId, const std::string& link, const std::vector& data, bool checksumOk) { std::lock_guard lock(m_mutex); if (!m_db || !m_stmtInsert) return; // 消息名:尝试从链路名无法直接获取,这里用 msgId 的十六进制作为名称的一部分 char name[32]; std::snprintf(name, sizeof(name), "msg_0x%04X", msgId); std::string hex = toHex(data); sqlite3_reset(m_stmtInsert); sqlite3_clear_bindings(m_stmtInsert); // 时间:使用当前 unix 时间(秒),供网页展示 sqlite3_bind_double(m_stmtInsert, 1, static_cast(::time(nullptr))); sqlite3_bind_text(m_stmtInsert, 2, link.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_int(m_stmtInsert, 3, direction); sqlite3_bind_int(m_stmtInsert, 4, msgId); sqlite3_bind_text(m_stmtInsert, 5, name, -1, SQLITE_TRANSIENT); sqlite3_bind_int(m_stmtInsert, 6, static_cast(data.size())); sqlite3_bind_int(m_stmtInsert, 7, checksumOk ? 1 : 0); sqlite3_bind_text(m_stmtInsert, 8, hex.c_str(), -1, SQLITE_TRANSIENT); int rc = sqlite3_step(m_stmtInsert); if (rc != SQLITE_DONE) { m_lastError = sqlite3_errmsg(m_db); } } void DbStore::insertBcuNode(uint8_t addr, uint8_t func, const BcuNodeStatus& v) { std::lock_guard lock(m_mutex); if (!m_db || !m_stmtBcuInsert) return; char bits[16]; std::snprintf(bits, sizeof(bits), "%02X%02X%02X%02X%02X%02X", v.alarmBits[0], v.alarmBits[1], v.alarmBits[2], v.alarmBits[3], v.alarmBits[4], v.alarmBits[5]); sqlite3_reset(m_stmtBcuInsert); sqlite3_clear_bindings(m_stmtBcuInsert); // 时间:与 comm_log 一致用 unix 时间(秒) int i = 1; sqlite3_bind_double(m_stmtBcuInsert, i++, static_cast(::time(nullptr))); sqlite3_bind_int(m_stmtBcuInsert, i++, addr); sqlite3_bind_int(m_stmtBcuInsert, i++, func); sqlite3_bind_double(m_stmtBcuInsert, i++, v.totalVoltage); sqlite3_bind_double(m_stmtBcuInsert, i++, v.current); sqlite3_bind_double(m_stmtBcuInsert, i++, v.soc); sqlite3_bind_int(m_stmtBcuInsert, i++, v.alarmCode); sqlite3_bind_int(m_stmtBcuInsert, i++, v.selfCheck); sqlite3_bind_double(m_stmtBcuInsert, i++, v.maxCellVoltage); sqlite3_bind_int(m_stmtBcuInsert, i++, v.maxCellVoltageNo); sqlite3_bind_double(m_stmtBcuInsert, i++, v.minCellVoltage); sqlite3_bind_int(m_stmtBcuInsert, i++, v.minCellVoltageNo); sqlite3_bind_double(m_stmtBcuInsert, i++, v.avgCellVoltage); sqlite3_bind_double(m_stmtBcuInsert, i++, v.maxCellTemp); sqlite3_bind_int(m_stmtBcuInsert, i++, v.maxCellTempNo); sqlite3_bind_double(m_stmtBcuInsert, i++, v.minCellTemp); sqlite3_bind_int(m_stmtBcuInsert, i++, v.minCellTempNo); sqlite3_bind_double(m_stmtBcuInsert, i++, v.avgCellTemp); sqlite3_bind_int(m_stmtBcuInsert, i++, v.posRelay); sqlite3_bind_int(m_stmtBcuInsert, i++, v.negRelay); sqlite3_bind_double(m_stmtBcuInsert, i++, v.posInsulationKohm); sqlite3_bind_double(m_stmtBcuInsert, i++, v.negInsulationKohm); sqlite3_bind_double(m_stmtBcuInsert, i++, v.portVoltage); sqlite3_bind_double(m_stmtBcuInsert, i++, v.posRelayOuterVoltage); sqlite3_bind_text(m_stmtBcuInsert, i++, bits, -1, SQLITE_TRANSIENT); sqlite3_bind_int(m_stmtBcuInsert, i++, v.alarmExtraByte6); sqlite3_bind_int(m_stmtBcuInsert, i++, v.alarmExtraByte7); sqlite3_bind_int64(m_stmtBcuInsert, i++, static_cast(v.rxMask)); sqlite3_bind_double(m_stmtBcuInsert, i++, v.lastRxTime); int rc = sqlite3_step(m_stmtBcuInsert); if (rc != SQLITE_DONE) { m_lastError = sqlite3_errmsg(m_db); } } std::vector DbStore::queryRecent(const std::string& link, int direction, uint16_t msgId, int limit) { std::lock_guard lock(m_mutex); std::vector rows; if (!m_db) return rows; if (limit <= 0) limit = 100; std::string sql = "SELECT id, time, link, direction, msg_id, msg_name, length, checksum_ok, hex " "FROM comm_log WHERE 1=1"; if (!link.empty()) { sql += " AND link='" + link + "'"; } if (direction >= 0) { sql += " AND direction=" + std::to_string(direction); } if (msgId != 0) { char buf[16]; std::snprintf(buf, sizeof(buf), "%u", msgId); sql += " AND msg_id=" + std::string(buf); } sql += " ORDER BY id DESC LIMIT " + std::to_string(limit) + ";"; sqlite3_stmt* stmt = nullptr; if (sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { m_lastError = sqlite3_errmsg(m_db); return rows; } while (sqlite3_step(stmt) == SQLITE_ROW) { CommLogRow r; r.id = sqlite3_column_int64(stmt, 0); r.time = sqlite3_column_double(stmt, 1); r.link = reinterpret_cast(sqlite3_column_text(stmt, 2)); r.direction = sqlite3_column_int(stmt, 3); r.msgId = static_cast(sqlite3_column_int(stmt, 4)); r.msgName = reinterpret_cast(sqlite3_column_text(stmt, 5)); r.length = sqlite3_column_int(stmt, 6); r.checksumOk = sqlite3_column_int(stmt, 7); r.hex = reinterpret_cast(sqlite3_column_text(stmt, 8)); rows.push_back(std::move(r)); } sqlite3_finalize(stmt); return rows; } long long DbStore::count() const { std::lock_guard lock(m_mutex); if (!m_db) return 0; sqlite3_stmt* stmt = nullptr; if (sqlite3_prepare_v2(m_db, "SELECT COUNT(*) FROM comm_log;", -1, &stmt, nullptr) != SQLITE_OK) return 0; long long n = 0; if (sqlite3_step(stmt) == SQLITE_ROW) n = sqlite3_column_int64(stmt, 0); sqlite3_finalize(stmt); return n; } long long DbStore::countBcu() const { std::lock_guard lock(m_mutex); if (!m_db) return 0; sqlite3_stmt* stmt = nullptr; if (sqlite3_prepare_v2(m_db, "SELECT COUNT(*) FROM bcu_node;", -1, &stmt, nullptr) != SQLITE_OK) return 0; long long n = 0; if (sqlite3_step(stmt) == SQLITE_ROW) n = sqlite3_column_int64(stmt, 0); sqlite3_finalize(stmt); return n; } } // namespace ccu