RTU/mimo/plan/icp67多实例重构方案.md

10 KiB
Raw Permalink Blame History

ICP67 模块多实例重构方案

概述

libicp67 从全局单例回调架构重构为多实例安全架构,同时修复重传竞态 bug、填充空桩、统一硬编码为命名常量。


涉及文件

文件 改动量
release/inc/myIcp67.h 添加宏常量、添加 method 字段到 stru_icp67、修改 setter 声明
src/protocol/libicp67/src/icp67.cpp 移除 icp67_get_genneral_method() 调用、修复重传竞态、填充空桩
src/protocol/libicp67/src/general_method.cpp 删除 g_genneral_method、setter 改为实例化、method 初始化移入 icp67_init
src/protocol/libicp67/inc/general_method.h 删除文件(不再需要)
src/protocol/libicp67/inc/icp67.h 添加内部常量宏
src/system/libself_ptl/src/self_ptl.cpp setter 调用增加 p_icp67 参数、get 类回调改为直接通过 p_icp67->method 访问
src/system/libself_ptl/src/method.cpp get 类回调调用方式调整
src/system/libcom_decode/src/decode_frame_codec.cpp 更新 icp67_show_frame() 路径引用

任务分解

T1: 添加常量宏定义

文件: release/inc/myIcp67.hsrc/protocol/libicp67/inc/icp67.h

myIcp67.h 中新增:

#define ICP67_TX_BUF_SIZE   2048       // tx/resend_tx 缓冲区大小
#define ICP67_RESEND_MAX    5          // 重发最大次数
#define ICP67_TM_TICK_MS    10         // 定时器 tick 周期 (ms)
#define ICP67_TM_OUT_MS     1000       // 重传超时 (ms)
#define ICP67_MD5_LEN       16         // MD5 长度

icp67.h 中新增:

#define ICP67_HEAD_OVERHEAD 4          // head1(1)+len(2)+head2(1)
#define ICP67_FRAME_OVERHEAD 6         // head_overhead(4)+crc(1)+tail(1)
#define ICP67_UPGRADE_TYPE   1          // 升级类型
#define ICP67_NAME_MAX       256        // 文件名最大长度
#define ICP67_TM_STRUCT_LEN  7          // 时间结构体长度

将所有魔数替换为宏或 sizeof()pos += 4pos += sizeof(uint32_t) pos += 2pos += sizeof(uint16_t)16ICP67_MD5_LEN 等)。

stru_icp67 中的 tx[ICP67_TX_MAX_LEN]resend_tx[ICP67_TX_MAX_LEN] 改为 [ICP67_TX_BUF_SIZE]md5[16] 改为 md5[ICP67_MD5_LEN]


T2: 将 stru_genneral_method 移入 stru_icp67

文件: release/inc/myIcp67.h

stru_icp67 结构体中添加字段:

typedef struct _icp67
{
    void *arg;
    icp67_send_cb           send_cb;
    icp67_search_frame_cb   search_frame_cb;
    icp67_decode_cb         decode_cb;
    icp67_timer_handler_cb  timer_handler_cb;

    stru_genneral_method    method;   // ← 新增,替代全局 g_genneral_method

    uint8_t                 md5[ICP67_MD5_LEN];
    uint8_t                 tx[ICP67_TX_BUF_SIZE];
    // ... 其余不变
}icp67;

T3: 重构 setter/getter 为实例化接口

文件: src/protocol/libicp67/src/general_method.cpprelease/inc/myIcp67.h

