新增复合管控器(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
+380
View File
@@ -0,0 +1,380 @@
#include "SnapshotBuilder.h"
#include "../store/DbStore.h"
#include "json/json.h"
#include <sstream>
#include <cstdio>
namespace ccu {
using JsonVal = Json::Value;
namespace {
void put(JsonVal& j, const char* k, uint8_t v) { j[k] = JsonVal(v); }
void put(JsonVal& j, const char* k, uint16_t v){ j[k] = JsonVal(v); }
void put(JsonVal& j, const char* k, uint32_t v){ j[k] = JsonVal(static_cast<Json::UInt>(v)); }
//--------------------------------------------------------------------------
// 消息描述:根据链路 + 消息 ID + 方向(0=收 1=发),给出中文名与"来源→去向"。
// 方向是相对 pCCU 而言:收=对方→CCU,发=CCU→对方。
//--------------------------------------------------------------------------
std::string describeMessageName(const std::string& link, uint16_t id) {
if (link == "fc") {
switch (id) {
case 0x0001: return "FC控制指令";
case 0x0002: return "FC状态反馈";
default: break;
}
} else if (link == "pm") {
switch (id) {
case 0x0001: return "PM操控指令";
case 0x0002: return "PM参数设定";
case 0x0003: return "PM参数反馈";
case 0x0004: return "PM状态报文";
default: break;
}
}
char buf[32];
std::snprintf(buf, sizeof(buf), "未知(0x%04X)", id);
return buf;
}
std::string describeDirection(const std::string& link, int direction) {
const std::string peer = (link == "fc") ? "FC" : "PM";
return (direction == 1) ? ("CCU→" + peer) : (peer + "→CCU");
}
// 把数据库一行记录转成 JSON(含消息名与方向描述)
JsonVal commLogToJson(const CommLogRow& r) {
JsonVal e(Json::objectValue);
e["time"] = JsonVal(r.time);
e["link"] = r.link;
e["direction"] = r.direction;
e["msgId"] = r.msgId;
e["checksumOk"] = r.checksumOk;
e["hex"] = r.hex;
e["name"] = describeMessageName(r.link, r.msgId);
e["dirText"] = describeDirection(r.link, r.direction);
return e;
}
// 燃料电池状态 -> JSON
JsonVal fcStatusJson(const FcStatusValue& v) {
JsonVal j(Json::objectValue);
put(j, "fc_mode", v.fc_mode);
put(j, "fc_status", v.fc_status);
put(j, "fault_level_1", v.fault_level_1);
put(j, "fault_level_2", v.fault_level_2);
put(j, "fault_level_3", v.fault_level_3);
put(j, "fault_level_4", v.fault_level_4);
put(j, "total_generation_time", v.total_generation_time);
put(j, "fc_fault_level", v.fc_fault_level);
put(j, "generation_power", v.generation_power);
put(j, "hydrogen_capacity", v.hydrogen_capacity);
put(j, "liquid_oxygen_capacity", v.liquid_oxygen_capacity);
put(j, "fc1_min_cell_voltage", v.fc1_min_cell_voltage);
put(j, "fc1_min_cell_pos", v.fc1_min_cell_pos);
put(j, "fc1_avg_cell_voltage", v.fc1_avg_cell_voltage);
put(j, "fc2_min_cell_voltage", v.fc2_min_cell_voltage);
put(j, "fc2_min_cell_pos", v.fc2_min_cell_pos);
put(j, "fc2_avg_cell_voltage", v.fc2_avg_cell_voltage);
put(j, "palladium_temp", v.palladium_temp);
put(j, "buffer_tank_pressure", v.buffer_tank_pressure);
put(j, "flue_total_emission", v.flue_total_emission);
put(j, "flue_pressure", v.flue_pressure);
put(j, "reactor_pressure", v.reactor_pressure);
put(j, "electric_valve_open", v.electric_valve_open);
put(j, "dcdc1_in_voltage", v.dcdc1_in_voltage);
put(j, "dcdc1_in_current", v.dcdc1_in_current);
put(j, "dcdc2_in_voltage", v.dcdc2_in_voltage);
put(j, "dcdc2_in_current", v.dcdc2_in_current);
put(j, "dcdc_out_voltage", v.dcdc_out_voltage);
put(j, "dcdc_out_current", v.dcdc_out_current);
put(j, "dcdc_ctrl_voltage", v.dcdc_ctrl_voltage);
put(j, "dcdc_aux_voltage", v.dcdc_aux_voltage);
put(j, "methanol_total_use", v.methanol_total_use);
put(j, "methanol_feed", v.methanol_feed);
put(j, "oxygen_side_water_level", v.oxygen_side_water_level);
put(j, "hydrogen_side_water_level", v.hydrogen_side_water_level);
put(j, "ballast_water_level", v.ballast_water_level);
put(j, "exhaust_inlet_pressure", v.exhaust_inlet_pressure);
put(j, "exhaust_outlet_pressure", v.exhaust_outlet_pressure);
put(j, "cabin_pressure1", v.cabin_pressure1);
put(j, "cabin_pressure2", v.cabin_pressure2);
put(j, "cabin_temp1", v.cabin_temp1);
put(j, "cabin_temp2", v.cabin_temp2);
put(j, "cabin_humidity1", v.cabin_humidity1);
put(j, "cabin_humidity2", v.cabin_humidity2);
put(j, "h2_concentration1", v.h2_concentration1);
put(j, "h2_concentration2", v.h2_concentration2);
put(j, "h2_concentration3", v.h2_concentration3);
put(j, "o2_concentration1", v.o2_concentration1);
put(j, "o2_concentration2", v.o2_concentration2);
put(j, "ch3oh_concentration1", v.ch3oh_concentration1);
put(j, "ch3oh_concentration2", v.ch3oh_concentration2);
put(j, "flame_detector1", v.flame_detector1);
put(j, "flame_detector2", v.flame_detector2);
put(j, "emergency_float_depth", v.emergency_float_depth);
put(j, "emergency_float_time", v.emergency_float_time);
put(j, "exhaust_run_freq", v.exhaust_run_freq);
put(j, "exhaust_inlet_temp", v.exhaust_inlet_temp);
put(j, "exhaust_outlet_temp", v.exhaust_outlet_temp);
put(j, "exhaust_water_in_pressure", v.exhaust_water_in_pressure);
put(j, "exhaust_water_out_pressure", v.exhaust_water_out_pressure);
put(j, "tank_lo2_pressure", v.tank_lo2_pressure);
put(j, "tank_co2_pressure", v.tank_co2_pressure);
put(j, "tank_lo2_level", v.tank_lo2_level);
put(j, "alloy_h2_flow", v.alloy_h2_flow);
put(j, "fc_h2_flow", v.fc_h2_flow);
put(j, "fc_o2_flow", v.fc_o2_flow);
put(j, "heartbeat", v.heartbeat);
put(j, "emergency_cmd", v.emergency_cmd);
return j;
}
// PM 操控指令 -> JSON
JsonVal pmControlJson(const PmControlValue& v) {
JsonVal j(Json::objectValue);
put(j, "year", v.year);
put(j, "month", v.month);
put(j, "day", v.day);
put(j, "hour", v.hour);
put(j, "minute", v.minute);
put(j, "second", v.second);
put(j, "millisecond10", v.millisecond10);
put(j, "mode", v.mode);
put(j, "cmd", v.cmd);
put(j, "outputPower", v.outputPower);
j["pitch"] = JsonVal(static_cast<int>(v.pitch)); // 有符号 int16,×0.1°
j["roll"] = JsonVal(static_cast<int>(v.roll));
put(j, "emergencyAllow", v.emergencyAllow);
put(j, "depth", v.depth);
put(j, "supplyCmd", v.supplyCmd);
put(j, "reservedCmd5", v.reservedCmd5);
put(j, "reservedCmd6", v.reservedCmd6);
put(j, "insBatCmd", v.insBatCmd);
put(j, "dynBatCmd", v.dynBatCmd);
put(j, "dynBatPower", v.dynBatPower);
put(j, "heartbeat", v.heartbeat);
put(j, "hostState", v.hostState);
return j;
}
// FC 控制指令(转发给 FC)-> JSON
JsonVal fcControlJson(const FcControlValue& v) {
JsonVal j(Json::objectValue);
put(j, "mode", v.mode);
put(j, "cmd", v.cmd);
put(j, "outputPower", v.outputPower);
j["pitch"] = JsonVal(static_cast<int>(v.pitch)); // 有符号 int16,×0.1°
j["roll"] = JsonVal(static_cast<int>(v.roll));
put(j, "emergencyAllow", v.emergencyAllow);
put(j, "depth", v.depth);
put(j, "supplyCmd", v.supplyCmd);
put(j, "reservedCmd5", v.reservedCmd5);
put(j, "reservedCmd6", v.reservedCmd6);
put(j, "heartbeat", v.heartbeat);
return j;
}
// PM 参数设定(收到)-> JSON
JsonVal pmParamJson(const PmParamSetValue& v) {
JsonVal j(Json::objectValue);
put(j, "insSocHold1", v.insSocHold1);
put(j, "insSocHold2", v.insSocHold2);
put(j, "insSocHold3", v.insSocHold3);
put(j, "insOutputPower", v.insOutputPower);
put(j, "dynSocHold1", v.dynSocHold1);
put(j, "dynSocHold2", v.dynSocHold2);
put(j, "dynSocHold3", v.dynSocHold3);
put(j, "dynOutputPower", v.dynOutputPower);
return j;
}
// PM 参数反馈(发送)-> JSON
JsonVal pmFbJson(const PmParamSetFbValue& v) {
JsonVal j(Json::objectValue);
put(j, "flag", v.flag);
put(j, "failCode", v.failCode);
return j;
}
// PM 状态报文(整合后发送给控制主机)-> JSON(关键字段)
JsonVal pmStatusJson(const PmStatusValue& v) {
JsonVal j(Json::objectValue);
put(j, "fc_mode", v.fc_mode);
put(j, "fc_status", v.fc_status);
put(j, "fault_level_1", v.fault_level_1);
put(j, "fault_level_2", v.fault_level_2);
put(j, "fault_level_3", v.fault_level_3);
put(j, "fault_level_4", v.fault_level_4);
put(j, "total_generation_time", v.total_generation_time);
put(j, "fc_fault_level", v.fc_fault_level);
put(j, "output_power_limit", v.output_power_limit);
put(j, "generation_power", v.generation_power);
put(j, "hydrogen_capacity", v.hydrogen_capacity);
put(j, "liquid_oxygen_capacity", v.liquid_oxygen_capacity);
put(j, "palladium_temp", v.palladium_temp);
put(j, "buffer_tank_pressure", v.buffer_tank_pressure);
put(j, "main_pipe_pressure", v.main_pipe_pressure);
put(j, "aux_pipe_pressure", v.aux_pipe_pressure);
put(j, "dcdc1_in_voltage", v.dcdc1_in_voltage);
put(j, "dcdc1_in_current", v.dcdc1_in_current);
put(j, "dcdc2_in_voltage", v.dcdc2_in_voltage);
put(j, "dcdc2_in_current", v.dcdc2_in_current);
put(j, "dcdc_out_voltage", v.dcdc_out_voltage);
put(j, "dcdc_out_current", v.dcdc_out_current);
put(j, "methanol_total_use", v.methanol_total_use);
put(j, "methanol_feed", v.methanol_feed);
put(j, "exhaust_inlet_pressure", v.exhaust_inlet_pressure);
put(j, "exhaust_outlet_pressure", v.exhaust_outlet_pressure);
put(j, "exhaust_run_freq", v.exhaust_run_freq);
put(j, "tank_lo2_pressure", v.tank_lo2_pressure);
put(j, "tank_co2_pressure", v.tank_co2_pressure);
put(j, "tank_lo2_level", v.tank_lo2_level);
put(j, "alloy_h2_flow", v.alloy_h2_flow);
put(j, "fc_h2_flow", v.fc_h2_flow);
put(j, "fc_o2_flow", v.fc_o2_flow);
put(j, "emergency_float_depth", v.emergency_float_depth);
put(j, "emergency_float_time", v.emergency_float_time);
put(j, "cabin_pressure1", v.cabin_pressure1);
put(j, "cabin_pressure2", v.cabin_pressure2);
put(j, "cabin_temp1", v.cabin_temp1);
put(j, "cabin_temp2", v.cabin_temp2);
put(j, "cabin_humidity1", v.cabin_humidity1);
put(j, "cabin_humidity2", v.cabin_humidity2);
put(j, "h2_concentration1", v.h2_concentration1);
put(j, "h2_concentration2", v.h2_concentration2);
put(j, "h2_concentration3", v.h2_concentration3);
put(j, "o2_concentration1", v.o2_concentration1);
put(j, "o2_concentration2", v.o2_concentration2);
put(j, "ch3oh_concentration1", v.ch3oh_concentration1);
put(j, "ch3oh_concentration2", v.ch3oh_concentration2);
put(j, "flame_detector1", v.flame_detector1);
put(j, "flame_detector2", v.flame_detector2);
put(j, "emergency_battery1_voltage", v.emergency_battery1_voltage);
put(j, "emergency_battery1_current", v.emergency_battery1_current);
put(j, "emergency_battery1_max_temp", v.emergency_battery1_max_temp);
put(j, "emergency_battery1_fault_word", v.emergency_battery1_fault_word);
put(j, "emergency_battery2_voltage", v.emergency_battery2_voltage);
put(j, "emergency_battery2_current", v.emergency_battery2_current);
put(j, "emergency_battery2_max_temp", v.emergency_battery2_max_temp);
put(j, "emergency_battery2_fault_word", v.emergency_battery2_fault_word);
put(j, "ins_cabin_ox_concentration", v.ins_cabin_ox_concentration);
put(j, "ins_cabin_temperature", v.ins_cabin_temperature);
put(j, "ins_cabin_humidity", v.ins_cabin_humidity);
put(j, "ins_cabin_pressure", v.ins_cabin_pressure);
put(j, "dyn_cabin_ox_concentration", v.dyn_cabin_ox_concentration);
put(j, "dyn_cabin_temperature", v.dyn_cabin_temperature);
put(j, "dyn_cabin_humidity", v.dyn_cabin_humidity);
put(j, "dyn_cabin_pressure", v.dyn_cabin_pressure);
put(j, "ins_relay_status1", v.ins_relay_status1);
put(j, "ins_relay_status2", v.ins_relay_status2);
put(j, "dyn_relay_status1", v.dyn_relay_status1);
put(j, "dyn_relay_status2", v.dyn_relay_status2);
put(j, "ins_max_discharge_power", v.ins_max_discharge_power);
put(j, "dyn_max_discharge_power", v.dyn_max_discharge_power);
put(j, "ins_soc", v.ins_soc);
put(j, "dyn_soc", v.dyn_soc);
put(j, "ins_total_energy", v.ins_total_energy);
put(j, "dyn_total_energy", v.dyn_total_energy);
put(j, "ins_power_input", v.ins_power_input);
put(j, "dyn_power_input", v.dyn_power_input);
put(j, "ins_charge_status", v.ins_charge_status);
put(j, "dyn_charge_status", v.dyn_charge_status);
put(j, "ins_voltage_link", v.ins_voltage_link);
put(j, "ins_voltage_pack", v.ins_voltage_pack);
put(j, "ins_current", v.ins_current);
put(j, "ins_resistance_pos", v.ins_resistance_pos);
put(j, "ins_resistance_neg", v.ins_resistance_neg);
put(j, "dyn_voltage_link", v.dyn_voltage_link);
put(j, "dyn_voltage_pack", v.dyn_voltage_pack);
put(j, "dyn_current", v.dyn_current);
put(j, "dyn_resistance_pos", v.dyn_resistance_pos);
put(j, "dyn_resistance_neg", v.dyn_resistance_neg);
put(j, "ins_emergency_status", v.ins_emergency_status);
put(j, "dyn_emergency_status", v.dyn_emergency_status);
put(j, "device_online_flag1", v.device_online_flag1);
put(j, "device_online_flag2", v.device_online_flag2);
put(j, "heartbeat", v.heartbeat);
put(j, "emergency_cmd", v.emergency_cmd);
return j;
}
JsonVal linkJson(const LinkManager* lm) {
JsonVal j(Json::objectValue);
if (!lm) return j;
j["rx"] = JsonVal(static_cast<Json::UInt>(lm->rxCount()));
j["tx"] = JsonVal(static_cast<Json::UInt>(lm->txCount()));
j["err"] = JsonVal(static_cast<Json::UInt>(lm->errorCount()));
j["lastRx"] = JsonVal(lm->lastRxTime());
return j;
}
} // namespace
SnapshotBuilder::SnapshotBuilder(SystemData* sys, LinkManager* fc, LinkManager* pm, DbStore* db)
: m_sys(sys), m_fc(fc), m_pm(pm), m_db(db) {}
std::string SnapshotBuilder::build() const {
JsonVal root(Json::objectValue);
if (m_sys) {
root["fc"] = fcStatusJson(m_sys->fcStatus());
root["pmCmd"] = pmControlJson(m_sys->pmControl());
root["fcCmd"] = fcControlJson(m_sys->fcControl());
root["pmParam"] = pmParamJson(m_sys->pmParamSet());
root["pmFb"] = pmFbJson(m_sys->pmParamFb());
root["pmStatus"] = pmStatusJson(m_sys->pmStatus());
root["fcStatusCount"] = JsonVal(static_cast<Json::UInt>(m_sys->fcStatusCount()));
root["pmControlCount"] = JsonVal(static_cast<Json::UInt>(m_sys->pmControlCount()));
root["fcControlCount"] = JsonVal(static_cast<Json::UInt>(m_sys->fcControlCount()));
root["pmStatusCount"] = JsonVal(static_cast<Json::UInt>(m_sys->pmStatusCount()));
} else {
root["fc"] = JsonVal(Json::objectValue);
root["pmCmd"] = JsonVal(Json::objectValue);
root["fcCmd"] = JsonVal(Json::objectValue);
root["pmParam"] = JsonVal(Json::objectValue);
root["pmFb"] = JsonVal(Json::objectValue);
root["pmStatus"] = JsonVal(Json::objectValue);
}
JsonVal links(Json::objectValue);
links["fc"] = linkJson(m_fc);
links["pm"] = linkJson(m_pm);
root["links"] = links;
// 最近原始帧(默认 20 条)
if (m_db) {
JsonVal logs(Json::arrayValue);
auto rows = m_db->queryRecent("", -1, 0, 20);
for (const auto& r : rows) {
logs.append(commLogToJson(r));
}
root["logs"] = logs;
}
Json::StreamWriterBuilder wb;
wb.settings_["indentation"] = "";
return Json::writeString(wb, root);
}
std::string SnapshotBuilder::buildLogs(int limit) const {
if (!m_db) return "[]";
JsonVal logs(Json::arrayValue);
auto rows = m_db->queryRecent("", -1, 0, limit);
for (const auto& r : rows) {
logs.append(commLogToJson(r));
}
Json::StreamWriterBuilder wb;
wb.settings_["indentation"] = "";
return Json::writeString(wb, logs);
}
} // namespace ccu