# 问题处理文档 --- ## 2026-07-07: PLC 逻辑图编辑器"生成配置"输出错误的 XML 配置 ### 问题现象 在 PLC 逻辑配置页面(`#plc_config`)中,逻辑图可视化**查看器能正确解析配置文件**并展示逻辑图;但编辑器中点击**"生成配置"**后生成的 XML 配置文件不正确——多门组合链(Chain 3)中 AND#0、AND#1、OR#1 三个中间门及其输出信号全部丢失,只保留了链级门 NOT#1。 ### 原因分析 `plc_config.js` 中 `generateXmlConfig()` 函数存在 4 个 Bug: 1. **核心 Bug:`combTerm` 逻辑错误** — `combTerm` 要求门的**全部**下游连接都在 `cGates` 内部。但中间门 AND#1 的下游同时包含 OR#1(门)和输出信号 1366,导致 `downAllChain = false`,所有中间门被排除,`combTerm` 为空数组。 2. **`combXml` 递归缺少 `` 包装** — 递归处理上游门时直接输出裸内容,未包裹在 `` 标签中,导致生成的 XML 结构与解析器期望不匹配。 3. **链级 `` 包含所有输出** — 旧代码直接输出 `cOuts`(全部输出节点),但 Comb 内的输出信号(1363-1367)已在 `combXml` 中处理,链级只应包含 chainGate 的输出(1368)。 4. **`delayMs` 提取截断** — `(n.id >> 8) & 0xFF` 只取低 8 位,延迟 > 255ms 时应为 `n.id >> 8`。 ### 解决方案 修改 `test/web_root/js/plc_config.js` 中 `generateXmlConfig()` 函数: - **Bug #1**:将 `combTerm` 替换为 `combRoots`,基于 chainGate 的上游门计算 Comb 根节点(`combXml` 递归处理嵌套) - **Bug #2**:在 `combXml` 递归调用处包裹 `` 标签 - **Bug #3**:链级 `` 仅输出连接到 chainGate 的信号 - **Bug #4**:`delayMs` 提取改为 `n.id >> 8` ### 验证 - 编译通过,浏览器测试确认生成的 XML 与原始配置完全一致 - 查看器可正确解析生成的配置(23节点 20连线) - PLC 模块正常运行,所有输出信号处理正确 --- ## 2026-07-01: com_channel_recv_cb 三级路由重构 (转发→bind→IEC) ### 需求背景 RTU 有 3 种数据通道,各自职责不同: 1. **uart0 ↔ STM32**: self_ptl ICP67 规约通讯 2. **tcp_s0 → uart0**: 维护软件透明隧道,仅转发 ICP67 + DEV_MAINTENANCE_ADDR 报文 3. **tcp_s1**: IEC 101/104 协议,RTU 自行解析 原有问题: - `com_recv_data` 做 ICP67/IEC 协议头分发,但 uart 数据是碎片帧,不应在此层区分 - 转发隧道双向化导致 uart→tcp 回环,且不校验维护软件报文有效性 - bind_app 的数据与协议头分发混在一起,职责不清 ### 三级路由设计 ``` com_channel_recv_cb(): 1. 转发隧道 (from_saddr→to_saddr, 单向) └→ 维护软件隧道: 校验 ICP67 + DEV_MAINTENANCE_ADDR, 通过则 comm_send 2. bind_app 直接路由 └→ uart0 bind="self_ptl" → MQ_COM_TO_SELF_PTL (不解析,不区分协议) 3. 兜底 IEC 101/104 协议检测 └→ tcp_s1 → com_recv_data → IEC 头检测 ``` - **出境方向**: self_ptl 从 STM32 收到维护软件应答(dev_addr==DEV_MAINTENANCE_ADDR) → 通过 `g_p_tcp_s0_if` 转发给 tcp_s0;正常 RTU 应答留在 self_ptl 处理 - **init1 阶段兼容**: p_event NULL 时数据只入队不发事件,self_ptl 线程启动时排空队列 ### 涉及文件 `decode_data_router.cpp`(三级路由+队列兼容), `decode_channel_mgr.cpp`(单向转发), `self_ptl.cpp`(TCP 反查+队列排空), `channel_config.xml`(配置更新) ### 配置示例 ```xml ``` ### 验证 RK 运行: `self_ptl_init: uart uart0 bind ok` / `tcp tcp_s0 via forward`,无 p_event NULL 刷屏,无 unknown protocol 错误 --- ## 2026-07-01: self_ptl 通道绑定改为 bind 字段 + 数据路由防御性修复 ### 需求背景 1. self_ptl 通过 Forward 规则的 `app="self_ptl"` 来查找通道,不配转发则找不到合理通道 2. `com_recv_data` 中硬编码 `"uart0"` 做通道区分,不应在数据路由层关心具体通道 3. 不配转发时 `com_scan_send_data_to_app` 的 `p_event` 为 NULL 导致异常 4. 串口噪声数据经 `com_recv_data` 因缺少 rx_len 校验导致协议误判 ### 修复内容 #### 1. 通道 `bind` 字段 — 显式绑定私有规约 - **`mySystem.h`**: `stru_ch_cfg` 新增 `bind_app` 字段 + 声明 `channel_cfg_find_by_bind()` - **`channel_cfg.cpp`**: 解析 XML `bind` 属性 + 实现 `channel_cfg_find_by_bind(bind_app, type)` - **`self_ptl.cpp`**: `self_ptl_init()` 改为 `channel_cfg_find_by_bind("self_ptl", type)` 直接查找 - **`channel_config.xml`**: 通道增加 `bind="self_ptl"` 属性 - **效果**: 不再依赖 Forward 规则或自动检测,显式配置`bind="自协议名"`绑定通道 #### 2. com_recv_data 防御性修复 - **硬编码去除**: 删除 `channel_cfg_find_by_saddr("uart0")` 通道判断,统一走协议头分发 - **rx_len 校验**: 增加 `rx_len >= sizeof(stru_icp67_head)` 防不完整帧误判 0x67 头 #### 3. com_scan_send_data_to_app 空指针保护 - 拆分为三步逐项检查: `app_get_ptr` → `p_event` NULL → `task_event_send`,每步失败都有明确 LOG ### 涉及文件 `mySystem.h`, `channel_cfg.cpp`, `self_ptl.cpp`, `decode_data_router.cpp`, `channel_config.xml` ### 验证 x86 + ARM 编译通过,RK 上运行日志显示 `self_ptl_init: uart uart0 bind ok` / `tcp tcp_s0 bind ok` --- ## 2026-07-01: channel_config 三合一优化:合并文件 + 双向转发 + 去除66转67 ### 需求背景 1. channel_config.xml(x86)和 channel_config_rk.xml(RK3568)两套配置文件,程序通过 `#ifdef RK356x` 宏区分 2. 转发通道只单向匹配 `from_saddr`,双向转发需配两条规则 3. 串口 66→67 帧转换逻辑在程序中实现,现由外部完成 ### 修改内容 #### 1. 合并 channel_config 配置文件 - **文件**: `src/system/RTU/src/channel_cfg.cpp` - **修改**: 删除 `#ifdef RK356x` 条件编译,统一加载 `channel_config.xml` - **效果**: 只有一套配置文件,由使用者在 x86/RK 间自行调整 IP、端口、串口设备 #### 2. 转发通道双向互通 - **文件**: `src/system/libcom_decode/src/decode_channel_mgr.cpp` - **修改**: `com_channel_get_send_info()` 中同时匹配 `from_saddr` 和 `to_saddr`,配一条 `tcp_s0→uart0` 自动实现双向转发 - **效果**: 配置文件只需写一条 Forward 即可实现两通道互通 #### 3. 去除 66→67 转换逻辑 - **文件**: - `src/system/libcom_decode/src/decode_channel_mgr.cpp` — 删除 UART 返回 0 的 66→67 分支 - `src/system/libcom_decode/src/decode_data_router.cpp` — 删除 `else if(0 == result)` 分支,串口数据直接走 `com_recv_data` 协议解析 - `src/system/libcom_decode/src/decode_frame_codec.cpp` — 清空所有 66→67 帧转换函数(`icp66_frame_decode`、`icp66_search_frame`、`icp66_to_icp67`、`icp66_search_first_head`、`icp67_show_frame`、环形缓冲区等),保留文件占位 - `src/system/libcom_decode/inc/com_decode.h` — 删除 `icp66_frame_decode` 声明 ### 涉及文件 `channel_cfg.cpp`, `decode_channel_mgr.cpp`, `decode_data_router.cpp`, `decode_frame_codec.cpp`, `com_decode.h` ### 验证 `./release/build.sh` x86 编译通过,零错误零警告 --- ## 2026-06-30: lib60870 替换为 CBB 库后编译失败 — 类型未定义 **问题**:`src/protocol/lib60870/` 替换为科大智能 CBB 库后,执行 `./release/build.sh` 编译失败,报大量 `unknown type name 'u8'/'u16'/'u32'/'BOOL'/'s16'/'s32'/'f32'` 等错误。 **根因**:CBB 库的源文件(如 `gb103.c`、`gb103.h`)大量使用了 `u8`、`u16`、`u32`、`s16`、`s32`、`f32`、`BOOL`、`TRUE`、`FALSE` 等自定义类型/宏,但这些类型别名在原项目的 `lib60870_inc.h` 中未定义。CBB 库来自不同项目,依赖项目特定的 `ostype.h` 等公共头文件提供这些类型。 **解决方案**(不修改 CBB 库文件): 1. 创建项目自有兼容头文件 `release/inc/cbb_compat.h`,定义 CBB 库所需的类型别名: ```c typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef int16_t s16; typedef int32_t s32; typedef float f32; typedef bool BOOL; #define TRUE true #define FALSE false ``` 2. 在 `release/src/protocol/lib60870/makefile` 中通过 gcc `-include` 注入该头文件: ```makefile APP_C_FLAGS += -include cbb_compat.h ``` 此方案**零修改 CBB 库源文件**,类型定义通过编译器注入,不影响库的独立性。 **调用方影响**:`src/system/libiec/` 是 lib60870 的主要调用方,使用 `CS10x` 类型。CBB 库中 `CS10x` 通过 `typedef struct CS10x_t { ... } CS10x;` 正确定义,调用方代码兼容,无需修改。 --- ## 2026-06-26: 远程调试 gdbserver 未自动启动 & RTU 打印信息查看问题 **问题**:F5 选择 "ARM 编译+部署+调试 (RK3568)" 后无法断点调试。 **根因**:`deploy-to-rk` 任务只做了编译+scp 传输,没有自动启动 gdbserver。VS Code GDB 尝试连接 `198.120.0.100:2345` 时端口无监听。 **修复**: 1. 修改 `deploy-to-rk` 任务:scp 传输后自动 `killall gdbserver` + 启动新 gdbserver 2. 修改 `start-gdbserver` 任务:统一用 `>/tmp/rtu_output.log 2>&1` 重定向 gdbserver+RTU 输出 3. 新增 `tail-rtu-output` 任务:一键 `tail -f /tmp/rtu_output.log` 实时查看 RTU printf 输出 **原理**:RTU 作为 gdbserver 的子进程,继承 gdbserver 重定向后的 fd 1/2,所以 printf/LOG_I/LOG_E 全部进入 `/tmp/rtu_output.log`。 **涉及文件**:`.vscode/tasks.json` **注意事项**:RTU 的 stdin 被 gdbserver 接管,无法在 tail 窗口输入 CLI 命令。交互式命令测试需单独 `ssh root@198.120.0.100 /mnt/RTU/RTU`。 **验证**:GDB 连接→continue→tail 文件成功看到所有 LOG_I/LOG_E 输出。 ### 补充修复 (2026-06-26): "ARM 调试 (不编译)" preLaunchTask 卡住 **问题**:F5 选 "ARM 调试 (不编译, RK3568)" 不工作,且后续引入复合任务后 "ARM 编译+部署+调试" 也失效。 **根因**:尝试用 `dependsOrder: "sequence"/"parallel"` + `isBackground: true` 复合任务作为 preLaunchTask。VS Code preLaunchTask 需要子任务明确完成信号,复合任务+后台子任务组合行为不可靠(exit code 异常、卡住)。 **最终修复**: 1. 删除所有复合任务 (`deploy-with-tail`, `start-gdbserver-with-tail`) 2. `deploy-to-rk` = scp + gdbserver(同步,preLaunchTask 可靠) 3. `start-gdbserver` = gdbserver(同步,preLaunchTask 可靠) 4. `tail-rtu-output` = 手动 `tail -f`(独立任务,不参与 preLaunchTask) 5. `stopAtEntry` 改为 `false`,RTU 直接运行不需手动按继续 **涉及文件**:`.vscode/tasks.json`、`.vscode/launch.json` **tail 使用方式**:F5 启动调试后,`Ctrl+Shift+P` → Run Task → `tail-rtu-output` 查看实时打印。 --- ## 2026-06-18: X-Macro 应用模块注册表 — 名称大小写匹配修复 **问题**:X-Macro 生成 g_vec_app 名称时使用 `"app_" #name`(如 `app_SYS`),但配置文件 `app_config.json` 使用小写 `app_sys`,导致名称不匹配,所有线程被 "skipped (disabled by config)"。 **根因**:C 预处理器 `#name` 运算符产生宏参数的原始文本,`APP_MODULE(SYS, ...)` → `"app_SYS"`,无法自动转小写。 **修复**:增加第二个参数 `cfg_name`(小写),与 `name`(大写,枚举后缀)分离: ```c // 修改前: APP_MODULE(SYS, init1, init2, func) // 修改后: APP_MODULE(SYS, sys, init1, init2, func) ``` - `#cfg_name` → `"app_sys"`(匹配配置文件) **涉及文件**:`app_modules.h`、`mySystem.h`(两处)、`app_sys.cpp`(一处) **注意事项**:旧进程残留占端口会导致 `mms_s_init` 绑定失败。app_modules.h 禁止加 `#pragma once`。 **验证**:编译通过,8 个线程全部 `pthread_create success`。 --- ## 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` 等)保持 static(read-only,多实例安全) ### #11 libcom_channel + libcom_scan 合并为 libcom_decode **问题**:两个模块紧耦合(com_channel 调 com_recv_data,com_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 不是行终止符不会触发 select;100ms 定时器引入最多 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_TIMER2(100ms)调用 3. `CLAUDE.md`:全文翻译为中文 **验证**:编译通过(零错误零警告),`./test/RTU < /dev/null` 运行 3 秒 CPU 占用 0.0%。 --- ## 2026-06-18: 跨模块耦合解耦 — self_ptl_set_interface → dc_signal + app_get_ptr → app_get_mq_target **问题**:系统模块间存在两种硬编码耦合: 1. `decode_channel_mgr` 直接调用 `self_ptl_set_interface()` 传递 TCP 接口 → 强依赖 self_ptl 头文件 2. `iec.cpp`/`self_ptl.cpp`/`decode_data_router.cpp` 中通过 `app_get_ptr(ENUM_APP_XXX)` + 硬编码事件号发送跨模块通知 → 模块间直接知晓对方身份 **根因**:早期快速迭代中,模块间交互未经过中间层抽象。协议处理模块(iec/self_ptl/comm)互相知道对方的应用 ID、事件号、接口函数。 **修复**: ### 问题一:self_ptl_set_interface 解耦 - `decode_channel_mgr.cpp`:移除 `#include "self_ptl.h"` 和 `self_ptl_set_interface()` 调用,改为 `dc_signal_out("sys.tcp_c0_interface", ...)` 通过 datacenter 发布接口信息 - `self_ptl.cpp`:移除 `self_ptl_set_interface()` 函数,`self_ptl_init()` 改为通过 `dc_get_out_signal_info("sys.tcp_c0_interface")` 从 datacenter 读取接口 ### 问题二:app_get_ptr + 硬编码事件号解耦 - `mySystem.h`:新增 `stru_mq_target {app_id, event}` 结构和 `app_get_mq_target(enum_mq_id)` 声明 - `app_sys.cpp`:新增 `g_mq_target_map[ENUM_MQ_MAX]` 全局映射表和 `app_get_mq_target()` 实现 - `iec.cpp`:`iec_data_tx()` 中 `app_get_ptr(ENUM_APP_COM_DECODE) + EV_COM_RX_IEC` → `app_get_mq_target(ENUM_MQ_IEC_TO_COM)` - `self_ptl.cpp`:`self_ptl_data_tx()` 中硬编码事件号 → `app_get_mq_target(ENUM_MQ_SELF_PTL_TO_COM)` - `decode_data_router.cpp`:`com_scan_send_data_to_app()` 中 if/else 判断目标 MQ → 通过 `app_get_mq_target(mq_id)` 统一处理 **状态**:✅ 已完成 **涉及文件**:`decode_channel_mgr.cpp`, `self_ptl.cpp`, `iec.cpp`, `decode_data_router.cpp`, `mySystem.h`, `app_sys.cpp` **验证**:`./release/build.sh` 编译通过,运行日志显示 `self_ptl_init got tcp_c0_if from dc: 20006` 确认 datacenter 信号通信正常。 --- ## 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` --- ## 2026-06-16: libdatacenter 缺陷修复 **来源**:[libdatacenter模块分析](./工程/libdatacenter模块分析.md) ### 修复项 1. **缺陷1&2: AO/Param 重注册持锁外 delete** — `dc_signal_ao` 和 `dc_signal_param` 的 re-registration 路径中 `dc_delete_signal_data` 改为在 `signal_ao.mtx`/`signal_param.mtx` 锁内执行,消除 use-after-free 风险。 2. **缺陷3: 回调向量无锁写入** — `out_change_cb_list`/`change_cb_list` 的 `push_back` 加对应的 `stru_signal_map.mtx` 锁保护。 3. **缺陷4: YK 跳过值校验** — `dc_signal_yk_set_status` 的 DIRECT 步骤增加 `dc_check_val_valid` 调用,与 AO/Param 行为一致。 4. **缺陷5: strncpy 安全** — IP/MAC/C 类型 `dc_set_signal_val` 中的 `strncpy` 改为 `memcpy`,避免源串等于缓冲区长时无 `\0` 终止。 **状态**:✅ 已完成 **涉及文件**:`src/system/libdatacenter/src/dc_signal.cpp` --- ## 2026-06-16: 参数缺省值重构 **来源**:设计讨论——缺省值应只存在于配置文件,不应在运行时存储。 ### 完成项 1. **移除 `stru_signal.vec_p_default_data`** — 默认值不再存储于运行时结构中 2. **移除 API 默认值参数** — `dc_signal_ao()` / `dc_signal_param()` 不再接收 `p_default_data`;`dc_get_ao_signal_info()` / `dc_get_param_signal_info()` 不再输出默认值 3. **删除 `dc_signal_{ao,param}_add_check()`** — 不再需要比较默认值变化 4. **dc_param.cpp 不再创建默认值** — 初始化时只从 `param.xml`/`self_param.xml` 读值并注册 5. **self_ptl 不再创建/传默认值** — 删除 `vec_p_default_data` 创建和传参 6. **ws_method 去掉默认值** — 删除读取/展示/序列化 7. **iec61850m 不再创建默认值** — 删除 `p_default[]` 创建 8. **self_param.xml 不写 default 字段** — `dc_param_cfg_check()` 只写 `value` **状态**:✅ 已完成 **涉及文件**:`dc_signal.h/cpp`, `myDatacenter.h`, `dc_param.cpp`, `self_ptl.h/cpp`, `method.cpp`, `ws_method.cpp`, `iec61850m.cpp`, `iec61850s.cpp`