summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@theqtcompany.com>2016-03-02 10:46:15 +0100
committerAlex Blasche <alexander.blasche@theqtcompany.com>2016-03-02 10:47:37 +0100
commit78577b4de46555ef03c2bad0a898d73b7782f772 (patch)
treebeecf2e55f917df3561e9f5216afcdf1c8fe02de
parent246c93df987e3ee49ba18a3d50cc9d96d35b9672 (diff)
parente40343337940705ceb830f9f20a09084185ab198 (diff)
Merge remote-tracking branch 'gerrit/5.6' into 5.7
-rw-r--r--examples/serialbus/modbus/master/doc/images/modbusmaster.pngbin0 -> 14613 bytes
-rw-r--r--examples/serialbus/modbus/master/doc/src/modbusmaster.qdoc43
-rw-r--r--examples/serialbus/modbus/slave/doc/images/modbusserver.pngbin0 -> 14146 bytes
-rw-r--r--examples/serialbus/modbus/slave/doc/src/modbusslave.qdoc45
-rw-r--r--src/serialbus/doc/qtserialbus.qdocconf4
-rw-r--r--src/serialbus/doc/snippets/snippetmain.cpp18
-rw-r--r--src/serialbus/doc/src/qtserialbus-index.qdoc2
-rw-r--r--src/serialbus/qmodbuspdu.cpp143
-rw-r--r--src/serialbus/qmodbuspdu.h1
-rw-r--r--src/serialbus/qmodbusrtuserialmaster.cpp7
-rw-r--r--src/serialbus/qmodbusrtuserialmaster_p.h36
-rw-r--r--src/serialbus/qmodbusrtuserialslave.cpp30
-rw-r--r--src/serialbus/qmodbusrtuserialslave.h2
-rw-r--r--src/serialbus/qmodbusrtuserialslave_p.h21
-rw-r--r--src/serialbus/qmodbustcpserver.cpp5
-rw-r--r--tests/auto/qmodbuspdu/tst_qmodbuspdu.cpp251
16 files changed, 527 insertions, 81 deletions
diff --git a/examples/serialbus/modbus/master/doc/images/modbusmaster.png b/examples/serialbus/modbus/master/doc/images/modbusmaster.png
new file mode 100644
index 0000000..faf66c7
--- /dev/null
+++ b/examples/serialbus/modbus/master/doc/images/modbusmaster.png
Binary files differ
diff --git a/examples/serialbus/modbus/master/doc/src/modbusmaster.qdoc b/examples/serialbus/modbus/master/doc/src/modbusmaster.qdoc
new file mode 100644
index 0000000..4492bbf
--- /dev/null
+++ b/examples/serialbus/modbus/master/doc/src/modbusmaster.qdoc
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*! \example modbus/master
+ \title Modbus Master example
+
+ \brief The example implements a Modbus master application.
+
+ The example acts as Modbus master sending Modbus request via serial line
+ and TCP respectively. The shown dialog allows the definition of standard requests and
+ displays incoming responses.
+
+ \image ../images/modbusmaster.png
+
+ \include examples-run.qdocinc
+
+ The example must be used in conjunction with the \l {Modbus Slave example} or another
+ Mobus device which is either connected via TCP or Serial Bus.
+*/
diff --git a/examples/serialbus/modbus/slave/doc/images/modbusserver.png b/examples/serialbus/modbus/slave/doc/images/modbusserver.png
new file mode 100644
index 0000000..795457a
--- /dev/null
+++ b/examples/serialbus/modbus/slave/doc/images/modbusserver.png
Binary files differ
diff --git a/examples/serialbus/modbus/slave/doc/src/modbusslave.qdoc b/examples/serialbus/modbus/slave/doc/src/modbusslave.qdoc
new file mode 100644
index 0000000..1a7ae55
--- /dev/null
+++ b/examples/serialbus/modbus/slave/doc/src/modbusslave.qdoc
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*! \example modbus/slave
+ \title Modbus Slave example
+
+ \brief The example implements a Modbus slave application.
+
+ The example acts as Modbus slave. It receives standard Modbus requests,
+ adjusts its internal states based on the request and responds with the
+ appropriate reply. As such a slave is the equivalent of a Modbus server.
+
+ \image ../images/modbusserver.png
+
+ \include examples-run.qdocinc
+
+ This example must be used in conjunction with the \l {Modbus Master example}.
+ This example should be started and put into the listen state before
+ the Modbus master example is started. Subsequent interactions between the two examples
+ use the Modbus protocol.
+*/
diff --git a/src/serialbus/doc/qtserialbus.qdocconf b/src/serialbus/doc/qtserialbus.qdocconf
index 79b3de1..e78b63e 100644
--- a/src/serialbus/doc/qtserialbus.qdocconf
+++ b/src/serialbus/doc/qtserialbus.qdocconf
@@ -4,7 +4,7 @@ project = QtSerialBus
description = Qt Serial Bus Reference Documentation
version = $QT_VERSION
-examplesinstallpath = serialbus
+examplesinstallpath = qtserialbus/serialbus
qhp.projects = QtSerialBus
@@ -35,8 +35,6 @@ headerdirs += ..
sourcedirs += ..
-examplesinstallpath = serialbus
-
exampledirs += ../../../examples/serialbus \
snippets/ \
../../plugins/canbus/socketcan
diff --git a/src/serialbus/doc/snippets/snippetmain.cpp b/src/serialbus/doc/snippets/snippetmain.cpp
index 614357d..c26acfc 100644
--- a/src/serialbus/doc/snippets/snippetmain.cpp
+++ b/src/serialbus/doc/snippets/snippetmain.cpp
@@ -47,29 +47,37 @@ int main(int /*argc*/, char ** /*argv*/)
//! [Filter Examples]
QCanBusDevice::Filter filter;
+ QList<QCanBusDevice::Filter> filterList;
// filter all CAN bus packages with id 0x444 (base) or 0xXXXXX444 (extended)
filter.frameId = 0x444u;
filter.frameIdMask = 0x7FFu;
filter.format = QCanBusDevice::Filter::MatchBaseAndExtendedFormat;
filter.type = QCanBusFrame::InvalidFrame;
+ filterList.append(filter);
// filter all DataFrames with extended CAN bus package format
filter.frameId = 0x0;
filter.frameIdMask = 0x0;
filter.format = QCanBusDevice::Filter::MatchExtendedFormat;
filter.type = QCanBusFrame::DataFrame;
+ filterList.append(filter);
+ // apply filter
+ device->setConfigurationParameter(QCanBusDevice::RawFilterKey, QVariant::fromValue(filterList));
//! [Filter Examples]
//! [SocketCan Filter Example]
QList<QCanBusDevice::Filter> list;
+ QCanBusDevice::Filter f;
- filter.frameId = 0x544;
- filter.frameIdMask = 0x7FF;
- filter.format = QCanBusDevice::Filter::MatchBaseAndExtendedFormat;
- filter.type = QCanBusFrame::RemoteRequestFrame;
- list.append(filter);
+ // only accept odd numbered frame id of type remote request
+ // frame can utilize extended or base format
+ f.frameId = 0x1;
+ f.frameIdMask = 0x1;
+ f.format = QCanBusDevice::Filter::MatchBaseAndExtendedFormat;
+ f.type = QCanBusFrame::RemoteRequestFrame;
+ list.append(f);
device->setConfigurationParameter(QCanBusDevice::RawFilterKey, QVariant::fromValue(list));
device->setConfigurationParameter(QCanBusDevice::ErrorFilterKey,
diff --git a/src/serialbus/doc/src/qtserialbus-index.qdoc b/src/serialbus/doc/src/qtserialbus-index.qdoc
index da79d0e..c5136d4 100644
--- a/src/serialbus/doc/src/qtserialbus-index.qdoc
+++ b/src/serialbus/doc/src/qtserialbus-index.qdoc
@@ -86,5 +86,7 @@
\list
\li \l {CAN Bus Example}
+ \li \l {Modbus Master example}
+ \li \l {Modbus Slave example}
\endlist
*/
diff --git a/src/serialbus/qmodbuspdu.cpp b/src/serialbus/qmodbuspdu.cpp
index da17a03..2f20747 100644
--- a/src/serialbus/qmodbuspdu.cpp
+++ b/src/serialbus/qmodbuspdu.cpp
@@ -98,50 +98,77 @@ static int minimumDataSize(const QModbusPdu &pdu, Type type)
static QDataStream &pduFromStream(QDataStream &stream, QModbusPdu &pdu, Type type)
{
- QModbusPdu::FunctionCode code;
- stream >> reinterpret_cast<quint8&> (code);
- pdu.setFunctionCode(static_cast<QModbusPdu::FunctionCode> (code));
-
- int size = (type == Type::Response) ? QModbusResponse::minimumDataSize(pdu)
- : QModbusRequest::minimumDataSize(pdu);
- if (size < 0)
- pdu.setFunctionCode(QModbusResponse::Invalid);
- if (size <= 0)
+ QModbusPdu::FunctionCode code = QModbusPdu::Invalid;
+ if (stream.readRawData((char*) (&code), sizeof(quint8)) != sizeof(quint8))
return stream;
+ pdu.setFunctionCode(code);
+
+ auto needsAdditionalRead = [](QModbusPdu &pdu, int size) -> bool {
+ if (size < 0)
+ pdu.setFunctionCode(QModbusResponse::Invalid);
+ if (size <= 0)
+ return false;
+ return true;
+ };
- QByteArray data(size, Qt::Uninitialized);
- if (stream.device()->peek(data.data(), size) != size)
+ const bool isResponse = (type == Type::Response);
+ int size = isResponse ? QModbusResponse::minimumDataSize(pdu)
+ : QModbusRequest::minimumDataSize(pdu);
+ if (!needsAdditionalRead(pdu, size))
return stream;
- size = type == Type::Response ? QModbusResponse::calculateDataSize(QModbusResponse(code, data))
- : QModbusRequest::calculateDataSize(QModbusResponse(code, data));
- if (size < 0) {
- pdu.setFunctionCode(QModbusResponse::Invalid);
+ QByteArray data(size, Qt::Uninitialized);
+ if (stream.device()->peek(data.data(), data.size()) != size)
return stream;
- }
- auto readRaw = [&]() -> QDataStream& {
- stream.readRawData(data.data(), data.size());
- pdu.setData(data);
+ pdu.setData(data);
+ size = isResponse ? QModbusResponse::calculateDataSize(pdu)
+ : QModbusRequest::calculateDataSize(pdu);
+ if (!needsAdditionalRead(pdu, size))
return stream;
- };
- quint16 subCode;
- data.resize(size);
- switch (pdu.functionCode()) {
- default: return readRaw();
- case QModbusPdu::Diagnostics:
+ if (isResponse && (code == QModbusPdu::EncapsulatedInterfaceTransport)) {
+ quint8 meiType;
+ pdu.decodeData(&meiType);
+ if (meiType == EncapsulatedInterfaceTransport::ReadDeviceIdentification) {
+ int left = size, offset = 0;
+ while ((left > 0) && (size <= 252)) { // The maximum PDU data size is 252 bytes.
+ data.resize(size);
+ const int read = stream.readRawData(data.data() + offset, size - offset);
+ if ((read < 0) || (read != (size - offset))) {
+ size = 255; // bogus size
+ stream.setStatus(QDataStream::ReadCorruptData);
+ break; // error reading, bail, reset further down
+ }
+ offset += read;
+ left = QModbusResponse::calculateDataSize(QModbusResponse(code, data)) - offset;
+ size += left;
+ }
+ if ((stream.status() == QDataStream::Ok) && (size <= 252)) {
+ pdu.setData(data);
+ return stream; // early return to avoid second read
+ }
+ } else {
+ data.resize(stream.device()->size() - 1); // One byte for the function code.
+ }
+ } else if (pdu.functionCode() == QModbusPdu::Diagnostics) {
+ quint16 subCode;
pdu.decodeData(&subCode);
- if (subCode != Diagnostics::ReturnQueryData)
- return readRaw();
- // deliberately fall trough in case of ReturnQueryData
- case QModbusPdu::EncapsulatedInterfaceTransport:
- data.resize(stream.device()->size() - 1); // One byte for the function code.
- if (data.size() <= 252) // The maximum PDU data size is 252 bytes.
- return readRaw();
- break;
+ if (subCode == Diagnostics::ReturnQueryData)
+ data.resize(stream.device()->size() - 1); // One byte for the function code.
+ }
+
+ // reset what we have so far, next read might fail as well
+ pdu.setData(QByteArray());
+ pdu.setFunctionCode(QModbusPdu::Invalid);
+
+ if (data.size() <= 252) { // The maximum PDU data size is 252 bytes.
+ data.resize(size);
+ if (stream.readRawData(data.data(), data.size()) == size) {
+ pdu.setData(data);
+ pdu.setFunctionCode(code);
+ }
}
- pdu.setFunctionCode(QModbusResponse::Invalid);
return stream;
}
@@ -526,6 +553,14 @@ int QModbusRequest::calculateDataSize(const QModbusRequest &request)
if (request.dataSize() >= 1)
size = 1 /*byte count*/ + request.data()[0] /*actual bytes*/;
break;
+ case QModbusPdu::EncapsulatedInterfaceTransport: {
+ if (request.dataSize() < minimum)
+ break; // can't calculate, let's return -1 to indicate error
+ quint8 meiType;
+ request.decodeData(&meiType);
+ // ReadDeviceIdentification -> 3 == MEI type + Read device ID + Object Id
+ size = (meiType == EncapsulatedInterfaceTransport::ReadDeviceIdentification) ? 3 : minimum;
+ } break;
default:
size = minimum;
break;
@@ -621,7 +656,8 @@ int QModbusResponse::calculateDataSize(const QModbusResponse &response)
return 1;
int size = -1;
- if (QModbusResponse::minimumDataSize(response) < 0)
+ int minimum = Private::minimumDataSize(response, Private::Type::Response);
+ if (minimum < 0)
return size;
switch (response.functionCode()) {
@@ -644,8 +680,43 @@ int QModbusResponse::calculateDataSize(const QModbusResponse &response)
size = rawSize + 2; // 2 bytes size info
}
} break;
+ case QModbusPdu::EncapsulatedInterfaceTransport: {
+ if (response.dataSize() < minimum)
+ break; // can't calculate, let's return -1 to indicate error
+
+ quint8 meiType;
+ response.decodeData(&meiType);
+
+ // update size, header 6 bytes: mei type + read device id + conformity level + more follows
+ // + next object id + number of object
+ // response data part 2 bytes: + object id + object size of the first object -> 8
+ size = (meiType == EncapsulatedInterfaceTransport::ReadDeviceIdentification) ? 8 : minimum;
+ if (meiType != EncapsulatedInterfaceTransport::ReadDeviceIdentification
+ || response.dataSize() < size) {
+ break; // TODO: calculate CanOpenGeneralReference instead of break
+ }
+
+ const QByteArray data = response.data();
+ quint8 numOfObjects = quint8(data[5]);
+ quint8 objectSize = quint8(data[7]);
+
+ // 6 byte header size + (2 * n bytes fixed per object) + first object size
+ size = 6 + (2 * numOfObjects) + objectSize;
+ if ((numOfObjects == 1) || (data.size() < size))
+ break;
+
+ // header + object id + object size + second object id (9 bytes) + first object size
+ int nextSizeField = 9 + objectSize;
+ for (int i = 1; i < numOfObjects; ++i) {
+ if (data.size() <= nextSizeField)
+ break;
+ objectSize = data[nextSizeField];
+ size += objectSize;
+ nextSizeField += objectSize + 2; // object size + object id field + object size field
+ }
+ } break;
default:
- size = QModbusResponse::minimumDataSize(response);
+ size = minimum;
break;
}
return size;
diff --git a/src/serialbus/qmodbuspdu.h b/src/serialbus/qmodbuspdu.h
index 3bdb445..12fd33f 100644
--- a/src/serialbus/qmodbuspdu.h
+++ b/src/serialbus/qmodbuspdu.h
@@ -159,6 +159,7 @@ private:
}
template<typename ... Args> void encode(Args ... newData) {
+ m_data.clear();
if (sizeof...(Args)) {
QDataStream stream(&m_data, QIODevice::WriteOnly);
char tmp[1024] = { (encode(&stream, newData), void(), '0')... };
diff --git a/src/serialbus/qmodbusrtuserialmaster.cpp b/src/serialbus/qmodbusrtuserialmaster.cpp
index 17d12ad..afdcac9 100644
--- a/src/serialbus/qmodbusrtuserialmaster.cpp
+++ b/src/serialbus/qmodbusrtuserialmaster.cpp
@@ -97,12 +97,8 @@ bool QModbusRtuSerialMaster::open()
return true;
Q_D(QModbusRtuSerialMaster);
-
- d->responseBuffer.clear();
-
- d->updateSerialPortConnectionInfo();
if (d->m_serialPort->open(QIODevice::ReadWrite)) {
- d->m_serialPort->clear();
+ d->setupEnvironment();
setState(QModbusDevice::ConnectedState);
} else {
setError(d->m_serialPort->errorString(), QModbusDevice::ConnectionError);
@@ -119,7 +115,6 @@ void QModbusRtuSerialMaster::close()
return;
Q_D(QModbusRtuSerialMaster);
-
if (d->m_serialPort->isOpen())
d->m_serialPort->close();
diff --git a/src/serialbus/qmodbusrtuserialmaster_p.h b/src/serialbus/qmodbusrtuserialmaster_p.h
index 9517777..c3b1b5a 100644
--- a/src/serialbus/qmodbusrtuserialmaster_p.h
+++ b/src/serialbus/qmodbusrtuserialmaster_p.h
@@ -218,32 +218,38 @@ public:
Q_Q(QModbusRtuSerialMaster);
if (q->state() != QModbusDevice::ClosingState)
q->close();
+ m_sendTimer.stop();
+ m_responseTimer.stop();
});
}
- void updateSerialPortConnectionInfo() {
+ void setupEnvironment() {
if (m_serialPort) {
+ m_serialPort->clear();
m_serialPort->setPortName(m_comPort);
m_serialPort->setParity(m_parity);
m_serialPort->setBaudRate(m_baudRate);
m_serialPort->setDataBits(m_dataBits);
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;
- }
+ // 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;
}
+
+ responseBuffer.clear();
+ m_state = QModbusRtuSerialMasterPrivate::Idle;
}
void scheduleNextRequest() {
diff --git a/src/serialbus/qmodbusrtuserialslave.cpp b/src/serialbus/qmodbusrtuserialslave.cpp
index 41f3215..718d739 100644
--- a/src/serialbus/qmodbusrtuserialslave.cpp
+++ b/src/serialbus/qmodbusrtuserialslave.cpp
@@ -107,9 +107,8 @@ bool QModbusRtuSerialSlave::open()
return true;
Q_D(QModbusRtuSerialSlave);
- d->updateSerialPortConnectionInfo();
if (d->m_serialPort->open(QIODevice::ReadWrite)) {
- d->m_serialPort->clear();
+ d->setupEnvironment();
setState(QModbusDevice::ConnectedState);
} else {
setError(d->m_serialPort->errorString(), QModbusDevice::ConnectionError);
@@ -126,13 +125,38 @@ void QModbusRtuSerialSlave::close()
return;
Q_D(QModbusRtuSerialSlave);
-
if (d->m_serialPort->isOpen())
d->m_serialPort->close();
setState(QModbusDevice::UnconnectedState);
}
+/*!
+ \reimp
+
+ Processes the Modbus client request specified by \a request and returns a
+ Modbus response.
+
+ The Modbus function \l QModbusRequest::EncapsulatedInterfaceTransport with
+ MEI Type 13 (0x0D) CANopen General Reference is filtered out because it is
+ usually Modbus TCP or Modbus serial ASCII only.
+
+ A request to the RTU serial slave will be answered with a Modbus exception
+ response with the exception code QModbusExceptionResponse::IllegalFunction.
+*/
+QModbusResponse QModbusRtuSerialSlave::processRequest(const QModbusPdu &request)
+{
+ if (request.functionCode() == QModbusRequest::EncapsulatedInterfaceTransport) {
+ quint8 meiType;
+ request.decodeData(&meiType);
+ if (meiType == EncapsulatedInterfaceTransport::CanOpenGeneralReference) {
+ return QModbusExceptionResponse(request.functionCode(),
+ QModbusExceptionResponse::IllegalFunction);
+ }
+ }
+ return QModbusServer::processRequest(request);
+}
+
#include "moc_qmodbusrtuserialslave.cpp"
QT_END_NAMESPACE
diff --git a/src/serialbus/qmodbusrtuserialslave.h b/src/serialbus/qmodbusrtuserialslave.h
index 6a66918..9cbc5a0 100644
--- a/src/serialbus/qmodbusrtuserialslave.h
+++ b/src/serialbus/qmodbusrtuserialslave.h
@@ -59,6 +59,8 @@ protected:
bool open() Q_DECL_OVERRIDE;
void close() Q_DECL_OVERRIDE;
+
+ QModbusResponse processRequest(const QModbusPdu &request) Q_DECL_OVERRIDE;
};
QT_END_NAMESPACE
diff --git a/src/serialbus/qmodbusrtuserialslave_p.h b/src/serialbus/qmodbusrtuserialslave_p.h
index 96f3db4..213572c 100644
--- a/src/serialbus/qmodbusrtuserialslave_p.h
+++ b/src/serialbus/qmodbusrtuserialslave_p.h
@@ -37,6 +37,7 @@
#ifndef QMODBUSRTUSERIALSLAVE_P_H
#define QMODBUSRTUSERIALSLAVE_P_H
+#include <QtCore/qbytearray.h>
#include <QtCore/qdebug.h>
#include <QtCore/qloggingcategory.h>
#include <QtSerialBus/qmodbusrtuserialslave.h>
@@ -72,10 +73,10 @@ public:
m_serialPort = new QSerialPort(q);
QObject::connect(m_serialPort, &QSerialPort::readyRead, [this]() {
- Q_Q(QModbusRtuSerialSlave);
-
const int size = m_serialPort->size();
- const QModbusSerialAdu adu(QModbusSerialAdu::Rtu, m_serialPort->read(size));
+ m_requestBuffer += m_serialPort->read(size);
+
+ const QModbusSerialAdu adu(QModbusSerialAdu::Rtu, m_requestBuffer);
qCDebug(QT_MODBUS_LOW) << "(RTU server) Received ADU:" << adu.rawData().toHex();
// Index -> description
@@ -83,7 +84,7 @@ public:
// FunctionCode -> 1 byte
// FunctionCode specific content -> 0-252 bytes
// CRC -> 2 bytes
-
+ Q_Q(QModbusRtuSerialSlave);
QModbusCommEvent event = QModbusCommEvent::ReceiveEvent;
if (q->value(QModbusServer::ListenOnlyMode).toBool())
event |= QModbusCommEvent::ReceiveFlag::CurrentlyInListenOnlyMode;
@@ -120,6 +121,10 @@ public:
return;
}
+ // We received the full message, including checksum. We do not expect more bytes to
+ // arrive, so clear the buffer. All new bytes are considered part of the next message.
+ m_requestBuffer.resize(0);
+
if (!adu.matchingChecksum()) {
qCWarning(QT_MODBUS) << "(RTU server) Discarding request with wrong CRC, received:"
<< adu.checksum<quint16>() << ", calculated CRC:"
@@ -316,18 +321,22 @@ public:
});
}
- void updateSerialPortConnectionInfo() {
+ void setupEnvironment() {
if (m_serialPort) {
+ m_serialPort->clear();
m_serialPort->setPortName(m_comPort);
m_serialPort->setParity(m_parity);
m_serialPort->setBaudRate(m_baudRate);
m_serialPort->setDataBits(m_dataBits);
m_serialPort->setStopBits(m_stopBits);
}
+
+ m_requestBuffer.clear();
}
- QSerialPort *m_serialPort;
+ QByteArray m_requestBuffer;;
bool m_processesBroadcast = false;
+ QSerialPort *m_serialPort = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/serialbus/qmodbustcpserver.cpp b/src/serialbus/qmodbustcpserver.cpp
index 3c95fb5..0f9aff2 100644
--- a/src/serialbus/qmodbustcpserver.cpp
+++ b/src/serialbus/qmodbustcpserver.cpp
@@ -140,7 +140,8 @@ void QModbusTcpServer::close()
/*!
\reimp
- Processes a Modbus client \a request and returns a Modbus response.
+ Processes the Modbus client request specified by \a request and returns a
+ Modbus response.
The following Modbus function codes are filtered out as they are serial
line only according to the Modbus Application Protocol Specification 1.1b:
@@ -152,7 +153,7 @@ void QModbusTcpServer::close()
\li \l QModbusRequest::ReportServerId
\endlist
A request to the TCP server will be answered with a Modbus exception
- response with exception code QModbusExceptionResponse::IllegalFunction.
+ response with the exception code QModbusExceptionResponse::IllegalFunction.
*/
QModbusResponse QModbusTcpServer::processRequest(const QModbusPdu &request)
{
diff --git a/tests/auto/qmodbuspdu/tst_qmodbuspdu.cpp b/tests/auto/qmodbuspdu/tst_qmodbuspdu.cpp
index 1713d0d..76bfea3 100644
--- a/tests/auto/qmodbuspdu/tst_qmodbuspdu.cpp
+++ b/tests/auto/qmodbuspdu/tst_qmodbuspdu.cpp
@@ -530,10 +530,10 @@ private slots:
QTest::newRow("EncapsulatedInterfaceTransport")
<< QModbusResponse::EncapsulatedInterfaceTransport
<< QByteArray::fromHex("0e01010000030016") + "Company identification" +
- QByteArray::fromHex("010d") + "Product code" + QByteArray::fromHex("0205") +
+ QByteArray::fromHex("010c") + "Product code" + QByteArray::fromHex("0205") +
"V2.11"
<< QByteArray::fromHex("2b0e01010000030016") + "Company identification" +
- QByteArray::fromHex("010d") + "Product code" + QByteArray::fromHex("0205") +
+ QByteArray::fromHex("010c") + "Product code" + QByteArray::fromHex("0205") +
"V2.11";
QModbusExceptionResponse ex(QModbusPdu::ReadCoils, QModbusPdu::IllegalDataAddress);
@@ -557,10 +557,251 @@ private slots:
QDataStream input(pdu);
input >> response;
if (response.isException())
- QCOMPARE(QModbusPdu::FunctionCode(fc &~ QModbusPdu::ExceptionByte), response.functionCode());
+ QCOMPARE(quint8(response.functionCode()), quint8(fc &~ QModbusPdu::ExceptionByte));
else
- QCOMPARE(fc, response.functionCode());
- QCOMPARE(data, response.data());
+ QCOMPARE(response.functionCode(), fc);
+ QCOMPARE(response.data(), data);
+ }
+
+ void testEncapsulatedInterfaceTransportStreamOperator()
+ {
+ const QByteArray valid = QByteArray::fromHex("0e01010000030016") + "Company identification"
+ + QByteArray::fromHex("010c") + "Product code" + QByteArray::fromHex("0205")
+ + "V2.11";
+ {
+ QByteArray ba;
+ QDataStream output(&ba, QIODevice::ReadWrite);
+ output << QModbusResponse(QModbusResponse::EncapsulatedInterfaceTransport, valid);
+
+ QModbusResponse response;
+ QDataStream input(ba);
+ input >> response;
+ QCOMPARE(response.functionCode(), QModbusResponse::EncapsulatedInterfaceTransport);
+ QCOMPARE(response.data(), valid);
+ }
+
+ // valid embedded
+ const QByteArray suffix = QByteArray::fromHex("ffffffffff");
+ {
+ QByteArray ba;
+ QDataStream output(&ba, QIODevice::ReadWrite);
+ output << QModbusResponse(QModbusResponse::EncapsulatedInterfaceTransport, valid
+ + suffix);
+
+ QModbusResponse response;
+ QDataStream input(ba);
+ input >> response;
+ QCOMPARE(response.functionCode(), QModbusResponse::EncapsulatedInterfaceTransport);
+ QCOMPARE(response.data(), valid);
+ }
+
+ const QByteArray invalid = QByteArray::fromHex("0e01010000030016") + "Company identification"
+ + QByteArray::fromHex("01ff") + "Product code" + QByteArray::fromHex("0205")
+ + "V2.11";
+ {
+ QByteArray ba;
+ QDataStream output(&ba, QIODevice::ReadWrite);
+ output << QModbusResponse(QModbusResponse::EncapsulatedInterfaceTransport, invalid);
+
+ QModbusResponse response;
+ QDataStream input(ba);
+ input >> response;
+ QCOMPARE(response.functionCode(), QModbusResponse::Invalid);
+ QCOMPARE(response.data(), QByteArray());
+ }
+
+ const QByteArray invalidShort = QByteArray::fromHex("0e01010000030016")
+ + "Company identification" + QByteArray::fromHex("01");
+ {
+ QByteArray ba;
+ QDataStream output(&ba, QIODevice::ReadWrite);
+ output << QModbusResponse(QModbusResponse::EncapsulatedInterfaceTransport, invalidShort);
+
+ QModbusResponse response;
+ QDataStream input(ba);
+ input >> response;
+ QCOMPARE(response.functionCode(), QModbusResponse::Invalid);
+ QCOMPARE(response.data(), QByteArray());
+ }
+ }
+
+ void testCalculateDataSize()
+ {
+ // CanOpenGeneralReference
+ QModbusResponse cogr(QModbusPdu::EncapsulatedInterfaceTransport, quint8(0x0d));
+ QCOMPARE(QModbusResponse::calculateDataSize(cogr), -1);
+ cogr.encodeData(quint8(0x0d), quint8(0x00));
+ QCOMPARE(QModbusResponse::calculateDataSize(cogr), 2);
+ // TODO: fix the following once we can calculate the size for CanOpenGeneralReference
+ cogr.encodeData(quint8(0x0d), quint8(0x00), quint8(0x00));
+ QCOMPARE(QModbusResponse::calculateDataSize(cogr), 2);
+
+ // ReadDeviceIdentification
+ QModbusResponse rdi(QModbusPdu::EncapsulatedInterfaceTransport, quint8(0x0e));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), -1);
+ rdi.encodeData(quint8(0x0e), quint8(0x01));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
+
+ // single object
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x01)); // object count
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x01),
+ quint8(0x00)); // object id
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x01),
+ quint8(0x00), quint8(0x01)); // object size
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 9);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x01),
+ quint8(0x00), quint8(0x01), quint8(0xff)); // object data
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 9);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x01),
+ quint8(0x00), quint8(0x01), quint8(0xff),
+ quint8(0xff)); // dummy additional data
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 9);
+
+ // multiple objects equal size
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0xff), quint8(0x02),
+ quint8(0x02)); // object count
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0xff), quint8(0x02),
+ quint8(0x02),
+ quint8(0x00)); // object id
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x02),
+ quint8(0x00), quint8(0x01)); // object size
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 11);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x02),
+ quint8(0x00), quint8(0x01), quint8(0xff)); // object data
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 11);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x02),
+ quint8(0x00), quint8(0x01), quint8(0xff),
+ quint8(0x01)); // second object id
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 11);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x02),
+ quint8(0x00), quint8(0x01), quint8(0xff),
+ quint8(0x01), quint8(0x01)); // second object size
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 12);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x02),
+ quint8(0x00), quint8(0x01), quint8(0xff),
+ quint8(0x01), quint8(0x01), quint8(0xff)); // second object data
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 12);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x02),
+ quint8(0x00), quint8(0x01), quint8(0xff),
+ quint8(0x01), quint8(0x01), quint8(0xff),
+ quint8(0xff)); // dummy additional data
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 12);
+
+ // multiple objects different size
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0xff), quint8(0x02),
+ quint8(0x03));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0xff), quint8(0x02),
+ quint8(0x03),
+ quint8(0x00));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x01));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 13);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 14);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 14);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 14);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01), quint8(0x03));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 14);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01), quint8(0x03), quint8(0xff));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 14);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 17);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 17);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
+ quint8(0x02));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 17);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
+ quint8(0x02), quint8(0x05));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
+ quint8(0x02), quint8(0x05), quint8(0xff));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
+ quint8(0x02), quint8(0x05), quint8(0xff), quint8(0xff));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
+ quint8(0x02), quint8(0x05), quint8(0xff), quint8(0xff), quint8(0xff));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
+ quint8(0x02), quint8(0x05), quint8(0xff), quint8(0xff), quint8(0xff), quint8(0xff));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
+ quint8(0x02), quint8(0x05), quint8(0xff), quint8(0xff), quint8(0xff), quint8(0xff), quint8(0xff));
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
+ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
+ quint8(0x03),
+ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
+ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
+ quint8(0x02), quint8(0x05), quint8(0xff), quint8(0xff), quint8(0xff), quint8(0xff), quint8(0xff),
+ quint8(0xff)); // dummy additional data
+ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
}
};