Add a timeout to ModbusRtuReply

QModbusReply seems to never finish in some rare cases:
For instance if a request is sent and before the reply arrives, the
modbus adapter is unplugged. This change should keep upper logic
working which relies on requests finishing eventually.

Also fixes the issue that we emit finished() multiple times if
QModbusClient retries multiple times and emits error() for each
attempt. We're only interested in a single result anyways.
This commit is contained in:
Michael Zanetti 2022-04-08 14:30:41 +02:00
parent 93f733311f
commit b2cc7ac769
3 changed files with 27 additions and 70 deletions

View File

@ -238,9 +238,10 @@ ModbusRtuReply *ModbusRtuMasterImpl::readCoil(int slaveAddress, int registerAddr
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, size); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, size);
QModbusReply *modbusReply = m_modbus->sendReadRequest(request, slaveAddress); QModbusReply *modbusReply = m_modbus->sendReadRequest(request, slaveAddress);
connect(modbusReply, &QModbusReply::finished, modbusReply, [=](){ // Cleaning up modbusReply when our reply finishes, regardless if that happened because modbusReply finished or reply timeouted
modbusReply->deleteLater(); connect(reply, &ModbusRtuReply::finished, modbusReply, &QModbusReply::deleteLater);
connect(modbusReply, &QModbusReply::finished, reply, [=](){
// Fill common reply data // Fill common reply data
reply->setFinished(true); reply->setFinished(true);
reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error())); reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error()));
@ -260,15 +261,6 @@ ModbusRtuReply *ModbusRtuMasterImpl::readCoil(int slaveAddress, int registerAddr
emit reply->finished(); emit reply->finished();
}); });
connect(modbusReply, &QModbusReply::errorOccurred, modbusReply, [=](QModbusDevice::Error error){
qCWarning(dcModbusRtu()) << "Read coil request finished with error" << error << modbusReply->errorString();
reply->setFinished(true);
reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error()));
reply->setErrorString(modbusReply->errorString());
emit reply->errorOccurred(reply->error());
emit reply->finished();
});
return qobject_cast<ModbusRtuReply *>(reply); return qobject_cast<ModbusRtuReply *>(reply);
#else #else
Q_UNUSED(slaveAddress) Q_UNUSED(slaveAddress)
@ -291,9 +283,10 @@ ModbusRtuReply *ModbusRtuMasterImpl::readDiscreteInput(int slaveAddress, int reg
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::DiscreteInputs, registerAddress, size); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::DiscreteInputs, registerAddress, size);
QModbusReply *modbusReply = m_modbus->sendReadRequest(request, slaveAddress); QModbusReply *modbusReply = m_modbus->sendReadRequest(request, slaveAddress);
connect(modbusReply, &QModbusReply::finished, modbusReply, [=](){ // Cleaning up modbusReply when our reply finishes, regardless if that happened because modbusReply finished or reply timeouted
modbusReply->deleteLater(); connect(reply, &ModbusRtuReply::finished, modbusReply, &QModbusReply::deleteLater);
connect(modbusReply, &QModbusReply::finished, reply, [=](){
// Fill common reply data // Fill common reply data
reply->setFinished(true); reply->setFinished(true);
reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error())); reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error()));
@ -313,15 +306,6 @@ ModbusRtuReply *ModbusRtuMasterImpl::readDiscreteInput(int slaveAddress, int reg
emit reply->finished(); emit reply->finished();
}); });
connect(modbusReply, &QModbusReply::errorOccurred, modbusReply, [=](QModbusDevice::Error error){
qCWarning(dcModbusRtu()) << "Read descrete inputs request finished with error" << error << modbusReply->errorString();
reply->setFinished(true);
reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error()));
reply->setErrorString(modbusReply->errorString());
emit reply->errorOccurred(reply->error());
emit reply->finished();
});
return qobject_cast<ModbusRtuReply *>(reply); return qobject_cast<ModbusRtuReply *>(reply);
#else #else
Q_UNUSED(slaveAddress) Q_UNUSED(slaveAddress)
@ -344,9 +328,10 @@ ModbusRtuReply *ModbusRtuMasterImpl::readInputRegister(int slaveAddress, int reg
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, registerAddress, size); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, registerAddress, size);
QModbusReply *modbusReply = m_modbus->sendReadRequest(request, slaveAddress); QModbusReply *modbusReply = m_modbus->sendReadRequest(request, slaveAddress);
connect(modbusReply, &QModbusReply::finished, modbusReply, [=](){ // Cleaning up modbusReply when our reply finishes, regardless if that happened because modbusReply finished or reply timeouted
modbusReply->deleteLater(); connect(reply, &ModbusRtuReply::finished, modbusReply, &QModbusReply::deleteLater);
connect(modbusReply, &QModbusReply::finished, reply, [=](){
// Fill common reply data // Fill common reply data
reply->setFinished(true); reply->setFinished(true);
reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error())); reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error()));
@ -366,15 +351,6 @@ ModbusRtuReply *ModbusRtuMasterImpl::readInputRegister(int slaveAddress, int reg
emit reply->finished(); emit reply->finished();
}); });
connect(modbusReply, &QModbusReply::errorOccurred, modbusReply, [=](QModbusDevice::Error error){
qCWarning(dcModbusRtu()) << "Read input registers request finished with error" << error << modbusReply->errorString();
reply->setFinished(true);
reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error()));
reply->setErrorString(modbusReply->errorString());
emit reply->errorOccurred(reply->error());
emit reply->finished();
});
return qobject_cast<ModbusRtuReply *>(reply); return qobject_cast<ModbusRtuReply *>(reply);
#else #else
Q_UNUSED(slaveAddress) Q_UNUSED(slaveAddress)
@ -397,9 +373,10 @@ ModbusRtuReply *ModbusRtuMasterImpl::readHoldingRegister(int slaveAddress, int r
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, size); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, size);
QModbusReply *modbusReply = m_modbus->sendReadRequest(request, slaveAddress); QModbusReply *modbusReply = m_modbus->sendReadRequest(request, slaveAddress);
connect(modbusReply, &QModbusReply::finished, modbusReply, [=](){ // Cleaning up modbusReply when our reply finishes, regardless if that happened because modbusReply finished or reply timeouted
modbusReply->deleteLater(); connect(reply, &ModbusRtuReply::finished, modbusReply, &QModbusReply::deleteLater);
connect(modbusReply, &QModbusReply::finished, reply, [=](){
// Fill common reply data // Fill common reply data
reply->setFinished(true); reply->setFinished(true);
reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error())); reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error()));
@ -419,15 +396,6 @@ ModbusRtuReply *ModbusRtuMasterImpl::readHoldingRegister(int slaveAddress, int r
emit reply->finished(); emit reply->finished();
}); });
connect(modbusReply, &QModbusReply::errorOccurred, modbusReply, [=](QModbusDevice::Error error){
qCWarning(dcModbusRtu()) << "Read holding registers request finished with error" << error << modbusReply->errorString();
reply->setFinished(true);
reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error()));
reply->setErrorString(modbusReply->errorString());
emit reply->errorOccurred(reply->error());
emit reply->finished();
});
return qobject_cast<ModbusRtuReply *>(reply); return qobject_cast<ModbusRtuReply *>(reply);
#else #else
Q_UNUSED(slaveAddress) Q_UNUSED(slaveAddress)
@ -449,12 +417,12 @@ ModbusRtuReply *ModbusRtuMasterImpl::writeCoils(int slaveAddress, int registerAd
// Create the actual modbus lib reply // Create the actual modbus lib reply
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, values.length()); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, values.length());
request.setValues(values); request.setValues(values);
QModbusReply *modbusReply = m_modbus->sendWriteRequest(request, slaveAddress); QModbusReply *modbusReply = m_modbus->sendWriteRequest(request, slaveAddress);
connect(modbusReply, &QModbusReply::finished, modbusReply, [=](){ // Cleaning up modbusReply when our reply finishes, regardless if that happened because modbusReply finished or reply timeouted
modbusReply->deleteLater(); connect(reply, &ModbusRtuReply::finished, modbusReply, &QModbusReply::deleteLater);
connect(modbusReply, &QModbusReply::finished, reply, [=](){
// Fill common reply data // Fill common reply data
reply->setFinished(true); reply->setFinished(true);
reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error())); reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error()));
@ -474,15 +442,6 @@ ModbusRtuReply *ModbusRtuMasterImpl::writeCoils(int slaveAddress, int registerAd
emit reply->finished(); emit reply->finished();
}); });
connect(modbusReply, &QModbusReply::errorOccurred, modbusReply, [=](QModbusDevice::Error error){
qCWarning(dcModbusRtu()) << "Write coil request finished with error" << error << modbusReply->errorString();
reply->setFinished(true);
reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error()));
reply->setErrorString(modbusReply->errorString());
emit reply->errorOccurred(reply->error());
emit reply->finished();
});
return qobject_cast<ModbusRtuReply *>(reply); return qobject_cast<ModbusRtuReply *>(reply);
#else #else
Q_UNUSED(slaveAddress) Q_UNUSED(slaveAddress)
@ -505,12 +464,12 @@ ModbusRtuReply *ModbusRtuMasterImpl::writeHoldingRegisters(int slaveAddress, int
// Create the actual modbus lib reply // Create the actual modbus lib reply
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, values.length()); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, values.length());
request.setValues(values); request.setValues(values);
QModbusReply *modbusReply = m_modbus->sendWriteRequest(request, slaveAddress); QModbusReply *modbusReply = m_modbus->sendWriteRequest(request, slaveAddress);
connect(modbusReply, &QModbusReply::finished, modbusReply, [=](){ // Cleaning up modbusReply when our reply finishes, regardless if that happened because modbusReply finished or reply timeouted
modbusReply->deleteLater(); connect(reply, &ModbusRtuReply::finished, modbusReply, &QModbusReply::deleteLater);
connect(modbusReply, &QModbusReply::finished, reply, [=](){
// Fill common reply data // Fill common reply data
reply->setFinished(true); reply->setFinished(true);
reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error())); reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error()));
@ -530,15 +489,6 @@ ModbusRtuReply *ModbusRtuMasterImpl::writeHoldingRegisters(int slaveAddress, int
emit reply->finished(); emit reply->finished();
}); });
connect(modbusReply, &QModbusReply::errorOccurred, modbusReply, [=](QModbusDevice::Error error){
qCWarning(dcModbusRtu()) << "Write holding register request finished with error" << error << modbusReply->errorString();
reply->setFinished(true);
reply->setError(static_cast<ModbusRtuReply::Error>(modbusReply->error()));
reply->setErrorString(modbusReply->errorString());
emit reply->errorOccurred(reply->error());
emit reply->finished();
});
return qobject_cast<ModbusRtuReply *>(reply); return qobject_cast<ModbusRtuReply *>(reply);
#else #else
Q_UNUSED(slaveAddress) Q_UNUSED(slaveAddress)

View File

@ -37,7 +37,13 @@ ModbusRtuReplyImpl::ModbusRtuReplyImpl(int slaveAddress, int registerAddress, QO
m_slaveAddress(slaveAddress), m_slaveAddress(slaveAddress),
m_registerAddress(registerAddress) m_registerAddress(registerAddress)
{ {
m_timeoutTimer.start(10000);
connect(&m_timeoutTimer, &QTimer::timeout, this, [=](){
if (!m_finished) {
m_error = TimeoutError;
emit errorOccurred(TimeoutError);
}
});
} }
bool ModbusRtuReplyImpl::isFinished() const bool ModbusRtuReplyImpl::isFinished() const

View File

@ -32,6 +32,7 @@
#define MODBUSRTUREPLYIMPL_H #define MODBUSRTUREPLYIMPL_H
#include <QObject> #include <QObject>
#include <QTimer>
#include "hardware/modbus/modbusrtureply.h" #include "hardware/modbus/modbusrtureply.h"
@ -65,7 +66,7 @@ private:
Error m_error = UnknownError; Error m_error = UnknownError;
QString m_errorString; QString m_errorString;
QVector<quint16> m_result; QVector<quint16> m_result;
QTimer m_timeoutTimer;
}; };
} }