新增复合管控器(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:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user