- FC 控制指令域修正:payload 14→18 字节,纵倾/横倾姿态拆分为数据1/数据2 - WebServer 广播改为待发队列模式,修复 mongoose 跨线程调用问题 - build-board.sh 增加板卡组网说明(/23 掩码方案)、pCCU 服务部署与 mission 同步 - 同步 0824 协议文档更新
382 lines
16 KiB
C++
382 lines
16 KiB
C++
#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
|