Files
H100PowerManger/src/pCCU/core/SnapshotBuilder.cpp
T
zjk c8becd1f6c 2026-08-27 调试完成版本
- FC 控制指令域修正:payload 14→18 字节,纵倾/横倾姿态拆分为数据1/数据2
- WebServer 广播改为待发队列模式,修复 mongoose 跨线程调用问题
- build-board.sh 增加板卡组网说明(/23 掩码方案)、pCCU 服务部署与 mission 同步
- 同步 0824 协议文档更新
2026-08-27 21:51:36 +08:00

382 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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["pitch1"] = JsonVal(static_cast<int>(v.pitch1)); // 有符号 int16,×0.1°
j["pitch2"] = JsonVal(static_cast<int>(v.pitch2));
j["roll1"] = JsonVal(static_cast<int>(v.roll1));
j["roll2"] = JsonVal(static_cast<int>(v.roll2));
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