mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-06-20 23:29:48 +08:00
羽信预约充电
This commit is contained in:
@@ -5,6 +5,7 @@ import com.google.common.collect.Lists;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.constant.Constants;
|
||||
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.ebike.EBikeChargeResponseEnum;
|
||||
import com.jsowell.common.enums.ykc.ChargingFailedReasonEnum;
|
||||
@@ -513,13 +514,16 @@ public class PileRemoteService {
|
||||
* @return result: 1-成功; 0-失败
|
||||
*/
|
||||
public String reservationCharging(ReservationChargingCommand command) {
|
||||
String result = "1";
|
||||
String result = "0";
|
||||
byte[] bytes = ykcPushCommandService.pushReservationChargingCommand(command);
|
||||
// 解析结果
|
||||
if (Objects.isNull(bytes)) {
|
||||
result = "0";
|
||||
} else {
|
||||
YKCDataProtocol ykcDataProtocol = new YKCDataProtocol(bytes);
|
||||
String responseFrameType = YKCUtils.frameType2Str(ykcDataProtocol.getFrameType());
|
||||
boolean yuxinResponse = StringUtils.equals(responseFrameType,
|
||||
YKCUtils.frameType2Str(YKCFrameTypeCode.YUXIN_RESERVATION_CHARGING_SETUP_ANSWER_CODE.getBytes()));
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
@@ -538,17 +542,24 @@ public class PileRemoteService {
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] connectorCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String connectorCode = BytesUtil.bcd2Str(connectorCodeByteArr);
|
||||
String connectorCode = yuxinResponse ? parseYuxinConnectorCode(connectorCodeByteArr) : BytesUtil.bcd2Str(connectorCodeByteArr);
|
||||
|
||||
String reservationType = null;
|
||||
if (yuxinResponse) {
|
||||
// 羽信0xB1在枪号后多一个预约方式字段: 0x00 立即预约, 0x01 取消预约。
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] reservationTypeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
reservationType = BytesUtil.bin2HexStr(reservationTypeByteArr);
|
||||
}
|
||||
|
||||
// 启动结果 0x00失败 0x01成功
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] resultCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String resultCode = BytesUtil.bcd2Str(resultCodeByteArr);
|
||||
String resultCode = yuxinResponse ? BytesUtil.bin2HexStr(resultCodeByteArr) : BytesUtil.bcd2Str(resultCodeByteArr);
|
||||
|
||||
if (StringUtils.equals(resultCode, "00")) {
|
||||
result = "0";
|
||||
} else {
|
||||
if (StringUtils.equals(resultCode, "01")) {
|
||||
result = "1";
|
||||
}
|
||||
|
||||
@@ -556,15 +567,38 @@ public class PileRemoteService {
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] failedReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String failedReason = BytesUtil.bcd2Str(failedReasonByteArr);
|
||||
String failedReasonMsg = ChargingFailedReasonEnum.getMsgByCode(Integer.parseInt(failedReason, 16));
|
||||
String failedReason = yuxinResponse ? BytesUtil.bin2HexStr(failedReasonByteArr) : BytesUtil.bcd2Str(failedReasonByteArr);
|
||||
String failedReasonMsg = yuxinResponse
|
||||
? getYuxinFailedReasonMsg(failedReason)
|
||||
: ChargingFailedReasonEnum.getMsgByCode(Integer.parseInt(failedReason, 16));
|
||||
|
||||
String responseFrameType = YKCUtils.frameType2Str(ykcDataProtocol.getFrameType());
|
||||
log.info("{}预约充电响应sync, 交易流水号:{}, 桩SN:{}, 枪口号:{}, 结果(00-失败; 01成功):{}, 失败原因:{}",
|
||||
responseFrameType, transactionCode, pileSn, connectorCode, resultCode, failedReasonMsg);
|
||||
if (yuxinResponse) {
|
||||
log.info("{}预约充电响应sync, 交易流水号:{}, 桩SN:{}, 枪口号:{}, 预约方式:{}, 结果(00-失败; 01成功):{}, 失败原因:{}",
|
||||
responseFrameType, transactionCode, pileSn, connectorCode, reservationType, resultCode, failedReasonMsg);
|
||||
} else {
|
||||
log.info("{}预约充电响应sync, 交易流水号:{}, 桩SN:{}, 枪口号:{}, 结果(00-失败; 01成功):{}, 失败原因:{}",
|
||||
responseFrameType, transactionCode, pileSn, connectorCode, resultCode, failedReasonMsg);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String parseYuxinConnectorCode(byte[] connectorCodeByteArr) {
|
||||
return String.format("%02d", connectorCodeByteArr[0] & 0xFF);
|
||||
}
|
||||
|
||||
private String getYuxinFailedReasonMsg(String failedReason) {
|
||||
if (StringUtils.equals(failedReason, "00")) {
|
||||
return "无";
|
||||
} else if (StringUtils.equals(failedReason, "01")) {
|
||||
return "保存失败";
|
||||
} else if (StringUtils.equals(failedReason, "02")) {
|
||||
return "系统当前时间小于主板时间";
|
||||
} else if (StringUtils.equals(failedReason, "03")) {
|
||||
return "预约时间小于当前时间";
|
||||
}
|
||||
return failedReason;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -136,56 +136,17 @@ public class PileReservationInfoServiceImpl implements PileReservationInfoServic
|
||||
*/
|
||||
@Override
|
||||
public void activateReserved(PileReservationDTO dto) {
|
||||
// 查询其他生效中的预约
|
||||
// List<PileReservationInfo> infoList = pileReservationInfoMapper.findByMemberIdAndPileSnAndStatus(dto.getMemberId(), dto.getPileSn(), "1");
|
||||
// if (CollectionUtils.isNotEmpty(infoList)) {
|
||||
// throw new BusinessException(ReturnCodeEnum.CODE_RESERVATION_ALREADY_EXISTS_ERROR);
|
||||
// }
|
||||
|
||||
PileReservationInfo pileReservationInfo = pileReservationInfoMapper.selectByPrimaryKey(Integer.parseInt(dto.getReservedId()));
|
||||
if (pileReservationInfo == null) {
|
||||
return;
|
||||
}
|
||||
// if (!StringUtils.equals(dto.getMemberId(), pileReservationInfo.getMemberId())) {
|
||||
// return;
|
||||
// }
|
||||
pileReservationInfo.setStatus(Constants.ONE);
|
||||
// 保存之前,校验时间是否重叠
|
||||
int i = saveReservation(pileReservationInfo);
|
||||
if (i > 0) {
|
||||
// 查询会员的绑定vin列表
|
||||
List<MemberPlateNumberVO> plateNumberVOList = memberPlateNumberRelationService.selectMemberPlateNumberRelation(pileReservationInfo.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("01")
|
||||
.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();
|
||||
// pileRemoteService.reservationCharging(command);
|
||||
if (!isTimeSlotAvailable(pileReservationInfo.getMemberId(), pileReservationInfo.getPileSn(),
|
||||
pileReservationInfo.getStartTime(), pileReservationInfo.getEndTime(), pileReservationInfo.getId())) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_UPDATE_RESERVED_STATUS_REFUSED);
|
||||
}
|
||||
sendReservationCommandAndAssertSuccess(pileReservationInfo, pileReservationInfo.getMemberId(), "01");
|
||||
this.insertOrUpdateSelective(pileReservationInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,48 +158,55 @@ public class PileReservationInfoServiceImpl implements PileReservationInfoServic
|
||||
if (pileReservationInfo == null) {
|
||||
return;
|
||||
}
|
||||
// if (!StringUtils.equals(dto.getMemberId(), pileReservationInfo.getMemberId())) {
|
||||
// return;
|
||||
// }
|
||||
// 校验通过可以修改预约
|
||||
pileReservationInfo.setStatus(Constants.ZERO);
|
||||
int i = pileReservationInfoMapper.updateByPrimaryKeySelective(pileReservationInfo);
|
||||
if (i > 0) {
|
||||
// 查询会员的绑定vin列表
|
||||
List<MemberPlateNumberVO> plateNumberVOList = memberPlateNumberRelationService.selectMemberPlateNumberRelation(pileReservationInfo.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("");
|
||||
}
|
||||
sendReservationCommandAndAssertSuccess(pileReservationInfo, pileReservationInfo.getMemberId(), "02");
|
||||
this.insertOrUpdateSelective(pileReservationInfo);
|
||||
}
|
||||
|
||||
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("02")
|
||||
.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();
|
||||
// pileRemoteService.reservationCharging(command);
|
||||
private void sendReservationCommandAndAssertSuccess(PileReservationInfo pileReservationInfo, String memberId, String operation) {
|
||||
ReservationChargingCommand command = buildReservationChargingCommand(pileReservationInfo, memberId, operation);
|
||||
String result = pileRemoteService.reservationCharging(command);
|
||||
if (!StringUtils.equals(result, Constants.ONE)) {
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_UPDATE_RESERVED_STATUS_ERROR.getValue(),
|
||||
ReturnCodeEnum.CODE_UPDATE_RESERVED_STATUS_ERROR.getLabel() + ": 充电桩返回修改失败");
|
||||
}
|
||||
}
|
||||
|
||||
private ReservationChargingCommand buildReservationChargingCommand(PileReservationInfo pileReservationInfo, String memberId, String operation) {
|
||||
List<String> vinCodes = getReservationVinCodes(memberId);
|
||||
String type = StringUtils.equals(pileReservationInfo.getReservationType(), "single") ? "00" : "01";
|
||||
return 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();
|
||||
}
|
||||
|
||||
private List<String> getReservationVinCodes(String memberId) {
|
||||
List<MemberPlateNumberVO> plateNumberVOList = memberPlateNumberRelationService.selectMemberPlateNumberRelation(memberId);
|
||||
List<String> vinCodes = Lists.newArrayList();
|
||||
int count = 0;
|
||||
for (MemberPlateNumberVO vo : plateNumberVOList) {
|
||||
if (count < 3 && StringUtils.isNotBlank(vo.getVinCode())) {
|
||||
vinCodes.add(vo.getVinCode());
|
||||
count++;
|
||||
}
|
||||
}
|
||||
while (vinCodes.size() < 3) {
|
||||
vinCodes.add("");
|
||||
}
|
||||
return vinCodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验时间是否重叠
|
||||
* @param memberId
|
||||
@@ -621,4 +589,3 @@ public class PileReservationInfoServiceImpl implements PileReservationInfoServic
|
||||
pileReservationInfoMapper.deleteByPileSn(pileSn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -765,6 +767,25 @@ public class YKCPushCommandServiceImpl implements YKCPushCommandService {
|
||||
*/
|
||||
@Override
|
||||
public byte[] pushReservationChargingCommand(ReservationChargingCommand command) {
|
||||
String pileSn = command.getPileSn();
|
||||
YKCFrameTypeCode frameTypeCode = getReservationChargingFrameType(pileSn);
|
||||
byte[] msg = frameTypeCode == YKCFrameTypeCode.YUXIN_RESERVATION_CHARGING_SETUP_CODE
|
||||
? buildYuxinReservationChargingMsg(command)
|
||||
: buildReservationChargingMsg(command);
|
||||
byte[] response;
|
||||
try {
|
||||
response = this.supplySend(msg, pileSn, frameTypeCode);
|
||||
} catch (Exception e) {
|
||||
log.error("发送消息异常", e);
|
||||
response = null;
|
||||
}
|
||||
|
||||
log.info("【=====平台下发指令=====】: 预约充电指令, 帧类型:{}, 交易流水号:{}, 桩编号:{}, 枪口号:{}, 操作(01-启动; 02-取消; 03-修改):{}, 身份验证:{}, 开始时间:{}, 结束时间:{}, 启动金额:{}",
|
||||
YKCUtils.frameType2Str(frameTypeCode.getBytes()), command.getTransactionCode(), pileSn, command.getConnectorCode(), command.getOperation(), command.getVerifyIdentity(), DateUtils.formatDateTime(command.getReservedStartTime()), DateUtils.formatDateTime(command.getReservedEndTime()), command.getAmount());
|
||||
return response;
|
||||
}
|
||||
|
||||
private byte[] buildReservationChargingMsg(ReservationChargingCommand command) {
|
||||
// 交易流水号
|
||||
String transactionCode = command.getTransactionCode();
|
||||
byte[] transactionCodeArr = BytesUtil.str2Bcd(transactionCode);
|
||||
@@ -815,23 +836,57 @@ public class YKCPushCommandServiceImpl implements YKCPushCommandService {
|
||||
BigDecimal amount = command.getAmount();
|
||||
byte[] amountByteArr = YKCUtils.getPriceByte(amount.toString(), 2);
|
||||
|
||||
// 拼装msg信息
|
||||
byte[] msg = Bytes.concat(transactionCodeArr, pileSnByteArr, connectorCodeByteArr, operateByteArr,
|
||||
return Bytes.concat(transactionCodeArr, pileSnByteArr, connectorCodeByteArr, operateByteArr,
|
||||
reservationTypeByteArr, verifyIdentityByteArr, vin1ByteArr, vin2ByteArr, vin3ByteArr,
|
||||
reservedStartTimeByteArr, reservedEndTimeByteArr, amountByteArr);
|
||||
}
|
||||
|
||||
YKCFrameTypeCode frameTypeCode = getReservationChargingFrameType(pileSn);
|
||||
byte[] response;
|
||||
try {
|
||||
response = this.supplySend(msg, pileSn, frameTypeCode);
|
||||
} catch (Exception e) {
|
||||
log.error("发送消息异常", e);
|
||||
response = null;
|
||||
private byte[] buildYuxinReservationChargingMsg(ReservationChargingCommand command) {
|
||||
byte[] transactionCodeArr = BytesUtil.ensureLengthPrependZero(BytesUtil.str2Bcd(command.getTransactionCode()), 16);
|
||||
byte[] pileSnByteArr = BytesUtil.ensureLengthPrependZero(BytesUtil.str2Bcd(command.getPileSn()), 7);
|
||||
byte[] connectorCodeByteArr = BytesUtil.intToBytes(parseDecimalByte(command.getConnectorCode(), "枪口号"), 1);
|
||||
byte[] accountBalanceByteArr = YKCUtils.getPriceByte(getAmount(command).toString(), 2);
|
||||
byte[] reservationTypeByteArr = BytesUtil.intToBytes(StringUtils.equals(command.getOperation(), "02") ? 0x01 : 0x00, 1);
|
||||
byte[] chargingStrategyByteArr = BytesUtil.intToBytes(0x00, 1);
|
||||
byte[] chargingParamByteArr = YKCUtils.getPriceByte(BigDecimal.ZERO.toString(), 2);
|
||||
byte[] systemTimeByteArr = Cp56Time2aUtil.date2Hbyte(new Date());
|
||||
byte[] reservedStartTimeByteArr = Cp56Time2aUtil.date2Hbyte(getYuxinReservedStartDate(command.getReservedStartTime()));
|
||||
byte[] reservationTimeoutByteArr = BytesUtil.intToBytes(getYuxinReservationTimeout(command), 1);
|
||||
|
||||
return Bytes.concat(transactionCodeArr, pileSnByteArr, connectorCodeByteArr, accountBalanceByteArr,
|
||||
reservationTypeByteArr, chargingStrategyByteArr, chargingParamByteArr, systemTimeByteArr,
|
||||
reservedStartTimeByteArr, reservationTimeoutByteArr);
|
||||
}
|
||||
|
||||
private BigDecimal getAmount(ReservationChargingCommand command) {
|
||||
return command.getAmount() == null ? BigDecimal.ZERO : command.getAmount();
|
||||
}
|
||||
|
||||
private Date getYuxinReservedStartDate(LocalTime reservedStartTime) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime reservedStartDateTime = now.toLocalDate().atTime(reservedStartTime);
|
||||
if (!reservedStartDateTime.isAfter(now)) {
|
||||
reservedStartDateTime = reservedStartDateTime.plusDays(1);
|
||||
}
|
||||
return DateUtils.localDateTime2Date(reservedStartDateTime);
|
||||
}
|
||||
|
||||
log.info("【=====平台下发指令=====】: 预约充电指令, 帧类型:{}, 交易流水号:{}, 桩编号:{}, 枪口号:{}, 操作(01-启动; 02-取消; 03-修改):{}, 身份验证:{}, 开始时间:{}, 结束时间:{}, 启动金额:{}",
|
||||
YKCUtils.frameType2Str(frameTypeCode.getBytes()), transactionCode, pileSn, connectorCode, operation, verifyIdentity, DateUtils.formatDateTime(reservedStartTime), DateUtils.formatDateTime(reservedEndTime), amount);
|
||||
return response;
|
||||
private int getYuxinReservationTimeout(ReservationChargingCommand command) {
|
||||
LocalTime reservedStartTime = command.getReservedStartTime();
|
||||
LocalTime reservedEndTime = command.getReservedEndTime();
|
||||
long timeout = Duration.between(reservedStartTime, reservedEndTime).toMinutes();
|
||||
if (timeout <= 0) {
|
||||
timeout += TimeUnit.DAYS.toMinutes(1);
|
||||
}
|
||||
return (int) Math.min(timeout, 0xFF);
|
||||
}
|
||||
|
||||
private int parseDecimalByte(String value, String fieldName) {
|
||||
int result = Integer.parseInt(value);
|
||||
if (result < 0 || result > 0xFF) {
|
||||
throw new IllegalArgumentException(fieldName + "超出1字节范围:" + value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private YKCFrameTypeCode getReservationChargingFrameType(String pileSn) {
|
||||
|
||||
Reference in New Issue
Block a user