返回主站|会员中心|保存桌面

软件定制开发公司    

软件定制开发公司,承接各类工控软件,上位机软件,plc程序开发,编程,调试,电气...

联系方式
  • 联系人:方经理
  • 电话:13073017555
  • 邮件:gzws26@126.com
新闻分类
  • 暂无分类
站内搜索
 
友情链接
首页 > 新闻中心 > 上位机软件 cc + 写程序的时候有哪些注意事项!
新闻中心
上位机软件 cc + 写程序的时候有哪些注意事项!
发布时间:2026-09-05        浏览次数:15        返回列表

上位机常见场景:串口、网口 (TCP/UDP)、PLC / 仪器通讯、数据采集、UI 界面、数据库存储、日志、多线程、与硬件交互,下面分工程架构、多线程、通讯、UI、内存资源、异常容错、性能、发布部署、安全、编码规范整理实际踩坑点。

常用框架:Qt (C++ 上位机最主流)、MFC、Win32;也有纯控制台后台服务型上位机。

一、架构与工程设计

  1. UI 与业务逻辑严格分离

    • UI 线程只负责界面刷新,通讯、解析、IO、数据库不要写在 UI 线程;Qt 禁止在子线程操作 UI 控件。

    • 建议分层:UI 层 → 业务逻辑层 → 通讯驱动层 → 硬件抽象层;硬件协议解析单独类,方便更换设备。

    • 不要把串口 / TCP 操作直接写按钮槽函数,界面卡死是上位机最常见问题。

  2. 硬件抽象,解耦协议

    • 串口、TCP、Modbus、自定义协议封装成统一接口,上层业务不用关心底层是串口还是网口。

    • 协议报文解析独立模块,不要散落在各处代码,后期改协议只改一处。

  3. 配置文件管理参数

    • 串口参数、IP 地址、端口、采集周期、阈值不要硬编码写死代码;使用 ini/json/xml 保存配置。

    • 程序启动加载配置,支持运行时修改配置,重启生效或即时生效。

二、多线程(上位机重灾区,80% BUG 来源)

  1. 绝对禁止子线程直接操作 UI 控件

    • Qt:子线程数据通过信号槽 (QueuedConnection) 抛给 UI 线程更新界面;不要子线程调用 ui->xxx->setText。

  2. 共享数据必须加锁

    • 采集缓存、接收缓冲区、全局状态变量,多线程读写,使用QMutex/std::mutex保护;

    • 注意死锁:锁的顺序固定,不要一个函数拿 A 锁再拿 B 锁,另一个函数拿 B 锁再拿 A 锁。

    • 尽量减少锁持有时间,不要锁里面做耗时操作。

  3. 线程安全退出,不要暴力 terminate

    • 不要使用terminate()强制杀死线程;设置标志位m_running,循环检测标志优雅退出。

    • 程序关闭时:先停止采集线程 → 关闭通讯端口 → 释放资源,再退出主程序,避免崩溃、内存泄漏、串口被占用。

  4. 线程数量控制

    • 不要为每一次接收创建新线程;接收用长驻工作线程,数据队列做缓冲。

    • 数据队列注意:生产者 (接收线程)、消费者 (解析线程),队列读写加锁。

三、通讯部分:串口 / TCP / UDP

串口

  1. 打开串口前先判断是否已经打开,避免重复打开;关闭之后再重新打开。

  2. 串口接收是字节流,没有报文边界!

    重点:串口是流式,一次 read 可能收到半帧、多帧拼接,必须做缓冲区 + 帧协议解析,不能读一次就当成一帧数据。

    • 维护接收环形缓冲区,不断追加字节,在缓冲区内查找帧头帧尾,截取完整报文,剩余字节留到下次接收。

  3. 异常处理:串口拔插、断开,捕获错误,自动重连机制;不要程序直接崩溃。

  4. 串口关闭:程序异常退出也要释放串口句柄,否则串口被锁死,必须拔插设备或者重启电脑才能恢复。

TCP

  1. TCP 同样是字节流,和串口一样无报文边界,必须自定义帧协议(长度域 / 帧头帧尾分包),不能按 recv 返回次数当作数据包。

  2. 区分连接断开:recv返回 0 代表对端关闭;返回负值代表错误;及时处理断开,触发重连逻辑。

  3. 发送不要无脑 send:发送缓冲区满会阻塞,非阻塞模式要处理发送未发完的剩余数据。

  4. 客户端:心跳包机制,检测设备离线;服务端注意多客户端连接管理。

  5. UDP:不可靠,会丢包、乱序;需要序号、重传、应答机制,如果用于重要采集业务,UDP 要自己做可靠性。

通用通讯坑

  • 超时!所有读写都要有超时时间,禁止无限阻塞等待 read,否则界面卡死。

  • 粘包、分包问题,是上位机高频问题,一定要做协议拆包。

  • 重连逻辑:断开之后自动重连,但要加间隔,不要无限死循环疯狂重连占用 CPU。

四、UI 编程(Qt 为主)

  1. 耗时操作一律放到子线程;sleep()不要在 UI 线程调用,直接界面冻结。

  2. 大量数据刷新界面:高频采集 (几十 ms 一次) 不要每收到一条就刷新表格,做合并刷新,比如 200ms 批量刷新一次,否则 CPU 占满界面卡顿。

  3. 表格控件大量数据:不要一次性加载上万行,可以分页;避免频繁 update。

  4. 窗口关闭事件重写closeEvent:在这里停止线程、关闭通讯设备,防止点叉直接销毁窗口,线程还在后台跑导致崩溃。

  5. 信号槽连接方式:跨线程务必使用Qt::QueuedConnection;跨线程不要用 DirectConnection。

