一、前言

为了增强本系统的拓展性,做成通用的物联网管理平台,特意将控制器主设备类型、探测器子设备类型、对应种类符号等信息,全部做成表格可自定义添加和修改,这样在控制器信息表和探测器信息表管理的时候,可以自由下拉选择,而不用去更改源代码程序,这样处理和架构就离通用的物联网平台系统更进一步了,在表格或者地图设备上,显示的对应单位符号也是由用户自己选择的,当然也默认了一套类型种类,方便懒人使用。

最开始做这个对应数据的时候,一开始是放在配置文件中,后面发现不妥,有部分类型种类是有对应关系的,后面又改了一版做的是文本文件存储信息,发现还是不够完美和便捷,最终选择采用数据库表的形式来处理,用qtableview开启编辑属性,用户更新后保存到数据库中即可,程序初始化的时候自动读取整个表信息,添加到对应的类型种类队列即可。

二、功能特点

2.1 软件模块

  1. 设备监控模块,包括数据监控(表格形式展示)、设备面板(面板形式展示)、地图监控(地图形式展示)、曲线监控(曲线形式展示)。
  2. 数据查询模块,包括报警记录、运行记录、操作记录。
  3. 系统设置模块,包括基本设置、端口管理、控制器管理、探测器管理、报警联动、类型设置等。
  4. 其他设置模块,包括用户管理、地图管理、位置调整、组态设计、设备调试等。

2.2 基础功能

  1. 设备数据采集,支持串口、网络,串口可设置串口号、波特率,网络可设置IP地址、通讯端口。
  2. 每个端口支持采集周期时间,默认1秒钟一个设备。
  3. 支持设置通讯超时次数,默认3次。
  4. 支持最大重连时间,用于重新读取离线的设备。
  5. 控制器信息,能够添加控制器名称,选择控制器地址、控制器型号,设置该控制器下面的探测器数量。
  6. 探测器信息,能够添加位号、探测器型号、气体种类、气体符号、高报值、低报值、缓冲值、清零值、是否启用、报警声音、背景地图、存储周期、数值换算小数点位数、报警延时时间、报警的类型(HH,LL,HL)等。
  7. 类型管理可配置控制器型号、探测器型号、气体种类、气体符号等。
  8. 地图支持导入和删除,所有的探测器在地图上的位置可自由拖动保存。
  9. 端口信息、控制器信息、探测器信息、类型信息、用户信息等,都支持导入、导出、导出到excel、打印。
  10. 运行记录、报警记录、操作记录,都支持多条件组合查询,比如时间段、控制器、探测器等,所有记录支持导出到excel/pdf和打印。
  11. 运行记录、报警记录、操作记录都可删除指定时间范围内的数据。
  12. 系统设置可选择对应表最大保存记录数,自动清理早期数据,留出足够的空间存储重要的数据。
  13. 报警短信转发,支持多个接收手机号码,可设定发送间隔,比如即时发送或者6个小时发送一次所有的报警信息,短信内容过长,自动拆分多条短信。
  14. 报警邮件转发,支持多个接收邮箱,可设定发送间隔,比如即时发送或者6个小时发送一次所有的报警信息,支持附件发送。
  15. 设置软件的中文标题、英文标题、logo路径、版权所有等。
  16. 开关设置开机运行、报警声音、自动登录、记住密码等。
  17. 报警声音可设置播放次数,界面风格样式提供18套皮肤文件选择。
  18. 用户管理,包括用户权限配置,不同用户可以有不同模块的权限。
  19. 用户登录和用户退出,可以记住密码和自动登录,超过三次报错提示并关闭程序。
  20. 四种监控模式,设备面板监控、地图监控、表格数据监控、曲线数据监控,可自由切换,四种模式下都实时展示采集到的数据,报警闪烁等。
  21. 报警继电器联动,一个位号可以跨串口联动多个模块和继电器号,支持多对多。

