10 KiB
10 KiB
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
初始化
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
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)
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);
重传管理 (线程安全)
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 — 协议实例
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 设置 |
完成状态
✅ 已完成
- 帧定界/校验 (search_frame, CRC)
- 16 种 TI 解码 (含文件/ZIP 子命令)
- 13 个 Sender 函数 (组帧发送)
- 17 个回调 setter (实例化注册)
- 重传管理 (4 函数, sem 线程安全)
- 定时器处理 (10ms tick, 5 次重试)
- TI_4/TI_203 空桩填充
- 多实例隔离 (回调表实例化)
- 魔数 → 命名常量
- C 数组查表替代 std::map
- 单元测试 (21 项全通过)
⬜ 未完成
- TI_203 遥控交互实现 (FTU 固件不支持, 暂缓)
以下两项因 FTU 固件不可改动, 已删除:
CRC 升级 (需 FTU 同步改帧格式)帧序号/去重 (需 FTU 同步改帧格式)
新增功能 (2026-07-09)
可配置超时/重试
// 旧
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。
零拷贝环形缓冲区搜索
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 多实例 | 实例间回调隔离 | ✅ |