RTU/claude/工程/libiec61850s模块分析.md

274 lines
9.5 KiB
Markdown
Raw 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.

# libiec61850s 模块分析
**日期**2026-06-10
---
## 1. 模块概览
`libiec61850s` 是 IEC 61850 MMS 服务端的应用线程模块(`app_iec61850s`),属于系统层的第 9 号线程。它作为桥接层,连接三个子系统:
```
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ DataCenter │ ←→ │ libiec61850s │ ←→ │ libmms_s │
│ (信号数据中心) │ │ (应用线程/桥接层) │ │ (MMS 服务端封装) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
┌──────────────────┐
│ mms_s.xml 配置 │
└──────────────────┘
```
### 目录结构
```
src/system/libiec61850s/
├── inc/
│ ├── iec61850s.h # 头文件(包含 mySystem/myMms_s 等)
│ └── parse_xml.h # XML 配置解析类型 + 接口
└── src/
├── iec61850s.cpp # 应用线程主逻辑(~487 行)
└── parse_xml.cpp # mms_s.xml 解析(~129 行)
```
---
## 2. 应用线程模型
与所有 app 线程一致libiec61850s 遵循 `init1 → init2 → fun_cb` 三段式:
| 阶段 | 函数 | 主要工作 |
|------|------|---------|
| init1 | `app_iec61850s_init1()` | 解析配置、注册回调 |
| init2 | `app_iec61850s_init2()` | 初始化信号、启动 MMS 服务器 |
| fun_cb | `app_iec61850s()` | 主循环3 定时器,当前空闲) |
### 2.1 init1 流程([iec61850s.cpp:392](src/system/libiec61850s/src/iec61850s.cpp#L392)
```
1. get_base_path() → 获取进程目录
2. parse_mms_xml("config/MMS/mms_s.xml") → 解析 XML 配置
3. mms_s_dbg_switch(false) → 关闭调试
4. mms_s_file_path_set(base_path) → 设置文件根目录
5. mms_s_value_update_register(&cb) → 注册值更新回调
```
### 2.2 init2 流程([iec61850s.cpp:421](src/system/libiec61850s/src/iec61850s.cpp#L421)
```
1. iec61850s_signals_init() → 初始化五类信号
2. mms_s_init("config/MMS/PCS.icd", 102) → 启动 MMS 服务器
```
### 2.3 主循环([iec61850s.cpp:446](src/system/libiec61850s/src/iec61850s.cpp#L446)
三个定时器事件(`EV_TIMER1/2/3`)当前均为空闲,仅 `EV_TIMER3``run_cnt++`。信号驱动的工作全部通过 libmms_s 的内部线程和 DataCenter 回调完成——本线程主要作为容器,维护 MMS 服务器的生命周期。
---
## 3. 配置解析子系统([parse_xml.cpp](src/system/libiec61850s/src/parse_xml.cpp)
### 3.1 XML 结构mms_s.xml
```xml
<Config>
<St> <!-- 遥信信号 -->
<Signal link="st.0" />
</St>
<Mx> <!-- 遥测信号 -->
<Signal link="mx.0" />
</Mx>
<Co> <!-- 遥控信号 -->
<Signal link="co.0" />
</Co>
<Ao> <!-- 定值信号 -->
<Signal link="ao.0" />
</Ao>
<Param> <!-- 参数信号 -->
<Signal link="param.0" />
</Param>
</Config>
```
### 3.2 数据结构
```cpp
typedef struct {
std::vector<stru_mms_s_signal_base> vec_st; // 遥信
std::vector<stru_mms_s_signal_base> vec_mx; // 遥测
std::vector<stru_mms_s_signal_base> vec_co; // 遥控
std::vector<stru_mms_s_signal_base> vec_ao; // 定值
std::vector<stru_mms_s_signal_base> vec_param; // 参数
} stru_mms_cfg;
```
每个 Signal 只需要 `link`sAddr属性`type` 和 `ctrl_model` 在后续从 DataCenter 获取。
---
## 4. 五类信号初始化
`iec61850s_signals_init()` 按顺序初始化五类信号([iec61850s.cpp:347](src/system/libiec61850s/src/iec61850s.cpp#L347)
### 4.1 遥信ST初始化
```
for each st signal:
dc_signal_out_link_with_callback(saddr, &p_data, iec61850s_st_mx_change_callback)
```
将 DataCenter 的 `out` 信号与本地指针绑定,注册变化回调 `iec61850s_st_mx_change_callback`
### 4.2 遥测MX初始化
逻辑与 ST 完全一致,共用同一个回调函数。
### 4.3 ST/MX 变化回调
```cpp
iec61850s_st_mx_change_callback(saddr, type, p_data, p_last_data)
dc_get_signal_val(p_data, type) 获取当前值字符串
g_mms_s_value_update_cb(saddr, val) 更新 MMS 模型中的 DA
```
数据流向:**DataCenter 信号变化 → 回调 → libmms_s::mms_s_value_update() → IedServer 模型更新**
### 4.4 遥控CO初始化
```
for each co signal:
dc_get_yk_signal_info(saddr, desc, type, ctrl_model, &p_data)
mms_s_control_register(&g_vec_control, iec61850s_control_callback)
```
控制执行时,`mms_s_control.cpp` 的 `control_handler()` 会触发 `iec61850s_control_callback()`,根据 `ctrl_model` 调用 DataCenter 的 `dc_signal_yk_set_status()`
**ctrl_model 映射**
| MMS Control Model | DataCenter 动作 |
|-------------------|----------------|
| DIRECT_NORMAL / DIRECT_ENHANCED | `SIGNAL_CTRL_TYPE::DIRECT_NORMAL``dc_signal_yk_set_status(DIRECT)` |
| SBO_NORMAL / SBO_ENHANCED | `SIGNAL_CTRL_TYPE::SBO_NORMAL` → select + direct 两步 |
| STATUS_ONLY | 仅日志,不操作 |
### 4.5 定值AO初始化
```
for each ao signal:
dc_get_ao_signal_info(saddr, desc, type, null, ctrl_model, &p_data, null)
mms_s_setting_register(&g_vec_setting, iec61850s_setting_callback)
```
客户端写定值时,`mms_s_setting.cpp` 的 `writeAccessHandler()` 校验通过后触发 `iec61850s_setting_callback()`,调用 `dc_signal_ao_set_val()`
### 4.6 参数Param初始化
```
for each param signal:
dc_get_param_signal_info(saddr, desc, type, null, ctrl_model, &p_data_vec, null)
mms_s_param_register(&g_vec_param, iec61850s_param_callback)
```
参数支持多定值区(`p_data[MMS_S_PARAM_MAX]`,最大 16 组)。客户端确认编辑后,`edit_sg_confirmation_handler()` 触发 `iec61850s_param_callback()`,调用 `dc_signal_param_set_val()`,传入 `setting_zone` 索引。
---
## 5. 完整数据流向
### 5.1 上行数据RTU → 客户端)
```
DataCenter 信号变化
→ iec61850s_st_mx_change_callback()
→ g_mms_s_value_update_cb(saddr, val)
→ mms_s_value_update() [mms_s_value.cpp]
→ IedServer_update*AttributeValue()
→ libiec61850 内部触发报告(根据 TrgOps 配置)
→ MMS 报告上送到客户端
```
### 5.2 下行数据(客户端 → RTU
**控制**
```
客户端 Select/Operate
→ libiec61850 → control_handler() [mms_s_control.cpp]
→ iec61850s_control_callback()
→ dc_signal_yk_set_status()
```
**定值**
```
客户端 Write SP
→ libiec61850 → writeAccessHandler() [mms_s_setting.cpp]
→ 值校验(范围/步长)
→ iec61850s_setting_callback()
→ dc_signal_ao_set_val()
```
**参数(定值组)**
```
客户端切换定值组
→ libiec61850 → param_active_sg_changed_handler() [mms_s_param.cpp]
→ param_load_active_sg_values() → 加载 SG DA
客户端编辑确认
→ edit_sg_confirmation_handler() [mms_s_param.cpp]
→ iec61850s_param_callback()
→ dc_signal_param_set_val(setting_zone)
```
---
## 6. 数据结构
### 6.1 本地信号存储
| 类型 | 容器 | 数据结构 |
|------|------|---------|
| 遥信 | `g_vec_st` | `vector<stru_local_st_mx>` — {base, p_data} |
| 遥测 | `g_vec_mx` | `vector<stru_local_st_mx>` — {base, p_data} |
| 遥控 | `g_vec_control` | `vector<stru_mms_s_control>` — {base, p_data} |
| 定值 | `g_vec_setting` | `vector<stru_mms_s_setting>` — {base, p_data} |
| 参数 | `g_vec_param` | `vector<stru_mms_s_param>` — {base, param_num, p_data[]} |
### 6.2 公共信号基类([myMms_s.h](release/inc/myMms_s.h)
```cpp
typedef struct {
char saddr[MMS_S_STR_LEN]; // 短地址(如 "st.0", "mx.5"
char desc[MMS_S_STR_LEN]; // 描述
uint8_t type; // 数据类型DATA_TYPE_*
uint8_t ctrl_model; // 控制模型
} stru_mms_s_signal_base;
```
---
## 7. 与 libmms_m / libiec61850m 的对比
| 维度 | MMS 客户端 (m) | MMS 服务端 (s) |
|------|---------------|---------------|
| **角色** | 主动连接 IED订阅报告 | 被动等待客户端连接 |
| **模型来源** | 从 IED 发现 | ICD 文件预定义 |
| **数据方向** | 先订阅 RCB再接收报告 | 收到控制/写请求后更新 DataCenter |
| **线程模型** | libmms_m 启动 pthreadlibiec61850m 做桥接 | libmms_s 启动 pthreadlibiec61850s 做桥接 |
| **桥接层复杂度** | 复杂RCB 发现/匹配/订阅/GI 触发/重连 | 相对简单:注册信号+回调libmms_s 处理细节 |
| **配置方式** | mms_m.xmlIED 连接参数) | mms_s.xml信号映射+ PCS.icd模型定义 |
---
## 8. 对外接口
| 函数 | 说明 |
|------|------|
| `app_iec61850s_init1(void *arg)` | 第一段初始化:解析 XML、注册值更新回调 |
| `app_iec61850s_init2(void *arg)` | 第二段初始化:初始化信号、启动 MMS 服务器 |
| `app_iec61850s(void *arg)` | 主线程循环 |
| `parse_mms_xml(path)` | 解析 mms_s.xml 配置 |
| `mms_cfg_ptr_get()` | 获取配置数据指针 |
| `show_mms_xml(cfg)` | 打印配置内容(调试) |