锂电池改用 BCU-MBMS CAN 协议按节点解析 + bcu_node 解析数据落库 + 板卡对时脚本
- pCCU/CanBms:按《04KT38电池BCU-MBMS通信(CAN)定义》重写解码, BmsStatus 单状态模型改为 BcuNodeStatus 多节点模型(0x10XX00YY, 节点地址 01~36h),支持 0x0000 电压/电流/SOC/告警码、0x0001 单体 电压、0x0002 单体温度、0x0003 继电器、0x0006 绝缘/端口电压、 0x0010 告警位(附录1 中文码表)六类报文 - 平均单体温度偏移修正:协议文档 BYTE5 写"偏移0"有误,实测固件与 最高/最低一致均带 -40℃ 偏移(实车 0x10020002 原始 68/69 减 40 后 为 28/29℃,落在最低28~最高30区间内,按文档直读则超出物理范围) - pCCU/DbStore:新增 bcu_node 解析数据表,锂电池 BMS 报文每帧落一行 节点合成状态(原 comm_log 仅原始帧,BMS/CAN 帧此前不落 pCCU 库), buildReport 增加 BMS 记录数 - 快照/网页:BCU 按节点分组展示(告警位解析中文含义、数据 age), PM 状态报文锂电池/应急电池卡片 - pPowerManger:新增 iport 本地输入端口配置(UDP bind 延迟到 OnStartUp 读取配置后执行,保证 iport/ccuhost/ccuport 生效), 各 mission 文件补充注释 - test:CAN BCU 解码用例按新协议/新偏移更新(实车抓包 + 文档示例值), 133 项全部通过 - docs:删除旧 BMS 协议(xlsx/20230324docx),归档 04KT38 BCU-MBMS CAN 定义、配电控制器通信协议 20260824、配电系统 CAN 通讯协议 - scripts:新增 sync-board-time.sh,223 板卡(RK3588)时间校正 (本机为基准 + RTT 折半补偿对时,尽力写 RTC,支持 status/--time)
This commit is contained in:
+121
-1
@@ -65,7 +65,45 @@ bool DbStore::open() {
|
||||
");")) return false;
|
||||
exec("CREATE INDEX IF NOT EXISTS idx_comm_log_time ON comm_log(time);");
|
||||
|
||||
return prepareInsert();
|
||||
// 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() {
|
||||
@@ -79,12 +117,33 @@ bool DbStore::prepareInsert() {
|
||||
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<std::mutex> 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;
|
||||
@@ -119,6 +178,55 @@ void DbStore::onRawFrame(int direction, uint16_t msgId, const std::string& link,
|
||||
}
|
||||
}
|
||||
|
||||
void DbStore::insertBcuNode(uint8_t addr, uint8_t func, const BcuNodeStatus& v) {
|
||||
std::lock_guard<std::mutex> 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<double>(::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<sqlite3_int64>(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<CommLogRow> DbStore::queryRecent(const std::string& link, int direction,
|
||||
uint16_t msgId, int limit) {
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
@@ -175,4 +283,16 @@ long long DbStore::count() const {
|
||||
return n;
|
||||
}
|
||||
|
||||
long long DbStore::countBcu() const {
|
||||
std::lock_guard<std::mutex> 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
|
||||
Reference in New Issue
Block a user