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

12 KiB
Raw Blame History

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 帧:

  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_icp67decode/send 回调从实例字段读取,使协议库支持多实例 🔴
生产/调试分离 test_ao_get 等测试命令通过编译宏 (#ifdef SELF_PTL_TEST) 隔离,或统一作为外部命令接口由 DC 触发 🔴
临界区保护 环形缓冲的 put/get/cnt 操作增加关中断/信号量保护,防止 ISR 与 task 竞态 🔴

7.2 协议处理增强

方向 措施 优先级
IEC-104 状态机 完善 I/S/U 帧的发送序列号和接收序列号管理,支持 APCI 层面的确认和窗口控制 🟡
缓冲区计量 增加 overflow_cntdiscard_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 系统的协议集成层,承担了双协议解复用信号注册分发回调桥接三大核心职责。当前实现功能完备,但在线程安全和多实例扩展方面存在明显短板。最高优先级的三项改进——回调实例化、生产/调试分离、临界区保护——可以解决当前架构中最关键的可靠性和可扩展性问题。