package com.depower;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
/**
* 测试多数据源
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class DataRTest {
@Test
public void dataReciveTest() {
// 示例数据2
String hexData = "0,100,31,2,226,16,6,0,6,0,0,0,0,0,0,0,0,0,0,0,0,2,0,6,0,0,0,5,0,0,13,31,13,33,13,35,13,33,13,34,13,37,13,36,13,29,13,26,13,32,13,25,13,31,13,33,13,34,13,30,13,29,13,31,13,30,13,25,13,27,13,31,13,27,0,0,12,233,0,0,7,96,27,28,28,27,6,38,221,10,38,213,38,212,0,129,3,132,79,12,116,114,97,107,1,97,0,49,164,57,111,153,1,29,0,0,0,0,0,0,0,0,0,0,0,0,56,57,56,54,48,52,68,50,49,57,50,53,68,48,49,52,54,49,54,57,56,54,52,50,51,57,48,54,52,55,51,52,57,49,53,212,235,204,104,0,0,0,0,208,208,234,8,96,4,0,0,0,0,0,0,113,40,0,0,129,1,0,0,20,5,0,0,56,0,0,0,231,127";
Map<String,Object> contents=new HashMap<String,Object>();
BmsTrackerParser(hexData,contents);
}
public void BmsTrackerParser(String hexData,Map<String,Object> contents) {
log.info("原始数据:{}",hexData);
String[] parts = hexData.substring(0, hexData.length()).split(",");
byte[] bytes = new byte[parts.length];
for (int i = 0; i < parts.length; i++) {
bytes[i] = (byte) Integer.parseInt(parts[i].trim());
}
ByteBuffer buffer = ByteBuffer.wrap(bytes);
// 解析 BMS 大端数据
dataParse(buffer,contents);
// 设置小端模式解析 Tracker 数据
trackerParse(buffer,contents);
}
private static void trackerParse(ByteBuffer buffer, Map<String, Object> mapinfo) {
log.info("=== Tracker 小端数据解析 ===");
byte[] header = new byte[4];
buffer.get(header);
log.info("包头: " + new String(header));
mapinfo.put("header", new String(header));
byte cmd = buffer.get();
log.info("命令: " + String.format("0x%02X", cmd));
mapinfo.put("cmd", String.format("0x%02X", cmd));
short length = buffer.getShort();
log.info("数据长度: " + length);
mapinfo.put("length", length);
byte[] ts = new byte[6];
buffer.get(ts);
long timestamp = 0;
for (byte b : ts) {
timestamp = (timestamp << 8) | (b & 0xFF);
}
Date date = new Date();
date.setTime(timestamp);
String uploadtime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(date);
log.info("上报时间戳: " + timestamp);
log.info("上报时间: " + uploadtime);
mapinfo.put("upload_time", uploadtime);
if (String.format("0x%02X", cmd).equals("0x02")) {
short crc = buffer.getShort();
log.info("CRC校验: " + String.format("0x%04X", crc));
}
if (String.format("0x%02X", cmd).equals("0x01")) {
byte csq = buffer.get();
log.info("4G信号强度: " + csq);
mapinfo.put("gps_signal", csq);
byte gpsMaxSignal = buffer.get();
log.info("GPS最大信号强度: " + gpsMaxSignal);
mapinfo.put("gps_max_signal", gpsMaxSignal);
int longitude = buffer.getInt();
log.info("GPS经度: " + longitude / 1e6);
mapinfo.put("longitude", longitude / 1e6);
int latitude = buffer.getInt();
log.info("GPS纬度: " + latitude / 1e6);
mapinfo.put("latitude", latitude / 1e6);
byte speed = buffer.get();
log.info("GPS速度: " + speed);
mapinfo.put("gps_speed", speed);
short angle = buffer.getShort();
log.info("GPS对地角度: " + angle);
mapinfo.put("gps_cog", angle);
byte[] iccid = new byte[20];
buffer.get(iccid);
log.info("ICCID: " + new String(iccid).trim());
mapinfo.put("iccid", new String(iccid).trim());
byte[] imei = new byte[15];
buffer.get(imei);
log.info("IMEI: " + new String(imei).trim());
mapinfo.put("imei", new String(imei).trim());
int timestamp10 = buffer.getInt();
log.info("10位时间戳版本: " + timestamp10);
Date date2 = new Date();
date2.setTime(timestamp10);
String timestamp10Str = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date2);
mapinfo.put("tracker_software_version", timestamp10Str);
log.info("10位时间版本: " + timestamp10Str);
// 小区信息
JSONArray cellList = new JSONArray(); // 创建JSON数组
for (int i = 0; i < 10; i++) {
int flag = buffer.getInt();
int cid = buffer.getInt();
int mcc = buffer.getInt();
int mnc = buffer.getInt();
int tac = buffer.getInt();
int pci = buffer.getInt();
int earfcn = buffer.getInt();
int rssi = buffer.getInt();
if (flag == 0 && cid == 0 && mcc == 0 && mnc == 0 && tac == 0 && pci == 0 && earfcn == 0 && rssi == 0) {
break;
}
// 创建单个小区的JSON对象
JSONObject cell = new JSONObject();
cell.put("flag", flag);
cell.put("cid", cid);
cell.put("mcc", mcc);
cell.put("mnc", mnc);
cell.put("tac", tac);
cell.put("pci", pci);
cell.put("earfcn", earfcn);
cell.put("rssi", rssi);
cellList.add(cell); // 添加到JSON数组
log.info("小区信息[{}] - flag: {}, cid: {}, mcc: {}, mnc: {}, tac: {}, pci: {}, earfcn: {}, rssi: {}\n", i,
flag, cid, mcc, mnc, tac, pci, earfcn, rssi);
}
mapinfo.put("cellList", cellList.toString());
short crc = buffer.getShort();
log.info("CRC校验: " + String.format("0x%04X", crc));
}
}
private static void dataParse(ByteBuffer buffer, Map<String, Object> contents) {
log.info("=== Data 大端数据解析 ===");
// 解析动态数据
byte dataType = buffer.get();
// 数据类型
log.info("数据类型: {}", Integer.toHexString(dataType & 0xFF).equals("0") ? "动态数据" : "静态数据");
contents.put("data_type", Integer.toHexString(dataType & 0xFF));
if (Integer.toHexString(dataType & 0xFF).equals("0")) {
// 2. SOC (U8)
Integer soc = Byte.toUnsignedInt(buffer.get());
log.info("SOC: {}%", soc);
contents.put("battery_soc", soc);
// 低电量告警
Integer lowBatteryWarn = soc < 30 ? 1 : 0;
contents.put("low_battery_warn", lowBatteryWarn);
// 3. MOS温度 (S8)
Integer mosTemp = Integer.valueOf(buffer.get());
log.info("MOS温度: {}℃", mosTemp);
contents.put("mos_temp", mosTemp);
// 4. 整包电压 (U16)
Integer packVoltage = Short.toUnsignedInt(buffer.getShort());
log.info("电池整包电压: {}mV", packVoltage * 100);
contents.put("battery_voltage", packVoltage * 100);
// 5. 电池包状态 (U8)
Integer packStatus = Byte.toUnsignedInt(buffer.get());
log.info("电池包状态: {} - {}", packStatus, resolvePackStatus(packStatus));
contents.put("battery_work_mode", packStatus);
// 6. MOS管状态 (U8)
Integer mosStatus = Byte.toUnsignedInt(buffer.get());
log.info("MOS管状态: {}", parseMosStatus(mosStatus));
contents.put("mos_status", mosStatus);
// 7. 当前电流 (S16)
int current = buffer.getShort();
log.info("当前电流: {}mA", Integer.valueOf(current * 100));
contents.put("battery_current", Integer.valueOf(current * 100));
// 8. 故障码 (U32*3)
long[] faultCodes = new long[3];
for (int i = 0; i < 3; i++) {
faultCodes[i] = Integer.toUnsignedLong(buffer.getInt());
}
log.info("故障码原始值: {}", Arrays.toString(faultCodes));
// 转换为24字符的十六进制字符串
StringBuilder hexBuilder = new StringBuilder(24); // 24字符 = 3 *
// 8个十六进制字符
for (long code : faultCodes) {
// 格式化为8位十六进制,不足8位自动补前导0
hexBuilder.append(String.format("%08x", code));
}
String errorCodeHex = hexBuilder.toString();
contents.put("battery_error_code", errorCodeHex);
log.info("故障码十六进制: {}", errorCodeHex);
// 9. 充电模式 (U8)
Integer chargeMode = Byte.toUnsignedInt(buffer.get());
log.info("充电模式: {} - {}", chargeMode, resolveChargeMode(chargeMode));
contents.put("charge_mode", chargeMode);
// 10. 最大充电电流 (U16)
Integer maxChargeCurrent = Short.toUnsignedInt(buffer.getShort());
log.info("最大充电电流: {}mA", maxChargeCurrent * 100);
contents.put("charge_current_max", maxChargeCurrent * 100);
// 11. 最大放电电流 (U16)
Integer maxDischargeCurrent = Short.toUnsignedInt(buffer.getShort());
log.info("最大放电电流: {}mmA", Integer.valueOf(maxDischargeCurrent * 100));
contents.put("discharge_current_max", Integer.valueOf(maxDischargeCurrent * 100));
// 12. 平均充电电流 (U16)
Integer avgChargeCurrent = Short.toUnsignedInt(buffer.getShort());
log.info("平均充电电流: {}mA", Integer.valueOf(avgChargeCurrent * 100));
contents.put("charge_current_avg", Integer.valueOf(avgChargeCurrent * 100));
// 13. 平均放电电流 (U16)
int avgDischargeCurrent = Short.toUnsignedInt(buffer.getShort());
log.info("平均放电电流: {}mA", Integer.valueOf(avgDischargeCurrent * 100));
contents.put("discharge_current_avg", Integer.valueOf(avgDischargeCurrent * 100));
// 14. 单体电压 (22节电芯)
List<Integer> cellVoltages = new ArrayList<>();
for (int i = 0; i < 22; i++) {
cellVoltages.add(Short.toUnsignedInt(buffer.getShort()));
}
String result = cellVoltages.stream().map(String::valueOf) // 将整数转为字符串
.collect(Collectors.joining("#")); // 用 # 拼接
log.info("单体电压: {} mV", result);
contents.put("cell_voltage", result);
// 单体电压最高
contents.put("battery_voltage_max", Collections.max(cellVoltages));
log.info("单体电压最高: {} mV", Collections.max(cellVoltages));
// 单体电压最低
contents.put("battery_voltage_min", Collections.min(cellVoltages));
log.info("单体电压最低: {} mV", Collections.min(cellVoltages));
// 15. 历史充电容量 (U32)
Long chargeCapacity = Integer.toUnsignedLong(buffer.getInt());
log.info("历史充电容量: {}mah", chargeCapacity);
contents.put("charge_capacity_history", chargeCapacity);
// 16. 历史放电容量 (U32)
Long dischargeCapacity = Integer.toUnsignedLong(buffer.getInt());
log.info("历史放电容量: {}mah", dischargeCapacity);
contents.put("discharge_capacity_history", dischargeCapacity);
// 17-20. NTC温度 (S8)
List<Integer> temps = new ArrayList<>();
for (int i = 1; i <= 4; i++) {
int ntcTemp = buffer.get();
log.info("NTC{}温度: {}℃", i, ntcTemp);
contents.put("ntc" + i, ntcTemp);
temps.add(ntcTemp);
}
// 计算温度指标
Integer maxTemp = Collections.max(temps);
Integer minTemp = Collections.min(temps);
Double avgTemp = temps.stream().mapToInt(Integer::intValue).average().orElse(0.0);
// 输出结果(实际使用中可能需要存储或返回这些值)
log.info("最高温度: {}℃", maxTemp);
log.info("最低温度: {}℃", minTemp);
log.info("平均温度: {}℃", avgTemp); // 保留两位小数
// 最高温度
contents.put("battery_temperature_max", maxTemp);
// 最低温度
contents.put("battery_temperature_min", minTemp);
// 平均温度
contents.put("battery_temperature_avg", avgTemp);
// 21. tracker通讯状态 (U8)
int trackerStatus = Byte.toUnsignedInt(buffer.get());
log.info("Tracker通讯状态: {}", parseTrackerStatus(trackerStatus));
contents.put("tracker_communication_status", parseTrackerStatus(trackerStatus));
// 22-28. 扩展字段
Integer dsoc = Short.toUnsignedInt(buffer.getShort());
log.info("DSOC: {}", dsoc);
contents.put("dsoc", dsoc);
Integer correctionRate = Byte.toUnsignedInt(buffer.get());
log.info("修正速率: {}", correctionRate);
contents.put("correction_rate", correctionRate);
Integer tsoc = Integer.valueOf(buffer.getShort());
log.info("TSOC: {}", tsoc);
contents.put("tsoc", tsoc);
Integer bsoc = Short.toUnsignedInt(buffer.getShort());
log.info("BSOC: {}", bsoc);
contents.put("bsoc", bsoc);
int chargeSop = Short.toUnsignedInt(buffer.getShort());
log.info("充电SOP: {}mA", Integer.valueOf(chargeSop * 100));
contents.put("charge_sop", Integer.valueOf(chargeSop * 100));
int dischargeSop = Short.toUnsignedInt(buffer.getShort());
log.info("放电SOP: {}mA", Integer.valueOf(dischargeSop * 100));
contents.put("discharge_sop", Integer.valueOf(dischargeSop * 100));
// 29. 校验码 CRC16 (U16)
Integer checksum = Short.toUnsignedInt(buffer.getShort());
log.info("校验码: {}", checksum);
contents.put("check_code", checksum);
}
else if (Integer.toHexString(dataType & 0xFF).equals("1")) {
// 2. 未满充计数 (U16)
Integer unchargedCount = Short.toUnsignedInt(buffer.getShort());
log.info("未满充计数: {}", unchargedCount);
contents.put("empty_count", unchargedCount);
// 3. 延时10S计数 (U16)
int delay10sCount = Short.toUnsignedInt(buffer.getShort());
log.info("延时10S计数: {} s", Integer.valueOf(delay10sCount * 10));
contents.put("task_max_delay_10ms", Integer.valueOf(delay10sCount * 10));
// 4. BMS软件版本号 (u32)
int version = buffer.getInt();
String softwareVersion = String.format("V%d.%d&&TV%d.%d", (version >> 24) & 0xFF, (version >> 16) & 0xFF,
(version >> 8) & 0xFF, version & 0xFF);
log.info("BMS软件版本号: {}", softwareVersion);
contents.put("battery_soft_version", softwareVersion);
// 5. BMS硬件版本号 (U16)
int hwVersion = Short.toUnsignedInt(buffer.getShort());
String hardwareVersion = String.format("V%d.%d", (hwVersion >> 8) & 0xFF, hwVersion & 0xFF);
log.info("BMS硬件版本号: {}", hardwareVersion);
contents.put("battery_hard_version", hardwareVersion);
// 6. SN码 (ASCII码)
byte[] snBytes = new byte[32];
buffer.get(snBytes);
String serialNumber = new String(snBytes).trim();
log.info("SN码: {}", serialNumber);
contents.put("sn", serialNumber);
// 7. 历史充电次数 (U32)
Long chargeCycleCount = buffer.getInt() & 0xFFFFFFFFL;
log.info("历史充电次数: {} 次", chargeCycleCount);
contents.put("charge_cycles_history", chargeCycleCount);
// 8. 循环次数 (U16)
Integer cycleCount = Short.toUnsignedInt(buffer.getShort());
log.info("循环次数: {} 次", cycleCount);
contents.put("battery_cycle_times", cycleCount);
// 9. SOH (U8)
Integer soh = Byte.toUnsignedInt(buffer.get());
log.info("SOH: {}", soh);
contents.put("soh", soh);
// 10. 真实SOH (U16)
Integer realSoh = Short.toUnsignedInt(buffer.getShort());
log.info("真实SOH: {}", realSoh);
contents.put("actual_soh", realSoh);
// 11. 历史充电总能量 (U32)
Long totalChargeEnergy = buffer.getInt() & 0xFFFFFFFFL;
log.info("历史充电总能量: {} kwh", totalChargeEnergy);
contents.put("total_charge_energy", totalChargeEnergy);
// 12. FCC (U32)
Long fcc = buffer.getInt() & 0xFFFFFFFFL;
log.info("FCC: {} mah", fcc);
contents.put("fcc", fcc);
// 13. 校验码 (U16)
Integer checksum = Short.toUnsignedInt(buffer.getShort());
log.info("校验码: {}", checksum);
contents.put("check_code", checksum);
}
}
private static String resolvePackStatus(int code) {
switch (code) {
case 0x01:
return "放电模式";
case 0x10:
return "充电模式";
case 0x11:
return "充电准备";
case 0x21:
return "保护模式";
case 0x30:
return "待机无输出";
case 0x31:
return "待机预放电";
case 0xFF:
return "故障需返厂";
default:
return "未知状态";
}
}
private static String parseMosStatus(int status) {
return String.format("放电MOS:%s 充电MOS:%s 预放电MOS:%s 均衡管:%s", (status & 0x02) != 0 ? "开" : "关",
(status & 0x04) != 0 ? "开" : "关", (status & 0x08) != 0 ? "开" : "关", (status & 0x10) != 0 ? "开" : "关");
}
private static String resolveChargeMode(int mode) {
switch (mode) {
case 1:
return "标准充";
case 2:
return "快充";
case 3:
return "盲充";
default:
return "未知模式";
}
}
private static String parseTrackerStatus(int status) {
if ((status & 0x01) != 0) {
return "未收到";
} else if ((status & 0x02) != 0) {
return "未回复";
} else if ((status & 0x04) != 0) {
return "GPS异常";
} else {
return "正常";
}
}
// 大端模式CRC校验(无长度字段)
public static boolean checkBigEndianCRC(ByteBuffer buffer) {
if (buffer.remaining() < 2) {
log.warn("大端CRC校验失败:缓冲区不足");
return false;
}
// 保存原始位置
int originalPosition = buffer.position();
try {
// 获取存储的CRC值(大端)
int expectedCRC = Short.toUnsignedInt(buffer.getShort(buffer.limit() - 2));
log.info("大端CRC期望值: 0x" + Integer.toHexString(expectedCRC));
// 计算缓冲区实际数据的CRC(不包括最后2字节的CRC)
byte[] data = new byte[buffer.limit() - 2];
buffer.position(0);
buffer.get(data);
short calculatedCRC = calculateCRC16(data, 0, data.length);
// 比较校验结果
if (Short.toUnsignedInt(calculatedCRC) == expectedCRC) {
log.info("大端CRC校验通过");
return true;
}
log.warn("大端CRC校验失败:计算值0x" + Integer.toHexString(Short.toUnsignedInt(calculatedCRC)) + " != 期望值0x"
+ Integer.toHexString(expectedCRC));
return false;
} finally {
buffer.position(originalPosition);
}
}
// 小端模式CRC校验(有长度字段)
public static boolean checkLittleEndianCRC(ByteBuffer buffer) {
if (buffer.remaining() < 4) { // 至少需要长度字段(2) + CRC(2)
log.warn("小端CRC校验失败:缓冲区不足");
return false;
}
// 保存原始位置和字节序
int originalPosition = buffer.position();
ByteOrder originalOrder = buffer.order();
try {
// 读取长度字段(假设为大端)
buffer.position(0);
int dataLength = Short.toUnsignedInt(buffer.getShort());
// 检查数据完整性
if (buffer.limit() < 2 + dataLength + 2) {
log.warn("小端CRC校验失败:数据不完整,需要长度: " + (2 + dataLength + 2) + ", 实际长度: " + buffer.limit());
return false;
}
// 获取存储的CRC值(小端)
buffer.position(2 + dataLength);
buffer.order(ByteOrder.LITTLE_ENDIAN);
int expectedCRC = Short.toUnsignedInt(buffer.getShort());
log.info("小端CRC期望值: 0x" + Integer.toHexString(expectedCRC));
// 计算实际数据的CRC(长度字段 + 数据)
byte[] data = new byte[2 + dataLength];
buffer.position(0);
buffer.get(data);
short calculatedCRC = calculateCRC16(data, 0, data.length);
// 比较校验结果
if (Short.toUnsignedInt(calculatedCRC) == expectedCRC) {
log.info("小端CRC校验通过");
return true;
}
log.warn("小端CRC校验失败:计算值0x" + Integer.toHexString(Short.toUnsignedInt(calculatedCRC)) + " != 期望值0x"
+ Integer.toHexString(expectedCRC));
return false;
} finally {
buffer.position(originalPosition);
buffer.order(originalOrder);
}
}
// CRC-16/MODBUS计算实现
public static short calculateCRC16(byte[] data, int offset, int length) {
int crc = 0xFFFF;
int polynomial = 0xA001; // MODBUS多项式
for (int i = offset; i < offset + length; i++) {
crc ^= (data[i] & 0xFF);
for (int j = 0; j < 8; j++) {
if ((crc & 0x0001) != 0) {
crc = (crc >> 1) ^ polynomial;
} else {
crc = crc >> 1;
}
}
}
return (short) crc;
}
// 测试方法
public static void main(String[] args) {
// 测试大端模式
System.out.println("===== 测试大端模式 =====");
String bigEndianData = "大端模式测试数据";
ByteBuffer bigEndianBuffer = ByteBuffer.allocate(bigEndianData.getBytes().length + 2);
bigEndianBuffer.put(bigEndianData.getBytes());
short bigEndianCRC = calculateCRC16(bigEndianData.getBytes(), 0, bigEndianData.getBytes().length);
bigEndianBuffer.putShort(bigEndianCRC);
bigEndianBuffer.flip();
boolean bigEndianResult = checkBigEndianCRC(bigEndianBuffer);
System.out.println("大端校验结果: " + bigEndianResult);
// 测试小端模式
System.out.println("\n===== 测试小端模式 =====");
String littleEndianPayload = "小端模式测试数据";
byte[] payloadBytes = littleEndianPayload.getBytes();
ByteBuffer littleEndianBuffer = ByteBuffer.allocate(2 + payloadBytes.length + 2);
// 写入长度字段(大端)
littleEndianBuffer.putShort((short) payloadBytes.length);
// 写入实际数据
littleEndianBuffer.put(payloadBytes);
// 计算CRC(长度字段+数据)
byte[] dataForCRC = new byte[2 + payloadBytes.length];
littleEndianBuffer.position(0);
littleEndianBuffer.get(dataForCRC);
short littleEndianCRC = calculateCRC16(dataForCRC, 0, dataForCRC.length);
// 以小端模式写入CRC
littleEndianBuffer.order(ByteOrder.LITTLE_ENDIAN);
littleEndianBuffer.putShort(littleEndianCRC);
littleEndianBuffer.flip();
boolean littleEndianResult = checkLittleEndianCRC(littleEndianBuffer);
System.out.println("小端校验结果: " + littleEndianResult);
// 测试无效数据
System.out.println("\n===== 测试无效数据 =====");
ByteBuffer invalidBuffer = ByteBuffer.allocate(10);
invalidBuffer.put("无效数据".getBytes());
invalidBuffer.putShort((short) 0x1234); // 随机CRC
invalidBuffer.flip();
boolean invalidBigEndian = checkBigEndianCRC(invalidBuffer);
System.out.println("无效数据大端校验结果: " + invalidBigEndian);
// 测试小端模式无效数据
ByteBuffer invalidLittleBuffer = ByteBuffer.allocate(10);
invalidLittleBuffer.putShort((short) 5); // 长度字段
invalidLittleBuffer.put("123".getBytes()); // 数据不足
invalidLittleBuffer.order(ByteOrder.LITTLE_ENDIAN);
invalidLittleBuffer.putShort((short) 0x5678);
invalidLittleBuffer.flip();
boolean invalidLittleEndian = checkLittleEndianCRC(invalidLittleBuffer);
System.out.println("无效数据小端校验结果: " + invalidLittleEndian);
}
}
这是原始代码,分析代码是否正确