Files
jsowell-charger-web/jsowell-pile/src/main/java/com/jsowell/pile/service/PileRemoteService.java

452 lines
16 KiB
Java
Raw Normal View History

2023-07-08 09:44:05 +08:00
package com.jsowell.pile.service;
2023-03-04 16:29:55 +08:00
2024-09-14 17:19:05 +08:00
import com.alibaba.fastjson2.JSON;
2023-03-04 16:29:55 +08:00
import com.google.common.collect.Lists;
import com.jsowell.common.constant.CacheConstants;
2024-09-20 09:00:45 +08:00
import com.jsowell.common.constant.Constants;
2024-08-01 18:03:21 +08:00
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
import com.jsowell.common.core.redis.RedisCache;
2024-09-20 08:39:45 +08:00
import com.jsowell.common.enums.ebike.EBikeChargeResponseEnum;
2024-08-01 18:03:21 +08:00
import com.jsowell.common.enums.ykc.ChargingFailedReasonEnum;
2023-06-29 16:58:29 +08:00
import com.jsowell.common.enums.ykc.ReturnCodeEnum;
import com.jsowell.common.exception.BusinessException;
2024-08-01 18:03:21 +08:00
import com.jsowell.common.util.BytesUtil;
2023-03-04 16:29:55 +08:00
import com.jsowell.common.util.StringUtils;
import com.jsowell.pile.domain.PileBillingTemplate;
2023-06-29 16:58:29 +08:00
import com.jsowell.pile.domain.PileFirmwareInfo;
2024-09-14 17:19:05 +08:00
import com.jsowell.pile.domain.ebike.deviceupload.ChargingOperationResponse;
2023-06-29 16:58:29 +08:00
import com.jsowell.pile.domain.ykcCommond.*;
2023-03-04 16:29:55 +08:00
import com.jsowell.pile.dto.PublishBillingTemplateDTO;
2023-06-29 16:58:29 +08:00
import com.jsowell.pile.dto.RemoteAccountBalanceUpdateDTO;
import com.jsowell.pile.dto.UpdateFirmwareDTO;
2023-03-04 16:29:55 +08:00
import com.jsowell.pile.vo.web.BillingTemplateVO;
2023-03-10 14:08:10 +08:00
import com.jsowell.pile.vo.web.PileDetailVO;
2024-09-20 14:45:47 +08:00
import com.jsowell.wxpay.service.WxAppletRemoteService;
2023-03-04 16:29:55 +08:00
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
2024-05-17 17:07:19 +08:00
import org.springframework.beans.factory.annotation.Value;
2023-03-04 16:29:55 +08:00
import org.springframework.stereotype.Service;
2023-03-16 15:23:01 +08:00
import java.math.BigDecimal;
2024-05-21 10:05:57 +08:00
import java.net.InetAddress;
import java.net.UnknownHostException;
2023-03-04 16:29:55 +08:00
import java.util.Date;
import java.util.List;
2024-08-01 17:34:01 +08:00
import java.util.Objects;
2024-11-21 14:48:42 +08:00
import java.util.stream.Collectors;
2023-03-04 16:29:55 +08:00
@Service
public class PileRemoteService {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
2024-01-06 15:20:28 +08:00
private PileBillingTemplateService pileBillingTemplateService;
2023-03-04 16:29:55 +08:00
@Autowired
2024-01-06 15:20:28 +08:00
private PileBasicInfoService pileBasicInfoService;
2023-03-04 16:29:55 +08:00
@Autowired
private YKCPushCommandService ykcPushCommandService;
2023-04-06 16:18:06 +08:00
@Autowired
2024-01-06 15:20:28 +08:00
private PileFirmwareInfoService pileFirmwareInfoService;
2023-06-29 16:58:29 +08:00
@Autowired
private RedisCache redisCache;
2024-05-17 17:07:19 +08:00
2024-09-20 14:45:47 +08:00
@Autowired
private WxAppletRemoteService wxAppletRemoteService;
2024-09-12 13:51:43 +08:00
@Autowired
private EBikeSendCommandService eBikeSendCommandService;
2024-09-19 14:58:48 +08:00
@Autowired
private OrderBasicInfoService orderBasicInfoService;
2024-05-17 17:07:19 +08:00
@Value("${remoteUpdate.server}")
private String serverAddress;
@Value("${remoteUpdate.port}")
private int port;
2024-05-20 11:43:00 +08:00
@Value("${remoteUpdate.username}")
private String username;
@Value("${remoteUpdate.password}")
private String password;
2023-03-04 16:29:55 +08:00
/**
* 获取充电桩实时数据信息
*
* @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
*/
2023-12-06 10:40:46 +08:00
public void remoteStartCharging(String pileSn, String connectorCode, String transactionCode, BigDecimal chargeAmount) {
2023-03-16 15:23:01 +08:00
if (StringUtils.isEmpty(pileSn) || StringUtils.isEmpty(connectorCode)) {
log.warn("远程启动充电, 充电桩编号和枪口号不能为空");
return;
}
2024-09-20 14:45:47 +08:00
// 发送启动充电指令前,再次下发计费模板 2025年2月6日15点01分取消启动前下发计费模板
// BillingTemplateVO billingTemplateVO = pileBillingTemplateService.selectBillingTemplateDetailByPileSn(pileSn);
// this.publishPileBillingTemplate(pileSn, billingTemplateVO);
// log.info("发送启动充电指令前,再次下发计费模板, transactionCode:{}, 计费模板:{}", transactionCode, JSON.toJSONString(billingTemplateVO));
2024-09-20 14:45:47 +08:00
2024-07-30 11:59:25 +08:00
log.info("【=====平台下发指令=====】: 远程启动充电, 桩号:{}, 枪口号:{}", pileSn, connectorCode);
2023-03-16 15:23:01 +08:00
StartChargingCommand startChargingCommand = StartChargingCommand.builder()
.pileSn(pileSn)
.connectorCode(connectorCode)
.transactionCode(transactionCode)
2023-12-06 10:40:46 +08:00
.chargeAmount(chargeAmount)
2023-03-16 15:23:01 +08:00
.build();
ykcPushCommandService.pushStartChargingCommand(startChargingCommand);
}
2023-03-04 16:29:55 +08:00
2024-09-12 13:51:43 +08:00
/**
* 电单车远程启动充电
*/
public void remoteStartChargingEBike(String pileSn, String connectorCode, String transactionCode, BigDecimal chargeAmount) {
if (StringUtils.isEmpty(pileSn) || StringUtils.isEmpty(connectorCode)) {
2024-09-20 14:57:46 +08:00
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);
2024-09-12 13:51:43 +08:00
return;
}
log.info("【=====平台下发指令=====】: 电单车远程启动充电, 桩号:{}, 枪口号:{}", pileSn, connectorCode);
2024-09-18 16:44:24 +08:00
EBikeStartChargingCommand startChargingCommand = new EBikeStartChargingCommand();
startChargingCommand.setPileSn(pileSn);
startChargingCommand.setConnectorCode(connectorCode);
startChargingCommand.setTransactionCode(transactionCode);
2024-09-20 14:57:46 +08:00
startChargingCommand.setChargeAmount(chargeAmount); // 支付金额
2024-09-20 14:45:47 +08:00
startChargingCommand.setRateMode(2);
// 根据启动金额计算电量
2024-09-20 14:57:46 +08:00
BigDecimal flatElectricityPrice = billingTemplateVO.getFlatElectricityPrice();
2024-09-20 14:45:47 +08:00
BigDecimal electricity = BigDecimal.ZERO;
2024-09-20 14:57:46 +08:00
if (flatElectricityPrice.compareTo(BigDecimal.ZERO) > 0) {
electricity = chargeAmount.divide(flatElectricityPrice, 2, BigDecimal.ROUND_HALF_UP);
}
2024-09-20 14:45:47 +08:00
startChargingCommand.setElectricity(electricity);
2024-09-18 16:44:24 +08:00
2024-09-14 13:56:58 +08:00
try {
2024-09-14 17:19:05 +08:00
ChargingOperationResponse startChargingResponse = eBikeSendCommandService.sendStartChargingCommand(startChargingCommand);
log.info("StartChargingResponse:{}", JSON.toJSONString(startChargingResponse));
2024-09-19 14:58:48 +08:00
if (startChargingResponse != null) {
int result = startChargingResponse.getResult();
if (result == 0) {
// 启动成功
orderBasicInfoService.chargingPileStartedSuccessfully(transactionCode);
} else {
2024-09-20 08:39:45 +08:00
String failedReasonMsg = EBikeChargeResponseEnum.getDescriptionByCode(result);
2024-09-19 14:58:48 +08:00
// 启动失败 682204000001000000000041
orderBasicInfoService.chargingPileFailedToStart(transactionCode, failedReasonMsg);
}
}
2024-09-14 13:56:58 +08:00
} catch (Exception e) {
log.error("电单车远程启动充电error", e);
}
2024-09-12 13:51:43 +08:00
}
2023-03-04 16:29:55 +08:00
/**
* 远程停止充电
*/
public void remoteStopCharging(String pileSn, String connectorCode, String transactionCode) {
// 获取充电桩0x13数据校验交易流水号是否一致
String pileIsChargingKey = CacheConstants.PILE_IS_CHARGING + pileSn + connectorCode;
String redisResult = redisCache.getCacheObject(pileIsChargingKey);
2024-02-28 14:47:47 +08:00
if (StringUtils.isNotBlank(redisResult) && !StringUtils.equals(redisResult, transactionCode)) {
2024-06-12 09:13:18 +08:00
log.info("发送远程停止充电指令-充电桩枪口编号:{}, 获取到正在充电中的交易流水号:{}, 与入参交易流水号:{}不一致, function return", pileSn + connectorCode, redisResult, transactionCode);
return;
}
2024-09-20 09:00:45 +08:00
// 查询桩信息
PileDetailVO pileDetailVO = pileBasicInfoService.selectPileDetailByPileSn(pileSn);
if (pileDetailVO == null) {
return;
}
2023-03-04 16:29:55 +08:00
StopChargingCommand command = StopChargingCommand.builder()
.pileSn(pileSn)
.connectorCode(connectorCode)
2024-09-20 09:00:45 +08:00
.transactionCode(transactionCode)
2023-03-04 16:29:55 +08:00
.build();
2024-09-20 09:00:45 +08:00
String chargePortType = pileDetailVO.getChargePortType();
if (StringUtils.equals(chargePortType, Constants.THREE)) {
// 发送电动自行车桩停止充电指令
2024-09-20 09:22:15 +08:00
log.info("发送电单车桩停止充电指令, chargePortType:{}", chargePortType);
2024-09-20 09:00:45 +08:00
eBikeSendCommandService.sendStopChargingCommand(command);
} else {
// 发送电动汽车桩停止充电指令
2024-09-20 09:22:15 +08:00
log.info("发送汽车桩停止充电指令, chargePortType:{}", chargePortType);
2024-09-20 09:00:45 +08:00
ykcPushCommandService.pushStopChargingCommand(command);
}
2024-06-12 09:13:18 +08:00
log.info("remoteStopCharging success, pileConnectorCode:{}, transactionCode:{}", pileSn + connectorCode, transactionCode);
2023-03-04 16:29:55 +08:00
}
2024-09-20 09:00:45 +08:00
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);
}
}
2023-03-04 16:29:55 +08:00
/**
* 下发充电桩二维码
*
* @param pileSn 充电桩sn
*/
public void issueQRCode(String pileSn) {
2023-04-07 09:18:31 +08:00
issueQRCode(pileSn, null);
}
public void issueQRCode(String pileSn, String qrcodePrefix) {
2023-03-04 16:29:55 +08:00
IssueQRCodeCommand command = IssueQRCodeCommand.builder().pileSn(pileSn).build();
2023-04-07 09:18:31 +08:00
if (StringUtils.isNotBlank(qrcodePrefix)) {
command.setQrcodePrefix(qrcodePrefix);
2023-04-06 16:18:06 +08:00
}
2023-04-07 08:23:04 +08:00
ykcPushCommandService.pushIssueQRCodeCommand(command);
2023-03-04 16:29:55 +08:00
}
/**
* 充电桩对时
*
* @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) {
2024-11-04 15:13:40 +08:00
log.info("下发充电桩计费模型, pileSn:{}, threadName:{}", pileSn, Thread.currentThread().getName());
2023-03-04 16:29:55 +08:00
PublishPileBillingTemplateCommand command = PublishPileBillingTemplateCommand.builder()
.billingTemplateVO(billingTemplateVO)
.pileSn(pileSn)
.build();
ykcPushCommandService.pushPublishPileBillingTemplate(command);
}
/**
* 下发计费模板
* @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;
}
2025-03-25 16:25:15 +08:00
// 会员优惠计费模板不发布
if (Constants.ONE.equals(billingTemplateVO.getMemberFlag())) {
return false;
}
// 电单车计费模板不发布
if (Constants.TWO.equals(billingTemplateVO.getDeviceType())) {
return false;
}
2023-03-04 16:29:55 +08:00
// 更新计费模板的发布时间
PileBillingTemplate pileBillingTemplate = new PileBillingTemplate();
pileBillingTemplate.setId(Long.valueOf(billingTemplateVO.getTemplateId()));
pileBillingTemplate.setPublishTime(new Date());
pileBillingTemplateService.updatePileBillingTemplate(pileBillingTemplate);
2024-11-21 14:48:42 +08:00
// 获取到站点下所有的桩
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)) {
2024-11-21 14:37:19 +08:00
// 下发指令
2024-11-04 15:13:40 +08:00
pileList.parallelStream().forEach(pileInfoVO -> publishPileBillingTemplate(pileInfoVO.getPileSn(), billingTemplateVO));
2024-09-04 14:27:15 +08:00
}
2023-03-04 16:29:55 +08:00
}
2023-06-27 11:11:43 +08:00
// 修改计费模板状态
pileBillingTemplateService.changeStationTemplate(dto.getStationId(), dto.getTemplateId(), billingTemplateVO.getDeviceType(), billingTemplateVO.getMemberFlag());
2023-03-04 16:29:55 +08:00
return true;
}
/**
* 远程更新
*/
2023-06-27 09:14:30 +08:00
public void updateFirmware(UpdateFirmwareDTO dto) {
2023-06-29 16:58:29 +08:00
PileFirmwareInfo pileFirmwareInfo = pileFirmwareInfoService.selectPileFirmwareInfoById(Long.valueOf(dto.getFirmwareId()));
if (pileFirmwareInfo == null) {
throw new BusinessException(ReturnCodeEnum.CODE_FIRMWARE_NOT_FOUND_ERROR);
}
2024-05-21 10:05:57 +08:00
String ip;
try {
2024-05-21 15:32:40 +08:00
String server = StringUtils.removeHttp(serverAddress);
ip = InetAddress.getByName(server).getHostAddress();
2024-05-21 10:05:57 +08:00
} catch (UnknownHostException e) {
throw new BusinessException("", "无法解析出IP");
}
2023-06-27 09:14:30 +08:00
UpdateFirmwareCommand command = UpdateFirmwareCommand.builder()
.pileSnList(dto.getPileSns())
2024-05-21 10:05:57 +08:00
.serverAddress(ip)
2024-05-17 17:07:19 +08:00
.port(port)
2024-05-20 11:43:00 +08:00
.username(username)
.password(password)
2023-06-29 16:58:29 +08:00
.filePath(pileFirmwareInfo.getFilePath())
2023-06-27 09:14:30 +08:00
.build();
2023-03-04 16:29:55 +08:00
ykcPushCommandService.pushUpdateFileCommand(command);
}
2024-05-21 15:32:40 +08:00
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);
}
/**
* 远程账户余额更新
*/
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);
}
2023-08-02 10:35:13 +08:00
public void remoteControlGroundLock(RemoteControlGroundLockCommand command) {
ykcPushCommandService.pushRemoteControlGroundLock(command);
}
2024-05-20 16:51:11 +08:00
/**
2024-07-02 15:24:36 +08:00
* 预约充电指令/预约指令
2024-08-01 17:34:01 +08:00
* @return result: 1-成功; 0-失败
2024-05-20 16:51:11 +08:00
*/
2024-08-01 17:34:01 +08:00
public String reservationCharging(ReservationChargingCommand command) {
String result = "1";
byte[] bytes = ykcPushCommandService.pushReservationChargingCommand(command);
// 解析结果
if (Objects.isNull(bytes)) {
result = "0";
2024-08-01 18:03:21 +08:00
} else {
YKCDataProtocol ykcDataProtocol = new YKCDataProtocol(bytes);
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 = BytesUtil.bcd2Str(connectorCodeByteArr);
// 启动结果 0x00失败 0x01成功
startIndex += length;
length = 1;
byte[] resultCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String resultCode = BytesUtil.bcd2Str(resultCodeByteArr);
if (StringUtils.equals(resultCode, "00")) {
result = "0";
} else {
result = "1";
}
// 失败原因
startIndex += length;
length = 1;
byte[] failedReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
String failedReason = BytesUtil.bcd2Str(failedReasonByteArr);
String failedReasonMsg = ChargingFailedReasonEnum.getMsgByCode(Integer.parseInt(failedReason, 16));
2024-08-28 14:00:20 +08:00
log.info("0x59预约充电响应sync, 交易流水号:{}, 桩SN:{}, 枪口号:{}, 结果(00-失败; 01成功):{}, 失败原因:{}",
2024-08-01 18:03:21 +08:00
transactionCode, pileSn, connectorCode, resultCode, failedReasonMsg);
2024-08-01 17:34:01 +08:00
}
return result;
2024-05-20 16:51:11 +08:00
}
2024-08-01 15:13:14 +08:00
2023-03-04 16:29:55 +08:00
}