码上通QT实战33--监控页面14-刻度盘旋转

1、前言

QDial 概述

QDial 是 Qt 框架中的一个圆形刻度盘控件,常用于模拟物理旋钮或选择器。它继承自 QAbstractSlider,支持用户通过旋转或点击来调整数值范围。QDial 通常用于需要连续或离散数值输入的场景,如音量控制、参数调节等。

QDial 的主要属性

  • minimum/maximum:设置刻度盘的最小值和最大值。

  • value:当前值,可通过编程或用户交互改变。

  • notchesVisible:是否显示刻度标记。

  • notchTarget:刻度间隔的像素距离。

  • wrapping:是否允许数值循环(从最大值跳转到最小值)。

  • sliderPosition:滑块当前位置(可能与 value 不同,如动画期间)。

QLCDNumber 概述

QLCDNumber 是 Qt 框架提供的控件,用于显示数字或简单文本,模拟液晶显示屏(LCD)效果。支持不同进制(十进制、十六进制等)、显示格式和样式调整,常用于计时器、计数器等场景。

Modbus功能码06详解

Modbus功能码06(写单个寄存器)用于向Modbus从设备写入单个寄存器的值。该功能码适用于保持寄存器(4XXXXX地址范围),常用于配置设备参数或发送控制指令。

功能码06格式

请求报文格式:

  • 功能码:1字节(0x06)
  • 寄存器地址:2字节(高位在前)
  • 写入值:2字节(高位在前)

示例请求报文(十六进制):

cpp 复制代码
[设备地址][0x06][寄存器地址高][寄存器地址低][值高][值低]

典型应用场景

  • 修改设备运行参数(如设定温度、压力阈值)
  • 发送控制命令(如启动/停止设备)
  • 配置设备工作模式

2、开始干

1、准备设备

2、页面布局

3、编写槽函数

cpp 复制代码
#include "monitorview.h"
#include "ui_monitorview.h"
#include <QSerialPortInfo>
#include <QSerialPort>
#include <SystemUtils.h>

MonitorView::MonitorView(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::MonitorView)
{
    ui->setupUi(this);

    SystemUtils utils;
    utils.SetDropShadowEffect(ui->wdg_link_container,QColor("#80FF0000"),5);//设置连接设备的阴影效果

    // 设置字体图标
    QFont font=QFont(QString("zx_icons"),9);
    // 监控状态
    ui->lbl_status_title_icon->setFont(font);
    ui->lbl_status_title_icon->setText(QChar(0xe807));
    //串口区域的刷新按钮
    QFont font2=QFont(QString("zx_icons"),11);
    ui->pb_port_refresh->setFont(font2);
    ui->pb_port_refresh->setText(QChar(0xe71e));

    //设置1-6号灯珠的图标及大小
    font.setPixelSize(30);
    ui->lbl_light_icon_1->setFont(font);
    ui->lbl_light_icon_1->setText(QChar(0xe9e2));
    ui->lbl_light_icon_2->setFont(font);
    ui->lbl_light_icon_2->setText(QChar(0xe9e2));
    ui->lbl_light_icon_3->setFont(font);
    ui->lbl_light_icon_3->setText(QChar(0xe9e2));
    ui->lbl_light_icon_4->setFont(font);
    ui->lbl_light_icon_4->setText(QChar(0xe9e2));
    ui->lbl_light_icon_5->setFont(font);
    ui->lbl_light_icon_5->setText(QChar(0xe9e2));
    ui->lbl_light_icon_6->setFont(font);
    ui->lbl_light_icon_6->setText(QChar(0xe9e2));

    //加载串口列表
    //使用QSerialPortInfo::availablePorts()方法可以获取系统中所有可用的串口。这个方法返回一个QList<QSerialPortInfo>,其中包含了所有串口的信息
    // 获取所有可用的串口信息 QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        ui->cb_port->addItem(info.portName());
    }
    //初始化波特率
    ui->cb_baud->addItem("2400");
    ui->cb_baud->addItem("4800");
    ui->cb_baud->addItem("9600");
    ui->cb_baud->addItem("19200");
    ui->cb_baud->setCurrentText("9600");//设置默认选项

    // 初始化校验位列表
    ui->cb_parity->addItem("No");
    ui->cb_parity->addItem("Odd");
    ui->cb_parity->addItem("Even");
    ui->cb_parity->addItem("Space");
    ui->cb_parity->addItem("Mark");
    ui->cb_parity->setCurrentText("No");//设置默认选项

    // 初始化数据位列表
    ui->cb_data->addItem("5");
    ui->cb_data->addItem("6");
    ui->cb_data->addItem("7");
    ui->cb_data->addItem("8");
    ui->cb_data->setCurrentText("8");//设置默认选项

    // 初始化停止位列表
    ui->cb_stop->addItem("One");
    ui->cb_stop->addItem("OneAndHalf");
    ui->cb_stop->addItem("Two");
    ui->cb_stop->setCurrentText("One");  //设置默认选项

    //关联灯珠状态开关的信号onCheckedChanged和槽函数onStatusChanged
    connect(ui->sb_status_1,SIGNAL(onCheckedChanged(bool,int)),this,SLOT(onStatusChanged(bool,int)));
    connect(ui->sb_status_2,SIGNAL(onCheckedChanged(bool,int)),this,SLOT(onStatusChanged(bool,int)));
    connect(ui->sb_status_3,SIGNAL(onCheckedChanged(bool,int)),this,SLOT(onStatusChanged(bool,int)));
    connect(ui->sb_status_4,SIGNAL(onCheckedChanged(bool,int)),this,SLOT(onStatusChanged(bool,int)));
    connect(ui->sb_status_5,SIGNAL(onCheckedChanged(bool,int)),this,SLOT(onStatusChanged(bool,int)));
    connect(ui->sb_status_6,SIGNAL(onCheckedChanged(bool,int)),this,SLOT(onStatusChanged(bool,int)));


    // 刻度盘样式:红色刻度+浅灰背景+边框美化
    ui->dial->setStyleSheet(
        "QDial{"
        "color: red;"
        "background-color: #f5f5f5;"
        "border:3px solid #ddd;"
        "border-radius:5px;"
        "}"
        );
    ui->dial->setNotchesVisible(true);   // 显示刻度
    ui->myled_1->setStyleSheet("color: blue;");
    ui->myled_1->display(10);

}

