Files
H100PowerManger/test/integration/full_feeder.cpp
T
zjk 12f0dee86e 修复:跨线程数据竞争 + 协议字段越界 + UDP 发送失败吞错
- 并发安全:PowerManger 新增 m_stateMutex,保护 m_pmCurrentState /
  m_currentDisBreakerState / m_CcuColCmd / faultCode,并封装
  addFaultCode/clearFaultCodes/getFaultCodes 访问器
- SystemData 新增跨线程读访问器(getDynVoltageLink/getPowerBusStatus/
  getDcdcStatus/getDeviceCommonStatus 等)及配电故障码集合加锁访问
- upmsg buildMsg / httpserver pushSystemData / LowerCommManager 均改为
  持锁快照,避免 listen 线程与 comm 线程撕裂读写
- 修复析构顺序:先删 httpServer 再删其他对象,避免 use-after-free
- 修复 ccuState reserved2 越界读(协议预留 2 字节,结构体仅声明 1),
  修正 coutMsg 中 SOC 门限/功率限制字段打印
- udpComm::sendDisSysCmd 不再吞掉异常,返回 sendMsg 实际结果并记录 ERROR,
  新增 SendDisSysCmdFailsWithoutSocket 单测
- 新增 fullFeeder 全类型 UDP 集成仿真器与 fetch-data/clean-data 脚本,
  .gitignore 补充 SQLite 运行期文件与 fullFeeder 二进制
2026-08-21 11:36:19 +08:00

