Initial commit: CCU621_M firmware project with BLE debug link support.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,730 @@
|
||||
# TCP调试服务器通信协议说明
|
||||
|
||||
## 协议概述
|
||||
|
||||
本协议用于CCU601E充电桩TCP调试服务器与客户端之间的通信。协议基于JSON格式,包含心跳、系统信息、充电枪数据、充电控制、参数查询与修改等功能。
|
||||
|
||||
## 基本协议格式
|
||||
|
||||
所有消息遵循以下基本格式:
|
||||
```json
|
||||
{
|
||||
"command": "命令类型",
|
||||
"data": { ... },
|
||||
"timestamp": "YYYY-MM-DD HH:MM:SS.000",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `command`: 命令类型(字符串)
|
||||
- `data`: 命令数据(对象)
|
||||
- `timestamp`: 时间戳,格式为"YYYY-MM-DD HH:MM:SS.000"
|
||||
- `version`: 协议版本,固定为"2.0"
|
||||
|
||||
## 命令类型列表
|
||||
|
||||
| 命令 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| 心跳 | Heartbeat | 心跳消息,用于保持连接 |
|
||||
| 系统信息 | SystemInfo | 系统信息查询/上报 |
|
||||
| 枪信息 | GunInfo | 充电枪数据查询/上报 |
|
||||
| 充电控制 | ChargeControl | 充电启停控制 |
|
||||
| 参数查询 | ParamQuery | 参数查询 |
|
||||
| 参数修改 | ParamModify | 参数修改 |
|
||||
| 文件操作 | FileOperations | 文件系统操作(查询、删除、下载、上传) |
|
||||
|
||||
## 1. 心跳消息 (Heartbeat)
|
||||
|
||||
### 1.1 服务器发送心跳
|
||||
```json
|
||||
{
|
||||
"command": "Heartbeat",
|
||||
"data": {
|
||||
"sequence": 123,
|
||||
"source": "device"
|
||||
},
|
||||
"timestamp": "2025-12-27 14:48:27.000",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `sequence`: 序列号,每次递增
|
||||
- `source`: 来源,固定为"device"
|
||||
|
||||
### 1.2 客户端回复心跳
|
||||
```json
|
||||
{
|
||||
"command": "Heartbeat",
|
||||
"data": {
|
||||
"sequence": 123,
|
||||
"source": "client"
|
||||
},
|
||||
"timestamp": "2025-12-27 14:48:27.100",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 系统信息 (SystemInfo)
|
||||
|
||||
### 2.1 服务器发送系统信息
|
||||
```json
|
||||
{
|
||||
"command": "SystemInfo",
|
||||
"data": {
|
||||
"systemModel": "IES-2000",
|
||||
"gunCount": 2,
|
||||
"devicePower": 120.0,
|
||||
"networkStatus": 1,
|
||||
"faultCount": 0,
|
||||
"version": "V1.2.3"
|
||||
},
|
||||
"timestamp": "2025-12-27 14:48:28.000",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `systemModel`: 系统型号
|
||||
- `gunCount`: 枪数量
|
||||
- `devicePower`: 设备功率(kW)
|
||||
- `networkStatus`: 网络状态(0:异常, 1:正常)
|
||||
- `faultCount`: 故障数量
|
||||
- `version`: 软件版本
|
||||
|
||||
## 3. 充电枪信息 (GunInfo)
|
||||
|
||||
### 3.1 服务器发送枪信息
|
||||
```json
|
||||
{
|
||||
"command": "GunInfo",
|
||||
"data": {
|
||||
"gunNumber": 1,
|
||||
"chargingStatus": 2,
|
||||
"gunConnectionStatus": 1,
|
||||
"chargingTime": 125,
|
||||
"demandVoltage": 400.0,
|
||||
"demandCurrent": 32.0,
|
||||
"actualVoltage": 398.5,
|
||||
"actualCurrent": 30.2,
|
||||
"energy": 15.7,
|
||||
"cost": 31.4,
|
||||
"batterySOC": 65.0,
|
||||
"userId": "user001",
|
||||
"orderNumber": "ORD20231227001"
|
||||
},
|
||||
"timestamp": "2025-12-27 14:48:29.000",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `gunNumber`: 枪编号(1-2)
|
||||
- `chargingStatus`: 充电状态(0:空闲, 1:已连接, 2:充电中, 3:充电完成, 4:故障)
|
||||
- `gunConnectionStatus`: 枪连接状态(0:未连接, 1:已连接)
|
||||
- `chargingTime`: 充电时间(分钟)
|
||||
- `demandVoltage`: 需求电压(V)
|
||||
- `demandCurrent`: 需求电流(A)
|
||||
- `actualVoltage`: 实际电压(V)
|
||||
- `actualCurrent`: 实际电流(A)
|
||||
- `energy`: 充电能量(kWh)
|
||||
- `cost`: 充电费用(元)
|
||||
- `batterySOC`: 电池SOC(%)
|
||||
- `userId`: 用户ID
|
||||
- `orderNumber`: 订单号
|
||||
|
||||
## 4. 充电控制 (ChargeControl)
|
||||
|
||||
### 4.1 客户端发送充电控制
|
||||
```json
|
||||
{
|
||||
"command": "ChargeControl",
|
||||
"data": {
|
||||
"gunNumber": 1,
|
||||
"action": 1
|
||||
},
|
||||
"timestamp": "2025-12-27 14:48:30.000",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `gunNumber`: 枪编号(1-2)
|
||||
- `action`: 动作(0:停止充电, 1:启动充电)
|
||||
|
||||
### 4.2 服务器响应充电控制
|
||||
```json
|
||||
{
|
||||
"command": "ChargeControl",
|
||||
"data": {
|
||||
"gunNumber": 1,
|
||||
"action": 1,
|
||||
"result": 0,
|
||||
"message": "充电启动成功"
|
||||
},
|
||||
"timestamp": "2025-12-27 14:48:30.500",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `result`: 结果(0:成功, 1:失败)
|
||||
- `message`: 结果消息
|
||||
|
||||
## 5. 参数查询与修改
|
||||
|
||||
### 5.1 参数分类(type字段)
|
||||
|
||||
| 类型 | 值 | 说明 |
|
||||
|------|-----|------|
|
||||
| 系统基本参数 | BasicParameters | 桩编号、功率、电压电流等 |
|
||||
| 网络参数 | NetworkParameters | IP、域名、平台配置等 |
|
||||
| 电表参数 | MeterParameters | 电表数量、归属、地址等 |
|
||||
| 使能参数 | EnableParameters | 门禁、枪锁、国标等使能标志 |
|
||||
| 费率参数 | FeeParameters | 计费模型、费率类型等 |
|
||||
| SDK参数 | SDKParameters | 产品秘钥、设备密钥等 |
|
||||
| 二维码参数 | QRCodeParameters | 各枪二维码内容 |
|
||||
|
||||
### 5.2 参数查询请求
|
||||
```json
|
||||
{
|
||||
"command": "ParamQuery",
|
||||
"data": {
|
||||
"type": "BasicParameters"
|
||||
},
|
||||
"timestamp": "2025-12-27 14:48:31.000",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 参数查询响应
|
||||
```json
|
||||
{
|
||||
"command": "ParamQuery",
|
||||
"data": {
|
||||
"type": "BasicParameters",
|
||||
"count": 18,
|
||||
"params": [
|
||||
{
|
||||
"name": "pile_serial_num",
|
||||
"type": "string",
|
||||
"value": "CCU601E-001"
|
||||
},
|
||||
{
|
||||
"name": "u8_gunCnt",
|
||||
"type": "uint8",
|
||||
"value": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
"timestamp": "2025-12-27 14:48:31.500",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `count`: 参数总数
|
||||
- `params`: 参数数组
|
||||
- `name`: 参数名称
|
||||
- `type`: 参数类型
|
||||
- `value`: 参数值
|
||||
|
||||
### 5.4 参数修改请求
|
||||
```json
|
||||
{
|
||||
"command": "ParamModify",
|
||||
"data": {
|
||||
"type": "BasicParameters",
|
||||
"params": [
|
||||
{
|
||||
"name": "pile_serial_num",
|
||||
"type": "string",
|
||||
"value": "CCU601E-002"
|
||||
},
|
||||
{
|
||||
"name": "u8_threshold_gunTemp",
|
||||
"type": "int",
|
||||
"value": 85
|
||||
}
|
||||
]
|
||||
},
|
||||
"timestamp": "2025-12-27 14:48:32.000",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.5 参数修改响应
|
||||
```json
|
||||
{
|
||||
"command": "ParamModify",
|
||||
"data": {
|
||||
"type": "BasicParameters",
|
||||
"received": 2,
|
||||
"processed": 2,
|
||||
"success": 2,
|
||||
"failed": 0
|
||||
},
|
||||
"timestamp": "2025-12-27 14:48:32.500",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `received`: 接收到的参数数量
|
||||
- `processed`: 已处理的参数数量
|
||||
- `success`: 成功的参数数量
|
||||
- `failed`: 失败的参数数量
|
||||
|
||||
## 6. 参数详细列表
|
||||
|
||||
### 6.1 BasicParameters(系统基本参数)
|
||||
| 参数名 | 类型 | 说明 | 示例值 |
|
||||
|--------|------|------|--------|
|
||||
| pile_serial_num | string | 桩编号 | "CCU601E-001" |
|
||||
| u8_pwd | string | 维护密码 | "123456" |
|
||||
| u8_gunCnt | uint8 | 枪数量 | 2 |
|
||||
| u8_pile_type | uint8 | 桩类型 | 1 |
|
||||
| u8_terminal | uint8_array | 终端地址 | [0, 0] |
|
||||
| e_ledType | uint8 | 灯板类型 | 0 |
|
||||
| u8_threshold_gunTemp | uint8 | 枪温阈值(°C) | 90 |
|
||||
| u8_soc_limit | uint8 | SOC阈值(%) | 100 |
|
||||
| u16_ratePow | uint32 | 额定功率(W) | 120000 |
|
||||
| u16_max_volt | uint16 | 最大电压(0.1V) | 10000 |
|
||||
| u16_min_volt | uint16 | 最小电压(0.1V) | 2000 |
|
||||
| u16_trickle_limit | uint16 | 涓流充电停机阈值(0.1A) | 100 |
|
||||
| u16_max_cur | uint16 | 最大电流(0.1A) | 2500 |
|
||||
| u8_mdu_num | uint8 | 充电模块数量 | 4 |
|
||||
| u8_MduType | uint8 | 模块类型 | 1 |
|
||||
| u8_mduTemp_flag | uint8 | 模块温度获取使能 | 1 |
|
||||
| u8_mdu_rated_pow | uint8 | 模块额定功率(kW) | 30 |
|
||||
| u16_mdu_max_vol | uint16 | 模块最大电压(0.1V) | 10000 |
|
||||
| u16_mdu_max_cur | uint16 | 模块最大电流(0.1A) | 3000 |
|
||||
|
||||
### 6.2 NetworkParameters(网络参数)
|
||||
| 参数名 | 类型 | 说明 | 示例值 |
|
||||
|--------|------|------|--------|
|
||||
| ethIp | array | IP地址(4字节数组) | [192,168,1,140] |
|
||||
| ethGate | array | 网关(4字节数组) | [192,168,1,254] |
|
||||
| ethMask | array | 子网掩码(4字节数组) | [255,255,255,0] |
|
||||
| ethMac | array | MAC地址(6字节数组) | [0,14,234,3,165,1] |
|
||||
| serverADomain | string | 服务器A域名 | "39.104.96.26,8897" |
|
||||
| serverBDomain | string | 服务器B域名 | "" |
|
||||
| platAPileNum | string | 平台1桩编号 | "" |
|
||||
| platBPileNum | string | 平台2桩编号 | "" |
|
||||
| programType | bit | 程序类型(2位) 0:社会,1:sdk,2:ocpp | 0b00 |
|
||||
| netAType | bit | 平台1联网方式(2位) 0:以太网,1:GPRS | 0b00 |
|
||||
| netBType | bit | 平台2联网方式(2位) 0:以太网,1:GPRS | 0b00 |
|
||||
| platAEn | bit | 平台1使能(2位) 0:不使能,1:使能 | 0b01 |
|
||||
| platBEn | bit | 平台2使能(2位) 0:不使能,1:使能 | 0b01 |
|
||||
| platAType | bit | 平台1类型(4位) 0:云快充,1:... | 0b0000 |
|
||||
| platBType | bit | 平台2类型(4位) 默认积成平台 | 0b0000 |
|
||||
| simAnpType | bit | SIM卡APN类型(2位) 0:公网卡,1:国网移动,2:国网联通,3:国网电信 | 0b00 |
|
||||
|
||||
### 6.3 MeterParameters(电表参数)
|
||||
| 参数名 | 类型 | 说明 | 示例值 |
|
||||
|--------|------|------|--------|
|
||||
| u8_meter_num | uint8 | 电表数量(0-4) | 2 |
|
||||
| meter_0_inGun | uint8 | 电表0归属枪号(0:无效,1:A枪,2:B枪) | 1 |
|
||||
| meter_0_highPrec | uint8 | 电表0高精度支持(0:不支持,1:支持) | 1 |
|
||||
| meter_0_isAC | uint8 | 电表0是否为交流表(0:直流表,1:交流表) | 0 |
|
||||
| meter_0_addr | hex_array | 电表0地址(6字节) | "00 00 00 00 00 00" |
|
||||
| meter_1_inGun | uint8 | 电表1归属枪号 | 2 |
|
||||
| meter_1_highPrec | uint8 | 电表1高精度支持 | 1 |
|
||||
| meter_1_isAC | uint8 | 电表1是否为交流表 | 0 |
|
||||
| meter_1_addr | hex_array | 电表1地址 | "00 00 00 00 00 00" |
|
||||
| meter_2_inGun | uint8 | 电表2归属枪号 | 1 |
|
||||
| meter_2_highPrec | uint8 | 电表2高精度支持 | 0 |
|
||||
| meter_2_isAC | uint8 | 电表2是否为交流表 | 1 |
|
||||
| meter_2_addr | hex_array | 电表2地址 | "00 00 00 00 00 00" |
|
||||
| meter_3_inGun | uint8 | 电表3归属枪号 | 2 |
|
||||
| meter_3_highPrec | uint8 | 电表3高精度支持 | 0 |
|
||||
| meter_3_isAC | uint8 | 电表3是否为交流表 | 1 |
|
||||
| meter_3_addr | hex_array | 电表3地址 | "00 00 00 00 00 00" |
|
||||
|
||||
### 6.4 EnableParameters(使能参数)
|
||||
| 参数名 | 类型 | 说明 | 示例值 |
|
||||
|--------|------|------|--------|
|
||||
| bit_doorEnable | uint8 | 门禁使能 | 0 |
|
||||
| bit_gunLockEna | uint8 | 枪锁使能 | 1 |
|
||||
| bit_gunLockNeg | uint8 | 枪锁信号取反 | 0 |
|
||||
| bit_AcContactEna | uint8 | 交流接触器使能 | 1 |
|
||||
| bit_gunHoming | uint8 | 枪归位信号有无 | 1 |
|
||||
| bit_GBEnable | uint8 | 国标使能 | 1 |
|
||||
| bit_LcdEnable | uint8 | 液晶使能 | 1 |
|
||||
| bit_auxpower | uint8 | 辅助电压返回状态使能 | 0 |
|
||||
| bit_reliefen | uint8 | 泄放使能 | 1 |
|
||||
| bit_YeLenEn | uint8 | 液冷通讯使能 | 0 |
|
||||
| bit_ABcommEn | uint8 | 主副班通讯使能 | 0 |
|
||||
| bit_ScreenSaverEn | uint8 | UI屏保使能 | 1 |
|
||||
| bit_PlugEn | uint8 | 即插即充使能 | 0 |
|
||||
| bit_uiEnglishEn | uint8 | UI界面英语使能 | 0 |
|
||||
|
||||
### 6.5 FeeParameters(费率参数)
|
||||
| 参数名 | 类型 | 说明 | 示例值 |
|
||||
|--------|------|------|--------|
|
||||
| u8_model_no | string | 计费模型编号(17字节) | "FEE20231227001" |
|
||||
| u8_feeCnt | uint8 | 费率条数(0-30) | 5 |
|
||||
| fee_type_0_rate | uint32 | 费率类型0费率(小数点后5位) | 15000 |
|
||||
| fee_type_0_sevice | uint32 | 费率类型0服务费(小数点后5位) | 500 |
|
||||
| fee_type_1_rate | uint32 | 费率类型1费率 | 18000 |
|
||||
| fee_type_1_sevice | uint32 | 费率类型1服务费 | 600 |
|
||||
| fee_type_2_rate | uint32 | 费率类型2费率 | 20000 |
|
||||
| fee_type_2_sevice | uint32 | 费率类型2服务费 | 700 |
|
||||
| ... | ... | ... | ... |
|
||||
| fee_type_29_rate | uint32 | 费率类型29费率 | 0 |
|
||||
| fee_type_29_sevice | uint32 | 费率类型29服务费 | 0 |
|
||||
| u8_fee_type_0 | uint8 | 时段0费率类型(1-30) | 1 |
|
||||
| u8_fee_type_1 | uint8 | 时段1费率类型 | 1 |
|
||||
| u8_fee_type_2 | uint8 | 时段2费率类型 | 1 |
|
||||
| ... | ... | ... | ... |
|
||||
| u8_fee_type_95 | uint8 | 时段95费率类型 | 3 |
|
||||
|
||||
### 6.6 SDKParameters(SDK参数)
|
||||
| 参数名 | 类型 | 说明 | 示例值 |
|
||||
|--------|------|------|--------|
|
||||
| product_key | string | 产品秘钥(20字节) | "" |
|
||||
| device_name | string | 资产码(32字节) | "" |
|
||||
| device_secret | string | 设备密钥(64字节) | "" |
|
||||
| device_reg_code | string | 注册码(64字节) | "" |
|
||||
| device_uid | string | 出厂编码(64字节) | "" |
|
||||
| firmware_version | string | 固件版本信息(64字节) | "V1.0.0" |
|
||||
|
||||
### 6.7 QRCodeParameters(二维码参数)
|
||||
| 参数名 | 类型 | 说明 | 示例值 |
|
||||
|--------|------|------|--------|
|
||||
| qrcode_gun1 | string | 枪1二维码 | "https://example.com/qr/001" |
|
||||
| qrcode_gun2 | string | 枪2二维码 | "https://example.com/qr/002" |
|
||||
|
||||
## 7. 文件操作 (FileOperations)
|
||||
|
||||
文件操作用于上位机对单片机文件系统进行操作,包含查询、删除、下载、上传四种操作。
|
||||
|
||||
### 7.1 文件操作子命令
|
||||
|
||||
| 子命令 | 值 | 说明 |
|
||||
|--------|-----|------|
|
||||
| 文件查询 | Query | 查询指定目录下的文件列表 |
|
||||
| 文件删除 | Delete | 删除指定文件 |
|
||||
| 文件下载 | Download | 下载文件内容 |
|
||||
| 文件上传 | Upload | 上传文件到设备 |
|
||||
|
||||
### 7.2 文件查询请求
|
||||
```json
|
||||
{
|
||||
"command": "FileOperations",
|
||||
"data": {
|
||||
"subCommand": "Query",
|
||||
"path": "/",
|
||||
"recursive": 0
|
||||
},
|
||||
"timestamp": "2025-12-29 14:00:00.000",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `subCommand`: 子命令,固定为"Query"
|
||||
- `path`: 查询路径,如"/"表示根目录
|
||||
- `recursive`: 是否递归查询(0:否, 1:是)
|
||||
|
||||
### 7.3 文件查询响应
|
||||
```json
|
||||
{
|
||||
"command": "FileOperations",
|
||||
"data": {
|
||||
"subCommand": "Query",
|
||||
"path": "/",
|
||||
"fileCount": 3,
|
||||
"dirCount": 2,
|
||||
"totalSize": 1050624,
|
||||
"files": [
|
||||
{
|
||||
"name": "config.txt",
|
||||
"type": 0,
|
||||
"size": 1024,
|
||||
"modifiedTime": "2025-12-29 10:00:00",
|
||||
"createdTime": "2025-12-29 10:00:00"
|
||||
},
|
||||
{
|
||||
"name": "log.txt",
|
||||
"type": 0,
|
||||
"size": 2048,
|
||||
"modifiedTime": "2025-12-29 11:30:00",
|
||||
"createdTime": "2025-12-29 11:30:00"
|
||||
},
|
||||
{
|
||||
"name": "firmware.bin",
|
||||
"type": 0,
|
||||
"size": 1048576,
|
||||
"modifiedTime": "2025-12-29 12:00:00",
|
||||
"createdTime": "2025-12-29 12:00:00"
|
||||
}
|
||||
]
|
||||
},
|
||||
"timestamp": "2025-12-29 14:00:00.500",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `fileCount`: 文件数量
|
||||
- `dirCount`: 目录数量
|
||||
- `totalSize`: 总文件大小(字节)
|
||||
- `files`: 文件信息数组
|
||||
- `name`: 文件名
|
||||
- `type`: 文件类型(0:普通文件, 1:目录, 2:系统文件)
|
||||
- `size`: 文件大小(字节)
|
||||
- `modifiedTime`: 修改时间
|
||||
- `createdTime`: 创建时间
|
||||
|
||||
### 7.4 文件删除请求
|
||||
|
||||
文件删除请求支持两种格式:
|
||||
|
||||
#### 格式1:直接指定完整路径
|
||||
```json
|
||||
{
|
||||
"command": "FileOperations",
|
||||
"data": {
|
||||
"subCommand": "Delete",
|
||||
"path": "/config.txt",
|
||||
"force": 0
|
||||
},
|
||||
"timestamp": "2025-12-29 14:01:00.000",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
#### 格式2:指定路径和文件名(推荐格式)
|
||||
```json
|
||||
{
|
||||
"command": "FileOperations",
|
||||
"data": {
|
||||
"subCommand": "Delete",
|
||||
"fileName": "20260104_143116.log",
|
||||
"path": "/log",
|
||||
"force": 0
|
||||
},
|
||||
"timestamp": "2026-01-04 15:06:04.630",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `subCommand`: 子命令,固定为"Delete"
|
||||
- `path`: 文件路径(格式1)或目录路径(格式2)
|
||||
- `fileName`: 文件名(仅格式2需要)
|
||||
- `force`: 强制删除(0:否, 1:是),可选字段,默认为0
|
||||
|
||||
**注意**:
|
||||
1. 格式1中,`path`字段包含完整的文件路径(如`/config.txt`)
|
||||
2. 格式2中,`path`字段为目录路径,`fileName`为文件名,系统会自动组合为完整路径:`path + "/" + fileName`
|
||||
3. 如果同时提供`path`和`fileName`字段,优先使用格式2(组合路径)
|
||||
4. `force`字段为可选字段,默认为0(不强制删除)。当设置为1时,允许删除系统文件
|
||||
|
||||
### 7.5 文件删除响应
|
||||
```json
|
||||
{
|
||||
"command": "FileOperations",
|
||||
"data": {
|
||||
"subCommand": "Delete",
|
||||
"path": "/config.txt",
|
||||
"status": 0,
|
||||
"message": "File deleted successfully"
|
||||
},
|
||||
"timestamp": "2025-12-29 14:01:00.500",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `status`: 删除状态(0:成功, 1:失败, 3:文件未找到, 5:权限不足)
|
||||
- `message`: 结果消息
|
||||
|
||||
### 7.6 文件下载请求
|
||||
```json
|
||||
{
|
||||
"command": "FileOperations",
|
||||
"data": {
|
||||
"subCommand": "Download",
|
||||
"path": "/log.txt",
|
||||
"offset": 0,
|
||||
"chunkSize": 1024
|
||||
},
|
||||
"timestamp": "2025-12-29 14:02:00.000",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `subCommand`: 子命令,固定为"Download"
|
||||
- `path`: 文件路径
|
||||
- `offset`: 起始偏移(字节),用于断点续传。首次下载设置为0
|
||||
- `chunkSize`: 分块大小(字节),最大1024字节,超过1024会自动限制为1024
|
||||
|
||||
**下载流程说明**:
|
||||
1. 客户端首次请求时,设置`offset=0`,`chunkSize`建议设置为1024(最大支持值)
|
||||
2. 服务器返回文件的第一块数据,包含`fileSize`(文件总大小)和`isLastChunk`标志
|
||||
3. 客户端根据返回的`dataLen`计算下一次请求的偏移:`offset = offset + dataLen`
|
||||
4. 重复步骤1-3,直到`isLastChunk=1`表示下载完成
|
||||
5. 如果下载中断,可以从上次的偏移位置继续下载(断点续传)
|
||||
|
||||
### 7.7 文件下载响应
|
||||
```json
|
||||
{
|
||||
"command": "FileOperations",
|
||||
"data": {
|
||||
"subCommand": "Download",
|
||||
"path": "/log.txt",
|
||||
"fileSize": 2048,
|
||||
"offset": 0,
|
||||
"chunkSize": 1024,
|
||||
"dataLen": 11,
|
||||
"isLastChunk": 0,
|
||||
"data": "SGVsbG8gV29ybGQ="
|
||||
},
|
||||
"timestamp": "2025-12-29 14:02:00.500",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `fileSize`: 文件总大小(字节)
|
||||
- `offset`: 当前数据块的起始偏移(字节)
|
||||
- `chunkSize`: 实际使用的分块大小(字节),可能与请求的chunkSize不同(受1024字节限制)
|
||||
- `dataLen`: Base64编码后的数据长度(字节)
|
||||
- `isLastChunk`: 是否为最后一块(0:否, 1:是)。当`offset + dataLen >= fileSize`时为1
|
||||
- `data`: 数据内容(Base64编码)。实际二进制数据大小为`dataLen`字节,Base64编码后长度约为`dataLen * 4/3`
|
||||
|
||||
**技术细节**:
|
||||
1. **分块限制**:每块数据最大1024字节。如果请求的`chunkSize`超过1024,会自动限制为1024
|
||||
2. **Base64编码**:所有文件数据都使用Base64编码传输,确保二进制数据的安全传输
|
||||
3. **进度计算**:下载进度可通过公式计算:`进度 = (offset + dataLen) / fileSize * 100%`
|
||||
4. **错误处理**:
|
||||
- 如果文件不存在,返回错误
|
||||
- 如果`offset`超过文件大小,返回错误
|
||||
- 如果读取文件失败,返回错误
|
||||
5. **内存管理**:客户端需要及时释放接收到的数据内存,避免内存泄漏
|
||||
|
||||
### 7.8 文件上传请求
|
||||
```json
|
||||
{
|
||||
"command": "FileOperations",
|
||||
"data": {
|
||||
"subCommand": "Upload",
|
||||
"path": "/upload/newfile.txt",
|
||||
"fileSize": 4096,
|
||||
"chunkSize": 1024,
|
||||
"offset": 0,
|
||||
"data": "SGVsbG8gV29ybGQ=",
|
||||
"dataLen": 11,
|
||||
"isLastChunk": 0
|
||||
},
|
||||
"timestamp": "2025-12-29 14:03:00.000",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `subCommand`: 子命令,固定为"Upload"
|
||||
- `path`: 文件路径
|
||||
- `fileSize`: 文件总大小
|
||||
- `chunkSize`: 分块大小
|
||||
- `offset`: 当前偏移
|
||||
- `data`: 数据内容(Base64编码)
|
||||
- `dataLen`: 数据长度
|
||||
- `isLastChunk`: 是否为最后一块(0:否, 1:是)
|
||||
|
||||
### 7.9 文件上传响应
|
||||
```json
|
||||
{
|
||||
"command": "FileOperations",
|
||||
"data": {
|
||||
"subCommand": "Upload",
|
||||
"path": "/upload/newfile.txt",
|
||||
"receivedSize": 1024,
|
||||
"totalSize": 4096,
|
||||
"status": 2,
|
||||
"message": "Upload in progress"
|
||||
},
|
||||
"timestamp": "2025-12-29 14:03:00.500",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
字段说明:
|
||||
- `receivedSize`: 已接收大小
|
||||
- `totalSize`: 文件总大小
|
||||
- `status`: 上传状态(0:成功, 1:失败, 2:进行中, 4:空间不足)
|
||||
- `message`: 结果消息
|
||||
|
||||
### 7.10 文件操作状态码
|
||||
|
||||
| 状态码 | 值 | 说明 |
|
||||
|--------|-----|------|
|
||||
| 成功 | 0 | 操作成功 |
|
||||
| 失败 | 1 | 操作失败 |
|
||||
| 进行中 | 2 | 操作进行中(适用于上传/下载) |
|
||||
| 文件未找到 | 3 | 文件不存在 |
|
||||
| 空间不足 | 4 | 磁盘空间不足 |
|
||||
| 权限不足 | 5 | 权限不足 |
|
||||
|
||||
## 8. 数据类型说明
|
||||
|
||||
| 类型 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| string | 字符串 | "CCU601E-001" |
|
||||
| int | 整数 | 85 |
|
||||
| uint8 | 无符号8位整数 | 2 |
|
||||
| uint16 | 无符号16位整数 | 2500 |
|
||||
| uint32 | 无符号32位整数 | 120000 |
|
||||
| ip_address | IP地址字符串 | "192.168.1.140" |
|
||||
| mac_address | MAC地址字符串 | "00:0E:EA:03:A5:01" |
|
||||
| hex_array | 十六进制数组字符串 | "00 00 00 00 00 00" |
|
||||
| uint8_array | 无符号8位整数数组 | [1,1,1,2,2,2,3,3,3] |
|
||||
|
||||
## 8. 通信流程
|
||||
|
||||
### 8.1 连接建立
|
||||
1. 客户端连接TCP服务器(端口9699)
|
||||
2. 服务器接受连接(仅允许一个客户端)
|
||||
3. 开始周期性通信
|
||||
|
||||
### 8.2 周期性消息
|
||||
- 心跳:每10秒发送一次
|
||||
- 系统信息:每10秒发送一次
|
||||
- 充电枪数据:每1秒发送一次(两把枪分别发送)
|
||||
|
||||
### 8.3 心跳超时
|
||||
- 客户端超过60秒未发送心跳,服务器主动断开连接
|
||||
|
||||
### 8.4 命令交互
|
||||
1. 客户端发送命令请求
|
||||
2. 服务器处理命令并返回响应
|
||||
3. 错误时返回相应的错误信息
|
||||
|
||||
## 9. 错误处理
|
||||
|
||||
### 9.1 通用错误格式
|
||||
```json
|
||||
{
|
||||
"command": "原命令",
|
||||
"data": {
|
||||
"error": {
|
||||
"code": 1001,
|
||||
"message": "错误描述"
|
||||
}
|
||||
},
|
||||
"timestamp": "2025-12-27 14:48:33.000",
|
||||
"version": "2.0"
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 错误码定义
|
||||
| 错误码 | 说明 |
|
||||
|--------|------|
|
||||
| 1000 | 参数解析错误 |
|
||||
| 1001 | 命令不支持 |
|
||||
| 1002 | 参数缺失 |
|
||||
| 1003 | 参数值无效
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* @file fc41d_ble.h
|
||||
* @brief FC41D 蓝牙调试:配置宏 + 对外接口(仅 BLE_DEBUG_EN=1 有效)
|
||||
*
|
||||
* 分层:
|
||||
* - 本头文件:编译配置、DIP 选路、comm_link 后端 API
|
||||
* - fc41d_ble.c:AT 命令、模组电源、BLE 状态机、QBLETRANMODE 透传收发(内部 static)
|
||||
*/
|
||||
|
||||
#ifndef FC41D_BLE_H
|
||||
#define FC41D_BLE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "publicdata/public_define.h"
|
||||
|
||||
#if BLE_DEBUG_EN
|
||||
|
||||
#include "usart.h"
|
||||
|
||||
/* ---------- 硬件 / 串口 ---------- */
|
||||
#ifndef FC41D_USART_ID
|
||||
#define FC41D_USART_ID E_USART_7 /* UART7 PE1/PE0,接 FC41D */
|
||||
#endif
|
||||
|
||||
#ifndef FC41D_UART_HW_NUM
|
||||
#define FC41D_UART_HW_NUM 7U /* UART7 PE1/PE0 */
|
||||
#endif
|
||||
|
||||
#ifndef FC41D_RST_PULSE_MS
|
||||
#define FC41D_RST_PULSE_MS 200U /* 复位脉冲高电平保持时间 */
|
||||
#endif
|
||||
|
||||
#ifndef FC41D_POWER_ON_DELAY_MS
|
||||
#define FC41D_POWER_ON_DELAY_MS 5000U /* 复位释放后等待模组启动 */
|
||||
#endif
|
||||
|
||||
#ifndef FC41D_UART_BAUD
|
||||
#define FC41D_UART_BAUD 115200U /* UART7 固定波特率,不再做双波特率探测 */
|
||||
#endif
|
||||
|
||||
#ifndef FC41D_AT_CMD_WAIT_MS
|
||||
#define FC41D_AT_CMD_WAIT_MS 1500U /* 常规 AT 阻塞等待 */
|
||||
#endif
|
||||
|
||||
#ifndef FC41D_GATT_AT_WAIT_MS
|
||||
#define FC41D_GATT_AT_WAIT_MS 1200U /* QBLEGATTSNTFY 阻塞等待 */
|
||||
#endif
|
||||
|
||||
#ifndef FC41D_AT_INIT_WAIT_MS
|
||||
#define FC41D_AT_INIT_WAIT_MS 8000U /* QBLEINIT 栈初始化较慢,单独加长 */
|
||||
#endif
|
||||
|
||||
#ifndef FC41D_STAT_POLL_MS
|
||||
#define FC41D_STAT_POLL_MS 10000U /* 等待连接时 QBLESTAT 兜底查询间隔 */
|
||||
#endif
|
||||
|
||||
#ifndef FC41D_CONNECT_WAIT_MS
|
||||
#define FC41D_CONNECT_WAIT_MS (120000U) /* 等待手机连接最长时间 */
|
||||
#endif
|
||||
|
||||
/* ---------- BLE GATT / 广播名(Quectel 手册 §4.2.1 外设示例) ---------- */
|
||||
#ifndef FC41D_BLE_NAME
|
||||
#define FC41D_BLE_NAME "CCU621_Debug"
|
||||
#endif
|
||||
|
||||
#ifndef FC41D_SRV_UUID
|
||||
#define FC41D_SRV_UUID "fff1"
|
||||
#endif
|
||||
|
||||
#ifndef FC41D_CHAR_WRITE_UUID
|
||||
#define FC41D_CHAR_WRITE_UUID "fff2" /* 手机 → 设备 Write */
|
||||
#endif
|
||||
|
||||
#ifndef FC41D_CHAR_NOTIFY_UUID
|
||||
#define FC41D_CHAR_NOTIFY_UUID "fff3" /* 设备 → 手机 Notify */
|
||||
#endif
|
||||
|
||||
/** QBLETRANMODE 优先尝试的 UUID(部分固件需 fff2 而非 fff3) */
|
||||
#ifndef FC41D_TRAN_UUID
|
||||
#define FC41D_TRAN_UUID FC41D_CHAR_WRITE_UUID
|
||||
#endif
|
||||
|
||||
/** 1=连接后 AT+QBLETRANMODE 透传(手册);0=GATT 非透传 +QBLERECV */
|
||||
#ifndef FC41D_BLE_USE_TRANSPARENT
|
||||
#define FC41D_BLE_USE_TRANSPARENT 1
|
||||
#endif
|
||||
|
||||
#ifndef BLE_DEBUG_PRINT
|
||||
#define BLE_DEBUG_PRINT 1
|
||||
#endif
|
||||
|
||||
#if BLE_DEBUG_PRINT
|
||||
#include <stdio.h>
|
||||
#define BT_PRINT(fmt, ...) printf("[BT] " fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define BT_PRINT(fmt, ...) do {} while (0)
|
||||
#endif
|
||||
|
||||
/* ---------- DIP1 拨码选路(副板 sub_comm 上报) ---------- */
|
||||
|
||||
/** @brief 等待副板 DI 帧就绪,便于读取 DIP1 */
|
||||
int ble_link_wait_dip_ready(uint32_t timeout_ms);
|
||||
|
||||
/** @brief DIP1=1 时为蓝牙调试上位机模式 */
|
||||
uint8_t ble_link_is_bt_mode(void);
|
||||
|
||||
/* ---------- comm_link 后端(与 tcp_server 共用 JSON 协议) ---------- */
|
||||
|
||||
int comm_link_ble_start(void);
|
||||
void comm_link_ble_stop(void);
|
||||
void comm_link_ble_process(void);
|
||||
int comm_link_ble_is_client_connected(void);
|
||||
void comm_link_ble_close_client(void);
|
||||
int comm_link_ble_receive_data(uint8_t *buffer, uint32_t buffer_size);
|
||||
int comm_link_ble_rx_upgrade_begin(void);
|
||||
int comm_link_ble_rx_upgrade_end(void);
|
||||
void comm_link_ble_rx_flush_pending(void);
|
||||
int comm_link_ble_diag_trace(void);
|
||||
int comm_link_ble_send_data(const uint8_t *data, uint32_t len);
|
||||
int comm_link_ble_get_peer_info(char *buf, uint32_t buf_size);
|
||||
|
||||
#endif /* BLE_DEBUG_EN */
|
||||
|
||||
#endif /* FC41D_BLE_H */
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* @file fc41d_ble_priv.h
|
||||
* @brief FC41D BLE 后端私有上下文(与 tcp_link_ctx_t 在 comm_link 中 union 复用)
|
||||
*/
|
||||
|
||||
#ifndef FC41D_BLE_PRIV_H
|
||||
#define FC41D_BLE_PRIV_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "fc41d_ble.h"
|
||||
|
||||
#define FC41D_AT_RX_ACC_SIZE 768U
|
||||
#define FC41D_AT_LINE_MAX 128U
|
||||
#define FC41D_AT_CMD_MAX 1200U
|
||||
|
||||
/** link 解析缓冲(AT 行与 +QBLERECV) */
|
||||
#define FC41D_LINK_RX_NORMAL 512U
|
||||
/** 固件升级:单包 1024B Base64 JSON + UART 残包 */
|
||||
#define FC41D_LINK_RX_UPGRADE 2048U
|
||||
|
||||
/** 应用 RX 环(心跳/参数查询等) */
|
||||
#define FC41D_APP_RX_NORMAL 1024U
|
||||
/** 固件升级 Data JSON 拼包(1024B 载荷约 1.6KB JSON) */
|
||||
#define FC41D_APP_RX_UPGRADE 2048U
|
||||
|
||||
typedef enum {
|
||||
FC41D_BLE_ST_IDLE = 0,
|
||||
FC41D_BLE_ST_POWER_ON,
|
||||
FC41D_BLE_ST_WAIT_CONNECT,
|
||||
FC41D_BLE_ST_CONNECTED,
|
||||
FC41D_BLE_ST_ERROR,
|
||||
} fc41d_ble_state_t;
|
||||
|
||||
typedef enum {
|
||||
FC41D_BLE_URC_NONE = 0,
|
||||
FC41D_BLE_URC_CONNECTED,
|
||||
FC41D_BLE_URC_DISCONNECTED,
|
||||
} fc41d_ble_urc_t;
|
||||
|
||||
typedef struct {
|
||||
char rx_acc[FC41D_AT_RX_ACC_SIZE];
|
||||
uint16_t rx_acc_len;
|
||||
fc41d_ble_urc_t pending_urc;
|
||||
char at_cmd_buf[FC41D_AT_LINE_MAX];
|
||||
char ble_cmd_buf[64];
|
||||
char gatt_at_cmd[FC41D_AT_CMD_MAX];
|
||||
} fc41d_ble_at_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *link_rx_acc;
|
||||
uint16_t link_rx_acc_cap;
|
||||
uint16_t link_rx_acc_len;
|
||||
uint16_t qble_payload_need;
|
||||
uint8_t *app_rx;
|
||||
uint16_t app_rx_cap;
|
||||
uint16_t app_rx_wr;
|
||||
uint16_t app_rx_rd;
|
||||
} fc41d_ble_io_t;
|
||||
|
||||
typedef struct {
|
||||
fc41d_ble_state_t state;
|
||||
uint64_t connect_deadline_ms;
|
||||
uint64_t power_on_deadline_ms;
|
||||
uint64_t last_stat_poll_ms;
|
||||
uint8_t transparent; /* 1=QBLETRANMODE 已生效,UART 直传 JSON */
|
||||
uint8_t gatt_fallback; /* 1=透传 AT 失败,回退 +QBLERECV/QBLEGATTSNTFY */
|
||||
} fc41d_ble_sm_t;
|
||||
|
||||
typedef struct {
|
||||
fc41d_ble_at_t at;
|
||||
fc41d_ble_io_t io;
|
||||
fc41d_ble_sm_t sm;
|
||||
} fc41d_ble_ctx_t;
|
||||
|
||||
#endif /* FC41D_BLE_PRIV_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* @file debug_files.h
|
||||
* @brief 文件类业务:FileOperations (FATFS) + FirmwareUpgrade (Flash IAP) — 阶段2合并
|
||||
*
|
||||
* 合并自:system_filectrl.h、system_filetransfer.h
|
||||
*/
|
||||
|
||||
#ifndef DEBUG_FILES_H
|
||||
#define DEBUG_FILES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ========== FileOperations (FATFS) ========== */
|
||||
|
||||
#define FILE_OP_SUBCMD_QUERY "Query"
|
||||
#define FILE_OP_SUBCMD_DELETE "Delete"
|
||||
#define FILE_OP_SUBCMD_DOWNLOAD "Download"
|
||||
#define FILE_OP_SUBCMD_UPLOAD "Upload"
|
||||
|
||||
#define FILE_OP_STATUS_SUCCESS 0
|
||||
#define FILE_OP_STATUS_FAILED 1
|
||||
#define FILE_OP_STATUS_IN_PROGRESS 2
|
||||
#define FILE_OP_STATUS_NOT_FOUND 3
|
||||
#define FILE_OP_STATUS_NO_SPACE 4
|
||||
#define FILE_OP_STATUS_PERMISSION_DENIED 5
|
||||
|
||||
#define FILE_TYPE_REGULAR 0
|
||||
#define FILE_TYPE_DIRECTORY 1
|
||||
#define FILE_TYPE_SYSTEM 2
|
||||
|
||||
typedef struct {
|
||||
char name[128];
|
||||
uint8_t type;
|
||||
uint32_t size;
|
||||
char modified_time[32];
|
||||
char created_time[32];
|
||||
} file_info_t;
|
||||
|
||||
typedef struct {
|
||||
char path[256];
|
||||
uint8_t recursive;
|
||||
} file_query_request_t;
|
||||
|
||||
typedef struct {
|
||||
char path[256];
|
||||
uint16_t file_count;
|
||||
uint16_t dir_count;
|
||||
uint32_t total_size;
|
||||
file_info_t *files;
|
||||
} file_query_response_t;
|
||||
|
||||
typedef struct {
|
||||
char path[256];
|
||||
uint8_t force;
|
||||
} file_delete_request_t;
|
||||
|
||||
typedef struct {
|
||||
char path[256];
|
||||
uint8_t status;
|
||||
char message[128];
|
||||
} file_delete_response_t;
|
||||
|
||||
typedef struct {
|
||||
char path[256];
|
||||
uint32_t offset;
|
||||
uint32_t chunk_size;
|
||||
} file_download_request_t;
|
||||
|
||||
typedef struct {
|
||||
char path[256];
|
||||
uint32_t file_size;
|
||||
uint32_t offset;
|
||||
uint32_t chunk_size;
|
||||
uint32_t data_len;
|
||||
uint8_t *data;
|
||||
uint8_t is_last_chunk;
|
||||
} file_download_response_t;
|
||||
|
||||
typedef struct {
|
||||
char path[256];
|
||||
uint32_t file_size;
|
||||
uint32_t chunk_size;
|
||||
uint32_t offset;
|
||||
uint32_t data_len;
|
||||
uint8_t *data;
|
||||
uint8_t is_last_chunk;
|
||||
} file_upload_request_t;
|
||||
|
||||
typedef struct {
|
||||
char path[256];
|
||||
uint32_t received_size;
|
||||
uint32_t total_size;
|
||||
uint8_t status;
|
||||
char message[128];
|
||||
} file_upload_response_t;
|
||||
|
||||
typedef struct {
|
||||
char subCommand[32];
|
||||
union {
|
||||
file_query_request_t query;
|
||||
file_delete_request_t delete;
|
||||
file_download_request_t download;
|
||||
file_upload_request_t upload;
|
||||
} request;
|
||||
union {
|
||||
file_query_response_t query;
|
||||
file_delete_response_t delete;
|
||||
file_download_response_t download;
|
||||
file_upload_response_t upload;
|
||||
} response;
|
||||
} file_operation_data_t;
|
||||
|
||||
int system_filectrl_init(void);
|
||||
int system_filectrl_process_query(const file_query_request_t *request, file_query_response_t *response);
|
||||
int system_filectrl_process_delete(const file_delete_request_t *request, file_delete_response_t *response);
|
||||
int system_filectrl_process_download(const file_download_request_t *request, file_download_response_t *response);
|
||||
int system_filectrl_process_upload(const file_upload_request_t *request, file_upload_response_t *response);
|
||||
void system_filectrl_free_query_response(file_query_response_t *response);
|
||||
void system_filectrl_free_download_response(file_download_response_t *response);
|
||||
void system_filectrl_free_upload_request(file_upload_request_t *request);
|
||||
int system_filectrl_get_filesystem_info(uint32_t *total_space, uint32_t *free_space);
|
||||
int system_filectrl_file_exists(const char *path);
|
||||
int system_filectrl_get_file_info(const char *path, file_info_t *info);
|
||||
|
||||
#ifndef TCP_DEBUG_FILE_AVAILABLE
|
||||
#define TCP_DEBUG_FILE_AVAILABLE 0
|
||||
#endif
|
||||
|
||||
/* ========== FirmwareUpgrade (Flash IAP) ========== */
|
||||
|
||||
typedef enum {
|
||||
FILE_TRANSFER_IDLE = 0,
|
||||
FILE_TRANSFER_STARTED,
|
||||
FILE_TRANSFER_RECEIVING,
|
||||
FILE_TRANSFER_COMPLETED,
|
||||
FILE_TRANSFER_FAILED
|
||||
} file_transfer_state_t;
|
||||
|
||||
typedef struct {
|
||||
file_transfer_state_t state;
|
||||
uint32_t file_size;
|
||||
uint32_t total_packets;
|
||||
uint32_t packet_size;
|
||||
uint32_t received_packets;
|
||||
uint32_t received_bytes;
|
||||
char firmware_version[32];
|
||||
uint32_t crc32;
|
||||
uint8_t *buffer;
|
||||
} firmware_upgrade_context_t;
|
||||
|
||||
int system_filetransfer_init(void);
|
||||
int system_filetransfer_start_firmware_upgrade(uint32_t file_size, uint32_t total_packets,
|
||||
uint32_t packet_size, const char *firmware_version);
|
||||
/** 应答 Start 后执行:擦除升级区并写升级头(可能阻塞数秒) */
|
||||
int system_filetransfer_commit_firmware_start(void);
|
||||
int system_filetransfer_receive_firmware_packet(uint32_t packet_index, const uint8_t *data,
|
||||
uint32_t data_len, uint32_t crc);
|
||||
int system_filetransfer_end_firmware_upgrade(uint8_t status, const char *error_message);
|
||||
int system_filetransfer_is_reboot_pending(void);
|
||||
void system_filetransfer_clear_reboot_pending(void);
|
||||
int system_filetransfer_get_firmware_status(firmware_upgrade_context_t *context);
|
||||
int system_filetransfer_is_active(void);
|
||||
int system_filetransfer_get_expected_packet_bytes(uint32_t packet_index, uint32_t *out_len);
|
||||
void system_filetransfer_touch_activity_on_rx(void);
|
||||
void system_filetransfer_poll_idle_timeout(void);
|
||||
void system_filetransfer_reset(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DEBUG_FILES_H */
|
||||
@@ -0,0 +1,621 @@
|
||||
/**
|
||||
* @file debug_link.c
|
||||
* @brief 调试链路层实现(comm_link_ctx + TCP Socket + comm_link 分发)
|
||||
*/
|
||||
|
||||
#include "debug_link.h"
|
||||
#include "tcp_protocol.h"
|
||||
#include "publicdata/public_define.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "semphr.h"
|
||||
#include "app_fatfs/fatfs_init.h"
|
||||
|
||||
#if BLE_DEBUG_EN
|
||||
#include "ble_link/fc41d_ble.h"
|
||||
#endif
|
||||
|
||||
#if TCP_DEBUG_EN
|
||||
|
||||
/* =============================================================================
|
||||
* 会话上下文(原 comm_link_ctx.c)
|
||||
* ============================================================================= */
|
||||
|
||||
static comm_link_ctx_t s_comm_link_ctx;
|
||||
|
||||
comm_link_ctx_t *comm_link_ctx_get(void)
|
||||
{
|
||||
return &s_comm_link_ctx;
|
||||
}
|
||||
|
||||
comm_link_session_t *comm_link_session(void)
|
||||
{
|
||||
return &s_comm_link_ctx.session;
|
||||
}
|
||||
|
||||
void comm_link_ctx_reset_session(void)
|
||||
{
|
||||
comm_link_type_t saved_type = s_comm_link_ctx.session.type;
|
||||
|
||||
memset(&s_comm_link_ctx.session, 0, sizeof(s_comm_link_ctx.session));
|
||||
s_comm_link_ctx.session.type = saved_type;
|
||||
}
|
||||
|
||||
void comm_link_ctx_reset_all(void)
|
||||
{
|
||||
memset(&s_comm_link_ctx, 0, sizeof(s_comm_link_ctx));
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
* TCP Socket 后端(原 tcp_server_socket.c,本文件内 static)
|
||||
* ============================================================================= */
|
||||
|
||||
#ifndef AF_INET
|
||||
#define AF_INET 2
|
||||
#endif
|
||||
#ifndef SOCK_STREAM
|
||||
#define SOCK_STREAM 1
|
||||
#endif
|
||||
#ifndef SOL_SOCKET
|
||||
#define SOL_SOCKET 0xFFF
|
||||
#endif
|
||||
#ifndef SO_REUSEADDR
|
||||
#define SO_REUSEADDR 0x0004
|
||||
#endif
|
||||
#ifndef INADDR_ANY
|
||||
#define INADDR_ANY 0x00000000
|
||||
#endif
|
||||
#ifndef F_GETFL
|
||||
#define F_GETFL 3
|
||||
#endif
|
||||
#ifndef F_SETFL
|
||||
#define F_SETFL 4
|
||||
#endif
|
||||
#ifndef O_NONBLOCK
|
||||
#define O_NONBLOCK 0x0004
|
||||
#endif
|
||||
#ifndef EWOULDBLOCK
|
||||
#define EWOULDBLOCK 11
|
||||
#endif
|
||||
#ifndef EAGAIN
|
||||
#define EAGAIN 11
|
||||
#endif
|
||||
#ifndef ECONNRESET
|
||||
#define ECONNRESET 104
|
||||
#endif
|
||||
#ifndef EPIPE
|
||||
#define EPIPE 32
|
||||
#endif
|
||||
#ifndef ECONNABORTED
|
||||
#define ECONNABORTED 103
|
||||
#endif
|
||||
#ifndef ETIMEDOUT
|
||||
#define ETIMEDOUT 110
|
||||
#endif
|
||||
#ifndef ENETDOWN
|
||||
#define ENETDOWN 100
|
||||
#endif
|
||||
#ifndef ENETUNREACH
|
||||
#define ENETUNREACH 101
|
||||
#endif
|
||||
#ifndef EHOSTUNREACH
|
||||
#define EHOSTUNREACH 113
|
||||
#endif
|
||||
#ifndef errno
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
#define TCP_LOCAL_PORT TCP_DEBUG_PORT
|
||||
#define MAX_CLIENTS TCP_DEBUG_MAX_CLIENTS
|
||||
|
||||
typedef struct {
|
||||
int server_socket;
|
||||
int client_socket;
|
||||
uint8_t is_client_connected;
|
||||
struct sockaddr_in client_addr;
|
||||
} tcp_socket_server_t;
|
||||
|
||||
static tcp_socket_server_t socket_server = {
|
||||
.server_socket = -1,
|
||||
.client_socket = -1,
|
||||
.is_client_connected = 0,
|
||||
};
|
||||
|
||||
static SemaphoreHandle_t socket_mutex = NULL;
|
||||
|
||||
static int tcp_socket_create_server(void)
|
||||
{
|
||||
int sock;
|
||||
struct sockaddr_in server_addr;
|
||||
int opt = 1;
|
||||
|
||||
sock = lwip_socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0) {
|
||||
TCP_PRINT("Failed to create socket: %d\r\n", sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lwip_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
server_addr.sin_port = htons(TCP_LOCAL_PORT);
|
||||
|
||||
if (lwip_bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
|
||||
TCP_PRINT("Failed to bind socket to port %d\r\n", TCP_LOCAL_PORT);
|
||||
lwip_close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (lwip_listen(sock, MAX_CLIENTS) < 0) {
|
||||
TCP_PRINT("Failed to listen on socket\r\n");
|
||||
lwip_close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
int flags = lwip_fcntl(sock, F_GETFL, 0);
|
||||
lwip_fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
||||
}
|
||||
|
||||
TCP_PRINT("TCP server socket created on port %d\r\n", TCP_LOCAL_PORT);
|
||||
return sock;
|
||||
}
|
||||
|
||||
static int tcp_socket_init(void)
|
||||
{
|
||||
socket_mutex = xSemaphoreCreateMutex();
|
||||
if (socket_mutex == NULL) {
|
||||
TCP_PRINT("Failed to create socket mutex\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
socket_server.server_socket = tcp_socket_create_server();
|
||||
if (socket_server.server_socket < 0) {
|
||||
vSemaphoreDelete(socket_mutex);
|
||||
socket_mutex = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
socket_server.client_socket = -1;
|
||||
socket_server.is_client_connected = 0;
|
||||
|
||||
TCP_PRINT("Socket server initialized successfully\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcp_socket_accept_client(void)
|
||||
{
|
||||
struct sockaddr_in client_addr;
|
||||
socklen_t addr_len = sizeof(client_addr);
|
||||
int new_sock;
|
||||
|
||||
if (socket_server.server_socket < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreTake(socket_mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
new_sock = lwip_accept(socket_server.server_socket,
|
||||
(struct sockaddr *)&client_addr, &addr_len);
|
||||
|
||||
if (new_sock >= 0) {
|
||||
if (socket_server.is_client_connected) {
|
||||
TCP_PRINT("Rejecting new connection - already have a client\r\n");
|
||||
lwip_close(new_sock);
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreGive(socket_mutex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
{
|
||||
int flags = lwip_fcntl(new_sock, F_GETFL, 0);
|
||||
lwip_fcntl(new_sock, F_SETFL, flags | O_NONBLOCK);
|
||||
}
|
||||
|
||||
socket_server.client_socket = new_sock;
|
||||
socket_server.is_client_connected = 1;
|
||||
memcpy(&socket_server.client_addr, &client_addr, sizeof(client_addr));
|
||||
|
||||
TCP_PRINT("Client connected from %s:%d\r\n",
|
||||
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
|
||||
}
|
||||
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreGive(socket_mutex);
|
||||
}
|
||||
|
||||
return (new_sock >= 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int tcp_socket_receive_data(uint8_t *buffer, uint32_t buffer_size)
|
||||
{
|
||||
int received;
|
||||
|
||||
if (buffer == NULL || buffer_size == 0U) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreTake(socket_mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
if (!socket_server.is_client_connected || socket_server.client_socket < 0) {
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreGive(socket_mutex);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
received = lwip_recv(socket_server.client_socket, buffer, buffer_size, 0);
|
||||
|
||||
if (received > 0) {
|
||||
TCP_PRINT("Received %d bytes from client\r\n", received);
|
||||
} else if (received == 0) {
|
||||
TCP_PRINT("Client disconnected\r\n");
|
||||
lwip_close(socket_server.client_socket);
|
||||
socket_server.client_socket = -1;
|
||||
socket_server.is_client_connected = 0;
|
||||
received = -1;
|
||||
#if FATFS_EN
|
||||
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
|
||||
#endif
|
||||
} else {
|
||||
int err = errno;
|
||||
if (err != EWOULDBLOCK && err != EAGAIN) {
|
||||
TCP_PRINT("Socket receive error: %d\r\n", err);
|
||||
if (err == ECONNRESET || err == EPIPE || err == ECONNABORTED ||
|
||||
err == ETIMEDOUT || err == ENETDOWN || err == ENETUNREACH ||
|
||||
err == EHOSTUNREACH) {
|
||||
TCP_PRINT("Connection error detected, closing socket\r\n");
|
||||
lwip_close(socket_server.client_socket);
|
||||
socket_server.client_socket = -1;
|
||||
socket_server.is_client_connected = 0;
|
||||
#if FATFS_EN
|
||||
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
|
||||
#endif
|
||||
}
|
||||
received = -1;
|
||||
} else {
|
||||
received = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreGive(socket_mutex);
|
||||
}
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
static int tcp_socket_send_data(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
int sent;
|
||||
|
||||
if (data == NULL || len == 0U) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreTake(socket_mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
if (!socket_server.is_client_connected || socket_server.client_socket < 0) {
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreGive(socket_mutex);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
sent = lwip_send(socket_server.client_socket, data, len, 0);
|
||||
|
||||
if (sent > 0) {
|
||||
TCP_PRINT("Sent %d bytes to client\r\n", sent);
|
||||
} else {
|
||||
TCP_PRINT("Failed to send data: %d\r\n", sent);
|
||||
if (sent < 0) {
|
||||
int err = errno;
|
||||
if (err == ECONNRESET || err == EPIPE) {
|
||||
TCP_PRINT("Connection broken, closing socket\r\n");
|
||||
lwip_close(socket_server.client_socket);
|
||||
socket_server.client_socket = -1;
|
||||
socket_server.is_client_connected = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreGive(socket_mutex);
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
static int tcp_socket_is_client_connected(void)
|
||||
{
|
||||
int connected;
|
||||
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreTake(socket_mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
connected = socket_server.is_client_connected;
|
||||
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreGive(socket_mutex);
|
||||
}
|
||||
|
||||
return connected;
|
||||
}
|
||||
|
||||
static void tcp_socket_close_client(void)
|
||||
{
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreTake(socket_mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
if (socket_server.client_socket >= 0) {
|
||||
lwip_close(socket_server.client_socket);
|
||||
socket_server.client_socket = -1;
|
||||
}
|
||||
|
||||
socket_server.is_client_connected = 0;
|
||||
|
||||
#if FATFS_EN
|
||||
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
|
||||
#endif
|
||||
|
||||
TCP_PRINT("Client connection closed\r\n");
|
||||
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreGive(socket_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void tcp_socket_close_server(void)
|
||||
{
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreTake(socket_mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
if (socket_server.client_socket >= 0) {
|
||||
lwip_close(socket_server.client_socket);
|
||||
socket_server.client_socket = -1;
|
||||
}
|
||||
|
||||
if (socket_server.server_socket >= 0) {
|
||||
lwip_close(socket_server.server_socket);
|
||||
socket_server.server_socket = -1;
|
||||
}
|
||||
|
||||
socket_server.is_client_connected = 0;
|
||||
|
||||
#if FATFS_EN
|
||||
fatfs_set_write_disable_flag(FATFS_WRITE_DISABLE_TCP_DEBUG, 0);
|
||||
#endif
|
||||
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreGive(socket_mutex);
|
||||
vSemaphoreDelete(socket_mutex);
|
||||
socket_mutex = NULL;
|
||||
}
|
||||
|
||||
TCP_PRINT("Socket server closed\r\n");
|
||||
}
|
||||
|
||||
static int tcp_socket_get_client_ip(char *ip_buffer, uint32_t buffer_size)
|
||||
{
|
||||
const char *ip_str;
|
||||
|
||||
if (ip_buffer == NULL || buffer_size == 0U) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreTake(socket_mutex, portMAX_DELAY);
|
||||
}
|
||||
|
||||
if (!socket_server.is_client_connected) {
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreGive(socket_mutex);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
ip_str = inet_ntoa(socket_server.client_addr.sin_addr);
|
||||
if (ip_str != NULL) {
|
||||
strncpy(ip_buffer, ip_str, buffer_size - 1U);
|
||||
ip_buffer[buffer_size - 1U] = '\0';
|
||||
} else {
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreGive(socket_mutex);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (socket_mutex != NULL) {
|
||||
xSemaphoreGive(socket_mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* =============================================================================
|
||||
* comm_link 分发(原 comm_link.c)
|
||||
* ============================================================================= */
|
||||
|
||||
#define SESS (comm_link_session())
|
||||
|
||||
static uint8_t comm_link_using_ble(void)
|
||||
{
|
||||
#if BLE_DEBUG_EN
|
||||
return (SESS->type == COMM_LINK_BLE) ? 1U : 0U;
|
||||
#else
|
||||
return 0U;
|
||||
#endif
|
||||
}
|
||||
|
||||
void comm_link_set_type(comm_link_type_t type)
|
||||
{
|
||||
SESS->type = type;
|
||||
}
|
||||
|
||||
comm_link_type_t comm_link_get_type(void)
|
||||
{
|
||||
return SESS->type;
|
||||
}
|
||||
|
||||
comm_link_type_t comm_link_type_from_dip(void)
|
||||
{
|
||||
#if BLE_DEBUG_EN
|
||||
return ble_link_is_bt_mode() ? COMM_LINK_BLE : COMM_LINK_TCP;
|
||||
#else
|
||||
return COMM_LINK_TCP;
|
||||
#endif
|
||||
}
|
||||
|
||||
int comm_link_start(void)
|
||||
{
|
||||
if (comm_link_using_ble() != 0U) {
|
||||
#if BLE_DEBUG_EN
|
||||
return comm_link_ble_start();
|
||||
#endif
|
||||
}
|
||||
return tcp_socket_init();
|
||||
}
|
||||
|
||||
void comm_link_stop(void)
|
||||
{
|
||||
if (comm_link_using_ble() != 0U) {
|
||||
#if BLE_DEBUG_EN
|
||||
comm_link_ble_stop();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
tcp_socket_close_server();
|
||||
}
|
||||
|
||||
void comm_link_process(void)
|
||||
{
|
||||
#if BLE_DEBUG_EN
|
||||
if (comm_link_using_ble() != 0U) {
|
||||
comm_link_ble_process();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int comm_link_accept_client(void)
|
||||
{
|
||||
if (comm_link_using_ble() != 0U) {
|
||||
return 0;
|
||||
}
|
||||
return tcp_socket_accept_client();
|
||||
}
|
||||
|
||||
int comm_link_is_client_connected(void)
|
||||
{
|
||||
if (comm_link_using_ble() != 0U) {
|
||||
#if BLE_DEBUG_EN
|
||||
return comm_link_ble_is_client_connected();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
return tcp_socket_is_client_connected();
|
||||
}
|
||||
|
||||
void comm_link_close_client(void)
|
||||
{
|
||||
if (comm_link_using_ble() != 0U) {
|
||||
#if BLE_DEBUG_EN
|
||||
comm_link_ble_close_client();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
tcp_socket_close_client();
|
||||
}
|
||||
|
||||
int comm_link_receive_data(uint8_t *buffer, uint32_t buffer_size)
|
||||
{
|
||||
if (comm_link_using_ble() != 0U) {
|
||||
#if BLE_DEBUG_EN
|
||||
return comm_link_ble_receive_data(buffer, buffer_size);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
return tcp_socket_receive_data(buffer, buffer_size);
|
||||
}
|
||||
|
||||
int comm_link_send_data(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
if (comm_link_using_ble() != 0U) {
|
||||
#if BLE_DEBUG_EN
|
||||
return comm_link_ble_send_data(data, len);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
return tcp_socket_send_data(data, len);
|
||||
}
|
||||
|
||||
int comm_link_get_peer_info(char *buf, uint32_t buf_size)
|
||||
{
|
||||
if ((buf == NULL) || (buf_size == 0U)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SESS->peer_info[0] != '\0') {
|
||||
(void)snprintf(buf, buf_size, "%s", SESS->peer_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (comm_link_using_ble() != 0U) {
|
||||
#if BLE_DEBUG_EN
|
||||
return comm_link_ble_get_peer_info(buf, buf_size);
|
||||
#endif
|
||||
}
|
||||
return tcp_socket_get_client_ip(buf, buf_size);
|
||||
}
|
||||
|
||||
#else /* TCP_DEBUG_EN */
|
||||
|
||||
comm_link_ctx_t *comm_link_ctx_get(void) { return NULL; }
|
||||
comm_link_session_t *comm_link_session(void) { return NULL; }
|
||||
void comm_link_ctx_reset_session(void) { }
|
||||
void comm_link_ctx_reset_all(void) { }
|
||||
|
||||
void comm_link_set_type(comm_link_type_t type) { (void)type; }
|
||||
comm_link_type_t comm_link_get_type(void) { return COMM_LINK_TCP; }
|
||||
comm_link_type_t comm_link_type_from_dip(void) { return COMM_LINK_TCP; }
|
||||
int comm_link_start(void) { return -1; }
|
||||
void comm_link_stop(void) { }
|
||||
void comm_link_process(void) { }
|
||||
int comm_link_accept_client(void) { return -1; }
|
||||
int comm_link_is_client_connected(void) { return 0; }
|
||||
void comm_link_close_client(void) { }
|
||||
int comm_link_receive_data(uint8_t *buffer, uint32_t buffer_size)
|
||||
{
|
||||
(void)buffer;
|
||||
(void)buffer_size;
|
||||
return -1;
|
||||
}
|
||||
int comm_link_send_data(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
(void)data;
|
||||
(void)len;
|
||||
return -1;
|
||||
}
|
||||
int comm_link_get_peer_info(char *buf, uint32_t buf_size)
|
||||
{
|
||||
(void)buf;
|
||||
(void)buf_size;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* TCP_DEBUG_EN */
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @file debug_link.h
|
||||
* @brief 调试链路层:TCP Socket 后端 + comm_link 适配 + 会话上下文(阶段1合并)
|
||||
*
|
||||
* 合并自:comm_link.h、comm_link_ctx.h、tcp/tcp_link_priv.h、tcp_server_socket 对外 API
|
||||
*/
|
||||
|
||||
#ifndef DEBUG_LINK_H
|
||||
#define DEBUG_LINK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "publicdata/public_define.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---------- comm_link 类型与 API ---------- */
|
||||
|
||||
typedef enum {
|
||||
COMM_LINK_TCP = 0,
|
||||
#if BLE_DEBUG_EN
|
||||
COMM_LINK_BLE = 1,
|
||||
#endif
|
||||
} comm_link_type_t;
|
||||
|
||||
#if BLE_DEBUG_EN
|
||||
#define COMM_LINK_IS_BLE(t) ((t) == COMM_LINK_BLE)
|
||||
#else
|
||||
#define COMM_LINK_IS_BLE(t) (0)
|
||||
#endif
|
||||
|
||||
void comm_link_set_type(comm_link_type_t type);
|
||||
comm_link_type_t comm_link_get_type(void);
|
||||
comm_link_type_t comm_link_type_from_dip(void);
|
||||
|
||||
int comm_link_start(void);
|
||||
void comm_link_stop(void);
|
||||
void comm_link_process(void);
|
||||
|
||||
int comm_link_accept_client(void);
|
||||
int comm_link_is_client_connected(void);
|
||||
void comm_link_close_client(void);
|
||||
|
||||
int comm_link_receive_data(uint8_t *buffer, uint32_t buffer_size);
|
||||
int comm_link_send_data(const uint8_t *data, uint32_t len);
|
||||
int comm_link_get_peer_info(char *buf, uint32_t buf_size);
|
||||
|
||||
/* ---------- comm_link_ctx(TCP/BLE backend union) ---------- */
|
||||
|
||||
#define COMM_LINK_PEER_INFO_SIZE 48U
|
||||
|
||||
typedef struct {
|
||||
comm_link_type_t type;
|
||||
uint8_t client_connected;
|
||||
char peer_info[COMM_LINK_PEER_INFO_SIZE];
|
||||
} comm_link_session_t;
|
||||
|
||||
#if BLE_DEBUG_EN
|
||||
#include "ble_link/fc41d_ble_priv.h"
|
||||
#endif
|
||||
|
||||
#include "lwip/sockets.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "semphr.h"
|
||||
|
||||
typedef struct {
|
||||
int server_socket;
|
||||
int client_socket;
|
||||
struct sockaddr_in client_addr;
|
||||
SemaphoreHandle_t mutex;
|
||||
} tcp_link_ctx_t;
|
||||
|
||||
typedef union {
|
||||
#if BLE_DEBUG_EN
|
||||
fc41d_ble_ctx_t ble;
|
||||
#endif
|
||||
tcp_link_ctx_t tcp;
|
||||
} comm_link_backend_u;
|
||||
|
||||
typedef struct {
|
||||
comm_link_session_t session;
|
||||
comm_link_backend_u backend;
|
||||
} comm_link_ctx_t;
|
||||
|
||||
comm_link_ctx_t *comm_link_ctx_get(void);
|
||||
comm_link_session_t *comm_link_session(void);
|
||||
void comm_link_ctx_reset_session(void);
|
||||
void comm_link_ctx_reset_all(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DEBUG_LINK_H */
|
||||
@@ -0,0 +1,853 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file system_data.c
|
||||
* @brief 系统数据交互模块实现(简化版)
|
||||
*
|
||||
* 该模块负责系统信息、充电信息获取、充电控制等功能
|
||||
* 为参数配置、文件获取等功能预留接口
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "system_data.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "semphr.h"
|
||||
#include "publicdata/type.h"
|
||||
#include "publicdata/public_define.h"
|
||||
|
||||
#if TCP_DEBUG_EN
|
||||
|
||||
#include "tcp_ser/tcp_server.h"
|
||||
#include "tcp_ser/tcp_protocol.h"
|
||||
#include "flash_file_mgr/fault_flash_impl.h"
|
||||
#include "plat_comm/impl/bs_public_impl.h"
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "flowctrl/flowctrl_task.h"
|
||||
#include "collect_ctrl/collect_task.h"
|
||||
#include "bms/bms_interface.h"
|
||||
#include "meter_calculate/meter_calculate_impl.h"
|
||||
#include "flash_file_mgr/meter_calculate_flash_impl.h"
|
||||
#include "fault_cheak/fault_interface.h"
|
||||
#include "app_fatfs/fatfs_card.h"
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 获取默认系统信息(从system_data模块获取)
|
||||
* @param systemInfo 输出系统信息结构体
|
||||
*/
|
||||
void tcp_get_default_system_info(tcp_system_info_data_t *systemInfo)
|
||||
{
|
||||
REAL_FAULT_DATA_T rl_data;
|
||||
|
||||
if (systemInfo == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
v_get_fault_data(&rl_data);
|
||||
memset(systemInfo, 0, sizeof(tcp_system_info_data_t));
|
||||
|
||||
/* 从system_data模块获取系统信息 */
|
||||
strcpy(systemInfo->systemModel, (char *)Device_Model); //设备型号
|
||||
systemInfo->gunCount = GUN_MAX_CNT; //最大支持枪数量
|
||||
systemInfo->devicePower = g_t_share_data.t_sys_fix_cfg.u16_ratePow; //设备功率 kW
|
||||
systemInfo->networkStatus = u8_bs_get_log_on_flag();
|
||||
systemInfo->faultCount = rl_data.u8_fault_cnt;
|
||||
strcpy(systemInfo->version, (char *)Software_Version);
|
||||
}
|
||||
|
||||
static void tcp_copy_log_field(char *dst, size_t dst_sz, const U8_T *src, size_t src_sz)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if ((dst == NULL) || (dst_sz == 0U)) {
|
||||
return;
|
||||
}
|
||||
dst[0] = '\0';
|
||||
if (src == NULL) {
|
||||
return;
|
||||
}
|
||||
for (i = 0U; i + 1U < dst_sz && i < src_sz; i++) {
|
||||
U8_T b = src[i];
|
||||
if (b == 0U) {
|
||||
break;
|
||||
}
|
||||
if (b < 0x20U || b > 0x7EU) {
|
||||
break;
|
||||
}
|
||||
dst[i] = (char)b;
|
||||
}
|
||||
dst[i] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取默认充电枪数据(从system_data模块获取)
|
||||
* @param gunData 输出充电枪数据结构体
|
||||
* @param gunNumber 枪编号 (1-2)
|
||||
*/
|
||||
void tcp_get_default_gun_data(tcp_gun_data_t *gunData, uint8_t gunNumber)
|
||||
{
|
||||
U8_T u8_gunNo = gunNumber -1;
|
||||
U8_T u8_gunState = u16_flow_put_data(u8_gunNo , E_FLOW_PUT_DATA_GUN_REAL_STATE,0); //0空闲 1充电启动 2绝缘启动 3绝缘计算完成 4绝缘检测模块停止 5开始预充 6预充完成 7充电中 8BMS通讯超时 9充电停止 10充电停止完成
|
||||
U8_T u8_gunLink = u8_get_gun_yx_data(YX_GUN_LINK,u8_gunNo);
|
||||
U16_T u16_needVol = u16_bms_put_data(u8_gunNo,E_BMS_PUT_DATA_BMS_NEED_VOL,0);
|
||||
U16_T u16_needCur = u16_bms_put_data(u8_gunNo,E_BMS_PUT_DATA_BMS_NEED_CUR,0);
|
||||
U16_T u16_outVol = (U16_T)(u64_get_meter_data(u8_gunNo,0,E_METER_DATA_VOL)/1000); //充电电压 精度 0.1V
|
||||
U16_T u16_outCur = (U16_T)(u64_get_meter_data(u8_gunNo,0,E_METER_DATA_CUR)/1000); //充电电流 精度 0.1A
|
||||
S_LOG_DATA s_log;
|
||||
char user_field[32];
|
||||
|
||||
memset(&s_log , 0 ,sizeof(S_LOG_DATA));
|
||||
v_meterlog_put_char_log_data(u8_gunNo,&s_log); //获取订单信息
|
||||
|
||||
if (gunData == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(gunData, 0, sizeof(tcp_gun_data_t));
|
||||
|
||||
/* 设置枪编号 */
|
||||
gunData->gunNumber = gunNumber;
|
||||
|
||||
/* 从system_data模块获取枪信息 */
|
||||
if(u8_gunState == 0 && u8_gunLink == 0)
|
||||
gunData->chargingStatus = CHARGING_STATUS_IDLE; /* 空闲 */
|
||||
else if(u8_gunState == 0 && u8_gunLink == 1)
|
||||
gunData->chargingStatus = CHARGING_STATUS_CONNECTED; /* 已连接 */
|
||||
else if(u8_gunState > 0 && u8_gunState < 7)
|
||||
gunData->chargingStatus = CHARGING_STATUS_STARTING; /* 启动中 */
|
||||
else if(u8_gunState >= 7 && u8_gunState <= 8)
|
||||
gunData->chargingStatus = CHARGING_STATUS_CHARGING; /* 充电中 */
|
||||
else if(u8_gunState > 8)
|
||||
gunData->chargingStatus = CHARGING_STATUS_FINISHED; /* 充电完成 */
|
||||
|
||||
|
||||
|
||||
gunData->gunConnectionStatus = u8_gunLink;
|
||||
gunData->demandVoltage = u16_needVol / 10.0f;
|
||||
gunData->demandCurrent = u16_needCur / 10.0f;
|
||||
gunData->actualVoltage = u16_outVol / 10.0f;
|
||||
gunData->actualCurrent = u16_outCur / 10.0f;
|
||||
|
||||
/* 空闲/仅插枪:不上送无效订单残留,避免 JSON 过长导致 BLE 分片异常 */
|
||||
if (gunData->chargingStatus <= CHARGING_STATUS_CONNECTED) {
|
||||
gunData->energy = 0.0f;
|
||||
gunData->cost = 0.0f;
|
||||
gunData->batterySOC = 0.0f;
|
||||
gunData->charge_time = 0U;
|
||||
gunData->userId[0] = '\0';
|
||||
gunData->orderNumber[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
gunData->energy = (float)s_log.u32_used_energy * 0.0001f;
|
||||
gunData->cost = (float)s_log.u32_used_money * 0.0001f;
|
||||
gunData->batterySOC = (s_log.u8_endSoc <= 100U) ? (float)s_log.u8_endSoc : 100.0f;
|
||||
gunData->charge_time = s_log.u16_ChargedTime;
|
||||
|
||||
tcp_copy_log_field(user_field, sizeof(user_field), s_log.u8_user_id, sizeof(s_log.u8_user_id));
|
||||
if (user_field[0] != '\0') {
|
||||
(void)snprintf(gunData->userId, sizeof(gunData->userId), "USER_%s", user_field);
|
||||
}
|
||||
|
||||
tcp_copy_log_field(gunData->orderNumber, sizeof(gunData->orderNumber),
|
||||
s_log.u8_preTradeNo, sizeof(s_log.u8_preTradeNo));
|
||||
|
||||
//测试
|
||||
// if(u8_gunNo == 0) gunData->chargingStatus = CHARGING_STATUS_CONNECTED;
|
||||
// else gunData->chargingStatus = CHARGING_STATUS_CHARGING;
|
||||
// gunData->gunConnectionStatus = 1;
|
||||
}
|
||||
|
||||
void v_mydebug_char_ctrl(U8_T u8_gunNo, U8_T u8_flag, U16_T u16_res)
|
||||
{
|
||||
S_FLOW_CHARGE_CTRL_INFO e_charge_ctrl_info;
|
||||
|
||||
memset(&e_charge_ctrl_info, 0, sizeof(S_FLOW_CHARGE_CTRL_INFO));
|
||||
e_charge_ctrl_info.e_cmd = u8_flag;
|
||||
|
||||
if (u8_flag == CMD_ON) {
|
||||
e_charge_ctrl_info.e_start_res = CTR_CMD_DEBUG;
|
||||
e_charge_ctrl_info.e_sdk_chargMode = E_SDK_CHGMOD_NONE;
|
||||
} else {
|
||||
e_charge_ctrl_info.e_stop_res = E_NORMAL_1001;
|
||||
}
|
||||
|
||||
v_flow_start_stop_ctrl(u8_gunNo, &e_charge_ctrl_info);
|
||||
|
||||
TCP_PRINT("[%d]gun charge ctrl[%d],[%d]\n", u8_gunNo, u8_flag, u16_res);
|
||||
|
||||
if (u8_flag == CMD_OFF) {
|
||||
v_meterlog_set_stop_char_log_info(u8_gunNo, E_NORMAL_1001);
|
||||
} else {
|
||||
S_CHAR_LOG_START_DATA s_log_start_data;
|
||||
memset(&s_log_start_data, 0, sizeof(S_CHAR_LOG_START_DATA));
|
||||
s_log_start_data.e_start_reason = e_charge_ctrl_info.e_start_res;
|
||||
strncpy((char *)s_log_start_data.u8_user_id, "DEBUG_001", EVS_MAX_TRADE_LEN);
|
||||
v_meterlog_set_start_char_log_info(u8_gunNo, &s_log_start_data);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief 停止充电(模拟实现)
|
||||
* @param gunNumber 枪编号 (1-2)
|
||||
* @param message 输出消息
|
||||
* @retval 0成功,-1失败
|
||||
*/
|
||||
int system_data_stop_charge(uint8_t gunNumber, char *message)
|
||||
{
|
||||
if (gunNumber < 1 || gunNumber > GUN_MAX_CNT) {
|
||||
if (message) strcpy(message, "参数错误:枪编号无效");
|
||||
return -1;
|
||||
}
|
||||
|
||||
v_mydebug_char_ctrl(gunNumber-1 , CMD_OFF ,0);
|
||||
|
||||
if (message) strcpy(message, "充电停止成功(模拟)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启动充电(模拟实现)
|
||||
* @param gunNumber 枪编号 (1-2)
|
||||
* @param userId 用户ID
|
||||
* @param orderNumber 订单号
|
||||
* @param message 输出消息
|
||||
* @retval 0成功,-1失败
|
||||
*/
|
||||
int system_data_start_charge(uint8_t gunNumber, const char *userId,
|
||||
const char *orderNumber, char *message)
|
||||
{
|
||||
if (gunNumber < 1 || gunNumber > GUN_MAX_CNT) {
|
||||
if (message) strcpy(message, "参数错误:枪编号无效");
|
||||
return -1;
|
||||
}
|
||||
|
||||
v_mydebug_char_ctrl(gunNumber-1 , CMD_ON ,0);
|
||||
|
||||
if (message) strcpy(message, "充电启动成功(模拟)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取系统故障数据
|
||||
* @param response 输出故障信息响应结构体
|
||||
* @retval 无
|
||||
*/
|
||||
void tcp_get_system_fault_data(tcp_fault_info_response_t *response)
|
||||
{
|
||||
REAL_FAULT_DATA_T rl_data;
|
||||
U8_T valid_fault_count = 0;
|
||||
int i, j;
|
||||
|
||||
if (response == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(response, 0, sizeof(tcp_fault_info_response_t));
|
||||
v_get_fault_data(&rl_data); //获取故障信息
|
||||
|
||||
|
||||
/* 首先统计有效故障数量(e_mag != 0) */
|
||||
for (i = 0; i < CURR_FAULT_MAX_CNT; i++) {
|
||||
if (rl_data.fault_data[i].e_mag != 0) {
|
||||
valid_fault_count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 填充响应结构体 */
|
||||
response->faultCount = valid_fault_count;
|
||||
|
||||
/* 分配故障项数组内存 */
|
||||
if (valid_fault_count > 0) {
|
||||
response->faults = (tcp_fault_item_t *)pvPortMalloc(valid_fault_count * sizeof(tcp_fault_item_t));
|
||||
if (response->faults == NULL) {
|
||||
TCP_PRINT("Failed to allocate memory for fault items\r\r\n");
|
||||
response->faultCount = 0;
|
||||
return;
|
||||
}
|
||||
memset(response->faults, 0, valid_fault_count * sizeof(tcp_fault_item_t));
|
||||
|
||||
/* 转换故障数据格式 */
|
||||
for (i = 0, j = 0; i < CURR_FAULT_MAX_CNT && j < valid_fault_count; i++) {
|
||||
Fault_data *fault = &rl_data.fault_data[i];
|
||||
|
||||
/* 跳过无效故障(e_mag == 0) */
|
||||
if (fault->e_mag == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 转换故障位置 */
|
||||
/*
|
||||
E_WHOLE_PILE = 0, //整桩-终端
|
||||
E_WHOLE_HOST = 500, //整桩-主机-功率分配
|
||||
E_GUN = 1000, //枪地址
|
||||
E_CHAR_MDU = 2000, //充电模块地址
|
||||
E_SWH_MDU = 3000, //开关模块地址
|
||||
E_OTHER = 4000, //其他位置
|
||||
*/
|
||||
/* 根据E_FAULT_SITE枚举值判断故障位置 */
|
||||
|
||||
if (fault->e_ingun >= E_OTHER) {
|
||||
snprintf(response->faults[j].location,sizeof(response->faults[j].location) -1,
|
||||
"其他故障");
|
||||
} else if (fault->e_ingun >= E_SWH_MDU) {
|
||||
snprintf(response->faults[j].location,sizeof(response->faults[j].location) -1,
|
||||
"开关模块 %d",fault->e_ingun - E_SWH_MDU);
|
||||
}
|
||||
else if (fault->e_ingun >= E_CHAR_MDU) {
|
||||
snprintf(response->faults[j].location,sizeof(response->faults[j].location) -1,
|
||||
"充电模块 %d",fault->e_ingun - E_CHAR_MDU);
|
||||
}
|
||||
else if (fault->e_ingun >= E_GUN) {
|
||||
snprintf(response->faults[j].location,sizeof(response->faults[j].location) -1,
|
||||
"充电枪 %d",fault->e_ingun - E_GUN);
|
||||
}
|
||||
else if (fault->e_ingun >= E_WHOLE_HOST) {
|
||||
snprintf(response->faults[j].location,sizeof(response->faults[j].location) -1,
|
||||
"整桩主机 %d",fault->e_ingun - E_WHOLE_HOST);
|
||||
}
|
||||
else {
|
||||
snprintf(response->faults[j].location,sizeof(response->faults[j].location) -1,
|
||||
"整桩终端 %d",fault->e_ingun);
|
||||
}
|
||||
|
||||
snprintf(response->faults[j].code,sizeof(response->faults[j].code) -1,
|
||||
"%d",fault->e_mag);
|
||||
|
||||
char mag[FAULT_GET_MAG_MAX_LEN]= {0};
|
||||
v_get_fault_info(fault->e_mag , mag ,FAULT_GET_MAG_MAX_LEN);
|
||||
|
||||
snprintf(response->faults[j].description,sizeof(response->faults[j].description) -1,
|
||||
"%s",mag);
|
||||
|
||||
|
||||
/* 转换时间格式 */
|
||||
snprintf(response->faults[j].time, sizeof(response->faults[j].time),
|
||||
"%04d-%02d-%02d %02d:%02d:%02d",
|
||||
fault->time.iYear, fault->time.ucMonth, fault->time.ucDay,
|
||||
fault->time.ucHour, fault->time.ucMin, fault->time.ucSec);
|
||||
|
||||
j++; /* 移动到下一个有效故障项 */
|
||||
}
|
||||
} else {
|
||||
response->faults = NULL;
|
||||
}
|
||||
|
||||
TCP_PRINT("Got %u valid faults from 0 total faults\r\r\n", valid_fault_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理自定义数据
|
||||
* @param request 自定义数据请求
|
||||
* @param response 自定义数据响应
|
||||
* @retval 0成功,-1失败
|
||||
*/
|
||||
int tcp_process_custom_data(const tcp_custom_data_t *request, tcp_custom_data_t *response)
|
||||
{
|
||||
if (request == NULL || response == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
TCP_PRINT("Processing custom data:\r\r\n");
|
||||
TCP_PRINT(" functionContent: %s\r\r\n", request->functionContent);
|
||||
TCP_PRINT(" functionField: %s\r\r\n", request->functionField);
|
||||
TCP_PRINT(" functionParams: %s\r\r\n", request->functionParams);
|
||||
|
||||
/* 复制请求数据到响应(根据用户要求,字段内容可以自定义) */
|
||||
memset(response, 0, sizeof(tcp_custom_data_t));
|
||||
|
||||
/* 这里可以根据实际需求处理自定义数据 */
|
||||
/* 示例:如果functionContent是"哈哈哈",则回复"呵呵呵" */
|
||||
if(request->functionField[0] == '\0') //无数据
|
||||
{
|
||||
TCP_PRINT("functionField is null\r\n");
|
||||
//request->functionField
|
||||
//response->functionContent
|
||||
//request->functionParams
|
||||
}
|
||||
else if(strcmp(request->functionField, "reboot") == 0)
|
||||
{
|
||||
v_sys_reboot();
|
||||
}
|
||||
else if(strcmp(request->functionField, "paramInit") == 0)
|
||||
{
|
||||
v_public_param_init();
|
||||
strncpy(response->functionField , request->functionField , sizeof(response->functionField)-1);
|
||||
strncpy(response->functionContent , "success" , sizeof(response->functionContent)-1);
|
||||
strncpy(response->functionParams , "success" , sizeof(response->functionParams)-1);
|
||||
}
|
||||
#if BS_OCPP_EN
|
||||
else if(strcmp(request->functionField, "ocpp_cfg") == 0) //
|
||||
{
|
||||
strncpy(response->functionField , request->functionField , sizeof(response->functionField)-1);
|
||||
strncpy(response->functionContent , request->functionContent , sizeof(response->functionContent)-1);
|
||||
|
||||
if(strcmp(request->functionContent, "getInfo") == 0) //获取信息
|
||||
{
|
||||
v_get_ocpp_cfg_info(response->functionParams ,sizeof(response->functionParams)-1);
|
||||
}
|
||||
else if(request->functionContent[0] != '\0') //不为空 则是设置命令
|
||||
{
|
||||
if(u8_set_ocpp_cfg_info(request->functionContent,request->functionParams) == 1)
|
||||
{
|
||||
strncpy(response->functionParams ,success , sizeof(response->functionContent)-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(response->functionParams ,failure , sizeof(response->functionContent)-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
strncpy(response->functionField , request->functionField , sizeof(response->functionField)-1);
|
||||
strncpy(response->functionContent , "unknown" , sizeof(response->functionContent)-1);
|
||||
}
|
||||
|
||||
TCP_PRINT("Custom data processed:\r\r\n");
|
||||
TCP_PRINT(" functionContent: %s\r\r\n", response->functionContent);
|
||||
TCP_PRINT(" functionField: %s\r\r\n", response->functionField);
|
||||
TCP_PRINT(" functionParams: %s\r\r\n", response->functionParams);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t tcp_history_get_total_count(const char *historyType)
|
||||
{
|
||||
if (historyType == NULL) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
if (strcmp(historyType, "fault") == 0) {
|
||||
return u16_fault_get_his_count();
|
||||
}
|
||||
|
||||
if (strcmp(historyType, "charging") == 0) {
|
||||
v_chg_order_flash_init();
|
||||
return v_get_chg_order_count();
|
||||
}
|
||||
|
||||
if (strcmp(historyType, "card") == 0) {
|
||||
uint32_t cnt = 0u;
|
||||
card_init();
|
||||
if (card_get_count(&cnt) == CARD_STATUS_OK) {
|
||||
if (cnt > 0xFFFFu) {
|
||||
return 0xFFFFu;
|
||||
}
|
||||
return (uint16_t)cnt;
|
||||
}
|
||||
return 0u;
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
|
||||
static void tcp_history_sanitize_commas(char *s)
|
||||
{
|
||||
/* 协议content字段内不得出现英文逗号,否则上位机会split(',')解析出错 */
|
||||
if (s == NULL) {
|
||||
return;
|
||||
}
|
||||
while (*s != '\0') {
|
||||
if (*s == ',') {
|
||||
*s = ';';
|
||||
}
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
static void tcp_history_format_comm_time(const Comm_Time *t, char *buf, size_t bufSize)
|
||||
{
|
||||
if (t == NULL || buf == NULL || bufSize == 0u) {
|
||||
return;
|
||||
}
|
||||
(void)snprintf(buf, bufSize,
|
||||
"%04d-%02d-%02d %02d:%02d:%02d",
|
||||
(int)t->iYear,
|
||||
(int)t->ucMonth,
|
||||
(int)t->ucDay,
|
||||
(int)t->ucHour,
|
||||
(int)t->ucMin,
|
||||
(int)t->ucSec);
|
||||
buf[bufSize - 1u] = '\0';
|
||||
}
|
||||
|
||||
/* 将订单中的停止原因转为可读文本:仅取 e_stop_reason[] 中第一个非空项(与多条 | 拼接相比更短,利于协议长度) */
|
||||
static void tcp_history_build_stop_reason_text(const S_LOG_DATA *order, char *dst, size_t dstSize)
|
||||
{
|
||||
if (order == NULL || dst == NULL || dstSize == 0u) {
|
||||
return;
|
||||
}
|
||||
dst[0] = '\0';
|
||||
|
||||
for (int i = 0; i < NUM_1; i++) {
|
||||
E_FAULT_INFO code = order->e_stop_reason[i];
|
||||
if (code == E_FAULT_NULL) {
|
||||
continue;
|
||||
}
|
||||
char frag[128] = {0};
|
||||
v_get_fault_info(code, frag, (U16_T)sizeof(frag));
|
||||
if (frag[0] == '\0') {
|
||||
(void)snprintf(frag, sizeof(frag), "%d", (int)code);
|
||||
}
|
||||
tcp_history_sanitize_commas(frag);
|
||||
(void)strncpy(dst, frag, dstSize - 1u);
|
||||
dst[dstSize - 1u] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
(void)strncpy(dst, "-", dstSize - 1u);
|
||||
dst[dstSize - 1u] = '\0';
|
||||
}
|
||||
|
||||
uint16_t tcp_history_build_records(const char *historyType,
|
||||
uint16_t startIndex,
|
||||
uint16_t fetchCount,
|
||||
char (*time_buf)[HISTORY_TIME_STR_SIZE],
|
||||
char (*content_buf)[HISTORY_CONTENT_STR_SIZE],
|
||||
uint8_t *outIncludeTime,
|
||||
uint16_t *outTotalCount)
|
||||
{
|
||||
uint16_t totalCount = 0u;
|
||||
uint16_t wantedCount = 0u;
|
||||
uint16_t filledCount = 0u;
|
||||
uint8_t includeTime = 0u;
|
||||
|
||||
if (historyType == NULL || time_buf == NULL || content_buf == NULL || outIncludeTime == NULL || outTotalCount == NULL) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
memset(time_buf, 0, (size_t)HISTORY_QUERY_MAX_RECORDS * (size_t)HISTORY_TIME_STR_SIZE);
|
||||
memset(content_buf, 0, (size_t)HISTORY_QUERY_MAX_RECORDS * (size_t)HISTORY_CONTENT_STR_SIZE);
|
||||
|
||||
if (strcmp(historyType, "fault") == 0) {
|
||||
includeTime = 1u;
|
||||
totalCount = u16_fault_get_his_count();
|
||||
} else if (strcmp(historyType, "charging") == 0) {
|
||||
includeTime = 1u;
|
||||
v_chg_order_flash_init();
|
||||
totalCount = v_get_chg_order_count();
|
||||
} else if (strcmp(historyType, "card") == 0) {
|
||||
includeTime = 0u;
|
||||
uint32_t cnt = 0u;
|
||||
card_init();
|
||||
if (card_get_count(&cnt) == CARD_STATUS_OK) {
|
||||
totalCount = (cnt > 0xFFFFu) ? 0xFFFFu : (uint16_t)cnt;
|
||||
} else {
|
||||
totalCount = 0u;
|
||||
}
|
||||
} else {
|
||||
*outIncludeTime = 0u;
|
||||
*outTotalCount = 0u;
|
||||
return 0u;
|
||||
}
|
||||
|
||||
*outIncludeTime = includeTime;
|
||||
*outTotalCount = totalCount;
|
||||
|
||||
if (fetchCount == 0u || totalCount == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
wantedCount = fetchCount;
|
||||
if (wantedCount > HISTORY_QUERY_MAX_RECORDS) {
|
||||
wantedCount = HISTORY_QUERY_MAX_RECORDS;
|
||||
}
|
||||
|
||||
if (startIndex >= totalCount) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
if (wantedCount > (uint16_t)(totalCount - startIndex)) {
|
||||
wantedCount = (uint16_t)(totalCount - startIndex);
|
||||
}
|
||||
|
||||
if (strcmp(historyType, "fault") == 0) {
|
||||
HIS_FAULT_DATA_T hisFault;
|
||||
memset(&hisFault, 0, sizeof(hisFault));
|
||||
|
||||
for (uint16_t i = 0u; i < wantedCount; i++) {
|
||||
uint16_t newest_pos = (uint16_t)(startIndex + i);
|
||||
if (u8_fault_read_his_by_newest(newest_pos, &hisFault) == 0u) {
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(time_buf[i], HISTORY_TIME_STR_SIZE,
|
||||
"%04d-%02d-%02d %02d:%02d:%02d",
|
||||
hisFault.s_occur_time.iYear,
|
||||
hisFault.s_occur_time.ucMonth,
|
||||
hisFault.s_occur_time.ucDay,
|
||||
hisFault.s_occur_time.ucHour,
|
||||
hisFault.s_occur_time.ucMin,
|
||||
hisFault.s_occur_time.ucSec);
|
||||
|
||||
char location[64] = {0};
|
||||
char code[16] = {0};
|
||||
char desc[128] = {0};
|
||||
char actionTypeStr[16] = {0};
|
||||
char value1Str[32] = {0};
|
||||
char value2Str[32] = {0};
|
||||
char field_content[HISTORY_CONTENT_STR_SIZE] = {0};
|
||||
|
||||
if (hisFault.e_site >= E_OTHER) {
|
||||
snprintf(location, sizeof(location), "其他故障");
|
||||
} else if (hisFault.e_site >= E_SWH_MDU) {
|
||||
snprintf(location, sizeof(location), "开关模块 %d", hisFault.e_site - E_SWH_MDU);
|
||||
} else if (hisFault.e_site >= E_CHAR_MDU) {
|
||||
snprintf(location, sizeof(location), "充电模块 %d", hisFault.e_site - E_CHAR_MDU);
|
||||
} else if (hisFault.e_site >= E_GUN) {
|
||||
snprintf(location, sizeof(location), "充电枪 %d", hisFault.e_site - E_GUN);
|
||||
} else if (hisFault.e_site >= E_WHOLE_HOST) {
|
||||
snprintf(location, sizeof(location), "整桩主机 %d", hisFault.e_site - E_WHOLE_HOST);
|
||||
} else {
|
||||
snprintf(location, sizeof(location), "整桩终端 %d", hisFault.e_site);
|
||||
}
|
||||
|
||||
snprintf(code, sizeof(code), "%d", (int)hisFault.e_fault_info);
|
||||
|
||||
v_get_fault_info(hisFault.e_fault_info, desc, (U16_T)sizeof(desc));
|
||||
snprintf(actionTypeStr, sizeof(actionTypeStr), "%d", (int)hisFault.ActType);
|
||||
snprintf(value1Str, sizeof(value1Str), "%d", (int)hisFault.s16_value1);
|
||||
snprintf(value2Str, sizeof(value2Str), "%d", (int)hisFault.s16_value2);
|
||||
|
||||
tcp_history_sanitize_commas(location);
|
||||
tcp_history_sanitize_commas(desc);
|
||||
|
||||
snprintf(field_content, sizeof(field_content),
|
||||
"%s,%s,%s,%s,%s,%s",
|
||||
location,
|
||||
code,
|
||||
desc,
|
||||
actionTypeStr,
|
||||
value1Str,
|
||||
value2Str);
|
||||
|
||||
strncpy(content_buf[i], field_content, HISTORY_CONTENT_STR_SIZE - 1);
|
||||
content_buf[i][HISTORY_CONTENT_STR_SIZE - 1] = '\0';
|
||||
filledCount++;
|
||||
}
|
||||
} else if (strcmp(historyType, "charging") == 0) {
|
||||
S_LOG_DATA order;
|
||||
memset(&order, 0, sizeof(order));
|
||||
|
||||
U32_T lastIndex = v_get_chg_order_last_index();
|
||||
if (lastIndex == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0u; i < wantedCount; i++) {
|
||||
U32_T offset = (U32_T)(startIndex + i);
|
||||
if (lastIndex <= offset) {
|
||||
break;
|
||||
}
|
||||
U32_T index = lastIndex - offset;
|
||||
if (v_read_chg_order_record(&order, index) != 0u) {
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(time_buf[i], HISTORY_TIME_STR_SIZE,
|
||||
"%04d-%02d-%02d %02d:%02d:%02d",
|
||||
order.EndChargeTime.iYear,
|
||||
order.EndChargeTime.ucMonth,
|
||||
order.EndChargeTime.ucDay,
|
||||
order.EndChargeTime.ucHour,
|
||||
order.EndChargeTime.ucMin,
|
||||
order.EndChargeTime.ucSec);
|
||||
|
||||
char userId[40] = {0};
|
||||
char stopCmd[16] = {0};
|
||||
char tradeNoStr[EVS_MAX_TRADE_LEN + 4] = {0};
|
||||
char preTradeStr[EVS_MAX_TRADE_LEN + 4] = {0};
|
||||
char startTimeStr[HISTORY_TIME_STR_SIZE] = {0};
|
||||
char stopTimeStr[HISTORY_TIME_STR_SIZE] = {0};
|
||||
char stopReasonText[256] = {0};
|
||||
|
||||
const char *uid_src = (const char *)order.u8_user_id;
|
||||
uint16_t n = 0u;
|
||||
while (n < 32u && n < (uint16_t)(sizeof(userId) - 1u) && uid_src[n] != '\0') {
|
||||
userId[n] = uid_src[n];
|
||||
n++;
|
||||
}
|
||||
userId[n] = '\0';
|
||||
|
||||
/* 停机码为 4 字节原始码,可能含 0x00,不能按 C 字符串拷贝,否则上位机看到为空 */
|
||||
(void)snprintf(stopCmd, sizeof(stopCmd), "%d",
|
||||
(unsigned int)order.e_stop_reason[0]);
|
||||
|
||||
{
|
||||
const char *ts = (const char *)order.u8_tradeNo;
|
||||
n = 0u;
|
||||
while (n < (uint16_t)(EVS_MAX_TRADE_LEN - 1u) && n < (uint16_t)(sizeof(tradeNoStr) - 1u) && ts[n] != '\0') {
|
||||
tradeNoStr[n] = ts[n];
|
||||
n++;
|
||||
}
|
||||
tradeNoStr[n] = '\0';
|
||||
}
|
||||
{
|
||||
const char *ps = (const char *)order.u8_preTradeNo;
|
||||
n = 0u;
|
||||
while (n < (uint16_t)(EVS_MAX_TRADE_LEN - 1u) && n < (uint16_t)(sizeof(preTradeStr) - 1u) && ps[n] != '\0') {
|
||||
preTradeStr[n] = ps[n];
|
||||
n++;
|
||||
}
|
||||
preTradeStr[n] = '\0';
|
||||
}
|
||||
|
||||
tcp_history_format_comm_time(&order.StartChargeTime, startTimeStr, sizeof(startTimeStr));
|
||||
tcp_history_format_comm_time(&order.EndChargeTime, stopTimeStr, sizeof(stopTimeStr));
|
||||
|
||||
tcp_history_sanitize_commas(userId);
|
||||
tcp_history_sanitize_commas(stopCmd);
|
||||
tcp_history_sanitize_commas(tradeNoStr);
|
||||
tcp_history_sanitize_commas(preTradeStr);
|
||||
|
||||
tcp_history_build_stop_reason_text(&order, stopReasonText, sizeof(stopReasonText));
|
||||
tcp_history_sanitize_commas(stopReasonText);
|
||||
|
||||
char usedMoneyStr[64] = {0};
|
||||
char usedEnergyStr[64] = {0};
|
||||
snprintf(usedMoneyStr, sizeof(usedMoneyStr), "%u.%04u",
|
||||
(unsigned int)(order.u32_used_money / 10000u),
|
||||
(unsigned int)(order.u32_used_money % 10000u));
|
||||
snprintf(usedEnergyStr, sizeof(usedEnergyStr), "%u.%04u",
|
||||
(unsigned int)(order.u32_used_energy / 10000u),
|
||||
(unsigned int)(order.u32_used_energy % 10000u));
|
||||
|
||||
/*
|
||||
* content 逗号分隔字段顺序(上位机表格不单独展示“停止时间”,与 JSON time 同源):
|
||||
* 订单索引,枪号,开始时间,停止时间,充电时长(分钟),开始SOC,停止SOC,用电量,金额,
|
||||
* 用户ID,设备流水号,平台流水号,停机原因(仅e_stop_reason首条),停机码(u8_stop_cmd四字节HEX共8位)
|
||||
*/
|
||||
char field_content[HISTORY_CONTENT_STR_SIZE] = {0};
|
||||
snprintf(field_content, sizeof(field_content),
|
||||
"%u,%u,%s,%s,%u,%u,%u,%s,%s,%s,%s,%s,%s,%s",
|
||||
(unsigned int)order.u32_index,
|
||||
(unsigned int)order.u8_gunNo,
|
||||
startTimeStr,
|
||||
stopTimeStr,
|
||||
(unsigned int)order.u16_ChargedTime,
|
||||
(unsigned int)order.u8_startSoc,
|
||||
(unsigned int)order.u8_endSoc,
|
||||
usedEnergyStr,
|
||||
usedMoneyStr,
|
||||
userId,
|
||||
tradeNoStr,
|
||||
preTradeStr,
|
||||
stopReasonText,
|
||||
stopCmd);
|
||||
|
||||
strncpy(content_buf[i], field_content, HISTORY_CONTENT_STR_SIZE - 1);
|
||||
content_buf[i][HISTORY_CONTENT_STR_SIZE - 1] = '\0';
|
||||
filledCount++;
|
||||
}
|
||||
} else if (strcmp(historyType, "card") == 0) {
|
||||
uint32_t cnt = 0u;
|
||||
CardInfo *card_list = NULL;
|
||||
|
||||
if (totalCount == 0u) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
card_list = (CardInfo *)pvPortMalloc(sizeof(CardInfo) * (size_t)totalCount);
|
||||
if (card_list == NULL) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
memset(card_list, 0, sizeof(CardInfo) * (size_t)totalCount);
|
||||
|
||||
if (card_get_all(card_list, &cnt, totalCount) != CARD_STATUS_OK) {
|
||||
vPortFree(card_list);
|
||||
return 0u;
|
||||
}
|
||||
|
||||
uint16_t u16_cnt = (uint16_t)cnt;
|
||||
if (u16_cnt < totalCount) {
|
||||
totalCount = u16_cnt;
|
||||
*outTotalCount = totalCount;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0u; i < wantedCount; i++) {
|
||||
uint16_t ascPos = (uint16_t)(totalCount - 1u - (uint16_t)(startIndex + i));
|
||||
if (ascPos >= u16_cnt) {
|
||||
break;
|
||||
}
|
||||
|
||||
char cardNum[40] = {0};
|
||||
strncpy(cardNum, card_list[ascPos].card_num, 31);
|
||||
cardNum[31] = '\0';
|
||||
tcp_history_sanitize_commas(cardNum);
|
||||
|
||||
char field_content[HISTORY_CONTENT_STR_SIZE] = {0};
|
||||
snprintf(field_content, sizeof(field_content),
|
||||
"%s,%u,%u",
|
||||
cardNum,
|
||||
(unsigned int)card_list[ascPos].balance,
|
||||
(unsigned int)card_list[ascPos].status);
|
||||
|
||||
strncpy(content_buf[i], field_content, HISTORY_CONTENT_STR_SIZE - 1);
|
||||
content_buf[i][HISTORY_CONTENT_STR_SIZE - 1] = '\0';
|
||||
filledCount++;
|
||||
}
|
||||
|
||||
vPortFree(card_list);
|
||||
card_list = NULL;
|
||||
}
|
||||
|
||||
return filledCount;
|
||||
}
|
||||
|
||||
int tcp_history_clear_records(const char *historyType, char *outMessage, uint32_t outMessageSize)
|
||||
{
|
||||
if (historyType == NULL) {
|
||||
if (outMessage && outMessageSize > 0u) {
|
||||
strncpy(outMessage, "未知historyType", outMessageSize - 1u);
|
||||
outMessage[outMessageSize - 1u] = '\0';
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(historyType, "fault") == 0) {
|
||||
v_fault_clear_his_fault();
|
||||
if (outMessage && outMessageSize > 0u) {
|
||||
strncpy(outMessage, "Fault records cleared", outMessageSize - 1u);
|
||||
outMessage[outMessageSize - 1u] = '\0';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(historyType, "charging") == 0) {
|
||||
v_chg_order_clear_all();
|
||||
if (outMessage && outMessageSize > 0u) {
|
||||
strncpy(outMessage, "Charge records cleared", outMessageSize - 1u);
|
||||
outMessage[outMessageSize - 1u] = '\0';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(historyType, "card") == 0) {
|
||||
CardStatus st = card_clear_all();
|
||||
if (st == CARD_STATUS_OK) {
|
||||
if (outMessage && outMessageSize > 0u) {
|
||||
strncpy(outMessage, "卡信息已清除", outMessageSize - 1u);
|
||||
outMessage[outMessageSize - 1u] = '\0';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (outMessage && outMessageSize > 0u) {
|
||||
snprintf(outMessage, outMessageSize, "Card info clear failed: %d", (int)st);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (outMessage && outMessageSize > 0u) {
|
||||
strncpy(outMessage, "未知historyType", outMessageSize - 1u);
|
||||
outMessage[outMessageSize - 1u] = '\0';
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file system_data.h
|
||||
* @brief 系统数据交互模块头文件
|
||||
*
|
||||
* 该模块负责系统信息、充电信息获取、充电控制等功能
|
||||
* 为参数配置、文件获取等功能预留接口
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef __SYSTEM_DATA_H
|
||||
#define __SYSTEM_DATA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "publicdata/public_define.h"
|
||||
#include "tcp_ser/system_paracfg.h"
|
||||
#include "tcp_ser/tcp_protocol.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* 函数声明 */
|
||||
const char* param_type_to_string(param_data_type_t type);
|
||||
int param_get_value_as_string(const param_map_item_t *item, char *buffer, uint32_t size);
|
||||
uint32_t param_get_enable_bit_value(uint16_t param_id, uint8_t op_flag, uint32_t value);
|
||||
uint32_t param_get_netcomm_bit_value(uint16_t param_id, uint8_t op_flag, uint32_t value);
|
||||
int param_set_value_from_string(const param_map_item_t *item, const char *value_str);
|
||||
|
||||
/* 参数映射表获取函数 */
|
||||
const param_map_item_t* param_get_basic_map(void);
|
||||
uint16_t param_get_basic_map_size(void);
|
||||
const param_map_item_t* param_get_network_map(void);
|
||||
uint16_t param_get_network_map_size(void);
|
||||
const param_map_item_t* param_get_meter_map(void);
|
||||
uint16_t param_get_meter_map_size(void);
|
||||
const param_map_item_t* param_get_enable_map(void);
|
||||
uint16_t param_get_enable_map_size(void);
|
||||
const param_map_item_t* param_get_fee_map(void);
|
||||
uint16_t param_get_fee_map_size(void);
|
||||
const param_map_item_t* param_get_sdk_map(void);
|
||||
uint16_t param_get_sdk_map_size(void);
|
||||
const param_map_item_t* param_get_qrcode_map(void);
|
||||
uint16_t param_get_qrcode_map_size(void);
|
||||
|
||||
/* 故障信息获取函数 */
|
||||
void tcp_get_system_fault_data(tcp_fault_info_response_t *response);
|
||||
int tcp_process_custom_data(const tcp_custom_data_t *request, tcp_custom_data_t *response);
|
||||
|
||||
/* ================= 历史记录(HistoryOperations) ================= */
|
||||
#define HISTORY_TIME_STR_SIZE 32
|
||||
#define HISTORY_CONTENT_STR_SIZE 640
|
||||
#define HISTORY_QUERY_MAX_RECORDS 20
|
||||
|
||||
/**
|
||||
* @brief 历史查询一包记录的传输缓冲区(体积大,勿在栈上分配;用 pvPortMalloc 申请并在用后 vPortFree)
|
||||
*/
|
||||
typedef struct tcp_history_query_xfer {
|
||||
char time_buf[HISTORY_QUERY_MAX_RECORDS][HISTORY_TIME_STR_SIZE];
|
||||
char content_buf[HISTORY_QUERY_MAX_RECORDS][HISTORY_CONTENT_STR_SIZE];
|
||||
} tcp_history_query_xfer_t;
|
||||
|
||||
/**
|
||||
* @brief 获取历史记录总数
|
||||
* @param historyType "fault"|"charging"|"card"
|
||||
* @return 总条数
|
||||
*/
|
||||
uint16_t tcp_history_get_total_count(const char *historyType);
|
||||
|
||||
/**
|
||||
* @brief 根据 startIndex(0=最新) 构建单包记录内容
|
||||
*
|
||||
* @param historyType "fault"|"charging"|"card"
|
||||
* @param startIndex 距离最新的偏移
|
||||
* @param fetchCount 请求条数(实际会受 HISTORY_QUERY_MAX_RECORDS 与设备实际数量约束)
|
||||
* @param time_buf [HISTORY_QUERY_MAX_RECORDS][HISTORY_TIME_STR_SIZE] fault/charging填充
|
||||
* @param content_buf [HISTORY_QUERY_MAX_RECORDS][HISTORY_CONTENT_STR_SIZE] 三种类型均填充
|
||||
* @param outIncludeTime 输出:1=返回time字段,0=不返回time字段
|
||||
* @param outTotalCount 输出:设备该类型总条数
|
||||
* @return 返回实际填充的记录条数(<=fetchCount)
|
||||
*/
|
||||
uint16_t tcp_history_build_records(const char *historyType,
|
||||
uint16_t startIndex,
|
||||
uint16_t fetchCount,
|
||||
char (*time_buf)[HISTORY_TIME_STR_SIZE],
|
||||
char (*content_buf)[HISTORY_CONTENT_STR_SIZE],
|
||||
uint8_t *outIncludeTime,
|
||||
uint16_t *outTotalCount);
|
||||
|
||||
/**
|
||||
* @brief 清除历史记录(全部)
|
||||
* @param historyType "fault"|"charging"|"card"
|
||||
* @param outMessage 输出提示信息(可选)
|
||||
* @param outMessageSize outMessage大小(可选)
|
||||
* @return 0=成功,非0=失败
|
||||
*/
|
||||
int tcp_history_clear_records(const char *historyType, char *outMessage, uint32_t outMessageSize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SYSTEM_DATA_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,263 @@
|
||||
|
||||
#ifndef SYSTEM_PARACFG_H
|
||||
#define SYSTEM_PARACFG_H
|
||||
|
||||
#ifdef __cplusplus /*C++编译环境下兼容C语言*/
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*------ Exported includes(头文件) ----------------------*/
|
||||
#include <stdint.h>
|
||||
#include "publicdata/public_define.h"
|
||||
#include "publicdata/publicdata.h"
|
||||
/*------ Exported macro(宏定义) -------------------------*/
|
||||
|
||||
/*------ Exported struct(结构体定义) --------------------*/
|
||||
/* 参数类型枚举(节省内存) */
|
||||
typedef enum {
|
||||
PARAM_TYPE_STRING = 0, /* 字符串类型 */
|
||||
PARAM_TYPE_UINT8 = 1, /* uint8类型 */
|
||||
PARAM_TYPE_UINT16 = 2, /* uint16类型 */
|
||||
PARAM_TYPE_UINT32 = 3, /* uint32类型 */
|
||||
PARAM_TYPE_FLOAT = 4, /* 浮点类型 */
|
||||
PARAM_TYPE_INT = 5, /* 整数类型 */
|
||||
PARAM_TYPE_ARRAY = 6, /* 数组类型(字节数组) */
|
||||
PARAM_TYPE_BIT = 7, /* 位字段类型 */
|
||||
} param_data_type_t;
|
||||
|
||||
/* 基本参数枚举 */
|
||||
typedef enum {
|
||||
PARAM_BASIC_GUN_COUNT, /* 枪数量 */
|
||||
PARAM_BASIC_RATE_POW, /* 额定功率 */
|
||||
PARAM_BASIC_MAX_VOLT, /* 最大电压 */
|
||||
PARAM_BASIC_MAX_CUR, /* 最大电流 */
|
||||
PARAM_BASIC_PWD, /* 维护密码 */
|
||||
PARAM_BASIC_PILE_TYPE, /* 桩类型 */
|
||||
PARAM_BASIC_TERMINAL_0, /* 终端地址0 */
|
||||
PARAM_BASIC_TERMINAL_1, /* 终端地址1 */
|
||||
PARAM_BASIC_LED_TYPE, /* 灯板类型 */
|
||||
PARAM_BASIC_TIME_ZONE, /* 时区设置*/
|
||||
PARAM_BASIC_THRESHOLD_GUN_TEMP, /* 枪温阈值 */
|
||||
PARAM_BASIC_SOC_LIMIT, /* SOC阈值 */
|
||||
PARAM_BASIC_MIN_VOLT, /* 最小电压 */
|
||||
PARAM_BASIC_TRICKLE_LIMIT, /* 涓流充电停机阈值 */
|
||||
PARAM_BASIC_MDU_NUM, /* 充电模块数量 */
|
||||
PARAM_BASIC_MDU_TYPE, /* 模块类型 */
|
||||
PARAM_BASIC_MDU_TEMP_FLAG, /* 模块温度获取使能 */
|
||||
PARAM_BASIC_MDU_RATED_POW, /* 模块额定功率 */
|
||||
PARAM_BASIC_MDU_MAX_VOL, /* 模块最大电压 */
|
||||
PARAM_BASIC_MDU_MAX_CUR, /* 模块最大电流 */
|
||||
PARAM_BASIC_GUN_TYPE_0, /* 枪1类型 0欧标枪 1国标枪 */
|
||||
PARAM_BASIC_GUN_TYPE_1, /* 枪2类型 0欧标枪 1国标枪 */
|
||||
PARAM_BASIC_GUN_POWER_LIMIT_0, /* 枪1充电功率限制 0表示不限制 单位kW */
|
||||
PARAM_BASIC_GUN_POWER_LIMIT_1, /* 枪2充电功率限制 0表示不限制 单位kW */
|
||||
PARAM_BASIC_PILE_POWER_LIMIT, /* 整桩功率限制 0表示不限制 单位kW */
|
||||
PARAM_BASIC_COUNT /* 基本参数数量 */
|
||||
} param_basic_enum_t;
|
||||
|
||||
/* 网络参数枚举 */
|
||||
typedef enum {
|
||||
PARAM_NETWORK_ETH_IP = 0, /* IP地址 */
|
||||
PARAM_NETWORK_ETH_GATE, /* 网关 */
|
||||
PARAM_NETWORK_ETH_MASK, /* 子网掩码 */
|
||||
PARAM_NETWORK_ETH_MAC, /* MAC地址 */
|
||||
PARAM_NETWORK_SERVER_A_DOMAIN, /* 服务器A域名 */
|
||||
PARAM_NETWORK_SERVER_B_DOMAIN, /* 服务器B域名 */
|
||||
PARAM_NETWORK_PLAT_A_PILENUM, /* 平台1桩编号 */
|
||||
PARAM_NETWORK_PLAT_B_PILENUM, /* 平台2桩编号 */
|
||||
PARAM_NETWORK_NET_COMM_PARA, /* 网络连接参数(整体)- 保留用于兼容性 */
|
||||
/* U_NET_COMM_PARA 位字段参数 */
|
||||
PARAM_NETWORK_PROGRAM_TYPE, /* 程序类型(2位) */
|
||||
PARAM_NETWORK_NET_A_TYPE, /* 平台1联网方式(2位) */
|
||||
PARAM_NETWORK_NET_B_TYPE, /* 平台2联网方式(2位) */
|
||||
PARAM_NETWORK_PLAT_A_EN, /* 平台1使能(2位) */
|
||||
PARAM_NETWORK_PLAT_B_EN, /* 平台2使能(2位) */
|
||||
PARAM_NETWORK_PLAT_A_TYPE, /* 平台1类型(4位) */
|
||||
PARAM_NETWORK_PLAT_B_TYPE, /* 平台2类型(4位) */
|
||||
PARAM_NETWORK_SIM_ANP_TYPE, /* SIM卡APN类型(2位) */
|
||||
PARAM_NETWORK_COUNT /* 网络参数数量 */
|
||||
} param_network_enum_t;
|
||||
|
||||
/* 电表参数枚举 */
|
||||
typedef enum {
|
||||
PARAM_METER_METER_NUM = 0, /* 电表数量 */
|
||||
PARAM_METER_0_IN_GUN, /* 电表0归属枪号 */
|
||||
PARAM_METER_0_HIGH_PREC, /* 电表0高精度支持 */
|
||||
PARAM_METER_0_IS_AC, /* 电表0是否为交流表 */
|
||||
PARAM_METER_0_ADDR, /* 电表0地址 */
|
||||
PARAM_METER_1_IN_GUN, /* 电表1归属枪号 */
|
||||
PARAM_METER_1_HIGH_PREC, /* 电表1高精度支持 */
|
||||
PARAM_METER_1_IS_AC, /* 电表1是否为交流表 */
|
||||
PARAM_METER_1_ADDR, /* 电表1地址 */
|
||||
PARAM_METER_2_IN_GUN, /* 电表2归属枪号 */
|
||||
PARAM_METER_2_HIGH_PREC, /* 电表2高精度支持 */
|
||||
PARAM_METER_2_IS_AC, /* 电表2是否为交流表 */
|
||||
PARAM_METER_2_ADDR, /* 电表2地址 */
|
||||
PARAM_METER_3_IN_GUN, /* 电表3归属枪号 */
|
||||
PARAM_METER_3_HIGH_PREC, /* 电表3高精度支持 */
|
||||
PARAM_METER_3_IS_AC, /* 电表3是否为交流表 */
|
||||
PARAM_METER_3_ADDR, /* 电表3地址 */
|
||||
PARAM_METER_COUNT /* 电表参数数量 */
|
||||
} param_meter_enum_t;
|
||||
|
||||
/* 使能参数枚举 */
|
||||
typedef enum {
|
||||
PARAM_ENABLE_DOOR_ENABLE = 0, /* 门禁使能(2位) */
|
||||
PARAM_ENABLE_GUN_LOCK_ENA, /* 枪锁使能(2位) */
|
||||
PARAM_ENABLE_GUN_LOCK_NEG, /* 枪锁信号取反(2位) */
|
||||
PARAM_ENABLE_AC_CONTACT_ENA, /* 交流接触器使能(2位) */
|
||||
PARAM_ENABLE_GUN_HOMING, /* 枪归位信号有无(2位) */
|
||||
PARAM_ENABLE_GB_ENABLE, /* 国标使能(2位) */
|
||||
PARAM_ENABLE_LCD_ENABLE, /* 液晶使能(2位) */
|
||||
PARAM_ENABLE_AUX_POWER, /* 辅助电压返回状态使能(2位) */
|
||||
PARAM_ENABLE_RELIEF_EN, /* 泄放使能(2位) */
|
||||
PARAM_ENABLE_YE_LEN_EN, /* 液冷通讯使能(2位) */
|
||||
PARAM_ENABLE_AB_COMM_EN, /* 主副班通讯使能(2位) */
|
||||
PARAM_ENABLE_SCREEN_SAVER_EN, /* UI屏保显示使能(2位) */
|
||||
PARAM_ENABLE_PLUG_EN, /* 即插即充使能(2位) */
|
||||
PARAM_ENABLE_UI_ENGLISH_EN, /* UI界面英语使能(2位) */
|
||||
PARAM_ENABLE_DC_CONTACT_EN, /* 直流接触器使能(2位) */
|
||||
PARAM_ENABLE_GUN_FAN_EN, /* 枪风扇使能(2位) */
|
||||
PARAM_ENABLE_MDU_ZERO_POW_EN, /* 零功控制使能(2位) */
|
||||
PARAM_ENABLE_LED_GB_EN, /* LED国标使能(2位) */
|
||||
PARAM_ENABLE_COUNT /* 使能参数数量 */
|
||||
} param_enable_enum_t;
|
||||
|
||||
/* 费率参数枚举 */
|
||||
typedef enum {
|
||||
PARAM_FEE_MODEL_NO = 0, /* 计费模型编号 */
|
||||
PARAM_FEE_FEE_CNT, /* 费率条数 */
|
||||
PARAM_FEE_TYPE_0_RATE, /* 费率类型0费率 */
|
||||
PARAM_FEE_TYPE_0_SERVICE, /* 费率类型0服务费 */
|
||||
PARAM_FEE_TYPE_1_RATE, /* 费率类型1费率 */
|
||||
PARAM_FEE_TYPE_1_SERVICE, /* 费率类型1服务费 */
|
||||
PARAM_FEE_TYPE_2_RATE, /* 费率类型2费率 */
|
||||
PARAM_FEE_TYPE_2_SERVICE, /* 费率类型2服务费 */
|
||||
PARAM_FEE_TYPE_3_RATE, /* 费率类型3费率 */
|
||||
PARAM_FEE_TYPE_3_SERVICE, /* 费率类型3服务费 */
|
||||
PARAM_FEE_TYPE_4_RATE, /* 费率类型4费率 */
|
||||
PARAM_FEE_TYPE_4_SERVICE, /* 费率类型4服务费 */
|
||||
PARAM_FEE_TYPE_5_RATE, /* 费率类型5费率 */
|
||||
PARAM_FEE_TYPE_5_SERVICE, /* 费率类型5服务费 */
|
||||
PARAM_FEE_TYPE_6_RATE, /* 费率类型6费率 */
|
||||
PARAM_FEE_TYPE_6_SERVICE, /* 费率类型6服务费 */
|
||||
PARAM_FEE_TYPE_7_RATE, /* 费率类型7费率 */
|
||||
PARAM_FEE_TYPE_7_SERVICE, /* 费率类型7服务费 */
|
||||
PARAM_FEE_TYPE_8_RATE, /* 费率类型8费率 */
|
||||
PARAM_FEE_TYPE_8_SERVICE, /* 费率类型8服务费 */
|
||||
PARAM_FEE_TYPE_9_RATE, /* 费率类型9费率 */
|
||||
PARAM_FEE_TYPE_9_SERVICE, /* 费率类型9服务费 */
|
||||
PARAM_FEE_TYPE_10_RATE, /* 费率类型10费率 */
|
||||
PARAM_FEE_TYPE_10_SERVICE, /* 费率类型10服务费 */
|
||||
PARAM_FEE_TYPE_11_RATE, /* 费率类型11费率 */
|
||||
PARAM_FEE_TYPE_11_SERVICE, /* 费率类型11服务费 */
|
||||
PARAM_FEE_TYPE_12_RATE, /* 费率类型12费率 */
|
||||
PARAM_FEE_TYPE_12_SERVICE, /* 费率类型12服务费 */
|
||||
PARAM_FEE_TYPE_13_RATE, /* 费率类型13费率 */
|
||||
PARAM_FEE_TYPE_13_SERVICE, /* 费率类型13服务费 */
|
||||
PARAM_FEE_TYPE_14_RATE, /* 费率类型14费率 */
|
||||
PARAM_FEE_TYPE_14_SERVICE, /* 费率类型14服务费 */
|
||||
PARAM_FEE_TYPE_15_RATE, /* 费率类型15费率 */
|
||||
PARAM_FEE_TYPE_15_SERVICE, /* 费率类型15服务费 */
|
||||
PARAM_FEE_TYPE_16_RATE, /* 费率类型16费率 */
|
||||
PARAM_FEE_TYPE_16_SERVICE, /* 费率类型16服务费 */
|
||||
PARAM_FEE_TYPE_17_RATE, /* 费率类型17费率 */
|
||||
PARAM_FEE_TYPE_17_SERVICE, /* 费率类型17服务费 */
|
||||
PARAM_FEE_TYPE_18_RATE, /* 费率类型18费率 */
|
||||
PARAM_FEE_TYPE_18_SERVICE, /* 费率类型18服务费 */
|
||||
PARAM_FEE_TYPE_19_RATE, /* 费率类型19费率 */
|
||||
PARAM_FEE_TYPE_19_SERVICE, /* 费率类型19服务费 */
|
||||
PARAM_FEE_TYPE_20_RATE, /* 费率类型20费率 */
|
||||
PARAM_FEE_TYPE_20_SERVICE, /* 费率类型20服务费 */
|
||||
PARAM_FEE_TYPE_21_RATE, /* 费率类型21费率 */
|
||||
PARAM_FEE_TYPE_21_SERVICE, /* 费率类型21服务费 */
|
||||
PARAM_FEE_TYPE_22_RATE, /* 费率类型22费率 */
|
||||
PARAM_FEE_TYPE_22_SERVICE, /* 费率类型22服务费 */
|
||||
PARAM_FEE_TYPE_23_RATE, /* 费率类型23费率 */
|
||||
PARAM_FEE_TYPE_23_SERVICE, /* 费率类型23服务费 */
|
||||
PARAM_FEE_TYPE_24_RATE, /* 费率类型24费率 */
|
||||
PARAM_FEE_TYPE_24_SERVICE, /* 费率类型24服务费 */
|
||||
PARAM_FEE_TYPE_25_RATE, /* 费率类型25费率 */
|
||||
PARAM_FEE_TYPE_25_SERVICE, /* 费率类型25服务费 */
|
||||
PARAM_FEE_TYPE_26_RATE, /* 费率类型26费率 */
|
||||
PARAM_FEE_TYPE_26_SERVICE, /* 费率类型26服务费 */
|
||||
PARAM_FEE_TYPE_27_RATE, /* 费率类型27费率 */
|
||||
PARAM_FEE_TYPE_27_SERVICE, /* 费率类型27服务费 */
|
||||
PARAM_FEE_TYPE_28_RATE, /* 费率类型28费率 */
|
||||
PARAM_FEE_TYPE_28_SERVICE, /* 费率类型28服务费 */
|
||||
PARAM_FEE_TYPE_29_RATE, /* 费率类型29费率 */
|
||||
PARAM_FEE_TYPE_29_SERVICE, /* 费率类型29服务费 */
|
||||
PARAM_FEE_TYPE_1_24, /* 时段0费率类型 */
|
||||
PARAM_FEE_TYPE_25_48, /* 时段0费率类型 */
|
||||
PARAM_FEE_TYPE_49_72, /* 时段0费率类型 */
|
||||
PARAM_FEE_TYPE_73_96, /* 时段0费率类型 */
|
||||
|
||||
|
||||
PARAM_FEE_COUNT /* 费率参数数量 */
|
||||
} param_fee_enum_t;
|
||||
|
||||
/* SDK参数枚举 */
|
||||
typedef enum {
|
||||
PARAM_SDK_PRODUCT_KEY = 0, /* 产品秘钥 */
|
||||
PARAM_SDK_DEVICE_NAME, /* 资产码 */
|
||||
PARAM_SDK_DEVICE_SECRET, /* 设备密钥 */
|
||||
PARAM_SDK_DEVICE_REG_CODE, /* 注册码 */
|
||||
PARAM_SDK_DEVICE_UID, /* 出厂编码 */
|
||||
PARAM_SDK_FIRMWARE_VERSION, /* 固件版本信息 */
|
||||
PARAM_SDK_COUNT /* SDK参数数量 */
|
||||
} param_sdk_enum_t;
|
||||
|
||||
/* 二维码参数枚举 */
|
||||
typedef enum {
|
||||
PARAM_QRCODE_GUN1 = 0, /* 枪1二维码 */
|
||||
PARAM_QRCODE_GUN2, /* 枪2二维码 */
|
||||
PARAM_QRCODE_CUSTOM_VER, /* 自定义版本号 */
|
||||
PARAM_QRCODE_APN_STR, /* 4G apn */
|
||||
PARAM_QRCODE_OCPP_AUTH_KEY, /* OCPP鉴权密钥 */
|
||||
PARAM_QRCODE_COUNT /* 二维码参数数量 */
|
||||
} param_qrcode_enum_t;
|
||||
|
||||
/* 参数映射表项结构 */
|
||||
typedef struct {
|
||||
uint16_t param_id; /* 参数ID(枚举值) */
|
||||
const char *param_name; /* 参数名称 */
|
||||
param_data_type_t data_type; /* 参数数据类型 */
|
||||
void *data_addr; /* 参数数据地址 */
|
||||
uint16_t data_size; /* 参数数据大小 */
|
||||
const char *decs; /* 参数描述(可为NULL) */
|
||||
} param_map_item_t;
|
||||
/*------ Exported variables(变量定义) -------------------*/
|
||||
|
||||
/*------ Exported function prototypes (函数声明) -------*/
|
||||
|
||||
#if TCP_DEBUG_EN
|
||||
|
||||
const param_map_item_t *param_get_basic_map(void);
|
||||
uint16_t param_get_basic_map_size(void);
|
||||
const param_map_item_t *param_get_network_map(void);
|
||||
uint16_t param_get_network_map_size(void);
|
||||
const param_map_item_t *param_get_meter_map(void);
|
||||
uint16_t param_get_meter_map_size(void);
|
||||
const param_map_item_t *param_get_enable_map(void);
|
||||
uint16_t param_get_enable_map_size(void);
|
||||
const param_map_item_t *param_get_fee_map(void);
|
||||
uint16_t param_get_fee_map_size(void);
|
||||
const param_map_item_t *param_get_sdk_map(void);
|
||||
uint16_t param_get_sdk_map_size(void);
|
||||
const param_map_item_t *param_get_qrcode_map(void);
|
||||
uint16_t param_get_qrcode_map_size(void);
|
||||
|
||||
const char *param_type_to_string(param_data_type_t type);
|
||||
uint32_t param_get_enable_bit_value(uint16_t param_id, uint8_t op_flag, uint32_t value);
|
||||
uint32_t param_get_netcomm_bit_value(uint16_t param_id, uint8_t op_flag, uint32_t value);
|
||||
int param_get_value_as_string(const param_map_item_t *item, char *buffer, uint32_t size);
|
||||
int param_set_value_from_string(const param_map_item_t *item, const char *value_str);
|
||||
const param_map_item_t *param_get_map_item_by_id(uint16_t param_id);
|
||||
|
||||
#endif /* TCP_DEBUG_EN */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SYSTEM_PARACFG_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,489 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file tcp_protocol.h
|
||||
* @brief TCP通信协议解析头文件
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef __TCP_PROTOCOL_H
|
||||
#define __TCP_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 命令类型定义(字符串类型) */
|
||||
#define CMD_HEARTBEAT "Heartbeat"
|
||||
#define CMD_SYSTEM_INFO "SystemInfo"
|
||||
#define CMD_GUN_INFO "GunInfo"
|
||||
#define CMD_CHARGE_CONTROL "ChargeControl"
|
||||
#define CMD_ALARM_NOTIFY "AlarmNotify"
|
||||
#define CMD_STATUS_RESPONSE "StatusResponse"
|
||||
#define CMD_PARAM_QUERY "ParamQuery"
|
||||
#define CMD_PARAM_MODIFY "ParamModify"
|
||||
#define CMD_FIRMWARE_UPGRADE "FirmwareUpgrade"
|
||||
#define CMD_FAULT_INFO "FaultInfo"
|
||||
#define CMD_CUSTOM_DATA "CustomData"
|
||||
|
||||
/* 网络状态定义 */
|
||||
#define NETWORK_STATUS_OK 1 /* 网络正常 */
|
||||
#define NETWORK_STATUS_ERROR 0 /* 网络异常 */
|
||||
|
||||
/* 充电状态定义 */
|
||||
#define CHARGING_STATUS_IDLE 0 /* 空闲 */
|
||||
#define CHARGING_STATUS_CONNECTED 1 /* 已连接 */
|
||||
#define CHARGING_STATUS_STARTING 2 /* 启动中 */
|
||||
#define CHARGING_STATUS_CHARGING 3 /* 充电中 */
|
||||
#define CHARGING_STATUS_FINISHED 4 /* 充电完成 */
|
||||
|
||||
/* 枪连接状态定义 */
|
||||
#define GUN_CONNECTION_DISCONNECTED 0 /* 未连接 */
|
||||
#define GUN_CONNECTION_CONNECTED 1 /* 已连接 */
|
||||
|
||||
/* 充电控制动作定义 */
|
||||
#define CHARGE_ACTION_STOP 0 /* 停止充电 */
|
||||
#define CHARGE_ACTION_START 1 /* 启动充电 */
|
||||
|
||||
/* 固件升级子命令定义 */
|
||||
#define FIRMWARE_SUBCMD_START "Start"
|
||||
#define FIRMWARE_SUBCMD_DATA "Data"
|
||||
#define FIRMWARE_SUBCMD_END "End"
|
||||
#define FIRMWARE_SUBCMD_RESPONSE "Response"
|
||||
|
||||
/* 固件升级状态定义 */
|
||||
#define FIRMWARE_STATUS_SUCCESS 0 /* 成功 */
|
||||
#define FIRMWARE_STATUS_FAILED 1 /* 失败 */
|
||||
#define FIRMWARE_STATUS_IN_PROGRESS 2 /* 进行中 */
|
||||
#define FIRMWARE_STATUS_COMPLETED 3 /* 完成 */
|
||||
|
||||
/* 文件操作命令定义 */
|
||||
#define CMD_FILE_OPERATIONS "FileOperations"
|
||||
|
||||
/* 文件操作子命令定义 */
|
||||
#define FILE_OP_SUBCMD_QUERY "Query"
|
||||
#define FILE_OP_SUBCMD_DELETE "Delete"
|
||||
#define FILE_OP_SUBCMD_DOWNLOAD "Download"
|
||||
#define FILE_OP_SUBCMD_UPLOAD "Upload"
|
||||
|
||||
/* 历史记录查询/清除命令定义 */
|
||||
#define CMD_HISTORY_OPERATIONS "HistoryOperations"
|
||||
#define HISTORY_OP_SUBCMD_QUERY "Query"
|
||||
#define HISTORY_OP_SUBCMD_CLEAR "Clear"
|
||||
|
||||
/* 文件操作状态定义 */
|
||||
#define FILE_OP_STATUS_SUCCESS 0 /* 成功 */
|
||||
#define FILE_OP_STATUS_FAILED 1 /* 失败 */
|
||||
#define FILE_OP_STATUS_IN_PROGRESS 2 /* 进行中 */
|
||||
#define FILE_OP_STATUS_NOT_FOUND 3 /* 文件未找到 */
|
||||
#define FILE_OP_STATUS_NO_SPACE 4 /* 空间不足 */
|
||||
#define FILE_OP_STATUS_PERMISSION_DENIED 5 /* 权限不足 */
|
||||
|
||||
/* 文件类型定义 */
|
||||
#define FILE_TYPE_REGULAR 0 /* 普通文件 */
|
||||
#define FILE_TYPE_DIRECTORY 1 /* 目录 */
|
||||
#define FILE_TYPE_SYSTEM 2 /* 系统文件 */
|
||||
|
||||
/* 文件信息结构 */
|
||||
typedef struct {
|
||||
char name[128]; /* 文件名 */
|
||||
uint8_t type; /* 文件类型 */
|
||||
uint32_t size; /* 文件大小(字节) */
|
||||
char modified_time[32]; /* 修改时间 */
|
||||
char created_time[32]; /* 创建时间 */
|
||||
} tcp_file_info_t;
|
||||
|
||||
/* 文件查询请求结构 */
|
||||
typedef struct {
|
||||
char path[256]; /* 查询路径 */
|
||||
uint8_t recursive; /* 是否递归查询(0:否, 1:是) */
|
||||
} tcp_file_query_request_t;
|
||||
|
||||
/* 文件查询响应结构 */
|
||||
typedef struct {
|
||||
char path[256]; /* 查询路径 */
|
||||
uint16_t file_count; /* 文件数量 */
|
||||
uint16_t dir_count; /* 目录数量 */
|
||||
uint32_t total_size; /* 总大小(字节) */
|
||||
tcp_file_info_t *files; /* 文件信息数组(动态分配) */
|
||||
} tcp_file_query_response_t;
|
||||
|
||||
/* 文件删除请求结构 */
|
||||
typedef struct {
|
||||
char path[256]; /* 文件路径 */
|
||||
uint8_t force; /* 强制删除(0:否, 1:是) */
|
||||
} tcp_file_delete_request_t;
|
||||
|
||||
/* 文件删除响应结构 */
|
||||
typedef struct {
|
||||
char path[256]; /* 文件路径 */
|
||||
uint8_t status; /* 删除状态 */
|
||||
char message[128]; /* 结果消息 */
|
||||
} tcp_file_delete_response_t;
|
||||
|
||||
/* 文件下载请求结构 */
|
||||
typedef struct {
|
||||
char path[256]; /* 文件路径 */
|
||||
uint32_t offset; /* 起始偏移(字节) */
|
||||
uint32_t chunk_size; /* 分块大小(字节) */
|
||||
} tcp_file_download_request_t;
|
||||
|
||||
/* 文件下载响应结构 */
|
||||
typedef struct {
|
||||
char path[256]; /* 文件路径 */
|
||||
uint32_t file_size; /* 文件总大小 */
|
||||
uint32_t offset; /* 当前偏移 */
|
||||
uint32_t chunk_size; /* 分块大小 */
|
||||
uint32_t data_len; /* 数据长度 */
|
||||
uint8_t *data; /* 数据内容(Base64编码) */
|
||||
uint8_t is_last_chunk; /* 是否为最后一块(0:否, 1:是) */
|
||||
} tcp_file_download_response_t;
|
||||
|
||||
/* 文件上传请求结构 */
|
||||
typedef struct {
|
||||
char path[256]; /* 文件路径 */
|
||||
uint32_t file_size; /* 文件总大小 */
|
||||
uint32_t chunk_size; /* 分块大小 */
|
||||
uint32_t offset; /* 当前偏移 */
|
||||
uint32_t data_len; /* 数据长度 */
|
||||
uint8_t *data; /* 数据内容(Base64编码) */
|
||||
uint8_t is_last_chunk; /* 是否为最后一块(0:否, 1:是) */
|
||||
} tcp_file_upload_request_t;
|
||||
|
||||
/* 文件上传响应结构 */
|
||||
typedef struct {
|
||||
char path[256]; /* 文件路径 */
|
||||
uint32_t received_size; /* 已接收大小 */
|
||||
uint32_t total_size; /* 文件总大小 */
|
||||
uint8_t status; /* 上传状态 */
|
||||
char message[128]; /* 结果消息 */
|
||||
} tcp_file_upload_response_t;
|
||||
|
||||
/* 文件操作通用数据结构 */
|
||||
typedef struct {
|
||||
char subCommand[32]; /* 子命令: Query, Delete, Download, Upload */
|
||||
union {
|
||||
tcp_file_query_request_t query; /* 查询请求 */
|
||||
tcp_file_delete_request_t delete; /* 删除请求 */
|
||||
tcp_file_download_request_t download; /* 下载请求 */
|
||||
tcp_file_upload_request_t upload; /* 上传请求 */
|
||||
} request;
|
||||
union {
|
||||
tcp_file_query_response_t query; /* 查询响应 */
|
||||
tcp_file_delete_response_t delete; /* 删除响应 */
|
||||
tcp_file_download_response_t download; /* 下载响应 */
|
||||
tcp_file_upload_response_t upload; /* 上传响应 */
|
||||
} response;
|
||||
} tcp_file_operation_data_t;
|
||||
|
||||
/* 心跳消息结构 */
|
||||
typedef struct {
|
||||
uint32_t sequence; /* 序列号 */
|
||||
char source[32]; /* 来源 */
|
||||
} tcp_heartbeat_data_t;
|
||||
|
||||
/* 系统信息结构 */
|
||||
typedef struct {
|
||||
char systemModel[32]; /* 系统型号 */
|
||||
uint8_t gunCount; /* 枪数量 */
|
||||
float devicePower; /* 设备功率 */
|
||||
uint8_t networkStatus; /* 网络状态 */
|
||||
uint8_t faultCount; /* 故障数量 */
|
||||
char version[32]; /* 版本号 */
|
||||
} tcp_system_info_data_t;
|
||||
|
||||
/* 充电枪数据结构 */
|
||||
typedef struct {
|
||||
uint8_t gunNumber; /* 枪编号 (1-2) */
|
||||
uint8_t chargingStatus; /* 充电状态 */
|
||||
uint8_t gunConnectionStatus; /* 枪连接状态 */
|
||||
uint16_t charge_time; /* 充电时间 min*/
|
||||
float demandVoltage; /* 需求电压 */
|
||||
float demandCurrent; /* 需求电流 */
|
||||
float actualVoltage; /* 实际电压 */
|
||||
float actualCurrent; /* 实际电流 */
|
||||
float energy; /* 充电能量 (kWh) */
|
||||
float cost; /* 充电费用 */
|
||||
float batterySOC; /* 电池SOC (%) */
|
||||
char userId[32]; /* 用户ID */
|
||||
char orderNumber[32]; /* 订单号 */
|
||||
} tcp_gun_data_t;
|
||||
|
||||
/* 充电控制结构 */
|
||||
typedef struct {
|
||||
uint8_t gunNumber; /* 枪编号 (1-2) */
|
||||
uint8_t action; /* 动作 (0:停止, 1:启动) */
|
||||
} tcp_charge_control_t;
|
||||
|
||||
/* 参数类型定义 */
|
||||
#define PARAM_TYPE_BASIC "BasicParameters"
|
||||
#define PARAM_TYPE_NETWORK "NetworkParameters"
|
||||
#define PARAM_TYPE_METER "MeterParameters"
|
||||
#define PARAM_TYPE_ENABLE "EnableParameters"
|
||||
#define PARAM_TYPE_FEE "FeeParameters"
|
||||
#define PARAM_TYPE_SDK "SDKParameters"
|
||||
#define PARAM_TYPE_QRCODE "QRCodeParameters"
|
||||
|
||||
/* 参数项结构 */
|
||||
typedef struct {
|
||||
char name[32]; /* 参数名称 */
|
||||
char type[16]; /* 参数类型 */
|
||||
char value[64]; /* 参数值(字符串形式) */
|
||||
char decs[64]; /* 参数描述(可为空) */
|
||||
} tcp_param_item_t;
|
||||
|
||||
/* 参数查询请求结构 */
|
||||
typedef struct {
|
||||
char type[32]; /* 参数类型 */
|
||||
} tcp_param_query_request_t;
|
||||
|
||||
/* 参数查询响应结构 */
|
||||
typedef struct {
|
||||
char type[32]; /* 参数类型 */
|
||||
uint16_t count; /* 参数数量 */
|
||||
tcp_param_item_t *params; /* 参数数组(动态分配) */
|
||||
} tcp_param_query_response_t;
|
||||
|
||||
/* 参数修改请求结构 */
|
||||
typedef struct {
|
||||
char type[32]; /* 参数类型 */
|
||||
uint16_t count; /* 参数数量 */
|
||||
tcp_param_item_t *params; /* 参数数组(动态分配) */
|
||||
} tcp_param_modify_request_t;
|
||||
|
||||
/* 参数修改响应结构 */
|
||||
typedef struct {
|
||||
char type[32]; /* 参数类型 */
|
||||
uint16_t received; /* 接收到的参数数量 */
|
||||
uint16_t processed; /* 已处理的参数数量 */
|
||||
uint16_t success; /* 成功的参数数量 */
|
||||
uint16_t failed; /* 失败的参数数量 */
|
||||
} tcp_param_modify_response_t;
|
||||
|
||||
/* 固件升级数据结构 */
|
||||
typedef struct {
|
||||
char subCommand[32]; /* 子命令: Start, Data, End, Response */
|
||||
union {
|
||||
struct {
|
||||
uint32_t fileSize; /* 文件大小(字节) */
|
||||
uint32_t totalPackets; /* 总包数 */
|
||||
uint32_t packetSize; /* 包大小(字节) */
|
||||
char firmwareVersion[32]; /* 固件版本 */
|
||||
} start;
|
||||
struct {
|
||||
uint32_t packetIndex; /* 包索引 */
|
||||
uint32_t totalPackets; /* 总包数 */
|
||||
char *data; /* 数据(Base64编码),动态分配 */
|
||||
uint32_t data_len; /* 数据长度 */
|
||||
uint32_t crc; /* CRC校验值 */
|
||||
} data;
|
||||
struct {
|
||||
uint8_t status; /* 状态 */
|
||||
char errorMessage[128]; /* 错误信息 */
|
||||
} end;
|
||||
struct {
|
||||
uint8_t response; /* 响应码 */
|
||||
int packetIndex; /* 包索引 */
|
||||
char message[64]; /* 消息 */
|
||||
} response;
|
||||
} u;
|
||||
} tcp_firmware_upgrade_data_t;
|
||||
|
||||
/* CustomData数据结构 */
|
||||
typedef struct {
|
||||
char functionField[32]; /* 功能字段 */
|
||||
char functionContent[32]; /* 功能内容 */
|
||||
char functionParams[128]; /* 功能参数 */
|
||||
} tcp_custom_data_t;
|
||||
|
||||
/* 故障信息数据结构 */
|
||||
typedef struct {
|
||||
char faultCode[16]; /* 故障代码(可选),如"F001",为空表示查询所有故障 */
|
||||
} tcp_fault_info_request_t;
|
||||
|
||||
/* 历史记录操作请求结构 */
|
||||
typedef struct {
|
||||
char historyType[16]; /* fault|charging|card */
|
||||
char subCommand[32]; /* Query|Clear */
|
||||
uint32_t queryId; /* 一次查询会话标识 */
|
||||
uint16_t startIndex; /* 距离最新的偏移:0=最新 */
|
||||
uint16_t fetchCount; /* 希望获取的条数(服务端会裁剪/按字节预算修正) */
|
||||
} tcp_history_operations_data_t;
|
||||
|
||||
/* 单个故障信息结构 */
|
||||
typedef struct {
|
||||
char location[32]; /* 故障位置,如"充电枪1" */
|
||||
char code[16]; /* 故障代码,如"F001" */
|
||||
char time[32]; /* 故障时间,格式"YYYY-MM-DD HH:MM:SS" */
|
||||
char description[128]; /* 故障描述,如"过流保护触发" */
|
||||
} tcp_fault_item_t;
|
||||
|
||||
/* 故障信息响应结构 */
|
||||
typedef struct {
|
||||
uint8_t status; /* 状态码,0表示成功 */
|
||||
uint16_t faultCount; /* 故障数量 */
|
||||
tcp_fault_item_t *faults; /* 故障信息数组(动态分配) */
|
||||
} tcp_fault_info_response_t;
|
||||
|
||||
/* 通用消息结构 */
|
||||
typedef struct {
|
||||
char version[16]; /* 协议版本 */
|
||||
char timestamp[32]; /* 时间戳 */
|
||||
char command[32]; /* 命令类型(字符串) */
|
||||
union {
|
||||
tcp_heartbeat_data_t heartbeat; /* 心跳数据 */
|
||||
tcp_system_info_data_t systemInfo; /* 系统信息数据 */
|
||||
tcp_gun_data_t gunData; /* 充电枪数据 */
|
||||
tcp_charge_control_t chargeControl; /* 充电控制数据 */
|
||||
tcp_param_query_request_t paramQuery; /* 参数查询请求 */
|
||||
tcp_param_modify_request_t paramModify; /* 参数修改请求 */
|
||||
tcp_firmware_upgrade_data_t firmwareUpgrade; /* 固件升级数据 */
|
||||
tcp_file_operation_data_t fileOperation; /* 文件操作数据 */
|
||||
tcp_fault_info_request_t faultInfo; /* 故障信息请求 */
|
||||
tcp_history_operations_data_t historyOperations; /* 历史记录查询/清除 */
|
||||
tcp_custom_data_t customData; /* CustomData数据 */
|
||||
} data;
|
||||
} tcp_message_t;
|
||||
|
||||
/* 函数声明 */
|
||||
|
||||
/**
|
||||
* @brief 生成当前时间戳字符串
|
||||
* @param buffer 缓冲区
|
||||
* @param size 缓冲区大小
|
||||
* @retval 0成功,-1失败
|
||||
*/
|
||||
int tcp_generate_timestamp(char *buffer, uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief 生成心跳消息JSON
|
||||
* @param sequence 序列号
|
||||
* @param source 来源字符串
|
||||
* @param buffer 输出缓冲区
|
||||
* @param size 缓冲区大小
|
||||
* @retval JSON字符串长度,-1表示失败
|
||||
*/
|
||||
int tcp_generate_heartbeat_json(uint32_t sequence, const char *source, char *buffer, uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief 生成系统信息消息JSON
|
||||
* @param systemInfo 系统信息结构体指针
|
||||
* @param buffer 输出缓冲区
|
||||
* @param size 缓冲区大小
|
||||
* @retval JSON字符串长度,-1表示失败
|
||||
*/
|
||||
int tcp_generate_system_info_json(const tcp_system_info_data_t *systemInfo, char *buffer, uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief 生成充电枪数据消息JSON
|
||||
* @param gunData 充电枪数据结构体指针
|
||||
* @param buffer 输出缓冲区
|
||||
* @param size 缓冲区大小
|
||||
* @retval JSON字符串长度,-1表示失败
|
||||
*/
|
||||
int tcp_generate_gun_data_json(const tcp_gun_data_t *gunData, char *buffer, uint32_t size);
|
||||
|
||||
/**
|
||||
* @brief 解析接收到的JSON消息
|
||||
* @param json JSON字符串
|
||||
* @param message 输出消息结构体
|
||||
* @retval 0成功,-1失败
|
||||
*/
|
||||
int tcp_parse_json_message(const char *json, tcp_message_t *message);
|
||||
|
||||
/**
|
||||
* @brief 释放消息结构体中分配的内存
|
||||
* @param message 消息结构体指针
|
||||
*/
|
||||
void tcp_free_message(tcp_message_t *message);
|
||||
|
||||
/**
|
||||
* @brief 处理接收到的消息
|
||||
* @param message 消息结构体指针
|
||||
* @retval 0成功,-1失败
|
||||
*/
|
||||
int tcp_process_received_message(const tcp_message_t *message);
|
||||
|
||||
/**
|
||||
* @brief 获取默认系统信息
|
||||
* @param systemInfo 输出系统信息结构体
|
||||
*/
|
||||
void tcp_get_default_system_info(tcp_system_info_data_t *systemInfo);
|
||||
|
||||
/**
|
||||
* @brief 获取默认充电枪数据
|
||||
* @param gunData 输出充电枪数据结构体
|
||||
* @param gunNumber 枪编号 (1-2)
|
||||
*/
|
||||
void tcp_get_default_gun_data(tcp_gun_data_t *gunData, uint8_t gunNumber);
|
||||
|
||||
int system_data_stop_charge(uint8_t gunNumber, char *message);
|
||||
int system_data_start_charge(uint8_t gunNumber, const char *userId,
|
||||
const char *orderNumber, char *message);
|
||||
|
||||
/* 参数查询相关函数 */
|
||||
int tcp_generate_param_query_response(const tcp_param_query_response_t *response, char *buffer, uint32_t size);
|
||||
int tcp_generate_param_modify_response(const tcp_param_modify_response_t *response, char *buffer, uint32_t size);
|
||||
int tcp_process_param_query(const tcp_param_query_request_t *request, tcp_param_query_response_t *response);
|
||||
int tcp_process_param_modify(const tcp_param_modify_request_t *request, tcp_param_modify_response_t *response);
|
||||
|
||||
/* 固件升级相关函数 */
|
||||
void tcp_firmware_upgrade_log_io(const char *dir, const char *data, uint32_t len);
|
||||
void tcp_firmware_upgrade_log_buf(const char *dir, const uint8_t *data, uint32_t len);
|
||||
void tcp_firmware_upgrade_log_meta(const char *tag, uint32_t a, uint32_t b, uint32_t c, uint32_t d);
|
||||
int tcp_generate_firmware_upgrade_json(const tcp_firmware_upgrade_data_t *firmwareUpgrade, const char *subCommand, char *buffer, uint32_t size);
|
||||
int tcp_process_firmware_upgrade(const tcp_firmware_upgrade_data_t *firmwareUpgrade);
|
||||
|
||||
/* 文件操作相关函数 */
|
||||
int tcp_generate_file_query_response(const tcp_file_query_response_t *response, char *buffer, uint32_t size);
|
||||
int tcp_generate_file_delete_response(const tcp_file_delete_response_t *response, char *buffer, uint32_t size);
|
||||
int tcp_generate_file_download_response(const tcp_file_download_response_t *response, char *buffer, uint32_t size);
|
||||
int tcp_generate_file_upload_response(const tcp_file_upload_response_t *response, char *buffer, uint32_t size);
|
||||
int tcp_process_file_operation(const tcp_file_operation_data_t *fileOperation);
|
||||
|
||||
/* CustomData相关函数 */
|
||||
int tcp_generate_custom_data_json(const tcp_custom_data_t *customData, char *buffer, uint32_t size);
|
||||
|
||||
/* TCP调试打印宏定义 */
|
||||
#ifndef TCP_DEBUG_PRINT
|
||||
#define TCP_DEBUG_PRINT 0 /* 默认使能调试打印 */
|
||||
#endif
|
||||
|
||||
#if TCP_DEBUG_PRINT
|
||||
#define TCP_PRINT(fmt, ...) printf("[TCP] " fmt, ##__VA_ARGS__)
|
||||
#define TCP_PRINT_RAW(fmt, ...) printf(fmt, ##__VA_ARGS__)
|
||||
#define TCP_PRINT_RAW_DATA(data, len) do { \
|
||||
if ((data) != NULL && (len) > 0) { \
|
||||
char *buf = (char *)pvPortMalloc((len) + 1); \
|
||||
if (buf != NULL) { \
|
||||
memcpy(buf, (data), (len)); \
|
||||
buf[(len)] = '\0'; \
|
||||
TCP_PRINT("RECV RAW DATA: %s\r\n", buf); \
|
||||
vPortFree(buf); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
#define TCP_PRINT_RAW_SEND(data, len) do { \
|
||||
if ((data) != NULL && (len) > 0) { \
|
||||
char *buf = (char *)pvPortMalloc((len) + 1); \
|
||||
if (buf != NULL) { \
|
||||
memcpy(buf, (data), (len)); \
|
||||
buf[(len)] = '\0'; \
|
||||
TCP_PRINT("SEND RAW DATA: %s\r\n", buf); \
|
||||
vPortFree(buf); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
#else
|
||||
#define TCP_PRINT(fmt, ...) do {} while(0)
|
||||
#define TCP_PRINT_RAW(fmt, ...) do {} while(0)
|
||||
#define TCP_PRINT_RAW_DATA(data, len) do {} while(0)
|
||||
#define TCP_PRINT_RAW_SEND(data, len) do {} while(0)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TCP_PROTOCOL_H */
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "tcp_ser/tcp_ser_task.h"
|
||||
|
||||
#include "app_init/app_init.h"
|
||||
#include "app_init/task_lock.h"
|
||||
#include "mylog/mylog.h"
|
||||
#include "publicdata/publicdata.h"
|
||||
#include "tcp_ser/tcp_server.h"
|
||||
#include "debug_link.h"
|
||||
#include "wdt_task/wdt_task.h"
|
||||
|
||||
#if TCP_DEBUG_EN
|
||||
#define TCPSER_TASK_ID (TASK_ID_TcpSer)
|
||||
#define TCPSER_SLEEP_TIME_MS() (MY_GET_SLEEP_TIME(TCPSER_TASK_ID))
|
||||
#define TCPSER_LOCK_INIT() (u8_task_lock_ctrl(TCPSER_TASK_ID, TASK_LOCK_OP_INIT))
|
||||
#define TCPSER_LOCK_TAKE() (u8_task_lock_ctrl(TCPSER_TASK_ID, TASK_LOCK_OP_TAKE))
|
||||
#define TCPSER_LOCK_GIVE() (u8_task_lock_ctrl(TCPSER_TASK_ID, TASK_LOCK_OP_GIVE))
|
||||
#define TCPSER_FEED_DOG() (v_wdt_setFlag(TCPSER_TASK_ID))
|
||||
#endif
|
||||
#define TCPSER_LOG(fmt, ...) MYLOG_MSG(TCPSER_TASK_ID, (fmt), ##__VA_ARGS__)
|
||||
|
||||
#if TCP_DEBUG_EN && BLE_DEBUG_EN
|
||||
#include "ble_link/fc41d_ble.h"
|
||||
#else
|
||||
#include "sub_comm/sub_comm_task.h"
|
||||
#endif
|
||||
|
||||
static const char *tcp_ser_link_name(void)
|
||||
{
|
||||
#if BLE_DEBUG_EN
|
||||
return COMM_LINK_IS_BLE(comm_link_get_type()) ? "BLE" : "TCP";
|
||||
#else
|
||||
return "TCP";
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief tcpSer task:协议/业务共用 tcp_server;DIP1 选 TCP 或 BLE 物理链路。
|
||||
*/
|
||||
void v_tcp_ser_task(void *argument)
|
||||
{
|
||||
#if TCP_DEBUG_EN
|
||||
uint16_t sleep_time_ms;
|
||||
uint32_t last_send_time;
|
||||
uint32_t current_time;
|
||||
uint32_t last_start_try_time;
|
||||
uint8_t server_started;
|
||||
|
||||
(void)argument;
|
||||
(void)TCPSER_LOCK_INIT();
|
||||
sleep_time_ms = TCPSER_SLEEP_TIME_MS();
|
||||
last_send_time = get_current_seconds();
|
||||
last_start_try_time = 0U;
|
||||
server_started = 0U;
|
||||
|
||||
#if BLE_DEBUG_EN
|
||||
(void)ble_link_wait_dip_ready(3000U);
|
||||
comm_link_set_type(comm_link_type_from_dip());
|
||||
#else
|
||||
comm_link_set_type(COMM_LINK_TCP);
|
||||
if (u8_sub_comm_yx_get(E_SUB_DIP_1) != 0U) {
|
||||
TCPSER_LOG("DIP1=1 but BLE_DEBUG_EN=0, force TCP link");
|
||||
}
|
||||
#endif
|
||||
|
||||
TCPSER_LOG("debug link=%s (BLE_DEBUG_EN=%d)", tcp_ser_link_name(), BLE_DEBUG_EN);
|
||||
|
||||
for (;;) {
|
||||
TCPSER_FEED_DOG();
|
||||
current_time = get_current_seconds();
|
||||
|
||||
if (server_started == 0U) {
|
||||
if ((last_start_try_time == 0U) ||
|
||||
(u32_safe_seconds_since(last_start_try_time) >= 2U)) {
|
||||
if (tcp_server_start() == 0) {
|
||||
server_started = 1U;
|
||||
TCPSER_LOG("debug server start ok, link=%s", tcp_ser_link_name());
|
||||
} else {
|
||||
TCPSER_LOG("debug server start fail, retry in 2s");
|
||||
}
|
||||
last_start_try_time = current_time;
|
||||
}
|
||||
} else {
|
||||
tcp_server_process();
|
||||
}
|
||||
|
||||
if ((TASK_RUN_PRINT_TIME != 0U) &&
|
||||
(u32_safe_seconds_since(last_send_time) >= TASK_RUN_PRINT_TIME)) {
|
||||
if (TCPSER_LOCK_TAKE() == 1U) {
|
||||
TCPSER_LOG("tcp ser running, link=%s sleep=%ums",
|
||||
tcp_ser_link_name(), sleep_time_ms);
|
||||
(void)TCPSER_LOCK_GIVE();
|
||||
}
|
||||
last_send_time = current_time;
|
||||
}
|
||||
{
|
||||
mSleep(sleep_time_ms);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)argument;
|
||||
for (;;) {
|
||||
mSleep(1000U);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef TCP_SER_TASK_H
|
||||
#define TCP_SER_TASK_H
|
||||
|
||||
void v_tcp_ser_task(void *argument);
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file tcp_server.h
|
||||
* @brief TCP调试服务器头文件
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef __TCP_SERVER_H
|
||||
#define __TCP_SERVER_H
|
||||
|
||||
#include "debug_link.h"
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/tcp.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 消息类型枚举 */
|
||||
typedef enum {
|
||||
TCP_MSG_HEARTBEAT = 1, /* 心跳消息 */
|
||||
TCP_MSG_SYSTEM_INFO = 2, /* 系统信息 */
|
||||
TCP_MSG_GUN_DATA = 3, /* 充电枪数据 */
|
||||
/* 可以在此添加其他消息类型 */
|
||||
} tcp_msg_type_t;
|
||||
|
||||
/* 命令类型枚举(用于接收解析) */
|
||||
typedef enum {
|
||||
TCP_CMD_HEARTBEAT = 1, /* 心跳 */
|
||||
TCP_CMD_SYSTEM_INFO = 2, /* 系统信息 */
|
||||
TCP_CMD_GUN_DATA = 3, /* 充电枪数据(服务器发送) */
|
||||
TCP_CMD_CHARGE_CONTROL = 4, /* 充电启停控制(客户端发送) */
|
||||
} tcp_cmd_type_t;
|
||||
|
||||
/* 服务器状态结构体 */
|
||||
typedef struct {
|
||||
struct tcp_pcb *server_pcb; /* 服务器PCB */
|
||||
struct tcp_pcb *client_pcb; /* 客户端PCB(仅支持一个) */
|
||||
uint8_t is_client_connected; /* 客户端连接状态 */
|
||||
|
||||
/* 协议相关状态 */
|
||||
uint32_t heartbeat_sequence; /* 心跳序列号 */
|
||||
uint8_t system_info_sent; /* 系统信息是否已发送 */
|
||||
|
||||
/* 定时计数变量 */
|
||||
uint32_t tick_counter; /* 全局tick计数器,每秒递增 */
|
||||
|
||||
/* 消息发送间隔(秒) */
|
||||
uint32_t heartbeat_last_tick; /* 上次心跳发送时的tick */
|
||||
uint32_t system_info_last_tick; /* 上次系统信息发送时的tick */
|
||||
uint32_t gun_data_last_tick; /* 上次充电枪数据发送时的tick */
|
||||
uint32_t last_heartbeat_received_tick; /* 上次收到心跳时的tick */
|
||||
} tcp_server_state_t;
|
||||
|
||||
/* 函数声明 */
|
||||
err_t tcp_server_start(void);
|
||||
void tcp_server_process(void);
|
||||
void tcp_server_stop(void);
|
||||
void tcp_server_task(void *arg);
|
||||
|
||||
/* 协议相关函数 */
|
||||
void tcp_server_send_message(tcp_msg_type_t msg_type);
|
||||
void tcp_server_process_received_data(const char *data, uint16_t len);
|
||||
|
||||
/* 通用发送函数 */
|
||||
err_t tcp_server_send_json(const char *json_data, uint16_t len);
|
||||
|
||||
int tcp_server_rx_upgrade_begin(void);
|
||||
int tcp_server_rx_upgrade_end(void);
|
||||
void tcp_server_rx_clear_pending(void);
|
||||
void tcp_server_link_rx_drain(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TCP_SERVER_H */
|
||||
@@ -0,0 +1,371 @@
|
||||
# tcp_ser 模块说明
|
||||
|
||||
> 本文档描述 `CCU621_M` 工程 `app/tcp_ser` 目录的职责划分、**完整文件清单**、各源文件功能、TCP/BLE 双链路与命令处理流程。
|
||||
> 返回主说明:[README](../../README.md)
|
||||
|
||||
**文档版本**:与当前工程树同步(含 BLE 调试、`debug_link` 链路层;**阶段1** 已合并 `comm_link`+Socket;**阶段2** 已合并文件类业务;**不含** `fwbin_protocol` / `fwup_protocol`)。
|
||||
|
||||
## 快速跳转
|
||||
|
||||
- [1. 模块总览](#1-模块总览)
|
||||
- [2. 目录与文件清单(完整)](#2-目录与文件清单完整)
|
||||
- [3. Keil 工程源文件](#3-keil-工程源文件)
|
||||
- [4. 各文件功能说明](#4-各文件功能说明)
|
||||
- [5. 分层架构与调用关系](#5-分层架构与调用关系)
|
||||
- [6. TCP / BLE 双链路](#6-tcp--ble-双链路)
|
||||
- [7. 已支持命令与处理路径](#7-已支持命令与处理路径)
|
||||
- [8. 关键流程](#8-关键流程)
|
||||
- [9. 固件升级(FirmwareUpgrade)](#9-固件升级firmwareupgrade)
|
||||
- [10. GunInfo 数据规则](#10-guninfo-数据规则)
|
||||
- [11. CustomData / HistoryOperations](#11-customdata--historyoperations)
|
||||
- [12. 编译开关与运行条件](#12-编译开关与运行条件)
|
||||
- [13. 维护注意事项](#13-维护注意事项)
|
||||
- [14. 已移除 / 不在树内的文件](#14-已移除--不在树内的文件)
|
||||
- [关联文档](#关联文档)
|
||||
|
||||
---
|
||||
|
||||
## 1. 模块总览
|
||||
|
||||
`tcp_ser` 是 CCU621 的 **上位机调试服务**模块:协议与业务在 `tcp_server` / `tcp_protocol` 中**共用**,物理链路通过 `comm_link` 在 **以太网 TCP** 与 **蓝牙 BLE(FC41D)** 之间切换。
|
||||
|
||||
| 职责 | 说明 |
|
||||
|---|---|
|
||||
| 链路适配 | `comm_link`:DIP1 选 TCP 或 BLE,对上统一 `receive/send/process` |
|
||||
| TCP 服务 | 监听端口(默认 **9699**),单客户端 |
|
||||
| BLE 服务 | FC41D GATT 非透传(fff1/fff2/fff3),广播名默认 `CCU621_Debug` |
|
||||
| 周期上送 | 每 **2s** 推送 `SystemInfo` + 双枪 `GunInfo`(客户端发 `Heartbeat` 保活) |
|
||||
| 命令处理 | JSON v2.0 解析、分发、组响应 |
|
||||
| 业务解耦 | 协议层与充电控制、参数、文件、升级等分离 |
|
||||
|
||||
```
|
||||
上位机 / 小程序
|
||||
│ JSON(同一套 v2.0 协议)
|
||||
▼
|
||||
tcp_server.c ← RX 缓冲、JSON 抽帧、周期上送、连接管理
|
||||
│
|
||||
├── debug_link.c ← 链路层(comm_link 分发 + TCP Socket + 会话上下文)
|
||||
│ └── ble_link/fc41d_ble.c (UART7 AT + GATT,BLE 后端)
|
||||
│
|
||||
└── tcp_protocol.c ← JSON 解析 / 组包 / 命令路由
|
||||
├── debug_files.c ← FileOperations + FirmwareUpgrade
|
||||
├── system_data.c
|
||||
└── system_paracfg.c
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 目录与文件清单(完整)
|
||||
|
||||
当前 `app/tcp_ser/` 共 **19 个文件**(含 `ble_link/` 子目录;`.c` 源文件 7 个):
|
||||
|
||||
```
|
||||
app/tcp_ser/
|
||||
├── tcp_ser_task.c # FreeRTOS 任务入口
|
||||
├── tcp_ser_task.h
|
||||
├── tcp_server.c # 调试服务主逻辑(经 comm_link 收发)
|
||||
├── tcp_server.h
|
||||
├── debug_link.c # 链路层:comm_link 分发 + lwIP Socket + 会话上下文
|
||||
├── debug_link.h # 链路层对外 API 与类型
|
||||
├── debug_files.c # 文件类业务:FileOperations + FirmwareUpgrade
|
||||
├── debug_files.h # 文件类业务对外 API 与类型
|
||||
├── tcp_protocol.c # JSON v2.0 协议
|
||||
├── tcp_protocol.h
|
||||
├── system_data.c # 运行时数据、充电控制、历史、CustomData
|
||||
├── system_data.h
|
||||
├── system_paracfg.c # 参数映射表
|
||||
├── system_paracfg.h
|
||||
├── ble_link/ # ── BLE 子目录(BLE_DEBUG_EN=1 时编入)
|
||||
│ ├── fc41d_ble.c # FC41D 完整实现 + comm_link 后端
|
||||
│ ├── fc41d_ble.h # 配置宏、对外 API
|
||||
│ └── fc41d_ble_priv.h # BLE 私有上下文(与 tcp_link_ctx union)
|
||||
├── TCP_DEBUG_PROTOCOL.md # JSON 报文字段说明
|
||||
└── tcp_ser模块说明.md # 本文档
|
||||
```
|
||||
|
||||
| 子目录 | 说明 |
|
||||
|---|---|
|
||||
| `ble_link/` | 仅 FC41D 蓝牙;UART7 AT 辅助函数为 **本目录 static**,不扩展公共 `BSP/com/usart.c` |
|
||||
|
||||
---
|
||||
|
||||
## 3. Keil 工程源文件
|
||||
|
||||
`Project/GD32H759.uvprojx` 中 `tcp_ser` 相关 **.c** 源文件(按工程登记):
|
||||
|
||||
| 源文件 | 路径 |
|
||||
|---|---|
|
||||
| `tcp_ser_task.c` | `app/tcp_ser/` |
|
||||
| `tcp_server.c` | `app/tcp_ser/` |
|
||||
| `debug_link.c` | `app/tcp_ser/` |
|
||||
| `fc41d_ble.c` | `app/tcp_ser/ble_link/` |
|
||||
| `tcp_protocol.c` | `app/tcp_ser/` |
|
||||
| `system_data.c` | `app/tcp_ser/` |
|
||||
| `system_paracfg.c` | `app/tcp_ser/` |
|
||||
| `debug_files.c` | `app/tcp_ser/` |
|
||||
|
||||
头文件通过 Include Path `..\app\tcp_ser` 及子目录引用,无需单独加入工程。
|
||||
|
||||
---
|
||||
|
||||
## 4. 各文件功能说明
|
||||
|
||||
### 4.1 任务与调度
|
||||
|
||||
| 文件 | 功能 |
|
||||
|---|---|
|
||||
| **`tcp_ser_task.c`** | `v_tcp_ser_task`:任务锁、看门狗;`BLE_DEBUG_EN` 时 `ble_link_wait_dip_ready()` 后 `comm_link_set_type(comm_link_type_from_dip())`;每 2s 重试 `tcp_server_start()`;循环 `tcp_server_process()`。 |
|
||||
| **`tcp_ser_task.h`** | 声明 `v_tcp_ser_task`。 |
|
||||
|
||||
### 4.2 链路层(`debug_link`)
|
||||
|
||||
| 文件 | 功能 |
|
||||
|---|---|
|
||||
| **`debug_link.c`** | 阶段1 合并实现,分三段:<br>• **会话上下文**:单例 `comm_link_ctx_t`(`session` + `backend` union)<br>• **TCP Socket 后端**(本文件内 `static`):lwIP `init/accept/receive/send/close` 等<br>• **comm_link 分发**:`comm_link_start/stop/process`、`comm_link_receive_data`、`comm_link_send_data` 等;按 `session.type` 转发到 TCP 或 `comm_link_ble_*` |
|
||||
| **`debug_link.h`** | 链路层统一头:`COMM_LINK_TCP/BLE`、`comm_link_*` API、`comm_link_ctx_*`、`tcp_link_ctx_t`、`comm_link_backend_u` |
|
||||
|
||||
### 4.3 传输后端(BLE)
|
||||
| **`ble_link/fc41d_ble.c`** | FC41D:AT 层、PE6 复位、BLE 状态机、GATT Notify 发送、`+QBLERECV` 解析;实现 `comm_link_ble_*`;UART7 **固定 115200**(`FC41D_UART_BAUD`),阻塞收包在 `fc41d_ble.c` 内 static 实现。 |
|
||||
| **`ble_link/fc41d_ble.h`** | 硬件/AT 超时宏、`FC41D_BLE_NAME`、GATT UUID、`ble_link_is_bt_mode()`、`comm_link_ble_*` API。 |
|
||||
| **`ble_link/fc41d_ble_priv.h`** | AT 缓冲、link/app RX 环、状态机枚举与 `fc41d_ble_ctx_t`。 |
|
||||
|
||||
### 4.4 服务主控层
|
||||
|
||||
| 文件 | 功能 |
|
||||
|---|---|
|
||||
| **`tcp_server.c`** | 核心状态机:<br>• 动态 RX 缓冲:常态 **1024B**,固件升级 **8192B**(`tcp_server_rx_upgrade_begin/end`)<br>• `comm_link_receive_data` 收包 → JSON 括号抽帧<br>• 周期上送:每 **2s** `SystemInfo` + 双枪 `GunInfo`;BLE 枪间加延时<br>• 升级进行中暂停周期上送;跳过前导垃圾字节(BLE 分片)<br>• 心跳超时 30s(升级中不判超时)<br>• TCP 回包可追加 `\r\n`;BLE 纯 JSON 无 CRLF |
|
||||
| **`tcp_server.h`** | `tcp_server_state_t`、`tcp_server_start/process/stop`、`tcp_server_rx_upgrade_*`。 |
|
||||
|
||||
### 4.5 协议层
|
||||
|
||||
| 文件 | 功能 |
|
||||
|---|---|
|
||||
| **`tcp_protocol.c`** | JSON v2.0 组包/解析/分发;**BLE 链路拒绝 `FirmwareUpgrade`**;其余命令与 TCP 相同。 |
|
||||
| **`tcp_protocol.h`** | `CMD_*`、结构体、状态枚举。 |
|
||||
|
||||
### 4.6 业务层
|
||||
|
||||
| 文件 | 功能 |
|
||||
|---|---|
|
||||
| **`system_data.c`** | `tcp_get_default_system_info/gun_data`、充电启停、`tcp_process_custom_data`、故障与历史记录;GunInfo 见 [§10](#10-guninfo-数据规则)。 |
|
||||
| **`system_data.h`** | 业务接口与历史查询结构。 |
|
||||
| **`system_paracfg.c/h`** | 七类参数映射表与读写。 |
|
||||
| **`debug_files.c`** | 阶段2 合并实现,分两段:<br>• **FileOperations**(`FATFS_EN=1`):Query/Delete/Download/Upload<br>• **FirmwareUpgrade**:TCP JSON+Base64 写外置 Flash;`system_filetransfer_is_active`、空闲超时等 |
|
||||
| **`debug_files.h`** | 文件类业务统一头:`file_operation_data_t`、`firmware_upgrade_context_t`、`system_filectrl_*`、`system_filetransfer_*` |
|
||||
|
||||
### 4.7 协议文档
|
||||
|
||||
| 文件 | 功能 |
|
||||
|---|---|
|
||||
| **`TCP_DEBUG_PROTOCOL.md`** | 上位机 JSON 字段与示例(TCP/BLE 共用)。 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 分层架构与调用关系
|
||||
|
||||
| 层次 | 文件 | 职责 |
|
||||
|---|---|---|
|
||||
| **任务** | `tcp_ser_task.c` | 选链路、启服务、周期 `tcp_server_process` |
|
||||
| **服务** | `tcp_server.c` | 缓冲、抽帧、定时上送、连接/超时 |
|
||||
| **链路** | `debug_link.c` | TCP/BLE 字节流统一接口 + Socket 后端 |
|
||||
| **后端** | `debug_link.c`(TCP)/ `fc41d_ble.c`(BLE) | 物理收发 |
|
||||
| **协议** | `tcp_protocol.c` | JSON ↔ 结构体、路由 |
|
||||
| **业务** | `system_data.c` / `system_paracfg.c` / `debug_files.c` | 桩数据、参数、升级、文件 |
|
||||
|
||||
扩展新命令:改 `tcp_protocol.h/c` → 对应 `system_*.c` 或 `debug_files.c` → 更新 `TCP_DEBUG_PROTOCOL.md`。
|
||||
|
||||
---
|
||||
|
||||
## 6. TCP / BLE 双链路
|
||||
|
||||
### 6.1 选路
|
||||
|
||||
| 条件 | 物理链路 |
|
||||
|---|---|
|
||||
| `BLE_DEBUG_EN=0` | 恒为 TCP(DIP1=1 仅打日志,仍走 TCP) |
|
||||
| `BLE_DEBUG_EN=1` 且 DIP1=0 | 以太网 TCP,端口 **9699** |
|
||||
| `BLE_DEBUG_EN=1` 且 DIP1=1 | FC41D BLE GATT |
|
||||
|
||||
DIP 来源:副板 `sub_comm` → `u8_sub_comm_yx_get(E_SUB_DIP_1)`。
|
||||
|
||||
### 6.2 协议差异
|
||||
|
||||
| 项目 | TCP | BLE |
|
||||
|---|---|---|
|
||||
| 报文格式 | JSON v2.0 | 同左 |
|
||||
| 下行结束符 | 可选 `\r\n` | 无 CRLF |
|
||||
| 周期上报 | 2s SystemInfo + GunInfo×2 | 同左(枪间 ~80ms 间隔) |
|
||||
| `FirmwareUpgrade` | 支持 | **拒绝**(`tcp_protocol.c`) |
|
||||
| 固件相关 RX 缓冲放大 | 8192B | 同左(若走 TCP 升级) |
|
||||
|
||||
### 6.3 硬件(BLE)
|
||||
|
||||
| 项目 | 说明 |
|
||||
|---|---|
|
||||
| 模组 | Quectel FC41D |
|
||||
| UART | UART7,PE1/PE0 |
|
||||
| 复位 | PE6,`FC41D_RST` 高有效 |
|
||||
| 广播名 | `CCU621_Debug`(可宏改) |
|
||||
| 服务/特征 | fff1 / fff2(Write) / fff3(Notify) |
|
||||
|
||||
---
|
||||
|
||||
## 7. 已支持命令与处理路径
|
||||
|
||||
| command | 方向 | TCP | BLE | 业务入口 |
|
||||
|---|---|:---:|:---:|---|
|
||||
| `Heartbeat` | 双向 | ✓ | ✓ | 更新 `last_heartbeat_received_tick` |
|
||||
| `SystemInfo` | 设备→上位机 | ✓ | ✓ | `tcp_get_default_system_info` |
|
||||
| `GunInfo` | 设备→上位机 | ✓ | ✓ | `tcp_get_default_gun_data` |
|
||||
| `ChargeControl` | 上位机→设备 | ✓ | ✓ | `system_data_start/stop_charge` |
|
||||
| `ParamQuery` / `ParamModify` | 上位机→设备 | ✓ | ✓ | `system_paracfg` |
|
||||
| `FileOperations` | 上位机→设备 | ✓ | ✓ | `system_filectrl_process_*` |
|
||||
| `HistoryOperations` | 上位机→设备 | ✓ | ✓ | `tcp_history_build/clear_records` |
|
||||
| `FaultInfo` | 上位机→设备 | ✓ | ✓ | `tcp_get_system_fault_data` |
|
||||
| `CustomData` | 上位机→设备 | ✓ | ✓ | `tcp_process_custom_data` |
|
||||
| `FirmwareUpgrade` | 上位机→设备 | ✓ | **✗** | `system_filetransfer_*`(仅 TCP) |
|
||||
|
||||
---
|
||||
|
||||
## 8. 关键流程
|
||||
|
||||
### 8.1 下行(上位机 → 设备)
|
||||
|
||||
```
|
||||
comm_link_receive_data()
|
||||
→ tcp_rx_buffer_append()
|
||||
→ tcp_rx_buffer_extract_json() // 括号匹配;升级期可 skip 前导垃圾
|
||||
→ tcp_server_process_received_data()
|
||||
→ tcp_parse_json_message()
|
||||
→ tcp_process_received_message()
|
||||
→ system_* 业务
|
||||
→ tcp_generate_*_json()
|
||||
→ comm_link_send_data()
|
||||
```
|
||||
|
||||
### 8.2 上行(周期,每 2s)
|
||||
|
||||
`tcp_server_send_ctrl()`:
|
||||
|
||||
1. 发送 `SystemInfo`
|
||||
2. BLE 延时 ~120ms / TCP ~20ms
|
||||
3. 依次发送枪 1、枪 2 的 `GunInfo`(BLE 枪间 ~80ms)
|
||||
|
||||
固件升级进行中:**不**做周期上送。
|
||||
|
||||
### 8.3 任务主循环
|
||||
|
||||
```
|
||||
v_tcp_ser_task
|
||||
├─ [BLE] ble_link_wait_dip_ready + comm_link_set_type(DIP1)
|
||||
├─ tcp_server_start() → comm_link_start()
|
||||
└─ tcp_server_process()
|
||||
├─ comm_link_process() // BLE 状态机
|
||||
├─ comm_link_accept_client() // 仅 TCP
|
||||
├─ comm_link_receive_data()
|
||||
├─ tcp_server_process_rx_buffer()
|
||||
├─ system_filetransfer_poll_idle_timeout()
|
||||
└─ tcp_server_send_ctrl()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 固件升级(FirmwareUpgrade)
|
||||
|
||||
**仅 TCP 链路**。JSON + Base64 分包,写外部 Flash 升级区。
|
||||
|
||||
| subCommand | 关键字段 |
|
||||
|---|---|
|
||||
| `Start` | `fileSize`, `totalPackets`, `packetSize`, `firmwareVersion` |
|
||||
| `Data` | `packetIndex`, `data`(Base64), `crc` |
|
||||
| `End` | `status`, `errorMessage` |
|
||||
| `Response` | `response`(ACK/NAK), `packetIndex` |
|
||||
|
||||
实现:`tcp_protocol.c` + `debug_files.c`(原 `system_filetransfer`)。升级时 RX 缓冲扩至 8192B,并可用 `system_filetransfer_get_expected_packet_bytes` 判断 Data JSON 是否收齐。
|
||||
|
||||
---
|
||||
|
||||
## 10. GunInfo 数据规则
|
||||
|
||||
`tcp_get_default_gun_data()`(`system_data.c`):
|
||||
|
||||
1. **空闲 / 仅插枪**(`chargingStatus <= CONNECTED`):`energy`、`cost`、`batterySOC`、`chargingTime` 置 **0**;`userId`、`orderNumber` 置 **空串**(避免 Flash 残留导致 JSON 过长、BLE 分片异常)。
|
||||
2. **充电中**:从 `v_meterlog_put_char_log_data` 填实时量;`tcp_copy_log_field()` 安全拷贝用户/订单字段(遇非可打印 ASCII 或 `'\0'` 停止)。
|
||||
3. **SOC**:限制 0–100。
|
||||
|
||||
---
|
||||
|
||||
## 11. CustomData / HistoryOperations
|
||||
|
||||
### CustomData
|
||||
|
||||
| functionField | 行为 |
|
||||
|---|---|
|
||||
| `paramInit` | `v_public_param_init()` |
|
||||
| `reboot` | 应答后 `v_sys_reboot()` |
|
||||
| `faultInfo` | 当前故障摘要 |
|
||||
|
||||
### HistoryOperations
|
||||
|
||||
`subCommand=Query`,`historyType=fault`:分页 `queryId` / `startIndex` / `fetchCount`,`content` 格式:
|
||||
`location,code,desc,actType,value1,value2`。
|
||||
|
||||
---
|
||||
|
||||
## 12. 编译开关与运行条件
|
||||
|
||||
| 宏 | 默认值 | 说明 |
|
||||
|---|---|---|
|
||||
| `TCP_DEBUG_EN` | `1` | 总开关;为 0 时整模块空桩 |
|
||||
| `BLE_DEBUG_EN` | `1` | 为 0 时不编入 `fc41d_ble.c` 有效代码,恒 TCP |
|
||||
| `TCP_DEBUG_PORT` | `9699` | TCP 监听端口 |
|
||||
| `TCP_DEBUG_MAX_CLIENTS` | `1` | 单客户端 |
|
||||
|
||||
约束:`BLE_DEBUG_EN` 要求 `TCP_DEBUG_EN=1`(`#error` 保护)。
|
||||
|
||||
运行前提:
|
||||
|
||||
- **TCP**:lwIP 已初始化、网口可用
|
||||
- **BLE**:FC41D 硬件 + UART7;`BSP/GPIO` 中 `FC41D_RST_PORT/PIN`(PE6)
|
||||
- **文件操作**:FatFS 已挂载
|
||||
- **固件升级**:外部 Flash + IAP
|
||||
|
||||
---
|
||||
|
||||
## 13. 维护注意事项
|
||||
|
||||
1. **协议与链路分离**:JSON 逻辑只在 `tcp_server` / `tcp_protocol`;换链路只动 `comm_link` 与后端。
|
||||
2. **勿改公共 usart**:FC41D 的 UART7 固定 **115200**(`FC41D_UART_BAUD`),阻塞收包在 `fc41d_ble.c` 内 static 实现。
|
||||
3. **GunInfo 字符串**:订单/用户字段必须用 `tcp_copy_log_field`,禁止对定长 `U8_T[]` 直接 `%s` / `strcpy`。
|
||||
4. **BLE 包长**:单条 GunInfo JSON 宜控制在 GATT 分片友好范围;空闲态勿带长 `orderNumber`。
|
||||
5. **Keil**:勿再加入已删除的 `fwbin_protocol.c`、`fwup_protocol.c`、`comm_link*.c`、`tcp_server_socket.c`、`system_filectrl.c`、`system_filetransfer.c`;须包含 `debug_link.c`、`debug_files.c`、`ble_link/fc41d_ble.c`。
|
||||
6. **DIP 调试**:无模组时设 `BLE_DEBUG_EN=0` 或 DIP1=0 走 TCP。
|
||||
|
||||
---
|
||||
|
||||
## 14. 已移除 / 不在树内的文件
|
||||
|
||||
以下文件**不在**当前 `app/tcp_ser` 目录,亦**不应**加入 Keil 工程:
|
||||
|
||||
| 文件 | 说明 |
|
||||
|---|---|
|
||||
| `fwbin_protocol.c/h` | FWBIN 二进制升级(`F0 F9`),已废弃 |
|
||||
| `fwup_protocol.c` | FWUP 行协议升级,已废弃 |
|
||||
| `tcp/tcp_socket.c` | 旧路径;Socket 已合并进 `debug_link.c` |
|
||||
| `comm_link.c` / `comm_link_ctx.c` / `tcp_server_socket.c` | 阶段1 已合并为 `debug_link.c` |
|
||||
| `system_filectrl.c` / `system_filetransfer.c` | 阶段2 已合并为 `debug_files.c` |
|
||||
| `comm_link.h` / `comm_link_ctx.h` / `system_filectrl.h` / `system_filetransfer.h` / `tcp/` | 兼容头已删除,统一 `#include "debug_link.h"` / `"debug_files.h"` |
|
||||
| `_backup_phase1/` / `_backup_phase2/` | 合并过程临时备份,已删除 |
|
||||
|
||||
历史方案可参考备份目录 `CCU621_M-蓝牙功能升级失败-退回前保存` 与 `docs/CCU621_M_固件升级十六进制协议_FWUP.md`(**已过时**)。
|
||||
|
||||
---
|
||||
|
||||
## 关联文档
|
||||
|
||||
| 文档 | 说明 |
|
||||
|---|---|
|
||||
| [`CCU621_M/README.md`](../../README.md) | 工程主索引 |
|
||||
| [`TCP_DEBUG_PROTOCOL.md`](TCP_DEBUG_PROTOCOL.md) | JSON 报文字段与示例 |
|
||||
| [`docs/CCU621_M_蓝牙调试通信方案.md`](../../docs/CCU621_M_蓝牙调试通信方案.md) | 蓝牙联调方案 |
|
||||
| 本文 | `app/tcp_ser/tcp_ser模块说明.md` |
|
||||
Reference in New Issue
Block a user