278 lines
10 KiB
Markdown
278 lines
10 KiB
Markdown
# 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` 中新增:
|
||
```c
|
||
#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` 中新增:
|
||
```c
|
||
#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` 结构体中添加字段:
|
||
```c
|
||
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`
|
||
|
||
**变更**:
|
||
|
||
1. **删除** `g_genneral_method` 全局变量和 `icp67_get_genneral_method()` 函数。
|
||
|
||
2. **删除** `general_method.h` 文件(不再需要 `icp67_get_genneral_method()` 前向声明)。
|
||
|
||
3. **"pop_out/trans" 类 setter**(17个):参数增加 `stru_icp67 *p_icp67`,写入 `p_icp67->method.xxx_cb`。示例:
|
||
```c
|
||
void icp67_set_mx_trans_cb(stru_icp67 *p_icp67, icp67_mx_trans_cb cb) {
|
||
p_icp67->method.mx_trans_cb = cb;
|
||
}
|
||
```
|
||
|
||
4. **"get" 类 setter**(12个):不再从全局表"拉取"到 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。
|
||
```c
|
||
// 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` 中声明:
|
||
```c
|
||
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 函数中,将:
|
||
```c
|
||
stru_genneral_method *p_genneral_method = icp67_get_genneral_method();
|
||
p_genneral_method->ao_pop_out_cb(...);
|
||
```
|
||
改为:
|
||
```c
|
||
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)`:
|
||
```c
|
||
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` 保护:
|
||
```c
|
||
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 调用:
|
||
```c
|
||
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 防御:
|
||
```c
|
||
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):
|
||
```c
|
||
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):
|
||
```c
|
||
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()` 函数):
|
||
|
||
1. **"pop_out/trans" 类**(17个):每个setter 增加 `p_icp67` 第一参数:
|
||
```c
|
||
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: 编译验证
|
||
|
||
```bash
|
||
./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 设置
|