# libiec61850m 模块工程文档 ## 概述 libiec61850m 是 RTU 项目中 IEC 61850 MMS **客户端应用线程**,位于 `src/system/libiec61850m/`。它作为应用层桥接器,将底层的 `libmms_m` 封装库与 RTU 的**数据中心(Datacenter)**连接起来,实现完整的 IEC 61850 客户端功能。 ### 在系统架构中的位置 ``` app_iec61850m (应用线程) ↓ 调用 API libiec61850m (本模块) ──── 信号注册/回调 ↓ 调用 libmms_m API ↓ dc_signal_* libmms_m (协议封装层) Datacenter (数据中心) ↓ IEC 61850 Client API libiec61850 (第三方库) ↓ MMS/TCP 远端 IED 设备 ``` ### 核心职责 1. **配置解析**:将 XML 配置文件解析为 `stru_cfg` 结构 2. **信号注册**:将信号点注册到数据中心(out/yk/ao/param) 3. **数据桥接**:将 libmms_m 回调的数据转发到数据中心 4. **遥控/设值转换**:将数据中心的变化回调转为 libmms_m 事件 ### 文件结构 ``` src/system/libiec61850m/ ├── inc/ │ ├── iec61850m.h ← 模块主头文件(app 入口声明) │ └── parse_xml.h ← XML 配置解析声明 └── src/ ├── iec61850m.cpp ← 核心实现(~830 行) └── parse_xml.cpp ← XML 解析实现(~320 行) ``` 引用头文件 `release/inc/myMms_m.h`(数据结构定义)。 --- ## 1. XML 配置文件解析(parse_xml.cpp) ### 1.1 XML 结构 ```xml ``` ### 1.2 元素/属性映射 | XML 元素 | 说明 | |---------|------| | `Root` | 根节点 | | `Para` | 连接参数(IP、端口、IED 名) | | `Point` | 信号点容器 | | `St/Mx/Co/Ao/Param` | 5 种信号类型子节点 | | `Item` | 单个信号点 | | Item 属性 | 存储字段 | 说明 | |----------|---------|------| | `saddr` | `item.saddr` | 短地址(数据中心标识) | | `desc` | `item.desc` | 描述 | | `type` | `item.type` | MMS 数据类型(MMS_BOOLEAN=2, FLOAT=6, INTEGER=4 等) | | `fc` | `item.fc` | 功能约束(ST=0, MX=1, CO=12, SG=6 等) | | `LDev` | `item.ldev` | 逻辑设备名 | | `LNode` | `item.lnode` | 逻辑节点名 | | `DoName` | `item.doname` | 数据对象名 | | `ctlModel` | `item.ctrl_model` | 控制模型(仅 Co 类型需要) | **引用自动生成规则**: ``` reference = ied + LDev + "/" + LNode + "." + DoName 例如: "IEDNAMETEMPLATE/GGIO1.Ind1" ``` ### 1.3 公共 API ```cpp // 解析 XML 配置文件,返回 stru_cfg* stru_cfg *parse_cfg(const std::string &cfg_file); // 打印配置内容到 stdout void show_cfg(stru_cfg &cfg); ``` --- ## 2. 核心实现(iec61850m.cpp) ### 2.1 类型映射 ```cpp // MMS 类型 → 本地 DATA_TYPE_* 类型 LOCAL std::map g_mms_m_type_to_local_type = { {MMS_BOOLEAN, DATA_TYPE_U8}, {MMS_INTEGER, DATA_TYPE_S32}, {MMS_UNSIGNED, DATA_TYPE_U32}, {MMS_FLOAT, DATA_TYPE_F32}, {MMS_STRING, DATA_TYPE_STR}, }; // IEC 61850 控制模型 → RTU 本地控制类型 LOCAL std::map g_mms_m_ctrl_type_to_local_ctrl_type = { {CONTROL_MODEL_STATUS_ONLY, SIGNAL_CTRL_TYPE::NONE}, {CONTROL_MODEL_DIRECT_NORMAL, SIGNAL_CTRL_TYPE::DIRECT_NORMAL}, {CONTROL_MODEL_SBO_NORMAL, SIGNAL_CTRL_TYPE::SBO_NORMAL}, {CONTROL_MODEL_DIRECT_ENHANCED, SIGNAL_CTRL_TYPE::DIRECT_NORMAL}, {CONTROL_MODEL_SBO_ENHANCED, SIGNAL_CTRL_TYPE::SBO_NORMAL}, }; ``` ### 2.2 模块实例管理 ```cpp typedef struct { int fd; // mms_m 客户端句柄 const char *prj_name; // 项目/配置文件名(如 "mms_m.xml") int debug; // 调试标志(0=关, 1=开) uint32_t connectionTimeout; // 连接超时 [毫秒, 默认10000] stru_mms_m_event mms_event; // 预分配的事件(用于遥控/设值) stru_cfg *p_cfg; // 配置指针 } stru_iec61850m_info; LOCAL std::vector g_vec_iec61850m_info = { { .fd = -1, .prj_name = "mms_m.xml", .debug = MMS_M_DEBUG_PRINT_OFF, .connectionTimeout = 10000, .mms_event = { .p_func = mms_event_back }, .p_cfg = NULL, } }; ``` 当前只配置了**一个**客户端实例,配置文件路径为 `<进程目录>/config/MMS/mms_m.xml`。 ### 2.3 初始化流程 ``` app_iec61850m_init1() ├── dc_signal_out("iec61850m.run_cnt", ...) ← 注册运行计数 ├── get_base_path() → g_61850m_prj_path ← "<进程目录>/config/MMS/" │ └── iec61850m_init() ├── for each g_vec_iec61850m_info[i]: │ ├── parse_cfg(g_61850m_prj_path + prj_name) ← 解析 XML │ ├── show_cfg() ← 打印配置 │ │ │ ├── iec61850m_signal_init(*p_cfg) ← 注册信号到数据中心 │ │ ├── 为每个信号点分配数据内存 (dc_create_data_ptr_by_type) │ │ ├── dc_signal_out() ← ST/MX 类型 │ │ ├── dc_signal_yk() ← CO 类型, 带 iec61850m_signal_co_change_callback │ │ ├── dc_signal_ao() ← AO 类型, 带 iec61850m_signal_ao_change_callback │ │ └── dc_signal_param() ← Param 类型, 带 iec61850m_signal_param_change_callback │ │ │ ├── mms_m_out_init(p_cfg, debug, timeout) ← 启动 MMS 客户端 │ │ │ ├── mms_m_out_bind_param_zone_signal(fd, p_cfg->point.p_ao[0].saddr) │ │ └── 将第一个 AO 信号绑定为定值区指示器 │ │ │ ├── mms_m_out_get_value(fd, mms_data_back) ← 注册数据回调 │ └── mms_m_out_get_connect_status(fd, iec61850m_connect_status) ← 注册状态回调 ``` ### 2.4 数据类型与信号注册详细 | 信号类型 | 数据中心函数 | 变化回调 | 参数说明 | |---------|-------------|---------|---------| | ST(遥信) | `dc_signal_out` | 无(只读输出) | p_val[0] 为值 | | MX(遥测) | `dc_signal_out` | 无(只读输出) | p_val[0] 为值 | | CO(遥控) | `dc_signal_yk` | `iec61850m_signal_co_change_callback` | ctrl_model 映射为本地类型 | | AO(模拟输出) | `dc_signal_ao` | `iec61850m_signal_ao_change_callback` | 带 p_default[0] 默认值 | | Param(参数) | `dc_signal_param` | `iec61850m_signal_param_change_callback` | 带 p_default[0], p_val[0..N-1] 多区值 | ### 2.5 数据回传链路(mms_data_back) ``` mms_data_back() ← libmms_m 数据回调入口 │ ├── 按 reason 过滤: │ 忽略: REASON_DATA_CHANGE, REASON_GI, │ REASON_INTEGRITY, REASON_ALL_CALL │ 处理: REASON_READ_AO, REASON_READ_PARAM │ ├── 打印日志(按 MMS 类型格式化) │ └── 按信号类型匹配并写入 Datacenter: ├── ST 点匹配: dc_set_out_signal_val(saddr, p_value) ├── MX 点匹配: dc_set_out_signal_val(saddr, p_value) ├── AO 点匹配: dc_signal_ao_set_val_without_check(saddr, local_type, p_value) └── Param 点匹配: dc_signal_param_set_val_without_check(saddr, local_type, set_zone-1, p_value) ``` **注意**:`mms_data_back` 只处理 AO 和 Param 的**主动读取**结果。数据变化和报告上送的数据通过 libmms_m 的报告回调机制直接输出,但当前代码中报告回调仅打印日志,未对接数据中心。 ### 2.6 遥控/设值下发链路 ``` Datacenter 信号变化 │ ├── CO: iec61850m_signal_co_change_callback() │ └── iec61850m_signal_change_decode() ← 解析 step/data_type/value │ └── mms_send_control() ← 构造 mms_event │ └── mms_m_out_do_set_yk() ← 发送到 libmms_m │ ├── AO: iec61850m_signal_ao_change_callback() │ └── 同上流程 (ctrl_type = _MMS_M_EVENT_AO_WRITE) │ └── Param: iec61850m_signal_param_change_callback() └── 同上流程 (ctrl_type = _MMS_M_EVENT_PARAM_WRITE, 传入 set_zone+1) ``` ### 2.7 连接上线后的处理 ``` iec61850m_connect_status(fd, ON_LINE) ├── iec61850m_ext_demo(fd) │ ├── mms_m_query_server(fd, ...) ← 查询 IED 信息 │ ├── mms_m_read_sg_info(fd, "PROT", ...) ← 读取定值组 │ └── 其他 ext demo(文件操作已注释) │ ├── mms_m_out_read_ao_or_params(fd, AO_READ, NULL) ← 读全部 AO └── mms_m_out_read_ao_or_params(fd, PARAM_READ, NULL) ← 读全部参数 ``` ### 2.8 应用线程函数 ```cpp void *app_iec61850m(void *arg) { // 标准 RTU app 线程模板 while (1) { task_event_recv(p_event, EV_TIMER1 | EV_TIMER2 | EV_TIMER3, TASK_EVENT_FLAG_OR | TASK_EVENT_FLAG_CLEAR, TASK_EVENT_WAIT_FOREVER, &event); if (event & EV_TIMER1) { ; } // 10ms 定时器(预留) if (event & EV_TIMER2) { ; } // 100ms 定时器(预留) if (event & EV_TIMER3) { p_app->run_cnt++; // 1s 定时器:运行计数 } } } ``` **注意**:app_iec61850m 线程本身不执行任何 MMS 操作。所有 MMS 通信由 libmms_m 内部的独立 `pthread_task` 线程驱动。 --- ## 3. CLI 调试命令 ```bash iec61850m info # 查看所有客户端实例信息 iec61850m yk # 手动遥控(框架已有,部分代码注释) iec61850m set # 手动设值(框架已有) ``` 注册方式:`CMD_REGISTER("iec61850m", cmd_iec61850m, "iec61850客户端线程的控制命令")` --- ## 4. 线程模型 ``` RTU 主进程 ├── app_sys 线程 (ap_sys.cpp) ├── ... │ ├── app_iec61850m 线程 (本模块) │ └── 3 个标准 RTU 定时器 (10ms / 100ms / 1000ms) │ └── 仅 1000ms 定时器:p_app->run_cnt++ │ ├── libmms_m 内部线程 (pthread_task) ← 由 mms_m_out_init() 创建 │ └── 主循环 300ms 周期 │ ├── 连接状态维护 │ ├── 事件处理 │ └── 4 个业务定时器 (120s/60s/30s/20s) │ └── libiec61850 库内部线程 (IedConnection 线程模式) └── MMS 报文收发、报告处理 ``` **三层线程协作**: | 层级 | 线程 | 职责 | |------|------|------| | RTU 应用层 | `app_iec61850m` | 信号注册、运行计数 | | 协议封装层 | libmms_m `pthread_task` | 连接管理、事件调度、数据路由 | | 第三方库层 | libiec61850 内部 | MMS 协议栈、报告分发 | --- ## 5. 数据流向总览 ``` ┌──────────────────────────────┐ │ 远端 IED (服务端) │ └──────────┬───────────────────┘ │ MMS/TCP ┌──────────▼───────────────────┐ │ libiec61850 (第三方库) │ │ IedConnection + Report │ └──────────┬───────────────────┘ │ ┌──────────────────┴──────────────────┐ │ libmms_m (封装层) │ │ ┌─────────────────────────────┐ │ │ │ mms_m_report_callback() │ │ ← 报告数据(当前仅打印) │ │ mms_m_send_call_all() │ │ ← 周期总召 │ │ mms_m_send_read_ao/param() │ │ ← 读 AO/参数 │ │ mms_m_send_co() │ │ ← 遥控 │ │ mms_m_send_param_write() │ │ ← 设值 │ └──────────┬──────────────────┘ │ └─────────────┼───────────────────────┘ │ mms_m_out_value_cb ┌─────────────▼───────────────────────┐ │ libiec61850m (应用层) │ │ ┌──────────────────────────────┐ │ │ │ mms_data_back() │ │ ← 数据回调→Datacenter │ │ iec61850m_signal_*_callback() │ │ ← Datacenter→遥控/设值 │ └──────────┬───────────────────┘ │ └─────────────┼───────────────────────┘ │ dc_signal_* / dc_set_* ┌─────────────▼───────────────────────┐ │ Datacenter (数据中心) │ │ out / in / yk / ao / param │ └─────────────────────────────────────┘ ```