# 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,高性能、轻量级、零框架依赖,专为充电桩与平台对接场景设计。
[](https://maven.apache.org/)
[](https://www.oracle.com/java/)
[](LICENSE)
[](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!