293 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.
//=======================================================================
// full_feeder.cpp: 集成测试用 — 仿真真实 CCU + 配电系统,向 pPowerManger
// 发送全部 5 类状态反馈 UDP 消息(CCU 状态 + 4 路配电反馈)。
// 直接使用真实 C++ 结构体构造消息(字节级与协议一致,无打包漂移)。
// 用法: fullFeeder [host] [port] [rounds] [interval_ms]
// rounds=0 表示持续发送直到被 kill。
//=======================================================================
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <thread>
#include <chrono>
#include "udpcomm/ccuUdpMsg.h"
static int g_fd = -1;
static sockaddr_in g_addr{};
static bool sendBuf(const void* buf, size_t len) {
ssize_t n = sendto(g_fd, buf, len, 0,
reinterpret_cast<sockaddr*>(&g_addr), sizeof(g_addr));
return n == (ssize_t)len;
}
// CCU 消息校验和:4 字节全宽(sum 覆盖 sizeof-4 字节)
static unsigned int ccuChecksum(const unsigned char* d, int size) {
unsigned int s = 0;
for (int i = 0; i < size - (int)sizeof(Checksum); ++i) s += d[i];
return s;
}
// 配电消息校验和:1 字节(sum 覆盖 sizeof-1 字节,&0xFF)
static unsigned char disChecksum(const unsigned char* d, int size) {
unsigned int s = 0;
for (int i = 0; i < size - (int)sizeof(cChecksum); ++i) s += d[i];
return (unsigned char)(s & 0xFF);
}
static void fillCcuState(msg_CcuStateFbMsg& m, unsigned int beat) {
memset(&m, 0, sizeof(m));
m.header.start1 = CCU_UDPMSG_START1;
m.header.start2 = CCU_UDPMSG_START2;
m.header.id = CCU_STATE_FB_ID;
m.header.length = sizeof(ccuState);
m.data.fc_mode = 0x02; // 自动
m.data.fc_status = 0x04; // 运行
m.data.fc_fault_level = 0x00; // 无故障
m.data.output_power_limit = 30000;
m.data.total_generation_time = 1234;
m.data.total_generation_power = 8000;
m.data.hydrogen_capacity = 70;
m.data.liquid_oxygen_capacity = 65;
m.data.palladium_temp = 60;
m.data.main_pipe_pressure = 200;
m.data.aux_pipe_pressure = 180;
m.data.cabin_pressure1 = 101;
m.data.cabin_pressure2 = 101;
m.data.cabin_temp1 = 25;
m.data.cabin_temp2 = 26;
m.data.cabin_humidity1 = 50;
m.data.cabin_humidity2 = 52;
m.data.flame_detector1 = 0x00;
m.data.flame_detector2 = 0x00;
m.data.h2_concentration1 = 5;
m.data.o2_concentration1 = 2000;
m.data.ch3oh_concentration1 = 3;
m.data.emergency_battery1_voltage = 240;
m.data.emergency_battery1_current = 10;
m.data.emergency_battery1_max_temp = 30;
m.data.emergency_battery2_voltage = 240;
m.data.emergency_battery2_current = 10;
m.data.emergency_battery2_max_temp = 31;
m.data.cabin_ox_concentration = 2000;
m.data.cabin_temperature = 25;
m.data.cabin_humidity = 50;
m.data.cabin_pressure = 101;
m.data.power_cabin_ox_concentration = 2000;
m.data.power_cabin_temperature = 28;
m.data.power_cabin_humidity = 48;
m.data.power_cabin_pressure = 101;
m.data.ins_relay_status1 = 0x11;
m.data.ins_relay_status2 = 0x12;
m.data.dyn_relay_status1 = 0x11;
m.data.dyn_relay_status2 = 0x12;
m.data.ins_max_discharge_power = 200;
m.data.dyn_max_discharge_power = 400;
m.data.ins_soc = 85;
m.data.dyn_soc = 80;
m.data.ins_total_energy = 100;
m.data.dyn_total_energy = 500;
m.data.ins_power_input = 10;
m.data.dyn_power_input = 30;
m.data.ins_charge_status = 0x20; // 放电
m.data.dyn_charge_status = 0x20;
m.data.ins_voltage_link = 2400; // 240.0V
m.data.ins_voltage_pack = 2350;
m.data.ins_current = 400; // 20A
m.data.ins_resistance_pos = 999;
m.data.ins_resistance_neg = 999;
m.data.dyn_voltage_link = 5200; // 520.0V
m.data.dyn_voltage_pack = 5150;
m.data.dyn_current = 600; // 30A
m.data.dyn_resistance_pos = 999;
m.data.dyn_resistance_neg = 999;
m.data.ins_emergency_status = 0x00;
m.data.dyn_emergency_status = 0x00;
m.data.ins_soc_threshold1 = 90;
m.data.ins_soc_threshold2 = 80;
m.data.ins_soc_threshold3 = 70;
m.data.dyn_soc_threshold1 = 90;
m.data.dyn_soc_threshold2 = 80;
m.data.dyn_soc_threshold3 = 70;
m.data.device_online_flag1 = 0x000000FF;
m.data.device_online_flag2 = 0x0000001F;
m.data.dyn_charge_level = 0x01;
m.data.dyn_water_leak_status = 0x00;
m.data.ins_water_leak_dcdc_status = 0x00;
m.data.heartbeat = beat;
m.checkCode = ccuChecksum(reinterpret_cast<unsigned char*>(&m), sizeof(m));
}
static void fillDisHighVolBus(msg_disHighVolBusFbMsg& m) {
memset(&m, 0, sizeof(m));
m.header.start1 = DIS_MSG_HEAD1;
m.header.start2 = DIS_MSG_HEAD2;
m.header.id = DIS_HVBUS_FB_ID;
m.header.length = sizeof(disHighVolBusState);
m.data.fuelCellCircuitBreaker = BREAK_ON;
m.data.powerLithiumBatteryCircuitBreaker = BREAK_ON;
m.data.propulsionMotorCircuitBreaker = BREAK_OFF;
m.data.lithiumBatteryGroupInstrumentCircuitBreaker = BREAK_ON;
m.data.dcDc5ModuleCircuitBreaker = BREAK_ON;
m.data.bowHighVoltageDistributionBoxCircuitBreaker = BREAK_ON;
m.data.sternFTDevice45CircuitBreaker = BREAK_OFF;
m.data.sternRudderSwitch1CircuitBreaker = BREAK_OFF;
m.data.sternRudderSwitch2CircuitBreaker = BREAK_OFF;
m.data.fbReservedCircuitBreaker = BREAK_OFF;
m.data.tyzReservedCircuitBreaker = BREAK_OFF;
m.data.reservedCircuitBreaker = BREAK_OFF;
m.data.dcDcFaultWord1 = 0x00;
m.data.dcDcFaultWord2 = 0x62; // 工作状态启动 + 冷却泵运行
m.data.powerBusInsulationStatus = BREAK_ON; // 正常
m.data.aBusInsulationStatus = BREAK_ON;
m.data.meterPowerLossSignal = BREAK_ON;
m.data.emergencyPowerLossSignal = BREAK_ON;
m.data.dcDcTemperature = 60;
m.data.powerBusVoltage = 5000; // 500.0V
m.data.dcDc5ModuleCurrent = 200;
m.data.powerBusCurrent = 400;
m.data.busbarAVoltage = 5000;
m.data.propulsionMotorControlBoxCurrent = 10;
m.data.busbarACurrent = 300;
m.data.lithiumBatteryGroupMeterCurrent = 50;
m.data.bowHighVoltageDistributionBoxCurrent = 100;
m.data.sternFTDevice45Current = 0;
m.data.tyzReservedSwitchCurrent = 0;
m.data.sternRudderSwitch1Current = 0;
m.data.sternRudderSwitch2Current = 0;
m.data.coolWaterPressure = 150;
m.checkCode = disChecksum(reinterpret_cast<unsigned char*>(&m), sizeof(m));
}
static void fillDisHighAVolBus(msg_disHighAVolBusFbMsg& m) {
memset(&m, 0, sizeof(m));
m.header.start1 = DIS_MSG_HEAD1;
m.header.start2 = DIS_MSG_HEAD2;
m.header.id = DIS_HVBUSA_FB_ID;
m.header.length = sizeof(disHighAVolBusState);
m.data.bowFTDevice123CircuitBreaker = BREAK_ON;
m.data.actuatorCircuitBreaker = BREAK_OFF;
m.data.mastSteeringGearControlBoxCircuitBreaker = BREAK_OFF;
m.data.xczCircuitBreaker = BREAK_ON;
m.data.bowRudderControlBoxCircuitBreaker = BREAK_OFF;
m.data.openWaterCoverStartCylinderCircuitBreaker = BREAK_OFF;
m.data.instrumentPowerFailureSignal = BREAK_ON;
m.data.emergencyPowerFailureSignal = BREAK_ON;
m.data.waterIngressionAlarm = BREAK_ON; // 正常
m.data.busbarBVoltage = 5000;
m.data.busbarBCurrent = 300;
m.data.bowFTDevice123Current = 80;
m.data.actuatorCurrent = 0;
m.data.mastSteeringGearControlBoxCurrent = 0;
m.data.xczCurrent = 40;
m.data.bowRudderControlBoxCurrent = 0;
m.data.openWaterCoverStartCylinderCurrent = 0;
m.checkCode = disChecksum(reinterpret_cast<unsigned char*>(&m), sizeof(m));
}
static void fillDisLowMainBus(msg_disLowMainBusFbMsg& m) {
memset(&m, 0, sizeof(m));
m.header.start1 = DIS_MSG_HEAD1;
m.header.start2 = DIS_MSG_HEAD2;
m.header.id = DIS_HVBUSB_FB_ID;
m.header.length = sizeof(disLowMainBusState);
m.data.lithiumBatteryGroupInstrumentCircuitBreaker = BREAK_ON;
m.data.bowLowVoltageDistributionBoxCircuitBreaker = BREAK_ON;
m.data.unit4InstrumentDC48VCircuitBreaker = BREAK_ON;
m.data.dcC1DCDistributionPanelCircuitBreaker = BREAK_ON;
m.data.reservedCircuitBreaker1 = BREAK_OFF;
m.data.reservedCircuitBreaker2 = BREAK_OFF;
m.data.emergencyLithiumBatteryGroup2CircuitBreaker = BREAK_ON;
m.data.instrumentPowerFailureSignal = BREAK_ON;
m.data.emergencyPowerFailureSignal = BREAK_ON;
m.data.instrumentBusbarInsulationLow = BREAK_ON; // 正常
m.data.instrumentBusbarVoltage = 480; // 48.0V
m.data.bowLowVoltageDistributionBoxCurrent = 50;
m.data.dcC1InstrumentDC48VCurrent = 30;
m.data.emergencyLithiumBatteryGroup2Current = 10;
m.data.lithiumBatteryGroupInstrumentCurrent = 20;
m.data.unit4InstrumentDC48VCurrent = 15;
m.data.emergency2BusbarVoltage = 480;
m.data.compositeEnergyManagementSystemEmergencyDC48VCurrent = 5;
m.data.fuelCellSecuritySystemEmergencyDC48VCurrent = 5;
m.data.plcControlPowerCurrent = 8;
m.checkCode = disChecksum(reinterpret_cast<unsigned char*>(&m), sizeof(m));
}
static void fillDisLowBus(msg_disLowBusFbMsg& m) {
memset(&m, 0, sizeof(m));
m.header.start1 = DIS_MSG_HEAD1;
m.header.start2 = DIS_MSG_HEAD2;
m.header.id = DIS_LVBUS_FB_ID;
m.header.length = sizeof(disLowBusState);
m.data.unit1CircuitBreaker = BREAK_ON;
m.data.unit2CircuitBreaker = BREAK_ON;
m.data.unit3CircuitBreaker = BREAK_ON;
m.data.unit5CircuitBreaker = BREAK_OFF;
m.data.bowPZDeviceCircuitBreaker = BREAK_ON;
m.data.reservedCircuitBreaker1 = BREAK_OFF;
m.data.reservedCircuitBreaker2 = BREAK_OFF;
m.data.emergencyLithiumBatteryGroup1CircuitBreaker = BREAK_ON;
m.data.powerFailureSignal = BREAK_ON;
m.data.emergencyPowerFailureSignal = BREAK_ON;
m.data.waterIngressionAlarm = BREAK_ON; // 正常
m.data.instrumentBusbarVoltage = 240; // 24.0V
m.data.instrumentBusbarCurrent = 60;
m.data.unit1Current = 15;
m.data.unit2Current = 15;
m.data.unit3Current = 15;
m.data.unit5Current = 0;
m.data.bowPZDeviceCurrent = 10;
m.data.emergencyLithiumBatteryGroup1Current = 8;
m.data.emergency1BusbarVoltage = 240;
m.checkCode = disChecksum(reinterpret_cast<unsigned char*>(&m), sizeof(m));
}
int main(int argc, char* argv[]) {
const char* host = (argc > 1) ? argv[1] : "127.0.0.1";
int port = (argc > 2) ? atoi(argv[2]) : 5001;
int rounds = (argc > 3) ? atoi(argv[3]) : 0; // 0 = 持续
int intervalMs = (argc > 4) ? atoi(argv[4]) : 200;
g_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (g_fd < 0) { perror("socket"); return 1; }
g_addr.sin_family = AF_INET;
g_addr.sin_port = htons(static_cast<unsigned short>(port));
if (inet_pton(AF_INET, host, &g_addr.sin_addr) != 1) {
perror("inet_pton"); close(g_fd); return 1;
}
unsigned int beat = 1;
int sent = 0;
while (rounds == 0 || sent < rounds) {
msg_CcuStateFbMsg ccu;
msg_disHighVolBusFbMsg hv;
msg_disHighAVolBusFbMsg hva;
msg_disLowMainBusFbMsg lvmain;
msg_disLowBusFbMsg lv;
fillCcuState(ccu, beat);
fillDisHighVolBus(hv);
fillDisHighAVolBus(hva);
fillDisLowMainBus(lvmain);
fillDisLowBus(lv);
if (!sendBuf(&ccu, sizeof(ccu)) ||
!sendBuf(&hv, sizeof(hv)) ||
!sendBuf(&hva, sizeof(hva)) ||
!sendBuf(&lvmain, sizeof(lvmain)) ||
!sendBuf(&lv, sizeof(lv))) {
perror("sendto"); break;
}
++beat;
++sent;
std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs));
}
close(g_fd);
printf("sent %d round(s) of 5 message types to %s:%d\n", sent, host, port);
return 0;
}