Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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:

    https://stackoverflow.com/questions/5610785/how-to-tell-qthread-to-wait-until-work-is-done-and-then-finish



  • 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


Log in to reply