mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-07-02 21:18:05 +08:00
Merge branch 'dev' of http://192.168.2.46:8099/jsowell/jsowell-charger-web into dev
This commit is contained in:
@@ -185,7 +185,7 @@ public class OrderBasicInfoController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试订单退款接口
|
||||
* 重试订单退款接口/重试退款按钮
|
||||
* http://localhost:8080/order/retryRefundOrder
|
||||
*/
|
||||
@PostMapping("/retryRefundOrder")
|
||||
|
||||
@@ -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]};
|
||||
@@ -35,4 +48,29 @@ public class YouDianUtils {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package com.jsowell.common.core.domain.ebike;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 友电电单车协议指令
|
||||
*/
|
||||
@Slf4j
|
||||
public enum EBikeCommandEnum {
|
||||
// HEARTBEAT("01", "设备心跳包"),
|
||||
REGISTRATION("20", "设备注册包"),
|
||||
GET_SERVER_TIME("22", "设备获取服务器时间"),
|
||||
HEARTBEAT_2("21", "设备心跳包"),
|
||||
CARD_OPERATION("02", "刷卡操作"),
|
||||
SETTLEMENT_UPLOAD("03", "结算消费信息上传"),
|
||||
CHARGE_PORT_CONFIRMATION("04", "充电端口订单确认"),
|
||||
POWER_HEARTBEAT("06", "端口充电时功率心跳包"),
|
||||
CHARGER_HEARTBEAT("41", "充电柜专有心跳包"),
|
||||
ALARM_PUSH("42", "报警推送指令"),
|
||||
CHARGE_COMPLETE("43", "充电完成通知,但不结算"),
|
||||
PORT_PUSH("44", "端口推送指令");
|
||||
|
||||
;
|
||||
|
||||
EBikeCommandEnum(String code, String desc) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
private final String code; // 帧类型code
|
||||
private final String desc; // 帧类型名称
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.jsowell.common.core.domain.ebike;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 云快充数据模板
|
||||
*/
|
||||
@Data
|
||||
public class EBikeDataProtocol {
|
||||
/**
|
||||
* 包头 3字节
|
||||
*/
|
||||
private byte[] head;
|
||||
|
||||
/**
|
||||
* 数据长度 2字节
|
||||
*/
|
||||
private byte[] length;
|
||||
|
||||
/**
|
||||
* 物理id 4字节
|
||||
*/
|
||||
private byte[] physicalId;
|
||||
|
||||
/**
|
||||
* 消息id 2字节
|
||||
*/
|
||||
private byte[] messageId;
|
||||
|
||||
/**
|
||||
* 命令 1字节
|
||||
*/
|
||||
private byte[] command;
|
||||
|
||||
/**
|
||||
* 消息体 N字节
|
||||
*/
|
||||
private byte[] msgBody;
|
||||
|
||||
/**
|
||||
* 帧校验域 2字节
|
||||
*/
|
||||
private byte[] checksum;
|
||||
|
||||
public EBikeDataProtocol(byte[] msg) {
|
||||
// 起始标志
|
||||
this.head = BytesUtil.copyBytes(msg, 0, 3);
|
||||
// 数据长度
|
||||
this.length = BytesUtil.copyBytes(msg, 3, 2);
|
||||
// 物理id
|
||||
this.physicalId = BytesUtil.copyBytes(msg, 5, 4);
|
||||
// 消息id
|
||||
this.messageId = BytesUtil.copyBytes(msg, 9, 2);
|
||||
// 命令
|
||||
this.command = BytesUtil.copyBytes(msg, 11, 1);
|
||||
// 消息体
|
||||
this.msgBody = BytesUtil.copyBytes(msg, 12, msg.length - 14);
|
||||
// 帧校验域
|
||||
this.checksum = new byte[]{msg[msg.length - 2], msg[msg.length - 1]};
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为十六进制字符串
|
||||
*
|
||||
* @return 报文
|
||||
*/
|
||||
public String getHEXString() {
|
||||
return BytesUtil.binary(this.getBytes(), 16);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return Bytes.concat(this.head, this.length, this.physicalId, this.messageId, this.command, this.msgBody, this.checksum);
|
||||
}
|
||||
}
|
||||
@@ -27,24 +27,7 @@ public class SyncPromise {
|
||||
this.rpcResult = rpcResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步等待返回结果
|
||||
*/
|
||||
// public RpcResponse get(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
// // 等待阻塞,超时时间内countDownLatch减到0,将提前唤醒,以此作为是否超时判断
|
||||
// boolean earlyWakeUp = countDownLatch.await(timeout, unit);
|
||||
//
|
||||
// if(earlyWakeUp) {
|
||||
// // 超时时间内countDownLatch减到0,提前唤醒,说明已有结果
|
||||
// return rpcResponse;
|
||||
// } else {
|
||||
// // 超时时间内countDownLatch没有减到0,自动唤醒,说明超时时间内没有等到结果
|
||||
// isTimeout = true;
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
public byte[] get2(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
public byte[] get(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
// 等待阻塞,超时时间内countDownLatch减到0,将提前唤醒,以此作为是否超时判断
|
||||
boolean earlyWakeUp = countDownLatch.await(timeout, unit);
|
||||
|
||||
@@ -61,12 +44,5 @@ public class SyncPromise {
|
||||
public void wake() {
|
||||
countDownLatch.countDown();
|
||||
}
|
||||
|
||||
// public RpcResponse getRpcResponse() {
|
||||
// return rpcResponse;
|
||||
// }
|
||||
// public void setRpcResponse(RpcResponse rpcResponse) {
|
||||
// this.rpcResponse = rpcResponse;
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package com.jsowell.netty.decoder;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CustomDecoder extends ByteToMessageDecoder {
|
||||
private static final byte START_FLAG = (byte) 0x68;
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||
// 检查可读数据长度是否大于等于起始标志符和数据长度字段的长度
|
||||
if (in.readableBytes() >= 3) {
|
||||
// 标记当前读取位置
|
||||
in.markReaderIndex();
|
||||
// 读取起始标志符
|
||||
byte startFlag = in.readByte();
|
||||
// 检查起始标志符是否正确
|
||||
if (startFlag != START_FLAG) {
|
||||
// 如果不正确,重置读取位置,并抛出异常
|
||||
in.resetReaderIndex();
|
||||
throw new CorruptedFrameException("Invalid start flag: " + startFlag);
|
||||
}
|
||||
// 读取数据长度
|
||||
byte length = in.readByte();
|
||||
// 检查可读数据长度是否大于等于数据长度字段的值
|
||||
if (in.readableBytes() >= length) {
|
||||
// 读取完整数据包
|
||||
ByteBuf frame = in.readBytes(length + 2); // 包括校验位
|
||||
out.add(frame);
|
||||
} else {
|
||||
// 如果可读数据长度不够,重置读取位置,并等待下一次读取
|
||||
in.resetReaderIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.jsowell.netty.handler.electricbicycles;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.jsowell.common.YouDianUtils;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.core.redis.RedisCache;
|
||||
import com.jsowell.common.enums.ykc.PileChannelEntity;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.CRC16Util;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@@ -25,41 +25,31 @@ public abstract class AbstractEBikeHandler implements InitializingBean {
|
||||
* 执行逻辑
|
||||
* 有应答
|
||||
*/
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext ctx) {
|
||||
public byte[] supplyProcess(EBikeDataProtocol dataProtocol, ChannelHandlerContext ctx) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行逻辑
|
||||
* 不需要应答
|
||||
*/
|
||||
// public void pushProcess() {
|
||||
// throw new UnsupportedOperationException();
|
||||
// }
|
||||
|
||||
/**
|
||||
* 组装应答的结果
|
||||
* @param ykcDataProtocol 请求数据
|
||||
* @param dataProtocol 请求数据
|
||||
* @param messageBody 消息体
|
||||
* @return 应答结果
|
||||
*/
|
||||
protected byte[] getResult(YKCDataProtocol ykcDataProtocol, byte[] messageBody) {
|
||||
protected byte[] getResult(EBikeDataProtocol dataProtocol, byte[] messageBody) {
|
||||
// 起始标志
|
||||
byte[] head = ykcDataProtocol.getHead();
|
||||
// 序列号域
|
||||
byte[] serialNumber = ykcDataProtocol.getSerialNumber();
|
||||
byte[] head = dataProtocol.getHead();
|
||||
// 长度 = 物理ID(4) + 消息ID(2) + 命令(1) + 数据(n) + 校验(2),每包最多256字节
|
||||
byte[] length = BytesUtil.intToBytesLittle(9 + messageBody.length);
|
||||
// 物理id
|
||||
byte[] physicalId = dataProtocol.getPhysicalId();
|
||||
// 加密标志
|
||||
byte[] encryptFlag = ykcDataProtocol.getEncryptFlag();
|
||||
byte[] messageId = dataProtocol.getMessageId();
|
||||
// 请求帧类型
|
||||
byte[] requestFrameType = ykcDataProtocol.getFrameType();
|
||||
// 应答帧类型
|
||||
byte[] responseFrameType = YKCFrameTypeCode.PlatformAnswersRelation.getResponseFrameTypeBytes(requestFrameType);
|
||||
|
||||
// 数据域 值为“序列号域+加密标志+帧类型标志+消息体”字节数之和
|
||||
byte[] dataFields = Bytes.concat(serialNumber, encryptFlag, responseFrameType, messageBody);
|
||||
// 计算crc: 从序列号域到数据域的 CRC 校验
|
||||
int crc16 = CRC16Util.calcCrc16(dataFields);
|
||||
return Bytes.concat(head, BytesUtil.intToBytes(dataFields.length, 1), dataFields, BytesUtil.intToBytes(crc16));
|
||||
byte[] command = dataProtocol.getCommand();
|
||||
// 整个数据包中的每个字节(不包括校验字段本身)
|
||||
byte[] dataFields = Bytes.concat(head, length, physicalId, messageId, command, messageBody);
|
||||
byte[] checkFieldBytes = YouDianUtils.getCheckFieldBytes(dataFields);
|
||||
return Bytes.concat(dataFields, checkFieldBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package com.jsowell.netty.handler.electricbicycles;
|
||||
|
||||
import com.jsowell.common.core.domain.ebike.EBikeCommandEnum;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.netty.factory.EBikeOperateFactory;
|
||||
import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
|
||||
import com.jsowell.pile.domain.ebike.EBikeCommandEnum;
|
||||
import com.jsowell.pile.domain.ebike.deviceupload.EBikeMessageCmd22;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 设备心跳包
|
||||
* 设备获取服务器时间
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@@ -17,4 +23,23 @@ public class GetServerTimeHandler extends AbstractEBikeHandler {
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
EBikeOperateFactory.register(type, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行逻辑
|
||||
* 有应答
|
||||
*
|
||||
* @param dataProtocol
|
||||
* @param ctx
|
||||
*/
|
||||
@Override
|
||||
public byte[] supplyProcess(EBikeDataProtocol dataProtocol, ChannelHandlerContext ctx) {
|
||||
// 解析字节数组
|
||||
EBikeMessageCmd22 message = (EBikeMessageCmd22) AbsEBikeMessage.parseMessage(dataProtocol.getBytes());
|
||||
log.info("设备获取服务器时间:{}", JSON.toJSONString(message));
|
||||
|
||||
// 获取当前服务器10位时间戳
|
||||
byte[] timeBytes = BytesUtil.getIntBytes((int) (System.currentTimeMillis() / 1000));
|
||||
// System.out.println("data: " + BytesUtil.bytesToIntLittle(timeBytes));
|
||||
return getResult(dataProtocol, timeBytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
package com.jsowell.netty.handler.electricbicycles;
|
||||
|
||||
import com.jsowell.common.core.domain.ebike.EBikeCommandEnum;
|
||||
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;
|
||||
import com.jsowell.pile.domain.ebike.EBikeCommandEnum;
|
||||
import com.jsowell.pile.domain.ebike.deviceupload.EBikeMessageCmd21;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -17,4 +23,20 @@ public class HeartbeatHandler extends AbstractEBikeHandler {
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
EBikeOperateFactory.register(type, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行逻辑
|
||||
* 有应答
|
||||
*
|
||||
* @param dataProtocol
|
||||
* @param ctx
|
||||
*/
|
||||
@Override
|
||||
public byte[] supplyProcess(EBikeDataProtocol dataProtocol, ChannelHandlerContext ctx) {
|
||||
// 解析字节数组
|
||||
EBikeMessageCmd21 message = (EBikeMessageCmd21) AbsEBikeMessage.parseMessage(dataProtocol.getBytes());
|
||||
EBikeMessageCmd21.DeviceHeartbeat deviceHeartbeat = message.getDeviceHeartbeat();
|
||||
log.info("设备心跳包:{}", JSON.toJSONString(message));
|
||||
return getResult(dataProtocol, Constants.zeroByteArray);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package com.jsowell.netty.handler.electricbicycles;
|
||||
|
||||
import com.jsowell.common.core.domain.ebike.EBikeCommandEnum;
|
||||
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;
|
||||
import com.jsowell.pile.domain.ebike.EBikeCommandEnum;
|
||||
import com.jsowell.pile.domain.ebike.deviceupload.EBikeMessageCmd20;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 设备心跳包
|
||||
* 设备注册包
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@@ -17,4 +23,20 @@ public class RegistrationHandler extends AbstractEBikeHandler {
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
EBikeOperateFactory.register(type, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行逻辑
|
||||
* 有应答
|
||||
*
|
||||
* @param dataProtocol
|
||||
* @param ctx
|
||||
*/
|
||||
@Override
|
||||
public byte[] supplyProcess(EBikeDataProtocol dataProtocol, ChannelHandlerContext ctx) {
|
||||
// 解析字节数组
|
||||
EBikeMessageCmd20 message = (EBikeMessageCmd20) AbsEBikeMessage.parseMessage(dataProtocol.getBytes());
|
||||
EBikeMessageCmd20.DeviceRegister deviceRegister = message.getDeviceRegister();
|
||||
log.info("设备注册包:{}", JSON.toJSONString(message));
|
||||
return getResult(dataProtocol, Constants.zeroByteArray);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package com.jsowell.netty.handler.electricbicycles;
|
||||
|
||||
import com.jsowell.common.core.domain.ebike.EBikeCommandEnum;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
|
||||
import com.jsowell.netty.factory.EBikeOperateFactory;
|
||||
import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
|
||||
import com.jsowell.pile.domain.ebike.EBikeCommandEnum;
|
||||
import com.jsowell.pile.domain.ebike.deviceupload.EBikeMessageCmd03;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 设备心跳包
|
||||
* 结算消费信息上传
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@@ -17,4 +22,21 @@ public class SettlementUploadHandler extends AbstractEBikeHandler {
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
EBikeOperateFactory.register(type, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行逻辑
|
||||
* 有应答
|
||||
*
|
||||
* @param dataProtocol
|
||||
* @param ctx
|
||||
*/
|
||||
@Override
|
||||
public byte[] supplyProcess(EBikeDataProtocol dataProtocol, ChannelHandlerContext ctx) {
|
||||
// 解析字节数组
|
||||
EBikeMessageCmd03 message = (EBikeMessageCmd03) AbsEBikeMessage.parseMessage(dataProtocol.getBytes());
|
||||
EBikeMessageCmd03.SettlementInfo settlementInfo = message.getSettlementInfo();
|
||||
log.info("结算消费信息上传:{}", JSON.toJSONString(message));
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,6 +34,55 @@ public class ReservationChargingStartupResultHandler extends AbstractYkcHandler
|
||||
YKCOperateFactory.register(type, this);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 获取消息体
|
||||
String msg = "8823000000071801240823102300000088230000000718010190";
|
||||
byte[] msgBody = BytesUtil.str2Bcd(msg);
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
|
||||
// 交易流水号
|
||||
byte[] transactionCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String transactionCode = BytesUtil.bcd2Str(transactionCodeByteArr);
|
||||
|
||||
// 桩编码
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] connectorCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String connectorCode = BytesUtil.bcd2Str(connectorCodeByteArr);
|
||||
|
||||
// vin
|
||||
startIndex += length;
|
||||
length = 17;
|
||||
byte[] vinCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String vinCode = YKCUtils.parseVin(vinCodeByteArr); // 解析vin
|
||||
|
||||
// 启动结果 0x00失败 0x01成功
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] startupResultByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String startupResult = BytesUtil.bcd2Str(startupResultByteArr);
|
||||
String startupResultMsg = StringUtils.equals(startupResult, "00") ? "失败" : "成功";
|
||||
|
||||
// 失败原因
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] failReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String failReason = BytesUtil.bcd2Str(failReasonByteArr);
|
||||
String failReasonMsg = ChargingFailedReasonEnum.getMsgByCode(Integer.parseInt(failReason, 16));
|
||||
|
||||
log.info("[===预约充电启动结果上送===]交易流水号:{}, 桩编号:{}, 枪号:{}, vin:{}, 启动结果:{}, 失败原因:{}",
|
||||
transactionCode, pileSn, connectorCode, vinCode, startupResult + "-" +startupResultMsg, failReasonMsg);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
log.info("[===预约充电启动结果上送===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
@@ -95,7 +144,7 @@ public class ReservationChargingStartupResultHandler extends AbstractYkcHandler
|
||||
.pileSn(pileSn)
|
||||
.connectorCode(connectorCode)
|
||||
.vinCode(vinCode)
|
||||
.startupResult(startupResultMsg)
|
||||
.startupResult(startupResult)
|
||||
.failReason(failReasonMsg)
|
||||
.build();
|
||||
pileBasicInfoService.startupResult(chargingStartupResult);
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.springframework.stereotype.Component;
|
||||
* 对时设置
|
||||
*
|
||||
* 运营平台同步充电桩时钟,以保证充电桩与运营平台的时钟一致
|
||||
*
|
||||
* @deprecated
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 15:11
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package com.jsowell.netty.server.electricbicycles;
|
||||
|
||||
import com.jsowell.netty.decoder.MessageDecode;
|
||||
import com.jsowell.netty.decoder.MessageEncode;
|
||||
import com.jsowell.netty.decoder.StartAndLengthFieldFrameDecoder;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.bytes.ByteArrayDecoder;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -22,8 +21,10 @@ public class ElectricBicyclesServerChannelInitializer extends ChannelInitializer
|
||||
protected void initChannel(SocketChannel channel) throws Exception {
|
||||
ChannelPipeline pipeline = channel.pipeline();
|
||||
pipeline.addLast("frameDecoder", new StartAndLengthFieldFrameDecoder());
|
||||
pipeline.addLast("decoder", new MessageDecode());
|
||||
pipeline.addLast("encoder", new MessageEncode());
|
||||
// pipeline.addLast("decoder", new MessageDecode());
|
||||
// pipeline.addLast("encoder", new MessageEncode());
|
||||
pipeline.addLast("decoder", new ByteArrayDecoder());
|
||||
pipeline.addLast("encoder", new ByteArrayDecoder());
|
||||
//读超时时间设置为10s,0表示不监控
|
||||
pipeline.addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS));
|
||||
pipeline.addLast("handler", chargingPileHandler);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.jsowell.netty.server.electricbicycles;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.netty.service.electricbicycles.EBikeBusinessService;
|
||||
import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -11,7 +13,9 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -19,16 +23,39 @@ import java.util.concurrent.TimeUnit;
|
||||
@ChannelHandler.Sharable
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ElectricBicyclesServerHandler extends SimpleChannelInboundHandler<AbsEBikeMessage> {
|
||||
public class ElectricBicyclesServerHandler extends SimpleChannelInboundHandler<Object> {
|
||||
|
||||
private final Map<String, ResponseFuture> responseFutureMap = new ConcurrentHashMap<>();
|
||||
|
||||
private final List<String> notPrintFrameTypeList = Lists.newArrayList("0x03");
|
||||
|
||||
@Autowired
|
||||
private EBikeBusinessService eBikeService;
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, AbsEBikeMessage msg) throws Exception {
|
||||
log.info("收到消息, channelId:{}, msg:{}", ctx.channel().id().toString(), JSON.toJSONString(msg));
|
||||
protected void channelRead0(ChannelHandlerContext ctx, Object message) throws Exception {
|
||||
Channel channel = ctx.channel();
|
||||
log.info("收到消息, channelId:{}, msg:{}", channel.id().toString(), JSON.toJSONString(message));
|
||||
byte[] msg = (byte[]) message;
|
||||
|
||||
// 处理数据
|
||||
byte[] response = eBikeService.process(msg, ctx);
|
||||
if (Objects.nonNull(response)) {
|
||||
log.info("响应数据:{}", BytesUtil.binary(response, 16));
|
||||
// 响应客户端
|
||||
ByteBuf buffer = ctx.alloc().buffer().writeBytes(response);
|
||||
// this.channelWrite(channel.id(), buffer);
|
||||
ctx.writeAndFlush(buffer);
|
||||
// if (!CollectionUtils.containsAny(notPrintFrameTypeList, frameType)) {
|
||||
// // 应答帧类型
|
||||
// byte[] responseFrameTypeBytes = YKCFrameTypeCode.PlatformAnswersRelation.getResponseFrameTypeBytes(frameTypeBytes);
|
||||
// String responseFrameType = YKCUtils.frameType2Str(responseFrameTypeBytes);
|
||||
// log.info("【>>>>>平台响应消息>>>>>】channel:{}, 响应帧类型:{}, 响应帧名称:{}, 原帧类型:{}, 原帧名称:{}, 序列号域:{}, response:{}",
|
||||
// channel.id(), responseFrameType, YKCFrameTypeCode.getFrameTypeStr(responseFrameType),
|
||||
// frameType, YKCFrameTypeCode.getFrameTypeStr(frameType), serialNumber,
|
||||
// BytesUtil.binary(response, 16));
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
public String sendCommandAndWaitForResponse(Channel channel, String command, long timeout) throws InterruptedException {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package com.jsowell.netty.service.electricbicycles.impl;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.enums.ykc.PileChannelEntity;
|
||||
import com.jsowell.common.enums.ykc.PileConnectorDataBaseStatusEnum;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.netty.factory.YKCOperateFactory;
|
||||
import com.jsowell.netty.handler.yunkuaichong.AbstractYkcHandler;
|
||||
import com.jsowell.netty.factory.EBikeOperateFactory;
|
||||
import com.jsowell.netty.handler.electricbicycles.AbstractEBikeHandler;
|
||||
import com.jsowell.netty.service.electricbicycles.EBikeBusinessService;
|
||||
import com.jsowell.pile.service.OrderBasicInfoService;
|
||||
import com.jsowell.pile.service.PileConnectorInfoService;
|
||||
@@ -33,16 +33,15 @@ public class EBikeBusinessServiceImpl implements EBikeBusinessService {
|
||||
|
||||
@Override
|
||||
public byte[] process(byte[] msg, ChannelHandlerContext ctx) {
|
||||
if (!YKCUtils.checkMsg(msg)) {
|
||||
// 校验不通过,丢弃消息
|
||||
return null;
|
||||
}
|
||||
YKCDataProtocol ykcDataProtocol = new YKCDataProtocol(msg);
|
||||
EBikeDataProtocol ykcDataProtocol = new EBikeDataProtocol(msg);
|
||||
// 获取帧类型
|
||||
String frameType = YKCUtils.frameType2Str(ykcDataProtocol.getFrameType());
|
||||
String command = BytesUtil.bin2HexStr(ykcDataProtocol.getCommand());
|
||||
// 获取业务处理handler
|
||||
AbstractYkcHandler invokeStrategy = YKCOperateFactory.getInvokeStrategy(frameType);
|
||||
return invokeStrategy.supplyProcess(ykcDataProtocol, ctx);
|
||||
AbstractEBikeHandler invokeStrategy = EBikeOperateFactory.getInvokeStrategy(command);
|
||||
if (invokeStrategy != null) {
|
||||
return invokeStrategy.supplyProcess(ykcDataProtocol, ctx);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -101,4 +101,43 @@ public abstract class AbsEBikeMessage {
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String s = "44 4e 59 00 0d 3b 37 ab 04 b9 00 22 54 33 cc 66 03 7b".replace(" ", "");
|
||||
byte[] messageBytes = BytesUtil.hexStringToByteArray(s);
|
||||
// 读取包头
|
||||
byte[] headerBytes = Arrays.copyOfRange(messageBytes, 0, 3);
|
||||
String header = new String(headerBytes, StandardCharsets.UTF_8);
|
||||
System.out.println("header: " + header);
|
||||
|
||||
// 读取长度
|
||||
byte[] lengthBytes = Arrays.copyOfRange(messageBytes, 3, 5);
|
||||
int length = BytesUtil.bytesToIntLittle(lengthBytes);
|
||||
System.out.println("length: " + length);
|
||||
|
||||
// 读取物理ID
|
||||
byte[] physicalIdBytes = Arrays.copyOfRange(messageBytes, 5, 9);
|
||||
int physicalId = BytesUtil.bytesToIntLittle(physicalIdBytes);
|
||||
System.out.println("physicalId: " + physicalId);
|
||||
|
||||
// 读取消息ID
|
||||
byte[] messageIdBytes = Arrays.copyOfRange(messageBytes, 9, 11);
|
||||
int messageId = BytesUtil.bytesToIntLittle(messageIdBytes);
|
||||
System.out.println("messageId: " + messageId);
|
||||
|
||||
// 读取命令
|
||||
byte commandByte = messageBytes[11];
|
||||
String command = BytesUtil.bcd2StrLittle(new byte[]{commandByte});
|
||||
System.out.println("command: " + command);
|
||||
|
||||
// 读取数据
|
||||
byte[] dataBytes = Arrays.copyOfRange(messageBytes, 12, messageBytes.length - 2);
|
||||
String data = BytesUtil.bytesToIntLittle(dataBytes) + "";
|
||||
System.out.println("data: " + data);
|
||||
|
||||
// 读取校验
|
||||
byte[] checksumBytes = Arrays.copyOfRange(messageBytes, messageBytes.length - 2, messageBytes.length);
|
||||
int checksum = BytesUtil.bytesToIntLittle(checksumBytes);
|
||||
System.out.println("checksum: " + checksum);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.jsowell.pile.domain.ebike;
|
||||
|
||||
import com.jsowell.pile.domain.ebike.deviceupload.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 友电电单车协议指令
|
||||
*/
|
||||
@Slf4j
|
||||
public enum EBikeCommandEnum {
|
||||
// HEARTBEAT("01", "设备心跳包"),
|
||||
REGISTRATION("20", "设备注册包", EBikeMessageCmd20.class),
|
||||
GET_SERVER_TIME("22", "设备获取服务器时间", EBikeMessageCmd22.class),
|
||||
HEARTBEAT_2("21", "设备心跳包", EBikeMessageCmd21.class),
|
||||
CARD_OPERATION("02", "刷卡操作", EBikeMessageCmd02.class),
|
||||
SETTLEMENT_UPLOAD("03", "结算消费信息上传", EBikeMessageCmd03.class),
|
||||
CHARGE_PORT_CONFIRMATION("04", "充电端口订单确认", EBikeMessageCmd04.class),
|
||||
POWER_HEARTBEAT("06", "端口充电时功率心跳包", EBikeMessageCmd06.class),
|
||||
// CHARGER_HEARTBEAT("41", "充电柜专有心跳包", EBikeMessageCmd41.class),
|
||||
// ALARM_PUSH("42", "报警推送指令", EBikeMessageCmd20.class),
|
||||
// CHARGE_COMPLETE("43", "充电完成通知,但不结算", EBikeMessageCmd20.class),
|
||||
// PORT_PUSH("44", "端口推送指令", EBikeMessageCmd20.class);
|
||||
|
||||
;
|
||||
|
||||
EBikeCommandEnum(String code, String desc, Class<? extends AbsEBikeMessage> msgClass) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
this.msgClass = msgClass;
|
||||
}
|
||||
|
||||
private final String code; // 帧类型code
|
||||
private final String desc; // 帧类型名称
|
||||
private final Class<? extends AbsEBikeMessage> msgClass;
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public Class<? extends AbsEBikeMessage> getMsgClass() {
|
||||
return msgClass;
|
||||
}
|
||||
|
||||
public static Class<? extends AbsEBikeMessage> getMsgClassByCode(String code) {
|
||||
for (EBikeCommandEnum e : EBikeCommandEnum.values()) {
|
||||
if (e.getCode().equals(code)) {
|
||||
return e.getMsgClass();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -60,10 +60,10 @@ public class EBikeMessageCmd20 extends AbsEBikeMessage {
|
||||
private String powerBoardVersion;
|
||||
|
||||
public DeviceRegister(byte[] dataBytes) {
|
||||
this.firmwareVersion = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 0, 2)) + "";
|
||||
this.firmwareVersion = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 0, 2)) * 0.01 + "";
|
||||
this.portNumber = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 2, 3)) + "";
|
||||
this.virtualId = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 3, 4)) + "";
|
||||
this.deviceType = BytesUtil.bcd2StrLittle(Arrays.copyOfRange(dataBytes, 4, 5));
|
||||
this.deviceType = BytesUtil.printHexBinary(Arrays.copyOfRange(dataBytes, 4, 5));
|
||||
this.workMode = BytesUtil.bcd2StrLittle(Arrays.copyOfRange(dataBytes, 5, 6));
|
||||
this.powerBoardVersion = BytesUtil.bcd2StrLittle(Arrays.copyOfRange(dataBytes, 6, 8));
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ public class EBikeMessageCmd21 extends AbsEBikeMessage {
|
||||
public static class DeviceHeartbeat {
|
||||
/**
|
||||
* 电压:设备的当前电压(打包发送心跳包指令时的当前时间点的实时电压)
|
||||
* 2024年8月26日15点02分 已经转为标准单位V
|
||||
*/
|
||||
private String voltage;
|
||||
|
||||
@@ -68,10 +69,10 @@ public class EBikeMessageCmd21 extends AbsEBikeMessage {
|
||||
|
||||
|
||||
public DeviceHeartbeat(byte[] dataBytes) {
|
||||
this.voltage = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 0, 2)) + "";
|
||||
this.voltage = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 0, 2)) * 0.1 + "";
|
||||
this.portNumber = BytesUtil.bytesToIntLittle(Arrays.copyOfRange(dataBytes, 2, 3));
|
||||
|
||||
byte[] statusBytes = Arrays.copyOfRange(dataBytes, 3, this.portNumber);
|
||||
byte[] statusBytes = BytesUtil.copyBytes(dataBytes, 3, this.portNumber);
|
||||
List<String> statusList = Lists.newArrayList();
|
||||
for (byte statusByte : statusBytes) {
|
||||
int status = BytesUtil.bytesToIntLittle(new byte[]{statusByte});
|
||||
|
||||
@@ -2121,7 +2121,7 @@ public class OrderBasicInfoServiceImpl implements OrderBasicInfoService {
|
||||
} catch (BaseAdaPayException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
logger.info("根据订单号:{}, 查询到支付信息:{}", orderCode, JSON.toJSONString(paymentInfoSet));
|
||||
logger.info("根据订单号:{}, 查询到支付信息:{}, ", orderCode, JSON.toJSONString(paymentInfoSet));
|
||||
if (CollectionUtils.isNotEmpty(paymentInfoSet)) {
|
||||
for (PaymentInfo paymentInfo : paymentInfoSet) {
|
||||
String paymentId = paymentInfo.getPaymentId();
|
||||
@@ -3796,7 +3796,7 @@ public class OrderBasicInfoServiceImpl implements OrderBasicInfoService {
|
||||
String orderCode = generateNewOrderCode();
|
||||
String transactionCode = chargingStartupResult.getTransactionCode();
|
||||
|
||||
String status = StringUtils.equals(chargingStartupResult.getStartupResult(), "00")
|
||||
String status = StringUtils.equals(chargingStartupResult.getStartupResult(), "01")
|
||||
? OrderStatusEnum.IN_THE_CHARGING.getValue()
|
||||
: OrderStatusEnum.ORDER_CLOSE_TIMEOUT.getValue();
|
||||
|
||||
@@ -3826,11 +3826,17 @@ public class OrderBasicInfoServiceImpl implements OrderBasicInfoService {
|
||||
.reason(chargingStartupResult.getFailReason())
|
||||
.build();
|
||||
|
||||
OrderDetail orderDetail = OrderDetail.builder()
|
||||
.orderCode(orderCode)
|
||||
.delFlag(DelFlagEnum.NORMAL.getValue())
|
||||
.build();
|
||||
|
||||
// 保存到数据库
|
||||
OrderTransactionDTO createOrderTransactionDTO = OrderTransactionDTO.builder()
|
||||
.orderBasicInfo(orderBasicInfo)
|
||||
.orderDetail(null)
|
||||
.orderDetail(orderDetail)
|
||||
.build();
|
||||
logger.info("createReservationOrder, chargingStartupResult:{}, 订单信息:{}", JSON.toJSONString(chargingStartupResult), JSON.toJSONString(orderBasicInfo));
|
||||
pileTransactionService.doCreateOrder(createOrderTransactionDTO);
|
||||
|
||||
OrderPayRecord principalPayRecord = OrderPayRecord.builder()
|
||||
|
||||
@@ -412,6 +412,18 @@ public class PileReservationInfoServiceImpl implements PileReservationInfoServic
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 是否发指令 false:不发指令 true:发指令, 默认不发指令
|
||||
boolean sendFlag = false;
|
||||
if (StringUtils.equals(pileReservationInfo.getStatus(), "1")) {
|
||||
sendFlag = true;
|
||||
}
|
||||
|
||||
// 发指令结果 false:失败 true:成功, 默认失败
|
||||
boolean sendResult = false;
|
||||
|
||||
// 是否更新数据库 false:不更新 true:更新, 默认更新
|
||||
boolean updateFlag = true;
|
||||
|
||||
// 操作 0x01:启动 0x02:取消 0x03:修改
|
||||
String operation = "03";
|
||||
if (StringUtils.isNotBlank(dto.getStartTime())) {
|
||||
@@ -431,47 +443,55 @@ public class PileReservationInfoServiceImpl implements PileReservationInfoServic
|
||||
if (StringUtils.equals(dto.getStatus(), Constants.ZERO)) {
|
||||
// 停用
|
||||
operation = "02";
|
||||
sendFlag = true;
|
||||
} else if (StringUtils.equals(dto.getStatus(), Constants.ONE)) {
|
||||
// 启用
|
||||
operation = "01";
|
||||
sendFlag = true;
|
||||
}
|
||||
}
|
||||
pileReservationInfo.setUpdateBy(dto.getMemberId());
|
||||
|
||||
/*
|
||||
先发送指令, 收到回复更新数据库
|
||||
发送指令
|
||||
*/
|
||||
// 查询会员的绑定vin列表 2024年7月30日11点04分 以当前请求会员的VIN为准
|
||||
List<MemberPlateNumberVO> plateNumberVOList = memberPlateNumberRelationService.selectMemberPlateNumberRelation(dto.getMemberId());
|
||||
List<String> vinCodes = Lists.newArrayList();
|
||||
int count = 0;
|
||||
for (MemberPlateNumberVO vo : plateNumberVOList) {
|
||||
if (count < 3 && StringUtils.isNotBlank(vo.getVinCode())) {
|
||||
vinCodes.add(vo.getVinCode());
|
||||
count++;
|
||||
if (sendFlag) {
|
||||
// 查询会员的绑定vin列表 2024年7月30日11点04分 以当前请求会员的VIN为准
|
||||
List<MemberPlateNumberVO> plateNumberVOList = memberPlateNumberRelationService.selectMemberPlateNumberRelation(dto.getMemberId());
|
||||
List<String> vinCodes = Lists.newArrayList();
|
||||
int count = 0;
|
||||
for (MemberPlateNumberVO vo : plateNumberVOList) {
|
||||
if (count < 3 && StringUtils.isNotBlank(vo.getVinCode())) {
|
||||
vinCodes.add(vo.getVinCode());
|
||||
count++;
|
||||
}
|
||||
}
|
||||
// 如果 vinCodes 的数量少于 3,用 "0" 补足
|
||||
while (vinCodes.size() < 3) {
|
||||
vinCodes.add("");
|
||||
}
|
||||
String type = StringUtils.equals(pileReservationInfo.getReservationType(), "single") ? "00" : "01";
|
||||
ReservationChargingCommand command = ReservationChargingCommand.builder()
|
||||
.transactionCode(Constants.ILLEGAL_TRANSACTION_CODE)
|
||||
.pileSn(pileReservationInfo.getPileSn())
|
||||
.connectorCode(pileReservationInfo.getPileConnectorCode().replace(pileReservationInfo.getPileSn(), ""))
|
||||
.operation(operation)
|
||||
.reservationType(type)
|
||||
.verifyIdentity(pileReservationInfo.getVerifyIdentity())
|
||||
.vin1(vinCodes.get(0))
|
||||
.vin2(vinCodes.get(1))
|
||||
.vin3(vinCodes.get(2))
|
||||
.reservedStartTime(pileReservationInfo.getStartTime().toLocalTime())
|
||||
.reservedEndTime(pileReservationInfo.getEndTime().toLocalTime())
|
||||
.amount(Constants.WHITELIST_DEFAULT_AMOUNT)
|
||||
.build();
|
||||
String result = pileRemoteService.reservationCharging(command);
|
||||
sendResult = StringUtils.equals(result, Constants.ONE);
|
||||
}
|
||||
// 如果 vinCodes 的数量少于 3,用 "0" 补足
|
||||
while (vinCodes.size() < 3) {
|
||||
vinCodes.add("");
|
||||
}
|
||||
String type = StringUtils.equals(pileReservationInfo.getReservationType(), "single") ? "00" : "01";
|
||||
ReservationChargingCommand command = ReservationChargingCommand.builder()
|
||||
.transactionCode(Constants.ILLEGAL_TRANSACTION_CODE)
|
||||
.pileSn(pileReservationInfo.getPileSn())
|
||||
.connectorCode(pileReservationInfo.getPileConnectorCode().replace(pileReservationInfo.getPileSn(), ""))
|
||||
.operation(operation)
|
||||
.reservationType(type)
|
||||
.verifyIdentity(pileReservationInfo.getVerifyIdentity())
|
||||
.vin1(vinCodes.get(0))
|
||||
.vin2(vinCodes.get(1))
|
||||
.vin3(vinCodes.get(2))
|
||||
.reservedStartTime(pileReservationInfo.getStartTime().toLocalTime())
|
||||
.reservedEndTime(pileReservationInfo.getEndTime().toLocalTime())
|
||||
.amount(Constants.WHITELIST_DEFAULT_AMOUNT)
|
||||
.build();
|
||||
String result = pileRemoteService.reservationCharging(command);
|
||||
if (StringUtils.equals(result, Constants.ONE)) {
|
||||
|
||||
log.info("修改预约充电信息, updateFlag:{}, sendFlag:{}, sendResult:{}", updateFlag, sendFlag, sendResult);
|
||||
// 更新数据库, 一般情况下是都更新的, 只有在发送指令并发送失败的情况下才不更新
|
||||
if (updateFlag && (sendFlag == sendResult)) {
|
||||
return this.insertOrUpdateSelective(pileReservationInfo);
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -207,7 +207,7 @@ public class YKCPushCommandServiceImpl implements YKCPushCommandService {
|
||||
});
|
||||
|
||||
// 等待获取结果
|
||||
byte[] rpcResponse = syncPromise.get2(timeout, unit);
|
||||
byte[] rpcResponse = syncPromise.get(timeout, unit);
|
||||
|
||||
if(rpcResponse == null) {
|
||||
if(syncPromise.isTimeout()) {
|
||||
|
||||
@@ -2549,6 +2549,7 @@
|
||||
AND t1.create_time <![CDATA[ <= ]]> #{endTime,jdbcType=VARCHAR}
|
||||
AND t1.order_status = '6'
|
||||
AND t1.del_flag = '0'
|
||||
order by t1.create_time desc
|
||||
</select>
|
||||
|
||||
<select id="getListByOrderCodes" resultType="com.jsowell.pile.vo.uniapp.customer.OrderVO">
|
||||
|
||||
@@ -285,7 +285,6 @@ export default {
|
||||
payDetail: [],
|
||||
// 订单退款列表
|
||||
orderRefundInfoList: [],
|
||||
// 重试退款按钮
|
||||
// retryRefundFlag: false,
|
||||
userInfo: [],
|
||||
obj: {},
|
||||
|
||||
Reference in New Issue
Block a user