码上通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协议中基础且重要的操作。

复制代码
原创不易,打字不易,截图不易,撸码不易,整理不易,走过路过,不要错过,欢迎点赞,收藏,转载,复制,抄袭,留言,灌水,请动动你的金手指,祝您早日实现财务自由。
相关推荐
用户805533698033 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner3 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz8 天前
QML Hello World 入门示例
qt
xcyxiner11 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner12 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner12 天前
DicomViewer (添加模型类)3
qt
xcyxiner13 天前
DicomViewer (目录调整) 2
qt
xcyxiner13 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00615 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术15 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript