mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-05-06 02:50:13 +08:00
update 电单车协议
This commit is contained in:
@@ -35,4 +35,26 @@ public class YouDianUtils {
|
||||
log.info("计算的校验值:{}, 接收到的校验值:{}", calculatedChecksum, receivedChecksum);
|
||||
return calculatedChecksum == receivedChecksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算校验字段
|
||||
*/
|
||||
public static int calculateCheckField(byte[] bytes) {
|
||||
// 计算累加和
|
||||
int sum = 0;
|
||||
for (int i = 0; i < bytes.length - 2; i++) {
|
||||
sum += (bytes[i] & 0xFF); // 将每个字节视为无符号值进行累加
|
||||
}
|
||||
|
||||
// 取累加和的低 2 字节(16 位)
|
||||
return sum & 0xFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取校验字段byte数组
|
||||
*/
|
||||
public static byte[] getCheckFieldBytes(byte[] bytes) {
|
||||
int calculatedChecksum = calculateCheckField(bytes);
|
||||
return BytesUtil.intToBytesLittle(calculatedChecksum);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,10 @@ public class EBikeDataProtocol {
|
||||
* @return 报文
|
||||
*/
|
||||
public String getHEXString() {
|
||||
byte[] bytes = Bytes.concat(this.head, this.length, this.physicalId, this.messageId, this.command, this.msgBody, this.checksum);
|
||||
return BytesUtil.binary(bytes, 16);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +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;
|
||||
@@ -39,20 +38,22 @@ public abstract class AbstractEBikeHandler implements InitializingBean {
|
||||
protected byte[] getResult(EBikeDataProtocol dataProtocol, byte[] messageBody) {
|
||||
// 起始标志
|
||||
byte[] head = dataProtocol.getHead();
|
||||
// 序列号域
|
||||
byte[] serialNumber = dataProtocol.getPhysicalId();
|
||||
// 加密标志
|
||||
byte[] encryptFlag = dataProtocol.getMessageId();
|
||||
// 请求帧类型
|
||||
byte[] requestFrameType = dataProtocol.getCommand();
|
||||
// 应答帧类型
|
||||
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));
|
||||
// 长度 = 物理ID(4) + 消息ID(2) + 命令(1) + 数据(n) + 校验(2),每包最多256字节
|
||||
byte[] length = BytesUtil.intToBytes(9 + messageBody.length);
|
||||
|
||||
// 物理id
|
||||
byte[] physicalId = dataProtocol.getPhysicalId();
|
||||
// 加密标志
|
||||
byte[] messageId = dataProtocol.getMessageId();
|
||||
// 请求帧类型
|
||||
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,14 +1,18 @@
|
||||
package com.jsowell.netty.handler.electricbicycles;
|
||||
|
||||
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
|
||||
@@ -24,11 +28,18 @@ public class GetServerTimeHandler extends AbstractEBikeHandler {
|
||||
* 执行逻辑
|
||||
* 有应答
|
||||
*
|
||||
* @param msg
|
||||
* @param dataProtocol
|
||||
* @param ctx
|
||||
*/
|
||||
@Override
|
||||
public byte[] supplyProcess(EBikeDataProtocol dataProtocol, ChannelHandlerContext ctx) {
|
||||
return new byte[0];
|
||||
// 解析字节数组
|
||||
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,8 +1,11 @@
|
||||
package com.jsowell.netty.handler.electricbicycles;
|
||||
|
||||
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.EBikeMessageCmd21;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -29,6 +32,10 @@ public class HeartbeatHandler extends AbstractEBikeHandler {
|
||||
*/
|
||||
@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 new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package com.jsowell.netty.handler.electricbicycles;
|
||||
|
||||
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.EBikeMessageCmd20;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 设备心跳包
|
||||
* 设备注册包
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@@ -24,11 +27,15 @@ public class RegistrationHandler extends AbstractEBikeHandler {
|
||||
* 执行逻辑
|
||||
* 有应答
|
||||
*
|
||||
* @param msg
|
||||
* @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 new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package com.jsowell.netty.handler.electricbicycles;
|
||||
|
||||
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
|
||||
@@ -29,6 +32,10 @@ public class SettlementUploadHandler extends AbstractEBikeHandler {
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.springframework.stereotype.Component;
|
||||
* 对时设置
|
||||
*
|
||||
* 运营平台同步充电桩时钟,以保证充电桩与运营平台的时钟一致
|
||||
*
|
||||
* @deprecated
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 15:11
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@ 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 io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
@@ -40,6 +41,7 @@ public class ElectricBicyclesServerHandler extends SimpleChannelInboundHandler<O
|
||||
// 处理数据
|
||||
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);
|
||||
|
||||
@@ -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 ee 32 cc 66 04 14".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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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});
|
||||
|
||||
Reference in New Issue
Block a user