mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-06-17 05:39:50 +08:00
update 电单车协议
This commit is contained in:
@@ -3,29 +3,42 @@ package com.jsowell.common;
|
|||||||
import com.jsowell.common.util.BytesUtil;
|
import com.jsowell.common.util.BytesUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 友电电单车充电桩协议工具类
|
* 友电电单车充电桩协议工具类
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class YouDianUtils {
|
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位),作为校验字段的值
|
* 整个数据包中的每个字节(不包括校验字段本身),将它们的数值累加起来。然后取累加和的低2字节(16位),作为校验字段的值
|
||||||
|
* @param bytes 完整数据包, 包含校验字段
|
||||||
*/
|
*/
|
||||||
public static boolean validateChecksum(byte[] bytes) {
|
public static boolean validateChecksum(byte[] bytes) {
|
||||||
if (bytes.length < 2) {
|
if (bytes.length < 2) {
|
||||||
return false; // 校验字段长度不足时返回 false
|
return false; // 校验字段长度不足时返回 false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算累加和
|
byte[] copyOfRange = Arrays.copyOfRange(bytes, 0, bytes.length - 2);
|
||||||
int sum = 0;
|
int calculatedChecksum = calculateCheckField(copyOfRange);
|
||||||
for (int i = 0; i < bytes.length - 2; i++) {
|
|
||||||
sum += (bytes[i] & 0xFF); // 将每个字节视为无符号值进行累加
|
|
||||||
}
|
|
||||||
|
|
||||||
// 取累加和的低 2 字节(16 位)
|
|
||||||
int calculatedChecksum = sum & 0xFFFF;
|
|
||||||
|
|
||||||
// 读取校验字段的值
|
// 读取校验字段的值
|
||||||
byte[] checksumBytes = {bytes[bytes.length - 2], bytes[bytes.length - 1]};
|
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) {
|
public static int calculateCheckField(byte[] bytes) {
|
||||||
// 计算累加和
|
// 计算累加和
|
||||||
int sum = 0;
|
int sum = 0;
|
||||||
for (int i = 0; i < bytes.length - 2; i++) {
|
for (byte aByte : bytes) {
|
||||||
sum += (bytes[i] & 0xFF); // 将每个字节视为无符号值进行累加
|
sum += (aByte & 0xFF); // 将每个字节视为无符号值进行累加
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取累加和的低 2 字节(16 位)
|
// 取累加和的低 2 字节(16 位)
|
||||||
return sum & 0xFFFF;
|
int i = sum & 0xFFFF;
|
||||||
|
log.info("计算校验字段:{}", i);
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取校验字段byte数组
|
* 获取校验字段byte数组 小端
|
||||||
*/
|
*/
|
||||||
public static byte[] getCheckFieldBytes(byte[] bytes) {
|
public static byte[] getCheckFieldBytes(byte[] bytes) {
|
||||||
int calculatedChecksum = calculateCheckField(bytes);
|
int calculatedChecksum = calculateCheckField(bytes);
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ package com.jsowell.common.util;
|
|||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
import com.jsowell.common.constant.Constants;
|
import com.jsowell.common.constant.Constants;
|
||||||
import org.springframework.mail.MailMessage;
|
|
||||||
|
|
||||||
import javax.xml.bind.DatatypeConverter;
|
import javax.xml.bind.DatatypeConverter;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
@@ -18,12 +17,20 @@ public class BytesUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将int数值转换为占两个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序。
|
* 将int数值转换为占两个字节的byte数组,本方法适用于(高位在前,低位在后)的顺序。
|
||||||
|
* 大端模式
|
||||||
*/
|
*/
|
||||||
public static byte[] intToBytes(int value) {
|
public static byte[] intToBytes(int value) {
|
||||||
//limit 传入2
|
//limit 传入2
|
||||||
return intToBytes(value, 2);
|
return intToBytes(value, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将int转换byte 大端模式
|
||||||
|
*
|
||||||
|
* @param value int值
|
||||||
|
* @param limit 保留长度
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public static byte[] intToBytes(int value, int limit) {
|
public static byte[] intToBytes(int value, int limit) {
|
||||||
byte[] src = new byte[limit];
|
byte[] src = new byte[limit];
|
||||||
for (int i = 0; i < limit; i++) {
|
for (int i = 0; i < limit; i++) {
|
||||||
@@ -37,6 +44,52 @@ public class BytesUtil {
|
|||||||
return src;
|
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) {
|
public static int bytesToInt(byte[] src) {
|
||||||
return bytesToInt(src, 0);
|
return bytesToInt(src, 0);
|
||||||
}
|
}
|
||||||
@@ -148,6 +201,7 @@ public class BytesUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将byte[]转为十六进制的字符串
|
* 将byte[]转为十六进制的字符串
|
||||||
|
* 可以与hexStringToByteArray互转
|
||||||
* @param bytes byte[]
|
* @param bytes byte[]
|
||||||
* @return 转换后的字符串
|
* @return 转换后的字符串
|
||||||
*/
|
*/
|
||||||
@@ -270,36 +324,7 @@ public class BytesUtil {
|
|||||||
return revert(temp);
|
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) {
|
public static void main(String[] args) {
|
||||||
byte[] length = new byte[] {0x09, 0x00};
|
byte[] length = new byte[] {0x09, 0x00};
|
||||||
@@ -744,16 +769,31 @@ public class BytesUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将String转换为byte[]
|
* 将String转换为byte[]
|
||||||
*
|
* 可以与printHexBinary互转
|
||||||
* @param s String
|
* @param s String
|
||||||
* @return byte[]
|
* @return byte[]
|
||||||
*/
|
*/
|
||||||
public static byte[] hexStringToByteArray(String s) {
|
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();
|
int len = s.length();
|
||||||
byte[] data = new byte[len / 2];
|
byte[] data = new byte[len / 2];
|
||||||
|
|
||||||
for (int i = 0; i < len; i += 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;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public abstract class AbstractEBikeHandler implements InitializingBean {
|
|||||||
byte[] head = dataProtocol.getHead();
|
byte[] head = dataProtocol.getHead();
|
||||||
|
|
||||||
// 长度 = 物理ID(4) + 消息ID(2) + 命令(1) + 数据(n) + 校验(2),每包最多256字节
|
// 长度 = 物理ID(4) + 消息ID(2) + 命令(1) + 数据(n) + 校验(2),每包最多256字节
|
||||||
byte[] length = BytesUtil.intToBytes(9 + messageBody.length);
|
byte[] length = BytesUtil.intToBytesLittle(9 + messageBody.length);
|
||||||
|
|
||||||
// 物理id
|
// 物理id
|
||||||
byte[] physicalId = dataProtocol.getPhysicalId();
|
byte[] physicalId = dataProtocol.getPhysicalId();
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class GetServerTimeHandler extends AbstractEBikeHandler {
|
|||||||
|
|
||||||
// 获取当前服务器10位时间戳
|
// 获取当前服务器10位时间戳
|
||||||
byte[] timeBytes = BytesUtil.getIntBytes((int) (System.currentTimeMillis() / 1000));
|
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);
|
return getResult(dataProtocol, timeBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.jsowell.netty.handler.electricbicycles;
|
package com.jsowell.netty.handler.electricbicycles;
|
||||||
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.jsowell.common.constant.Constants;
|
||||||
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
|
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
|
||||||
import com.jsowell.netty.factory.EBikeOperateFactory;
|
import com.jsowell.netty.factory.EBikeOperateFactory;
|
||||||
import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
|
import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
|
||||||
@@ -36,6 +37,6 @@ public class HeartbeatHandler extends AbstractEBikeHandler {
|
|||||||
EBikeMessageCmd21 message = (EBikeMessageCmd21) AbsEBikeMessage.parseMessage(dataProtocol.getBytes());
|
EBikeMessageCmd21 message = (EBikeMessageCmd21) AbsEBikeMessage.parseMessage(dataProtocol.getBytes());
|
||||||
EBikeMessageCmd21.DeviceHeartbeat deviceHeartbeat = message.getDeviceHeartbeat();
|
EBikeMessageCmd21.DeviceHeartbeat deviceHeartbeat = message.getDeviceHeartbeat();
|
||||||
log.info("设备心跳包:{}", JSON.toJSONString(message));
|
log.info("设备心跳包:{}", JSON.toJSONString(message));
|
||||||
return new byte[0];
|
return getResult(dataProtocol, Constants.zeroByteArray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user