update 电单车协议

This commit is contained in:
Guoqs
2024-09-05 09:58:21 +08:00
parent 2a6daa56df
commit b341862071
9 changed files with 107 additions and 21 deletions

View File

@@ -14,6 +14,7 @@ import com.jsowell.pile.domain.AdapayMemberAccount;
import com.jsowell.pile.domain.MemberPlateNumberRelation; import com.jsowell.pile.domain.MemberPlateNumberRelation;
import com.jsowell.pile.domain.OrderBasicInfo; import com.jsowell.pile.domain.OrderBasicInfo;
import com.jsowell.pile.domain.ykcCommond.ReservationChargingCommand; import com.jsowell.pile.domain.ykcCommond.ReservationChargingCommand;
import com.jsowell.pile.domain.ykcCommond.StartChargingCommand;
import com.jsowell.pile.dto.*; import com.jsowell.pile.dto.*;
import com.jsowell.pile.service.*; import com.jsowell.pile.service.*;
import com.jsowell.pile.service.programlogic.AbstractProgramLogic; import com.jsowell.pile.service.programlogic.AbstractProgramLogic;
@@ -83,7 +84,11 @@ public class TempController extends BaseController {
public RestApiResponse<?> tempStartCharging(@RequestBody QueryOrderDTO dto) { public RestApiResponse<?> tempStartCharging(@RequestBody QueryOrderDTO dto) {
RestApiResponse<?> response = null; RestApiResponse<?> response = null;
try { try {
eBikeSendCommandService.startCharging(dto.getPileSn(), dto.getConnectorCode()); StartChargingCommand command = StartChargingCommand.builder()
.pileSn(dto.getPileSn())
.connectorCode(dto.getConnectorCode())
.build();
eBikeSendCommandService.sendStartChargingCommand(command);
response = new RestApiResponse<>(); response = new RestApiResponse<>();
} catch (Exception e) { } catch (Exception e) {
logger.error("电单车开始充电 error", e); logger.error("电单车开始充电 error", e);

View File

@@ -10,6 +10,8 @@ import java.math.BigDecimal;
* @author jsowell * @author jsowell
*/ */
public class Constants { public class Constants {
// 电单车协议包头
public static final String EBIKE_HEADER = "DNY";
// 十六进制前缀 // 十六进制前缀
public static final String HEX_PREFIX = "0x"; public static final String HEX_PREFIX = "0x";

View File

@@ -380,7 +380,7 @@ public class BytesUtil {
// int i = BytesUtil.bytesToIntLittle(length); // int i = BytesUtil.bytesToIntLittle(length);
// System.out.println(i); // System.out.println(i);
String testStr = "DNY"; String testStr = Constants.EBIKE_HEADER;
byte[] hexBytes = stringToHexBytes(testStr, 3); byte[] hexBytes = stringToHexBytes(testStr, 3);
System.out.println("Hex Bytes: " + bytesToHex(hexBytes)); System.out.println("Hex Bytes: " + bytesToHex(hexBytes));
} }

View File

@@ -1,5 +1,6 @@
package com.jsowell.netty.decoder; package com.jsowell.netty.decoder;
import com.jsowell.common.constant.Constants;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
@@ -47,7 +48,7 @@ public class StartAndLengthFieldFrameDecoder extends ByteToMessageDecoder {
buffer.getBytes(beginReader, headerBytes, 0, HEADER_LENGTH_DNY); buffer.getBytes(beginReader, headerBytes, 0, HEADER_LENGTH_DNY);
String header = new String(headerBytes, StandardCharsets.UTF_8); String header = new String(headerBytes, StandardCharsets.UTF_8);
// log.info("检查包头是否是DNY, header:{}", header); // log.info("检查包头是否是DNY, header:{}", header);
if ("DNY".equals(header)) { if (Constants.EBIKE_HEADER.equals(header)) {
// 处理 DNY 协议 // 处理 DNY 协议
decodeDnyMessage(buffer, out, beginReader); decodeDnyMessage(buffer, out, beginReader);
return; return;
@@ -73,7 +74,7 @@ public class StartAndLengthFieldFrameDecoder extends ByteToMessageDecoder {
byte[] headerBytes = new byte[HEADER_LENGTH_DNY]; byte[] headerBytes = new byte[HEADER_LENGTH_DNY];
buffer.getBytes(beginReader, headerBytes, 0, HEADER_LENGTH_DNY); buffer.getBytes(beginReader, headerBytes, 0, HEADER_LENGTH_DNY);
String header = new String(headerBytes, StandardCharsets.UTF_8); String header = new String(headerBytes, StandardCharsets.UTF_8);
return "DNY".equals(header); return Constants.EBIKE_HEADER.equals(header);
} }
return false; return false;
} }

View File

@@ -2,6 +2,7 @@ package com.jsowell.pile.domain.ebike.serversend;
import com.google.common.primitives.Bytes; import com.google.common.primitives.Bytes;
import com.jsowell.common.YouDianUtils; import com.jsowell.common.YouDianUtils;
import com.jsowell.common.constant.Constants;
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.*; import lombok.*;
@@ -35,7 +36,7 @@ public class EBikeMessageCmd82 extends AbsEBikeMessage {
@Override @Override
public byte[] getMessageBytes() { public byte[] getMessageBytes() {
// 包头 // 包头
byte[] headerBytes = BytesUtil.stringToHexBytes(header, 3); byte[] headerBytes = BytesUtil.stringToHexBytes(Constants.EBIKE_HEADER, 3);
// 物理ID // 物理ID
byte[] physicalIdBytes = YouDianUtils.getPhysicalIdBytes(physicalId); byte[] physicalIdBytes = YouDianUtils.getPhysicalIdBytes(physicalId);
// 消息ID // 消息ID
@@ -75,13 +76,13 @@ public class EBikeMessageCmd82 extends AbsEBikeMessage {
private int chargeCommand; // 充电命令 (1字节) private int chargeCommand; // 充电命令 (1字节)
// 告知设备的充电时长/电量, 如是0x0000则说明是充满自停, 其他数值则按照时长/电量充电, 且充满不会自停, // 告知设备的充电时长/电量, 如是0x0000则说明是充满自停, 其他数值则按照时长/电量充电, 且充满不会自停,
private int chargeDurationOrPower; // 充电时长/电量 (2字节) private int chargeTimeOrPower; // 充电时长/电量 (2字节)
// 生成给桩的交易流水号, 桩协议叫订单编号 // 生成给桩的交易流水号, 桩协议叫订单编号
private String transactionCode; // 订单编号 (16字节) private String transactionCode; // 订单编号 (16字节)
// 最大充电时长、过载功率只对当前端口当前订单有效不影响其他端口动态设置此参数如果参数为0表示不修改会使用设备的设置值默认10小时 // 最大充电时长、过载功率只对当前端口当前订单有效不影响其他端口动态设置此参数如果参数为0表示不修改会使用设备的设置值默认10小时
private int maxChargeDuration; // 最大充电时长 (2字节) private int maxChargeTime; // 最大充电时长 (2字节)
private int overloadPower; // 过载功率 (2字节) private int overloadPower; // 过载功率 (2字节)
// 二维码灯0打开1关闭针对部分有二维码灯的设备有效。此设置是针对下一次插头插入时是否点亮二维码背光灯保存在内存中断电重启后就会失效 // 二维码灯0打开1关闭针对部分有二维码灯的设备有效。此设置是针对下一次插头插入时是否点亮二维码背光灯保存在内存中断电重启后就会失效
@@ -112,13 +113,13 @@ public class EBikeMessageCmd82 extends AbsEBikeMessage {
this.chargeCommand = BytesUtil.bytesToIntLittle(new byte[]{chargeCommandBytes}); this.chargeCommand = BytesUtil.bytesToIntLittle(new byte[]{chargeCommandBytes});
byte[] chargeDurationOrPowerBytes = Arrays.copyOfRange(dataBytes, 7, 9); byte[] chargeDurationOrPowerBytes = Arrays.copyOfRange(dataBytes, 7, 9);
this.chargeDurationOrPower = BytesUtil.bytesToIntLittle(chargeDurationOrPowerBytes); this.chargeTimeOrPower = BytesUtil.bytesToIntLittle(chargeDurationOrPowerBytes);
byte[] orderNumberBytes = Arrays.copyOfRange(dataBytes, 9, 25); byte[] orderNumberBytes = Arrays.copyOfRange(dataBytes, 9, 25);
this.transactionCode = BytesUtil.bcd2StrLittle(orderNumberBytes); this.transactionCode = BytesUtil.bcd2StrLittle(orderNumberBytes);
byte[] maxChargeDurationBytes = Arrays.copyOfRange(dataBytes, 25, 27); byte[] maxChargeDurationBytes = Arrays.copyOfRange(dataBytes, 25, 27);
this.maxChargeDuration = BytesUtil.bytesToIntLittle(maxChargeDurationBytes); this.maxChargeTime = BytesUtil.bytesToIntLittle(maxChargeDurationBytes);
byte[] overloadPowerBytes = Arrays.copyOfRange(dataBytes, 27, 29); byte[] overloadPowerBytes = Arrays.copyOfRange(dataBytes, 27, 29);
this.overloadPower = BytesUtil.bytesToIntLittle(overloadPowerBytes); this.overloadPower = BytesUtil.bytesToIntLittle(overloadPowerBytes);
@@ -159,11 +160,11 @@ public class EBikeMessageCmd82 extends AbsEBikeMessage {
// 充电命令 // 充电命令
byte[] chargeCommandBytes = BytesUtil.intToBytesLittle(chargeCommand, 1); byte[] chargeCommandBytes = BytesUtil.intToBytesLittle(chargeCommand, 1);
// 充电时长/电量 // 充电时长/电量
byte[] chargeDurationOrPowerBytes = BytesUtil.intToBytesLittle(chargeDurationOrPower, 2); byte[] chargeDurationOrPowerBytes = BytesUtil.intToBytesLittle(chargeTimeOrPower, 2);
// 订单编号 // 订单编号
byte[] orderNumberBytes = BytesUtil.ensureLength(BytesUtil.str2Bcd(transactionCode), 16); byte[] orderNumberBytes = BytesUtil.ensureLength(BytesUtil.str2Bcd(transactionCode), 16);
// 最大充电时长 // 最大充电时长
byte[] maxChargeDurationBytes = BytesUtil.intToBytesLittle(maxChargeDuration, 2); byte[] maxChargeDurationBytes = BytesUtil.intToBytesLittle(maxChargeTime, 2);
// 过载功率 // 过载功率
byte[] overloadPowerBytes = BytesUtil.intToBytesLittle(overloadPower, 2); byte[] overloadPowerBytes = BytesUtil.intToBytesLittle(overloadPower, 2);
// 二维码灯 // 二维码灯

View File

@@ -15,4 +15,9 @@ import lombok.NoArgsConstructor;
public class StopChargingCommand { public class StopChargingCommand {
private String pileSn; private String pileSn;
private String connectorCode; private String connectorCode;
/**
* 交易流水号
*/
private String transactionCode;
} }

View File

@@ -1,10 +1,23 @@
package com.jsowell.pile.service; package com.jsowell.pile.service;
import com.jsowell.pile.domain.ykcCommond.StartChargingCommand;
import com.jsowell.pile.domain.ykcCommond.StopChargingCommand;
/** /**
* 电单车发送命令服务 * 电单车发送命令服务
*/ */
public interface EBikeSendCommandService { public interface EBikeSendCommandService {
// void send(String pileSn, AbsEBikeMessage msg);
void startCharging(String pileSn, String connectorCode); /**
* 启动充电
* @param command
*/
void sendStartChargingCommand(StartChargingCommand command);
/**
* 停止充电
* @param command
*/
void sendStopChargingCommand(StopChargingCommand command);
} }

View File

@@ -3,9 +3,12 @@ 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.common.util.RandomUtil;
import com.jsowell.common.util.StringUtils;
import com.jsowell.common.util.id.IdUtils; import com.jsowell.common.util.id.IdUtils;
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.domain.ebike.serversend.EBikeMessageCmd82;
import com.jsowell.pile.domain.ykcCommond.StartChargingCommand;
import com.jsowell.pile.domain.ykcCommond.StopChargingCommand;
import com.jsowell.pile.service.EBikeSendCommandService; import com.jsowell.pile.service.EBikeSendCommandService;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
@@ -20,15 +23,23 @@ import java.util.Objects;
@Service @Service
public class EBikeSendCommandServiceImpl implements EBikeSendCommandService { public class EBikeSendCommandServiceImpl implements EBikeSendCommandService {
// 过载功率, 单位0.1W, 2450 * 0.1 = 2450W
private final int OVER_LOAD_POWER = 2450;
/** /**
* 电单车发送启动充电指令 * 电单车发送启动充电指令
* @param pileSn 电单车桩号
* @param connectorCode 枪口号
*/ */
@Override @Override
public void startCharging(String pileSn, String connectorCode) { public void sendStartChargingCommand(StartChargingCommand command) {
String pileSn = command.getPileSn();
String connectorCode = command.getConnectorCode();
String transactionCode = command.getTransactionCode();
if (StringUtils.isBlank(transactionCode)) {
transactionCode = IdUtils.generateTransactionCode(pileSn, connectorCode);
}
// 组装参数
EBikeMessageCmd82 message = new EBikeMessageCmd82(); EBikeMessageCmd82 message = new EBikeMessageCmd82();
message.setHeader("DNY");
message.setPhysicalId(Integer.parseInt(pileSn)); message.setPhysicalId(Integer.parseInt(pileSn));
message.setMessageId(RandomUtil.getRandomNumber(4)); message.setMessageId(RandomUtil.getRandomNumber(4));
message.setCommand("82"); message.setCommand("82");
@@ -44,16 +55,63 @@ public class EBikeSendCommandServiceImpl implements EBikeSendCommandService {
data.setChargeCommand(1); data.setChargeCommand(1);
// 充电时长/功率 // 充电时长/功率
int chargeDurationOrPower = 0; int chargeDurationOrPower = 0;
data.setChargeDurationOrPower(chargeDurationOrPower); data.setChargeTimeOrPower(chargeDurationOrPower);
// 订单编号 // 订单编号
String transactionCode = IdUtils.generateTransactionCode(pileSn, connectorCode);
data.setTransactionCode(transactionCode); data.setTransactionCode(transactionCode);
// 最大充电时长 // 最大充电时长
data.setMaxChargeDuration(0); data.setMaxChargeTime(0);
// 过载功率 // 过载功率
data.setOverloadPower(3000); data.setOverloadPower(OVER_LOAD_POWER);
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 command
*/
@Override
public void sendStopChargingCommand(StopChargingCommand command) {
String pileSn = command.getPileSn();
String connectorCode = command.getConnectorCode();
String transactionCode = command.getTransactionCode();
// 组装参数
EBikeMessageCmd82 message = new EBikeMessageCmd82();
message.setPhysicalId(Integer.parseInt(pileSn));
message.setMessageId(RandomUtil.getRandomNumber(4));
message.setCommand("82");
EBikeMessageCmd82.SpecificData data = new EBikeMessageCmd82.SpecificData();
// 充电模式
data.setRateMode(3);
// 余额或有效期
data.setBalanceOrValidity(1234);
// 端口号
data.setPortNumber(Integer.parseInt(connectorCode));
// 充电命令
data.setChargeCommand(0);
// 充电时长/功率
int chargeDurationOrPower = 0;
data.setChargeTimeOrPower(chargeDurationOrPower);
// 订单编号
data.setTransactionCode(transactionCode);
// 最大充电时长
data.setMaxChargeTime(0);
// 过载功率
data.setOverloadPower(OVER_LOAD_POWER);
data.setQrCodeLight(0); data.setQrCodeLight(0);
data.setLongChargeMode(0); data.setLongChargeMode(0);
data.setExtraFloatChargeTime(0); data.setExtraFloatChargeTime(0);

View File

@@ -281,6 +281,7 @@ public class YKCPushCommandServiceImpl implements YKCPushCommandService {
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
log.info("【=====平台下发充电指令=====】:订单id:{}, 桩号:{}, 枪口号:{}, 逻辑卡号:{}, 物理卡号:{}, 账户余额:{}", log.info("【=====平台下发充电指令=====】:订单id:{}, 桩号:{}, 枪口号:{}, 逻辑卡号:{}, 物理卡号:{}, 账户余额:{}",
transactionCode, pileSn, BytesUtil.bcd2Str(connectorCodeByteArr), logicCardNum, physicsCardNum, chargeAmount); transactionCode, pileSn, BytesUtil.bcd2Str(connectorCodeByteArr), logicCardNum, physicsCardNum, chargeAmount);
} }