MonitorView::~MonitorView()
{
    delete ui;
}

//刷新串口列表
void MonitorView::on_pb_port_refresh_clicked()
{
    //加载串口列表
    ui->cb_port->clear();//清空
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        ui->cb_port->addItem(info.portName());
    }
}

//连接设备
void MonitorView::on_pb_link_clicked()
{
    // QSerialPort  打开  加电   数据发送  请求
    // 过程需要后台线程处理
    //获取选择的串口参数
    QString port=ui->cb_port->currentText();
    QString baud=ui->cb_baud->currentText();
    QString parity=ui->cb_parity->currentText();
    QString data=ui->cb_data->currentText();
    QString stop=ui->cb_stop->currentText();

    // 触发连接的信号
    emit onConnect(port,baud,parity,data,stop);
}


//串口打开状态
void MonitorView::setOpenState(bool state)
{
    //处理状态
    if(state){//连接成功
        ui->pb_link->setText("断开连接");
        ui->cb_port->setEnabled(false);
        ui->cb_baud->setEnabled(false);
        ui->cb_parity->setEnabled(false);
        ui->cb_data->setEnabled(false);
        ui->cb_stop->setEnabled(false);

        // 改变所有状态灯的默认颜色
        ui->lbl_temp_green->setStyleSheet("background-color:'#DD00FF00';");
        ui->lbl_humi_green->setStyleSheet("background-color:'#DD00FF00';");
        ui->lbl_bright_green->setStyleSheet("background-color:'#DD00FF00';");
        //设置灯控开关的按钮全部为可用
        ui->sb_status_1->setIsEnabled(true);
        ui->sb_status_2->setIsEnabled(true);
        ui->sb_status_3->setIsEnabled(true);
        ui->sb_status_4->setIsEnabled(true);
        ui->sb_status_5->setIsEnabled(true);
        ui->sb_status_6->setIsEnabled(true);

    }else{//连接失败或断开连接
        ui->pb_link->setText("连接设备");
        ui->cb_port->setEnabled(true);
        ui->cb_baud->setEnabled(true);
        ui->cb_parity->setEnabled(true);
        ui->cb_data->setEnabled(true);
        ui->cb_stop->setEnabled(true);

        // 恢复所有状态灯的默认颜色
        //1、红色灯
        ui->lbl_temp_red->setStyleSheet("background-color:'#33FF0000';");
        ui->lbl_humi_red->setStyleSheet("background-color:'#33FF0000';");
        ui->lbl_bright_red->setStyleSheet("background-color:'#33FF0000';");
        //2、黄色灯
        ui->lbl_temp_yellow->setStyleSheet("background-color:'#33FF9000';");
        ui->lbl_humi_yellow->setStyleSheet("background-color:'#33FF9000';");
        ui->lbl_bright_yellow->setStyleSheet("background-color:'#33FF9000';");
        //3、绿色灯
        ui->lbl_temp_green->setStyleSheet("background-color:'#3300FF00';");
        ui->lbl_humi_green->setStyleSheet("background-color:'#3300FF00';");
        ui->lbl_bright_green->setStyleSheet("background-color:'#3300FF00';");

        //设置灯控开关的按钮全部为禁用
        ui->sb_status_1->setIsEnabled(false);
        ui->sb_status_2->setIsEnabled(false);
        ui->sb_status_3->setIsEnabled(false);
        ui->sb_status_4->setIsEnabled(false);
        ui->sb_status_5->setIsEnabled(false);
        ui->sb_status_6->setIsEnabled(false);
    }
}

