9.5 KiB
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)
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)
1. iec61850s_signals_init() → 初始化五类信号
2. mms_s_init("config/MMS/PCS.icd", 102) → 启动 MMS 服务器
2.3 主循环(iec61850s.cpp:446)
三个定时器事件(EV_TIMER1/2/3)当前均为空闲,仅 EV_TIMER3 做 run_cnt++。信号驱动的工作全部通过 libmms_s 的内部线程和 DataCenter 回调完成——本线程主要作为容器,维护 MMS 服务器的生命周期。
3. 配置解析子系统(parse_xml.cpp)
3.1 XML 结构(mms_s.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 数据结构
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):
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 变化回调
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)
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) |
打印配置内容(调试) |