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

233 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 系统的协议集成层,承担了**双协议解复用**、**信号注册分发**、**回调桥接**三大核心职责。当前实现功能完备,但在线程安全和多实例扩展方面存在明显短板。最高优先级的三项改进——回调实例化、生产/调试分离、临界区保护——可以解决当前架构中最关键的可靠性和可扩展性问题。