# 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 ``` ### 2.2 解析流程 ``` parse_mms_xml(path): → 加载 XML → 根元素 → mms_m_parse_para(root, cfg): 提取全局 rcb_numbers 属性 → 遍历每个 子元素: 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 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 vec_st; std::vector vec_mx; std::vector vec_co; std::vector vec_ao; std::vector 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 装置 ```