2.3 特色功能

  1. 通信协议支持modbus_com、modbus_tcp_rtu,后期拓展mqtt等协议。
  2. 数据源除了真实的硬件设备采集,还可选数据库采集,这样用户可以安排其他程序员比如java程序员将前端采集好的数据放到数据库,本系统直接从数据库采集即可。数据库采集模式可以作为通用的系统使用,更适合多人多系统协作。
  3. 智能跳过超时的设备,加快对在线设备的采集速度,当设备数量很多的时候尤其有用。
  4. 对智能跳过的超时的设备,在设定的重连时间自动采集一次,以便探测设备是否又重新上线。
  5. 每个探测器可控是否启用,不启用则不会采集,也不会在界面显示,相当于运行阶段临时关闭。
  6. 探测器可设置缓冲值和报警延时时间,在该值附近波动产生的报警,不计入报警,只有持续处于报警值且超过报警延时时间才算真正报警,这样可以规避很多波动导致的误报。
  7. 探测器可设置存储周期,按照设定的时间来存储一条运行记录,可以按照重要程度对重要性高的设定存储周期短一些,不重要的设定大一些,这样可以节省不少的存储空间,也保证了重要的数据及时存储。
  8. 探测器可设置清零值,在一些高精度高灵敏的设备可能出厂的时候默认值未必是0,需要设定清零值来表示初始值。
  9. 探测器可设置小数点,用于计算后的真实数据控制小数点点位显示,相当于除以10、除以100、除以1000,这样大部分的探测器数据直接通过小数点位设置控制真实换算后的值,极个别的需要特殊转换的可以在通信协议中约定。
  10. 探测器报警的类型支持多种,有些设备是高于某个值高报,低于某个值低报,而有些设备是在最小值最大值范围内是高报,低于最小值低报,高于最大值正常。这样可以分情况处理,涵盖各种报警类型。
  11. 原创数据导入、导出、打印机制,跨平台不依赖任何组件,瞬间导出数据。
  12. 导出到excel的记录支持所有excel、wps等表格文件版本,不依赖excel等软件。
  13. 高报颜色、低报颜色、正常颜色、默认值颜色等,都可以自由设置。
  14. 支持云端数据同步,将本地采集到的数据实时同步到云端。
  15. 支持网络转发和网络接收,网络接收开启后,软件从udp接收数据进行解析。网络转发支持多个目标IP,这样就实现了本地采集的软件,自由将数据转到客户端,随时查看采集到的数据。
  16. 自动记住用户最后停留的界面以及其他配置信息,重启后自动应用。
  17. 报警自动切换到对应的地图,探测器按钮闪烁,表格数据对应颜色显示。
  18. 双击探测器图标,弹出对应探测器详细信息,可以根据需要定制回控操作。
  19. 数据库支持多种,包括sqlite、mysql、sqlserver、postgresql、oracle、人大金仓等。
  20. 本地设备采集到的数据实时上传到云端,以便手机APP或者web等其他方式提取。
  21. 自带设备模拟工具,支持不同型号的多个设备数据模拟,同时还带数据库数据模拟,以便在没有设备的时候测试数据。
  22. 标准modbus协议,各种控制器类型、探测器类型、种类、符号等全部自定义,非常灵活和强大,通信协议示例数据非常完整,通用各种modbus协议系统,适用于各种应用场景接入。
  23. 同时集成了串口通信、网络通信、数据库通信、数据导入导出打印、通信协议解析、界面UI、全局换肤等众多组件和知识点,非常适合新手入门和进阶。
  24. 支持xp、win7、win10、、win11、linux、mac、各种国产系统(UOS、中标麒麟、银河麒麟等)、嵌入式linux等系统。
  25. 注释完整,项目结构清晰,超级详细完整的使用开发手册,精确到每个代码文件的功能说明,不断持续迭代版本。

三、体验地址

  1. 国内站点:https://gitee.com/feiyangqingyun
  2. 国际站点:https://github.com/feiyangqingyun
  3. 个人主页:https://blog.csdn.net/feiyangqingyun
  4. 知乎主页:https://www.zhihu.com/people/feiyangqingyun
  5. 产品主页:https://blog.csdn.net/feiyangqingyun/article/details/97565652
  6. 在线文档:https://feiyangqingyun.gitee.io/qwidgetdemo/iotsystem/
  7. 体验地址:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A 提取码:o05q 文件名:bin_iotsystem.zip。
  8. 文章导航:https://qtchina.blog.csdn.net/article/details/121330922

四、效果图



五、相关代码

