272 lines
10 KiB
Markdown
272 lines
10 KiB
Markdown
# libicp67 — ICP67 自定义协议库 API 参考
|
||
|
||
## 模块概述
|
||
|
||
**位置**: `src/protocol/libicp67/`
|
||
|
||
**定位**: ICP67 二进制协议引擎 — 负责帧定界、编解码、重传管理。纯 C 实现,extern "C" 接口。
|
||
|
||
**依赖**: `liblog` (日志), POSIX `sem_t` (信号量)
|
||
|
||
**被依赖**: `libself_ptl` (自协议应用层)
|
||
|
||
## 协议帧格式
|
||
|
||
```
|
||
| 0x67 | len(2B) | 0x67 | src | dst | dir | ti | cot | info_addr(2B) | dev_addr(4B) | data... | CRC(1B) | 0x16 |
|
||
| 帧头1 | 长度 | 帧头2 | 源 | 目的 | 方向 |类型| 原因 | 信息体地址 | 设备地址 | 数据体 | 校验和 | 帧尾 |
|
||
```
|
||
|
||
- **帧头**: 0x67 0x67 (双字节定界)
|
||
- **校验**: 8-bit 累加和 (CRC8)
|
||
- **帧尾**: 0x16
|
||
- **设备地址**: src/dst 取值 0=维护软件, 1=主板, 2=主板, 3=采样板, 4=LCD
|
||
|
||
## 架构
|
||
|
||
```
|
||
libself_ptl (应用层)
|
||
│
|
||
├─ icp67_init() 初始化
|
||
├─ icp67_set_*_cb() 注册 17 个回调
|
||
│
|
||
▼
|
||
libicp67 (协议引擎)
|
||
├─ icp67_search_frame() 帧定界
|
||
├─ icp67_decode() 解码分发
|
||
├─ icp67_timer_handler() 重传管理
|
||
├─ Sender 函数 (13个) 组帧发送
|
||
└─ 16 种 TI 解码函数
|
||
```
|
||
|
||
## 文件清单
|
||
|
||
| 文件 | 行数 | 说明 |
|
||
|---|---|---|
|
||
| `release/inc/myIcp67.h` | 331 | 公共契约: 帧结构、回调类型、`stru_icp67`、常量宏 |
|
||
| `src/protocol/libicp67/inc/icp67.h` | ~500 | 内部头: TI/COT枚举、ASDU结构体、内部API声明 |
|
||
| `src/protocol/libicp67/src/icp67_frame.c` | 126 | 帧定界、CRC校验、帧构造、调试打印 |
|
||
| `src/protocol/libicp67/src/icp67_decode.c` | 1024 | 16种TI解码+文件/ZIP子命令分发 |
|
||
| `src/protocol/libicp67/src/icp67_core.c` | 645 | 初始化、回调注册、重传管理、Sender函数、定时器 |
|
||
|
||
## 对外 API
|
||
|
||
### 初始化
|
||
|
||
```c
|
||
void icp67_init(stru_icp67 *p_icp67, icp67_send_cb send_cb, void *arg);
|
||
```
|
||
|
||
初始化协议实例,绑定发送回调和用户参数。内部自动绑定:
|
||
- `search_frame_cb` → `icp67_search_frame()`
|
||
- `decode_cb` → `icp67_decode()`
|
||
- `timer_handler_cb` → `icp67_timer_handler()`
|
||
- 创建重传信号量 `rtx_sem`
|
||
- 初始化 13 个 Sender 回调为内部函数
|
||
- 设置超时参数 `tm_out = 1000ms / 10ms = 100 tick`
|
||
|
||
```c
|
||
int icp67_set_ao_cfg_md5(stru_icp67 *p_icp67, uint8_t *p_md5);
|
||
```
|
||
|
||
设置 AO 参数配置的 MD5 校验值 (16字节),用于后续 TI_1/TI_2 帧的 MD5 验证。
|
||
|
||
### 回调注册 (17 个 setter)
|
||
|
||
**pop_out 类 (3 个)** — 解码后回调注册:
|
||
| 函数 | 注册回调 | 触发场景 |
|
||
|---|---|---|
|
||
| `icp67_set_ao_pop_out_cb` | `ao_pop_out_cb` | TI_1 参数读取响应 |
|
||
| `icp67_set_iec_point_tbl_pop_out_cb` | `iec_point_tbl_pop_out_cb` | TI_3 点表读取响应 |
|
||
| `icp67_set_self_check_pop_out_cb` | `self_check_pop_out_cb` | TI_6 自检响应 |
|
||
|
||
**文件操作类 (6 个):**
|
||
| 函数 | 注册回调 | 触发场景 |
|
||
|---|---|---|
|
||
| `icp67_set_dir_pop_out_cb` | `dir_pop_out_cb` | TI_11_2 目录响应 |
|
||
| `icp67_set_file_read_act_confirm_cb` | `file_read_act_confirm_cb` | TI_11_4 读文件激活响应 |
|
||
| `icp67_set_file_read_cb` | `file_read_cb` | TI_11_5 读文件内容 |
|
||
| `icp67_set_file_write_act_confirm_cb` | `file_write_act_confirm_cb` | TI_11_8 写文件激活响应 |
|
||
| `icp67_set_file_write_confirm_cb` | `file_write_confirm_cb` | TI_11_10 写文件确认 |
|
||
| `icp67_set_file_write_end_cb` | `file_write_end_cb` | TI_11_10 写文件结束 |
|
||
|
||
**ZIP 类 (2 个):**
|
||
| 函数 | 注册回调 | 触发场景 |
|
||
|---|---|---|
|
||
| `icp67_set_zip_dir_pop_out_cb` | `zip_dir_pop_out_cb` | TI_30_1 录波目录 |
|
||
| `icp67_set_zip_file_pop_out_cb` | `zip_file_pop_out_cb` | TI_30_3 录波文件内容 |
|
||
|
||
**数据传输类 (6 个):**
|
||
| 函数 | 注册回调 | 触发场景 |
|
||
|---|---|---|
|
||
| `icp67_set_mx_trans_cb` | `mx_trans_cb` | TI_200 遥测传输 |
|
||
| `icp67_set_st_trans_cb` | `st_trans_cb` | TI_201 遥信传输 |
|
||
| `icp67_set_dd_trans_cb` | `dd_trans_cb` | TI_202 电度传输 |
|
||
| `icp67_set_soe_trans_cb` | `soe_trans_cb` | TI_204 SOE 事件 |
|
||
| `icp67_set_fault_trans_cb` | `fault_trans_cb` | TI_205 故障事件 |
|
||
| `icp67_set_mx_change_cb` | `mx_change_cb` | TI_207 遥测扰动 |
|
||
|
||
### 帧操作 (内部 API)
|
||
|
||
```c
|
||
int icp67_search_frame(uint8_t *p_data, uint16_t len, uint16_t *p_pos, uint16_t *p_valid_len);
|
||
int icp67_decode(stru_icp67 *p_icp67, uint8_t *p_data, uint16_t len);
|
||
void icp67_timer_handler(stru_icp67 *p_icp67);
|
||
void icp67_make_head(uint8_t dst, uint8_t ti, uint8_t cot, uint16_t inf, uint8_t *p_tx, uint16_t *tx_len);
|
||
void icp67_make_tail(uint8_t *p_tx, uint16_t *tx_len);
|
||
void icp67_show_frame(uint8_t *p_data, uint16_t len);
|
||
```
|
||
|
||
### 重传管理 (线程安全)
|
||
|
||
```c
|
||
void icp67_rtx_flag_set(stru_icp67 *p_icp67, uint8_t flag, uint8_t resend_cnt, uint32_t tm_cnt);
|
||
void icp67_rtx_flag_get(stru_icp67 *p_icp67, uint8_t *flag, uint8_t *resend_cnt, uint32_t *tm_cnt);
|
||
void icp67_rtx_resend_inc(stru_icp67 *p_icp67);
|
||
void icp67_rtx_tm_inc(stru_icp67 *p_icp67);
|
||
```
|
||
|
||
所有读写操作通过 `rtx_sem` 信号量保护。
|
||
|
||
## 核心数据结构
|
||
|
||
### stru_icp67 — 协议实例
|
||
|
||
```c
|
||
typedef struct _icp67 {
|
||
void *arg; // 用户数据 (interface ID)
|
||
icp67_send_cb send_cb; // 发送回调 (→物理层)
|
||
icp67_search_frame_cb search_frame_cb; // 帧搜索
|
||
icp67_decode_cb decode_cb; // 解码
|
||
icp67_timer_handler_cb timer_handler_cb; // 定时器
|
||
stru_genneral_method method; // 回调表 (实例化)
|
||
uint8_t md5[16]; // AO参数MD5
|
||
uint8_t tx[2048]; // 发送缓冲区
|
||
uint8_t resend_tx[2048]; // 重发备份
|
||
uint8_t rtx_flag; // 重传标志
|
||
sem_t rtx_sem; // 重传信号量
|
||
uint8_t resend_cnt; // 重发计数
|
||
uint32_t tm_out; // 超时阈值
|
||
uint32_t tm_cnt; // 当前tick
|
||
} icp67;
|
||
```
|
||
|
||
### stru_genneral_method — 回调表 (29 个函数指针)
|
||
|
||
- **Sender 组** (13个): 方向 self_ptl → icp67 → 物理层
|
||
- ao_get, ao_set, iec_point_tbl_get, time_set, self_check_get
|
||
- upgrade_start, dir_read, file_read_act, file_write_act
|
||
- file_write, file_read_confirm, file_write_end_confirm
|
||
|
||
- **Receiver 组** (16个): 方向 物理层 → icp67 → self_ptl
|
||
- ao_pop_out, iec_point_tbl_pop_out, self_check_pop_out
|
||
- dir_pop_out, file_read_act_confirm, file_read
|
||
- file_write_act_confirm, file_write_confirm, file_write_end
|
||
- zip_dir_pop_out, zip_file_pop_out
|
||
- mx_trans, st_trans, dd_trans, soe_trans, fault_trans, mx_change
|
||
|
||
## TI 消息类型清单
|
||
|
||
| TI | 名称 | COT | 方向 | 说明 |
|
||
|:--:|---|---|---|---|
|
||
| 1 | 读独立编码参数 | 5→7 | 请求→响应 | 带 MD5 校验的参数读取 |
|
||
| 2 | 写独立编码参数 | 6→7 | 激活→确认 | 参数写入,含 MD5 |
|
||
| 3 | 读组合编码参数 | 5→7 | 请求→响应 | 点表/校准数据,支持分帧 |
|
||
| 4 | 写组合编码参数 | - | - | 空桩 (未使用) |
|
||
| 5 | 对时 | 5→7 | 请求→响应 | 终端时间同步 |
|
||
| 6 | 自检读取 | 5→7 | 请求→响应 | 终端状态/版本号 |
|
||
| 9 | 升级 | 5→7 | 请求→响应 | 固件升级 |
|
||
| 11 | 文件操作 | 5→7 | 请求→响应 | 目录/文件读写, 9 种子命令 |
|
||
| 30 | 录波文件 | 5 | 上行 | ZIP 波形目录/文件传输 |
|
||
| 200 | 遥测传输 | 6 | 上行 | 采样板→主板 MX |
|
||
| 201 | 遥信传输 | 6 | 上行 | 采样板→主板 ST |
|
||
| 202 | 计量传输 | 6 | 上行 | 采样板→主板 DD |
|
||
| 203 | 遥控交互 | - | - | 空桩 (未使用) |
|
||
| 204 | SOE 传输 | 3 | 上行 | 事件顺序记录 |
|
||
| 205 | 故障传输 | 3 | 上行 | 故障事件 (含 ST+MX) |
|
||
| 207 | 扰动传输 | 3 | 上行 | 遥测扰动数据 |
|
||
|
||
## 与原工程的差异
|
||
|
||
| 维度 | 原 RTU (icp67.cpp) | 重构后 (纯C 4文件) |
|
||
|---|---|---|
|
||
| 回调表 | 全局 `g_genneral_method` | 实例化 `p_icp67->method` |
|
||
| TI 映射 | C++ `std::map` | C 数组 + 线性查表 |
|
||
| 故障解码 | `std::vector` | 固定数组 `[MAX_FAULT_*_INFO]` |
|
||
| 文件格式 | `icp67.cpp` (1429行) + `general_method.cpp` (88行) | 4 文件: frame(126) + decode(1024) + core(645) + header(~830) |
|
||
| 语言 | C++ | 纯 C (extern "C" 接口) |
|
||
| 常量 | 魔数 | 命名 `#define` (ICP67_TX_BUF_SIZE 等) |
|
||
| 空桩 | TI_4/TI_203 空 | LOG_E + RX_FLAG 设置 |
|
||
|
||
## 完成状态
|
||
|
||
### ✅ 已完成
|
||
|
||
- [x] 帧定界/校验 (search_frame, CRC)
|
||
- [x] 16 种 TI 解码 (含文件/ZIP 子命令)
|
||
- [x] 13 个 Sender 函数 (组帧发送)
|
||
- [x] 17 个回调 setter (实例化注册)
|
||
- [x] 重传管理 (4 函数, sem 线程安全)
|
||
- [x] 定时器处理 (10ms tick, 5 次重试)
|
||
- [x] TI_4/TI_203 空桩填充
|
||
- [x] 多实例隔离 (回调表实例化)
|
||
- [x] 魔数 → 命名常量
|
||
- [x] C 数组查表替代 std::map
|
||
- [x] 单元测试 (21 项全通过)
|
||
|
||
### ⬜ 未完成
|
||
|
||
- [ ] TI_203 遥控交互实现 (FTU 固件不支持, 暂缓)
|
||
|
||
> 以下两项因 FTU 固件不可改动, 已删除:
|
||
> ~~CRC 升级 (需 FTU 同步改帧格式)~~
|
||
> ~~帧序号/去重 (需 FTU 同步改帧格式)~~
|
||
|
||
## 新增功能 (2026-07-09)
|
||
|
||
### 可配置超时/重试
|
||
|
||
```c
|
||
// 旧
|
||
void icp67_init(stru_icp67 *p, icp67_send_cb cb, void *arg);
|
||
|
||
// 新: 传 0 使用默认值 (1000ms, 5次)
|
||
void icp67_init(stru_icp67 *p, icp67_send_cb cb, void *arg,
|
||
uint32_t tm_out_ms, uint8_t resend_max);
|
||
|
||
// 示例
|
||
icp67_init(&icp, send_cb, &iface, 2000, 3); // 串口慢速: 2s超时, 重试3次
|
||
icp67_init(&icp, send_cb, &iface, 0, 0); // 使用默认值
|
||
```
|
||
|
||
`stru_icp67` 新增字段: `resend_max`, `tm_out_ms`。
|
||
|
||
### 零拷贝环形缓冲区搜索
|
||
|
||
```c
|
||
int icp67_search_frame_in_ring(const uint8_t *buf, uint16_t size,
|
||
uint16_t rptr, uint16_t cnt,
|
||
uint16_t *p_pos, uint16_t *p_valid_len);
|
||
```
|
||
|
||
直接从环形缓冲区读指针扫描帧, 省去 `get_rx_data()` 的 2048 字节 `memcpy`。
|
||
`self_ptl` 已接入, 仅 IEC-104 检测保留一次全量拷贝。
|
||
|
||
## 测试覆盖
|
||
|
||
| 测试项 | 覆盖内容 | 状态 |
|
||
|---|---|---|
|
||
| T1 帧构造 | make_head + make_tail | ✅ |
|
||
| T2 帧搜索 | 正常帧、无效帧头、NULL参数 | ✅ |
|
||
| T3 CRC | 基本累加、溢出回绕 | ✅ |
|
||
| T4 初始化 | 绑定回调、信号量、超时参数 | ✅ |
|
||
| T5 重传管理 | set/get、resend_inc | ✅ |
|
||
| T6 回调注册 | 17 个 setter 全路径 | ✅ |
|
||
| T7 解码无效 | NULL参数 | ✅ |
|
||
| T8 定时器 | NULL、空闲态 | ✅ |
|
||
| T9 MD5 | 正常设置、NULL参数 | ✅ |
|
||
| T10 未知 TI | 0xFF 帧 | ✅ |
|
||
| T11 Sender | self_check_get、iec_point_tbl_get、NULL安全 | ✅ |
|
||
| T12 解码 TI | TI_1 MD5 匹配、TI_4 空桩 | ✅ |
|
||
| T13 多实例 | 实例间回调隔离 | ✅ |
|