# k-cloud-charge-analysis **Repository Path**: KevenBlue/k-cloud-charge-analysis ## Basic Information - **Project Name**: k-cloud-charge-analysis - **Description**: 高性能、轻量级GB/T 27930-2023(云快充)协议SDK,专为充电桩与平台对接场景的零框架设计。简化充电桩与平台的协议解析、指令生成与校验流程。 本仓库为同步仓库,请以Github代码为准!!! - **Primary Language**: Java - **License**: MIT - **Default Branch**: main - **Homepage**: https://github.com/KevenPotter/k-cloud-charge-analysis - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2026-05-15 - **Last Updated**: 2026-09-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: Java, SDK, smart-socket, gbt-27930, 云快充 ## README # k-cloud-charge-analysis 云快充协议(GB/T 27930-2023)解析与构建SDK,高性能、轻量级、零框架依赖,专为充电桩与平台对接场景设计。 [![Maven](https://img.shields.io/badge/Maven-3.9+-blue.svg)](https://maven.apache.org/) [![Java](https://img.shields.io/badge/Java-8+-green.svg)](https://www.oracle.com/java/) [![MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![GitHub Stars](https://img.shields.io/github/stars/KevenPotter/k-cloud-charge-analysis)](https://github.com/KevenPotter/k-cloud-charge-analysis) --- ## 📌 核心功能 - 🔐 **双向协议支持**,完整实现充电桩上报与平台下发双向报文解析 - 🎭 **智能模拟支持**,内置TCP模拟器,支持自动化充电流程仿真测试 - 🔄 **高效协议解析**,零框架依赖,字节数组直接转换,无冗余开销 - 🎯 **高效指令生成**,提供全平台控制指令构建能力,一键生成标准下发报文 - 🛠️ **数据安全校验**,内置CRC16/MODBUS等多种校验算法 - 🔗 **简易接口开发**,接口极简,仅负责字节数组与对象转换,网络通信完全解耦,适配各种业务场景 - 🔧 **灵活属性配置**,支持日志开关动态配置,适应多种运行环境 --- ## 🔧 安装使用 > **必须完整配置以下所有步骤** ### 1. 配置 GitHub Maven 仓库 在项目 `pom.xml` 中添加: ```xml github https://maven.pkg.github.com/KevenPotter/k-cloud-charge-analysis ``` ### 2. 引入依赖 ```xml com.wantllife k-cloud-charge-analysis 1.1.0 ``` ### 3. 🔔 必须配置 GitHub Token GitHub Packages 即使是公开仓库,**也必须配置 token 才能下载依赖**。 找到 Maven 的 `settings.xml` 文件: - Windows:`C:\Users\你的用户名\.m2\settings.xml` - Mac/Linux:`~/.m2/settings.xml` 添加以下配置: ```xml github 你的GitHub用户名 你的GitHub Token ``` > 💡 **如何生成 GitHub Token**:GitHub → Settings → Developer settings → Personal access tokens → Generate new token → 勾选 `repo` 和 `packages` 权限 --- ## ⚙️ SDK 配置 ### 日志输出配置 SDK 默认开启解析日志输出。如需关闭,可通过以下方式配置: ```java @Configuration public class KCloudChargeConfig { /** * 用户手动配置解析属性 */ @Bean public CloudChargeConfig cloudChargeConfig() { CloudChargeConfig config = new CloudChargeConfig(); // 解析器-日志开关 config.setAnalysisLogOutput(true); // 解析器-心跳日志开关 config.setAnalysisHeartbeatLogOutput(false); // 模拟器-日志开关 config.setSimulatorLogOutput(true); // 模拟器-心跳日志开关 config.setSimulatorHeartbeatLogOutput(false); // 自定义单次最大充电时长(分钟) config.setMaxChargeMinutes(600); CloudChargeHolder.setGlobalConfig(config); return config; } /** * 设备模拟器Bean方式配置 *