void frmConfigType::on_btnAdd_clicked()
{
int count = model->rowCount();
model->insertRow(count); int row = count - 1;
int typeID = model->index(row, 0).data().toInt() + 1;
QString deviceType = model->index(row, 1).data().toString();
QString nodeNumber = model->index(row, 2).data().toString();
QString nodeType = model->index(row, 3).data().toString();
QString nodeClass = model->index(row, 4).data().toString();
QString nodeSign = model->index(row, 5).data().toString(); //当前为第一行时候的默认值
if (count == 0) {
typeID = 1;
deviceType = "FC-1003-8";
nodeNumber = "8";
nodeType = "FT-2104";
nodeClass = "O2";
nodeSign = "PPM";
} //设置新增加的行默认值
model->setData(model->index(count, 0), typeID);
model->setData(model->index(count, 1), deviceType);
model->setData(model->index(count, 2), nodeNumber);
model->setData(model->index(count, 3), nodeType);
model->setData(model->index(count, 4), nodeClass);
model->setData(model->index(count, 5), nodeSign); //立即选中当前新增加的行
ui->tableView->setCurrentIndex(model->index(count, 0));
} void frmConfigType::on_btnSave_clicked()
{
//过滤判断是否有重复的,只有有一个重复就返回
int count = model->rowCount();
QStringList deviceTypes, nodeTypes, nodeClasss, nodeSigns;
for (int i = 0; i < count; ++i) {
QString deviceType = model->index(i, 1).data().toString();
if (!deviceType.isEmpty()) {
if (deviceTypes.contains(deviceType)) {
QUIHelper::showMessageBoxError("控制器类型不能重复, 请重新输入!", 3);
return;
} else {
deviceTypes << deviceType;
}
} QString nodeType = model->index(i, 3).data().toString();
if (!nodeType.isEmpty()) {
if (nodeTypes.contains(nodeType)) {
QUIHelper::showMessageBoxError("探测器类型不能重复, 请重新输入!", 3);
return;
} else {
nodeTypes << nodeType;
}
} QString nodeClass = model->index(i, 4).data().toString();
if (!nodeClass.isEmpty()) {
if (nodeClasss.contains(nodeClass)) {
QUIHelper::showMessageBoxError("气体种类不能重复, 请重新输入!", 3);
return;
} else {
nodeClasss << nodeClass;
}
} QString nodeSign = model->index(i, 5).data().toString();
if (!nodeSign.isEmpty()) {
if (nodeSigns.contains(nodeSign)) {
QUIHelper::showMessageBoxError("气体符号不能重复, 请重新输入!", 3);
return;
} else {
nodeSigns << nodeSign;
}
}
} //重新设置下焦点避免MAC系统上当单元格处于编辑状态保存不成功的BUG
ui->tableView->setFocus(); //开启数据库事务提交数据
model->database().transaction();
if (model->submitAll()) {
model->database().commit(); //分别判断那种改动过,改动过的才需要发送对应的信号
bool deviceTypeChange = false;
int deviceTypeCount = deviceTypes.count();
if (DbData::TypeInfo_DeviceType.count() != deviceTypeCount) {
deviceTypeChange = true;
} else {
for (int i = 0; i < deviceTypeCount; ++i) {
if (DbData::TypeInfo_DeviceType.at(i) != deviceTypes.at(i)) {
deviceTypeChange = true;
break;
}
}
} bool nodeTypeChange = false;
int nodeTypeCount = nodeTypes.count();
if (DbData::TypeInfo_NodeType.count() != nodeTypeCount) {
nodeTypeChange = true;
} else {
for (int i = 0; i < nodeTypeCount; ++i) {
if (DbData::TypeInfo_NodeType.at(i) != nodeTypes.at(i)) {
nodeTypeChange = true;
break;
}
}
} bool nodeClassChange = false;
int nodeClassCount = nodeClasss.count();
if (DbData::TypeInfo_NodeClass.count() != nodeClassCount) {
nodeClassChange = true;
} else {
for (int i = 0; i < nodeClassCount; ++i) {
if (DbData::TypeInfo_NodeClass.at(i) != nodeClasss.at(i)) {
nodeClassChange = true;
break;
}
}
} bool nodeSignChange = false;
int nodeSignCount = nodeSigns.count();
if (DbData::TypeInfo_NodeSign.count() != nodeSignCount) {
nodeSignChange = true;
} else {
for (int i = 0; i < nodeSignCount; ++i) {
if (DbData::TypeInfo_NodeSign.at(i) != nodeSigns.at(i)) {
nodeSignChange = true;
break;
}
}
} DbQuery::loadTypeInfo(); if (deviceTypeChange) {
emit deviceTypeChanged();
} if (nodeTypeChange) {
emit nodeTypeChanged();
} if (nodeClassChange) {
emit nodeClassChanged();
} if (nodeSignChange) {
emit nodeSignChanged();
}
} else {
model->database().rollback();
QUIHelper::showMessageBoxError("保存信息失败, 请重新填写!");
} //有些数据库需要主动查询一下不然是空白的比如odbc数据源
model->select();
} void frmConfigType::on_btnDelete_clicked()
{
if (ui->tableView->currentIndex().row() < 0) {
QUIHelper::showMessageBoxError("请选择要删除的行!");
return;
} if (QUIHelper::showMessageBoxQuestion("确定要删除该类型吗? 删除后不能恢复!") == QMessageBox::Yes) {
int row = ui->tableView->currentIndex().row();
model->removeRow(row);
model->submitAll();
ui->tableView->setCurrentIndex(model->index(model->rowCount() - 1, 0));
}
} void frmConfigType::on_btnReturn_clicked()
{
model->revertAll();
} void frmConfigType::on_btnClear_clicked()
{
int count = model->rowCount();
if (count <= 0) {
return;
} if (QUIHelper::showMessageBoxQuestion("确定要清空所有信息吗? 清空后不能恢复!") == QMessageBox::Yes) {
DbQuery::clearTypeInfo();
model->select();
}
} void frmConfigType::on_btnInput_clicked()
{
FormHelper::inputData(model, columnNames, "TypeInfo", "类型信息");
on_btnSave_clicked();
} void frmConfigType::on_btnOutput_clicked()
{
FormHelper::outputData("TypeID asc", columnNames, "TypeInfo", "类型信息");
} void frmConfigType::dataout(quint8 type)
{
QString columns = "*";
QString where = "";
FormHelper::dataout(type, columnNames, columnWidths, "类型信息", "TypeInfo", columns, where);
} void frmConfigType::on_btnPrint_clicked()
{
dataout(0);
} void frmConfigType::on_btnXls_clicked()
{
dataout(2);
}