五、内存、资源管理

  1. C++ 优先使用智能指针std::shared_ptr/std::unique_ptr,减少裸指针 new/delete;Qt 对象树机制理解清楚,避免双重释放。

  2. 各类句柄:串口句柄、socket、文件句柄、数据库连接,打开之后一定要关闭;异常分支也要释放,防止句柄泄漏。

  3. 接收缓冲区、环形缓冲区设置上限,防止设备疯狂发数据,内存持续暴涨(内存暴涨 OOM)。

  4. 不要内存无限增长:采集历史数据、缓存队列设置最大长度,超出自动丢弃旧数据。

六、数据解析、数值处理

  1. 大小端转换!硬件设备很多大端,PC 是小端;报文解析务必做字节序转换,htons htonl ntohs ntohl

  2. 严格校验报文:帧头、帧尾、长度、校验和 (CRC、校验码),非法报文直接丢弃,不要直接解析,防止越界访问内存崩溃。

    外部设备发来的数据是不可信的,所有字段都要校验长度范围,这是稳定性关键。

  3. 数值溢出:short、int 转换,注意硬件返回数据范围,做好边界判断。

七、日志系统

  1. 上位机必须完整日志:发送报文、接收报文、错误信息、状态变更。便于现场定位问题。

  2. 日志分级:DEBUG INFO WARN ERROR;发布版本可以关闭调试日志。

  3. 日志文件滚动:限制单个日志大小,按大小 / 时间分割日志,防止单个日志几个 G 占满磁盘。

  4. 多线程写日志:写文件操作加锁,避免日志错乱、文件损坏。

八、数据库

  1. 数据库连接不要全局单连接多线程共用!SQLite 多线程访问要注意线程模式;Mysql/Postgres 每个工作线程使用独立连接或者连接池。

  2. 高频采集不要收到一条数据就 insert 一次,批量插入,大幅提升性能。

  3. 捕获数据库异常:磁盘满、数据库锁、文件损坏,做好容错。

  4. 定期清理历史数据,防止数据库无限膨胀。

九、异常、容错、健壮性(工业上位机最重要)

  1. 所有外部输入:网络数据、配置文件、用户输入,全部做校验,不要假设输入一定合法。

  2. 所有 IO 操作(串口、socket、文件、数据库)必须捕获错误,不能让异常直接抛到主程序造成崩溃。

  3. 软件要支持设备随时离线、随时上电接入;不能设备拔掉,程序直接卡死崩溃。

  4. 增加看门狗逻辑:通讯长时间没有收到数据,判定设备失联,主动断开,触发重连。

  5. 捕获崩溃信息:生成 dump 文件,现场崩溃可以回查调用栈定位 BUG(Windows MiniDumpWriteDump)。

十、性能

  1. 循环里面不要做频繁内存分配 new,高频率采集尽量复用缓冲区对象。

  2. 死循环务必加 sleep/wait,否则 CPU 占用 100%;不要 while (true) 空跑。

  3. 区分:等待事件用条件变量QWaitCondition/std::condition_variable,不要忙轮询。

十一、发布部署 Windows

  1. Qt 程序发布:依赖库部署,注意 Debug 版本不要发给现场,Debug 库缺失直接打不开。

  2. 文件路径:不要使用相对路径 "./",发布之后工作目录会变;使用 API 获取程序 exe 所在目录。

  3. 权限问题:不要把配置、日志写在 C:\Program Files 目录,该目录普通用户无写入权限,写到 AppData 目录。

  4. 字符编码统一:优先 UTF‑8,避免中文乱码;Windows 下注意 GBK 和 UTF‑8 转换坑。

十二、容易踩坑的小细节清单

  1. ✘ 不要 UI 线程读写串口 / TCP;✔ 通讯工作线程处理

  2. ✘ 不要把 TCP / 串口流直接当成数据包;✔ 缓冲区 + 帧解析

  3. ✘ 子线程操作 UI 控件;✔ 信号槽跨线程传递数据

  4. ✘ 多线程共享变量不加锁;✔ mutex 保护共享资源

  5. ✘ 线程 terminate 强制杀死;✔ flag 标记优雅退出

  6. ✘ 报文不校验 CRC、长度;✔ 外部数据不可信

  7. ✘ 硬编码 IP、串口参数;✔ 使用配置文件

  8. ✘ while (true) 无 sleep,CPU100%;✔ 使用条件变量等待

  9. ✘ 程序退出不关闭串口 socket;✔ closeEvent 释放资源

  10. ✘ 高频采集每条数据刷新 UI;✔ 批量合并刷新界面

十三、调试建议

  1. 开发阶段打印完整收发原始报文,方便定位协议解析错误。

  2. 使用 wireshark 抓 TCP 报文;串口工具抓串口数据流,区分是硬件问题还是软件解析问题。

  3. 生成 dump,现场崩溃可以回溯问题。

  4. 模拟异常场景:拔掉设备、断网、断电,测试软件会不会卡死崩溃。

收缩
  • QQ咨询

  • 电话咨询

  • 13073017555
  • 添加微信客服