package com.jsowell.common; import com.google.common.primitives.Bytes; import com.jsowell.common.enums.ebike.EBikeTypeEnum; import com.jsowell.common.util.BytesUtil; import lombok.extern.slf4j.Slf4j; import java.util.Arrays; /** * 友电电单车充电桩协议工具类 */ @Slf4j public class YouDianUtils { /** * 解析物理id * 电单车二维码地址:https://api.jsowellcloud.com/app-xcx-h5/pile/pileDetail/1921ed07/ff * 1921ed07是前端解析出来的pileSn, 但不是实际的物理ID,需要通过识别码和编号计算出物理ID, 解析后为15540505 * @param pileSN * @return */ public static String resolvePhysicalId(String pileSN) { byte[] bytes = BytesUtil.str2Bcd(pileSN); int i = convertToPhysicalId(bytes); return i + ""; } /** * 将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); } /** * 获取物理IDbyte数组 * @param deviceNumber * @return */ public static byte[] getPhysicalIdBytes(int deviceNumber) { byte[] physicalIdBytes = BytesUtil.intToBytes(deviceNumber, 3); // 拼接识别码, 12路插座 byte[] concat = Bytes.concat(EBikeTypeEnum.socket_12way.getBytes(), physicalIdBytes); return BytesUtil.reverse(concat); } public static String getPhysicalIdHEXStr(int deviceNumber) { byte[] physicalIdBytes = BytesUtil.intToBytes(deviceNumber, 3); // 拼接识别码, 12路插座 byte[] concat = Bytes.concat(EBikeTypeEnum.socket_12way.getBytes(), physicalIdBytes); byte[] reverse = BytesUtil.reverse(concat); return BytesUtil.printHexBinary(reverse); } // public static String convertPortNumberToHex(int portNumber) { // if (portNumber < 1 || portNumber > 16) { // throw new IllegalArgumentException("Port number must be between 1 and 16."); // } // // // 端口号从1开始,因此需要减去1 // int hexValue = portNumber - 1; // // // 转换为16进制字符串,并确保长度为2位 // String hexString = String.format("0x%02X", hexValue); // // return hexString; // } /** * 端口号转换 * 指充电桩的插口号,端口号从0开始,如0x00-0x0F则代表第1路-第16路,0x00=第1路,0x09=第十路,0x0A=第十一路,FF=设备智能选择端口(服务器下发); * @param portNumber * @return */ public static byte[] convertPortNumberToBytes(int portNumber) { if (portNumber < 1 || portNumber > 16) { throw new IllegalArgumentException("Port number must be between 1 and 16."); } // 端口号从1开始,因此需要减去1 int hexValue = portNumber - 1; // 转换为字节数组 return BytesUtil.intToBytesLittle(hexValue, 1); } /** * 端口号从0开始 * @param portNumber * @return */ public static String convertPortNumberToString(int portNumber) { // if (portNumber < 0 || portNumber > 15) { // throw new IllegalArgumentException("Port number must be between 0 and 15."); // } // 转换为字节数组 return String.format("%02d", portNumber + 1); } }