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

215 lines
6.6 KiB
Markdown
Raw Permalink 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-12
**基于源码**: `src/system/libiec61850s/` (iec61850s.cpp, parse_xml.cpp, 共4个文件)
---
## 1. 模块定位
`libiec61850s` 位于系统层,是 `libmms_s` 的上层封装。它负责将 datacenter 中的信号注册到 MMS 服务端模型中,使远方客户端能通过 IEC 61850 MMS 协议读取 RTU 数据并下发控制命令。
**与其他模块的关系**:
```
远方 MMS 客户端
↓ MMS 协议
libmms_s (协议层, IedServer)
↑↓ 回调
libiec61850s (本模块)
↑↓ dc_signal_* API
datacenter (信号存储)
```
## 2. 配置解析parse_xml.cpp
### 2.1 XML 结构
```xml
<mms_s>
<St>
<Signal no="1" link="signal_saddr_1" />
</St>
<Mx>
<Signal no="1" link="signal_saddr_2" />
</Mx>
<Co>
<Signal no="1" link="signal_saddr_3" />
</Co>
<Ao>
<Signal no="1" link="signal_saddr_4" />
</Ao>
<Param>
<Signal no="1" link="signal_saddr_5" />
</Param>
</mms_s>
```
解析函数 `parse_mms_xml(path)` 使用 tinyxml2 遍历五类信号元素,提取 `link` 属性存入 `stru_mms_cfg`
### 2.2 数据结构
```cpp
typedef struct
{
char saddr[MMS_S_STR_LEN];
char desc[MMS_S_STR_LEN];
uint8_t type;
uint8_t ctrl_model;
}stru_mms_s_signal_base;
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;
```
配置保存在全局变量 `g_cfg` 中,通过 `mms_cfg_ptr_get()` 获取。
## 3. 两阶段初始化
### 3.1 `app_iec61850s_init1`(第一阶段)
```
1. 获取进程目录
2. 解析 config/MMS/mms_s.xml → stru_mms_cfg
3. mms_s_dbg_switch(false) // 关闭调试
4. mms_s_file_path_set(base_path) // 设置文件服务根目录
5. mms_s_value_update_register(&g_mms_s_value_update_cb) // 注册值更新回调
```
此时仅完成配置解析和底层回调注册datacenter 尚未就绪。
### 3.2 `app_iec61850s_init2`(第二阶段)
```
1. iec61850s_signals_init(): // 核心信号初始化
- iec61850s_st_signals_init:
对每个 ST 信号调用 dc_signal_out_link_with_callback
→ 获取 datacenter 信号指针 + 注册变更回调
→ 回调: st_mx → mms_s_value_update (更新 MMS 模型)
- iec61850s_mx_signals_init:
同上MX 信号)
- iec61850s_control_signals_init:
对每个 CO 信号:
dc_get_yk_signal_info → 获取类型和 ctrl_model
mms_s_control_register → 向 libmms_s 注册控制回调
回调接收远方控制命令 → dc_signal_yk_set_status
- iec61850s_setting_signals_init:
对每个 AO 信号:
dc_signal_ao_link_with_callback → 绑定 SP 定值
mms_s_setting_register → 注册写入回调
回调接收客户端修改 → dc_signal_ao_set_val
- iec61850s_param_signals_init:
对每个 Param 信号:
dc_get_param_signal_info → 获取各定值区指针
mms_s_param_register → 注册 ConfirmEditSG 回调
回调接收定值确认 → dc_signal_param_set_val
2. mms_s_init(PCS.icd, 102):
解析 ICD → 创建模型 → 启动 IedServer端口 102
3. mms_s_bind_param_zone_signal(zone_saddr, iec61850s_sg_change_callback):
绑定定值区号 → 定值区切换时自动同步
```
## 4. 信号方向与回调链
### 4.1 数据上行datacenter → MMS 客户端)
```
datacenter 信号变化
→ iec61850s_st_mx_change_callback(saddr, type, p_data, p_last_data)
→ dc_get_signal_val → 格式化为字符串
→ g_mms_s_value_update_cb(saddr, val_str) // 即 mms_s_value_update
→ IedServer_updateAttributeValue → 更新模型值 → 触发 RCB 报告 → 通知订阅客户端
```
### 4.2 控制下行MMS 客户端 → datacenter
```
远方客户端控制命令
→ libmms_s control_handler
→ iec61850s_control_callback(p_control, state):
state == 1 (命令到达):
根据 ctrl_model:
DIRECT/SBO: dc_signal_yk_set_status(SELECT + DIRECT)
STATUS_ONLY: 日志记录
```
### 4.3 SP 定值修改MMS 客户端 → datacenter
```
远方客户端修改 SP 定值
→ libmms_s setting writeAccessHandler
→ iec61850s_setting_callback(p_setting, p_data):
dc_set_signal_val_from_str → 解析字符串值
dc_signal_ao_set_val(SELECT + DIRECT or DIRECT only)
```
### 4.4 定值组修改MMS 客户端 → datacenter
```
远方客户端 ConfirmEditSG
→ libmms_s confirmEditSG_callback
→ iec61850s_param_callback(p_param, p_data, zone):
dc_set_signal_val_from_str → 解析
dc_signal_param_set_val(zone, data)
```
### 4.5 定值区切换同步
```
datacenter 定值区号变化
→ iec61850s_sg_change_callback(zone_saddr, new_act_sg):
找到对应的 setting 信号
dc_signal_ao_set_val: 同步新值到 AO 信号
设置 g_sg_zone_saddr / g_sg_zone_act_sg
→ check_sg_zone_change() (EV_TIMER3 中):
mms_s_value_update: 更新 MMS 模型中 SGCB.ActSG 值
```
## 5. 主线程循环
```cpp
void *app_iec61850s(void *arg):
while(1):
task_event_recv(EV_TIMER1 | EV_TIMER2 | EV_TIMER3)
EV_TIMER1 (10ms): 空闲
EV_TIMER2 (100ms): 空闲
EV_TIMER3 (1000ms):
check_sg_zone_change() // 定值区号变更同步到 MMS 模型
p_app->run_cnt++
```
## 6. 全局变量
| 变量 | 类型 | 说明 |
|------|------|------|
| `g_vec_st` | `vector<stru_local_st_mx>` | ST 信号列表(含 datacenter 指针) |
| `g_vec_mx` | `vector<stru_local_st_mx>` | MX 信号列表 |
| `g_vec_control` | `vector<stru_mms_s_control>` | 控制信号列表 |
| `g_vec_setting` | `vector<stru_mms_s_setting>` | SP 定值信号列表 |
| `g_vec_param` | `vector<stru_mms_s_param>` | 定值组参数列表 |
| `g_mms_s_value_update_cb` | 函数指针 | MMS 值更新回调(= mms_s_value_update |
| `g_sg_zone_saddr` | string | 待同步的定值区信号地址 |
| `g_sg_zone_act_sg` | string | 待同步的新定值区号 |
## 7. 与 libiec61850m 的对比
| 特性 | libiec61850m (客户端) | libiec61850s (服务端) |
|------|----------------------|----------------------|
| 角色 | 主动读取/控制远方 IED | 被动响应远方客户端请求 |
| 数据方向 | 拉取All-Call/GI | 推送RCB 报告触发) |
| 控制 | 下发控制命令 | 接收并执行控制命令 |
| 连接 | 每个 IED 一个连接 | 监听端口,接受多客户端 |
| 定时器 | T0(120s)/T1(60s)/T2(30s) | EV_TIMER3(1s) 仅定值区同步 |