6.0 KiB
6.0 KiB
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 结构
<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 数据结构
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 对应):
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 装置