# libicp67 协议库分析文档 ## 1. 模块概述 **路径**: `src/protocol/libicp67/` **定位**: ICP67 二进制协议库——用于维护软件与 RTU/FTU 自侧之间、以及主板与采样板之间通信的私有协议引擎。负责帧定界、编解码、重传管理。 **文件结构**: ``` src/protocol/libicp67/ ├── inc/ │ ├── icp67.h -- TI/COT 常量、帧 ASDU 结构体 │ └── general_method.h -- icp67_get_genneral_method() 前向声明 └── src/ ├── icp67.cpp -- 核心:搜帧、解码分发、初始化、组帧、重传 └── general_method.cpp -- 回调 setter、默认 sender 实现 release/inc/myIcp67.h -- 共享契约:stru_head、stru_icp67、stru_genneral_method ``` ## 2. 协议帧格式 ``` | 0x67 | len(2B) | 0x67 | src | dst | dir | ti | cot | info_addr(2B) | dev_addr(4B) | data... | CRC(1B) | 0x16 | | 帧头1 | 长度 | 帧头2 | 源 | 目的 | 方向 |类型| 传送原因 | 信息体地址 | 设备地址 | 数据体 | 校验和 | 帧尾 | ``` - **TI (Type Identifier)**: 16 种消息类型 (TI_1 ~ TI_207) - **COT (Cause of Transmission)**: 请求(5)、激活(6)、正常确认(7)、终端响应(8)、MD5不匹配(20) - **校验**: 8-bit 累加校验和 (data 部分) - **设备地址**: src/dst 取值:1=维护软件, 2=主板, 3=采样板, 4=LCD, 5=XTU ## 3. 核心数据结构 ### 3.1 `stru_head` (1字节对齐) 帧头结构,包含所有固定头部字段 + 可变数据区。`len` 字段为 `info_addr` + `dev_addr` + `data[...]` 的总长度。 ### 3.2 `stru_icp67` —— 协议实例 | 字段 | 用途 | |------|------| | `send_cb` / `search_frame_cb` / `decode_cb` / `timer_handler_cb` | 四个对外回调 | | `tx[2048]` / `tx_len` | 发送缓冲区 | | `resend_tx[2048]` / `resend_tx_len` | 重发备份缓冲区 | | `rtx_flag` / `rtx_sem` / `resend_cnt` / `tm_out` / `tm_cnt` | 重传状态机 | | `md5[16]` | AO 参数 MD5 校验值 | | `arg` | 用户数据 (libself_ptl 传入 interface ID) | ### 3.3 `stru_genneral_method` —— 全局回调表 26 个函数指针,分为两组: - **sender 组** (13个): ao_get, ao_set, iec_point_tbl_get, time_set, self_check_get, upgrade_start, dir_read, file_read_act/confirm, file_write_act/write/write_end_confirm - **receiver 组** (13个): 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 ## 4. 核心函数 ### 4.1 `icp67_init(p, send_cb, arg)` 绑定 `send_cb`、`search_frame_cb`(→icp67_search_frame)、`decode_cb`(→icp67_decode)、`timer_handler_cb`(→icp67_timer_handler)。初始化重传信号量、TM 周期(100)、重发计数(0)。 ### 4.2 `icp67_search_frame(p_rx, rx_len, p_pos, p_len)` 帧定界器:扫描 `0x67 0x67`,验证长度边界、CRC 校验和、帧尾 `0x16`。返回帧位置和有效长度。 ### 4.3 `icp67_decode(p_icp67, p_data, len)` 顶层解码分发器:从帧头读取 `ti`,查 `g_map_ti_decode` 映射表,调用对应的 `icp67_decode_ti_{N}` 函数。 ### 4.4 `icp67_timer_handler(p_icp67)` 重传定时器:TX_FLAG 状态下,每 tick `tm_cnt++`,达到 `tm_out`(100 tick ≈ 1000ms) 且重发次数 < 5 时,重新发送 `resend_tx`。超过 5 次清除 TX_FLAG。 ### 4.5 各 TI 解码函数 - TI_1: 读参数响应 → `ao_pop_out_cb` - TI_2: 写参数响应 (无数据,仅确认) - TI_3: 读 IEC 点表响应 (支持分帧) → `iec_point_tbl_pop_out_cb` - TI_4: 写 IEC 点表响应 (空桩) - TI_5: 对时响应 - TI_6: 自检响应 → `self_check_pop_out_cb` - TI_9: 升级响应 - TI_11: 文件操作分发 (9种子命令) - TI_30: ZIP 波形分发 (4种子命令) - TI_200: MX 遥测传输 → `mx_trans_cb` - TI_201: ST 遥信传输 → `st_trans_cb` - TI_202: DD 电度传输 → `dd_trans_cb` - TI_203: 遥控 (空桩) - TI_204: SOE 事件 → `soe_trans_cb` - TI_205: 故障事件 → `fault_trans_cb` - TI_207: 测量扰动 → `mx_change_cb` ## 5. 与 libself_ptl 的交互 ``` ┌─── icp67_init() 初始化 ───┐ │ │ libself_ptl ◄────► g_genneral_method (26回调) ◄────► libicp67 (应用层) ├─ sender: self → icp67 │ (协议引擎) └─ receiver: icp67 → self │ │ self_ptl_data_rx ──► search_frame_cb ──► decode_cb ──► method回调 ──► 数据中心 self_ptl_data_tx ◄── send_cb ◄── frame builder (sender函数) ``` ## 6. 优缺点分析 ### 6.1 优点 | # | 优点 | 说明 | |---|------|------| | A1 | **协议与业务分离** | 通过回调表实现清晰的分层架构,ICP67 库不依赖任何上层模块 | | A2 | **TI-COT 状态机完整** | 覆盖 16 种消息类型,支持请求/确认/响应等完整交互模式 | | A3 | **重传机制** | 内置超时重传 (5次×1s),保障可靠通信 | | A4 | **分帧支持** | TI_3 (IEC 点表) 支持多帧数据传输,带帧序号和续传标志 | | A5 | **帧格式校验完善** | 帧头 0x67×2 + 长度验证 + CRC + 帧尾 0x16,五重保障 | | A6 | **MD5 参数校验** | 参数读写带 MD5 校验,防止配置不匹配 | ### 6.2 缺点 | # | 缺点 | 严重程度 | 说明 | |---|------|----------|------| | D1 | **全局单例回调表** | 🔴 高 | `g_genneral_method` 是全局变量,多实例场景下无法使用。`stru_icp67` 虽有实例字段但 decode 回调全部走全局表 | | D2 | **空桩函数** | 🟡 中 | TI_4 (写IEC点表响应)、TI_203 (遥控) 等为空实现,功能不完整 | | D3 | **重传设计粗糙** | 🟡 中 | 只备份最后一条发送帧,并发场景下只维护一个序列号;重传期间收到响应会误清 flag | | D4 | **CRC 校验弱** | 🟡 中 | 8-bit 累加和,容易碰撞,不适合工业环境的长帧 | | D5 | **回调设置侵入式** | 🟡 中 | sender 回调的 setter 通过写函数指针体覆盖默认实现,对 callback 未设置的路径静默失败 | | D6 | **硬编码参数** | 🟢 低 | 缓冲区 2048、重试 5 次、超时 1000ms、设备地址硬编码,缺乏配置入口 | | D7 | **无帧序号/去重** | 🟢 低 | 无应用层帧序号,重复帧/乱序帧无法检测 | | D8 | **C 风格内存操作** | 🟢 低 | 大量 `memcpy`、裸指针运算、默认结构体 pack(1) 对齐,跨平台移植有隐患 | ## 7. 改进方向与措施 ### 7.1 架构改进 | 方向 | 措施 | 优先级 | |------|------|--------| | **去全局化** | 将 `g_genneral_method` 移入 `stru_icp67`,decode 函数通过 `p_icp67->method.xxx_cb` 回调;setter 改为 `icp67_set_xxx(p_icp67, cb)` | 🔴 高 | | **状态机重构** | 引入独立的类/结构体管理多路请求状态 (request_id + state + timer),替代当前单一的 `rtx_flag/resend_cnt/tm_cnt` 三元组 | 🟡 中 | ### 7.2 协议增强 | 方向 | 措施 | 优先级 | |------|------|--------| | **CRC 升级** | 8-bit 累加和替换为 CRC-16/CRC-CCITT,向后兼容可保留 CRC8 但增加 CRC16 字段 | 🟡 中 | | **帧序号** | 在帧头增加 1 字节 frame_seq,发送端递增,接收端检测重复和丢帧 | 🟢 低 | | **填充空桩** | 实现 TI_4、TI_203 的空桩函数,至少返回 NOT_SUPPORTED 确认 | 🟢 低 | | **可配置参数** | 超时、重试次数、缓冲区大小通过 `icp67_init` 参数传入,存储在 `stru_icp67` 中 | 🟢 低 | ### 7.3 代码质量 | 方向 | 措施 | 优先级 | |------|------|--------| | **消除硬编码** | 用 `#define`/`constexpr` 替代魔数 (0x67, 0x16, 2048, 5, 100 等),统一放到 `icp67.h` | 🟡 中 | | **回调返回值** | sender 回调统一返回 `int` (0=成功),decode 回调失败时返回错误码给应用层,替代静默吞错 | 🟡 中 | | **打包兼容** | `pack(1)` 改为显式 `uint8_t` 数组 + 序列化/反序列化函数,消除跨平台对齐风险 | 🟢 低 | ## 8. 总结 `libicp67` 是一个功能完整但架构老旧的嵌入式协议库。核心编解码和重传逻辑是可靠的,主要问题集中在**全局单例耦合**和**可配置性不足**两个层面。最高优先级的改进是将全局回调表实例化,使其从"单设备绑定"升级为"多实例可复用"的协议引擎,从而支撑多通道场景(主板与多采样板同时通信)。