charging-cabinet/README.md

367 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 充电柜矩阵系统 (Power Matrix System / PMS)
产品名安知充anzhizhichong
## 项目简介
36仓体直流电瓶车充电柜仓数不固定1柜控板+N仓控板每仓控板管6仓最小6仓。
## 技术栈
- **后端**Rust + Axum
- **前端**React + Arco Design Pro
- **数据库**MySQL阿里云RDS
- **缓存**Redis阿里云Redis
- **设备通讯**TCP长连接柜控板4G模块Air780E Cat.1
## 项目结构
```
charging-cabinet/
├── tasks/ # 任务文件opencode执行
├── docs/ # 开发文档、协议文档
├── software/
│ ├── server/ # Rust后端
│ └── web/ # React前端
├── hardware/ # 硬件资料(原理图/PCB等
└── test/ # 测试用例/报告
```
## 部署架构
| 环境 | 域名 | 说明 |
|------|------|------|
| 正式环境 | app.anzhizhichong.com | 后台管理 + API服务 |
| 测试环境 | test.anzhizhichong.com | 测试环境 |
**服务器**CentOS Stream 9
**内部服务**
- Gitea: https://app.anzhizhichong.com/doc/
- 文档Wiki: https://app.anzhizhichong.com/doc/anzhizhichong/charging-cabinet/wiki/
## 服务架构
```
┌─────────────────────────────────────────────────────────────┐
│ API服务 (api-server) — 主服务 │
│ - HTTP API后台管理 + H5端口: 3000 │
│ - 前端轮询刷新每5秒
│ - 业务逻辑处理(验证/存DB/告警) │
│ - 节点管理(设备服务注册/心跳) │
│ - 后台协程处理Redis队列 │
└─────────────────────────────────────────────────────────────┘
↑ Redis LIST ↑ Redis LIST
│ device:report:{imei} │ device:cmd:{imei}
│ (设备上报) │ (平台指令)
│ │
┌─────────────────────────────────────────────────────────────┐
│ 设备服务 (device-server) — 轻量化 │
│ - TCP监听: 3002设备长连接
│ - 只处理通信和指令解析 │
│ - 不存DB、不验证签名、不处理业务 │
│ - 注册到API服务启动/心跳/注销) │
│ - Redis连接注册: device:{imei} → {node_id, ip, port} │
└─────────────────────────────────────────────────────────────┘
```
**核心设计原则:**
1. **TCP服务轻量化** — 只处理TCP通信和指令解析不处理业务逻辑
2. **Redis做缓冲** — 服务间通信用Redis LIST不用HTTP直接调用
3. **全异步处理** — 所有指令异步处理msg_id匹配响应
4. **协程处理任务** — API服务用tokio::spawn处理Redis队列消息
**通信流程:**
- **设备上报**:设备 → TCP → 设备服务 → Redis LIST → API服务协程处理
- **平台指令**API服务 → Redis LIST → 设备服务 → TCP → 设备
- **指令响应**:设备 → TCP → 设备服务 → Redis LIST → API服务匹配msg_id
**节点管理:**
- 设备服务启动时注册到API服务
- 每60秒心跳180秒无心跳视为离线
- 支持多节点扩展(当前单节点)
## TCP通讯协议
### 通信规则
- TCP长连接无需PING包
- 空闲1分钟上报充电15秒上报
- 超时5000ms
- JSON去空格回车省流量LF处理粘包
- `dev_id`IMEI10进制`sub_device_id`485 ID
- `msg_id`自增会话ID
- 全局通道号:`dev_id-sub_device_id-通道号`(如`123456789012345-01-6`
### 签名机制
- 设备首次连接调 `auth_str` 接口获取8位安全码仅一次
- `sign = SHA256(dev_id + auth_str + timestamp).substring(56, 63)`
- 时间戳秒级平台校验±60秒
### 指令清单
| 动作 | 方向 | id格式 | 说明 |
|------|------|--------|------|
| `login` | 设备→平台 | 无 | 登录含sign+timestamp |
| `auth_str` | 设备→平台 | 无 | 获取安全码(仅首次) |
| `status_post` | 设备→平台 | 无 | 定时上报rssi+status字典 |
| `pow_fail` | 设备→平台 | 无 | 电源掉电上报含info区分交直流 |
| `bat_in` | 设备→平台 | `IMEI-仓控板-通道` | 电池插入含BMS数据字段待定 |
| `bat_out` | 设备→平台 | `IMEI-仓控板-通道` | 电池拔出 |
| `on` | **双向** | `IMEI-仓控板-通道` | 开始充电(平台下发/设备上报) |
| `off` | **双向** | `IMEI-仓控板-通道` | 停止供电(平台手动/设备自动) |
| `open` | 平台→设备 | `IMEI` | 开门 |
| `pow_on` | 平台→设备 | `IMEI` | 接触器合闸恢复380V供电 |
| `pow_off` | 平台→设备 | `IMEI` | 接触器分闸切断380V供电 |
| `reset` | 平台→设备 | `IMEI``IMEI-仓控板` | 重启整柜或仓控板 |
### status_post格式
```json
{
"act": "status_post",
"msg_id": 42,
"dev_id": "123456789012345",
"rssi": 85,
"status": {
"123456789012345-01": "000100020003000400050006",
"123456789012345-02": "000100020003000400050006"
}
}
```
- key: `IMEI-仓控板ID`
- value: 6个通道状态拼接每通道2字节=4个HEX字符
- 平台从key解析仓控板数量
### 指令详情
#### login设备→平台
设备登录,验证签名。
```json
// 请求
{"act":"login","msg_id":1,"dev_id":"123456789012345","sign":"abc12345","timestamp":1719800000,"h_ver":"1.0.0","s_ver":"1.0.0"}
// 响应
{"suc":1,"msg_id":1,"cell":6}
```
**字段说明:**
- `sign` — 签名SHA256(dev_id+auth_str+timestamp)后8位
- `timestamp` — 秒级时间戳平台校验±60秒
- `h_ver` — 硬件版本
- `s_ver` — 软件版本
- `cell` — 仓控板数量
---
#### auth_str设备→平台
获取8位安全码仅首次连接调用。
```json
// 请求
{"act":"auth_str","msg_id":2,"dev_id":"123456789012345","ccid":"89860000000000000000"}
// 响应
{"suc":1,"msg_id":2,"data":{"auth_str":"abcd1234"}}
```
**字段说明:**
- `ccid` — SIM卡ICCID号
- `auth_str` — 8位随机安全码仅返回一次
---
#### status_post设备→平台
定时上报空闲60秒/充电15秒。
```json
// 请求
{"act":"status_post","msg_id":10,"dev_id":"123456789012345","rssi":85,"status":{"123456789012345-01":"000100020003000400050006","123456789012345-02":"000100020003000400050006"}}
// 响应
{"suc":1,"msg_id":10}
```
**status字段说明**
- key: `IMEI-仓控板ID`
- value: 6个通道状态拼接每通道2字节=4个HEX字符
- 平台从key解析仓控板数量
---
#### pow_fail设备→平台
电源掉电上报。
```json
// 请求
{"act":"pow_fail","msg_id":15,"dev_id":"123456789012345","info":"0"}
// 响应
{"suc":1,"msg_id":15}
```
**字段说明:**
- `info` — 停电类型:`0`=直流停电(12V掉电)`1`=交流停电(380V掉电)
---
#### pow_on平台→设备
接触器合闸恢复380V充电电源供电。
```json
// 请求
{"act":"pow_on","msg_id":16,"dev_id":"123456789012345"}
// 响应
{"suc":1,"msg_id":16}
```
---
#### pow_off平台→设备
接触器分闸切断380V充电电源供电。
```json
// 请求
{"act":"pow_off","msg_id":16,"dev_id":"123456789012345"}
// 响应
{"suc":1,"msg_id":16}
```
---
#### bat_in设备→平台
电池插入事件,立即上报。
```json
// 请求
{"act":"bat_in","msg_id":20,"id":"123456789012345-01-6","bms":{"soc":45,"voltage":52.3}}
// 响应
{"suc":1,"msg_id":20}
```
**平台处理:**
1. 记录电池信息
2. 验证电池合规性
3. 自动下发 `on` 指令
**BMS数据字段待定。**
---
#### bat_out设备→平台
电池拔出事件,立即上报。
```json
// 请求
{"act":"bat_out","msg_id":21,"id":"123456789012345-01-6"}
// 响应
{"suc":1,"msg_id":21}
```
---
#### on双向
**平台→设备:** 开始充电
```json
// 请求
{"act":"on","msg_id":30,"id":"123456789012345-01-6"}
// 响应
{"suc":1,"msg_id":30}
```
**设备→平台:** 充电开始确认(自动充电模式)
```json
// 上报
{"act":"on","msg_id":30,"id":"123456789012345-01-6"}
```
---
#### off双向
**平台→设备:** 手动停止充电
```json
// 请求
{"act":"off","msg_id":31,"id":"123456789012345-01-6"}
// 响应
{"suc":1,"msg_id":31}
```
**设备→平台:** 充满自动停止
```json
// 上报
{"act":"off","msg_id":31,"id":"123456789012345-01-6"}
```
---
#### open平台→设备
开门只需IMEI。
```json
// 请求
{"act":"open","msg_id":40,"id":"123456789012345"}
// 响应
{"suc":1,"msg_id":40}
```
**错误码63 = 门锁故障。**
---
#### reset平台→设备
重启整柜或仓控板。
```json
// 重启整柜
{"act":"reset","msg_id":60,"id":"123456789012345"}
// 重启仓控板
{"act":"reset","msg_id":61,"id":"123456789012345-01"}
// 响应
{"suc":1,"msg_id":60}
```
### 充电结束原因(平台判断)
| 平台收到 | 判断原因 |
|---------|---------|
| `off` (设备→平台) | `normal` — 正常充满 |
| `off` (平台→设备) | `user_stop` — 用户手动停止 |
| `bat_out` | `normal` — 电池拔出 |
| `bat_in` (通道已有电池) | `forced` — 新电池插入,旧电池强制结束 |
| 超时未响应 | `timeout` — 通信超时 |
## 开发规范
- 任务需求写到 `tasks/` 目录的md文件
- 通过Agent子代理包裹opencode执行异步通知
- 质量约束模板固定追加到每个任务文件
- 完成代码后执行对应栈静态校验Rust→cargo checkTS→tsc --noEmit零报错零警告