//设置数据值
void MonitorView::setValue(uint16_t dtemp, uint16_t dhumi, uint16_t dbright)
{
    ui->wdg_temp_meter->setValue(dtemp*0.1);
    ui->wdg_humi_meter->setValue(dhumi*0.1);
    ui->wdg_bright_meter->setValue(dbright*0.1);

    //历史最高温度,最低温度
    if(min_temp>dtemp){
        min_temp=dtemp;
    }
    if(max_temp<dtemp){
        max_temp=dtemp;
    }
    ui->lbl_temp1->setText(QString::number(max_temp*0.1));
    ui->lbl_temp2->setText(QString::number(min_temp*0.1));

    //历史最高湿度,最低湿度
    if(min_humi>dhumi){
        min_humi=dhumi;
    }
    if(max_humi<dhumi){
        max_humi=dhumi;
    }
    ui->lbl_humi1->setText(QString::number(max_humi*0.1));
    ui->lbl_humi2->setText(QString::number(min_humi*0.1));

    //历史最高亮度,最低亮度
    if(min_bright>dbright){
        min_bright=dbright;
    }
    if(max_bright<dbright){
        max_bright=dbright;
    }
    ui->lbl_bright1->setText(QString::number(max_bright*0.1));
    ui->lbl_bright2->setText(QString::number(min_bright*0.1));

    //对平均数据的处理逻辑,保存100个数据,大于100时移除第1个
    //平均温度
    templist.append(dtemp);
    if(templist.count()>100){
        templist.removeAt(0);
    }
    int32_t sum=0;
    foreach(int16_t v,templist){
        sum+=v;
    }
    avg_temp=sum/templist.count();//计算平均值
    ui->lbl_temp3->setText(QString::number(avg_temp*0.1));

    //平均湿度
    humilist.append(dhumi);
    if(humilist.count()>100){
        humilist.removeAt(0);
    }
    sum=0;
    foreach(int16_t v,humilist){
        sum+=v;
    }
    avg_humi=sum/humilist.count();//计算平均值
    ui->lbl_humi3->setText(QString::number(avg_humi*0.1));

    //平均亮度
    brightlist.append(dbright);
    if(brightlist.count()>100){
        brightlist.removeAt(0);
    }
    sum=0;
    foreach(int16_t v,brightlist){
        sum+=v;
    }
    avg_bright=sum/brightlist.count();//计算平均值
    ui->lbl_bright3->setText(QString::number(avg_bright*0.1));
}

//设置灯珠状态
void MonitorView::setStatus(bool l1, bool l2, bool l3, bool l4, bool l5)
{
    //5个灯珠
    ui->sb_status_1->setIsChecked(l1);
    ui->sb_status_2->setIsChecked(l2);
    ui->sb_status_3->setIsChecked(l3);
    ui->sb_status_4->setIsChecked(l4);
    ui->sb_status_5->setIsChecked(l5);
    //总控灯
    ui->sb_status_6->setIsChecked(l1&l2&l3&l4&l5);
}


//灯珠状态改变
void MonitorView::onStatusChanged(bool state, int index)
{
    emit onStatusSend(state,index);//激发信号
}

//刻度盘旋转事件
void MonitorView::on_dial_valueChanged(int value)
{
    //1、向led屏显示数字
     ui->myled_1->display(value);

    //2、向设备下发指令
     emit onTextSend(QString::number(value));

}

