QModbus in a Thread
-
On my programm I have a worker thread (QThread) that is independent from the GUI-thread which is collecting data from an RS232 device and a modbus (RS485) device. While RS232 is easy to handle, I have a problem with the modbus device since QModbus is obviously running in a separate thread. I have programmed it like the modbus example that comes with the QT installation, so I send a read request and connect the reply with a slot which takes care of picking up the data. Currently I'm waiting for the answer using this code:
do { QEventLoop(this).processEvents(); } while (!answerReceived);
But this seems to be an ugly hack, so I get tons of such error messages in QTCreator:
QBasicTimer::start: Timers cannot be started from another thread
What would be the proper way to implement this?
-
I've tried these two suggestions from here, but I still get tons of these timer errors:
-
Here the full code of the involved functions:
void Worker::requestValue(int address, int length) { answerReceived = false; qDebug() << "Read values"; read(address, length); do { QEventLoop(this).processEvents(); } while (!answerReceived); } void Worker::read(int address, int length) { if (!modbusDevice) return; if (auto *reply = modbusDevice->sendReadRequest(readRequest(address, length), 10)) { if (!reply->isFinished()) connect(reply, &QModbusReply::finished, this, &Worker::onReceiveValues); else delete reply; // broadcast replies return immediately } else { qDebug() << tr("Read error: ") + modbusDevice->errorString(); } } void Worker::onReceiveValues() { auto reply = qobject_cast<QModbusReply *>(sender()); if (!reply) return; if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); QVector<quint16> receivedValues = unit.values(); switch(answerType) { case AnswerType::Flow: { prominentPumvValues.flow = processFloatValue(receivedValues); } break; case AnswerType::Stroke: { prominentPumvValues.stroke = processFloatValue(receivedValues); } break; case AnswerType::Frequency: prominentPumvValues.frequency = receivedValues.at(0); break; case AnswerType::SerialNumber: { prominentPumvValues.serialNumber = processString(receivedValues); } default: break; } } else if (reply->error() == QModbusDevice::ProtocolError) { qDebug() << tr("Read response error: %1 (Mobus exception: 0x%2)"). arg(reply->errorString()). arg(reply->rawResult().exceptionCode(), -1, 16); } else { qDebug() << tr("Read response error: %1 (code: 0x%2)"). arg(reply->errorString()). arg(reply->error(), -1, 16); } reply->deleteLater(); answerReceived = true; }
I've found this thread here: https://forum.qt.io/topic/82819/qbasictimer-start-timers-cannot-be-started-from-another-thread-issue/10 and modified the read function as follows:
void Worker::read(int address, int length) { if (!modbusDevice) return; if (auto *reply = modbusDevice->sendReadRequest(readRequest(address, length), 10)) { if (!reply->isFinished()) { qDebug() << "Reply not finished"; qDebug() << "Connect: " << connect(reply, &QModbusReply::finished, [reply, this]() { qDebug() << "connect"; if(reply->error() == QModbusClient::NoError) { const QModbusDataUnit unit = reply->result(); receivedValues = unit.values(); qDebug() << "Lambda: " << receivedValues.length(); } else qDebug() << "Reply error"; reply->deleteLater(); qDebug() << "Bye"; }); qDebug() << "Ade"; } else { qDebug() << "Reply finished"; delete reply; // broadcast replies return immediately } } else { qDebug() << tr("Read error: ") + modbusDevice->errorString(); } qDebug() <<receivedValues[0] << receivedValues[1]; }
The answer of connect is true but so it looks the code inside the lambda is not executed, there is no output of the qDebug() functions inside the lambda