协议层:
- 新增 protocol/CanBms.{h,cpp}:按 docs/BMS_协议字段定义.xlsx 解码
0x10010000(总电压/SOC/故障码/状态位)、0x10010005(最大电流限制/
总电流/最高单体电压)、0x10010006(总电压校验)。
缩放用除法保证与十进制字面量严格一致;总电流按 2 字节 s16 实现
(xlsx 标注 3 字节与其自身示例/实车帧矛盾,已在代码注释说明)。
- 删除 UDP 锂电池协议:protocol/BatProtocol.{h,cpp}、
comm/BatLinkManager.h(动力/仪表双链路、自检/接触器/功率指令)。
pCCU 主体:
- CCU 订阅 pCanBridge 发布的 CAN_0x* 二进制消息并解码 BMS 报文;
SystemData 双电池槽位改为单一 BmsStatusValue + CAN 帧计数;
PM 电池启停/功率指令改为告警忽略(BMS CAN 协议未定义控制报文)。
- 修复两个本地联调发现的阻断性 bug:
1) MOOS 通配订阅必须用三参数 Register("CAN_0x*","*",0),
单参数形式不做通配匹配;
2) 变量名前缀匹配偏移(compare 少比 1 字符 / sscanf +5 指向 'x'),
改为 rfind 前缀判断 + +6 偏移。
- PowerCoordinator/CoordFsm 剥离电池接触器/切换时序(依赖已删除的
UDP 指令),保留 FC 联动与协调策略占位(CoordSwitchingState 改为
通用的 CoordBusyState)。
网页:
- 动力/仪表锂电池两个标签页合并为"锂电池BMS(CAN)":总电压/总电流/
SOC/最大电流限制/最高单体电压/校验帧/故障码高亮/在线状态,
附报文来源说明;链路状态页改为 CAN 链路行(pCanBridge)。
配置与测试:
- pCCU.moos / missions/h100.moos 移除电池 UDP 配置;
- pccuTest:删除旧电池编解码用例,新增 testCanBmsDecode
(向量取自实车抓包:533.8V/20.1%/49/3.752V/-25.0A)103/103 通过;
- 集成测试改为纯 FC/PM 流程,本地实测 PASSED(含 Web 页面校验)。
This commit is contained in:
+73
-106
@@ -2,7 +2,9 @@
|
||||
#include "MBUtils.h"
|
||||
#include "../pPowerManger/logc/loguru.hpp"
|
||||
#include "protocol/Frame.h"
|
||||
#include "protocol/CanBms.h"
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
|
||||
using namespace ccu;
|
||||
using namespace std;
|
||||
@@ -17,9 +19,7 @@ CCU::~CCU() {
|
||||
if (m_coord) { delete m_coord; m_coord = nullptr; }
|
||||
if (m_fcLink) { m_fcLink->stop(); delete m_fcLink; m_fcLink = nullptr; }
|
||||
if (m_pmLink) { m_pmLink->stop(); delete m_pmLink; m_pmLink = nullptr; }
|
||||
if (m_dynBatLink) { m_dynBatLink->stop(); delete m_dynBatLink; m_dynBatLink = nullptr; }
|
||||
if (m_insBatLink) { m_insBatLink->stop(); delete m_insBatLink; m_insBatLink = nullptr; }
|
||||
if (m_db) { delete m_db; m_db = nullptr; }
|
||||
if (m_db) { m_db->close(); delete m_db; m_db = nullptr; }
|
||||
if (m_snap) { delete m_snap; m_snap = nullptr; }
|
||||
if (m_sysData) { delete m_sysData; m_sysData = nullptr; }
|
||||
}
|
||||
@@ -49,13 +49,6 @@ bool CCU::OnStartUp() {
|
||||
else if (param == "pm_local_port") m_pmLocalPort = atol(value.c_str());
|
||||
else if (param == "pm_remote_ip") m_pmHost = value;
|
||||
else if (param == "pm_remote_port") m_pmPort = atol(value.c_str());
|
||||
else if (param == "dyn_bat_local_port") m_dynBatLocalPort = atol(value.c_str());
|
||||
else if (param == "dyn_bat_remote_ip") m_dynBatHost = value;
|
||||
else if (param == "dyn_bat_remote_port") m_dynBatPort = atol(value.c_str());
|
||||
else if (param == "ins_bat_local_port") m_insBatLocalPort = atol(value.c_str());
|
||||
else if (param == "ins_bat_remote_ip") m_insBatHost = value;
|
||||
else if (param == "ins_bat_remote_port") m_insBatPort = atol(value.c_str());
|
||||
else if (param == "bat_work_condition") m_batWorkCondition = atol(value.c_str());
|
||||
else if (param == "dbpath") m_dbPath = value;
|
||||
else if (param == "logpath") m_logPath = value;
|
||||
else if (param == "web_port") m_webPort = atoi(value.c_str());
|
||||
@@ -106,38 +99,20 @@ bool CCU::OnStartUp() {
|
||||
m_pmLink->setOnMessage([this](Message* msg, const std::vector<uint8_t>& frame) {
|
||||
handlePmMessage(msg, frame);
|
||||
});
|
||||
m_pmLink->setOnRawFrame([](int dir, const std::vector<uint8_t>&, bool) {
|
||||
m_pmLink->setOnRawFrame([](int, const std::vector<uint8_t>&, bool) {
|
||||
});
|
||||
if (!m_pmLink->start())
|
||||
LOG_F(ERROR, "PM link start failed");
|
||||
|
||||
// 锂电池链路(动力 / 仪表)
|
||||
m_dynBatLink = new BatLinkManager(BatRole::Dyn, m_dynBatLocalPort, m_dynBatHost, m_dynBatPort);
|
||||
m_dynBatLink->setLogSink(m_db);
|
||||
m_dynBatLink->setOnMessage([this](Message* msg, const std::vector<uint8_t>& frame) {
|
||||
handleBatMessage(BatRole::Dyn, msg, frame);
|
||||
});
|
||||
m_dynBatLink->setOnRawFrame([](int, const std::vector<uint8_t>&, bool) {});
|
||||
if (!m_dynBatLink->start())
|
||||
LOG_F(ERROR, "Dyn battery link start failed");
|
||||
// 锂电池:CAN 总线(BMS 协议),数据经 pCanBridge 以 CAN_0x* 消息透传,
|
||||
// 在 OnNewMail 中订阅解码(见 registerVariables/handleCanMessage)。
|
||||
|
||||
m_insBatLink = new BatLinkManager(BatRole::Ins, m_insBatLocalPort, m_insBatHost, m_insBatPort);
|
||||
m_insBatLink->setLogSink(m_db);
|
||||
m_insBatLink->setOnMessage([this](Message* msg, const std::vector<uint8_t>& frame) {
|
||||
handleBatMessage(BatRole::Ins, msg, frame);
|
||||
});
|
||||
m_insBatLink->setOnRawFrame([](int, const std::vector<uint8_t>&, bool) {});
|
||||
if (!m_insBatLink->start())
|
||||
LOG_F(ERROR, "Ins battery link start failed");
|
||||
|
||||
// 电源协调器(操作->指令下发 + 锂电池/燃料电池协调占位)
|
||||
// 电源协调器(FC 联动 + 协调算法占位)
|
||||
m_coord = new PowerCoordinator();
|
||||
m_coord->setup(m_sysData, m_fcLink, m_pmLink, m_dynBatLink, m_insBatLink);
|
||||
m_coord->setWorkCondition(static_cast<uint8_t>(m_batWorkCondition));
|
||||
m_coord->setup(m_sysData, m_fcLink, m_pmLink);
|
||||
|
||||
// 快照构建器(链路就绪后创建)
|
||||
m_snap = new SnapshotBuilder(m_sysData, m_fcLink, m_pmLink,
|
||||
m_dynBatLink, m_insBatLink, m_db);
|
||||
m_snap = new SnapshotBuilder(m_sysData, m_fcLink, m_pmLink, m_db);
|
||||
|
||||
// 网页
|
||||
if (m_webEnable) {
|
||||
@@ -154,11 +129,9 @@ bool CCU::OnStartUp() {
|
||||
}
|
||||
|
||||
LOG_F(INFO, "pCCU started: fc_link local=%ld -> %s:%ld, pm_link local=%ld -> %s:%ld, "
|
||||
"bat_dyn local=%ld -> %s:%ld, bat_ins local=%ld -> %s:%ld",
|
||||
"battery=BMS/CAN via pCanBridge(CAN_0x*)",
|
||||
m_fcLocalPort, m_fcHost.c_str(), m_fcPort,
|
||||
m_pmLocalPort, m_pmHost.c_str(), m_pmPort,
|
||||
m_dynBatLocalPort, m_dynBatHost.c_str(), m_dynBatPort,
|
||||
m_insBatLocalPort, m_insBatHost.c_str(), m_insBatPort);
|
||||
m_pmLocalPort, m_pmHost.c_str(), m_pmPort);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -175,11 +148,13 @@ bool CCU::OnConnectToServer() {
|
||||
|
||||
void CCU::registerVariables() {
|
||||
AppCastingMOOSApp::RegisterVariables();
|
||||
// pCCU 数据交换主要走 UDP;MOOSDB 仅登记运行状态变量
|
||||
// 订阅 pCanBridge 透传的全部 CAN 帧(变量名 CAN_0x%08X,二进制数据域)。
|
||||
// 注意:MOOS 通配订阅必须用三参数重载(变量模式 + 来源模式),
|
||||
// 单参数 Register("CAN_0x*") 不会做通配匹配。
|
||||
Register("CAN_0x*", "*", 0);
|
||||
// 运行状态变量
|
||||
Register("CCU_FC_LINK_STATE", 0);
|
||||
Register("CCU_PM_LINK_STATE", 0);
|
||||
Register("CCU_DYN_BAT_LINK_STATE", 0);
|
||||
Register("CCU_INS_BAT_LINK_STATE", 0);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
@@ -190,11 +165,57 @@ bool CCU::OnNewMail(MOOSMSG_LIST &NewMail) {
|
||||
MOOSMSG_LIST::iterator p;
|
||||
for (p = NewMail.begin(); p != NewMail.end(); p++) {
|
||||
CMOOSMsg &msg = *p;
|
||||
// 预留:未来如需通过 MOOSDB 下发指令可在此处理
|
||||
// pCanBridge 透传的 CAN 帧(变量名前缀 CAN_0x)
|
||||
if (msg.m_sKey.rfind("CAN_0x", 0) == 0) {
|
||||
handleCanMessage(msg);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// handleCanMessage:解码 pCanBridge 透传的 CAN 帧
|
||||
//
|
||||
// 消息格式(pCanBridge 约定):
|
||||
// m_sKey = "CAN_0x%08X"(CAN ID,扩展帧)
|
||||
// m_sVal = 二进制数据域(dlc 字节)
|
||||
// m_dfVal2 = 原始帧信息字节(bit7 FF / bit6 RTR / bit3~0 DLC)
|
||||
|
||||
void CCU::handleCanMessage(CMOOSMsg& msg) {
|
||||
if (!m_sysData) return;
|
||||
|
||||
// 从变量名解析 CAN ID(跳过前缀 "CAN_0x" 共 6 字符)
|
||||
uint32_t canId = 0;
|
||||
if (std::sscanf(msg.m_sKey.c_str() + 6, "%x", &canId) != 1) return;
|
||||
|
||||
m_sysData->incCanFrameCount();
|
||||
|
||||
// 远程帧无数据域,直接跳过
|
||||
if (msg.IsBinary() && msg.GetBinaryDataSize() > 0) {
|
||||
unsigned int n = msg.GetBinaryDataSize();
|
||||
unsigned char* d = msg.GetBinaryData();
|
||||
if (!d) return;
|
||||
|
||||
// 合并语义:取当前快照 -> 解码部分更新 -> 写回
|
||||
BmsStatusValue v = m_sysData->bmsStatus();
|
||||
if (decodeCanBmsFrame(canId, d, static_cast<int>(n), v)) {
|
||||
v.valid = true;
|
||||
v.lastRxTime = MOOSTime(false);
|
||||
m_sysData->updateBmsStatus(v);
|
||||
|
||||
// 节流日志:1s 一条,便于联调观察
|
||||
double now = MOOSTime();
|
||||
if (now - m_lastBmsLog >= 1.0) {
|
||||
m_lastBmsLog = now;
|
||||
LOG_F(INFO, "[BMS/CAN] u=%.1fV i=%.1fA soc=%.1f%% fault=%d status=%d cellMax=%.3fV",
|
||||
v.totalVoltage, v.totalCurrent, v.soc,
|
||||
v.faultCode, v.statusBits, v.maxCellVoltage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Iterate:周期任务(AppTick 次/秒)
|
||||
|
||||
@@ -209,7 +230,7 @@ bool CCU::Iterate() {
|
||||
m_lastStatusTx = now;
|
||||
}
|
||||
|
||||
// 电源协调器:操作步骤推进 + 锂电池心跳指令(1s) + 协调算法占位
|
||||
// 电源协调器:协调算法占位
|
||||
if (m_coord)
|
||||
m_coord->tick(now);
|
||||
|
||||
@@ -279,14 +300,11 @@ void CCU::handlePmMessage(Message* msg, const std::vector<uint8_t>& frame) {
|
||||
m_fcLink->sendMessage(0x0001, &f);
|
||||
LOG_F(INFO, "[FC] forward control cmd=%d power=%d", f.cmd, f.outputPower);
|
||||
|
||||
// 锂电池启停/功率指令 -> 生成电池操作序列(协调器按步骤下发)
|
||||
// 启停指令:00无效 / 10启动 / 20关闭
|
||||
if (m_coord) {
|
||||
if (c.insBatCmd == 0x10) m_coord->submitPowerOn(BatRole::Ins);
|
||||
else if (c.insBatCmd == 0x20) m_coord->submitPowerOff(BatRole::Ins);
|
||||
if (c.dynBatCmd == 0x10) m_coord->submitPowerOn(BatRole::Dyn);
|
||||
else if (c.dynBatCmd == 0x20) m_coord->submitPowerOff(BatRole::Dyn);
|
||||
if (c.dynBatPower > 0) m_coord->submitSetPower(BatRole::Dyn, c.dynBatPower);
|
||||
// 锂电池启停/功率指令:BMS CAN 协议未定义控制报文,暂不支持
|
||||
if (c.insBatCmd != 0 || c.dynBatCmd != 0 || c.dynBatPower != 0) {
|
||||
LOG_F(WARNING, "[PM] battery cmd ignored (insBat=0x%02X dynBat=0x%02X dynPwr=%u): "
|
||||
"BMS CAN 协议未定义控制报文",
|
||||
c.insBatCmd, c.dynBatCmd, c.dynBatPower);
|
||||
}
|
||||
} else {
|
||||
LOG_F(ERROR, "[PM] control decode failed");
|
||||
@@ -316,43 +334,6 @@ void CCU::handlePmMessage(Message* msg, const std::vector<uint8_t>& frame) {
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// handleBatMessage:处理锂电池链路收到的消息(动力/仪表共用协议)
|
||||
|
||||
void CCU::handleBatMessage(BatRole role, Message* msg, const std::vector<uint8_t>& frame) {
|
||||
if (!msg) return;
|
||||
const char* tag = (role == BatRole::Dyn) ? "BAT_DYN" : "BAT_INS";
|
||||
switch (msg->id()) {
|
||||
case 0x0003: { // 电池组状态反馈
|
||||
BatStatusValue v;
|
||||
if (msg->decode(frame, static_cast<void*>(&v))) {
|
||||
m_sysData->updateBatStatus(static_cast<int>(role), v);
|
||||
LOG_F(INFO, "[%s] status: wc=0x%02X selfCheck=0x%02X bus=0x%02X "
|
||||
"soc=%.1f%% pwr_out=%.1fkW hb=%d",
|
||||
tag, v.workCondition, v.selfCheckState, v.busContactorState,
|
||||
v.soc * 0.1, v.currentAccessPower * 0.1, v.heartbeat);
|
||||
} else {
|
||||
LOG_F(ERROR, "[%s] status decode failed", tag);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x0004: { // 电池包报警信息
|
||||
BatPackAlarmValue v;
|
||||
if (msg->decode(frame, static_cast<void*>(&v))) {
|
||||
m_sysData->updateBatAlarm(static_cast<int>(role), v);
|
||||
LOG_F(INFO, "[%s] pack alarm: packNo=%d alarm1=0x%02X alarm2=0x%02X",
|
||||
tag, v.packNo, v.alarmFlag[0], v.alarmFlag[1]);
|
||||
} else {
|
||||
LOG_F(ERROR, "[%s] pack alarm decode failed", tag);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_F(WARNING, "[%s] unhandled msg id 0x%04X", tag, msg->id());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// sendPmStatus:整合最新 FC 状态,编码 PM 状态报文发送
|
||||
|
||||
@@ -489,29 +470,15 @@ bool CCU::buildReport() {
|
||||
<< " tx:" << m_pmLink->txCount()
|
||||
<< " err:" << m_pmLink->errorCount() << "\n";
|
||||
}
|
||||
if (m_dynBatLink) {
|
||||
m_msgs << "动力锂电池链路 local:" << m_dynBatLink->localPort()
|
||||
<< " rx:" << m_dynBatLink->rxCount()
|
||||
<< " tx:" << m_dynBatLink->txCount()
|
||||
<< " err:" << m_dynBatLink->errorCount() << "\n";
|
||||
}
|
||||
if (m_insBatLink) {
|
||||
m_msgs << "仪表锂电池链路 local:" << m_insBatLink->localPort()
|
||||
<< " rx:" << m_insBatLink->rxCount()
|
||||
<< " tx:" << m_insBatLink->txCount()
|
||||
<< " err:" << m_insBatLink->errorCount() << "\n";
|
||||
}
|
||||
if (m_coord) {
|
||||
m_msgs << "协调器待执行步骤:" << m_coord->pendingSteps() << "\n";
|
||||
}
|
||||
if (m_sysData) {
|
||||
m_msgs << "锂电池: BMS/CAN (经 pCanBridge CAN_0x* 订阅)\n";
|
||||
m_msgs << "CAN 帧接收计数:" << m_sysData->canFrameCount() << "\n";
|
||||
m_msgs << "BMS 报文接收计数:" << m_sysData->bmsStatusCount() << "\n";
|
||||
m_msgs << "FC 状态接收次数:" << m_sysData->fcStatusCount() << "\n";
|
||||
m_msgs << "PM 指令接收次数:" << m_sysData->pmControlCount() << "\n";
|
||||
m_msgs << "动力电池状态接收次数:" << m_sysData->batStatusCount(0) << "\n";
|
||||
m_msgs << "仪表电池状态接收次数:" << m_sysData->batStatusCount(1) << "\n";
|
||||
}
|
||||
if (m_db) {
|
||||
m_msgs << "数据库记录数:" << m_db->count() << "\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user