update 电单车协议

This commit is contained in:
Guoqs
2024-09-02 19:55:02 +08:00
parent a41264d1b9
commit 256e358be5
16 changed files with 245 additions and 115 deletions

View File

@@ -1,18 +1,19 @@
package com.jsowell.pile.domain.ebike;
import com.google.common.primitives.Bytes;
import com.jsowell.common.YouDianUtils;
import com.jsowell.common.util.BytesUtil;
import com.jsowell.pile.domain.ebike.deviceupload.*;
import lombok.Getter;
import lombok.Setter;
import lombok.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@Getter
@Setter
public abstract class AbsEBikeMessage {
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AbsEBikeMessage {
protected String header; // 包头 (3字节)
protected int length; // 长度 (2字节)
protected int physicalId; // 物理ID (4字节)
@@ -21,17 +22,9 @@ public abstract class AbsEBikeMessage {
protected Object payload; // 数据 (n字节)
protected int checksum; // 校验 (2字节)
public AbsEBikeMessage(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum) {
this.header = header;
this.length = length;
this.physicalId = physicalId;
this.messageId = messageId;
this.command = command;
this.payload = payload;
this.checksum = checksum;
}
public void parsePayload(byte[] dataBytes) {
public abstract void parsePayload(byte[] dataBytes);
};
private static AbsEBikeMessage createMessageInstance(String command, String header, int length, int physicalId, int messageId, int checksum, byte[] dataBytes) {
switch (command) {
@@ -104,10 +97,13 @@ public abstract class AbsEBikeMessage {
}
}
public abstract byte[] getMessageBytes();
public byte[] getMessageBytes() {
return null;
};
public static void main(String[] args) {
String s = "44 4E 59 26 00 3B 37 AB 04 02 00 82 00 64 01 00 00 01 01 00 00 12 34 56 78 12 34 56 78 12 34 56 78 12 34 56 78 80 70 88 13 F8 08";
String s = "444e592700198bca00072482333000000030313000300000000000000000000000000000003000300030e104";
String msg82 = "44 4E 59 26 00 3B 37 AB 04 02 00 82 00 64 01 00 00 01 01 00 00 12 34 56 78 12 34 56 78 12 34 56 78 12 34 56 78 80 70 88 13 F8 08";
s = s.replace(" ", "");
byte[] messageBytes = BytesUtil.hexStringToByteArray(s);
// 读取包头
@@ -115,36 +111,36 @@ public abstract class AbsEBikeMessage {
String header = new String(headerBytes, StandardCharsets.UTF_8);
System.out.println("header: " + header);
byte[] header2 = header.getBytes();
System.out.println("反序列header: " + header2);
// byte[] header2 = header.getBytes();
// System.out.println("反序列header: " + header2);
// 读取长度
byte[] lengthBytes = Arrays.copyOfRange(messageBytes, 3, 5);
int length = BytesUtil.bytesToIntLittle(lengthBytes);
System.out.println("length: " + length);
byte[] length2 = BytesUtil.intToBytesLittle(length);
System.out.println("反序列length: " + length2);
// byte[] length2 = BytesUtil.intToBytesLittle(length);
// System.out.println("反序列length: " + length2);
// 读取物理ID
byte[] physicalIdBytes = Arrays.copyOfRange(messageBytes, 5, 9);
int physicalId = BytesUtil.bytesToIntLittle(physicalIdBytes);
System.out.println("physicalId: " + physicalId);
byte[] physicalId2 = BytesUtil.intToBytesLittle(physicalId, 4);
System.out.println("反序列physicalId: " + physicalId2);
// byte[] physicalId2 = BytesUtil.intToBytesLittle(physicalId, 4);
// System.out.println("反序列physicalId: " + physicalId2);
// 读取消息ID
byte[] messageIdBytes = Arrays.copyOfRange(messageBytes, 9, 11);
int messageId = BytesUtil.bytesToIntLittle(messageIdBytes);
System.out.println("messageId: " + messageId);
byte[] messageId2 = BytesUtil.intToBytesLittle(messageId);
System.out.println("反序列messageId: " + messageId2);
// byte[] messageId2 = BytesUtil.intToBytesLittle(messageId);
// System.out.println("反序列messageId: " + messageId2);
// 读取命令
byte commandByte = messageBytes[11];
String command = BytesUtil.bcd2StrLittle(new byte[]{commandByte});
System.out.println("command: " + command);
byte[] command2 = BytesUtil.str2Bcd(command);
System.out.println("反序列command: " + command2);
// byte[] command2 = BytesUtil.str2Bcd(command);
// System.out.println("反序列command: " + command2);
// 读取数据
byte[] dataBytes = Arrays.copyOfRange(messageBytes, 12, messageBytes.length - 2);
@@ -156,8 +152,8 @@ public abstract class AbsEBikeMessage {
int checksum = BytesUtil.bytesToIntLittle(checksumBytes);
System.out.println("checksum: " + checksum);
byte[] concat = Bytes.concat(header2, length2, physicalId2, messageId2, command2);
System.out.println("concat: " + BytesUtil.printHexBinary(concat));
// byte[] concat = Bytes.concat(header2, length2, physicalId2, messageId2, command2);
// System.out.println("concat: " + BytesUtil.printHexBinary(concat));
}
}

View File

@@ -1,5 +1,6 @@
package com.jsowell.pile.domain.ebike;
import com.jsowell.common.util.BytesUtil;
import com.jsowell.pile.domain.ebike.deviceupload.*;
import lombok.extern.slf4j.Slf4j;
@@ -9,14 +10,14 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public enum EBikeCommandEnum {
// HEARTBEAT("01", "设备心跳包"),
HOST_GET_SERVER_TIME("12", "主机获取服务器时间", EBikeMessageCmd12.class),
REGISTRATION("20", "设备注册包", EBikeMessageCmd20.class),
DEVICE_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),
HOST_GET_SERVER_TIME(0x12, "主机获取服务器时间", EBikeMessageCmd12.class),
REGISTRATION(0x20, "设备注册包", EBikeMessageCmd20.class),
DEVICE_GET_SERVER_TIME(0x22, "设备获取服务器时间", EBikeMessageCmd22.class),
HEARTBEAT_2(0x21, "设备心跳包", EBikeMessageCmd21.class),
CARD_OPERATION(0x02, "刷卡操作", EBikeMessageCmd02.class),
SETTLEMENT_UPLOAD(0x03, "结算消费信息上传", EBikeMessageCmd03.class),
CHARGE_PORT_CONFIRMATION(0x04, "充电端口订单确认", EBikeMessageCmd04.class),
POWER_HEARTBEAT(0x06, "端口充电时功率心跳包", EBikeMessageCmd06.class),
// CHARGER_HEARTBEAT("41", "充电柜专有心跳包", EBikeMessageCmd41.class),
// ALARM_PUSH("42", "报警推送指令", EBikeMessageCmd20.class),
// CHARGE_COMPLETE("43", "充电完成通知,但不结算", EBikeMessageCmd20.class),
@@ -24,17 +25,17 @@ public enum EBikeCommandEnum {
;
EBikeCommandEnum(String code, String desc, Class<? extends AbsEBikeMessage> msgClass) {
EBikeCommandEnum(int code, String desc, Class<? extends AbsEBikeMessage> msgClass) {
this.code = code;
this.desc = desc;
this.msgClass = msgClass;
}
private final String code; // 帧类型code
private final int code; // 帧类型code
private final String desc; // 帧类型名称
private final Class<? extends AbsEBikeMessage> msgClass;
public String getCode() {
public int getCode() {
return code;
}
@@ -46,13 +47,17 @@ public enum EBikeCommandEnum {
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;
public byte[] getBytes() {
return BytesUtil.intToBytesLittle(code, 1);
}
// public static Class<? extends AbsEBikeMessage> getMsgClassByCode(String code) {
// for (EBikeCommandEnum e : EBikeCommandEnum.values()) {
// if (e.getCode().equals(code)) {
// return e.getMsgClass();
// }
// }
// return null;
// }
}

View File

@@ -9,10 +9,6 @@ import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
*/
public class EBikeMessageCmd81 extends AbsEBikeMessage {
public EBikeMessageCmd81(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum) {
super(header, length, physicalId, messageId, command, payload, checksum);
}
@Override
public void parsePayload(byte[] dataBytes) {

View File

@@ -4,42 +4,59 @@ import com.google.common.primitives.Bytes;
import com.jsowell.common.YouDianUtils;
import com.jsowell.common.util.BytesUtil;
import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
import lombok.Getter;
import lombok.Setter;
import lombok.*;
import java.util.Arrays;
/**
* 服务器开始、停止充电操作82指令
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EBikeMessageCmd82 extends AbsEBikeMessage {
private SpecificData specificData;
private SpecificData data;
public EBikeMessageCmd82(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum, SpecificData specificData) {
super(header, length, physicalId, messageId, command, payload, checksum);
this.specificData = specificData;
}
// public EBikeMessageCmd82(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum, SpecificData specificData) {
// super(header, length, physicalId, messageId, command, payload, checksum);
// this.specificData = specificData;
// }
// public EBikeMessageCmd82(SpecificData specificData) {
// super(header, length, physicalId, messageId, command, payload, checksum);
// this.specificData = specificData;
// }
@Override
public void parsePayload(byte[] dataBytes) {
this.specificData = new SpecificData(dataBytes);
this.data = new SpecificData(dataBytes);
}
public static void main(String[] args) {
int i = 0x82;
byte[] commandBytes = new byte[]{(byte) i};
System.out.println(BytesUtil.bcd2StrLittle(commandBytes));
int i2 = 82;
byte[] commandBytes2 = new byte[]{(byte) i2};
System.out.println(BytesUtil.bcd2StrLittle(commandBytes2));
}
@Override
public byte[] getMessageBytes() {
// 包头
byte[] headerBytes = BytesUtil.str2AscLittle(header);
// 长度
byte[] lengthBytes = BytesUtil.intToBytesLittle(length);
byte[] headerBytes = BytesUtil.stringToHexBytes(header, 3);
// 物理ID
byte[] physicalIdBytes = BytesUtil.intToBytesLittle(physicalId);
byte[] physicalIdBytes = BytesUtil.intToBytesLittle(physicalId, 4);
// 消息ID
byte[] messageIdBytes = BytesUtil.intToBytesLittle(messageId);
byte[] messageIdBytes = BytesUtil.intToBytesLittle(messageId, 2);
// 命令
byte[] commandBytes = BytesUtil.str2Bcd(command);
// byte[] commandBytes = BytesUtil.intToBytesLittle(Integer.parseInt(command), 1);
byte[] commandBytes = new byte[]{(byte) 0x82};
// 数据
byte[] payloadBytes = specificData.getBytes();
byte[] payloadBytes = data.getBytes();
// 长度 物理ID+消息ID+命令+数据(n) +校验(2)每包最多256字节
byte[] lengthBytes = BytesUtil.intToBytesLittle(9 + payloadBytes.length);
// 拼接
byte[] concat = Bytes.concat(headerBytes, lengthBytes, physicalIdBytes, messageIdBytes, commandBytes, payloadBytes);
// 校验和
@@ -47,12 +64,13 @@ public class EBikeMessageCmd82 extends AbsEBikeMessage {
return Bytes.concat(concat, checksumBytes);
}
public SpecificData getSpecificData() {
return specificData;
public SpecificData getData() {
return data;
}
@Getter
@Setter
@NoArgsConstructor
public static class SpecificData {
private String rateMode; // 费率模式 (1字节)
private String balanceOrValidity; // 余额/有效期 (4字节)
@@ -124,42 +142,43 @@ public class EBikeMessageCmd82 extends AbsEBikeMessage {
// 获取字节数组
public byte[] getBytes() {
// 费率模式
byte[] rateModeBytes = BytesUtil.str2Bcd(rateMode);
byte[] rateModeBytes = BytesUtil.stringToHexBytes(rateMode, 1);
// 余额/有效期
byte[] balanceOrValidityBytes = BytesUtil.str2Bcd(balanceOrValidity);
byte[] balanceOrValidityBytes = BytesUtil.stringToHexBytes(balanceOrValidity, 4);
// 端口号
byte[] portNumberBytes = BytesUtil.str2Bcd(portNumber);
byte[] portNumberBytes = BytesUtil.stringToHexBytes(portNumber, 1);
// 充电命令
byte[] chargeCommandBytes = BytesUtil.str2Bcd(chargeCommand);
byte[] chargeCommandBytes = BytesUtil.stringToHexBytes(chargeCommand, 1);
// 充电时长/电量
byte[] chargeDurationOrPowerBytes = BytesUtil.str2Bcd(chargeDurationOrPower);
byte[] chargeDurationOrPowerBytes = BytesUtil.stringToHexBytes(chargeDurationOrPower, 2);
// 订单编号
byte[] orderNumberBytes = BytesUtil.str2Bcd(orderNumber);
byte[] orderNumberBytes = BytesUtil.stringToHexBytes(orderNumber, 16);
// 最大充电时长
byte[] maxChargeDurationBytes = BytesUtil.str2Bcd(maxChargeDuration);
byte[] maxChargeDurationBytes = BytesUtil.stringToHexBytes(maxChargeDuration, 2);
// 过载功率
byte[] overloadPowerBytes = BytesUtil.str2Bcd(overloadPower);
byte[] overloadPowerBytes = BytesUtil.stringToHexBytes(overloadPower, 2);
// 二维码灯
byte[] qrCodeLightBytes = BytesUtil.str2Bcd(qrCodeLight);
byte[] qrCodeLightBytes = BytesUtil.stringToHexBytes(qrCodeLight, 1);
// 长充模式
byte[] longChargeModeBytes = BytesUtil.str2Bcd(longChargeMode);
// byte[] longChargeModeBytes = BytesUtil.stringToHexBytes(longChargeMode, 1);
// 额外浮充时间
byte[] extraFloatChargeTimeBytes = BytesUtil.str2Bcd(extraFloatChargeTime);
// byte[] extraFloatChargeTimeBytes = BytesUtil.stringToHexBytes(extraFloatChargeTime, 2);
// 是否跳过短路检测
byte[] skipShortCircuitDetectionBytes = BytesUtil.str2Bcd(skipShortCircuitDetection);
// byte[] skipShortCircuitDetectionBytes = BytesUtil.stringToHexBytes(skipShortCircuitDetection, 1);
// 不判断用户拔出
byte[] noUserPullOutCheckBytes = BytesUtil.str2Bcd(noUserPullOutCheck);
// byte[] noUserPullOutCheckBytes = BytesUtil.stringToHexBytes(noUserPullOutCheck, 1);
// 强制带充满自停
byte[] forceAutoStopWhenFullBytes = BytesUtil.str2Bcd(forceAutoStopWhenFull);
// byte[] forceAutoStopWhenFullBytes = BytesUtil.stringToHexBytes(forceAutoStopWhenFull, 1);
// 充满功率
byte[] fullChargePowerBytes = BytesUtil.str2Bcd(fullChargePower);
// byte[] fullChargePowerBytes = BytesUtil.stringToHexBytes(fullChargePower, 1);
// 最大充满功率最长判断时间
byte[] maxFullChargePowerCheckTimeBytes = BytesUtil.str2Bcd(maxFullChargePowerCheckTime);
// byte[] maxFullChargePowerCheckTimeBytes = BytesUtil.stringToHexBytes(maxFullChargePowerCheckTime, 1);
return Bytes.concat(rateModeBytes, balanceOrValidityBytes, portNumberBytes, chargeCommandBytes,
chargeDurationOrPowerBytes, orderNumberBytes, maxChargeDurationBytes, overloadPowerBytes,
qrCodeLightBytes, longChargeModeBytes, extraFloatChargeTimeBytes, skipShortCircuitDetectionBytes,
noUserPullOutCheckBytes, forceAutoStopWhenFullBytes, fullChargePowerBytes,
maxFullChargePowerCheckTimeBytes);
chargeDurationOrPowerBytes, orderNumberBytes, maxChargeDurationBytes, overloadPowerBytes, qrCodeLightBytes
// ,longChargeModeBytes, extraFloatChargeTimeBytes, skipShortCircuitDetectionBytes,
// noUserPullOutCheckBytes, forceAutoStopWhenFullBytes, fullChargePowerBytes,
// maxFullChargePowerCheckTimeBytes
);
}
}
}

View File

@@ -1,10 +1,10 @@
package com.jsowell.pile.service;
import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
/**
* 电单车发送命令服务
*/
public interface EBikeSendCommandService {
void send(String pileSn, AbsEBikeMessage msg);
// void send(String pileSn, AbsEBikeMessage msg);
void startCharging(String pileSn);
}

View File

@@ -2,7 +2,9 @@ package com.jsowell.pile.service.impl;
import com.jsowell.common.enums.ykc.PileChannelEntity;
import com.jsowell.common.util.BytesUtil;
import com.jsowell.common.util.RandomUtil;
import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
import com.jsowell.pile.domain.ebike.serversend.EBikeMessageCmd82;
import com.jsowell.pile.service.EBikeSendCommandService;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
@@ -14,21 +16,49 @@ import java.util.Objects;
@Service
public class EBikeSendCommandServiceImpl implements EBikeSendCommandService {
@Override
public void send(String pileSn, AbsEBikeMessage msg) {
public void startCharging(String pileSn) {
EBikeMessageCmd82 message = new EBikeMessageCmd82();
message.setHeader("DNY");
message.setPhysicalId(Integer.parseInt(pileSn));
message.setMessageId(RandomUtil.getRandomNumber(4));
message.setCommand("82");
EBikeMessageCmd82.SpecificData data = new EBikeMessageCmd82.SpecificData();
data.setRateMode("3");
data.setBalanceOrValidity("0");
data.setPortNumber("0");
data.setChargeCommand("1");
data.setChargeDurationOrPower("0");
data.setOrderNumber("0");
data.setMaxChargeDuration("0");
data.setOverloadPower("0");
data.setQrCodeLight("0");
data.setLongChargeMode("0");
data.setExtraFloatChargeTime("0");
data.setSkipShortCircuitDetection("0");
data.setNoUserPullOutCheck("0");
data.setForceAutoStopWhenFull("0");
data.setFullChargePower("0");
data.setMaxFullChargePowerCheckTime("0");
message.setData(data);
this.send(message);
}
/**
* 公共方法, 发送指令
* @param msg
*/
private void send(AbsEBikeMessage msg) {
String pileSn = msg.getPhysicalId() + "";
byte[] messageBytes = msg.getMessageBytes();
PileChannelEntity.output();
log.info("发送电单车命令, pileSn:{}, messageBytes:{}", pileSn, BytesUtil.binary(messageBytes, 16));
// 获取桩的channel
ChannelHandlerContext ctx = PileChannelEntity.getChannelByPileSn(pileSn);
if (Objects.isNull(ctx)) {
log.error("电单车send命令失败, 桩号:{}无法获取到长连接, 请检查充电桩连接状态!", pileSn);
throw new NullPointerException("channel");
}
if (Objects.isNull(msg)) {
log.error("电单车send命令失败, msg为空!");
throw new NullPointerException("msg");
}
String str = "44 4E 59 26 00 3B 37 AB 04 02 00 82 00 64 01 00 00 01 01 00 00 12 34 56 78 12 34 56 78 12 34 56 78 12 34 56 78 80 70 88 13 F8 08";
byte[] messageBytes = BytesUtil.hexStringToByteArray(str);
ctx.writeAndFlush(messageBytes);
// ctx.writeAndFlush(messageBytes);
}
}