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(); }) ;
-
@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.
-
@beecksche @MartinChan When I tried the code I get an error in the line:
loop.start();
as
error: no member named 'start' in 'QEventLoop'
What can cause that ?
-
@Yunus said in About QModbus usage(How to read coils at a fixed frequecy?):
What can cause that ?
It's not start but https://doc.qt.io/qt-6/qeventloop.html#exec ...