2024-08-06 14:39:09 +08:00
|
|
|
|
package com.jsowell.common;
|
|
|
|
|
|
|
2024-09-04 09:17:08 +08:00
|
|
|
|
import com.google.common.primitives.Bytes;
|
|
|
|
|
|
import com.jsowell.common.enums.ebike.EBikeTypeEnum;
|
2024-08-06 14:39:09 +08:00
|
|
|
|
import com.jsowell.common.util.BytesUtil;
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
|
|
2024-08-26 16:53:52 +08:00
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
|
|
2024-08-06 14:39:09 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 友电电单车充电桩协议工具类
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
|
public class YouDianUtils {
|
2024-08-29 15:24:29 +08:00
|
|
|
|
|
2024-09-30 15:49:55 +08:00
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
|
String s = "1921ed07";
|
|
|
|
|
|
|
|
|
|
|
|
String s1 = resolvePhysicalId(s);
|
|
|
|
|
|
System.out.println(s1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解析物理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 + "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-29 15:24:29 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 将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;
|
2024-08-26 16:53:52 +08:00
|
|
|
|
}
|
2024-08-06 14:39:09 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 校验方法
|
|
|
|
|
|
* 整个数据包中的每个字节(不包括校验字段本身),将它们的数值累加起来。然后取累加和的低2字节(16位),作为校验字段的值
|
2024-08-26 16:53:52 +08:00
|
|
|
|
* @param bytes 完整数据包, 包含校验字段
|
2024-08-06 14:39:09 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public static boolean validateChecksum(byte[] bytes) {
|
|
|
|
|
|
if (bytes.length < 2) {
|
|
|
|
|
|
return false; // 校验字段长度不足时返回 false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-08-26 16:53:52 +08:00
|
|
|
|
byte[] copyOfRange = Arrays.copyOfRange(bytes, 0, bytes.length - 2);
|
|
|
|
|
|
int calculatedChecksum = calculateCheckField(copyOfRange);
|
2024-08-06 14:39:09 +08:00
|
|
|
|
|
|
|
|
|
|
// 读取校验字段的值
|
|
|
|
|
|
byte[] checksumBytes = {bytes[bytes.length - 2], bytes[bytes.length - 1]};
|
|
|
|
|
|
int receivedChecksum = BytesUtil.bytesToIntLittle(checksumBytes);
|
|
|
|
|
|
|
|
|
|
|
|
// 比较计算的校验值和接收到的校验值
|
|
|
|
|
|
log.info("计算的校验值:{}, 接收到的校验值:{}", calculatedChecksum, receivedChecksum);
|
|
|
|
|
|
return calculatedChecksum == receivedChecksum;
|
|
|
|
|
|
}
|
2024-08-26 15:48:22 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 计算校验字段
|
2024-08-26 16:53:52 +08:00
|
|
|
|
* @param bytes 数据包不含校验字段, 包头+长度+物理ID+消息ID+命令+数据
|
2024-08-26 15:48:22 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public static int calculateCheckField(byte[] bytes) {
|
|
|
|
|
|
// 计算累加和
|
|
|
|
|
|
int sum = 0;
|
2024-08-26 16:53:52 +08:00
|
|
|
|
for (byte aByte : bytes) {
|
|
|
|
|
|
sum += (aByte & 0xFF); // 将每个字节视为无符号值进行累加
|
2024-08-26 15:48:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 取累加和的低 2 字节(16 位)
|
2024-08-26 16:53:52 +08:00
|
|
|
|
int i = sum & 0xFFFF;
|
2024-08-27 16:30:05 +08:00
|
|
|
|
// log.info("计算校验字段:{}", i);
|
2024-08-26 16:53:52 +08:00
|
|
|
|
return i;
|
2024-08-26 15:48:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2024-08-26 16:53:52 +08:00
|
|
|
|
* 获取校验字段byte数组 小端
|
2024-08-26 15:48:22 +08:00
|
|
|
|
*/
|
|
|
|
|
|
public static byte[] getCheckFieldBytes(byte[] bytes) {
|
|
|
|
|
|
int calculatedChecksum = calculateCheckField(bytes);
|
|
|
|
|
|
return BytesUtil.intToBytesLittle(calculatedChecksum);
|
|
|
|
|
|
}
|
2024-09-03 11:41:33 +08:00
|
|
|
|
|
2024-09-04 09:17:08 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取物理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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-09-03 11:41:33 +08:00
|
|
|
|
// 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;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
2024-09-03 11:54:30 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 端口号转换
|
|
|
|
|
|
* 指充电桩的插口号,端口号从0开始,如0x00-0x0F则代表第1路-第16路,0x00=第1路,0x09=第十路,0x0A=第十一路,FF=设备智能选择端口(服务器下发);
|
2024-09-04 14:42:44 +08:00
|
|
|
|
* @param portNumber
|
2024-09-03 11:54:30 +08:00
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
2024-09-04 14:42:44 +08:00
|
|
|
|
public static byte[] convertPortNumberToBytes(int portNumber) {
|
2024-09-03 11:54:30 +08:00
|
|
|
|
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);
|
2024-09-03 11:41:33 +08:00
|
|
|
|
}
|
2024-09-13 09:33:23 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 端口号从0开始
|
|
|
|
|
|
* @param portNumber
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static String convertPortNumberToString(int portNumber) {
|
2024-09-14 17:19:05 +08:00
|
|
|
|
// if (portNumber < 0 || portNumber > 15) {
|
|
|
|
|
|
// throw new IllegalArgumentException("Port number must be between 0 and 15.");
|
|
|
|
|
|
// }
|
2024-09-13 09:33:23 +08:00
|
|
|
|
|
|
|
|
|
|
// 转换为字节数组
|
|
|
|
|
|
return String.format("%02d", portNumber + 1);
|
|
|
|
|
|
}
|
2024-08-06 14:39:09 +08:00
|
|
|
|
}
|