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

278 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 设置