#include "SQLite.h" #include #include #include #include #include #include "../logc/loguru.hpp" namespace { const int SCHEMA_VERSION = 2; } //==================================================================== // 列绑定宏:字段名 + SQL 类型 + 绑定器(无捕获函数指针,不抛异常、不分配) //==================================================================== #define COL(Struct, member) \ SQLite::Column{ #member, "INTEGER", [](sqlite3_stmt* s, int& i, const void* o){ \ sqlite3_bind_int64(s, i++, static_cast(static_cast(o)->member)); } } #define COL_AS(Struct, member, colname) \ SQLite::Column{ colname, "INTEGER", [](sqlite3_stmt* s, int& i, const void* o){ \ sqlite3_bind_int64(s, i++, static_cast(static_cast(o)->member)); } } #define COL_ARR(Struct, arr, index, colname) \ SQLite::Column{ colname, "INTEGER", [](sqlite3_stmt* s, int& i, const void* o){ \ sqlite3_bind_int64(s, i++, static_cast(static_cast(o)->arr[index])); } } //==================================================================== // 表描述注册表:DDL(建表) 与 DML(插入) 共用的唯一数据源。 // 每张协议结构表的数据列只在这里定义一次;id/timestamp 元列由建表逻辑自动追加。 //==================================================================== const std::vector& SQLite::allTables() { static const std::vector tables = { { "ccuState", { COL(ccuState, fc_mode), COL(ccuState, fc_status), COL(ccuState, fc_fault_level), COL(ccuState, fault_level_1), COL(ccuState, fault_level_2), COL(ccuState, fault_level_3), COL(ccuState, fault_level_4), COL(ccuState, total_generation_time), COL(ccuState, output_power_limit), COL(ccuState, total_generation_power), COL(ccuState, hydrogen_capacity), COL(ccuState, liquid_oxygen_capacity), COL(ccuState, fc1_min_cell_voltage), COL(ccuState, fc1_min_cell_pos), COL(ccuState, fc1_avg_cell_voltage), COL(ccuState, fc2_min_cell_voltage), COL(ccuState, fc2_min_cell_pos), COL(ccuState, fc2_avg_cell_voltage), COL(ccuState, palladium_temp), COL(ccuState, buffer_tank_pressure), COL(ccuState, flue_total_emission), COL(ccuState, flue_pressure), COL(ccuState, reactor_pressure), COL(ccuState, electric_valve_open), COL(ccuState, main_pipe_pressure), COL(ccuState, aux_pipe_pressure), COL(ccuState, dcdc1_in_voltage), COL(ccuState, dcdc1_in_current), COL(ccuState, dcdc2_in_voltage), COL(ccuState, dcdc2_in_current), COL(ccuState, dcdc_out_voltage), COL(ccuState, dcdc_out_current), COL(ccuState, dcdc_control_voltage), COL(ccuState, dcdc_aux_out_voltage), COL(ccuState, methanol_total_usage), COL(ccuState, methanol_solution_feed), COL(ccuState, oxygen_water_tank_level), COL(ccuState, hydrogen_water_tank_level), COL(ccuState, ballast_water_tank_level), COL(ccuState, exhaust_in_pressure), COL(ccuState, exhaust_out_pressure), COL(ccuState, exhaust_run_freq), COL(ccuState, exhaust_in_temp), COL(ccuState, exhaust_out_temp), COL(ccuState, exhaust_in_water_pressure), COL(ccuState, exhaust_out_water_pressure), COL(ccuState, tank_lo2_pressure), COL(ccuState, tank_co2_pressure), COL(ccuState, tank_lo2_level), COL(ccuState, alloy_h2_flow), COL(ccuState, fc_h2_flow), COL(ccuState, fc_o2_flow), COL(ccuState, emergency_float_depth), COL(ccuState, emergency_float_time), COL(ccuState, cabin_pressure1), COL(ccuState, cabin_pressure2), COL(ccuState, cabin_temp1), COL(ccuState, cabin_temp2), COL(ccuState, cabin_humidity1), COL(ccuState, cabin_humidity2), COL(ccuState, h2_concentration1), COL(ccuState, h2_concentration2), COL(ccuState, h2_concentration3), COL(ccuState, o2_concentration1), COL(ccuState, o2_concentration2), COL(ccuState, ch3oh_concentration1), COL(ccuState, ch3oh_concentration2), COL(ccuState, flame_detector1), COL(ccuState, flame_detector2), COL_ARR(ccuState, reserved1, 0, "reserved1_1"), COL_ARR(ccuState, reserved1, 1, "reserved1_2"), COL(ccuState, emergency_battery1_voltage), COL(ccuState, emergency_battery1_current), COL(ccuState, emergency_battery1_max_temp), COL(ccuState, emergency_battery1_fault_word), COL(ccuState, emergency_battery2_voltage), COL(ccuState, emergency_battery2_current), COL(ccuState, emergency_battery2_max_temp), COL(ccuState, emergency_battery2_fault_word), COL(ccuState, cabin_ox_concentration), COL(ccuState, cabin_temperature), COL(ccuState, cabin_humidity), COL(ccuState, cabin_pressure), COL(ccuState, power_cabin_ox_concentration), COL(ccuState, power_cabin_temperature), COL(ccuState, power_cabin_humidity), COL(ccuState, power_cabin_pressure), COL_ARR(ccuState, reserved2, 0, "reserved2"), COL_ARR(ccuState, dyn_alarm_flag, 0, "dyn_alarm_flag1"), COL_ARR(ccuState, dyn_alarm_flag, 1, "dyn_alarm_flag2"), COL_ARR(ccuState, dyn_alarm_flag, 2, "dyn_alarm_flag3"), COL_ARR(ccuState, dyn_alarm_flag, 3, "dyn_alarm_flag4"), COL_ARR(ccuState, dyn_alarm_flag, 4, "dyn_alarm_flag5"), COL_ARR(ccuState, dyn_alarm_flag, 5, "dyn_alarm_flag6"), COL_ARR(ccuState, ins_alarm_flag, 0, "ins_alarm_flag1"), COL_ARR(ccuState, ins_alarm_flag, 1, "ins_alarm_flag2"), COL_ARR(ccuState, ins_alarm_flag, 2, "ins_alarm_flag3"), COL_ARR(ccuState, ins_alarm_flag, 3, "ins_alarm_flag4"), COL_ARR(ccuState, ins_alarm_flag, 4, "ins_alarm_flag5"), COL_ARR(ccuState, ins_alarm_flag, 5, "ins_alarm_flag6"), COL(ccuState, ins_relay_status1), COL(ccuState, ins_relay_status2), COL(ccuState, dyn_relay_status1), COL(ccuState, dyn_relay_status2), COL(ccuState, ins_max_discharge_power), COL(ccuState, dyn_max_discharge_power), COL(ccuState, ins_soc), COL(ccuState, dyn_soc), COL(ccuState, ins_total_energy), COL(ccuState, dyn_total_energy), COL(ccuState, ins_power_input), COL(ccuState, dyn_power_input), COL(ccuState, ins_charge_status), COL(ccuState, dyn_charge_status), COL(ccuState, ins_voltage_link), COL(ccuState, ins_voltage_pack), COL(ccuState, ins_current), COL(ccuState, ins_resistance_pos), COL(ccuState, ins_resistance_neg), COL(ccuState, dyn_voltage_link), COL(ccuState, dyn_voltage_pack), COL(ccuState, dyn_current), COL(ccuState, dyn_resistance_pos), COL(ccuState, dyn_resistance_neg), COL(ccuState, ins_emergency_status), COL(ccuState, dyn_emergency_status), COL(ccuState, ins_soc_threshold1), COL(ccuState, ins_soc_threshold2), COL(ccuState, ins_soc_threshold3), COL(ccuState, ins_power_limit1), COL(ccuState, ins_power_limit2), COL(ccuState, dyn_soc_threshold1), COL(ccuState, dyn_soc_threshold2), COL(ccuState, dyn_soc_threshold3), COL(ccuState, dyn_power_limit1), COL(ccuState, dyn_power_limit2), COL(ccuState, device_online_flag1), COL(ccuState, device_online_flag2), COL(ccuState, reserved3), COL(ccuState, heartbeat), COL(ccuState, emergency_command), }}, { "disHighVolBusState", { COL(disHighVolBusState, fuelCellCircuitBreaker), COL(disHighVolBusState, powerLithiumBatteryCircuitBreaker), COL(disHighVolBusState, propulsionMotorCircuitBreaker), COL(disHighVolBusState, lithiumBatteryGroupInstrumentCircuitBreaker), COL(disHighVolBusState, dcDc5ModuleCircuitBreaker), COL(disHighVolBusState, bowHighVoltageDistributionBoxCircuitBreaker), COL(disHighVolBusState, sternFTDevice45CircuitBreaker), COL(disHighVolBusState, sternRudderSwitch1CircuitBreaker), COL(disHighVolBusState, sternRudderSwitch2CircuitBreaker), COL(disHighVolBusState, fbReservedCircuitBreaker), COL(disHighVolBusState, tyzReservedCircuitBreaker), COL(disHighVolBusState, reservedCircuitBreaker), COL(disHighVolBusState, dcDcFaultWord1), COL(disHighVolBusState, dcDcFaultWord2), COL(disHighVolBusState, powerBusInsulationStatus), COL(disHighVolBusState, aBusInsulationStatus), COL(disHighVolBusState, meterPowerLossSignal), COL(disHighVolBusState, emergencyPowerLossSignal), COL(disHighVolBusState, powerBusVoltage), COL(disHighVolBusState, dcDc5ModuleCurrent), COL(disHighVolBusState, powerBusCurrent), COL(disHighVolBusState, busbarAVoltage), COL(disHighVolBusState, propulsionMotorControlBoxCurrent), COL(disHighVolBusState, busbarACurrent), COL(disHighVolBusState, lithiumBatteryGroupMeterCurrent), COL(disHighVolBusState, bowHighVoltageDistributionBoxCurrent), COL(disHighVolBusState, sternFTDevice45Current), COL(disHighVolBusState, tyzReservedSwitchCurrent), COL(disHighVolBusState, sternRudderSwitch1Current), COL(disHighVolBusState, reservedCurrent1), COL(disHighVolBusState, sternRudderSwitch2Current), COL(disHighVolBusState, reservedCurrent2), COL(disHighVolBusState, fbReservedSwitchCurrent), COL(disHighVolBusState, reservedCurrent3), COL(disHighVolBusState, coolWaterPressure), }}, { "disHighAVolBusState", { COL(disHighAVolBusState, bowFTDevice123CircuitBreaker), COL(disHighAVolBusState, actuatorCircuitBreaker), COL(disHighAVolBusState, mastSteeringGearControlBoxCircuitBreaker), COL(disHighAVolBusState, xczCircuitBreaker), COL(disHighAVolBusState, bowRudderControlBoxCircuitBreaker), COL(disHighAVolBusState, openWaterCoverStartCylinderCircuitBreaker), COL(disHighAVolBusState, instrumentPowerFailureSignal), COL(disHighAVolBusState, emergencyPowerFailureSignal), COL(disHighAVolBusState, waterIngressionAlarm), COL(disHighAVolBusState, busbarBVoltage), COL(disHighAVolBusState, busbarBCurrent), COL(disHighAVolBusState, bowFTDevice123Current), COL(disHighAVolBusState, actuatorCurrent), COL(disHighAVolBusState, mastSteeringGearControlBoxCurrent), COL(disHighAVolBusState, xczCurrent), COL(disHighAVolBusState, bowRudderControlBoxCurrent), COL(disHighAVolBusState, openWaterCoverStartCylinderCurrent), }}, { "disLowMainBusState", { COL(disLowMainBusState, lithiumBatteryGroupInstrumentCircuitBreaker), COL(disLowMainBusState, bowLowVoltageDistributionBoxCircuitBreaker), COL(disLowMainBusState, unit4InstrumentDC48VCircuitBreaker), COL(disLowMainBusState, dcC1DCDistributionPanelCircuitBreaker), COL(disLowMainBusState, reservedCircuitBreaker1), COL(disLowMainBusState, reservedCircuitBreaker2), COL(disLowMainBusState, emergencyLithiumBatteryGroup2CircuitBreaker), COL(disLowMainBusState, instrumentPowerFailureSignal), COL(disLowMainBusState, emergencyPowerFailureSignal), COL(disLowMainBusState, instrumentBusbarInsulationLow), COL(disLowMainBusState, instrumentBusbarVoltage), COL(disLowMainBusState, bowLowVoltageDistributionBoxCurrent), COL(disLowMainBusState, dcC1InstrumentDC48VCurrent), COL(disLowMainBusState, reservedCurrent1), COL(disLowMainBusState, emergencyLithiumBatteryGroup2Current), COL(disLowMainBusState, lithiumBatteryGroupInstrumentCurrent), COL(disLowMainBusState, unit4InstrumentDC48VCurrent), COL(disLowMainBusState, reservedCurrent2), COL(disLowMainBusState, emergency2BusbarVoltage), COL(disLowMainBusState, compositeEnergyManagementSystemEmergencyDC48VCurrent), COL(disLowMainBusState, fuelCellSecuritySystemEmergencyDC48VCurrent), COL(disLowMainBusState, plcControlPowerCurrent), }}, { "disLowBusState", { COL(disLowBusState, unit1CircuitBreaker), COL(disLowBusState, unit2CircuitBreaker), COL(disLowBusState, unit3CircuitBreaker), COL(disLowBusState, unit5CircuitBreaker), COL(disLowBusState, bowPZDeviceCircuitBreaker), COL(disLowBusState, reservedCircuitBreaker1), COL(disLowBusState, reservedCircuitBreaker2), COL(disLowBusState, emergencyLithiumBatteryGroup1CircuitBreaker), COL(disLowBusState, powerFailureSignal), COL(disLowBusState, emergencyPowerFailureSignal), COL(disLowBusState, waterIngressionAlarm), COL(disLowBusState, instrumentBusbarVoltage), COL(disLowBusState, instrumentBusbarCurrent), COL(disLowBusState, unit1Current), COL(disLowBusState, unit2Current), COL(disLowBusState, unit3Current), COL(disLowBusState, unit5Current), COL(disLowBusState, bowPZDeviceCurrent), COL(disLowBusState, emergencyLithiumBatteryGroup1Current), COL(disLowBusState, emergency1BusbarVoltage), }}, { "ccuSetParmFb", { COL(msg_CcuSetParmFbMsg, flag), COL(msg_CcuSetParmFbMsg, failureCode), }}, { "ccuColCmd", { COL_AS(ccuColCmd, fcmode, "workCondition"), COL(ccuColCmd, fcuCmd), COL(ccuColCmd, fcuPowerEfficiency), COL(ccuColCmd, pitchData1), COL(ccuColCmd, rollData1), COL(ccuColCmd, emergencyAllow), COL(ccuColCmd, depth), COL(ccuColCmd, supplyExhaustCmd), COL(ccuColCmd, reservedCmd5), COL(ccuColCmd, reservedCmd6), COL(ccuColCmd, insBatCmd), COL(ccuColCmd, powerBatCmd), COL(ccuColCmd, powerBatEfficiency), COL(ccuColCmd, heartbeat), COL(ccuColCmd, masterState), COL(ccuColCmd, paceholder1), COL(ccuColCmd, paceholder2), }}, { "ccuSetParmCmd", { COL(ccuSetParmCmd, insSocHold1), COL(ccuSetParmCmd, insSocHold2), COL(ccuSetParmCmd, insSocHold3), COL(ccuSetParmCmd, insOutputPower), COL(ccuSetParmCmd, reserved1), COL(ccuSetParmCmd, powerSocHold1), COL(ccuSetParmCmd, powerSocHold2), COL(ccuSetParmCmd, powerSocHold3), COL(ccuSetParmCmd, powerOutputPower), COL(ccuSetParmCmd, reserved2), COL(ccuSetParmCmd, reserved3), COL(ccuSetParmCmd, reserved4), }}, { "disHighVolBusCmd", { COL(disHighVolBusCmd, fuelCellCircuitBreaker), COL(disHighVolBusCmd, powerLithiumBatteryCircuitBreaker), COL(disHighVolBusCmd, propulsionMotorCircuitBreaker), COL(disHighVolBusCmd, lithiumBatteryGroupInstrumentCircuitBreaker), COL(disHighVolBusCmd, dcDc5ModuleCircuitBreaker), COL(disHighVolBusCmd, bowHighVoltageDistributionBoxCircuitBreaker), COL(disHighVolBusCmd, sternFTDevice45CircuitBreaker), COL(disHighVolBusCmd, sternRudderSwitch1CircuitBreaker), COL(disHighVolBusCmd, sternRudderSwitch2CircuitBreaker), COL(disHighVolBusCmd, fbReservedCircuitBreaker), COL(disHighVolBusCmd, tyzReservedCircuitBreaker), COL(disHighVolBusCmd, reservedCircuitBreaker), COL(disHighVolBusCmd, dcdc5Module), COL(disHighVolBusCmd, coolingSystem), }}, { "disHighAVolBusCmd", { COL(disHighAVolBusCmd, bowFTDevice123CircuitBreaker), COL(disHighAVolBusCmd, actuatorCircuitBreaker), COL(disHighAVolBusCmd, mastSteeringGearControlBoxCircuitBreaker), COL(disHighAVolBusCmd, xczCircuitBreaker), COL(disHighAVolBusCmd, bowRudderControlBoxCircuitBreaker), COL(disHighAVolBusCmd, openWaterCoverStartCylinderCircuitBreaker), }}, { "disLowMainBusCmd", { COL(disLowMainBusCmd, lithiumBatteryGroupInstrumentCircuitBreaker), COL(disLowMainBusCmd, bowLowVoltageDistributionBoxCircuitBreaker), COL(disLowMainBusCmd, unit4InstrumentDC48VCircuitBreaker), COL(disLowMainBusCmd, dcC1DCDistributionPanelCircuitBreaker), COL(disLowMainBusCmd, reservedCircuitBreaker1), COL(disLowMainBusCmd, reservedCircuitBreaker2), COL(disLowMainBusCmd, emergencyLithiumBatteryGroup2CircuitBreaker), }}, { "disLowBusCmd", { COL(disLowBusCmd, unit1CircuitBreaker), COL(disLowBusCmd, unit2CircuitBreaker), COL(disLowBusCmd, unit3CircuitBreaker), COL(disLowBusCmd, unit5CircuitBreaker), COL(disLowBusCmd, bowPZDeviceCircuitBreaker), COL(disLowBusCmd, reservedCircuitBreaker1), COL(disLowBusCmd, reservedCircuitBreaker2), COL(disLowBusCmd, emergencyLithiumBatteryGroup1CircuitBreaker), }}, }; return tables; } #undef COL #undef COL_AS #undef COL_ARR SQLite::SQLite(const std::string& dbPath) : m_db(nullptr), m_status(Status::Disabled), m_dbPath(dbPath) { int rc = sqlite3_open_v2(dbPath.c_str(), &m_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX, nullptr); if (rc != SQLITE_OK) { m_lastError = m_db ? sqlite3_errmsg(m_db) : "unknown error"; LOG_F(ERROR, "无法打开数据库 %s: %s", dbPath.c_str(), m_lastError.c_str()); if (m_db) { sqlite3_close(m_db); m_db = nullptr; } m_status = Status::Disabled; return; } m_status = Status::OK; if (!applyPragmas()) { m_status = Status::Degraded; } if (!migrate()) { int err = sqlite3_errcode(m_db); if (err == SQLITE_CORRUPT || err == SQLITE_NOTADB) { LOG_F(ERROR, "数据库损坏,尝试备份并重建: %s", dbPath.c_str()); sqlite3_close(m_db); m_db = nullptr; std::string backup = dbPath + ".corrupt"; std::rename(dbPath.c_str(), backup.c_str()); if (sqlite3_open_v2(dbPath.c_str(), &m_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX, nullptr) == SQLITE_OK) { m_status = Status::OK; applyPragmas(); if (!migrate()) { if (m_status == Status::OK) m_status = Status::Degraded; } } else { m_status = Status::Disabled; } } else if (m_status == Status::OK) { m_status = Status::Degraded; } } } SQLite::~SQLite() { if (m_db) sqlite3_close(m_db); } std::string SQLite::lastError() const { std::lock_guard lock(m_dbMutex); return m_lastError; } bool SQLite::setError(int rc, const char* op) { m_lastError = m_db ? sqlite3_errmsg(m_db) : "database not open"; LOG_F(ERROR, "SQLite %s 失败 (rc=%d): %s", op, rc, m_lastError.c_str()); if (rc == SQLITE_CORRUPT || rc == SQLITE_NOTADB) { m_status = Status::Disabled; } else if (rc == SQLITE_FULL || rc == SQLITE_BUSY || rc == SQLITE_LOCKED) { m_status = Status::Degraded; } return false; } bool SQLite::applyPragmas() { const char* pragmas[] = { "PRAGMA journal_mode=WAL;", "PRAGMA synchronous=NORMAL;", "PRAGMA busy_timeout=5000;", "PRAGMA foreign_keys=OFF;" }; for (const char* p : pragmas) { if (sqlite3_exec(m_db, p, nullptr, nullptr, nullptr) != SQLITE_OK) { setError(sqlite3_errcode(m_db), p); return false; } } return true; } bool SQLite::migrate() { int version = 0; sqlite3_stmt* stmt = nullptr; if (sqlite3_prepare_v2(m_db, "PRAGMA user_version;", -1, &stmt, nullptr) == SQLITE_OK) { if (sqlite3_step(stmt) == SQLITE_ROW) version = sqlite3_column_int(stmt, 0); sqlite3_finalize(stmt); } if (!createAllTablesUnlocked()) return false; // 老库缺列自动补齐(协议演进新增字段后,旧表由 CREATE TABLE IF NOT EXISTS // 创建、不会带新列,导致 INSERT prepare 失败、该表数据无法落库) if (!migrateColumnsUnlocked()) return false; if (version < SCHEMA_VERSION) { std::string sql = "PRAGMA user_version = " + std::to_string(SCHEMA_VERSION) + ";"; sqlite3_exec(m_db, sql.c_str(), nullptr, nullptr, nullptr); } return true; } //==================================================================== // migrateColumnsUnlocked:按注册表逐一对比表现有列,缺哪列补哪列 // (ALTER TABLE ADD COLUMN,新增列对旧行取默认值 0/NULL)。 //==================================================================== bool SQLite::migrateColumnsUnlocked() { for (const TableDesc& t : allTables()) { // 读取现有列(PRAGMA table_info 第 2 列为列名) std::vector existing; sqlite3_stmt* stmt = nullptr; std::string pragma = std::string("PRAGMA table_info(") + t.name + ");"; if (sqlite3_prepare_v2(m_db, pragma.c_str(), -1, &stmt, nullptr) == SQLITE_OK) { while (sqlite3_step(stmt) == SQLITE_ROW) { const unsigned char* name = sqlite3_column_text(stmt, 1); if (name) existing.push_back(reinterpret_cast(name)); } sqlite3_finalize(stmt); } if (existing.empty()) continue; // 表不存在(新库),建表时已含全部列 for (const Column& c : t.columns) { bool found = false; for (const std::string& e : existing) { if (e == c.name) { found = true; break; } } if (found) continue; std::string sql = std::string("ALTER TABLE ") + t.name + " ADD COLUMN " + c.name + " " + c.sqlType + ";"; char* errMsg = nullptr; int rc = sqlite3_exec(m_db, sql.c_str(), nullptr, nullptr, &errMsg); if (rc == SQLITE_OK) { LOG_F(INFO, "SQLite 迁移: 表 %s 新增列 %s", t.name, c.name); } else { // 记录失败但不中断打开:其余列/表继续尝试, // 未补齐列的 insert 仍会按原样报 prepare 错误 m_lastError = errMsg ? errMsg : "alter table failed"; LOG_F(ERROR, "SQLite 迁移失败: %s (%s)", sql.c_str(), m_lastError.c_str()); if (errMsg) sqlite3_free(errMsg); } } } return true; } bool SQLite::createTableInternal(const std::string& tableName, const std::string& columns) { std::string sql = "CREATE TABLE IF NOT EXISTS " + tableName + " (" + columns + ");"; char* errMsg = nullptr; int rc = sqlite3_exec(m_db, sql.c_str(), nullptr, nullptr, &errMsg); if (rc != SQLITE_OK) { m_lastError = errMsg ? errMsg : "create table failed"; LOG_F(ERROR, "创建表失败: %s (%s)", tableName.c_str(), m_lastError.c_str()); if (errMsg) sqlite3_free(errMsg); return false; } return true; } bool SQLite::createTable(const std::string& tableName, const std::string& columns) { std::lock_guard lock(m_dbMutex); if (!m_db) { m_lastError = "database not open"; return false; } return createTableInternal(tableName, columns); } bool SQLite::createTable() { std::lock_guard lock(m_dbMutex); if (!m_db) { m_lastError = "database not open"; return false; } return createAllTablesUnlocked(); } bool SQLite::createAllTablesUnlocked() { // 由描述符生成各协议结构表的建表语句(自动追加 id/timestamp 元列) for (const TableDesc& t : allTables()) { std::string cols = "id INTEGER PRIMARY KEY AUTOINCREMENT, " "timestamp DATETIME DEFAULT (datetime('now', 'localtime'))"; for (const Column& c : t.columns) { cols += ", "; cols += c.name; cols += " "; cols += c.sqlType; } if (!createTableInternal(t.name, cols)) return false; } // msg_log(原始收发帧日志,列类型多样,保持手写) std::string msgLogColumns = R"( id INTEGER PRIMARY KEY AUTOINCREMENT, ts REAL NOT NULL, ts_local TEXT DEFAULT (datetime('now', 'localtime')), direction INTEGER NOT NULL, msg_id INTEGER NOT NULL, msg_name TEXT, length INTEGER, payload_hex TEXT, checksum_ok INTEGER, note TEXT )"; if (!createTableInternal("msg_log", msgLogColumns)) return false; // 索引 sqlite3_exec(m_db, "CREATE INDEX IF NOT EXISTS idx_msg_log_ts ON msg_log(ts);", nullptr, nullptr, nullptr); sqlite3_exec(m_db, "CREATE INDEX IF NOT EXISTS idx_msg_log_id ON msg_log(msg_id);", nullptr, nullptr, nullptr); return true; } //==================================================================== // 泛化插入:按表名在注册表中查找描述并绑定,所有类型共用这一条路径 //==================================================================== bool SQLite::insertGeneric(const char* tableName, const void* obj) { const TableDesc* table = nullptr; for (const TableDesc& t : allTables()) { if (std::strcmp(t.name, tableName) == 0) { table = &t; break; } } if (!table) return false; try { std::lock_guard lock(m_dbMutex); if (!m_db) { setError(SQLITE_MISUSE, "insert (db not open)"); return false; } if (m_status == Status::Disabled) return false; if (table->columns.empty()) return false; std::string sql = "INSERT INTO "; sql += table->name; sql += " ("; for (size_t i = 0; i < table->columns.size(); ++i) { if (i) sql += ","; sql += table->columns[i].name; } sql += ") VALUES ("; for (size_t i = 0; i < table->columns.size(); ++i) { if (i) sql += ","; sql += "?"; } sql += ")"; sqlite3_stmt* stmt = nullptr; int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr); if (rc != SQLITE_OK) { setError(rc, "prepare"); return false; } int idx = 1; for (const Column& c : table->columns) { c.bind(stmt, idx, obj); } rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { setError(rc, "step"); return false; } if (m_status == Status::Degraded) m_status = Status::OK; return true; } catch (const std::exception& e) { LOG_F(ERROR, "SQLite insert 异常: %s", e.what()); return false; } catch (...) { LOG_F(ERROR, "SQLite insert 未知异常"); return false; } } //==================================================================== // 状态反馈数据 //==================================================================== bool SQLite::insertData(ccuState data) { return insertGeneric("ccuState", &data); } bool SQLite::insertData(disHighVolBusState data) { return insertGeneric("disHighVolBusState", &data); } bool SQLite::insertData(disHighAVolBusState data) { return insertGeneric("disHighAVolBusState", &data); } bool SQLite::insertData(disLowMainBusState data) { return insertGeneric("disLowMainBusState", &data); } bool SQLite::insertData(disLowBusState data) { return insertGeneric("disLowBusState", &data); } bool SQLite::insertData(const msg_CcuSetParmFbMsg& fb) { return insertGeneric("ccuSetParmFb", &fb); } //==================================================================== // 下发指令数据 //==================================================================== bool SQLite::insertData(ccuColCmd cmd) { return insertGeneric("ccuColCmd", &cmd); } bool SQLite::insertData(ccuSetParmCmd cmd) { return insertGeneric("ccuSetParmCmd", &cmd); } bool SQLite::insertData(disHighVolBusCmd cmd) { return insertGeneric("disHighVolBusCmd", &cmd); } bool SQLite::insertData(disHighAVolBusCmd cmd) { return insertGeneric("disHighAVolBusCmd", &cmd); } bool SQLite::insertData(disLowMainBusCmd cmd) { return insertGeneric("disLowMainBusCmd", &cmd); } bool SQLite::insertData(disLowBusCmd cmd) { return insertGeneric("disLowBusCmd", &cmd); } //==================================================================== // 原始收发帧落库 //==================================================================== void SQLite::onFrame(int direction, unsigned short msgId, const char* name, const unsigned char* data, unsigned int len, bool checksumOk) { try { std::lock_guard lock(m_dbMutex); if (!m_db || m_status == Status::Disabled) return; static const char* hexDigits = "0123456789ABCDEF"; std::string hex; hex.reserve(len * 2); for (unsigned int i = 0; i < len; ++i) { hex.push_back(hexDigits[data[i] >> 4]); hex.push_back(hexDigits[data[i] & 0x0F]); } double ts = std::chrono::duration( std::chrono::system_clock::now().time_since_epoch()).count(); const char* sql = "INSERT INTO msg_log " "(ts, direction, msg_id, msg_name, length, payload_hex, checksum_ok) " "VALUES (?,?,?,?,?,?,?)"; sqlite3_stmt* stmt = nullptr; int rc = sqlite3_prepare_v2(m_db, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { setError(rc, "prepare msg_log"); return; } sqlite3_bind_double(stmt, 1, ts); sqlite3_bind_int(stmt, 2, direction); sqlite3_bind_int(stmt, 3, static_cast(msgId)); sqlite3_bind_text(stmt, 4, name ? name : "", -1, SQLITE_TRANSIENT); sqlite3_bind_int(stmt, 5, static_cast(len)); sqlite3_bind_text(stmt, 6, hex.c_str(), static_cast(hex.size()), SQLITE_TRANSIENT); sqlite3_bind_int(stmt, 7, checksumOk ? 1 : 0); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); if (rc != SQLITE_DONE) { setError(rc, "step msg_log"); return; } if (m_status == Status::Degraded) m_status = Status::OK; } catch (const std::exception& e) { LOG_F(ERROR, "SQLite onFrame 异常: %s", e.what()); } catch (...) { LOG_F(ERROR, "SQLite onFrame 未知异常"); } } //==================================================================== // 查询接口 //==================================================================== bool SQLite::queryInt(const std::string& sql, long long& out) { std::lock_guard lock(m_dbMutex); if (!m_db) return false; sqlite3_stmt* stmt = nullptr; int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr); if (rc != SQLITE_OK) { setError(rc, "query"); return false; } bool ok = false; if (sqlite3_step(stmt) == SQLITE_ROW) { out = sqlite3_column_int64(stmt, 0); ok = true; } sqlite3_finalize(stmt); return ok; } long long SQLite::count(const std::string& table) { std::lock_guard lock(m_dbMutex); if (!m_db) return 0; std::string sql = "SELECT COUNT(*) FROM " + table; sqlite3_stmt* stmt = nullptr; int rc = sqlite3_prepare_v2(m_db, sql.c_str(), -1, &stmt, nullptr); if (rc != SQLITE_OK) { setError(rc, "count"); return 0; } long long n = 0; if (sqlite3_step(stmt) == SQLITE_ROW) n = sqlite3_column_int64(stmt, 0); sqlite3_finalize(stmt); return n; }