4、发送指令

cpp 复制代码
#include "mainwindow.h"
#include "systemutils.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <QMouseEvent>
#include <QThread>
#include <QtConcurrent/QtConcurrentRun>
#include <QDebug>
#include <QSqlError>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 1.设置窗口无边框和窗口透明
    this->setWindowFlags(Qt::FramelessWindowHint);
    this->setAttribute(Qt::WA_TranslucentBackground);


    // 设置相关阴影效果
    SystemUtils * utils=new SystemUtils();
    utils->SetDropShadowEffect(ui->wdg_root,Qt::gray,10);

    //设置关闭,最小,最大的图标
    // 设置相关字体图标
    QFont font=QFont(QString("zx_icons"),9);
    // 关闭
    ui->pb_close->setFont(font);
    ui->pb_close->setText(QChar(0xe653));
    // 最大化
    ui->pb_max->setFont(font);
    ui->pb_max->setText(QChar(0xe694));
    // 最小化
    ui->pb_min->setFont(font);
    ui->pb_min->setText(QChar(0xe7e6));
    //设置第二行消息的图标
    ui->lbl_message_icon->setFont(font);
    ui->lbl_message_icon->setText(QChar(0xe7ff));

    //设置标题字体
    QFont font2=QFont(QString("钉钉进步体"),14);
    ui->lbl_title->setFont(font2);

    //设置"实时监控"按钮的信号和槽函数
    connect(ui->nb_monitor,SIGNAL(onClicked(int)),SLOT(onNavClicked(int)));
    //设置"趋势图表"导航按钮的信号和槽函数
    connect(ui->nb_trend,SIGNAL(onClicked(int)),SLOT(onNavClicked(int)));
    //设置"异常报警"导航按钮的信号和槽函数
    connect(ui->nb_alarm,SIGNAL(onClicked(int)),SLOT(onNavClicked(int)));
    //设置"系统设置"导航按钮的信号和槽函数
    connect(ui->nb_settings,SIGNAL(onClicked(int)),SLOT(onNavClicked(int)));

    //绑定监控页面Monitor的信号和槽函数关联,即monitor页面中的信号函数onConnect由本页面的槽函数onConnect响应处理
    connect(ui->Monitor,SIGNAL(onConnect(QString,QString,QString,QString,QString)),SLOT(onConnect(QString,QString,QString,QString,QString)));
    //绑定监控页面Monitor的信号和槽函数关联,即monitor页面中的信号函数onTextSend由本页面的槽函数onTextCompleted响应处理
    connect(ui->Monitor,SIGNAL(onTextSend(QString)),this,SLOT(onTextCompleted(QString)));
    //绑定监控页面Monitor的信号和槽函数关联,即monitor页面中的信号函数onStatusSend由本页面的槽函数onStatusCompleted响应处理
    connect(ui->Monitor,SIGNAL(onStatusSend(bool,int)),this,SLOT(onStatusCompleted(bool,int)));


    //创建线程及对象,处理串口
    thread=new QThread();
    worker=new QWorker();
    //1、本页面的信号函数onOpen由worker对象的槽函数openPort响应处理
    connect(this,SIGNAL(onOpen(QString,QString,QString,QString,QString)),worker,SLOT(openPort(QString,QString,QString,QString,QString)));
    //2、worker对象中的信号函数openCompleted由本页面的onSetopenState槽函数响应处理
    connect(worker,SIGNAL(openCompleted(bool)),this,SLOT(onSetopenState(bool)));
    //3、本页面的信号函数onSend由worker对象的槽函数sendData响应处理
    connect(this,SIGNAL(onSend(QByteArray,int)),worker,SLOT(sendData(QByteArray,int)));
    //4、worker对象中的信号函数sendCompleted由本页面的槽函数onSendCompleted响应处理
    connect(worker,SIGNAL(sendCompleted(QByteArray,int)),this,SLOT(onSendCompleted(QByteArray,int)));


    worker->moveToThread(thread);//把对象worker放在多线程中
    thread->start();//启动线程

    //f1=QtConcurrent::run(this,&MainWindow::onMonitor);//这是qt5.X的写法,这个写法在6.x中不支持
    //实例化异步任务
    f1 = QtConcurrent::run([this]() {
        this->onMonitor(); // 替换为你的成员函数名
    });

    //数据初始化
    DB=QSqlDatabase::addDatabase("QSQLITE");
    DB.setDatabaseName(QApplication::applicationDirPath()+"/data.db3");
    if(!DB.open()){
         ui->lbl_message->setText("数据库对象初始化失败");
    }else{
        ui->lbl_message->setText("数据库对象初始化成功");
        ui->Settings->setDatabase(DB);//将数据库对象传递给系统设置页面
        ui->Alarm->setDatabase(DB);//将数据库对象传递给报警信息页面
    }
    //报警条件初始化
    this->initCondtions();
}

