Modbus RTU: readyRead() not called
-
I'm trying to "port" the example of Modbus master to my application.
Here some relevant code:QModbusClient *_modbus; bool ModbusMaster::open(QString port) { _modbus = new QModbusRtuSerialMaster(this); _modbus->setConnectionParameter(QModbusDevice::SerialPortNameParameter, port); _modbus->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity); _modbus->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud57600); _modbus->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8); _modbus->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop); _modbus->setTimeout(2000); _modbus->setNumberOfRetries(3); return _modbus->connectDevice(); } bool ModbusMaster::read(QModbusDataUnit::RegisterType type, int startAddress, quint16 count) { if (!_modbus) return false; if (_modbus->state() != QModbusDevice::ConnectedState) return false; qDebug() << "Read" << startAddress; QModbusDataUnit req(type, startAddress, count); if (auto *reply = _modbus->sendReadRequest(req, 1)) { qDebug() << reply->isFinished(); if (!reply->isFinished()) connect(reply, &QModbusReply::finished, this, &ModbusMaster::readyRead); else readReady(); return true; } return false; } void ModbusMaster::readyRead () { qDebug() << "readyRead executed!"; auto reply = qobject_cast<QModbusReply *>(sender()); if (!reply) return; reply->deleteLater(); if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); // do something } else if (reply->error() == QModbusDevice::ProtocolError) qDebug() << reply->errorString(); else qDebug() << reply->errorString(); // call next reading emit readFinished(); }
but it seems even if received data
readyRead()
is not called, example:Debug: read 495 Debug: false Debug: (RTU client) Sent Serial PDU: 0x0301ef0001 Debug: (RTU client) Sent Serial ADU: 0x010301ef0001b403 Debug: (RTU client) Response buffer: "0103020000b844" Debug: (RTU client) Received ADU: "0103020000b844" Debug: (RTU client) Send successful: 0x0301ef0001
after few seconds I got a timeout from my timer. As far as I understand the response is actually received, but the
readyRead()
slot is not called.Is there something obvious I'm missing?
the QModbusRtuSerialMaster has also errors that can be emitted. And those differ from the reply, check those also.
connect(_modbus, & QModbusRtuSerialMaster::errorOccurred, this, [](QModbusDevice::Error error)->void{ qDebug() << error;});
-
the QModbusRtuSerialMaster has also errors that can be emitted. And those differ from the reply, check those also.
connect(_modbus, & QModbusRtuSerialMaster::errorOccurred, this, [](QModbusDevice::Error error)->void{ qDebug() << error;});
-
@J.Hilk You're right. Added and tried. Whenever the "hang" happens also this slot shows nothing, unfortunately.
-
@Mark81 how much time passes between open and the first read request ?
I noticed, on Windows, that when I call immediately after opening the device the first data , then most of the time nothing happens at all.
@J.Hilk adding a small delay seems to drastically improve the situation. Occasionally it still happens that the debug messages show a received answer but the slot is not called. That's weird because I can understand if some reasons the answer cannot be received, but this is not the case...
-
UPDATE
I think (not sure, though) I noticed something interesting.
When it works, the debug output from qt.modbus is like this:qt.modbus: (RTU client) Sent Serial PDU: 0x0304270001 qt.modbus.lowlevel: (RTU client) Sent Serial ADU: 0x0103042700013531 qt.modbus: (RTU client) Send successful: 0x0304270001 qt.modbus.lowlevel: (RTU client) Response buffer: "01030200017984" qt.modbus: (RTU client) Received ADU: "01030200017984"
and my slot is correctly called. Instead, when the problem arises the output is like the one reported in the first post or even this one:
qt.modbus: (RTU client) Sent Serial PDU: 0x0304180002 qt.modbus.lowlevel: (RTU client) Sent Serial ADU: 0x010304180002453c qt.modbus.lowlevel: (RTU client) Response buffer: "01030400004080ca53" qt.modbus: (RTU client) Received ADU: "01030400004080ca53" qt.modbus: (RTU client) Send successful: 0x0304180002 qt.modbus: (RTU client) Receive timeout: 0x0304180002 qt.modbus: (RTU client) Sent Serial PDU: 0x0304180002 qt.modbus.lowlevel: (RTU client) Sent Serial ADU: 0x010304180002453c qt.modbus.lowlevel: (RTU client) Response buffer: "01030400004080ca53" qt.modbus: (RTU client) Received ADU: "01030400004080ca53" qt.modbus: (RTU client) Send successful: 0x0304180002 qt.modbus: (RTU client) Receive timeout: 0x0304180002
Here my slot is not called.
I see that every time this happens the "Response buffer" line comes before the "Send successful" one.
I'm looking at the code but I don't understand if this might be a problem.(Sorry for the outdated link but I have difficult to find the actual source code of Qt5 - btw I'm using Qt5.11.1)
-
Perhaps I found it here https://code.woboq.org/qt5/qtserialbus/src/serialbus/qmodbusrtuserialmaster_p.h.html#154:
if (m_state != State::Receive) { qCDebug(QT_MODBUS) << "(RTU server) Ignoring response due to non receive state"; return; }
but https://code.woboq.org/qt5/qtserialbus/src/serialbus/qmodbusrtuserialmaster_p.h.html#351:
qCDebug(QT_MODBUS) << "(RTU client) Send successful:" << m_current.requestPdu; m_state = Receive;
So if the state is not set to
Receive
it discard the response. Anyway I didn't see the debug output "Ignoring response...". -
UPDATE
I think (not sure, though) I noticed something interesting.
When it works, the debug output from qt.modbus is like this:qt.modbus: (RTU client) Sent Serial PDU: 0x0304270001 qt.modbus.lowlevel: (RTU client) Sent Serial ADU: 0x0103042700013531 qt.modbus: (RTU client) Send successful: 0x0304270001 qt.modbus.lowlevel: (RTU client) Response buffer: "01030200017984" qt.modbus: (RTU client) Received ADU: "01030200017984"
and my slot is correctly called. Instead, when the problem arises the output is like the one reported in the first post or even this one:
qt.modbus: (RTU client) Sent Serial PDU: 0x0304180002 qt.modbus.lowlevel: (RTU client) Sent Serial ADU: 0x010304180002453c qt.modbus.lowlevel: (RTU client) Response buffer: "01030400004080ca53" qt.modbus: (RTU client) Received ADU: "01030400004080ca53" qt.modbus: (RTU client) Send successful: 0x0304180002 qt.modbus: (RTU client) Receive timeout: 0x0304180002 qt.modbus: (RTU client) Sent Serial PDU: 0x0304180002 qt.modbus.lowlevel: (RTU client) Sent Serial ADU: 0x010304180002453c qt.modbus.lowlevel: (RTU client) Response buffer: "01030400004080ca53" qt.modbus: (RTU client) Received ADU: "01030400004080ca53" qt.modbus: (RTU client) Send successful: 0x0304180002 qt.modbus: (RTU client) Receive timeout: 0x0304180002
Here my slot is not called.
I see that every time this happens the "Response buffer" line comes before the "Send successful" one.
I'm looking at the code but I don't understand if this might be a problem.(Sorry for the outdated link but I have difficult to find the actual source code of Qt5 - btw I'm using Qt5.11.1)
@Mark81 said in Modbus RTU: readyRead() not called:
(Sorry for the outdated link but I have difficult to find the actual source code of Qt5 - btw I'm using Qt5.11.1)
Then you should really test with a recent version 5.12.x. There has been quite some fixes last year.
-
Update to Qt5.12.2. Now with the very same code and data after a while it crashes with this error:
ASSERT failure in processQueue: "response timer active", file qmodbusrtuserialmaster_p.h, line 302
What should one do in such a case?
@Mark81 vote for the issue and pray, like I did
https://bugreports.qt.io/browse/QTBUG-73965
also leave a comment, it helps when people state that they have the same issue.
-
@Mark81 vote for the issue and pray, like I did
https://bugreports.qt.io/browse/QTBUG-73965
also leave a comment, it helps when people state that they have the same issue.
-
@J.Hilk said in Modbus RTU: readyRead() not called:
@Mark81 vote for the issue and pray, like I did
I'll try to suggest to my customer to pray and wait a fix too.
@Mark81
you can download the source code and in qmodbusrtumaster comment that lineQObject::connect(m_serialPort, &QSerialPort;::bytesWritten, q, [this](qint64 bytes) { m_current.bytesWritten += bytes; if (m_state == Send && (m_current.bytesWritten ==m_current.adu.size()) && !m_current.reply.isNull()) { // the if conditions above are copied from processQueue() qCDebug(QT_MODBUS) << "(RTU client) Send successful(quick):" << m_current.requestPdu; m_state = Receive; m_sendTimer.stop(); // m_responseTimer.start(m_responseTimeoutDuration); comment this line } });
thats the quick and dirty fix I made, fixes the unexpected state issue