# 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 │
└─────────────────────────────────────┘
```