!16 云快充1.5.0 充电阶段BMS中止

* Merge branch 'Feat_0x1D' into develop
* 解析中止原因; 定义领域模型
* add 云快充1.5.0 充电阶段BMS中止
This commit is contained in:
2025-08-05 07:32:39 +00:00
committed by 三丙
parent fe054820fa
commit ccd5d3d943
6 changed files with 251 additions and 2 deletions

View File

@@ -93,4 +93,10 @@ public interface PileProtocolService {
* 充电过程BMS信息
*/
void onBmsCharingInfo(UplinkQueueMessage uplinkQueueMessage, Callback callback);
/**
* 充电阶段BMS中止
*/
void bmsAbortDuringCharging(UplinkQueueMessage uplinkQueueMessage, Callback callback);
}

View File

@@ -6,6 +6,8 @@
*/
package sanbing.jcpp.app.service.impl;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -315,11 +317,14 @@ public class DefaultPileProtocolService implements PileProtocolService {
}
@Override
public void onBmsChargingErrorProto(UplinkQueueMessage uplinkQueueMessage, Callback callback) {
log.info("充电桩与 BMS 充电错误上报 {}", uplinkQueueMessage);
}
public void bmsAbortDuringCharging(UplinkQueueMessage uplinkQueueMessage, Callback callback) {
log.info("接收到充电阶段BMS中止报文 {}", uplinkQueueMessage);
// TODO 处理相关业务逻辑
callback.onSuccess();
}

View File