MainWindow::~MainWindow()
{
    delete ui;
}

//监控事件
void MainWindow::onMonitor()
{
    while(this->isMonitor){
        if(m_isConnected){
            // 从设备中获取到相关数据
            QByteArray bytes;
            bytes.resize(6);
            bytes[0]=0x01;//设备地址
            bytes[1]=0x03;//功能码
            bytes[2]=0x00;//开始地址
            bytes[3]=0x00;
            bytes[4]=0x00;//请求数量
            bytes[5]=0x03;

            // CRC16算法
            auto crc=this->CRC16(bytes);
            uint8_t hi=uint8_t(crc>>8);
            uint8_t lo=uint8_t(crc);
            bytes.resize(8);
            bytes[6]=lo;//crc校验码
            bytes[7]=hi;

            // 需要给QWorker对象发送一个信号
            emit onSend(bytes,1);//向传感器发送信号

            QByteArray bytes2;
            bytes2.resize(6);
            bytes2[0]=0x01;//设备地址
            bytes2[1]=0x01;//功能码
            bytes2[2]=0x00;//开始地址
            bytes2[3]=0x00;
            bytes2[4]=0x00;//请求数量
            bytes2[5]=0x05;

            // CRC16算法
            auto crc2=this->CRC16(bytes2);
            uint8_t hi2=uint8_t(crc2>>8);
            uint8_t lo2=uint8_t(crc2);
            bytes2.resize(8);
            bytes2[6]=lo2;//crc校验码
            bytes2[7]=hi2;

            emit onSend(bytes2,2);//向灯珠发送信号
        }
        QThread::msleep(5000);
    }
}

//鼠标按下事件
void MainWindow::mousePressEvent(QMouseEvent *event){
    if (event->button() == Qt::LeftButton) {  // 如果按下左边按钮
        m_drag = true;//表示要移动
        // 获取当前光标的位置
        m_dragPos = event->pos();
        // 当前鼠标点相对于桌面屏幕左上角的坐标(0,0),全局坐标;
        m_resizeDownPos = event->globalPosition().toPoint();
        // 获取当前窗口的相关参数,包括位置,大小等等各种参数
        m_mouseDownRect = this->rect();
    }
}

//鼠标移动事件
void MainWindow::mouseMoveEvent(QMouseEvent *event){
    // 如果是鼠标在拖动时,当前窗口是全屏,不做任何处理
    if (isFullScreen()) {
        return;
    }
    if (m_move) {
        move(event->globalPosition().toPoint() - m_dragPos);
        return;
    }

    setCursor(Qt::ArrowCursor);
    if (m_drag && (event->buttons() & Qt::LeftButton)) {
        m_move = true;
        move(event->globalPosition().toPoint() - m_dragPos);
    }
}

//鼠标释放事件
void MainWindow::mouseReleaseEvent(QMouseEvent *event){
    Q_UNUSED(event)
    m_drag = false;
    if (m_move) {
        m_move = false;
    }
    setCursor(Qt::ArrowCursor);
}

//关闭事件
void MainWindow::on_pb_close_clicked()
{
    QMessageBox::StandardButton ret=QMessageBox::question(this,tr("关闭系统运行"),tr("您确定要退出系统吗?"),QMessageBox::Yes | QMessageBox::No,QMessageBox::Yes );
    if(ret== QMessageBox::Yes){
        this->isMonitor=false;
        this->close();
    }
}

//最大化
void MainWindow::on_pb_max_clicked()
{
    this->showFullScreen();  // 全屏展示
}

//最小化
void MainWindow::on_pb_min_clicked()
{
    this->showMinimized();
}

//窗体双击事件
void MainWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
    Q_UNUSED(event); // 标记参数未使用,消除警告
    if (isFullScreen()) {
        showNormal();  // 如果是全屏,就恢复到非全屏
    } else {
        showFullScreen();  // 否则就变成全屏的
    }
}

//导航按钮的点击响应事件
void MainWindow::onNavClicked(int index)
{
    ui->sw_pages->setCurrentIndex(index);
}

