diff options
author | Karsten Heimrich <karsten.heimrich@theqtcompany.com> | 2016-03-09 14:52:06 +0100 |
---|---|---|
committer | Alex Blasche <alexander.blasche@theqtcompany.com> | 2016-03-09 13:55:14 +0000 |
commit | 63e40c9f8ac12e280ae89e938916d5a65bdda8c2 (patch) | |
tree | 4e37c9646da6ff7f91f9e74893b6094847536627 | |
parent | d0c415d0ac9d59caf309452d2bfd7d3927fb8bba (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.cpp | 32 | ||||
-rw-r--r-- | src/serialbus/qmodbusrtuserialmaster.h | 3 | ||||
-rw-r--r-- | src/serialbus/qmodbusrtuserialmaster_p.h | 47 | ||||
-rw-r--r-- | tests/auto/auto.pro | 3 | ||||
-rw-r--r-- | tests/auto/qmodbusrtuserialmaster/qmodbusrtuserialmaster.pro | 8 | ||||
-rw-r--r-- | tests/auto/qmodbusrtuserialmaster/tst_qmodbusrtuserialmaster.cpp | 62 |
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" |