Files
H100PowerManger/src/pPowerManger/sqlit3/SQLite.cpp
T

751 lines
33 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "SQLite.h"
#include <string>
#include <vector>
#include <chrono>
#include <cstdio>
#include <cstring>
#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<sqlite3_int64>(static_cast<const Struct*>(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<sqlite3_int64>(static_cast<const Struct*>(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<sqlite3_int64>(static_cast<const Struct*>(o)->arr[index])); } }
//====================================================================
// 表描述注册表:DDL(建表) 与 DML(插入) 共用的唯一数据源。
// 每张协议结构表的数据列只在这里定义一次;id/timestamp 元列由建表逻辑自动追加。
//====================================================================
const std::vector<SQLite::TableDesc>& SQLite::allTables()
{
static const std::vector<TableDesc> 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<std::mutex> 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<std::string> 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<const char*>(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<std::mutex> lock(m_dbMutex);
if (!m_db) {
m_lastError = "database not open";
return false;
}
return createTableInternal(tableName, columns);
}
bool SQLite::createTable()
{
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<double>(
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<int>(msgId));
sqlite3_bind_text(stmt, 4, name ? name : "", -1, SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, 5, static_cast<int>(len));
sqlite3_bind_text(stmt, 6, hex.c_str(), static_cast<int>(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<std::mutex> 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<std::mutex> 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;
}