变更

  1. 删除 g_genneral_method 全局变量和 icp67_get_genneral_method() 函数。

  2. 删除 general_method.h 文件(不再需要 icp67_get_genneral_method() 前向声明)。

  3. "pop_out/trans" 类 setter17个参数增加 stru_icp67 *p_icp67,写入 p_icp67->method.xxx_cb。示例:

    void icp67_set_mx_trans_cb(stru_icp67 *p_icp67, icp67_mx_trans_cb cb) {
        p_icp67->method.mx_trans_cb = cb;
    }
    
  4. "get" 类 setter12个不再从全局表"拉取"到 caller 结构体。改为让 caller 直接通过 p_icp67->method.xxx_cb() 调用。这些 setter 变为空操作或删除——caller 直接访问 g_self_ptl.icp67.method.ao_get_cb(...)

  5. 更新 myIcp67.h 中所有 extern 声明:增加 stru_icp67 *p_icp67 第一参数。"get" 类 12 个 setter 删除声明。

  6. method 初始化:在 icp67_init() 中将 p_icp67->method 的 sender 字段初始化为内部函数指针pop_out/trans 字段初始化为 NULL。

    // icp67_init 中新增
    p_icp67->method.ao_get_cb = icp67_ao_get;
    p_icp67->method.ao_set_cb = icp67_ao_set;
    // ... 13 个 sender 初始化
    
  7. 将 internal sender 函数LOCAL 改为公开(或提供 extern 声明),以便 icp67_init 可以引用它们。在 icp67.cpp 中声明:

    extern void icp67_ao_get(stru_icp67 *p, stru_ti_1_2_data *d, uint16_t n);
    extern void icp67_ao_set(stru_icp67 *p, uint8_t *d, uint16_t n);
    // ... 13个
    
  8. 更新 general_method.cpp:删除 g_genneral_method 初始化代码,将 13 个 sender 函数删除 LOCAL(或使用条件编译保留 static 内部链接,在 icp67.cpp 中使用函数指针表初始化)。


T4: 重构 decode 函数——从 instance 读回调

文件: src/protocol/libicp67/src/icp67.cpp

所有 decode 函数中,将:

stru_genneral_method *p_genneral_method = icp67_get_genneral_method();
p_genneral_method->ao_pop_out_cb(...);

改为:

p_icp67->method.ao_pop_out_cb(...);

涉及 16 个 decode 函数。移除 #include "general_method.h"


T5: 修复重传竞态Bug 1

文件: src/protocol/libicp67/src/icp67.cppicp67_timer_handler()~line 960-983

resend_cnt++tm_cnt = 0 从信号量外移入保护区内:

方案:在 icp67_rtx_flag_set 中增加参数控制 resend 递增,或在 timer handler 中通过一个新的受保护操作完成。

推荐方案:在 icp67_rtx_flag_set 增加重载或新函数 icp67_rtx_resend_inc(stru_icp67 *p_icp67)

void icp67_rtx_resend_inc(stru_icp67 *p_icp67) {
    sem_wait(&p_icp67->rtx_sem);
    p_icp67->resend_cnt++;
    p_icp67->tm_cnt = 0;
    sem_post(&p_icp67->rtx_sem);
}

timer handler 中调用此函数而非直接操作字段。tm_cnt++line 982也移入 rtx_sem 保护:

void icp67_rtx_tm_inc(stru_icp67 *p_icp67) {
    sem_wait(&p_icp67->rtx_sem);
    p_icp67->tm_cnt++;
    sem_post(&p_icp67->rtx_sem);
}

timer handler 调用:

icp67_rtx_tm_inc(p_icp67);
icp67_rtx_flag_get(p_icp67, rtx_flag, resend_cnt, tm_cnt);
if (rtx_flag == ICP67_TX_FLAG && resend_cnt < ICP67_RESEND_MAX && tm_cnt >= p_icp67->tm_out) {
    p_icp67->send_cb(p_icp67->resend_tx, p_icp67->resend_tx_len, p_icp67->arg);
    icp67_rtx_resend_inc(p_icp67);
    LOG_I("resend cnt %d", p_icp67->resend_cnt);
    icp67_show_frame(p_icp67->resend_tx, p_icp67->resend_tx_len);
}

T6: 发送新请求覆盖旧 resend_tx 防御Bug 3

文件: src/protocol/libicp67/src/icp67.cppicp67_rtx_flag_set()

在函数内增加 LOG_W 防御:

