diff options
author | Alex Blasche <alexander.blasche@theqtcompany.com> | 2016-03-02 10:46:15 +0100 |
---|---|---|
committer | Alex Blasche <alexander.blasche@theqtcompany.com> | 2016-03-02 10:47:37 +0100 |
commit | 78577b4de46555ef03c2bad0a898d73b7782f772 (patch) | |
tree | beecf2e55f917df3561e9f5216afcdf1c8fe02de | |
parent | 246c93df987e3ee49ba18a3d50cc9d96d35b9672 (diff) | |
parent | e40343337940705ceb830f9f20a09084185ab198 (diff) |
Merge remote-tracking branch 'gerrit/5.6' into 5.7
Change-Id: I414a42572d46b6bbfcd7378f9f38eced3bc1e74c
-rw-r--r-- | examples/serialbus/modbus/master/doc/images/modbusmaster.png | bin | 0 -> 14613 bytes | |||
-rw-r--r-- | examples/serialbus/modbus/master/doc/src/modbusmaster.qdoc | 43 | ||||
-rw-r--r-- | examples/serialbus/modbus/slave/doc/images/modbusserver.png | bin | 0 -> 14146 bytes | |||
-rw-r--r-- | examples/serialbus/modbus/slave/doc/src/modbusslave.qdoc | 45 | ||||
-rw-r--r-- | src/serialbus/doc/qtserialbus.qdocconf | 4 | ||||
-rw-r--r-- | src/serialbus/doc/snippets/snippetmain.cpp | 18 | ||||
-rw-r--r-- | src/serialbus/doc/src/qtserialbus-index.qdoc | 2 | ||||
-rw-r--r-- | src/serialbus/qmodbuspdu.cpp | 143 | ||||
-rw-r--r-- | src/serialbus/qmodbuspdu.h | 1 | ||||
-rw-r--r-- | src/serialbus/qmodbusrtuserialmaster.cpp | 7 | ||||
-rw-r--r-- | src/serialbus/qmodbusrtuserialmaster_p.h | 36 | ||||
-rw-r--r-- | src/serialbus/qmodbusrtuserialslave.cpp | 30 | ||||
-rw-r--r-- | src/serialbus/qmodbusrtuserialslave.h | 2 | ||||
-rw-r--r-- | src/serialbus/qmodbusrtuserialslave_p.h | 21 | ||||
-rw-r--r-- | src/serialbus/qmodbustcpserver.cpp | 5 | ||||
-rw-r--r-- | tests/auto/qmodbuspdu/tst_qmodbuspdu.cpp | 251 |
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 Binary files differnew file mode 100644 index 0000000..faf66c7 --- /dev/null +++ b/examples/serialbus/modbus/master/doc/images/modbusmaster.png 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 Binary files differnew file mode 100644 index 0000000..795457a --- /dev/null +++ b/examples/serialbus/modbus/slave/doc/images/modbusserver.png 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); } }; |