//连接串口设备
void MainWindow::onConnect(QString port, QString baud, QString parity, QString data, QString stop)
{
    //执行串口对象的连接动作,动作必须在后台线程中处理
    //触发信号,让qworker这个对象来接收信号
    emit onOpen(port,baud,parity,data,stop);
}

//串口连接状态
void MainWindow::onSetopenState(bool state)
{
    m_isConnected=true;
    //接收到worker对象中的打开状态信号
    ui->Monitor->setOpenState(state);
}

//计算CRC校验码
uint16_t MainWindow::CRC16(QByteArray bytes)
{
    int len=bytes.size();
    uint16_t wcrc=0XFFFF;//预置16位crc寄存器,初值全部为1
    uint8_t temp;//定义中间变量
    int i=0,j=0;//定义计数

    for(i=0;i<len;i++)//循环计算每个数据
    {
        temp=bytes.at(i);
        wcrc^=temp;
        for(j=0;j<8;j++){
            //判断右移出的是不是1,如果是1则与多项式进行异或。
            if(wcrc&0X0001){
                wcrc>>=1;//先将数据右移一位
                wcrc^=0XA001;//与上面的多项式进行异或
            }
            else//如果不是1,则直接移出
                wcrc>>=1;//直接移出
        }
    }
    temp=wcrc;//crc的值
    return wcrc;
}

//初始化报警条件信息
void MainWindow::initCondtions()
{
    QSqlQuery sqlQuery(DB);
    sqlQuery.exec("SELECT c_num,compare_value,alarm_msg,status FROM conditions");//获取到6行数据
    if(!sqlQuery.exec())
    {
        qDebug() << "Error: Fail to query table. " << sqlQuery.lastError();
    }
    else{//获取温度、湿度、亮度的报警条件,并赋给各种变量
        if(sqlQuery.next()){//温度最高报警条件
            QString id = sqlQuery.value(0).toString();
            this->m_num_temp_hi=id;//温度最高预警id
            QString value = sqlQuery.value(1).toString();
            this->m_value_temp_hi=value.toUInt();//温度最高预警值
            QString msg = sqlQuery.value(2).toString();
            this->m_msg_temp_hi=msg;//温度最高预警消息
            bool state=sqlQuery.value(3).toString()=="1";
            this->m_state_temp_hi=state;//温度最高预警状态
        }
        if(sqlQuery.next()){//温度最低报警条件
            QString id = sqlQuery.value(0).toString();
            this->m_num_temp_lo=id;//温度最低预警id
            QString value = sqlQuery.value(1).toString();
            this->m_value_temp_lo=value.toUInt();//温度最低预警值
            QString msg = sqlQuery.value(2).toString();
            this->m_msg_temp_lo=msg;//温度最低预警消息
            bool state=sqlQuery.value(3).toString()=="1";
            this->m_state_temp_lo=state;//温度最低预警状态
        }
        if(sqlQuery.next()){//湿度最高报警条件
            QString id = sqlQuery.value(0).toString();
            this->m_num_humi_hi=id;//湿度最高预警id
            QString value = sqlQuery.value(1).toString();
            this->m_value_humi_hi=value.toUInt();//湿度最高预警值
            QString msg = sqlQuery.value(2).toString();
            this->m_msg_humi_hi=msg;//湿度最高预警消息
            bool state=sqlQuery.value(3).toString()=="1";
            this->m_state_humi_hi=state;//湿度最高预警状态
        }
        if(sqlQuery.next()){//湿度最低报警条件
            QString id = sqlQuery.value(0).toString();
            this->m_num_humi_lo=id;//湿度最低预警id
            QString value = sqlQuery.value(1).toString();
            this->m_value_humi_lo=value.toUInt();//湿度最低预警值
            QString msg = sqlQuery.value(2).toString();
            this->m_msg_humi_lo=msg;//湿度最低预警消息
            bool state=sqlQuery.value(3).toString()=="1";
            this->m_state_humi_lo=state;//湿度最低预警状态
        }
        if(sqlQuery.next()){//亮度最高报警条件
            QString id = sqlQuery.value(0).toString();
            this->m_num_bright_hi=id;//亮度最高预警id
            QString value = sqlQuery.value(1).toString();
            this->m_value_bright_hi=value.toUInt();//亮度最高预警值
            QString msg = sqlQuery.value(2).toString();
            this->m_msg_bright_hi=msg;//亮度最高预警消息
            bool state=sqlQuery.value(3).toString()=="1";
            this->m_state_bright_hi=state;//亮度最高预警状态
        }
        if(sqlQuery.next()){//亮度最低报警条件
            QString id = sqlQuery.value(0).toString();
            this->m_num_bright_lo=id;//亮度最低预警id
            QString value = sqlQuery.value(1).toString();
            this->m_value_bright_lo=value.toUInt();//亮度最低预警值
            QString msg = sqlQuery.value(2).toString();
            this->m_msg_bright_lo=msg;//亮度最低预警消息
            bool state=sqlQuery.value(3).toString()=="1";
            this->m_state_bright_lo=state;//亮度最低预警状态
        }
    }
}



