diff --git a/jsowell-common/src/main/java/com/jsowell/common/YouDianUtils.java b/jsowell-common/src/main/java/com/jsowell/common/YouDianUtils.java index fd12d8f6b..a1d1d61f0 100644 --- a/jsowell-common/src/main/java/com/jsowell/common/YouDianUtils.java +++ b/jsowell-common/src/main/java/com/jsowell/common/YouDianUtils.java @@ -3,29 +3,42 @@ package com.jsowell.common; import com.jsowell.common.util.BytesUtil; import lombok.extern.slf4j.Slf4j; +import java.util.Arrays; + /** * 友电电单车充电桩协议工具类 */ @Slf4j public class YouDianUtils { + public static void main(String[] args) { + String s = "44 4e 59 0a 00 3b 37 ab 04 01 00 21 00 38 02"; + byte[] bytes = BytesUtil.hexStringToByteArray(s); + + String s2 = BytesUtil.printHexBinary(bytes); + System.out.println(s2); + byte[] bytes2 = BytesUtil.hexStringToByteArray(s2); + System.out.println(bytes2); + boolean b = validateChecksum(bytes); + + // + String s3 = "44 4e 59 0a 00 3b 37 ab 04 01 00 21 00"; + byte[] bytes3 = BytesUtil.hexStringToByteArray(s3); + int i = calculateCheckField(bytes3); + BytesUtil.intToBytesLittle(i); + } /** * 校验方法 * 整个数据包中的每个字节(不包括校验字段本身),将它们的数值累加起来。然后取累加和的低2字节(16位),作为校验字段的值 + * @param bytes 完整数据包, 包含校验字段 */ public static boolean validateChecksum(byte[] bytes) { if (bytes.length < 2) { return false; // 校验字段长度不足时返回 false } - // 计算累加和 - int sum = 0; - for (int i = 0; i < bytes.length - 2; i++) { - sum += (bytes[i] & 0xFF); // 将每个字节视为无符号值进行累加 - } - - // 取累加和的低 2 字节(16 位) - int calculatedChecksum = sum & 0xFFFF; + byte[] copyOfRange = Arrays.copyOfRange(bytes, 0, bytes.length - 2); + int calculatedChecksum = calculateCheckField(copyOfRange); // 读取校验字段的值 byte[] checksumBytes = {bytes[bytes.length - 2], bytes[bytes.length - 1]}; @@ -38,20 +51,23 @@ public class YouDianUtils { /** * 计算校验字段 + * @param bytes 数据包不含校验字段, 包头+长度+物理ID+消息ID+命令+数据 */ public static int calculateCheckField(byte[] bytes) { // 计算累加和 int sum = 0; - for (int i = 0; i < bytes.length - 2; i++) { - sum += (bytes[i] & 0xFF); // 将每个字节视为无符号值进行累加 + for (byte aByte : bytes) { + sum += (aByte & 0xFF); // 将每个字节视为无符号值进行累加 } // 取累加和的低 2 字节(16 位) - return sum & 0xFFFF; + int i = sum & 0xFFFF; + log.info("计算校验字段:{}", i); + return i; } /** - * 获取校验字段byte数组 + * 获取校验字段byte数组 小端 */ public static byte[] getCheckFieldBytes(byte[] bytes) { int calculatedChecksum = calculateCheckField(bytes); diff --git a/jsowell-common/src/main/java/com/jsowell/common/util/BytesUtil.java b/jsowell-common/src/main/java/com/jsowell/common/util/BytesUtil.java index 4f43ded4b..bfa054737 100644 --- a/jsowell-common/src/main/java/com/jsowell/common/util/BytesUtil.java +++ b/jsowell-common/src/main/java/com/jsowell/common/util/BytesUtil.java @@ -2,13 +2,12 @@ package com.jsowell.common.util; import com.google.common.primitives.Bytes; import com.jsowell.common.constant.Constants; -import org.springframework.mail.MailMessage; import javax.xml.bind.DatatypeConverter; import java.io.UnsupportedEncodingException; import java.math.BigInteger; +import java.nio.ByteBuffer; import java.time.LocalDateTime; -import java.util.Arrays; import java.util.Date; import java.util.Stack; @@ -18,12 +17,20 @@ public class BytesUtil { /** * 将int数值转换为占两个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序。 + * 大端模式 */ public static byte[] intToBytes(int value) { //limit 传入2 return intToBytes(value, 2); } + /** + * 将int转换byte 大端模式 + * + * @param value int值 + * @param limit 保留长度 + * @return + */ public static byte[] intToBytes(int value, int limit) { byte[] src = new byte[limit]; for (int i = 0; i < limit; i++) { @@ -37,6 +44,52 @@ public class BytesUtil { return src; } + /** + * 将int转换为byte数组,并采用小端模式。 + * + * @param value 需要转换的整型值 + * @return 转换后的字节数组 + */ + public static byte[] intToBytesLittleEndian(int value) { + ByteBuffer buffer = ByteBuffer.allocate(2); // 分配4字节的空间 + buffer.order(java.nio.ByteOrder.LITTLE_ENDIAN); // 设置为小端模式 + buffer.putInt(value); // 将整数放入缓冲区 + return buffer.array(); // 返回字节数组 + } + + /** + * 将int转为byte 小端模式 + * + * @param value int值 + * @return + */ + public static byte[] intToBytesLittle(int value) { + //limit 传入2 + return intToBytesLittle(value, 2); + } + + /** + * 将int转换byte 小端模式 + * + * @param value int值 + * @param limit 保留长度 + * @return + */ + public static byte[] intToBytesLittle(int value, int limit) { + // 参数检查,确保limit非负且不超过int类型的字节长度 + if (limit < 0 || limit > 4) { + throw new IllegalArgumentException("Limit must be between 0 and 4 inclusive."); + } + + byte[] src = new byte[limit]; + for (int i = 0; i < limit; i++) { + int x = 8 * i; + // 直接进行位移操作,无需特殊判断x是否为0 + src[i] = (byte) ((value >> x) & 0xFF); + } + return src; + } + public static int bytesToInt(byte[] src) { return bytesToInt(src, 0); } @@ -148,6 +201,7 @@ public class BytesUtil { /** * 将byte[]转为十六进制的字符串 + * 可以与hexStringToByteArray互转 * @param bytes byte[] * @return 转换后的字符串 */ @@ -270,36 +324,7 @@ public class BytesUtil { return revert(temp); } - /** - * 将int转为byte 小端模式 - * - * @param value int值 - * @return - */ - public static byte[] intToBytesLittle(int value) { - //limit 传入2 - return intToBytes(value, 2); - } - /** - * 将int转换byte 小端模式 - * - * @param value int值 - * @param limit 保留长度 - * @return - */ - public static byte[] intToBytesLittle(int value, int limit) { - byte[] src = new byte[limit]; - for (int i = 0; i < limit; i++) { - int x = 8 * i; - if (x == 0) { - src[i] = (byte) (value & 0xFF); - } else { - src[i] = (byte) ((value >> x) & 0xFF); - } - } - return src; - } public static void main(String[] args) { byte[] length = new byte[] {0x09, 0x00}; @@ -744,16 +769,31 @@ public class BytesUtil { /** * 将String转换为byte[] - * + * 可以与printHexBinary互转 * @param s String * @return byte[] */ public static byte[] hexStringToByteArray(String s) { + if (s == null || s.length() % 2 != 0) { + throw new IllegalArgumentException("Input string must not be null and its length must be even."); + } + + s = s.replaceAll("\\s", ""); // 使用正则表达式去除所有空白字符 + int len = s.length(); byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); + int byteValue = Character.digit(s.charAt(i), 16) << 4; + byteValue += Character.digit(s.charAt(i + 1), 16); + + if (byteValue < 0) { + throw new IllegalArgumentException("Invalid character in the input string at index " + i); + } + + data[i / 2] = (byte) byteValue; } + return data; } } diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/AbstractEBikeHandler.java b/jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/AbstractEBikeHandler.java index 99d505eb3..8d49ec51b 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/AbstractEBikeHandler.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/AbstractEBikeHandler.java @@ -40,7 +40,7 @@ public abstract class AbstractEBikeHandler implements InitializingBean { byte[] head = dataProtocol.getHead(); // 长度 = 物理ID(4) + 消息ID(2) + 命令(1) + 数据(n) + 校验(2),每包最多256字节 - byte[] length = BytesUtil.intToBytes(9 + messageBody.length); + byte[] length = BytesUtil.intToBytesLittle(9 + messageBody.length); // 物理id byte[] physicalId = dataProtocol.getPhysicalId(); diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/GetServerTimeHandler.java b/jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/GetServerTimeHandler.java index 88e7f7d07..37cb97b42 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/GetServerTimeHandler.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/GetServerTimeHandler.java @@ -39,7 +39,7 @@ public class GetServerTimeHandler extends AbstractEBikeHandler { // 获取当前服务器10位时间戳 byte[] timeBytes = BytesUtil.getIntBytes((int) (System.currentTimeMillis() / 1000)); - System.out.println("data: " + BytesUtil.bytesToIntLittle(timeBytes)); + // System.out.println("data: " + BytesUtil.bytesToIntLittle(timeBytes)); return getResult(dataProtocol, timeBytes); } } diff --git a/jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/HeartbeatHandler.java b/jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/HeartbeatHandler.java index afd7547a7..e7d1200b1 100644 --- a/jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/HeartbeatHandler.java +++ b/jsowell-netty/src/main/java/com/jsowell/netty/handler/electricbicycles/HeartbeatHandler.java @@ -1,6 +1,7 @@ package com.jsowell.netty.handler.electricbicycles; import com.alibaba.fastjson2.JSON; +import com.jsowell.common.constant.Constants; import com.jsowell.common.core.domain.ebike.EBikeDataProtocol; import com.jsowell.netty.factory.EBikeOperateFactory; import com.jsowell.pile.domain.ebike.AbsEBikeMessage; @@ -36,6 +37,6 @@ public class HeartbeatHandler extends AbstractEBikeHandler { EBikeMessageCmd21 message = (EBikeMessageCmd21) AbsEBikeMessage.parseMessage(dataProtocol.getBytes()); EBikeMessageCmd21.DeviceHeartbeat deviceHeartbeat = message.getDeviceHeartbeat(); log.info("设备心跳包:{}", JSON.toJSONString(message)); - return new byte[0]; + return getResult(dataProtocol, Constants.zeroByteArray); } }