pCCU新增锂电池通信/协调操作 + 电源协调状态机骨架(惰性)

锂电池通信(docs/锂电池协议20230324.docx):
- 协议层 BatProtocol:自检0x0000(2B)/设备控制0x0001(17B)/电池组状态0x0003(80B)/
  电池包报警0x0004(68B),uint32字节和校验、小端
- 链路层 BatLinkManager:动力/仪表锂电池独立UDP链路,本机监听与远端IP端口
  均由.moos配置(dyn_bat_*/ins_bat_*/bat_work_condition)
- SystemData/SnapshotBuilder/网页:电池数据按动力/仪表归类展示(运行状态/接触器
  电气量/电池包电压温度/报警),收发帧落库SQLite,网页新增电池标签页

操作->指令下发(PowerCoordinator):
- 操作接口:自检/上下电(母线接触器55H接通77H断开,含预充)/功率设定(0~500kW)/
  电池切换(低压->高压6步、高压->低压5步,按协议时序)
- 步骤引擎:每电池独立步骤队列,下发->状态反馈确认->超时兜底;周期1s心跳控制帧
- PM操控指令insBatCmd/dynBatCmd/dynBatPower自动映射为电池操作

电源协调状态机骨架(CoordFsm, TinyFSM):
- 事件Tick/PmControl/StepDone + 状态Normal/Switching/Fault,react均为TODO占位
- 当前未接线(无start/dispatch调用,运行行为与接入前一致),CoordFsm.hpp头部附接线指南

测试与部署:
- pccuTest新增电池协议编解码/帧长/链路分发用例(175项通过);集成测试覆盖电池
  心跳、启停映射(0x10->0x55/0x20->0x77)、状态落库;同步pccu_it.moos与ws_check
- h100.moos与pCCU.moos增加锂电池链路配置;归档锂电池协议文档
This commit is contained in:
zjk
2026-08-30 14:40:27 +08:00
parent 2590282aed
commit 7e4d08808a
25 changed files with 2691 additions and 23 deletions
+111 -3
View File
@@ -14,8 +14,11 @@ CCU::CCU() {}
CCU::~CCU() {
if (m_web) { m_web->stop(); delete m_web; m_web = nullptr; }
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_snap) { delete m_snap; m_snap = nullptr; }
if (m_sysData) { delete m_sysData; m_sysData = nullptr; }
@@ -46,6 +49,13 @@ 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());
@@ -101,8 +111,33 @@ bool CCU::OnStartUp() {
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");
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");
// 电源协调器(操作->指令下发 + 锂电池/燃料电池协调占位)
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_snap = new SnapshotBuilder(m_sysData, m_fcLink, m_pmLink, m_db);
m_snap = new SnapshotBuilder(m_sysData, m_fcLink, m_pmLink,
m_dynBatLink, m_insBatLink, m_db);
// 网页
if (m_webEnable) {
@@ -118,9 +153,12 @@ bool CCU::OnStartUp() {
}
}
LOG_F(INFO, "pCCU started: fc_link local=%ld -> %s:%ld, pm_link local=%ld -> %s:%ld",
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",
m_fcLocalPort, m_fcHost.c_str(), m_fcPort,
m_pmLocalPort, m_pmHost.c_str(), m_pmPort);
m_pmLocalPort, m_pmHost.c_str(), m_pmPort,
m_dynBatLocalPort, m_dynBatHost.c_str(), m_dynBatPort,
m_insBatLocalPort, m_insBatHost.c_str(), m_insBatPort);
return true;
}
@@ -140,6 +178,8 @@ void CCU::registerVariables() {
// pCCU 数据交换主要走 UDP;MOOSDB 仅登记运行状态变量
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);
}
//---------------------------------------------------------
@@ -169,6 +209,10 @@ bool CCU::Iterate() {
m_lastStatusTx = now;
}
// 电源协调器:操作步骤推进 + 锂电池心跳指令(1s) + 协调算法占位
if (m_coord)
m_coord->tick(now);
// 周期(1Hz)推送网页快照
if (m_web) {
static double lastWebPush = 0;
@@ -234,6 +278,16 @@ void CCU::handlePmMessage(Message* msg, const std::vector<uint8_t>& frame) {
m_sysData->updateFcControl(f);
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);
}
} else {
LOG_F(ERROR, "[PM] control decode failed");
}
@@ -262,6 +316,43 @@ 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 状态报文发送
@@ -398,9 +489,26 @@ 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 << "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";