//发送完成后
void MainWindow::onSendCompleted(QByteArray bytes, int flag)
{
    if(flag==1){
        if(bytes.length()>3&&(bytes[1]&0x80)==0){//这个条件说明返回的数据没有问题,这里只是一个简单的判断
            bool ok;
            //截取指定位置指定长度的数据,转换成16进制,再转换成10进制
            uint16_t temp=bytes.mid(3,2).toHex().toUInt(&ok,16);
            uint16_t humi=bytes.mid(5,2).toHex().toUInt(&ok,16);
            uint16_t bright=bytes.mid(7,2).toHex().toUInt(&ok,16);
            // qDebug()<<"温度:"<<temp;
            // qDebug()<<"湿度:"<<humi;
            // qDebug()<<"亮度:"<<bright;
            ui->Monitor->setValue(temp,humi,bright);//设置监控页面数据值
            ui->Trend->onDataReceive(temp,humi,bright);//设置趋势页面数据值

            //将获取的数据与已预设的数值进行比较,如果触发条件则提交到alarm页面,触发条件是:当前值<最小值,当前值>最大值
            //1.1,如果温度最高报警状态已启用并且当前温度大于温度最高预警值,则提交报警信息
            if(m_state_temp_hi&&temp*0.1>m_value_temp_hi){
                // 提交Alarm消息
                ui->Alarm->setAlarmInformation(temp,m_num_temp_hi,m_msg_temp_hi,"温度","40001-1");
            }
            //1.2,如果温度最低报警状态已启用并且当前温度小于温度最低预警值,则提交报警信息
            if(m_state_temp_lo&&temp*0.1<m_value_temp_lo){
                // 提交Alarm消息
                ui->Alarm->setAlarmInformation(temp,m_num_temp_lo,m_msg_temp_lo,"温度","40001-2");
            }

            //2.1,如果湿度最高报警状态已启用并且当前湿度大于湿度最高预警值,则提交报警信息
            if(m_state_humi_hi&&humi*0.1>m_value_humi_hi){
                // 提交Alarm消息
                ui->Alarm->setAlarmInformation(humi,m_num_humi_hi,m_msg_humi_hi,"湿度","40002-1");
            }
            //2.1,如果湿度最低报警状态已启用并且当前湿度小于湿度最高预警值,则提交报警信息
            if(m_state_humi_lo&&humi*0.1<m_value_humi_lo){
                // 提交Alarm消息
                ui->Alarm->setAlarmInformation(humi,m_num_humi_lo,m_msg_humi_lo,"湿度","40002-2");
            }

             //3.1,如果亮度最高报警状态已启用并且当前亮度大于亮度最高预警值,则提交报警信息
            if(m_state_bright_hi&&bright*0.1>m_value_bright_hi){
                // 提交Alarm消息
                ui->Alarm->setAlarmInformation(bright,m_num_bright_hi,m_msg_bright_hi,"亮度","40003-1");
            }
             //3.2,如果亮度最低报警状态已启用并且当前亮度小于亮度最低预警值,则提交报警信息
            if(m_state_bright_lo&&bright*0.1<m_value_bright_lo){
                // 提交Alarm消息
                ui->Alarm->setAlarmInformation(bright,m_num_bright_lo,m_msg_bright_lo,"亮度","40003-2");
            }
        }
    }else if(flag==2){
        //处理灯珠状态,如果不是写的命令,即是读的命令时才执行读取灯珠状态
        if(!m_isWriting&&bytes.length()>3&&(bytes[1]&0x80)==0){
            bool ok;
            Q_UNUSED(ok); // 标记参数未使用,消除警告
            //解析状态数据
            bool s1=bytes[3]&1;
            bool s2=bytes[3]&2;
            bool s3=bytes[3]&4;
            bool s4=bytes[3]&8;
            bool s5=bytes[3]&16;
            ui->Monitor->setStatus(s1,s2,s3,s4,s5);//将数据交给界面显示
        }
        if(this->m_isWriting){
            m_isWriting=false;//修改写的状态为假,即读
        }
    }
}

