Files
jsowell-charger-web/jsowell-pile/src/main/java/com/jsowell/pile/service/PileRemoteService.java
2026-06-26 14:55:07 +08:00

717 lines
27 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.jsowell.pile.service;
import com.alibaba.fastjson2.JSON;
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;
import com.jsowell.common.enums.ykc.ReturnCodeEnum;
import com.jsowell.common.exception.BusinessException;
import com.jsowell.common.util.BytesUtil;
import com.jsowell.common.util.StringUtils;
import com.jsowell.common.util.YKCUtils;
import com.jsowell.pile.domain.PileBillingTemplate;
import com.jsowell.pile.domain.PileConnectorInfo;
import com.jsowell.pile.domain.PileFirmwareInfo;
import com.jsowell.pile.domain.ebike.deviceupload.ChargingOperationResponse;
import com.jsowell.pile.domain.ykcCommond.*;
import com.jsowell.pile.dto.PublishBillingTemplateDTO;
import com.jsowell.pile.dto.RemoteAccountBalanceUpdateDTO;
import com.jsowell.pile.dto.UpdateFirmwareDTO;
import com.jsowell.pile.vo.web.BillingTemplateVO;
import com.jsowell.pile.vo.web.PileDetailVO;
import com.jsowell.wxpay.service.WxAppletRemoteService;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Service
public class PileRemoteService {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
private PileBillingTemplateService pileBillingTemplateService;
@Autowired
private PileBasicInfoService pileBasicInfoService;
@Autowired
private YKCPushCommandService ykcPushCommandService;
@Autowired
private PileFirmwareInfoService pileFirmwareInfoService;
@Autowired
private RedisCache redisCache;
@Autowired
private WxAppletRemoteService wxAppletRemoteService;
@Autowired
private EBikeSendCommandService eBikeSendCommandService;
@Autowired
private OrderBasicInfoService orderBasicInfoService;
@Autowired
private PileConnectorInfoService pileConnectorInfoService;
@Value("${remoteUpdate.server}")
private String serverAddress;
@Value("${remoteUpdate.port}")
private int port;
@Value("${remoteUpdate.username}")
private String username;
@Value("${remoteUpdate.password}")
private String password;
/**
* 获取充电桩实时数据信息
*
* @param pileSn 充电桩sn
* @param connectorCode 枪口号
*/
public void getRealTimeMonitorData(String pileSn, String connectorCode) {
if (StringUtils.isNotEmpty(pileSn) || StringUtils.isNotEmpty(connectorCode)) {
GetRealTimeMonitorDataCommand command = GetRealTimeMonitorDataCommand.builder()
.pileSn(pileSn)
.connectorCode(connectorCode)
.build();
ykcPushCommandService.pushGetRealTimeMonitorDataCommand(command);
}
}
/**
* 重启指令
*
* @param pileSn 充电桩sn
*/
public void reboot(String pileSn) {
RebootCommand command = RebootCommand.builder().pileSn(pileSn).build();
ykcPushCommandService.pushRebootCommand(command);
}
/**
* 远程启动充电 0x34
*
* @param pileSn 充电桩sn
*/
public void remoteStartCharging(String pileSn, String connectorCode, String transactionCode, BigDecimal chargeAmount) {
if (StringUtils.isEmpty(pileSn) || StringUtils.isEmpty(connectorCode)) {
log.warn("远程启动充电, 充电桩编号和枪口号不能为空");
return;
}
// 发送启动充电指令前,再次下发计费模板 2025年2月6日15点01分取消启动前下发计费模板
// BillingTemplateVO billingTemplateVO = pileBillingTemplateService.selectBillingTemplateDetailByPileSn(pileSn);
// this.publishPileBillingTemplate(pileSn, billingTemplateVO);
// log.info("发送启动充电指令前,再次下发计费模板, transactionCode:{}, 计费模板:{}", transactionCode, JSON.toJSONString(billingTemplateVO));
log.info("【=====平台下发指令=====】: 远程启动充电, 桩号:{}, 枪口号:{}", pileSn, connectorCode);
StartChargingCommand startChargingCommand = StartChargingCommand.builder()
.pileSn(pileSn)
.connectorCode(connectorCode)
.transactionCode(transactionCode)
.chargeAmount(chargeAmount)
.build();
ykcPushCommandService.pushStartChargingCommand(startChargingCommand);
}
/**
* 远程启动并充充电 0xA4
* @param pileSn
* @param transactionCode
* @param chargeAmount
*/
public void remoteStartMergeCharging(String pileSn, String connectorCode, String transactionCode,
BigDecimal chargeAmount, String mergeChargeCode) {
if (StringUtils.isEmpty(pileSn) || StringUtils.isEmpty(connectorCode)) {
log.warn("远程启动并充充电, 充电桩编号、枪口号不能为空");
return;
}
// 确定主枪
String mainPileConnectorCode = pileSn + connectorCode;
// 查询该桩下所有的枪口列表
List<PileConnectorInfo> pileConnectorInfos = pileConnectorInfoService.selectPileConnectorInfoList(pileSn);
log.info("【=====平台下发指令=====】: 远程启动并充充电, 桩号:{}, 枪口号:{}", pileSn, connectorCode);
if (CollectionUtils.isEmpty(pileConnectorInfos)) {
log.info("远程启动并充充电, 枪口列表为空");
return;
}
// 筛选出枪口号,与传来的枪口号对比,将传来的枪口号作为主枪排在首位
List<String> connectorCodeList = pileConnectorInfos.stream()
.map(PileConnectorInfo::getPileConnectorCode)
.collect(Collectors.toList())
.stream()
.sorted(Comparator.comparing(x -> !x.equals(mainPileConnectorCode)))
.collect(Collectors.toList());
for (String pileConnectorCode : connectorCodeList) {
// 获取枪口号
String gunCode = YKCUtils.getConnectorCode(pileConnectorCode);
// 两把枪都要下发启动充电指令
StartMergeChargeCommand command = StartMergeChargeCommand.builder()
.pileSn(pileSn)
.connectorCode(gunCode)
// .logicCardCode()
.transactionCode(transactionCode)
// .physicsCardCode()
.accountAmount(chargeAmount)
.mergeChargeCode(mergeChargeCode)
.build();
ykcPushCommandService.pushStartMergeChargingCommand(command);
}
}
/**
* 电单车远程启动充电
*/
public void remoteStartChargingEBike(String pileSn, String connectorCode, String transactionCode, BigDecimal chargeAmount) {
if (StringUtils.isEmpty(pileSn) || StringUtils.isEmpty(connectorCode)) {
log.error("电单车远程启动充电, 充电桩编号和枪口号不能为空");
return;
}
PileDetailVO pileDetailVO = pileBasicInfoService.selectPileDetailByPileSn(pileSn);
if (pileDetailVO == null) {
log.error("电单车远程启动充电, 充电桩:{}查询不能为空", pileSn);
return;
}
BillingTemplateVO billingTemplateVO = pileBillingTemplateService.queryUsedBillingTemplateForEBike(pileDetailVO.getStationId());
if (billingTemplateVO == null) {
log.error("电单车远程启动充电, 站点id:{}, 充电桩:{}, 没有配置计费模板", pileDetailVO.getStationId(), pileSn);
return;
}
log.info("【=====平台下发指令=====】: 电单车远程启动充电, 桩号:{}, 枪口号:{}", pileSn, connectorCode);
EBikeStartChargingCommand startChargingCommand = new EBikeStartChargingCommand();
startChargingCommand.setPileSn(pileSn);
startChargingCommand.setConnectorCode(connectorCode);
startChargingCommand.setTransactionCode(transactionCode);
startChargingCommand.setChargeAmount(chargeAmount); // 支付金额
startChargingCommand.setRateMode(2);
// 根据启动金额计算电量
BigDecimal flatElectricityPrice = billingTemplateVO.getFlatElectricityPrice();
BigDecimal electricity = BigDecimal.ZERO;
if (flatElectricityPrice.compareTo(BigDecimal.ZERO) > 0) {
electricity = chargeAmount.divide(flatElectricityPrice, 2, BigDecimal.ROUND_HALF_UP);
}
startChargingCommand.setElectricity(electricity);
try {
ChargingOperationResponse startChargingResponse = eBikeSendCommandService.sendStartChargingCommand(startChargingCommand);
log.info("StartChargingResponse:{}", JSON.toJSONString(startChargingResponse));
if (startChargingResponse != null) {
int result = startChargingResponse.getResult();
if (result == 0) {
// 启动成功
orderBasicInfoService.chargingPileStartedSuccessfully(transactionCode);
} else {
String failedReasonMsg = EBikeChargeResponseEnum.getDescriptionByCode(result);
// 启动失败 682204000001000000000041
orderBasicInfoService.chargingPileFailedToStart(transactionCode, failedReasonMsg);
}
}
} catch (Exception e) {
log.error("电单车远程启动充电error", e);
}
}
/**
* 远程停止充电
*/
public void remoteStopCharging(String pileSn, String connectorCode, String transactionCode) {
// 获取充电桩0x13数据校验交易流水号是否一致
String pileIsChargingKey = CacheConstants.PILE_IS_CHARGING + pileSn + connectorCode;
String redisResult = redisCache.getCacheObject(pileIsChargingKey);
if (StringUtils.isNotBlank(redisResult) && !StringUtils.equals(redisResult, transactionCode)) {
log.info("发送远程停止充电指令-充电桩枪口编号:{}, 获取到正在充电中的交易流水号:{}, 与入参交易流水号:{}不一致, function return", pileSn + connectorCode, redisResult, transactionCode);
return;
}
// 查询桩信息
PileDetailVO pileDetailVO = pileBasicInfoService.selectPileDetailByPileSn(pileSn);
if (pileDetailVO == null) {
return;
}
StopChargingCommand command = StopChargingCommand.builder()
.pileSn(pileSn)
.connectorCode(connectorCode)
.transactionCode(transactionCode)
.build();
String chargePortType = pileDetailVO.getChargePortType();
if (StringUtils.equals(chargePortType, Constants.THREE)) {
// 发送电动自行车桩停止充电指令
log.info("发送电单车桩停止充电指令, chargePortType:{}", chargePortType);
eBikeSendCommandService.sendStopChargingCommand(command);
} else {
// 发送电动汽车桩停止充电指令
log.info("发送汽车桩停止充电指令, chargePortType:{}", chargePortType);
ykcPushCommandService.pushStopChargingCommand(command);
}
log.info("remoteStopCharging success, pileConnectorCode:{}, transactionCode:{}", pileSn + connectorCode, transactionCode);
}
public void remoteStopChargingForEBike(String pileSn, String connectorCode, String transactionCode) {
StopChargingCommand command = StopChargingCommand.builder()
.pileSn(pileSn)
.connectorCode(connectorCode)
.transactionCode(transactionCode)
.build();
try {
eBikeSendCommandService.sendStopChargingCommand(command);
} catch (Exception e) {
log.error("remoteStopChargingForEBike error, pileSn:{}, connectorCode:{}, transactionCode:{}"
,pileSn, connectorCode, transactionCode, e);
}
}
/**
* 下发充电桩二维码
*
* @param pileSn 充电桩sn
*/
public void issueQRCode(String pileSn) {
issueQRCode(pileSn, null);
}
public void issueQRCode(String pileSn, String qrcodePrefix) {
IssueQRCodeCommand command = IssueQRCodeCommand.builder().pileSn(pileSn).build();
if (StringUtils.isNotBlank(qrcodePrefix)) {
command.setQrcodePrefix(qrcodePrefix);
}
ykcPushCommandService.pushIssueQRCodeCommand(command);
}
/**
* 充电桩对时
*
* @param pileSn 充电桩sn
*/
public void proofreadTime(String pileSn) {
ProofreadTimeCommand command = ProofreadTimeCommand.builder().pileSn(pileSn).build();
ykcPushCommandService.pushProofreadTimeCommand(command);
}
/**
* 下发充电桩计费模型
*/
public void publishPileBillingTemplate(String pileSn, BillingTemplateVO billingTemplateVO) {
log.info("下发充电桩计费模型, pileSn:{}, threadName:{}", pileSn, Thread.currentThread().getName());
PublishPileBillingTemplateCommand command = PublishPileBillingTemplateCommand.builder()
.billingTemplateVO(billingTemplateVO)
.pileSn(pileSn)
.build();
ykcPushCommandService.pushPublishPileBillingTemplate(command);
}
/**
* 下发计费模板
* @param dto
* @return
*/
// public boolean publishBillingTemplateOld(PublishBillingTemplateDTO dto) {
// // 获取计费模板信息
// BillingTemplateVO billingTemplateVO = pileBillingTemplateService.selectBillingTemplateByTemplateId(dto.getTemplateId());
// if (billingTemplateVO == null) {
// log.warn("获取计费模板信息, 通过模板id:{}查询计费模板为null", dto.getTemplateId());
// return false;
// }
// // 会员优惠计费模板不发布
// if (Constants.ONE.equals(billingTemplateVO.getMemberFlag())) {
// return false;
// }
// // 电单车计费模板不发布
// if (Constants.TWO.equals(billingTemplateVO.getDeviceType())) {
// return false;
// }
// // 更新计费模板的发布时间
// PileBillingTemplate pileBillingTemplate = new PileBillingTemplate();
// pileBillingTemplate.setId(Long.valueOf(billingTemplateVO.getTemplateId()));
// pileBillingTemplate.setPublishTime(new Date());
// pileBillingTemplateService.updatePileBillingTemplate(pileBillingTemplate);
// // 获取到站点下所有的桩
// List<PileDetailVO> pileList = pileBasicInfoService.selectPileListByStationIds(Lists.newArrayList(Long.valueOf(dto.getStationId())));
// if (CollectionUtils.isNotEmpty(pileList)) {
// // 删除缓存
// List<String> collect = pileList.parallelStream()
// .map(vo -> CacheConstants.BILLING_TEMPLATE_BY_PILE_SN + vo.getPileSn())
// .collect(Collectors.toList());
// redisCache.deleteObject(collect);
// // 下发计费模板, 电单车不支持
// if (StringUtils.equals(billingTemplateVO.getDeviceType(), Constants.ONE)) {
// // 下发指令
// pileList.parallelStream().forEach(pileInfoVO -> publishPileBillingTemplate(pileInfoVO.getPileSn(), billingTemplateVO));
// }
// }
// // 修改计费模板状态
// pileBillingTemplateService.changeStationTemplate(dto.getStationId(), dto.getTemplateId(), billingTemplateVO.getDeviceType(), billingTemplateVO.getMemberFlag());
// return true;
// }
/**
* 下发计费模板
* @param dto
* @return
*/
public boolean publishBillingTemplate(PublishBillingTemplateDTO dto) {
// 获取计费模板信息
BillingTemplateVO billingTemplateVO = pileBillingTemplateService.selectBillingTemplateByTemplateId(dto.getTemplateId());
if (billingTemplateVO == null) {
log.warn("获取计费模板信息, 通过模板id:{}查询计费模板为null", dto.getTemplateId());
return false;
}
// 判断是否下发计费模板 默认下发
boolean sendBillingTemplateToPile = true;
// 会员优惠计费模板不下发
if (Constants.ONE.equals(billingTemplateVO.getMemberFlag())) {
sendBillingTemplateToPile = false;
}
// 电单车计费模板不下发
if (Constants.TWO.equals(billingTemplateVO.getDeviceType())) {
sendBillingTemplateToPile = false;
}
// 更新计费模板的发布时间
PileBillingTemplate pileBillingTemplate = new PileBillingTemplate();
pileBillingTemplate.setId(Long.valueOf(billingTemplateVO.getTemplateId()));
pileBillingTemplate.setPublishTime(new Date());
pileBillingTemplateService.updatePileBillingTemplate(pileBillingTemplate);
// 修改计费模板状态
pileBillingTemplateService.changeStationTemplate(dto.getStationId(), dto.getTemplateId(), billingTemplateVO.getDeviceType(), billingTemplateVO.getMemberFlag());
// 下发计费模板, 电单车不支持
if (sendBillingTemplateToPile) {
// 获取到站点下所有的桩
List<PileDetailVO> pileList = pileBasicInfoService.selectPileListByStationIds(Lists.newArrayList(Long.valueOf(dto.getStationId())));
if (CollectionUtils.isNotEmpty(pileList)) {
// 删除缓存
List<String> collect = pileList.parallelStream()
.map(vo -> CacheConstants.BILLING_TEMPLATE_BY_PILE_SN + vo.getPileSn())
.collect(Collectors.toList());
redisCache.deleteObject(collect);
// 下发计费模板指令
pileList.parallelStream().forEach(pileInfoVO -> publishPileBillingTemplate(pileInfoVO.getPileSn(), billingTemplateVO));
}
}
return true;
}
/**
* 远程更新
*/
public void updateFirmware(UpdateFirmwareDTO dto) {
PileFirmwareInfo pileFirmwareInfo = pileFirmwareInfoService.selectPileFirmwareInfoById(Long.valueOf(dto.getFirmwareId()));
if (pileFirmwareInfo == null) {
throw new BusinessException(ReturnCodeEnum.CODE_FIRMWARE_NOT_FOUND_ERROR);
}
String ip;
try {
String server = StringUtils.removeHttp(serverAddress);
ip = InetAddress.getByName(server).getHostAddress();
} catch (UnknownHostException e) {
throw new BusinessException("", "无法解析出IP");
}
UpdateFirmwareCommand command = UpdateFirmwareCommand.builder()
.pileSnList(dto.getPileSns())
.serverAddress(ip)
.port(port)
.username(username)
.password(password)
.filePath(pileFirmwareInfo.getFilePath())
.build();
ykcPushCommandService.pushUpdateFileCommand(command);
}
public static void main(String[] args) {
// //获取百度IP地址
// try {
// System.out.println("www.baidu.com的地址: "+InetAddress.getByName("www.baidu.com").getHostAddress());
// } catch (UnknownHostException e) {
// throw new RuntimeException(e);
// }
//
// //获取百度IP地址
// try {
// System.out.println("jsowell的地址: "+InetAddress.getByName("apitest.jsowellcloud.com").getHostAddress());
// } catch (UnknownHostException e) {
// throw new RuntimeException(e);
// }
//
// String serverAddress = "http://apitest.jsowellcloud.com";
// String ip;
// try {
// String server = StringUtils.removeHttp(serverAddress);;
// ip = InetAddress.getByName(server).getHostAddress();
// } catch (UnknownHostException e) {
// throw new BusinessException("", "无法解析出IP");
// }
// System.out.println("=====" + ip);
List<String> list = Lists.newArrayList("123", "321", "666", "789");
String a = "789";
System.out.println("排序前 list: " + list);
List<String> connectorCodeList = list.stream()
.sorted(Comparator.comparing(x -> !x.equals(a)))
.collect(Collectors.toList());
System.out.println("排序后 list: " + connectorCodeList);
}
/**
* 远程账户余额更新
*/
public void remoteAccountBalanceUpdate(RemoteAccountBalanceUpdateDTO dto) {
RemoteAccountBalanceUpdateCommand command = RemoteAccountBalanceUpdateCommand.builder()
.pileSn(dto.getPileSn())
.connectorCode(dto.getConnectorCode())
.accountBalance(dto.getAccountBalance())
.build();
if (StringUtils.isNotBlank(dto.getLogicCard())) {
command.setLogicCard(dto.getLogicCard());
}
ykcPushCommandService.pushAccountBalanceUpdateCommand(command);
}
public void remoteControlGroundLock(RemoteControlGroundLockCommand command) {
ykcPushCommandService.pushRemoteControlGroundLock(command);
}
/**
* 预约充电指令/预约指令
* @return result: 1-成功; 0-失败
*/
public String reservationCharging(ReservationChargingCommand command) {
byte[] bytes = ykcPushCommandService.pushReservationChargingCommand(command);
return parseReservationChargingResponse(bytes);
}
/**
* 羽信预约充电指令
*
* @return result: 1-成功; 0-失败
*/
public String yuxinReservationCharging(YuxinReservationChargingCommand command) {
byte[] bytes = ykcPushCommandService.pushYuxinReservationChargingCommand(command);
return parseYuxinReservationChargingResponse(bytes);
}
private String parseYuxinReservationChargingResponse(byte[] bytes) {
String result = "0";
if (Objects.isNull(bytes)) {
log.warn("羽信预约启动充电回复为空, 原始报文:null");
return result;
}
String rawMessage = BytesUtil.bin2HexStr(bytes);
try {
YKCDataProtocol ykcDataProtocol = new YKCDataProtocol(bytes);
String responseFrameType = YKCUtils.frameType2Str(ykcDataProtocol.getFrameType());
byte[] msgBody = ykcDataProtocol.getMsgBody();
if (!StringUtils.equals(responseFrameType,
YKCUtils.frameType2Str(YKCFrameTypeCode.YUXIN_RESERVATION_CHARGING_SETUP_ANSWER_CODE.getBytes()))) {
log.warn("羽信预约启动充电回复帧类型异常, 帧类型:{}, 原始报文:{}", responseFrameType, rawMessage);
return result;
}
if (Objects.isNull(msgBody) || msgBody.length < 27) {
log.warn("羽信预约启动充电回复长度异常, 帧类型:{}, 报文体长度:{}, 原始报文:{}",
responseFrameType, Objects.isNull(msgBody) ? null : msgBody.length, rawMessage);
return result;
}
int startIndex = 0;
int length = 16;
byte[] transactionCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String transactionCode = BytesUtil.bcd2Str(transactionCodeByteArr);
startIndex += length;
length = 7;
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
startIndex += length;
length = 1;
byte[] connectorCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String connectorCode = parseYuxinConnectorCode(connectorCodeByteArr);
startIndex += length;
length = 1;
byte[] reservationTypeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String reservationType = BytesUtil.bin2HexStr(reservationTypeByteArr);
startIndex += length;
length = 1;
byte[] resultCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String resultCode = BytesUtil.bin2HexStr(resultCodeByteArr);
startIndex += length;
length = 1;
byte[] failedReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String failedReason = BytesUtil.bin2HexStr(failedReasonByteArr);
String failedReasonMsg = getYuxinFailedReasonMsg(failedReason);
if (StringUtils.equals(resultCode, "01")) {
result = "1";
}
log.info("{}羽信预约启动充电回复sync, 交易流水号:{}, 桩SN:{}, 枪口号:{}({}), 预约方式:{}({}), 设置结果:{}({}), 失败原因:{}({}), 原始报文:{}",
responseFrameType, transactionCode, pileSn, connectorCode, getYuxinConnectorCodeMsg(connectorCode),
reservationType, getYuxinReservationTypeMsg(reservationType),
resultCode, getYuxinSetupResultMsg(resultCode), failedReason, failedReasonMsg, rawMessage);
if (!StringUtils.equals(resultCode, "01")) {
log.warn("羽信预约启动充电设置失败, 交易流水号:{}, 桩SN:{}, 枪口号:{}({}), 失败原因:{}({}), 原始报文:{}",
transactionCode, pileSn, connectorCode, getYuxinConnectorCodeMsg(connectorCode),
failedReason, failedReasonMsg, rawMessage);
}
} catch (Exception e) {
log.warn("羽信预约启动充电回复解析异常, 原始报文:{}", rawMessage, e);
}
return result;
}
private String parseReservationChargingResponse(byte[] bytes) {
String result = "0";
// 解析结果
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;
// 交易流水号
byte[] transactionCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String transactionCode = BytesUtil.bcd2Str(transactionCodeByteArr);
// 桩编码
startIndex += length;
length = 7;
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
// 枪口号
startIndex += length;
length = 1;
byte[] connectorCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
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 = yuxinResponse ? BytesUtil.bin2HexStr(resultCodeByteArr) : BytesUtil.bcd2Str(resultCodeByteArr);
if (StringUtils.equals(resultCode, "01")) {
result = "1";
}
// 失败原因
startIndex += length;
length = 1;
byte[] failedReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String failedReason = yuxinResponse ? BytesUtil.bin2HexStr(failedReasonByteArr) : BytesUtil.bcd2Str(failedReasonByteArr);
String failedReasonMsg = yuxinResponse
? getYuxinFailedReasonMsg(failedReason)
: ChargingFailedReasonEnum.getMsgByCode(Integer.parseInt(failedReason, 16));
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 getYuxinConnectorCodeMsg(String connectorCode) {
if (StringUtils.equals(connectorCode, "00")) {
return "所有枪";
}
return Integer.parseInt(connectorCode) + "号枪";
}
private String getYuxinReservationTypeMsg(String reservationType) {
if (StringUtils.equals(reservationType, "00")) {
return "立即预约";
} else if (StringUtils.equals(reservationType, "01")) {
return "取消预约";
}
return reservationType;
}
private String getYuxinSetupResultMsg(String resultCode) {
if (StringUtils.equals(resultCode, "00")) {
return "失败";
} else if (StringUtils.equals(resultCode, "01")) {
return "成功";
}
return resultCode;
}
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;
}
}