ARTICLE · 980779
从零构建工业软件控制面板:六个子区域的架构与取舍
工业软件的"控制面板"不只是几个按钮的集合——它是操作员和工程师的日常运维入口,是系统状态的"健康仪表盘"。本文以 PLCMonitor 的控制面板 Tab 为例,分享从需求分析到落地实现的完整过程,以及设计中的关键取舍。
本文目录:①背景与定位 ②六个区域 ③关键设计原则 ④实施取舍 ⑤性能保障 ⑥结语
背景:为什么需要控制面板?
PLCMonitor 主窗口有六大 Tab:工艺总貌、实时数据、趋势图表、控制面板、报警中心、报表。前三个是"看"的,后三个是"管"的。
但早期版本中,"控制面板"只有一行占位文本:
控制面板 (开发中...)
而系统其实已经具备了很多"有数据无面板"的能力:
CommManager的设备连接信号已定义,但没有 UI 消费 审计日志已按月落盘,但没有查看入口 系统运行时长、内存、磁盘空间可获取,但没有展示 Web 报表服务后台已有,但没有开关按钮
这就造成了一个矛盾:系统有很多能力,但操作员找不到入口。
设计目标:面向谁?解决什么问题?
控制面板不是给开发者的调试工具,而是给操作员和工程师的运维管控台。
与"看工艺/看数据/看趋势"不同,控制面板要专管系统。
架构设计:六个子区域 (本截图未显示对外接口)

