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

@@ -72,6 +72,25 @@ public class TempController extends BaseController {
@Autowired @Autowired
private PileRemoteService pileRemoteService; private PileRemoteService pileRemoteService;
@Autowired
private EBikeSendCommandService eBikeSendCommandService;
/**
* 电单车开始充电
* http://localhost:8080/temp/tempStartCharging
*/
@PostMapping("/tempStartCharging")
public RestApiResponse<?> tempStartCharging(@RequestBody QueryOrderDTO dto) {
RestApiResponse<?> response = null;
try {
eBikeSendCommandService.startCharging(dto.getPileSn());
} catch (Exception e) {
logger.error("电单车开始充电 error", e);
response = new RestApiResponse<>(ReturnCodeEnum.CODE_FAILED);
}
return response;
}
/** /**
* 对时 * 对时
* http://localhost:8080/temp/proofreadTimeTest * http://localhost:8080/temp/proofreadTimeTest

View File

@@ -55,6 +55,7 @@ public class PileChannelEntity {
* @return * @return
*/ */
public static ChannelHandlerContext getChannelByPileSn(String pileSn) { public static ChannelHandlerContext getChannelByPileSn(String pileSn) {
output();
return manager.get(pileSn); return manager.get(pileSn);
} }
@@ -78,7 +79,7 @@ public class PileChannelEntity {
public static void output() { public static void output() {
for (HashMap.Entry<String, ChannelHandlerContext> entry : manager.entrySet()) { for (HashMap.Entry<String, ChannelHandlerContext> entry : manager.entrySet()) {
System.out.println("pileSn:" + entry.getKey() + System.out.println("pileSn:" + entry.getKey() +
",ChannelId:" + entry.getValue().channel().id().asLongText()); ",ChannelId:" + entry.getValue().channel().id().asShortText());
} }
} }

View File

@@ -272,6 +272,55 @@ public class BytesUtil {
return stringBuilder.toString(); return stringBuilder.toString();
} }
/**
* 将字符串转换为指定长度的16进制字节数组。
*
* @param str 需要转换的字符串
* @param targetLength 目标字节数组的长度
* @return 对应的16进制字节数组
*/
public static byte[] stringToHexBytes(String str, int targetLength) {
if (str == null) {
throw new IllegalArgumentException("字符串不能为null");
}
int strLength = str.length();
if (targetLength < strLength) {
throw new IllegalArgumentException("目标字节数组长度必须大于等于字符串长度");
}
byte[] hexBytes = new byte[targetLength];
for (int i = 0; i < strLength; i++) {
char c = str.charAt(i);
hexBytes[i] = (byte) c;
}
// 剩余部分用0填充
for (int i = strLength; i < targetLength; i++) {
hexBytes[i] = 0;
}
return hexBytes;
}
/**
* 将字节数组转换为十六进制字符串,用于打印查看。
*
* @param bytes 字节数组
* @return 十六进制字符串
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xFF & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
/** /**
* @函数功能: 10进制串转为BCD码 * @函数功能: 10进制串转为BCD码
* @输入参数: 10进制串 * @输入参数: 10进制串
@@ -326,12 +375,14 @@ public class BytesUtil {
return revert(temp); return revert(temp);
} }
public static void main(String[] args) { public static void main(String[] args) {
byte[] length = new byte[] {0x09, 0x00}; // byte[] length = new byte[] {0x09, 0x00};
int i = BytesUtil.bytesToIntLittle(length); // int i = BytesUtil.bytesToIntLittle(length);
System.out.println(i); // System.out.println(i);
String testStr = "DNY";
byte[] hexBytes = stringToHexBytes(testStr, 3);
System.out.println("Hex Bytes: " + bytesToHex(hexBytes));
} }
/** /**

View File

@@ -14,7 +14,7 @@ import java.util.Set;
public class RandomUtil { public class RandomUtil {
private static final String SYMBOLS_NUM = "0123456789"; // 纯数字 private static final String SYMBOLS_NUM = "0123456789"; // 纯数字
//如果需加入字母就改成0123456789abcdefg........... //如果需加入字母就改成0123456789abcdefg...........
private static final String SYMBOLS_NUM_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789"; // private static final String SYMBOLS_NUM_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
private static final Random RANDOM = new SecureRandom(); private static final Random RANDOM = new SecureRandom();
@@ -24,7 +24,7 @@ public class RandomUtil {
* @return 随机数字 * @return 随机数字
*/ */
public static String getSMSVerificationCode() { public static String getSMSVerificationCode() {
return getRandomNumber(6); return getRandomNumberStr(6);
} }
/** /**
@@ -33,7 +33,7 @@ public class RandomUtil {
* @param length 需要的长度 * @param length 需要的长度
* @return 随机数 * @return 随机数
*/ */
public static String getRandomNumber(int length) { public static String getRandomNumberStr(int length) {
char[] nonceChars = new char[length]; //指定长度,自己可以设置 char[] nonceChars = new char[length]; //指定长度,自己可以设置
for (int index = 0; index < nonceChars.length; ++index) { for (int index = 0; index < nonceChars.length; ++index) {
nonceChars[index] = SYMBOLS_NUM.charAt(RANDOM.nextInt(SYMBOLS_NUM.length())); nonceChars[index] = SYMBOLS_NUM.charAt(RANDOM.nextInt(SYMBOLS_NUM.length()));
@@ -41,6 +41,10 @@ public class RandomUtil {
return new String(nonceChars); return new String(nonceChars);
} }
public static int getRandomNumber(int length) {
return Integer.parseInt(getRandomNumberStr(length));
}
/** /**
* 获取一组不重复的6位数随机数 * 获取一组不重复的6位数随机数
* *

View File

@@ -87,7 +87,7 @@ public class IdUtils {
public static String generateTransactionCode(String pileConnectorCode) { public static String generateTransactionCode(String pileConnectorCode) {
String timeNow = DateUtils.dateTimeNow(DateUtils.YYMMDDHHMMSS); String timeNow = DateUtils.dateTimeNow(DateUtils.YYMMDDHHMMSS);
// 随机生成一个四位整数 // 随机生成一个四位整数
String randomNumber = RandomUtil.getRandomNumber(4); String randomNumber = RandomUtil.getRandomNumberStr(4);
return pileConnectorCode + timeNow + randomNumber; return pileConnectorCode + timeNow + randomNumber;
} }

View File

@@ -3,6 +3,7 @@ package com.jsowell.netty.handler.electricbicycles;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol; import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
import com.jsowell.common.util.BytesUtil; import com.jsowell.common.util.BytesUtil;
import com.jsowell.common.util.YKCUtils;
import com.jsowell.netty.factory.EBikeOperateFactory; import com.jsowell.netty.factory.EBikeOperateFactory;
import com.jsowell.pile.domain.ebike.AbsEBikeMessage; import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
import com.jsowell.pile.domain.ebike.EBikeCommandEnum; import com.jsowell.pile.domain.ebike.EBikeCommandEnum;
@@ -17,7 +18,8 @@ import org.springframework.stereotype.Component;
@Slf4j @Slf4j
@Component @Component
public class DeviceGetServerTimeHandler extends AbstractEBikeHandler { public class DeviceGetServerTimeHandler extends AbstractEBikeHandler {
private final String type = EBikeCommandEnum.DEVICE_GET_SERVER_TIME.getCode(); // private final String type = EBikeCommandEnum.DEVICE_GET_SERVER_TIME.getCode();
private final String type = YKCUtils.frameType2Str(EBikeCommandEnum.DEVICE_GET_SERVER_TIME.getBytes());
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {

View File

@@ -5,6 +5,7 @@ import com.jsowell.common.constant.Constants;
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol; import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
import com.jsowell.common.enums.ebike.PortStatusEnum; import com.jsowell.common.enums.ebike.PortStatusEnum;
import com.jsowell.common.util.StringUtils; import com.jsowell.common.util.StringUtils;
import com.jsowell.common.util.YKCUtils;
import com.jsowell.netty.factory.EBikeOperateFactory; import com.jsowell.netty.factory.EBikeOperateFactory;
import com.jsowell.pile.domain.ebike.AbsEBikeMessage; import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
import com.jsowell.pile.domain.ebike.EBikeCommandEnum; import com.jsowell.pile.domain.ebike.EBikeCommandEnum;
@@ -24,7 +25,7 @@ import java.util.List;
@Slf4j @Slf4j
@Component @Component
public class HeartbeatHandler extends AbstractEBikeHandler { public class HeartbeatHandler extends AbstractEBikeHandler {
private final String type = EBikeCommandEnum.HEARTBEAT_2.getCode(); private final String type = YKCUtils.frameType2Str(EBikeCommandEnum.HEARTBEAT_2.getBytes());
@Autowired @Autowired
private PileBasicInfoService pileBasicInfoService; private PileBasicInfoService pileBasicInfoService;

View File

@@ -3,6 +3,7 @@ package com.jsowell.netty.handler.electricbicycles;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol; import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
import com.jsowell.common.util.BytesUtil; import com.jsowell.common.util.BytesUtil;
import com.jsowell.common.util.YKCUtils;
import com.jsowell.netty.factory.EBikeOperateFactory; import com.jsowell.netty.factory.EBikeOperateFactory;
import com.jsowell.pile.domain.ebike.AbsEBikeMessage; import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
import com.jsowell.pile.domain.ebike.EBikeCommandEnum; import com.jsowell.pile.domain.ebike.EBikeCommandEnum;
@@ -17,7 +18,8 @@ import org.springframework.stereotype.Component;
@Slf4j @Slf4j
@Component @Component
public class HostGetServerTimeHandler extends AbstractEBikeHandler { public class HostGetServerTimeHandler extends AbstractEBikeHandler {
private final String type = EBikeCommandEnum.HOST_GET_SERVER_TIME.getCode(); // private final String type = EBikeCommandEnum.HOST_GET_SERVER_TIME.getCode();
private final String type = YKCUtils.frameType2Str(EBikeCommandEnum.HOST_GET_SERVER_TIME.getBytes());
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {

View File

@@ -3,6 +3,7 @@ package com.jsowell.netty.handler.electricbicycles;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.jsowell.common.constant.Constants; import com.jsowell.common.constant.Constants;
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol; import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
import com.jsowell.common.util.YKCUtils;
import com.jsowell.netty.factory.EBikeOperateFactory; import com.jsowell.netty.factory.EBikeOperateFactory;
import com.jsowell.pile.domain.ebike.AbsEBikeMessage; import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
import com.jsowell.pile.domain.ebike.EBikeCommandEnum; import com.jsowell.pile.domain.ebike.EBikeCommandEnum;
@@ -19,7 +20,8 @@ import org.springframework.stereotype.Component;
@Slf4j @Slf4j
@Component @Component
public class RegistrationHandler extends AbstractEBikeHandler { public class RegistrationHandler extends AbstractEBikeHandler {
private final String type = EBikeCommandEnum.REGISTRATION.getCode(); // private final String type = EBikeCommandEnum.REGISTRATION.getCode();
private final String type = YKCUtils.frameType2Str(EBikeCommandEnum.REGISTRATION.getBytes());
@Autowired @Autowired
private PileBasicInfoService pileBasicInfoService; private PileBasicInfoService pileBasicInfoService;

View File

@@ -2,6 +2,7 @@ package com.jsowell.netty.handler.electricbicycles;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.jsowell.common.core.domain.ebike.EBikeDataProtocol; import com.jsowell.common.core.domain.ebike.EBikeDataProtocol;
import com.jsowell.common.util.YKCUtils;
import com.jsowell.netty.factory.EBikeOperateFactory; import com.jsowell.netty.factory.EBikeOperateFactory;
import com.jsowell.pile.domain.ebike.AbsEBikeMessage; import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
import com.jsowell.pile.domain.ebike.EBikeCommandEnum; import com.jsowell.pile.domain.ebike.EBikeCommandEnum;
@@ -16,7 +17,8 @@ import org.springframework.stereotype.Component;
@Slf4j @Slf4j
@Component @Component
public class SettlementUploadHandler extends AbstractEBikeHandler { public class SettlementUploadHandler extends AbstractEBikeHandler {
private final String type = EBikeCommandEnum.SETTLEMENT_UPLOAD.getCode(); // private final String type = EBikeCommandEnum.SETTLEMENT_UPLOAD.getCode();
private final String type = YKCUtils.frameType2Str(EBikeCommandEnum.SETTLEMENT_UPLOAD.getBytes());
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {

View File

@@ -1,18 +1,19 @@
package com.jsowell.pile.domain.ebike; package com.jsowell.pile.domain.ebike;
import com.google.common.primitives.Bytes;
import com.jsowell.common.YouDianUtils; import com.jsowell.common.YouDianUtils;
import com.jsowell.common.util.BytesUtil; import com.jsowell.common.util.BytesUtil;
import com.jsowell.pile.domain.ebike.deviceupload.*; import com.jsowell.pile.domain.ebike.deviceupload.*;
import lombok.Getter; import lombok.*;
import lombok.Setter;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
@Getter @Getter
@Setter @Setter
public abstract class AbsEBikeMessage { @NoArgsConstructor
@AllArgsConstructor
@Builder
public class AbsEBikeMessage {
protected String header; // 包头 (3字节) protected String header; // 包头 (3字节)
protected int length; // 长度 (2字节) protected int length; // 长度 (2字节)
protected int physicalId; // 物理ID (4字节) protected int physicalId; // 物理ID (4字节)
@@ -21,17 +22,9 @@ public abstract class AbsEBikeMessage {
protected Object payload; // 数据 (n字节) protected Object payload; // 数据 (n字节)
protected int checksum; // 校验 (2字节) protected int checksum; // 校验 (2字节)
public AbsEBikeMessage(String header, int length, int physicalId, int messageId, String command, Object payload, int checksum) { public void parsePayload(byte[] dataBytes) {
this.header = header;
this.length = length;
this.physicalId = physicalId;
this.messageId = messageId;
this.command = command;
this.payload = payload;
this.checksum = checksum;
}
public abstract void parsePayload(byte[] dataBytes); };
private static AbsEBikeMessage createMessageInstance(String command, String header, int length, int physicalId, int messageId, int checksum, byte[] dataBytes) { private static AbsEBikeMessage createMessageInstance(String command, String header, int length, int physicalId, int messageId, int checksum, byte[] dataBytes) {
switch (command) { 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) { 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(" ", ""); s = s.replace(" ", "");
byte[] messageBytes = BytesUtil.hexStringToByteArray(s); byte[] messageBytes = BytesUtil.hexStringToByteArray(s);
// 读取包头 // 读取包头
@@ -115,36 +111,36 @@ public abstract class AbsEBikeMessage {
String header = new String(headerBytes, StandardCharsets.UTF_8); String header = new String(headerBytes, StandardCharsets.UTF_8);
System.out.println("header: " + header); System.out.println("header: " + header);
byte[] header2 = header.getBytes(); // byte[] header2 = header.getBytes();
System.out.println("反序列header: " + header2); // System.out.println("反序列header: " + header2);
// 读取长度 // 读取长度
byte[] lengthBytes = Arrays.copyOfRange(messageBytes, 3, 5); byte[] lengthBytes = Arrays.copyOfRange(messageBytes, 3, 5);
int length = BytesUtil.bytesToIntLittle(lengthBytes); int length = BytesUtil.bytesToIntLittle(lengthBytes);
System.out.println("length: " + length); System.out.println("length: " + length);
byte[] length2 = BytesUtil.intToBytesLittle(length); // byte[] length2 = BytesUtil.intToBytesLittle(length);
System.out.println("反序列length: " + length2); // System.out.println("反序列length: " + length2);
// 读取物理ID // 读取物理ID
byte[] physicalIdBytes = Arrays.copyOfRange(messageBytes, 5, 9); byte[] physicalIdBytes = Arrays.copyOfRange(messageBytes, 5, 9);
int physicalId = BytesUtil.bytesToIntLittle(physicalIdBytes); int physicalId = BytesUtil.bytesToIntLittle(physicalIdBytes);
System.out.println("physicalId: " + physicalId); System.out.println("physicalId: " + physicalId);
byte[] physicalId2 = BytesUtil.intToBytesLittle(physicalId, 4); // byte[] physicalId2 = BytesUtil.intToBytesLittle(physicalId, 4);
System.out.println("反序列physicalId: " + physicalId2); // System.out.println("反序列physicalId: " + physicalId2);
// 读取消息ID // 读取消息ID
byte[] messageIdBytes = Arrays.copyOfRange(messageBytes, 9, 11); byte[] messageIdBytes = Arrays.copyOfRange(messageBytes, 9, 11);
int messageId = BytesUtil.bytesToIntLittle(messageIdBytes); int messageId = BytesUtil.bytesToIntLittle(messageIdBytes);
System.out.println("messageId: " + messageId); System.out.println("messageId: " + messageId);
byte[] messageId2 = BytesUtil.intToBytesLittle(messageId); // byte[] messageId2 = BytesUtil.intToBytesLittle(messageId);
System.out.println("反序列messageId: " + messageId2); // System.out.println("反序列messageId: " + messageId2);
// 读取命令 // 读取命令
byte commandByte = messageBytes[11]; byte commandByte = messageBytes[11];
String command = BytesUtil.bcd2StrLittle(new byte[]{commandByte}); String command = BytesUtil.bcd2StrLittle(new byte[]{commandByte});
System.out.println("command: " + command); System.out.println("command: " + command);
byte[] command2 = BytesUtil.str2Bcd(command); // byte[] command2 = BytesUtil.str2Bcd(command);
System.out.println("反序列command: " + command2); // System.out.println("反序列command: " + command2);
// 读取数据 // 读取数据
byte[] dataBytes = Arrays.copyOfRange(messageBytes, 12, messageBytes.length - 2); byte[] dataBytes = Arrays.copyOfRange(messageBytes, 12, messageBytes.length - 2);
@@ -156,8 +152,8 @@ public abstract class AbsEBikeMessage {
int checksum = BytesUtil.bytesToIntLittle(checksumBytes); int checksum = BytesUtil.bytesToIntLittle(checksumBytes);
System.out.println("checksum: " + checksum); System.out.println("checksum: " + checksum);
byte[] concat = Bytes.concat(header2, length2, physicalId2, messageId2, command2); // byte[] concat = Bytes.concat(header2, length2, physicalId2, messageId2, command2);
System.out.println("concat: " + BytesUtil.printHexBinary(concat)); // System.out.println("concat: " + BytesUtil.printHexBinary(concat));
} }
} }

View File

@@ -1,5 +1,6 @@
package com.jsowell.pile.domain.ebike; package com.jsowell.pile.domain.ebike;
import com.jsowell.common.util.BytesUtil;
import com.jsowell.pile.domain.ebike.deviceupload.*; import com.jsowell.pile.domain.ebike.deviceupload.*;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -9,14 +10,14 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public enum EBikeCommandEnum { public enum EBikeCommandEnum {
// HEARTBEAT("01", "设备心跳包"), // HEARTBEAT("01", "设备心跳包"),
HOST_GET_SERVER_TIME("12", "主机获取服务器时间", EBikeMessageCmd12.class), HOST_GET_SERVER_TIME(0x12, "主机获取服务器时间", EBikeMessageCmd12.class),
REGISTRATION("20", "设备注册包", EBikeMessageCmd20.class), REGISTRATION(0x20, "设备注册包", EBikeMessageCmd20.class),
DEVICE_GET_SERVER_TIME("22", "设备获取服务器时间", EBikeMessageCmd22.class), DEVICE_GET_SERVER_TIME(0x22, "设备获取服务器时间", EBikeMessageCmd22.class),
HEARTBEAT_2("21", "设备心跳包", EBikeMessageCmd21.class), HEARTBEAT_2(0x21, "设备心跳包", EBikeMessageCmd21.class),
CARD_OPERATION("02", "刷卡操作", EBikeMessageCmd02.class), CARD_OPERATION(0x02, "刷卡操作", EBikeMessageCmd02.class),
SETTLEMENT_UPLOAD("03", "结算消费信息上传", EBikeMessageCmd03.class), SETTLEMENT_UPLOAD(0x03, "结算消费信息上传", EBikeMessageCmd03.class),
CHARGE_PORT_CONFIRMATION("04", "充电端口订单确认", EBikeMessageCmd04.class), CHARGE_PORT_CONFIRMATION(0x04, "充电端口订单确认", EBikeMessageCmd04.class),
POWER_HEARTBEAT("06", "端口充电时功率心跳包", EBikeMessageCmd06.class), POWER_HEARTBEAT(0x06, "端口充电时功率心跳包", EBikeMessageCmd06.class),
// CHARGER_HEARTBEAT("41", "充电柜专有心跳包", EBikeMessageCmd41.class), // CHARGER_HEARTBEAT("41", "充电柜专有心跳包", EBikeMessageCmd41.class),
// ALARM_PUSH("42", "报警推送指令", EBikeMessageCmd20.class), // ALARM_PUSH("42", "报警推送指令", EBikeMessageCmd20.class),
// CHARGE_COMPLETE("43", "充电完成通知,但不结算", 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.code = code;
this.desc = desc; this.desc = desc;
this.msgClass = msgClass; this.msgClass = msgClass;
} }
private final String code; // 帧类型code private final int code; // 帧类型code
private final String desc; // 帧类型名称 private final String desc; // 帧类型名称
private final Class<? extends AbsEBikeMessage> msgClass; private final Class<? extends AbsEBikeMessage> msgClass;
public String getCode() { public int getCode() {
return code; return code;
} }
@@ -46,13 +47,17 @@ public enum EBikeCommandEnum {
return msgClass; return msgClass;
} }
public static Class<? extends AbsEBikeMessage> getMsgClassByCode(String code) { public byte[] getBytes() {
for (EBikeCommandEnum e : EBikeCommandEnum.values()) { return BytesUtil.intToBytesLittle(code, 1);
if (e.getCode().equals(code)) {
return e.getMsgClass();
}
}
return null;
} }
// 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 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 @Override
public void parsePayload(byte[] dataBytes) { 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.YouDianUtils;
import com.jsowell.common.util.BytesUtil; import com.jsowell.common.util.BytesUtil;
import com.jsowell.pile.domain.ebike.AbsEBikeMessage; import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
import lombok.Getter; import lombok.*;
import lombok.Setter;
import java.util.Arrays; import java.util.Arrays;
/** /**
* 服务器开始、停止充电操作82指令 * 服务器开始、停止充电操作82指令
*/ */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class EBikeMessageCmd82 extends AbsEBikeMessage { 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) { // 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); // super(header, length, physicalId, messageId, command, payload, checksum);
this.specificData = specificData; // this.specificData = specificData;
} // }
// public EBikeMessageCmd82(SpecificData specificData) {
// super(header, length, physicalId, messageId, command, payload, checksum);
// this.specificData = specificData;
// }
@Override @Override
public void parsePayload(byte[] dataBytes) { 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 @Override
public byte[] getMessageBytes() { public byte[] getMessageBytes() {
// 包头 // 包头
byte[] headerBytes = BytesUtil.str2AscLittle(header); byte[] headerBytes = BytesUtil.stringToHexBytes(header, 3);
// 长度
byte[] lengthBytes = BytesUtil.intToBytesLittle(length);
// 物理ID // 物理ID
byte[] physicalIdBytes = BytesUtil.intToBytesLittle(physicalId); byte[] physicalIdBytes = BytesUtil.intToBytesLittle(physicalId, 4);
// 消息ID // 消息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); byte[] concat = Bytes.concat(headerBytes, lengthBytes, physicalIdBytes, messageIdBytes, commandBytes, payloadBytes);
// 校验和 // 校验和
@@ -47,12 +64,13 @@ public class EBikeMessageCmd82 extends AbsEBikeMessage {
return Bytes.concat(concat, checksumBytes); return Bytes.concat(concat, checksumBytes);
} }
public SpecificData getSpecificData() { public SpecificData getData() {
return specificData; return data;
} }
@Getter @Getter
@Setter @Setter
@NoArgsConstructor
public static class SpecificData { public static class SpecificData {
private String rateMode; // 费率模式 (1字节) private String rateMode; // 费率模式 (1字节)
private String balanceOrValidity; // 余额/有效期 (4字节) private String balanceOrValidity; // 余额/有效期 (4字节)
@@ -124,42 +142,43 @@ public class EBikeMessageCmd82 extends AbsEBikeMessage {
// 获取字节数组 // 获取字节数组
public byte[] getBytes() { 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, return Bytes.concat(rateModeBytes, balanceOrValidityBytes, portNumberBytes, chargeCommandBytes,
chargeDurationOrPowerBytes, orderNumberBytes, maxChargeDurationBytes, overloadPowerBytes, chargeDurationOrPowerBytes, orderNumberBytes, maxChargeDurationBytes, overloadPowerBytes, qrCodeLightBytes
qrCodeLightBytes, longChargeModeBytes, extraFloatChargeTimeBytes, skipShortCircuitDetectionBytes, // ,longChargeModeBytes, extraFloatChargeTimeBytes, skipShortCircuitDetectionBytes,
noUserPullOutCheckBytes, forceAutoStopWhenFullBytes, fullChargePowerBytes, // noUserPullOutCheckBytes, forceAutoStopWhenFullBytes, fullChargePowerBytes,
maxFullChargePowerCheckTimeBytes); // maxFullChargePowerCheckTimeBytes
);
} }
} }
} }

View File

@@ -1,10 +1,10 @@
package com.jsowell.pile.service; package com.jsowell.pile.service;
import com.jsowell.pile.domain.ebike.AbsEBikeMessage;
/** /**
* 电单车发送命令服务 * 电单车发送命令服务
*/ */
public interface EBikeSendCommandService { 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.enums.ykc.PileChannelEntity;
import com.jsowell.common.util.BytesUtil; 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.AbsEBikeMessage;
import com.jsowell.pile.domain.ebike.serversend.EBikeMessageCmd82;
import com.jsowell.pile.service.EBikeSendCommandService; import com.jsowell.pile.service.EBikeSendCommandService;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -14,21 +16,49 @@ import java.util.Objects;
@Service @Service
public class EBikeSendCommandServiceImpl implements EBikeSendCommandService { public class EBikeSendCommandServiceImpl implements EBikeSendCommandService {
@Override @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 // 获取桩的channel
ChannelHandlerContext ctx = PileChannelEntity.getChannelByPileSn(pileSn); ChannelHandlerContext ctx = PileChannelEntity.getChannelByPileSn(pileSn);
if (Objects.isNull(ctx)) { if (Objects.isNull(ctx)) {
log.error("电单车send命令失败, 桩号:{}无法获取到长连接, 请检查充电桩连接状态!", pileSn); log.error("电单车send命令失败, 桩号:{}无法获取到长连接, 请检查充电桩连接状态!", pileSn);
throw new NullPointerException("channel"); throw new NullPointerException("channel");
} }
if (Objects.isNull(msg)) { // ctx.writeAndFlush(messageBytes);
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);
} }
} }