RTU/mimo/plan/libiec修改计划.md

10 KiB
Raw Blame History

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

int32_t GetDdValueByGroup(..., TimeDd_Info *pstInfo, ...)
{
    // ... 计算 usMin, usMax ...
    // 缺少: 根据 usIdx 从 vec_dd 获取值并填充 pstInfo
    
    return 0;  // BUG: 永远返回 0pstInfo 未填充
}

修复: 根据 usIdx 和计算的 usMin/usMax 范围从 vec_dd 中读取实际电度值填充 pstInfo

2.2 🐛 Bug: YX 变化回调被禁用

文件: iec_point.cpp:22-28
现象: 遥信值变化被检测到,但既未写入 SOE 队列,也未更新 p_last_data

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 依赖此字段判断单/双点。

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 覆盖,前者无效。

StampToCp56(&pstInfo->staSoe[i].stCTime, ..., ...);  // 被覆盖
memcpy(&pstInfo->staSoe[i].stCTime, &..., sizeof(stCP56Time2a)); // 覆盖前者

修复: 删除 StampToCp56 调用,保留 memcpy(数据已在入队时计算好)。

2.5 ⚠️ 问题: GetTimeItfs/SetTimeItfs 为空桩

文件: iec_method.cpp:20-29
现象: 对时功能完全不可用。

int GetTimeItfs(__CP56Time2a *pTime) { return 0; }
int SetTimeItfs(__CP56Time2a *pTime) { return 0; }

修复: 实现 GetTimeItfs 调用系统时间,SetTimeItfs 调用 settimeofday

2.6 ⚠️ 问题: 遥控校验始终返回通过

文件: iec_method.cpp:754-757
现象: Cs10xYkVerifyItfs 始终返回 1允许遥控无实际校验逻辑。

uint8_t Cs10xYkVerifyItfs(void *pvParam) { return 1; }

修复: 接入数据中心的远方/就地状态和软压板状态进行校验。

2.7 ⚠️ 问题: 硬编码起始地址与 XML inf 属性冲突

文件: iec_method.cpp:9-11
现象: YX/YC/DD 起始地址硬编码,但 XML 配置中已有 inf 属性。

#define YX_START_PORT 0x01
#define YC_START_PORT 0x4001
#define DD_START_PORT 0x6401

GetYcValueByGroup 中:

pstInfo->uiInfoAddr = p_mx_info->inf;  // 使用了 XML 的 inf

GetYcCountByGroup 中:

*puiStartInf = YC_START_PORT;  // 仍用硬编码
*puiEndInf = YC_START_PORT + vec_mx.size();

修复: 统一使用 XML 配置中的 inf 值,移除硬编码起始地址。对于没有配置 inf 的点,自动分配序号。

2.8 ⚠️ 问题: 参数/文件/升级回调未注册

文件: iec.cpp:117-125
现象: CBB 库支持的参数读写、文件传输、软件升级功能均已注释。

// CS10x_S_IECParamHandler(...)     // 参数读写 → 未注册
// CS10x_S_GetParamInfoHandler(...) // 参数信息 → 未注册
// CS10x_S_FileReadOprtHandler(...)  // 文件读取 → 未注册
// CS10x_S_FileWriteOprtHandler(...) // 文件写入 → 未注册
// CS10x_S_UpdateProcHandler(...)    // 软件升级 → 未注册

修复: 根据需求逐步启用,实现对应的回调函数。

2.9 ⚠️ 问题: Dd 电度变化回调被禁用

文件: iec_point.cpp:76-79
现象: 电度值变化检测到,但未写入扰动电度队列。

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 修复

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 变化恢复

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 补充

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