RTU/claude/工程/libiec61850m模块分析.md

355 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
<Root>
<Para desc="描述" host_ip="192.168.1.100" host_port="102" ied="IEDNAME"/>
<Point desc="描述">
<St> <!-- 遥信 -->
<Item no="1" saddr="..." desc="..." type="2" fc="0" LDev="TEMPLATE" LNode="GGIO1" DoName="Ind1"/>
</St>
<Mx> <!-- 遥测 -->
<Item no="1" saddr="..." desc="..." type="6" fc="1" LDev="TEMPLATE" LNode="MMXU1" DoName="TotW"/>
</Mx>
<Co> <!-- 遥控 -->
<Item no="1" saddr="..." desc="..." type="2" fc="12" LDev="TEMPLATE" LNode="GGIO1" DoName="SPCSO1" ctlModel="2"/>
</Co>
<Ao> <!-- 模拟输出/设值 -->
<Item no="1" saddr="..." desc="..." type="6" fc="12" LDev="TEMPLATE" LNode="GGIO1" DoName="AnOut1"/>
</Ao>
<Param> <!-- 参数/定值 -->
<Item no="1" saddr="..." desc="..." type="6" fc="6" LDev="PROT" LNode="PDIS1" DoName="PhStr"/>
</Param>
</Point>
</Root>
```
### 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<uint8_t, uint8_t> 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<uint8_t, uint8_t> 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<stru_iec61850m_info> 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 <fd> <saddr> <val> <ctrl_type> # 手动遥控(框架已有,部分代码注释)
iec61850m set <fd> <saddr> <val> <set_type> # 手动设值(框架已有)
```
注册方式:`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 │
└─────────────────────────────────────┘
```