赞
踩
在使用QT5.14.2时 使用QT自带的QModbusClient类实现对一个力传感器的数据读取。本人为了测试就将modbus读取逻辑等都写在主线程中,但是本人采用modbus异步通讯的方式,给从站发一个读取数据的信号(sendReadRequest),然后使用信号槽等待从站回复数据(readSerialForceData)。这种方式的好处是不会阻塞主线程。
但是!本人发现一个问题,就是在我拖动主界面的时候,modbus通讯就停止了!当我松开鼠标的时候,modbus正常通讯!目前还不知道为什么会这样,猜测是鼠标事件影响了modbus的信号槽机制。但是我试了一下,其他的槽函数都是能正常触发的,比如定时器之类。在我拖动界面的时候,UI界面也可以正常的刷新。只有modbus被阻塞了。
void MainWindow::on_readRequest() { // 读取寄存器 QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters,0,1); if (auto *reply = modbusDevice->sendReadRequest(readUnit, 1)) { if (!reply->isFinished()) connect(reply, &QModbusReply::finished, this, &MainWindow::readSerialForceData); else delete reply; // broadcast replies return immediately } else { //底部状态栏显示 ui->statusBar->showMessage(tr("Read error: ") + modbusDevice->errorString(), 5000); } }
void MainWindow::readSerialForceData() { auto reply = qobject_cast<QModbusReply *>(sender()); if (!reply) return; if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); //遍历数据单元的值 for (int i = 0, total = int(unit.valueCount()); i < total; ++i) { qint16 sValue = static_cast<qint16>(unit.value(i)); forceData_Temp = sValue/10.0; const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i).arg(QString::number(sValue)); ui->textBrowser_Log->append(entry); } } else if (reply->error() == QModbusDevice::ProtocolError) { statusBar()->showMessage(tr("Read response error: %1 (Mobus exception: 0x%2)"). arg(reply->errorString()). arg(reply->rawResult().exceptionCode(), -1, 16), 5000); } else { statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)"). arg(reply->errorString()). arg(reply->error(), -1, 16), 5000); } reply->deleteLater(); }
经过测试,发现将modbus放在子线程中,通过信号槽与主线程进行通讯,将数据保存成静态变量或者全局变量,即可解决问题。
这里新建一个serialThread 类用
.h文件
class serialThread : public QObject { Q_OBJECT public: serialThread(QObject *parent = Q_NULLPTR); ~serialThread(); QModbusClient *modbusDevice = nullptr; QDateTime startTime; QDateTime endTime; double intervalTime = 0;//串口数据间隔时间 double intervalTime_Last=0; //数据采集定时器 QTimer *pollTimer;//串口数据读取定时器 signals: void sig_connectSerialFinished(bool flag); void sig_disconnectSerialFinished(bool flag); public slots: void startThread(); void on_connectSerial(); void on_disconnectSerial(); void on_startReadData(); void on_stopReadData(); void on_readRequest(); void on_readRequestFinished(); }; #endif // SERIALTHREAD_H
.cpp文件
#include "serialthread.h" serialThread::serialThread(QObject *parent ) : QObject(parent) { qDebug()<<"串口线程构造函数:"<<QThread::currentThreadId(); } serialThread::~serialThread() { //退出串口 if (modbusDevice) { modbusDevice->disconnectDevice(); } delete modbusDevice; } void serialThread::startThread() { qDebug()<<"串口线程成员函数 startThread :"<<QThread::currentThreadId(); modbusDevice = new QModbusRtuSerialMaster(this); } void serialThread::on_connectSerial() { qDebug()<<"串口线程成员函数 on_connectSerial :"<<QThread::currentThreadId(); QMutexLocker locker(&ForceSensorGlobal::mutex); // 锁定互斥锁 //设置串口号 modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,ForceSensorGlobal::SeriComName); //设置波特率 modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,ForceSensorGlobal::BaudRate); //设置数据位 modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,ForceSensorGlobal::DataBits); //设置奇偶检验 switch(ForceSensorGlobal::ParityID) { case 0: modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::NoParity);break; case 1: modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::EvenParity);break; case 2: modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::OddParity);break; case 3: modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::SpaceParity);break; case 4: modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::MarkParity);break; default: break; } //设置停止位 switch(ForceSensorGlobal::StopBitsID) { case 0: modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QSerialPort::OneStop);break; case 1: modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QSerialPort::OneAndHalfStop);break; case 2: modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QSerialPort::TwoStop);break; default: break; } //设置执行请求时间 modbusDevice->setTimeout(1000); //设置执行请求次数 modbusDevice->setNumberOfRetries(3); //连接串口 并发送成功与否标志 emit sig_connectSerialFinished(modbusDevice->connectDevice()); pollTimer = new QTimer; pollTimer->setTimerType(Qt::PreciseTimer);//精确定时 pollTimer->setInterval(ForceSensorGlobal::IntervalTime); connect(pollTimer,&QTimer::timeout, this, &serialThread::on_readRequest); } void serialThread::on_disconnectSerial() { modbusDevice->disconnectDevice(); emit sig_disconnectSerialFinished(true); } void serialThread::on_startReadData() { QMutexLocker locker(&ForceSensorGlobal::mutex); // 锁定互斥锁 //清空一下数据 方便再次存储 ForceSensorGlobal:: recordData.clear(); QVector<double> vectorTemp; for (int i = 0; i < 2; i++) { ForceSensorGlobal::recordData.append(vectorTemp); } startTime = QDateTime::currentDateTime();//获取开始时间 pollTimer->setInterval(ForceSensorGlobal::IntervalTime); pollTimer->start(); qDebug() <<"子线程 on_startReadData "; } void serialThread::on_stopReadData() { pollTimer->stop(); qDebug() <<"子线程 on_stopReadData "; } void serialThread::on_readRequest() { // 读取寄存器 QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters,0,1); if (auto *reply = modbusDevice->sendReadRequest(readUnit, 1)) { if (!reply->isFinished()) connect(reply, &QModbusReply::finished, this, &serialThread::on_readRequestFinished); else reply->deleteLater(); // broadcast replies return immediately } else { //读取错误 qDebug() <<"读取错误: " <<modbusDevice->errorString(); } } void serialThread::on_readRequestFinished() { auto reply = qobject_cast<QModbusReply *>(sender()); if (!reply) return; QMutexLocker locker(&ForceSensorGlobal::mutex); // 锁定互斥锁 if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); //遍历数据单元的值 for (int i = 0, total = int(unit.valueCount()); i < total; ++i) { //接受信号的时间 endTime = QDateTime::currentDateTime(); intervalTime = startTime.msecsTo(endTime) / 1000.0; double now = (intervalTime - intervalTime_Last)*1000.0; qDebug() <<now; intervalTime_Last = intervalTime; //收到的数据是uint16 需转换为int16 qint16 sValue = static_cast<qint16>(unit.value(i)); //传感器默认有一位小数,需除以10 double forceData_Temp = sValue/10.0; //将时间 力数据存在到全局变量中 ForceSensorGlobal::recordData[0].append(intervalTime); ForceSensorGlobal::recordData[1].append(forceData_Temp); } } else if (reply->error() == QModbusDevice::ProtocolError) { //接收到的响应信息是协议错误 qDebug() <<"接收到的响应信息是协议错误: " <<modbusDevice->errorString(); } else { //接收到的响应信息是其他错误 qDebug() <<"接收到的响应信息是其他错误: " <<modbusDevice->errorString(); } reply->deleteLater(); }
在主线程中通过moveToThread,将modbus放入子线程
主界面.h文件
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QSerialPort> #include <QtSerialBus> #include <QModbusDataUnit> #include <QModbusClient> #include <QSerialPortInfo> #include <QMessageBox> #include <QDebug> #include <QSettings> #include "forcesensorglobal.h" #include "qcustomplot.h" #include "CurvePlot.h" #include "serialthread.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); /*************串口通讯线程***************/ //串口线程 QThread * mySerialThread; //串口操作类 serialThread *mySerialModbus; /***************定时器**********************/ QTimer *plotTimer;// //UI界面初始化 void initUI(); //搜索串口 void SearchSerialPorts(); signals: void sig_connectSerial(); void sig_disconnectSerial(); void sig_startReadData(); void sig_stopReadData(); private slots: void on_plotTimerTimeOut(); void on_pBtn_connectSerial_clicked(); void on_pBtn_disconnectSerial_clicked(); //接受子线程 modbus串口连接完成信号 void on_connectSerialFinished(bool flag); //接受子线程 modbus串口断开完成信号 void on_disconnectSerialFinished(bool flag); void on_pBtn_refreshSerial_clicked(); void on_pBtn_startRead_clicked(); void on_pBtn_stopRead_clicked(); void on_pBtn_sendData_clicked(); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H
主界面.cpp文件
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); initUI(); /*创建串口线程*/ mySerialModbus = new serialThread(); mySerialThread = new QThread; mySerialModbus->moveToThread(mySerialThread); //线程开始 mySerialThread->start(); connect(mySerialThread, &QThread::started, mySerialModbus, &serialThread::startThread); connect(mySerialThread, &QThread::finished, mySerialModbus, &QObject::deleteLater);//终止线程时要调用deleteLater槽函数 connect(mySerialThread, &QThread::finished, mySerialModbus, &QObject::deleteLater); /*绘图刷新定时器*/ plotTimer = new QTimer; plotTimer->setInterval(100); connect(plotTimer,&QTimer::timeout, this, &MainWindow::on_plotTimerTimeOut); /*****************信号槽*********************************/ //主线->子线程 串口连接信号 connect(this, &MainWindow::sig_connectSerial, mySerialModbus, &serialThread::on_connectSerial); //主线->子线程 串口断开信号 connect(this, &MainWindow::sig_disconnectSerial, mySerialModbus, &serialThread::on_disconnectSerial); //主线->子线程 开始采集信号 connect(this, &MainWindow::sig_startReadData, mySerialModbus, &serialThread::on_startReadData); //主线->子线程 停止采集信号 connect(this, &MainWindow::sig_stopReadData, mySerialModbus, &serialThread::on_stopReadData); //子线程->主线 串口连接完成信号 connect(mySerialModbus, &serialThread::sig_connectSerialFinished,this,&MainWindow::on_connectSerialFinished); //子线程->主线 串口断开完成信号 connect(mySerialModbus, &serialThread::sig_disconnectSerialFinished,this,&MainWindow::on_disconnectSerialFinished); void MainWindow::on_pBtn_connectSerial_clicked() { //更新数据 ForceSensorGlobal::SeriComName = ui->cBox_SeriComName->currentText(); ForceSensorGlobal::BaudRate = ui->cBox_BaudRate->currentText().toInt(); ForceSensorGlobal::DataBits = ui->cBox_DataBits->currentText().toInt(); ForceSensorGlobal::ParityID = ui->cBox_Parity->currentIndex(); ForceSensorGlobal::StopBitsID = ui->cBox_StopBits->currentIndex(); ForceSensorGlobal::IntervalTime = ui->sBox_Interval->value(); emit sig_connectSerial(); qDebug()<<"主线程 sig_connectSerial :"<<QThread::currentThreadId(); } void MainWindow::on_pBtn_disconnectSerial_clicked() { on_pBtn_stopRead_clicked(); emit sig_disconnectSerial(); } void MainWindow::on_connectSerialFinished(bool flag) { if (flag) { // 设置控件可否使用 ui->pBtn_connectSerial->setEnabled(false); ui->pBtn_refreshSerial->setEnabled(false); ui->pBtn_disconnectSerial->setEnabled(true); } else //打开失败提示 { QMessageBox::information(this,tr("错误"),tr("连接从站失败!"),QMessageBox::Ok); } } void MainWindow::on_disconnectSerialFinished(bool flag) { if (flag) { // 设置控件可否使用 ui->pBtn_connectSerial->setEnabled(true); ui->pBtn_refreshSerial->setEnabled(true); ui->pBtn_disconnectSerial->setEnabled(false); } else { QMessageBox::information(this,tr("错误"),tr("断开连接失败!"),QMessageBox::Ok); } } void MainWindow::on_pBtn_refreshSerial_clicked() { //填充串口号组合框 SearchSerialPorts(); } void MainWindow::on_pBtn_startRead_clicked() { ForceSensorGlobal::IntervalTime = ui->sBox_Interval->value(); emit sig_startReadData(); plotTimer->start(); } void MainWindow::on_pBtn_stopRead_clicked() { plotTimer->stop(); emit sig_stopReadData(); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。