diff options
author | Alex Blasche <alexander.blasche@qt.io> | 2018-02-28 11:02:23 +0100 |
---|---|---|
committer | Alex Blasche <alexander.blasche@qt.io> | 2018-10-23 12:19:21 +0000 |
commit | 682c5ca041483b7d3e8994e7fac5bcab6327e5c2 (patch) | |
tree | f9ee089b514cfbec7cc811079eca1a6bea5c07d6 | |
parent | c9b9d9ed19566aa6e0a6d6a54bd2e4b819c8c48b (diff) |
Permit dropping of RTU slave request buffer under certain scenarios
If there is a wrong character in between individual modbus PDUs the
request buffer becomes longer and longer w/o ever being cleared.
As a consequence the only possible way to recover is to reset the RTU slave.
The patch ensures that request buffer processing waits for chunked PDUs
(due to low baud rates) while at the same time does not block if it
contains garbage. The timer is based on the modbus spec which specifies that
every modbus package is separated by at least 3.5 chars. Using the current baud
rate of the serial port permits the exact caluclation of the implied
delay/timeout.
For baud rates above 19200 the timeout drops below 2 ms which is not
measurable by Qt anymore. Furthermore the modbus spec specifies a minimal
delay of 1.75 ms as well. Hence the timeout is bound to at least 2ms.
Fixes: QTBUG-59286
Change-Id: Ie43ff3f5819519abe2081757150d367820d50020
Reviewed-by: Karsten Heimrich <karsten.heimrich@qt.io>
-rw-r--r-- | src/serialbus/qmodbusrtuserialslave_p.h | 24 |
1 files changed, 24 insertions, 0 deletions
diff --git a/src/serialbus/qmodbusrtuserialslave_p.h b/src/serialbus/qmodbusrtuserialslave_p.h index 3a6d4fe..be9cf10 100644 --- a/src/serialbus/qmodbusrtuserialslave_p.h +++ b/src/serialbus/qmodbusrtuserialslave_p.h @@ -39,7 +39,9 @@ #include <QtCore/qbytearray.h> #include <QtCore/qdebug.h> +#include <QtCore/qelapsedtimer.h> #include <QtCore/qloggingcategory.h> +#include <QtCore/qmath.h> #include <QtSerialBus/qmodbusrtuserialslave.h> #include <QtSerialPort/qserialport.h> @@ -73,6 +75,21 @@ public: m_serialPort = new QSerialPort(q); QObject::connect(m_serialPort, &QSerialPort::readyRead, [this]() { + + if (m_interFrameTimer.isValid() + && m_interFrameTimer.elapsed() > m_interFrameDelayMilliseconds + && !m_requestBuffer.isEmpty()) { + // This permits response buffer clearing if it contains garbage + // but still permits cases where very slow baud rates can cause + // chunked and delayed packets + qCDebug(QT_MODBUS_LOW) << "(RTU server) Dropping older ADU fragments due to larger than 3.5 char delay (expected:" + << m_interFrameDelayMilliseconds << ", max:" + << m_interFrameTimer.elapsed() << ")"; + m_requestBuffer.clear(); + } + + m_interFrameTimer.start(); + const int size = m_serialPort->size(); m_requestBuffer += m_serialPort->read(size); @@ -330,12 +347,19 @@ public: m_serialPort->setStopBits(m_stopBits); } + // for calulcation details see + // QModbusRtuSerialMasterPrivate::calculateInterFrameDelay() + m_interFrameDelayMilliseconds = qMax(m_interFrameDelayMilliseconds, + qCeil(3500. / (qreal(m_baudRate) / 11.))); + m_requestBuffer.clear(); } QByteArray m_requestBuffer; bool m_processesBroadcast = false; QSerialPort *m_serialPort = nullptr; + QElapsedTimer m_interFrameTimer; + int m_interFrameDelayMilliseconds = 2; }; QT_END_NAMESPACE |