summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarsten Heimrich <karsten.heimrich@theqtcompany.com>2016-03-09 14:52:06 +0100
committerAlex Blasche <alexander.blasche@theqtcompany.com>2016-03-09 13:55:14 +0000
commit63e40c9f8ac12e280ae89e938916d5a65bdda8c2 (patch)
tree4e37c9646da6ff7f91f9e74893b6094847536627
parentd0c415d0ac9d59caf309452d2bfd7d3927fb8bba (diff)
Add API to set and retrieve the Modbus RTU inter frame delay.
Change-Id: Id09913509802c74b1a389d7a83a1136bbeccbaf6 Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
-rw-r--r--src/serialbus/qmodbusrtuserialmaster.cpp32
-rw-r--r--src/serialbus/qmodbusrtuserialmaster.h3
-rw-r--r--src/serialbus/qmodbusrtuserialmaster_p.h47
-rw-r--r--tests/auto/auto.pro3
-rw-r--r--tests/auto/qmodbusrtuserialmaster/qmodbusrtuserialmaster.pro8
-rw-r--r--tests/auto/qmodbusrtuserialmaster/tst_qmodbusrtuserialmaster.cpp62
6 files changed, 135 insertions, 20 deletions
diff --git a/src/serialbus/qmodbusrtuserialmaster.cpp b/src/serialbus/qmodbusrtuserialmaster.cpp
index afdcac9..c8697cc 100644
--- a/src/serialbus/qmodbusrtuserialmaster.cpp
+++ b/src/serialbus/qmodbusrtuserialmaster.cpp
@@ -76,6 +76,38 @@ QModbusRtuSerialMaster::~QModbusRtuSerialMaster()
}
/*!
+ \since 5.7
+
+ Returns the amount of \a microseconds for the silent interval between two
+ consecutive Modbus messages.
+
+ \sa setInterFrameDelay()
+*/
+int QModbusRtuSerialMaster::interFrameDelay() const
+{
+ Q_D(const QModbusRtuSerialMaster);
+ return d->m_interFrameDelayMilliseconds * 1000;
+}
+
+/*!
+ \since 5.7
+
+ Sets the amount of \a microseconds for the silent interval between two
+ consecutive Modbus messages. By default, the class implementation will use
+ a pre-calculated value according to the Modbus specification. A active or
+ running connection is not affected by such delay changes.
+
+ \note If \a microseconds is set to -1 or \a microseconds is less than the
+ pre-calculated delay then this pre-calculated value is used as frame delay.
+*/
+void QModbusRtuSerialMaster::setInterFrameDelay(int microseconds)
+{
+ Q_D(QModbusRtuSerialMaster);
+ d->m_interFrameDelayMilliseconds = qCeil(qreal(microseconds) / 1000.);
+ d->calculateInterFrameDelay();
+}
+
+/*!
\internal
*/
QModbusRtuSerialMaster::QModbusRtuSerialMaster(QModbusRtuSerialMasterPrivate &dd, QObject *parent)
diff --git a/src/serialbus/qmodbusrtuserialmaster.h b/src/serialbus/qmodbusrtuserialmaster.h
index 7f879f5..ebdf21f 100644
--- a/src/serialbus/qmodbusrtuserialmaster.h
+++ b/src/serialbus/qmodbusrtuserialmaster.h
@@ -52,6 +52,9 @@ public:
explicit QModbusRtuSerialMaster(QObject *parent = Q_NULLPTR);
~QModbusRtuSerialMaster();
+ int interFrameDelay() const;
+ void setInterFrameDelay(int microseconds);
+
protected:
QModbusRtuSerialMaster(QModbusRtuSerialMasterPrivate &dd, QObject *parent = Q_NULLPTR);
diff --git a/src/serialbus/qmodbusrtuserialmaster_p.h b/src/serialbus/qmodbusrtuserialmaster_p.h
index c3b1b5a..43fe630 100644
--- a/src/serialbus/qmodbusrtuserialmaster_p.h
+++ b/src/serialbus/qmodbusrtuserialmaster_p.h
@@ -157,7 +157,7 @@ public:
m_state = Schedule; // reschedule, even if empty
m_serialPort->clear(QSerialPort::AllDirections);
- QTimer::singleShot(m_timeoutThreeDotFiveMs, [this]() { processQueue(); });
+ QTimer::singleShot(m_interFrameDelayMilliseconds, [this]() { processQueue(); });
});
using TypeId = void (QSerialPort::*)(QSerialPort::SerialPortError);
@@ -223,6 +223,28 @@ public:
});
}
+ /*!
+ According to the Modbus specification, in RTU mode message frames
+ are separated by a silent interval of at least 3.5 character times.
+ Calculate the timeout if we are less than 19200 baud, use a fixed
+ timeout for everything equal or greater than 19200 baud.
+ If the user set the timeout to be longer than the calculated one,
+ we'll keep the user defined.
+ */
+ void calculateInterFrameDelay() {
+ // The spec recommends a timeout value of 1.750 msec. Without such
+ // precise single-shot timers use a approximated value of 1.750 msec.
+ int delayMilliSeconds = 2;
+ if (m_baudRate < 19200) {
+ // Example: 9600 baud, 11 bit per packet -> 872 char/sec
+ // so: 1000 ms / 872 char = 1.147 ms/char * 3.5 character
+ // Always round up because the spec requests at least 3.5 char.
+ delayMilliSeconds = qCeil(3500. / (qreal(m_baudRate) / 11.));
+ }
+ if (m_interFrameDelayMilliseconds < delayMilliSeconds)
+ m_interFrameDelayMilliseconds = delayMilliSeconds;
+ }
+
void setupEnvironment() {
if (m_serialPort) {
m_serialPort->clear();
@@ -233,20 +255,7 @@ public:
m_serialPort->setStopBits(m_stopBits);
}
- // According to the Modbus specification, in RTU mode message frames
- // are separated by a silent interval of at least 3.5 character times.
- // Calculate the timeout if we are less than 19200 baud, use a fixed
- // timeout for everything equal or greater than 19200 baud.
- if (m_baudRate < 19200) {
- // Example: 9600 baud, 11 bit per packet -> 872 char/sec
- // so: 1000 ms / 872 char = 1.147 ms/char * 3.5 character
- // Always round up because the spec requests at least 3.5 char.
- m_timeoutThreeDotFiveMs = qCeil(3500. / (qreal(m_baudRate) / 11.));
- } else {
- // The spec recommends a timeout value of 1.750 msec. Without such
- // precise single-shot timers use a approximated value of 1.750 msec.
- m_timeoutThreeDotFiveMs = 2;
- }
+ calculateInterFrameDelay();
responseBuffer.clear();
m_state = QModbusRtuSerialMasterPrivate::Idle;
@@ -255,7 +264,7 @@ public:
void scheduleNextRequest() {
m_state = Schedule;
m_serialPort->clear(QSerialPort::AllDirections);
- QTimer::singleShot(m_timeoutThreeDotFiveMs, [this]() { processQueue(); });
+ QTimer::singleShot(m_interFrameDelayMilliseconds, [this]() { processQueue(); });
}
QModbusReply *enqueueRequest(const QModbusRequest &request, int serverAddress,
@@ -323,7 +332,7 @@ public:
scheduleNextRequest();
} else {
m_serialPort->clear(QSerialPort::AllDirections);
- QTimer::singleShot(m_timeoutThreeDotFiveMs, [writeAdu]() { writeAdu(); });
+ QTimer::singleShot(m_interFrameDelayMilliseconds, [writeAdu]() { writeAdu(); });
}
} else {
qCDebug(QT_MODBUS) << "(RTU client) Send successful:" << m_current.requestPdu;
@@ -346,7 +355,7 @@ public:
} else {
m_state = Send;
m_serialPort->clear(QSerialPort::AllDirections);
- QTimer::singleShot(m_timeoutThreeDotFiveMs, [this, writeAdu]() { writeAdu(); });
+ QTimer::singleShot(m_interFrameDelayMilliseconds, [this, writeAdu]() { writeAdu(); });
}
break;
@@ -384,7 +393,7 @@ public:
QQueue<QueueElement> m_queue;
QSerialPort *m_serialPort = Q_NULLPTR;
- int m_timeoutThreeDotFiveMs = 2; // A approximated value of 1.750 msec.
+ int m_interFrameDelayMilliseconds = 2; // A approximated value of 1.750 msec.
};
QT_END_NAMESPACE
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 838eb53..2542d65 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -11,7 +11,8 @@ SUBDIRS += cmake \
qmodbusserver \
qmodbuscommevent \
qmodbusadu \
- qmodbusdeviceidentification
+ qmodbusdeviceidentification \
+ qmodbusrtuserialmaster
qcanbus.depends += plugins
qcanbusdevice.depends += plugins
diff --git a/tests/auto/qmodbusrtuserialmaster/qmodbusrtuserialmaster.pro b/tests/auto/qmodbusrtuserialmaster/qmodbusrtuserialmaster.pro
new file mode 100644
index 0000000..3125a27
--- /dev/null
+++ b/tests/auto/qmodbusrtuserialmaster/qmodbusrtuserialmaster.pro
@@ -0,0 +1,8 @@
+CONFIG -= app_bundle
+CONFIG += testcase c++11
+
+QT = core testlib serialbus
+
+TARGET = tst_qmodbusrtuserialmaster
+SOURCES += tst_qmodbusrtuserialmaster.cpp
+
diff --git a/tests/auto/qmodbusrtuserialmaster/tst_qmodbusrtuserialmaster.cpp b/tests/auto/qmodbusrtuserialmaster/tst_qmodbusrtuserialmaster.cpp
new file mode 100644
index 0000000..589ec46
--- /dev/null
+++ b/tests/auto/qmodbusrtuserialmaster/tst_qmodbusrtuserialmaster.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtSerialBus/qmodbusrtuserialmaster.h>
+#include <QtSerialPort/qserialport.h>
+
+#include <QtTest/QtTest>
+
+class tst_QModbusRtuSerialMaster : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void testInterFrameDelay()
+ {
+ QModbusRtuSerialMaster qmrsm;
+ QCOMPARE(qmrsm.interFrameDelay(), 2000);
+ qmrsm.setInterFrameDelay(1000);
+ QCOMPARE(qmrsm.interFrameDelay(), 2000);
+ qmrsm.setInterFrameDelay(3000);
+ QCOMPARE(qmrsm.interFrameDelay(), 3000);
+ qmrsm.setInterFrameDelay(-1);
+ QCOMPARE(qmrsm.interFrameDelay(), 2000);
+ }
+};
+
+QTEST_MAIN(tst_QModbusRtuSerialMaster)
+
+#include "tst_qmodbusrtuserialmaster.moc"