From f28aeee87cfda4f807864a1b139ec7f843056f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E9=A3=8E?= <7823548+huangkongshenghugh@user.noreply.gitee.com> Date: Fri, 1 Aug 2025 03:00:50 +0000 Subject: [PATCH] =?UTF-8?q?!11=20=E5=91=BD=E4=BB=A4=E7=A0=81=EF=BC=9A0x17?= =?UTF-8?q?=20(=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE=E5=B8=A7=E4=B8=8A?= =?UTF-8?q?=E8=A1=8C=E5=91=BD=E4=BB=A4)=20*=20BMS=200x17=20(=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E9=85=8D=E7=BD=AE=E5=B8=A7=E4=B8=8A=E8=A1=8C=E5=91=BD?= =?UTF-8?q?=E4=BB=A4)=20=3D=20=E6=9B=B4=E6=96=B0BmsParamConfigReport=20*?= =?UTF-8?q?=20BMS=200x17=20(=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE=E5=B8=A7?= =?UTF-8?q?=E4=B8=8A=E8=A1=8C=E5=91=BD=E4=BB=A4)=20=3D=20additionalInfo?= =?UTF-8?q?=E9=87=8C=E5=A1=9E=E4=BA=86=E4=B8=80=E9=81=8D=EF=BC=8C=E5=9B=BA?= =?UTF-8?q?=E5=AE=9A=E5=AD=97=E6=AE=B5=E9=87=8C=E4=B9=9F=E5=A1=9E=E4=BA=86?= =?UTF-8?q?=E4=B8=80=E9=81=8D=EF=BC=8C=E6=94=B9=E6=88=90=E5=8F=AA=E5=A1=9E?= =?UTF-8?q?=E4=B8=80=E9=81=8D=E5=9B=BA=E5=AE=9A=E5=AD=97=E6=AE=B5=20*=20BM?= =?UTF-8?q?S=200x17=20(=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE=E5=B8=A7?= =?UTF-8?q?=E4=B8=8A=E8=A1=8C=E5=91=BD=E4=BB=A4)=20=3D=20=E5=88=A0?= =?UTF-8?q?=E9=99=A4ParamConfigRequest=20*=20BMS=200x17=20(=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E9=85=8D=E7=BD=AE=E5=B8=A7=E4=B8=8A=E8=A1=8C=E5=91=BD?= =?UTF-8?q?=E4=BB=A4)=20=3D=20=E4=BF=AE=E6=94=B9=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=91=BD=E5=90=8DBmsParamConfigReport=E5=92=8C=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84=E7=B1=BB=E9=87=8D=E6=96=B0=E5=91=BD=E5=90=8D?= =?UTF-8?q?=20*=20BMS=200x17=20(=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=B8=A7=E4=B8=8A=E8=A1=8C=E5=91=BD=E4=BB=A4)=20=3D=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9cmd=20=20paramConfigProto=20=3D=2033=20*=20BM?= =?UTF-8?q?S=200x17=20(=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE=E5=B8=A7?= =?UTF-8?q?=E4=B8=8A=E8=A1=8C=E5=91=BD=E4=BB=A4)=20=3D=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9protocol.proto=E3=80=81=E6=95=B0=E5=80=BC=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E6=94=B9=E6=88=90BigDecimal=20*=20BMS=200x17=20(?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE=E5=B8=A7=E4=B8=8A=E8=A1=8C?= =?UTF-8?q?=E5=91=BD=E4=BB=A4)=20+=20=E4=B8=8A=E8=A1=8CBMS=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E9=85=8D=E7=BD=AE=E6=8A=A5=E6=96=87=20*=20BMS=20=20?= =?UTF-8?q?=200x17=20(=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE=E5=B8=A7?= =?UTF-8?q?=E4=B8=8A=E8=A1=8C=E5=91=BD=E4=BB=A4)=20*=200x17=20(=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E9=85=8D=E7=BD=AE=E5=B8=A7=E4=B8=8A=E8=A1=8C=E5=91=BD?= =?UTF-8?q?=E4=BB=A4)=20*=20=E5=91=BD=E4=BB=A4=E7=A0=81=EF=BC=9A0x17=20(?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE=E5=B8=A7=E4=B8=8A=E8=A1=8C?= =?UTF-8?q?=E5=91=BD=E4=BB=A4)=20*=20=E5=91=BD=E4=BB=A4=E7=A0=81=EF=BC=9A0?= =?UTF-8?q?x17=20(=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE=E5=B8=A7=E4=B8=8A?= =?UTF-8?q?=E8=A1=8C=E5=91=BD=E4=BB=A4)=20*=20Revert=20"=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E7=A0=81=EF=BC=9A0x17=20(=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=B8=A7=E4=B8=8A=E8=A1=8C=E5=91=BD=E4=BB=A4)"=20*=20=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E7=A0=81=EF=BC=9A0x17=20(=E5=8F=82=E6=95=B0=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=B8=A7=E4=B8=8A=E8=A1=8C=E5=91=BD=E4=BB=A4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jcpp/app/service/PileProtocolService.java | 5 + .../impl/DefaultPileProtocolService.java | 6 + .../ProtocolUplinkConsumerService.java | 4 + .../src/main/proto/protocol.proto | 19 ++ jcpp-protocol-yunkuaichong/READMD.md | 9 +- .../YunKuaiChongV150BmsParamConfigULCmd.java | 185 ++++++++++++++++++ 6 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsParamConfigULCmd.java diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/PileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/PileProtocolService.java index ffd512c..e012a74 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/PileProtocolService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/PileProtocolService.java @@ -78,4 +78,9 @@ public interface PileProtocolService { * 下发计费 */ void setPricing(String pileCode, SetPricingRequest setPricingRequest); + + /** + * 充电桩与 BMS 参数配置阶段报文 + */ + void onBmsParamConfigReport(UplinkQueueMessage uplinkQueueMsg, Callback callback); } \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java index fc00196..d8422db 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/impl/DefaultPileProtocolService.java @@ -314,6 +314,7 @@ public class DefaultPileProtocolService implements PileProtocolService { downlinkCallService.sendDownlinkMessage(downlinkRequestMessageBuilder, pileCode); } + private static Period createPeriod(int sn, LocalTime beginTime, LocalTime endTime, PricingModelFlag flag) { Period period = new Period(); period.setSn(sn); @@ -337,4 +338,9 @@ public class DefaultPileProtocolService implements PileProtocolService { builder.setRequestData(uplinkQueueMessage.getRequestData()); return builder; } + + @Override + public void onBmsParamConfigReport(UplinkQueueMessage uplinkQueueMsg, Callback callback) { + log.info("充电桩与 BMS 参数配置阶段报文 {}", uplinkQueueMsg); + } } \ No newline at end of file diff --git a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java index de8b33b..38b6841 100644 --- a/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java +++ b/jcpp-app/src/main/java/sanbing/jcpp/app/service/queue/consumer/ProtocolUplinkConsumerService.java @@ -192,6 +192,10 @@ public class ProtocolUplinkConsumerService extends AbstractConsumerService imple pileProtocolService.onTransactionRecord(uplinkQueueMsg, callback); + } else if (uplinkQueueMsg.hasBmsParamConfigReport()) { + + pileProtocolService.onBmsParamConfigReport(uplinkQueueMsg, callback); + } else { callback.onSuccess(); diff --git a/jcpp-infrastructure-proto/src/main/proto/protocol.proto b/jcpp-infrastructure-proto/src/main/proto/protocol.proto index d89126a..c7b6348 100644 --- a/jcpp-infrastructure-proto/src/main/proto/protocol.proto +++ b/jcpp-infrastructure-proto/src/main/proto/protocol.proto @@ -66,6 +66,7 @@ message UplinkQueueMessage { RemoteStartChargingResponse remoteStartChargingResponse = 28; RemoteStopChargingResponse remoteStopChargingResponse = 29; TransactionRecord transactionRecord = 30; + BmsParamConfigReport bmsParamConfigReport = 33; } message DownlinkRequestMessage { @@ -288,4 +289,22 @@ message TransactionRecord { message TransactionRecordAck { string tradeNo = 6; bool success = 7; +} + +message BmsParamConfigReport { + int64 ts = 1; // 时间戳 + string pileCode = 2; // 桩编码 + string gunCode = 3; // 枪编码 + string tradeNo = 4; // 交易号 + string maxSingleCellVoltage = 5; // BMS单体最高允许充电电压 (V) + string maxChargeCurrent = 6; // BMS最高允许充电电流 (A) + string ratedEnergy = 7; // BMS动力蓄电池标称总能量 (kWh) + string maxTotalChargeVoltage = 8; // BMS最高允许充电总电压 (V) + string maxTemperature = 9; // BMS最高允许温度 (℃) + string soc = 10; // BMS荷电状态SOC (%) + string currentBatteryVoltage = 11; // BMS当前电池电压 (V) + string pileMaxOutputVoltage = 12; // 电桩最高输出电压 (V) + string pileMinOutputVoltage = 13; // 电桩最低输出电压 (V) + string pileMaxOutputCurrent = 14; // 电桩最大输出电流 (A) + string pileMinOutputCurrent = 15; // 电桩最小输出电流 (A) } \ No newline at end of file diff --git a/jcpp-protocol-yunkuaichong/READMD.md b/jcpp-protocol-yunkuaichong/READMD.md index cf6fdee..9cab02e 100644 --- a/jcpp-protocol-yunkuaichong/READMD.md +++ b/jcpp-protocol-yunkuaichong/READMD.md @@ -49,4 +49,11 @@ #### 上行交易记录 `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` #### 下行交易就应答 -`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 ` \ No newline at end of file +`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 ` + +--- + +### 7.4上行BMS参数配置 +`68 31 00 16 00 17 32 01 02 00 00 00 00 11 15 11 16 15 55 35 02 60 20 23 12 12 00 00 10 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 BB` + + diff --git a/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsParamConfigULCmd.java b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsParamConfigULCmd.java new file mode 100644 index 0000000..fcca59a --- /dev/null +++ b/jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150BmsParamConfigULCmd.java @@ -0,0 +1,185 @@ +/** + * 开源代码,仅供学习和交流研究使用,商用请联系三丙 + * 微信:mohan_88888 + * 抖音:程序员三丙 + * 付费课程知识星球:https://t.zsxq.com/aKtXo + */ +package sanbing.jcpp.protocol.yunkuaichong.v150.cmd; + +import lombok.extern.slf4j.Slf4j; +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 sanbing.jcpp.proto.gen.ProtocolProto.UplinkQueueMessage; +import sanbing.jcpp.infrastructure.util.trace.TracerContextUtil; +import sanbing.jcpp.proto.gen.ProtocolProto.BmsParamConfigReport; + + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import sanbing.jcpp.infrastructure.util.codec.BCDUtil; + +import java.math.BigDecimal; + +/** + * 云快充协议上行命令处理类 - 参数配置帧解析 (V1.5.0版本) + * 命令码:0x17 (参数配置帧上行命令) + */ +@Slf4j +@YunKuaiChongCmd(0x17) +public class YunKuaiChongV150BmsParamConfigULCmd extends YunKuaiChongUplinkCmdExe { + // 电流值偏移量常量(单位:安培) + private static final BigDecimal CURRENT_OFFSET = new BigDecimal("-400.0"); + // 温度值偏移量常量(单位:摄氏度) + private static final BigDecimal TEMP_OFFSET = new BigDecimal("-50.0"); + + /** + * 执行命令解析 + * @param tcpSession TCP会话对象 + * @param yunKuaiChongUplinkMessage 上行消息对象 + * @param ctx 协议上下文 + */ + @Override + public void execute(TcpSession tcpSession, YunKuaiChongUplinkMessage yunKuaiChongUplinkMessage, ProtocolContext ctx) { + log.info("{} 云快充1.5.0充电桩参数配置帧请求", tcpSession); + // 将消息体包装为ByteBuf以便读取 + ByteBuf byteBuf = Unpooled.wrappedBuffer(yunKuaiChongUplinkMessage.getMsgBody()); + // 从Tracer总获取当前时间 + long ts = TracerContextUtil.getCurrentTracer().getTracerTs(); + + /* 按协议顺序解析消息体 */ + + // 1. 交易流水号:16字节BCD编码字符串 + String tradeNo = readBcdString(byteBuf, 16); + + // 2. 桩编号:7字节BCD编码字符串 + String pileCode = readBcdString(byteBuf, 7); + + // 3. 枪号:1字节BCD编码字符串 + String gunCode = readBcdString(byteBuf, 1); + + // 4. BMS单体最高允许充电电压:2字节无符号整数,单位0.01V + BigDecimal maxSingleCellVoltage = readVoltage(byteBuf, 100); + + // 5. BMS最高允许充电电流:2字节无符号整数,单位0.1A(需加-400A偏移量) + BigDecimal maxChargeCurrent = readCurrent(byteBuf); + + // 6. BMS动力蓄电池标称总能量:2字节无符号整数,单位0.1kWh + BigDecimal ratedEnergy = readEnergy(byteBuf); // 单位0.1kWh + + // 7. BMS最高允许充电总电压:2字节无符号整数,单位0.1V + BigDecimal maxTotalChargeVoltage = readVoltage(byteBuf, 10); + + // 8. BMS最高允许温度:1字节无符号整数,单位1℃(需加-50℃偏移量) + BigDecimal maxTemperature = readTemperature(byteBuf); + + // 9. BMS荷电状态SOC:2字节无符号整数,单位0.1% + BigDecimal soc = readPercentage(byteBuf); + + // 10. BMS当前电池电压:2字节无符号整数,单位0.1V + BigDecimal currentBatteryVoltage = readVoltage(byteBuf, 10); + + // 11. 电桩最高输出电压:2字节无符号整数,单位0.1V + BigDecimal pileMaxOutputVoltage = readVoltage(byteBuf, 10); + + // 12. 电桩最低输出电压:2字节无符号整数,单位0.1V + BigDecimal pileMinOutputVoltage = readVoltage(byteBuf, 10); + + // 13. 电桩最大输出电流:2字节无符号整数,单位0.1A(需加-400A偏移量) + BigDecimal pileMaxOutputCurrent = readCurrent(byteBuf); + + // 14. 电桩最小输出电流:2字节无符号整数,单位0.1A(需加-400A偏移量) + BigDecimal pileMinOutputCurrent = readCurrent(byteBuf); + + // 转发到后端 + BmsParamConfigReport bmsParamConfigReport = BmsParamConfigReport.newBuilder() + .setTs(ts) + .setPileCode(pileCode) + .setTradeNo(tradeNo) + .setGunCode(gunCode) + .setMaxSingleCellVoltage(maxSingleCellVoltage.toPlainString()) + .setMaxChargeCurrent(maxChargeCurrent.toPlainString()) + .setRatedEnergy(ratedEnergy.toPlainString()) + .setMaxTotalChargeVoltage(maxTotalChargeVoltage.toPlainString()) + .setMaxTemperature(maxTemperature.toPlainString()) + .setSoc(soc.toPlainString()) + .setCurrentBatteryVoltage(currentBatteryVoltage.toPlainString()) + .setPileMaxOutputVoltage(pileMaxOutputVoltage.toPlainString()) + .setPileMinOutputVoltage(pileMinOutputVoltage.toPlainString()) + .setPileMaxOutputCurrent(pileMaxOutputCurrent.toPlainString()) + .setPileMinOutputCurrent(pileMinOutputCurrent.toPlainString()) + .build(); + UplinkQueueMessage uplinkQueueMessage = uplinkMessageBuilder(bmsParamConfigReport.getPileCode(), tcpSession, yunKuaiChongUplinkMessage) + .setBmsParamConfigReport(bmsParamConfigReport) + .build(); + tcpSession.getForwarder().sendMessage(uplinkQueueMessage); + + } + + //=== 协议数据解析辅助方法 ===// + + /** + * 读取BCD编码字符串 + * @param buf 字节缓冲区 + * @param length 读取字节长度 + * @return 解析后的字符串 + */ + private String readBcdString(ByteBuf buf, int length) { + byte[] bytes = new byte[length]; + buf.readBytes(bytes); + return BCDUtil.toString(bytes); // 调用BCD工具类转换 + } + /** + * 读取电压值(返回BigDecimal) + * @param buf 字节缓冲区 + * @param magnification 放大倍数(如100表示原始值需除以100) + * @return 电压值(BigDecimal) + */ + public BigDecimal readVoltage(ByteBuf buf, int magnification) { + long value = buf.readUnsignedShortLE(); + return reduceMagnification(value, magnification, 4); + } + /** + * 读取电流值(返回BigDecimal) + * @param buf 字节缓冲区 + * @return 电流值(BigDecimal),已应用偏移量 + */ + public BigDecimal readCurrent(ByteBuf buf) { + long value = buf.readUnsignedShortLE(); + BigDecimal current = reduceMagnification(value, 10, 4); // 0.1倍率 + return current.add(CURRENT_OFFSET); // 应用电流偏移 + } + + /** + * 读取能量值(返回BigDecimal) + * @param buf 字节缓冲区 + * @return 能量值(BigDecimal) + */ + private BigDecimal readEnergy(ByteBuf buf) { + long value = buf.readUnsignedShortLE(); + return reduceMagnification(value, 10, 4); // 0.1倍率 + } + + /** + * 读取温度值(返回BigDecimal) + * @param buf 字节缓冲区 + * @return 温度值(BigDecimal),已应用偏移量 + */ + public BigDecimal readTemperature(ByteBuf buf) { + long value = buf.readUnsignedByte(); + BigDecimal temp = reduceMagnification(value, 1, 4); // 原始值 + return temp.add(TEMP_OFFSET); // 应用温度偏移 + } + + /** + * 读取百分比值(返回BigDecimal) + * @param buf 字节缓冲区 + * @return 百分比值(BigDecimal) + */ + public BigDecimal readPercentage(ByteBuf buf) { + long value = buf.readUnsignedShortLE(); + return reduceMagnification(value, 10, 4); // 0.1倍率 + } +} \ No newline at end of file