修复:跨线程数据竞争 + 协议字段越界 + 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 二进制
This commit is contained in:
@@ -108,3 +108,20 @@ target_link_libraries(udpFeeder
|
||||
pthread
|
||||
dl
|
||||
)
|
||||
|
||||
################################################fullFeeder(集成测试用)#######
|
||||
# 全类型 UDP 仿真器:CCU 状态 + 4 路配电反馈(自包含,仅依赖协议结构体头文件)
|
||||
add_executable(fullFeeder
|
||||
integration/full_feeder.cpp
|
||||
)
|
||||
target_include_directories(fullFeeder PRIVATE
|
||||
${PM_SRC_DIR}
|
||||
${PM_SRC_DIR}/udpcomm
|
||||
)
|
||||
set_target_properties(fullFeeder PROPERTIES
|
||||
CXX_STANDARD 11
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
)
|
||||
target_link_libraries(fullFeeder
|
||||
pthread
|
||||
)
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
//=======================================================================
|
||||
// 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;
|
||||
}
|
||||
@@ -170,3 +170,16 @@ TEST(UdpCommTest, GetDate)
|
||||
EXPECT_LE(minute, 59u);
|
||||
EXPECT_LE(second, 59u);
|
||||
}
|
||||
|
||||
TEST(UdpCommTest, SendDisSysCmdFailsWithoutSocket)
|
||||
{
|
||||
udpComm u; // 默认构造 udpScoket=nullptr,sendMsg 应返回 false
|
||||
disHighVolBusCmd c1{};
|
||||
disHighAVolBusCmd c2{};
|
||||
disHighBVolBusCmd c3{};
|
||||
disLowBusCmd c4{};
|
||||
EXPECT_FALSE(u.sendDisSysCmd(c1));
|
||||
EXPECT_FALSE(u.sendDisSysCmd(c2));
|
||||
EXPECT_FALSE(u.sendDisSysCmd(c3));
|
||||
EXPECT_FALSE(u.sendDisSysCmd(c4));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user