274 lines
9.5 KiB
Markdown
274 lines
9.5 KiB
Markdown
# 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 启动 pthread,libiec61850m 做桥接 | libmms_s 启动 pthread,libiec61850s 做桥接 |
|
||
| **桥接层复杂度** | 复杂:RCB 发现/匹配/订阅/GI 触发/重连 | 相对简单:注册信号+回调,libmms_s 处理细节 |
|
||
| **配置方式** | mms_m.xml(IED 连接参数) | 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)` | 打印配置内容(调试) |
|