新增复合管控器(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
+82
View File
@@ -0,0 +1,82 @@
#ifndef PCCU_CHECKSUM_POLICY_H
#define PCCU_CHECKSUM_POLICY_H
#include <cstdint>
#include <cstddef>
#include <vector>
namespace ccu {
//============================================================================
// ChecksumPolicy:校验和策略(可插拔)。
//
// 协议联调阶段校验和的实现可能调整,因此对每条消息独立配置策略:
// - None : 无校验和字段(如 FC 状态帧文档未列校验行)
// - Sum32 : uint32 字节和,从域起始符到校验和之前所有数据的字节和
// - Sum16 : uint16 字节和(预留,备用)
// 新增策略只需在此类扩展,不影响其它代码。
//============================================================================
enum class ChecksumType {
None, // 无校验
Sum32, // uint32 字节和
Sum16, // uint16 字节和
};
class ChecksumPolicy {
public:
explicit ChecksumPolicy(ChecksumType type) : m_type(type) {}
ChecksumType type() const { return m_type; }
// 校验和字段占用的字节数;None 返回 0
size_t size() const {
switch (m_type) {
case ChecksumType::Sum32: return 4;
case ChecksumType::Sum16: return 2;
case ChecksumType::None: return 0;
}
return 0;
}
// 计算校验和:对 data[0 .. data.size()] 全部字节求和(调用方保证不含校验和自身)
uint32_t compute(const std::vector<uint8_t>& data) const {
uint32_t sum = 0;
for (size_t i = 0; i < data.size(); ++i) {
sum += data[i];
}
return sum;
}
// 校验给定帧(含校验和字段):返回帧尾校验和字段是否与计算值一致
// frame 必须包含从域起始符开始的完整帧。校验和位于末尾 size() 字节。
bool verify(const std::vector<uint8_t>& frame) const {
size_t cs = size();
if (cs == 0) return true; // 无校验,视为通过
if (frame.size() < cs) return false;
std::vector<uint8_t> body(frame.begin(), frame.end() - cs);
uint32_t expect = compute(body);
uint32_t actual = 0;
size_t offset = frame.size() - cs;
if (m_type == ChecksumType::Sum32) {
actual = static_cast<uint32_t>(frame[offset]) |
(static_cast<uint32_t>(frame[offset + 1]) << 8) |
(static_cast<uint32_t>(frame[offset + 2]) << 16) |
(static_cast<uint32_t>(frame[offset + 3]) << 24);
return expect == actual;
}
// Sum16
actual = static_cast<uint32_t>(frame[offset]) |
(static_cast<uint32_t>(frame[offset + 1]) << 8);
return (expect & 0xFFFF) == actual;
}
private:
ChecksumType m_type;
};
} // namespace ccu
#endif // PCCU_CHECKSUM_POLICY_H
+318
View File
@@ -0,0 +1,318 @@
#include "FcProtocol.h"
#include "FieldCodec.h"
#include "Frame.h"
#include "MessageRegistry.h"
namespace ccu {
void registerFcMessages(MessageRegistry& reg) {
reg.registerMessage(std::unique_ptr<Message>(new FcControlMessage()));
reg.registerMessage(std::unique_ptr<Message>(new FcStatusMessage()));
}
//============================================================================
// 0x0001 控制指令域编解码
// 数据域 14 字节,偏移 0~13(对应文档字节 7~20)
//============================================================================
std::vector<uint8_t> FcControlMessage::encode(const void* obj) const {
const FcControlValue* v = static_cast<const FcControlValue*>(obj);
std::vector<uint8_t> payload(payloadLength(), 0);
FieldCodec::putU8(payload, 0, v->mode);
FieldCodec::putU8(payload, 1, v->cmd);
FieldCodec::putU8(payload, 2, v->outputPower);
FieldCodec::putU16(payload, 3, static_cast<uint16_t>(v->pitch)); // 数据1=低位 / 数据2=高位
FieldCodec::putU16(payload, 5, static_cast<uint16_t>(v->roll));
FieldCodec::putU8(payload, 7, v->emergencyAllow);
FieldCodec::putU16(payload, 8, v->depth);
FieldCodec::putU8(payload, 10, v->supplyCmd);
FieldCodec::putU8(payload, 11, v->reservedCmd5);
FieldCodec::putU8(payload, 12, v->reservedCmd6);
FieldCodec::putU8(payload, 13, v->heartbeat);
return Frame::build(id(), payload, checksum());
}
bool FcControlMessage::decode(const std::vector<uint8_t>& frame, void* obj) const {
if (!validateFrame(frame, id(), checksum(), totalLength())) return false;
FcControlValue* v = static_cast<FcControlValue*>(obj);
size_t o = FRAME_HEADER_LEN; // 数据域起点
v->mode = FieldCodec::getU8(frame, o + 0);
v->cmd = FieldCodec::getU8(frame, o + 1);
v->outputPower = FieldCodec::getU8(frame, o + 2);
v->pitch = static_cast<int16_t>(FieldCodec::getU16(frame, o + 3));
v->roll = static_cast<int16_t>(FieldCodec::getU16(frame, o + 5));
v->emergencyAllow = FieldCodec::getU8(frame, o + 7);
v->depth = FieldCodec::getU16(frame, o + 8);
v->supplyCmd = FieldCodec::getU8(frame, o + 10);
v->reservedCmd5 = FieldCodec::getU8(frame, o + 11);
v->reservedCmd6 = FieldCodec::getU8(frame, o + 12);
v->heartbeat = FieldCodec::getU8(frame, o + 13);
return true;
}
//============================================================================
// 0x0002 状态反馈域编解码
// 数据域 144 字节
//============================================================================
namespace {
// 数据域内各字段偏移(相对数据域起点,offset = 文档字节号 - 7)
// 注意:文档中"系统故障等级"为字节19(1字节),字节20为隐式保留位,
// "系统发电功率"从字节21(offset 14)开始,其后的字段均按此定位。
enum : size_t {
OFF_MODE = 0, // 运行模式 (字节7)
OFF_STATUS = 1, // 运行状态 (字节8)
OFF_FL1 = 2, // 一级故障码 u16 (字节9-10)
OFF_FL2 = 4, // 二级故障码 (字节11-12)
OFF_FL3 = 6, // 三级故障码 (字节13-14)
OFF_FL4 = 8, // 四级故障码 (字节15-16)
OFF_GEN_TIME = 10, // 累积发电时间 u16 (字节17-18)
OFF_FAULT_LEVEL = 12, // 系统故障等级 (字节19)
// 字节20 隐式保留(offset 13)
OFF_GEN_POWER = 14, // 系统发电功率 u16 (字节21-22)
OFF_H2_CAP = 16, // 储氢剩余容量 (字节23)
OFF_LO2_CAP = 17, // 液氧剩余容量 (字节24)
OFF_FC1_MIN_V = 18, // I#FC最低单片电压 (字节25)
OFF_FC1_MIN_POS = 19, // I#FC最低单片电压位置 (字节26)
OFF_FC1_AVG_V = 20, // I#FC平均单片电压 (字节27)
OFF_FC2_MIN_V = 21, // 2#FC最低单片电压 (字节28)
OFF_FC2_MIN_POS = 22, // 2#FC最低单片电压位置 (字节29)
OFF_FC2_AVG_V = 23, // 2#FC平均单片电压 (字节30)
OFF_PALLADIUM_TEMP = 24, // 钯膜最高温度 u16 (字节31-32)
OFF_BUFFER_PRES = 26, // 缓冲罐压力 (字节33-34)
OFF_FLUE_TOTAL = 28, // 烟气累计排放量 (字节35-36)
OFF_FLUE_PRES = 30, // 烟气压力 (字节37-38)
OFF_REACTOR_PRES = 32, // 反应器压力 (字节39-40)
OFF_EVALVE_OPEN = 34, // 电动阀开度 (字节41-42)
OFF_DCDC1_IN_V = 36, // DC/DC通道1输入电压 (字节43-44)
OFF_DCDC1_IN_I = 38, // DC/DC通道1输入电流 (字节45-46)
OFF_DCDC2_IN_V = 40, // DC/DC通道2输入电压 (字节47-48)
OFF_DCDC2_IN_I = 42, // DC/DC通道2输入电流 (字节49-50)
OFF_DCDC_OUT_V = 44, // DC/DC输出电压 (字节51-52)
OFF_DCDC_OUT_I = 46, // DC/DC输出电流 (字节53-54)
OFF_DCDC_CTRL_V = 48, // DC/DC控制电源电压 (字节55)
OFF_DCDC_AUX_V = 49, // DC/DC辅电输出电压 (字节56)
OFF_METHANOL_TOTAL = 50, // 甲醇累计使用量 u16 (字节57-58)
OFF_METHANOL_FEED = 52, // 甲醇溶液进料量 (字节59-60)
OFF_O2_WATER = 54, // 氧侧生成水箱液位 (字节61-62)
OFF_H2_WATER = 56, // 氢侧生成水箱液位 (字节63-64)
OFF_BALLAST_WATER = 58, // 配重水箱液位 (字节65-66)
OFF_EXH_IN_PRES = 60, // 尾气装置进气压力 (字节67-68)
OFF_EXH_OUT_PRES = 62, // 尾气装置排气压力 (字节69-70)
OFF_CABIN_P1 = 64, // 舱室压力1 (字节71-72)
OFF_CABIN_P2 = 66, // 舱室压力2 (字节73-74)
OFF_CABIN_T1 = 68, // 舱室温度1 (字节75-76)
OFF_CABIN_T2 = 70, // 舱室温度2 (字节77-78)
OFF_CABIN_H1 = 72, // 舱室湿度1 (字节79-80)
OFF_CABIN_H2 = 74, // 舱室湿度2 (字节81-82)
OFF_H2_C1 = 76, // 舱室H2浓度1 (字节83-84)
OFF_H2_C2 = 78, // 舱室H2浓度2 (字节85-86)
OFF_H2_C3 = 80, // 舱室H2浓度3 (字节87-88)
OFF_O2_C1 = 82, // 舱室O2浓度1 (字节89-90)
OFF_O2_C2 = 84, // 舱室O2浓度2 (字节91-92)
OFF_CH3OH_C1 = 86, // 舱室甲醇浓度1 (字节93-94)
OFF_CH3OH_C2 = 88, // 舱室甲醇浓度2 (字节95-96)
OFF_FLAME1 = 90, // 火焰探测器1 (字节97)
OFF_FLAME2 = 91, // 火焰探测器2 (字节98)
OFF_EMER_DEPTH = 92, // 应急上浮深度 u16 (字节99-100)
OFF_EMER_TIME = 94, // 应急上浮时间 (字节101-102)
OFF_EXH_FREQ = 96, // 尾气运行频率 (字节103-104)
OFF_EXH_IN_TEMP = 98, // 尾气进气温度 (字节105-106)
OFF_EXH_OUT_TEMP = 100, // 尾气排气温度 (字节107-108)
OFF_EXH_WIN_PRES = 102, // 尾气进水压力 (字节109-110)
OFF_EXH_WOUT_PRES = 104, // 尾气排水压力 (字节111-112)
OFF_TANK_LO2_PRES = 106, // 液氧罐压力 (字节113-114)
OFF_TANK_CO2_PRES = 108, // 二氧化碳压力 (字节115-116)
OFF_TANK_LO2_LEVEL = 110, // 液氧罐液位 (字节117-118)
OFF_ALLOY_H2_FLOW = 112, // 合金供氢流量 (字节119-120)
OFF_FC_H2_FLOW = 114, // FC供氢流量 (字节121-122)
OFF_FC_O2_FLOW = 116, // FC供氧流量 (字节123-124)
OFF_RESERVED = 118, // 预留1~12 u16*12 -> 118..141 (字节125-148)
OFF_HEARTBEAT = 142, // 通信心跳 (字节149)
OFF_EMERGENCY_CMD = 143, // 应急指令 (字节150)
};
inline void getU16Arr(const std::vector<uint8_t>& frame, size_t off, uint16_t* dst, size_t n) {
for (size_t i = 0; i < n; ++i) dst[i] = FieldCodec::getU16(frame, off + i * 2);
}
inline void putU16Arr(std::vector<uint8_t>& p, size_t off, const uint16_t* src, size_t n) {
for (size_t i = 0; i < n; ++i) FieldCodec::putU16(p, off + i * 2, src[i]);
}
} // namespace
std::vector<uint8_t> FcStatusMessage::encode(const void* obj) const {
const FcStatusValue* v = static_cast<const FcStatusValue*>(obj);
std::vector<uint8_t> p(payloadLength(), 0);
FieldCodec::putU8(p, OFF_MODE, v->fc_mode);
FieldCodec::putU8(p, OFF_STATUS, v->fc_status);
FieldCodec::putU16(p, OFF_FL1, v->fault_level_1);
FieldCodec::putU16(p, OFF_FL2, v->fault_level_2);
FieldCodec::putU16(p, OFF_FL3, v->fault_level_3);
FieldCodec::putU16(p, OFF_FL4, v->fault_level_4);
FieldCodec::putU16(p, OFF_GEN_TIME, v->total_generation_time);
FieldCodec::putU8(p, OFF_FAULT_LEVEL, v->fc_fault_level);
FieldCodec::putU16(p, OFF_GEN_POWER, v->generation_power);
FieldCodec::putU8(p, OFF_H2_CAP, v->hydrogen_capacity);
FieldCodec::putU8(p, OFF_LO2_CAP, v->liquid_oxygen_capacity);
FieldCodec::putU8(p, OFF_FC1_MIN_V, v->fc1_min_cell_voltage);
FieldCodec::putU8(p, OFF_FC1_MIN_POS, v->fc1_min_cell_pos);
FieldCodec::putU8(p, OFF_FC1_AVG_V, v->fc1_avg_cell_voltage);
FieldCodec::putU8(p, OFF_FC2_MIN_V, v->fc2_min_cell_voltage);
FieldCodec::putU8(p, OFF_FC2_MIN_POS, v->fc2_min_cell_pos);
FieldCodec::putU8(p, OFF_FC2_AVG_V, v->fc2_avg_cell_voltage);
FieldCodec::putU16(p, OFF_PALLADIUM_TEMP, v->palladium_temp);
FieldCodec::putU16(p, OFF_BUFFER_PRES, v->buffer_tank_pressure);
FieldCodec::putU16(p, OFF_FLUE_TOTAL, v->flue_total_emission);
FieldCodec::putU16(p, OFF_FLUE_PRES, v->flue_pressure);
FieldCodec::putU16(p, OFF_REACTOR_PRES, v->reactor_pressure);
FieldCodec::putU16(p, OFF_EVALVE_OPEN, v->electric_valve_open);
FieldCodec::putU16(p, OFF_DCDC1_IN_V, v->dcdc1_in_voltage);
FieldCodec::putU16(p, OFF_DCDC1_IN_I, v->dcdc1_in_current);
FieldCodec::putU16(p, OFF_DCDC2_IN_V, v->dcdc2_in_voltage);
FieldCodec::putU16(p, OFF_DCDC2_IN_I, v->dcdc2_in_current);
FieldCodec::putU16(p, OFF_DCDC_OUT_V, v->dcdc_out_voltage);
FieldCodec::putU16(p, OFF_DCDC_OUT_I, v->dcdc_out_current);
FieldCodec::putU8(p, OFF_DCDC_CTRL_V, v->dcdc_ctrl_voltage);
FieldCodec::putU8(p, OFF_DCDC_AUX_V, v->dcdc_aux_voltage);
FieldCodec::putU16(p, OFF_METHANOL_TOTAL, v->methanol_total_use);
FieldCodec::putU16(p, OFF_METHANOL_FEED, v->methanol_feed);
FieldCodec::putU16(p, OFF_O2_WATER, v->oxygen_side_water_level);
FieldCodec::putU16(p, OFF_H2_WATER, v->hydrogen_side_water_level);
FieldCodec::putU16(p, OFF_BALLAST_WATER, v->ballast_water_level);
FieldCodec::putU16(p, OFF_EXH_IN_PRES, v->exhaust_inlet_pressure);
FieldCodec::putU16(p, OFF_EXH_OUT_PRES, v->exhaust_outlet_pressure);
FieldCodec::putU16(p, OFF_CABIN_P1, v->cabin_pressure1);
FieldCodec::putU16(p, OFF_CABIN_P2, v->cabin_pressure2);
FieldCodec::putU16(p, OFF_CABIN_T1, v->cabin_temp1);
FieldCodec::putU16(p, OFF_CABIN_T2, v->cabin_temp2);
FieldCodec::putU16(p, OFF_CABIN_H1, v->cabin_humidity1);
FieldCodec::putU16(p, OFF_CABIN_H2, v->cabin_humidity2);
FieldCodec::putU16(p, OFF_H2_C1, v->h2_concentration1);
FieldCodec::putU16(p, OFF_H2_C2, v->h2_concentration2);
FieldCodec::putU16(p, OFF_H2_C3, v->h2_concentration3);
FieldCodec::putU16(p, OFF_O2_C1, v->o2_concentration1);
FieldCodec::putU16(p, OFF_O2_C2, v->o2_concentration2);
FieldCodec::putU16(p, OFF_CH3OH_C1, v->ch3oh_concentration1);
FieldCodec::putU16(p, OFF_CH3OH_C2, v->ch3oh_concentration2);
FieldCodec::putU8(p, OFF_FLAME1, v->flame_detector1);
FieldCodec::putU8(p, OFF_FLAME2, v->flame_detector2);
FieldCodec::putU16(p, OFF_EMER_DEPTH, v->emergency_float_depth);
FieldCodec::putU16(p, OFF_EMER_TIME, v->emergency_float_time);
FieldCodec::putU16(p, OFF_EXH_FREQ, v->exhaust_run_freq);
FieldCodec::putU16(p, OFF_EXH_IN_TEMP, v->exhaust_inlet_temp);
FieldCodec::putU16(p, OFF_EXH_OUT_TEMP, v->exhaust_outlet_temp);
FieldCodec::putU16(p, OFF_EXH_WIN_PRES, v->exhaust_water_in_pressure);
FieldCodec::putU16(p, OFF_EXH_WOUT_PRES, v->exhaust_water_out_pressure);
FieldCodec::putU16(p, OFF_TANK_LO2_PRES, v->tank_lo2_pressure);
FieldCodec::putU16(p, OFF_TANK_CO2_PRES, v->tank_co2_pressure);
FieldCodec::putU16(p, OFF_TANK_LO2_LEVEL, v->tank_lo2_level);
FieldCodec::putU16(p, OFF_ALLOY_H2_FLOW, v->alloy_h2_flow);
FieldCodec::putU16(p, OFF_FC_H2_FLOW, v->fc_h2_flow);
FieldCodec::putU16(p, OFF_FC_O2_FLOW, v->fc_o2_flow);
putU16Arr(p, OFF_RESERVED, v->reserved, 12);
FieldCodec::putU8(p, OFF_HEARTBEAT, v->heartbeat);
FieldCodec::putU8(p, OFF_EMERGENCY_CMD, v->emergency_cmd);
return Frame::build(id(), p, checksum());
}
bool FcStatusMessage::decode(const std::vector<uint8_t>& frame, void* obj) const {
if (!validateFrame(frame, id(), checksum(), totalLength())) return false;
FcStatusValue* v = static_cast<FcStatusValue*>(obj);
size_t o = FRAME_HEADER_LEN;
v->fc_mode = FieldCodec::getU8(frame, o + OFF_MODE);
v->fc_status = FieldCodec::getU8(frame, o + OFF_STATUS);
v->fault_level_1 = FieldCodec::getU16(frame, o + OFF_FL1);
v->fault_level_2 = FieldCodec::getU16(frame, o + OFF_FL2);
v->fault_level_3 = FieldCodec::getU16(frame, o + OFF_FL3);
v->fault_level_4 = FieldCodec::getU16(frame, o + OFF_FL4);
v->total_generation_time = FieldCodec::getU16(frame, o + OFF_GEN_TIME);
v->fc_fault_level = FieldCodec::getU8(frame, o + OFF_FAULT_LEVEL);
v->generation_power = FieldCodec::getU16(frame, o + OFF_GEN_POWER);
v->hydrogen_capacity = FieldCodec::getU8(frame, o + OFF_H2_CAP);
v->liquid_oxygen_capacity = FieldCodec::getU8(frame, o + OFF_LO2_CAP);
v->fc1_min_cell_voltage = FieldCodec::getU8(frame, o + OFF_FC1_MIN_V);
v->fc1_min_cell_pos = FieldCodec::getU8(frame, o + OFF_FC1_MIN_POS);
v->fc1_avg_cell_voltage = FieldCodec::getU8(frame, o + OFF_FC1_AVG_V);
v->fc2_min_cell_voltage = FieldCodec::getU8(frame, o + OFF_FC2_MIN_V);
v->fc2_min_cell_pos = FieldCodec::getU8(frame, o + OFF_FC2_MIN_POS);
v->fc2_avg_cell_voltage = FieldCodec::getU8(frame, o + OFF_FC2_AVG_V);
v->palladium_temp = FieldCodec::getU16(frame, o + OFF_PALLADIUM_TEMP);
v->buffer_tank_pressure = FieldCodec::getU16(frame, o + OFF_BUFFER_PRES);
v->flue_total_emission = FieldCodec::getU16(frame, o + OFF_FLUE_TOTAL);
v->flue_pressure = FieldCodec::getU16(frame, o + OFF_FLUE_PRES);
v->reactor_pressure = FieldCodec::getU16(frame, o + OFF_REACTOR_PRES);
v->electric_valve_open = FieldCodec::getU16(frame, o + OFF_EVALVE_OPEN);
v->dcdc1_in_voltage = FieldCodec::getU16(frame, o + OFF_DCDC1_IN_V);
v->dcdc1_in_current = FieldCodec::getU16(frame, o + OFF_DCDC1_IN_I);
v->dcdc2_in_voltage = FieldCodec::getU16(frame, o + OFF_DCDC2_IN_V);
v->dcdc2_in_current = FieldCodec::getU16(frame, o + OFF_DCDC2_IN_I);
v->dcdc_out_voltage = FieldCodec::getU16(frame, o + OFF_DCDC_OUT_V);
v->dcdc_out_current = FieldCodec::getU16(frame, o + OFF_DCDC_OUT_I);
v->dcdc_ctrl_voltage = FieldCodec::getU8(frame, o + OFF_DCDC_CTRL_V);
v->dcdc_aux_voltage = FieldCodec::getU8(frame, o + OFF_DCDC_AUX_V);
v->methanol_total_use = FieldCodec::getU16(frame, o + OFF_METHANOL_TOTAL);
v->methanol_feed = FieldCodec::getU16(frame, o + OFF_METHANOL_FEED);
v->oxygen_side_water_level= FieldCodec::getU16(frame, o + OFF_O2_WATER);
v->hydrogen_side_water_level = FieldCodec::getU16(frame, o + OFF_H2_WATER);
v->ballast_water_level = FieldCodec::getU16(frame, o + OFF_BALLAST_WATER);
v->exhaust_inlet_pressure = FieldCodec::getU16(frame, o + OFF_EXH_IN_PRES);
v->exhaust_outlet_pressure= FieldCodec::getU16(frame, o + OFF_EXH_OUT_PRES);
v->cabin_pressure1 = FieldCodec::getU16(frame, o + OFF_CABIN_P1);
v->cabin_pressure2 = FieldCodec::getU16(frame, o + OFF_CABIN_P2);
v->cabin_temp1 = FieldCodec::getU16(frame, o + OFF_CABIN_T1);
v->cabin_temp2 = FieldCodec::getU16(frame, o + OFF_CABIN_T2);
v->cabin_humidity1 = FieldCodec::getU16(frame, o + OFF_CABIN_H1);
v->cabin_humidity2 = FieldCodec::getU16(frame, o + OFF_CABIN_H2);
v->h2_concentration1 = FieldCodec::getU16(frame, o + OFF_H2_C1);
v->h2_concentration2 = FieldCodec::getU16(frame, o + OFF_H2_C2);
v->h2_concentration3 = FieldCodec::getU16(frame, o + OFF_H2_C3);
v->o2_concentration1 = FieldCodec::getU16(frame, o + OFF_O2_C1);
v->o2_concentration2 = FieldCodec::getU16(frame, o + OFF_O2_C2);
v->ch3oh_concentration1 = FieldCodec::getU16(frame, o + OFF_CH3OH_C1);
v->ch3oh_concentration2 = FieldCodec::getU16(frame, o + OFF_CH3OH_C2);
v->flame_detector1 = FieldCodec::getU8(frame, o + OFF_FLAME1);
v->flame_detector2 = FieldCodec::getU8(frame, o + OFF_FLAME2);
v->emergency_float_depth = FieldCodec::getU16(frame, o + OFF_EMER_DEPTH);
v->emergency_float_time = FieldCodec::getU16(frame, o + OFF_EMER_TIME);
v->exhaust_run_freq = FieldCodec::getU16(frame, o + OFF_EXH_FREQ);
v->exhaust_inlet_temp = FieldCodec::getU16(frame, o + OFF_EXH_IN_TEMP);
v->exhaust_outlet_temp = FieldCodec::getU16(frame, o + OFF_EXH_OUT_TEMP);
v->exhaust_water_in_pressure = FieldCodec::getU16(frame, o + OFF_EXH_WIN_PRES);
v->exhaust_water_out_pressure= FieldCodec::getU16(frame, o + OFF_EXH_WOUT_PRES);
v->tank_lo2_pressure = FieldCodec::getU16(frame, o + OFF_TANK_LO2_PRES);
v->tank_co2_pressure = FieldCodec::getU16(frame, o + OFF_TANK_CO2_PRES);
v->tank_lo2_level = FieldCodec::getU16(frame, o + OFF_TANK_LO2_LEVEL);
v->alloy_h2_flow = FieldCodec::getU16(frame, o + OFF_ALLOY_H2_FLOW);
v->fc_h2_flow = FieldCodec::getU16(frame, o + OFF_FC_H2_FLOW);
v->fc_o2_flow = FieldCodec::getU16(frame, o + OFF_FC_O2_FLOW);
getU16Arr(frame, o + OFF_RESERVED, v->reserved, 12);
v->heartbeat = FieldCodec::getU8(frame, o + OFF_HEARTBEAT);
v->emergency_cmd = FieldCodec::getU8(frame, o + OFF_EMERGENCY_CMD);
return true;
}
} // namespace ccu
+174
View File
@@ -0,0 +1,174 @@
#ifndef PCCU_FC_PROTOCOL_H
#define PCCU_FC_PROTOCOL_H
#include <cstdint>
#include <vector>
#include <memory>
#include "Message.h"
namespace ccu {
//============================================================================
// FC 协议(燃料电池系统 <-> 复合管控器)
// 依据:docs/超滑FC和复合管控器通讯协议 - 0821.docx
//
// 帧格式统一:0x40 0x40 + 域标识符(2B) + 域字节数(2B) + 数据域 + [校验和]
// 注意:FC 协议文档未列出校验和行,故默认采用 None(可插拔,联调可切换)。
//
// 两条消息:
// 0x0001 复合管控器->燃料电池控制器(控制指令域) payload 14B,总长 20B
// 0x0002 燃料电池控制器->复合管控器(状态反馈域) payload 144B,总长 150B
//============================================================================
//--------------------------------------------------------------------------
// 0x0001 控制指令域(复合管控器 -> 燃料电池)
// payload 14 字节,对应文档字节 7~20
//--------------------------------------------------------------------------
struct FcControlValue {
uint8_t mode = 0; // 模式设定 00~03
uint8_t cmd = 0; // 操控指令 00~0B
uint8_t outputPower = 0; // 输出功率指令 0~250,分辨率0.1kW
int16_t pitch = 0; // 纵倾姿态(数据1=低8位 / 数据2=高8位,int16),分辨率0.1°
int16_t roll = 0; // 横倾姿态(数据1=低8位 / 数据2=高8位,int16),分辨率0.1°
uint8_t emergencyAllow = 0; // 应急允许:Bit0 允许降载 / Bit1 允许排气
uint16_t depth = 0; // 潜深深度,单位 m
uint8_t supplyCmd = 0; // 补给/排放指令 00~0B
uint8_t reservedCmd5 = 0; // 预留指令5
uint8_t reservedCmd6 = 0; // 预留指令6
uint8_t heartbeat = 0; // 通信心跳,每次+1
};
//--------------------------------------------------------------------------
// 0x0002 状态反馈域(燃料电池 -> 复合管控器)
// payload 144 字节,对应文档字节 7~150
//--------------------------------------------------------------------------
struct FcStatusValue {
uint8_t fc_mode = 0; // 燃料电池系统运行模式
uint8_t fc_status = 0; // 燃料电池系统运行状态
uint16_t fault_level_1 = 0; // 一级故障码
uint16_t fault_level_2 = 0; // 二级故障码
uint16_t fault_level_3 = 0; // 三级故障码
uint16_t fault_level_4 = 0; // 四级故障码
uint16_t total_generation_time = 0;// 累积发电时间,0.1h
uint8_t fc_fault_level = 0; // 系统故障等级 00~04
uint16_t generation_power = 0; // 系统发电功率,0.01kW
uint8_t hydrogen_capacity = 0; // 储氢剩余容量 %
uint8_t liquid_oxygen_capacity = 0; // 液氧剩余容量 %
uint8_t fc1_min_cell_voltage = 0; // I#FC最低单片电压,10mV
uint8_t fc1_min_cell_pos = 0; // I#FC最低单片电压位置
uint8_t fc1_avg_cell_voltage = 0; // I#FC平均单片电压,10mV
uint8_t fc2_min_cell_voltage = 0; // 2#FC最低单片电压
uint8_t fc2_min_cell_pos = 0; // 2#FC最低单片电压位置
uint8_t fc2_avg_cell_voltage = 0; // 2#FC平均单片电压
uint16_t palladium_temp = 0; // 甲醇制氢装置钯膜最高温度,0.1℃
uint16_t buffer_tank_pressure = 0; // 缓冲罐压力,0.1kPa
uint16_t flue_total_emission = 0; // 烟气累计排放量,0.1kg
uint16_t flue_pressure = 0; // 烟气压力,0.001MPa
uint16_t reactor_pressure = 0; // 反应器压力,0.001MPa
uint16_t electric_valve_open = 0; // 电动阀开度,0.1%
uint16_t dcdc1_in_voltage = 0; // DC/DC通道1输入电压,0.1V
uint16_t dcdc1_in_current = 0; // DC/DC通道1输入电流,0.1A
uint16_t dcdc2_in_voltage = 0; // DC/DC通道2输入电压,0.1V
uint16_t dcdc2_in_current = 0; // DC/DC通道2输入电流,0.1A
uint16_t dcdc_out_voltage = 0; // DC/DC输出电压,0.1V
uint16_t dcdc_out_current = 0; // DC/DC输出电流,0.1A
uint8_t dcdc_ctrl_voltage = 0; // DC/DC控制电源电压,0.25V
uint8_t dcdc_aux_voltage = 0; // DC/DC辅电输出电压,0.125V
uint16_t methanol_total_use = 0; // 甲醇累计使用量,0.1kg
uint16_t methanol_feed = 0; // 甲醇溶液进料量,1mL/min
uint16_t oxygen_side_water_level = 0; // 氧侧生成水箱液位,0.01mm
uint16_t hydrogen_side_water_level = 0;// 氢侧生成水箱液位,0.01mm
uint16_t ballast_water_level = 0; // 配重水箱液位,0.01mm
uint16_t exhaust_inlet_pressure = 0; // 尾气装置进气压力,0.01MPa
uint16_t exhaust_outlet_pressure = 0; // 尾气装置排气压力,0.01MPa
uint16_t cabin_pressure1 = 0; // 舱室压力1,0.01kPa
uint16_t cabin_pressure2 = 0; // 舱室压力2,0.01kPa
uint16_t cabin_temp1 = 0; // 舱室温度1,0.01℃
uint16_t cabin_temp2 = 0; // 舱室温度2,0.01℃
uint16_t cabin_humidity1 = 0; // 舱室湿度1,0.01%RH
uint16_t cabin_humidity2 = 0; // 舱室湿度2,0.01%RH
uint16_t h2_concentration1 = 0; // 舱室H2浓度1,0.01%LEL
uint16_t h2_concentration2 = 0; // 舱室H2浓度2,0.01%LEL
uint16_t h2_concentration3 = 0; // 舱室H2浓度3,0.01%LEL
uint16_t o2_concentration1 = 0; // 舱室O2浓度1,0.01%Vol
uint16_t o2_concentration2 = 0; // 舱室O2浓度2,0.01%Vol
uint16_t ch3oh_concentration1 = 0; // 舱室甲醇浓度1,0.01%LEL
uint16_t ch3oh_concentration2 = 0; // 舱室甲醇浓度2,0.01%LEL
uint8_t flame_detector1 = 0; // 火焰探测器1状态
uint8_t flame_detector2 = 0; // 火焰探测器2状态
uint16_t emergency_float_depth = 0; // 应急上浮深度,1m
uint16_t emergency_float_time = 0; // 应急上浮时间,1min
uint16_t exhaust_run_freq = 0; // 尾气装置运行频率,0.01Hz
uint16_t exhaust_inlet_temp = 0; // 尾气装置进气温度,0.01℃
uint16_t exhaust_outlet_temp = 0; // 尾气装置排气温度,0.01℃
uint16_t exhaust_water_in_pressure = 0;// 尾气装置进水压力,0.01kPa
uint16_t exhaust_water_out_pressure = 0;// 尾气装置排水压力,0.01kPa
uint16_t tank_lo2_pressure = 0; // 一体化罐装置液氧罐压力,0.01MPa
uint16_t tank_co2_pressure = 0; // 一体化罐装置二氧化碳压力,0.01MPa
uint16_t tank_lo2_level = 0; // 一体化罐装置液氧罐液位,0.01mm
uint16_t alloy_h2_flow = 0; // 合金供氢流量,0.01L/min
uint16_t fc_h2_flow = 0; // FC供氢流量,0.01L/min
uint16_t fc_o2_flow = 0; // FC供氧流量,0.01L/min
uint16_t reserved[12] = {0}; // 预留1~12
uint8_t heartbeat = 0; // 通信心跳,每次+1
uint8_t emergency_cmd = 0; // 应急指令:Bit0 降载/Bit1 上浮/...
};
//--------------------------------------------------------------------------
// 消息类
//--------------------------------------------------------------------------
class FcControlMessage : public Message {
public:
uint16_t id() const override { return 0x0001; }
const char* name() const override { return "fc_control"; }
const ChecksumPolicy& checksum() const override { return m_checksum; }
size_t payloadLength() const override { return 14; }
std::vector<uint8_t> encode(const void* obj) const override;
bool decode(const std::vector<uint8_t>& frame, void* obj) const override;
// 便捷重载
std::vector<uint8_t> encode(const FcControlValue& v) const { return encode(&v); }
bool decode(const std::vector<uint8_t>& frame, FcControlValue& v) const {
return decode(frame, static_cast<void*>(&v));
}
private:
ChecksumPolicy m_checksum{ChecksumType::None};
};
class FcStatusMessage : public Message {
public:
uint16_t id() const override { return 0x0002; }
const char* name() const override { return "fc_status"; }
const ChecksumPolicy& checksum() const override { return m_checksum; }
size_t payloadLength() const override { return 144; }
std::vector<uint8_t> encode(const void* obj) const override;
bool decode(const std::vector<uint8_t>& frame, void* obj) const override;
std::vector<uint8_t> encode(const FcStatusValue& v) const { return encode(&v); }
bool decode(const std::vector<uint8_t>& frame, FcStatusValue& v) const {
return decode(frame, static_cast<void*>(&v));
}
private:
ChecksumPolicy m_checksum{ChecksumType::None};
};
// 注册 FC 消息到给定注册表(见 MessageRegistry.h)
void registerFcMessages(class MessageRegistry& reg);
} // namespace ccu
#endif // PCCU_FC_PROTOCOL_H
+58
View File
@@ -0,0 +1,58 @@
#ifndef PCCU_FIELD_CODEC_H
#define PCCU_FIELD_CODEC_H
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include <stdexcept>
namespace ccu {
//============================================================================
// FieldCodec:小端序(Little-Endian)编解码原语。
//
// 两条协议(FC 协议 / PM 协议)均采用:
// - 多字节数据小端序
// - 域起始符 0x40 0x40
// - 域字节数 = 整个协议包的数据长度
// 本类提供对裸缓冲区的字段读写,不关心具体业务含义。
//============================================================================
class FieldCodec {
public:
// 在 dst 偏移 offset 处写入单字节
static void putU8(std::vector<uint8_t>& dst, size_t offset, uint8_t v) {
dst[offset] = v;
}
// 小端写入 2 字节
static void putU16(std::vector<uint8_t>& dst, size_t offset, uint16_t v) {
dst[offset] = static_cast<uint8_t>(v & 0xFF);
dst[offset + 1] = static_cast<uint8_t>((v >> 8) & 0xFF);
}
// 小端写入 4 字节
static void putU32(std::vector<uint8_t>& dst, size_t offset, uint32_t v) {
dst[offset] = static_cast<uint8_t>(v & 0xFF);
dst[offset + 1] = static_cast<uint8_t>((v >> 8) & 0xFF);
dst[offset + 2] = static_cast<uint8_t>((v >> 16) & 0xFF);
dst[offset + 3] = static_cast<uint8_t>((v >> 24) & 0xFF);
}
static uint8_t getU8 (const std::vector<uint8_t>& src, size_t offset) {
return src[offset];
}
static uint16_t getU16(const std::vector<uint8_t>& src, size_t offset) {
return static_cast<uint16_t>(src[offset]) |
(static_cast<uint16_t>(src[offset + 1]) << 8);
}
static uint32_t getU32(const std::vector<uint8_t>& src, size_t offset) {
return static_cast<uint32_t>(src[offset]) |
(static_cast<uint32_t>(src[offset + 1]) << 8) |
(static_cast<uint32_t>(src[offset + 2]) << 16) |
(static_cast<uint32_t>(src[offset + 3]) << 24);
}
};
} // namespace ccu
#endif // PCCU_FIELD_CODEC_H
+50
View File
@@ -0,0 +1,50 @@
#include "Frame.h"
#include "FieldCodec.h"
namespace ccu {
bool Frame::parseId(const std::vector<uint8_t>& buf, uint16_t& outId) {
if (buf.size() < FRAME_HEADER_LEN) return false;
if (buf[0] != FRAME_START1 || buf[1] != FRAME_START2) return false;
outId = FieldCodec::getU16(buf, 2);
return true;
}
bool Frame::parseHeader(const std::vector<uint8_t>& buf, FrameHeader& h) {
if (buf.size() < FRAME_HEADER_LEN) return false;
if (buf[0] != FRAME_START1 || buf[1] != FRAME_START2) return false;
h.start1 = buf[0];
h.start2 = buf[1];
h.id = FieldCodec::getU16(buf, 2);
h.length = FieldCodec::getU16(buf, 4);
return true;
}
std::vector<uint8_t> Frame::build(
uint16_t id,
const std::vector<uint8_t>& payload,
const ChecksumPolicy& checksum) {
size_t csSize = checksum.size();
size_t total = FRAME_HEADER_LEN + payload.size() + csSize;
std::vector<uint8_t> frame(total, 0);
frame[0] = FRAME_START1;
frame[1] = FRAME_START2;
FieldCodec::putU16(frame, 2, id);
FieldCodec::putU16(frame, 4, static_cast<uint16_t>(total)); // 域字节数=整个包长度
std::copy(payload.begin(), payload.end(), frame.begin() + FRAME_HEADER_LEN);
if (csSize > 0) {
// 校验和:对起始符到校验和之前所有字节求和
std::vector<uint8_t> body(frame.begin(), frame.end() - csSize);
uint32_t sum = checksum.compute(body);
size_t off = frame.size() - csSize;
if (csSize == 4) FieldCodec::putU32(frame, off, sum);
else FieldCodec::putU16(frame, off, static_cast<uint16_t>(sum));
}
return frame;
}
} // namespace ccu
+54
View File
@@ -0,0 +1,54 @@
#ifndef PCCU_FRAME_H
#define PCCU_FRAME_H
#include <cstdint>
#include <cstddef>
#include <vector>
#include "ChecksumPolicy.h"
namespace ccu {
//============================================================================
// Frame:两套协议共用的通用帧模型。
//
// 两条协议帧结构完全一致:
// 域起始符1 (1B) + 域起始符2 (1B) + 域标识符 (2B) + 域字节数 (2B) + 数据域 + 校验和
//
// - header 域字节数 = 整个协议包的数据长度(含起始符与校验和)
// - 小端序
// - 校验策略可插拔(见 ChecksumPolicy)
//============================================================================
static constexpr uint8_t FRAME_START1 = 0x40;
static constexpr uint8_t FRAME_START2 = 0x40;
static constexpr size_t FRAME_HEADER_LEN = 6; // 2B start + 2B id + 2B length
struct FrameHeader {
uint8_t start1;
uint8_t start2;
uint16_t id;
uint16_t length; // 整个协议包的数据长度
};
class Frame {
public:
Frame() = default;
// 从原始缓冲解析出消息标识符(不校验完整性)
// 返回 true 且 id 有效(起始符正确)时填充 outId
static bool parseId(const std::vector<uint8_t>& buf, uint16_t& outId);
// 解析帧头;返回是否成功且起始符正确
static bool parseHeader(const std::vector<uint8_t>& buf, FrameHeader& h);
// 构造一个完整帧:data 为数据域(不含起始符与校验和),校验和自动追加
// 调用方需保证 data.size() >= 帧头之前已经包含起始符+id+length 部分
static std::vector<uint8_t> build(
uint16_t id,
const std::vector<uint8_t>& payload,
const ChecksumPolicy& checksum);
};
} // namespace ccu
#endif // PCCU_FRAME_H
+22
View File
@@ -0,0 +1,22 @@
#include "Message.h"
#include "Frame.h"
namespace ccu {
bool Message::validateFrame(const std::vector<uint8_t>& frame,
uint16_t expectId,
const ChecksumPolicy& policy,
size_t expectTotalLen) {
if (frame.size() < FRAME_HEADER_LEN) return false;
FrameHeader h;
if (!Frame::parseHeader(frame, h)) return false;
if (h.id != expectId) return false;
if (h.length != expectTotalLen) return false;
// 校验和(长度不足由 verify 内部判断)
if (!policy.verify(frame)) return false;
return true;
}
} // namespace ccu
+59
View File
@@ -0,0 +1,59 @@
#ifndef PCCU_MESSAGE_H
#define PCCU_MESSAGE_H
#include <cstdint>
#include <cstddef>
#include <string>
#include <vector>
#include "ChecksumPolicy.h"
#include "Frame.h"
namespace ccu {
//============================================================================
// Message:协议消息抽象接口。
//
// 每条协议消息实现该接口。协议变更时只需:
// 1. 修改实现类的字段偏移/长度定义
// 2. 修改 encode()/decode() 中字段取值逻辑
// 无需改动上层(Comm / Web / DB / 主流程)。
//
// 提供统一的:
// - id()/name() :消息标识与显示名
// - checksum() :该校验策略
// - payloadLength() :数据域长度(不含 6 字节帧头与校验和)
// - encode()/decode() :业务值 <-> 完整帧
//============================================================================
class Message {
public:
virtual ~Message() = default;
virtual uint16_t id() const = 0;
virtual const char* name() const = 0;
virtual const ChecksumPolicy& checksum() const = 0;
// 数据域长度(不含 6 字节帧头与校验和)
virtual size_t payloadLength() const = 0;
// 整个协议包长度 = 6 + payloadLength + checksum.size()
size_t totalLength() const {
return FRAME_HEADER_LEN + payloadLength() + checksum().size();
}
// 业务值编码为完整帧(含帧头+数据域+校验和)
// obj 为具体业务对象指针(如 FcStatusValue*)
virtual std::vector<uint8_t> encode(const void* obj) const = 0;
// 从完整帧解析业务值;成功返回 true 并填充 obj
virtual bool decode(const std::vector<uint8_t>& frame, void* obj) const = 0;
// 供实现类解码前做通用校验:起始符 / id / 总长度 / 校验和
static bool validateFrame(const std::vector<uint8_t>& frame,
uint16_t expectId,
const ChecksumPolicy& policy,
size_t expectTotalLen);
};
} // namespace ccu
#endif // PCCU_MESSAGE_H
+29
View File
@@ -0,0 +1,29 @@
#include "MessageRegistry.h"
namespace ccu {
bool MessageRegistry::registerMessage(std::unique_ptr<Message> msg) {
if (!msg) return false;
uint16_t id = msg->id();
if (m_msgs.count(id) != 0) return false; // id 冲突
m_msgs[id] = std::move(msg);
return true;
}
Message* MessageRegistry::find(uint16_t id) const {
auto it = m_msgs.find(id);
return (it != m_msgs.end()) ? it->second.get() : nullptr;
}
size_t MessageRegistry::size() const {
return m_msgs.size();
}
std::vector<Message*> MessageRegistry::all() const {
std::vector<Message*> out;
out.reserve(m_msgs.size());
for (const auto& kv : m_msgs) out.push_back(kv.second.get());
return out;
}
} // namespace ccu
+42
View File
@@ -0,0 +1,42 @@
#ifndef PCCU_MESSAGE_REGISTRY_H
#define PCCU_MESSAGE_REGISTRY_H
#include <cstdint>
#include <map>
#include <memory>
#include <vector>
#include "Message.h"
namespace ccu {
//============================================================================
// MessageRegistry:消息注册表与收发分发中心。
//
// 每条链路(FC 链路 / PM 链路)持有各自的注册表实例。
// 由于 FC 协议与 PM 协议的域标识符存在重叠(0x0001/0x0002),
// 分属不同注册表可完全隔离,互不干扰。
//
// 协议新增/调整消息时只需 register() 对应 Message 实现,上层无需改动。
//============================================================================
class MessageRegistry {
public:
// 注册一条消息;返回是否成功(id 冲突时返回 false)
bool registerMessage(std::unique_ptr<Message> msg);
// 按 id 查找消息;未注册返回 nullptr
Message* find(uint16_t id) const;
// 已注册消息数量
size_t size() const;
// 所有已注册消息(按 id 升序)
std::vector<Message*> all() const;
private:
std::map<uint16_t, std::unique_ptr<Message>> m_msgs;
};
} // namespace ccu
#endif // PCCU_MESSAGE_REGISTRY_H
+662
View File
@@ -0,0 +1,662 @@
#include "PmProtocol.h"
#include "FieldCodec.h"
#include "Frame.h"
#include "MessageRegistry.h"
namespace ccu {
void registerPmMessages(MessageRegistry& reg) {
reg.registerMessage(std::unique_ptr<Message>(new PmControlMessage()));
reg.registerMessage(std::unique_ptr<Message>(new PmParamSetMessage()));
reg.registerMessage(std::unique_ptr<Message>(new PmParamSetFbMessage()));
reg.registerMessage(std::unique_ptr<Message>(new PmStatusMessage()));
}
//============================================================================
// 0x0001 操控指令域编解码
// 数据域 35 字节,偏移 0~34(对应文档字节 7~41)
//============================================================================
namespace {
enum : size_t {
PO_YEAR = 0, // u16
PO_MONTH = 2,
PO_DAY = 3,
PO_HOUR = 4,
PO_MINUTE = 5,
PO_SECOND = 6,
PO_MS10 = 7,
PO_MODE = 8,
PO_CMD = 9,
PO_OUTPOWER = 10,
PO_PITCH = 11, // 纵倾姿态 数据1=低8位/数据2=高8位 (字节18-19)
PO_ROLL = 13, // 横倾姿态 数据1=低8位/数据2=高8位 (字节20-21)
PO_EMER_ALLOW = 15,
PO_DEPTH = 16, // u16
PO_SUPPLY_CMD = 18,
PO_RESV_CMD5 = 19,
PO_RESV_CMD6 = 20,
PO_INS_BAT_CMD = 21,
PO_DYN_BAT_CMD = 22,
PO_DYN_BAT_PWR = 23, // u16
PO_HEARTBEAT = 25,
PO_HOST_STATE = 26,
PO_RESV1 = 27, // u32
PO_RESV2 = 31, // u32
};
}
std::vector<uint8_t> PmControlMessage::encode(const void* obj) const {
const PmControlValue* v = static_cast<const PmControlValue*>(obj);
std::vector<uint8_t> p(payloadLength(), 0);
FieldCodec::putU16(p, PO_YEAR, v->year);
FieldCodec::putU8(p, PO_MONTH, v->month);
FieldCodec::putU8(p, PO_DAY, v->day);
FieldCodec::putU8(p, PO_HOUR, v->hour);
FieldCodec::putU8(p, PO_MINUTE, v->minute);
FieldCodec::putU8(p, PO_SECOND, v->second);
FieldCodec::putU8(p, PO_MS10, v->millisecond10);
FieldCodec::putU8(p, PO_MODE, v->mode);
FieldCodec::putU8(p, PO_CMD, v->cmd);
FieldCodec::putU8(p, PO_OUTPOWER, v->outputPower);
FieldCodec::putU16(p, PO_PITCH, static_cast<uint16_t>(v->pitch)); // 数据1=低位/数据2=高位
FieldCodec::putU16(p, PO_ROLL, static_cast<uint16_t>(v->roll));
FieldCodec::putU8(p, PO_EMER_ALLOW, v->emergencyAllow);
FieldCodec::putU16(p, PO_DEPTH, v->depth);
FieldCodec::putU8(p, PO_SUPPLY_CMD, v->supplyCmd);
FieldCodec::putU8(p, PO_RESV_CMD5, v->reservedCmd5);
FieldCodec::putU8(p, PO_RESV_CMD6, v->reservedCmd6);
FieldCodec::putU8(p, PO_INS_BAT_CMD, v->insBatCmd);
FieldCodec::putU8(p, PO_DYN_BAT_CMD, v->dynBatCmd);
FieldCodec::putU16(p, PO_DYN_BAT_PWR, v->dynBatPower);
FieldCodec::putU8(p, PO_HEARTBEAT, v->heartbeat);
FieldCodec::putU8(p, PO_HOST_STATE, v->hostState);
FieldCodec::putU32(p, PO_RESV1, v->reserved1);
FieldCodec::putU32(p, PO_RESV2, v->reserved2);
return Frame::build(id(), p, checksum());
}
bool PmControlMessage::decode(const std::vector<uint8_t>& frame, void* obj) const {
if (!validateFrame(frame, id(), checksum(), totalLength())) return false;
PmControlValue* v = static_cast<PmControlValue*>(obj);
size_t o = FRAME_HEADER_LEN;
v->year = FieldCodec::getU16(frame, o + PO_YEAR);
v->month = FieldCodec::getU8(frame, o + PO_MONTH);
v->day = FieldCodec::getU8(frame, o + PO_DAY);
v->hour = FieldCodec::getU8(frame, o + PO_HOUR);
v->minute = FieldCodec::getU8(frame, o + PO_MINUTE);
v->second = FieldCodec::getU8(frame, o + PO_SECOND);
v->millisecond10 = FieldCodec::getU8(frame, o + PO_MS10);
v->mode = FieldCodec::getU8(frame, o + PO_MODE);
v->cmd = FieldCodec::getU8(frame, o + PO_CMD);
v->outputPower = FieldCodec::getU8(frame, o + PO_OUTPOWER);
v->pitch = static_cast<int16_t>(FieldCodec::getU16(frame, o + PO_PITCH));
v->roll = static_cast<int16_t>(FieldCodec::getU16(frame, o + PO_ROLL));
v->emergencyAllow= FieldCodec::getU8(frame, o + PO_EMER_ALLOW);
v->depth = FieldCodec::getU16(frame, o + PO_DEPTH);
v->supplyCmd = FieldCodec::getU8(frame, o + PO_SUPPLY_CMD);
v->reservedCmd5 = FieldCodec::getU8(frame, o + PO_RESV_CMD5);
v->reservedCmd6 = FieldCodec::getU8(frame, o + PO_RESV_CMD6);
v->insBatCmd = FieldCodec::getU8(frame, o + PO_INS_BAT_CMD);
v->dynBatCmd = FieldCodec::getU8(frame, o + PO_DYN_BAT_CMD);
v->dynBatPower = FieldCodec::getU16(frame, o + PO_DYN_BAT_PWR);
v->heartbeat = FieldCodec::getU8(frame, o + PO_HEARTBEAT);
v->hostState = FieldCodec::getU8(frame, o + PO_HOST_STATE);
v->reserved1 = FieldCodec::getU32(frame, o + PO_RESV1);
v->reserved2 = FieldCodec::getU32(frame, o + PO_RESV2);
return true;
}
//============================================================================
// 0x0002 参数设定指令域
// 数据域 18 字节,偏移 0~17
//============================================================================
namespace {
enum : size_t {
PP_INS_H1 = 0,
PP_INS_H2 = 1,
PP_INS_H3 = 2,
PP_INS_PWR = 3,
PP_INS_RESV = 4,
PP_DYN_H1 = 5,
PP_DYN_H2 = 6,
PP_DYN_H3 = 7,
PP_DYN_PWR = 8,
PP_DYN_RESV = 9,
PP_RESV1 = 10, // u32
PP_RESV2 = 14, // u32
};
}
std::vector<uint8_t> PmParamSetMessage::encode(const void* obj) const {
const PmParamSetValue* v = static_cast<const PmParamSetValue*>(obj);
std::vector<uint8_t> p(payloadLength(), 0);
FieldCodec::putU8(p, PP_INS_H1, v->insSocHold1);
FieldCodec::putU8(p, PP_INS_H2, v->insSocHold2);
FieldCodec::putU8(p, PP_INS_H3, v->insSocHold3);
FieldCodec::putU8(p, PP_INS_PWR, v->insOutputPower);
FieldCodec::putU8(p, PP_INS_RESV, v->insReserved);
FieldCodec::putU8(p, PP_DYN_H1, v->dynSocHold1);
FieldCodec::putU8(p, PP_DYN_H2, v->dynSocHold2);
FieldCodec::putU8(p, PP_DYN_H3, v->dynSocHold3);
FieldCodec::putU8(p, PP_DYN_PWR, v->dynOutputPower);
FieldCodec::putU8(p, PP_DYN_RESV, v->dynReserved);
FieldCodec::putU32(p, PP_RESV1, v->reserved1);
FieldCodec::putU32(p, PP_RESV2, v->reserved2);
return Frame::build(id(), p, checksum());
}
bool PmParamSetMessage::decode(const std::vector<uint8_t>& frame, void* obj) const {
if (!validateFrame(frame, id(), checksum(), totalLength())) return false;
PmParamSetValue* v = static_cast<PmParamSetValue*>(obj);
size_t o = FRAME_HEADER_LEN;
v->insSocHold1 = FieldCodec::getU8(frame, o + PP_INS_H1);
v->insSocHold2 = FieldCodec::getU8(frame, o + PP_INS_H2);
v->insSocHold3 = FieldCodec::getU8(frame, o + PP_INS_H3);
v->insOutputPower = FieldCodec::getU8(frame, o + PP_INS_PWR);
v->insReserved = FieldCodec::getU8(frame, o + PP_INS_RESV);
v->dynSocHold1 = FieldCodec::getU8(frame, o + PP_DYN_H1);
v->dynSocHold2 = FieldCodec::getU8(frame, o + PP_DYN_H2);
v->dynSocHold3 = FieldCodec::getU8(frame, o + PP_DYN_H3);
v->dynOutputPower = FieldCodec::getU8(frame, o + PP_DYN_PWR);
v->dynReserved = FieldCodec::getU8(frame, o + PP_DYN_RESV);
v->reserved1 = FieldCodec::getU32(frame, o + PP_RESV1);
v->reserved2 = FieldCodec::getU32(frame, o + PP_RESV2);
return true;
}
//============================================================================
// 0x0003 参数设定反馈域
// 数据域 2 字节
//============================================================================
std::vector<uint8_t> PmParamSetFbMessage::encode(const void* obj) const {
const PmParamSetFbValue* v = static_cast<const PmParamSetFbValue*>(obj);
std::vector<uint8_t> p(payloadLength(), 0);
FieldCodec::putU8(p, 0, v->flag);
FieldCodec::putU8(p, 1, v->failCode);
return Frame::build(id(), p, checksum());
}
bool PmParamSetFbMessage::decode(const std::vector<uint8_t>& frame, void* obj) const {
if (!validateFrame(frame, id(), checksum(), totalLength())) return false;
PmParamSetFbValue* v = static_cast<PmParamSetFbValue*>(obj);
size_t o = FRAME_HEADER_LEN;
v->flag = FieldCodec::getU8(frame, o + 0);
v->failCode = FieldCodec::getU8(frame, o + 1);
return true;
}
//============================================================================
// 0x0004 状态报文域
// 数据域 238 字节,偏移 0~237(对应文档字节 7~244)
//============================================================================
namespace {
enum : size_t {
PS_MODE = 0,
PS_STATUS = 1,
PS_FL1 = 2, // u16
PS_FL2 = 4,
PS_FL3 = 6,
PS_FL4 = 8,
PS_GEN_TIME = 10, // u16
PS_FAULT_LEVEL = 12,
PS_OUT_POWER_LIMIT = 13, // u16
PS_GEN_POWER = 15, // u16
PS_H2_CAP = 17,
PS_LO2_CAP = 18,
PS_FC1_MIN_V = 19,
PS_FC1_MIN_POS = 20,
PS_FC1_AVG_V = 21,
PS_FC2_MIN_V = 22,
PS_FC2_MIN_POS = 23,
PS_FC2_AVG_V = 24,
PS_PALLADIUM = 25, // u16
PS_BUFFER_PRES = 27,
PS_FLUE_TOTAL = 29,
PS_FLUE_PRES = 31,
PS_REACTOR_PRES = 33,
PS_EVALVE_OPEN = 35,
PS_MAIN_PIPE_PRES = 37,
PS_AUX_PIPE_PRES = 39,
PS_DCDC1_IN_V = 41,
PS_DCDC1_IN_I = 43,
PS_DCDC2_IN_V = 45,
PS_DCDC2_IN_I = 47,
PS_DCDC_OUT_V = 49,
PS_DCDC_OUT_I = 51,
PS_DCDC_CTRL_V = 53,
PS_DCDC_AUX_V = 54,
PS_METHANOL_TOTAL = 55, // u16
PS_METHANOL_FEED = 57,
PS_O2_WATER = 59,
PS_H2_WATER = 61,
PS_BALLAST_WATER = 63,
PS_EXH_IN_PRES = 65,
PS_EXH_OUT_PRES = 67,
PS_EXH_FREQ = 69,
PS_EXH_IN_TEMP = 71,
PS_EXH_OUT_TEMP = 73,
PS_EXH_WIN_PRES = 75,
PS_EXH_WOUT_PRES = 77,
PS_TANK_LO2_PRES = 79,
PS_TANK_CO2_PRES = 81,
PS_TANK_LO2_LEVEL = 83,
PS_ALLOY_H2_FLOW = 85,
PS_FC_H2_FLOW = 87,
PS_FC_O2_FLOW = 89,
PS_EMER_DEPTH = 91, // u16
PS_EMER_TIME = 93,
PS_CABIN_P1 = 95,
PS_CABIN_P2 = 97,
PS_CABIN_T1 = 99,
PS_CABIN_T2 = 101,
PS_CABIN_H1 = 103,
PS_CABIN_H2 = 105,
PS_H2_C1 = 107,
PS_H2_C2 = 109,
PS_H2_C3 = 111,
PS_O2_C1 = 113,
PS_O2_C2 = 115,
PS_CH3OH_C1 = 117,
PS_CH3OH_C2 = 119,
PS_FLAME1 = 121,
PS_FLAME2 = 122,
PS_RESV1 = 123, // u16
PS_RESV2 = 125, // u16
PS_EB1_VOLTAGE = 127, // u16
PS_EB1_CURRENT = 129,
PS_EB1_MAX_TEMP = 131,
PS_EB1_FAULT = 133,
PS_EB2_VOLTAGE = 135,
PS_EB2_CURRENT = 137,
PS_EB2_MAX_TEMP = 139,
PS_EB2_FAULT = 141,
PS_INS_CABIN_OX = 143, // u16
PS_INS_CABIN_TEMP = 145,
PS_INS_CABIN_HUM = 147,
PS_INS_CABIN_PRES = 149,
PS_DYN_CABIN_OX = 151,
PS_DYN_CABIN_TEMP = 153,
PS_DYN_CABIN_HUM = 155,
PS_DYN_CABIN_PRES = 157,
PS_RESV3 = 159, // u8
PS_DYN_ALARM = 160, // u8 * 6
PS_INS_ALARM = 166, // u8 * 6
PS_INS_RELAY1 = 172,
PS_INS_RELAY2 = 173,
PS_DYN_RELAY1 = 174,
PS_DYN_RELAY2 = 175,
PS_INS_MAX_PWR = 176, // u16
PS_DYN_MAX_PWR = 178, // u16
PS_INS_SOC = 180,
PS_DYN_SOC = 181,
PS_INS_TOTAL_ENERGY= 182, // u16
PS_DYN_TOTAL_ENERGY= 184, // u16
PS_INS_PWR_IN = 186, // u16
PS_DYN_PWR_IN = 188, // u16
PS_INS_CHARGE_ST = 190,
PS_DYN_CHARGE_ST = 191,
PS_INS_V_LINK = 192, // u16
PS_INS_V_PACK = 194, // u16
PS_INS_CURRENT = 196, // u16
PS_INS_RES_POS = 198, // u16
PS_INS_RES_NEG = 200, // u16
PS_DYN_V_LINK = 202, // u16
PS_DYN_V_PACK = 204, // u16
PS_DYN_CURRENT = 206, // u16
PS_DYN_RES_POS = 208, // u16
PS_DYN_RES_NEG = 210, // u16
PS_INS_EMERGENCY = 212,
PS_DYN_EMERGENCY = 213,
PS_INS_SOC_H1 = 214,
PS_INS_SOC_H2 = 215,
PS_INS_SOC_H3 = 216,
PS_INS_PWR_L1 = 217,
PS_INS_PWR_L2 = 218,
PS_DYN_SOC_H1 = 219,
PS_DYN_SOC_H2 = 220,
PS_DYN_SOC_H3 = 221,
PS_DYN_PWR_L1 = 222,
PS_DYN_PWR_L2 = 223,
PS_ONLINE_FLAG1 = 224, // u32
PS_ONLINE_FLAG2 = 228, // u32
PS_RESV4 = 232, // u32
PS_HEARTBEAT = 236,
PS_EMERGENCY_CMD = 237,
};
inline void getU8Arr(const std::vector<uint8_t>& f, size_t off, uint8_t* dst, size_t n) {
for (size_t i = 0; i < n; ++i) dst[i] = f[off + i];
}
inline void putU8Arr(std::vector<uint8_t>& p, size_t off, const uint8_t* src, size_t n) {
for (size_t i = 0; i < n; ++i) p[off + i] = src[i];
}
}
std::vector<uint8_t> PmStatusMessage::encode(const void* obj) const {
const PmStatusValue* v = static_cast<const PmStatusValue*>(obj);
std::vector<uint8_t> p(payloadLength(), 0);
FieldCodec::putU8(p, PS_MODE, v->fc_mode);
FieldCodec::putU8(p, PS_STATUS, v->fc_status);
FieldCodec::putU16(p, PS_FL1, v->fault_level_1);
FieldCodec::putU16(p, PS_FL2, v->fault_level_2);
FieldCodec::putU16(p, PS_FL3, v->fault_level_3);
FieldCodec::putU16(p, PS_FL4, v->fault_level_4);
FieldCodec::putU16(p, PS_GEN_TIME, v->total_generation_time);
FieldCodec::putU8(p, PS_FAULT_LEVEL, v->fc_fault_level);
FieldCodec::putU16(p, PS_OUT_POWER_LIMIT, v->output_power_limit);
FieldCodec::putU16(p, PS_GEN_POWER, v->generation_power);
FieldCodec::putU8(p, PS_H2_CAP, v->hydrogen_capacity);
FieldCodec::putU8(p, PS_LO2_CAP, v->liquid_oxygen_capacity);
FieldCodec::putU8(p, PS_FC1_MIN_V, v->fc1_min_cell_voltage);
FieldCodec::putU8(p, PS_FC1_MIN_POS, v->fc1_min_cell_pos);
FieldCodec::putU8(p, PS_FC1_AVG_V, v->fc1_avg_cell_voltage);
FieldCodec::putU8(p, PS_FC2_MIN_V, v->fc2_min_cell_voltage);
FieldCodec::putU8(p, PS_FC2_MIN_POS, v->fc2_min_cell_pos);
FieldCodec::putU8(p, PS_FC2_AVG_V, v->fc2_avg_cell_voltage);
FieldCodec::putU16(p, PS_PALLADIUM, v->palladium_temp);
FieldCodec::putU16(p, PS_BUFFER_PRES, v->buffer_tank_pressure);
FieldCodec::putU16(p, PS_FLUE_TOTAL, v->flue_total_emission);
FieldCodec::putU16(p, PS_FLUE_PRES, v->flue_pressure);
FieldCodec::putU16(p, PS_REACTOR_PRES, v->reactor_pressure);
FieldCodec::putU16(p, PS_EVALVE_OPEN, v->electric_valve_open);
FieldCodec::putU16(p, PS_MAIN_PIPE_PRES, v->main_pipe_pressure);
FieldCodec::putU16(p, PS_AUX_PIPE_PRES, v->aux_pipe_pressure);
FieldCodec::putU16(p, PS_DCDC1_IN_V, v->dcdc1_in_voltage);
FieldCodec::putU16(p, PS_DCDC1_IN_I, v->dcdc1_in_current);
FieldCodec::putU16(p, PS_DCDC2_IN_V, v->dcdc2_in_voltage);
FieldCodec::putU16(p, PS_DCDC2_IN_I, v->dcdc2_in_current);
FieldCodec::putU16(p, PS_DCDC_OUT_V, v->dcdc_out_voltage);
FieldCodec::putU16(p, PS_DCDC_OUT_I, v->dcdc_out_current);
FieldCodec::putU8(p, PS_DCDC_CTRL_V, v->dcdc_ctrl_voltage);
FieldCodec::putU8(p, PS_DCDC_AUX_V, v->dcdc_aux_voltage);
FieldCodec::putU16(p, PS_METHANOL_TOTAL, v->methanol_total_use);
FieldCodec::putU16(p, PS_METHANOL_FEED, v->methanol_feed);
FieldCodec::putU16(p, PS_O2_WATER, v->oxygen_side_water_level);
FieldCodec::putU16(p, PS_H2_WATER, v->hydrogen_side_water_level);
FieldCodec::putU16(p, PS_BALLAST_WATER, v->ballast_water_level);
FieldCodec::putU16(p, PS_EXH_IN_PRES, v->exhaust_inlet_pressure);
FieldCodec::putU16(p, PS_EXH_OUT_PRES, v->exhaust_outlet_pressure);
FieldCodec::putU16(p, PS_EXH_FREQ, v->exhaust_run_freq);
FieldCodec::putU16(p, PS_EXH_IN_TEMP, v->exhaust_inlet_temp);
FieldCodec::putU16(p, PS_EXH_OUT_TEMP, v->exhaust_outlet_temp);
FieldCodec::putU16(p, PS_EXH_WIN_PRES, v->exhaust_water_in_pressure);
FieldCodec::putU16(p, PS_EXH_WOUT_PRES, v->exhaust_water_out_pressure);
FieldCodec::putU16(p, PS_TANK_LO2_PRES, v->tank_lo2_pressure);
FieldCodec::putU16(p, PS_TANK_CO2_PRES, v->tank_co2_pressure);
FieldCodec::putU16(p, PS_TANK_LO2_LEVEL, v->tank_lo2_level);
FieldCodec::putU16(p, PS_ALLOY_H2_FLOW, v->alloy_h2_flow);
FieldCodec::putU16(p, PS_FC_H2_FLOW, v->fc_h2_flow);
FieldCodec::putU16(p, PS_FC_O2_FLOW, v->fc_o2_flow);
FieldCodec::putU16(p, PS_EMER_DEPTH, v->emergency_float_depth);
FieldCodec::putU16(p, PS_EMER_TIME, v->emergency_float_time);
FieldCodec::putU16(p, PS_CABIN_P1, v->cabin_pressure1);
FieldCodec::putU16(p, PS_CABIN_P2, v->cabin_pressure2);
FieldCodec::putU16(p, PS_CABIN_T1, v->cabin_temp1);
FieldCodec::putU16(p, PS_CABIN_T2, v->cabin_temp2);
FieldCodec::putU16(p, PS_CABIN_H1, v->cabin_humidity1);
FieldCodec::putU16(p, PS_CABIN_H2, v->cabin_humidity2);
FieldCodec::putU16(p, PS_H2_C1, v->h2_concentration1);
FieldCodec::putU16(p, PS_H2_C2, v->h2_concentration2);
FieldCodec::putU16(p, PS_H2_C3, v->h2_concentration3);
FieldCodec::putU16(p, PS_O2_C1, v->o2_concentration1);
FieldCodec::putU16(p, PS_O2_C2, v->o2_concentration2);
FieldCodec::putU16(p, PS_CH3OH_C1, v->ch3oh_concentration1);
FieldCodec::putU16(p, PS_CH3OH_C2, v->ch3oh_concentration2);
FieldCodec::putU8(p, PS_FLAME1, v->flame_detector1);
FieldCodec::putU8(p, PS_FLAME2, v->flame_detector2);
FieldCodec::putU16(p, PS_RESV1, v->pmReserved1);
FieldCodec::putU16(p, PS_RESV2, v->pmReserved2);
FieldCodec::putU16(p, PS_EB1_VOLTAGE, v->emergency_battery1_voltage);
FieldCodec::putU16(p, PS_EB1_CURRENT, v->emergency_battery1_current);
FieldCodec::putU16(p, PS_EB1_MAX_TEMP, v->emergency_battery1_max_temp);
FieldCodec::putU16(p, PS_EB1_FAULT, v->emergency_battery1_fault_word);
FieldCodec::putU16(p, PS_EB2_VOLTAGE, v->emergency_battery2_voltage);
FieldCodec::putU16(p, PS_EB2_CURRENT, v->emergency_battery2_current);
FieldCodec::putU16(p, PS_EB2_MAX_TEMP, v->emergency_battery2_max_temp);
FieldCodec::putU16(p, PS_EB2_FAULT, v->emergency_battery2_fault_word);
FieldCodec::putU16(p, PS_INS_CABIN_OX, v->ins_cabin_ox_concentration);
FieldCodec::putU16(p, PS_INS_CABIN_TEMP, v->ins_cabin_temperature);
FieldCodec::putU16(p, PS_INS_CABIN_HUM, v->ins_cabin_humidity);
FieldCodec::putU16(p, PS_INS_CABIN_PRES, v->ins_cabin_pressure);
FieldCodec::putU16(p, PS_DYN_CABIN_OX, v->dyn_cabin_ox_concentration);
FieldCodec::putU16(p, PS_DYN_CABIN_TEMP, v->dyn_cabin_temperature);
FieldCodec::putU16(p, PS_DYN_CABIN_HUM, v->dyn_cabin_humidity);
FieldCodec::putU16(p, PS_DYN_CABIN_PRES, v->dyn_cabin_pressure);
FieldCodec::putU8(p, PS_RESV3, v->pmReserved3);
putU8Arr(p, PS_DYN_ALARM, v->dyn_alarm_flag, 6);
putU8Arr(p, PS_INS_ALARM, v->ins_alarm_flag, 6);
FieldCodec::putU8(p, PS_INS_RELAY1, v->ins_relay_status1);
FieldCodec::putU8(p, PS_INS_RELAY2, v->ins_relay_status2);
FieldCodec::putU8(p, PS_DYN_RELAY1, v->dyn_relay_status1);
FieldCodec::putU8(p, PS_DYN_RELAY2, v->dyn_relay_status2);
FieldCodec::putU16(p, PS_INS_MAX_PWR, v->ins_max_discharge_power);
FieldCodec::putU16(p, PS_DYN_MAX_PWR, v->dyn_max_discharge_power);
FieldCodec::putU8(p, PS_INS_SOC, v->ins_soc);
FieldCodec::putU8(p, PS_DYN_SOC, v->dyn_soc);
FieldCodec::putU16(p, PS_INS_TOTAL_ENERGY, v->ins_total_energy);
FieldCodec::putU16(p, PS_DYN_TOTAL_ENERGY, v->dyn_total_energy);
FieldCodec::putU16(p, PS_INS_PWR_IN, v->ins_power_input);
FieldCodec::putU16(p, PS_DYN_PWR_IN, v->dyn_power_input);
FieldCodec::putU8(p, PS_INS_CHARGE_ST, v->ins_charge_status);
FieldCodec::putU8(p, PS_DYN_CHARGE_ST, v->dyn_charge_status);
FieldCodec::putU16(p, PS_INS_V_LINK, v->ins_voltage_link);
FieldCodec::putU16(p, PS_INS_V_PACK, v->ins_voltage_pack);
FieldCodec::putU16(p, PS_INS_CURRENT, v->ins_current);
FieldCodec::putU16(p, PS_INS_RES_POS, v->ins_resistance_pos);
FieldCodec::putU16(p, PS_INS_RES_NEG, v->ins_resistance_neg);
FieldCodec::putU16(p, PS_DYN_V_LINK, v->dyn_voltage_link);
FieldCodec::putU16(p, PS_DYN_V_PACK, v->dyn_voltage_pack);
FieldCodec::putU16(p, PS_DYN_CURRENT, v->dyn_current);
FieldCodec::putU16(p, PS_DYN_RES_POS, v->dyn_resistance_pos);
FieldCodec::putU16(p, PS_DYN_RES_NEG, v->dyn_resistance_neg);
FieldCodec::putU8(p, PS_INS_EMERGENCY, v->ins_emergency_status);
FieldCodec::putU8(p, PS_DYN_EMERGENCY, v->dyn_emergency_status);
FieldCodec::putU8(p, PS_INS_SOC_H1, v->ins_soc_threshold1);
FieldCodec::putU8(p, PS_INS_SOC_H2, v->ins_soc_threshold2);
FieldCodec::putU8(p, PS_INS_SOC_H3, v->ins_soc_threshold3);
FieldCodec::putU8(p, PS_INS_PWR_L1, v->ins_power_limit1);
FieldCodec::putU8(p, PS_INS_PWR_L2, v->ins_power_limit2);
FieldCodec::putU8(p, PS_DYN_SOC_H1, v->dyn_soc_threshold1);
FieldCodec::putU8(p, PS_DYN_SOC_H2, v->dyn_soc_threshold2);
FieldCodec::putU8(p, PS_DYN_SOC_H3, v->dyn_soc_threshold3);
FieldCodec::putU8(p, PS_DYN_PWR_L1, v->dyn_power_limit1);
FieldCodec::putU8(p, PS_DYN_PWR_L2, v->dyn_power_limit2);
FieldCodec::putU32(p, PS_ONLINE_FLAG1, v->device_online_flag1);
FieldCodec::putU32(p, PS_ONLINE_FLAG2, v->device_online_flag2);
FieldCodec::putU32(p, PS_RESV4, v->pmReserved4);
FieldCodec::putU8(p, PS_HEARTBEAT, v->heartbeat);
FieldCodec::putU8(p, PS_EMERGENCY_CMD, v->emergency_cmd);
return Frame::build(id(), p, checksum());
}
bool PmStatusMessage::decode(const std::vector<uint8_t>& frame, void* obj) const {
if (!validateFrame(frame, id(), checksum(), totalLength())) return false;
PmStatusValue* v = static_cast<PmStatusValue*>(obj);
size_t o = FRAME_HEADER_LEN;
v->fc_mode = FieldCodec::getU8(frame, o + PS_MODE);
v->fc_status = FieldCodec::getU8(frame, o + PS_STATUS);
v->fault_level_1 = FieldCodec::getU16(frame, o + PS_FL1);
v->fault_level_2 = FieldCodec::getU16(frame, o + PS_FL2);
v->fault_level_3 = FieldCodec::getU16(frame, o + PS_FL3);
v->fault_level_4 = FieldCodec::getU16(frame, o + PS_FL4);
v->total_generation_time = FieldCodec::getU16(frame, o + PS_GEN_TIME);
v->fc_fault_level = FieldCodec::getU8(frame, o + PS_FAULT_LEVEL);
v->output_power_limit = FieldCodec::getU16(frame, o + PS_OUT_POWER_LIMIT);
v->generation_power = FieldCodec::getU16(frame, o + PS_GEN_POWER);
v->hydrogen_capacity = FieldCodec::getU8(frame, o + PS_H2_CAP);
v->liquid_oxygen_capacity = FieldCodec::getU8(frame, o + PS_LO2_CAP);
v->fc1_min_cell_voltage = FieldCodec::getU8(frame, o + PS_FC1_MIN_V);
v->fc1_min_cell_pos = FieldCodec::getU8(frame, o + PS_FC1_MIN_POS);
v->fc1_avg_cell_voltage = FieldCodec::getU8(frame, o + PS_FC1_AVG_V);
v->fc2_min_cell_voltage = FieldCodec::getU8(frame, o + PS_FC2_MIN_V);
v->fc2_min_cell_pos = FieldCodec::getU8(frame, o + PS_FC2_MIN_POS);
v->fc2_avg_cell_voltage = FieldCodec::getU8(frame, o + PS_FC2_AVG_V);
v->palladium_temp = FieldCodec::getU16(frame, o + PS_PALLADIUM);
v->buffer_tank_pressure = FieldCodec::getU16(frame, o + PS_BUFFER_PRES);
v->flue_total_emission = FieldCodec::getU16(frame, o + PS_FLUE_TOTAL);
v->flue_pressure = FieldCodec::getU16(frame, o + PS_FLUE_PRES);
v->reactor_pressure = FieldCodec::getU16(frame, o + PS_REACTOR_PRES);
v->electric_valve_open = FieldCodec::getU16(frame, o + PS_EVALVE_OPEN);
v->main_pipe_pressure = FieldCodec::getU16(frame, o + PS_MAIN_PIPE_PRES);
v->aux_pipe_pressure = FieldCodec::getU16(frame, o + PS_AUX_PIPE_PRES);
v->dcdc1_in_voltage = FieldCodec::getU16(frame, o + PS_DCDC1_IN_V);
v->dcdc1_in_current = FieldCodec::getU16(frame, o + PS_DCDC1_IN_I);
v->dcdc2_in_voltage = FieldCodec::getU16(frame, o + PS_DCDC2_IN_V);
v->dcdc2_in_current = FieldCodec::getU16(frame, o + PS_DCDC2_IN_I);
v->dcdc_out_voltage = FieldCodec::getU16(frame, o + PS_DCDC_OUT_V);
v->dcdc_out_current = FieldCodec::getU16(frame, o + PS_DCDC_OUT_I);
v->dcdc_ctrl_voltage = FieldCodec::getU8(frame, o + PS_DCDC_CTRL_V);
v->dcdc_aux_voltage = FieldCodec::getU8(frame, o + PS_DCDC_AUX_V);
v->methanol_total_use = FieldCodec::getU16(frame, o + PS_METHANOL_TOTAL);
v->methanol_feed = FieldCodec::getU16(frame, o + PS_METHANOL_FEED);
v->oxygen_side_water_level = FieldCodec::getU16(frame, o + PS_O2_WATER);
v->hydrogen_side_water_level = FieldCodec::getU16(frame, o + PS_H2_WATER);
v->ballast_water_level = FieldCodec::getU16(frame, o + PS_BALLAST_WATER);
v->exhaust_inlet_pressure = FieldCodec::getU16(frame, o + PS_EXH_IN_PRES);
v->exhaust_outlet_pressure = FieldCodec::getU16(frame, o + PS_EXH_OUT_PRES);
v->exhaust_run_freq = FieldCodec::getU16(frame, o + PS_EXH_FREQ);
v->exhaust_inlet_temp = FieldCodec::getU16(frame, o + PS_EXH_IN_TEMP);
v->exhaust_outlet_temp = FieldCodec::getU16(frame, o + PS_EXH_OUT_TEMP);
v->exhaust_water_in_pressure = FieldCodec::getU16(frame, o + PS_EXH_WIN_PRES);
v->exhaust_water_out_pressure = FieldCodec::getU16(frame, o + PS_EXH_WOUT_PRES);
v->tank_lo2_pressure = FieldCodec::getU16(frame, o + PS_TANK_LO2_PRES);
v->tank_co2_pressure = FieldCodec::getU16(frame, o + PS_TANK_CO2_PRES);
v->tank_lo2_level = FieldCodec::getU16(frame, o + PS_TANK_LO2_LEVEL);
v->alloy_h2_flow = FieldCodec::getU16(frame, o + PS_ALLOY_H2_FLOW);
v->fc_h2_flow = FieldCodec::getU16(frame, o + PS_FC_H2_FLOW);
v->fc_o2_flow = FieldCodec::getU16(frame, o + PS_FC_O2_FLOW);
v->emergency_float_depth = FieldCodec::getU16(frame, o + PS_EMER_DEPTH);
v->emergency_float_time = FieldCodec::getU16(frame, o + PS_EMER_TIME);
v->cabin_pressure1 = FieldCodec::getU16(frame, o + PS_CABIN_P1);
v->cabin_pressure2 = FieldCodec::getU16(frame, o + PS_CABIN_P2);
v->cabin_temp1 = FieldCodec::getU16(frame, o + PS_CABIN_T1);
v->cabin_temp2 = FieldCodec::getU16(frame, o + PS_CABIN_T2);
v->cabin_humidity1 = FieldCodec::getU16(frame, o + PS_CABIN_H1);
v->cabin_humidity2 = FieldCodec::getU16(frame, o + PS_CABIN_H2);
v->h2_concentration1 = FieldCodec::getU16(frame, o + PS_H2_C1);
v->h2_concentration2 = FieldCodec::getU16(frame, o + PS_H2_C2);
v->h2_concentration3 = FieldCodec::getU16(frame, o + PS_H2_C3);
v->o2_concentration1 = FieldCodec::getU16(frame, o + PS_O2_C1);
v->o2_concentration2 = FieldCodec::getU16(frame, o + PS_O2_C2);
v->ch3oh_concentration1 = FieldCodec::getU16(frame, o + PS_CH3OH_C1);
v->ch3oh_concentration2 = FieldCodec::getU16(frame, o + PS_CH3OH_C2);
v->flame_detector1 = FieldCodec::getU8(frame, o + PS_FLAME1);
v->flame_detector2 = FieldCodec::getU8(frame, o + PS_FLAME2);
v->pmReserved1 = FieldCodec::getU16(frame, o + PS_RESV1);
v->pmReserved2 = FieldCodec::getU16(frame, o + PS_RESV2);
v->emergency_battery1_voltage = FieldCodec::getU16(frame, o + PS_EB1_VOLTAGE);
v->emergency_battery1_current = FieldCodec::getU16(frame, o + PS_EB1_CURRENT);
v->emergency_battery1_max_temp = FieldCodec::getU16(frame, o + PS_EB1_MAX_TEMP);
v->emergency_battery1_fault_word = FieldCodec::getU16(frame, o + PS_EB1_FAULT);
v->emergency_battery2_voltage = FieldCodec::getU16(frame, o + PS_EB2_VOLTAGE);
v->emergency_battery2_current = FieldCodec::getU16(frame, o + PS_EB2_CURRENT);
v->emergency_battery2_max_temp = FieldCodec::getU16(frame, o + PS_EB2_MAX_TEMP);
v->emergency_battery2_fault_word = FieldCodec::getU16(frame, o + PS_EB2_FAULT);
v->ins_cabin_ox_concentration = FieldCodec::getU16(frame, o + PS_INS_CABIN_OX);
v->ins_cabin_temperature = FieldCodec::getU16(frame, o + PS_INS_CABIN_TEMP);
v->ins_cabin_humidity = FieldCodec::getU16(frame, o + PS_INS_CABIN_HUM);
v->ins_cabin_pressure = FieldCodec::getU16(frame, o + PS_INS_CABIN_PRES);
v->dyn_cabin_ox_concentration = FieldCodec::getU16(frame, o + PS_DYN_CABIN_OX);
v->dyn_cabin_temperature = FieldCodec::getU16(frame, o + PS_DYN_CABIN_TEMP);
v->dyn_cabin_humidity = FieldCodec::getU16(frame, o + PS_DYN_CABIN_HUM);
v->dyn_cabin_pressure = FieldCodec::getU16(frame, o + PS_DYN_CABIN_PRES);
v->pmReserved3 = FieldCodec::getU8(frame, o + PS_RESV3);
getU8Arr(frame, o + PS_DYN_ALARM, v->dyn_alarm_flag, 6);
getU8Arr(frame, o + PS_INS_ALARM, v->ins_alarm_flag, 6);
v->ins_relay_status1 = FieldCodec::getU8(frame, o + PS_INS_RELAY1);
v->ins_relay_status2 = FieldCodec::getU8(frame, o + PS_INS_RELAY2);
v->dyn_relay_status1 = FieldCodec::getU8(frame, o + PS_DYN_RELAY1);
v->dyn_relay_status2 = FieldCodec::getU8(frame, o + PS_DYN_RELAY2);
v->ins_max_discharge_power = FieldCodec::getU16(frame, o + PS_INS_MAX_PWR);
v->dyn_max_discharge_power = FieldCodec::getU16(frame, o + PS_DYN_MAX_PWR);
v->ins_soc = FieldCodec::getU8(frame, o + PS_INS_SOC);
v->dyn_soc = FieldCodec::getU8(frame, o + PS_DYN_SOC);
v->ins_total_energy = FieldCodec::getU16(frame, o + PS_INS_TOTAL_ENERGY);
v->dyn_total_energy = FieldCodec::getU16(frame, o + PS_DYN_TOTAL_ENERGY);
v->ins_power_input = FieldCodec::getU16(frame, o + PS_INS_PWR_IN);
v->dyn_power_input = FieldCodec::getU16(frame, o + PS_DYN_PWR_IN);
v->ins_charge_status = FieldCodec::getU8(frame, o + PS_INS_CHARGE_ST);
v->dyn_charge_status = FieldCodec::getU8(frame, o + PS_DYN_CHARGE_ST);
v->ins_voltage_link = FieldCodec::getU16(frame, o + PS_INS_V_LINK);
v->ins_voltage_pack = FieldCodec::getU16(frame, o + PS_INS_V_PACK);
v->ins_current = FieldCodec::getU16(frame, o + PS_INS_CURRENT);
v->ins_resistance_pos = FieldCodec::getU16(frame, o + PS_INS_RES_POS);
v->ins_resistance_neg = FieldCodec::getU16(frame, o + PS_INS_RES_NEG);
v->dyn_voltage_link = FieldCodec::getU16(frame, o + PS_DYN_V_LINK);
v->dyn_voltage_pack = FieldCodec::getU16(frame, o + PS_DYN_V_PACK);
v->dyn_current = FieldCodec::getU16(frame, o + PS_DYN_CURRENT);
v->dyn_resistance_pos = FieldCodec::getU16(frame, o + PS_DYN_RES_POS);
v->dyn_resistance_neg = FieldCodec::getU16(frame, o + PS_DYN_RES_NEG);
v->ins_emergency_status = FieldCodec::getU8(frame, o + PS_INS_EMERGENCY);
v->dyn_emergency_status = FieldCodec::getU8(frame, o + PS_DYN_EMERGENCY);
v->ins_soc_threshold1 = FieldCodec::getU8(frame, o + PS_INS_SOC_H1);
v->ins_soc_threshold2 = FieldCodec::getU8(frame, o + PS_INS_SOC_H2);
v->ins_soc_threshold3 = FieldCodec::getU8(frame, o + PS_INS_SOC_H3);
v->ins_power_limit1 = FieldCodec::getU8(frame, o + PS_INS_PWR_L1);
v->ins_power_limit2 = FieldCodec::getU8(frame, o + PS_INS_PWR_L2);
v->dyn_soc_threshold1 = FieldCodec::getU8(frame, o + PS_DYN_SOC_H1);
v->dyn_soc_threshold2 = FieldCodec::getU8(frame, o + PS_DYN_SOC_H2);
v->dyn_soc_threshold3 = FieldCodec::getU8(frame, o + PS_DYN_SOC_H3);
v->dyn_power_limit1 = FieldCodec::getU8(frame, o + PS_DYN_PWR_L1);
v->dyn_power_limit2 = FieldCodec::getU8(frame, o + PS_DYN_PWR_L2);
v->device_online_flag1 = FieldCodec::getU32(frame, o + PS_ONLINE_FLAG1);
v->device_online_flag2 = FieldCodec::getU32(frame, o + PS_ONLINE_FLAG2);
v->pmReserved4 = FieldCodec::getU32(frame, o + PS_RESV4);
v->heartbeat = FieldCodec::getU8(frame, o + PS_HEARTBEAT);
v->emergency_cmd = FieldCodec::getU8(frame, o + PS_EMERGENCY_CMD);
return true;
}
} // namespace ccu
+317
View File
@@ -0,0 +1,317 @@
#ifndef PCCU_PM_PROTOCOL_H
#define PCCU_PM_PROTOCOL_H
#include <cstdint>
#include <vector>
#include "Message.h"
namespace ccu {
//============================================================================
// PM 协议(复合管控器 <-> 控制主机 pPowerManger)
// 依据:docs/控制主机与复合管控器通信协议 - 0824.xlsx
//
// 帧格式统一:0x40 0x40 + 域标识符(2B) + 域字节数(2B) + 数据域 + uint32 校验和
// 校验和为从域起始符到校验和之前所有数据的字节和。
//
// 四条消息(均带 uint32 校验和):
// 0x0001 操控指令域 (控制主机->复合管控器)payload 35B,总长 45B
// 0x0002 参数设定指令域 (控制主机->复合管控器)payload 18B,总长 28B
// 0x0003 参数设定反馈域 (复合管控器->控制主机)payload 2B,总长 12B
// 0x0004 状态报文域 (复合管控器->控制主机)payload 238B,总长 248B
//============================================================================
//--------------------------------------------------------------------------
// 0x0001 操控指令域
// payload 35 字节,对应文档字节 7~41
//--------------------------------------------------------------------------
struct PmControlValue {
// 系统时间
uint16_t year = 0;
uint8_t month = 0;
uint8_t day = 0;
uint8_t hour = 0;
uint8_t minute = 0;
uint8_t second = 0;
uint8_t millisecond10 = 0; // 10毫秒
// 指令
uint8_t mode = 0; // 模式设定 00~03
uint8_t cmd = 0; // 操控指令 00~0B
uint8_t outputPower = 0; // 输出功率指令 0~250,0.1kW
int16_t pitch = 0; // 纵倾姿态(数据1=低8位 / 数据2=高8位,int16),0.1°
int16_t roll = 0; // 横倾姿态(数据1=低8位 / 数据2=高8位,int16),0.1°
uint8_t emergencyAllow = 0; // 应急允许
uint16_t depth = 0; // 潜深深度 m
uint8_t supplyCmd = 0; // 补给/排放指令
uint8_t reservedCmd5 = 0; // 预留指令5
uint8_t reservedCmd6 = 0; // 预留指令6
uint8_t insBatCmd = 0; // 仪表锂电池启停指令 00/10/20
uint8_t dynBatCmd = 0; // 动力锂电池启停指令 00/10/20
uint16_t dynBatPower = 0; // 动力锂电池功率配置值 kW
uint8_t heartbeat = 0; // 通信心跳,每次+1
uint8_t hostState = 0; // 主机状态 运行AAH/关机FFH/故障其他
uint32_t reserved1 = 0; // 预留
uint32_t reserved2 = 0; // 预留
};
//--------------------------------------------------------------------------
// 0x0002 参数设定指令域
// payload 18 字节,对应文档字节 7~24
//--------------------------------------------------------------------------
struct PmParamSetValue {
uint8_t insSocHold1 = 0; // 仪表电池充电一级功率SOC门限
uint8_t insSocHold2 = 0; // 仪表电池充电二级功率SOC门限
uint8_t insSocHold3 = 0; // 仪表电池充电三级功率SOC门限
uint8_t insOutputPower = 0; // 仪表锂电池输出功率 %
uint8_t insReserved = 0; // 预留
uint8_t dynSocHold1 = 0; // 动力电池充电一级功率SOC门限
uint8_t dynSocHold2 = 0; // 动力电池充电二级功率SOC门限
uint8_t dynSocHold3 = 0; // 动力电池充电三级功率SOC门限
uint8_t dynOutputPower = 0; // 动力电池输出功率 %
uint8_t dynReserved = 0; // 预留
uint32_t reserved1 = 0; // 预留
uint32_t reserved2 = 0; // 预留
};
//--------------------------------------------------------------------------
// 0x0003 参数设定反馈域
// payload 2 字节,对应文档字节 7~8
//--------------------------------------------------------------------------
struct PmParamSetFbValue {
uint8_t flag = 0; // 设定标志:00H无效 / 10H成功 / 20H失败
uint8_t failCode = 0; // 设定失败原因
};
//--------------------------------------------------------------------------
// 0x0004 状态报文域
// payload 238 字节,对应文档字节 7~244
//--------------------------------------------------------------------------
struct PmStatusValue {
uint8_t fc_mode = 0; // 燃料电池系统运行模式
uint8_t fc_status = 0; // 燃料电池系统运行状态
uint16_t fault_level_1 = 0; // 一级故障码
uint16_t fault_level_2 = 0; // 二级故障码
uint16_t fault_level_3 = 0; // 三级故障码
uint16_t fault_level_4 = 0; // 四级故障码
uint16_t total_generation_time = 0; // 累积发电时间 0.1h
uint8_t fc_fault_level = 0; // 系统故障等级 00~04
uint16_t output_power_limit = 0; // 系统输出功率限定功率 W
uint16_t generation_power = 0; // 系统发电功率 0.01kW
uint8_t hydrogen_capacity = 0; // 储氢剩余容量 %
uint8_t liquid_oxygen_capacity = 0; // 液氧剩余容量 %
uint8_t fc1_min_cell_voltage = 0; // I#FC最低单片电压 10mV
uint8_t fc1_min_cell_pos = 0; // I#FC最低单片电压位置
uint8_t fc1_avg_cell_voltage = 0; // I#FC平均单片电压
uint8_t fc2_min_cell_voltage = 0; // 2#FC最低单片电压
uint8_t fc2_min_cell_pos = 0; // 2#FC最低单片电压位置
uint8_t fc2_avg_cell_voltage = 0; // 2#FC平均单片电压
uint16_t palladium_temp = 0; // 甲醇制氢装置钯膜最高温度 0.1℃
uint16_t buffer_tank_pressure = 0; // 缓冲罐压力 0.1kPa
uint16_t flue_total_emission = 0; // 烟气累计排放量 0.1kg
uint16_t flue_pressure = 0; // 烟气压力 0.001MPa
uint16_t reactor_pressure = 0; // 反应器压力 0.001MPa
uint16_t electric_valve_open = 0; // 电动阀开度 0.1%
uint16_t main_pipe_pressure = 0; // 主水路盘管侧压力 0.01kPa
uint16_t aux_pipe_pressure = 0; // 辅水路盘管侧压力 0.01kPa
uint16_t dcdc1_in_voltage = 0; // DC/DC通道1输入电压 0.1V
uint16_t dcdc1_in_current = 0; // DC/DC通道1输入电流 0.1A
uint16_t dcdc2_in_voltage = 0; // DC/DC通道2输入电压 0.1V
uint16_t dcdc2_in_current = 0; // DC/DC通道2输入电流 0.1A
uint16_t dcdc_out_voltage = 0; // DC/DC输出电压 0.1V
uint16_t dcdc_out_current = 0; // DC/DC输出电流 0.1A
uint8_t dcdc_ctrl_voltage = 0; // DC/DC控制电源电压 0.25V
uint8_t dcdc_aux_voltage = 0; // DC/DC辅电输出电压 0.125V
uint16_t methanol_total_use = 0; // 甲醇累计使用量 0.1kg
uint16_t methanol_feed = 0; // 甲醇溶液进料量 1mL/min
uint16_t oxygen_side_water_level = 0; // 氧侧生成水箱液位 0.01mm
uint16_t hydrogen_side_water_level = 0; // 氢侧生成水箱液位
uint16_t ballast_water_level = 0; // 配重水箱液位
uint16_t exhaust_inlet_pressure = 0; // 尾气装置进气压力 0.01MPa
uint16_t exhaust_outlet_pressure = 0; // 尾气装置排气压力 0.01MPa
uint16_t exhaust_run_freq = 0; // 尾气装置运行频率 0.01Hz
uint16_t exhaust_inlet_temp = 0; // 尾气装置进气温度 0.01℃
uint16_t exhaust_outlet_temp = 0; // 尾气装置排气温度 0.01℃
uint16_t exhaust_water_in_pressure = 0; // 尾气装置进水压力 0.01kPa
uint16_t exhaust_water_out_pressure = 0; // 尾气装置排水压力 0.01kPa
uint16_t tank_lo2_pressure = 0; // 一体化罐装置液氧罐压力 0.01MPa
uint16_t tank_co2_pressure = 0; // 一体化罐装置二氧化碳压力 0.01MPa
uint16_t tank_lo2_level = 0; // 一体化罐装置液氧罐液位 0.01mm
uint16_t alloy_h2_flow = 0; // 合金供氢流量 0.01L/min
uint16_t fc_h2_flow = 0; // FC供氢流量 0.01L/min
uint16_t fc_o2_flow = 0; // FC供氧流量 0.01L/min
uint16_t emergency_float_depth = 0; // 应急上浮深度 1m
uint16_t emergency_float_time = 0; // 应急上浮时间 1min
uint16_t cabin_pressure1 = 0; // 舱室压力1 0.01kPa
uint16_t cabin_pressure2 = 0; // 舱室压力2
uint16_t cabin_temp1 = 0; // 舱室温度1 0.01℃
uint16_t cabin_temp2 = 0; // 舱室温度2
uint16_t cabin_humidity1 = 0; // 舱室湿度1 0.01%RH
uint16_t cabin_humidity2 = 0; // 舱室湿度2
uint16_t h2_concentration1 = 0; // 舱室H2浓度1 0.01%LEL
uint16_t h2_concentration2 = 0; // 舱室H2浓度2
uint16_t h2_concentration3 = 0; // 舱室H2浓度3
uint16_t o2_concentration1 = 0; // 舱室O2浓度1 0.01%Vol
uint16_t o2_concentration2 = 0; // 舱室O2浓度2
uint16_t ch3oh_concentration1 = 0; // 舱室甲醇浓度1 0.01%LEL
uint16_t ch3oh_concentration2 = 0; // 舱室甲醇浓度2
uint8_t flame_detector1 = 0; // 火焰探测器1状态
uint8_t flame_detector2 = 0; // 火焰探测器2状态
uint16_t pmReserved1 = 0; // 预留
uint16_t pmReserved2 = 0; // 预留
uint16_t emergency_battery1_voltage = 0; // 应急电池1总电压
uint16_t emergency_battery1_current = 0; // 应急电池1总电流
uint16_t emergency_battery1_max_temp = 0; // 应急电池1最高温度
uint16_t emergency_battery1_fault_word = 0;// 应急电池1故障字
uint16_t emergency_battery2_voltage = 0; // 应急电池2总电压
uint16_t emergency_battery2_current = 0; // 应急电池2总电流
uint16_t emergency_battery2_max_temp = 0; // 应急电池2最高温度
uint16_t emergency_battery2_fault_word = 0;// 应急电池2故障字
uint16_t ins_cabin_ox_concentration = 0; // 仪表舱内氧气浓度 %
uint16_t ins_cabin_temperature = 0; // 仪表舱内温度 ℃
uint16_t ins_cabin_humidity = 0; // 仪表舱内湿度 %RH
uint16_t ins_cabin_pressure = 0; // 仪表舱内大气压 kPa
uint16_t dyn_cabin_ox_concentration = 0; // 动力舱内氧气浓度 %
uint16_t dyn_cabin_temperature = 0; // 动力舱内温度 ℃
uint16_t dyn_cabin_humidity = 0; // 动力舱内湿度 %RH
uint16_t dyn_cabin_pressure = 0; // 动力舱内大气压 kPa
uint8_t pmReserved3 = 0; // 预留
uint8_t dyn_alarm_flag[6] = {0}; // 动力锂电池报警标识字1~6
uint8_t ins_alarm_flag[6] = {0}; // 仪表锂电池报警标识字1~6
uint8_t ins_relay_status1 = 0; // 仪表电池正极+充电继电器状态
uint8_t ins_relay_status2 = 0; // 仪表电池预充+负极继电器状态
uint8_t dyn_relay_status1 = 0; // 动力电池正极+充电继电器状态
uint8_t dyn_relay_status2 = 0; // 动力电池预充+负极继电器状态
uint16_t ins_max_discharge_power = 0; // 仪表电池允许最高放电功率 0.05kW
uint16_t dyn_max_discharge_power = 0; // 动力电池允许最高放电功率 0.05kW
uint8_t ins_soc = 0; // 仪表锂电池SOC %
uint8_t dyn_soc = 0; // 动力锂电池SOC %
uint16_t ins_total_energy = 0; // 仪表锂电池总能量 0.1kWh
uint16_t dyn_total_energy = 0; // 动力锂电池总电量 0.1kWh
uint16_t ins_power_input = 0; // 仪表电池当前接入功率 kW
uint16_t dyn_power_input = 0; // 动力电池当前接入功率 kW
uint8_t ins_charge_status = 0; // 仪表电池充电状态
uint8_t dyn_charge_status = 0; // 动力电池充电状态
uint16_t ins_voltage_link = 0; // 仪表电池电压LINK端 0.01V
uint16_t ins_voltage_pack = 0; // 仪表电池电压PACK端 0.01V
uint16_t ins_current = 0; // 仪表电池放电电流 0.05A
uint16_t ins_resistance_pos = 0; // 仪表电池正端绝缘电阻 kΩ
uint16_t ins_resistance_neg = 0; // 仪表电池负端绝缘电阻 kΩ
uint16_t dyn_voltage_link = 0; // 动力电池电压LINK端 0.01V
uint16_t dyn_voltage_pack = 0; // 动力电池电压PACK端 0.01V
uint16_t dyn_current = 0; // 动力电池放电电流 0.05A
uint16_t dyn_resistance_pos = 0; // 动力电池正端绝缘电阻 kΩ
uint16_t dyn_resistance_neg = 0; // 动力电池负端绝缘电阻 kΩ
uint8_t ins_emergency_status = 0; // 仪表电池紧急状态
uint8_t dyn_emergency_status = 0; // 动力电池紧急状态
uint8_t ins_soc_threshold1 = 0; // 仪表电池充电一级功率SOC门限当前值
uint8_t ins_soc_threshold2 = 0; // 仪表电池充电二级
uint8_t ins_soc_threshold3 = 0; // 仪表电池充电三级
uint8_t ins_power_limit1 = 0; // 仪表通路FC输出功率一级限制当前值
uint8_t ins_power_limit2 = 0; // 仪表通路FC输出功率二级限制当前值
uint8_t dyn_soc_threshold1 = 0; // 动力电池充电一级功率SOC门限当前值
uint8_t dyn_soc_threshold2 = 0; // 动力电池充电二级
uint8_t dyn_soc_threshold3 = 0; // 动力电池充电三级
uint8_t dyn_power_limit1 = 0; // 动力通路FC输出功率一级限制当前值
uint8_t dyn_power_limit2 = 0; // 动力通路FC输出功率二级限制当前值
uint32_t device_online_flag1 = 0; // 设备在线状态标志字1
uint32_t device_online_flag2 = 0; // 设备在线状态标志字2
uint32_t pmReserved4 = 0; // 预留
uint8_t heartbeat = 0; // 通信心跳,每次+1
uint8_t emergency_cmd = 0; // 应急指令
};
//--------------------------------------------------------------------------
// 消息类
//--------------------------------------------------------------------------
class PmControlMessage : public Message {
public:
uint16_t id() const override { return 0x0001; }
const char* name() const override { return "pm_control"; }
const ChecksumPolicy& checksum() const override { return m_checksum; }
size_t payloadLength() const override { return 35; }
std::vector<uint8_t> encode(const void* obj) const override;
bool decode(const std::vector<uint8_t>& frame, void* obj) const override;
std::vector<uint8_t> encode(const PmControlValue& v) const { return encode(&v); }
bool decode(const std::vector<uint8_t>& frame, PmControlValue& v) const {
return decode(frame, static_cast<void*>(&v));
}
private:
ChecksumPolicy m_checksum{ChecksumType::Sum32};
};
class PmParamSetMessage : public Message {
public:
uint16_t id() const override { return 0x0002; }
const char* name() const override { return "pm_param_set"; }
const ChecksumPolicy& checksum() const override { return m_checksum; }
size_t payloadLength() const override { return 18; }
std::vector<uint8_t> encode(const void* obj) const override;
bool decode(const std::vector<uint8_t>& frame, void* obj) const override;
std::vector<uint8_t> encode(const PmParamSetValue& v) const { return encode(&v); }
bool decode(const std::vector<uint8_t>& frame, PmParamSetValue& v) const {
return decode(frame, static_cast<void*>(&v));
}
private:
ChecksumPolicy m_checksum{ChecksumType::Sum32};
};
class PmParamSetFbMessage : public Message {
public:
uint16_t id() const override { return 0x0003; }
const char* name() const override { return "pm_param_set_fb"; }
const ChecksumPolicy& checksum() const override { return m_checksum; }
size_t payloadLength() const override { return 2; }
std::vector<uint8_t> encode(const void* obj) const override;
bool decode(const std::vector<uint8_t>& frame, void* obj) const override;
std::vector<uint8_t> encode(const PmParamSetFbValue& v) const { return encode(&v); }
bool decode(const std::vector<uint8_t>& frame, PmParamSetFbValue& v) const {
return decode(frame, static_cast<void*>(&v));
}
private:
ChecksumPolicy m_checksum{ChecksumType::Sum32};
};
class PmStatusMessage : public Message {
public:
uint16_t id() const override { return 0x0004; }
const char* name() const override { return "pm_status"; }
const ChecksumPolicy& checksum() const override { return m_checksum; }
size_t payloadLength() const override { return 238; }
std::vector<uint8_t> encode(const void* obj) const override;
bool decode(const std::vector<uint8_t>& frame, void* obj) const override;
std::vector<uint8_t> encode(const PmStatusValue& v) const { return encode(&v); }
bool decode(const std::vector<uint8_t>& frame, PmStatusValue& v) const {
return decode(frame, static_cast<void*>(&v));
}
private:
ChecksumPolicy m_checksum{ChecksumType::Sum32};
};
// 注册 PM 消息到给定注册表
void registerPmMessages(class MessageRegistry& reg);
} // namespace ccu
#endif // PCCU_PM_PROTOCOL_H