summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@qt.io>2018-02-28 11:02:23 +0100
committerAlex Blasche <alexander.blasche@qt.io>2018-10-23 12:19:21 +0000
commit682c5ca041483b7d3e8994e7fac5bcab6327e5c2 (patch)
treef9ee089b514cfbec7cc811079eca1a6bea5c07d6
parentc9b9d9ed19566aa6e0a6d6a54bd2e4b819c8c48b (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.h24
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