2023-03-04 16:29:55 +08:00
|
|
|
|
package com.jsowell.common.util;
|
|
|
|
|
|
|
|
|
|
|
|
import com.google.common.primitives.Bytes;
|
2024-02-20 16:05:54 +08:00
|
|
|
|
import com.jsowell.common.constant.Constants;
|
2023-12-26 14:41:25 +08:00
|
|
|
|
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
2023-03-04 16:29:55 +08:00
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
|
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
|
public class YKCUtils {
|
|
|
|
|
|
|
|
|
|
|
|
private static final BigDecimal multiple = new BigDecimal("100000");
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 校验云快充请求数据格式
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param msg 请求报文
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static boolean checkMsg(byte[] msg) {
|
2024-07-31 15:27:59 +08:00
|
|
|
|
// log.info("checkMsg:{}", BytesUtil.binary(msg, 16));
|
2023-03-04 16:29:55 +08:00
|
|
|
|
// 起始标志
|
|
|
|
|
|
byte[] head = BytesUtil.copyBytes(msg, 0, 1);
|
|
|
|
|
|
// 数据长度
|
|
|
|
|
|
byte[] length = BytesUtil.copyBytes(msg, 1, 1);
|
|
|
|
|
|
// 序列号域
|
|
|
|
|
|
byte[] serialNumber = BytesUtil.copyBytes(msg, 2, 2);
|
|
|
|
|
|
// 加密标志
|
|
|
|
|
|
byte[] encryptFlag = BytesUtil.copyBytes(msg, 4, 1);
|
|
|
|
|
|
// 帧类型标志
|
|
|
|
|
|
byte[] frameType = BytesUtil.copyBytes(msg, 5, 1);
|
|
|
|
|
|
// 消息体
|
|
|
|
|
|
byte[] msgBody = BytesUtil.copyBytes(msg, 6, msg.length - 8);
|
|
|
|
|
|
// 帧校验域
|
|
|
|
|
|
byte[] crcByte = new byte[]{msg[msg.length - 2], msg[msg.length - 1]};
|
|
|
|
|
|
|
|
|
|
|
|
//起始位必须是0x68
|
|
|
|
|
|
if (0x68 != head[0]) {
|
|
|
|
|
|
log.error("起始位必须是0x68");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 序列号域+加密标志+帧类型标志+消息体
|
|
|
|
|
|
byte[] data = Bytes.concat(serialNumber, encryptFlag, frameType, msgBody);
|
|
|
|
|
|
// 校验长度
|
|
|
|
|
|
if (data.length != BytesUtil.bytesToIntLittle(length)) {
|
2024-07-31 15:12:05 +08:00
|
|
|
|
log.error("数据长度不正确, 数据长度:{}, 实际长度:{}", BytesUtil.bytesToIntLittle(length), data.length);
|
2023-03-04 16:29:55 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
// CRC校验 source target
|
|
|
|
|
|
String sourceCRC = String.format("%04x", BytesUtil.bytesToInt(crcByte, 0));
|
|
|
|
|
|
String targetCRC = String.format("%04x", CRC16Util.calcCrc16(data));
|
|
|
|
|
|
String oldTargetCRC = String.format("%04x", CRC16Util.calcCrc16Old(data));
|
2023-06-29 09:16:50 +08:00
|
|
|
|
// 将高低位位置反转,得出新的crc
|
|
|
|
|
|
String lowString = StringUtils.substring(targetCRC, 0, 2);
|
|
|
|
|
|
String highString = StringUtils.substring(targetCRC, 2, 4);
|
|
|
|
|
|
String crc = highString + lowString;
|
|
|
|
|
|
// 若目标crc和高低位反转前/后的crc都不一致,则校验不通过
|
|
|
|
|
|
if (sourceCRC.equalsIgnoreCase(targetCRC) || sourceCRC.equalsIgnoreCase(crc)) {
|
|
|
|
|
|
return true;
|
2023-03-04 16:29:55 +08:00
|
|
|
|
}
|
2023-12-26 14:41:25 +08:00
|
|
|
|
log.error("CRC校验不通过, 源crc:{}, 计算得出crc:{}, 老crc计算:{}, 高低位反转后crc:{}, 帧类型:{}, 帧名称:{},报文:{}, msg:{}"
|
|
|
|
|
|
, sourceCRC, targetCRC, oldTargetCRC, crc, YKCUtils.frameType2Str(frameType),
|
|
|
|
|
|
YKCFrameTypeCode.getFrameTypeStr(YKCUtils.frameType2Str(frameType)),
|
|
|
|
|
|
BytesUtil.binary(msg, 16), Arrays.toString(msg));
|
2023-06-29 09:16:50 +08:00
|
|
|
|
return false;
|
2023-03-04 16:29:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-07-31 15:27:59 +08:00
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
|
byte[] length = new byte[]{0x22};
|
|
|
|
|
|
System.out.println(BytesUtil.bytesToIntLittle(length));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-03-04 16:29:55 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 转换电压电流以及起始soc
|
|
|
|
|
|
* 精确到小数点后一位;待机置零
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param bytes
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static String convertVoltageCurrent(byte[] bytes) {
|
|
|
|
|
|
// 转换为int 最后一位是小数位
|
|
|
|
|
|
int i = BytesUtil.bytesToIntLittle(bytes);
|
|
|
|
|
|
// 使用BigDecimal
|
|
|
|
|
|
BigDecimal bigDecimal = new BigDecimal(i);
|
|
|
|
|
|
BigDecimal divide = bigDecimal.divide(new BigDecimal(10), 1, BigDecimal.ROUND_UP);
|
|
|
|
|
|
return divide.toString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 转换小数点
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param bytes
|
|
|
|
|
|
* @param scale 小数位
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static String convertDecimalPoint(byte[] bytes, int scale) {
|
|
|
|
|
|
// 转换为int
|
|
|
|
|
|
int i = BytesUtil.bytesToIntLittle(bytes);
|
|
|
|
|
|
// 使用BigDecimal
|
|
|
|
|
|
BigDecimal bigDecimal = new BigDecimal(i);
|
|
|
|
|
|
BigDecimal divide = bigDecimal.divide(BigDecimal.valueOf(Math.pow(10d, scale)), scale, BigDecimal.ROUND_UP);
|
|
|
|
|
|
return divide.toString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取价格byte数组
|
|
|
|
|
|
* 价格转换为byte数组
|
|
|
|
|
|
* @param price 实际价格,单位元
|
|
|
|
|
|
* @param scale 保留小数位
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static byte[] getPriceByte(String price, int scale) {
|
|
|
|
|
|
// 保留小数位 例:保留5位小数,需要乘以10的5次方
|
|
|
|
|
|
BigDecimal value = BigDecimal.valueOf(Math.pow(10d, scale));
|
|
|
|
|
|
int i = new BigDecimal(price).multiply(value).intValue();
|
|
|
|
|
|
return BytesUtil.intToBytesLittle(i, 4);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2024-02-20 16:05:54 +08:00
|
|
|
|
* byte转帧类型字符串 如:"01"--> "0x01"
|
2023-03-04 16:29:55 +08:00
|
|
|
|
* @param bytes
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static String frameType2Str(byte[] bytes) {
|
|
|
|
|
|
String s = BytesUtil.bin2HexStr(bytes);
|
2024-02-20 16:05:54 +08:00
|
|
|
|
return Constants.HEX_PREFIX + s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static byte[] frameTypeStr2Bytes(String frameTypeStr) {
|
|
|
|
|
|
byte[] bytes = BytesUtil.hexString2Bytes(frameTypeStr);
|
|
|
|
|
|
return bytes;
|
2023-03-04 16:29:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-03-27 11:49:32 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 转换温度
|
|
|
|
|
|
* BIN 码 1 整形,偏移量-50;待机置零
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static String transitionTemperature(byte[] bytes) {
|
|
|
|
|
|
String s = BytesUtil.binary(bytes, 10);
|
|
|
|
|
|
int i = Integer.parseInt(s);
|
2023-03-27 17:03:28 +08:00
|
|
|
|
if (i > 0) {
|
|
|
|
|
|
return String.valueOf(i - 50);
|
|
|
|
|
|
}
|
|
|
|
|
|
return "一";
|
2023-03-04 16:29:55 +08:00
|
|
|
|
}
|
2024-08-02 15:45:15 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 解析vin
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static String parseVin(byte[] vinCodeByteArr) {
|
|
|
|
|
|
return BytesUtil.ascii2Str(vinCodeByteArr);
|
|
|
|
|
|
}
|
2023-03-04 16:29:55 +08:00
|
|
|
|
}
|