About QModbus usage(How to read coils at a fixed frequecy?)



  • Hi everyone.

    I used to ask a question about how to read coil in forum before,and I got a good answer.

    However,I came about another question:I don't just want to read the coils a 'Singel' time,I want to read these coils with a fixed interval(like 10ms or less).

    I tried the code like following to solve these quesition,but I didn't think it's a good way to solve this question:

    #include "modclient.h"
    #include <QVariant>
    #include <QModbusDataUnit>
    #include <QDebug>
    #include <QTime>
    #include <QCoreApplication>
    modclient::modclient()
    {
        client=new QModbusTcpClient();
        client->setConnectionParameter(QModbusDevice::NetworkAddressParameter,"192.168.0.201");
        client->setConnectionParameter(QModbusDevice::NetworkPortParameter,502);
        client->setTimeout(200);
        client->setNumberOfRetries(3);
        first[0]=false;
        first[1]=false;
        control=true;
    }
    
    void modclient::connecttoModbusServer(){
        control=true;
        if(client->state()==QModbusClient::UnconnectedState){
            if(!client->connectDevice()){
                qDebug()<<QString("There's something wrong with the device");
            }else {
                qDebug()<<QString("Right connection");
                connect(client,&QModbusTcpClient::stateChanged,this,&modclient::query);
    /****************Here is the first which puzzles me***************/ 
            }
        }
    }
    
    void modclient::readready(){
        if(control){
            QModbusReply *reply=qobject_cast<QModbusReply *>(sender());
            const QModbusDataUnit result=reply->result();
            bool temp0=false,temp1=false;
            qDebug()<<QString("The value of %1 is %2").arg(1).arg(result.value(1));
            qDebug()<<QString("The value of %1 is %2").arg(3).arg(result.value(3));
            if (result.value(1)==1) temp0=true;
            if (result.value(3)==1) temp1=true;
    
            if (temp1==true && first[1]==false){
                //delay(Time2);
                emit finishedreading2();
            }
            else if(temp0 ==true && first[0]==false){
                //delay(Time1);
                emit finishedreading();
            }
            first[0]=temp0;
            first[1]=temp1;
        }
    }
    
    
    void modclient::query(){
        QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
    /****************Here is the second which puzzles me***************/ 
        while(1){
                if (auto *reply = client->sendReadRequest(readUnit,1))
                {
                    if (!reply->isFinished())
                    {
                        // connect the finished signal of the request to your read slot
                        connect(reply, &QModbusReply::finished,this,&modclient::readready);
                    }
                    else
                    {
                        delete reply; // broadcast replies return immediately
                    }
    
                }
                else
                {
                    // request error
                }
                delay(10);
            }
    
    }
    
    void modclient::Quit_Query(){
        control=false;
        //disconnect(client,&QModbusTcpClient::stateChanged,this,&modclient::query);
    }
    
    void modclient::delay(int a){
        QTime t;
        t.start();
        while(t.elapsed()<a)
        QCoreApplication::processEvents();
    }
    

    I marked two places which puzzles me:
    1)When the signal 'StateChanged' is emitted,it means the device state changed(like from unconnceted to connect,or reversed),or the coil state changed(I know its impossible but I still want to ask)?
    2)I just want to use a single QThread to do the reading(coils) job,however,it seems not recommended to use a 'infinite' while loop in the class which will be added to a QThread.So,what should I do is a better way to construct a right way to 'continously' read the coil?



  • My previous topic was here:link

    And I used to refer to the Qt ModbusClient example,nevertheless,I didn't find too much about how to read coils synchronously,but when I test the program it did execute ‘synchronously’



  • Hi @MartinChan,

    I think you want to poll to your slave like http://www.modbusdriver.com/modpoll.html

    If you want to have a synchronous API i would start a local QEventLoop after sending your request and stop this loop when the reply has finished. Sth like that:

    void modclient::readCoil()
    {
     QModbusDataUnit readUnit(QModbusDataUnit::Coils,00000,4);
     auto *reply = client->sendReadRequest(readUnit,1);
    
     QEventLoop loop;
    
     auto connection = QObject::connect(reply, &QModbusReply::finished, ()[&loop] { loop.quit(); }
    
     loop.start();
     QObject::disconnect(connection);
    
     // now you can get the result of the query and do your stuff
    
     const QModbusDataUnit result=reply->result();
     bool temp0=false,temp1=false;
     qDebug()<<QString("The value of %1 is %2").arg(1).arg(result.value(1));
     qDebug()<<QString("The value of %1 is %2").arg(3).arg(result.value(3));
     if (result.value(1)==1) temp0=true;
     ...
    
     if (polling)
       QTimer::singleShot(interval, &modclient::readCoil)
    
    }
    

    Haven't testet the code, may you have to add some security mechanism in the event loop.



  • @beecksche Thx~!And I will try it soon~



  • @beecksche Hi,Thx first and I have another little quesition .

    The code u provided,there is a line:

     auto connection = QObject::connect(reply, &QModbusReply::finished, ()[&loop] { loop.quit(); }
    

    It seems u use the lambda expression in '()[&loop] { loop.quit(); }' (I am sorry for I didn't know too much about Lambda expression...)However,it didn't work,can I just use

    auto connection=QObject::connect(reply,&QModbusReply::finished,loop.quit()); 
    

    to replace it?If not,why??



  • @MartinChan
    Oh, yes of course. Forgot that quit is a slot of the QEventLoop. Then you have to change the function:

    auto connection = QObject::connect(reply, &QModbusReply::finished, &loop, &QEventLoop::quit);
    

    Edit:
    Because of the lambda function, i mixed up the correct writing. It should be:

    auto connection = QObject::connect(reply, &QModbusReply::finished, [&loop]()
    { 
    loop.quit();
    }) ;
    

  • Moderators

    @MartinChan You cannot do it like this:

    auto connection=QObject::connect(reply,&QModbusReply::finished,loop.quit());
    

    In this case you call the method loop.quit() instead of passing a pointer to it to connect. So, do it like @beecksche suggested.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.