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

305 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//============================================================================
// pccuTest:pCCU 协议编解码 / 校验和 / 帧长度 / 链路分发 / 数据库 单元测试
// 无第三方测试库,独立可执行。参照 test/until/testSqlitdb.cpp 的轻量风格。
//============================================================================
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
#include <iostream>
#include "../../src/pCCU/protocol/FieldCodec.h"
#include "../../src/pCCU/protocol/Frame.h"
#include "../../src/pCCU/protocol/ChecksumPolicy.h"
#include "../../src/pCCU/protocol/MessageRegistry.h"
#include "../../src/pCCU/protocol/FcProtocol.h"
#include "../../src/pCCU/protocol/PmProtocol.h"
#include "../../src/pCCU/comm/LinkManager.h"
#include "../../src/pCCU/comm/FcLinkManager.h"
#include "../../src/pCCU/comm/PmLinkManager.h"
#include "../../src/pCCU/store/DbStore.h"
using namespace ccu;
static int g_fail = 0;
static int g_pass = 0;
#define CHECK(cond) do { \
if (cond) { ++g_pass; } \
else { ++g_fail; std::printf("FAIL %s:%d %s\n", __FILE__, __LINE__, #cond); } \
} while (0)
//--------------------------------------------------------------------------
// 1. 帧长度必须与协议文档一致
static void testFrameLengths() {
FcControlMessage fcm;
FcStatusMessage fsm;
PmControlMessage pcm;
PmParamSetMessage pps;
PmParamSetFbMessage ppf;
PmStatusMessage psm;
CHECK(fcm.totalLength() == 24); // FC 控制指令 24B(1.1.1:域字节数0x0018)
CHECK(fsm.totalLength() == 150); // FC 状态反馈 150B
CHECK(pcm.totalLength() == 45); // PM 操控指令 45B
CHECK(pps.totalLength() == 28); // PM 参数设定 28B
CHECK(ppf.totalLength() == 12); // PM 参数反馈 12B
CHECK(psm.totalLength() == 248); // PM 状态报文 248B
}
//--------------------------------------------------------------------------
// 2. FC 控制指令编解码往返
static void testFcControlRoundTrip() {
FcControlValue v;
v.mode = 2; v.cmd = 2; v.outputPower = 100; v.pitch1 = 35; v.pitch2 = -21;
v.roll1 = -12; v.roll2 = 48;
v.emergencyAllow = 0x03; v.depth = 150;
v.supplyCmd = 1; v.reservedCmd5 = 5; v.reservedCmd6 = 6; v.heartbeat = 7;
FcControlMessage msg;
std::vector<uint8_t> frame = msg.encode(v);
CHECK(frame.size() == 24);
CHECK(frame[0] == 0x40 && frame[1] == 0x40);
CHECK(frame[4] == 0x18 && frame[5] == 0x00); // 域字节数 0x0018 小端
// 字节位置校验(数据域起点=帧头6):姿态各 int16 小端
CHECK(frame[6 + 3] == 0x23 && frame[6 + 4] == 0x00); // 纵倾数据1 = 35
CHECK(frame[6 + 5] == 0xEB && frame[6 + 6] == 0xFF); // 纵倾数据2 = -21 (0xFFEB)
CHECK(frame[6 + 7] == 0xF4 && frame[6 + 8] == 0xFF); // 横倾数据1 = -12 (0xFFF4)
CHECK(frame[6 + 9] == 0x30 && frame[6 + 10] == 0x00); // 横倾数据2 = 48
FcControlValue out;
CHECK(msg.decode(frame, static_cast<void*>(&out)));
CHECK(out.mode == 2);
CHECK(out.cmd == 2);
CHECK(out.outputPower == 100);
CHECK(out.pitch1 == 35);
CHECK(out.pitch2 == -21);
CHECK(out.roll1 == -12);
CHECK(out.roll2 == 48);
CHECK(out.emergencyAllow == 0x03);
CHECK(out.depth == 150);
CHECK(out.supplyCmd == 1);
CHECK(out.reservedCmd5 == 5 && out.reservedCmd6 == 6);
CHECK(out.heartbeat == 7);
}
//--------------------------------------------------------------------------
// 3. FC 状态编解码往返(关键字段抽查)
static void testFcStatusRoundTrip() {
FcStatusValue v;
v.fc_mode = 2; v.fc_status = 6; v.fc_fault_level = 0;
v.fault_level_1 = 0x0102; v.fault_level_2 = 0x0304;
v.fault_level_3 = 0x0506; v.fault_level_4 = 0x0708;
v.total_generation_time = 12345;
v.generation_power = 23456;
v.hydrogen_capacity = 80; v.liquid_oxygen_capacity = 70;
v.palladium_temp = 350; v.buffer_tank_pressure = 200;
v.dcdc1_in_voltage = 12; v.dcdc_out_current = 33;
v.methanol_total_use = 100;
v.cabin_temp1 = 25; v.h2_concentration1 = 500;
v.heartbeat = 9; v.emergency_cmd = 0x01;
v.reserved[0] = 0xAAAA; v.reserved[11] = 0xBBBB;
FcStatusMessage msg;
std::vector<uint8_t> frame = msg.encode(v);
CHECK(frame.size() == 150);
// 字节位置校验(对照 0821 docx):发电功率在字节21-22(offset 14),心跳在字节149(offset 142)
CHECK(frame[6 + 12] == 0); // 故障等级 offset12
CHECK(frame[6 + 14] == 0xA0 && frame[6 + 15] == 0x5B); // 发电功率 23456 -> 0x5BA0 小端
CHECK(frame[6 + 142] == 9); // 心跳 offset142
CHECK(frame[6 + 143] == 0x01);// 应急指令 offset143
FcStatusValue out;
CHECK(msg.decode(frame, static_cast<void*>(&out)));
CHECK(out.fc_mode == 2 && out.fc_status == 6);
CHECK(out.fault_level_1 == 0x0102 && out.fault_level_4 == 0x0708);
CHECK(out.total_generation_time == 12345);
CHECK(out.generation_power == 23456);
CHECK(out.hydrogen_capacity == 80 && out.liquid_oxygen_capacity == 70);
CHECK(out.palladium_temp == 350);
CHECK(out.dcdc1_in_voltage == 12 && out.dcdc_out_current == 33);
CHECK(out.methanol_total_use == 100);
CHECK(out.cabin_temp1 == 25 && out.h2_concentration1 == 500);
CHECK(out.heartbeat == 9 && out.emergency_cmd == 0x01);
CHECK(out.reserved[0] == 0xAAAA && out.reserved[11] == 0xBBBB);
}
//--------------------------------------------------------------------------
// 4. PM 操控指令编解码往返
static void testPmControlRoundTrip() {
PmControlValue v;
v.year = 2026; v.month = 8; v.day = 26; v.hour = 17; v.minute = 30; v.second = 5;
v.millisecond10 = 3;
v.mode = 2; v.cmd = 2; v.outputPower = 120;
v.pitch = 35; v.roll = -12;
v.emergencyAllow = 0x03; v.depth = 200;
v.supplyCmd = 1; v.reservedCmd5 = 5; v.reservedCmd6 = 6;
v.insBatCmd = 0x10; v.dynBatCmd = 0x20; v.dynBatPower = 300;
v.heartbeat = 11; v.hostState = 0xAA;
v.reserved1 = 0x11223344; v.reserved2 = 0x55667788;
PmControlMessage msg;
std::vector<uint8_t> frame = msg.encode(v);
CHECK(frame.size() == 45);
PmControlValue out;
CHECK(msg.decode(frame, static_cast<void*>(&out)));
CHECK(out.year == 2026 && out.month == 8 && out.day == 26);
CHECK(out.hour == 17 && out.minute == 30 && out.second == 5);
CHECK(out.millisecond10 == 3);
CHECK(out.mode == 2 && out.cmd == 2 && out.outputPower == 120);
CHECK(out.pitch == 35 && out.roll == -12);
CHECK(out.depth == 200);
CHECK(out.insBatCmd == 0x10 && out.dynBatCmd == 0x20 && out.dynBatPower == 300);
CHECK(out.heartbeat == 11 && out.hostState == 0xAA);
CHECK(out.reserved1 == 0x11223344 && out.reserved2 == 0x55667788);
}
//--------------------------------------------------------------------------
// 5. PM 状态报文编解码往返(抽查关键字段,覆盖各分组)
static void testPmStatusRoundTrip() {
PmStatusValue v;
v.fc_mode = 2; v.fc_status = 6;
v.fault_level_1 = 1; v.fault_level_2 = 2; v.fault_level_3 = 3; v.fault_level_4 = 4;
v.total_generation_time = 500; v.fc_fault_level = 0;
v.output_power_limit = 30000; v.generation_power = 100;
v.hydrogen_capacity = 80; v.liquid_oxygen_capacity = 70;
v.main_pipe_pressure = 500; v.aux_pipe_pressure = 400;
v.dcdc_out_voltage = 240; v.dcdc_out_current = 100;
v.cabin_temp1 = 2500; v.cabin_temp2 = 2400;
v.h2_concentration1 = 300; v.o2_concentration1 = 200;
v.emergency_battery1_voltage = 24; v.emergency_battery2_voltage = 26;
v.ins_soc = 60; v.dyn_soc = 55;
v.ins_voltage_link = 4800; v.dyn_voltage_link = 4900;
v.ins_current = 120; v.dyn_current = 130;
v.device_online_flag1 = 0x0F; v.device_online_flag2 = 0x07;
v.heartbeat = 42; v.emergency_cmd = 0x01;
v.dyn_alarm_flag[0] = 0xAA; v.ins_alarm_flag[5] = 0x55;
PmStatusMessage msg;
std::vector<uint8_t> frame = msg.encode(v);
CHECK(frame.size() == 248);
PmStatusValue out;
CHECK(msg.decode(frame, static_cast<void*>(&out)));
CHECK(out.fc_mode == 2 && out.fc_status == 6);
CHECK(out.fault_level_1 == 1 && out.fault_level_4 == 4);
CHECK(out.total_generation_time == 500 && out.fc_fault_level == 0);
CHECK(out.output_power_limit == 30000 && out.generation_power == 100);
CHECK(out.hydrogen_capacity == 80 && out.liquid_oxygen_capacity == 70);
CHECK(out.main_pipe_pressure == 500 && out.aux_pipe_pressure == 400);
CHECK(out.dcdc_out_voltage == 240 && out.dcdc_out_current == 100);
CHECK(out.cabin_temp1 == 2500 && out.h2_concentration1 == 300);
CHECK(out.emergency_battery1_voltage == 24 && out.emergency_battery2_voltage == 26);
CHECK(out.ins_soc == 60 && out.dyn_soc == 55);
CHECK(out.ins_voltage_link == 4800 && out.dyn_voltage_link == 4900);
CHECK(out.device_online_flag1 == 0x0F && out.device_online_flag2 == 0x07);
CHECK(out.heartbeat == 42 && out.emergency_cmd == 0x01);
CHECK(out.dyn_alarm_flag[0] == 0xAA && out.ins_alarm_flag[5] == 0x55);
}
//--------------------------------------------------------------------------
// 6. PM 校验和:正确帧通过,篡改字节校验失败
static void testChecksum() {
PmControlValue v;
v.mode = 2; v.cmd = 3; v.heartbeat = 1;
PmControlMessage msg;
std::vector<uint8_t> frame = msg.encode(v);
PmControlValue tmp;
CHECK(msg.decode(frame, static_cast<void*>(&tmp))); // 校验通过
// 篡改数据域中间字节
std::vector<uint8_t> bad = frame;
bad[10] ^= 0xFF; // 数据域某字节
CHECK(!msg.decode(bad, static_cast<void*>(&tmp)));
// 篡改帧头长度
std::vector<uint8_t> badLen = frame;
badLen[4] = 0x01; // length 低字节改小 -> 0x012D != 45
CHECK(!msg.decode(badLen, static_cast<void*>(&tmp)));
}
//--------------------------------------------------------------------------
// 7. 链路分发:FC 链路收到状态帧 -> 回调解码
static void testLinkDispatch() {
// 注册表按链路隔离:FC 与 PM 共用 0x0001/0x0002 不应冲突
MessageRegistry fcReg;
registerFcMessages(fcReg);
MessageRegistry pmReg;
registerPmMessages(pmReg);
CHECK(fcReg.size() == 2);
CHECK(pmReg.size() == 4);
CHECK(fcReg.find(0x0001)->id() == 0x0001);
CHECK(pmReg.find(0x0001)->id() == 0x0001); // id 重叠但隔离
// 用 LinkManager 的 handleIncoming 模拟收帧
FcStatusValue fv;
fv.fc_status = 6; fv.heartbeat = 8;
FcStatusMessage fsMsg;
std::vector<uint8_t> frame = fsMsg.encode(fv);
LinkManager lm("test_fc", 0);
registerFcMessages(lm.registry());
bool got = false;
lm.setOnMessage([&](Message* m, const std::vector<uint8_t>& f) {
if (m && m->id() == 0x0002) {
FcStatusValue out;
if (m->decode(f, static_cast<void*>(&out))) {
got = (out.fc_status == 6 && out.heartbeat == 8);
}
}
});
CHECK(lm.handleIncoming(frame));
CHECK(got);
}
//--------------------------------------------------------------------------
// 8. 数据库落库与查询
static void testDbStore() {
std::string path = "test_pccu.db";
std::remove(path.c_str());
{
DbStore db(path);
CHECK(db.open());
std::vector<uint8_t> frame = {0x40,0x40,0x01,0x00,0x14,0x00, 1,2,3,4,5,6,7,8,9,10,11,12,13,14};
db.onRawFrame(0, 0x0001, "fc", frame, true);
db.onRawFrame(1, 0x0004, "pm", frame, false);
CHECK(db.count() == 2);
auto rows = db.queryRecent("", -1, 0, 10);
CHECK(rows.size() == 2);
CHECK(rows[0].msgId == 0x0004 || rows[0].msgId == 0x0001);
CHECK(rows[0].checksumOk == 0); // 最近一条是 pm 的 false
auto fcRows = db.queryRecent("fc", 0, 0, 10);
CHECK(fcRows.size() == 1);
CHECK(fcRows[0].msgId == 0x0001 && fcRows[0].direction == 0);
db.close();
}
std::remove(path.c_str());
}
//--------------------------------------------------------------------------
int main() {
testFrameLengths();
testFcControlRoundTrip();
testFcStatusRoundTrip();
testPmControlRoundTrip();
testPmStatusRoundTrip();
testChecksum();
testLinkDispatch();
testDbStore();
std::printf("\n==== pccuTest: %d passed, %d failed ====\n", g_pass, g_fail);
return g_fail == 0 ? 0 : 1;
}