290 lines
10 KiB
Markdown
290 lines
10 KiB
Markdown
# libiec 修改计划
|
||
|
||
**日期**: 2026-06-29
|
||
**状态**: 阶段一二已完成 ✅ | 阶段三待定
|
||
**基于**: CBB 库 V2.01.059 + RTU 项目 libiec 源码分析
|
||
|
||
---
|
||
|
||
## 1. 模块概览
|
||
|
||
libiec 是 RTU 项目中 60870 协议的应用适配层,负责将 CBB 库与数据中心(datacenter)桥接。
|
||
|
||
```
|
||
┌──────────────────────────┐
|
||
│ libcom_decode (通信层) │
|
||
│ 原始数据 → interface路由 │
|
||
└──────────┬───────────────┘
|
||
│
|
||
┌──────────▼───────────────┐
|
||
│ libiec (本模块) │
|
||
│ ┌─────────────────────┐ │
|
||
│ │ iec.cpp │ │
|
||
│ │ • CS10x 实例管理(×3) │ │
|
||
│ │ • 101/104 自动识别 │ │
|
||
│ │ • 收发/定时调度 │ │
|
||
│ └────────┬────────────┘ │
|
||
│ │ │
|
||
│ ┌────────▼────────────┐ │
|
||
│ │ iec_method.cpp │ │
|
||
│ │ • 19个CBB回调实现 │ │
|
||
│ │ • 4个全局数据队列 │ │
|
||
│ └────────┬────────────┘ │
|
||
│ │ │
|
||
│ ┌────────▼────────────┐ │
|
||
│ │ iec_point.cpp │ │
|
||
│ │ • 信号→IEC回调绑定 │ │
|
||
│ │ • YX/YC变化检测 │ │
|
||
│ └────────┬────────────┘ │
|
||
│ │ │
|
||
│ ┌────────▼────────────┐ │
|
||
│ │ iec_cfg_parse.cpp │ │
|
||
│ │ • XML配置解析 │ │
|
||
│ └─────────────────────┘ │
|
||
└───────────────────────────┘
|
||
│
|
||
┌──────────▼───────────────┐
|
||
│ libdatacenter (数据中心) │
|
||
│ 信号池 / SOE队列 / 故障 │
|
||
└──────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 发现的问题
|
||
|
||
### 2.1 🐛 Bug: GetDdValueByGroup 始终返回 0
|
||
|
||
**文件**: `iec_method.cpp:540-577`
|
||
**现象**: 电度总召/组召获取值时,函数计算了 `usMin`/`usMax` 但结尾直接 `return 0`,从未填充 `pstInfo`。
|
||
|
||
```cpp
|
||
int32_t GetDdValueByGroup(..., TimeDd_Info *pstInfo, ...)
|
||
{
|
||
// ... 计算 usMin, usMax ...
|
||
// 缺少: 根据 usIdx 从 vec_dd 获取值并填充 pstInfo
|
||
|
||
return 0; // BUG: 永远返回 0,pstInfo 未填充
|
||
}
|
||
```
|
||
|
||
**修复**: 根据 `usIdx` 和计算的 `usMin`/`usMax` 范围从 `vec_dd` 中读取实际电度值填充 `pstInfo`。
|
||
|
||
### 2.2 🐛 Bug: YX 变化回调被禁用
|
||
|
||
**文件**: `iec_point.cpp:22-28`
|
||
**现象**: 遥信值变化被检测到,但既未写入 SOE 队列,也未更新 `p_last_data`。
|
||
|
||
```cpp
|
||
if(val != last_val)
|
||
{
|
||
// UserWriteDisturbYx(i, val); // 被注释
|
||
LOG_I("...");
|
||
// *(uint8_t *)p_last_data = val; // 被注释 → 每次变化都触发回调
|
||
}
|
||
```
|
||
|
||
**修复**:
|
||
1. 恢复 `UserWriteSoeEvent(i, val, ...)` 调用,确保 YX 变化能入 SOE 队列
|
||
2. 恢复 `*(uint8_t *)p_last_data = val` 避免重复触发
|
||
|
||
### 2.3 🐛 Bug: SOE 事件中 ucType 未赋值
|
||
|
||
**文件**: `iec_method.cpp:397-411`
|
||
**现象**: `UserWriteSoeEvent` 写入 SOE 时未设置 `ucType`,后续 `GetSOE` 依赖此字段判断单/双点。
|
||
|
||
```cpp
|
||
p_soe_info->usYxPot = usIndex;
|
||
p_soe_info->ucState = ucState;
|
||
p_soe_info->uiSeconds = uiSeconds;
|
||
p_soe_info->usMs = usMs;
|
||
// 缺失: p_soe_info->ucType = ...;
|
||
```
|
||
|
||
**修复**: 从 `vec_st[idx].type` 或配置获取遥信类型并赋值。
|
||
|
||
### 2.4 🐛 Bug: GetFaultEvent 时间双重写入
|
||
|
||
**文件**: `iec_method.cpp:718-724`
|
||
**现象**: 先用 `StampToCp56` 计算时间,紧接着 `memcpy` 覆盖,前者无效。
|
||
|
||
```cpp
|
||
StampToCp56(&pstInfo->staSoe[i].stCTime, ..., ...); // 被覆盖
|
||
memcpy(&pstInfo->staSoe[i].stCTime, &..., sizeof(stCP56Time2a)); // 覆盖前者
|
||
```
|
||
|
||
**修复**: 删除 `StampToCp56` 调用,保留 `memcpy`(数据已在入队时计算好)。
|
||
|
||
### 2.5 ⚠️ 问题: GetTimeItfs/SetTimeItfs 为空桩
|
||
|
||
**文件**: `iec_method.cpp:20-29`
|
||
**现象**: 对时功能完全不可用。
|
||
|
||
```cpp
|
||
int GetTimeItfs(__CP56Time2a *pTime) { return 0; }
|
||
int SetTimeItfs(__CP56Time2a *pTime) { return 0; }
|
||
```
|
||
|
||
**修复**: 实现 `GetTimeItfs` 调用系统时间,`SetTimeItfs` 调用 `settimeofday`。
|
||
|
||
### 2.6 ⚠️ 问题: 遥控校验始终返回通过
|
||
|
||
**文件**: `iec_method.cpp:754-757`
|
||
**现象**: `Cs10xYkVerifyItfs` 始终返回 1(允许遥控),无实际校验逻辑。
|
||
|
||
```cpp
|
||
uint8_t Cs10xYkVerifyItfs(void *pvParam) { return 1; }
|
||
```
|
||
|
||
**修复**: 接入数据中心的远方/就地状态和软压板状态进行校验。
|
||
|
||
### 2.7 ⚠️ 问题: 硬编码起始地址与 XML inf 属性冲突
|
||
|
||
**文件**: `iec_method.cpp:9-11`
|
||
**现象**: YX/YC/DD 起始地址硬编码,但 XML 配置中已有 `inf` 属性。
|
||
|
||
```cpp
|
||
#define YX_START_PORT 0x01
|
||
#define YC_START_PORT 0x4001
|
||
#define DD_START_PORT 0x6401
|
||
```
|
||
|
||
在 `GetYcValueByGroup` 中:
|
||
```cpp
|
||
pstInfo->uiInfoAddr = p_mx_info->inf; // 使用了 XML 的 inf
|
||
```
|
||
但 `GetYcCountByGroup` 中:
|
||
```cpp
|
||
*puiStartInf = YC_START_PORT; // 仍用硬编码
|
||
*puiEndInf = YC_START_PORT + vec_mx.size();
|
||
```
|
||
|
||
**修复**: 统一使用 XML 配置中的 `inf` 值,移除硬编码起始地址。对于没有配置 `inf` 的点,自动分配序号。
|
||
|
||
### 2.8 ⚠️ 问题: 参数/文件/升级回调未注册
|
||
|
||
**文件**: `iec.cpp:117-125`
|
||
**现象**: CBB 库支持的参数读写、文件传输、软件升级功能均已注释。
|
||
|
||
```cpp
|
||
// CS10x_S_IECParamHandler(...) // 参数读写 → 未注册
|
||
// CS10x_S_GetParamInfoHandler(...) // 参数信息 → 未注册
|
||
// CS10x_S_FileReadOprtHandler(...) // 文件读取 → 未注册
|
||
// CS10x_S_FileWriteOprtHandler(...) // 文件写入 → 未注册
|
||
// CS10x_S_UpdateProcHandler(...) // 软件升级 → 未注册
|
||
```
|
||
|
||
**修复**: 根据需求逐步启用,实现对应的回调函数。
|
||
|
||
### 2.9 ⚠️ 问题: Dd 电度变化回调被禁用
|
||
|
||
**文件**: `iec_point.cpp:76-79`
|
||
**现象**: 电度值变化检测到,但未写入扰动电度队列。
|
||
|
||
```cpp
|
||
if(fabs(fVal - last_fVal) > 0.1)
|
||
{
|
||
// UserWriteDisturbDd(i, fVal, 0, 0); // 被注释
|
||
// *(float *)p_last_data = fVal; // 被注释
|
||
}
|
||
```
|
||
|
||
**修复**: 恢复 `UserWriteDisturbDd` 调用并更新 `p_last_data`。
|
||
|
||
---
|
||
|
||
## 3. 执行步骤
|
||
|
||
### 阶段一:🐛 Bug 修复(高优先级)
|
||
|
||
| # | 步骤 | 文件 | 对应问题 |
|
||
|---|------|------|:---:|
|
||
| 1.1 | 修复 `GetDdValueByGroup` 返回值 | `iec_method.cpp` | 2.1 |
|
||
| 1.2 | 恢复 YX 变化 SOE 写入 | `iec_point.cpp` | 2.2 |
|
||
| 1.3 | 补充 SOE ucType 赋值 | `iec_method.cpp` | 2.3 |
|
||
| 1.4 | 删除 FaultEvent 双重时间写入 | `iec_method.cpp` | 2.4 |
|
||
|
||
### 阶段二:⚠️ 功能完善(中优先级)
|
||
|
||
| # | 步骤 | 文件 | 对应问题 |
|
||
|---|------|------|:---:|
|
||
| 2.1 | 实现 `GetTimeItfs`/`SetTimeItfs` | `iec_method.cpp` | 2.5 |
|
||
| 2.2 | 遥控校验接入数据中心 | `iec_method.cpp` | 2.6 |
|
||
| 2.3 | 恢复电度变化回调 | `iec_point.cpp` | 2.9 |
|
||
|
||
### 阶段三:🔧 架构优化(低优先级)
|
||
|
||
| # | 步骤 | 文件 | 对应问题 |
|
||
|---|------|------|:---:|
|
||
| 3.1 | 统一使用 XML `inf` 替代硬编码起始地址 | `iec_method.cpp` | 2.7 |
|
||
| 3.2 | 启用参数读写回调(按需) | `iec.cpp` + 新增 | 2.8 |
|
||
| 3.3 | 启用文件传输回调(按需) | `iec.cpp` + 新增 | 2.8 |
|
||
| 3.4 | 启用软件升级回调(按需) | `iec.cpp` + 新增 | 2.8 |
|
||
|
||
---
|
||
|
||
## 4. 修复细节
|
||
|
||
### 4.1 GetDdValueByGroup 修复
|
||
|
||
```cpp
|
||
int32_t GetDdValueByGroup(uint16_t usGroupID, uint16_t usIdx,
|
||
TimeDd_Info *pstInfo, void *pvParam, uint8_t ucTimeEn)
|
||
{
|
||
std::vector<stru_cfg_base> &vec_dd = iec_cfg_ptr_get()->vec_dd;
|
||
uint32_t dd_num = vec_dd.size();
|
||
|
||
if (usIdx >= dd_num) return 0;
|
||
|
||
// 确定索引范围
|
||
uint16_t usMin = 0, usMax = dd_num;
|
||
if (usGroupID != 0) {
|
||
if (ucTimeEn == 0) { usMin = 0; usMax = 8; }
|
||
else { usMin = 8; usMax = dd_num; }
|
||
}
|
||
if (usIdx >= usMax - usMin) return 0;
|
||
|
||
uint16_t idx = usMin + usIdx;
|
||
stru_cfg_base *p_dd_info = &vec_dd[idx];
|
||
pstInfo->uiInfoAddr = p_dd_info->inf;
|
||
pstInfo->fVal = p_dd_info->p_data ? *(float *)p_dd_info->p_data : 0.0f;
|
||
memset(&pstInfo->stCTime, 0, sizeof(__CP56Time2a));
|
||
return 1;
|
||
}
|
||
```
|
||
|
||
### 4.2 YX 变化恢复
|
||
|
||
```cpp
|
||
if (val != last_val) {
|
||
uint32_t ts_sec = time(NULL);
|
||
uint16_t ts_ms = 0;
|
||
UserWriteSoeEvent(i, val, ts_sec, ts_ms);
|
||
*(uint8_t *)p_last_data = val;
|
||
}
|
||
```
|
||
|
||
### 4.3 SOE ucType 补充
|
||
|
||
```cpp
|
||
p_soe_info->usYxPot = usIndex;
|
||
p_soe_info->ucState = ucState;
|
||
p_soe_info->ucType = vec_st[usIndex].type; // 新增
|
||
p_soe_info->uiSeconds = uiSeconds;
|
||
p_soe_info->usMs = usMs;
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 预期影响
|
||
|
||
| 修复项 | 影响 |
|
||
|--------|------|
|
||
| 2.1 电度 | 电度总召/组召恢复正常 |
|
||
| 2.2 YX变化 | 遥信变位能正常产生 SOE 事件 |
|
||
| 2.3 SOE ucType | SOE 事件中单/双点遥信正确区分 |
|
||
| 2.4 FaultEvent | 消除无效代码,时间戳正确 |
|
||
| 2.5 对时 | CBB 库对时功能可用 |
|
||
| 2.6 遥控 | 遥控操作接入安全校验 |
|
||
| 2.9 电度变化 | 电度扰动数据正常上送 |
|