mirror of
https://gitee.com/san-bing/JChargePointProtocol
synced 2026-07-17 12:17:53 +08:00
289 lines
17 KiB
Markdown
289 lines
17 KiB
Markdown
# 协议实现模块
|
||
|
||
<cite>
|
||
**本文档中引用的文件**
|
||
- [ProtocolBootstrap.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolBootstrap.java)
|
||
- [YunkuaichongV150ProtocolBootstrap.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java)
|
||
- [LvnengV340ProtocolBootstrap.java](file://jcpp-protocol-lvneng/src/main/java/sanbing/jcpp/protocol/lvneng/v340/LvnengV340ProtocolBootstrap.java)
|
||
- [ProtocolCmd.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/annotation/ProtocolCmd.java)
|
||
- [ProtocolSession.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolSession.java)
|
||
- [ProtocolMessageProcessor.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java)
|
||
- [ProtocolCommandRouter.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/routing/ProtocolCommandRouter.java)
|
||
- [protocol-service.yml](file://jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml)
|
||
- [YunKuaiChongV150HeartbeatULCmd.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java)
|
||
- [DefaultProtocolsConfigProvider.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolsConfigProvider.java)
|
||
- [ProtocolContext.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolContext.java)
|
||
- [DownlinkCmdEnum.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/DownlinkCmdEnum.java)
|
||
</cite>
|
||
|
||
## 目录
|
||
|
||
1. [引言](#引言)
|
||
2. [协议引导类实现](#协议引导类实现)
|
||
3. [上行命令处理器设计](#上行命令处理器设计)
|
||
4. [下行指令生成与发送](#下行指令生成与发送)
|
||
5. [新协议开发指南](#新协议开发指南)
|
||
6. [协议版本管理策略](#协议版本管理策略)
|
||
7. [配置文件详解](#配置文件详解)
|
||
|
||
## 引言
|
||
|
||
本文档旨在为协议实现模块提供详细的开发指南,重点介绍如何基于现有框架扩展新的协议处理功能。通过分析云快充(YunKuaiChong)和绿能(Lvneng)两个具体实现,本文将阐述协议引导类的继承与实现机制、上行命令处理器的设计模式、下行指令的生成与发送流程,并提供新协议开发的分步指南。此外,还将讨论协议版本管理的最佳实践。
|
||
|
||
## 协议引导类实现
|
||
|
||
本文档分析了云快充和绿能两种协议的引导类实现,它们都继承自`ProtocolBootstrap`抽象类。该基类提供了协议服务初始化、健康检查和资源销毁的通用框架。
|
||
`YunkuaichongV150ProtocolBootstrap`和`LvnengV340ProtocolBootstrap`通过重写抽象方法,实现了特定于各自协议的初始化配置。
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class ProtocolBootstrap {
|
||
<<abstract>>
|
||
+ProtocolContext protocolContext
|
||
+ProtocolCfg protocolCfg
|
||
+Listener listener
|
||
+Forwarder forwarder
|
||
+init() void
|
||
+destroy() void
|
||
+health() Health
|
||
+getProtocolName() String
|
||
+_init() void
|
||
+_destroy() void
|
||
+messageProcessor() ProtocolMessageProcessor
|
||
}
|
||
class YunkuaichongV150ProtocolBootstrap {
|
||
+PROTOCOL_NAME String
|
||
+getProtocolName() String
|
||
+_init() void
|
||
+_destroy() void
|
||
+messageProcessor() ProtocolMessageProcessor
|
||
}
|
||
class LvnengV340ProtocolBootstrap {
|
||
+PROTOCOL_NAME String
|
||
+getProtocolName() String
|
||
+_init() void
|
||
+_destroy() void
|
||
+messageProcessor() ProtocolMessageProcessor
|
||
}
|
||
ProtocolBootstrap <|-- YunkuaichongV150ProtocolBootstrap
|
||
ProtocolBootstrap <|-- LvnengV340ProtocolBootstrap
|
||
```
|
||
|
||
**图示来源**
|
||
|
||
- [ProtocolBootstrap.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolBootstrap.java)
|
||
- [YunkuaichongV150ProtocolBootstrap.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java)
|
||
- [LvnengV340ProtocolBootstrap.java](file://jcpp-protocol-lvneng/src/main/java/sanbing/jcpp/protocol/lvneng/v340/LvnengV340ProtocolBootstrap.java)
|
||
|
||
**本节来源**
|
||
|
||
- [ProtocolBootstrap.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolBootstrap.java#L1-L126)
|
||
- [YunkuaichongV150ProtocolBootstrap.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java#L1-L47)
|
||
- [LvnengV340ProtocolBootstrap.java](file://jcpp-protocol-lvneng/src/main/java/sanbing/jcpp/protocol/lvneng/v340/LvnengV340ProtocolBootstrap.java#L1-L41)
|
||
|
||
### 云快充V150协议引导类
|
||
|
||
`YunkuaichongV150ProtocolBootstrap`是云快充1.5.0版本协议的引导类。它通过`@ProtocolComponent`
|
||
注解注册到Spring容器中,确保在服务启动时被正确加载。该类的核心是重写`messageProcessor()`方法,返回一个
|
||
`YunKuaiChongProtocolMessageProcessor`实例,该实例负责处理所有与云快充协议相关的消息编解码和路由。
|
||
|
||
### 绿能V340协议引导类
|
||
|
||
`LvnengV340ProtocolBootstrap`是绿能3.4.0版本协议的引导类,其结构和实现方式与云快充引导类高度相似。它同样通过
|
||
`@ProtocolComponent`注解进行注册,并在`messageProcessor()`方法中返回一个`LvnengProtocolMessageProcessor`实例,用于处理绿能协议的特定消息。
|
||
|
||
## 上行命令处理器设计
|
||
|
||
上行命令处理器的设计采用了基于注解的命令路由模式。核心是`@ProtocolCmd`注解,它将一个命令类与特定的命令字(value)和协议名称(protocolNames)关联起来。
|
||
`ProtocolCommandRouter`负责在应用启动时扫描所有带有`@ProtocolCmd`注解的类,并构建一个从协议名+命令字到处理器实例的映射表。
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant 设备 as 充电桩设备
|
||
participant TcpListener as TcpListener
|
||
participant ProtocolCommandRouter as ProtocolCommandRouter
|
||
participant 处理器 as YunKuaiChongV150HeartbeatULCmd
|
||
设备->>TcpListener : 发送心跳包 (0x03)
|
||
TcpListener->>ProtocolCommandRouter : 根据协议名和命令字查找处理器
|
||
ProtocolCommandRouter-->>TcpListener : 返回处理器实例
|
||
TcpListener->>处理器 : 调用execute方法
|
||
处理器->>处理器 : 解析消息体,提取桩编码、枪号等信息
|
||
处理器->>处理器 : 刷新会话状态
|
||
处理器->>Kafka : 将心跳数据转发到后端
|
||
处理器->>设备 : 发送心跳响应
|
||
```
|
||
|
||
**图示来源**
|
||
|
||
- [ProtocolCmd.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/annotation/ProtocolCmd.java)
|
||
- [ProtocolCommandRouter.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/routing/ProtocolCommandRouter.java)
|
||
- [YunKuaiChongV150HeartbeatULCmd.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java)
|
||
|
||
**本节来源**
|
||
|
||
- [ProtocolCmd.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/annotation/ProtocolCmd.java#L1-L32)
|
||
- [ProtocolCommandRouter.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/routing/ProtocolCommandRouter.java#L1-L104)
|
||
- [YunKuaiChongV150HeartbeatULCmd.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java#L1-L84)
|
||
|
||
### @ProtocolCmd注解详解
|
||
|
||
`@ProtocolCmd`注解是命令路由机制的核心。其`value`属性定义了命令的唯一标识(如0x03代表心跳),`protocolNames`
|
||
属性则指定了该命令适用于哪些协议版本。例如,`YunKuaiChongV150HeartbeatULCmd`的`protocolNames`
|
||
包含了V150、V160和V170,表明该处理器可以处理这三个版本的心跳包。
|
||
|
||
### 心跳包处理器示例
|
||
|
||
`YunKuaiChongV150HeartbeatULCmd`是一个典型的上行命令处理器。当设备发送心跳包时,`ProtocolCommandRouter`
|
||
会根据协议名和命令字0x03找到该处理器。`execute`方法首先解析消息体,提取桩编码、枪号等信息,然后通过
|
||
`ProtocolSessionRegistryProvider`激活会话,最后将心跳数据封装成Protobuf消息并发送到Kafka主题,同时向设备发送一个心跳响应。
|
||
|
||
## 下行指令生成与发送
|
||
|
||
下行指令的生成与发送流程始于一个REST API调用。当后端应用需要向充电桩发送指令(如远程启动充电)时,它会调用协议服务的REST接口。该请求最终由
|
||
`DownlinkController`接收,并通过`ProtocolSession`下发到具体的TCP连接。
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant 前端 as 前端应用
|
||
participant DownlinkController as DownlinkController
|
||
participant ProtocolSession as ProtocolSession
|
||
participant TcpSession as TcpSession
|
||
participant 设备 as 充电桩设备
|
||
前端->>DownlinkController : POST /downlink (远程启动指令)
|
||
DownlinkController->>ProtocolSession : onDownlink(DownlinkRequestMessage)
|
||
ProtocolSession->>TcpSession : 获取对应的TCP会话
|
||
TcpSession->>TcpSession : 根据指令类型查找编码器
|
||
TcpSession->>TcpSession : 编码指令为二进制流
|
||
TcpSession->>设备 : 通过TCP连接发送指令
|
||
设备->>TcpSession : 返回响应
|
||
TcpSession->>ProtocolSession : 处理响应
|
||
ProtocolSession->>DownlinkController : 返回执行结果
|
||
DownlinkController->>前端 : 返回API响应
|
||
```
|
||
|
||
**图示来源**
|
||
|
||
- [DownlinkController.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/adapter/DownlinkController.java)
|
||
- [ProtocolSession.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolSession.java)
|
||
- [TcpSession.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/listener/tcp/TcpSession.java)
|
||
|
||
**本节来源**
|
||
|
||
- [ProtocolSession.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/ProtocolSession.java#L1-L123)
|
||
- [ProtocolMessageProcessor.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/ProtocolMessageProcessor.java#L1-L77)
|
||
- [DownlinkCmdEnum.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/domain/DownlinkCmdEnum.java#L1-L54)
|
||
|
||
### 流程详解
|
||
|
||
1. **API调用**:前端或后端服务通过HTTP POST请求调用`/downlink`接口,请求体包含指令类型(如`REMOTE_START_CHARGING`)和相关参数。
|
||
2. **指令接收**:`DownlinkController`接收到请求,将其转换为`DownlinkRequestMessage`对象。
|
||
3. **会话查找**:控制器通过`ProtocolSessionRegistryProvider`根据桩编码找到对应的`ProtocolSession`。
|
||
4. **指令下发**:`ProtocolSession`的`onDownlink`方法被调用,它会将指令委托给底层的`TcpSession`。
|
||
5. **消息编码**:`TcpSession`根据指令类型(由`DownlinkCmdEnum`定义)查找对应的编码器,将Protobuf消息编码为协议规定的二进制格式。
|
||
6. **网络发送**:编码后的二进制流通过Netty的`Channel`发送到充电桩设备。
|
||
7. **响应处理**:设备返回响应后,`TcpSession`会根据响应的命令字触发相应的上行处理器进行处理。
|
||
|
||
## 新协议开发指南
|
||
|
||
开发一个新协议的实现可以遵循以下分步指南:
|
||
|
||
### 第一步:创建Bootstrap类
|
||
|
||
1. 在`jcpp-protocol-<your-protocol-name>`模块中创建一个新的包,例如`v100`。
|
||
2. 创建一个名为`YourProtocolV100Bootstrap`的类,继承`ProtocolBootstrap`。
|
||
3. 使用`@ProtocolComponent`注解标记该类,并传入协议的唯一名称。
|
||
4. 重写`getProtocolName()`方法,返回协议名称常量。
|
||
5. 重写`messageProcessor()`方法,返回一个自定义的`ProtocolMessageProcessor`实现。
|
||
|
||
### 第二步:定义消息处理器
|
||
|
||
1. 创建一个`cmd`包来存放所有命令处理器。
|
||
2. 为每个上行命令创建一个处理器类,例如`YourProtocolV100LoginULCmd`。
|
||
3. 让处理器类继承`AbstractProtocolUplinkCmdExe`或类似基类。
|
||
4. 使用`@ProtocolCmd`注解标记该类,指定命令字和适用的协议名称。
|
||
5. 重写`execute`方法,实现消息解析、业务逻辑处理和响应生成。
|
||
|
||
### 第三步:配置文件
|
||
|
||
1. 在`protocol-service.yml`文件的`service.protocols`节点下,为新协议添加一个配置项。
|
||
2. 配置`enabled`、`listener.tcp.bind-port`和`forwarder.type`等关键参数。
|
||
3. 确保`bind-port`不与其他协议冲突。
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
Start([开始]) --> CreateBootstrap["创建Bootstrap类<br/>继承ProtocolBootstrap"]
|
||
CreateBootstrap --> Annotate["使用@ProtocolComponent注解"]
|
||
Annotate --> OverrideMethods["重写getProtocolName<br/>和messageProcessor方法"]
|
||
OverrideMethods --> DefineCmd["定义上行命令处理器<br/>在cmd包中"]
|
||
DefineCmd --> AnnotateCmd["使用@ProtocolCmd注解<br/>指定命令字和协议名"]
|
||
AnnotateCmd --> ImplementExecute["实现execute方法<br/>处理业务逻辑"]
|
||
ImplementExecute --> Configure["在protocol-service.yml中<br/>添加协议配置"]
|
||
Configure --> Test["测试新协议功能"]
|
||
Test --> End([完成])
|
||
```
|
||
|
||
**图示来源**
|
||
|
||
- [YunkuaichongV150ProtocolBootstrap.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java)
|
||
- [YunKuaiChongV150HeartbeatULCmd.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java)
|
||
- [protocol-service.yml](file://jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml)
|
||
|
||
**本节来源**
|
||
|
||
- [YunkuaichongV150ProtocolBootstrap.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java#L1-L47)
|
||
- [YunKuaiChongV150HeartbeatULCmd.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/cmd/YunKuaiChongV150HeartbeatULCmd.java#L1-L84)
|
||
- [protocol-service.yml](file://jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml#L1-L273)
|
||
|
||
## 协议版本管理策略
|
||
|
||
本框架通过模块化和配置化的方式支持多协议版本管理。每个协议版本都有独立的Bootstrap类和命令处理器,这使得不同版本的协议可以并行运行而互不干扰。
|
||
|
||
### 策略一:独立版本模块
|
||
|
||
如云快充协议所示,`v150`、`v160`和`v170`分别有独立的`YunkuaichongV150ProtocolBootstrap`、
|
||
`YunkuaichongV160ProtocolBootstrap`和`YunkuaichongV170ProtocolBootstrap`。每个版本监听不同的TCP端口(38001, 38002,
|
||
38003),从而实现了物理隔离。
|
||
|
||
### 策略二:共享处理器
|
||
|
||
对于功能兼容的版本,可以共享部分命令处理器。例如,`YunKuaiChongV150HeartbeatULCmd`的`@ProtocolCmd`注解中`protocolNames`
|
||
包含了V150、V160和V170,这意味着同一个处理器可以处理这三个版本的心跳包,减少了代码重复。
|
||
|
||
### 策略三:配置驱动
|
||
|
||
协议的启用、端口、转发器类型等都通过`protocol-service.yml`文件中的配置项控制。通过环境变量(如
|
||
`PROTOCOLS_YUNKUAICHONGV150_ENABLED`)可以动态地启用或禁用某个协议版本,无需重新编译代码。
|
||
|
||
**本节来源**
|
||
|
||
- [YunkuaichongV150ProtocolBootstrap.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v150/YunkuaichongV150ProtocolBootstrap.java)
|
||
- [YunkuaichongV160ProtocolBootstrap.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v160/YunkuaichongV160ProtocolBootstrap.java)
|
||
- [YunkuaichongV170ProtocolBootstrap.java](file://jcpp-protocol-yunkuaichong/src/main/java/sanbing/jcpp/protocol/yunkuaichong/v170/YunkuaichongV170ProtocolBootstrap.java)
|
||
- [protocol-service.yml](file://jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml)
|
||
|
||
## 配置文件详解
|
||
|
||
`protocol-service.yml`是协议服务的核心配置文件,它定义了所有协议的运行参数。
|
||
|
||
### 协议配置结构
|
||
|
||
每个协议的配置都位于`service.protocols`节点下,以协议名称(如`yunkuaichongV150`)作为键。其主要包含两个子节点:
|
||
|
||
- **listener.tcp**: 定义TCP监听器的配置,包括`bind-address`、`bind-port`、线程池大小和连接超时等。
|
||
- **forwarder**: 定义消息转发器的配置,包括`type`(kafka或memory)和具体的转发目标(如Kafka主题)。
|
||
|
||
### 环境变量注入
|
||
|
||
配置文件大量使用了环境变量(如`${PROTOCOLS_YUNKUAICHONGV150_LISTENER_TCP_BIND_PORT:38001}`
|
||
),这使得服务可以在不同环境中灵活部署。冒号后的值是默认值,当环境变量未设置时使用。
|
||
|
||
### 拆包器配置
|
||
|
||
`listener.tcp.handler.configuration`是一个关键配置,它定义了Netty的拆包器。例如,云快充协议使用
|
||
`JCPPLengthFieldBasedFrameDecoder`,并指定了帧头、长度字段偏移量等参数,确保能正确解析设备发送的二进制流。
|
||
|
||
**本节来源**
|
||
|
||
- [protocol-service.yml](file://jcpp-protocol-bootstrap/src/main/resources/protocol-service.yml#L1-L273)
|
||
- [DefaultProtocolsConfigProvider.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/provider/impl/DefaultProtocolsConfigProvider.java#L1-L38)
|
||
- [ProtocolCfg.java](file://jcpp-protocol-api/src/main/java/sanbing/jcpp/protocol/cfg/ProtocolCfg.java#L1-L26) |