# 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` —— 环形缓冲区 ```c 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` —— 全局单例 ```c 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 帧: 1. 检查 APCI 起始字节 `0x68` 2. 检查 ASDU 完整性 (非零 TI, 非零 VSQ, 有效 COT) 3. 分类为 I 帧 (编号数据传输)、S 帧 (编号确认)、U 帧 (控制功能) 或 ERR_FRAME 4. 有效帧转发到 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 系统的协议集成层,承担了**双协议解复用**、**信号注册分发**、**回调桥接**三大核心职责。当前实现功能完备,但在线程安全和多实例扩展方面存在明显短板。最高优先级的三项改进——回调实例化、生产/调试分离、临界区保护——可以解决当前架构中最关键的可靠性和可扩展性问题。