控制面板采用 QStackedWidget + 顶部切换按钮,避免嵌套 Tab 的复杂度。六个子区域按优先级分层:
区域一:通讯总貌(P0)
数据来源
CommManager::deviceIds()— 设备列表 CommManager::deviceConnected/disconnected/stateChanged— 实时状态信号 DeviceConfig::protocol— 协议类型(S7 / ModbusTCP / ModbusRTU) IConnector::connectionState()— 连接状态
表格列设计
DeviceConfig::deviceName | ||
关键设计决策
信号订阅 vs 轮询?
选择信号订阅——CommManager 的状态变化直接触发表格更新,零性能开销。如果用轮询,每秒钟都要查询所有设备状态,浪费资源。
状态色用哪个常量?
// ✓ 正确:使用状态色分组SemanticColor::StatusRunning // #00cc00 绿色SemanticColor::StatusStopped // #cc0000 红色SemanticColor::StatusWarn // #ffaa00 黄色SemanticColor::StatusOff // #666666 灰色// ✗ 错误:误用操作/报警语义色SemanticColor::Confirm // 按钮确认用色SemanticColor::Danger // 危险警告用色
区域二:系统健康(P1)
卡片网格布局
2 行 × 3 列,每个卡片显示一项系统指标:
g_appStartTime | ||
GetProcessMemoryInfo | ||
DatabaseManager::isMySQLConnected() | ||
DatabaseManager::alarmDbPath() | ||
WebServerManager::isRunning() | ||
QStorageInfo::rootDirectory() |
异常标记逻辑
MySQL 断开 → 卡片变红 磁盘 < 1GB → 卡片变黄 Web 服务异常 → 红色 + 一键重启按钮
性能考量
系统健康数据不需要实时更新,采用进入 Tab 时加载 + 定时刷新的策略,避免启动期构建重量级组件。
区域三:运行日志(P1)
两套日志系统
⚠️ 常见误区:运行日志 ≠ 审计日志
Logger | AuditLog | |
plcmonitor.log | audit_YYYYMM.jsonl | |
读取策略
按需读取,不常驻缓冲 正则解析每行: [时间] 级别: 消息 (文件:行号)QFileSystemWatcher监听文件变更,增量更新视图 限制最新 2000 行,超出显示"更多请导出查看"
筛选与导出
按级别筛选(DEBUG/INFO/WARNING/ERROR/FATAL) 关键字搜索(防抖 150ms) 导出为 .log文件
区域四:审计日志(P1)
数据结构
每条记录包含:seq / ts / op / actor / actor_role / result / args / prev / mac
显示格式
[2026-09-04 09:30:00] [config:init] admin(管理员) — success
筛选逻辑
审计日志没有 level 字段,按类别筛选需:
通讯类 → op含"connect"/"disconnect"配置变更 → op含"config"警告 → result∈ {"warning", "fail"}
区域五:快捷运维(P2)
按钮网格设计
2 行 × 4 列,每个按钮对应一个已有功能入口:
权限复用
快捷运维按钮不直接调用槽函数,而是通过信号转发到 MainWindow 的既有 QAction,复用已有的权限校验逻辑。
区域六:对外接口(P2)
REST API 管理
API Key 管理界面 接口启用/禁用开关 请求日志查看
Modbus TCP Server 管理
服务器启停 寄存器映射配置入口
MQTT 状态监控
Broker 连接状态 队列深度显示 已发布消息计数
关键设计原则总结
控制面板的第一性原则:操作员一眼看懂,工程师三秒定位,主线程零开销。
1. 懒加载
启动时不在控制面板放置真实 widget,只放占位。用户首次切换到该 Tab 时才构建 ControlPanelWidget,避免启动期构建重量级组件。
2. 信号驱动而非轮询
通讯总貌通过订阅 CommManager 的信号更新,而不是定时器轮询。性能更优,响应更及时。
3. 按需读取
运行日志和审计日志按需读取,不常驻内存缓冲。大文件限制行数,避免卡顿。
4. 主题适配
所有颜色走 SemanticColors,不写 hex 字面量。状态色、操作色、报警色严格区分。
5. 权限复用
快捷运维按钮通过信号转发到既有 QAction,复用已有的权限校验逻辑,不重复实现。
实施过程中的取舍
取舍 1:不用 .ui 文件
方案最初计划使用 .ui 文件,但最终选择在 C++ 构造函数内建布局。原因:
信号槽关系更清晰 避免 AUTOUIC 路径问题 代码可读性更好
取舍 2:按设备重连功能暂不实现
方案中有"重连选中设备"按钮,但实际未实现。原因:
CommManager没有按设备重连的公开 API connectToDevice在 IConnector上是虚函数,直接调用会绕开线程/重连簿记S7 自带自动重连定时器
保留"全连接/全断开/刷新"三个操作,满足大多数场景。
取舍 3:运行时长 API 选用方案
Qt 6 全系(包括 6.11.1)均未提供 QCoreApplication::applicationStartTime() API,故采用文件作用域静态变量近似:
// 静态初始化在 main() 之前完成static const QDateTime g_appStartTime = QDateTime::currentDateTime();
运行时计算:
int uptimeSecs = g_appStartTime.secsTo(QDateTime::currentDateTime());
待 Qt 未来版本引入该 API 后可无缝替换。
性能与稳定性保障
日志读取后台化
运行日志读取使用 QtConcurrent 在后台线程执行,主线程只负责渲染,避免大文件读取导致 UI 卡顿。
文件变更防抖
QFileSystemWatcher 的文件变更信号经过 400ms 防抖,避免频繁触发。
轮转处理
日志文件轮转时,重新监听新文件,确保增量更新不丢失。
结语
控制面板的设计不仅仅是"把数据展示出来",更要考虑谁在用、怎么用、在什么场景下用。
操作员需要一眼看到系统状态 工程师需要快速定位问题 系统需要零性能开销
这些需求看似简单,但每个细节都考验设计的功力。希望本文的流程和方法能给你一些启发。
如果您也在工业监控领域有类似需求,欢迎交流探讨技术方案。
如果觉得这篇文章对你有帮助,欢迎点赞收藏,下期继续分享 PLCMonitor 的其他设计实践。
项目地址:https://github.com/freddiezhang1990/plcmonitor
欢迎下载 V2.0 安装包试用,并提出宝贵意见。