12 KiB
12 KiB
libself_ptl 自协议层分析文档
1. 模块概述
路径: src/system/libself_ptl/
定位: 自协议层——RTU/FTU 系统中连接 ICP67 协议库与数据中心的上层集成模块。负责协议初始化、双协议帧解复用 (ICP67 + IEC-104)、信号注册、数据收发调度。
文件结构:
src/system/libself_ptl/
├── inc/
│ ├── self_ptl.h -- 帧类型定义、配置数据结构
│ └── method.h -- 回调函数声明、局部辅助类型
└── src/
├── self_ptl.cpp -- 核心:初始化、帧处理、信号注册、任务主循环
└── method.cpp -- 回调实现:AO、IEC点表、文件操作、SOE/故障/MX/ST/DD 传输处理
src/system/RTU/src/self_ptl_cfg.cpp -- XML 配置解析
test/config/SELF_PTL/self_ptl.xml -- 测试用 XML 配置 (790+ 信号)
2. 核心数据结构
2.1 stru_self_rx —— 环形缓冲区
typedef struct {
uint16_t rptr; // 读指针
uint16_t wptr; // 写指针
uint16_t size; // 缓冲区大小 (2048)
uint16_t cnt; // 有效数据计数
uint8_t buf[2048]; // 数据缓冲区
} stru_self_rx;
用于原始字节流的 FIFO 缓冲,支持循环覆盖写。
2.2 stru_self_ptl —— 全局单例
typedef struct {
uint32_t interface; // 通信通道 ID
stru_icp67 icp67; // 嵌入的 ICP67 协议实例
stru_genneral_method method; // 回调表本地副本
stru_self_rx icp67_rx; // ICP67 原始帧接收缓冲
stru_self_rx iec_rx; // IEC-104 原始帧接收缓冲
uint8_t temp_buf[2048]; // 临时缓冲区
} stru_self_ptl;
2.3 配置数据结构 (self_ptl.h)
| 结构 | 用途 |
|---|---|
stru_self_ptl_cfg_base_data |
运行时绑定: p_base(配置) → p_data(值) → p_ctrl(SBO控制) |
stru_self_ptl_cfg_param_data |
AO/Param 绑定: 配置 + SBO + 值向量 + 默认值向量 |
stru_self_ptl_cfg_data |
完整配置容器: st_vec, mx_vec, co_vec, dd_vec, ao_vec, param_vec |
2.4 局部辅助类型 (method.h)
| 结构 | 用途 |
|---|---|
stru_tlv |
TLV 编码: type + len + val[64] |
stru_dir_info |
文件目录项: name[256], len, attr, size |
stru_local_ao_get |
AO 读取分帧状态: rx_cnt, tx_cnt, per_cnt, offset |
stru_local_file |
文件操作上下文: sub_cmd, temp_str, dir_name, file_name |
stru_local_zip_info |
ZIP 传输状态: zip_type, zip_name, file_num, file_info[2] |
3. 核心流程
3.1 初始化流程
app_self_ptl_init1()
├── self_ptl_init()
│ ├── icp67_init(&g_self_ptl.icp67, self_ptl_data_tx, &interface)
│ ├── icp67_set_ao_cfg_md5() // 加载 AO 参数 MD5
│ ├── 方向A: icp67_set_{sender}_cb() × 13 → g_genneral_method.sender
│ └── 方向B: icp67_set_{receiver}_cb() × 13 → g_genneral_method.receiver
├── self_ptl_cfg_init() // 解析 XML 配置
└── self_ptl_do_signal_out()
├── dc_signal_out() × ST数量 // 注册遥信信号
├── dc_signal_out() × MX数量 // 注册遥测信号
├── dc_signal_yk() × CO数量 // 注册遥控信号
├── dc_signal_out() × DD数量 // 注册电度信号
├── dc_signal_ao() × AO数量 // 注册参数信号
└── dc_signal_param() × Param数量 // 注册定值信号
3.2 数据接收流程
app_self_ptl (RTOS 任务)
├── wait(EV_SELF_PTL_RX_COM)
└── self_ptl_data_rx(p_data)
├── 解析 stru_msg_head (interface + len)
└── self_ptl_search_frame(p_rx, len, p_icp67)
├── put_rx_data() → icp67_rx 环形缓冲
├── put_rx_data() → iec_rx 环形缓冲
├── do while:
│ ├── icp67.search_frame_cb() → icp67_search_frame()
│ │ └── 搜到帧 → icp67.decode_cb() → g_genneral_method.*_cb → 数据中心
│ └── self_ptl_search_iec_frame()
│ └── 搜到 IEC-104 帧 → msg_send() → COM 层
└── 滑窗: 第一个 0x66/0x68 之前的数据丢弃
3.3 IEC-104 帧处理
self_ptl_search_iec_frame() 负责识别 IEC-104 帧:
- 检查 APCI 起始字节
0x68 - 检查 ASDU 完整性 (非零 TI, 非零 VSQ, 有效 COT)
- 分类为 I 帧 (编号数据传输)、S 帧 (编号确认)、U 帧 (控制功能) 或 ERR_FRAME
- 有效帧转发到 COM 消息队列
MQ_SELF_PTL_TO_COM
3.4 数据发送流程
(应用层触发)
└── self_ptl_signal_change_callback()
└── g_self_ptl.method.ao_set_cb(p_icp67, data, cnt)
└── icp67_ao_set() → 构建 TI_2 帧
└── p_icp67->send_cb() → self_ptl_data_tx()
└── 包装 stru_msg_head → msg_send(MQ_SELF_PTL_TO_COM)
3.5 定时任务
app_self_ptl (RTOS 任务)
├── wait(EV_TIMER1/2/3)
├── self_ptl_task() // 调用 icp67.timer_handler_cb → 重传
└── self_ptl_method_task() // 测试命令驱动 (g_flag[])
├── test_ao_get() → 读取 AO 参数
├── test_iec_point_tbl_get() → 读取 IEC 点表
├── test_time_set() → 对时
├── test_self_check() → 自检
├── test_upgrade() → 升级
└── test_file() → 文件操作 (目录/读写)
4. 双协议共信道机制
libself_ptl 的核心设计之一是同一物理通道同时承载 ICP67 和 IEC-104 两种协议帧:
┌─── icp67_rx (环形缓冲) ──► icp67_search_frame ──► ICP67 decode
/
raw bytes → self_ptl_search_frame
\
└─── iec_rx (环形缓冲) ──► self_ptl_search_iec_frame ──► COM 转发
- 两种协议的帧被同时写入各自的环形缓冲区
- 帧搜索交替进行:先搜 ICP67 帧,再搜 IEC-104 帧
- IEC 帧不经过 ICP67 decode,直接转发到 IEC-104 协议栈所在的通信模块
- 各自缓冲区独立滑窗,互不干扰
5. 与 libicp67 的耦合关系
g_self_ptl.icp67 ←── icp67_init() ──→ icp67 协议实例
├─ send_cb → self_ptl_data_tx()
├─ search_frame_cb → icp67_search_frame()
├─ decode_cb → icp67_decode()
└─ timer_handler_cb → icp67_timer_handler()
g_genneral_method ←───────────────── icp67_set_*_cb() × 26
├─ [sender 组] method { ao_get, ao_set, iec_point_tbl_get, ... }
└─ [receiver 组] method { mx_trans, st_trans, soe_trans, fault_trans, ... }
6. 优缺点分析
6.1 优点
| # | 优点 | 说明 |
|---|---|---|
| A1 | 双协议解复用 | 在同一物理通道上同时承载 ICP67 和 IEC-104,通过双环形缓冲区实现协议帧分离 |
| A2 | 回调式架构 | 完整的回调驱动模型,协议解码与业务处理解耦,新增 TI 类型只需实现回调 |
| A3 | 配置驱动 | 信号表通过 XML 加载,无需修改代码即可调整遥信/遥测/遥控/电度/参数/定值 |
| A4 | SBO (选择-执行)控制 | 遥控和参数写入支持 select-before-operate 安全机制 |
| A5 | 文件传输支持 | 完整的 ZIP 波形和通用文件传输流程 (目录→读取/写入→确认→MD5校验) |
| A6 | AO 参数 MD5 | 读取回参数时 MD5 校验防配置不一致 |
| A7 | RTOS 任务模型 | 事件驱动的事件+定时器双驱任务,低 CPU 占用 |
6.2 缺点
| # | 缺点 | 严重程度 | 说明 |
|---|---|---|---|
| D1 | 全局单例 g_genneral_method |
🔴 高 | ICP67 的 decode 回调全部走全局表,导致多 ICP67 实例 (如主板同时连接多个采样板) 无法独立工作,所有实例的回调被最后一组 setter 覆盖 |
| D2 | 环形缓冲无锁保护 | 🔴 高 | stru_self_rx 在 ISR (生产者) 和 task (消费者) 间共享,无临界区保护,存在竞态风险 |
| D3 | 缓冲区溢出处理粗暴 | 🟡 中 | 环形缓冲溢出时仅输出 LOG,数据被静默丢弃且无计量统计 |
| D4 | 测试命令耦合 | 🟡 中 | self_ptl_method_task() 中硬编码了测试命令序列 (ao_get → iec_point_tbl_get → time_set → self_check → upgrade → file),生产代码中混入调试逻辑 |
| D5 | IEC-104 帧判断粗糙 | 🟡 中 | self_ptl_search_iec_frame() 仅检查 0x68 起始 + 简单字段完整性,对 APCI 控制域未做完整的 I/S/U 帧状态管理 |
| D6 | 硬编码参数 | 🟢 低 | 环形缓冲大小(2048)、临时缓冲区(2048)硬编码,无配置入口 |
| D7 | 双缓冲复制冗余 | 🟢 低 | 每次搜帧先 get_rx_data() 拷贝整个缓冲区,再 memcpy 帧数据到 temp_buf,存在双重拷贝开销 |
| D8 | 错误处理不足 | 🟢 低 | 多数回调失败仅 LOG,不向上层返回错误状态;SBO 控制无超时自动取消 |
| D9 | 滑窗丢弃策略 | 🟢 低 | icp66_search_first_head() 丢弃帧头前的所有字节,若两帧间误入一个 0 字节会把整帧丢失 |
7. 改进方向与措施
7.1 架构改进
| 方向 | 措施 | 优先级 |
|---|---|---|
| 回调实例化 | 将 g_genneral_method 移入 stru_icp67,decode/send 回调从实例字段读取,使协议库支持多实例 |
🔴 高 |
| 生产/调试分离 | 将 test_ao_get 等测试命令通过编译宏 (#ifdef SELF_PTL_TEST) 隔离,或统一作为外部命令接口由 DC 触发 |
🔴 高 |
| 临界区保护 | 环形缓冲的 put/get/cnt 操作增加关中断/信号量保护,防止 ISR 与 task 竞态 | 🔴 高 |
7.2 协议处理增强
| 方向 | 措施 | 优先级 |
|---|---|---|
| IEC-104 状态机 | 完善 I/S/U 帧的发送序列号和接收序列号管理,支持 APCI 层面的确认和窗口控制 | 🟡 中 |
| 缓冲区计量 | 增加 overflow_cnt、discard_bytes 统计字段,定期上报到数据中心 |
🟢 低 |
| 零拷贝优化 | get_rx_data 后的全量拷贝改为直接操作环形缓冲区的指针范围,减少一次拷贝 |
🟢 低 |
7.3 可靠性增强
| 方向 | 措施 | 优先级 |
|---|---|---|
| SBO 超时 | SBO 选择后增加看门狗定时器 (如 30s),超时自动发送取消命令 | 🟡 中 |
| 错误码传递 | 所有回调统一返回 int 错误码,self_ptl_data_rx 统计成功/失败帧数并上报 |
🟡 中 |
| 滑动窗口改进 | icp66_search_first_head 改为仅跳过连续非帧头数据,保留帧头前至少 1 字节作为下一帧的候选起始 |
🟢 低 |
7.4 可维护性
| 方向 | 措施 | 优先级 |
|---|---|---|
| 常量集中管理 | 缓冲区大小、最大帧长、超时值统一为模块内 constexpr |
🟢 低 |
| XML 配置解耦 | self_ptl_cfg_init() 中的 XML 类型判断逻辑改用策略模式/注册表,避免硬编码类型分支 |
🟢 低 |
8. 总结
libself_ptl 作为 RTU/FTU 系统的协议集成层,承担了双协议解复用、信号注册分发、回调桥接三大核心职责。当前实现功能完备,但在线程安全和多实例扩展方面存在明显短板。最高优先级的三项改进——回调实例化、生产/调试分离、临界区保护——可以解决当前架构中最关键的可靠性和可扩展性问题。