* 初始化TCP设备模拟器 * 项目启动自动连接,项目停止自动关闭连接.可配置多个设备 * */ @Bean(initMethod = "start", destroyMethod = "stop") public DeviceSimulator deviceSimulator() { StandardDevice standardDevice1 = new StandardDevice() .setDeviceId("52010600109041") .setDeviceType(1) .setGunNum(1) .setProtocolVersion("1.6") .setProgramVersion("DLF:1218") .setNetworkLinkType(2) .setSimNo("89860222152530123307") .setCarrier(0); StandardDevice standardDevice2 = new StandardDevice() .setDeviceId("52010600109042") .setDeviceType(1) .setGunNum(2) .setProtocolVersion("1.6") .setProgramVersion("DLF:1219") .setNetworkLinkType(1) .setSimNo("89860222152530123308") .setCarrier(2); List deviceList = new ArrayList(); deviceList.add(standardDevice1); deviceList.add(standardDevice2); // 创建设备模拟器 return DeviceSimulator.create( SimulatorConfig.builder() .serverIP("127.0.0.1") .serverPort(9001) .simulatorDeviceList(deviceList) .build() ); } } ``` > 配置变更时,控制台会打印当前配置状态(只有手动配置后才会输入以下日志): > - 开启:`🔋 [k-cloud-charge-analysis] User configured analysis logging as ENABLED ✅` > - 关闭:`🔋 [k-cloud-charge-analysis] User configured analysis logging as DISABLED ❌` > - 开启:`🔋 [k-cloud-charge-analysis] User configured analysis heartbeat logging as ENABLED ✅` > - 关闭:`🔋 [k-cloud-charge-analysis] User configured analysis heartbeat logging as DISABLED ❌` > - 开启:`👾 [k-cloud-charge-simulator] User configured simulator logging as ENABLED ✅` > - 关闭:`👾 [k-cloud-charge-simulator] User configured simulator logging as DISABLED ❌` > - 开启:`👾 [k-cloud-charge-simulator] User configured simulator heartbeat logging as ENABLED ✅` > - 关闭:`👾 [k-cloud-charge-simulator] User configured simulator heartbeat logging as DISABLED ❌` | 配置项 | 类型 | 默认值 | 说明 | |-----------------------------|---------|------|------------| | analysisLogOutput | boolean | true | 解析器-日志开关 | | analysisHeartbeatLogOutput | boolean | true | 解析器-心跳日志开关 | | simulatorLogOutput | boolean | true | 模拟器-日志开关 | | simulatorHeartbeatLogOutput | boolean | true | 模拟器-心跳日志开关 | --- ## 🚀 快速上手 ### 👨‍🚀 一、解析上报报文 实现 `MessageProcessor` 接口,通过帧类型 `frameType` 分发处理: ```java @Override public void process(AioSession session, ByteBuffer buffer) { try { // 1. 读取原始字节数组 byte[] data = new byte[buffer.remaining()]; buffer.get(data); // 2. 原始报文 String rawHexMsg = HexUtil.encodeHexStr(data).toUpperCase(); // 3. 基础校验 if (data.length < 6) { log.warn("报文长度过短,忽略"); return; } // 4.起始符必须 0x68(云快充协议) if ((data[0] & 0xFF) != 0x68) { log.warn("非云快充协议,忽略"); return; } byte frameType = data[5]; switch (frameType) { // 充电桩登录认证 case UP_LOGIN: AALoginReq loginReq = new AALoginReq(data, rawHexMsg); // 注册设备 if (StrUtil.isNotBlank(loginReq.getDeviceId())) { deviceChannelManager.register(loginReq.getDeviceId(), session); deviceChannelManager.sendMsg(loginReq.getDeviceId(), AALoginRes.buildCommand(loginReq)); } break; // 充电桩心跳包 case UP_HEARTBEAT: ABHeartbeatReq heartbeatReq = new ABHeartbeatReq(data, rawHexMsg); deviceChannelManager.sendMsg(heartbeatReq.getDeviceId(), ABHeartbeatRes.buildCommand(heartbeatReq)); break; // 计费模型验证请求 case UP_BILLING_MODE_VALID: ACBillingModelValidReq billingModelValidReq = new ACBillingModelValidReq(data, rawHexMsg); Long billingModeId = billingModelValidReq.getBillingModeId(); deviceChannelManager.sendMsg(billingModelValidReq.getDeviceId(), ACBillingModeValidRes.buildCommand(billingModelValidReq, 1096, billingModeId == 1096)); break; // 充电桩计费模型请求 case UP_BILLING_MODE: ADBillingModelReq billingModelReq = new ADBillingModelReq(data, rawHexMsg); List billingModelList = fakeBillingMode(); deviceChannelManager.sendMsg(billingModelReq.getDeviceId(), ADBillingModelRes.buildCommand(billingModelReq, billingModelList)); break; // 上传实时监测数据 case UP_REAL_TIME_MONITOR: AERealTimeMonitorReq realTimeMonitorReq = new AERealTimeMonitorReq(data, rawHexMsg); break; // 更多帧类型... } } catch (Exception e) { log.error("处理设备消息异常", e); } } ``` ### 构建下发指令 调用对应的 `buildCommand` 方法即可生成标准报文: ```java // 实时监测指令 byte[] realTimeData = AERealTimeMonitorRes.buildCommand(deviceId, gunNo); deviceChannelManager. sendMsg(deviceId, realTimeData); // 开电指令 byte[] startData = AOStartChargeRes.buildCommand(deviceId, gunNo, tradeNo, logicalCardNo, physicalCardNo, balance); deviceChannelManager. sendMsg(deviceId, startData); // 关电指令 byte[] stopData = APStopChargeRes.buildCommand(deviceId, gunNo); deviceChannelManager. sendMsg(deviceId, stopData); // 离线卡同步 byte[] cardSyncData = ASOfflineCardSyncRes.buildCommand(deviceId, cardList); deviceChannelManager. sendMsg(deviceId, cardSyncData); ``` ### 模拟数据 这个是模拟的数据,请按照实际方式进行构建: ```java /** * 离线卡模拟假数据 * * @return 返回离线卡 * @author KevenPotter */ private List fakeCardList() { List cardList = new ArrayList<>(); StandardCard card_1 = new StandardCard().setCardId(1L).setLogicalCardNo("10000001").setPhysicalCardNo("D14B0A54"); StandardCard card_2 = new StandardCard().setCardId(1L).setLogicalCardNo("10000002").setPhysicalCardNo("D14B0A55"); cardList.add(card_1); cardList.add(card_2); return cardList; } /** * 计费模型模拟假数据 * 规则:尖 > 峰 > 平 > 谷 * 价格:电费、服务费均按阶梯递减,谷段最低 * 格式:全部保留5位小数 * * @return 返回计费模式 * @author KevenPotter */ private List fakeBillingMode() { List billingModeList = new ArrayList<>(); // 尖 18:00-22:00 StandardBillingModel sharpMode = new StandardBillingModel(); sharpMode.setModeId(1L); sharpMode.setStrategyId(1096L); sharpMode.setTimeSlotType(1).setTimeSlotName("尖"); sharpMode.setStartTime("18:00").setEndTime("22:00"); sharpMode.setElectricityFee(new BigDecimal("1.72500")) .setServiceFee(new BigDecimal("0.58000")) .setCostFee(new BigDecimal("0.83000")); // 峰 10:00-18:00 StandardBillingModel peakMode = new StandardBillingModel(); peakMode.setModeId(2L); peakMode.setStrategyId(1096L); peakMode.setTimeSlotType(2).setTimeSlotName("峰"); peakMode.setStartTime("10:00").setEndTime("18:00"); peakMode.setElectricityFee(new BigDecimal("1.43600")) .setServiceFee(new BigDecimal("0.46000")) .setCostFee(new BigDecimal("0.76000")); // 平 07:00-10:00 StandardBillingModel flatMode = new StandardBillingModel(); flatMode.setModeId(3L); flatMode.setStrategyId(1096L); flatMode.setTimeSlotType(3).setTimeSlotName("平"); flatMode.setStartTime("07:00").setEndTime("10:00"); flatMode.setElectricityFee(new BigDecimal("1.16800")) .setServiceFee(new BigDecimal("0.37000")) .setCostFee(new BigDecimal("0.66000")); // 谷 22:00-07:00 StandardBillingModel valleyMode = new StandardBillingModel(); valleyMode.setModeId(4L); valleyMode.setStrategyId(1096L); valleyMode.setTimeSlotType(4).setTimeSlotName("谷"); valleyMode.setStartTime("22:00").setEndTime("07:00"); valleyMode.setElectricityFee(new BigDecimal("0.61200")) .setServiceFee(new BigDecimal("0.22000")) .setCostFee(new BigDecimal("0.46000")); billingModeList.add(sharpMode); billingModeList.add(peakMode); billingModeList.add(flatMode); billingModeList.add(valleyMode); return billingModeList; } ``` ### 👨‍🚀 二、操作设备模拟器数据 通过包内构建好的 **模拟类** 进行数据操作,以下为部分示例: ```java package com.ruoyi.device.controller; import cn.hutool.core.date.DateUtil; import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.core.domain.AjaxResult; import com.wantllife.domain.vo.StandardTradeRecord; import com.wantllife.simulator.res.SAQTradeRecordRes; import com.wantllife.util.SimulatorUtil; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import java.math.BigDecimal; import java.util.HashMap; import static com.ruoyi.common.utils.StringUtils.getDefaultInt; import static com.ruoyi.common.utils.StringUtils.getDefaultStr; /** * 模拟器-下发指令Controller * * @author KevenPotter */ @Anonymous @Slf4j @CrossOrigin() @RestController @AllArgsConstructor @RequestMapping("/fastCharging/simulatorCommand") public class SimulatorCommandController { /** * 模拟器-交易记录指令 * * @param params 前端请求 * @return 返回是否请求成功 * @author KevenPotter */ @ResponseBody @PostMapping(value = "/tradeRecordCommand") public AjaxResult tradeRecordRes(@RequestBody HashMap params) { String deviceId = getDefaultStr(params.get("deviceId")); Integer gunNo = getDefaultInt(params.get("gunNo")); String tradeNo = "52010600109042012604221118022583"; StandardTradeRecord standardTradeRecord = fakeTradeRecord(tradeNo, deviceId, gunNo); SimulatorUtil.simulatorSendMsg(deviceId, SAQTradeRecordRes.buildCommand(standardTradeRecord)); return AjaxResult.success(); } /** * 交易记录模拟假数据 * * @return 返回交易记录 * @author KevenPotter */ private StandardTradeRecord fakeTradeRecord(String tradeNo, String deviceId, Integer gunNo) { return new StandardTradeRecord() .setTradeNo(tradeNo).setDeviceId(deviceId).setGunNo(gunNo) .setStartTime(DateUtil.parse("2026-04-16 09:42:04.883")).setEndTime(DateUtil.parse("2026-04-16 09:49:08.224")) .setSharpUnitPrice(BigDecimal.valueOf(3D)).setSharpElectricity(BigDecimal.ZERO).setSharpLossElectricity(BigDecimal.ZERO).setSharpAmount(BigDecimal.ZERO) .setPeakUnitPrice(BigDecimal.valueOf(7D)).setPeakElectricity(BigDecimal.ZERO).setPeakLossElectricity(BigDecimal.ZERO).setPeakAmount(BigDecimal.ZERO) .setFlatUnitPrice(BigDecimal.valueOf(11D)).setFlatElectricity(BigDecimal.valueOf(0.0760D)).setFlatLossElectricity(BigDecimal.ZERO).setFlatAmount(BigDecimal.valueOf(0.8360D)) .setValleyUnitPrice(BigDecimal.valueOf(15D)).setValleyElectricity(BigDecimal.ZERO).setValleyLossElectricity(BigDecimal.ZERO).setValleyAmount(BigDecimal.ZERO) .setElectricityStart(BigDecimal.ZERO).setElectricityEnd(BigDecimal.valueOf(0.0760D)).setTotalElectricity(BigDecimal.valueOf(0.0760D)).setTotalLossElectricity(BigDecimal.ZERO) .setTotalAmount(BigDecimal.valueOf(0.8360D)).setVinCode("12345678901234567").setTradeIdentifier(1) .setTradeTime(DateUtil.parse("2026-04-16 09:49:08.224")).setStopReason(107).setPhysicalCardNo("0000000000000000"); } } ``` --- ## 📁 项目结构 ``` k-cloud-charge-analysis/ │ ├── src/main/ │ ├── java/com/wantllife/ │ │ ├── analysis/ # 协议解析层(平台接收充电桩上报数据) │ │ │ ├── req/ # 充电桩上报请求 (30个文件) │ │ │ │ ├── AALoginReq.java │ │ │ │ ├── ABHeartbeatReq.java │ │ │ │ ├── ACBillingModelValidReq.java │ │ │ │ ├── ADBillingModelReq.java │ │ │ │ ├── AERealTimeMonitorReq.java │ │ │ │ ├── AFChargingHandshakeReq.java │ │ │ │ ├── AGParamConfigReq.java │ │ │ │ ├── ······ │ │ │ │ │ │ │ └── res/ # 平台下发响应 (21个文件) │ │ │ ├── AALoginRes.java │ │ │ ├── ABHeartbeatRes.java │ │ │ ├── ACBillingModeValidRes.java │ │ │ ├── ADBillingModelRes.java │ │ │ ├── AERealTimeMonitorRes.java │ │ │ ├── ······ │ │ │ │ │ ├── config/ # 配置类 │ │ │ ├── CloudChargeConfig.java │ │ │ ├── SimulatorConfig.java │ │ │ └── holder/ │ │ │ └── CloudChargeHolder.java │ │ │ │ │ ├── constant/ # 常量定义 │ │ │ ├── CloudFastChargingConstants.java │ │ │ ├── ColorConstants.java │ │ │ └── SimulatorConstants.java │ │ │ │ │ ├── core/ # 核心类 │ │ │ └── FrameHeader.java # 帧头定义 │ │ │ │ │ ├── domain/ # 领域模型 │ │ │ └── vo/ # 值对象 (16个文件) │ │ │ ├── StandardApplyParallelCharging.java │ │ │ ├── StandardBillingModel.java │ │ │ ├── StandardCard.java │ │ │ ├── StandardChargeFinished.java │ │ │ ├── ······ │ │ │ │ │ ├── enums/ # 枚举类 │ │ │ └── TimeSegment.java # 时间段枚举 │ │ │ │ │ ├── simulator/ # 模拟器模块 │ │ │ ├── DeviceSimulator.java │ │ │ ├── business/ # 业务逻辑 │ │ │ │ └── ChargeSessionManager.java │ │ │ ├── client/ # TCP客户端 │ │ │ │ └── TcpClient.java │ │ │ ├── enums/ # 枚举 │ │ │ │ └── DeviceState.java # 设备状态枚举 │ │ │ ├── fake/ # 模拟数据 │ │ │ │ └── FakeData.java │ │ │ ├── manager/ # 管理器 │ │ │ │ ├── SimTimerScheduler.java │ │ │ │ └── TcpConnectionManager.java │ │ │ ├── process/ # 消息处理 │ │ │ │ ├── DeviceStateHolder.java │ │ │ │ ├── DeviceTimerFacade.java │ │ │ │ ├── MsgHandlerGroup.java │ │ │ │ └── SimDevMsgProcessor.java │ │ │ ├── req/ # 模拟器请求(平台下发指令)(21个文件) │ │ │ │ ├── SAALoginReq.java │ │ │ │ ├── SABHeartbeatReq.java │ │ │ │ ├── SACBillingModeValidReq.java │ │ │ │ ├── SADBillingModelReq.java │ │ │ │ ├── SAERealTimeMonitorReq.java │ │ │ │ ├── SANRequestChargingReq.java │ │ │ │ ├── ······ │ │ │ │ │ │ │ └── res/ # 模拟器响应(模拟充电桩上报)(29个文件) │ │ │ ├── SAALoginRes.java │ │ │ ├── SABHeartbeatRes.java │ │ │ ├── SACBillingModeValidRes.java │ │ │ ├── SADBillingModelRes.java │ │ │ ├── SAERealTimeMonitorRes.java │ │ │ ├── ······ │ │ │ │ │ └── util/ # 工具类 │ │ ├── CRCUtil.java # CRC校验工具 │ │ ├── SimulatorUtil.java # 模拟器工具 │ │ ├── StringUtil.java # 字符串工具 │ │ └── TimeUtil.java # 时间工具 │ │ │ └── resources/ │ ├── LICENSE ├── pom.xml # Maven配置文件 └── README.md # 项目说明文档 ``` --- ## 📦 依赖环境 | 依赖 | 版本 | 说明 | |--------|---------|------| | Lombok | 1.18.46 | 简化代码 | | Hutool | 5.8.44 | 工具库 | | SLF4J | 2.0.18 | 日志门面 | --- ## 📄 许可证 本项目采用 [MIT](LICENSE) 许可证,详情请参阅 LICENSE 文件。 --- ## 👨‍💻 作者 **KevenPotter** - GitHub: [https://github.com/KevenPotter](https://github.com/KevenPotter) - 项目地址: [https://github.com/KevenPotter/k-cloud-charge-analysis](https://github.com/KevenPotter/k-cloud-charge-analysis) --- ⭐ 如果这个项目对你有帮助,欢迎 star!