215 lines
6.6 KiB
Markdown
215 lines
6.6 KiB
Markdown
# 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) 仅定值区同步 |
|