Files
jsowell-charger-web/jsowell-common/src/main/java/com/jsowell/common/YouDianUtils.java
2024-10-11 09:19:19 +08:00

193 lines
6.6 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 {
public static boolean isEBikePileSn(String pileSn) {
// 判断是否为电单车桩号, 电单车pileSn长度为8, 前6位是十六进制, 尾部是07
if (pileSn.length() == 8 && pileSn.endsWith("07")) {
return true;
}
return false;
}
public static void main(String[] args) {
String pileSN = "15540507";
if (isEBikePileSn(pileSN)) {
pileSN = resolvePhysicalId(pileSN);
}
System.out.println(pileSN);
}
/**
* 解析物理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);
String resolvedPhysicalID = String.valueOf(i);
if (resolvedPhysicalID.length() < 8) {
// 转换完成后长度小于8, 返回原字符串
return pileSN;
}
return resolvedPhysicalID;
}
/**
* 将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);
}
}