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.core.domain.ykc.YKCDataProtocol; import com.jsowell.common.core.redis.RedisCache; 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.pile.domain.PileBillingTemplate; 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 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.Date; import java.util.List; import java.util.Objects; @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 EBikeSendCommandService eBikeSendCommandService; @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; } log.info("【=====平台下发指令=====】: 远程启动充电, 桩号:{}, 枪口号:{}", pileSn, connectorCode); StartChargingCommand startChargingCommand = StartChargingCommand.builder() .pileSn(pileSn) .connectorCode(connectorCode) .transactionCode(transactionCode) .chargeAmount(chargeAmount) .build(); ykcPushCommandService.pushStartChargingCommand(startChargingCommand); } /** * 电单车远程启动充电 */ public void remoteStartChargingEBike(String pileSn, String connectorCode, String transactionCode, BigDecimal chargeAmount) { if (StringUtils.isEmpty(pileSn) || StringUtils.isEmpty(connectorCode)) { log.warn("远程启动充电, 充电桩编号和枪口号不能为空"); return; } log.info("【=====平台下发指令=====】: 电单车远程启动充电, 桩号:{}, 枪口号:{}", pileSn, connectorCode); StartChargingCommand startChargingCommand = StartChargingCommand.builder() .pileSn(pileSn) .connectorCode(connectorCode) .transactionCode(transactionCode) .chargeAmount(chargeAmount) .build(); try { ChargingOperationResponse startChargingResponse = eBikeSendCommandService.sendStartChargingCommand(startChargingCommand); log.info("StartChargingResponse:{}", JSON.toJSONString(startChargingResponse)); } 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; } StopChargingCommand command = StopChargingCommand.builder() .pileSn(pileSn) .connectorCode(connectorCode) .build(); ykcPushCommandService.pushStopChargingCommand(command); log.info("remoteStopCharging success, pileConnectorCode:{}, transactionCode:{}", pileSn + connectorCode, transactionCode); } /** * 下发充电桩二维码 * * @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) { 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; } // 更新计费模板的发布时间 PileBillingTemplate pileBillingTemplate = new PileBillingTemplate(); pileBillingTemplate.setId(Long.valueOf(billingTemplateVO.getTemplateId())); pileBillingTemplate.setPublishTime(new Date()); pileBillingTemplateService.updatePileBillingTemplate(pileBillingTemplate); // 获取到站点下所有的桩, 下发计费模板, 电单车不支持 if (StringUtils.equals(billingTemplateVO.getDeviceType(), "1")) { List pileList = pileBasicInfoService.selectPileListByStationIds(Lists.newArrayList(Long.valueOf(dto.getStationId()))); if (CollectionUtils.isNotEmpty(pileList)) { for (PileDetailVO pileInfoVO : pileList) { // 下发计费模板 publishPileBillingTemplate(pileInfoVO.getPileSn(), billingTemplateVO); } } } // 修改计费模板状态 pileBillingTemplateService.changeStationTemplate(dto.getStationId(), dto.getTemplateId(), billingTemplateVO.getDeviceType()); 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); } /** * 远程账户余额更新 */ 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) { String result = "1"; byte[] bytes = ykcPushCommandService.pushReservationChargingCommand(command); // 解析结果 if (Objects.isNull(bytes)) { result = "0"; } 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)); log.info("0x59预约充电响应sync, 交易流水号:{}, 桩SN:{}, 枪口号:{}, 结果(00-失败; 01成功):{}, 失败原因:{}", transactionCode, pileSn, connectorCode, resultCode, failedReasonMsg); } return result; } }