@@ -204,6 +204,10 @@ public class ProtocolUplinkConsumerService extends AbstractConsumerService imple
pileProtocolService.onBmsCharingInfo(uplinkQueueMsg, callback);
} else if (uplinkQueueMsg.hasBmsAbortDuringChargingRequest()) {
pileProtocolService.bmsAbortDuringCharging(uplinkQueueMsg, callback);
} else {
callback.onSuccess();

View File

@@ -69,6 +69,7 @@ message UplinkQueueMessage {
BmsChargingErrorProto bmsChargingErrorProto = 31;
BmsParamConfigReport bmsParamConfigReport = 33;
BmsChargingInfoProto bmsChargingInfoProto = 34;
BmsAbortDuringChargingRequest bmsAbortDuringChargingRequest = 35;
}
message DownlinkRequestMessage {
@@ -324,4 +325,11 @@ message BmsChargingInfoProto {
string gunCode = 2;
string tradeNo = 3;
optional string additionalInfo = 4;
}
message BmsAbortDuringChargingRequest {
string pileCode = 4;
string gunCode = 5;
string tradeNo = 6;
optional string additionalInfo = 20;
}

View File

@@ -49,6 +49,7 @@
#### 0x3B 上行交易记录
`68 a2 00 46 00 3b 20 23 12 12 00 00 10 32 32 39 00 00 00 00 00 00 20 23 12 12 00 00 10 01 b0 36 04 11 6d 0c 17 a0 8c 09 11 6d 0c 17 f0 49 02 00 00 00 00 00 00 00 00 00 00 00 00 00 d0 fb 01 00 00 00 00 00 00 00 00 00 00 00 00 00 b0 ad 01 00 00 00 00 00 00 00 00 00 00 00 00 00 90 5f 01 00 72 06 00 00 72 06 00 00 78 05 00 00 00 00 00 00 00 00 00 00 00 00 72 06 00 00 72 06 00 00 78 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 a0 8c 09 11 6d 0c 17 40 00 00 00 00 00 00 00 00 03 E0`
#### 0x40 下行交易就应答
`68 15 46 00 00 40 20 23 12 12 00 00 10 32 32 39 00 00 00 00 00 00 00 9D 7B`
@@ -67,4 +68,10 @@
#### 0x25 充电过程BMS信息
`68 31 01 00 00 25 32 01 02 00 00 00 00 11 15 11 16 15 55 35 02 60 20 23 12 12 00 00 10 01 01 01 01 01 01 01 00 01 00 01 00 01 00 01 00 01 00 01 00 00 00 BE C8`
---
---
#### 下行交易就应答
`68 15 00 46 00 06 20 23 12 12 00 00 10 32 32 39 00 00 00 00 00 00 00 C6 2D `
---
#### 0x1D 充电阶段BMS中止
`68 20 00 18 00 1D 20 23 12 12 00 00 01 01 11 51 11 61 55 53 50 26 20 23 12 12 00 00 10 01 00 00 00 00 5a 23`

View File

@@ -0,0 +1,219 @@
/**
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
* 微信mohan_88888
* 抖音:程序员三丙
* 付费课程知识星球https://t.zsxq.com/aKtXo
*/
package sanbing.jcpp.protocol.yunkuaichong.v150.cmd;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Joiner;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j;
import sanbing.jcpp.infrastructure.util.codec.BCDUtil;
import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil;
import sanbing.jcpp.proto.gen.ProtocolProto.BmsAbortDuringChargingRequest;
import sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage;
import sanbing.jcpp.protocol.ProtocolContext;
import sanbing.jcpp.protocol.listener.tcp.TcpSession;
import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkCmdExe;
import sanbing.jcpp.protocol.yunkuaichong.YunKuaiChongUplinkMessage;
import sanbing.jcpp.protocol.yunkuaichong.annotation.YunKuaiChongCmd;
import java.util.ArrayList;
import java.util.List;
/**
* 云快充1.5.0 充电阶段BMS中止
*/
@Slf4j
@YunKuaiChongCmd(0x1D)
public class YunKuaiChongV150BmsAbortDuringChargingULCmd extends YunKuaiChongUplinkCmdExe {
@Override
public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) {
log.debug("{} 云快充1.5.0充电阶段BMS中止", tcpSession);
ByteBuf byteBuf = Unpooled.wrappedBuffer(yunKuaiChongUplinkMessage.getMsgBody());
ObjectNode additionalInfo = JacksonUtil.newObjectNode();
// 1.交易流水号
byte[] tradeNoBytes = new byte[16];
byteBuf.readBytes(tradeNoBytes);
String tradeNo = BCDUtil.toString(tradeNoBytes);
additionalInfo.put("交易流水号", tradeNo);
// 2.桩编号
byte[] pileCodeBytes = new byte[7];
byteBuf.readBytes(pileCodeBytes);
String pileCode = BCDUtil.toString(pileCodeBytes);
additionalInfo.put("桩编号", pileCode);
// 3.枪号
byte gunCodeByte = byteBuf.readByte();
String gunCode = BCDUtil.toString(gunCodeByte);
additionalInfo.put("枪号", gunCode);
// 4.BMS中止充电原因
byte reasonByte = byteBuf.readByte();
additionalInfo.put("BMS中止充电原因", parseAbortReasons(reasonByte));
// 5.BMS中止充电故障原因
byte[] faultReasonBytes = new byte[2];
additionalInfo.put("BMS中止充电故障原因", parseFaultReasons(faultReasonBytes));
// 6.BMS中止充电错误原因
byte errorReasonByte = byteBuf.readByte();
additionalInfo.put("BMS中止充电错误原因", parseErrorReasons(errorReasonByte));
BmsAbortDuringChargingRequest request = BmsAbortDuringChargingRequest.newBuilder()
.setPileCode(pileCode)
.setGunCode(gunCode)
.setTradeNo(tradeNo)
.setAdditionalInfo(additionalInfo.toString())
.build();
// 转发到后端
UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(pileCode, tcpSession, yunKuaiChongUplinkMessage)
.setBmsAbortDuringChargingRequest(request)
.build();
tcpSession.getForwarder().sendMessage(uplinkQueueMessage);
}
/**
* BMS中止充电原因枚举
*/
public enum AbortReasonEnum {
SOC_TARGET("需求SOC目标值"),
TOTAL_VOLTAGE("达到总电压设定值"),
CELL_VOLTAGE("达到单体电压设定值"),
CHARGER_INITIATED("充电机主动中止");
private final String description;
AbortReasonEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
/**
* 解析BMS中止充电原因字节
*
* @param reasonByte 1字节的原因字段值
* @return 触发的原因描述列表
*/
public String parseAbortReasons(byte reasonByte) {
List<String> reasons = new ArrayList<>();
// 将byte转为无符号整数 (0-255)
int value = reasonByte & 0xFF;
// 检查每组2bit的状态从低位到高位
for (AbortReasonEnum reason : AbortReasonEnum.values()) {
int bitPosition = reason.ordinal() * 2;
int mask = 0b11 << bitPosition; // 创建该组的位掩码
int groupValue = (value & mask) >>> bitPosition; // 提取组值
if (groupValue != 0) {
reasons.add(reason.getDescription());
}
}
return Joiner.on(",").join(reasons);
}
/**
* BMS中止充电故障原因枚举
*/
public enum FaultReasonsEnum {
INSULATION_FAULT("绝缘故障"),
CONNECTOR_OVERHEAT("输出连接器过温故障"),
BMS_COMPONENT_OVERHEAT("BMS元件过温故障"),
CHARGING_CONNECTOR_FAULT("充电连接器故障"),
BATTERY_OVERHEAT("电池组温度过高故障"),
HIGH_VOLTAGE_RELAY_FAULT("高压继电器故障"),
VOLTAGE_DETECTION_FAULT("检测点2电压检测故障"),
OTHER_FAULT("其他故障");
private final String description;
FaultReasonsEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
/**
* 解析BMS中止充电故障原因
* @param faultBytes 2字节的故障原因字段值
* @return 触发的故障描述列表
*/
public String parseFaultReasons(byte[] faultBytes) {
List<String> faults = new ArrayList<>();
if (faultBytes == null || faultBytes.length != 2) {
throw new IllegalArgumentException("故障原因字段必须是2字节长度");
}
// 将2字节转换为无符号整数 (0-65535)
int value = ((faultBytes[0] & 0xFF) << 8) | (faultBytes[1] & 0xFF);
// 检查每组2bit的状态
for (FaultReasonsEnum fault : FaultReasonsEnum.values()) {
int bitPosition = fault.ordinal() * 2;
int mask = 0b11 << bitPosition; // 创建该组的位掩码
int groupValue = (value & mask) >>> bitPosition; // 提取组值
if (groupValue != 0) {
faults.add(fault.getDescription());
}
}
return Joiner.on(",").join(faults);
}
/**
* BMS中止充电错误原因枚举
*/
public enum ErrorReasonsEnum {
CURRENT_OVERFLOW("电流过大"),
VOLTAGE_ABNORMAL("电压异常");
private final String description;
ErrorReasonsEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
/**
* 解析BMS中止充电错误原因
* @param errorByte 1字节的错误原因字段值
* @return 触发的错误描述列表
*/
public String parseErrorReasons(byte errorByte) {
List<String> errors = new ArrayList<>();
int value = errorByte & 0xFF;
for (ErrorReasonsEnum error : ErrorReasonsEnum.values()) {
int bitPosition = error.ordinal() * 2;
int mask = 0b11 << bitPosition;
int groupValue = (value & mask) >>> bitPosition;
if (groupValue != 0) {
errors.add(error.getDescription());
}
}
return Joiner.on(",").join(errors);
}
}