RTU/mimo/问题处理文档.md

251 lines
11 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.

# 问题处理文档
---
## 2026-06-16: ICP67 模块多实例重构
**来源**[libicp67模块分析](./工程/libicp67模块分析.md)
### 修复项
1. **全局回调表单例化** — 将 `g_genneral_method` 从 static 全局变量移入 `stru_icp67.method`。26 个 setter 改为实例化接口(`stru_icp67 *` 第一参数)。删除 `general_method.h`、`icp67_get_genneral_method()`。所有 decode 函数改为 `p_icp67->method.xxx_cb` 回调。
2. **重传竞态修复**`icp67_timer_handler``resend_cnt++`/`tm_cnt=0`/`tm_cnt++` 移入 `rtx_sem` 保护区。
3. **发送覆盖防御**`icp67_rtx_flag_set` 增加 TX_FLAG 重复设置 LOG_E 警告。
4. **空桩填充**`icp67_decode_ti_4`、`icp67_decode_ti_203` 改为 LOG_E + ICP67_RX_FLAG 设置。
5. **硬编码清理** — 所有魔数替换为命名宏(`ICP67_TX_BUF_SIZE`、`ICP67_MD5_LEN`、`ICP67_TM_TICK_MS`/`ICP67_TM_OUT_MS`、`ICP67_HEAD_OVERHEAD`/`ICP67_FRAME_OVERHEAD` 等)。
6. **配置参数宏化** — 超时/重试/缓冲/定时器 tick 统一在 `myIcp67.h` 中用 `#define` 管理。
7. **sender 函数迁移** — 12 个内部 sender 函数从 `general_method.cpp` 迁入 `icp67.cpp``icp67_init` 中初始化。
**状态**:✅ 已完成
**涉及文件**`myIcp67.h`, `icp67.h`, `icp67.cpp`, `general_method.cpp`, `self_ptl.cpp`, `method.cpp`, `general_method.h`(已删除)
**设计决策**
- pack(1) 不转换ARM/x86 小端固定,风险收益比不成立)
- TI_11 len==11 注释不启(子函数已各自处理标志)
- dispatch map`g_map_ti_decode` 等)保持 staticread-only多实例安全
### #11 libcom_channel + libcom_scan 合并为 libcom_decode
**问题**两个模块紧耦合com_channel 调 com_recv_datacom_scan 调 com_channel_interface_get且 app_comm_channel 线程为空壳
**修复**
1. 合并为 `libcom_decode` 模块,按功能拆分 4 个文件
2. 整合为 1 个 app 线程(删除空壳线程)
3. 枚举值合并ENUM_APP_COMM + ENUM_APP_COM_SCAN → ENUM_APP_COM_DECODE
**状态**:✅ 已完成
**涉及文件**:新建 `src/system/libcom_decode/`4源文件+makefile修改 `mySystem.h`、`app_sys.cpp`、`self_ptl.cpp`、`iec.cpp`、`makefile`
**验证**`./release/build.sh` 编译通过
---
### #10 libcomm + 调用者缺陷修复
**问题**
1. UDP `recvfrom` 永久阻塞,无超时无退出机制
2. UDP `close``state_cb(id, -1, disconnected)`,回调拿到的是已关闭的 -1
3. `com_scan`/`self_ptl`/`iec` 三处 send+event 回滚竞态:`msg_queue_send` 成功 → `event_send` 失败 → `try_recv` 可能取出旧消息
4. `com_channel_recv_cb``comm_send` 到已断开的 TCP_C_0 不检查 fd
**修复**
1. `udp_run``recvfrom` 前加 `select` 1s 超时
2. `udp_close` 先保存 fd 再 close回调传入正确值
3. 三处改为先 `event_send``msg_queue_send`,消除回滚竞态
4. `com_channel_recv_cb``comm_send` 前检查 `socket_fd >= 0`
**状态**:✅ 已完成
**涉及文件**`comm_udp.cpp`, `com_scan.cpp`, `self_ptl.cpp`, `iec.cpp`, `com_channel.cpp`
**验证**`./release/build.sh` 编译通过
---
### #9 libcomm 模块修补
**问题**
1. UART send 不完整:裸 write 无流量控制,缺少原始模式终端设置(~ICANON/~ECHO/~OPOST
2. TCP client select 100ms 空轮询,空闲时 CPU 空转
3.`comm_destroy` 接口,内存泄漏
4. `stru_comm` 结构体中 6 个函数指针字段从未使用,冗余
**修复**
1. 重写 `comm_uart.cpp`:恢复原始模式 + VTIME=1 读超时 + tcflush/tcdrain 完整发送
2. TCP client select 去掉超时参数,永久阻塞
3. 新增 `comm_destroy(id)` API
4. 删除 `stru_comm` 中 6 个未使用字段
**状态**:✅ 已完成
**涉及文件**`comm_uart.cpp`, `comm_tcp.cpp`, `comm.cpp`, `comm.h`, `myComm.h`
**验证**`./release/build.sh` 编译通过
---
### #8 libtask 事件/消息队列缺陷修复
**问题**
1. `task_event_destroy``task_msg_queue_destroy``free(p)` 后仍访问 `p->name`use-after-free
2. `task_event_recv` 中 AND 检查用 `opt == TASK_EVENT_FLAG_AND` 而非位测试,`AND | CLEAR` 组合被误识别为 OR
3. `task_event_send``pthread_cond_signal` 而非 `cond_broadcast`,多等待者场景可能唤醒不足
**修复**
1. `free(p)` 移到 `LOG_I` 之后,先打日志再释放
2. `opt ==` 改为 `opt &` 位测试
3. `cond_signal` 改为 `cond_broadcast`
**状态**:✅ 已完成
**涉及文件**`src/public/libtask/src/myTask.c`
**验证**`./release/build.sh` 编译通过
---
### #7 libtask 定时器 SIGEV_THREAD 线程爆炸
**问题**`myTask.c` 使用 `timer_create(CLOCK_REALTIME, SIGEV_THREAD)` 模式每次定时器超时内核创建一个新线程执行回调。9 个 app 线程 × 3 个定时器10ms/100ms/1000ms= 27 个定时器,每秒约 1000 次内核线程创建/销毁,系统开销极大。回调仅做 `task_event_send` 设一个事件位(几微秒),线程创建开销远大于实际工作。
**需求**
1. 消除每次定时器超时创建线程的开销
2. API 签名全部不变,调用方零修改
3. 仅 Linux 平台
**处理计划**[libtask定时器SIGEV_THREAD优化](./plan/libtask定时器SIGEV_THREAD优化.md)
**修复方案**:用 Linux **timerfd** + **epoll** + **单例管理器线程** 替代 `SIGEV_THREAD`
- `stru_task_timer``timer_t timerid` → `int timerfd`,新增 `pthread_cond_t cond`、`int in_epoll`
- 删除 `task_timer_sig_handler`,新增 `timer_manager_thread`epoll_wait 监听所有 timerfd超时时调用原始回调
- `task_timer_stop/destroy``while(running) usleep(1000)` 忙等 → `cond_wait` 零 CPU 阻塞
- 6 个 API 签名全部不变
**状态**:✅ 已完成
**涉及文件**`src/public/libtask/src/myTask.c`(仅此一个文件,`release/inc/myTask.h` 无改动)
**验证**`./release/build.sh` 编译通过,零错误零警告
---
## 2026-06-12
### #6 Tab 补全子命令前缀丢失
**问题**:输入 `datacenter ` + `p` + Tab 后,`datacenter ` 前缀被 `param` 覆盖,整行只剩 `param`
**根因**`linenoiseEdit` 中 Tab 键处理用 `strncpy(buf, completions[0])` 整行覆盖。
**修复**:改为找到最后一个空格,只替换空格之后的当前词,保留前缀。
---
### #5 app_cmd 交互卡顿与 Tab 补全失效
**问题**:上一轮将 `cmd_recv` 改为 select 非阻塞 + EV_TIMER2(100ms) 后:
1. 回车后 `cmd>` 回显不及时,有明显卡顿感
2. 输入 `d` 后按 Tab不会弹出命令补全
**根因**`select()` 方案与 `linenoise()` 交互式行编辑器根本性不兼容——select 时终端处于规范模式行缓冲Tab 不是行终止符不会触发 select100ms 定时器引入最多 100ms 延迟。
**修复**:将 `app_cmd` 线程从定时器事件驱动改为简单阻塞循环 `while(1) { cmd_recv(); }`。`linenoise()` 自管理终端模式切换和 Tab 补全,线程阻塞在 stdin 上零延迟响应。
**验证**:编译通过。
---
### #4 app_cmd 线程启用后 CPU 飙升至 108%
**问题**`app_cmd` 线程中 `cmd_recv()` 启用后CPU 占用 108%,导致该入口被暂时屏蔽([app_cmd.cpp:117](src/system/RTU/src/app_cmd.cpp#L117) 被注释)。
**根因**
1. **死循环层面**`linenoiseEdit()`[my_cmd.cpp:107](src/public/libcmd/src/my_cmd.cpp#L107))中 `read()` 只检查了返回 `-1`,未处理返回 `0`EOF。当进程无真正控制终端时RTU 嵌入式环境常见),`read()` 立即返回 0 形成死循环。
2. **架构层面**`linenoise()` 是同步阻塞调用,却被放在 10ms 定时器 EVI_TIMER1 中执行,与其他定时器事件循环模型不兼容。
**修复内容**
1. `my_cmd.cpp``read()` 返回值判断改为 `<= 0`(含 EOF 处理);`enableRawMode()` 返回值在 `linenoise()` 中检查,失败直接返回 NULL
2. `app_cmd.cpp`:重构 `cmd_recv``cmd_recv_nonblock()`,用 `select()` 实现非阻塞 stdin 检查 + `isatty()` 过滤非终端环境,移至 EV_TIMER2100ms调用
3. `CLAUDE.md`:全文翻译为中文
**验证**:编译通过(零错误零警告),`./test/RTU < /dev/null` 运行 3 秒 CPU 占用 0.0%。
---
## 2026-06-10
### #1 libmms_m RCB 订阅编号硬编码
**问题**`mms_m_icd_report_init()` 中硬编码 `if(0 == rpt_no.compare("01"))`,只订阅编号为 `"01"` 的 RCB其他编号被丢弃无法按需灵活订阅多个 RCB 实例。
**需求**
1. 灵活可配置订阅的控制块编号
2. libmms_m 提供接口,由 libiec61850m 传入
3. 可传入一个或多个编号,不传默认 `"01"`
4. 无效数据打印错误并返回失败
**处理计划**[RCB订阅编号可配置化](./mid/RCB订阅编号可配置化.md)
**状态**:✅ 已完成
**涉及文件**`myMms_m.h`, `mms_m.h`, `mms_m.cpp`, `iec61850m.cpp`
---
### #2 libweb_server 模块缺陷修复
**问题**WebSocket 服务端模块存在 10 个缺陷含严重级别单客户端、多线程竞态、悬空指针、non-null-terminated UB和高危/中等级别(调试 printf、SBO 阻塞、LOG 格式化缺失等)。
**需求**
1. 兼容多个 WebSocket 客户端同时连接
2. 完善断连处理
3. 多线程安全保护
4. 修复 UB 和格式化问题
5. 增量推送优化(仅变化时发送)
**处理计划**[libweb_server模块分析](../工程/libweb_server模块分析.md)
**状态**:✅ 已完成
**涉及文件**`web_server.cpp`, `ws_method.cpp`
---
### #3 libweb_server 多连接资源共享冲突
**问题**:支持多客户端后,所有连接共享同一套全局信号资源(`g_ws_out_signals` 等),一个客户端的 add/del 操作会影响其他客户端的数据推送。
**需求**
1. 每个连接独立的信号资源per-connection session
2. 连接建立时自动开辟资源,断开时自动释放
3. `ws_task()` 按 session 独立构建 JSON 推送到对应连接
**处理计划**[libweb_server模块分析](../工程/libweb_server模块分析.md)
**状态**:✅ 已完成
**涉及文件**`ws_method.h`, `ws_method.cpp`, `web_server.cpp`
---
---
## 2026-06-16: Self_PTL 缺陷优化
**来源**[libself_ptl模块分析](./工程/libself_ptl模块分析.md)
### 修复项
1. **D4 测试命令分离**`self_ptl_method_task()` 增加 `g_self_ptl_test_active` 守卫,仅当 CLI 触发测试命令(`cmd_self_ptl` 设置 flag时才进入遍历TIMER2 先检查 active 标志再调用。
2. **D2 环形缓冲区加锁**`stru_self_rx` 增加 `pthread_mutex_t mutex` 字段,`put_rx_data`/`get_rx_data` 操作加锁保护。
3. **D3 溢出日志启**`put_rx_data` 溢出处理中原被注释的 `LOG_E` 已启。
4. **D8 SBO 超时**`stru_signal_ctrl` 增加 `select_time_ms` + `sbo_timeout_ms` 字段。`dc_check_ctrl_valid` 在 SELECT 时记录时间戳。新增 `dc_signal_check_sbo_timeout()` 函数。`self_ptl_task()` 每定时器 tick 检查 SBO 状态,超时(默认 30s自动回退到 READY。
5. **D6 硬编码清理**`2048``SELF_PTL_BUF_SIZE``256` → `SELF_PTL_TEMP_SIZE`IEC-104 APCI 魔数 → `IEC_APCI_MIN_LEN`/`IEC_FRAME_OVERHEAD`。
### 未处理
- **D7 双缓冲优化** — 改动大、风险高,暂缓。
- **D5 IEC-104 帧校验** — 已完整,无需处理。
- **D9 滑窗丢弃** — 逻辑正确,无需处理。
**状态**:✅ 已完成
**涉及文件**`self_ptl.cpp`, `method.cpp`, `myDatacenter.h`, `dc_signal.cpp`