#include "SnapshotBuilder.h" #include "../store/DbStore.h" #include "json/json.h" #include #include 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(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(v.pitch)); // 有符号 int16,×0.1° j["roll"] = JsonVal(static_cast(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(v.pitch1)); // 有符号 int16,×0.1° j["pitch2"] = JsonVal(static_cast(v.pitch2)); j["roll1"] = JsonVal(static_cast(v.roll1)); j["roll2"] = JsonVal(static_cast(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(lm->rxCount())); j["tx"] = JsonVal(static_cast(lm->txCount())); j["err"] = JsonVal(static_cast(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(m_sys->fcStatusCount())); root["pmControlCount"] = JsonVal(static_cast(m_sys->pmControlCount())); root["fcControlCount"] = JsonVal(static_cast(m_sys->fcControlCount())); root["pmStatusCount"] = JsonVal(static_cast(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