Qt编写物联网管理平台40-类型种类的更多相关文章

  1. 年底奉献-QT编写视频监管平台(开源)

    忙忙碌碌又是一年,算算自己毕业四年半,一直在现在这家公司做研发外加总经理助理,研发起初用的VB.NET,而后全面转为C#,最后又全面转为QT,都是由于项目需要,算下来自己搞QT编程也已经四年了,201 ...

  2. 自学华为IoT物联网_08 IoT连接管理平台介绍

    点击返回自学华为IoT物流网 自学华为IoT物联网_08 IoT连接管理平台介绍 一.IoT连接管理平台的由来 1.1  物联网产业发展面临的挑战 新业务上线周期长,应用碎片化,开发周期长,场频上市慢 ...

  3. 自学华为IoT物联网_10 IoT联接管理平台配置及开发实验1

    点击返回自学华为IoT物流网 自学华为IoT物联网_10 IoT联接管理平台配置及开发实验1 实验1:OceanConnect平台实验             通过基本的编程操作与配置,帮助读者熟悉O ...

  4. Qt编写项目作品大全(自定义控件+输入法+大屏电子看板+视频监控+楼宇对讲+气体安全等)

    一.自定义控件大全 (一).控件介绍 超过160个精美控件,涵盖了各种仪表盘.进度条.进度球.指南针.曲线图.标尺.温度计.导航条.导航栏,flatui.高亮按钮.滑动选择器.农历等.远超qwt集成的 ...

  5. 基于SpringBoot+SSM实现的Dota2资料库智能管理平台

    Dota2资料库智能管理平台的设计与实现 摘    要 当今社会,游戏产业蓬勃发展,如PC端的绝地求生.坦克世界.英雄联盟,再到移动端的王者荣耀.荒野行动的火爆.都离不开科学的游戏管理系统,游戏管理系 ...

  6. Qt编写气体安全管理系统10-数据导出

    一.前言 数据导出一般指导出到excel表格,可能有部分用户还需要导出到pdf,因为pdf基本上不可编辑,防止用户重新编辑导出的数据,excel可能绝大部分用过电脑的人都知道,广为流行,主要就是微软的 ...

  7. 使用Qt编写模块化插件式应用程序

    动态链接库技术使软件工程师们兽血沸腾,它使得应用系统(程序)可以以二进制模块的形式灵活地组建起来.比起源码级别的模块化,二进制级别的模块划分使得各模块更加独立,各模块可以分别编译和链接,模块的升级不会 ...

  8. 【IoT】物联网NB-IoT之电信物联网开放平台对接流程浅析

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/liwei16611/article/de ...

  9. 如何不编写 YAML 管理 Kubernetes 应用?

    Kubernetes 将自身边界内的事物都抽象为资源.其中的主要部分,是以 Deployment.StatefulSet 为代表的 workload 工作负载控制器,其他各类资源都围绕这些主要的资源工 ...

  10. (视频) 《快速创建网站》1. 网站管理平台WordPress & 微软Azure 云计算简介

    网站并不神秘,过节了,在家闲的没事的,自己建个网站玩玩吧.每段视频不超过15分钟,地铁/公交/睡前/醒来看一段,几天之后变身建站专家,找老板加薪去! 在普通人眼里,创建网站是专业开发人员和IT工程师才 ...

