RTU_ALL_AI/docs/protocol/libicp67/API-libicp67.md

272 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

# 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 多实例 | 实例间回调隔离 | ✅ |