195 lines
6.0 KiB
Markdown
195 lines
6.0 KiB
Markdown
# libiec61850m 模块分析
|
||
|
||
**日期**: 2026-06-12
|
||
**基于源码**: `src/system/libiec61850m/` (iec61850m.cpp, parse_xml.cpp, 共4个文件)
|
||
|
||
---
|
||
|
||
## 1. 模块定位
|
||
|
||
`libiec61850m` 位于系统层,是 `libmms_m` 的上层封装。它负责:
|
||
- 解析 XML 配置(`mms_m.xml`)
|
||
- 编排各 IED 的信号初始化流程
|
||
- 将 MMS 读取的数据通过回调桥接到 `datacenter`
|
||
- 接收来自其他模块的遥控命令,转发到 `libmms_m`
|
||
|
||
**与其他模块的关系**:
|
||
```
|
||
app_sys (调度) → app_iec61850m (线程,EV_TIMER 驱动)
|
||
↓
|
||
libiec61850m (本模块,系统层封装)
|
||
↓ 调用 API
|
||
libmms_m (协议层,每个 IED 一个线程)
|
||
↓ IedConnection API
|
||
远方 IED 装置
|
||
```
|
||
|
||
## 2. 配置解析(parse_xml.cpp)
|
||
|
||
### 2.1 XML 结构
|
||
|
||
```xml
|
||
<mms_m rcb_numbers="01,02">
|
||
<IED name="PCS9700" ip="192.168.1.100" port="102">
|
||
<St><Signal no="1" reference="..." /></St>
|
||
<Mx><Signal no="1" reference="..." /></Mx>
|
||
<Co><Signal no="1" reference="..." /></Co>
|
||
<Ao><Signal no="1" reference="..." /></Ao>
|
||
<Param><Signal no="1" reference="..." /></Param>
|
||
</IED>
|
||
</mms_m>
|
||
```
|
||
|
||
### 2.2 解析流程
|
||
|
||
```
|
||
parse_mms_xml(path):
|
||
→ 加载 XML → <mms_m> 根元素
|
||
→ mms_m_parse_para(root, cfg):
|
||
提取全局 rcb_numbers 属性
|
||
→ 遍历每个 <IED> 子元素:
|
||
mms_m_parse_base(iedEle, cfg):
|
||
提取 name, ip, port
|
||
mms_m_parse_signals: 遍历 St/Mx/Co/Ao/Param
|
||
→ 每个 Signal: 提取 link(saddr), reference, type, ctrl_model
|
||
→ 存入 stru_mms_cfg 对应的 vector
|
||
存入 cfg.vec_ieds
|
||
```
|
||
|
||
### 2.3 数据结构
|
||
|
||
```cpp
|
||
typedef struct
|
||
{
|
||
std::string path; // XML 文件路径
|
||
int rcb_numbers_flag; // 是否有全局 rcb_numbers
|
||
std::string rcb_numbers; // 全局 RCB 编号(如 "01,02")
|
||
std::vector<stru_mms_ied_cfg> vec_ieds; // 各 IED 的配置
|
||
}stru_mms_cfg;
|
||
|
||
typedef struct
|
||
{
|
||
std::string name; // IED 名称
|
||
std::string ip; // IP 地址
|
||
int port; // MMS 端口(通常 102)
|
||
std::string rcb_numbers; // 此 IED 的 RCB 编号
|
||
std::vector<stru_mms_signal> vec_st;
|
||
std::vector<stru_mms_signal> vec_mx;
|
||
std::vector<stru_mms_signal> vec_co;
|
||
std::vector<stru_mms_signal> vec_ao;
|
||
std::vector<stru_mms_signal> vec_param;
|
||
}stru_mms_ied_cfg;
|
||
```
|
||
|
||
`stru_mms_signal`(与 `myMms_m.h` 中的 `stru_point_item` 对应):
|
||
```cpp
|
||
typedef struct
|
||
{
|
||
char saddr[MMS_M_STR_LEN]; // 信号地址(link 属性)
|
||
char reference[MMS_M_REF_LEN]; // MMS 对象引用
|
||
char desc[MMS_M_STR_LEN]; // 描述
|
||
uint8_t type; // 数据类型
|
||
uint8_t ctrl_model; // 控制模型
|
||
}stru_mms_signal;
|
||
```
|
||
|
||
## 3. 两阶段初始化
|
||
|
||
### 3.1 第一阶段:`app_iec61850m_init1`
|
||
|
||
```
|
||
1. 获取进程目录(/proc/self/exe → 项目根目录)
|
||
2. 解析 config/MMS/mms_m.xml → stru_mms_cfg
|
||
3. 为每个 IED:
|
||
a. 将信号配置转换为 libmms_m 需要的 stru_cfg 格式
|
||
b. 调用 mms_m_out_init(p_cfg, debug, timeout) → obj_fd
|
||
c. 调用 mms_m_out_get_value(obj_fd, iec61850m_value_callback)
|
||
注册数据回调 → 桥接到 datacenter
|
||
d. 调用 mms_m_out_get_connect_status(obj_fd, iec61850m_status_callback)
|
||
注册连接状态回调
|
||
4. 保存 obj_fd 列表到全局
|
||
```
|
||
|
||
### 3.2 第二阶段:`app_iec61850m_init2`
|
||
|
||
```
|
||
1. 遍历所有 IED 的 obj_fd:
|
||
a. mms_m_out_debug_print_swicth(fd, debug_flag) // 设置调试
|
||
b. 如果配置中指定了 rcb_numbers:
|
||
mms_m_out_set_rcb_numbers(fd, rcb_numbers)
|
||
c. 如果配置了定值区信号:
|
||
mms_m_out_bind_param_zone_signal(fd, zone_saddr)
|
||
```
|
||
|
||
### 3.3 为什么分两阶段
|
||
|
||
- `init1` 执行时 datacenter 尚未完全初始化
|
||
- `init2` 在 `init1` 之后执行,此时 datacenter 已就绪,可以安全注册信号绑定
|
||
|
||
## 4. 数据回调桥接
|
||
|
||
`iec61850m_value_callback` 是核心桥接函数,将 libmms_m 推送的 `stru_mms_m_out_value` 转换为 datacenter 操作:
|
||
|
||
```
|
||
iec61850m_value_callback(out_val):
|
||
→ 根据 out_val.reason 分类:
|
||
MMS_M_REASON_ALL_CALL: // 定时全数据刷新
|
||
MMS_M_REASON_READ_AO: // 主动读取 AO
|
||
MMS_M_REASON_READ_PARAM: // 主动读取 Param
|
||
IEC61850_REASON_DATA_CHANGE / QUALITY_CHANGE / GI:
|
||
// 报告触发
|
||
→ 写入 datacenter:
|
||
- ST/MX (out): dc_set_out_signal_val(name, p_value, type)
|
||
- AO: dc_signal_ao_set_val(...)
|
||
- Param: dc_signal_param_set_val(...)
|
||
- CO: // CO 由操作完成回调处理,不走数据回调
|
||
```
|
||
|
||
## 5. 遥控命令转发
|
||
|
||
其他模块(如 CLI、self_ptl)通过以下路径下发遥控:
|
||
|
||
```
|
||
外部模块 → iec61850m_do_set_yk(event)
|
||
→ 填充 stru_mms_m_event:
|
||
- app_fd: 目标 IED 句柄
|
||
- ctrl_type: SELECT/DIRECT/CANCEL
|
||
- saddr: 信号地址
|
||
- data: 操作数据
|
||
→ mms_m_out_do_set_yk(&event) → libmms_m 处理
|
||
```
|
||
|
||
## 6. 线程模型
|
||
|
||
```
|
||
app_iec61850m 线程:
|
||
while(1):
|
||
task_event_recv(EV_TIMER1 | EV_TIMER2 | EV_TIMER3)
|
||
EV_TIMER1 (10ms): 空闲
|
||
EV_TIMER2 (100ms): 空闲
|
||
EV_TIMER3 (1000ms): p_app->run_cnt++
|
||
```
|
||
|
||
主工作不在 `app_iec61850m` 线程中,而是在 libmms_m 为每个 IED 创建的独立线程 `mms_m_run_thread` 中。`app_iec61850m` 线程仅用于接收定时器事件和消息队列。
|
||
|
||
## 7. 模块间数据流
|
||
|
||
```
|
||
远方 IED 装置
|
||
↓ MMS 协议
|
||
libmms_m (mms_m_run_thread)
|
||
↓ mms_m_out_value_cb
|
||
iec61850m_value_callback
|
||
↓ dc_set_out_signal_val / dc_signal_ao_set_val / dc_signal_param_set_val
|
||
datacenter (signal_out/signal_ao/signal_param 表)
|
||
↓ 变更通知
|
||
其他模块 (self_ptl, com_channel, web_server, iec61850s)
|
||
|
||
控制方向(反向):
|
||
CLI / self_ptl
|
||
↓ dc_signal_yk_set_status / iec61850m_do_set_yk
|
||
libmms_m (事件队列 → mms_m_send_co)
|
||
↓ ControlObjectClient_select/operate/cancel
|
||
远方 IED 装置
|
||
```
|