随机推荐

  1. store文件夹的处理,vuex模块化modules

    index文件代码: import Vue from "vue"; import Vuex from "vuex"; // 导入根getters import ...

  2. Vnode 的渲染过程

    在tamplate编译的时候,generater 会生成一个render 函数,通过 render 函数会把 template 内容描述成 vnode ,然后vnode 通过一系列操作转换为真实的 D ...

  3. Android复习(五)设备兼容—>支持刘海屏

    支持刘海屏 刘海屏是指某些设备显示屏上的一个区域延伸到显示面,这样既能为用户提供全面屏体验,又能为设备正面的重要传感器留出空间.Android 在搭载 Android 9(API 级别 28)及更高版 ...

  4. centos rar文件解压不出

    楼主的项目开发是在centos系统上面的,但是经常小伙伴发的压缩文件都是rar格式的. 在centos系统自带的解压的软件并不能解压rar格式的文件: 双击文件时,能弹出解压窗口,可是发现可以解压的更 ...

  5. vue 父子组件之间传值

    在vue中父子组件传值是必不可少的,大家必须要学会! 首先父组件向子组件传值: 父组件:比如我们传teacher为index,如果我们传值变量可以使用 : 绑定我们在data中定义的变量. 子组件:为 ...

  6. (系列九)使用Vue3+Element Plus创建前端框架(附源码)

    说明 该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发). 该系统文章,我会尽量说的非常详细,做到不管新手.老手都能看懂. 说明:OverallAuth2 ...

  7. CSS动画效果(炫酷登录页面)

    1.整体效果 https://mmbiz.qpic.cn/sz_mmbiz_gif/EGZdlrTDJa6ibiceejK9loT70yREYASOhuSRaI6vQtQ42zN48oafaWDzdn ...

  8. EDUSRC | 记录几张edusrc证书站挖掘

    在web资产挖证书站是比较难的,尤其是没有账号密码进入后台或者统一的情况下,于是便转变思路,重点放在信息收集,收集偏远资产上. 一.XX大学 srping actuator未授权 茫茫c段,找到这么一 ...

  9. .NET 8.0 开源在线考试系统(支持移动端)

    前言 推荐一款基于.NET 8.0 免费开源跨平台在线考试系统,系统不仅支持桌面端,还特别优化了移动端的用户体验. 通过本系统可以轻松搭建自己的在线考试平台,实现随时随地的测试与评估. 本文将详细介绍 ...

  10. react hooks 渲染性能

    目录 目录 重复渲染 React.memo() 例子 React.useMemo 例子 React.useMemo 也可以绑定 jsx和tsx对象 React.useCallback() 例子 重复渲 ...