package com.jsowell.common; import com.jsowell.common.util.BytesUtil; import lombok.extern.slf4j.Slf4j; import java.util.Arrays; /** * 友电电单车充电桩协议工具类 */ @Slf4j public class YouDianUtils { /** * 将byte数组转换为物理ID * @param bytes 输入的byte数组 * @return 物理ID对象,包含设备识别码和二维码下方的编号 */ public static int convertToPhysicalId(byte[] bytes) { // 检查输入是否合法 if (bytes == null || bytes.length != 4) { throw new IllegalArgumentException("Input byte array must be of length 4."); } // 小端模式转大端模式 byte[] bigEndianBytes = new byte[4]; bigEndianBytes[0] = bytes[3]; bigEndianBytes[1] = bytes[2]; bigEndianBytes[2] = bytes[1]; bigEndianBytes[3] = bytes[0]; // 提取设备识别码 byte deviceId = bigEndianBytes[0]; // 剩余的三个字节转换为十进制 int deviceNumber = ((bigEndianBytes[1] & 0xFF) << 16) | ((bigEndianBytes[2] & 0xFF) << 8) | (bigEndianBytes[3] & 0xFF); log.debug("设备识别码:{}, 桩编号:{}", deviceId, deviceNumber); return deviceNumber; } /** * 校验方法 * 整个数据包中的每个字节(不包括校验字段本身),将它们的数值累加起来。然后取累加和的低2字节(16位),作为校验字段的值 * @param bytes 完整数据包, 包含校验字段 */ public static boolean validateChecksum(byte[] bytes) { if (bytes.length < 2) { return false; // 校验字段长度不足时返回 false } byte[] copyOfRange = Arrays.copyOfRange(bytes, 0, bytes.length - 2); int calculatedChecksum = calculateCheckField(copyOfRange); // 读取校验字段的值 byte[] checksumBytes = {bytes[bytes.length - 2], bytes[bytes.length - 1]}; int receivedChecksum = BytesUtil.bytesToIntLittle(checksumBytes); // 比较计算的校验值和接收到的校验值 log.info("计算的校验值:{}, 接收到的校验值:{}", calculatedChecksum, receivedChecksum); return calculatedChecksum == receivedChecksum; } /** * 计算校验字段 * @param bytes 数据包不含校验字段, 包头+长度+物理ID+消息ID+命令+数据 */ public static int calculateCheckField(byte[] bytes) { // 计算累加和 int sum = 0; for (byte aByte : bytes) { sum += (aByte & 0xFF); // 将每个字节视为无符号值进行累加 } // 取累加和的低 2 字节(16 位) int i = sum & 0xFFFF; // log.info("计算校验字段:{}", i); return i; } /** * 获取校验字段byte数组 小端 */ public static byte[] getCheckFieldBytes(byte[] bytes) { int calculatedChecksum = calculateCheckField(bytes); return BytesUtil.intToBytesLittle(calculatedChecksum); } }