8.4 KiB
8.4 KiB
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 是一个功能完整但架构老旧的嵌入式协议库。核心编解码和重传逻辑是可靠的,主要问题集中在全局单例耦合和可配置性不足两个层面。最高优先级的改进是将全局回调表实例化,使其从"单设备绑定"升级为"多实例可复用"的协议引擎,从而支撑多通道场景(主板与多采样板同时通信)。