void icp67_rtx_flag_set(stru_icp67 *p_icp67, uint8_t flag, uint8_t resend_cnt, uint32_t tm_cnt)
{
    sem_wait(&p_icp67->rtx_sem);
    if (flag == ICP67_TX_FLAG && p_icp67->rtx_flag == ICP67_TX_FLAG) {
        LOG_W("icp67_rtx_flag_set: overwriting pending TX, prev resend_cnt=%d", p_icp67->resend_cnt);
    }
    p_icp67->rtx_flag = flag;
    p_icp67->resend_cnt = resend_cnt;
    p_icp67->tm_cnt = tm_cnt;
    if (flag == ICP67_TX_FLAG) {
        memcpy(p_icp67->resend_tx, p_icp67->tx, p_icp67->tx_len);
        p_icp67->resend_tx_len = p_icp67->tx_len;
    }
    sem_post(&p_icp67->rtx_sem);
}

T7: 填充空桩函数

文件: src/protocol/libicp67/src/icp67.cpp

TI_4 (icp67_decode_ti_4, line 196-199)

LOCAL int icp67_decode_ti_4(stru_icp67 *p_icp67, uint8_t *p_rx, uint16_t rx_len) {
    LOG_W("TI_4 not supported");
    icp67_rtx_flag_set(p_icp67, ICP67_RX_FLAG, 0, 0);
    return 0;
}

TI_203 (icp67_decode_ti_203, line 434-437)

LOCAL int icp67_decode_ti_203(stru_icp67 *p_icp67, uint8_t *p_rx, uint16_t rx_len) {
    LOG_W("TI_203 not supported");
    icp67_rtx_flag_set(p_icp67, ICP67_RX_FLAG, 0, 0);
    return 0;
}

TI_206:保持死代码不变(不注册到 map不删除


T8: 更新调用方 self_ptl

文件: src/system/libself_ptl/src/self_ptl.cppinc/method.hsrc/method.cpp

self_ptl.cpp (self_ptl_init() 函数)

  1. "pop_out/trans" 类17个每个setter 增加 p_icp67 第一参数:

    icp67_set_ao_pop_out_cb(p_icp67, p_method->ao_pop_out_cb);
    icp67_set_iec_point_tbl_pop_out_cb(p_icp67, p_method->iec_point_tbl_pop_out_cb);
    // ... 17个
    
  2. "get" 类12个原本通过 icp67_set_xxx_cb(&p_method->xxx) 从全局表"拉取"。删除这些调用。调用方式改为直接通过 p_icp67->method.xxx_cb()

    • 原来 g_self_ptl.method.ao_get_cb(p_icp67, ...) → 不变method 中仍存储回调,但现在 icp67_init 直接把函数指针写入 p_icp67->method
    • 实际上 g_self_ptl.method 中存储的就是从旧全局表拉取的指针。现在改为让 callers 通过 g_self_ptl.icp67.method.ao_get_cb(...) 调用。
  3. self_ptl_init() 删除 12 个 get 类 setter 调用,并移除 g_self_ptl.method 中 receiver 组字段的初始化赋值——改为直接从 p_icp67->method 读取。

method.cpp:所有通过 p_method->xxx_cb(p_icp67, ...) 的调用改为 p_icp67->method.xxx_cb(p_icp67, ...)


T9: 更新 libcom_decode 引用

文件: src/system/libcom_decode/src/decode_frame_codec.cpp

仅包含 myIcp67.h 使用 stru_head 定义和 icp67_show_frame()。移除 #include "general_method.h" 如果存在。icp67_show_frame() 调用确认是否受影响(此函数在 icp67.cpp 中为 LOCAL,外部不可见——需确认正确的调用路径。若 decode_frame_codec.cpp 调用它,需将其改为公开)。


T10: 编译验证

./release/build.sh

确认 libicp67.a 构建成功、RTU 链接成功。


验证方法

  1. 编译:./release/build.sh 通过,输出 release/x86/exe/RTU
  2. 确认无新增 warning-Wall -Wextra 级别)
  3. 代码审查确认:
    • g_genneral_method 全部消失
    • icp67_get_genneral_method() 全部消失
    • decode 函数全部通过 p_icp67->method.xxx_cb 回调
    • 重传竞态已修复(字段操作在 sem 内)
    • 所有魔数替换为命名常量
    • TI_4/TI_203 有 LOG_W + RX_FLAG 设置