Qt编写物联网管理平台33-设备面板
时间:2022-12-06 00:30:00
一、前言
与表格显示相比,设备面板显示数据可能在一个页面上显示的设备数据较少,但一些用户和场景需要这种面板的形式,这可能更生动。特别是经过这么多年的社会打击,我的原则是:用户是上帝和叔叔,试着从用户的角度思考,只要需求合理或基本合理,甚至说只要不太多,给钱。
事实上,这种面板显示数据的需求,从我开始工作时开始,已经有了软件,比如主机对应的设备面板,除了显示相应的值(电压值、电流值等),每个面板都有一个按钮供用户交互操作。对于一般的物联网平台,需要交互的场景很少,其中大部分用于显示收集到的数据,因此系统的面板显示数据。如果需要回控操作,请单击相应的面板弹出详细的面板信息。
设备面板的几个特点:
- 在不同的状态下,不同的颜色通常使用全球风格的颜色、禁止离线的颜色、低黄色、高红色等。
- 双击分为两种,离线时双击立即重新收集设备,在线时双击弹出详细面板。
- 面板可选择多种风格、普通风格、仪表风格等。
二、功能特点
2.1 软件模块
- 设备监控模块包括数据监控(表格显示)、设备面板(面板显示)、地图监控(地图显示)、曲线监控(曲线显示)。
- 数据查询模块包括报警记录、操作记录和操作记录。
- 系统设置模块,包括基本设置、端口管理、控制器管理、探测器管理、报警联动、类型设置等。
- 用户管理、地图管理、位置调整、组态设计、设备调试等设置模块。
2.2 基础功能
- 设备数据采集支持串口和网络。串口可以设置串口号和波特率。IP地址、通信端口。
- 每个端口支持收集周期时间,默认每秒一个设备。
- 默认情况下,支持设置通信超时次数3次。
- 支持重新读取离线设备的最大重连时间。
- 控制器信息,可添加控制器名称,选择控制器地址、控制器型号,设置控制器下的探测器数量。
- 探测器信息可添加位数、探测器型号、气体类型、气体符号、高值、低值、缓冲值、零值、是否启用、报警声音、背景地图、存储周期、值转换小数点位数、报警延迟时间、报警类型(HH,LL,HL)等。
- 类型管理可配置控制器型号、探测器型号、气体类型、气体符号等。
- 地图支持导入和删除,所有探测器都可以在地图上自由拖动。
- 端口信息、控制器信息、探测器信息、类型信息、用户信息等。excel、打印。
- 操作记录、报警记录、操作记录都支持时间段、控制器、探测器等多出到excel/pdf和打印。
- 操作记录、报警记录围内的数据,包括操作记录、报警记录和操作记录。
- 可选择对应表的最大保存记录,自动清理早期数据,留出足够的空间存储重要数据。
- 报警短信转发,支持多个接收手机号码,可设置发送间隔,如立即或6小时发送所有报警信息,短信内容过长,多条短信自动拆分。
- 报警邮件转发,支持多个接收邮箱,可设置发送间隔,如立即或6小时发送所有报警信息,支持附件发送。
- 设置软件的中文标题、英文标题、logo路径、版权所有权等。
- 开关设置启动操作、报警声音、自动登录、记住密码等。
- 报警声音可设置播放次数,界面风格提供18套皮肤文件选择。
- 不同的用户可以拥有不同模块的权限,包括用户权限配置。
- 用户登录和退出,可记住密码和自动登录,三次以上报错提示并关闭程序。
- 设备面板监控、地图监控、表数据监控、曲线数据监控、可自由切换四种监控模式、实时显示收集数据、报警闪烁等。
- 报警继电器联动,一个位号可以跨串口联动多个模块和继电器号,支持多对多。
2.3 特色功能
- 通信协议支持modbus_com、modbus_tcp_rtu,后期拓展mqtt等协议。
- 除了真实的硬件设备采集外,数据源还可以选择数据库采集,以便用户可以安排其他程序员,如java程序员将前端收集的数据放入数据库中,系统可以直接从数据库中收集。数据库采集模式可用作一般系统,更适合多人多系统合作。
- 当设备数量较多时,智能跳过超时设备,加快在线设备的采集速度尤为有用。
- 在设定的重连时间内自动采集智能跳过的超时设备,以检测设备是否重新启动。
- 每个探测器都可以控制是否启用。如果不启用,则不会收集或显示在界面上,相当于运行阶段的临时关闭。
- 探测器可以设置缓冲值和报警延迟时间。该值附近波动引起的报警不包括在报警中。只有当报警值持续且超过报警延迟时间时,才能真正报警,以避免许多波动引起的误报。
- 探测器可以设置存储周期,并根据设定的时间存储操作记录。重要性高的设置存储周期可以更短,不重要的设置更大,可以节省大量的存储空间,保证重要数据的及时存储。
- 探测器可以设置零值。当一些高精度、高灵敏度的设备可能出厂时,默认值可能不是0。需要设置零值来表示初始值。
- 探测器可以设置小数点,用于计算真实数据控制小数点显示,相当于除以10,除以100,除以1000,所以大多数探测器数据直接通过小数点设置控制真实转换值,很少需要特殊转换可以在通信协议中约定。
- 探测器报警有多种类型的支持。有些设备高于一定值,低于一定值,有些设备在最小值和最大值范围内,低于最小值,高于最大值正常。这可以根据情况进行处理,涵盖各种报警类型。
- 原始数据导入、导出、打印机制,跨平台不依赖任何组件,即时导出数据。
- 导出到excel支持所有记录excel、wps不依赖表格文件版本excel等软件。
- 可自由设置高报色、低报色、正常色、默认值色等。
- 支持云端数据同步,将本地采集到的数据实时同步到云端。
- 支持网络转发和网络接收。网络接收打开后,软件从udp分析接收数据。网络转发支持多个目标IP,这样,本地收集的软件就可以自由地将数据转移到客户端,随时查看收集到的数据。
- 重启后自动记住用户最终停留的界面等配置信息。
- 报警自动切换到相应的地图,探测器按钮闪烁,表数据显示相应的颜色。
- 双击探测器图标,弹出相应探测器的详细信息,可根据需要定制回控操作。
- 数据库支持多种多样,包括sqlite、mysql、sqlserver、postgresql、oracle、人大金仓等。
- 本地设备收集的数据实时上传到云端,使手机APP或者web以其它方式提取。
- 配备自己的设备模拟工具,支持不同型号的多个设备数据模拟,并配备数据库数据模拟,以便在没有设备的情况下测试数据。
- 标准modbus协议、各种控制器类型、探测器类型、类型、符号等都是自定义的,非常灵活和强大,通信协议示例数据非常完整,通用modbus适用于各种应用场景接入的协议系统。
- 同时,串口通信、网络通信、数据库通信、数据导出打印、通信协议分析、界面集成UI、很多组件和知识点,比如全换肤,非常适合新手入门和进步。
- 支持xp、win7、win10、、win11、linux、mac、各种国产系统(UOS、中标麒麟、银河麒麟等。linux等系统。
- 注释完整,项目结构清晰,开发手册使用超详细完整,每个代码文件的功能描述准确,版本不断迭代。
三、体验地址
- 国内站点:https://gitee.com/feiyangqingyun
- 国际站点:https://github.com/feiyangqingyun
- 个人主页:https://blog.csdn.net/feiyangqingyun
- 知乎主页:https://www.zhihu.com/people/feiyangqingyun
- 产品主页:https://blog.csdn.net/feiyangqingyun/article/details/97565652
- 在线文档:https://feiyangqingyun.gitee.io/qwidgetdemo/iotsystem/
- 体验地址:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A 提取码:o05q 文件名:bin_iotsystem.zip。
- 文章导航:https://qtchina.blog.csdn.net/article/details/121330922
四、效果图
五、相关代码
#include "frmdevicenode.h" #include "ui_frmdevicenode.h" #include "quihelper.h" #include "iconhelper.h" #include "dbquery.h" #include "deviceserver.h" #include "frmdevicecontrol.h" frmDeviceNode::frmDeviceNode(QWidget *parent) : QWidget(parent), ui(new Ui::frmDeviceNode) { ui->setupUi(this); this->initForm(); this->initStyle(); //this->initControl(); } frmDeviceNode::~frmDeviceNode() { delete ui; } bool frmDeviceNode::eventFilter(QObject *watched, QEvent *event) { //要区分在线离线的情况 //在线双击则弹出具体详情面板 //离线双击则重连当前端口下的所有设备 if (event->type() == QEvent::MouseButtonDblClick) { if (!online) { QString deviceName = this->property("deviceName").toString(); QString portName = DbQuery::getPortName(deviceName); DeviceServer::Instance()->readValue(portName, 255, true); } else { QString positionID = this->property("positionID").toString(); frmDeviceControl::Instance()->setPositionID(positionID); frmDeviceControl::Instance()->show(); } } return QWidget::eventFilter(watched, event); } void frmDeviceNode::initForm() { value = 0; online = true; alarm = false; select = false; this->installEventFilter(this); QFont font; font.setPixelSize(QUIConfig::FontSize + 2); ui->labNodeInfo->setFont(font); ui->labNodeInfo->setFixedHeight(30); //设置图形字体 QFont iconFont = IconHelper::getIconFontAwesome(); ui->labNodeNamex->setFont(iconFont); ui->labPositionIDx->setFont(iconFont); ui->labNodeTypex->setFont(iconFont); ui->labNodeValuex->setFont(iconFont); ui->labNodeNamex->setText(QChar(0xf132)); ui->labPositionIDx->setText(QChar(0xf015)); ui->labNodeTypex->setText(QChar(0xf124)); ui->labNodeValuex->setText(QChar(0xf0c3)); //通过弱属性控制样式 ui->devicePanel2->setProperty("form", "panel"); ui->deviceTitle1->setProperty("form", "panel"); ui->devicePanel1->setProperty("form", "panel"); ui->deviceTitle1->setProperty("flag", "paneltitle"); ui->devicePanel1->setProperty("flag", "panelcontrol"); //可以自行根据项目需要调整范围值小数点等 ui->gaugeSpeed->setUnit(""); ui->gaugeSpeed->setPrecision(0); ui->gaugeSpeed->setDigitCount(3); ui->gaugeSpeed->setRange(0, 200); ui->gaugeSpeed->setValue(ui->gaugeSpeed->getMinValue()); } void frmDeviceNode::initStyle() { //根据不同的面板样式隐藏对应的面板 if (AppConfig::PanelStyle == 0) { ui->widget1->setVisible(true); ui->widget2->setVisible(false); } else if (AppConfig::PanelStyle == 1) { ui->widget1->setVisible(false); ui->widget2->setVisible(true); } QString textColor, bgColor; if (alarm) { //判断低报还是高报 QString positionID = this->property("positionID").toString(); int index = DbData::NodeInfo_PositionID.indexOf(positionID); bool alarmLimit = DbData::NodeInfo_AlarmLimit.at(index); bool alarmUpper = DbData::NodeInfo_AlarmUpper.at(index); //bool alarmOther = DbData::NodeInfo_AlarmOther.at(index); //报警面板颜色 textColor = "#EEEEEE"; if (alarmLimit) { bgColor = AppConfig::ColorLimit; } else if (alarmUpper) { bgColor = AppConfig::ColorUpper; } else { bgColor = AppConfig::ColorOther; } //报警状态下的选中将颜色加个透明度 if (select) { QColor color(bgColor); bgColor = QString("rgba(%1,%2,%3,150)").arg(color.red()).arg(color.green()).arg(color.blue()); } } else { //选中面板颜色 textColor = online ? QUIConfig::TextColor : QUIConfig::BorderColor; bgColor = select ? QUIConfig::DarkColorStart : QUIConfig::NormalColorStart; } QStringList qss; //仪表盘中的数码管禁用样式 qss << QString("QLCDNumber:disabled{background:%1;}").arg(QUIConfig::NormalColorStart); //仪表样式中的节点信息
标签 qss << QString("#labNodeInfo{color:%1;background:%2;}").arg(textColor).arg(alarm ? bgColor : QUIConfig::DarkColorEnd); //仪表样式面板需要调整下颜色 if (AppConfig::PanelStyle == 1 && alarm) { textColor = QUIConfig::TextColor; bgColor = select ? QUIConfig::DarkColorStart : QUIConfig::NormalColorStart; } //全局文字颜色 qss << QString("*{color:%1;}GaugeSpeed{qproperty-textColor:%1;}").arg(textColor); //整体面板背景颜色 qss << QString("#deviceTitle1,#devicePanel1,#devicePanel2{background:%1;}").arg(bgColor); this->setStyleSheet(qss.join("")); } void frmDeviceNode::initControl() { QString positionID = this->property("positionID").toString(); QString nodeName = this->property("nodeName").toString(); QString nodeType = this->property("nodeType").toString(); QString nodeSign = this->property("nodeSign").toString(); ui->labNodeName->setText("名称: " + nodeName); ui->labPositionID->setText("位号: " + positionID); ui->labNodeType->setText("型号: " + nodeType); ui->labNodeValue->setText(QString("数值: %1 %2").arg(value).arg(nodeSign)); ui->gaugeSpeed->setText(nodeSign); ui->gaugeSpeed->setValue(value); ui->labNodeInfo->setText(positionID + " -- " + nodeName); } void frmDeviceNode::setValue(float value) { this->value = value; QString nodeType = this->property("nodeType").toString(); QString nodeSign = this->property("nodeSign").toString(); //有两个传感器是开关量,数值是1 2显示时用正常和异常代替 if (nodeType == "SJ-0001" || nodeType == "JG-0001") { ui->labNodeValue->setText(QString("数值: %1").arg(value == 1 ? "正常" : "异常")); ui->gaugeSpeed->setValue(value == 1 ? ui->gaugeSpeed->getMinValue() : ui->gaugeSpeed->getMaxValue()); } else { ui->labNodeValue->setText(QString("数值: %1 %2").arg(value).arg(nodeSign)); ui->gaugeSpeed->setValue(value); } } void frmDeviceNode::setOnline(bool online) { if (this->online != online) { this->online = online; if (!online) { this->value = 0; this->alarm = false; this->select = false; } this->setEnabled(online); this->initStyle(); this->initControl(); } } void frmDeviceNode::setAlarm(bool alarm) { //可能由低报转为高报所以这里不要做过滤判断 this->alarm = alarm; this->initStyle(); } void frmDeviceNode::setSelect(bool select) { if (this->select != select) { this->select = select; this->initStyle(); } }