# 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
```
### 3.2 数据结构
```cpp
typedef struct {
std::vector vec_st; // 遥信
std::vector vec_mx; // 遥测
std::vector vec_co; // 遥控
std::vector vec_ao; // 定值
std::vector 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` — {base, p_data} |
| 遥测 | `g_vec_mx` | `vector` — {base, p_data} |
| 遥控 | `g_vec_control` | `vector` — {base, p_data} |
| 定值 | `g_vec_setting` | `vector` — {base, p_data} |
| 参数 | `g_vec_param` | `vector` — {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)` | 打印配置内容(调试) |