# 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 &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 电度变化 | 电度扰动数据正常上送 |