Compare commits
No commits in common. "e18f239d77ca9087a0ea60abbc1886dfc093a066" and "c03832a31e4283f2e9d3ddd5b029d1020a2fa911" have entirely different histories.
e18f239d77
...
c03832a31e
|
|
@ -5,7 +5,7 @@
|
||||||
"name": "Debug",
|
"name": "Debug",
|
||||||
"type": "cppdbg",
|
"type": "cppdbg",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/test/RTU",
|
"program": "${workspaceFolder}/release/x86/exe/RTU",
|
||||||
// "program": "${workspaceFolder}/release/x86/exe/FTU_cfg_parse",
|
// "program": "${workspaceFolder}/release/x86/exe/FTU_cfg_parse",
|
||||||
"args": [],
|
"args": [],
|
||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
|
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
# RCB 订阅编号可配置化
|
|
||||||
|
|
||||||
**日期**:2026-06-10
|
|
||||||
**状态**:已完成
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 问题描述
|
|
||||||
|
|
||||||
`libmms_m` 模块中 `mms_m_icd_report_init()` 硬编码了 `if(0 == rpt_no.compare("01"))`,只订阅编号末尾为 `"01"` 的 RCB 实例,其他编号直接丢弃。当 IED 提供多个 RCB 实例(如 EventsRCB01、EventsRCB02)时,无法灵活订阅。
|
|
||||||
|
|
||||||
## 需求
|
|
||||||
|
|
||||||
1. 灵活的可配置订阅的控制块编号
|
|
||||||
2. `libmms_m` 提供接口,由 `libiec61850m` 调用接口传入
|
|
||||||
3. 可以传入一个或多个,如果不调用,默认订阅 `"01"`
|
|
||||||
4. 如果传入无效数据,打印提示信息,返回失败,初始化流程返回失败
|
|
||||||
|
|
||||||
## 设计方案
|
|
||||||
|
|
||||||
### 涉及文件
|
|
||||||
|
|
||||||
| 文件 | 修改内容 |
|
|
||||||
|------|---------|
|
|
||||||
| `release/inc/myMms_m.h` | 新增 API 声明 |
|
|
||||||
| `src/protocol/libmms_m/inc/mms_m.h` | `stru_mms_m_obj` 新增存储字段 |
|
|
||||||
| `src/protocol/libmms_m/src/mms_m.cpp` | 新增 API 实现 + 修改过滤逻辑 |
|
|
||||||
| `src/system/libiec61850m/src/iec61850m.cpp` | 调用新 API 传入配置 |
|
|
||||||
|
|
||||||
### API 设计
|
|
||||||
|
|
||||||
```c
|
|
||||||
int mms_m_out_set_rcb_numbers(int app_fd, const char *rcb_numbers);
|
|
||||||
```
|
|
||||||
|
|
||||||
参数格式:逗号分隔的两位数字编号,如 `"01,02,03"`,特殊值 `"*"` 表示全部。
|
|
||||||
|
|
||||||
### 输入格式定义
|
|
||||||
|
|
||||||
| 输入 | 行为 |
|
|
||||||
|------|------|
|
|
||||||
| 不调用此 API | 默认订阅 `"01"` |
|
|
||||||
| `"01"` | 订阅编号 `"01"` |
|
|
||||||
| `"01,02,03"` | 订阅 `"01"` `"02"` `"03"` |
|
|
||||||
| `"*"` | 订阅全部 RCB |
|
|
||||||
| `"abc"` / `"1"` / `"012"` | 非法,返回 -1 |
|
|
||||||
|
|
||||||
### 存储设计
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// stru_mms_m_obj 新增字段
|
|
||||||
std::vector<std::string> rcb_numbers; // 空=默认"01"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 匹配函数
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
static bool mms_m_rcb_number_match(stru_mms_m_obj &obj, const std::string &rpt_no)
|
|
||||||
{
|
|
||||||
if (obj.rcb_numbers.empty()) return (rpt_no == "01"); // 默认
|
|
||||||
for (auto &n : obj.rcb_numbers)
|
|
||||||
if (rpt_no == n) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 实现流程
|
|
||||||
|
|
||||||
1. **myMms_m.h**:在 `mms_m_out_bind_param_zone_signal` 声明后新增 `mms_m_out_set_rcb_numbers` API 声明
|
|
||||||
2. **mms_m.h**:在 `stru_mms_m_obj.current_zone` 后新增 `rcb_numbers` 字段
|
|
||||||
3. **mms_m.cpp**:
|
|
||||||
- 新增 `#include <sstream>` 头文件
|
|
||||||
- 新增 `mms_m_rcb_number_match()` 静态匹配函数
|
|
||||||
- 新增 `mms_m_out_set_rcb_numbers()` API 实现(按逗号分割、去空格、校验恰好2位数字)
|
|
||||||
- 修改 `mms_m_icd_report_init()` 中 `rpt_no.compare("01")` → `mms_m_rcb_number_match(obj, rpt_no)`
|
|
||||||
4. **iec61850m.cpp**:在 `iec61850m_init()` 中 `mms_m_out_init()` 之后调用新 API,当前传入 `"01"`
|
|
||||||
|
|
||||||
## 变更记录
|
|
||||||
|
|
||||||
| 文件 | 行号(修改后) | 变更 |
|
|
||||||
|------|--------------|------|
|
|
||||||
| `myMms_m.h` | 170 | 新增 `int mms_m_out_set_rcb_numbers(...)` |
|
|
||||||
| `mms_m.h` | 183 | 新增 `std::vector<std::string> rcb_numbers` |
|
|
||||||
| `mms_m.cpp` | 7 | 新增 `#include <sstream>` |
|
|
||||||
| `mms_m.cpp` | 1342-1355 | 新增 `mms_m_rcb_number_match()` |
|
|
||||||
| `mms_m.cpp` | 1398 | `rpt_no.compare("01")` → `mms_m_rcb_number_match(obj, rpt_no)` |
|
|
||||||
| `mms_m.cpp` | 2097-2162 | 新增 `mms_m_out_set_rcb_numbers()` |
|
|
||||||
| `iec61850m.cpp` | 605-610 | 新增调用 `mms_m_out_set_rcb_numbers(fd, "01")` |
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,354 +0,0 @@
|
||||||
# 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 │
|
|
||||||
└─────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
@ -1,671 +0,0 @@
|
||||||
# libmms_m 模块工程文档
|
|
||||||
|
|
||||||
## 概述
|
|
||||||
|
|
||||||
libmms_m 是 RTU 项目中 IEC 61850 MMS 客户端的**封装库**,位于 `src/protocol/libmms_m/`,负责将 libiec61850 的 C 客户端 API 封装成 RTU 内部的事件驱动架构。
|
|
||||||
|
|
||||||
### 核心职责
|
|
||||||
|
|
||||||
1. **连接管理**:通过异步方式与远端 IED 建立/维护 MMS 连接
|
|
||||||
2. **报告接收**:通过 Report 回调接收 IED 主动上送的变化数据
|
|
||||||
3. **数据操作**:总召、总召遥信遥测、AO 读写、参数(定值)读写
|
|
||||||
4. **控制指令**:遥控(CO)的 Select/Operate/Cancel 操作
|
|
||||||
5. **扩展服务**:文件传输、服务器信息查询、定值组读取
|
|
||||||
|
|
||||||
### 文件结构
|
|
||||||
|
|
||||||
```
|
|
||||||
src/protocol/libmms_m/
|
|
||||||
├── inc/
|
|
||||||
│ ├── mms_m.h ← 核心头文件(日志宏、数据结构、API 声明)
|
|
||||||
│ ├── mms_m_ext.h ← 扩展功能接口声明(文件/服务器/定值组)
|
|
||||||
│ └── mms_m_errstr.h ← 错误码转字符串
|
|
||||||
└── src/
|
|
||||||
├── mms_m.cpp ← 核心实现(连接/报告/总召/遥控/参数)
|
|
||||||
├── mms_m_errstr.cpp ← 错误码/控制模型/AddCause 转字符串
|
|
||||||
├── mms_m_file.cpp ← 文件服务实现
|
|
||||||
├── mms_m_server.cpp ← 服务器身份/状态查询
|
|
||||||
└── mms_m_sg.cpp ← 定值组信息读取
|
|
||||||
```
|
|
||||||
|
|
||||||
**基础类型定义**位于 `release/inc/myMms_m.h`。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. 核心数据结构
|
|
||||||
|
|
||||||
### 1.1 全局对象管理
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
static std::map<int, stru_mms_m_obj *> g_mms_m_obj_map;
|
|
||||||
```
|
|
||||||
|
|
||||||
所有客户端实例通过全局 map 管理,key 为 `app_fd`(客户端句柄,从 1 开始自增),value 为对象指针。
|
|
||||||
|
|
||||||
### 1.2 主对象结构 `stru_mms_m_obj`
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
typedef struct {
|
|
||||||
int obj_fd; // 客户端句柄(自增)
|
|
||||||
uint32_t connectionTimeout; // 连接超时 [毫秒]
|
|
||||||
std::string cfg_path; // 配置文件路径
|
|
||||||
stru_cfg *p_cfg; // XML 配置解析结果
|
|
||||||
|
|
||||||
int debug_print_flag; // 调试打印开关(0/1)
|
|
||||||
|
|
||||||
std::string ied_name; // IED 名称
|
|
||||||
MmsValue *param_zone; // 定值区选择值(MmsValue, 可复用)
|
|
||||||
MmsValue *set_confirm; // 定值确认值(boolean, 可复用)
|
|
||||||
|
|
||||||
MMS_STR zone_saddr; // 关联定值区的信号 saddr(如 "ao.0")
|
|
||||||
int current_zone; // 当前定值区号
|
|
||||||
|
|
||||||
stru_mms_m_run run; // 运行时数据
|
|
||||||
std::vector<stru_ldev> ldevs; // 逻辑设备树(LD→LN→DO→Point)
|
|
||||||
std::vector<stru_ld_dataset> ld_datasets; // 数据集和报告配置
|
|
||||||
} stru_mms_m_obj;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.3 运行时结构 `stru_mms_m_run`
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
typedef struct {
|
|
||||||
std::string ip; // 服务器 IP
|
|
||||||
int port; // 服务器端口
|
|
||||||
bool running_init; // 是否已完成初始化
|
|
||||||
|
|
||||||
sem_t sem; // 回调同步信号量
|
|
||||||
pthread_t pthread_task; // 工作线程
|
|
||||||
|
|
||||||
IedConnection con; // libiec61850 连接句柄
|
|
||||||
IedConnectionState con_state; // 当前连接状态
|
|
||||||
IedConnectionState old_con_state; // 上一次连接状态
|
|
||||||
|
|
||||||
stru_mms_m_timer timer[_MMS_M_TIMER_END]; // 4 个定时器
|
|
||||||
|
|
||||||
stru_mms_m_event event; // 当前处理的事件
|
|
||||||
stru_event_queue event_queue; // 事件队列(容量 64)
|
|
||||||
// 事件类型(按枚举):
|
|
||||||
// [select] [operate] [cancel] ─→ 遥控
|
|
||||||
// [mms_m_send_co_select|direct|cancel]
|
|
||||||
// 通过 IedConnection_setRCBValues 发送总召
|
|
||||||
mms_m_out_status_cb out_status_cb; // 连接状态回调
|
|
||||||
std::vector<mms_m_out_value_cb> out_cb_lists; // 数据输出回调列表
|
|
||||||
} stru_mms_m_run;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.4 数据模型层次结构
|
|
||||||
|
|
||||||
```
|
|
||||||
stru_mms_m_obj
|
|
||||||
├── ldevs[] ← 逻辑设备列表
|
|
||||||
│ └── stru_ldev
|
|
||||||
│ ├── ld_name ← LD 名称(如 "TEMPLATE")
|
|
||||||
│ └── lnodes[] ← 逻辑节点列表
|
|
||||||
│ └── stru_lnode
|
|
||||||
│ ├── ln_name ← LN 名称(如 "GGIO1")
|
|
||||||
│ └── dobjs[] ← 数据对象列表
|
|
||||||
│ └── stru_dobj
|
|
||||||
│ ├── do_name ← DO 名称(如 "Ind1")
|
|
||||||
│ └── p_do_vec[] ← 指向 stru_point_item 的指针集合
|
|
||||||
│
|
|
||||||
└── ld_datasets[] ← 数据集/报告配置
|
|
||||||
└── stru_ld_dataset
|
|
||||||
├── ref ← LD/LN 引用
|
|
||||||
├── ld_name ← LD 名称
|
|
||||||
├── ln_name ← LN 名称
|
|
||||||
├── ln_datasets[] ← 数据集列表
|
|
||||||
│ └── stru_ln_dataset
|
|
||||||
│ ├── dataset_name ← 数据集全名
|
|
||||||
│ ├── dataset_ref ← 数据集引用 ($格式)
|
|
||||||
│ └── members[] ← 成员列表
|
|
||||||
│ └── stru_member
|
|
||||||
│ ├── ref ← FCDA 引用
|
|
||||||
│ ├── reason ← 包含原因
|
|
||||||
│ └── p_do_vec ← 关联的点集合
|
|
||||||
└── ln_rpts[] ← 报告列表
|
|
||||||
└── stru_ln_rpt
|
|
||||||
├── rpt_ref ← RCB 引用
|
|
||||||
├── rpt_ref_with_no ← RCB 引用+编号
|
|
||||||
├── ds_ref ← 关联数据集引用
|
|
||||||
├── type ← URCB 或 BRCB
|
|
||||||
├── rcb ← ClientReportControlBlock 句柄
|
|
||||||
├── p_app ← 指向所属 mms_m_obj
|
|
||||||
└── p_dataset ← 指向关联数据集
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1.5 事件与定时器
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// 事件类型
|
|
||||||
enum {
|
|
||||||
_MMS_M_EVENT_ALL_CALL, // 总召遥信遥测
|
|
||||||
_MMS_M_EVENT_GI_CALL, // 总召(GI)
|
|
||||||
_MMS_M_EVENT_CO_SELECT, // 遥控-选择
|
|
||||||
_MMS_M_EVENT_CO_DIRECT, // 遥控-操作
|
|
||||||
_MMS_M_EVENT_CO_CANCEL, // 遥控-取消
|
|
||||||
_MMS_M_EVENT_AO_READ, // 读取 AO
|
|
||||||
_MMS_M_EVENT_AO_WRITE, // 写入 AO
|
|
||||||
_MMS_M_EVENT_PARAM_READ, // 读取参数
|
|
||||||
_MMS_M_EVENT_PARAM_WRITE, // 写入参数
|
|
||||||
_MMS_M_EVENT_END
|
|
||||||
};
|
|
||||||
|
|
||||||
// 事件结构
|
|
||||||
typedef struct {
|
|
||||||
int app_fd; // 客户端句柄
|
|
||||||
MMS_STR ied; // IED 名称
|
|
||||||
MMS_STR saddr; // 短地址
|
|
||||||
uint8_t value_type; // MMS 数据类型
|
|
||||||
char val[MMS_M_DATA_STRING_LEN]; // 值(字符串形式)
|
|
||||||
uint8_t ctrl_type; // 事件类型
|
|
||||||
int set_zone; // 定值区号
|
|
||||||
void (*p_func)(void *arg, int ret); // 完成回调
|
|
||||||
} stru_mms_m_event;
|
|
||||||
|
|
||||||
// 事件队列(环形缓冲区,容量 64)
|
|
||||||
typedef struct {
|
|
||||||
uint8_t w_ptr; // 写指针
|
|
||||||
uint8_t r_ptr; // 读指针
|
|
||||||
uint8_t size; // 容量
|
|
||||||
uint8_t num; // 当前数量
|
|
||||||
stru_mms_m_event event[EVENT_QUEUE_SIZE];
|
|
||||||
} stru_event_queue;
|
|
||||||
|
|
||||||
// 4 个定时器
|
|
||||||
enum { _MMS_M_TIMER_T0, _MMS_M_TIMER_T1, _MMS_M_TIMER_T2, _MMS_M_TIMER_T3, _MMS_M_TIMER_END };
|
|
||||||
|
|
||||||
// 定时器时间定义
|
|
||||||
#define MMS_M_THREAD_RUN_TM (100 * 3) // 线程循环间隔:300ms
|
|
||||||
#define MMS_M_TIMER_T0 (120 * 3) // T0: 120s(总召所有数据)
|
|
||||||
#define MMS_M_TIMER_T1 (60 * 3) // T1: 60s(GI 触发)
|
|
||||||
#define MMS_M_TIMER_T2 (30 * 3) // T2: 30s(AO + 参数读取)
|
|
||||||
#define MMS_M_TIMER_T3 (20 * 3) // T3: 20s(预留,未使用)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. 线程模型与主流程
|
|
||||||
|
|
||||||
### 2.1 生命周期
|
|
||||||
|
|
||||||
```
|
|
||||||
mms_m_out_init()
|
|
||||||
├── 分配 stru_mms_m_obj
|
|
||||||
├── mms_m_ied_init() ← 构建 ldevs 树
|
|
||||||
├── sem_init() ← 初始化信号量
|
|
||||||
├── pthread_create(mms_m_run_thread) ← 创建工作线程
|
|
||||||
└── 加入 g_mms_m_obj_map[id]
|
|
||||||
|
|
||||||
mms_m_run_thread() ← 工作线程入口
|
|
||||||
├── mms_m_timer_init() ← 初始化4个定时器
|
|
||||||
├── 初始化事件队列
|
|
||||||
├── IedConnection_create()
|
|
||||||
├── IedConnection_installStateChangedHandler()
|
|
||||||
├── IedConnection_connectAsync()
|
|
||||||
└── 主循环(300ms 周期)
|
|
||||||
├── mms_m_do_comm() ← 连接状态机
|
|
||||||
└── if CONNECTED → mms_m_run()
|
|
||||||
├── mms_m_run_init() ← 首次建连时执行
|
|
||||||
├── mms_m_do_send() ← 处理事件队列
|
|
||||||
└── mms_m_timer_running() ← 检查定时器
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.2 连接状态机 `mms_m_do_comm()`
|
|
||||||
|
|
||||||
```
|
|
||||||
状态检查: IedConnection_getState()
|
|
||||||
|
|
||||||
CLOSED/CLOSING → connectAsync() ← 自动重连
|
|
||||||
CONNECTING → 等待
|
|
||||||
CONNECTED → [首次] mms_m_control_init() ← 创建 ControlObjectClient
|
|
||||||
[首次] 触发 out_status_cb(ON_LINE)
|
|
||||||
|
|
||||||
断连检测: old=CONNECTED, new!=CONNECTED
|
|
||||||
→ running_init = false ← 标记需要重新初始化
|
|
||||||
→ 触发 out_status_cb(OFF_LINE)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2.3 首次连接初始化 `mms_m_run_init()`
|
|
||||||
|
|
||||||
```
|
|
||||||
1. mms_m_icd_init()
|
|
||||||
├── getLogicalDeviceList() ← 获取 LD 列表
|
|
||||||
├── 遍历每对 LD+LN:
|
|
||||||
│ ├── mms_m_icd_dataset_init() ← 读取该 LN 下的所有 DataSet
|
|
||||||
│ │ └── getDataSetDirectory() ← 读数据集成员
|
|
||||||
│ ├── mms_m_icd_report_init(URCB) ← 发现 URCB 报告
|
|
||||||
│ └── mms_m_icd_report_init(BRCB) ← 发现 BRCB 报告
|
|
||||||
└── ld_datasets 构建完成
|
|
||||||
|
|
||||||
2. mms_m_ld_dataset_match_point_init()
|
|
||||||
└── 将数据集成员与 ldevs 中的 point 关联(构建 p_do_vec)
|
|
||||||
|
|
||||||
3. mms_m_rcb_init()
|
|
||||||
├── getRCBValues() ← 获取每个 RCB 当前值
|
|
||||||
├── 匹配数据集引用 → p_dataset
|
|
||||||
├── setResv(true) ← 预留 RCB
|
|
||||||
├── setTrgOps(dchg|qchg|gi)
|
|
||||||
├── setRptEna(true) ← 使能报告
|
|
||||||
├── installReportHandler() ← 安装回调
|
|
||||||
├── setRCBValues() ← 写入配置到服务器
|
|
||||||
└── setGI(true) ← 触发一次总召
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. 数据流程
|
|
||||||
|
|
||||||
### 3.1 周期性总召(T0 定时器)
|
|
||||||
|
|
||||||
```
|
|
||||||
mms_m_do_call_all() ← 每 120s 触发
|
|
||||||
└── push _MMS_M_EVENT_ALL_CALL 事件
|
|
||||||
└── mms_m_send_call_all()
|
|
||||||
└── 遍历 ST + MX 数据点
|
|
||||||
├── IedConnection_readObject() ← 逐一读取
|
|
||||||
├── mms_m_get_mmsValue() ← MmsValue→C 类型
|
|
||||||
├── mms_m_put_value() ← 通过回调输出
|
|
||||||
└── MmsValue_delete() ← 清理
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2 报告控制块(RCB)订阅全流程
|
|
||||||
|
|
||||||
RCB 订阅是 MMS 客户端最核心的机制,负责接收 IED 主动上送的数据变化。整个流程分为发现→匹配→激活→接收四个阶段。
|
|
||||||
|
|
||||||
#### 3.2.1 阶段一:发现 RCB(mms_m_icd_init → mms_m_icd_report_init)
|
|
||||||
|
|
||||||
连接成功后,遍历所有 LD→LN,对每对调用 `IedConnection_getLogicalNodeDirectory()` 分别查询两类 RCB:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// 查询 URCB (unbuffered,引用名含 "RP")
|
|
||||||
mms_m_icd_report_init(obj, ld_dataset, ACSI_CLASS_URCB);
|
|
||||||
// 查询 BRCB (buffered,引用名含 "BR")
|
|
||||||
mms_m_icd_report_init(obj, ld_dataset, ACSI_CLASS_BRCB);
|
|
||||||
```
|
|
||||||
|
|
||||||
`mms_m_icd_report_init()` 的核心筛选逻辑:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
std::string rpt_ref_str = (URCB == acsiClass) ? "RP" : "BR";
|
|
||||||
|
|
||||||
LinkedList reports = IedConnection_getLogicalNodeDirectory(
|
|
||||||
p_con, &err, ld_ds.ref.c_str(), acsiClass);
|
|
||||||
LinkedList report = LinkedList_getNext(reports);
|
|
||||||
|
|
||||||
while (report != NULL) {
|
|
||||||
std::string rpt_data = (char*) report->data;
|
|
||||||
|
|
||||||
// 提取名称和编号:最后2位是编号,前面是名称
|
|
||||||
rpt_name = rpt_data.substr(0, rpt_data.length() - 2);
|
|
||||||
rpt_no = rpt_data.substr(rpt_data.length() - 2);
|
|
||||||
|
|
||||||
rpt_ref = ld_ds.ref + "." + rpt_ref_str + ".";
|
|
||||||
|
|
||||||
if (0 == rpt_no.compare("01")) // 只取编号 "01"
|
|
||||||
{
|
|
||||||
ln_rpt.rpt_ref = rpt_ref + rpt_name;
|
|
||||||
// 例: "TEMPLATE/LLN0.RP.EventsRCB"
|
|
||||||
ln_rpt.rpt_ref_with_no = rpt_ref + rpt_name + rpt_no;
|
|
||||||
// 例: "TEMPLATE/LLN0.RP.EventsRCB01"
|
|
||||||
ld_ds.ln_rpts.push_back(ln_rpt);
|
|
||||||
}
|
|
||||||
// else: 其他编号直接丢弃
|
|
||||||
|
|
||||||
report = LinkedList_getNext(report);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**限制**:只订阅编号末尾为 `"01"` 的 RCB 实例。
|
|
||||||
|
|
||||||
#### 3.2.2 阶段二:匹配数据集(mms_m_ld_dataset_match_point_init)
|
|
||||||
|
|
||||||
将 XML 配置加载的 `ldevs` 信号树与从 IED 发现的 `ld_datasets` 数据集树进行关联,使得每个 dataset member 知道对应哪些信号点(saddr)。
|
|
||||||
|
|
||||||
```
|
|
||||||
数据集 member 的 FCDA 引用示例: "IEDNAME+TEMPLATE/GGIO1.ST.Ind1.stVal[ST]"
|
|
||||||
↓ 解析
|
|
||||||
ld = "IEDNAME+TEMPLATE" → 去 IED 前缀 → "TEMPLATE"
|
|
||||||
ln = "GGIO1"
|
|
||||||
d_name = "Ind1"
|
|
||||||
↓ 匹配 ldevs 树
|
|
||||||
ldevs[ld="TEMPLATE"] → lnodes[ln="GGIO1"] → dobjs[d_name="Ind1"]
|
|
||||||
↓
|
|
||||||
member.p_do_vec = &dobjs.p_do_vec // 建立关联
|
|
||||||
|
|
||||||
如果匹配不到(找不到对应的 LD/LN/DO),member.p_do_vec 为 NULL,
|
|
||||||
该 member 的报告数据将无法输出到任何信号点。
|
|
||||||
```
|
|
||||||
|
|
||||||
引用字符串解析规则([mms_m.cpp:1228-1257](src/protocol/libmms_m/src/mms_m.cpp#L1228-L1257)):
|
|
||||||
- 第1段(`/` 前):LD 名称,需去除 IED 名前缀
|
|
||||||
- 第2段(`.` 前):LN 名称
|
|
||||||
- 第3段(`[` 前):DO 名称
|
|
||||||
- 不匹配此格式的 member 直接跳过
|
|
||||||
|
|
||||||
#### 3.2.3 阶段三:配置并激活 RCB(mms_m_rcb_init)
|
|
||||||
|
|
||||||
对每个已发现的 RCB,执行完整的配置和激活流程([mms_m.cpp:1158-1214](src/protocol/libmms_m/src/mms_m.cpp#L1158-L1214)):
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─ 步骤1:getRCBValues(rcb_ref) 读服务端当前 RCB 值
|
|
||||||
│
|
|
||||||
├─ 步骤2:getDataSetReference(rcb) 获取 RCB 当前关联的数据集
|
|
||||||
│ ↓ ds_ref ← "TEMPLATE/LLN0$Events"
|
|
||||||
├─ 步骤3:匹配本地数据集
|
|
||||||
│ 遍历 ln_datasets,通过 dataset_ref 匹配
|
|
||||||
│ ln_rpt.p_dataset = &matched_dataset
|
|
||||||
│
|
|
||||||
├─ 步骤4:本地设置 RCB 参数
|
|
||||||
│ setResv(true) 预留 RCB(URCB)
|
|
||||||
│ setTrgOps(dchg | qchg | gi) 触发条件:数据变化+品质变化+总召
|
|
||||||
│ setDataSetReference(ds_ref) 确认数据集引用
|
|
||||||
│ setRptEna(true) 使能报告
|
|
||||||
│ ⚠ 未调用 setOptFlds() 完全依赖服务端默认值
|
|
||||||
│
|
|
||||||
├─ 步骤5:installReportHandler() 安装报告回调
|
|
||||||
│ rcbReference = rpt_ref 如 "LD/LLN0.RP.EventsRCB"
|
|
||||||
│ rptId = 从 RCB 获取
|
|
||||||
│ handler = mms_m_report_callback
|
|
||||||
│ parameter = &ln_rpt 回传 RCB 上下文
|
|
||||||
│
|
|
||||||
├─ 步骤6:setRCBValues() 写入服务端
|
|
||||||
│ parametersMask = RCB_ELEMENT_RPT_ENA |
|
|
||||||
│ RCB_ELEMENT_TRG_OPS |
|
|
||||||
│ RCB_ELEMENT_INTG_PD |
|
|
||||||
│ RCB_ELEMENT_GI
|
|
||||||
│ singleRequest = true
|
|
||||||
│ ⚠ mask 包含 INTG_PD 但未调用 setIntgPd()
|
|
||||||
│ ⚠ mask 不含 OPT_FLDS,OptFlds 沿用服务端默认
|
|
||||||
│
|
|
||||||
└─ 步骤7:触发总召
|
|
||||||
setGI(true) → setRCBValues(mask=RCB_ELEMENT_GI)
|
|
||||||
让 IED 立即上送一次完整数据
|
|
||||||
```
|
|
||||||
|
|
||||||
**步骤 6 的潜在问题**:
|
|
||||||
- `parametersMask` 中包含 `RCB_ELEMENT_INTG_PD`,但本地并未调用 `setIntgPd()`,写入的 IntgPd 值实际是步骤1从服务端读取的原始值
|
|
||||||
- `RCB_ELEMENT_OPT_FLDS` 未包含在 mask 中,意味着不会设置服务端的 OptFlds,完全依赖服务端默认配置 — 可能导致报告缺少 `DataReference`、`ConfRev`、`BufferOverflow` 等字段
|
|
||||||
- 使用 `singleRequest=true`,一次 MMS 写请求携带多个变量;如果服务端兼容性不好,可能需要改为 `false`
|
|
||||||
|
|
||||||
#### 3.2.4 阶段四:报告接收与分发(mms_m_report_callback)
|
|
||||||
|
|
||||||
当 IED 发送报告时,libiec61850 回调 `mms_m_report_callback()`([mms_m.cpp:1079-1153](src/protocol/libmms_m/src/mms_m.cpp#L1079-L1153)):
|
|
||||||
|
|
||||||
```
|
|
||||||
ClientReport 到达
|
|
||||||
│ parameter = &ln_rpt ← 阶段三步骤5传入的回调参数
|
|
||||||
│
|
|
||||||
├── ClientReport_getDataSetValues(report) → MmsValue* (MMS_ARRAY)
|
|
||||||
│
|
|
||||||
└── 遍历 dataset->members(按数据集顺序,index 从 0 递增)
|
|
||||||
│
|
|
||||||
├── reason = ClientReport_getReasonForInclusion(report, index)
|
|
||||||
│
|
|
||||||
├── if (reason == NOT_INCLUDED) → 跳过该成员
|
|
||||||
│
|
|
||||||
├── mms_value = MmsValue_getElement(dataset_value, index)
|
|
||||||
│ │
|
|
||||||
│ └── mms_m_get_MmsValue(point_value, out_value, mms_value, ...)
|
|
||||||
│ 递归解包 MmsValue → C 类型
|
|
||||||
│ ├── MMS_STRUCTURE/ARRAY → 递归展开子元素
|
|
||||||
│ ├── MMS_BOOLEAN → *(uint8_t*)p_val
|
|
||||||
│ ├── MMS_INTEGER → *(int32_t*)p_val
|
|
||||||
│ ├── MMS_UNSIGNED → *(uint32_t*)p_val
|
|
||||||
│ ├── MMS_FLOAT → *(float*)p_val
|
|
||||||
│ ├── MMS_BIT_STRING → MmsValue_getBitStringAsIntegerBigEndian()
|
|
||||||
│ ├── MMS_UTC_TIME → 解析时间 → point_value.time
|
|
||||||
│ └── MMS_BINARY_TIME → 解析时间 → point_value.time
|
|
||||||
│
|
|
||||||
├── 如果 mms_value 含时间戳 → tm_flag=1 → 使用报告中的时间
|
|
||||||
│ 否则 → tm_flag=0 → mms_m_get_local_time() 使用本地时间
|
|
||||||
│
|
|
||||||
└── 通过 member.p_do_vec 遍历关联的信号点
|
|
||||||
for (each point_item in p_do_vec)
|
|
||||||
out_value.name = point.saddr
|
|
||||||
out_value.desc = point.desc
|
|
||||||
out_value.reference = point.reference
|
|
||||||
out_value.reason = reason
|
|
||||||
out_value.p_value = 解析后的值指针
|
|
||||||
out_value.time = 时间(报告时间或本地时间)
|
|
||||||
out_value.app_fd = obj.obj_fd
|
|
||||||
↓
|
|
||||||
mms_m_put_value(obj, out_value) // 回调所有注册的 out_cb
|
|
||||||
```
|
|
||||||
|
|
||||||
**数据输出链路**:`mms_m_put_value()` → 遍历 `run.out_cb_lists` → 回调上层注册的数据处理函数(如 `libiec61850m` 中的 `mms_data_back`)。
|
|
||||||
|
|
||||||
#### 3.2.5 断连后的重新订阅
|
|
||||||
|
|
||||||
连接状态机在检测到断连时([mms_m.cpp:1579-1588](src/protocol/libmms_m/src/mms_m.cpp#L1579-L1588)):
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
if (run.con_state != CONNECTED && run.old_con_state == CONNECTED) {
|
|
||||||
obj.run.running_init = false; // 清除已初始化标志
|
|
||||||
out_status_cb(obj.obj_fd, OFF_LINE);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
下一次 `mms_m_run()` 检测到 `running_init==false` 且已重连时,自动**重新执行完整的四阶段订阅流程**(阶段一→二→三),确保断连恢复后重新订阅所有 RCB。
|
|
||||||
|
|
||||||
#### 3.2.6 GI 定时触发
|
|
||||||
|
|
||||||
在阶段三完成首次订阅后,定时器 T1(60s)周期性触发 GI 总召([mms_m.cpp:1636-1649](src/protocol/libmms_m/src/mms_m.cpp#L1636-L1649)):
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// 每 60s 一次
|
|
||||||
event.ctrl_type = _MMS_M_EVENT_GI_CALL;
|
|
||||||
mms_m_push_event(obj, event);
|
|
||||||
|
|
||||||
// → mms_m_send_set_gi()
|
|
||||||
// 遍历所有 RCB → setGI(true) → setRCBValues(mask=RCB_ELEMENT_GI)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3.2.7 流程总览图
|
|
||||||
|
|
||||||
```
|
|
||||||
首次连接 / 断连重连
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
阶段一 ✦ 发现 ──────────────────────────────────────
|
|
||||||
mms_m_icd_init() → mms_m_icd_report_init()
|
|
||||||
遍历 LD→LN → getLogicalNodeDirectory(URCB/BRCB)
|
|
||||||
└── 筛选:只取编号末尾为 "01" 的 RCB
|
|
||||||
产出: ld_datasets[].ln_rpts[] (RCB 列表)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
阶段二 ✦ 匹配 ──────────────────────────────────────
|
|
||||||
mms_m_ld_dataset_match_point_init()
|
|
||||||
解析 dataset member 的 FCDA 引用 (LD/LN/DO)
|
|
||||||
└── 匹配 ldevs 信号树 → member.p_do_vec 关联
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
阶段三 ✦ 激活 ──────────────────────────────────────
|
|
||||||
mms_m_rcb_init()
|
|
||||||
对每个 RCB:
|
|
||||||
├── getRCBValues() 读取服务端 RCB
|
|
||||||
├── setResv/setTrgOps/setRptEna 配置参数
|
|
||||||
├── installReportHandler() 安装回调
|
|
||||||
├── setRCBValues() 写入服务端
|
|
||||||
└── setGI(true) 触发总召
|
|
||||||
│
|
|
||||||
▼ (此后异步回调)
|
|
||||||
阶段四 ✦ 接收 ──────────────────────────────────────
|
|
||||||
mms_m_report_callback()
|
|
||||||
ClientReport 到达 → 遍历 dataset members
|
|
||||||
├── 跳过 NOT_INCLUDED 成员
|
|
||||||
├── 递归解包 MmsValue → C 类型
|
|
||||||
├── 解析时间戳
|
|
||||||
└── p_do_vec → mms_m_put_value() → 上层回调
|
|
||||||
|
|
||||||
周期性触发:
|
|
||||||
T1 (60s) → GI ─────────────────────────────────→ 阶段四再次触发
|
|
||||||
T0 (120s) → AllCall 读取全部 ST+MX 值
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.3 遥控流程
|
|
||||||
|
|
||||||
```
|
|
||||||
外部调用 mms_m_out_do_set_yk(event)
|
|
||||||
└── push 遥控事件到事件队列
|
|
||||||
└── mms_m_send_co() ← 处理遥控事件
|
|
||||||
├── 按 saddr 匹配 co_point
|
|
||||||
├── 首次: ControlObjectClient_create()
|
|
||||||
│ └── setOrigin(ORCAT_STATION_CONTROL)
|
|
||||||
├── 构造 set_value (MmsValue_newBoolean)
|
|
||||||
└── 按 ctrl_type 分发:
|
|
||||||
├── SELECT → mms_m_send_co_select()
|
|
||||||
│ ├── SBO_NORMAL: select()
|
|
||||||
│ └── SBO_ENHANCED: selectWithValue()
|
|
||||||
│ └── 先 setCommandTerminationHandler()
|
|
||||||
├── DIRECT → mms_m_send_co_direct()
|
|
||||||
│ ├── operate()
|
|
||||||
│ └── 按 ctrl_model 检查结果:
|
|
||||||
│ ├── DIRECT_NORMAL: 立即回读 stVal 验证
|
|
||||||
│ ├── DIRECT_ENHANCED: 等待 1s 后回读验证
|
|
||||||
│ └── SBO_ENHANCED: 等待 1s(不验证)
|
|
||||||
└── CANCEL → mms_m_send_co_cancel()
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.4 参数(定值)写入流程
|
|
||||||
|
|
||||||
```
|
|
||||||
_PARAM_WRITE 事件
|
|
||||||
└── mms_m_send_param_write()
|
|
||||||
├── 匹配参数点
|
|
||||||
├── 构造 EditSG 引用: {LD}/LLN0.SGCB.EditSG
|
|
||||||
├── 构造 CnfEdit 引用: {LD}/LLN0.SGCB.CnfEdit
|
|
||||||
├── 步骤1: writeObject(EditSG, FC=SP) ← 选择编辑定值组
|
|
||||||
├── 步骤2: writeObject(ref, FC=SE) ← 写入新定值
|
|
||||||
└── 步骤3: writeObject(CnfEdit, FC=SP) ← 确认编辑
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.5 定值区自动切换
|
|
||||||
|
|
||||||
```
|
|
||||||
mms_m_send_read_ao() 中检测:
|
|
||||||
如果某个 AO 信号的 saddr == zone_saddr(绑定的定值区信号)
|
|
||||||
→ 读值后比较 current_zone
|
|
||||||
→ 若变化 (new_zone != current_zone):
|
|
||||||
update current_zone
|
|
||||||
push _MMS_M_EVENT_PARAM_READ 事件 ← 自动触发参数重读
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. 对外接口
|
|
||||||
|
|
||||||
### 4.1 myMms_m.h 中声明的主要 API
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// === 生命周期 ===
|
|
||||||
int mms_m_out_init(stru_cfg *p_cfg, int debug_print_flag, uint32_t connectionTimeout);
|
|
||||||
// 返回 app_fd (>0 成功,-1 失败)
|
|
||||||
|
|
||||||
// === 状态回调 ===
|
|
||||||
int mms_m_out_get_connect_status(int fd, mms_m_out_status_cb p_func);
|
|
||||||
// 连接/断开时回调: void (int app_fd, int status) // MMS_M_ON_LINE / MMS_M_OFF_LINE
|
|
||||||
|
|
||||||
// === 数据回调 ===
|
|
||||||
int mms_m_out_get_value(int fd, mms_m_out_value_cb p_func);
|
|
||||||
// 数据到达时回调: void (stru_mms_m_out_value *p_value)
|
|
||||||
|
|
||||||
// === 遥控 ===
|
|
||||||
int mms_m_out_do_set_yk(stru_mms_m_event *p_event);
|
|
||||||
// 发送遥控事件(select/direct/cancel),返回 -1 失败
|
|
||||||
|
|
||||||
// === AO/参数操作 ===
|
|
||||||
int mms_m_out_read_ao_or_params(int app_fd, uint8_t type, const char *saddr);
|
|
||||||
// type: _MMS_M_EVENT_AO_READ 或 _MMS_M_EVENT_PARAM_READ
|
|
||||||
// saddr 为 NULL 则读取全部
|
|
||||||
|
|
||||||
// === 定值区绑定 ===
|
|
||||||
int mms_m_out_bind_param_zone_signal(int app_fd, const char *zone_saddr);
|
|
||||||
// 绑定一个 AO 信号作为定值区指示器
|
|
||||||
|
|
||||||
// === 调试 ===
|
|
||||||
int mms_m_out_debug_print_swicth(int id, int debug_print_flag);
|
|
||||||
|
|
||||||
// === 工具函数 ===
|
|
||||||
void *mms_m_create_data_ptr(uint8_t type); // 按 MMS 类型创建数据指针
|
|
||||||
int mms_m_set_data_value(void *srt, void *dst, uint8_t type); // 复制值
|
|
||||||
int mms_m_get_data_value_str(void *data, uint8_t type, char *str); // 转字符串
|
|
||||||
int mms_m_set_data_by_str(void *data, uint8_t type, const char *str); // 从字符串设置
|
|
||||||
char *mms_m_out_reason_str(int reason); // 原因码转字符串
|
|
||||||
void *mms_m_get_obj(int app_fd); // 获取对象(给扩展模块)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.2 扩展接口(mms_m_ext.h)
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// 文件服务
|
|
||||||
typedef void (*mms_m_file_read_cb)(int fd, const char *fn, const uint8_t *d, int len, bool mf, int e);
|
|
||||||
typedef void (*mms_m_file_op_cb)(int fd, const char *fn, int e);
|
|
||||||
int mms_m_read_file(int fd, const char *rf, mms_m_file_read_cb cb);
|
|
||||||
int mms_m_delete_file(int fd, const char *rf, mms_m_file_op_cb cb);
|
|
||||||
|
|
||||||
// 服务器信息查询
|
|
||||||
typedef void (*mms_m_server_cb)(int fd, const char *vendor, const char *model, const char *rev, int log_st, int phy_st, int e);
|
|
||||||
int mms_m_query_server(int fd, mms_m_server_cb cb);
|
|
||||||
|
|
||||||
// 定值组信息
|
|
||||||
typedef void (*mms_m_sg_cb)(int fd, const char *ld, int act_sg, int num_sg, int e);
|
|
||||||
int mms_m_read_sg_info(int fd, const char *ld, mms_m_sg_cb cb);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.3 扩展回调类型汇总
|
|
||||||
|
|
||||||
| 回调类型 | 签名 | 用途 |
|
|
||||||
|---------|------|------|
|
|
||||||
| `mms_m_out_status_cb` | `void(int fd, int status)` | 连接状态 |
|
|
||||||
| `mms_m_out_value_cb` | `void(stru_mms_m_out_value *p_value)` | 数据到达 |
|
|
||||||
| `mms_m_file_read_cb` | `void(int, const char*, const uint8_t*, int, bool, int)` | 文件分块读取 |
|
|
||||||
| `mms_m_file_op_cb` | `void(int fd, const char* filename, int err)` | 文件删除结果 |
|
|
||||||
| `mms_m_server_cb` | `void(int, const char*, const char*, const char*, int, int, int)` | 服务器信息 |
|
|
||||||
| `mms_m_sg_cb` | `void(int, const char*, int act_sg, int num_sg, int err)` | 定值组信息 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. 错误码(mms_m_errstr.cpp)
|
|
||||||
|
|
||||||
提供四种枚举值到字符串的映射:
|
|
||||||
|
|
||||||
| 函数 | 映射 |
|
|
||||||
|------|------|
|
|
||||||
| `mms_m_err_str(IedClientError)` | IED 客户端错误(30 个映射) |
|
|
||||||
| `mms_m_control_err_str(ControlLastApplError)` | 控制应用错误(4 个映射) |
|
|
||||||
| `mms_m_ctl_add_cause_str(ControlAddCause)` | 控制附加原因(28 个映射) |
|
|
||||||
| `mms_m_ctrl_model_str(ControlModel)` | 控制模型(5 个映射) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. 文件服务(mms_m_file.cpp)
|
|
||||||
|
|
||||||
### 实现状态
|
|
||||||
|
|
||||||
| 功能 | 状态 | 使用的 API |
|
|
||||||
|------|------|-----------|
|
|
||||||
| 文件打开 | 已实现 | `MmsConnection_fileOpen()` |
|
|
||||||
| 分块异步读取 | 未完成(TODO) | 待实现 `MmsConnection_fileReadAsync()` |
|
|
||||||
| 文件删除 | 已实现 | `MmsConnection_fileDelete()` |
|
|
||||||
|
|
||||||
### 调用方式
|
|
||||||
|
|
||||||
所有文件操作通过 `IedConnection_getMmsConnection()` 获取底层 `MmsConnection` 句柄,然后调用 MMS 低层文件 API。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. 设计要点
|
|
||||||
|
|
||||||
1. **事件驱动**:所有对外操作通过事件队列异步执行,避免阻塞调用线程
|
|
||||||
2. **自动重连**:连接断开后自动重试连接,连接恢复后重新初始化报告
|
|
||||||
3. **数据结构三棵树**:
|
|
||||||
- **ldevs**:按 LD→LN→DO 组织的信号点树(用于信号匹配输出)
|
|
||||||
- **ld_datasets/ln_rpts**:数据集和 RCB 树(用于报告接收和 GI 触发)
|
|
||||||
- **g_mms_m_obj_map**:多客户端实例管理
|
|
||||||
4. **定值区自动跟踪**:通过绑定 zone_saddr,自动检测定值区变化并重读参数
|
|
||||||
5. **调试标志**:全局 `debug_print_flag` 控制日志输出详细程度
|
|
||||||
6. **RCB 激活配置缺陷**:
|
|
||||||
- `setRCBValues` 的 `parametersMask` 包含 `RCB_ELEMENT_INTG_PD` 但本地未调用 `setIntgPd()`,写入的是从服务端读回的旧值
|
|
||||||
- `RCB_ELEMENT_OPT_FLDS` 未包含在 mask 中,OptFlds 完全依赖服务端默认值,可能导致报告缺少 `DataReference`、`ConfRev`、`BufferOverflow` 等字段
|
|
||||||
- `mms_m_icd_report_init` 只取编号 `"01"` 的 RCB,其他编号直接丢弃
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
# 问题处理文档
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2026-06-10
|
|
||||||
|
|
||||||
### #1 libmms_m RCB 订阅编号硬编码
|
|
||||||
|
|
||||||
**问题**:`mms_m_icd_report_init()` 中硬编码 `if(0 == rpt_no.compare("01"))`,只订阅编号为 `"01"` 的 RCB,其他编号被丢弃,无法按需灵活订阅多个 RCB 实例。
|
|
||||||
|
|
||||||
**需求**:
|
|
||||||
1. 灵活可配置订阅的控制块编号
|
|
||||||
2. libmms_m 提供接口,由 libiec61850m 传入
|
|
||||||
3. 可传入一个或多个编号,不传默认 `"01"`
|
|
||||||
4. 无效数据打印错误并返回失败
|
|
||||||
|
|
||||||
**处理计划**:[RCB订阅编号可配置化](./mid/RCB订阅编号可配置化.md)
|
|
||||||
|
|
||||||
**状态**:✅ 已完成
|
|
||||||
**涉及文件**:`myMms_m.h`, `mms_m.h`, `mms_m.cpp`, `iec61850m.cpp`
|
|
||||||
|
|
||||||
---
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -22,7 +22,6 @@ else
|
||||||
echo "====================================="
|
echo "====================================="
|
||||||
echo " 本地 X86 编译 开始"
|
echo " 本地 X86 编译 开始"
|
||||||
echo "====================================="
|
echo "====================================="
|
||||||
rm -f "$(dirname "$0")/../test/RTU"
|
|
||||||
make clean
|
make clean
|
||||||
make
|
make
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ int dc_signal_in(const std::string &saddr, const std::string &desc, const std::s
|
||||||
|
|
||||||
int dc_signal_in_with_callback(const std::string &saddr, const std::string &desc, const std::string &link_saddr, void **p_data, out_signal_change_cb cb);
|
int dc_signal_in_with_callback(const std::string &saddr, const std::string &desc, const std::string &link_saddr, void **p_data, out_signal_change_cb cb);
|
||||||
|
|
||||||
int dc_signal_ao(const std::string &saddr, const std::string &desc, uint8_t data_type, uint8_t ctrl_type, void *p_data, void *p_default_data, signal_change_cb cb);
|
int dc_signal_ao(const std::string &saddr, const std::string &desc, uint8_t data_type, const stru_signal_param ¶m, uint8_t ctrl_type, void *p_data, void *p_default_data, signal_change_cb cb);
|
||||||
|
|
||||||
int dc_signal_ao_link_with_callback(const std::string &saddr, void **p_data, signal_change_cb cb);
|
int dc_signal_ao_link_with_callback(const std::string &saddr, void **p_data, signal_change_cb cb);
|
||||||
|
|
||||||
|
|
@ -75,7 +75,7 @@ int dc_signal_ao_set_val(const std::string &saddr, SIGNAL_CTRL_STEP step, stru_s
|
||||||
int dc_signal_ao_set_val_without_check(const std::string &saddr, uint8_t data_type, void *p_data);
|
int dc_signal_ao_set_val_without_check(const std::string &saddr, uint8_t data_type, void *p_data);
|
||||||
|
|
||||||
// 数据中心参数信号注册接口
|
// 数据中心参数信号注册接口
|
||||||
int dc_signal_param(const std::string &saddr, const std::string &desc, uint8_t data_type, uint8_t ctrl_type, void **p_data, void **p_default_data, int num, signal_change_cb cb);
|
int dc_signal_param(const std::string &saddr, const std::string &desc, uint8_t data_type, const stru_signal_param ¶m, uint8_t ctrl_type, void **p_data, void **p_default_data, int num, signal_change_cb cb);
|
||||||
|
|
||||||
// 数据中心参数链接信号接口
|
// 数据中心参数链接信号接口
|
||||||
int dc_signal_param_link_with_callback(const std::string &saddr, void **p_data, int num, signal_change_cb cb);
|
int dc_signal_param_link_with_callback(const std::string &saddr, void **p_data, int num, signal_change_cb cb);
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,6 @@ int func_get_process_pid_by_name(const char *proc_name);
|
||||||
int func_process_exist_by_pid(int pid);
|
int func_process_exist_by_pid(int pid);
|
||||||
int func_get_process_path_by_pid(int pid, char *path, uint32_t len);
|
int func_get_process_path_by_pid(int pid, char *path, uint32_t len);
|
||||||
int func_get_process_self_path(char *path, uint32_t len);
|
int func_get_process_self_path(char *path, uint32_t len);
|
||||||
int func_get_process_self_dir(char *path, uint32_t len);
|
|
||||||
|
|
||||||
|
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,7 @@ extern "C" {
|
||||||
#define MY_GET_DATA_WITH_TYPE(p, type) (*(type *)(p))
|
#define MY_GET_DATA_WITH_TYPE(p, type) (*(type *)(p))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MMS_M_REASON_ALL_CALL 64 // 总召遥信遥测参数
|
|
||||||
#define MMS_M_REASON_READ_AO 65 // 读取参数
|
|
||||||
#define MMS_M_REASON_READ_PARAM 66 // 读取定值
|
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
|
@ -81,7 +79,6 @@ typedef struct
|
||||||
MMS_STR desc;
|
MMS_STR desc;
|
||||||
MMS_REF reference;
|
MMS_REF reference;
|
||||||
void *p_value;
|
void *p_value;
|
||||||
int set_zone; // 定值区
|
|
||||||
stru_mms_m_time time;
|
stru_mms_m_time time;
|
||||||
}stru_mms_m_out_value;
|
}stru_mms_m_out_value;
|
||||||
|
|
||||||
|
|
@ -160,14 +157,10 @@ int mms_m_out_get_connect_status(int fd, mms_m_out_status_cb p_func);
|
||||||
|
|
||||||
int mms_m_out_debug_print_swicth(int id, int debug_print_flag);
|
int mms_m_out_debug_print_swicth(int id, int debug_print_flag);
|
||||||
|
|
||||||
int mms_m_out_do_set_yk(stru_mms_m_event *p_event);
|
int mms_m_out_do_set_yk_or_param(stru_mms_m_event *p_event);
|
||||||
|
|
||||||
|
int mms_m_out_read_params(int app_fd);
|
||||||
|
int mms_m_out_read_param(int app_fd, const char *param_name);
|
||||||
int mms_m_out_read_ao_or_params(int app_fd, uint8_t type, const char *saddr);
|
|
||||||
|
|
||||||
int mms_m_out_bind_param_zone_signal(int app_fd, const char *zone_saddr);
|
|
||||||
int mms_m_out_set_rcb_numbers(int app_fd, const char *rcb_numbers);
|
|
||||||
|
|
||||||
int mms_m_out_get_value(int app_fd, mms_m_out_value_cb p_func);
|
int mms_m_out_get_value(int app_fd, mms_m_out_value_cb p_func);
|
||||||
char *mms_m_out_reason_str(int reason);
|
char *mms_m_out_reason_str(int reason);
|
||||||
|
|
@ -178,17 +171,6 @@ int mms_m_set_data_value(void *srt, void *dst, uint8_t type);
|
||||||
int mms_m_get_data_value_str(void *data, uint8_t type, char *str);
|
int mms_m_get_data_value_str(void *data, uint8_t type, char *str);
|
||||||
int mms_m_set_data_by_str(void *data, uint8_t type, const char *str);
|
int mms_m_set_data_by_str(void *data, uint8_t type, const char *str);
|
||||||
|
|
||||||
|
|
||||||
typedef void (*mms_m_file_read_cb)(int fd, const char *fn, const uint8_t *d, int len, bool mf, int e);
|
|
||||||
typedef void (*mms_m_file_op_cb)(int fd, const char *fn, int e);
|
|
||||||
typedef void (*mms_m_server_cb)(int fd, const char *vendor, const char *model, const char *rev, int log_st, int phy_st, int e);
|
|
||||||
typedef void (*mms_m_sg_cb)(int fd, const char *ld, int act_sg, int num_sg, int e);
|
|
||||||
|
|
||||||
int mms_m_read_file(int fd, const char *rf, mms_m_file_read_cb cb);
|
|
||||||
int mms_m_delete_file(int fd, const char *rf, mms_m_file_op_cb cb);
|
|
||||||
int mms_m_query_server(int fd, mms_m_server_cb cb);
|
|
||||||
int mms_m_read_sg_info(int fd, const char *ld, mms_m_sg_cb cb);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,6 @@ endif
|
||||||
LIB_REL = $(REL_ROOT_DIR)/lib
|
LIB_REL = $(REL_ROOT_DIR)/lib
|
||||||
EXE_REL = $(REL_ROOT_DIR)/exe
|
EXE_REL = $(REL_ROOT_DIR)/exe
|
||||||
|
|
||||||
# test目录(仅x86编译时复制产物用)
|
|
||||||
TEST_DIR = $(ROOT_DIR)/test
|
|
||||||
|
|
||||||
# 导出环境变量
|
# 导出环境变量
|
||||||
export CC
|
export CC
|
||||||
export CPP
|
export CPP
|
||||||
|
|
@ -78,4 +75,3 @@ export LIB_REL_MY
|
||||||
export LIB_REL
|
export LIB_REL
|
||||||
export EXE_REL
|
export EXE_REL
|
||||||
export LIB_61850
|
export LIB_61850
|
||||||
export TEST_DIR
|
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,10 @@
|
||||||
#define LOG_E(fmt, ...) LOG(COLOR_RED, "ERROR", fmt, ##__VA_ARGS__)
|
#define LOG_E(fmt, ...) LOG(COLOR_RED, "ERROR", fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
|
||||||
#define MMS_M_THREAD_RUN_TM 100*3 //线程运行间隔时间 100ms
|
#define MMS_M_THREAD_RUN_TM 100 //线程运行间隔时间 100ms
|
||||||
#define MMS_M_TIMER_T0 (120 * (MMS_M_THREAD_RUN_TM * 10 / 1000)) // 定时器T0 120秒
|
#define MMS_M_TIMER_T0 (120 * (MMS_M_THREAD_RUN_TM * 10 / 1000)) // 定时器T0 120秒
|
||||||
#define MMS_M_TIMER_T1 (60 * (MMS_M_THREAD_RUN_TM * 10 / 1000)) // 定时器T1 60秒
|
#define MMS_M_TIMER_T1 (60 * (MMS_M_THREAD_RUN_TM * 10 / 1000)) // 定时器T1 60秒
|
||||||
#define MMS_M_TIMER_T2 (30 * (MMS_M_THREAD_RUN_TM * 10 / 1000)) // 定时器T2 30秒
|
#define MMS_M_TIMER_T2 (15 * (MMS_M_THREAD_RUN_TM * 10 / 1000)) // 定时器T2 15秒
|
||||||
#define MMS_M_TIMER_T3 (20 * (MMS_M_THREAD_RUN_TM * 10 / 1000)) // 定时器T3 20秒
|
#define MMS_M_TIMER_T3 (20 * (MMS_M_THREAD_RUN_TM * 10 / 1000)) // 定时器T3 20秒
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -178,10 +178,6 @@ typedef struct
|
||||||
MmsValue *param_zone; // 参数定值区
|
MmsValue *param_zone; // 参数定值区
|
||||||
MmsValue *set_confirm; // 设置确认
|
MmsValue *set_confirm; // 设置确认
|
||||||
|
|
||||||
MMS_STR zone_saddr; // 关联的定值区信号saddr(如ao.0)
|
|
||||||
int current_zone; // 当前定值区号
|
|
||||||
std::vector<std::string> rcb_numbers; // 要订阅的RCB编号列表,空=默认"01"
|
|
||||||
|
|
||||||
stru_mms_m_run run; // 运行时数据结构
|
stru_mms_m_run run; // 运行时数据结构
|
||||||
|
|
||||||
std::vector<stru_ldev> ldevs; // 逻辑设备集合
|
std::vector<stru_ldev> ldevs; // 逻辑设备集合
|
||||||
|
|
@ -191,5 +187,3 @@ typedef struct
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void *mms_m_get_obj(int app_fd);
|
|
||||||
|
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
/**
|
|
||||||
* @file mms_m_ext.h
|
|
||||||
* @brief MMS 客户端扩展功能接口声明
|
|
||||||
*
|
|
||||||
* @details 本文件声明文件服务、服务器信息查询、定值组操作等扩展功能。
|
|
||||||
* 回调类型定义见 myMms_m.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "myMms_m.h"
|
|
||||||
#include "mms_m.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从远端 IED 读取文件
|
|
||||||
*
|
|
||||||
* 通过 MMS 文件服务 (ObtainFile) 从服务器下载指定文件。
|
|
||||||
* 文件内容通过回调分块返回。
|
|
||||||
*
|
|
||||||
* @param app_fd 客户端连接句柄
|
|
||||||
* @param rf 远端文件路径
|
|
||||||
* @param cb 文件读取回调,分块返回文件数据
|
|
||||||
* @return 0 成功发起请求,-1 参数无效或对象不存在
|
|
||||||
*/
|
|
||||||
int mms_m_read_file(int app_fd, const char *rf, mms_m_file_read_cb cb);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除远端 IED 上的文件
|
|
||||||
*
|
|
||||||
* @param app_fd 客户端连接句柄
|
|
||||||
* @param rf 远端文件路径
|
|
||||||
* @param cb 操作结果回调
|
|
||||||
* @return 0 成功,-1 参数无效或对象不存在
|
|
||||||
*/
|
|
||||||
int mms_m_delete_file(int app_fd, const char *rf, mms_m_file_op_cb cb);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询远端 IED 的服务器身份和运行状态
|
|
||||||
*
|
|
||||||
* 同时获取 MMS identify 服务返回的厂商/型号/版本信息,
|
|
||||||
* 以及 VMD 逻辑状态和物理状态。
|
|
||||||
*
|
|
||||||
* @param app_fd 客户端连接句柄
|
|
||||||
* @param cb 查询结果回调
|
|
||||||
* @return 0 成功,-1 参数无效或对象不存在
|
|
||||||
*/
|
|
||||||
int mms_m_query_server(int app_fd, mms_m_server_cb cb);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取远端 IED 的定值组信息
|
|
||||||
*
|
|
||||||
* 通过 SGCB 获取当前激活定值组号和定值组总数。
|
|
||||||
*
|
|
||||||
* @param app_fd 客户端连接句柄
|
|
||||||
* @param ld 逻辑设备名(如 "PROT")
|
|
||||||
* @param cb 查询结果回调
|
|
||||||
* @return 0 成功,-1 参数无效或对象不存在
|
|
||||||
*/
|
|
||||||
int mms_m_read_sg_info(int app_fd, const char *ld, mms_m_sg_cb cb);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <sstream>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
@ -14,7 +13,8 @@
|
||||||
|
|
||||||
// #include "conversions.h"
|
// #include "conversions.h"
|
||||||
|
|
||||||
|
#define MMS_M_REASON_ALL_CALL 64
|
||||||
|
#define MMS_M_REASON_READ_PARAM 65
|
||||||
#define MMS_M_RECONNECT_INTERVAL 60 // seconds
|
#define MMS_M_RECONNECT_INTERVAL 60 // seconds
|
||||||
|
|
||||||
static const std::string g_sgcb_edit_sg = "LLN0.SGCB.EditSG";
|
static const std::string g_sgcb_edit_sg = "LLN0.SGCB.EditSG";
|
||||||
|
|
@ -37,7 +37,6 @@ void Conversions_msTimeToGeneralizedTime(unsigned long msTime, unsigned char* bu
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//
|
|
||||||
static void sleep_ms(uint32_t ms)
|
static void sleep_ms(uint32_t ms)
|
||||||
{
|
{
|
||||||
struct timespec rqtp;
|
struct timespec rqtp;
|
||||||
|
|
@ -210,7 +209,7 @@ static int mms_m_get_mmsValue(MmsValue *mms_value, uint8_t &type, void *p_val)
|
||||||
strcpy((char *)p_val, MmsValue_toString(mms_value));
|
strcpy((char *)p_val, MmsValue_toString(mms_value));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_E("mms_m_get_mmsValue Failed to read value %d\n", mms_type);
|
LOG_E("mms_m_get_mmsValue Failed to read value (error code: %i)\n", MmsValue_getDataAccessError(mms_value));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,101 +286,6 @@ static int mms_m_param_value_sync(stru_point_item &item, stru_mms_m_event &event
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mms_m_send_read_ao(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
|
||||||
{
|
|
||||||
IedClientError err;
|
|
||||||
MmsValue *p_value = NULL;
|
|
||||||
|
|
||||||
stru_mms_m_out_value out_val;
|
|
||||||
stru_point_item *p_item = NULL;
|
|
||||||
|
|
||||||
// When saddr is empty, read ALL AO values; otherwise match specific saddr
|
|
||||||
bool match_all = (strlen(event.saddr) == 0);
|
|
||||||
for(int i = 0; i < obj.p_cfg->point.ao_num; i++)
|
|
||||||
{
|
|
||||||
if(match_all)
|
|
||||||
{
|
|
||||||
p_item = &obj.p_cfg->point.p_ao[i];
|
|
||||||
}
|
|
||||||
else if(obj.p_cfg->point.p_ao && 0 == strcmp(event.saddr, obj.p_cfg->point.p_ao[i].saddr))
|
|
||||||
{
|
|
||||||
p_item = &obj.p_cfg->point.p_ao[i];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(NULL == (p_value = IedConnection_readObject(obj.run.con, &err, p_item->reference, (FunctionalConstraint)p_item->fc)))
|
|
||||||
{
|
|
||||||
LOG_E("%s: saddr %s, ref %s read err:%s(%d)\n",
|
|
||||||
obj.cfg_path.c_str(), p_item->saddr, p_item->reference, mms_m_err_str(err), err);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
|
|
||||||
{
|
|
||||||
mms_m_show_MmsValue(p_value, &p_item->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(0 != mms_m_get_mmsValue(p_value, p_item->type, p_item->value.p_val[0]))
|
|
||||||
{
|
|
||||||
LOG_E("get mms value failed");
|
|
||||||
MmsValue_delete(p_value);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
mms_m_get_local_time(out_val.time);
|
|
||||||
out_val.p_value = p_item->value.p_val[0];
|
|
||||||
out_val.type = p_item->type;
|
|
||||||
out_val.quality = 0;
|
|
||||||
|
|
||||||
snprintf(out_val.name, sizeof(out_val.name), "%s", p_item->saddr);
|
|
||||||
snprintf(out_val.desc, sizeof(out_val.desc), "%s", p_item->desc);
|
|
||||||
snprintf(out_val.reference, sizeof(out_val.reference), "%s", p_item->reference);
|
|
||||||
out_val.reason = MMS_M_REASON_READ_AO; // call ao
|
|
||||||
out_val.app_fd = obj.obj_fd;
|
|
||||||
|
|
||||||
mms_m_put_value(obj, out_val);
|
|
||||||
|
|
||||||
// Check if this is the zone signal and detect zone change
|
|
||||||
if (strlen(obj.zone_saddr) > 0 && 0 == strcmp(obj.zone_saddr, p_item->saddr))
|
|
||||||
{
|
|
||||||
int new_zone = 0;
|
|
||||||
switch (p_item->type)
|
|
||||||
{
|
|
||||||
case MMS_INTEGER:
|
|
||||||
new_zone = MY_GET_DATA_WITH_TYPE(p_item->value.p_val[0], int32_t);
|
|
||||||
break;
|
|
||||||
case MMS_UNSIGNED:
|
|
||||||
new_zone = (int)MY_GET_DATA_WITH_TYPE(p_item->value.p_val[0], uint32_t);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (new_zone > 0 && (new_zone) != obj.current_zone)
|
|
||||||
{
|
|
||||||
LOG_I("%s: zone changed %d -> %d, trigger param re-read", obj.cfg_path.c_str(), obj.current_zone, new_zone);
|
|
||||||
obj.current_zone = new_zone;
|
|
||||||
|
|
||||||
stru_mms_m_event param_event;
|
|
||||||
memset(¶m_event, 0, sizeof(stru_mms_m_event));
|
|
||||||
param_event.ctrl_type = _MMS_M_EVENT_PARAM_READ;
|
|
||||||
mms_m_push_event(obj, param_event);
|
|
||||||
}
|
|
||||||
else if (obj.current_zone == 0 && new_zone > 0 && (new_zone) != obj.current_zone)
|
|
||||||
{
|
|
||||||
obj.current_zone = new_zone;
|
|
||||||
LOG_I("%s: init zone=%d from %s", obj.cfg_path.c_str(), obj.current_zone, p_item->saddr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MmsValue_delete(p_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int mms_m_send_ao_write(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
static int mms_m_send_ao_write(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
||||||
{
|
{
|
||||||
stru_point_item *p_item = NULL;
|
stru_point_item *p_item = NULL;
|
||||||
|
|
@ -432,75 +336,6 @@ static int mms_m_send_ao_write(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mms_m_send_read_param(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
|
||||||
{
|
|
||||||
MmsValue *p_value = NULL;
|
|
||||||
stru_mms_m_out_value out_val;
|
|
||||||
stru_point_item *p_item = NULL;
|
|
||||||
IedClientError err;
|
|
||||||
|
|
||||||
bool match_all = (strlen(event.saddr) == 0);
|
|
||||||
|
|
||||||
int act_sg = obj.current_zone;
|
|
||||||
|
|
||||||
LOG_I("%s: read params with zone=%d (bound=%s)", obj.cfg_path.c_str(), act_sg,
|
|
||||||
strlen(obj.zone_saddr) > 0 ? obj.zone_saddr : "unbound");
|
|
||||||
|
|
||||||
for(int i = 0; i < obj.p_cfg->point.param_num; i++)
|
|
||||||
{
|
|
||||||
if(match_all)
|
|
||||||
{
|
|
||||||
p_item = &obj.p_cfg->point.p_param[i];
|
|
||||||
}
|
|
||||||
else if(obj.p_cfg->point.p_param && 0 == strcmp(event.saddr, obj.p_cfg->point.p_param[i].saddr))
|
|
||||||
{
|
|
||||||
p_item = &obj.p_cfg->point.p_param[i];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(NULL == (p_value = IedConnection_readObject(obj.run.con, &err, p_item->reference, (FunctionalConstraint)p_item->fc)))
|
|
||||||
{
|
|
||||||
LOG_E("%s: saddr %s, ref %s read err:%s(%d)\n",
|
|
||||||
obj.cfg_path.c_str(), p_item->saddr, p_item->reference, mms_m_err_str(err), err);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
|
|
||||||
{
|
|
||||||
mms_m_show_MmsValue(p_value, &p_item->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// temp buffer to avoid polluting p_val[0] (== vec[0] after signal replace)
|
|
||||||
uint8_t tmp_val[MMS_M_DATA_STRING_LEN] = {0};
|
|
||||||
if(0 != mms_m_get_mmsValue(p_value, p_item->type, tmp_val))
|
|
||||||
{
|
|
||||||
LOG_E("get mms value failed, ref %s", p_item->reference);
|
|
||||||
MmsValue_delete(p_value);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
mms_m_get_local_time(out_val.time);
|
|
||||||
out_val.p_value = tmp_val;
|
|
||||||
out_val.type = p_item->type;
|
|
||||||
out_val.quality = 0;
|
|
||||||
|
|
||||||
snprintf(out_val.name, sizeof(out_val.name), "%s", p_item->saddr);
|
|
||||||
snprintf(out_val.desc, sizeof(out_val.desc), "%s", p_item->desc);
|
|
||||||
snprintf(out_val.reference, sizeof(out_val.reference), "%s", p_item->reference);
|
|
||||||
out_val.reason = MMS_M_REASON_READ_PARAM; // call param
|
|
||||||
out_val.app_fd = obj.obj_fd;
|
|
||||||
out_val.set_zone = act_sg;
|
|
||||||
|
|
||||||
mms_m_put_value(obj, out_val);
|
|
||||||
|
|
||||||
MmsValue_delete(p_value);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mms_m_send_param_write(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
static int mms_m_send_param_write(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
||||||
{
|
{
|
||||||
stru_point_item *p_item = NULL;
|
stru_point_item *p_item = NULL;
|
||||||
|
|
@ -521,7 +356,6 @@ static int mms_m_send_param_write(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string ref = p_item->reference;
|
std::string ref = p_item->reference;
|
||||||
size_t pos = ref.find("/");
|
size_t pos = ref.find("/");
|
||||||
if(pos == std::string::npos)
|
if(pos == std::string::npos)
|
||||||
|
|
@ -597,6 +431,60 @@ static int mms_m_send_param_write(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mms_m_send_read_ao(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
||||||
|
{
|
||||||
|
IedClientError err;
|
||||||
|
MmsValue *p_value = NULL;
|
||||||
|
|
||||||
|
stru_mms_m_out_value out_val;
|
||||||
|
stru_point_item *p_item = NULL;
|
||||||
|
|
||||||
|
for(int i = 0; i < obj.p_cfg->point.ao_num; i++)
|
||||||
|
{
|
||||||
|
if(obj.p_cfg->point.p_ao && 0 == strcmp(event.saddr, obj.p_cfg->point.p_ao[i].saddr))
|
||||||
|
{
|
||||||
|
p_item = &obj.p_cfg->point.p_ao[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(NULL == (p_value = IedConnection_readObject(obj.run.con, &err, p_item->reference, (FunctionalConstraint)p_item->fc)))
|
||||||
|
{
|
||||||
|
LOG_E("%s: saddr %s, ref %s read err:%s(%d)\n",
|
||||||
|
obj.cfg_path.c_str(), p_item->saddr, p_item->reference, mms_m_err_str(err), err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
|
||||||
|
{
|
||||||
|
mms_m_show_MmsValue(p_value, &p_item->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(0 != mms_m_get_mmsValue(p_value, p_item->type, p_item->value.p_val[0]))
|
||||||
|
{
|
||||||
|
LOG_E("get mms value failed");
|
||||||
|
MmsValue_delete(p_value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
mms_m_get_local_time(out_val.time);
|
||||||
|
out_val.p_value = p_item->value.p_val[0];
|
||||||
|
out_val.type = p_item->type;
|
||||||
|
out_val.quality = 0;
|
||||||
|
|
||||||
|
snprintf(out_val.name, sizeof(out_val.name), "%s", p_item->saddr);
|
||||||
|
snprintf(out_val.desc, sizeof(out_val.desc), "%s", p_item->desc);
|
||||||
|
snprintf(out_val.reference, sizeof(out_val.reference), "%s", p_item->reference);
|
||||||
|
out_val.reason = MMS_M_REASON_READ_PARAM; // call param
|
||||||
|
out_val.app_fd = obj.obj_fd;
|
||||||
|
|
||||||
|
mms_m_put_value(obj, out_val);
|
||||||
|
|
||||||
|
MmsValue_delete(p_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void mms_m_command_termination_handler(void *parameter, ControlObjectClient connection)
|
static void mms_m_command_termination_handler(void *parameter, ControlObjectClient connection)
|
||||||
{
|
{
|
||||||
|
|
@ -864,6 +752,7 @@ static void mms_m_send_call_all(stru_mms_m_obj &obj)
|
||||||
{
|
{
|
||||||
{&obj.p_cfg->point.st_num, obj.p_cfg->point.p_st},
|
{&obj.p_cfg->point.st_num, obj.p_cfg->point.p_st},
|
||||||
{&obj.p_cfg->point.mx_num, obj.p_cfg->point.p_mx},
|
{&obj.p_cfg->point.mx_num, obj.p_cfg->point.p_mx},
|
||||||
|
{&obj.p_cfg->point.ao_num, obj.p_cfg->point.p_ao},
|
||||||
};
|
};
|
||||||
|
|
||||||
IedClientError err;
|
IedClientError err;
|
||||||
|
|
@ -941,7 +830,6 @@ static void mms_m_do_send(stru_mms_m_obj &obj)
|
||||||
mms_m_send_set_callback(event, mms_m_send_ao_write(obj, event));
|
mms_m_send_set_callback(event, mms_m_send_ao_write(obj, event));
|
||||||
break;
|
break;
|
||||||
case _MMS_M_EVENT_PARAM_READ:
|
case _MMS_M_EVENT_PARAM_READ:
|
||||||
mms_m_send_read_param(obj, event);
|
|
||||||
break;
|
break;
|
||||||
case _MMS_M_EVENT_PARAM_WRITE:
|
case _MMS_M_EVENT_PARAM_WRITE:
|
||||||
mms_m_send_set_callback(event, mms_m_send_param_write(obj, event));
|
mms_m_send_set_callback(event, mms_m_send_param_write(obj, event));
|
||||||
|
|
@ -1067,8 +955,18 @@ static int mms_m_get_MmsValue(stru_point_value &point_value, stru_mms_m_out_valu
|
||||||
tm_flag = 1;
|
tm_flag = 1;
|
||||||
break;
|
break;
|
||||||
case MMS_BIT_STRING:
|
case MMS_BIT_STRING:
|
||||||
out_val.type = MMS_BIT_STRING;
|
point_value.quality = 0;
|
||||||
*(uint32_t *)p_val = MmsValue_getBitStringAsIntegerBigEndian(p_mms_values);
|
for (int i = 0; i < MmsValue_getBitStringSize(p_mms_values); i++)
|
||||||
|
{
|
||||||
|
if (MmsValue_getBitStringBit(p_mms_values, i))
|
||||||
|
{
|
||||||
|
point_value.quality |= (1 << i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
point_value.quality &= ~(1 << i);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -1339,24 +1237,6 @@ static int mms_m_icd_dataset_init(stru_mms_m_obj &obj, stru_ld_dataset &ld_ds)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool mms_m_rcb_number_match(stru_mms_m_obj &obj, const std::string &rpt_no)
|
|
||||||
{
|
|
||||||
if (obj.rcb_numbers.empty())
|
|
||||||
{
|
|
||||||
return (rpt_no == "01");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &n : obj.rcb_numbers)
|
|
||||||
{
|
|
||||||
if (rpt_no == n)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mms_m_icd_report_init(stru_mms_m_obj &obj, stru_ld_dataset &ld_ds, ACSIClass acsiClass)
|
static int mms_m_icd_report_init(stru_mms_m_obj &obj, stru_ld_dataset &ld_ds, ACSIClass acsiClass)
|
||||||
{
|
{
|
||||||
if(ACSI_CLASS_URCB != acsiClass && ACSI_CLASS_BRCB != acsiClass)
|
if(ACSI_CLASS_URCB != acsiClass && ACSI_CLASS_BRCB != acsiClass)
|
||||||
|
|
@ -1395,7 +1275,7 @@ static int mms_m_icd_report_init(stru_mms_m_obj &obj, stru_ld_dataset &ld_ds, AC
|
||||||
memset(&ln_rpt, 0, sizeof(ln_rpt));
|
memset(&ln_rpt, 0, sizeof(ln_rpt));
|
||||||
ln_rpt.type = acsiClass;
|
ln_rpt.type = acsiClass;
|
||||||
|
|
||||||
if(mms_m_rcb_number_match(obj, rpt_no))
|
if(0 == rpt_no.compare("01"))
|
||||||
{
|
{
|
||||||
ln_rpt.rpt_ref = rpt_ref + rpt_name;
|
ln_rpt.rpt_ref = rpt_ref + rpt_name;
|
||||||
ln_rpt.rpt_ref_with_no = rpt_ref + rpt_name + rpt_no;
|
ln_rpt.rpt_ref_with_no = rpt_ref + rpt_name + rpt_no;
|
||||||
|
|
@ -1597,8 +1477,7 @@ static void mms_m_do_comm(stru_mms_m_obj &obj)
|
||||||
|
|
||||||
if(IED_STATE_CONNECTED != run.con_state && IED_STATE_CONNECTED == run.old_con_state)
|
if(IED_STATE_CONNECTED != run.con_state && IED_STATE_CONNECTED == run.old_con_state)
|
||||||
{
|
{
|
||||||
LOG_I("mms_m %s connected %s:%d offline, reset RCB subscription", obj.ied_name.c_str(), obj.run.ip.c_str(), obj.run.port);
|
LOG_I("mms_m %s connected %s:%d offline", obj.ied_name.c_str(), obj.run.ip.c_str(), obj.run.port);
|
||||||
obj.run.running_init = false;
|
|
||||||
|
|
||||||
if(run.out_status_cb)
|
if(run.out_status_cb)
|
||||||
{
|
{
|
||||||
|
|
@ -1668,21 +1547,6 @@ static void mms_m_do_call_GI(void *arg)
|
||||||
mms_m_timer_start(p_obj->run.timer[_MMS_M_TIMER_T1]);
|
mms_m_timer_start(p_obj->run.timer[_MMS_M_TIMER_T1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mms_m_do_call_all_ao_param(void *arg)
|
|
||||||
{
|
|
||||||
stru_mms_m_obj *p_obj = (stru_mms_m_obj *)arg;
|
|
||||||
if(NULL == p_obj)
|
|
||||||
{
|
|
||||||
LOG_E("mms_m_do_call_all_ao_param p_obj is NULL");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mms_m_out_read_ao_or_params(p_obj->obj_fd, _MMS_M_EVENT_AO_READ, nullptr);
|
|
||||||
mms_m_out_read_ao_or_params(p_obj->obj_fd, _MMS_M_EVENT_PARAM_READ, nullptr);
|
|
||||||
|
|
||||||
mms_m_timer_start(p_obj->run.timer[_MMS_M_TIMER_T2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mms_m_timer_init(stru_mms_m_obj &obj)
|
static void mms_m_timer_init(stru_mms_m_obj &obj)
|
||||||
{
|
{
|
||||||
stru_mms_m_timer *p_timer = obj.run.timer;
|
stru_mms_m_timer *p_timer = obj.run.timer;
|
||||||
|
|
@ -1704,8 +1568,8 @@ static void mms_m_timer_init(stru_mms_m_obj &obj)
|
||||||
|
|
||||||
|
|
||||||
p_timer[_MMS_M_TIMER_T2].tmout = MMS_M_TIMER_T2;
|
p_timer[_MMS_M_TIMER_T2].tmout = MMS_M_TIMER_T2;
|
||||||
p_timer[_MMS_M_TIMER_T2].arg = &obj;
|
p_timer[_MMS_M_TIMER_T2].arg = NULL;
|
||||||
p_timer[_MMS_M_TIMER_T2].p_func = mms_m_do_call_all_ao_param;
|
p_timer[_MMS_M_TIMER_T2].p_func = NULL;
|
||||||
|
|
||||||
p_timer[_MMS_M_TIMER_T3].tmout = MMS_M_TIMER_T3;
|
p_timer[_MMS_M_TIMER_T3].tmout = MMS_M_TIMER_T3;
|
||||||
p_timer[_MMS_M_TIMER_T3].arg = NULL;
|
p_timer[_MMS_M_TIMER_T3].arg = NULL;
|
||||||
|
|
@ -1939,11 +1803,6 @@ static int mms_m_ied_init(stru_mms_m_obj &obj)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void *mms_m_get_obj(int app_fd){
|
|
||||||
auto it = g_mms_m_obj_map.find(app_fd);
|
|
||||||
return (it != g_mms_m_obj_map.end()) ? (void*)it->second : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mms_m_out_init(stru_cfg *p_cfg, int debug_print_flag, uint32_t connectionTimeout)
|
int mms_m_out_init(stru_cfg *p_cfg, int debug_print_flag, uint32_t connectionTimeout)
|
||||||
{
|
{
|
||||||
if(NULL == p_cfg)
|
if(NULL == p_cfg)
|
||||||
|
|
@ -2038,7 +1897,7 @@ int mms_m_out_debug_print_swicth(int app_fd, int debug_print_flag)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mms_m_out_do_set_yk(stru_mms_m_event *p_event)
|
int mms_m_out_do_set_yk_or_param(stru_mms_m_event *p_event)
|
||||||
{
|
{
|
||||||
if(NULL == p_event)
|
if(NULL == p_event)
|
||||||
{
|
{
|
||||||
|
|
@ -2072,7 +1931,7 @@ int mms_m_out_do_set_yk(stru_mms_m_event *p_event)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mms_m_out_bind_param_zone_signal(int app_fd, const char *zone_saddr)
|
int mms_m_out_read_params(int app_fd)
|
||||||
{
|
{
|
||||||
if(g_mms_m_obj_map.find(app_fd) == g_mms_m_obj_map.end())
|
if(g_mms_m_obj_map.find(app_fd) == g_mms_m_obj_map.end())
|
||||||
{
|
{
|
||||||
|
|
@ -2082,116 +1941,34 @@ int mms_m_out_bind_param_zone_signal(int app_fd, const char *zone_saddr)
|
||||||
|
|
||||||
stru_mms_m_obj &obj = *g_mms_m_obj_map[app_fd];
|
stru_mms_m_obj &obj = *g_mms_m_obj_map[app_fd];
|
||||||
|
|
||||||
if(nullptr == zone_saddr || strlen(zone_saddr) == 0)
|
|
||||||
{
|
|
||||||
LOG_E("zone_saddr is NULL or empty\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
strncpy(obj.zone_saddr, zone_saddr, sizeof(obj.zone_saddr));
|
|
||||||
obj.current_zone = 0;
|
|
||||||
|
|
||||||
LOG_I("%s: bind zone signal %s", obj.cfg_path.c_str(), zone_saddr);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mms_m_out_set_rcb_numbers(int app_fd, const char *rcb_numbers)
|
|
||||||
{
|
|
||||||
if(g_mms_m_obj_map.find(app_fd) == g_mms_m_obj_map.end())
|
|
||||||
{
|
|
||||||
LOG_E("IED with id %d not found\n", app_fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
stru_mms_m_obj &obj = *g_mms_m_obj_map[app_fd];
|
|
||||||
|
|
||||||
obj.rcb_numbers.clear();
|
|
||||||
|
|
||||||
if(nullptr == rcb_numbers || strlen(rcb_numbers) == 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string input(rcb_numbers);
|
|
||||||
|
|
||||||
if(input == "*")
|
|
||||||
{
|
|
||||||
obj.rcb_numbers.push_back("*");
|
|
||||||
LOG_I("%s: rcb_numbers set to '*' (all)", obj.cfg_path.c_str());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string token;
|
|
||||||
std::stringstream ss(input);
|
|
||||||
while(std::getline(ss, token, ','))
|
|
||||||
{
|
|
||||||
while(!token.empty() && token[0] == ' ')
|
|
||||||
token.erase(0, 1);
|
|
||||||
while(!token.empty() && token[token.length() - 1] == ' ')
|
|
||||||
token.erase(token.length() - 1, 1);
|
|
||||||
|
|
||||||
if(token.empty()) continue;
|
|
||||||
|
|
||||||
if(token.length() != 2 || !isdigit(token[0]) || !isdigit(token[1]))
|
|
||||||
{
|
|
||||||
LOG_E("%s: invalid rcb number '%s'", obj.cfg_path.c_str(), token.c_str());
|
|
||||||
obj.rcb_numbers.clear();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.rcb_numbers.push_back(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(obj.rcb_numbers.empty())
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(MMS_M_DEBUG_PRINT_ON == obj.debug_print_flag)
|
|
||||||
{
|
|
||||||
std::string dbg;
|
|
||||||
for(size_t i = 0; i < obj.rcb_numbers.size(); i++)
|
|
||||||
{
|
|
||||||
if(i > 0) dbg += ",";
|
|
||||||
dbg += obj.rcb_numbers[i];
|
|
||||||
}
|
|
||||||
LOG_I("%s: rcb_numbers set to [%s]", obj.cfg_path.c_str(), dbg.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mms_m_out_read_ao_or_params(int app_fd, uint8_t type, const char *saddr)
|
|
||||||
{
|
|
||||||
if(g_mms_m_obj_map.find(app_fd) == g_mms_m_obj_map.end())
|
|
||||||
{
|
|
||||||
LOG_E("IED with id %d not found\n", app_fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(type != _MMS_M_EVENT_AO_READ && type != _MMS_M_EVENT_PARAM_READ)
|
|
||||||
{
|
|
||||||
LOG_E("type %d is not supported", type);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
stru_mms_m_obj &obj = *g_mms_m_obj_map[app_fd];
|
|
||||||
|
|
||||||
stru_mms_m_event event;
|
stru_mms_m_event event;
|
||||||
memset(&event, 0, sizeof(stru_mms_m_event));
|
memset(&event, 0, sizeof(stru_mms_m_event));
|
||||||
|
|
||||||
event.ctrl_type = type;
|
event.ctrl_type = _MMS_M_EVENT_AO_READ;
|
||||||
if(nullptr != saddr)
|
|
||||||
{
|
|
||||||
strncpy(event.saddr, saddr, sizeof(event.saddr));
|
|
||||||
}
|
|
||||||
mms_m_push_event(obj, event);
|
mms_m_push_event(obj, event);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mms_m_out_read_param(int app_fd, const char *param_name)
|
||||||
|
{
|
||||||
|
if(g_mms_m_obj_map.find(app_fd) == g_mms_m_obj_map.end())
|
||||||
|
{
|
||||||
|
LOG_E("IED with id %d not found\n", app_fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
stru_mms_m_obj &obj = *g_mms_m_obj_map[app_fd];
|
||||||
|
|
||||||
|
stru_mms_m_event event;
|
||||||
|
memset(&event, 0, sizeof(stru_mms_m_event));
|
||||||
|
|
||||||
|
event.ctrl_type = _MMS_M_EVENT_AO_READ;
|
||||||
|
strncpy(event.saddr, param_name, sizeof(param_name));
|
||||||
|
mms_m_push_event(obj, event);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
/**
|
|
||||||
* @file mms_m_file.cpp
|
|
||||||
* @brief MMS 文件服务客户端实现
|
|
||||||
*
|
|
||||||
* @details 基于 libiec61850 MmsConnection 底层接口实现:
|
|
||||||
* - 远端文件读取 (ObtainFile)
|
|
||||||
* - 远端文件删除
|
|
||||||
*
|
|
||||||
* 文件读取流程:
|
|
||||||
* 1. MmsConnection_fileOpen() 打开远端文件,获取文件大小
|
|
||||||
* 2. MmsConnection_fileRead() 循环分块读取文件内容
|
|
||||||
* 3. MmsConnection_fileClose() 关闭文件句柄
|
|
||||||
* 4. 通过回调将完整文件数据返回上层
|
|
||||||
*
|
|
||||||
* 【当前状态】文件打开已验证,异步分块读取待实现。
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "myMms_m.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "mms_m.h"
|
|
||||||
#include "mms_m_ext.h"
|
|
||||||
#include "iec61850_client.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从远端 IED 读取文件
|
|
||||||
*
|
|
||||||
* 打开远端文件并获取文件大小。完整的分块异步读取流程待实现。
|
|
||||||
*
|
|
||||||
* @param app_fd 客户端连接句柄
|
|
||||||
* @param rf 远端文件路径
|
|
||||||
* @param cb 文件读取回调,分块返回文件数据
|
|
||||||
* @return 0 成功发起请求,-1 失败
|
|
||||||
*/
|
|
||||||
int mms_m_read_file(int app_fd, const char *rf, mms_m_file_read_cb cb)
|
|
||||||
{
|
|
||||||
if (NULL == rf || NULL == cb)
|
|
||||||
{
|
|
||||||
LOG_E("Invalid parameter, rf %p, cb %p", rf, cb);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
stru_mms_m_obj *p_obj = (stru_mms_m_obj *)mms_m_get_obj(app_fd);
|
|
||||||
if (NULL == p_obj)
|
|
||||||
{
|
|
||||||
LOG_E("app_fd %d not found", app_fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 获取底层 MmsConnection 以使用文件服务 API */
|
|
||||||
MmsConnection mc = IedConnection_getMmsConnection(p_obj->run.con);
|
|
||||||
if (NULL == mc)
|
|
||||||
{
|
|
||||||
LOG_E("%s: getMmsConnection failed", p_obj->cfg_path.c_str());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
MmsError me;
|
|
||||||
uint32_t file_size = 0;
|
|
||||||
uint64_t last_modified = 0;
|
|
||||||
|
|
||||||
/* 打开远端文件,获取大小和修改时间 */
|
|
||||||
int32_t frsm_id = MmsConnection_fileOpen(mc, &me, rf, 0,
|
|
||||||
&file_size, &last_modified);
|
|
||||||
|
|
||||||
if (MMS_ERROR_NONE != me)
|
|
||||||
{
|
|
||||||
LOG_E("%s: fileOpen(%s) failed: %d",
|
|
||||||
p_obj->cfg_path.c_str(), rf, me);
|
|
||||||
cb(app_fd, rf, NULL, 0, false, IED_ERROR_UNKNOWN);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_I("%s: file %s opened, size=%u, lastModified=%lu",
|
|
||||||
p_obj->cfg_path.c_str(), rf, file_size, last_modified);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: 实现异步分块读取
|
|
||||||
*
|
|
||||||
* 需要:
|
|
||||||
* 1. 分配接收缓冲区 (malloc file_size)
|
|
||||||
* 2. 循环调用 MmsConnection_fileReadAsync() 读取数据块
|
|
||||||
* 3. 在 read_cb 回调中追加数据到缓冲区
|
|
||||||
* 4. moreFollows=false 时说明读取完毕,调用上层回调
|
|
||||||
* 5. 读取完毕或出错时 MmsConnection_fileClose()
|
|
||||||
*/
|
|
||||||
MmsConnection_fileClose(mc, &me, frsm_id);
|
|
||||||
|
|
||||||
cb(app_fd, rf, NULL, 0, false, IED_ERROR_SERVICE_NOT_IMPLEMENTED);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除远端 IED 上的文件
|
|
||||||
*
|
|
||||||
* @param app_fd 客户端连接句柄
|
|
||||||
* @param rf 远端文件路径
|
|
||||||
* @param cb 操作结果回调,err==0 表示删除成功
|
|
||||||
* @return 0 成功,-1 失败
|
|
||||||
*/
|
|
||||||
int mms_m_delete_file(int app_fd, const char *rf, mms_m_file_op_cb cb)
|
|
||||||
{
|
|
||||||
if (NULL == rf || NULL == cb)
|
|
||||||
{
|
|
||||||
LOG_E("Invalid parameter, rf %p, cb %p", rf, cb);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
stru_mms_m_obj *p_obj = (stru_mms_m_obj *)mms_m_get_obj(app_fd);
|
|
||||||
if (NULL == p_obj)
|
|
||||||
{
|
|
||||||
LOG_E("app_fd %d not found", app_fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
MmsConnection mc = IedConnection_getMmsConnection(p_obj->run.con);
|
|
||||||
MmsError me;
|
|
||||||
|
|
||||||
MmsConnection_fileDelete(mc, &me, rf);
|
|
||||||
|
|
||||||
IedClientError ie = (MMS_ERROR_NONE == me) ? IED_ERROR_OK
|
|
||||||
: IED_ERROR_UNKNOWN;
|
|
||||||
cb(app_fd, rf, ie);
|
|
||||||
|
|
||||||
return (MMS_ERROR_NONE == me) ? 0 : -1;
|
|
||||||
}
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
/**
|
|
||||||
* @file mms_m_server.cpp
|
|
||||||
* @brief 远端 IED 服务器信息查询
|
|
||||||
*
|
|
||||||
* @details 通过 MMS identify 和 getServerStatus 服务获取:
|
|
||||||
* - 厂商名称 (vendorName)
|
|
||||||
* - 型号名称 (modelName)
|
|
||||||
* - 版本号 (revision)
|
|
||||||
* - VMD 逻辑状态 (0=允许变更, 1=禁止变更, 2=受限, 3=支持服务)
|
|
||||||
* - VMD 物理状态 (0=运行, 1=部分, 2=停运, 3=待调试)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "myMms_m.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "mms_m.h"
|
|
||||||
#include "mms_m_ext.h"
|
|
||||||
#include "iec61850_client.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询远端 IED 的服务器身份和运行状态
|
|
||||||
*
|
|
||||||
* 同时获取 MMS identify 和 getServerStatus 的结果,
|
|
||||||
* 通过回调一次性返回。
|
|
||||||
*
|
|
||||||
* @param app_fd 客户端连接句柄
|
|
||||||
* @param cb 查询结果回调
|
|
||||||
* @return 0 成功,-1 失败
|
|
||||||
*/
|
|
||||||
int mms_m_query_server(int app_fd, mms_m_server_cb cb)
|
|
||||||
{
|
|
||||||
if (NULL == cb)
|
|
||||||
{
|
|
||||||
LOG_E("cb is NULL");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
stru_mms_m_obj *p_obj = (stru_mms_m_obj *)mms_m_get_obj(app_fd);
|
|
||||||
if (NULL == p_obj)
|
|
||||||
{
|
|
||||||
LOG_E("app_fd %d not found", app_fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
MmsConnection mc = IedConnection_getMmsConnection(p_obj->run.con);
|
|
||||||
MmsError me;
|
|
||||||
|
|
||||||
/* MMS identify 服务:获取厂商/型号/版本 */
|
|
||||||
MmsServerIdentity *p_id = MmsConnection_identify(mc, &me);
|
|
||||||
|
|
||||||
/* MMS getServerStatus 服务:获取 VMD 运行状态 */
|
|
||||||
int log_st = 0;
|
|
||||||
int phy_st = 0;
|
|
||||||
MmsConnection_getServerStatus(mc, &me, &log_st, &phy_st, false);
|
|
||||||
|
|
||||||
const char *vendor = p_id ? p_id->vendorName : "";
|
|
||||||
const char *model = p_id ? p_id->modelName : "";
|
|
||||||
const char *rev = p_id ? p_id->revision : "";
|
|
||||||
|
|
||||||
IedClientError err = (MMS_ERROR_NONE != me) ? IED_ERROR_UNKNOWN
|
|
||||||
: IED_ERROR_OK;
|
|
||||||
|
|
||||||
cb(app_fd, vendor, model, rev, log_st, phy_st, err);
|
|
||||||
|
|
||||||
if (p_id)
|
|
||||||
{
|
|
||||||
MmsServerIdentity_destroy(p_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
/**
|
|
||||||
* @file mms_m_sg.cpp
|
|
||||||
* @brief 远端 IED 定值组 (Setting Group) 操作
|
|
||||||
*
|
|
||||||
* @details 定值组允许 IED 维护多套运行参数并可在线切换。
|
|
||||||
* 本模块通过 LLN0.SGCB 读取当前定值组状态。
|
|
||||||
*
|
|
||||||
* SGCB (Setting Group Control Block) 关键属性:
|
|
||||||
* - ActSG : 当前激活的定值组号
|
|
||||||
* - NumOfSG: 定值组总数
|
|
||||||
* - EditSG : 当前编辑的定值组号
|
|
||||||
*
|
|
||||||
* 后续可扩展:
|
|
||||||
* - 切换激活定值组 (Write ActSG)
|
|
||||||
* - 编辑确认 (Write CnfEdit)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "myMms_m.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "mms_m.h"
|
|
||||||
#include "mms_m_ext.h"
|
|
||||||
#include "iec61850_client.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 读取远端 IED 的定值组信息
|
|
||||||
*
|
|
||||||
* 通过读取 LLN0.SGCB 获取当前激活定值组号和定值组总数。
|
|
||||||
* 读取失败时返回默认值 act_sg=1, num_sg=1。
|
|
||||||
*
|
|
||||||
* @param app_fd 客户端连接句柄
|
|
||||||
* @param ld 逻辑设备名(如 "PROT"、"CTRL")
|
|
||||||
* @param cb 查询结果回调
|
|
||||||
* @return 0 成功,-1 失败
|
|
||||||
*/
|
|
||||||
int mms_m_read_sg_info(int app_fd, const char *ld, mms_m_sg_cb cb)
|
|
||||||
{
|
|
||||||
if (NULL == ld || NULL == cb)
|
|
||||||
{
|
|
||||||
LOG_E("Invalid parameter, ld %p, cb %p", ld, cb);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
stru_mms_m_obj *p_obj = (stru_mms_m_obj *)mms_m_get_obj(app_fd);
|
|
||||||
if (NULL == p_obj)
|
|
||||||
{
|
|
||||||
LOG_E("app_fd %d not found", app_fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int act_sg = 1;
|
|
||||||
int num_sg = 1;
|
|
||||||
IedClientError err = IED_ERROR_OK;
|
|
||||||
|
|
||||||
/* 拼接 SGCB 对象引用:{LD}/LLN0.SGCB */
|
|
||||||
char sgcbref[256];
|
|
||||||
snprintf(sgcbref, sizeof(sgcbref), "%s/LLN0.SGCB", ld);
|
|
||||||
|
|
||||||
/* 读取 SGCB 的 FC=ST 属性(包含 ActSG, NumOfSG, EditSG) */
|
|
||||||
MmsValue *p_val = IedConnection_readObject(p_obj->run.con, &err,
|
|
||||||
sgcbref, IEC61850_FC_ST);
|
|
||||||
|
|
||||||
if (p_val && IED_ERROR_OK == err)
|
|
||||||
{
|
|
||||||
MmsValue *p_act = MmsValue_getElement(p_val, 0);
|
|
||||||
MmsValue *p_num = MmsValue_getElement(p_val, 1);
|
|
||||||
|
|
||||||
if (p_act)
|
|
||||||
{
|
|
||||||
act_sg = MmsValue_toInt32(p_act);
|
|
||||||
}
|
|
||||||
if (p_num)
|
|
||||||
{
|
|
||||||
num_sg = MmsValue_toInt32(p_num);
|
|
||||||
}
|
|
||||||
|
|
||||||
MmsValue_delete(p_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
cb(app_fd, ld, act_sg, num_sg, err);
|
|
||||||
|
|
||||||
return (IED_ERROR_OK == err) ? 0 : -1;
|
|
||||||
}
|
|
||||||
|
|
@ -21,17 +21,4 @@
|
||||||
* @param icd ICD 数据结构引用
|
* @param icd ICD 数据结构引用
|
||||||
* @return 0 成功,-1 失败
|
* @return 0 成功,-1 失败
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* 联锁检查回调类型
|
|
||||||
* @param saddr 控制点短地址
|
|
||||||
* @param state 请求的状态值
|
|
||||||
* @return true 允许操作,false 拒绝
|
|
||||||
*/
|
|
||||||
typedef bool (*mms_s_interlock_cb)(const char *saddr, int state);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置联锁检查回调
|
|
||||||
*/
|
|
||||||
void mms_s_set_interlock_cb(mms_s_interlock_cb cb);
|
|
||||||
int control_init(IedServer server, stru_icd &icd);
|
int control_init(IedServer server, stru_icd &icd);
|
||||||
|
|
@ -462,8 +462,6 @@ typedef struct
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
ModelNode *node;
|
ModelNode *node;
|
||||||
DataAttribute *da_t;
|
|
||||||
DataAttribute *da_q;
|
|
||||||
}stru_saddr_point;
|
}stru_saddr_point;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -43,45 +43,6 @@ LOCAL IedServer gp_iedServer = NULL;
|
||||||
/* 调试输出开关 */
|
/* 调试输出开关 */
|
||||||
LOCAL bool g_dbg_switch = false;
|
LOCAL bool g_dbg_switch = false;
|
||||||
|
|
||||||
|
|
||||||
/* RCB event handler for monitoring client report subscriptions */
|
|
||||||
LOCAL void rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError)
|
|
||||||
{
|
|
||||||
if(mms_s_dbg_get())
|
|
||||||
{
|
|
||||||
const char* name = ReportControlBlock_getName(rcb);
|
|
||||||
char* rptId = ReportControlBlock_getRptID(rcb);
|
|
||||||
char* dataSet = ReportControlBlock_getDataSet(rcb);
|
|
||||||
switch (event)
|
|
||||||
{
|
|
||||||
case RCB_EVENT_ENABLE: LOG_I("RCB %s ENABLED rptId=%s datSet=%s trigOps=0x%x", name, rptId, dataSet, rcb->trgOps); break;
|
|
||||||
case RCB_EVENT_DISABLE: LOG_I("RCB %s DISABLED", name); break;
|
|
||||||
case RCB_EVENT_RESERVED: LOG_I("RCB %s RESERVED", name); break;
|
|
||||||
case RCB_EVENT_UNRESERVED:LOG_I("RCB %s UNRESERVED", name); break;
|
|
||||||
case RCB_EVENT_GI: LOG_I("RCB %s GI triggered", name); break;
|
|
||||||
case RCB_EVENT_SET_PARAMETER:
|
|
||||||
if (serviceError != DATA_ACCESS_ERROR_SUCCESS)
|
|
||||||
{
|
|
||||||
LOG_E("RCB %s SET param=%s FAILED err=%d", name, parameterName ? parameterName : "?", serviceError);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG_I("RCB %s SET param=%s OK trigOps=0x%x", name, parameterName ? parameterName : "?", rcb->trgOps);
|
|
||||||
if (parameterName && strcmp(parameterName, "TrgOps") == 0)
|
|
||||||
{
|
|
||||||
rcb->trgOps |= 0x01;
|
|
||||||
LOG_I("RCB %s forced dchg, trigOps=0x%x", name, rcb->trgOps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RCB_EVENT_GET_PARAMETER: LOG_I("RCB %s GET param=%s err=%d", name, parameterName ? parameterName : "?", serviceError); break;
|
|
||||||
default: LOG_I("RCB %s event=%d", name, event); break;
|
|
||||||
}
|
|
||||||
free(rptId);
|
|
||||||
free(dataSet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** SIGINT 信号处理:通知后台线程退出 */
|
/** SIGINT 信号处理:通知后台线程退出 */
|
||||||
LOCAL void sigint_handler(int signum)
|
LOCAL void sigint_handler(int signum)
|
||||||
{
|
{
|
||||||
|
|
@ -307,7 +268,6 @@ int mms_s_init(const char *icd_path, int port)
|
||||||
param_init(gp_iedServer, *gp_icd);
|
param_init(gp_iedServer, *gp_icd);
|
||||||
|
|
||||||
file_init(gp_iedServer);
|
file_init(gp_iedServer);
|
||||||
IedServer_setRCBEventHandler(gp_iedServer, rcbEventHandler, NULL);
|
|
||||||
|
|
||||||
IedServer_start(gp_iedServer, port);
|
IedServer_start(gp_iedServer, port);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,14 +32,6 @@
|
||||||
LOCAL std::vector<stru_mms_s_control> g_vec_control;
|
LOCAL std::vector<stru_mms_s_control> g_vec_control;
|
||||||
/** 控制操作回调函数 */
|
/** 控制操作回调函数 */
|
||||||
LOCAL mms_s_control_cb cb_control = NULL;
|
LOCAL mms_s_control_cb cb_control = NULL;
|
||||||
/** 联锁检查回调 */
|
|
||||||
LOCAL mms_s_interlock_cb cb_interlock = NULL;
|
|
||||||
|
|
||||||
void mms_s_set_interlock_cb(mms_s_interlock_cb cb)
|
|
||||||
{
|
|
||||||
cb_interlock = cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 控制操作处理器(Operate 阶段)
|
* 控制操作处理器(Operate 阶段)
|
||||||
|
|
@ -295,7 +287,7 @@ int control_init(IedServer server, stru_icd &icd)
|
||||||
|
|
||||||
if(g_vec_control.empty())
|
if(g_vec_control.empty())
|
||||||
{
|
{
|
||||||
LOG_I("g_vec_control is empty, no controls registered");
|
LOG_E("g_vec_control is empty");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -182,10 +182,10 @@ LOCAL int parse_ReportControl_TrgOps(XMLElement *ele_reportControl, stru_TrgOps
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
trgOps.dchg = (ele_trgOps->Attribute(key_dchg) == nullptr ? false : strcmp(ele_trgOps->Attribute(key_dchg), "true") == 0);
|
trgOps.dchg = (ele_trgOps->Attribute(key_dchg) == nullptr ? "false" : ele_trgOps->Attribute(key_dchg)) == "true";
|
||||||
trgOps.qchg = (ele_trgOps->Attribute(key_qchg) == nullptr ? false : strcmp(ele_trgOps->Attribute(key_qchg), "true") == 0);
|
trgOps.qchg = (ele_trgOps->Attribute(key_qchg) == nullptr ? "false" : ele_trgOps->Attribute(key_qchg)) == "true";
|
||||||
trgOps.dupd = (ele_trgOps->Attribute(key_dupd) == nullptr ? false : strcmp(ele_trgOps->Attribute(key_dupd), "true") == 0);
|
trgOps.dupd = (ele_trgOps->Attribute(key_dupd) == nullptr ? "false" : ele_trgOps->Attribute(key_dupd)) == "true";
|
||||||
trgOps.period = (ele_trgOps->Attribute(key_period) == nullptr ? false : strcmp(ele_trgOps->Attribute(key_period), "true") == 0);
|
trgOps.period = (ele_trgOps->Attribute(key_period) == nullptr ? "false" : ele_trgOps->Attribute(key_period)) == "true";
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -211,12 +211,12 @@ LOCAL int parse_ReportControl_OptFields(XMLElement *ele_reportControl, stru_OptF
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
optFields.seqNum = (ele_optFields->Attribute(key_seqNum) == nullptr ? false : strcmp(ele_optFields->Attribute(key_seqNum), "true") == 0);
|
optFields.seqNum = (ele_optFields->Attribute(key_seqNum) == nullptr ? "false" : ele_optFields->Attribute(key_seqNum)) == "true";
|
||||||
optFields.timeStamp = (ele_optFields->Attribute(key_timeStamp) == nullptr ? false : strcmp(ele_optFields->Attribute(key_timeStamp), "true") == 0);
|
optFields.timeStamp = (ele_optFields->Attribute(key_timeStamp) == nullptr ? "false" : ele_optFields->Attribute(key_timeStamp)) == "true";
|
||||||
optFields.reasonCode = (ele_optFields->Attribute(key_reasonCode) == nullptr ? false : strcmp(ele_optFields->Attribute(key_reasonCode), "true") == 0);
|
optFields.reasonCode = (ele_optFields->Attribute(key_reasonCode) == nullptr ? "false" : ele_optFields->Attribute(key_reasonCode)) == "true";
|
||||||
optFields.dataRef = (ele_optFields->Attribute(key_dataRef) == nullptr ? false : strcmp(ele_optFields->Attribute(key_dataRef), "true") == 0);
|
optFields.dataRef = (ele_optFields->Attribute(key_dataRef) == nullptr ? "false" : ele_optFields->Attribute(key_dataRef)) == "true";
|
||||||
optFields.entryID = (ele_optFields->Attribute(key_entryID) == nullptr ? false : strcmp(ele_optFields->Attribute(key_entryID), "true") == 0);
|
optFields.entryID = (ele_optFields->Attribute(key_entryID) == nullptr ? "false" : ele_optFields->Attribute(key_entryID)) == "true";
|
||||||
optFields.configRef = (ele_optFields->Attribute(key_configRef) == nullptr ? false : strcmp(ele_optFields->Attribute(key_configRef), "true") == 0);
|
optFields.configRef = (ele_optFields->Attribute(key_configRef) == nullptr ? "false" : ele_optFields->Attribute(key_configRef)) == "true";
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -611,7 +611,7 @@ LOCAL int model_DataSet_init(LogicalNode *LLN0, stru_icd &icd, std::map<std::str
|
||||||
for(it_fcda = p_dataset->vec_fcda.begin(); it_fcda != p_dataset->vec_fcda.end(); it_fcda++) // 遍历数据集成员
|
for(it_fcda = p_dataset->vec_fcda.begin(); it_fcda != p_dataset->vec_fcda.end(); it_fcda++) // 遍历数据集成员
|
||||||
{
|
{
|
||||||
std::string variable = it_fcda->prefix + it_fcda->lnClass + it_fcda->lnInst + "$" + \
|
std::string variable = it_fcda->prefix + it_fcda->lnClass + it_fcda->lnInst + "$" + \
|
||||||
it_fcda->fc + "$" + it_fcda->doName + (it_fcda->daName.empty() ? "" : "$" + it_fcda->daName); // 拼装 FCDA 变量名
|
it_fcda->fc + "$" + it_fcda->doName + "$" + it_fcda->daName; // 拼装 FCDA 变量名
|
||||||
|
|
||||||
for(int i = 0; i < variable.size(); i++) // 将变量名中所有 '.' 替换为 '$'
|
for(int i = 0; i < variable.size(); i++) // 将变量名中所有 '.' 替换为 '$'
|
||||||
{
|
{
|
||||||
|
|
@ -663,9 +663,7 @@ LOCAL int model_ReportControlBlock_init(LogicalNode *LLN0, stru_icd &icd, std::m
|
||||||
int Options = model_get_rpt_options(rcb.optFields); // 将 OptFields 结构转为位掩码
|
int Options = model_get_rpt_options(rcb.optFields); // 将 OptFields 结构转为位掩码
|
||||||
int trgOps = model_get_rpt_trgOps(rcb.trgOps); // 将 TrgOps 结构转为位掩码
|
int trgOps = model_get_rpt_trgOps(rcb.trgOps); // 将 TrgOps 结构转为位掩码
|
||||||
|
|
||||||
LOG_I("RCB CREATE: name=%s dchg=%d qchg=%d dupd=%d period=%d -> trgOps=0x%x",
|
ReportControlBlock_create(it_rcb->c_str(), LLN0, rcb.rptID.c_str(), rcb.buffered, rcb.datSet.c_str(), rcb.confRev, trgOps, Options, rcb.bufTime, rcb.intgPd); // 创建 RCB
|
||||||
it_rcb->c_str(), rcb.trgOps.dchg, rcb.trgOps.qchg, rcb.trgOps.dupd, rcb.trgOps.period, trgOps);
|
|
||||||
rcb.Rcb = ReportControlBlock_create(it_rcb->c_str(), LLN0, rcb.rptID.c_str(), rcb.buffered, rcb.datSet.c_str(), rcb.confRev, trgOps, Options, rcb.bufTime, rcb.intgPd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -812,24 +810,6 @@ LOCAL int model_ldevice_init(IedModel *model, stru_icd &icd)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCAL void find_tq_for_da(DataAttribute *da, DataAttribute* *t, DataAttribute* *q)
|
|
||||||
{
|
|
||||||
*t = NULL;
|
|
||||||
*q = NULL;
|
|
||||||
if (!da) return;
|
|
||||||
ModelNode *parent = da->parent;
|
|
||||||
while (parent && parent->modelType != DataObjectModelType)
|
|
||||||
parent = parent->parent;
|
|
||||||
if (parent && parent->modelType == DataObjectModelType) {
|
|
||||||
DataAttribute *child = (DataAttribute *)parent->firstChild;
|
|
||||||
while (child) {
|
|
||||||
if (strcmp(child->name, "t") == 0) *t = child;
|
|
||||||
else if (strcmp(child->name, "q") == 0) *q = child;
|
|
||||||
child = (DataAttribute *)child->sibling;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将 SDI 中的 DAI 默认值(sAddr / val)同步到模型树中的 all_DA 节点
|
* 将 SDI 中的 DAI 默认值(sAddr / val)同步到模型树中的 all_DA 节点
|
||||||
*
|
*
|
||||||
|
|
@ -925,7 +905,6 @@ void model_sync_da_default_values(stru_icd &icd, stru_SDI &SDI_item, stru_all_DA
|
||||||
p_all_da_se_temp->DA->sAddr = StringUtils_copyString(saddr.c_str());
|
p_all_da_se_temp->DA->sAddr = StringUtils_copyString(saddr.c_str());
|
||||||
icd.ied.vec_saddr.push_back(saddr);
|
icd.ied.vec_saddr.push_back(saddr);
|
||||||
icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da_se_temp->DA;
|
icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da_se_temp->DA;
|
||||||
find_tq_for_da(p_all_da_se_temp->DA, &icd.ied.map_saddr_point[saddr].da_t, &icd.ied.map_saddr_point[saddr].da_q);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1040,8 +1019,7 @@ void model_sync_ln_default_values(stru_icd &icd, std::map<std::string, stru_DOI>
|
||||||
|
|
||||||
p_all_da->DA->sAddr = StringUtils_copyString(saddr.c_str()); // 写入模型节点
|
p_all_da->DA->sAddr = StringUtils_copyString(saddr.c_str()); // 写入模型节点
|
||||||
icd.ied.vec_saddr.push_back(saddr); // 加入列表
|
icd.ied.vec_saddr.push_back(saddr); // 加入列表
|
||||||
icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da->DA;
|
icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da->DA; // 建立映射
|
||||||
find_tq_for_da(p_all_da->DA, &icd.ied.map_saddr_point[saddr].da_t, &icd.ied.map_saddr_point[saddr].da_q); // 建立映射
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -313,8 +313,6 @@ LOCAL bool param_edit_sg_changed_handler(void* parameter, SettingGroupControlBlo
|
||||||
* @param max 输出:最大值
|
* @param max 输出:最大值
|
||||||
* @param step 输出:步长
|
* @param step 输出:步长
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* TODO: 与 mms_s_setting.cpp 中的校验函数重复,建议抽取到公共模块 */
|
|
||||||
LOCAL void param_min_max_step_get(DataAttribute *node, float &min, float &max, float &step)
|
LOCAL void param_min_max_step_get(DataAttribute *node, float &min, float &max, float &step)
|
||||||
{
|
{
|
||||||
if(node == NULL)
|
if(node == NULL)
|
||||||
|
|
@ -467,12 +465,12 @@ LOCAL void param_get_INT(DataAttribute *node_SE, DataAttribute *node_SG, std::st
|
||||||
float min, max, step;
|
float min, max, step;
|
||||||
param_min_max_step_get(node_SG, min, max, step);
|
param_min_max_step_get(node_SG, min, max, step);
|
||||||
|
|
||||||
int intVal = MmsValue_toInt32(node_SE->mmsValue);
|
|
||||||
if((int)step == 0)
|
if((int)step == 0)
|
||||||
{
|
{
|
||||||
val = std::to_string(intVal);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int intVal = MmsValue_toInt32(node_SE->mmsValue);
|
||||||
if(intVal < (int)min || intVal > (int)max)
|
if(intVal < (int)min || intVal > (int)max)
|
||||||
{
|
{
|
||||||
LOG_E("Value %d is out of range [%d, %d]", intVal, (int)min, (int)max);
|
LOG_E("Value %d is out of range [%d, %d]", intVal, (int)min, (int)max);
|
||||||
|
|
@ -501,12 +499,12 @@ LOCAL void param_get_UINT(DataAttribute *node_SE, DataAttribute *node_SG, std::s
|
||||||
float min, max, step;
|
float min, max, step;
|
||||||
param_min_max_step_get(node_SG, min, max, step);
|
param_min_max_step_get(node_SG, min, max, step);
|
||||||
|
|
||||||
int uintVal = MmsValue_toUint32(node_SE->mmsValue);
|
|
||||||
if((uint32_t)step == 0)
|
if((uint32_t)step == 0)
|
||||||
{
|
{
|
||||||
val = std::to_string(uintVal);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int uintVal = MmsValue_toUint32(node_SE->mmsValue);
|
||||||
if(uintVal < (uint32_t)min || uintVal > (uint32_t)max)
|
if(uintVal < (uint32_t)min || uintVal > (uint32_t)max)
|
||||||
{
|
{
|
||||||
LOG_E("Value %d is out of range [%d, %d]", uintVal, (uint32_t)min, (uint32_t)max);
|
LOG_E("Value %d is out of range [%d, %d]", uintVal, (uint32_t)min, (uint32_t)max);
|
||||||
|
|
@ -544,16 +542,19 @@ LOCAL void param_get_FLOAT(DataAttribute *node_SE, DataAttribute *node_SG, std::
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float gap = floatVal - min;
|
if(fabs(step) < EPSILON)
|
||||||
if(fabs(step) > EPSILON)
|
|
||||||
{
|
{
|
||||||
|
val = std::to_string(floatVal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float gap = floatVal - min;
|
||||||
float gap_mod = fmod(gap, step);
|
float gap_mod = fmod(gap, step);
|
||||||
|
|
||||||
if(fabs(gap_mod) > EPSILON && fabs(gap_mod - step) > EPSILON)
|
if(fabs(gap_mod) > EPSILON && fabs(gap_mod - step) > EPSILON)
|
||||||
{
|
{
|
||||||
LOG_E("Value %f does not conform to step size %f", floatVal, step);
|
LOG_E("Value %f does not conform to step size %f", floatVal, step);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val = std::to_string(floatVal);
|
val = std::to_string(floatVal);
|
||||||
|
|
@ -703,7 +704,7 @@ LOCAL void param_values_show()
|
||||||
{
|
{
|
||||||
if(g_vec_params.empty())
|
if(g_vec_params.empty())
|
||||||
{
|
{
|
||||||
LOG_I("g_vec_params is empty, no params registered");
|
LOG_E("g_vec_params is empty");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -765,7 +766,7 @@ int param_init(IedServer server, stru_icd &icd)
|
||||||
{
|
{
|
||||||
if(g_vec_params.empty())
|
if(g_vec_params.empty())
|
||||||
{
|
{
|
||||||
LOG_I("g_vec_params is empty, no params registered");
|
LOG_E("g_vec_params is empty");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -286,6 +286,11 @@ LOCAL int setting_get_INT(DataAttribute *node, MmsValue* value, std::string &val
|
||||||
float min, max, step;
|
float min, max, step;
|
||||||
setting_min_max_step_get(node, min, max, step);
|
setting_min_max_step_get(node, min, max, step);
|
||||||
|
|
||||||
|
if((int)step == 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int intVal = MmsValue_toInt32(value);
|
int intVal = MmsValue_toInt32(value);
|
||||||
if(intVal < (int)min || intVal > (int)max)
|
if(intVal < (int)min || intVal > (int)max)
|
||||||
{
|
{
|
||||||
|
|
@ -317,6 +322,11 @@ LOCAL int setting_get_UINT(DataAttribute *node, MmsValue* value, std::string &va
|
||||||
float min, max, step;
|
float min, max, step;
|
||||||
setting_min_max_step_get(node, min, max, step);
|
setting_min_max_step_get(node, min, max, step);
|
||||||
|
|
||||||
|
if((uint32_t)step == 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
int uintVal = MmsValue_toUint32(value);
|
int uintVal = MmsValue_toUint32(value);
|
||||||
if(uintVal < (uint32_t)min || uintVal > (uint32_t)max)
|
if(uintVal < (uint32_t)min || uintVal > (uint32_t)max)
|
||||||
{
|
{
|
||||||
|
|
@ -357,16 +367,18 @@ LOCAL int setting_get_FLOAT(DataAttribute *node, MmsValue* value, std::string &v
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
float gap = floatVal - min;
|
if(fabs(step) < EPSILON)
|
||||||
if(fabs(step) > EPSILON)
|
|
||||||
{
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float gap = floatVal - min;
|
||||||
float gap_mod = fmod(gap, step);
|
float gap_mod = fmod(gap, step);
|
||||||
|
|
||||||
if(fabs(gap_mod) > EPSILON && fabs(gap_mod - step) > EPSILON)
|
if(fabs(gap_mod) > EPSILON && fabs(gap_mod - step) > EPSILON)
|
||||||
{
|
{
|
||||||
LOG_E("Value %f does not conform to step size %f", floatVal, step);
|
LOG_E("Value %f does not conform to step size %f", floatVal, step);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_I("Float value for node %s is %f", node->name, floatVal);
|
LOG_I("Float value for node %s is %f", node->name, floatVal);
|
||||||
|
|
@ -585,7 +597,7 @@ int setting_init(IedServer server, stru_icd &icd)
|
||||||
{
|
{
|
||||||
if(g_vec_settings.empty())
|
if(g_vec_settings.empty())
|
||||||
{
|
{
|
||||||
LOG_I("g_vec_settings is empty, no settings registered");
|
LOG_E("g_vec_settings is empty");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -353,48 +353,34 @@ LOCAL void mms_s_da_value_init_Unicode_String(DataAttribute *da, std::string val
|
||||||
/** 初始化 Timestamp:设为当前毫秒时间 */
|
/** 初始化 Timestamp:设为当前毫秒时间 */
|
||||||
LOCAL void mms_s_da_value_init_Timestamp(DataAttribute *da, std::string val)
|
LOCAL void mms_s_da_value_init_Timestamp(DataAttribute *da, std::string val)
|
||||||
{
|
{
|
||||||
if(NULL == da) return;
|
if(NULL == da || val.empty())
|
||||||
|
{
|
||||||
|
LOG_E("da or val is empty, da %p, val %s", da, val.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
da->mmsValue = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs());
|
da->mmsValue = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LOCAL std::string get_DA_path(ModelNode *node)
|
|
||||||
{
|
|
||||||
std::string str_temp = "";
|
|
||||||
if(NULL == node)
|
|
||||||
{
|
|
||||||
return str_temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
str_temp = node->name ? node->name : "?";
|
|
||||||
ModelNode *temp_node = node->parent; // 向上遍历父节点
|
|
||||||
|
|
||||||
while(temp_node) // 逐级拼装完整路径
|
|
||||||
{
|
|
||||||
str_temp = std::string(temp_node->name ? temp_node->name : "?") + "." + str_temp; // parent.name + "." + 当前路径
|
|
||||||
|
|
||||||
if(temp_node->modelType == LogicalDeviceModelType) // 到达逻辑设备层则停止
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
temp_node = temp_node->parent; // 继续向上一级
|
|
||||||
}
|
|
||||||
|
|
||||||
return str_temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化 Dbpos(双点位置):
|
* 初始化 Dbpos(双点位置):
|
||||||
* val=0 → INTERMEDIATE_STATE, 1 → OFF, 2 → ON, 3/其他 → BAD_STATE
|
* val=0 → INTERMEDIATE_STATE, 1 → OFF, 2 → ON, 3/其他 → BAD_STATE
|
||||||
*/
|
*/
|
||||||
LOCAL void mms_s_da_value_init_Dbpos(DataAttribute *da, std::string val)
|
LOCAL void mms_s_da_value_init_Dbpos(DataAttribute *da, std::string val)
|
||||||
{
|
{
|
||||||
if(NULL == da) return;
|
if(NULL == da || val.empty())
|
||||||
|
{
|
||||||
|
LOG_E("da or val is empty, da %p, val %s", da, val.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Dbpos pos = DBPOS_INTERMEDIATE_STATE;
|
Dbpos pos = DBPOS_INTERMEDIATE_STATE;
|
||||||
|
|
||||||
|
if(val == "")
|
||||||
if(!val.empty())
|
{
|
||||||
|
pos = DBPOS_INTERMEDIATE_STATE;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
int i = atoi(val.c_str());
|
int i = atoi(val.c_str());
|
||||||
if(i == 0)
|
if(i == 0)
|
||||||
|
|
@ -419,20 +405,11 @@ LOCAL void mms_s_da_value_init_Dbpos(DataAttribute *da, std::string val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IedServer server = mms_s_get_ied_server_ptr();
|
Dbpos_toMmsValue(da->mmsValue, pos);
|
||||||
if (server)
|
|
||||||
{
|
|
||||||
IedServer_updateDbposValue(server, da, pos);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
da->mmsValue = Dbpos_toMmsValue(NULL, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if(mms_s_dbg_get())
|
if(mms_s_dbg_get())
|
||||||
{
|
{
|
||||||
MmsType val_type = MmsValue_getType(da->mmsValue);
|
LOG_I("da %s-%s, value %s, pos %d", da->parent->name, da->name, val.c_str(), pos);
|
||||||
LOG_I("da %s-%s, value %s, pos %d, type %d, path %s", da->parent->name, da->name, val.c_str(), pos, val_type, get_DA_path((ModelNode *)da).c_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -500,7 +477,7 @@ LOCAL void mms_s_da_values_init(stru_icd &icd, std::string da_name, stru_all_DA
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(NULL != g_iec61850s_value_init_cb_map[p_all_da->bType] && (!p_all_da->val.empty() || p_all_da->bType == "Timestamp" || p_all_da->bType == "Quality" || p_all_da->bType == "Dbpos"))
|
if(NULL != g_iec61850s_value_init_cb_map[p_all_da->bType])
|
||||||
{
|
{
|
||||||
g_iec61850s_value_init_cb_map[p_all_da->bType](p_all_da->DA, p_all_da->val);
|
g_iec61850s_value_init_cb_map[p_all_da->bType](p_all_da->DA, p_all_da->val);
|
||||||
}
|
}
|
||||||
|
|
@ -603,9 +580,6 @@ LOCAL void mms_s_value_update(const char *str_saddr, const char *str_val)
|
||||||
|
|
||||||
DataAttribute *p_da = (DataAttribute *)p_node;
|
DataAttribute *p_da = (DataAttribute *)p_node;
|
||||||
|
|
||||||
LOG_I("mms_s_value_update: saddr=%s val=%s da_type=%d da_name=%s",
|
|
||||||
saddr.c_str(), val.c_str(), p_da->type, p_da->name);
|
|
||||||
|
|
||||||
if(g_iec61850s_value_update_cb_map.find(p_da->type) == g_iec61850s_value_update_cb_map.end())
|
if(g_iec61850s_value_update_cb_map.find(p_da->type) == g_iec61850s_value_update_cb_map.end())
|
||||||
{
|
{
|
||||||
LOG_E("EnumType %d not found in ICD", p_da->type);
|
LOG_E("EnumType %d not found in ICD", p_da->type);
|
||||||
|
|
@ -616,34 +590,7 @@ LOCAL void mms_s_value_update(const char *str_saddr, const char *str_val)
|
||||||
|
|
||||||
if(NULL != g_iec61850s_value_update_cb_map[p_da->type] && p_ied_server != NULL)
|
if(NULL != g_iec61850s_value_update_cb_map[p_da->type] && p_ied_server != NULL)
|
||||||
{
|
{
|
||||||
IedServer_lockDataModel(p_ied_server);
|
|
||||||
|
|
||||||
g_iec61850s_value_update_cb_map[p_da->type](p_ied_server, p_da, val);
|
g_iec61850s_value_update_cb_map[p_da->type](p_ied_server, p_da, val);
|
||||||
|
|
||||||
/* 向上查找父级 DO,同步更新时间戳(t)和品质(q) */
|
|
||||||
ModelNode *parent = p_node->parent;
|
|
||||||
while (parent && parent->modelType != DataObjectModelType)
|
|
||||||
{
|
|
||||||
parent = parent->parent;
|
|
||||||
}
|
|
||||||
if (parent && parent->modelType == DataObjectModelType)
|
|
||||||
{
|
|
||||||
DataAttribute *child = (DataAttribute *)parent->firstChild;
|
|
||||||
while (child)
|
|
||||||
{
|
|
||||||
if (strcmp(child->name, "t") == 0)
|
|
||||||
{
|
|
||||||
IedServer_updateUTCTimeAttributeValue(p_ied_server, child, Hal_getTimeInMs());
|
|
||||||
}
|
|
||||||
else if (strcmp(child->name, "q") == 0)
|
|
||||||
{
|
|
||||||
IedServer_updateQuality(p_ied_server, child, QUALITY_VALIDITY_GOOD);
|
|
||||||
}
|
|
||||||
child = (DataAttribute *)child->sibling;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IedServer_unlockDataModel(p_ied_server);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1272,32 +1272,6 @@ int func_get_process_self_path(char *path, uint32_t len)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前进程所在目录
|
|
||||||
int func_get_process_self_dir(char *path, uint32_t len)
|
|
||||||
{
|
|
||||||
if((path == NULL) || (len == 0))
|
|
||||||
{
|
|
||||||
LOG_E("func_get_process_self_dir input invalid, path=%p, len=%d", path, len);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(func_get_process_self_path(path, len) != 0)
|
|
||||||
{
|
|
||||||
LOG_E("func_get_process_self_dir func_get_process_self_path failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *p_ch = strrchr(path, '/');
|
|
||||||
if(NULL == p_ch)
|
|
||||||
{
|
|
||||||
LOG_E("func_get_process_self_dir strrchr failed, path=%s", path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*(p_ch + 1) = '\0';
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
// 环境变量操作函数
|
// 环境变量操作函数
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#include "myLog.h"
|
#include "myLog.h"
|
||||||
#include "myBase.h"
|
#include "myBase.h"
|
||||||
#include "myFunc.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
@ -40,7 +39,7 @@
|
||||||
#define CLRLINE "\r\e[K" //or "\e[1K\r"
|
#define CLRLINE "\r\e[K" //or "\e[1K\r"
|
||||||
|
|
||||||
// 日志配置
|
// 日志配置
|
||||||
static char g_log_path[256] = {0};
|
#define LOG_FILE_PATH "/mnt/RTU/release/exe/logs"
|
||||||
#define LOG_FILE_NAME "app.log"
|
#define LOG_FILE_NAME "app.log"
|
||||||
#define LOG_FILE_MAX_SIZE (10 * 1024 * 1024)
|
#define LOG_FILE_MAX_SIZE (10 * 1024 * 1024)
|
||||||
#define LOG_FILE_MAX_COUNT 5
|
#define LOG_FILE_MAX_COUNT 5
|
||||||
|
|
@ -244,41 +243,28 @@ static void get_log_file_path(char* path, size_t size, int index)
|
||||||
{
|
{
|
||||||
if (index == 0)
|
if (index == 0)
|
||||||
{
|
{
|
||||||
snprintf(path, size, "%s/%s", g_log_path, LOG_FILE_NAME);
|
snprintf(path, size, "%s/%s", LOG_FILE_PATH, LOG_FILE_NAME);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
snprintf(path, size, "%s/%s.%d", g_log_path, LOG_FILE_NAME, index);
|
snprintf(path, size, "%s/%s.%d", LOG_FILE_PATH, LOG_FILE_NAME, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化日志目录
|
// 初始化日志目录
|
||||||
static int init_log_directory(void)
|
static int init_log_directory(void)
|
||||||
{
|
{
|
||||||
if (g_log_path[0] == '\0')
|
|
||||||
{
|
|
||||||
char proc_dir[256] = {0};
|
|
||||||
if (func_get_process_self_dir(proc_dir, sizeof(proc_dir)) == 0)
|
|
||||||
{
|
|
||||||
snprintf(g_log_path, sizeof(g_log_path), "%slogs", proc_dir);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
snprintf(g_log_path, sizeof(g_log_path), "/tmp/logs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct stat st = {0};
|
struct stat st = {0};
|
||||||
if (stat(g_log_path, &st) == -1)
|
if (stat(LOG_FILE_PATH, &st) == -1)
|
||||||
{
|
{
|
||||||
LOG_E("Failed to stat log directory %s", g_log_path);
|
LOG_E("Failed to stat log directory %s", LOG_FILE_PATH);
|
||||||
if (mkdir(g_log_path, 0755) == -1)
|
if (mkdir(LOG_FILE_PATH, 0755) == -1)
|
||||||
{
|
{
|
||||||
LOG_E("Failed to create log directory %s", g_log_path);
|
LOG_E("Failed to create log directory %s", LOG_FILE_PATH);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_I("Log directory %s create success", g_log_path);
|
LOG_I("Log directory %s create success", LOG_FILE_PATH);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,16 @@
|
||||||
#include "myBase.h"
|
#include "myBase.h"
|
||||||
#include "myLog.h"
|
#include "myLog.h"
|
||||||
#include "myFunc.h"
|
|
||||||
#include "myTask.h"
|
#include "myTask.h"
|
||||||
|
|
||||||
#include "app_sys.h"
|
#include "app_sys.h"
|
||||||
#include "mySystem.h"
|
#include "mySystem.h"
|
||||||
|
|
||||||
|
|
||||||
LOCAL std::string get_config_path()
|
LOCAL std::string path = "/mnt/RTU/test/config/";
|
||||||
{
|
|
||||||
char proc_dir[512] = {0};
|
|
||||||
if(0 != func_get_process_self_dir(proc_dir, sizeof(proc_dir)))
|
|
||||||
{
|
|
||||||
MY_LOG_E("func_get_process_self_dir failed");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return std::string(proc_dir) + "config/SELF_PTL/";
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCAL std::string self_ptl_cfg = "self_ptl.xml";
|
LOCAL std::string self_ptl_cfg = "self_ptl.xml";
|
||||||
|
|
||||||
int app_cfg_parse()
|
int app_cfg_parse()
|
||||||
{
|
{
|
||||||
std::string path = get_config_path();
|
|
||||||
if(path.empty())
|
|
||||||
{
|
|
||||||
MY_LOG_E("get_config_path failed, cannot get config directory");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(0 != self_ptl_cfg_parse(path + self_ptl_cfg))
|
if(0 != self_ptl_cfg_parse(path + self_ptl_cfg))
|
||||||
{
|
{
|
||||||
MY_LOG_E("self_ptl_cfg_parse failed, path:%s", (path + self_ptl_cfg).c_str());
|
MY_LOG_E("self_ptl_cfg_parse failed, path:%s", (path + self_ptl_cfg).c_str());
|
||||||
|
|
@ -54,7 +36,6 @@ int main(void)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(0 != app_sys_init())
|
if(0 != app_sys_init())
|
||||||
{
|
{
|
||||||
MY_LOG_E("app_sys_init failed !");
|
MY_LOG_E("app_sys_init failed !");
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@
|
||||||
#include "dc_signal.h"
|
#include "dc_signal.h"
|
||||||
#include "myDatacenter.h"
|
#include "myDatacenter.h"
|
||||||
|
|
||||||
#include <unordered_map>
|
#define DC_PARAM_PATH "/mnt/RTU/test/file/PARAM/param.xml"
|
||||||
|
|
||||||
|
void dc_param_cfg_parse();
|
||||||
int dc_param_cfg_parse(const std::string &path);void dc_param_metadata_store(const std::string &saddr, const stru_signal_param ¶m);
|
|
||||||
bool dc_param_metadata_lookup(const std::string &saddr, stru_signal_param &out_param);
|
|
||||||
|
|
@ -37,7 +37,7 @@ typedef struct stru_signal
|
||||||
bool dc_get_param_cfg_change();
|
bool dc_get_param_cfg_change();
|
||||||
void dc_set_param_cfg_change(bool change);
|
void dc_set_param_cfg_change(bool change);
|
||||||
|
|
||||||
void dc_param_cfg_check(const std::string &path);
|
void dc_param_cfg_check();
|
||||||
|
|
||||||
void dc_signal_out_change_check();
|
void dc_signal_out_change_check();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,35 +4,14 @@
|
||||||
#include "dc_param.h"
|
#include "dc_param.h"
|
||||||
#include "dc_signal.h"
|
#include "dc_signal.h"
|
||||||
#include "dc_event.h"
|
#include "dc_event.h"
|
||||||
#include "myFunc.h"
|
|
||||||
|
|
||||||
LOCAL std::string g_param_path = "";
|
|
||||||
LOCAL std::string datacenter_get_param_path()
|
|
||||||
{
|
|
||||||
char proc_dir[256] = {0};
|
|
||||||
if(0 != func_get_process_self_dir(proc_dir, sizeof(proc_dir)))
|
|
||||||
{
|
|
||||||
MY_LOG_E("func_get_process_self_dir failed");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return std::string(proc_dir) + "config/PARAM/param.xml";
|
|
||||||
}
|
|
||||||
|
|
||||||
int datacenter_init()
|
int datacenter_init()
|
||||||
{
|
{
|
||||||
std::string path = datacenter_get_param_path();
|
dc_param_cfg_parse();
|
||||||
if(path.empty())
|
|
||||||
{
|
|
||||||
MY_LOG_E("datacenter_get_param_path failed, cannot get param path");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(0 != dc_param_cfg_parse(path))
|
|
||||||
{
|
|
||||||
MY_LOG_E("dc_param_cfg_parse failed, path:%s", path.c_str());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
g_param_path = path;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,6 +25,6 @@ void datacenter_run_100ms()
|
||||||
|
|
||||||
void datacenter_run_1000ms()
|
void datacenter_run_1000ms()
|
||||||
{
|
{
|
||||||
dc_param_cfg_check(g_param_path);
|
dc_param_cfg_check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,174 +1,25 @@
|
||||||
#include "dc_param.h"
|
#include "dc_param.h"
|
||||||
#include "tinyxml2.h"
|
#include "tinyxml2.h"
|
||||||
|
|
||||||
// 中央元数据表(以 saddr 为键,初始化时写入,运行时只读)
|
void dc_param_cfg_parse()
|
||||||
static std::unordered_map<std::string, stru_signal_param> g_param_metadata;
|
|
||||||
|
|
||||||
void dc_param_metadata_store(const std::string &saddr, const stru_signal_param ¶m)
|
|
||||||
{
|
|
||||||
g_param_metadata[saddr] = param;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dc_param_metadata_lookup(const std::string &saddr, stru_signal_param &out_param)
|
|
||||||
{
|
|
||||||
auto it = g_param_metadata.find(saddr);
|
|
||||||
if (it != g_param_metadata.end())
|
|
||||||
{
|
|
||||||
out_param = it->second;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析 param.xml,只提取元数据存入 g_param_metadata,不注册信号
|
|
||||||
static int dc_param_parse_metadata(const std::string &path)
|
|
||||||
{
|
{
|
||||||
using namespace tinyxml2;
|
using namespace tinyxml2;
|
||||||
|
|
||||||
XMLDocument doc;
|
XMLDocument doc;
|
||||||
if (XML_SUCCESS != doc.LoadFile(path.c_str()))
|
if (XML_SUCCESS != doc.LoadFile(DC_PARAM_PATH))
|
||||||
{
|
{
|
||||||
MY_LOG_E("failed to load param file: %s", path.c_str());
|
MY_LOG_E("failed to load param file: %s", DC_PARAM_PATH);
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
XMLElement *root = doc.RootElement();
|
XMLElement *root = doc.RootElement();
|
||||||
if (nullptr == root)
|
if (nullptr == root)
|
||||||
{
|
{
|
||||||
MY_LOG_E("param file has no root element");
|
MY_LOG_E("param file has no root element");
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析 Ao 段
|
// 解析 Ao 段:单值参数(一份值 + 一份缺省值)
|
||||||
XMLElement *ao_elem = root->FirstChildElement("Ao");
|
|
||||||
if (ao_elem)
|
|
||||||
{
|
|
||||||
for (XMLElement *sig = ao_elem->FirstChildElement("Signal"); sig != nullptr;
|
|
||||||
sig = sig->NextSiblingElement("Signal"))
|
|
||||||
{
|
|
||||||
const char *saddr = sig->Attribute("saddr");
|
|
||||||
if (nullptr == saddr)
|
|
||||||
{
|
|
||||||
MY_LOG_E("Ao Signal missing saddr, skipping");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
stru_signal_param param;
|
|
||||||
param.min = sig->FloatAttribute("min", 0.0f);
|
|
||||||
param.max = sig->FloatAttribute("max", 0.0f);
|
|
||||||
param.step = sig->FloatAttribute("step", 0.0f);
|
|
||||||
const char *unit = sig->Attribute("unit");
|
|
||||||
param.unit = unit ? std::string(unit) : std::string();
|
|
||||||
|
|
||||||
g_param_metadata[saddr] = param;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析 Param 段
|
|
||||||
XMLElement *param_elem = root->FirstChildElement("Param");
|
|
||||||
if (param_elem)
|
|
||||||
{
|
|
||||||
for (XMLElement *sig = param_elem->FirstChildElement("Signal"); sig != nullptr;
|
|
||||||
sig = sig->NextSiblingElement("Signal"))
|
|
||||||
{
|
|
||||||
const char *saddr = sig->Attribute("saddr");
|
|
||||||
if (nullptr == saddr)
|
|
||||||
{
|
|
||||||
MY_LOG_E("Param Signal missing saddr, skipping");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
stru_signal_param param;
|
|
||||||
param.min = sig->FloatAttribute("min", 0.0f);
|
|
||||||
param.max = sig->FloatAttribute("max", 0.0f);
|
|
||||||
param.step = sig->FloatAttribute("step", 0.0f);
|
|
||||||
const char *unit = sig->Attribute("unit");
|
|
||||||
param.unit = unit ? std::string(unit) : std::string();
|
|
||||||
|
|
||||||
g_param_metadata[saddr] = param;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从 self_param.xml 加载运行时值,返回 saddr → value字符串 的映射
|
|
||||||
// Ao段: key=saddr, value="value_attr"
|
|
||||||
// Param段: key=saddr + "_" + index, value="value_attr"
|
|
||||||
static std::unordered_map<std::string, std::string> dc_self_param_load(const std::string &path)
|
|
||||||
{
|
|
||||||
using namespace tinyxml2;
|
|
||||||
std::unordered_map<std::string, std::string> values;
|
|
||||||
|
|
||||||
XMLDocument doc;
|
|
||||||
if (XML_SUCCESS != doc.LoadFile(path.c_str()))
|
|
||||||
return values; // 文件不存在或解析失败,返回空映射
|
|
||||||
|
|
||||||
XMLElement *root = doc.RootElement();
|
|
||||||
if (nullptr == root) return values;
|
|
||||||
|
|
||||||
// 加载 Ao 段值
|
|
||||||
XMLElement *ao_elem = root->FirstChildElement("Ao");
|
|
||||||
if (ao_elem)
|
|
||||||
{
|
|
||||||
for (XMLElement *sig = ao_elem->FirstChildElement("Signal"); sig != nullptr;
|
|
||||||
sig = sig->NextSiblingElement("Signal"))
|
|
||||||
{
|
|
||||||
const char *saddr = sig->Attribute("saddr");
|
|
||||||
const char *val = sig->Attribute("value");
|
|
||||||
if (saddr && val)
|
|
||||||
values[saddr] = std::string(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载 Param 段值
|
|
||||||
XMLElement *param_elem = root->FirstChildElement("Param");
|
|
||||||
if (param_elem)
|
|
||||||
{
|
|
||||||
for (XMLElement *sig = param_elem->FirstChildElement("Signal"); sig != nullptr;
|
|
||||||
sig = sig->NextSiblingElement("Signal"))
|
|
||||||
{
|
|
||||||
const char *saddr = sig->Attribute("saddr");
|
|
||||||
if (nullptr == saddr) continue;
|
|
||||||
|
|
||||||
for (XMLElement *item = sig->FirstChildElement("Item"); item != nullptr;
|
|
||||||
item = item->NextSiblingElement("Item"))
|
|
||||||
{
|
|
||||||
int index = item->IntAttribute("index", -1);
|
|
||||||
const char *val = item->Attribute("value");
|
|
||||||
if (index >= 1 && val)
|
|
||||||
{
|
|
||||||
std::string key = std::string(saddr) + "_" + std::to_string(index);
|
|
||||||
values[key] = std::string(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据 param.xml 和 self_param.xml 的值生成信号并注册
|
|
||||||
static int dc_param_register_signals(const std::string ¶m_path,
|
|
||||||
const std::unordered_map<std::string, std::string> &self_values)
|
|
||||||
{
|
|
||||||
using namespace tinyxml2;
|
|
||||||
|
|
||||||
XMLDocument doc;
|
|
||||||
if (XML_SUCCESS != doc.LoadFile(param_path.c_str()))
|
|
||||||
{
|
|
||||||
MY_LOG_E("failed to reload param file: %s", param_path.c_str());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
XMLElement *root = doc.RootElement();
|
|
||||||
if (nullptr == root)
|
|
||||||
{
|
|
||||||
MY_LOG_E("param file has no root element");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注册 Ao 段信号
|
|
||||||
XMLElement *ao_elem = root->FirstChildElement("Ao");
|
XMLElement *ao_elem = root->FirstChildElement("Ao");
|
||||||
if (ao_elem)
|
if (ao_elem)
|
||||||
{
|
{
|
||||||
|
|
@ -191,25 +42,22 @@ static int dc_param_register_signals(const std::string ¶m_path,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认值始终从 param.xml 读取
|
float min = sig->FloatAttribute("min", 0.0f);
|
||||||
const char *default_val = sig->Attribute("default");
|
float max = sig->FloatAttribute("max", 0.0f);
|
||||||
|
float step = sig->FloatAttribute("step", 0.0f);
|
||||||
|
const char *unit = sig->Attribute("unit");
|
||||||
|
|
||||||
// 运行时值:优先使用 self_param.xml 中的值,否则使用 param.xml 的 value 属性
|
// Ao 段:从 Signal 属性读取单值
|
||||||
const char *val_attr = sig->Attribute("value");
|
const char *val = sig->Attribute("value");
|
||||||
std::string run_val;
|
const char *default_val = sig->Attribute("default");
|
||||||
auto it = self_values.find(std::string(saddr));
|
|
||||||
if (it != self_values.end())
|
|
||||||
run_val = it->second;
|
|
||||||
else if (val_attr && strlen(val_attr) > 0)
|
|
||||||
run_val = std::string(val_attr);
|
|
||||||
else if (default_val && strlen(default_val) > 0)
|
|
||||||
run_val = std::string(default_val);
|
|
||||||
|
|
||||||
void *p_data = dc_create_data_ptr_by_type(data_type);
|
void *p_data = dc_create_data_ptr_by_type(data_type);
|
||||||
if (p_data)
|
if (p_data)
|
||||||
{
|
{
|
||||||
if (!run_val.empty())
|
if (val && strlen(val) > 0)
|
||||||
dc_set_signal_val_from_str(p_data, data_type, run_val);
|
dc_set_signal_val_from_str(p_data, data_type, std::string(val));
|
||||||
|
else if (default_val && strlen(default_val) > 0)
|
||||||
|
dc_set_signal_val_from_str(p_data, data_type, std::string(default_val));
|
||||||
}
|
}
|
||||||
|
|
||||||
void *p_default_data = dc_create_data_ptr_by_type(data_type);
|
void *p_default_data = dc_create_data_ptr_by_type(data_type);
|
||||||
|
|
@ -221,14 +69,20 @@ static int dc_param_register_signals(const std::string ¶m_path,
|
||||||
|
|
||||||
if (p_data && p_default_data)
|
if (p_data && p_default_data)
|
||||||
{
|
{
|
||||||
dc_signal_ao(saddr, desc, data_type,
|
stru_signal_param param;
|
||||||
|
param.min = min;
|
||||||
|
param.max = max;
|
||||||
|
param.step = step;
|
||||||
|
param.unit = unit ? std::string(unit) : std::string();
|
||||||
|
|
||||||
|
dc_signal_ao(saddr, desc, data_type, param,
|
||||||
SIGNAL_CTRL_TYPE::SBO_NORMAL,
|
SIGNAL_CTRL_TYPE::SBO_NORMAL,
|
||||||
p_data, p_default_data, nullptr);
|
p_data, p_default_data, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册 Param 段信号
|
// 解析 Param 段:多值参数(多份值 + 多份缺省值)
|
||||||
XMLElement *param_elem = root->FirstChildElement("Param");
|
XMLElement *param_elem = root->FirstChildElement("Param");
|
||||||
if (param_elem)
|
if (param_elem)
|
||||||
{
|
{
|
||||||
|
|
@ -251,96 +105,62 @@ static int dc_param_register_signals(const std::string ¶m_path,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int num = sig->IntAttribute("num", -1);
|
float min = sig->FloatAttribute("min", 0.0f);
|
||||||
if (num < 1) continue;
|
float max = sig->FloatAttribute("max", 0.0f);
|
||||||
|
float step = sig->FloatAttribute("step", 0.0f);
|
||||||
|
const char *unit = sig->Attribute("unit");
|
||||||
|
|
||||||
std::vector<void *> vec_p_data;
|
std::vector<void *> vec_p_data;
|
||||||
std::vector<void *> vec_p_default_data;
|
std::vector<void *> vec_p_default_data;
|
||||||
|
|
||||||
for (XMLElement *item = sig->FirstChildElement("Item"); item != nullptr;
|
int num = sig->IntAttribute("num", -1);
|
||||||
item = item->NextSiblingElement("Item"))
|
if (num >= 1)
|
||||||
{
|
{
|
||||||
int index = item->IntAttribute("index", -1);
|
for (XMLElement *item = sig->FirstChildElement("Item"); item != nullptr;
|
||||||
const char *default_val = item->Attribute("default");
|
item = item->NextSiblingElement("Item"))
|
||||||
const char *val_attr = item->Attribute("value");
|
|
||||||
|
|
||||||
// 运行时值:优先使用 self_param.xml 中的值
|
|
||||||
std::string key = std::string(saddr) + "_" + std::to_string(index);
|
|
||||||
std::string run_val;
|
|
||||||
auto it = self_values.find(key);
|
|
||||||
if (it != self_values.end())
|
|
||||||
run_val = it->second;
|
|
||||||
else if (val_attr && strlen(val_attr) > 0)
|
|
||||||
run_val = std::string(val_attr);
|
|
||||||
else if (default_val && strlen(default_val) > 0)
|
|
||||||
run_val = std::string(default_val);
|
|
||||||
|
|
||||||
void *p_data = dc_create_data_ptr_by_type(data_type);
|
|
||||||
if (p_data)
|
|
||||||
{
|
{
|
||||||
if (!run_val.empty())
|
const char *val = item->Attribute("value");
|
||||||
dc_set_signal_val_from_str(p_data, data_type, run_val);
|
const char *default_val = item->Attribute("default");
|
||||||
vec_p_data.push_back(p_data);
|
|
||||||
|
void *p_data = dc_create_data_ptr_by_type(data_type);
|
||||||
|
if (p_data)
|
||||||
|
{
|
||||||
|
if (val && strlen(val) > 0)
|
||||||
|
dc_set_signal_val_from_str(p_data, data_type, std::string(val));
|
||||||
|
else if (default_val && strlen(default_val) > 0)
|
||||||
|
dc_set_signal_val_from_str(p_data, data_type, std::string(default_val));
|
||||||
|
vec_p_data.push_back(p_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *p_default_data = dc_create_data_ptr_by_type(data_type);
|
||||||
|
if (p_default_data)
|
||||||
|
{
|
||||||
|
if (default_val && strlen(default_val) > 0)
|
||||||
|
dc_set_signal_val_from_str(p_default_data, data_type, std::string(default_val));
|
||||||
|
vec_p_default_data.push_back(p_default_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *p_default_data = dc_create_data_ptr_by_type(data_type);
|
}
|
||||||
if (p_default_data)
|
else
|
||||||
{
|
{
|
||||||
if (default_val && strlen(default_val) > 0)
|
continue;
|
||||||
dc_set_signal_val_from_str(p_default_data, data_type, std::string(default_val));
|
|
||||||
vec_p_default_data.push_back(p_default_data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vec_p_data.empty() && vec_p_data.size() == vec_p_default_data.size())
|
if (!vec_p_data.empty())
|
||||||
{
|
{
|
||||||
printf("[REG_PARAM] saddr=%s num=%d\n", saddr, num);
|
stru_signal_param param;
|
||||||
for (size_t _i = 0; _i < vec_p_data.size(); _i++) {
|
param.min = min;
|
||||||
printf(" zone %zu: val=%s def=%s\n", _i, dc_get_signal_val(vec_p_data[_i], data_type).c_str(), dc_get_signal_val(vec_p_default_data[_i], data_type).c_str());
|
param.max = max;
|
||||||
}
|
param.step = step;
|
||||||
dc_signal_param(saddr, desc, data_type,
|
param.unit = unit ? std::string(unit) : std::string();
|
||||||
|
|
||||||
|
dc_signal_param(saddr, desc, data_type, param,
|
||||||
SIGNAL_CTRL_TYPE::SBO_NORMAL,
|
SIGNAL_CTRL_TYPE::SBO_NORMAL,
|
||||||
vec_p_data.data(), vec_p_default_data.data(), num, nullptr);
|
vec_p_data.data(), vec_p_default_data.data(), num, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 首次注册后标记变更,触发 self_param.xml 生成
|
|
||||||
dc_set_param_cfg_change(true);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dc_param_cfg_parse(const std::string &path)
|
|
||||||
{
|
|
||||||
// 1. 从 param.xml 提取元数据到 g_param_metadata
|
|
||||||
if (0 != dc_param_parse_metadata(path))
|
|
||||||
{
|
|
||||||
MY_LOG_E("dc_param_parse_metadata failed, path:%s", path.c_str());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 构造 self_param.xml 路径
|
|
||||||
std::string self_path = path;
|
|
||||||
size_t pos = self_path.rfind("param.xml");
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
self_path.replace(pos, 9, "self_param.xml");
|
|
||||||
else
|
|
||||||
self_path = path.substr(0, path.rfind('/') + 1) + "self_param.xml";
|
|
||||||
|
|
||||||
// 3. 尝试加载 self_param.xml 中的运行时值
|
|
||||||
std::unordered_map<std::string, std::string> self_values = dc_self_param_load(self_path);
|
|
||||||
if (!self_values.empty())
|
|
||||||
MY_LOG_I("loaded %zu saved values from self_param.xml", self_values.size());
|
|
||||||
else
|
|
||||||
MY_LOG_I("no self_param.xml found, using default values from param.xml");
|
|
||||||
|
|
||||||
// 4. 用合并后的值注册所有信号
|
|
||||||
if (0 != dc_param_register_signals(path, self_values))
|
|
||||||
{
|
|
||||||
MY_LOG_E("dc_param_register_signals failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -371,25 +371,7 @@ LOCAL int dc_data_compare(uint8_t data_type, void *p_data, void *p_data2)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOCAL bool dc_signal_ao_add_check(stru_signal *p_signal, const std::string &desc, uint8_t data_type, const stru_signal_param ¶m, uint8_t ctrl_type, void *p_default_data)
|
||||||
// 从中央元数据表查找并设置 signal.param
|
|
||||||
LOCAL void dc_signal_apply_metadata(stru_signal &signal)
|
|
||||||
{
|
|
||||||
stru_signal_param param;
|
|
||||||
if (dc_param_metadata_lookup(signal.saddr, param))
|
|
||||||
{
|
|
||||||
signal.param = param;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
signal.param.min = 0.0f;
|
|
||||||
signal.param.max = 0.0f;
|
|
||||||
signal.param.step = 0.0f;
|
|
||||||
signal.param.unit.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCAL bool dc_signal_ao_add_check(stru_signal *p_signal, const std::string &desc, uint8_t data_type, uint8_t ctrl_type, void *p_default_data)
|
|
||||||
{
|
{
|
||||||
if(p_signal == nullptr || p_default_data == nullptr)
|
if(p_signal == nullptr || p_default_data == nullptr)
|
||||||
{
|
{
|
||||||
|
|
@ -411,6 +393,12 @@ LOCAL bool dc_signal_ao_add_check(stru_signal *p_signal, const std::string &desc
|
||||||
change = true;
|
change = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(p_signal->param.min != param.min || p_signal->param.max != param.max || p_signal->param.step != param.step || p_signal->param.unit != param.unit)
|
||||||
|
{
|
||||||
|
p_signal->param = param;
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
|
||||||
if(p_signal->ctrl_type != ctrl_type)
|
if(p_signal->ctrl_type != ctrl_type)
|
||||||
{
|
{
|
||||||
p_signal->ctrl_type = ctrl_type;
|
p_signal->ctrl_type = ctrl_type;
|
||||||
|
|
@ -425,36 +413,38 @@ LOCAL bool dc_signal_ao_add_check(stru_signal *p_signal, const std::string &desc
|
||||||
return change;
|
return change;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCAL bool dc_signal_param_add_check(stru_signal *p_signal, const std::string &desc, uint8_t data_type, uint8_t ctrl_type, void **p_default_data, uint8_t num)
|
LOCAL bool dc_signal_param_add_check(stru_signal *p_signal, const std::string &desc, uint8_t data_type, const stru_signal_param ¶m, uint8_t ctrl_type, void **p_default_data, uint8_t num)
|
||||||
{
|
{
|
||||||
bool change = false;
|
bool change = false;
|
||||||
|
|
||||||
if(p_signal->desc != desc)
|
if(p_signal->desc != desc)
|
||||||
{
|
{
|
||||||
p_signal->desc = desc;
|
p_signal->desc = desc;
|
||||||
printf("desc=%s\n", p_signal->desc.c_str());
|
|
||||||
change = true;
|
change = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(p_signal->data_type != data_type)
|
if(p_signal->data_type != data_type)
|
||||||
{
|
{
|
||||||
p_signal->data_type = data_type;
|
p_signal->data_type = data_type;
|
||||||
printf("data_type=%d\n", p_signal->data_type);
|
|
||||||
change = true;
|
change = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if(p_signal->ctrl_type != ctrl_type)
|
if(p_signal->param.min != param.min || p_signal->param.max != param.max || p_signal->param.step != param.step || p_signal->param.unit != param.unit)
|
||||||
// {
|
{
|
||||||
// p_signal->ctrl_type = ctrl_type;
|
p_signal->param = param;
|
||||||
// printf("ctrl_type=%d\n", p_signal->ctrl_type);
|
change = true;
|
||||||
// change = true;
|
}
|
||||||
// }
|
|
||||||
|
if(p_signal->ctrl_type != ctrl_type)
|
||||||
|
{
|
||||||
|
p_signal->ctrl_type = ctrl_type;
|
||||||
|
change = true;
|
||||||
|
}
|
||||||
|
|
||||||
for(size_t i = 0; i < num; i++)
|
for(size_t i = 0; i < num; i++)
|
||||||
{
|
{
|
||||||
if(0 != dc_data_compare(p_signal->data_type, p_default_data[i], p_signal->vec_p_default_data[i]))
|
if(0 != dc_data_compare(p_signal->data_type, p_default_data[i], p_signal->vec_p_default_data[i]))
|
||||||
{
|
{
|
||||||
printf(" zone %d: val=%s def=%s, %p, %p\n", i+1, dc_get_signal_val(p_signal->vec_p_data[i], p_signal->data_type).c_str(), dc_get_signal_val(p_signal->vec_p_default_data[i], p_signal->data_type).c_str(), p_signal->vec_p_data[i], p_signal->vec_p_default_data[i]);
|
|
||||||
change = true;
|
change = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1093,7 +1083,7 @@ int dc_signal_in_with_callback(const std::string &saddr, const std::string &desc
|
||||||
return dc_signal_add_to_map(signal, g_datacenter.signal_in);
|
return dc_signal_add_to_map(signal, g_datacenter.signal_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
int dc_signal_ao(const std::string &saddr, const std::string &desc, uint8_t data_type, uint8_t ctrl_type, void *p_data, void *p_default_data, signal_change_cb cb)
|
int dc_signal_ao(const std::string &saddr, const std::string &desc, uint8_t data_type, const stru_signal_param ¶m, uint8_t ctrl_type, void *p_data, void *p_default_data, signal_change_cb cb)
|
||||||
{
|
{
|
||||||
if(p_data == nullptr || p_default_data == nullptr)
|
if(p_data == nullptr || p_default_data == nullptr)
|
||||||
{
|
{
|
||||||
|
|
@ -1105,16 +1095,14 @@ int dc_signal_ao(const std::string &saddr, const std::string &desc, uint8_t data
|
||||||
stru_signal *p_signal = dc_find_signal(saddr, g_datacenter.signal_ao);
|
stru_signal *p_signal = dc_find_signal(saddr, g_datacenter.signal_ao);
|
||||||
if(p_signal != nullptr && !p_signal->vec_p_data.empty() && !p_signal->vec_p_default_data.empty() && nullptr != p_signal->vec_p_data[0] && nullptr != p_signal->vec_p_default_data[0])
|
if(p_signal != nullptr && !p_signal->vec_p_data.empty() && !p_signal->vec_p_default_data.empty() && nullptr != p_signal->vec_p_data[0] && nullptr != p_signal->vec_p_default_data[0])
|
||||||
{
|
{
|
||||||
// Always copy old value to new pointer when data type matches
|
if(true == dc_signal_ao_add_check(p_signal, desc, data_type, param, ctrl_type, p_default_data))
|
||||||
if(p_signal->data_type == data_type)
|
|
||||||
{
|
|
||||||
dc_set_signal_val(p_data, data_type, p_signal->vec_p_data[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(true == dc_signal_ao_add_check(p_signal, desc, data_type, ctrl_type, p_default_data))
|
|
||||||
{
|
{
|
||||||
dc_set_param_cfg_change(true);
|
dc_set_param_cfg_change(true);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dc_set_signal_val(p_data, data_type, p_signal->vec_p_data[0]);
|
||||||
|
}
|
||||||
|
|
||||||
// delete p_signal->vec_p_data[0];
|
// delete p_signal->vec_p_data[0];
|
||||||
dc_delete_signal_data(p_signal->vec_p_data[0], data_type);
|
dc_delete_signal_data(p_signal->vec_p_data[0], data_type);
|
||||||
|
|
@ -1131,7 +1119,7 @@ int dc_signal_ao(const std::string &saddr, const std::string &desc, uint8_t data
|
||||||
signal.saddr.assign(saddr);
|
signal.saddr.assign(saddr);
|
||||||
signal.desc.assign(desc);
|
signal.desc.assign(desc);
|
||||||
signal.data_type = data_type;
|
signal.data_type = data_type;
|
||||||
dc_signal_apply_metadata(signal);
|
signal.param = param;
|
||||||
signal.ctrl_type = ctrl_type;
|
signal.ctrl_type = ctrl_type;
|
||||||
|
|
||||||
dc_signal_add_to_map(signal, g_datacenter.signal_ao);
|
dc_signal_add_to_map(signal, g_datacenter.signal_ao);
|
||||||
|
|
@ -1275,7 +1263,7 @@ int dc_signal_ao_set_val_without_check(const std::string &saddr, uint8_t data_ty
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dc_signal_param(const std::string &saddr, const std::string &desc, uint8_t data_type, uint8_t ctrl_type, void **p_data, void **p_default_data, int data_num, signal_change_cb cb)
|
int dc_signal_param(const std::string &saddr, const std::string &desc, uint8_t data_type, const stru_signal_param ¶m, uint8_t ctrl_type, void **p_data, void **p_default_data, int data_num, signal_change_cb cb)
|
||||||
{
|
{
|
||||||
// 先从参数表里解析,调用参数注册
|
// 先从参数表里解析,调用参数注册
|
||||||
// 程序注册时,从参数表里去查找,同一个信息,使用参数表中的保存的数据值,在线程初始化时,通知所有链接此信号的信号
|
// 程序注册时,从参数表里去查找,同一个信息,使用参数表中的保存的数据值,在线程初始化时,通知所有链接此信号的信号
|
||||||
|
|
@ -1287,12 +1275,10 @@ int dc_signal_param(const std::string &saddr, const std::string &desc, uint8_t d
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("[SIGNAL_PARAM] saddr=%s data_num=%d\n", saddr.c_str(), data_num);
|
|
||||||
for(int i = 0; i < data_num; i++)
|
for(int i = 0; i < data_num; i++)
|
||||||
{
|
{
|
||||||
if(p_data[i] == nullptr || p_default_data[i] == nullptr)
|
if(p_data[i] == nullptr || p_default_data[i] == nullptr)
|
||||||
{
|
{
|
||||||
printf("[SIGNAL_PARAM_ERR] saddr=%s i=%d p_data=%p p_def=%p\n", saddr.c_str(), i, (void*)p_data[i], (void*)p_default_data[i]);
|
|
||||||
MY_LOG_E("p_data[%d] or p_default_data[%d] is nullptr", i, i);
|
MY_LOG_E("p_data[%d] or p_default_data[%d] is nullptr", i, i);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -1302,28 +1288,17 @@ int dc_signal_param(const std::string &saddr, const std::string &desc, uint8_t d
|
||||||
stru_signal *p_signal = dc_find_signal(saddr, g_datacenter.signal_param);
|
stru_signal *p_signal = dc_find_signal(saddr, g_datacenter.signal_param);
|
||||||
if(p_signal != nullptr)
|
if(p_signal != nullptr)
|
||||||
{
|
{
|
||||||
// Always copy old values to new pointers when data type matches
|
|
||||||
if(p_signal->data_type == data_type)
|
|
||||||
{
|
|
||||||
size_t copy_num = (data_num < p_signal->vec_p_data.size()) ? data_num : p_signal->vec_p_data.size();
|
|
||||||
for(size_t i = 0; i < copy_num; i++)
|
|
||||||
{
|
|
||||||
dc_set_signal_val(p_data[i], data_type, p_signal->vec_p_data[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dc_set_signal_val(p_data, data_type, p_signal->p_data);
|
// dc_set_signal_val(p_data, data_type, p_signal->p_data);
|
||||||
bool check_result = dc_signal_param_add_check(p_signal, desc, data_type, ctrl_type, p_default_data, data_num);
|
if(true == dc_signal_param_add_check(p_signal, desc, data_type, param, ctrl_type, p_default_data, data_num))
|
||||||
printf("[SIGNAL_PARAM_REPLACE] saddr=%s add_check=%d old_size=%d new_size=%d\n",
|
|
||||||
saddr.c_str(), (int)check_result, (int)p_signal->vec_p_data.size(), data_num);
|
|
||||||
if (check_result)
|
|
||||||
{
|
{
|
||||||
printf("[SIGNAL_PARAM_REPLACE] add_check=true -> metadata changed, cfg_change set!\n");
|
|
||||||
dc_set_param_cfg_change(true);
|
dc_set_param_cfg_change(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("[SIGNAL_PARAM_REPLACE] add_check=false -> metadata unchanged\n");
|
for(size_t i = 0; i < data_num; i++)
|
||||||
|
{
|
||||||
|
dc_set_signal_val(p_data[i], data_type, p_signal->vec_p_data[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(size_t i = 0; i < data_num; i++)
|
for(size_t i = 0; i < data_num; i++)
|
||||||
|
|
@ -1354,7 +1329,7 @@ int dc_signal_param(const std::string &saddr, const std::string &desc, uint8_t d
|
||||||
signal.saddr.assign(saddr);
|
signal.saddr.assign(saddr);
|
||||||
signal.desc.assign(desc);
|
signal.desc.assign(desc);
|
||||||
signal.data_type = data_type;
|
signal.data_type = data_type;
|
||||||
dc_signal_apply_metadata(signal);
|
signal.param = param;
|
||||||
signal.ctrl_type = ctrl_type;
|
signal.ctrl_type = ctrl_type;
|
||||||
|
|
||||||
dc_signal_add_to_map(signal, g_datacenter.signal_param);
|
dc_signal_add_to_map(signal, g_datacenter.signal_param);
|
||||||
|
|
@ -1475,7 +1450,6 @@ int dc_signal_param_set_val_without_check(const std::string &saddr, uint8_t data
|
||||||
|
|
||||||
XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());
|
XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());
|
||||||
stru_signal *p_signal = dc_find_signal(saddr, g_datacenter.signal_param);
|
stru_signal *p_signal = dc_find_signal(saddr, g_datacenter.signal_param);
|
||||||
printf("[SET_PARAM] saddr=%s zone=%d data=%.6f\n", saddr.c_str(), (int)setting_zone, *(float*)p_data);
|
|
||||||
if(p_signal != nullptr && setting_zone < p_signal->vec_p_data.size())
|
if(p_signal != nullptr && setting_zone < p_signal->vec_p_data.size())
|
||||||
{
|
{
|
||||||
if(p_signal->data_type != data_type)
|
if(p_signal->data_type != data_type)
|
||||||
|
|
@ -1486,11 +1460,9 @@ int dc_signal_param_set_val_without_check(const std::string &saddr, uint8_t data
|
||||||
}
|
}
|
||||||
|
|
||||||
dc_set_signal_val(p_signal->vec_p_data[setting_zone], data_type, p_data);
|
dc_set_signal_val(p_signal->vec_p_data[setting_zone], data_type, p_data);
|
||||||
printf("[SET_PARAM_OK] saddr=%s zone=%d -> vec[%d]=%.6f, %p\n", saddr.c_str(), (int)setting_zone, (int)setting_zone, *(float*)p_signal->vec_p_data[setting_zone], p_signal->vec_p_data[setting_zone]);
|
|
||||||
dc_set_param_cfg_change(true);
|
dc_set_param_cfg_change(true);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
printf("[SET_PARAM_FAIL] saddr=%s zone=%d signal=%p size=%d\n", saddr.c_str(), (int)setting_zone, (void*)p_signal, p_signal ? (int)p_signal->vec_p_data.size() : -1);
|
|
||||||
|
|
||||||
LOG_E("saddr %s not found in param table", saddr.c_str());
|
LOG_E("saddr %s not found in param table", saddr.c_str());
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -2119,7 +2091,7 @@ LOCAL stru_signal *dc_find_signal_by_id(uint32_t id, stru_signal_map &dc_signal_
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void dc_param_cfg_check(const std::string &path)
|
void dc_param_cfg_check()
|
||||||
{
|
{
|
||||||
if (false == dc_get_param_cfg_change()) return;
|
if (false == dc_get_param_cfg_change()) return;
|
||||||
|
|
||||||
|
|
@ -2183,16 +2155,12 @@ void dc_param_cfg_check(const std::string &path)
|
||||||
sig_elem->SetAttribute("num", p_param->vec_p_data.size());
|
sig_elem->SetAttribute("num", p_param->vec_p_data.size());
|
||||||
|
|
||||||
|
|
||||||
printf("[SAVE_PARAM] saddr=%s num=%d\n", p_param->saddr.c_str(), (int)p_param->vec_p_data.size());
|
|
||||||
for(int i = 0; i < p_param->vec_p_data.size(); i++)
|
for(int i = 0; i < p_param->vec_p_data.size(); i++)
|
||||||
{
|
{
|
||||||
std::string val = dc_get_signal_val(p_param->vec_p_data[i], p_param->data_type);
|
|
||||||
std::string def = dc_get_signal_val(p_param->vec_p_default_data[i], p_param->data_type);
|
|
||||||
printf(" zone %d: val=%s def=%s, %p, %p\n", i+1, val.c_str(), def.c_str(), p_param->vec_p_data[i], p_param->vec_p_default_data[i]);
|
|
||||||
XMLElement *item_elem = doc.NewElement("Item");
|
XMLElement *item_elem = doc.NewElement("Item");
|
||||||
item_elem->SetAttribute("index", i+1);
|
item_elem->SetAttribute("index", i+1);
|
||||||
item_elem->SetAttribute("value", val.c_str());
|
item_elem->SetAttribute("value", dc_get_signal_val(p_param->vec_p_data[i], p_param->data_type).c_str());
|
||||||
item_elem->SetAttribute("default", def.c_str());
|
item_elem->SetAttribute("default", dc_get_signal_val(p_param->vec_p_default_data[i], p_param->data_type).c_str());
|
||||||
|
|
||||||
sig_elem->InsertEndChild(item_elem);
|
sig_elem->InsertEndChild(item_elem);
|
||||||
}
|
}
|
||||||
|
|
@ -2203,20 +2171,14 @@ void dc_param_cfg_check(const std::string &path)
|
||||||
root->InsertEndChild(ao_elem);
|
root->InsertEndChild(ao_elem);
|
||||||
root->InsertEndChild(param_elem);
|
root->InsertEndChild(param_elem);
|
||||||
|
|
||||||
// 保存到 self_param.xml,不覆盖参数模板 param.xml
|
if (XML_SUCCESS == doc.SaveFile(DC_PARAM_PATH))
|
||||||
std::string self_path = path;
|
|
||||||
size_t pos = self_path.rfind("param.xml");
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
self_path.replace(pos, 9, "self_param.xml");
|
|
||||||
|
|
||||||
if (XML_SUCCESS == doc.SaveFile(self_path.c_str()))
|
|
||||||
{
|
{
|
||||||
dc_set_param_cfg_change(false);
|
dc_set_param_cfg_change(false);
|
||||||
MY_LOG_I("param runtime saved to %s", self_path.c_str());
|
MY_LOG_I("param config file saved to %s", DC_PARAM_PATH);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MY_LOG_E("failed to save param runtime to %s", self_path.c_str());
|
MY_LOG_E("failed to save param config file to %s", DC_PARAM_PATH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#include "mySystem.h"
|
#include "mySystem.h"
|
||||||
#include "myLog.h"
|
#include "myLog.h"
|
||||||
#include "myFunc.h"
|
|
||||||
#include "myCmd.h"
|
#include "myCmd.h"
|
||||||
#include "iec.h"
|
#include "iec.h"
|
||||||
#include "iec_method.h"
|
#include "iec_method.h"
|
||||||
|
|
@ -395,15 +394,7 @@ void iec60870_task(uint16_t usGap)
|
||||||
|
|
||||||
int app_iec_init1(void *arg)
|
int app_iec_init1(void *arg)
|
||||||
{
|
{
|
||||||
char proc_dir[512] = {0};
|
if(0 != iec_cfg_parse("/mnt/RTU/test/config/iec1014.xml"))
|
||||||
if(0 != func_get_process_self_dir(proc_dir, sizeof(proc_dir)))
|
|
||||||
{
|
|
||||||
MY_LOG_E("func_get_process_self_dir failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
std::string cfg_path = std::string(proc_dir) + "config/IEC60870/iec1014.xml";
|
|
||||||
|
|
||||||
if(0 != iec_cfg_parse(cfg_path.c_str()))
|
|
||||||
{
|
{
|
||||||
MY_LOG_E("app_iec_init1 iec_cfg_parse failed");
|
MY_LOG_E("app_iec_init1 iec_cfg_parse failed");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,14 @@
|
||||||
#include "iec61850m.h"
|
#include "iec61850m.h"
|
||||||
#include "myLog.h"
|
#include "myLog.h"
|
||||||
#include "myFunc.h"
|
|
||||||
#include "myCmd.h"
|
#include "myCmd.h"
|
||||||
#include "iec61850_client.h"
|
#include "iec61850_client.h"
|
||||||
#include "myDatacenter.h"
|
#include "myDatacenter.h"
|
||||||
#include "parse_xml.h"
|
#include "parse_xml.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LOCAL void mms_event_back(void *arg, int ret);
|
LOCAL void mms_event_back(void *arg, int ret);
|
||||||
|
|
||||||
LOCAL std::string get_base_path()
|
LOCAL const char *g_61850m_prj_path = "/mnt/RTU/test/config/MMS/";
|
||||||
{
|
|
||||||
char proc_dir[512] = {0};
|
|
||||||
if(0 != func_get_process_self_dir(proc_dir, sizeof(proc_dir)))
|
|
||||||
{
|
|
||||||
MY_LOG_E("func_get_process_self_dir failed");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return std::string(proc_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCAL std::string g_61850m_prj_path;
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
|
@ -99,7 +86,7 @@ LOCAL void mms_send_control(int fd, const char *saddr, const char *val, uint8_t
|
||||||
|
|
||||||
MY_LOG_I("p_event->val %s", p_event->val);
|
MY_LOG_I("p_event->val %s", p_event->val);
|
||||||
|
|
||||||
mms_m_out_do_set_yk(p_event);
|
mms_m_out_do_set_yk_or_param(p_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCAL void mms_event_back(void *arg, int ret)
|
LOCAL void mms_event_back(void *arg, int ret)
|
||||||
|
|
@ -123,12 +110,7 @@ LOCAL void mms_data_back(stru_mms_m_out_value *p)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(p->reason != IEC61850_REASON_DATA_CHANGE &&
|
if(p->reason != IEC61850_REASON_DATA_CHANGE)
|
||||||
// p->reason != IEC61850_REASON_GI &&
|
|
||||||
// p->reason != IEC61850_REASON_INTEGRITY &&
|
|
||||||
// p->reason != 64 && // MMS_M_REASON_ALL_CALL
|
|
||||||
p->reason != MMS_M_REASON_READ_AO && // MMS_M_REASON_READ_PARAM
|
|
||||||
p->reason != MMS_M_REASON_READ_PARAM)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -146,9 +128,6 @@ LOCAL void mms_data_back(stru_mms_m_out_value *p)
|
||||||
case MMS_FLOAT:
|
case MMS_FLOAT:
|
||||||
printf("value %f, ", MY_GET_DATA_WITH_TYPE(p->p_value, float));
|
printf("value %f, ", MY_GET_DATA_WITH_TYPE(p->p_value, float));
|
||||||
break;
|
break;
|
||||||
case MMS_STRING:
|
|
||||||
printf("value %s, ", (char *)p->p_value);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -182,39 +161,10 @@ LOCAL void mms_data_back(stru_mms_m_out_value *p)
|
||||||
dc_set_out_signal_val(p_cfg->point.p_mx[j].saddr, p->p_value);
|
dc_set_out_signal_val(p_cfg->point.p_mx[j].saddr, p->p_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(uint32_t j = 0; j < p_cfg->point.ao_num; j++)
|
|
||||||
{
|
|
||||||
if(strcmp(p_cfg->point.p_ao[j].saddr, p->name) == 0 && NULL != p_cfg->point.p_ao[j].value.p_val[0])
|
|
||||||
{
|
|
||||||
auto it_ao = g_mms_m_type_to_local_type.find(p_cfg->point.p_ao[j].type);
|
|
||||||
uint8_t local_type = (it_ao != g_mms_m_type_to_local_type.end()) ? it_ao->second : 0;
|
|
||||||
if(local_type != 0)
|
|
||||||
{
|
|
||||||
dc_signal_ao_set_val_without_check(p_cfg->point.p_ao[j].saddr, local_type, p->p_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(uint32_t j = 0; j < p_cfg->point.param_num; j++)
|
|
||||||
{
|
|
||||||
if(strcmp(p_cfg->point.p_param[j].saddr, p->name) == 0 && NULL != p_cfg->point.p_param[j].value.p_val[0])
|
|
||||||
{
|
|
||||||
auto it_param = g_mms_m_type_to_local_type.find(p_cfg->point.p_param[j].type);
|
|
||||||
uint8_t local_type = (it_param != g_mms_m_type_to_local_type.end()) ? it_param->second : 0;
|
|
||||||
if(local_type != 0)
|
|
||||||
{
|
|
||||||
printf("mms_data_back: set param %s zone=%d\n", p_cfg->point.p_param[j].saddr, p->set_zone);
|
|
||||||
dc_signal_param_set_val_without_check(p_cfg->point.p_param[j].saddr, local_type, p->set_zone - 1, p->p_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCAL int iec61850m_ext_demo(int fd);
|
|
||||||
|
|
||||||
LOCAL void iec61850m_connect_status(int fd, int status)
|
LOCAL void iec61850m_connect_status(int fd, int status)
|
||||||
{
|
{
|
||||||
std::string prj_name = "";
|
std::string prj_name = "";
|
||||||
|
|
@ -234,13 +184,6 @@ LOCAL void iec61850m_connect_status(int fd, int status)
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_I("iec61850m_connect_status, fd %d, prj_name %s, status %s", fd, prj_name.c_str(), status == MMS_M_ON_LINE? "ON_LINE" : "OFF_LINE");
|
LOG_I("iec61850m_connect_status, fd %d, prj_name %s, status %s", fd, prj_name.c_str(), status == MMS_M_ON_LINE? "ON_LINE" : "OFF_LINE");
|
||||||
|
|
||||||
if (MMS_M_ON_LINE == status)
|
|
||||||
{
|
|
||||||
iec61850m_ext_demo(fd);
|
|
||||||
mms_m_out_read_ao_or_params(fd, _MMS_M_EVENT_AO_READ, NULL);
|
|
||||||
mms_m_out_read_ao_or_params(fd, _MMS_M_EVENT_PARAM_READ, NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -474,8 +417,9 @@ LOCAL int iec61850m_ao_signal_init(int num, stru_point_item *p_item)
|
||||||
|
|
||||||
uint8_t local_type = g_mms_m_type_to_local_type[p->type];
|
uint8_t local_type = g_mms_m_type_to_local_type[p->type];
|
||||||
uint8_t ctrl_type = SIGNAL_CTRL_TYPE::DIRECT_NORMAL;
|
uint8_t ctrl_type = SIGNAL_CTRL_TYPE::DIRECT_NORMAL;
|
||||||
|
stru_signal_param param = {0};
|
||||||
|
|
||||||
if(0 != dc_signal_ao(p->saddr, p->desc, local_type, ctrl_type, p->value.p_val[0], p->value.p_default[0], iec61850m_signal_ao_change_callback))
|
if(0 != dc_signal_ao(p->saddr, p->desc, local_type, param, ctrl_type, p->value.p_val[0], p->value.p_default[0], iec61850m_signal_ao_change_callback))
|
||||||
{
|
{
|
||||||
MY_LOG_E("dc_signal_ao failed, saddr %s, desc %s, type %d", p->saddr, p->desc, p->type);
|
MY_LOG_E("dc_signal_ao failed, saddr %s, desc %s, type %d", p->saddr, p->desc, p->type);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -501,8 +445,9 @@ LOCAL int iec61850m_param_signal_init(int num, stru_point_item *p_item)
|
||||||
|
|
||||||
uint8_t local_type = g_mms_m_type_to_local_type[p->type];
|
uint8_t local_type = g_mms_m_type_to_local_type[p->type];
|
||||||
uint8_t ctrl_type = SIGNAL_CTRL_TYPE::DIRECT_NORMAL;
|
uint8_t ctrl_type = SIGNAL_CTRL_TYPE::DIRECT_NORMAL;
|
||||||
|
stru_signal_param param = {0};
|
||||||
|
|
||||||
if(0 != dc_signal_param(p->saddr, p->desc, local_type, ctrl_type, p->value.p_val, p->value.p_default, MMS_M_MAX_VAL_NUM, iec61850m_signal_param_change_callback))
|
if(0 != dc_signal_param(p->saddr, p->desc, local_type, param, ctrl_type, p->value.p_val, p->value.p_default, MMS_M_MAX_VAL_NUM, iec61850m_signal_param_change_callback))
|
||||||
{
|
{
|
||||||
MY_LOG_E("dc_signal_param failed, saddr %s, desc %s, type %d", p->saddr, p->desc, p->type);
|
MY_LOG_E("dc_signal_param failed, saddr %s, desc %s, type %d", p->saddr, p->desc, p->type);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -602,22 +547,6 @@ int iec61850m_init()
|
||||||
}
|
}
|
||||||
|
|
||||||
MY_LOG_I("mms_m_out_init success, fd %d, no %d, prj_name %s", p_info->fd, i, p_info->prj_name);
|
MY_LOG_I("mms_m_out_init success, fd %d, no %d, prj_name %s", p_info->fd, i, p_info->prj_name);
|
||||||
|
|
||||||
if(0 != mms_m_out_set_rcb_numbers(p_info->fd, "01"))
|
|
||||||
// if(0 != mms_m_out_set_rcb_numbers(p_info->fd, "01,02,03"))
|
|
||||||
// if(0 != mms_m_out_set_rcb_numbers(p_info->fd, "*"))
|
|
||||||
{
|
|
||||||
MY_LOG_E("mms_m_out_set_rcb_numbers failed, fd %d, prj_name %s", p_info->fd, p_info->prj_name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind the first AO signal as the param zone indicator
|
|
||||||
stru_cfg *p_cfg = p_info->p_cfg;
|
|
||||||
if (p_cfg && p_cfg->point.ao_num > 0 && strlen(p_cfg->point.p_ao[0].saddr) > 0)
|
|
||||||
{
|
|
||||||
mms_m_out_bind_param_zone_signal(p_info->fd, p_cfg->point.p_ao[0].saddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
mms_m_out_get_value(p_info->fd, mms_data_back);
|
mms_m_out_get_value(p_info->fd, mms_data_back);
|
||||||
mms_m_out_get_connect_status(p_info->fd, iec61850m_connect_status);
|
mms_m_out_get_connect_status(p_info->fd, iec61850m_connect_status);
|
||||||
}
|
}
|
||||||
|
|
@ -632,14 +561,6 @@ int app_iec61850m_init1(void *arg)
|
||||||
|
|
||||||
dc_signal_out("iec61850m.run_cnt", "iec61850m.run_cnt", DATA_TYPE_U32, &p_app->run_cnt);
|
dc_signal_out("iec61850m.run_cnt", "iec61850m.run_cnt", DATA_TYPE_U32, &p_app->run_cnt);
|
||||||
|
|
||||||
std::string base_path = get_base_path();
|
|
||||||
if(base_path.empty())
|
|
||||||
{
|
|
||||||
MY_LOG_E("get_base_path failed, cannot get process directory");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
g_61850m_prj_path = base_path + "config/MMS/";
|
|
||||||
|
|
||||||
if(0 != iec61850m_init())
|
if(0 != iec61850m_init())
|
||||||
{
|
{
|
||||||
LOG_E("iec61850m_init failed");
|
LOG_E("iec61850m_init failed");
|
||||||
|
|
@ -649,88 +570,6 @@ int app_iec61850m_init1(void *arg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
* 新增功能演示回调
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
/***************************************************************************** * 新增功能演示回调 * 连接上线后由 iec61850m_ext_demo() 调用,演示文件服务、服务器查询、定值组操作 *****************************************************************************/
|
|
||||||
LOCAL void demo_file_read_cb(int fd, const char *filename,
|
|
||||||
const uint8_t *p_data, int data_len, bool more_follows,
|
|
||||||
int err)
|
|
||||||
/** 文件读取回调 */
|
|
||||||
{
|
|
||||||
if (IED_ERROR_OK != err)
|
|
||||||
{
|
|
||||||
LOG_E("file read failed: %s, err=%d", filename, err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_I("file %s read %d bytes", filename, data_len);
|
|
||||||
|
|
||||||
/* TODO: 将文件数据写入本地磁盘 */
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCAL void demo_file_delete_cb(int fd, const char *filename, int err)
|
|
||||||
{
|
|
||||||
if (IED_ERROR_OK == err)
|
|
||||||
{
|
|
||||||
/** 文件删除回调 */
|
|
||||||
LOG_I("file %s deleted successfully", filename);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG_E("file %s delete failed: %d", filename, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCAL void demo_server_query_cb(int fd, const char *vendor,
|
|
||||||
const char *model, const char *revision,
|
|
||||||
int logical_status, int physical_status, int err)
|
|
||||||
{
|
|
||||||
if (IED_ERROR_OK != err)
|
|
||||||
/** 服务器身份和状态查询回调 */
|
|
||||||
{
|
|
||||||
LOG_E("server query failed: %d", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_I("server: vendor=%s, model=%s, rev=%s, log_st=%d, phy_st=%d",
|
|
||||||
vendor, model, revision, logical_status, physical_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCAL void demo_sg_cb(int fd, const char *ld_name,
|
|
||||||
int act_sg, int num_sg, int err)
|
|
||||||
{
|
|
||||||
if (IED_ERROR_OK != err)
|
|
||||||
{
|
|
||||||
LOG_E("read SG info failed: %s, err=%d", ld_name, err);
|
|
||||||
/** 定值组信息读取回调 */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_I("%s: active SG=%d, total SG=%d", ld_name, act_sg, num_sg);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCAL int iec61850m_ext_demo(int fd)
|
|
||||||
{
|
|
||||||
/* 查询服务器信息 */
|
|
||||||
mms_m_query_server(fd, demo_server_query_cb);
|
|
||||||
|
|
||||||
/* 读取定值组信息 */
|
|
||||||
mms_m_read_sg_info(fd, "PROT", demo_sg_cb);
|
|
||||||
/** * 新功能演示入口 * 查询服务器信息、读取定值组状态 */
|
|
||||||
|
|
||||||
/* 删除文件示例 */
|
|
||||||
// mms_m_delete_file(fd, "IEDSERVER.BIN", demo_file_delete_cb);
|
|
||||||
|
|
||||||
/* 读取文件示例 */
|
|
||||||
// mms_m_read_file(fd, "COMTRADE/FAULT01.CFG", demo_file_read_cb);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int app_iec61850m_init2(void *arg)
|
int app_iec61850m_init2(void *arg)
|
||||||
{
|
{
|
||||||
static uint32_t *p_iec_run_cnt = NULL;
|
static uint32_t *p_iec_run_cnt = NULL;
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,10 @@
|
||||||
#include "iec61850_common.h"
|
#include "iec61850_common.h"
|
||||||
#include "iec61850s.h"
|
#include "iec61850s.h"
|
||||||
#include "myLog.h"
|
#include "myLog.h"
|
||||||
#include "myFunc.h"
|
|
||||||
#include "myCmd.h"
|
#include "myCmd.h"
|
||||||
#include "myDatacenter.h"
|
#include "myDatacenter.h"
|
||||||
#include "parse_xml.h"
|
#include "parse_xml.h"
|
||||||
|
|
||||||
LOCAL std::string get_base_path()
|
|
||||||
{
|
|
||||||
char proc_dir[512] = {0};
|
|
||||||
if(0 != func_get_process_self_dir(proc_dir, sizeof(proc_dir)))
|
|
||||||
{
|
|
||||||
MY_LOG_E("func_get_process_self_dir failed");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return std::string(proc_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
stru_mms_s_signal_base base;
|
stru_mms_s_signal_base base;
|
||||||
|
|
@ -42,7 +30,7 @@ LOCAL void iec61850s_st_mx_change_callback(std::string saddr, uint8_t data_type,
|
||||||
std::string val = dc_get_signal_val(p_data, data_type);
|
std::string val = dc_get_signal_val(p_data, data_type);
|
||||||
if(val.empty())
|
if(val.empty())
|
||||||
{
|
{
|
||||||
LOG_E("val is empty for saddr: %s", saddr.c_str());
|
LOG_E("val is empty");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -326,13 +314,13 @@ LOCAL int iec61850s_param_signals_init(std::vector<stru_mms_s_signal_base> &vec_
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_vec_param.push_back(param);
|
param.param_num = p_data_vec.size();
|
||||||
|
for(uint8_t i = 0; i < param.param_num; ++i)
|
||||||
g_vec_param.back().param_num = p_data_vec.size();
|
|
||||||
for(uint8_t i = 0; i < p_data_vec.size(); ++i)
|
|
||||||
{
|
{
|
||||||
g_vec_param.back().p_data[i] = p_data_vec[i];
|
param.p_data[i] = p_data_vec[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_vec_param.push_back(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(0 != mms_s_param_register(g_vec_param.data(), g_vec_param.size(), iec61850s_param_callback))
|
if(0 != mms_s_param_register(g_vec_param.data(), g_vec_param.size(), iec61850s_param_callback))
|
||||||
|
|
@ -391,23 +379,15 @@ int iec61850s_signals_init()
|
||||||
|
|
||||||
int app_iec61850s_init1(void *arg)
|
int app_iec61850s_init1(void *arg)
|
||||||
{
|
{
|
||||||
std::string base_path = get_base_path();
|
if(0 != parse_mms_xml("/mnt/RTU/test/config/MMS/mms_s.xml"))
|
||||||
if(base_path.empty())
|
|
||||||
{
|
|
||||||
MY_LOG_E("get_base_path failed, cannot get process directory");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
std::string mms_cfg_path = base_path + "config/MMS/mms_s.xml";
|
|
||||||
|
|
||||||
if(0 != parse_mms_xml(mms_cfg_path.c_str()))
|
|
||||||
{
|
{
|
||||||
MY_LOG_E("parse_mms_xml failed");
|
MY_LOG_E("parse_mms_xml failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mms_s_dbg_switch(false);
|
mms_s_dbg_switch(true);
|
||||||
mms_s_file_path_set(base_path.c_str());
|
mms_s_file_path_set("/mnt/RTU/test/");
|
||||||
mms_s_value_update_register(&g_mms_s_value_update_cb);
|
mms_s_value_update_register(&g_mms_s_value_update_cb);
|
||||||
if(NULL == g_mms_s_value_update_cb)
|
if(NULL == g_mms_s_value_update_cb)
|
||||||
{
|
{
|
||||||
|
|
@ -426,14 +406,7 @@ int app_iec61850s_init2(void *arg)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string base_path = get_base_path();
|
if(0 != mms_s_init("/mnt/RTU/test/config/MMS/PCS.icd", 102))
|
||||||
if(base_path.empty())
|
|
||||||
{
|
|
||||||
MY_LOG_E("get_base_path failed, cannot get process directory");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
std::string mms_icd_path = base_path + "config/MMS/PCS.icd";
|
|
||||||
if(0 != mms_s_init(mms_icd_path.c_str(), 102))
|
|
||||||
{
|
{
|
||||||
MY_LOG_E("mms_s_init failed");
|
MY_LOG_E("mms_s_init failed");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#include "method.h"
|
#include "method.h"
|
||||||
#include "myIcp67.h"
|
#include "myIcp67.h"
|
||||||
#include "myLog.h"
|
#include "myLog.h"
|
||||||
#include "myFunc.h"
|
|
||||||
|
|
||||||
#include "myMd5.h"
|
#include "myMd5.h"
|
||||||
#include "myCmd.h"
|
#include "myCmd.h"
|
||||||
|
|
@ -10,7 +9,6 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -23,21 +21,10 @@
|
||||||
|
|
||||||
LOCAL std::vector<stru_dir_info> g_dir_info_vec;
|
LOCAL std::vector<stru_dir_info> g_dir_info_vec;
|
||||||
|
|
||||||
LOCAL std::string get_base_path()
|
LOCAL std::string g_file_path = "/mnt/RTU/test/file/";
|
||||||
{
|
|
||||||
char proc_dir[512] = {0};
|
|
||||||
if(0 != func_get_process_self_dir(proc_dir, sizeof(proc_dir)))
|
|
||||||
{
|
|
||||||
LOG_E("func_get_process_self_dir failed, cannot get process directory, abort");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return std::string(proc_dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCAL std::string g_file_path = get_base_path() + "file/";
|
|
||||||
LOCAL std::string g_down_path = "DOWN";
|
LOCAL std::string g_down_path = "DOWN";
|
||||||
|
|
||||||
LOCAL std::string g_zip_path = get_base_path() + "zip/";
|
LOCAL std::string g_zip_path = "/mnt/RTU/test/zip/";
|
||||||
|
|
||||||
LOCAL std::string g_file_name = "";
|
LOCAL std::string g_file_name = "";
|
||||||
LOCAL std::string g_zip_name = "";
|
LOCAL std::string g_zip_name = "";
|
||||||
|
|
|
||||||
|
|
@ -892,7 +892,13 @@ int self_ptl_do_signal_out(stru_app *p_app)
|
||||||
stru_self_ptl_cfg_param_data *p_ao = &p_app_cfg_data->ao_vec.at(i);
|
stru_self_ptl_cfg_param_data *p_ao = &p_app_cfg_data->ao_vec.at(i);
|
||||||
if(p_ao && p_ao->p_param)
|
if(p_ao && p_ao->p_param)
|
||||||
{
|
{
|
||||||
ret |= dc_signal_ao(p_ao->p_param->base.saddr, p_ao->p_param->base.desc, p_ao->p_param->type, SIGNAL_CTRL_TYPE::SBO_NORMAL, p_ao->vec_p_data[0], p_ao->vec_p_default_data[0], self_ptl_signal_change_callback);
|
stru_signal_param param;
|
||||||
|
param.min = safeStringToFloat(p_ao->p_param->min);
|
||||||
|
param.max = safeStringToFloat(p_ao->p_param->max);
|
||||||
|
param.step = safeStringToFloat(p_ao->p_param->step);
|
||||||
|
param.unit = p_ao->p_param->unit;
|
||||||
|
|
||||||
|
ret |= dc_signal_ao(p_ao->p_param->base.saddr, p_ao->p_param->base.desc, p_ao->p_param->type, param, SIGNAL_CTRL_TYPE::SBO_NORMAL, p_ao->vec_p_data[0], p_ao->vec_p_default_data[0], self_ptl_signal_change_callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -906,7 +912,13 @@ int self_ptl_do_signal_out(stru_app *p_app)
|
||||||
stru_self_ptl_cfg_param_data *p_param = &p_app_cfg_data->param_vec.at(i);
|
stru_self_ptl_cfg_param_data *p_param = &p_app_cfg_data->param_vec.at(i);
|
||||||
if(p_param && p_param->p_param)
|
if(p_param && p_param->p_param)
|
||||||
{
|
{
|
||||||
ret |= dc_signal_param(p_param->p_param->base.saddr, p_param->p_param->base.desc, p_param->p_param->type,
|
stru_signal_param param;
|
||||||
|
param.min = safeStringToFloat(p_param->p_param->min);
|
||||||
|
param.max = safeStringToFloat(p_param->p_param->max);
|
||||||
|
param.step = safeStringToFloat(p_param->p_param->step);
|
||||||
|
param.unit = p_param->p_param->unit;
|
||||||
|
|
||||||
|
ret |= dc_signal_param(p_param->p_param->base.saddr, p_param->p_param->base.desc, p_param->p_param->type, param,
|
||||||
SIGNAL_CTRL_TYPE::SBO_NORMAL, p_param->vec_p_data.data(), p_param->vec_p_default_data.data(), p_param->p_param->num, self_ptl_signal_change_callback);
|
SIGNAL_CTRL_TYPE::SBO_NORMAL, p_param->vec_p_data.data(), p_param->vec_p_default_data.data(), p_param->p_param->num, self_ptl_signal_change_callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#include "web_server.h"
|
#include "web_server.h"
|
||||||
#include "ws_method.h"
|
#include "ws_method.h"
|
||||||
#include "myLog.h"
|
#include "myLog.h"
|
||||||
#include "myFunc.h"
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -84,15 +83,9 @@ LOCAL void *web_server_run(void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LOCAL int web_server_init()
|
LOCAL void web_server_init()
|
||||||
{
|
{
|
||||||
char proc_dir[512] = {0};
|
g_web_root = "/mnt/RTU/test/" + std::string(s_web_root);
|
||||||
if(0 != func_get_process_self_dir(proc_dir, sizeof(proc_dir)))
|
|
||||||
{
|
|
||||||
LOG_E("func_get_process_self_dir failed, cannot get process directory");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
g_web_root = std::string(proc_dir) + s_web_root;
|
|
||||||
|
|
||||||
mg_mgr_init(&mgr);
|
mg_mgr_init(&mgr);
|
||||||
|
|
||||||
|
|
@ -101,17 +94,12 @@ LOCAL int web_server_init()
|
||||||
pthread_create(&thread_id, NULL, web_server_run, NULL);
|
pthread_create(&thread_id, NULL, web_server_run, NULL);
|
||||||
|
|
||||||
LOG_I("web_server_init done");
|
LOG_I("web_server_init done");
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int app_web_server_init1(void *arg)
|
int app_web_server_init1(void *arg)
|
||||||
{
|
{
|
||||||
if(0 != web_server_init())
|
web_server_init();
|
||||||
{
|
|
||||||
LOG_E("web_server_init failed");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5441,25 +5441,25 @@
|
||||||
</LDevice>
|
</LDevice>
|
||||||
<LDevice inst="CTRL">
|
<LDevice inst="CTRL">
|
||||||
<LN0 lnType="NRR_LLN0_P9600" lnClass="LLN0" inst="">
|
<LN0 lnType="NRR_LLN0_P9600" lnClass="LLN0" inst="">
|
||||||
<DataSet name="dsRelayAin" desc="模拟量">
|
<!-- <DataSet name="dsRelayAin" desc="模拟量">
|
||||||
<FCDA ldInst="CTRL" lnClass="GAPC" lnInst="1" doName="DirHar1I0" daName="mag.f" fc="MX" />
|
<FCDA ldInst="CTRL" lnClass="GAPC" lnInst="1" doName="DirHar1I0" daName="mag.f" fc="MX" />
|
||||||
<FCDA ldInst="CTRL" lnClass="GAPC" lnInst="1" doName="DirHar5I0" daName="mag.f" fc="MX" />
|
<FCDA ldInst="CTRL" lnClass="GAPC" lnInst="1" doName="DirHar5I0" daName="mag.f" fc="MX" />
|
||||||
<FCDA ldInst="CTRL" lnClass="GAPC" lnInst="1" doName="MagHar1I0" daName="mag.f" fc="MX" />
|
<FCDA ldInst="CTRL" lnClass="GAPC" lnInst="1" doName="MagHar1I0" daName="mag.f" fc="MX" />
|
||||||
<FCDA ldInst="CTRL" lnClass="GAPC" lnInst="1" doName="MagHar5I0" daName="mag.f" fc="MX" />
|
<FCDA ldInst="CTRL" lnClass="GAPC" lnInst="1" doName="MagHar5I0" daName="mag.f" fc="MX" />
|
||||||
<FCDA ldInst="CTRL" lnClass="GAPC" lnInst="1" doName="VolHar1I0" daName="mag.f" fc="MX" />
|
<FCDA ldInst="CTRL" lnClass="GAPC" lnInst="1" doName="VolHar1I0" daName="mag.f" fc="MX" />
|
||||||
<FCDA ldInst="CTRL" lnClass="GAPC" lnInst="1" doName="VolHar5I0" daName="mag.f" fc="MX" />
|
<FCDA ldInst="CTRL" lnClass="GAPC" lnInst="1" doName="VolHar5I0" daName="mag.f" fc="MX" />
|
||||||
</DataSet>
|
</DataSet> -->
|
||||||
<DataSet name="dsCommState" desc="通信工况信号数据集">
|
<DataSet name="dsCommState" desc="通信工况信号数据集">
|
||||||
<FCDA ldInst="CTRL" prefix="TC1" lnClass="CSWI" lnInst="1" doName="Pos" fc="ST" />
|
<FCDA ldInst="CTRL" prefix="TC1" lnClass="CSWI" lnInst="1" doName="Pos" fc="ST" />
|
||||||
<FCDA ldInst="CTRL" prefix="TC2" lnClass="CSWI" lnInst="1" doName="Pos" fc="ST" />
|
<FCDA ldInst="CTRL" prefix="TC2" lnClass="CSWI" lnInst="1" doName="Pos" fc="ST" />
|
||||||
<FCDA ldInst="CTRL" prefix="TC3" lnClass="CSWI" lnInst="1" doName="Pos" fc="ST" />
|
<FCDA ldInst="CTRL" prefix="TC3" lnClass="CSWI" lnInst="1" doName="Pos" fc="ST" />
|
||||||
<FCDA ldInst="CTRL" prefix="TC4" lnClass="CSWI" lnInst="1" doName="Pos" fc="ST" />
|
<FCDA ldInst="CTRL" prefix="TC4" lnClass="CSWI" lnInst="1" doName="Pos" fc="ST" />
|
||||||
</DataSet>
|
</DataSet>
|
||||||
<ReportControl name="urcbRelayAin" datSet="dsRelayAin" intgPd="30000" rptID="CTRL/LLN0$RP$urcbRelayAin" confRev="1" buffered="false" bufTime="0">
|
<!-- <ReportControl name="urcbRelayAin" datSet="dsRelayAin" intgPd="30000" rptID="CTRL/LLN0$RP$urcbRelayAin" confRev="1" buffered="false" bufTime="0">
|
||||||
<TrgOps dchg="true" period="true" />
|
<TrgOps dchg="true" period="true" />
|
||||||
<OptFields seqNum="true" timeStamp="true" dataSet="true" reasonCode="true" dataRef="false" entryID="false" configRef="false" />
|
<OptFields seqNum="true" timeStamp="true" dataSet="true" reasonCode="true" dataRef="false" entryID="false" configRef="false" />
|
||||||
<RptEnabled max="16" />
|
<RptEnabled max="16" />
|
||||||
</ReportControl>
|
</ReportControl> -->
|
||||||
<ReportControl name="brcbCommState" datSet="dsCommState" intgPd="0" rptID="CTRL/LLN0$BR$brcbCommState" confRev="1" buffered="true" bufTime="0">
|
<ReportControl name="brcbCommState" datSet="dsCommState" intgPd="0" rptID="CTRL/LLN0$BR$brcbCommState" confRev="1" buffered="true" bufTime="0">
|
||||||
<TrgOps dchg="true" qchg="false" dupd="false" period="true" />
|
<TrgOps dchg="true" qchg="false" dupd="false" period="true" />
|
||||||
<OptFields seqNum="true" timeStamp="true" dataSet="true" reasonCode="true" dataRef="true" entryID="true" configRef="true" />
|
<OptFields seqNum="true" timeStamp="true" dataSet="true" reasonCode="true" dataRef="true" entryID="true" configRef="true" />
|
||||||
|
|
@ -5739,7 +5739,7 @@
|
||||||
</DAI>
|
</DAI>
|
||||||
</DOI>
|
</DOI>
|
||||||
</LN>
|
</LN>
|
||||||
<LN lnType="NRR_GAPC_V1.01" lnClass="GAPC" inst="1">
|
<!-- <LN lnType="NRR_GAPC_V1.01" lnClass="GAPC" inst="1">
|
||||||
<DOI name="Mod" desc="Mode">
|
<DOI name="Mod" desc="Mode">
|
||||||
<DAI name="ctlModel">
|
<DAI name="ctlModel">
|
||||||
<Val>0</Val>
|
<Val>0</Val>
|
||||||
|
|
@ -5848,9 +5848,9 @@
|
||||||
<Val>SGCC MODEL:2009</Val>
|
<Val>SGCC MODEL:2009</Val>
|
||||||
</DAI>
|
</DAI>
|
||||||
</DOI>
|
</DOI>
|
||||||
</LN>
|
</LN> -->
|
||||||
</LDevice>
|
</LDevice>
|
||||||
<LDevice inst="MEAS">
|
<!-- <LDevice inst="MEAS">
|
||||||
<LN0 lnType="NRR_LLN0_P9600" lnClass="LLN0" inst="">
|
<LN0 lnType="NRR_LLN0_P9600" lnClass="LLN0" inst="">
|
||||||
<DataSet name="dsAin" desc="遥测数据集">
|
<DataSet name="dsAin" desc="遥测数据集">
|
||||||
<FCDA ldInst="MEAS" prefix="M" lnClass="MMXU" lnInst="1" doName="Hz" daName="mag.f" fc="MX" />
|
<FCDA ldInst="MEAS" prefix="M" lnClass="MMXU" lnInst="1" doName="Hz" daName="mag.f" fc="MX" />
|
||||||
|
|
@ -6598,7 +6598,7 @@
|
||||||
</DAI>
|
</DAI>
|
||||||
</DOI>
|
</DOI>
|
||||||
</LN>
|
</LN>
|
||||||
</LDevice>
|
</LDevice> -->
|
||||||
</Server>
|
</Server>
|
||||||
</AccessPoint>
|
</AccessPoint>
|
||||||
</IED>
|
</IED>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
<Item no="1" type="13" ctlModel="4" saddr="iec61850m.prj.ao.1" desc="参数02" LDev="PROT" LNode="LPHD1" DoName="DeviceName.setVal" fc="2" />
|
<Item no="1" type="13" ctlModel="4" saddr="iec61850m.prj.ao.1" desc="参数02" LDev="PROT" LNode="LPHD1" DoName="DeviceName.setVal" fc="2" />
|
||||||
</Ao>
|
</Ao>
|
||||||
<Param desc="定值">
|
<Param desc="定值">
|
||||||
<Item no="0" type="6" ctlModel="4" saddr="iec61850m.prj.param.0" desc="定值01" LDev="PROT" LNode="PTOC1" DoName="StrVal.setMag.f" fc="6" />
|
<Item no="0" type="6" ctlModel="4" saddr="iec61850m.prj.param.0" desc="定值01" LDev="PROT" LNode="PTOC1" DoName="StrVal.setMag.f" fc="2" />
|
||||||
</Param>
|
</Param>
|
||||||
</Point>
|
</Point>
|
||||||
</Root>
|
</Root>
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue