夜雨聆风学习资料网

ARTICLE · 980779

从零构建工业软件控制面板:六个子区域的架构与取舍

从零构建工业软件控制面板:六个子区域的架构与取舍

工业软件的"控制面板"不只是几个按钮的集合——它是操作员和工程师的日常运维入口,是系统状态的"健康仪表盘"。本文以 PLCMonitor 的控制面板 Tab 为例,分享从需求分析到落地实现的完整过程,以及设计中的关键取舍。


本文目录:①背景与定位 ②六个区域 ③关键设计原则 ④实施取舍 ⑤性能保障 ⑥结语


背景:为什么需要控制面板?

PLCMonitor 主窗口有六大 Tab:工艺总貌、实时数据、趋势图表、控制面板、报警中心、报表。前三个是"看"的,后三个是"管"的。

但早期版本中,"控制面板"只有一行占位文本:

控制面板 (开发中...)

而系统其实已经具备了很多"有数据无面板"的能力:

  • CommManager
    的设备连接信号已定义,但没有 UI 消费
  • 审计日志已按月落盘,但没有查看入口
  • 系统运行时长、内存、磁盘空间可获取,但没有展示
  • Web 报表服务后台已有,但没有开关按钮

这就造成了一个矛盾:系统有很多能力,但操作员找不到入口。


设计目标:面向谁?解决什么问题?

控制面板不是给开发者的调试工具,而是给操作员和工程师的运维管控台。

角色
主要诉求
操作员
快速知道设备状态、系统是否正常、出问题了怎么办
工程师
配置管理、日志排查、快捷操作

与"看工艺/看数据/看趋势"不同,控制面板要专管系统。


架构设计:六个子区域  (本截图未显示对外接口)

控制面板采用 QStackedWidget + 顶部切换按钮,避免嵌套 Tab 的复杂度。六个子区域按优先级分层:

区域
优先级
核心职责
通讯总貌
P0
设备连接状态一览,操作员第一屏必看
系统健康
P1
运行时长/内存/数据库/磁盘空间
运行日志
P1
系统运行全过程记录,调试依据
审计日志
P1
敏感操作追溯,合规必需
快捷运维
P2
标签管理/工艺图配置/数据库配置等
对外接口
P2
REST API + MQTT + Modbus Server 管理

区域一:通讯总貌(P0)

数据来源

  • CommManager::deviceIds()
     — 设备列表
  • CommManager::deviceConnected/disconnected/stateChanged
     — 实时状态信号
  • DeviceConfig::protocol
     — 协议类型(S7 / ModbusTCP / ModbusRTU)
  • IConnector::connectionState()
     — 连接状态

表格列设计

列
内容
说明
设备名称
DeviceConfig::deviceName
来自配置
协议
S7 / ModbusTCP / ModbusRTU
一目了然
地址
IP:端口 或 COM口@波特率
区分协议格式
状态
绿色圆点(运行)/ 红色圆点(断开)/ 黄色圆点(警告)/ 灰色圆点(离线)
走 SemanticColors 状态色
最后通讯
N 秒前
1 秒刷新,感知实时性
错误信息
最近一条错误
快速定位问题

关键设计决策

信号订阅 vs 轮询?

选择信号订阅——CommManager 的状态变化直接触发表格更新,零性能开销。如果用轮询,每秒钟都要查询所有设备状态,浪费资源。

状态色用哪个常量?

// ✓ 正确:使用状态色分组SemanticColor::StatusRunning   // #00cc00 绿色SemanticColor::StatusStopped   // #cc0000 红色SemanticColor::StatusWarn      // #ffaa00 黄色SemanticColor::StatusOff       // #666666 灰色// ✗ 错误:误用操作/报警语义色SemanticColor::Confirm         // 按钮确认用色SemanticColor::Danger          // 危险警告用色

区域二:系统健康(P1)

卡片网格布局

2 行 × 3 列,每个卡片显示一项系统指标:

卡片
数据来源
刷新频率
运行时长
g_appStartTime
 静态初始化
1 秒
内存占用
Windows API GetProcessMemoryInfo
5 秒
MySQL 状态
DatabaseManager::isMySQLConnected()
进入时
SQLite 状态
DatabaseManager::alarmDbPath()
 + 文件存在性
进入时
Web 报表
WebServerManager::isRunning()
进入时
磁盘空间
QStorageInfo::rootDirectory()
 剩余空间
5 秒

异常标记逻辑

  • MySQL 断开 → 卡片变红
  • 磁盘 < 1GB → 卡片变黄
  • Web 服务异常 → 红色 + 一键重启按钮

性能考量

系统健康数据不需要实时更新,采用进入 Tab 时加载 + 定时刷新的策略,避免启动期构建重量级组件。


区域三:运行日志(P1)

两套日志系统

⚠️ 常见误区:运行日志 ≠ 审计日志

运行日志
审计日志
数据来源
Logger
 单例
AuditLog
 单例
内容
所有 qDebug/qInfo/qWarning
仅配置初始化/敏感操作
格式
纯文本,逐行追加
JSONL,结构化
文件
plcmonitor.logaudit_YYYYMM.jsonl
轮转
100MB/5个备份
按月,不删除

读取策略

  • 按需读取,不常驻缓冲
  • 正则解析每行:[时间] 级别: 消息 (文件:行号)
  • 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 列,每个按钮对应一个已有功能入口:

按钮
功能
权限控制
标签管理
打开设备配置对话框
浏览模式禁用
工艺图配置
打开工艺图配置对话框
浏览模式禁用
数据库配置
打开数据库配置对话框
浏览模式禁用
Web 报表开关
启停 Web 报表服务
仅管理员
审计日志
切换到审计日志页
所有角色
导出日志
导出运行日志
所有角色
退出登录
触发登录流程
已登录用户
用户管理
占位(开发中)
管理员

权限复用

快捷运维按钮不直接调用槽函数,而是通过信号转发到 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 安装包试用,并提出宝贵意见。

相关学习资料