新增复合管控器(pCCU) + 惯导数据解析 + 设备控制页增强 + 0824协议文档归档

- 新增 pCCU 复合管控器程序(src/pCCU):FC/PM 双链路(UDP)、协议编解码器
  (FcProtocol/PmProtocol/Frame/Message/MessageRegistry)、快照与落库(DbStore)、
  内置 WebServer;挂入 src/CMakeLists.txt 与板卡 mission(h100.moos)
- 惯导/深度数据:新增 uDevice_insData 消息解析,接入 UpperCommManager,
  pPowerMangerHost 注册监视,UpmsgTest 覆盖
- pPowerMangerHost 设备控制页增强(web/index.html)
- 部署/运维脚本改为 4 个 systemd 服务(moosdb/pPowerManger/pPowerMangerHost/pCCU):
  deploy/clean-data/fetch-data 同步适配
- 协议文档:归档 0824 控制主机与复合管控器通信协议 xlsx 及关联 docx,移除旧 md
- 测试:新增 pccu 单元/集成测试与 disSysTest Python 配电仿真
- 清理:移除未使用的 CCU 状态调试打印 coutMsg;.gitignore 补充编译产物/临时文件忽略
This commit is contained in:
zjk
2026-08-26 22:43:10 +08:00
parent e0488d513b
commit 54c32a180a
79 changed files with 8731 additions and 1398 deletions
+409
View File
@@ -0,0 +1,409 @@
#include "CCU.h"
#include "MBUtils.h"
#include "../pPowerManger/logc/loguru.hpp"
#include "protocol/Frame.h"
#include <chrono>
using namespace ccu;
using namespace std;
//---------------------------------------------------------
// Constructor / Destructor
CCU::CCU() {}
CCU::~CCU() {
if (m_web) { m_web->stop(); delete m_web; m_web = 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_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; }
}
//---------------------------------------------------------
// OnStartUp:读取配置并初始化各组件
bool CCU::OnStartUp() {
AppCastingMOOSApp::OnStartUp();
STRING_LIST sParams;
m_MissionReader.EnableVerbatimQuoting(false);
if (!m_MissionReader.GetConfiguration(GetAppName(), sParams))
reportConfigWarning("No config block found for " + GetAppName());
STRING_LIST::iterator p;
for (p = sParams.begin(); p != sParams.end(); p++) {
string orig = *p;
string line = *p;
string param = stripBlankEnds(tolower(biteStringX(line, '=')));
string value = stripBlankEnds(line);
bool handled = true;
if (param == "fc_local_port") m_fcLocalPort = atol(value.c_str());
else if (param == "fc_remote_ip") m_fcHost = value;
else if (param == "fc_remote_port") m_fcPort = atol(value.c_str());
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 == "dbpath") m_dbPath = value;
else if (param == "logpath") m_logPath = value;
else if (param == "web_port") m_webPort = atoi(value.c_str());
else if (param == "web_enable") {
m_webEnable = (tolower(value) == "true" || value == "1");
}
else handled = false;
if (!handled)
reportUnhandledConfigWarning(orig);
}
registerVariables();
// 日志文件
if (m_logPath.empty()) m_logPath = "pCCU.log";
loguru::add_file(m_logPath.c_str(), loguru::Append, loguru::Verbosity_MAX);
LOG_F(INFO, "pCCU log path: %s", m_logPath.c_str());
// 数据库
m_db = new DbStore(m_dbPath);
if (!m_db->open()) {
LOG_F(ERROR, "open database failed: %s", m_db->lastError().c_str());
delete m_db;
m_db = nullptr;
} else {
LOG_F(INFO, "database path: %s", m_dbPath.c_str());
}
// 系统数据快照
m_sysData = new SystemData();
// FC 链路
m_fcLink = new FcLinkManager(m_fcLocalPort, m_fcHost, m_fcPort);
m_fcLink->setLogSink(m_db);
m_fcLink->setOnMessage([this](Message* msg, const std::vector<uint8_t>& frame) {
handleFcMessage(msg, frame);
});
m_fcLink->setOnRawFrame([](int dir, const std::vector<uint8_t>&, bool) {
// 原始帧已通过 LogSink 落库;如需 Web 实时原始报文可在此扩展
});
if (!m_fcLink->start())
LOG_F(ERROR, "FC link start failed");
// PM 链路
m_pmLink = new PmLinkManager(m_pmLocalPort, m_pmHost, m_pmPort);
m_pmLink->setLogSink(m_db);
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) {
});
if (!m_pmLink->start())
LOG_F(ERROR, "PM link start failed");
// 快照构建器(链路就绪后创建)
m_snap = new SnapshotBuilder(m_sysData, m_fcLink, m_pmLink, m_db);
// 网页
if (m_webEnable) {
m_web = new WebServer();
m_web->setOnOpen([this]() { return buildSnapshot(); });
m_web->setApiHandler([this](const std::string& uri, const std::string& query) {
return handleApi(uri, query);
});
if (!m_web->start(m_webPort)) {
LOG_F(ERROR, "web server start failed on port %d", m_webPort);
delete m_web;
m_web = nullptr;
}
}
LOG_F(INFO, "pCCU started: fc_link local=%ld -> %s:%ld, pm_link local=%ld -> %s:%ld",
m_fcLocalPort, m_fcHost.c_str(), m_fcPort,
m_pmLocalPort, m_pmHost.c_str(), m_pmPort);
return true;
}
//---------------------------------------------------------
// OnConnectToServer
bool CCU::OnConnectToServer() {
registerVariables();
return true;
}
//---------------------------------------------------------
// registerVariables
void CCU::registerVariables() {
AppCastingMOOSApp::RegisterVariables();
// pCCU 数据交换主要走 UDP;MOOSDB 仅登记运行状态变量
Register("CCU_FC_LINK_STATE", 0);
Register("CCU_PM_LINK_STATE", 0);
}
//---------------------------------------------------------
// OnNewMail
bool CCU::OnNewMail(MOOSMSG_LIST &NewMail) {
AppCastingMOOSApp::OnNewMail(NewMail);
MOOSMSG_LIST::iterator p;
for (p = NewMail.begin(); p != NewMail.end(); p++) {
CMOOSMsg &msg = *p;
// 预留:未来如需通过 MOOSDB 下发指令可在此处理
}
return true;
}
//---------------------------------------------------------
// Iterate:周期任务(AppTick 次/秒)
bool CCU::Iterate() {
AppCastingMOOSApp::Iterate();
double now = MOOSTime();
// 周期(1Hz)整合 FC 状态并发送 PM 状态报文给控制主机
if (now - m_lastStatusTx >= 1.0) {
sendPmStatus();
m_lastStatusTx = now;
}
// 周期(1Hz)推送网页快照
if (m_web) {
static double lastWebPush = 0;
if (now - lastWebPush >= 1.0) {
lastWebPush = now;
m_web->broadcast(buildSnapshot());
}
}
AppCastingMOOSApp::PostReport();
return true;
}
//---------------------------------------------------------
// handleFcMessage:处理 FC 链路收到的消息
void CCU::handleFcMessage(Message* msg, const std::vector<uint8_t>& frame) {
if (!msg) return;
switch (msg->id()) {
case 0x0002: { // FC 状态反馈
FcStatusValue v;
if (msg->decode(frame, static_cast<void*>(&v))) {
m_sysData->updateFcStatus(v);
LOG_F(INFO, "[FC] status: mode=%d status=%d fault_level=%d",
v.fc_mode, v.fc_status, v.fc_fault_level);
} else {
LOG_F(ERROR, "[FC] status decode failed");
}
break;
}
default:
LOG_F(WARNING, "[FC] unhandled msg id 0x%04X", msg->id());
break;
}
}
//---------------------------------------------------------
// handlePmMessage:处理 PM 链路收到的消息
void CCU::handlePmMessage(Message* msg, const std::vector<uint8_t>& frame) {
if (!msg) return;
switch (msg->id()) {
case 0x0001: { // PM 操控指令 -> 转发给 FC
PmControlValue c;
if (msg->decode(frame, static_cast<void*>(&c))) {
m_sysData->updatePmControl(c);
LOG_F(INFO, "[PM] control: mode=%d cmd=%d power=%d",
c.mode, c.cmd, c.outputPower);
// 映射并转发到 FC
FcControlValue f;
f.mode = c.mode;
f.cmd = c.cmd;
f.outputPower = c.outputPower;
f.pitch = c.pitch;
f.roll = c.roll;
f.emergencyAllow = c.emergencyAllow;
f.depth = c.depth;
f.supplyCmd = c.supplyCmd;
f.reservedCmd5 = c.reservedCmd5;
f.reservedCmd6 = c.reservedCmd6;
f.heartbeat = c.heartbeat;
m_sysData->updateFcControl(f);
m_fcLink->sendMessage(0x0001, &f);
LOG_F(INFO, "[FC] forward control cmd=%d power=%d", f.cmd, f.outputPower);
} else {
LOG_F(ERROR, "[PM] control decode failed");
}
break;
}
case 0x0002: { // PM 参数设定 -> 回参数反馈
PmParamSetValue p;
if (msg->decode(frame, static_cast<void*>(&p))) {
m_sysData->updatePmParamSet(p);
LOG_F(INFO, "[PM] param set received");
// 当前阶段直接回成功
PmParamSetFbValue fb;
fb.flag = 0x10; // 设定成功
fb.failCode = 0;
m_sysData->updatePmParamFb(fb);
m_pmLink->sendMessage(0x0003, &fb);
LOG_F(INFO, "[PM] param set feedback sent");
} else {
LOG_F(ERROR, "[PM] param set decode failed");
}
break;
}
default:
LOG_F(WARNING, "[PM] unhandled msg id 0x%04X", msg->id());
break;
}
}
//---------------------------------------------------------
// sendPmStatus:整合最新 FC 状态,编码 PM 状态报文发送
void CCU::sendPmStatus() {
if (!m_pmLink || !m_sysData) return;
const FcStatusValue& fc = m_sysData->fcStatus();
PmStatusValue s;
s.fc_mode = fc.fc_mode;
s.fc_status = fc.fc_status;
s.fault_level_1 = fc.fault_level_1;
s.fault_level_2 = fc.fault_level_2;
s.fault_level_3 = fc.fault_level_3;
s.fault_level_4 = fc.fault_level_4;
s.total_generation_time = fc.total_generation_time;
s.fc_fault_level = fc.fc_fault_level;
s.output_power_limit = 0; // FC 状态未含此字段,置 0
s.generation_power = fc.generation_power;
s.hydrogen_capacity = fc.hydrogen_capacity;
s.liquid_oxygen_capacity = fc.liquid_oxygen_capacity;
s.fc1_min_cell_voltage = fc.fc1_min_cell_voltage;
s.fc1_min_cell_pos = fc.fc1_min_cell_pos;
s.fc1_avg_cell_voltage = fc.fc1_avg_cell_voltage;
s.fc2_min_cell_voltage = fc.fc2_min_cell_voltage;
s.fc2_min_cell_pos = fc.fc2_min_cell_pos;
s.fc2_avg_cell_voltage = fc.fc2_avg_cell_voltage;
s.palladium_temp = fc.palladium_temp;
s.buffer_tank_pressure = fc.buffer_tank_pressure;
s.flue_total_emission = fc.flue_total_emission;
s.flue_pressure = fc.flue_pressure;
s.reactor_pressure = fc.reactor_pressure;
s.electric_valve_open = fc.electric_valve_open;
s.main_pipe_pressure = 0;
s.aux_pipe_pressure = 0;
s.dcdc1_in_voltage = fc.dcdc1_in_voltage;
s.dcdc1_in_current = fc.dcdc1_in_current;
s.dcdc2_in_voltage = fc.dcdc2_in_voltage;
s.dcdc2_in_current = fc.dcdc2_in_current;
s.dcdc_out_voltage = fc.dcdc_out_voltage;
s.dcdc_out_current = fc.dcdc_out_current;
s.dcdc_ctrl_voltage = fc.dcdc_ctrl_voltage;
s.dcdc_aux_voltage = fc.dcdc_aux_voltage;
s.methanol_total_use = fc.methanol_total_use;
s.methanol_feed = fc.methanol_feed;
s.oxygen_side_water_level = fc.oxygen_side_water_level;
s.hydrogen_side_water_level = fc.hydrogen_side_water_level;
s.ballast_water_level = fc.ballast_water_level;
s.exhaust_inlet_pressure = fc.exhaust_inlet_pressure;
s.exhaust_outlet_pressure = fc.exhaust_outlet_pressure;
s.exhaust_run_freq = fc.exhaust_run_freq;
s.exhaust_inlet_temp = fc.exhaust_inlet_temp;
s.exhaust_outlet_temp = fc.exhaust_outlet_temp;
s.exhaust_water_in_pressure = fc.exhaust_water_in_pressure;
s.exhaust_water_out_pressure = fc.exhaust_water_out_pressure;
s.tank_lo2_pressure = fc.tank_lo2_pressure;
s.tank_co2_pressure = fc.tank_co2_pressure;
s.tank_lo2_level = fc.tank_lo2_level;
s.alloy_h2_flow = fc.alloy_h2_flow;
s.fc_h2_flow = fc.fc_h2_flow;
s.fc_o2_flow = fc.fc_o2_flow;
s.emergency_float_depth = fc.emergency_float_depth;
s.emergency_float_time = fc.emergency_float_time;
s.cabin_pressure1 = fc.cabin_pressure1;
s.cabin_pressure2 = fc.cabin_pressure2;
s.cabin_temp1 = fc.cabin_temp1;
s.cabin_temp2 = fc.cabin_temp2;
s.cabin_humidity1 = fc.cabin_humidity1;
s.cabin_humidity2 = fc.cabin_humidity2;
s.h2_concentration1 = fc.h2_concentration1;
s.h2_concentration2 = fc.h2_concentration2;
s.h2_concentration3 = fc.h2_concentration3;
s.o2_concentration1 = fc.o2_concentration1;
s.o2_concentration2 = fc.o2_concentration2;
s.ch3oh_concentration1 = fc.ch3oh_concentration1;
s.ch3oh_concentration2 = fc.ch3oh_concentration2;
s.flame_detector1 = fc.flame_detector1;
s.flame_detector2 = fc.flame_detector2;
// 通信心跳自增
s.heartbeat = m_pmHeartbeat++;
s.emergency_cmd = fc.emergency_cmd;
m_sysData->updatePmStatus(s);
m_pmLink->sendMessage(0x0004, &s);
LOG_F(INFO, "[PM] status sent: mode=%d status=%d heartbeat=%d",
s.fc_mode, s.fc_status, s.heartbeat);
}
//---------------------------------------------------------
// buildSnapshot
std::string CCU::buildSnapshot() {
if (m_snap) return m_snap->build();
return "{}";
}
//---------------------------------------------------------
// handleApi:/api/logs
std::string CCU::handleApi(const std::string& uri, const std::string& query) {
if (uri == "/api/logs") {
if (m_snap) return m_snap->buildLogs(50);
return "[]";
}
return "";
}
//---------------------------------------------------------
// buildReport
bool CCU::buildReport() {
m_msgs << "============================================" << "\n";
m_msgs << "pCCU 复合管控器" << "\n";
m_msgs << "============================================" << "\n";
if (m_fcLink) {
m_msgs << "FC 链路 local:" << m_fcLink->localPort()
<< " rx:" << m_fcLink->rxCount()
<< " tx:" << m_fcLink->txCount()
<< " err:" << m_fcLink->errorCount() << "\n";
}
if (m_pmLink) {
m_msgs << "PM 链路 local:" << m_pmLink->localPort()
<< " rx:" << m_pmLink->rxCount()
<< " tx:" << m_pmLink->txCount()
<< " err:" << m_pmLink->errorCount() << "\n";
}
if (m_sysData) {
m_msgs << "FC 状态接收次数:" << m_sysData->fcStatusCount() << "\n";
m_msgs << "PM 指令接收次数:" << m_sysData->pmControlCount() << "\n";
}
if (m_db) {
m_msgs << "数据库记录数:" << m_db->count() << "\n";
}
return true;
}