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