mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-04-20 02:55:04 +08:00
update 代码重构
This commit is contained in:
@@ -1,13 +1,10 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.core.redis.StaticRedisCache;
|
||||
import com.jsowell.common.enums.ykc.PileChannelEntity;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.CRC16Util;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -28,30 +25,30 @@ public interface AbstractYkcStrategy {
|
||||
* @param messageBody 消息体
|
||||
* @return 应答结果
|
||||
*/
|
||||
default byte[] getResult(YKCDataProtocol ykcDataProtocol, byte[] messageBody) {
|
||||
// 起始标志
|
||||
byte[] head = ykcDataProtocol.getHead();
|
||||
|
||||
// 序列号域
|
||||
byte[] serialNumber = ykcDataProtocol.getSerialNumber();
|
||||
|
||||
// 加密标志
|
||||
byte[] encryptFlag = ykcDataProtocol.getEncryptFlag();
|
||||
|
||||
// 请求帧类型
|
||||
byte[] requestFrameType = ykcDataProtocol.getFrameType();
|
||||
|
||||
// 应答帧类型
|
||||
byte[] responseFrameType = YKCFrameTypeCode.PlatformAnswersRelation.getResponseFrameTypeBytes(requestFrameType);
|
||||
|
||||
// 数据域 值为“序列号域+加密标志+帧类型标志+消息体”字节数之和
|
||||
byte[] dataFields = Bytes.concat(serialNumber, encryptFlag, responseFrameType, messageBody);
|
||||
|
||||
// 计算crc: 从序列号域到数据域的 CRC 校验
|
||||
int crc16 = CRC16Util.calcCrc16(dataFields);
|
||||
|
||||
return Bytes.concat(head, BytesUtil.intToBytes(dataFields.length, 1), dataFields, BytesUtil.intToBytes(crc16));
|
||||
}
|
||||
// default byte[] getResult(YKCDataProtocol ykcDataProtocol, byte[] messageBody) {
|
||||
// // 起始标志
|
||||
// byte[] head = ykcDataProtocol.getHead();
|
||||
//
|
||||
// // 序列号域
|
||||
// byte[] serialNumber = ykcDataProtocol.getSerialNumber();
|
||||
//
|
||||
// // 加密标志
|
||||
// byte[] encryptFlag = ykcDataProtocol.getEncryptFlag();
|
||||
//
|
||||
// // 请求帧类型
|
||||
// byte[] requestFrameType = ykcDataProtocol.getFrameType();
|
||||
//
|
||||
// // 应答帧类型
|
||||
// byte[] responseFrameType = YKCFrameTypeCode.PlatformAnswersRelation.getResponseFrameTypeBytes(requestFrameType);
|
||||
//
|
||||
// // 数据域 值为“序列号域+加密标志+帧类型标志+消息体”字节数之和
|
||||
// byte[] dataFields = Bytes.concat(serialNumber, encryptFlag, responseFrameType, messageBody);
|
||||
//
|
||||
// // 计算crc: 从序列号域到数据域的 CRC 校验
|
||||
// int crc16 = CRC16Util.calcCrc16(dataFields);
|
||||
//
|
||||
// return Bytes.concat(head, BytesUtil.intToBytes(dataFields.length, 1), dataFields, BytesUtil.intToBytes(crc16));
|
||||
// }
|
||||
|
||||
/**
|
||||
* 保存桩最后链接到平台的时间
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 充电阶段 BMS 中止Handler
|
||||
*
|
||||
* GBT-27930 充电桩与 BMS 充电阶段 BMS 中止报文
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 13:32
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BMSAbortDuringChargingStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.BMS_ABORT_DURING_CHARGING_PHASE_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===充电阶段 BMS 中止===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 获取消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
|
||||
// 交易流水号
|
||||
byte[] serialNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String orderCode = BytesUtil.bcd2Str(serialNumByteArr);
|
||||
|
||||
// 桩编码
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] pileConnectorNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String connectorCode = BytesUtil.bcd2Str(pileConnectorNumByteArr);
|
||||
|
||||
/**
|
||||
* BMS 中止充电原因
|
||||
* 1-2 位——所需求的 SOC 目标值
|
||||
* 3-4 位——达到总电压的设定值
|
||||
* 5-6 位——达到单体电压设定值
|
||||
* 7-8 位——充电机主动中止
|
||||
*/
|
||||
startIndex += length;
|
||||
byte[] BMSStopReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
|
||||
/**
|
||||
* BMS 中止充电故障原因
|
||||
* 1-2 位——绝缘故障
|
||||
* 3-4 位——输出连接器过温故障
|
||||
* 5-6 位——BMS 元件、输出连接 器过温
|
||||
* 7-8 位——充电连接器故障
|
||||
* 9-10 位——电池组温度过高故障
|
||||
* 11-12 位——高压继电器故障
|
||||
* 13-14 位——检测点 2 电压检测故障
|
||||
* 15-16 位——其他故障
|
||||
*/
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] BMSStopFaultReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
/**
|
||||
* BMS 中止充电错误原因
|
||||
* 1-2 位——电流过大
|
||||
* 3-4 位——电压异常
|
||||
* 5-8 位——预留位
|
||||
*/
|
||||
startIndex += length;
|
||||
byte[] BMSStopErrorReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 充电过程 BMS 需求与充电机输出 0x23
|
||||
*
|
||||
* GBT-27930 充电桩与 BMS 充电过程 BMS 需求、充电机输出
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 13:51
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BMSDemandAndChargerOutputStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.CHARGING_PROCESS_BMS_DEMAND_AND_CHARGER_OUTPUT_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===充电过程 BMS 需求与充电机输出===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 获取消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
|
||||
// 交易流水号
|
||||
byte[] serialNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 桩编码
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] pileConnectorNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 电压需求 0.1 V/位, 0 V 偏移量
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] bmsVoltageDemandByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 电流需求 0.1 A/位, -400 A 偏移量
|
||||
startIndex += length;
|
||||
byte[] bmsCurrentDemandByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 充电模式 0x01:恒压充电; 0x02:恒流充电
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] bmsChargingModelByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 充电电压测量值 0.1 V/位, 0 V 偏移量
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] bmsChargingVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 充电电流测量值 0.1 A/位, -400 A 偏移量
|
||||
startIndex += length;
|
||||
byte[] bmsChargingCurrentByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 最高单体动力蓄电池电压及组号 1-12 位:最高单体动力蓄电池电压, 数据分辨率: 0.01 V/位, 0 V 偏移量;数据范围: 0~24 V; 13-16 位: 最高单体动力蓄电池电 压所在组号,数据分辨率: 1/位, 0 偏移量;数据范围: 0~15
|
||||
startIndex += length;
|
||||
byte[] bmsMaxVoltageAndGroupNum = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 当前荷电状态 SOC( %) 1%/位, 0%偏移量; 数据范围: 0~100%
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] socByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 估算剩余充电时间 1 min/位, 0 min 偏移量; 数据范围: 0~600 min
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] bmsTheRestChargingTimeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 电桩电压输出值 0.1 V/位, 0 V 偏移量
|
||||
startIndex += length;
|
||||
byte[] pileVoltageOutputByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 电桩电流输出值 0.1 A/位, -400 A 偏移量
|
||||
startIndex += length;
|
||||
byte[] pileCurrentOutputByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 累计充电时间 1 min/位, 0 min 偏移量; 数据范围: 0~600 min
|
||||
startIndex += length;
|
||||
byte[] chargingTimeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 充电过程 BMS 信息 0x25
|
||||
*
|
||||
* GBT-27930 充电桩与 BMS 充电过程 BMS 信息
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 13:53
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BMSInformationStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.CHARGING_PROCESS_BMS_INFORMATION_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===充电过程 BMS 信息===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 获取消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
|
||||
// 交易流水号
|
||||
byte[] serialNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 桩编码
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] pileConnectorNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 最高单体动力蓄电池电压所在编号 1/位, 1 偏移量; 数据范围: 1~256
|
||||
startIndex += length;
|
||||
byte[] BMSMaxVoltageNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 最高动力蓄电池温度 1ºC/位, -50 ºC 偏移量;数据范 围: -50 ºC ~+200 ºC
|
||||
startIndex += length;
|
||||
byte[] BMSMaxBatteryTemperatureByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 最高温度检测点编号 1/位, 1 偏移量; 数据范围: 1~128
|
||||
startIndex += length;
|
||||
byte[] maxTemperatureDetectionNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 最低动力蓄电池温度 1ºC/位, -50 ºC 偏移量;数据范 围: -50 ºC ~+200 ºC
|
||||
startIndex += length;
|
||||
byte[] minBatteryTemperature = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 最低动力蓄电池温度检测点编号 1/位, 1 偏移量; 数据范围: 1~128
|
||||
startIndex += length;
|
||||
byte[] minTemperatureDetectionNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
/**
|
||||
* 9-12
|
||||
* 9: BMS 单体动力蓄电池电压过高 /过低 (<00> :=正常 ; <01> :=过高 ; <10>: =过低)
|
||||
* 10: BMS 整车动力蓄电池荷电状态 SOC 过高/过低 (<00> :=正常 ; <01> :=过高 ; <10>: =过低)
|
||||
* 11: BMS 动力蓄电池充电过电流 (<00> :=正常 ; <01> :=过流 ; <10>: =不可信状态)
|
||||
* 12: BMS 动力蓄电池温度过高 (<00> :=正常 ; <01> :=过流 ; <10>: =不可信状态)
|
||||
*/
|
||||
startIndex += length;
|
||||
byte[] numNineToTwelve = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
/**
|
||||
* 13-16
|
||||
* 13: BMS 动力蓄电池绝缘状态 (<00> :=正常 ; <01> :=过流 ; <10>: =不可信状态)
|
||||
* 14: BMS 动力蓄电池组输出连接器连接状态 (<00> :=正常 ; <01> :=过流 ; <10>: =不可信状态)
|
||||
* 15: 充电禁止 (<00>: =禁止; <01>: =允许)
|
||||
* 16: 预留位 00
|
||||
*/
|
||||
startIndex += length;
|
||||
byte[] numThirteenToSixteen = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.pile.service.PileBillingTemplateService;
|
||||
import com.jsowell.pile.service.YKCPushCommandService;
|
||||
import com.jsowell.pile.vo.web.BillingTemplateVO;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 计费模板请求 Handler
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/17 15:59
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BillingTemplateStrategy implements AbstractYkcStrategy {
|
||||
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.BILLING_TEMPLATE_CODE.getBytes());
|
||||
|
||||
@Autowired
|
||||
private PileBillingTemplateService pileBillingTemplateService;
|
||||
|
||||
@Autowired
|
||||
private YKCPushCommandService ykcPushCommandService;
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext ctx) {
|
||||
// log.info("[===执行计费模板请求逻辑===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 获取消息体(此请求消息体只有桩编码)
|
||||
byte[] pileSnByte = ykcDataProtocol.getMsgBody();
|
||||
String pileSn = BytesUtil.binary(pileSnByte, 16);
|
||||
// log.info("桩号:{}", pileSn);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, ctx);
|
||||
|
||||
// 根据桩号查询计费模板
|
||||
BillingTemplateVO billingTemplateVO = pileBillingTemplateService.selectBillingTemplateDetailByPileSn(pileSn);
|
||||
if (billingTemplateVO == null) {
|
||||
log.warn("根据桩号:{},查询计费模板为null", pileSn);
|
||||
return null;
|
||||
}
|
||||
|
||||
// log.info("下面进行下发二维码 pileSn:{}, thread:{}", pileSn, Thread.currentThread().getName());
|
||||
// CompletableFuture.runAsync(() -> {
|
||||
// try {
|
||||
// Thread.sleep(200);
|
||||
// } catch (InterruptedException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// // 下发二维码
|
||||
// IssueQRCodeCommand issueQRCodeCommand = IssueQRCodeCommand.builder().pileSn(pileSn).build();
|
||||
// ykcPushCommandService.pushIssueQRCodeCommand(issueQRCodeCommand);
|
||||
// });
|
||||
|
||||
byte[] messageBody = pileBillingTemplateService.generateBillingTemplateMsgBody(pileSn, billingTemplateVO);
|
||||
|
||||
return YKCUtils.getResult(ykcDataProtocol, messageBody);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -64,6 +64,6 @@ public class BillingTemplateValidateStrategy implements AbstractYkcStrategy {
|
||||
|
||||
// 消息体
|
||||
byte[] messageBody = Bytes.concat(pileSnByte, billingTemplateCodeByte, flag);
|
||||
return getResult(ykcDataProtocol, messageBody);
|
||||
return YKCUtils.getResult(ykcDataProtocol, messageBody);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.pile.domain.OrderBasicInfo;
|
||||
import com.jsowell.pile.service.OrderBasicInfoService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 充电结束Handler
|
||||
* 0x19
|
||||
* GBT-27930 充电桩与 BMS 充电结束阶段报文
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 13:27
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ChargeEndStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.CHARGE_END_CODE.getBytes());
|
||||
|
||||
@Autowired
|
||||
private OrderBasicInfoService orderBasicInfoService;
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===执行充电结束逻辑===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
|
||||
// 交易流水号
|
||||
byte[] serialNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String transactionCode = BytesUtil.bcd2Str(serialNumByteArr);
|
||||
|
||||
// 桩编码
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] connectorCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String connectorCode = BytesUtil.bcd2Str(connectorCodeByteArr);
|
||||
|
||||
// BMS中止荷电状态 SOC 1%/位, 0%偏移量; 数据范围: 0~100%
|
||||
startIndex += length;
|
||||
byte[] BMSStopChargingSOC = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String stopSoc = BytesUtil.binary(BMSStopChargingSOC, 10);
|
||||
|
||||
// BMS 动力蓄电池单体最低电压 0.01 V/位, 0 V 偏移量;数据范围: 0 ~24 V
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] BMSBatteryMinVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 动力蓄电池单体最高电压 0.01 V/位, 0 V 偏移量;数据范围: 0 ~24 V
|
||||
startIndex += length;
|
||||
byte[] BMSBatteryMaxVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 动力蓄电池最低温度 1ºC/位, -50 ºC 偏移量;数据范围: -50 ºC ~+200 ºC
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] BMSBatteryMinTemperatureByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 动力蓄电池最高温度 1ºC/位, -50 ºC 偏移量;数据范围: -50 ºC ~+200 ºC
|
||||
startIndex += length;
|
||||
byte[] BMSBatteryMaxTemperatureByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 电桩累计充电时间 1 min/位, 0 min 偏移量; 数据范围: 0~600 min
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] pileSumChargingTimeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 电桩输出能量 0.1 kWh/位, 0 kWh 偏移量; 数据范围: 0~1000 kWh
|
||||
startIndex += length;
|
||||
byte[] pileOutputEnergyByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 电桩充电机编号 充电机编号, 1/位, 1偏移量 ,数据范围 : 0 ~ 0xFFFFFFFF
|
||||
startIndex += length;
|
||||
length = 4;
|
||||
byte[] pileChargedCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 查询订单 将结束soc传入
|
||||
OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByTransactionCode(transactionCode);
|
||||
if (Objects.nonNull(orderInfo)) {
|
||||
Date nowDate = DateUtils.getNowDate(); // 当前时间
|
||||
|
||||
// 只更新个别字段
|
||||
OrderBasicInfo updateOrder = new OrderBasicInfo();
|
||||
updateOrder.setId(orderInfo.getId());
|
||||
updateOrder.setEndSoc(stopSoc);
|
||||
if (orderInfo.getChargeEndTime() == null) {
|
||||
updateOrder.setChargeEndTime(nowDate); // 结束充电时间
|
||||
}
|
||||
updateOrder.setUpdateTime(nowDate);
|
||||
orderBasicInfoService.updateOrderBasicInfo(updateOrder);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 充电阶段充电机中止Handler 0x21
|
||||
*
|
||||
* GBT-27930 充电桩与 BMS 充电阶段充电机中止报文
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 13:35
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ChargerAbortedDuringChargingPhaseStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.THE_CHARGER_IS_ABORTED_DURING_THE_CHARGING_PHASE_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===充电阶段充电机中止===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 获取消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
|
||||
// 交易流水号
|
||||
byte[] serialNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 桩编码
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] pileConnectorNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
/**
|
||||
* BMS 中止充电原因
|
||||
* 1-2 位——达到充电机设定 的条件中止
|
||||
* 3-4 位——人工中止
|
||||
* 5-6 位——异常中止
|
||||
* 7-8 位——BMS 主动中止
|
||||
*/
|
||||
startIndex += length;
|
||||
byte[] BMSStopChargingReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
/**
|
||||
* BMS 中止充电故障原因
|
||||
* 1-2 位——充电机过温故障
|
||||
* 3-4 位——充电连接器故障
|
||||
* 5-6 位——充电机内部过温故障
|
||||
* 7-8 位——所需电量不 能传送
|
||||
* 9-10 位——充电机急停故障
|
||||
* 11-12 位——其他故障
|
||||
* 13-16 位——预留位
|
||||
*/
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] BMSStopChargingFaultReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
/**
|
||||
* BMS 中止充电错误原因
|
||||
* 1-2 位——电流不匹配
|
||||
* 3-4 位——电压异常
|
||||
* 5-8 位——预留位
|
||||
*/
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] BMSStopChargingErrorReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 充电握手
|
||||
*
|
||||
* GBT-27930 充电桩与 BMS 充电握手阶段报文
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 13:20
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ChargingHandshakeStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.CHARGING_HANDSHAKE_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===执行充电握手逻辑===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
|
||||
// 交易流水号
|
||||
byte[] serialNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 桩编码
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] pileConnectorNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 通信协议版本号 当前版本为 V1.1, 表示为: byte3, byte2—0001H;byte1—01H
|
||||
startIndex += length;
|
||||
length = 3;
|
||||
byte[] BMSCommunicationProtocolVersionByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 电池类型 01H:铅酸电池;02H:氢 电池;03H:磷酸铁锂电池;04H:锰 酸锂电池;05H:钴酸锂电池 ;06H: 三元材料电池;07H:聚合物锂离子 电池;08H:钛酸锂电池;FFH:其他
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] BMSBatteryTypeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 整车动力蓄电池系统额定容量 0.1 Ah /位, 0 Ah 偏移量
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] BMSBatteryCapacityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 整车动力蓄电池系统额定总电压 0.1V/位, 0V 偏移量
|
||||
startIndex += length;
|
||||
byte[] BMSBatteryVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 电池生产厂商名称
|
||||
startIndex += length;
|
||||
length = 4;
|
||||
byte[] BMSBatteryFactoryByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 电池组序号
|
||||
startIndex += length;
|
||||
byte[] BMSBatteryNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 电池组生产日期年 1985 年偏移量, 数据范围: 1985~ 2235 年
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] BMSProductionDateYearByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 电池组生产日期月 0 月偏移量, 数据范围: 1~12 月
|
||||
startIndex += length;
|
||||
byte[] BMSProductionDateMonthByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 电池组生产日期日 0 日偏移量, 数据范围: 1~31 日
|
||||
startIndex += length;
|
||||
byte[] BMSProductionDateDayByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 电池组充电次数 1 次/位, 0 次偏移量, 以 BMS 统 计为准
|
||||
startIndex += length;
|
||||
length = 3;
|
||||
byte[] BMSChargingTimesByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 电池组产权标识 (<0>: =租赁; <1>: =车自有)
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] BMSPropertyIdentificationByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 预留位
|
||||
startIndex += length;
|
||||
byte[] BMSReservePosition = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 车辆识别码
|
||||
startIndex += length;
|
||||
length = 17;
|
||||
byte[] BMSCarIdentifyCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// BMS 软件版本号
|
||||
startIndex += length;
|
||||
length = 8;
|
||||
byte[] BMSSoftwareVersionByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.google.common.primitives.Bytes;
|
||||
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.enums.ykc.CardStatusEnum;
|
||||
import com.jsowell.common.enums.ykc.ReturnCodeEnum;
|
||||
import com.jsowell.common.enums.ykc.StartModeEnum;
|
||||
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.MemberPlateNumberRelation;
|
||||
import com.jsowell.pile.domain.PileAuthCard;
|
||||
import com.jsowell.pile.dto.ConfirmStartChargingData;
|
||||
import com.jsowell.pile.dto.GenerateOrderDTO;
|
||||
import com.jsowell.pile.service.MemberPlateNumberRelationService;
|
||||
import com.jsowell.pile.service.OrderBasicInfoService;
|
||||
import com.jsowell.pile.service.PileAuthCardService;
|
||||
import com.jsowell.pile.service.PileMsgRecordService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 充电桩主动申请启动充电 0x31
|
||||
*
|
||||
* 启动充电鉴权结果
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 14:29
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ConfirmStartChargingStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.REQUEST_START_CHARGING_CODE.getBytes());
|
||||
|
||||
@Autowired
|
||||
private PileAuthCardService pileAuthCardService;
|
||||
|
||||
@Autowired
|
||||
private OrderBasicInfoService orderBasicInfoService;
|
||||
|
||||
@Autowired
|
||||
private MemberPlateNumberRelationService memberPlateNumberRelationService;
|
||||
|
||||
@Autowired
|
||||
private PileMsgRecordService pileMsgRecordService;
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===充电桩主动申请启动充电===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 获取消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩编码
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.binary(pileSnByteArr, 16);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] connectorNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String connectorCode = BytesUtil.bcd2Str(connectorNumByteArr);
|
||||
|
||||
// 启动方式
|
||||
// 0x01 表示通过刷卡启动充电
|
||||
// 0x02 表求通过帐号启动充电 (暂不支持)
|
||||
// 0x03 表示vin码启动充电
|
||||
startIndex += length;
|
||||
byte[] startModeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String startMode = BytesUtil.bcd2Str(startModeByteArr);
|
||||
|
||||
// 是否需要密码 0x00 不需要 0x01 需要
|
||||
startIndex += length;
|
||||
byte[] needPasswordFlagByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String needPasswordFlag = BytesUtil.bcd2Str(needPasswordFlagByteArr);
|
||||
|
||||
// 物理卡号 不足 8 位补 0
|
||||
startIndex += length;
|
||||
length = 8;
|
||||
byte[] cardNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
log.info("充电桩主动申请启动充电cardNumByteArr:{}", cardNumByteArr);
|
||||
String physicsCard = BytesUtil.binary(cardNumByteArr, 16);
|
||||
|
||||
// 输入密码 对用户输入的密码进行16 位MD5 加密,采用小写上传
|
||||
startIndex += length;
|
||||
length = 16;
|
||||
byte[] inputPasswordByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String inputPasswordHexStr = BytesUtil.bin2HexStr(inputPasswordByteArr);
|
||||
|
||||
// VIN码
|
||||
startIndex += length;
|
||||
length = 17;
|
||||
byte[] vinCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String vinCode = BytesUtil.ascii2StrLittle(vinCodeByteArr);
|
||||
|
||||
ConfirmStartChargingData confirmStartChargingData = ConfirmStartChargingData.builder()
|
||||
.pileSn(pileSn)
|
||||
.connectorCode(connectorCode)
|
||||
.startMode(startMode)
|
||||
.needPasswordFlag(needPasswordFlag)
|
||||
.inputPasswordByteArr(inputPasswordHexStr)
|
||||
.physicsCard(physicsCard)
|
||||
.vinCode(vinCode)
|
||||
.build();
|
||||
|
||||
byte[] defeatReasonByteArr = Constants.zeroByteArray;
|
||||
/**
|
||||
* 刷卡启动充电
|
||||
*/
|
||||
String logicCard = "";
|
||||
byte[] authenticationFlagByteArr = Constants.zeroByteArray; // 鉴权成功标识
|
||||
byte[] accountBalanceByteArr = Constants.zeroByteArray; // 账户余额
|
||||
String transactionCode = "";
|
||||
try {
|
||||
if (StringUtils.equals("01", startMode)) {
|
||||
log.info("桩号:{}, 申请充电物理卡号:{}", pileSn, physicsCard);
|
||||
// 查询卡信息 根据传过来的物理卡号查询数据库中此卡信息
|
||||
PileAuthCard pileAuthCardInfo = pileAuthCardService.selectCardInfoByLogicCard(physicsCard);
|
||||
if (pileAuthCardInfo == null) {
|
||||
// 未查到此卡信息
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_THIS_CARD_HAS_NO_INFO);
|
||||
}
|
||||
if (StringUtils.isBlank(pileAuthCardInfo.getMemberId())) {
|
||||
// 卡未绑定用户
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_THIS_CARD_NOT_BIND_USER);
|
||||
}
|
||||
|
||||
// 判断卡状态
|
||||
if (!StringUtils.equals(CardStatusEnum.NORMAL.getCode(), pileAuthCardInfo.getStatus())) {
|
||||
log.info("卡号:{}, 状态:{}, 非正常使用状态", physicsCard, CardStatusEnum.getCardStatus(pileAuthCardInfo.getStatus()));
|
||||
return null;
|
||||
}
|
||||
|
||||
// 刷卡生成订单 刷卡启动充电
|
||||
GenerateOrderDTO dto = new GenerateOrderDTO();
|
||||
dto.setPileAuthCardInfo(pileAuthCardInfo);
|
||||
dto.setPileSn(pileSn);
|
||||
dto.setConnectorCode(connectorCode);
|
||||
dto.setStartMode(StartModeEnum.AUTH_CARD.getValue());
|
||||
dto.setMemberId(pileAuthCardInfo.getMemberId());
|
||||
Map<String, Object> map = orderBasicInfoService.generateOrderByCard(dto);
|
||||
if (map != null) {
|
||||
transactionCode = (String) map.get("transactionCode");
|
||||
accountBalanceByteArr = YKCUtils.getPriceByte(String.valueOf(map.get("accountBalance")), 2);
|
||||
// 鉴权成功标识 0x00 失败 0x01 成功
|
||||
authenticationFlagByteArr = Constants.oneByteArray;
|
||||
}else {
|
||||
throw new BusinessException("", "生成刷卡订单失败");
|
||||
}
|
||||
}
|
||||
} catch (BusinessException e){
|
||||
transactionCode = Constants.ILLEGAL_TRANSACTION_CODE;
|
||||
accountBalanceByteArr = BytesUtil.checkLengthAndBehindAppendZero(accountBalanceByteArr, 8);
|
||||
authenticationFlagByteArr = Constants.zeroByteArray;
|
||||
log.error("刷卡启动充电鉴权 error:{}, {}", e.getCode(), e.getMessage());
|
||||
} catch (Exception e){
|
||||
transactionCode = Constants.ILLEGAL_TRANSACTION_CODE;
|
||||
accountBalanceByteArr = BytesUtil.checkLengthAndBehindAppendZero(accountBalanceByteArr, 8);
|
||||
authenticationFlagByteArr = Constants.zeroByteArray;
|
||||
log.error("刷卡启动充电鉴权 error", e);
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
* VIN码启动充电
|
||||
*/
|
||||
if (StringUtils.equals("03", startMode)) {
|
||||
log.info("桩号:{}, 申请充电VIN码:{}, 反转后:{}", pileSn, vinCode, StringUtils.reverse(vinCode));
|
||||
// 通过vin码查询数据库绑定用户信息
|
||||
MemberPlateNumberRelation plateInfo = memberPlateNumberRelationService.getMemberPlateInfoByVinCode(vinCode);
|
||||
if (plateInfo == null) {
|
||||
// throw new BusinessException("", vinCode + "未查到绑定用户信息");
|
||||
log.error(vinCode + "未查到绑定用户信息");
|
||||
defeatReasonByteArr = new byte[] {0x09}; // 系统中vin 码不存在
|
||||
transactionCode = Constants.ILLEGAL_TRANSACTION_CODE;
|
||||
accountBalanceByteArr = BytesUtil.checkLengthAndBehindAppendZero(accountBalanceByteArr, 8);
|
||||
authenticationFlagByteArr = Constants.zeroByteArray;
|
||||
}
|
||||
// if (!StringUtils.equals("1", plateInfo.getVinStatus())) {
|
||||
// // 1- 正常使用
|
||||
// throw new BusinessException("", vinCode + "vin状态不正确");
|
||||
// }
|
||||
// vin码生成订单 vin启动充电
|
||||
GenerateOrderDTO dto = new GenerateOrderDTO();
|
||||
dto.setMemberPlateNumberRelation(plateInfo);
|
||||
dto.setPileSn(pileSn);
|
||||
dto.setConnectorCode(connectorCode);
|
||||
dto.setStartMode(StartModeEnum.VIN_CODE.getValue());
|
||||
dto.setMemberId(plateInfo.getMemberId());
|
||||
Map<String, Object> map = orderBasicInfoService.generateOrderByCard(dto);
|
||||
if (map != null) {
|
||||
transactionCode = (String) map.get("transactionCode");
|
||||
accountBalanceByteArr = YKCUtils.getPriceByte(String.valueOf(map.get("accountBalance")), 2);
|
||||
// 鉴权成功标识 0x00 失败 0x01 成功
|
||||
authenticationFlagByteArr = Constants.oneByteArray;
|
||||
}else {
|
||||
throw new BusinessException("", "生成vin订单失败");
|
||||
}
|
||||
}
|
||||
}catch (BusinessException e){
|
||||
transactionCode = Constants.ILLEGAL_TRANSACTION_CODE;
|
||||
accountBalanceByteArr = BytesUtil.checkLengthAndBehindAppendZero(accountBalanceByteArr, 8);
|
||||
authenticationFlagByteArr = Constants.zeroByteArray;
|
||||
String code = e.getCode();
|
||||
String message = e.getMessage();
|
||||
if (StringUtils.length(code) == 2) {
|
||||
defeatReasonByteArr = BytesUtil.str2Bcd(code);
|
||||
}
|
||||
|
||||
log.error("VIN码启动充电鉴权 error:{}, {}", code, message);
|
||||
}catch (Exception e) {
|
||||
transactionCode = Constants.ILLEGAL_TRANSACTION_CODE;
|
||||
accountBalanceByteArr = BytesUtil.checkLengthAndBehindAppendZero(accountBalanceByteArr, 8);
|
||||
authenticationFlagByteArr = Constants.zeroByteArray;
|
||||
|
||||
log.error("VIN码启动充电鉴权 error", e);
|
||||
}
|
||||
|
||||
// 应答
|
||||
// 交易流水号
|
||||
// String transactionCode = IdUtils.generateTransactionCode(pileSn, connectorCode);
|
||||
byte[] serialNumByteArr = BytesUtil.str2Bcd(transactionCode);
|
||||
|
||||
/**
|
||||
* 失败原因
|
||||
* 0x01 账户不存在
|
||||
* 0x02 账户冻结
|
||||
* 0x03 账户余额不足
|
||||
* 0x04 该卡存在未结账记录
|
||||
* 0x05 桩停用
|
||||
* 0x06 该账户不能在此桩上充电
|
||||
* 0x07 密码错误
|
||||
* 0x08 电站电容不足
|
||||
* 0x09 系统中vin 码不存在
|
||||
* 0x0A 该桩存在未结账记录
|
||||
* 0x0B 该桩不支持刷卡
|
||||
*/
|
||||
// byte[] defeatReasonByteArr = Constants.zeroByteArray;
|
||||
|
||||
// 保存报文
|
||||
String jsonMsg = JSON.toJSONString(confirmStartChargingData);
|
||||
pileMsgRecordService.save(pileSn, pileSn, type, jsonMsg, ykcDataProtocol.getHEXString());
|
||||
|
||||
// 拼装消息体
|
||||
byte[] msgBodyByteArr = Bytes.concat(serialNumByteArr, pileSnByteArr, connectorNumByteArr, cardNumByteArr, accountBalanceByteArr,
|
||||
authenticationFlagByteArr, defeatReasonByteArr);
|
||||
|
||||
return YKCUtils.getResult(ykcDataProtocol, msgBodyByteArr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 充电桩主动申请充电 逻辑
|
||||
* @param confirmStartChargingData
|
||||
* @return
|
||||
*/
|
||||
private byte[] confirmStartCharging(ConfirmStartChargingData confirmStartChargingData) {
|
||||
String startMode = confirmStartChargingData.getStartMode();
|
||||
String pileSn = confirmStartChargingData.getPileSn();
|
||||
String connectorCode = confirmStartChargingData.getConnectorCode();
|
||||
|
||||
GenerateOrderDTO dto = null;
|
||||
try {
|
||||
if (StringUtils.equals("01", startMode)) {
|
||||
String physicsCard = confirmStartChargingData.getPhysicsCard();
|
||||
// 查询卡信息 根据传过来的物理卡号查询数据库中此卡信息
|
||||
PileAuthCard pileAuthCardInfo = pileAuthCardService.selectCardInfoByLogicCard(physicsCard);
|
||||
if (pileAuthCardInfo == null) {
|
||||
// 未查到此卡信息
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_THIS_CARD_HAS_NO_INFO);
|
||||
}
|
||||
if (StringUtils.isBlank(pileAuthCardInfo.getMemberId())) {
|
||||
// 卡未绑定用户
|
||||
throw new BusinessException(ReturnCodeEnum.CODE_THIS_CARD_NOT_BIND_USER);
|
||||
}
|
||||
|
||||
// 判断卡状态
|
||||
if (!StringUtils.equals(CardStatusEnum.NORMAL.getCode(), pileAuthCardInfo.getStatus())) {
|
||||
log.info("卡号:{}, 状态:{}, 非正常使用状态", physicsCard, CardStatusEnum.getCardStatus(pileAuthCardInfo.getStatus()));
|
||||
return null;
|
||||
}
|
||||
dto = new GenerateOrderDTO();
|
||||
dto.setPileAuthCardInfo(pileAuthCardInfo);
|
||||
dto.setPileSn(pileSn);
|
||||
dto.setConnectorCode(connectorCode);
|
||||
dto.setStartMode(StartModeEnum.AUTH_CARD.getValue());
|
||||
dto.setMemberId(pileAuthCardInfo.getMemberId());
|
||||
} else if (StringUtils.equals("03", startMode)) {
|
||||
String vinCode = confirmStartChargingData.getVinCode();
|
||||
// 通过vin码查询数据库绑定用户信息
|
||||
MemberPlateNumberRelation plateInfo = memberPlateNumberRelationService.getMemberPlateInfoByVinCode(vinCode);
|
||||
if (plateInfo == null) {
|
||||
throw new BusinessException("", vinCode + "未查到绑定用户信息");
|
||||
}
|
||||
dto = new GenerateOrderDTO();
|
||||
dto.setMemberPlateNumberRelation(plateInfo);
|
||||
dto.setPileSn(pileSn);
|
||||
dto.setConnectorCode(connectorCode);
|
||||
dto.setStartMode(StartModeEnum.VIN_CODE.getValue());
|
||||
dto.setMemberId(plateInfo.getMemberId());
|
||||
}
|
||||
|
||||
if (dto != null) {
|
||||
// Map<String, Object> map = orderBasicInfoService.generateOrderByCard(dto);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String msg = "01";
|
||||
System.out.println(StringUtils.length(msg));
|
||||
byte[] bytes = BytesUtil.str2Bcd(msg);
|
||||
byte[] a = new byte[]{0x01};
|
||||
String s = Arrays.toString(bytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 错误报文Handler
|
||||
*
|
||||
* GBT-27930 充电桩与 BMS 充电错误报文
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 13:29
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ErrorMessageStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.ERROR_MESSAGE_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===错误报文===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 获取消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
|
||||
// 交易流水号
|
||||
|
||||
byte[] serialNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 桩编码
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] pileConnectorNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 序号4-6 <00>: =正常; <01>: =超时; <10>: =不可信状态
|
||||
startIndex += length;
|
||||
byte[] NumFourToSix = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 序号7-9
|
||||
startIndex += length;
|
||||
byte[] NumSevenToNine = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 序号10-12
|
||||
startIndex += length;
|
||||
byte[] NumTenToTwelve = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 序号13-14
|
||||
startIndex += length;
|
||||
byte[] NumThirteenToFourteen = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 序号15-16
|
||||
startIndex += length;
|
||||
byte[] NumFifteenToSixteen = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 序号17-19
|
||||
startIndex += length;
|
||||
byte[] NumSeventeenToNineteen = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 序号20-23
|
||||
startIndex += length;
|
||||
byte[] NumTwentyToTwentyThree = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 序号24-25
|
||||
startIndex += length;
|
||||
byte[] NumTwentyFourToTwentyFive = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.core.domain.ykc.GroundLockData;
|
||||
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.parkplatform.ParkingLockAlarmEnum;
|
||||
import com.jsowell.common.enums.parkplatform.ParkingLockStatusEnum;
|
||||
import com.jsowell.common.enums.parkplatform.ParkingStatusEnum;
|
||||
import com.jsowell.common.enums.uniapp.OccupyOrderPayStatusEnum;
|
||||
import com.jsowell.common.enums.uniapp.OccupyOrderStatusEnum;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.pile.domain.OrderPileOccupy;
|
||||
import com.jsowell.pile.service.OrderPileOccupyService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 地锁数据上送
|
||||
*
|
||||
* 地锁状态/报警信息变化时,桩立刻上送变位/报警信息;地锁状态不变化时,每隔 5 分钟周期 性上送地锁状态。若无报警信息,不上送。
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 15:21
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class GroundLockDataUploadStrategy implements AbstractYkcStrategy {
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
@Autowired
|
||||
private OrderPileOccupyService orderPileOccupyService;
|
||||
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.GROUND_LOCK_DATA_UPLOAD_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===地锁数据上送===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
// 桩编码
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, 0, 7);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 枪号
|
||||
byte[] connectorCodeByteArr = BytesUtil.copyBytes(msgBody, 7, 1);
|
||||
String connectorCode = BytesUtil.bcd2Str(connectorCodeByteArr);
|
||||
|
||||
/**
|
||||
* 车位锁状态
|
||||
* 0x00:未到位状态
|
||||
* 0x55:升锁到位状态
|
||||
* 0xFF:降锁到位状态
|
||||
*/
|
||||
byte[] parkingLockStatusByteArr = BytesUtil.copyBytes(msgBody, 8, 1);
|
||||
// String parkingLockStatus = BytesUtil.bcd2Str(parkingLockStatusByteArr);
|
||||
String parkingLockStatus = BytesUtil.bin2HexStr(parkingLockStatusByteArr).toUpperCase(Locale.ROOT);
|
||||
String parkingLockStatusDesc = ParkingLockStatusEnum.getLabelByValue(parkingLockStatus);
|
||||
|
||||
// 车位状态 0x00:无车辆 0xFF:停放车辆
|
||||
byte[] parkingStatusByteArr = BytesUtil.copyBytes(msgBody, 9, 1);
|
||||
// String parkingStatus = BytesUtil.bcd2Str(parkingStatusByteArr);
|
||||
String parkingStatus = BytesUtil.bin2HexStr(parkingStatusByteArr).toUpperCase(Locale.ROOT);
|
||||
String parkingStatusDesc = ParkingStatusEnum.getLabelByValue(parkingStatus);
|
||||
|
||||
// 地锁电量状态 百分比值0~100
|
||||
byte[] groundLockElectricByteArr = BytesUtil.copyBytes(msgBody, 10, 1);
|
||||
String groundLockElectric = BytesUtil.bcd2Str(groundLockElectricByteArr);
|
||||
|
||||
// 报警状态 0x00:正常无报警 0xFF:待机状态摇臂破坏 0x55:摇臂升降异常(未到位)
|
||||
byte[] alarmStatusByteArr = BytesUtil.copyBytes(msgBody, 11, 1);
|
||||
// String alarmStatus = BytesUtil.bcd2Str(alarmStatusByteArr);
|
||||
String alarmStatus = BytesUtil.bin2HexStr(alarmStatusByteArr).toUpperCase(Locale.ROOT);
|
||||
String alarmStatusDesc = ParkingLockAlarmEnum.getLabelByValue(alarmStatus);
|
||||
|
||||
// 预留位 全部置0
|
||||
byte[] waitingUseByteArr = BytesUtil.copyBytes(msgBody, 12, 4);
|
||||
String waitingUse = BytesUtil.bcd2Str(waitingUseByteArr);
|
||||
|
||||
if (StringUtils.equals(parkingLockStatus, ParkingLockStatusEnum.LOCKED_RAISED.getValue())) {
|
||||
if (StringUtils.equals(parkingStatus, ParkingStatusEnum.NO_VEHICLES.getValue())) {
|
||||
try {
|
||||
raiseTheGroundLock(pileSn, connectorCode);
|
||||
}catch (Exception e) {
|
||||
log.error("升锁逻辑error,", e);
|
||||
}
|
||||
}
|
||||
} else if (StringUtils.equals(parkingLockStatus, ParkingLockStatusEnum.LOCKED_LOWERED.getValue())) {
|
||||
if (StringUtils.equals(parkingStatus, ParkingStatusEnum.PARKED_VEHICLES.getValue())) {
|
||||
try {
|
||||
lowerTheGroundLock(pileSn, connectorCode);
|
||||
}catch (Exception e) {
|
||||
log.error("降锁逻辑error,", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 封装到对象中
|
||||
GroundLockData data = GroundLockData.builder()
|
||||
.pileSn(pileSn)
|
||||
.connectorCode(connectorCode)
|
||||
.parkingLockStatus(parkingLockStatus)
|
||||
.parkingStatus(parkingStatus)
|
||||
.groundLockElectric(groundLockElectric)
|
||||
.alarmStatus(alarmStatus)
|
||||
.time(DateUtils.getDateTime())
|
||||
.build();
|
||||
|
||||
// 地锁信息放缓存中 缓存10分钟
|
||||
String redisKey = CacheConstants.GROUND_LOCK_DATA + pileSn + connectorCode;
|
||||
redisCache.setCacheObject(redisKey, data, CacheConstants.cache_expire_time_10m);
|
||||
|
||||
log.info("[===地锁数据上送===] result: 桩编码:{}, 枪号:{}, 车位锁状态:{}, 车位锁状态描述:{}, 车位状态:{}, 车位状态描述:{}, 地锁电量状态:{}, 报警状态:{}, 报警状态描述:{}",
|
||||
pileSn, connectorCode, parkingLockStatus, parkingLockStatusDesc, parkingStatus, parkingStatusDesc, groundLockElectric, alarmStatus, alarmStatusDesc);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 降锁成功的时候调用
|
||||
*/
|
||||
private void lowerTheGroundLock(String pileSn, String connectorCode) {
|
||||
// 查出草稿单占桩订单,将开始时间set进订单信息
|
||||
OrderPileOccupy orderInfo = orderPileOccupyService.getDraftOccupyOrder(pileSn, connectorCode);
|
||||
if (orderInfo == null) {
|
||||
return;
|
||||
}
|
||||
if (Objects.isNull(orderInfo.getStartTime())) {
|
||||
orderInfo.setStatus(OccupyOrderStatusEnum.OCCUPIED.getCode());
|
||||
orderInfo.setStartTime(DateUtils.getNowDate());
|
||||
orderInfo.setPayStatus(OccupyOrderPayStatusEnum.UN_PAY.getCode());
|
||||
// 修改数据库
|
||||
log.info("降锁成功,修改订单状态:{}", JSON.toJSONString(orderInfo));
|
||||
orderPileOccupyService.updateByPrimaryKeySelective(orderInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 升锁成功时调用
|
||||
*/
|
||||
private void raiseTheGroundLock(String pileSn, String connectorCode) {
|
||||
// 两种情况 1是没有停车,地锁自动升起;2是停车完成地锁升起
|
||||
// boolean stopCarFlag = true;
|
||||
|
||||
// 获取现在缓存中占桩订单编号
|
||||
String redisKey = CacheConstants.GROUND_LOCK_OCCUPY_ORDER + pileSn + connectorCode;
|
||||
String occupyCode = redisCache.getCacheObject(redisKey);
|
||||
if (StringUtils.isBlank(occupyCode)) {
|
||||
log.info("桩号:{}, 枪口:{}未查询到占桩订单", pileSn, connectorCode);
|
||||
return;
|
||||
}
|
||||
// 根据占桩订单号查询订单信息
|
||||
OrderPileOccupy orderPileOccupy = orderPileOccupyService.queryByOccupyCode(occupyCode);
|
||||
String orderStatus = orderPileOccupy.getStatus();
|
||||
if (StringUtils.equals(OccupyOrderStatusEnum.DRAFT_ORDER.getCode(), orderStatus)) {
|
||||
// 草稿单,关闭占桩订单
|
||||
orderPileOccupyService.closeOccupyPileOrder(pileSn, connectorCode);
|
||||
}else {
|
||||
// 占桩订单,停止占桩订单计时
|
||||
orderPileOccupyService.stopOccupyPileOrder(pileSn, connectorCode);
|
||||
}
|
||||
|
||||
String occupyOrderKey = CacheConstants.GROUND_LOCK_OCCUPY_ORDER + pileSn + connectorCode;
|
||||
redisCache.deleteObject(occupyOrderKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
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.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.pile.service.PileBasicInfoService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 充电桩心跳包
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class HeartbeatRequestStrategy implements AbstractYkcStrategy {
|
||||
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.HEART_BEAT_CODE.getBytes());
|
||||
|
||||
@Autowired
|
||||
private PileBasicInfoService pileBasicInfoService;
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===充电桩心跳包===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 获取消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩号
|
||||
byte[] pileSnByte = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.binary(pileSnByte, 16);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 校验channel
|
||||
// PileChannelEntity.checkChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] pileConnectorNumByte = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileConnectorNum = String.format("%02d", Integer.parseInt(BytesUtil.binary(pileConnectorNumByte, 16)));
|
||||
|
||||
//枪状态(不回复)
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] connectorStatusByte = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String connectorStatus = BytesUtil.binary(connectorStatusByte, 16);
|
||||
// log.info("桩号:{}, 枪号:{}, 枪状态:{}", pileSn, pileConnectorNum, connectorStatus);
|
||||
|
||||
// updateStatus(pileSn, pileConnectorNum, connectorStatus);
|
||||
|
||||
// 公共方法修改状态
|
||||
try {
|
||||
pileBasicInfoService.updateStatus(BytesUtil.bcd2Str(ykcDataProtocol.getFrameType()), pileSn, pileConnectorNum, connectorStatus, null);
|
||||
} catch (Exception e) {
|
||||
log.error("公共方法修改状态error", e);
|
||||
}
|
||||
|
||||
// 心跳应答(置0)
|
||||
byte[] flag = Constants.zeroByteArray;
|
||||
|
||||
// 消息体
|
||||
byte[] messageBody = Bytes.concat(pileSnByte, pileConnectorNumByte, flag);
|
||||
return YKCUtils.getResult(ykcDataProtocol, messageBody);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -279,6 +279,6 @@ public class LoginStrategy implements AbstractYkcStrategy {
|
||||
|
||||
// 消息体
|
||||
byte[] messageBody = Bytes.concat(pileSnByte, flag);
|
||||
return getResult(ykcDataProtocol, messageBody);
|
||||
return YKCUtils.getResult(ykcDataProtocol, messageBody);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 离线卡数据清除应答
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/27 9:59
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class OfflineCardDataCleaningStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.OFFLINE_CARD_DATA_CLEANING_ANSWER_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===离线卡数据清除应答===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩编码
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 第 1 个卡物理卡号
|
||||
startIndex += length;
|
||||
length = 8;
|
||||
byte[] firstCardNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 清除标记 0x00 清除失败 0x01 清除成功
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] cleanFlagByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 失败原因 0x01 卡号格式错误 0x02 清除成功
|
||||
startIndex += length;
|
||||
byte[] failedReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 第N个物理卡号
|
||||
|
||||
// 清除标记
|
||||
|
||||
// 失败原因
|
||||
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 离线卡数据查询应答
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/27 10:20
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class OfflineCardDataQueryStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.OFFLINE_CARD_DATA_QUERY_ANSWER_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===离线卡数据查询应答===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩编号
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 第1个卡物理卡号
|
||||
startIndex += length;
|
||||
length = 8;
|
||||
byte[] firstCardNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 查询结果
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] firstCardResultByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 第N+1个卡物理卡号
|
||||
// 查询结果
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 离线卡数据同步应答
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/27 9:31
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class OfflineCardDataSyncStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.OFFLINE_CARD_DATA_SYNCHRONIZATION_ANSWER_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===离线卡数据同步应答===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
//消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩编码
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 保存结果 0x00 失败 0x01 成功
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] resultByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
// 失败原因 0x01 卡号格式错误 0x02 储存空间不足
|
||||
startIndex += length;
|
||||
byte[] failedReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.pile.domain.OrderBasicInfo;
|
||||
import com.jsowell.pile.service.OrderBasicInfoService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 参数配置 Handler
|
||||
*
|
||||
* GBT-27930 充电桩与 BMS 参数配置阶段报文
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 13:24
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ParameterConfigStrategy implements AbstractYkcStrategy {
|
||||
@Autowired
|
||||
private OrderBasicInfoService orderBasicInfoService;
|
||||
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.PARAMETER_CONFIGURATION_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===参数配置===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 获取消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
|
||||
// 交易流水号
|
||||
byte[] serialNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String transactionCode = BytesUtil.bcd2Str(serialNumByteArr);
|
||||
|
||||
// 桩编码
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] pileConnectorNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileConnectorCode = BytesUtil.bcd2Str(pileConnectorNumByteArr);
|
||||
|
||||
// BMS 单体动力蓄电池最高允许充电电压 0.01 V/位, 0 V 偏移量; 数据范围: 0~24 V
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] BMSMaxVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String BMSMaxVoltage = String.valueOf(BytesUtil.bytesToIntLittle(BMSMaxVoltageByteArr) * 0.01);
|
||||
|
||||
// BMS 最高允许充电电流 0.1 A/位, -400A 偏移量
|
||||
startIndex += length;
|
||||
byte[] BMSMaxCurrentByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String BMSMaxCurrent = String.valueOf(BytesUtil.bytesToIntLittle(BMSMaxCurrentByteArr) * 0.1 - 400);
|
||||
|
||||
// BMS 动力蓄电池标称总能量 0.1 kWh/位, 0 kWh 偏移量; 数据范围: 0~1000 kWh
|
||||
startIndex += length;
|
||||
byte[] BMSSumEnergyByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String BMSSumEnergy = String.valueOf(BytesUtil.bytesToIntLittle(BMSSumEnergyByteArr) * 0.1);
|
||||
|
||||
// BMS 最高允许充电总电压 0.1 V/位, 0 V 偏移量
|
||||
startIndex += length;
|
||||
byte[] BMSMaxChargingVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String BMSMaxChargingVoltage = String.valueOf(BytesUtil.bytesToIntLittle(BMSMaxChargingVoltageByteArr) * 0.1);
|
||||
|
||||
// BMS 最高允许温度 1ºC/位, -50 ºC 偏移量;数据范 围: -50 ºC ~+200 ºC
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] BMSMaxTemperatureByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String BMSMaxTemperature = String.valueOf(BytesUtil.bytesToIntLittle(BMSMaxTemperatureByteArr) - 50);
|
||||
|
||||
// BMS 整车动力 蓄电池荷电状态(soc) 0.1%/位, 0%偏移量;数据范围: 0~100%
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] BMSSocByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String soc = YKCUtils.convertVoltageCurrent(BMSSocByteArr);
|
||||
|
||||
// BMS 整车动力蓄电池当前电池电压 整车动力蓄电池总电压
|
||||
startIndex += length;
|
||||
byte[] BMSRealTimeVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String BMSRealTimeVoltage = String.valueOf(BytesUtil.bytesToIntLittle(BMSRealTimeVoltageByteArr) * 0.1);
|
||||
|
||||
// 电桩最高输出电压 0.1 V /位, 0 V 偏移量
|
||||
startIndex += length;
|
||||
byte[] pileMaxOutputVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileMaxOutputVoltage = String.valueOf(BytesUtil.bytesToIntLittle(pileMaxOutputVoltageByteArr) * 0.1);
|
||||
|
||||
// 电桩最低输出电压 0.1 V /位, 0 V 偏移量
|
||||
startIndex += length;
|
||||
byte[] pileMinOutputVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileMinOutputVoltage = String.valueOf(BytesUtil.bytesToIntLittle(pileMinOutputVoltageByteArr) * 0.1);
|
||||
|
||||
// 电桩最大输出电流 0.1 A/位, -400 A 偏移量
|
||||
startIndex += length;
|
||||
byte[] pileMaxOutputCurrentByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileMaxOutputCurrent = String.valueOf(BytesUtil.bytesToIntLittle(pileMaxOutputCurrentByteArr) * 0.1 - 400);
|
||||
|
||||
// 电桩最小输出电流 0.1 A/位, -400 A 偏移量
|
||||
startIndex += length;
|
||||
byte[] pileMinOutputCurrentByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileMinOutputCurrent = String.valueOf(BytesUtil.bytesToIntLittle(pileMinOutputCurrentByteArr) * 0.1 - 400);
|
||||
|
||||
// 查询该订单下信息,将起始soc传入
|
||||
OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByTransactionCode(transactionCode);
|
||||
if (Objects.nonNull(orderInfo)) {
|
||||
OrderBasicInfo orderBasicInfo = OrderBasicInfo.builder()
|
||||
.id(orderInfo.getId())
|
||||
.startSoc(soc)
|
||||
.build();
|
||||
orderBasicInfoService.updateOrderBasicInfo(orderBasicInfo);
|
||||
log.info("更新订单起始SOC, orderCode:{}, transactionCode:{}, startSoc:{}", orderInfo.getOrderCode(), transactionCode, soc);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 充电桩工作参数设置应答
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/27 10:40
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class PileWorkingParameterSettingStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.CHARGING_PILE_WORKING_PARAMETER_SETTING_ANSWER_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===充电桩工作参数设置应答===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩编号
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 设置结果
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] settingResultByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 0x27 充电桩査询工作参数回复
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2023/4/4 10:06
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class QueryPileWorkParamsStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.QUERY_PILE_WORK_PARAMS_ANSWER_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[====充电桩査询工作参数回复====] param:{}", JSON.toJSONString(ykcDataProtocol));
|
||||
// 获取消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩编码
|
||||
byte[] pileSnByte = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.binary(pileSnByte, 16);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 充电桩类型 0x00:直流0x01:交流
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] pileTypeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileType = BytesUtil.bcd2Str(pileTypeByteArr);
|
||||
|
||||
// 最高充电电压 精确到小数点后一位;待机置零
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] maxChargingVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String maxChargingVoltage = YKCUtils.convertVoltageCurrent(maxChargingVoltageByteArr);
|
||||
|
||||
// 最高充电电流
|
||||
startIndex += length;
|
||||
byte[] maxChargingCurrentByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String maxChargingCurrent = YKCUtils.convertVoltageCurrent(maxChargingCurrentByteArr);
|
||||
|
||||
// 最大充电功率
|
||||
startIndex += length;
|
||||
byte[] maxChargingPowerByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String maxChargingPower = YKCUtils.convertVoltageCurrent(maxChargingPowerByteArr);
|
||||
|
||||
// 当前充电电压
|
||||
startIndex += length;
|
||||
byte[] instantChargingVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String instantChargingVoltage = YKCUtils.convertVoltageCurrent(instantChargingVoltageByteArr);
|
||||
|
||||
// 当前充电电流
|
||||
startIndex += length;
|
||||
byte[] instantChargingCurrentByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String instantChargingCurrent = YKCUtils.convertVoltageCurrent(instantChargingCurrentByteArr);
|
||||
|
||||
// 当前充电功率
|
||||
startIndex += length;
|
||||
byte[] instantChargingPowerByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String instantChargingPower = YKCUtils.convertVoltageCurrent(instantChargingPowerByteArr);
|
||||
|
||||
log.info("[====充电桩査询工作参数回复====] 桩编号:{}, 充电桩类型:{}, 最大充电电压:{}, 最高充电电流:{}, " +
|
||||
"最大充电功率:{}, 当前充电电压:{}, 当前充电电流:{}, 当前充电功率:{}",
|
||||
pileSn, pileType, maxChargingVoltage, maxChargingCurrent, maxChargingPower, instantChargingVoltage,
|
||||
instantChargingCurrent, instantChargingPower);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.pile.service.OrderPileOccupyService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 充电桩返回遥控地锁升锁与降锁数据(上行)
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/27 13:21
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RemoteControlGroundLockStrategy implements AbstractYkcStrategy {
|
||||
|
||||
@Autowired
|
||||
private OrderPileOccupyService orderPileOccupyService;
|
||||
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_CONTROL_GROUND_LOCK_ANSWER_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===充电桩返回遥控地锁升锁与降锁数据(上行)===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int 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);
|
||||
|
||||
// 升/降地锁标识 升锁 0X55,降锁 0XFF
|
||||
startIndex += length;
|
||||
byte[] raiseLowMarkingByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String raiseLowMarking = BytesUtil.bin2HexStr(raiseLowMarkingByteArr).toUpperCase(Locale.ROOT);
|
||||
|
||||
// 地锁控制返回标志 布尔型( 1, 鉴权成功; 0, 鉴权失败)
|
||||
startIndex += length;
|
||||
byte[] controlResultByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String controlResult = BytesUtil.bcd2Str(controlResultByteArr);
|
||||
|
||||
// 预留位
|
||||
startIndex += length;
|
||||
length = 4;
|
||||
byte[] waitingUseByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
log.info("===充电桩返回遥控地锁升锁与降锁数据(上行)=== result: 桩编号:{}, 枪号:{}, 升/降地锁标识:{}, 地锁控制返回标志:( 1, 鉴权成功; 0, 鉴权失败){}",
|
||||
pileSn, connectorCode,raiseLowMarking, controlResult);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 桩应答远程下发二维码前缀指令
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/29 14:10
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RemoteIssuedQrCodeStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_ISSUE_QRCODE_ANSWER_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===桩应答远程下发二维码前缀指令===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩编码
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 下发结果 0x00:成功 0x01:失败
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] issuedResultByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.pile.service.PileMsgRecordService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 远程重启应答
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/27 13:27
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RemoteRestartStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_RESTART_ANSWER_CODE.getBytes());
|
||||
|
||||
@Autowired
|
||||
private PileMsgRecordService pileMsgRecordService;
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===远程重启应答===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩编号
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 保存报文
|
||||
String jsonMsg = JSON.toJSONString(ykcDataProtocol);
|
||||
pileMsgRecordService.save(pileSn, null, type, jsonMsg, ykcDataProtocol.getHEXString());
|
||||
|
||||
// 设置结果
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] settingResultByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
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.enums.ykc.ChargingFailedReasonEnum;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.common.util.spring.SpringUtils;
|
||||
import com.jsowell.pile.domain.OrderBasicInfo;
|
||||
import com.jsowell.pile.service.OrderBasicInfoService;
|
||||
import com.jsowell.thirdparty.common.CommonService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* 远程启动充电命令回复 0x33, 0x34
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 14:35
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RemoteStartChargingStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_CONTROL_START_CHARGING_ANSWER_CODE.getBytes());
|
||||
|
||||
@Autowired
|
||||
private OrderBasicInfoService orderBasicInfoService;
|
||||
|
||||
@Autowired
|
||||
private CommonService commonService;
|
||||
|
||||
// 引入线程池
|
||||
private ThreadPoolTaskExecutor executor = SpringUtils.getBean("threadPoolTaskExecutor");
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===远程启动充电命令回复===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
|
||||
// 交易流水号
|
||||
byte[] orderCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String transactionCode = BytesUtil.bcd2Str(orderCodeByteArr);
|
||||
|
||||
// 桩编码
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] connectorCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String connectorCode = BytesUtil.bcd2Str(connectorCodeByteArr);
|
||||
|
||||
// 启动结果 0x00失败 0x01成功
|
||||
startIndex += length;
|
||||
byte[] startResultByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String startResult = BytesUtil.bcd2Str(startResultByteArr);
|
||||
|
||||
/**
|
||||
* 失败原因
|
||||
*
|
||||
* 桩在收到启充命令后,检测到未插枪则发送 0x33 报文回复充电失败。
|
||||
* 若在 60 秒(以收到 0x34 时间开始计算)内检测到枪重新连接,则补送 0x33 成功报文;超时或者离线等其他异常,桩不启充、不补发 0x33 报文
|
||||
* 0x00 无
|
||||
* 0x01 设备编号不匹配
|
||||
* 0x02 枪已在充电
|
||||
* 0x03 设备故障
|
||||
* 0x04 设备离线
|
||||
* 0x05 未插枪
|
||||
*/
|
||||
startIndex += length;
|
||||
byte[] failedReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String failedReason = BytesUtil.bin2HexStr(failedReasonByteArr);
|
||||
String failedReasonMsg = ChargingFailedReasonEnum.getMsgByCode(Integer.parseInt(failedReason, 16));
|
||||
|
||||
if (StringUtils.equals(startResult, Constants.DOUBLE_ZERO)) {
|
||||
// 启动失败 682204000001000000000041
|
||||
orderBasicInfoService.chargingPileFailedToStart(transactionCode, failedReasonMsg);
|
||||
} else {
|
||||
// 启动成功
|
||||
orderBasicInfoService.chargingPileStartedSuccessfully(transactionCode);
|
||||
}
|
||||
// orderBasicInfoService.updateOrderBasicInfo(orderInfo);
|
||||
log.info("远程启动充电命令回复-交易流水号:{}, 桩编码:{}, 枪号:{}, 启动结果(00-失败, 01-成功):{}, 失败原因:{}", transactionCode, pileSn, connectorCode, startResult, failedReasonMsg);
|
||||
|
||||
// 异步推送第三方平台
|
||||
CompletableFuture.runAsync(() -> {
|
||||
OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByTransactionCode(transactionCode);
|
||||
if (orderInfo == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 启动结果回复
|
||||
commonService.commonPushStartChargeResult(orderInfo);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 启动失败, 推送第三方订单信息
|
||||
if (StringUtils.equals(startResult, Constants.DOUBLE_ZERO)) {
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
commonService.commonPushOrderInfo(orderInfo);
|
||||
} catch (Exception e) {
|
||||
log.error("统一推送第三方平台订单信息error, ", e);
|
||||
}
|
||||
}
|
||||
}, executor);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.enums.ykc.OrderStatusEnum;
|
||||
import com.jsowell.common.enums.ykc.StopChargingFailedReasonEnum;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.pile.domain.OrderBasicInfo;
|
||||
import com.jsowell.pile.service.OrderBasicInfoService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 远程停机命令回复
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 14:37
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RemoteStopChargingStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_CONTROL_STOP_CHARGING_ANSWER_CODE.getBytes());
|
||||
|
||||
@Autowired
|
||||
private OrderBasicInfoService orderBasicInfoService;
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===远程停机命令回复===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 获取消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩编号
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] connectorCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String connectorCode = BytesUtil.bcd2Str(connectorCodeByteArr);
|
||||
|
||||
// 停止结果 0x00失败 0x01成功
|
||||
startIndex += length;
|
||||
byte[] stopResultByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String stopResult = BytesUtil.bcd2Str(stopResultByteArr);
|
||||
|
||||
/**
|
||||
* 失败原因
|
||||
* 0x00 无
|
||||
* 0x01 设备编号不匹配
|
||||
* 0x02 枪未处于充电状态
|
||||
* 0x03 其他
|
||||
*/
|
||||
startIndex += length;
|
||||
byte[] reasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String reasonCode = BytesUtil.bcd2Str(reasonByteArr);
|
||||
String reason = StopChargingFailedReasonEnum.getMsgByCode(reasonCode);
|
||||
|
||||
// 通过桩编号+枪口号 查出订单
|
||||
OrderBasicInfo order = orderBasicInfoService.queryChargingByPileSnAndConnectorCode(pileSn, connectorCode);
|
||||
if (order != null) {
|
||||
// 收到停机回复后,修改订单状态
|
||||
if (StringUtils.equals(stopResult, "01")) {
|
||||
// 停机成功,修改订单状态为 待结算
|
||||
order.setOrderStatus(OrderStatusEnum.STAY_SETTLEMENT.getValue());
|
||||
if (order.getChargeEndTime() == null) {
|
||||
order.setChargeEndTime(new Date()); // 结束充电时间
|
||||
}
|
||||
} else {
|
||||
// 停机失败,修改订单状态为 异常
|
||||
order.setOrderStatus(OrderStatusEnum.ABNORMAL.getValue());
|
||||
order.setReason(reason);
|
||||
}
|
||||
orderBasicInfoService.updateOrderBasicInfo(order);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 远程更新应答
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/27 13:32
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RemoteUpdateStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.REMOTE_UPDATE_ANSWER_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[====远程更新应答====] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩编号
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, 0, 7);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 升级状态 0x00-成功 0x01-编号错误 0x02-程序与桩型号不符 0x03-下载更新文件超时
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] updateStatusByteArr = BytesUtil.copyBytes(msgBody, 7, 1);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.google.common.primitives.Bytes;
|
||||
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.enums.ykc.ChargingFailedReasonEnum;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.pile.dto.ReservationChargingStartupResult;
|
||||
import com.jsowell.pile.service.PileBasicInfoService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 0x65预约充电启动结果上送
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ReservationChargingStartupResultStrategy implements AbstractYkcStrategy {
|
||||
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.RESERVATION_CHARGING_STARTUP_RESULT_CODE.getBytes());
|
||||
|
||||
@Autowired
|
||||
private PileBasicInfoService pileBasicInfoService;
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 获取消息体
|
||||
String msg = "8823000000071801240823102300000088230000000718010190";
|
||||
byte[] msgBody = BytesUtil.str2Bcd(msg);
|
||||
|
||||
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);
|
||||
|
||||
// vin
|
||||
startIndex += length;
|
||||
length = 17;
|
||||
byte[] vinCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String vinCode = YKCUtils.parseVin(vinCodeByteArr); // 解析vin
|
||||
|
||||
// 启动结果 0x00失败 0x01成功
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] startupResultByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String startupResult = BytesUtil.bcd2Str(startupResultByteArr);
|
||||
String startupResultMsg = StringUtils.equals(startupResult, "00") ? "失败" : "成功";
|
||||
|
||||
// 失败原因
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] failReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String failReason = BytesUtil.bcd2Str(failReasonByteArr);
|
||||
String failReasonMsg = ChargingFailedReasonEnum.getMsgByCode(Integer.parseInt(failReason, 16));
|
||||
|
||||
log.info("[===预约充电启动结果上送===]交易流水号:{}, 桩编号:{}, 枪号:{}, vin:{}, 启动结果:{}, 失败原因:{}",
|
||||
transactionCode, pileSn, connectorCode, vinCode, startupResult + "-" +startupResultMsg, failReasonMsg);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
log.info("[===预约充电启动结果上送===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
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);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] connectorCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String connectorCode = BytesUtil.bcd2Str(connectorCodeByteArr);
|
||||
|
||||
// vin
|
||||
startIndex += length;
|
||||
length = 17;
|
||||
byte[] vinCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String vinCode = YKCUtils.parseVin(vinCodeByteArr); // 解析vin
|
||||
|
||||
// 启动结果 0x00失败 0x01成功
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] startupResultByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String startupResult = BytesUtil.bcd2Str(startupResultByteArr);
|
||||
String startupResultMsg = StringUtils.equals(startupResult, "00") ? "失败" : "成功";
|
||||
|
||||
// 失败原因
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] failReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String failReason = BytesUtil.bcd2Str(failReasonByteArr);
|
||||
String failReasonMsg = ChargingFailedReasonEnum.getMsgByCode(Integer.parseInt(failReason, 16));
|
||||
|
||||
log.info("[===预约充电启动结果上送===]交易流水号:{}, 桩编号:{}, 枪号:{}, vin:{}, 启动结果:{}, 失败原因:{}",
|
||||
transactionCode, pileSn, connectorCode, vinCode, startupResult + "-" +startupResultMsg, failReasonMsg);
|
||||
/*
|
||||
应答
|
||||
确认结果 0x00 成功 0x01 失败
|
||||
*/
|
||||
byte[] confirmResultBytes = Constants.zeroByteArray;
|
||||
try {
|
||||
ReservationChargingStartupResult chargingStartupResult = ReservationChargingStartupResult.builder()
|
||||
.transactionCode(transactionCode)
|
||||
.pileSn(pileSn)
|
||||
.connectorCode(connectorCode)
|
||||
.vinCode(vinCode)
|
||||
.startupResult(startupResult)
|
||||
.failReason(failReasonMsg)
|
||||
.build();
|
||||
pileBasicInfoService.startupResult(chargingStartupResult);
|
||||
} catch (Exception e) {
|
||||
log.error("预约充电启动结果上送error", e);
|
||||
confirmResultBytes = Constants.oneByteArray;
|
||||
}
|
||||
|
||||
byte[] concatMsgBody = Bytes.concat(transactionCodeByteArr, pileSnByteArr, connectorCodeByteArr, confirmResultBytes);
|
||||
return YKCUtils.getResult(ykcDataProtocol, concatMsgBody);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
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.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.pile.domain.PileReservationInfo;
|
||||
import com.jsowell.pile.service.PileReservationInfoService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 预约充电响应
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ReservationChargingStrategy implements AbstractYkcStrategy {
|
||||
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.RESERVATION_CHARGING_SETUP_ANSWER_CODE.getBytes());
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
@Autowired
|
||||
private PileReservationInfoService pileReservationInfoService;
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[====远程更新应答====] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
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);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪口号
|
||||
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);
|
||||
|
||||
// 失败原因
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] failedReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String failedReason = BytesUtil.bcd2Str(failedReasonByteArr);
|
||||
|
||||
log.info("0x59预约充电响应, 交易流水号:{}, 桩SN:{}, 枪口号:{}, 结果:{}, 失败原因:{}",
|
||||
transactionCode, pileSn, connectorCode, resultCode, failedReason);
|
||||
|
||||
// 如果收到成功, 从redis取值, 保存到数据库
|
||||
if ("01".equals(resultCode)) {
|
||||
// 预约成功, 删除redis中的预约信息
|
||||
String redisKey = CacheConstants.UPDATE_RESERVATION_INFO + pileSn + connectorCode;
|
||||
String cacheObject = redisCache.getCacheObject(redisKey);
|
||||
log.info("预约充电-收到成功, redisKey:{}, result:{}", redisKey, cacheObject);
|
||||
if (cacheObject != null) {
|
||||
log.info("修改预约充电相应成功, 更新数据库");
|
||||
PileReservationInfo pileReservationInfo = JSON.parseObject(cacheObject, PileReservationInfo.class);
|
||||
pileReservationInfoService.insertOrUpdateSelective(pileReservationInfo);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 0x29 平台设置工作参数回复
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2023/4/4 13:43
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SettingPileWorkParamsStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.SETTING_PILE_WORK_PARAMS_ANSWER_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[====平台设置工作参数回复====] param:{}", JSON.toJSONString(ykcDataProtocol));
|
||||
// 获取消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩编码
|
||||
byte[] pileSnByte = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.binary(pileSnByte, 16);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 设置状态 0x00-成功 0x01-失败
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] settingStatusByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String settingStatus = BytesUtil.bcd2Str(settingStatusByteArr);
|
||||
|
||||
// 充电桩类型 0x00:直流 0x01:交流
|
||||
startIndex += length;
|
||||
byte[] pileTypeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileType = BytesUtil.bcd2Str(pileTypeByteArr);
|
||||
|
||||
// 最高充电电压 精确到小数点后一位;待机置零
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] maxChargingVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String maxChargingVoltage = YKCUtils.convertVoltageCurrent(maxChargingVoltageByteArr);
|
||||
|
||||
// 最高充电电流
|
||||
startIndex += length;
|
||||
byte[] maxChargingCurrentByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String maxChargingCurrent = YKCUtils.convertVoltageCurrent(maxChargingCurrentByteArr);
|
||||
|
||||
// 最大充电功率
|
||||
startIndex += length;
|
||||
byte[] maxChargingPowerByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String maxChargingPower = YKCUtils.convertVoltageCurrent(maxChargingPowerByteArr);
|
||||
|
||||
// 当前充电电压
|
||||
startIndex += length;
|
||||
byte[] instantChargingVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String instantChargingVoltage = YKCUtils.convertVoltageCurrent(instantChargingVoltageByteArr);
|
||||
|
||||
// 当前充电电流
|
||||
startIndex += length;
|
||||
byte[] instantChargingCurrentByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String instantChargingCurrent = YKCUtils.convertVoltageCurrent(instantChargingCurrentByteArr);
|
||||
|
||||
// 当前充电功率
|
||||
startIndex += length;
|
||||
byte[] instantChargingPowerByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String instantChargingPower = YKCUtils.convertVoltageCurrent(instantChargingPowerByteArr);
|
||||
|
||||
log.info("[====平台设置工作参数回复====] 桩编号:{}, 设置状态:{}, 充电桩类型:{}, 最大充电电压:{}, 最高充电电流:{}, " +
|
||||
"最大充电功率:{}, 当前充电电压:{}, 当前充电电流:{}, 当前充电功率:{}",
|
||||
pileSn, settingStatus, pileType, maxChargingVoltage, maxChargingCurrent, maxChargingPower, instantChargingVoltage,
|
||||
instantChargingCurrent, instantChargingPower);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.core.domain.ykc.YKCDataProtocol;
|
||||
import com.jsowell.common.core.domain.ykc.YKCFrameTypeCode;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.Cp56Time2a.Cp56Time2aUtil;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 对时设置应答
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/27 11:09
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TimeCheckSettingStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.TIME_CHECK_SETTING_ANSWER_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===对时设置应答===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 7;
|
||||
|
||||
// 桩编号
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 当前时间
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] currentTimeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
Date date = Cp56Time2aUtil.byte2Hdate(currentTimeByteArr);
|
||||
log.info("对时设置应答, pileSn:{}, channelId:{}, 充电桩当前时间:{}", pileSn, channel.channel().id().asShortText(), DateUtils.formatDateTime(date));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,689 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.core.domain.ykc.TransactionRecordsData;
|
||||
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.ykc.OrderStatusEnum;
|
||||
import com.jsowell.common.enums.ykc.YKCChargingStopReasonEnum;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.Cp56Time2a.Cp56Time2aUtil;
|
||||
import com.jsowell.common.util.DateUtils;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.common.util.id.IdUtils;
|
||||
import com.jsowell.common.util.spring.SpringUtils;
|
||||
import com.jsowell.pile.domain.OrderBasicInfo;
|
||||
import com.jsowell.pile.domain.PileBasicInfo;
|
||||
import com.jsowell.pile.service.*;
|
||||
import com.jsowell.pile.service.programlogic.AbstractProgramLogic;
|
||||
import com.jsowell.pile.service.programlogic.ProgramLogicFactory;
|
||||
import com.jsowell.thirdparty.common.CommonService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* 交易记录确认
|
||||
* 这一帧仅是报文交互使用, 意指平台成功接收到交易记录报文,并不代表交易订单成功结算
|
||||
* 运营平台接收到结算账单上传后,都需回复此确认信息。若桩未收到回复帧,则 5 分钟后继续 上送一次交易记录,
|
||||
* 此情况下无论平台是否成功回复都停止上送。
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 14:40
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TransactionRecordsStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.TRANSACTION_RECORDS_CODE.getBytes());
|
||||
private final String oldVersionType = YKCUtils.frameType2Str(YKCFrameTypeCode.TRANSACTION_RECORDS_OLD_VERSION_CODE.getBytes());
|
||||
|
||||
// 引入线程池
|
||||
private ThreadPoolTaskExecutor executor = SpringUtils.getBean("threadPoolTaskExecutor");
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// YKCOperateFactory.register(oldVersionType, this);
|
||||
// }
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
@Autowired
|
||||
private PileMsgRecordService pileMsgRecordService;
|
||||
|
||||
@Autowired
|
||||
private OrderBasicInfoService orderBasicInfoService;
|
||||
|
||||
@Autowired
|
||||
private PileMerchantInfoService pileMerchantInfoService;
|
||||
|
||||
@Autowired
|
||||
private CommonService commonService;
|
||||
|
||||
@Autowired
|
||||
private PileBasicInfoService pileBasicInfoService;
|
||||
|
||||
@Autowired
|
||||
private PersonalChargingRecordService personalChargingRecordService;
|
||||
|
||||
|
||||
/*public static void main(String[] args) {
|
||||
BigDecimal totalElectricity = new BigDecimal("23.73");
|
||||
if (totalElectricity.compareTo(BigDecimal.TEN) > 0) {
|
||||
// 充电度数大于10度
|
||||
System.out.println("123");
|
||||
}
|
||||
|
||||
|
||||
// 获取消息体
|
||||
String msg = "000000000000000000000000000000008823000000030601a08c2e0d0404170000380d0404170000000000000000000000000000000000000000000000000000000000000000400d0300ee250000ee250000c84b000000000000000000000000000000000000e0bb040000cee1040000ee250000ee250000c84b00000000000000000000000000000000000000010000380d04041745a511101970000000";
|
||||
byte[] msgBody = BytesUtil.str2Bcd(msg);
|
||||
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
|
||||
// 交易流水号
|
||||
byte[] orderCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String transactionCode = BytesUtil.bcd2Str(orderCodeByteArr);
|
||||
|
||||
// 桩编码
|
||||
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);
|
||||
|
||||
|
||||
// 开始时间 CP56Time2a 格式
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] startTimeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
// String binary = BytesUtil.binary(startTimeByteArr, 16);
|
||||
Date startDate = Cp56Time2aUtil.byte2Hdate(startTimeByteArr);
|
||||
String startTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, startDate);
|
||||
|
||||
|
||||
// 结束时间 CP56Time2a 格式
|
||||
startIndex += length;
|
||||
byte[] endTimeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
Date endDate = Cp56Time2aUtil.byte2Hdate(endTimeByteArr);
|
||||
String endTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, endDate);
|
||||
|
||||
// 尖单价 精确到小数点后五位(尖电费+尖服务费,见费率帧)
|
||||
startIndex += length;
|
||||
length = 4;
|
||||
byte[] sharpPriceByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String sharpPrice = YKCUtils.convertDecimalPoint(sharpPriceByteArr, 5);
|
||||
|
||||
// 尖电量 精确到小数点后四位
|
||||
startIndex += length;
|
||||
length = 4;
|
||||
byte[] sharpUsedElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String sharpUsedElectricity = YKCUtils.convertDecimalPoint(sharpUsedElectricityByteArr, 4);
|
||||
|
||||
// 计损尖电量
|
||||
startIndex += length;
|
||||
byte[] sharpPlanLossElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String sharpPlanLossElectricity = YKCUtils.convertDecimalPoint(sharpPlanLossElectricityByteArr, 4);
|
||||
|
||||
// 尖金额
|
||||
startIndex += length;
|
||||
byte[] sharpAmountByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String sharpAmount = YKCUtils.convertDecimalPoint(sharpAmountByteArr, 4);
|
||||
|
||||
// 峰单价 精确到小数点后五位(峰电费+峰服务费)
|
||||
startIndex += length;
|
||||
byte[] peakPriceByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String peakPrice = YKCUtils.convertDecimalPoint(peakPriceByteArr, 5);
|
||||
|
||||
// 峰电量
|
||||
startIndex += length;
|
||||
byte[] peakUsedElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String peakUsedElectricity = YKCUtils.convertDecimalPoint(peakUsedElectricityByteArr, 4);
|
||||
|
||||
// 计损峰电量
|
||||
startIndex += length;
|
||||
byte[] peakPlanLossElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String peakPlanLossElectricity = YKCUtils.convertDecimalPoint(peakPlanLossElectricityByteArr, 4);
|
||||
|
||||
// 峰金额
|
||||
startIndex += length;
|
||||
byte[] peakAmountByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String peakAmount = YKCUtils.convertDecimalPoint(peakAmountByteArr, 4);
|
||||
|
||||
// 平单价 精确到小数点后五位(平电费+平服务费)
|
||||
startIndex += length;
|
||||
byte[] flatPriceByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String flatPrice = YKCUtils.convertDecimalPoint(flatPriceByteArr, 5);
|
||||
|
||||
// 平电量
|
||||
startIndex += length;
|
||||
byte[] flatUsedElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String flatUsedElectricity = YKCUtils.convertDecimalPoint(flatUsedElectricityByteArr, 4);
|
||||
|
||||
// 计损平电量
|
||||
startIndex += length;
|
||||
byte[] flatPlanLossElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String flatPlanLossElectricity = YKCUtils.convertDecimalPoint(flatPlanLossElectricityByteArr, 4);
|
||||
|
||||
// 平金额
|
||||
startIndex += length;
|
||||
byte[] flatAmountByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String flatAmount = YKCUtils.convertDecimalPoint(flatAmountByteArr, 4);
|
||||
|
||||
// 谷单价 精确到小数点后五位(谷电费+谷 服务费)
|
||||
startIndex += length;
|
||||
byte[] valleyPriceByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String valleyPrice = YKCUtils.convertDecimalPoint(valleyPriceByteArr, 5);
|
||||
|
||||
// 谷电量
|
||||
startIndex += length;
|
||||
byte[] valleyUsedElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String valleyUsedElectricity = YKCUtils.convertDecimalPoint(valleyUsedElectricityByteArr, 4);
|
||||
|
||||
// 计损谷电量
|
||||
startIndex += length;
|
||||
byte[] valleyPlanLossElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String valleyPlanLossElectricity = YKCUtils.convertDecimalPoint(valleyPlanLossElectricityByteArr, 4);
|
||||
|
||||
// 谷金额
|
||||
startIndex += length;
|
||||
byte[] valleyAmountByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String valleyAmount = YKCUtils.convertDecimalPoint(valleyAmountByteArr, 4);
|
||||
|
||||
// 电表总起值
|
||||
startIndex += length;
|
||||
length = 5;
|
||||
byte[] ammeterTotalStartByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String ammeterTotalStart = YKCUtils.convertDecimalPoint(ammeterTotalStartByteArr, 4);
|
||||
|
||||
// 电表总止值
|
||||
startIndex += length;
|
||||
byte[] ammeterTotalEndByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String ammeterTotalEnd = YKCUtils.convertDecimalPoint(ammeterTotalEndByteArr, 4);
|
||||
|
||||
// 总电量
|
||||
startIndex += length;
|
||||
length = 4;
|
||||
byte[] totalElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
// String totalElectricity = YKCUtils.convertDecimalPoint(totalElectricityByteArr, 4);
|
||||
|
||||
// 计损总电量
|
||||
startIndex += length;
|
||||
byte[] planLossTotalElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String planLossTotalElectricity = YKCUtils.convertDecimalPoint(planLossTotalElectricityByteArr, 4);
|
||||
|
||||
// 消费金额 精确到小数点后四位,包含电费、 服务费
|
||||
startIndex += length;
|
||||
byte[] consumptionAmountByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String consumptionAmount = YKCUtils.convertDecimalPoint(consumptionAmountByteArr, 4);
|
||||
|
||||
// VIN 码 VIN 码,此处 VIN 码和充电时 VIN 码不同, 正序直接上传, 无需补 0 和反序
|
||||
startIndex += length;
|
||||
length = 17;
|
||||
byte[] vinCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String vinCode = BytesUtil.ascii2Str(vinCodeByteArr);
|
||||
|
||||
*//**
|
||||
* 交易标识
|
||||
* 0x01: app 启动
|
||||
* 0x02:卡启动
|
||||
* 0x04:离线卡启动
|
||||
* 0x05: vin 码启动充电
|
||||
*//*
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] transactionIdentifierByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String transactionIdentifier = BytesUtil.bcd2Str(transactionIdentifierByteArr);
|
||||
|
||||
// 交易时间 CP56Time2a 格式
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] transactionTimeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
Date transactionDate = Cp56Time2aUtil.byte2Hdate(transactionTimeByteArr);
|
||||
String transactionTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, transactionDate);
|
||||
|
||||
// 停止原因
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] stopReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String stopReason = BytesUtil.bin2HexStr(stopReasonByteArr);
|
||||
String stopReasonMsg = YKCChargingStopReasonEnum.getMsgByCode(Integer.parseInt(stopReason, 16));
|
||||
|
||||
// 物理卡号 不足 8 位补 0
|
||||
startIndex += length;
|
||||
length = 8;
|
||||
byte[] cardNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
// byte[] logicCardNum = BytesUtil.checkLengthAndBehindAppendZero(cardNumByteArr, 16);
|
||||
String logicCard = BytesUtil.binary(cardNumByteArr, 16);
|
||||
log.info("桩号:{}, 发送交易记录物理卡号:{}", pileSn, logicCard);
|
||||
|
||||
log.info("[===交易记录===]交易流水号:{}, 桩编号:{}, 枪号:{}, 开始时间:{}, 结束时间:{}, 尖单价:{}, 尖电量:{}, 计损尖电量:{}, 尖金额:{}, " +
|
||||
"峰单价:{}, 峰电量:{}, 计损峰电量:{}, 峰金额:{}, 平单价:{}, 平电量:{}, 计损平电量:{}, 平金额:{}, " +
|
||||
"谷单价:{}, 谷电量:{}, 计损谷电量:{}, 谷金额:{}, 电表总起值:{}, 电表总止值:{}, 总电量:{}, 计损总电量:{}, 消费金额:{}, " +
|
||||
"电动汽车唯一标识:{}, 交易标识:{}, 交易日期、时间:{}, 停止原因码:{}, 停止原因描述:{}, 物理卡号:{}",
|
||||
transactionCode, pileSn, connectorCode, startTime, endTime, sharpPrice, sharpUsedElectricity, sharpPlanLossElectricity, sharpAmount,
|
||||
peakPrice, peakUsedElectricity, peakPlanLossElectricity, peakAmount, flatPrice, flatUsedElectricity, flatPlanLossElectricity, flatAmount,
|
||||
valleyPrice, valleyUsedElectricity, valleyPlanLossElectricity, valleyAmount, ammeterTotalStart, ammeterTotalEnd, totalElectricity, planLossTotalElectricity,
|
||||
consumptionAmount, vinCode, transactionIdentifier, transactionTime, stopReason, stopReasonMsg, logicCard);
|
||||
|
||||
// 交易记录封装到对象里
|
||||
TransactionRecordsData data = TransactionRecordsData.builder()
|
||||
// .orderCode(transactionCode)
|
||||
.transactionCode(transactionCode)
|
||||
.pileSn(pileSn)
|
||||
.connectorCode(connectorCode)
|
||||
.startTime(startTime)
|
||||
.endTime(endTime)
|
||||
.sharpPrice(sharpPrice)
|
||||
.sharpUsedElectricity(sharpUsedElectricity)
|
||||
.sharpPlanLossElectricity(sharpPlanLossElectricity)
|
||||
.sharpAmount(sharpAmount)
|
||||
.peakPrice(peakPrice)
|
||||
.peakUsedElectricity(peakUsedElectricity)
|
||||
.peakPlanLossElectricity(peakPlanLossElectricity)
|
||||
.peakAmount(peakAmount)
|
||||
.flatPrice(flatPrice)
|
||||
.flatUsedElectricity(flatUsedElectricity)
|
||||
.flatPlanLossElectricity(flatPlanLossElectricity)
|
||||
.flatAmount(flatAmount)
|
||||
.valleyPrice(valleyPrice)
|
||||
.valleyUsedElectricity(valleyUsedElectricity)
|
||||
.valleyPlanLossElectricity(valleyPlanLossElectricity)
|
||||
.valleyAmount(valleyAmount)
|
||||
.ammeterTotalStart(ammeterTotalStart)
|
||||
.ammeterTotalEnd(ammeterTotalEnd)
|
||||
// .totalElectricity(totalElectricity)
|
||||
.planLossTotalElectricity(planLossTotalElectricity)
|
||||
.consumptionAmount(consumptionAmount)
|
||||
.vinCode(vinCode)
|
||||
.transactionIdentifier(transactionIdentifier)
|
||||
.transactionTime(transactionTime)
|
||||
.stopReasonMsg(stopReasonMsg)
|
||||
.logicCard(logicCard)
|
||||
.build();
|
||||
|
||||
boolean flag = !StringUtils.equals("0000000000000000", "a511101970000000");
|
||||
System.out.println(flag);
|
||||
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===交易记录===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
// 获取消息体
|
||||
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);
|
||||
|
||||
|
||||
// 开始时间 CP56Time2a 格式
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] startTimeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
// String binary = BytesUtil.binary(startTimeByteArr, 16);
|
||||
Date startDate = Cp56Time2aUtil.byte2Hdate(startTimeByteArr);
|
||||
String startTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, startDate);
|
||||
|
||||
|
||||
// 结束时间 CP56Time2a 格式
|
||||
startIndex += length;
|
||||
byte[] endTimeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
Date endDate = Cp56Time2aUtil.byte2Hdate(endTimeByteArr);
|
||||
String endTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, endDate);
|
||||
|
||||
// 尖单价 精确到小数点后五位(尖电费+尖服务费,见费率帧)
|
||||
startIndex += length;
|
||||
length = 4;
|
||||
byte[] sharpPriceByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String sharpPrice = YKCUtils.convertDecimalPoint(sharpPriceByteArr, 5);
|
||||
|
||||
// 尖电量 精确到小数点后四位
|
||||
startIndex += length;
|
||||
length = 4;
|
||||
byte[] sharpUsedElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String sharpUsedElectricity = YKCUtils.convertDecimalPoint(sharpUsedElectricityByteArr, 4);
|
||||
|
||||
// 计损尖电量
|
||||
startIndex += length;
|
||||
byte[] sharpPlanLossElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String sharpPlanLossElectricity = YKCUtils.convertDecimalPoint(sharpPlanLossElectricityByteArr, 4);
|
||||
|
||||
// 尖金额
|
||||
startIndex += length;
|
||||
byte[] sharpAmountByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String sharpAmount = YKCUtils.convertDecimalPoint(sharpAmountByteArr, 4);
|
||||
|
||||
// 峰单价 精确到小数点后五位(峰电费+峰服务费)
|
||||
startIndex += length;
|
||||
byte[] peakPriceByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String peakPrice = YKCUtils.convertDecimalPoint(peakPriceByteArr, 5);
|
||||
|
||||
// 峰电量
|
||||
startIndex += length;
|
||||
byte[] peakUsedElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String peakUsedElectricity = YKCUtils.convertDecimalPoint(peakUsedElectricityByteArr, 4);
|
||||
|
||||
// 计损峰电量
|
||||
startIndex += length;
|
||||
byte[] peakPlanLossElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String peakPlanLossElectricity = YKCUtils.convertDecimalPoint(peakPlanLossElectricityByteArr, 4);
|
||||
|
||||
// 峰金额
|
||||
startIndex += length;
|
||||
byte[] peakAmountByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String peakAmount = YKCUtils.convertDecimalPoint(peakAmountByteArr, 4);
|
||||
|
||||
// 平单价 精确到小数点后五位(平电费+平服务费)
|
||||
startIndex += length;
|
||||
byte[] flatPriceByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String flatPrice = YKCUtils.convertDecimalPoint(flatPriceByteArr, 5);
|
||||
|
||||
// 平电量
|
||||
startIndex += length;
|
||||
byte[] flatUsedElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String flatUsedElectricity = YKCUtils.convertDecimalPoint(flatUsedElectricityByteArr, 4);
|
||||
|
||||
// 计损平电量
|
||||
startIndex += length;
|
||||
byte[] flatPlanLossElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String flatPlanLossElectricity = YKCUtils.convertDecimalPoint(flatPlanLossElectricityByteArr, 4);
|
||||
|
||||
// 平金额
|
||||
startIndex += length;
|
||||
byte[] flatAmountByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String flatAmount = YKCUtils.convertDecimalPoint(flatAmountByteArr, 4);
|
||||
|
||||
// 谷单价 精确到小数点后五位(谷电费+谷 服务费)
|
||||
startIndex += length;
|
||||
byte[] valleyPriceByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String valleyPrice = YKCUtils.convertDecimalPoint(valleyPriceByteArr, 5);
|
||||
|
||||
// 谷电量
|
||||
startIndex += length;
|
||||
byte[] valleyUsedElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String valleyUsedElectricity = YKCUtils.convertDecimalPoint(valleyUsedElectricityByteArr, 4);
|
||||
|
||||
// 计损谷电量
|
||||
startIndex += length;
|
||||
byte[] valleyPlanLossElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String valleyPlanLossElectricity = YKCUtils.convertDecimalPoint(valleyPlanLossElectricityByteArr, 4);
|
||||
|
||||
// 谷金额
|
||||
startIndex += length;
|
||||
byte[] valleyAmountByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String valleyAmount = YKCUtils.convertDecimalPoint(valleyAmountByteArr, 4);
|
||||
|
||||
// 电表总起值
|
||||
startIndex += length;
|
||||
length = 5;
|
||||
byte[] ammeterTotalStartByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String ammeterTotalStart = YKCUtils.convertDecimalPoint(ammeterTotalStartByteArr, 4);
|
||||
|
||||
// 电表总止值
|
||||
startIndex += length;
|
||||
byte[] ammeterTotalEndByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String ammeterTotalEnd = YKCUtils.convertDecimalPoint(ammeterTotalEndByteArr, 4);
|
||||
|
||||
// 总电量
|
||||
startIndex += length;
|
||||
length = 4;
|
||||
byte[] totalElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String totalElectricity = YKCUtils.convertDecimalPoint(totalElectricityByteArr, 4);
|
||||
|
||||
// 计损总电量
|
||||
startIndex += length;
|
||||
byte[] planLossTotalElectricityByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String planLossTotalElectricity = YKCUtils.convertDecimalPoint(planLossTotalElectricityByteArr, 4);
|
||||
|
||||
// 消费金额 精确到小数点后四位,包含电费、 服务费
|
||||
startIndex += length;
|
||||
byte[] consumptionAmountByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String consumptionAmount = YKCUtils.convertDecimalPoint(consumptionAmountByteArr, 4);
|
||||
|
||||
// VIN 码 VIN 码,此处 VIN 码和充电时 VIN 码不同, 正序直接上传, 无需补 0 和反序
|
||||
startIndex += length;
|
||||
length = 17;
|
||||
byte[] vinCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String vinCode = YKCUtils.parseVin(vinCodeByteArr); // 解析vin
|
||||
|
||||
/**
|
||||
* 交易标识
|
||||
* 0x01: app 启动
|
||||
* 0x02:卡启动
|
||||
* 0x04:离线卡启动
|
||||
* 0x05: vin 码启动充电
|
||||
*/
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] transactionIdentifierByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String transactionIdentifier = BytesUtil.bcd2Str(transactionIdentifierByteArr);
|
||||
|
||||
// 交易时间 CP56Time2a 格式
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] transactionTimeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
Date transactionDate = Cp56Time2aUtil.byte2Hdate(transactionTimeByteArr);
|
||||
String transactionTime = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, transactionDate);
|
||||
|
||||
// 停止原因
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] stopReasonByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String stopReason = BytesUtil.bin2HexStr(stopReasonByteArr);
|
||||
int i = Integer.parseInt(stopReason, 16);
|
||||
String stopReasonMsg = YKCChargingStopReasonEnum.getMsgByCode(i);
|
||||
|
||||
// 物理卡号 不足 8 位补 0
|
||||
startIndex += length;
|
||||
length = 8;
|
||||
byte[] cardNumByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
// log.info("交易记录确认cardNumByteArr:{}", cardNumByteArr);
|
||||
String logicCard = BytesUtil.binary(cardNumByteArr, 16);
|
||||
// log.info("桩号:{}, 发送交易记录物理卡号:{}", pileSn, logicCard);
|
||||
|
||||
log.info("[===交易记录===]交易流水号:{}, 桩编号:{}, 枪号:{}, 开始时间:{}, 结束时间:{}, 尖单价:{}, 尖电量:{}, 计损尖电量:{}, 尖金额:{}, " +
|
||||
"峰单价:{}, 峰电量:{}, 计损峰电量:{}, 峰金额:{}, 平单价:{}, 平电量:{}, 计损平电量:{}, 平金额:{}, " +
|
||||
"谷单价:{}, 谷电量:{}, 计损谷电量:{}, 谷金额:{}, 电表总起值:{}, 电表总止值:{}, 总电量:{}, 计损总电量:{}, 消费金额:{}, " +
|
||||
"vin码:{}, 交易标识:{}, 交易日期、时间:{}, 停止原因码:{}, 停止原因描述:{}, 物理卡号:{}",
|
||||
transactionCode, pileSn, connectorCode, startTime, endTime, sharpPrice, sharpUsedElectricity, sharpPlanLossElectricity, sharpAmount,
|
||||
peakPrice, peakUsedElectricity, peakPlanLossElectricity, peakAmount, flatPrice, flatUsedElectricity, flatPlanLossElectricity, flatAmount,
|
||||
valleyPrice, valleyUsedElectricity, valleyPlanLossElectricity, valleyAmount, ammeterTotalStart, ammeterTotalEnd, totalElectricity, planLossTotalElectricity,
|
||||
consumptionAmount, vinCode, transactionIdentifier, transactionTime, stopReason, stopReasonMsg, logicCard);
|
||||
|
||||
// 交易记录封装到对象里
|
||||
TransactionRecordsData data = TransactionRecordsData.builder()
|
||||
// .orderCode(transactionCode)
|
||||
.transactionCode(transactionCode)
|
||||
.pileSn(pileSn)
|
||||
.connectorCode(connectorCode)
|
||||
.startTime(startTime)
|
||||
.endTime(endTime)
|
||||
.sharpPrice(sharpPrice)
|
||||
.sharpUsedElectricity(sharpUsedElectricity)
|
||||
.sharpPlanLossElectricity(sharpPlanLossElectricity)
|
||||
.sharpAmount(sharpAmount)
|
||||
.peakPrice(peakPrice)
|
||||
.peakUsedElectricity(peakUsedElectricity)
|
||||
.peakPlanLossElectricity(peakPlanLossElectricity)
|
||||
.peakAmount(peakAmount)
|
||||
.flatPrice(flatPrice)
|
||||
.flatUsedElectricity(flatUsedElectricity)
|
||||
.flatPlanLossElectricity(flatPlanLossElectricity)
|
||||
.flatAmount(flatAmount)
|
||||
.valleyPrice(valleyPrice)
|
||||
.valleyUsedElectricity(valleyUsedElectricity)
|
||||
.valleyPlanLossElectricity(valleyPlanLossElectricity)
|
||||
.valleyAmount(valleyAmount)
|
||||
.ammeterTotalStart(ammeterTotalStart)
|
||||
.ammeterTotalEnd(ammeterTotalEnd)
|
||||
.totalElectricity(totalElectricity)
|
||||
.planLossTotalElectricity(planLossTotalElectricity)
|
||||
.consumptionAmount(consumptionAmount)
|
||||
.vinCode(vinCode)
|
||||
.transactionIdentifier(transactionIdentifier)
|
||||
.transactionTime(transactionTime)
|
||||
.stopReasonMsg(stopReasonMsg)
|
||||
.logicCard(logicCard)
|
||||
.build();
|
||||
|
||||
// 保存报文
|
||||
String jsonMsg = JSON.toJSONString(data);
|
||||
pileMsgRecordService.save(pileSn, pileSn + connectorCode, type, jsonMsg, ykcDataProtocol.getHEXString());
|
||||
|
||||
// 处理订单加锁
|
||||
String lockKey = "settle_order_" + transactionCode;
|
||||
String uuid = IdUtils.fastUUID();
|
||||
try {
|
||||
// redis锁
|
||||
Boolean isLock = redisCache.lock(lockKey, uuid, 1500);
|
||||
if (isLock) {
|
||||
processOrder(data);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("处理订单transactionCode:{}, 发生异常", transactionCode, e);
|
||||
} finally {
|
||||
if (uuid.equals(redisCache.getCacheObject(lockKey).toString())) {
|
||||
redisCache.unLock(lockKey);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
应答
|
||||
确认结果 0x00 上传成功 0x01 非法账单
|
||||
2022年12月15日11点28分发现返回 01非法账单,充电桩会持续上传交易记录,后面产生的交易记录被阻塞
|
||||
*/
|
||||
byte[] confirmResultBytes = Constants.zeroByteArray;
|
||||
byte[] concatMsgBody = Bytes.concat(transactionCodeByteArr, confirmResultBytes);
|
||||
|
||||
return YKCUtils.getResult(ykcDataProtocol, concatMsgBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到交易记录 处理订单
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
private void processOrder(TransactionRecordsData data) {
|
||||
String pileSn = data.getPileSn(); // 充电桩编号
|
||||
PileBasicInfo pileBasicInfo = pileBasicInfoService.selectPileBasicInfoBySN(pileSn);
|
||||
if (StringUtils.equals(pileBasicInfo.getBusinessType(), Constants.TWO)) {
|
||||
personalChargingRecordService.processPersonalChargingRecord(data);
|
||||
// return;
|
||||
}
|
||||
|
||||
String transactionCode = data.getTransactionCode();
|
||||
// 根据交易流水号查询订单信息
|
||||
OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByTransactionCode(transactionCode);
|
||||
if (orderBasicInfo != null) {
|
||||
// 平台存在订单
|
||||
orderBasicInfo.setReason(data.getStopReasonMsg());
|
||||
// 如果订单状态为 异常,则改为 待结算
|
||||
if (StringUtils.equals(OrderStatusEnum.ABNORMAL.getValue(), orderBasicInfo.getOrderStatus())) {
|
||||
orderBasicInfo.setOrderStatus(OrderStatusEnum.STAY_SETTLEMENT.getValue());
|
||||
}
|
||||
// 校验一下开始时间和结束时间,防止充电中桩离线,时间不准确
|
||||
if (Objects.isNull(orderBasicInfo.getChargeStartTime())) { // 开始时间
|
||||
orderBasicInfo.setChargeStartTime(DateUtils.parseDate(data.getStartTime()));
|
||||
}
|
||||
if (Objects.isNull(orderBasicInfo.getChargeEndTime())) { // 结束时间
|
||||
orderBasicInfo.setChargeEndTime(DateUtils.parseDate(data.getEndTime()));
|
||||
}
|
||||
|
||||
// 校验结束时间
|
||||
if (orderBasicInfo.getChargeEndTime().before(orderBasicInfo.getChargeStartTime())) {
|
||||
orderBasicInfo.setChargeEndTime(new Date());
|
||||
}
|
||||
|
||||
orderBasicInfoService.updateOrderBasicInfo(orderBasicInfo);
|
||||
|
||||
// 重新查询订单信息
|
||||
orderBasicInfo = orderBasicInfoService.getOrderInfoByTransactionCode(transactionCode);
|
||||
|
||||
// 结算订单操作
|
||||
try {
|
||||
// 新逻辑
|
||||
String mode = pileMerchantInfoService.getDelayModeByMerchantId(orderBasicInfo.getMerchantId());
|
||||
AbstractProgramLogic orderLogic = ProgramLogicFactory.getProgramLogic(mode);
|
||||
orderLogic.settleOrder(data, orderBasicInfo);
|
||||
} catch (Exception e) {
|
||||
log.error("结算订单发生异常 orderCode:{}", orderBasicInfo.getOrderCode(), e);
|
||||
}
|
||||
|
||||
// 异步绑定第三方平台优惠券
|
||||
OrderBasicInfo finalOrderBasicInfo = orderBasicInfo;
|
||||
String redisKey = CacheConstants.CAR_BIND_COUPON_BY_ORDER_CODE + orderBasicInfo.getOrderCode();
|
||||
Object cacheObject = redisCache.getCacheObject(redisKey);
|
||||
if (cacheObject == null) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
String bindResult = commonService.bindCoupon(finalOrderBasicInfo);
|
||||
log.info("绑定优惠券 订单信息:{}, result:{}", finalOrderBasicInfo, bindResult);
|
||||
// 删除绑定优惠券缓存
|
||||
redisCache.deleteObject(redisKey);
|
||||
} catch (Exception e) {
|
||||
log.error("绑定优惠券 error,", e);
|
||||
}
|
||||
}, executor);
|
||||
}
|
||||
|
||||
// 异步推送第三方平台订单信息
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
commonService.commonPushOrderInfo(finalOrderBasicInfo);
|
||||
} catch (Exception e) {
|
||||
log.error("推送第三方平台订单信息error, ", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, executor);
|
||||
|
||||
// 异步推送第三方平台订单信息V2
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
commonService.commonPushOrderInfoV2(finalOrderBasicInfo);
|
||||
} catch (Exception e) {
|
||||
log.error("推送第三方平台订单信息error, ", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, executor);
|
||||
} else {
|
||||
// 平台没有查到订单
|
||||
orderBasicInfoService.saveAbnormalOrder(data);
|
||||
log.warn("充电桩传来的交易记录,根据交易流水号:{}查询不到订单,判定为可疑账单", transactionCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
package com.jsowell.netty.strategy.ykc;
|
||||
|
||||
import com.jsowell.common.constant.CacheConstants;
|
||||
import com.jsowell.common.constant.Constants;
|
||||
import com.jsowell.common.core.domain.ykc.RealTimeMonitorData;
|
||||
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.ykc.OrderPayStatusEnum;
|
||||
import com.jsowell.common.enums.ykc.OrderStatusEnum;
|
||||
import com.jsowell.common.enums.ykc.PileConnectorStatusEnum;
|
||||
import com.jsowell.common.enums.ykc.YKCPileFaultReasonEnum;
|
||||
import com.jsowell.common.util.BytesUtil;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.common.util.YKCUtils;
|
||||
import com.jsowell.common.util.spring.SpringUtils;
|
||||
import com.jsowell.pile.domain.OrderBasicInfo;
|
||||
import com.jsowell.pile.service.OrderBasicInfoService;
|
||||
import com.jsowell.pile.service.PileBasicInfoService;
|
||||
import com.jsowell.thirdparty.common.CommonService;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 获取桩上传的实时监测数据 0x13
|
||||
*
|
||||
* @author JS-ZZA
|
||||
* @date 2022/9/19 9:08
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class UploadRealTimeMonitorStrategy implements AbstractYkcStrategy {
|
||||
private final String type = YKCUtils.frameType2Str(YKCFrameTypeCode.UPLOAD_REAL_TIME_MONITOR_DATA_CODE.getBytes());
|
||||
private final String oldVersionType = YKCUtils.frameType2Str(YKCFrameTypeCode.UPLOAD_REAL_TIME_MONITOR_DATA_OLD_VERSION_CODE.getBytes());
|
||||
|
||||
// @Override
|
||||
// public void afterPropertiesSet() throws Exception {
|
||||
// YKCOperateFactory.register(type, this);
|
||||
// YKCOperateFactory.register(oldVersionType, this);
|
||||
// }
|
||||
|
||||
// 引入线程池
|
||||
private ThreadPoolTaskExecutor executor = SpringUtils.getBean("threadPoolTaskExecutor");
|
||||
|
||||
@Autowired
|
||||
private PileBasicInfoService pileBasicInfoService;
|
||||
|
||||
@Autowired
|
||||
private OrderBasicInfoService orderBasicInfoService;
|
||||
|
||||
@Autowired
|
||||
private CommonService commonService;
|
||||
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] supplyProcess(YKCDataProtocol ykcDataProtocol, ChannelHandlerContext channel) {
|
||||
// log.info("[===获取桩上传的实时监测数据===] param:{}, channel:{}", JSON.toJSONString(ykcDataProtocol), channel.toString());
|
||||
RealTimeMonitorData realTimeMonitorData = new RealTimeMonitorData();
|
||||
|
||||
// 获取消息体
|
||||
byte[] msgBody = ykcDataProtocol.getMsgBody();
|
||||
// log.info("上传实时数据msgBody:{}", BytesUtil.bcd2Str(msgBody));
|
||||
int startIndex = 0;
|
||||
int length = 16;
|
||||
|
||||
// 交易流水号
|
||||
byte[] orderCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String transactionCode = BytesUtil.bcd2Str(orderCodeByteArr);
|
||||
realTimeMonitorData.setTransactionCode(transactionCode);
|
||||
|
||||
// 桩编码
|
||||
startIndex += length;
|
||||
length = 7;
|
||||
byte[] pileSnByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String pileSn = BytesUtil.bcd2Str(pileSnByteArr);
|
||||
realTimeMonitorData.setPileSn(pileSn);
|
||||
|
||||
// 保存时间
|
||||
saveLastTimeAndCheckChannel(pileSn, channel);
|
||||
|
||||
// 枪号
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] connectorCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String connectorCode = BytesUtil.bcd2Str(connectorCodeByteArr);
|
||||
realTimeMonitorData.setConnectorCode(connectorCode);
|
||||
|
||||
// 枪口编号
|
||||
String pileConnectorCode = pileSn + connectorCode;
|
||||
|
||||
// 枪口状态 0x00:离线 0x01:故障 0x02:空闲 0x03:充电 0x04 预约中
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] connectorStatusByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String connectorStatus = BytesUtil.bcd2Str(connectorStatusByteArr);
|
||||
realTimeMonitorData.setConnectorStatus(connectorStatus);
|
||||
|
||||
// 是否归位 0x00:否 0x01:是 0x02:未知(无法检测到枪是否插回枪座即 未知)
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] homingFlagByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String homingFlag = BytesUtil.bcd2Str(homingFlagByteArr);
|
||||
realTimeMonitorData.setHomingFlag(homingFlag);
|
||||
|
||||
// 是否插枪 0x00:否 0x01:是
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] isChargerPluggedInByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String isChargerPluggedIn = BytesUtil.bcd2Str(isChargerPluggedInByteArr);
|
||||
realTimeMonitorData.setPutGunType(isChargerPluggedIn);
|
||||
|
||||
// 输出电压 精确到小数点后一位;待机置零
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] outputVoltageByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String outputVoltage = YKCUtils.convertVoltageCurrent(outputVoltageByteArr);
|
||||
realTimeMonitorData.setOutputVoltage(outputVoltage);
|
||||
|
||||
// 输出电流 精确到小数点后一位;待机置零
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] outputCurrentByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String outputCurrent = YKCUtils.convertVoltageCurrent(outputCurrentByteArr);
|
||||
realTimeMonitorData.setOutputCurrent(outputCurrent);
|
||||
|
||||
// 枪线温度 整形, 偏移量-50;待机置零
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] gunLineTemperatureByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String gunLineTemperature = YKCUtils.transitionTemperature(gunLineTemperatureByteArr);
|
||||
realTimeMonitorData.setGunLineTemperature(gunLineTemperature);
|
||||
|
||||
// 枪线编码 没有置零
|
||||
startIndex += length;
|
||||
length = 8;
|
||||
byte[] gunLineCodeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String gunLineCode = BytesUtil.bcd2Str(gunLineCodeByteArr);
|
||||
realTimeMonitorData.setGunLineCode(gunLineCode);
|
||||
|
||||
// SOC 待机置零;交流桩置零
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] SOCByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String SOC = String.valueOf(SOCByteArr[0]);
|
||||
realTimeMonitorData.setSOC(SOC);
|
||||
|
||||
// 电池组最高温度 整形, 偏移量-50 ºC;待机置零; 交流桩置零
|
||||
startIndex += length;
|
||||
length = 1;
|
||||
byte[] batteryMaxTemperatureByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String batteryMaxTemperature = YKCUtils.transitionTemperature(batteryMaxTemperatureByteArr);
|
||||
realTimeMonitorData.setBatteryMaxTemperature(batteryMaxTemperature);
|
||||
|
||||
// 累计充电时间 单位: min;待机置零
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] sumChargingTimeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
int sumChargingTime = BytesUtil.bytesToIntLittle(sumChargingTimeByteArr);
|
||||
realTimeMonitorData.setSumChargingTime(String.valueOf(sumChargingTime));
|
||||
|
||||
// 剩余时间 单位: min;待机置零、交流桩置零
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] timeRemainingByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
int timeRemaining = BytesUtil.bytesToIntLittle(timeRemainingByteArr);
|
||||
realTimeMonitorData.setTimeRemaining(String.valueOf(timeRemaining));
|
||||
|
||||
// 充电度数 精确到小数点后四位;待机置零
|
||||
startIndex += length;
|
||||
length = 4;
|
||||
byte[] chargingDegreeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String chargingDegree = YKCUtils.convertDecimalPoint(chargingDegreeByteArr, 4);
|
||||
realTimeMonitorData.setChargingDegree(chargingDegree);
|
||||
|
||||
// 计损充电度数 精确到小数点后四位;待机置零 未设置计损比例时等于充电度数
|
||||
startIndex += length;
|
||||
length = 4;
|
||||
byte[] lossDegreeByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String lossDegree = YKCUtils.convertDecimalPoint(lossDegreeByteArr, 4);
|
||||
realTimeMonitorData.setLossDegree(lossDegree);
|
||||
|
||||
// 已充金额 精确到小数点后四位;待机置零 (电费+服务费) *计损充电度数
|
||||
startIndex += length;
|
||||
length = 4;
|
||||
byte[] chargingAmountByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String chargingAmount = YKCUtils.convertDecimalPoint(chargingAmountByteArr, 4);
|
||||
realTimeMonitorData.setChargingAmount(chargingAmount);
|
||||
|
||||
/**
|
||||
* 硬件故障
|
||||
*
|
||||
* Bit 位表示(0 否 1 是), 低位到高位顺序
|
||||
* Bit1:急停按钮动作故障;
|
||||
* Bit2:无可用整流模块;
|
||||
* Bit3:出风口温度过高;
|
||||
* Bit4:交流防雷故障;
|
||||
* Bit5:交直流模块 DC20 通信中断;
|
||||
* Bit6:绝缘检测模块 FC08 通信中断;
|
||||
* Bit7:电度表通信中断;
|
||||
* Bit8:读卡器通信中断;
|
||||
* Bit9: RC10 通信中断;
|
||||
* Bit10:风扇调速板故障;
|
||||
* Bit11:直流熔断器故障;
|
||||
* Bit12:高压接触器故障;
|
||||
* Bit13:门打开;
|
||||
*/
|
||||
startIndex += length;
|
||||
length = 2;
|
||||
byte[] hardwareFaultTempByteArr = BytesUtil.copyBytes(msgBody, startIndex, length);
|
||||
String hardwareFaultTemp = BytesUtil.bcd2Str(hardwareFaultTempByteArr);
|
||||
String faultReason = "无";
|
||||
|
||||
if (!StringUtils.equals(hardwareFaultTemp, "0000")) {
|
||||
// 不等于0000说明有故障
|
||||
StringBuffer sb = new StringBuffer(hardwareFaultTemp);
|
||||
String lowOrder = sb.substring(0, 2);
|
||||
String highOrder = sb.substring(2, 4);
|
||||
|
||||
// String hardwareFault = highOrder + lowOrder;
|
||||
byte[] hardwareFaultByteArr = BytesUtil.str2Bcd(highOrder + lowOrder);
|
||||
String binStr = BytesUtil.bytes2BinStr(hardwareFaultByteArr);
|
||||
// log.info("binStr:{}", binStr); // 0000 0000 0000 0001
|
||||
int faultCode = 0;
|
||||
for (int i = 0; i < binStr.length(); i++) {
|
||||
if (binStr.charAt(i) == '1') {
|
||||
faultCode = 15 - i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
faultReason = YKCPileFaultReasonEnum.getValueByCode(faultCode);
|
||||
}
|
||||
realTimeMonitorData.setHardwareFault(faultReason);
|
||||
|
||||
if (!StringUtils.equals(connectorStatus, PileConnectorStatusEnum.FREE.getValue())) {
|
||||
log.info("0x13上传实时监测数据==交易流水号:{}, 桩编号:{}, 枪口号:{}, 枪口状态:{}, 枪口状态描述:{}, 枪是否归位(00-否;01-是;02-未知):{}, 是否插枪(00-否;01-是):{}, 输出电压:{}, 输出电流:{}, 枪线温度:{}, " +
|
||||
"枪线编码:{}, SOC:{}, 电池组最高温度:{}, 累计充电时间:{}, 剩余时间:{}, 充电度数:{}, 记损充电度数:{}, 已充金额:{}, " +
|
||||
"硬件故障:{}, 故障码转换结果:{}", transactionCode, pileSn, connectorCode, connectorStatus, PileConnectorStatusEnum.getLabelByValue(connectorStatus), homingFlag, isChargerPluggedIn, outputVoltage,
|
||||
outputCurrent, gunLineTemperature, gunLineCode, SOC, batteryMaxTemperature, sumChargingTime, timeRemaining,
|
||||
chargingDegree, lossDegree, chargingAmount, hardwareFaultTemp, faultReason
|
||||
);
|
||||
}
|
||||
|
||||
// 插枪状态
|
||||
String plugRedisKey = CacheConstants.CHARGER_PLUGGED_IN_STATUS + pileConnectorCode;
|
||||
if (StringUtils.equals("01", isChargerPluggedIn)) {
|
||||
// 插枪状态
|
||||
if (redisCache.setnx(plugRedisKey, pileConnectorCode, CacheConstants.cache_expire_time_30d)) {
|
||||
// log.info("时间:{}, 枪口号:{}, 首次插入充电枪", DateUtils.getDateTime(), pileConnectorCode);
|
||||
// 设置成功说明 第一次插枪
|
||||
// pileBasicInfoService.firstPlugInCharger(pileConnectorCode);
|
||||
}
|
||||
} else {
|
||||
// 未插枪状态
|
||||
if (redisCache.hasKey(plugRedisKey) && redisCache.deleteObject(plugRedisKey)) {
|
||||
// log.info("时间:{}, 枪口号:{}, 首次拔出充电枪", DateUtils.getDateTime(), pileConnectorCode);
|
||||
// redis有值,并且删除成功,说明首次拔枪
|
||||
// pileBasicInfoService.firstUnplugCharger(pileConnectorCode);
|
||||
}
|
||||
}
|
||||
|
||||
// 公共方法修改状态
|
||||
pileBasicInfoService.updateStatus(BytesUtil.bcd2Str(ykcDataProtocol.getFrameType()), pileSn, connectorCode, connectorStatus, isChargerPluggedIn);
|
||||
|
||||
// 01表示故障
|
||||
if (StringUtils.equals(connectorStatus, PileConnectorStatusEnum.FAULT.getValue())) {
|
||||
// 故障原因存入缓存
|
||||
String redisKey = CacheConstants.PILE_HARDWARE_FAULT + pileConnectorCode;
|
||||
redisCache.setCacheObject(redisKey, faultReason, 5, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
// 03表示充电中
|
||||
if (StringUtils.equals(connectorStatus, PileConnectorStatusEnum.OCCUPIED_CHARGING.getValue())) {
|
||||
// 默认保存到redis
|
||||
boolean saveRedisFlag = true;
|
||||
|
||||
// 查询数据库中该订单当前信息
|
||||
OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByTransactionCode(transactionCode);
|
||||
if (Objects.nonNull(orderInfo)) {
|
||||
if (StringUtils.equals(orderInfo.getOrderStatus(), OrderStatusEnum.ORDER_COMPLETE.getValue())
|
||||
|| StringUtils.equals(orderInfo.getOrderStatus(), OrderStatusEnum.STAY_SETTLEMENT.getValue())) {
|
||||
// 在订单状态为 订单完成或待结算,不保存
|
||||
saveRedisFlag = false;
|
||||
}
|
||||
|
||||
boolean updateFlag = false;
|
||||
if (StringUtils.equals(orderInfo.getOrderStatus(), OrderStatusEnum.NOT_START.getValue())
|
||||
|| StringUtils.equals(orderInfo.getOrderStatus(), OrderStatusEnum.ABNORMAL.getValue())
|
||||
|| StringUtils.equals(orderInfo.getOrderStatus(), OrderStatusEnum.STAY_SETTLEMENT.getValue())) {
|
||||
updateFlag = true;
|
||||
// 如果是未启动状态或者异常状态, 修改这个订单状态为充电中 2023年7月7日新增 如果是待结算状态,也改为充电中
|
||||
orderInfo.setOrderStatus(OrderStatusEnum.IN_THE_CHARGING.getValue());
|
||||
}
|
||||
|
||||
if (StringUtils.equals(orderInfo.getPayStatus(), OrderPayStatusEnum.unpaid.getValue())) {
|
||||
// 如果发现该订单的支付状态为 0-待支付,将该订单支付状态改为 1-支付完成
|
||||
orderInfo.setPayStatus(OrderPayStatusEnum.paid.getValue());
|
||||
}
|
||||
|
||||
// 如果原来没有开始充电时间就保存当前时间为开始充电时间
|
||||
if (orderInfo.getChargeStartTime() == null) {
|
||||
updateFlag = true;
|
||||
orderInfo.setChargeStartTime(new Date());
|
||||
}
|
||||
|
||||
if (updateFlag) {
|
||||
orderBasicInfoService.updateOrderBasicInfo(orderInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// 充电时保存实时数据到redis
|
||||
if (saveRedisFlag) {
|
||||
pileBasicInfoService.saveRealTimeMonitorData2Redis(realTimeMonitorData);
|
||||
}
|
||||
// 判断该订单是否需要下发优惠券
|
||||
String redisKey = CacheConstants.CAR_BIND_COUPON_BY_ORDER_CODE + orderInfo.getOrderCode();
|
||||
Object cacheObject = redisCache.getCacheObject(redisKey);
|
||||
if (cacheObject == null && sumChargingTime >= 10) {
|
||||
// 异步绑定优惠券并设置缓存
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
commonService.bindCoupon(orderInfo);
|
||||
redisCache.setCacheObject(redisKey, Boolean.TRUE, 24, TimeUnit.HOURS);
|
||||
} catch (Exception e) {
|
||||
log.error("异步绑定车辆优惠券 error,", e);
|
||||
}
|
||||
}, executor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 异步推送第三方平台实时数据
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
commonService.pushRealTimeInfo(pileSn, connectorCode, connectorStatus, realTimeMonitorData, transactionCode);
|
||||
} catch (Exception e) {
|
||||
log.error("统一推送第三方平台实时数据 error,", e);
|
||||
}
|
||||
}, executor);
|
||||
|
||||
// 异步推送第三方平台实时数据V2
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
commonService.pushRealTimeInfoV2(pileSn, connectorCode, connectorStatus, realTimeMonitorData, transactionCode);
|
||||
} catch (Exception e) {
|
||||
log.error("统一推送第三方平台实时数据V2 error, ", e);
|
||||
}
|
||||
}, executor);
|
||||
|
||||
if (StringUtils.equals(connectorStatus, Constants.ONE)) {
|
||||
// 故障
|
||||
// 异步推送第三方平台告警信息
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
commonService.commonPushAlarmInfo(pileConnectorCode, connectorStatus, realTimeMonitorData.getPutGunType());
|
||||
} catch (Exception e) {
|
||||
log.error("统一推送第三方平台告警信息 error, ", e);
|
||||
}
|
||||
}, executor);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
StringBuffer sb = new StringBuffer("0100");
|
||||
String lowOrder = sb.substring(0, 2);
|
||||
String highOrder = sb.substring(2, 4);
|
||||
|
||||
// String hardwareFault = highOrder + lowOrder;
|
||||
byte[] hardwareFaultByteArr = BytesUtil.str2Bcd(highOrder + lowOrder);
|
||||
String binStr = BytesUtil.bytes2BinStr(hardwareFaultByteArr);
|
||||
// log.info("binStr:{}", binStr); // 0000 0000 0000 0001
|
||||
int faultCode = 0;
|
||||
for (int i = 0; i < binStr.length(); i++) {
|
||||
if (binStr.charAt(i) == '1') {
|
||||
faultCode = 16 - i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
String faultReason = YKCPileFaultReasonEnum.getValueByCode(faultCode);
|
||||
System.out.println(faultReason);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user