//发送指令
void MainWindow::onTextCompleted(QString text)
{
    //04 06 00 00 00 23 C8 13
    int dec1=text.toInt();
    QByteArray hex1 = QString("%1").arg(dec1, 4, 16, QChar('0')).toUpper().toLatin1(); //将qstring转换4位长度的16进制格式,不足部分补0,然后再转换成QByteArray
     // 将前面文本数据拼接在一起发给设备
    QByteArray req = QByteArray::fromHex("040600000000");//初始指令
    req.replace(4, 2, QByteArray::fromHex(hex1));//将第4位和第5位替换成传递过来的参数
    auto crc=this->CRC16(req);
    uint8_t hi=uint8_t(crc>>8);
    uint8_t lo=uint8_t(crc);
    req.append(lo);
    req.append(hi);

    // 将指定数据指令发到QWorker里处理
    emit onSend(req,3);
}


//灯珠状态发送指令完成
void MainWindow::onStatusCompleted(bool state, int index)
{
    this->m_isWriting=true;

    QByteArray bytes;
    if(index==5){// 灯珠统一设置的命令
        char l1=state?0x01:0x00;
        char l2=state?(0x01<<1):0x00;
        char l3=state?(0x01<<2):0x00;
        char l4=state?(0x01<<3):0x00;
        char l5=state?(0x01<<4):0x00;

        bytes.resize(8);
        bytes[0]=0x01;
        bytes[1]=0x0F;
        bytes[2]=0x00;
        bytes[3]=0x00;
        bytes[4]=0x00;
        bytes[5]=0x05;
        bytes[6]=0x01;
        bytes[7]=0x00|l1|l2|l3|l4|l5;

        auto result=this->CRC16(bytes);
        uint8_t hi=uint8_t(result>>8);
        uint8_t lo=uint8_t(result);
        bytes.append(lo);
        bytes.append(hi);
    }
    else {// 单个灯珠设置的命令
        bytes.resize(6);
        bytes[0]=0x01;
        bytes[1]=0x05;
        bytes[2]=0x00;
        bytes[3]=uint8_t(index);
        bytes[4]=state?0xFF:0x00;
        bytes[5]=0x00;

        auto result=this->CRC16(bytes);
        uint8_t hi=uint8_t(result>>8);
        uint8_t lo=uint8_t(result);
        bytes.append(lo);
        bytes.append(hi);
    }
    emit onSend(bytes,3);
}

5、测试效果

6、小结

Modbus功能码06(0x06)是‌写单个保持寄存器‌的指令,用于主站向从站设备的单个寄存器写入16位数据。它属于"预置单个寄存器"功能,响应帧会完整回显请求帧,是Modbus协议中基础且重要的操作。

复制代码
原创不易,打字不易,截图不易,撸码不易,整理不易,走过路过,不要错过,欢迎点赞,收藏,转载,复制,抄袭,留言,灌水,请动动你的金手指,祝您早日实现财务自由。
相关推荐
源代码•宸1 小时前
Golang原理剖析(channel源码分析)
开发语言·后端·golang·select·channel·hchan·sudog
liuyunshengsir2 小时前
golang Gin 框架下的大数据量 CSV 流式下载
开发语言·golang·gin
BlockChain8882 小时前
MPC 钱包实战(三):Rust MPC Node + Java 调度层 + ETH 实际转账(可运行)
java·开发语言·rust
吉吉612 小时前
在 Windows 和 Linux 的 VSCode 中配置 PHP Debug
开发语言·php
蜜汁小强2 小时前
macOS 上升级到 python 3.12
开发语言·python·macos
Remember_9932 小时前
【数据结构】Java集合核心:线性表、List接口、ArrayList与LinkedList深度解析
java·开发语言·数据结构·算法·leetcode·list
小旭95272 小时前
【Java 面试高频考点】finally 与 return 执行顺序 解析
java·开发语言·jvm·面试·intellij-idea
hixiong1232 小时前
C# OpenVinoSharp部署Yolo26模型进行推理
开发语言·c#·openvino·yolo26
不会c嘎嘎2 小时前
QT中的各种对话框
开发语言·qt