RTU/mimo/工程/libicp67模块分析.md

8.4 KiB
Raw Permalink Blame History

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