10 KiB
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.h、src/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 += 4 → pos += sizeof(uint32_t); pos += 2 → pos += sizeof(uint16_t);16 → ICP67_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.cpp、release/inc/myIcp67.h
变更:
-
删除
g_genneral_method全局变量和icp67_get_genneral_method()函数。 -
删除
general_method.h文件(不再需要icp67_get_genneral_method()前向声明)。 -
"pop_out/trans" 类 setter(17个):参数增加
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; } -
"get" 类 setter(12个):不再从全局表"拉取"到 caller 结构体。改为让 caller 直接通过
p_icp67->method.xxx_cb()调用。这些 setter 变为空操作或删除——caller 直接访问g_self_ptl.icp67.method.ao_get_cb(...)。 -
更新
myIcp67.h中所有 extern 声明:增加stru_icp67 *p_icp67第一参数。"get" 类 12 个 setter 删除声明。 -
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 初始化 -
将 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个 -
更新
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.cpp,icp67_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.cpp,icp67_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.cpp、inc/method.h、src/method.cpp
self_ptl.cpp (self_ptl_init() 函数):
-
"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个 -
"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(...)调用。
- 原来
-
从
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 链接成功。
验证方法
- 编译:
./release/build.sh通过,输出release/x86/exe/RTU - 确认无新增 warning(
-Wall -Wextra级别) - 代码审查确认:
g_genneral_method全部消失icp67_get_genneral_method()全部消失- decode 函数全部通过
p_icp67->method.xxx_cb回调 - 重传竞态已修复(字段操作在 sem 内)
- 所有魔数替换为命名常量
- TI_4/TI_203 有 LOG_W + RX_FLAG 设置