27 KiB
问题处理文档
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:
-
核心 Bug:
combTerm逻辑错误 —combTerm要求门的全部下游连接都在cGates内部。但中间门 AND#1 的下游同时包含 OR#1(门)和输出信号 1366,导致downAllChain = false,所有中间门被排除,combTerm为空数组。 -
combXml递归缺少<Comb>包装 — 递归处理上游门时直接输出裸内容,未包裹在<Comb>标签中,导致生成的 XML 结构与解析器期望不匹配。 -
链级
<Outputs>包含所有输出 — 旧代码直接输出cOuts(全部输出节点),但 Comb 内的输出信号(1363-1367)已在combXml中处理,链级只应包含 chainGate 的输出(1368)。 -
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递归调用处包裹<Comb>标签 - Bug #3:链级
<Outputs>仅输出连接到 chainGate 的信号 - Bug #4:
delayMs提取改为n.id >> 8
验证
- 编译通过,浏览器测试确认生成的 XML 与原始配置完全一致
- 查看器可正确解析生成的配置(23节点 20连线)
- PLC 模块正常运行,所有输出信号处理正确
2026-07-01: com_channel_recv_cb 三级路由重构 (转发→bind→IEC)
需求背景
RTU 有 3 种数据通道,各自职责不同:
- uart0 ↔ STM32: self_ptl ICP67 规约通讯
- tcp_s0 → uart0: 维护软件透明隧道,仅转发 ICP67 + DEV_MAINTENANCE_ADDR 报文
- 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(配置更新)
配置示例
<Channel mode="tcp_server" saddr="tcp_s0" desc="维护软件TCP转发隧道" bind=""/>
<Channel mode="tcp_server" saddr="tcp_s1" desc="IEC 101/104 TCP服务器" bind=""/>
<Channel mode="uart" saddr="uart0" desc="连接STM32的串口" bind="self_ptl"/>
<Forward from_saddr="tcp_s0" to_saddr="uart0" app="forward"/>
验证
RK 运行: self_ptl_init: uart uart0 bind ok / tcp tcp_s0 via forward,无 p_event NULL 刷屏,无 unknown protocol 错误
2026-07-01: self_ptl 通道绑定改为 bind 字段 + 数据路由防御性修复
需求背景
- self_ptl 通过 Forward 规则的
app="self_ptl"来查找通道,不配转发则找不到合理通道 com_recv_data中硬编码"uart0"做通道区分,不应在数据路由层关心具体通道- 不配转发时
com_scan_send_data_to_app的p_event为 NULL 导致异常 - 串口噪声数据经
com_recv_data因缺少 rx_len 校验导致协议误判
修复内容
1. 通道 bind 字段 — 显式绑定私有规约
mySystem.h:stru_ch_cfg新增bind_app字段 + 声明channel_cfg_find_by_bind()channel_cfg.cpp: 解析 XMLbind属性 + 实现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_eventNULL →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
需求背景
- channel_config.xml(x86)和 channel_config_rk.xml(RK3568)两套配置文件,程序通过
#ifdef RK356x宏区分 - 转发通道只单向匹配
from_saddr,双向转发需配两条规则 - 串口 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 库文件):
- 创建项目自有兼容头文件
release/inc/cbb_compat.h,定义 CBB 库所需的类型别名:
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
- 在
release/src/protocol/lib60870/makefile中通过 gcc-include注入该头文件:
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 时端口无监听。
修复:
- 修改
deploy-to-rk任务:scp 传输后自动killall gdbserver+ 启动新 gdbserver - 修改
start-gdbserver任务:统一用>/tmp/rtu_output.log 2>&1重定向 gdbserver+RTU 输出 - 新增
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 异常、卡住)。
最终修复:
- 删除所有复合任务 (
deploy-with-tail,start-gdbserver-with-tail) deploy-to-rk= scp + gdbserver(同步,preLaunchTask 可靠)start-gdbserver= gdbserver(同步,preLaunchTask 可靠)tail-rtu-output= 手动tail -f(独立任务,不参与 preLaunchTask)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(大写,枚举后缀)分离:
// 修改前: 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模块分析
修复项
-
全局回调表单例化 — 将
g_genneral_method从 static 全局变量移入stru_icp67.method。26 个 setter 改为实例化接口(stru_icp67 *第一参数)。删除general_method.h、icp67_get_genneral_method()。所有 decode 函数改为p_icp67->method.xxx_cb回调。 -
重传竞态修复 —
icp67_timer_handler中resend_cnt++/tm_cnt=0/tm_cnt++移入rtx_sem保护区。 -
发送覆盖防御 —
icp67_rtx_flag_set增加 TX_FLAG 重复设置 LOG_E 警告。 -
空桩填充 —
icp67_decode_ti_4、icp67_decode_ti_203改为 LOG_E + ICP67_RX_FLAG 设置。 -
硬编码清理 — 所有魔数替换为命名宏(
ICP67_TX_BUF_SIZE、ICP67_MD5_LEN、ICP67_TM_TICK_MS/ICP67_TM_OUT_MS、ICP67_HEAD_OVERHEAD/ICP67_FRAME_OVERHEAD等)。 -
配置参数宏化 — 超时/重试/缓冲/定时器 tick 统一在
myIcp67.h中用#define管理。 -
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 线程为空壳
修复:
- 合并为
libcom_decode模块,按功能拆分 4 个文件 - 整合为 1 个 app 线程(删除空壳线程)
- 枚举值合并(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 + 调用者缺陷修复
问题:
- UDP
recvfrom永久阻塞,无超时无退出机制 - UDP
close后state_cb(id, -1, disconnected),回调拿到的是已关闭的 -1 com_scan/self_ptl/iec三处 send+event 回滚竞态:msg_queue_send成功 →event_send失败 →try_recv可能取出旧消息com_channel_recv_cb中comm_send到已断开的 TCP_C_0 不检查 fd
修复:
udp_run中recvfrom前加select1s 超时udp_close先保存 fd 再 close,回调传入正确值- 三处改为先
event_send再msg_queue_send,消除回滚竞态 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 模块修补
问题:
- UART send 不完整:裸 write 无流量控制,缺少原始模式终端设置(~ICANON/~ECHO/~OPOST)
- TCP client select 100ms 空轮询,空闲时 CPU 空转
- 无
comm_destroy接口,内存泄漏 stru_comm结构体中 6 个函数指针字段从未使用,冗余
修复:
- 重写
comm_uart.cpp:恢复原始模式 + VTIME=1 读超时 + tcflush/tcdrain 完整发送 - TCP client select 去掉超时参数,永久阻塞
- 新增
comm_destroy(id)API - 删除
stru_comm中 6 个未使用字段
状态:✅ 已完成
涉及文件:comm_uart.cpp, comm_tcp.cpp, comm.cpp, comm.h, myComm.h
验证:./release/build.sh 编译通过
#8 libtask 事件/消息队列缺陷修复
问题:
task_event_destroy和task_msg_queue_destroy在free(p)后仍访问p->name(use-after-free)task_event_recv中 AND 检查用opt == TASK_EVENT_FLAG_AND而非位测试,AND | CLEAR组合被误识别为 ORtask_event_send用pthread_cond_signal而非cond_broadcast,多等待者场景可能唤醒不足
修复:
free(p)移到LOG_I之后,先打日志再释放opt ==改为opt &位测试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 设一个事件位(几微秒),线程创建开销远大于实际工作。
需求:
- 消除每次定时器超时创建线程的开销
- API 签名全部不变,调用方零修改
- 仅 Linux 平台
修复方案:用 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) 后:
- 回车后
cmd>回显不及时,有明显卡顿感 - 输入
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 被注释)。
根因:
- 死循环层面:
linenoiseEdit()(my_cmd.cpp:107)中read()只检查了返回-1,未处理返回0(EOF)。当进程无真正控制终端时(RTU 嵌入式环境常见),read()立即返回 0 形成死循环。 - 架构层面:
linenoise()是同步阻塞调用,却被放在 10ms 定时器 EVI_TIMER1 中执行,与其他定时器事件循环模型不兼容。
修复内容:
my_cmd.cpp:read()返回值判断改为<= 0(含 EOF 处理);enableRawMode()返回值在linenoise()中检查,失败直接返回 NULLapp_cmd.cpp:重构cmd_recv为cmd_recv_nonblock(),用select()实现非阻塞 stdin 检查 +isatty()过滤非终端环境,移至 EV_TIMER2(100ms)调用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
问题:系统模块间存在两种硬编码耦合:
decode_channel_mgr直接调用self_ptl_set_interface()传递 TCP 接口 → 强依赖 self_ptl 头文件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 实例。
需求:
- 灵活可配置订阅的控制块编号
- libmms_m 提供接口,由 libiec61850m 传入
- 可传入一个或多个编号,不传默认
"01" - 无效数据打印错误并返回失败
处理计划:RCB订阅编号可配置化
状态:✅ 已完成
涉及文件:myMms_m.h, mms_m.h, mms_m.cpp, iec61850m.cpp
#2 libweb_server 模块缺陷修复
问题:WebSocket 服务端模块存在 10 个缺陷,含严重级别(单客户端、多线程竞态、悬空指针、non-null-terminated UB)和高危/中等级别(调试 printf、SBO 阻塞、LOG 格式化缺失等)。
需求:
- 兼容多个 WebSocket 客户端同时连接
- 完善断连处理
- 多线程安全保护
- 修复 UB 和格式化问题
- 增量推送优化(仅变化时发送)
处理计划:libweb_server模块分析
状态:✅ 已完成
涉及文件:web_server.cpp, ws_method.cpp
#3 libweb_server 多连接资源共享冲突
问题:支持多客户端后,所有连接共享同一套全局信号资源(g_ws_out_signals 等),一个客户端的 add/del 操作会影响其他客户端的数据推送。
需求:
- 每个连接独立的信号资源(per-connection session)
- 连接建立时自动开辟资源,断开时自动释放
ws_task()按 session 独立构建 JSON 推送到对应连接
处理计划:libweb_server模块分析
状态:✅ 已完成
涉及文件:ws_method.h, ws_method.cpp, web_server.cpp
2026-06-16: Self_PTL 缺陷优化
修复项
-
D4 测试命令分离 —
self_ptl_method_task()增加g_self_ptl_test_active守卫,仅当 CLI 触发测试命令(cmd_self_ptl设置 flag)时才进入遍历;TIMER2 先检查 active 标志再调用。 -
D2 环形缓冲区加锁 —
stru_self_rx增加pthread_mutex_t mutex字段,put_rx_data/get_rx_data操作加锁保护。 -
D3 溢出日志启 —
put_rx_data溢出处理中原被注释的LOG_E已启。 -
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。 -
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 缺陷修复
修复项
-
缺陷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 风险。 -
缺陷3: 回调向量无锁写入 —
out_change_cb_list/change_cb_list的push_back加对应的stru_signal_map.mtx锁保护。 -
缺陷4: YK 跳过值校验 —
dc_signal_yk_set_status的 DIRECT 步骤增加dc_check_val_valid调用,与 AO/Param 行为一致。 -
缺陷5: strncpy 安全 — IP/MAC/C 类型
dc_set_signal_val中的strncpy改为memcpy,避免源串等于缓冲区长时无\0终止。
状态:✅ 已完成
涉及文件:src/system/libdatacenter/src/dc_signal.cpp
2026-06-16: 参数缺省值重构
来源:设计讨论——缺省值应只存在于配置文件,不应在运行时存储。
完成项
- 移除
stru_signal.vec_p_default_data— 默认值不再存储于运行时结构中 - 移除 API 默认值参数 —
dc_signal_ao()/dc_signal_param()不再接收p_default_data;dc_get_ao_signal_info()/dc_get_param_signal_info()不再输出默认值 - 删除
dc_signal_{ao,param}_add_check()— 不再需要比较默认值变化 - dc_param.cpp 不再创建默认值 — 初始化时只从
param.xml/self_param.xml读值并注册 - self_ptl 不再创建/传默认值 — 删除
vec_p_default_data创建和传参 - ws_method 去掉默认值 — 删除读取/展示/序列化
- iec61850m 不再创建默认值 — 删除
p_default[]创建 - 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