# 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)` | 打印配置内容(调试) |