QModbusRtuSerialMaster run in other threads.
-
Hi. I get message "QBasicTimer::start: Timers cannot be started from another thread" when class with member QModbusRtuSerialMaster moved to thread and modbus request was sended. I start internal checkTimer and every ten seconds send modbus requests to my device.
No message if it isn't moved to another thread. Can anybody help?Some fragments of my code
FuelCounterHandler * fchl=new FuelCounterHandler(fsi); fchl->moveToThread(thread_FuelCounters); connect(thread_FuelCounters,SIGNAL(started()),fchl,SLOT(start())); thread_FuelCounters->start();
class FuelCounterHandler : public QObject { Q_OBJECT public: explicit FuelCounterHandler(FuelSystemInfo *fsi, QObject *parent = nullptr); ... public slots: void start(); private: FuelSystemInfo *fsi; QTimer *checkTimer; QModbusRtuSerialMaster *modbusMasterDevice; ... void sendRequest(quint8 slaveAdress); private slots: void readyRead(); ... };
void FuelCounterHandler::sendRequest(quint8 slaveAdress){ QModbusReply *reply=nullptr; reply=modbusMasterDevice->sendReadRequest(QModbusDataUnit(QModbusDataUnit::HoldingRegisters,0,4),slaveAdress); if(reply){ if (!reply->isFinished()){ connect(reply, SIGNAL(finished()), this, SLOT(readyRead())); } else delete reply; // broadcast replies return immediately } else { // errorhandler() } }
void FuelCounterHandler::readyRead(){ auto reply = qobject_cast<QModbusReply *>(sender()); if (!reply) return; if (reply->error() == QModbusDevice::NoError) { // do somthing() } else{ // error handler() } reply->deleteLater(); }
-
HI @sol007 and welcome
please show us the instantiation of
checkTimer
andmodbusMasterDevice
as well as how you do the threading exactly.Also information about the used Qt Version is vital!
That said, why do you Thread it ? You use the non blocking api of QModbusRtuSerial anyway
-
@J-Hilk
Thank you for answer.
I use Qt version 5.13.2, minGW 7.3 32bit compiler.FuelCounterHandler::FuelCounterHandler(FuelSystemInfo*fsi, QObject *parent) : QObject(parent),fsi(fsi) { checkTimer=new QTimer(this); checkTimer->setSingleShot(false); connect(checkTimer,SIGNAL(timeout()),this,SLOT(readCountersValues())); modbusMasterDevice= new QModbusRtuSerialMaster(this); modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,"COM2"); modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,QSerialPort::Baud9600); modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,QSerialPort::Data8); modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QSerialPort::NoParity); modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QSerialPort::OneStop); modbusMasterDevice->setTimeout(1000); modbusMasterDevice->setNumberOfRetries(5); }
@J-Hilk said in QModbusRtuSerialMaster run in other threads.:
That said, why do you Thread it ? You use the non blocking api of QModbusRtuSerial anyway
Is it mean that QModbuRtuSerial will never block GUI?
-
@sol007 said in QModbusRtuSerialMaster run in other threads.:
Is it mean that QModbuRtuSerial will never block GUI?
Yes. That's what signals&slots are for. Except you use
waitFor...
function, that is.Regards
-
I don't use any waitFor...() methods in the thread.
therefore, unless you do infinite loops while processing the data, QModbusRtuSerialMaster will never block your ui
I use Qt version 5.13.2, minGW 7.3 32bit compiler.
That should work fine, older versions, even LTS ones have some modbus trouble 🤷♂️
//block of code
This actually still does not show us where you do your threading
-
@J-Hilk said in QModbusRtuSerialMaster run in other threads.:
This actually still does not show us where you do your threading
I wrote a simple console application that displays the problem.
file main.cpp:#include <QCoreApplication> #include <QThread> #include "mdb_handler.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QThread *thread=new QThread(); MDB_handler *m_handler=new MDB_handler(); m_handler->moveToThread(thread); QObject::connect(thread,SIGNAL(started()),m_handler,SLOT(start())); thread->start(); qDebug()<<"Main threadId="<<QThread::currentThreadId(); return a.exec(); }
file mdb_handler.h
#ifndef MDB_HANDLER_H #define MDB_HANDLER_H #include <QObject> #include <QDebug> #include <QTimer> #include <QModbusDevice> #include <QModbusDataUnit> #include <QModbusRtuSerialMaster> #include <QSerialPort> #include <QVariant> class MDB_handler:public QObject { Q_OBJECT private: QTimer *checkTimer; QModbusRtuSerialMaster *modbusMasterDevice; public: explicit MDB_handler(QObject * parent=nullptr); ~MDB_handler(); public slots: void start(); void sendrequest(); void readyRead(); }; #endif // MDB_HANDLER_H
file mdb_hadler.cpp
#include "mdb_handler.h" #include <QThread> MDB_handler::MDB_handler(QObject *parent) : QObject(parent) { checkTimer =new QTimer(this); checkTimer->setSingleShot(false); modbusMasterDevice=new QModbusRtuSerialMaster(this); modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,QVariant("COM2")); modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,QVariant(QSerialPort::Baud9600)); modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,QVariant(QSerialPort::Data8)); modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,QVariant(QSerialPort::NoParity)); modbusMasterDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,QVariant(QSerialPort::OneStop)); modbusMasterDevice->setTimeout(1000); modbusMasterDevice->setNumberOfRetries(5); connect(checkTimer,SIGNAL(timeout()),this,SLOT(sendrequest())); } void MDB_handler::start(){ qDebug()<<"MDB_handler's threadId="<<QThread::currentThreadId(); if(modbusMasterDevice->connectDevice()){ checkTimer->start(10000); } else {qDebug()<<"Connect failed: "+ modbusMasterDevice->errorString();} } void MDB_handler::sendrequest(){ QModbusReply *reply=nullptr; reply=modbusMasterDevice->sendReadRequest(QModbusDataUnit(QModbusDataUnit::HoldingRegisters,0,1),1); if(reply){ if (!reply->isFinished()) connect(reply, SIGNAL(finished()), this, SLOT(readyRead())); else delete reply; // broadcast replies return immediately } else { qDebug()<<modbusMasterDevice->errorString(); } } void MDB_handler::readyRead(){ auto reply = qobject_cast<QModbusReply *>(sender()); if (!reply) return; if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); qDebug()<<"Value of register is "<<unit.value(0); } else{ qDebug()<<(QString("Read response error: %1 (code: 0x%2)"). arg(reply->errorString()). arg(reply->error(), -1, 16));} reply->deleteLater(); } MDB_handler::~MDB_handler(){ checkTimer->stop(); modbusMasterDevice->disconnectDevice(); delete modbusMasterDevice; delete checkTimer; }
Output console result is:
"Main thread_ID= 0x600 MDB_handler's thread_ID= 0x504 QBasicTimer::start: Timers cannot be started from another thread Value of register is 1402"
If MDB_handler run in another thread I will never get timeout error;
If I run MDB_handler in main thread I don't get message about QBasicTimer!I think that QBasicTimer starts for timeout.
Now I do not need to send requests in a separate thread.
In future I will develop a system which needs sending requests in a loop.
I think it makes sense to move the QModbusRtuSerialMaster to a separate thread -
@sol007 I found the same error, and I solved this error by modify the qserialbus source code!
in the file qmodbusrtuserialmaster_p.hclass Timer : public QObject { Q_OBJECT public: Timer() = default; Timer(QObject* parent = nullptr):QObject(parent) {} //++++ ... }; void setupSerialPort() { Q_Q(QModbusRtuSerialMaster); m_responseTimer = new Timer(q); m_serialPort = new QSerialPort(q); //++++ ... } Timer *m_responseTimer = nullptr; //Timer m_responseTimer QByteArray m_responseBuffer;
The reason is when you move modbus master to another thread using moveToThread, only the modbus master’s children will be move to the new thread, but the object "m_responseTimer" has no parent, it's still in the thread who new the modbus master, that's why the error occured when modbus send timeout. so I modify the source code, also you can solve the error though new modbus master in the new thread.