mirror of
https://gitee.com/san-bing/JChargePointProtocol
synced 2026-05-04 18:09:54 +08:00
!47 新增 充电桩主动申请启动充电(0x31)2.运营平台确认启动充电(0x32)
* fix(ProtocolUplinkConsumerService):指标初始化代码恢复 * update:启动方式枚举类调整 * update:增加 0x31、0x32 的枚举类 * update:添加下行日志打印 * add:1.充电桩主动申请启动充电(0x31)2.运营平台确认启动充电(0x32)
This commit is contained in:
@@ -45,6 +45,13 @@
|
||||
|
||||
---
|
||||
|
||||
#### 0x31 充电桩主动申请充电
|
||||
`68 37 00 04 00 31 20 23 12 12 00 00 10 01 01 00 00 00 00 00 D1 4B 0A 54 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FA E8`
|
||||
#### 0x32 运营平台确认启动充电
|
||||
`68 28 04 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 23 12 12 00 00 10 01 00 00 00 00 00 00 00 00 00 00 01 06 29 42`
|
||||
|
||||
---
|
||||
|
||||
#### 0x34 下发启动充电
|
||||
`68 30 01 00 00 34 00 00 00 00 00 00 12 34 56 78 90 12 34 56 78 90 20 23 12 12 00 00 10 01 56 78 90 12 34 56 78 90 56 78 90 12 34 56 78 90 10 27 00 00 5f d9`
|
||||
#### 0x33 上行启动应答
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.protocol.yunkuaichong.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 云快充协议密码验证标志枚举
|
||||
*
|
||||
* @author baiban
|
||||
* @since 2024-12-16
|
||||
*/
|
||||
@Getter
|
||||
public enum YunKuaiChongPasswordRequiredEnum {
|
||||
|
||||
/** 不需要密码 */
|
||||
NOT_REQUIRED(0x00, false, "不需要密码"),
|
||||
|
||||
/** 需要密码 */
|
||||
REQUIRED(0x01, true,"需要密码");
|
||||
|
||||
/** 密码验证标志代码 */
|
||||
private final int code;
|
||||
|
||||
/** 是否需要密码的布尔值 */
|
||||
private final boolean value;
|
||||
|
||||
/** 密码验证标志描述 */
|
||||
private final String description;
|
||||
|
||||
YunKuaiChongPasswordRequiredEnum(int code, boolean value, String description) {
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码获取密码验证标志枚举
|
||||
*
|
||||
* @param code 密码验证标志代码
|
||||
* @return 密码验证标志枚举,未找到时返回NOT_REQUIRED
|
||||
*/
|
||||
public static YunKuaiChongPasswordRequiredEnum fromCode(int code) {
|
||||
for (YunKuaiChongPasswordRequiredEnum passwordRequired : values()) {
|
||||
if (passwordRequired.code == code) {
|
||||
return passwordRequired;
|
||||
}
|
||||
}
|
||||
return NOT_REQUIRED;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码获取密码验证标志描述
|
||||
*
|
||||
* @param code 密码验证标志代码
|
||||
* @return 密码验证标志描述
|
||||
*/
|
||||
public static String getDescription(int code) {
|
||||
return fromCode(code).getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码检查是否需要密码
|
||||
*
|
||||
* @param code 密码验证标志代码
|
||||
* @return true表示需要密码,false表示不需要密码
|
||||
*/
|
||||
public static boolean isPasswordRequired(int code) {
|
||||
return fromCode(code).isValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据布尔值获取对应的枚举
|
||||
*
|
||||
* @param required 是否需要密码
|
||||
* @return 对应的枚举值
|
||||
*/
|
||||
public static YunKuaiChongPasswordRequiredEnum fromBoolean(boolean required) {
|
||||
return required ? REQUIRED : NOT_REQUIRED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.protocol.yunkuaichong.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 云快充协议启动充电失败原因枚举
|
||||
*
|
||||
* @author baiban
|
||||
* @since 2024-12-16
|
||||
*/
|
||||
@Getter
|
||||
public enum YunKuaiChongStartChargeFailureReasonEnum {
|
||||
|
||||
/** 成功(无失败原因) */
|
||||
SUCCESS(0x00, "SUCCESS", "成功"),
|
||||
|
||||
/** 账户不存在 */
|
||||
ACCOUNT_NOT_EXISTS(0x01, "ACCOUNT_NOT_EXISTS", "账户不存在"),
|
||||
|
||||
/** 账户冻结 */
|
||||
ACCOUNT_FROZEN(0x02, "ACCOUNT_FROZEN", "账户冻结"),
|
||||
|
||||
/** 账户余额不足 */
|
||||
INSUFFICIENT_BALANCE(0x03, "INSUFFICIENT_BALANCE", "账户余额不足"),
|
||||
|
||||
/** 该卡存在未结账记录 */
|
||||
CARD_HAS_UNPAID_RECORD(0x04, "CARD_HAS_UNPAID_RECORD", "该卡存在未结账记录"),
|
||||
|
||||
/** 桩停用 */
|
||||
PILE_DISABLED(0x05, "PILE_DISABLED", "桩停用"),
|
||||
|
||||
/** 该账户不能在此桩上充电 */
|
||||
ACCOUNT_NOT_ALLOWED_ON_PILE(0x06, "ACCOUNT_NOT_ALLOWED_ON_PILE", "该账户不能在此桩上充电"),
|
||||
|
||||
/** 密码错误 */
|
||||
PASSWORD_ERROR(0x07, "PASSWORD_ERROR", "密码错误"),
|
||||
|
||||
/** 电站电容不足 */
|
||||
INSUFFICIENT_STATION_CAPACITY(0x08, "INSUFFICIENT_STATION_CAPACITY", "电站电容不足"),
|
||||
|
||||
/** 系统中vin码不存在 */
|
||||
VIN_CODE_NOT_EXISTS(0x09, "VIN_CODE_NOT_EXISTS", "系统中vin码不存在"),
|
||||
|
||||
/** 该桩存在未结账记录 */
|
||||
PILE_HAS_UNPAID_RECORD(0x0A, "PILE_HAS_UNPAID_RECORD", "该桩存在未结账记录"),
|
||||
|
||||
/** 该桩不支持刷卡 */
|
||||
PILE_NOT_SUPPORT_CARD(0x0B, "PILE_NOT_SUPPORT_CARD", "该桩不支持刷卡"),
|
||||
|
||||
/** 未知错误 */
|
||||
UNKNOWN(0xFF, "UNKNOWN", "未知错误");
|
||||
|
||||
/** 失败原因代码 */
|
||||
private final int code;
|
||||
|
||||
/** 失败原因值 */
|
||||
private final String value;
|
||||
|
||||
/** 失败原因描述 */
|
||||
private final String description;
|
||||
|
||||
YunKuaiChongStartChargeFailureReasonEnum(int code, String value, String description) {
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码获取失败原因枚举
|
||||
*
|
||||
* @param code 失败原因代码
|
||||
* @return 失败原因枚举,未找到时返回UNKNOWN
|
||||
*/
|
||||
public static YunKuaiChongStartChargeFailureReasonEnum fromCode(int code) {
|
||||
for (YunKuaiChongStartChargeFailureReasonEnum reason : values()) {
|
||||
if (reason.code == code) {
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据值获取失败原因枚举
|
||||
*
|
||||
* @param value 失败原因值
|
||||
* @return 失败原因枚举,未找到时返回UNKNOWN
|
||||
*/
|
||||
public static YunKuaiChongStartChargeFailureReasonEnum fromValue(String value) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
for (YunKuaiChongStartChargeFailureReasonEnum reason : values()) {
|
||||
if (reason.value.equals(value)) {
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据描述获取失败原因枚举
|
||||
*
|
||||
* @param description 失败原因描述
|
||||
* @return 失败原因枚举,未找到时返回UNKNOWN
|
||||
*/
|
||||
public static YunKuaiChongStartChargeFailureReasonEnum fromDescription(String description) {
|
||||
if (description == null || description.trim().isEmpty()) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
for (YunKuaiChongStartChargeFailureReasonEnum reason : values()) {
|
||||
if (reason.description.equals(description)) {
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码获取失败原因值
|
||||
*
|
||||
* @param code 失败原因代码
|
||||
* @return 失败原因值
|
||||
*/
|
||||
public static String getValue(int code) {
|
||||
return fromCode(code).getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据值获取失败原因代码
|
||||
*
|
||||
* @param value 失败原因值
|
||||
* @return 失败原因代码
|
||||
*/
|
||||
public static int getCode(String value) {
|
||||
return fromValue(value).getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据值获取失败原因描述
|
||||
*
|
||||
* @param value 失败原因值
|
||||
* @return 失败原因描述
|
||||
*/
|
||||
public static String getDescription(String value) {
|
||||
return fromValue(value).getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码获取失败原因描述
|
||||
*
|
||||
* @param code 失败原因代码
|
||||
* @return 失败原因描述
|
||||
*/
|
||||
public static String getDescription(int code) {
|
||||
return fromCode(code).getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为成功状态
|
||||
*
|
||||
* @param code 失败原因代码
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
public static boolean isSuccess(int code) {
|
||||
return code == SUCCESS.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为成功状态
|
||||
*
|
||||
* @return true表示成功,false表示失败
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return this == SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字节形式的代码
|
||||
*
|
||||
* @return 字节形式的失败原因代码
|
||||
*/
|
||||
public byte getByteCode() {
|
||||
return (byte) code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.protocol.yunkuaichong.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 云快充协议启动方式枚举
|
||||
*
|
||||
* @author baiban
|
||||
* @since 2024-12-16
|
||||
*/
|
||||
@Getter
|
||||
public enum YunKuaiChongStartTypeEnum {
|
||||
|
||||
/** 通过刷卡启动充电 */
|
||||
CARD_START(0x01, "CARD_START", "刷卡启动"),
|
||||
|
||||
/** 通过账号启动充电(暂不支持) */
|
||||
ACCOUNT_START(0x02, "ACCOUNT_START", "账号启动"),
|
||||
|
||||
/** VIN码启动充电 */
|
||||
VIN_START(0x03, "VIN_START", "VIN码启动"),
|
||||
|
||||
/** 未知启动方式 */
|
||||
UNKNOWN(0xFF, "UNKNOWN", "未知启动方式");
|
||||
|
||||
/** 启动方式代码 */
|
||||
private final int code;
|
||||
|
||||
/** 启动方式值 */
|
||||
private final String value;
|
||||
|
||||
/** 启动方式描述 */
|
||||
private final String description;
|
||||
|
||||
YunKuaiChongStartTypeEnum(int code, String value, String description) {
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码获取启动方式枚举
|
||||
*
|
||||
* @param code 启动方式代码
|
||||
* @return 启动方式枚举,未找到时返回UNKNOWN
|
||||
*/
|
||||
public static YunKuaiChongStartTypeEnum fromCode(int code) {
|
||||
for (YunKuaiChongStartTypeEnum startType : values()) {
|
||||
if (startType.code == code) {
|
||||
return startType;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据值获取启动方式枚举
|
||||
*
|
||||
* @param value 启动方式值
|
||||
* @return 启动方式枚举,未找到时返回UNKNOWN
|
||||
*/
|
||||
public static YunKuaiChongStartTypeEnum fromValue(String value) {
|
||||
for (YunKuaiChongStartTypeEnum startType : values()) {
|
||||
if (startType.value.equals(value)) {
|
||||
return startType;
|
||||
}
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码获取启动方式值
|
||||
*
|
||||
* @param code 启动方式代码
|
||||
* @return 启动方式值
|
||||
*/
|
||||
public static String getValue(int code) {
|
||||
return fromCode(code).getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据值获取启动方式代码
|
||||
*
|
||||
* @param value 启动方式值
|
||||
* @return 启动方式代码
|
||||
*/
|
||||
public static int getCode(String value) {
|
||||
return fromValue(value).getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据值获取启动方式描述
|
||||
*
|
||||
* @param value 启动方式值
|
||||
* @return 启动方式描述
|
||||
*/
|
||||
public static String getDescription(String value) {
|
||||
return fromValue(value).getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据代码获取启动方式描述
|
||||
*
|
||||
* @param code 启动方式代码
|
||||
* @return 启动方式描述
|
||||
*/
|
||||
public static String getDescription(int code) {
|
||||
return fromCode(code).getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为有效的启动方式代码
|
||||
*
|
||||
* @param code 启动方式代码
|
||||
* @return true表示有效,false表示无效
|
||||
*/
|
||||
public static boolean isValid(int code) {
|
||||
return fromCode(code) != UNKNOWN;
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ public class YunKuaiChongDownlinkCmdConverter implements DownlinkCmdConverter {
|
||||
COMMAND_MAP.put(DownlinkCmdEnum.VERIFY_PRICING_ACK, 0x06);
|
||||
COMMAND_MAP.put(DownlinkCmdEnum.QUERY_PRICING_ACK, 0X0A);
|
||||
COMMAND_MAP.put(DownlinkCmdEnum.SET_PRICING, 0x58);
|
||||
COMMAND_MAP.put(DownlinkCmdEnum.START_CHARGE_ACK, 0x32);
|
||||
COMMAND_MAP.put(DownlinkCmdEnum.REMOTE_START_CHARGING, 0x34);
|
||||
COMMAND_MAP.put(DownlinkCmdEnum.REMOTE_STOP_CHARGING, 0x36);
|
||||
COMMAND_MAP.put(DownlinkCmdEnum.TRANSACTION_RECORD_ACK, 0x40);
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.protocol.yunkuaichong.v150.cmd;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto;
|
||||
import sanbing.jcpp.protocol.ProtocolContext;
|
||||
import sanbing.jcpp.protocol.annotation.ProtocolCmd;
|
||||
import sanbing.jcpp.protocol.listener.tcp.TcpSession;
|
||||
import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDownlinkCmdExe;
|
||||
import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage;
|
||||
import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage;
|
||||
import sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongStartChargeFailureReasonEnum;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import static sanbing.jcpp.protocol.domain.DownlinkCmdEnum.START_CHARGE_ACK;
|
||||
import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.FAILURE_BYTE;
|
||||
import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongDwonlinkMessage.SUCCESS_BYTE;
|
||||
import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongProtocolConstants.ProtocolNames.*;
|
||||
|
||||
/**
|
||||
* 云快充1.5.0 充电桩主动申请启动充电
|
||||
*
|
||||
* @author baiban
|
||||
*/
|
||||
@Slf4j
|
||||
@ProtocolCmd(value = 0x32, protocolNames = {V150, V160, V170})
|
||||
public class YunKuaiChongV150StartChargeAckDLCmd extends YunKuaiChongDownlinkCmdExe {
|
||||
|
||||
@Override
|
||||
public void execute(TcpSession tcpSession, YunKuaiChongDwonlinkMessage yunKuaiChongDwonlinkMessage, ProtocolContext ctx) {
|
||||
log.info("{} 云快充1.5.0运营平台确认启动充电", tcpSession);
|
||||
|
||||
if (!yunKuaiChongDwonlinkMessage.getMsg().hasStartChargeResponse()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProtocolProto.StartChargeResponse startChargeResponse = yunKuaiChongDwonlinkMessage.getMsg().getStartChargeResponse();
|
||||
String tradeNo = startChargeResponse.getTradeNo();
|
||||
String pileCode = startChargeResponse.getPileCode();
|
||||
String gunCode = startChargeResponse.getGunCode();
|
||||
String logicalCardNo = startChargeResponse.getLogicalCardNo();
|
||||
String limitYuan = startChargeResponse.getLimitYuan();
|
||||
String failReasonValue = startChargeResponse.getFailReason();
|
||||
boolean authSuccess = startChargeResponse.getAuthSuccess();
|
||||
int failReasonCode = YunKuaiChongStartChargeFailureReasonEnum.getCode(failReasonValue);
|
||||
|
||||
ByteBuf msgBody = Unpooled.buffer(44);
|
||||
// 交易流水号
|
||||
msgBody.writeBytes(encodeTradeNo(tradeNo));
|
||||
// 桩编码
|
||||
msgBody.writeBytes(encodePileCode(pileCode));
|
||||
// 枪号
|
||||
msgBody.writeBytes(encodeGunCode(gunCode));
|
||||
// 逻辑卡号
|
||||
msgBody.writeBytes(encodeCardNo(logicalCardNo));
|
||||
// 账户余额
|
||||
msgBody.writeIntLE(new BigDecimal(limitYuan).multiply(new BigDecimal("100")).intValue());
|
||||
// 鉴权成功标志
|
||||
msgBody.writeByte(authSuccess ? SUCCESS_BYTE : FAILURE_BYTE);
|
||||
// 失败原因
|
||||
msgBody.writeByte(failReasonCode);
|
||||
|
||||
YunKuaiChongUplinkMessage requestData = JacksonUtil.fromBytes(yunKuaiChongDwonlinkMessage.getMsg().getRequestData().toByteArray(), YunKuaiChongUplinkMessage.class);
|
||||
|
||||
encodeAndWriteFlush(START_CHARGE_ACK,
|
||||
requestData.getSequenceNumber(),
|
||||
requestData.getEncryptionFlag(),
|
||||
msgBody,
|
||||
tcpSession);
|
||||
|
||||
if (!authSuccess) {
|
||||
log.info("业务[云快充1.5.0 充电桩主动申请启动充电失败] 失败原因:{}", YunKuaiChongStartChargeFailureReasonEnum.getDescription(failReasonCode));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.protocol.yunkuaichong.v150.cmd;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import sanbing.jcpp.infrastructure.util.codec.BCDUtil;
|
||||
import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil;
|
||||
import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil;
|
||||
import sanbing.jcpp.proto.gen.ProtocolProto;
|
||||
import sanbing.jcpp.protocol.ProtocolContext;
|
||||
import sanbing.jcpp.protocol.annotation.ProtocolCmd;
|
||||
import sanbing.jcpp.protocol.listener.tcp.TcpSession;
|
||||
import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe;
|
||||
import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage;
|
||||
import sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongStartTypeEnum;
|
||||
import sanbing.jcpp.protocol.yunkuaichong.enums.YunKuaiChongPasswordRequiredEnum;
|
||||
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongProtocolConstants.ProtocolNames.*;
|
||||
|
||||
/**
|
||||
* 云快充1.5.0 充电桩主动申请启动充电
|
||||
*
|
||||
* @author baiban
|
||||
*/
|
||||
@Slf4j
|
||||
@ProtocolCmd(value = 0x31, protocolNames = {V150, V160, V170})
|
||||
public class YunKuaiChongV150StartChargeULCmd extends YunKuaiChongUplinkCmdExe {
|
||||
|
||||
@Override
|
||||
public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) {
|
||||
log.debug("{} 云快充1.5.0充电桩主动申请启动充电", tcpSession);
|
||||
|
||||
ByteBuf byteBuf = Unpooled.wrappedBuffer(yunKuaiChongUplinkMessage.getMsgBody());
|
||||
|
||||
ObjectNode additionalInfo = JacksonUtil.newObjectNode();
|
||||
|
||||
// 从Tracer中获取当前时间
|
||||
long ts = TracerContextUtil.getCurrentTracer().getTracerTs();
|
||||
|
||||
// 桩编号
|
||||
byte[] pileCodeBytes = new byte[7];
|
||||
byteBuf.readBytes(pileCodeBytes);
|
||||
String pileCode = BCDUtil.toString(pileCodeBytes);
|
||||
|
||||
// 枪号
|
||||
byte gunCodeByte = byteBuf.readByte();
|
||||
String gunCode = BCDUtil.toString(gunCodeByte);
|
||||
|
||||
// 启动方式
|
||||
int startTypeCode = byteBuf.readUnsignedByte();
|
||||
String startType = YunKuaiChongStartTypeEnum.getValue(startTypeCode);
|
||||
|
||||
// 是否需要密码
|
||||
int needPasswordCode = byteBuf.readUnsignedByte();
|
||||
boolean needPassword = YunKuaiChongPasswordRequiredEnum.isPasswordRequired(needPasswordCode);
|
||||
|
||||
// 物理卡号
|
||||
byte[] cardNoBytes = new byte[8];
|
||||
byteBuf.readBytes(cardNoBytes);
|
||||
String cardNo = BCDUtil.toString(cardNoBytes);
|
||||
|
||||
// 密码
|
||||
byte[] passwordBytes = new byte[16];
|
||||
byteBuf.readBytes(passwordBytes);
|
||||
String password = DigestUtils.md5DigestAsHex(passwordBytes).substring(8, 24).toLowerCase();
|
||||
|
||||
// VIN码
|
||||
byte[] carVinCodeBytes = new byte[17];
|
||||
byteBuf.readBytes(carVinCodeBytes);
|
||||
// VIN码反序处理
|
||||
String carVinCode = reverseVinCode(new String(carVinCodeBytes, StandardCharsets.US_ASCII));
|
||||
|
||||
// 转发到后端
|
||||
ProtocolProto.StartChargeRequest startChargingRequest = ProtocolProto.StartChargeRequest.newBuilder()
|
||||
.setTs(ts)
|
||||
.setPileCode(pileCode)
|
||||
.setGunCode(gunCode)
|
||||
.setStartType(startType)
|
||||
.setNeedPassword(needPassword)
|
||||
.setCardNo(cardNo)
|
||||
.setPassword(password)
|
||||
.setCarVinCode(carVinCode)
|
||||
.setAdditionalInfo(additionalInfo.toString())
|
||||
.build();
|
||||
|
||||
ProtocolProto.UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(startChargingRequest.getPileCode(), tcpSession, yunKuaiChongUplinkMessage)
|
||||
.setStartChargeRequest(startChargingRequest)
|
||||
.build();
|
||||
|
||||
tcpSession.getForwarder().sendMessage(uplinkQueueMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* VIN码反序处理
|
||||
*
|
||||
* @param originalVinCode 原始VIN码
|
||||
* @return 反序后的VIN码
|
||||
*/
|
||||
private String reverseVinCode(String originalVinCode) {
|
||||
if (originalVinCode == null || originalVinCode.trim().isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 移除末尾的null字符和空格
|
||||
String trimmedVin = originalVinCode.trim().replaceAll("\0", "");
|
||||
|
||||
if (trimmedVin.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 反序VIN码
|
||||
return new StringBuilder(trimmedVin).reverse().toString();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user