diff options
42 files changed, 533 insertions, 293 deletions
diff --git a/.qmake.conf b/.qmake.conf index 85f53bf..ebb0ade 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -2,4 +2,4 @@ load(qt_build_config) CONFIG += warning_clean DEFINES += QT_NO_FOREACH -MODULE_VERSION = 5.12.3 +MODULE_VERSION = 5.13.0 diff --git a/examples/serialbus/modbus/adueditor/adueditor.pro b/examples/serialbus/modbus/adueditor/adueditor.pro index bba3c0d..facba76 100644 --- a/examples/serialbus/modbus/adueditor/adueditor.pro +++ b/examples/serialbus/modbus/adueditor/adueditor.pro @@ -5,6 +5,7 @@ TARGET = adueditor QT += serialbus serialport widgets QT += serialbus-private core-private requires(qtConfig(combobox)) +requires(qtConfig(modbus-serialport)) FORMS += interface.ui SOURCES += main.cpp mainwindow.cpp modbustcpclient.cpp diff --git a/examples/serialbus/modbus/adueditor/modbustcpclient_p.h b/examples/serialbus/modbus/adueditor/modbustcpclient_p.h index 9d091d8..c1b37a8 100644 --- a/examples/serialbus/modbus/adueditor/modbustcpclient_p.h +++ b/examples/serialbus/modbus/adueditor/modbustcpclient_p.h @@ -98,7 +98,7 @@ public: using TypeId = void (QTimer::*)(int); q->connect(q, &QModbusClient::timeoutChanged, element.timer.data(), static_cast<TypeId>(&QTimer::setInterval)); - QObject::connect(element.timer.data(), &QTimer::timeout, [this, writeToSocket]() { + QObject::connect(element.timer.data(), &QTimer::timeout, q, [this, writeToSocket]() { if (!m_transactionStore.contains(m_tId)) return; diff --git a/examples/serialbus/modbus/master/mainwindow.cpp b/examples/serialbus/modbus/master/mainwindow.cpp index 18bba06..6600af8 100644 --- a/examples/serialbus/modbus/master/mainwindow.cpp +++ b/examples/serialbus/modbus/master/mainwindow.cpp @@ -90,8 +90,15 @@ MainWindow::MainWindow(QWidget *parent) ui->writeTable->addItem(tr("Input Registers"), QModbusDataUnit::InputRegisters); ui->writeTable->addItem(tr("Holding Registers"), QModbusDataUnit::HoldingRegisters); +#if QT_CONFIG(modbus_serialport) ui->connectType->setCurrentIndex(0); on_connectType_currentIndexChanged(0); +#else + // lock out the serial port option + ui->connectType->setCurrentIndex(1); + on_connectType_currentIndexChanged(1); + ui->connectType->setEnabled(false); +#endif auto model = new QStandardItemModel(10, 1, this); for (int i = 0; i < 10; ++i) @@ -154,7 +161,9 @@ void MainWindow::on_connectType_currentIndexChanged(int index) auto type = static_cast<ModbusConnection> (index); if (type == Serial) { +#if QT_CONFIG(modbus_serialport) modbusDevice = new QModbusRtuSerialMaster(this); +#endif } else if (type == Tcp) { modbusDevice = new QModbusTcpClient(this); if (ui->portEdit->text().isEmpty()) @@ -187,6 +196,7 @@ void MainWindow::on_connectButton_clicked() if (static_cast<ModbusConnection> (ui->connectType->currentIndex()) == Serial) { modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, ui->portEdit->text()); +#if QT_CONFIG(modbus_serialport) modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, m_settingsDialog->settings().parity); modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, @@ -195,6 +205,7 @@ void MainWindow::on_connectButton_clicked() m_settingsDialog->settings().dataBits); modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, m_settingsDialog->settings().stopBits); +#endif } else { const QUrl url = QUrl::fromUserInput(ui->portEdit->text()); modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port()); diff --git a/examples/serialbus/modbus/master/master.pro b/examples/serialbus/modbus/master/master.pro index 682ce82..188fbc1 100644 --- a/examples/serialbus/modbus/master/master.pro +++ b/examples/serialbus/modbus/master/master.pro @@ -1,6 +1,8 @@ -QT += serialbus serialport widgets +QT += serialbus widgets requires(qtConfig(combobox)) +qtConfig(modbus-serialport): QT += serialport + TARGET = modbusmaster TEMPLATE = app CONFIG += c++11 diff --git a/examples/serialbus/modbus/master/settingsdialog.cpp b/examples/serialbus/modbus/master/settingsdialog.cpp index 8b2fce8..0a5eb43 100644 --- a/examples/serialbus/modbus/master/settingsdialog.cpp +++ b/examples/serialbus/modbus/master/settingsdialog.cpp @@ -58,19 +58,23 @@ SettingsDialog::SettingsDialog(QWidget *parent) : ui->setupUi(this); ui->parityCombo->setCurrentIndex(1); +#if QT_CONFIG(modbus_serialport) ui->baudCombo->setCurrentText(QString::number(m_settings.baud)); ui->dataBitsCombo->setCurrentText(QString::number(m_settings.dataBits)); ui->stopBitsCombo->setCurrentText(QString::number(m_settings.stopBits)); +#endif ui->timeoutSpinner->setValue(m_settings.responseTime); ui->retriesSpinner->setValue(m_settings.numberOfRetries); connect(ui->applyButton, &QPushButton::clicked, [this]() { +#if QT_CONFIG(modbus_serialport) m_settings.parity = ui->parityCombo->currentIndex(); if (m_settings.parity > 0) m_settings.parity++; m_settings.baud = ui->baudCombo->currentText().toInt(); m_settings.dataBits = ui->dataBitsCombo->currentText().toInt(); m_settings.stopBits = ui->stopBitsCombo->currentText().toInt(); +#endif m_settings.responseTime = ui->timeoutSpinner->value(); m_settings.numberOfRetries = ui->retriesSpinner->value(); diff --git a/examples/serialbus/modbus/master/settingsdialog.h b/examples/serialbus/modbus/master/settingsdialog.h index fa8550b..98ba946 100644 --- a/examples/serialbus/modbus/master/settingsdialog.h +++ b/examples/serialbus/modbus/master/settingsdialog.h @@ -51,8 +51,11 @@ #ifndef SETTINGSDIALOG_H #define SETTINGSDIALOG_H +#include <QtSerialBus/qtserialbusglobal.h> #include <QDialog> +#if QT_CONFIG(modbus_serialport) #include <QSerialPort> +#endif QT_BEGIN_NAMESPACE @@ -68,10 +71,12 @@ class SettingsDialog : public QDialog public: struct Settings { +#if QT_CONFIG(modbus_serialport) int parity = QSerialPort::EvenParity; int baud = QSerialPort::Baud19200; int dataBits = QSerialPort::Data8; int stopBits = QSerialPort::OneStop; +#endif int responseTime = 1000; int numberOfRetries = 3; }; diff --git a/examples/serialbus/modbus/slave/mainwindow.cpp b/examples/serialbus/modbus/slave/mainwindow.cpp index 1b7ad72..4a4c194 100644 --- a/examples/serialbus/modbus/slave/mainwindow.cpp +++ b/examples/serialbus/modbus/slave/mainwindow.cpp @@ -72,8 +72,15 @@ MainWindow::MainWindow(QWidget *parent) ui->setupUi(this); setupWidgetContainers(); +#if QT_CONFIG(modbus_serialport) ui->connectType->setCurrentIndex(0); on_connectType_currentIndexChanged(0); +#else + // lock out the serial port option + ui->connectType->setCurrentIndex(1); + on_connectType_currentIndexChanged(1); + ui->connectType->setEnabled(false); +#endif m_settingsDialog = new SettingsDialog(this); initActions(); @@ -114,7 +121,9 @@ void MainWindow::on_connectType_currentIndexChanged(int index) ModbusConnection type = static_cast<ModbusConnection> (index); if (type == Serial) { +#if QT_CONFIG(modbus_serialport) modbusDevice = new QModbusRtuSerialSlave(this); +#endif } else if (type == Tcp) { modbusDevice = new QModbusTcpServer(this); if (ui->portEdit->text().isEmpty()) @@ -177,6 +186,7 @@ void MainWindow::on_connectButton_clicked() if (static_cast<ModbusConnection> (ui->connectType->currentIndex()) == Serial) { modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, ui->portEdit->text()); +#if QT_CONFIG(modbus_serialport) modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, m_settingsDialog->settings().parity); modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, @@ -185,6 +195,7 @@ void MainWindow::on_connectButton_clicked() m_settingsDialog->settings().dataBits); modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, m_settingsDialog->settings().stopBits); +#endif } else { const QUrl url = QUrl::fromUserInput(ui->portEdit->text()); modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port()); diff --git a/examples/serialbus/modbus/slave/settingsdialog.cpp b/examples/serialbus/modbus/slave/settingsdialog.cpp index c7f1e0c..77ec8af 100644 --- a/examples/serialbus/modbus/slave/settingsdialog.cpp +++ b/examples/serialbus/modbus/slave/settingsdialog.cpp @@ -58,18 +58,21 @@ SettingsDialog::SettingsDialog(QWidget *parent) : ui->setupUi(this); ui->parityCombo->setCurrentIndex(1); +#if QT_CONFIG(modbus_serialport) ui->baudCombo->setCurrentText(QString::number(m_settings.baud)); ui->dataBitsCombo->setCurrentText(QString::number(m_settings.dataBits)); ui->stopBitsCombo->setCurrentText(QString::number(m_settings.stopBits)); +#endif connect(ui->applyButton, &QPushButton::clicked, [this]() { +#if QT_CONFIG(modbus_serialport) m_settings.parity = ui->parityCombo->currentIndex(); if (m_settings.parity > 0) m_settings.parity++; m_settings.baud = ui->baudCombo->currentText().toInt(); m_settings.dataBits = ui->dataBitsCombo->currentText().toInt(); m_settings.stopBits = ui->stopBitsCombo->currentText().toInt(); - +#endif hide(); }); } diff --git a/examples/serialbus/modbus/slave/settingsdialog.h b/examples/serialbus/modbus/slave/settingsdialog.h index bc352c3..dfe7b98 100644 --- a/examples/serialbus/modbus/slave/settingsdialog.h +++ b/examples/serialbus/modbus/slave/settingsdialog.h @@ -51,8 +51,11 @@ #ifndef SETTINGSDIALOG_H #define SETTINGSDIALOG_H +#include <QtSerialBus/qtserialbusglobal.h> #include <QDialog> +#if QT_CONFIG(modbus_serialport) #include <QSerialPort> +#endif QT_BEGIN_NAMESPACE @@ -68,10 +71,12 @@ class SettingsDialog : public QDialog public: struct Settings { +#if QT_CONFIG(modbus_serialport) int parity = QSerialPort::EvenParity; int baud = QSerialPort::Baud19200; int dataBits = QSerialPort::Data8; int stopBits = QSerialPort::OneStop; +#endif }; explicit SettingsDialog(QWidget *parent = nullptr); diff --git a/examples/serialbus/modbus/slave/slave.pro b/examples/serialbus/modbus/slave/slave.pro index b803c5d..aadef07 100644 --- a/examples/serialbus/modbus/slave/slave.pro +++ b/examples/serialbus/modbus/slave/slave.pro @@ -1,6 +1,8 @@ -QT += serialbus serialport widgets +QT += serialbus widgets requires(qtConfig(combobox)) +qtConfig(modbus-serialport): QT += serialport + TARGET = modbusslave TEMPLATE = app CONFIG += c++11 diff --git a/qtserialbus.pro b/qtserialbus.pro index 6261f9f..ebc35b3 100644 --- a/qtserialbus.pro +++ b/qtserialbus.pro @@ -1,4 +1,4 @@ -requires(qtHaveModule(serialport):!wasm) +requires(!wasm) lessThan(QT_MAJOR_VERSION, 5) { message("Cannot build current QtSerialBus sources with Qt version $${QT_VERSION}.") diff --git a/src/plugins/canbus/canbus.pro b/src/plugins/canbus/canbus.pro index 4366027..ab9755c 100644 --- a/src/plugins/canbus/canbus.pro +++ b/src/plugins/canbus/canbus.pro @@ -11,5 +11,8 @@ qtConfig(socketcan) { qtConfig(library) { SUBDIRS += passthrucan peakcan tinycan - win32:SUBDIRS += systeccan vectorcan + win32 { + SUBDIRS += systeccan + !winrt:SUBDIRS += vectorcan + } } diff --git a/src/serialbus/configure.json b/src/serialbus/configure.json index d640f57..15d4863 100644 --- a/src/serialbus/configure.json +++ b/src/serialbus/configure.json @@ -25,6 +25,12 @@ "label": "Socket CAN FD", "condition": "config.linux && features.socketcan && tests.socketcan_fd", "output": [ "privateFeature"] + }, + "modbus-serialport" : { + "label": "SerialPort Support", + "condition": "module.serialport", + "purpose": "Enables Serial-based Modbus Support", + "output": [ "publicFeature"] } }, @@ -46,7 +52,8 @@ "section": "Qt SerialBus", "entries": [ "socketcan", - "socketcan_fd" + "socketcan_fd", + "modbus-serialport" ] } ] diff --git a/src/serialbus/qcanbus.h b/src/serialbus/qcanbus.h index 695d312..6170fcc 100644 --- a/src/serialbus/qcanbus.h +++ b/src/serialbus/qcanbus.h @@ -38,7 +38,7 @@ #define QCANBUS_H #include <QtCore/qobject.h> -#include <QtSerialBus/qserialbusglobal.h> +#include <QtSerialBus/qtserialbusglobal.h> #include <QtSerialBus/qcanbusdevice.h> #include <QtSerialBus/qcanbusdeviceinfo.h> diff --git a/src/serialbus/qcanbusdeviceinfo.h b/src/serialbus/qcanbusdeviceinfo.h index fbe2c71..c46302d 100644 --- a/src/serialbus/qcanbusdeviceinfo.h +++ b/src/serialbus/qcanbusdeviceinfo.h @@ -39,7 +39,7 @@ #include <QtCore/qshareddata.h> #include <QtCore/qstring.h> -#include <QtSerialBus/qserialbusglobal.h> +#include <QtSerialBus/qtserialbusglobal.h> QT_BEGIN_NAMESPACE diff --git a/src/serialbus/qcanbusfactory.h b/src/serialbus/qcanbusfactory.h index d74903c..cd7322b 100644 --- a/src/serialbus/qcanbusfactory.h +++ b/src/serialbus/qcanbusfactory.h @@ -38,7 +38,7 @@ #define QCANBUSFACTORY_H #include <QtCore/qstringlist.h> -#include <QtSerialBus/qserialbusglobal.h> +#include <QtSerialBus/qtserialbusglobal.h> #include <QtSerialBus/qcanbusdevice.h> #include <QtSerialBus/qcanbusdeviceinfo.h> diff --git a/src/serialbus/qcanbusframe.h b/src/serialbus/qcanbusframe.h index d1f8857..69bde41 100644 --- a/src/serialbus/qcanbusframe.h +++ b/src/serialbus/qcanbusframe.h @@ -39,7 +39,7 @@ #include <QtCore/qmetatype.h> #include <QtCore/qobject.h> -#include <QtSerialBus/qserialbusglobal.h> +#include <QtSerialBus/qtserialbusglobal.h> QT_BEGIN_NAMESPACE diff --git a/src/serialbus/qmodbusclient.cpp b/src/serialbus/qmodbusclient.cpp index 32be316..d273a3a 100644 --- a/src/serialbus/qmodbusclient.cpp +++ b/src/serialbus/qmodbusclient.cpp @@ -351,6 +351,9 @@ QModbusRequest QModbusClientPrivate::createRWRequest(const QModbusDataUnit &read void QModbusClientPrivate::processQueueElement(const QModbusResponse &pdu, const QueueElement &element) { + if (element.reply.isNull()) + return; + element.reply->setRawResult(pdu); if (pdu.isException()) { element.reply->setError(QModbusDevice::ProtocolError, @@ -358,7 +361,7 @@ void QModbusClientPrivate::processQueueElement(const QModbusResponse &pdu, return; } - if (element.reply->type() == QModbusReply::Raw) { + if (element.reply->type() != QModbusReply::Common) { element.reply->setFinished(true); return; } diff --git a/src/serialbus/qmodbusclient_p.h b/src/serialbus/qmodbusclient_p.h index 2286fc8..f9a0dfb 100644 --- a/src/serialbus/qmodbusclient_p.h +++ b/src/serialbus/qmodbusclient_p.h @@ -126,6 +126,7 @@ public: QSharedPointer<QTimer> timer; QByteArray adu; qint64 bytesWritten = 0; + qint32 m_timerId = INT_MIN; }; void processQueueElement(const QModbusResponse &pdu, const QueueElement &element); }; diff --git a/src/serialbus/qmodbuscommevent_p.h b/src/serialbus/qmodbuscommevent_p.h index 47967e7..087c060 100644 --- a/src/serialbus/qmodbuscommevent_p.h +++ b/src/serialbus/qmodbuscommevent_p.h @@ -37,7 +37,7 @@ #ifndef QMODBUSCOMMEVENT_P_H #define QMODBUSCOMMEVENT_P_H -#include <QtSerialBus/qserialbusglobal.h> +#include <QtSerialBus/qtserialbusglobal.h> // // W A R N I N G diff --git a/src/serialbus/qmodbusdevice.cpp b/src/serialbus/qmodbusdevice.cpp index aa1bddc..9891d16 100644 --- a/src/serialbus/qmodbusdevice.cpp +++ b/src/serialbus/qmodbusdevice.cpp @@ -124,6 +124,7 @@ QVariant QModbusDevice::connectionParameter(int parameter) const { Q_D(const QModbusDevice); switch (parameter) { +#if QT_CONFIG(modbus_serialport) case SerialPortNameParameter: return d->m_comPort; case SerialDataBitsParameter: @@ -134,6 +135,7 @@ QVariant QModbusDevice::connectionParameter(int parameter) const return d->m_stopBits; case SerialBaudRateParameter: return d->m_baudRate; +#endif case NetworkPortParameter: return d->m_networkPort; case NetworkAddressParameter: @@ -156,6 +158,7 @@ void QModbusDevice::setConnectionParameter(int parameter, const QVariant &value) { Q_D(QModbusDevice); switch (parameter) { +#if QT_CONFIG(modbus_serialport) case SerialPortNameParameter: d->m_comPort = value.toString(); break; @@ -171,6 +174,7 @@ void QModbusDevice::setConnectionParameter(int parameter, const QVariant &value) case SerialBaudRateParameter: d->m_baudRate = QSerialPort::BaudRate(value.toInt()); break; +#endif case NetworkPortParameter: d->m_networkPort = value.toInt(); break; diff --git a/src/serialbus/qmodbusdevice.h b/src/serialbus/qmodbusdevice.h index 20fa1b3..468fdf4 100644 --- a/src/serialbus/qmodbusdevice.h +++ b/src/serialbus/qmodbusdevice.h @@ -38,7 +38,7 @@ #include <QtCore/qobject.h> #include <QtCore/qiodevice.h> -#include <QtSerialBus/qserialbusglobal.h> +#include <QtSerialBus/qtserialbusglobal.h> QT_BEGIN_NAMESPACE diff --git a/src/serialbus/qmodbusdevice_p.h b/src/serialbus/qmodbusdevice_p.h index 52ace00..a845a89 100644 --- a/src/serialbus/qmodbusdevice_p.h +++ b/src/serialbus/qmodbusdevice_p.h @@ -39,7 +39,9 @@ #include <QtCore/qvariant.h> #include <QtSerialBus/qmodbusdevice.h> +#if QT_CONFIG(modbus_serialport) #include <QtSerialPort/qserialport.h> +#endif #include <private/qobject_p.h> @@ -65,11 +67,13 @@ public: QModbusDevice::Error error = QModbusDevice::NoError; QString errorString; +#if QT_CONFIG(modbus_serialport) QString m_comPort; QSerialPort::DataBits m_dataBits = QSerialPort::Data8; QSerialPort::Parity m_parity = QSerialPort::EvenParity; QSerialPort::StopBits m_stopBits = QSerialPort::OneStop; QSerialPort::BaudRate m_baudRate = QSerialPort::Baud19200; +#endif int m_networkPort = 502; QString m_networkAddress = QStringLiteral("127.0.0.1"); diff --git a/src/serialbus/qmodbusdeviceidentification.h b/src/serialbus/qmodbusdeviceidentification.h index 63d7ee3..9f08f55 100644 --- a/src/serialbus/qmodbusdeviceidentification.h +++ b/src/serialbus/qmodbusdeviceidentification.h @@ -39,7 +39,7 @@ #include <QtCore/qmap.h> #include <QtCore/qmetatype.h> -#include <QtSerialBus/qserialbusglobal.h> +#include <QtSerialBus/qtserialbusglobal.h> QT_BEGIN_NAMESPACE diff --git a/src/serialbus/qmodbuspdu.h b/src/serialbus/qmodbuspdu.h index 28b1f8a..14cef59 100644 --- a/src/serialbus/qmodbuspdu.h +++ b/src/serialbus/qmodbuspdu.h @@ -39,7 +39,7 @@ #include <QtCore/qdatastream.h> #include <QtCore/qmetatype.h> #include <QtCore/qvector.h> -#include <QtSerialBus/qserialbusglobal.h> +#include <QtSerialBus/qtserialbusglobal.h> QT_BEGIN_NAMESPACE diff --git a/src/serialbus/qmodbusreply.cpp b/src/serialbus/qmodbusreply.cpp index 74cb3e6..7201a33 100644 --- a/src/serialbus/qmodbusreply.cpp +++ b/src/serialbus/qmodbusreply.cpp @@ -74,6 +74,9 @@ public: \value Common The reply originates from a common read, write or read/write request. See \l QModbusClient::sendReadRequest, \l QModbusClient::sendWriteRequest and \l QModbusClient::sendReadWriteRequest + \value Broadcast The replay originates from a Modbus broadcast request. The + \l serverAddress() will return \c 0 and the \l finished() + signal will be emitted immediately. */ /*! @@ -275,6 +278,6 @@ void QModbusReply::setRawResult(const QModbusResponse &response) d->m_response = response; } -#include "moc_qmodbusreply.cpp" - QT_END_NAMESPACE + +#include "moc_qmodbusreply.cpp" diff --git a/src/serialbus/qmodbusreply.h b/src/serialbus/qmodbusreply.h index 9b54687..ffefc89 100644 --- a/src/serialbus/qmodbusreply.h +++ b/src/serialbus/qmodbusreply.h @@ -53,7 +53,8 @@ class Q_SERIALBUS_EXPORT QModbusReply : public QObject public: enum ReplyType { Raw, - Common + Common, + Broadcast }; Q_ENUM(ReplyType) diff --git a/src/serialbus/qmodbusrtuserialmaster.cpp b/src/serialbus/qmodbusrtuserialmaster.cpp index 99006f4..a54b253 100644 --- a/src/serialbus/qmodbusrtuserialmaster.cpp +++ b/src/serialbus/qmodbusrtuserialmaster.cpp @@ -104,6 +104,33 @@ void QModbusRtuSerialMaster::setInterFrameDelay(int microseconds) } /*! + \since 5.13 + + Returns the amount of milliseconds for the silent interval between a Modbus + broadcast and a consecutive Modbus messages. The default value is set to + \c 100 milliseconds. +*/ +int QModbusRtuSerialMaster::turnaroundDelay() const +{ + Q_D(const QModbusRtuSerialMaster); + return d->m_turnaroundDelay; +} + +/*! + \since 5.13 + + Sets the amount of milliseconds for the silent interval between a Modbus + broadcast and a consecutive Modbus messages to \a turnaroundDelay. + Typically the turnaround delay is in the range of \c 100 to \c 200 + milliseconds. +*/ +void QModbusRtuSerialMaster::setTurnaroundDelay(int turnaroundDelay) +{ + Q_D(QModbusRtuSerialMaster); + d->m_turnaroundDelay = turnaroundDelay; +} + +/*! \internal */ QModbusRtuSerialMaster::QModbusRtuSerialMaster(QModbusRtuSerialMasterPrivate &dd, QObject *parent) @@ -150,10 +177,6 @@ void QModbusRtuSerialMaster::close() if (d->m_serialPort->isOpen()) d->m_serialPort->close(); - // enqueue current active request back for abortion - d->m_queue.enqueue(d->m_current); - d->m_current = QModbusClientPrivate::QueueElement(); - int numberOfAborts = 0; while (!d->m_queue.isEmpty()) { // Finish each open reply and forget them diff --git a/src/serialbus/qmodbusrtuserialmaster.h b/src/serialbus/qmodbusrtuserialmaster.h index 5c607e2..87f58df 100644 --- a/src/serialbus/qmodbusrtuserialmaster.h +++ b/src/serialbus/qmodbusrtuserialmaster.h @@ -55,6 +55,9 @@ public: int interFrameDelay() const; void setInterFrameDelay(int microseconds); + int turnaroundDelay() const; + void setTurnaroundDelay(int turnaroundDelay); + protected: QModbusRtuSerialMaster(QModbusRtuSerialMasterPrivate &dd, QObject *parent = nullptr); diff --git a/src/serialbus/qmodbusrtuserialmaster_p.h b/src/serialbus/qmodbusrtuserialmaster_p.h index 5e440b2..9672684 100644 --- a/src/serialbus/qmodbusrtuserialmaster_p.h +++ b/src/serialbus/qmodbusrtuserialmaster_p.h @@ -65,174 +65,254 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS) Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS_LOW) +class Timer : public QObject +{ + Q_OBJECT + +public: + Timer() = default; + int start(int msec) + { + m_timer = QBasicTimer(); + m_timer.start(msec, Qt::PreciseTimer, this); + return m_timer.timerId(); + } + void stop() { m_timer.stop(); } + bool isActive() const { return m_timer.isActive(); } + +signals: + void timeout(int timerId); + +private: + void timerEvent(QTimerEvent *event) override + { + const auto id = m_timer.timerId(); + if (event->timerId() == id) + emit timeout(id); + } + +private: + QBasicTimer m_timer; +}; + class QModbusRtuSerialMasterPrivate : public QModbusClientPrivate { Q_DECLARE_PUBLIC(QModbusRtuSerialMaster) - enum State { + enum State + { Idle, - Schedule, - Send, - Receive, + WaitingForReplay, + ProcessReply } m_state = Idle; public: - void setupSerialPort() + void onReadyRead() { - Q_Q(QModbusRtuSerialMaster); + m_responseBuffer += m_serialPort->read(m_serialPort->bytesAvailable()); + qCDebug(QT_MODBUS_LOW) << "(RTU client) Response buffer:" << m_responseBuffer.toHex(); - m_sendTimer.setSingleShot(true); - QObject::connect(&m_sendTimer, &QTimer::timeout, q, [this]() { processQueue(); }); + if (m_responseBuffer.size() < 2) { + qCDebug(QT_MODBUS) << "(RTU client) Modbus ADU not complete"; + return; + } - m_responseTimer.setSingleShot(true); - QObject::connect(&m_responseTimer, &QTimer::timeout, q, [this]() { processQueue(); }); + const QModbusSerialAdu tmpAdu(QModbusSerialAdu::Rtu, m_responseBuffer); + int pduSizeWithoutFcode = QModbusResponse::calculateDataSize(tmpAdu.pdu()); + if (pduSizeWithoutFcode < 0) { + // wait for more data + qCDebug(QT_MODBUS) << "(RTU client) Cannot calculate PDU size for function code:" + << tmpAdu.pdu().functionCode() << ", delaying pending frame"; + return; + } - m_serialPort = new QSerialPort(q); - QObject::connect(m_serialPort, &QSerialPort::readyRead, q, [this]() { - responseBuffer += m_serialPort->read(m_serialPort->bytesAvailable()); - qCDebug(QT_MODBUS_LOW) << "(RTU client) Response buffer:" << responseBuffer.toHex(); + // server address byte + function code byte + PDU size + 2 bytes CRC + int aduSize = 2 + pduSizeWithoutFcode + 2; + if (tmpAdu.rawSize() < aduSize) { + qCDebug(QT_MODBUS) << "(RTU client) Incomplete ADU received, ignoring"; + return; + } - if (responseBuffer.size() < 2) { - qCDebug(QT_MODBUS) << "(RTU client) Modbus ADU not complete"; - return; + if (m_queue.isEmpty()) + return; + auto ¤t = m_queue.first(); + + // Special case for Diagnostics:ReturnQueryData. The response has no + // length indicator and is just a simple echo of what we have send. + if (tmpAdu.pdu().functionCode() == QModbusPdu::Diagnostics) { + const QModbusResponse response = tmpAdu.pdu(); + if (canMatchRequestAndResponse(response, tmpAdu.serverAddress())) { + quint16 subCode = 0xffff; + response.decodeData(&subCode); + if (subCode == Diagnostics::ReturnQueryData) { + if (response.data() != current.requestPdu.data()) + return; // echo does not match request yet + aduSize = 2 + response.dataSize() + 2; + if (tmpAdu.rawSize() < aduSize) + return; // echo matches, probably checksum missing + } } + } - const QModbusSerialAdu tmpAdu(QModbusSerialAdu::Rtu, responseBuffer); - int pduSizeWithoutFcode = QModbusResponse::calculateDataSize(tmpAdu.pdu()); - if (pduSizeWithoutFcode < 0) { - // wait for more data - qCDebug(QT_MODBUS) << "(RTU client) Cannot calculate PDU size for function code:" - << tmpAdu.pdu().functionCode() << ", delaying pending frame"; - return; - } + const QModbusSerialAdu adu(QModbusSerialAdu::Rtu, m_responseBuffer.left(aduSize)); + m_responseBuffer.remove(0, aduSize); - // server address byte + function code byte + PDU size + 2 bytes CRC - int aduSize = 2 + pduSizeWithoutFcode + 2; - if (tmpAdu.rawSize() < aduSize) { - qCDebug(QT_MODBUS) << "(RTU client) Incomplete ADU received, ignoring"; - return; - } + qCDebug(QT_MODBUS) << "(RTU client) Received ADU:" << adu.rawData().toHex(); + if (QT_MODBUS().isDebugEnabled() && !m_responseBuffer.isEmpty()) + qCDebug(QT_MODBUS_LOW) << "(RTU client) Pending buffer:" << m_responseBuffer.toHex(); - // Special case for Diagnostics:ReturnQueryData. The response has no - // length indicator and is just a simple echo of what we have send. - if (tmpAdu.pdu().functionCode() == QModbusPdu::Diagnostics) { - const QModbusResponse response = tmpAdu.pdu(); - if (canMatchRequestAndResponse(response, tmpAdu.serverAddress())) { - quint16 subCode = 0xffff; - response.decodeData(&subCode); - if (subCode == Diagnostics::ReturnQueryData) { - if (response.data() != m_current.requestPdu.data()) - return; // echo does not match request yet - aduSize = 2 + response.dataSize() + 2; - if (tmpAdu.rawSize() < aduSize) - return; // echo matches, probably checksum missing - } - } - } + // check CRC + if (!adu.matchingChecksum()) { + qCWarning(QT_MODBUS) << "(RTU client) Discarding response with wrong CRC, received:" + << adu.checksum<quint16>() << ", calculated CRC:" + << QModbusSerialAdu::calculateCRC(adu.data(), adu.size()); + return; + } - const QModbusSerialAdu adu(QModbusSerialAdu::Rtu, responseBuffer.left(aduSize)); - responseBuffer.remove(0, aduSize); + const QModbusResponse response = adu.pdu(); + if (!canMatchRequestAndResponse(response, adu.serverAddress())) { + qCWarning(QT_MODBUS) << "(RTU client) Cannot match response with open request, " + "ignoring"; + return; + } - qCDebug(QT_MODBUS)<< "(RTU client) Received ADU:" << adu.rawData().toHex(); - if (QT_MODBUS().isDebugEnabled() && !responseBuffer.isEmpty()) - qCDebug(QT_MODBUS_LOW) << "(RTU client) Pending buffer:" << responseBuffer.toHex(); + m_state = ProcessReply; + m_responseTimer.stop(); + current.m_timerId = INT_MIN; - // check CRC - if (!adu.matchingChecksum()) { - qCWarning(QT_MODBUS) << "(RTU client) Discarding response with wrong CRC, received:" - << adu.checksum<quint16>() << ", calculated CRC:" - << QModbusSerialAdu::calculateCRC(adu.data(), adu.size()); - return; - } + processQueueElement(response, m_queue.dequeue()); - const QModbusResponse response = adu.pdu(); - if (!canMatchRequestAndResponse(response, adu.serverAddress())) { - qCWarning(QT_MODBUS) << "(RTU client) Cannot match response with open request, " - "ignoring"; - return; - } + m_state = Idle; + scheduleNextRequest(m_interFrameDelayMilliseconds); + } - if (m_state != State::Receive) { - qCDebug(QT_MODBUS) << "(RTU server) Ignoring response due to non receive state"; - return; - } + void onAboutToClose() + { + Q_Q(QModbusRtuSerialMaster); + Q_UNUSED(q) // avoid warning in release mode + Q_ASSERT(q->state() == QModbusDevice::ClosingState); + + m_responseTimer.stop(); + } + + void onResponseTimeout(int timerId) + { + m_responseTimer.stop(); + if (m_state != State::WaitingForReplay || m_queue.isEmpty()) + return; + const auto current = m_queue.first(); - m_sendTimer.stop(); - m_responseTimer.stop(); - processQueueElement(response, m_current); + if (current.m_timerId != timerId) + return; - m_state = Schedule; // reschedule, even if empty - m_serialPort->clear(QSerialPort::AllDirections); - QTimer::singleShot(m_interFrameDelayMilliseconds, [this]() { processQueue(); }); - }); + qCDebug(QT_MODBUS) << "(RTU client) Receive timeout:" << current.requestPdu; - using TypeId = void (QSerialPort::*)(QSerialPort::SerialPortError); - QObject::connect(m_serialPort, static_cast<TypeId>(&QSerialPort::error), - [this](QSerialPort::SerialPortError error) { - if (error == QSerialPort::NoError) - return; - - qCDebug(QT_MODBUS) << "(RTU server) QSerialPort error:" << error - << (m_serialPort ? m_serialPort->errorString() : QString()); - - Q_Q(QModbusRtuSerialMaster); - - switch (error) { - case QSerialPort::DeviceNotFoundError: - q->setError(QModbusDevice::tr("Referenced serial device does not exist."), - QModbusDevice::ConnectionError); - break; - case QSerialPort::PermissionError: - q->setError(QModbusDevice::tr("Cannot open serial device due to permissions."), - QModbusDevice::ConnectionError); - break; - case QSerialPort::OpenError: - case QSerialPort::NotOpenError: - q->setError(QModbusDevice::tr("Cannot open serial device."), - QModbusDevice::ConnectionError); - break; - case QSerialPort::WriteError: - q->setError(QModbusDevice::tr("Write error."), QModbusDevice::WriteError); - break; - case QSerialPort::ReadError: - q->setError(QModbusDevice::tr("Read error."), QModbusDevice::ReadError); - break; - case QSerialPort::ResourceError: - q->setError(QModbusDevice::tr("Resource error."), QModbusDevice::ConnectionError); - break; - case QSerialPort::UnsupportedOperationError: - q->setError(QModbusDevice::tr("Device operation is not supported error."), - QModbusDevice::ConfigurationError); - break; - case QSerialPort::TimeoutError: - q->setError(QModbusDevice::tr("Timeout error."), QModbusDevice::TimeoutError); - break; - case QSerialPort::UnknownError: - q->setError(QModbusDevice::tr("Unknown error."), QModbusDevice::UnknownError); - break; - default: - qCDebug(QT_MODBUS) << "(RTU server) Unhandled QSerialPort error" << error; - break; + if (current.numberOfRetries <= 0) { + auto item = m_queue.dequeue(); + if (item.reply) { + item.reply->setError(QModbusDevice::TimeoutError, + QModbusClient::tr("Request timeout.")); } + } + + m_state = Idle; + scheduleNextRequest(m_interFrameDelayMilliseconds); + } + + void onBytesWritten(qint64 bytes) + { + if (m_queue.isEmpty()) + return; + auto ¤t = m_queue.first(); + + current.bytesWritten += bytes; + if (current.bytesWritten != current.adu.size()) + return; + + qCDebug(QT_MODBUS) << "(RTU client) Send successful:" << current.requestPdu; + + if (!current.reply.isNull() && current.reply->type() == QModbusReply::Broadcast) { + m_state = ProcessReply; + processQueueElement({}, m_queue.dequeue()); + m_state = Idle; + scheduleNextRequest(m_turnaroundDelay); + } else { + current.m_timerId = m_responseTimer.start(m_responseTimeoutDuration); + } + } + + void onError(QSerialPort::SerialPortError error) + { + if (error == QSerialPort::NoError) + return; + + qCDebug(QT_MODBUS) << "(RTU server) QSerialPort error:" << error + << (m_serialPort ? m_serialPort->errorString() : QString()); + + Q_Q(QModbusRtuSerialMaster); + + switch (error) { + case QSerialPort::DeviceNotFoundError: + q->setError(QModbusDevice::tr("Referenced serial device does not exist."), + QModbusDevice::ConnectionError); + break; + case QSerialPort::PermissionError: + q->setError(QModbusDevice::tr("Cannot open serial device due to permissions."), + QModbusDevice::ConnectionError); + break; + case QSerialPort::OpenError: + case QSerialPort::NotOpenError: + q->setError(QModbusDevice::tr("Cannot open serial device."), + QModbusDevice::ConnectionError); + break; + case QSerialPort::WriteError: + q->setError(QModbusDevice::tr("Write error."), QModbusDevice::WriteError); + break; + case QSerialPort::ReadError: + q->setError(QModbusDevice::tr("Read error."), QModbusDevice::ReadError); + break; + case QSerialPort::ResourceError: + q->setError(QModbusDevice::tr("Resource error."), QModbusDevice::ConnectionError); + break; + case QSerialPort::UnsupportedOperationError: + q->setError(QModbusDevice::tr("Device operation is not supported error."), + QModbusDevice::ConfigurationError); + break; + case QSerialPort::TimeoutError: + q->setError(QModbusDevice::tr("Timeout error."), QModbusDevice::TimeoutError); + break; + case QSerialPort::UnknownError: + q->setError(QModbusDevice::tr("Unknown error."), QModbusDevice::UnknownError); + break; + default: + qCDebug(QT_MODBUS) << "(RTU server) Unhandled QSerialPort error" << error; + break; + } + } + + void setupSerialPort() + { + Q_Q(QModbusRtuSerialMaster); + m_serialPort = new QSerialPort(q); + + QObject::connect(&m_responseTimer, &Timer::timeout, q, [this](int timerId) { + onResponseTimeout(timerId); }); - QObject::connect(m_serialPort, &QSerialPort::bytesWritten, q, [this](qint64 bytes) { - m_current.bytesWritten += bytes; - if (m_state == Send && (m_current.bytesWritten == m_current.adu.size()) && !m_current.reply.isNull()) { - // the if conditions above are copied from processQueue() - qCDebug(QT_MODBUS) << "(RTU client) Send successful (quick):" << m_current.requestPdu; - m_state = Receive; - m_sendTimer.stop(); - m_responseTimer.start(m_responseTimeoutDuration); - } + QObject::connect(m_serialPort, &QSerialPort::readyRead, q, [this]() { + onReadyRead(); }); QObject::connect(m_serialPort, &QSerialPort::aboutToClose, q, [this]() { - Q_Q(QModbusRtuSerialMaster); - Q_UNUSED(q); // To avoid unused variable warning in release mode - Q_ASSERT(q->state() == QModbusDevice::ClosingState); + onAboutToClose(); + }); - m_sendTimer.stop(); - m_responseTimer.stop(); + QObject::connect(m_serialPort, &QSerialPort::bytesWritten, q, [this](qint64 bytes) { + onBytesWritten(bytes); + }); + + QObject::connect(m_serialPort, QOverload<QSerialPort::SerialPortError>::of(&QSerialPort::error), + q, [this](QSerialPort::SerialPortError error) { + onError(error); }); } @@ -244,7 +324,8 @@ public: If the user set the timeout to be longer than the calculated one, we'll keep the user defined. */ - void calculateInterFrameDelay() { + 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; @@ -258,7 +339,8 @@ public: m_interFrameDelayMilliseconds = delayMilliSeconds; } - void setupEnvironment() { + void setupEnvironment() + { if (m_serialPort) { m_serialPort->setPortName(m_comPort); m_serialPort->setParity(m_parity); @@ -269,123 +351,71 @@ public: calculateInterFrameDelay(); - responseBuffer.clear(); + m_responseBuffer.clear(); m_state = QModbusRtuSerialMasterPrivate::Idle; } - void scheduleNextRequest() { - Q_Q(QModbusRtuSerialMaster); - - m_state = Schedule; - m_serialPort->clear(QSerialPort::AllDirections); - QTimer::singleShot(m_interFrameDelayMilliseconds, q, [this]() { processQueue(); }); - } - QModbusReply *enqueueRequest(const QModbusRequest &request, int serverAddress, const QModbusDataUnit &unit, QModbusReply::ReplyType type) override { Q_Q(QModbusRtuSerialMaster); - auto reply = new QModbusReply(type, serverAddress, q); + auto reply = new QModbusReply(serverAddress == 0 ? QModbusReply::Broadcast : type, + serverAddress, q); QueueElement element(reply, request, unit, m_numberOfRetries + 1); element.adu = QModbusSerialAdu::create(QModbusSerialAdu::Rtu, serverAddress, request); m_queue.enqueue(element); - if (m_state == Idle) - scheduleNextRequest(); + scheduleNextRequest(m_interFrameDelayMilliseconds); + return reply; } - void processQueue() + void scheduleNextRequest(int delay) { - Q_ASSERT_X(!m_sendTimer.isActive(), "processQueue", "send timer active"); - Q_ASSERT_X(!m_responseTimer.isActive(), "processQueue", "response timer active"); - - auto writeAdu = [this]() { - responseBuffer.clear(); - m_current.bytesWritten = 0; - m_current.numberOfRetries--; - m_serialPort->write(m_current.adu); - m_sendTimer.start(m_interFrameDelayMilliseconds); - - qCDebug(QT_MODBUS) << "(RTU client) Sent Serial PDU:" << m_current.requestPdu; - qCDebug(QT_MODBUS_LOW).noquote() << "(RTU client) Sent Serial ADU: 0x" + m_current.adu - .toHex(); - }; - - switch (m_state) { - case Schedule: - m_current = QueueElement(); - if (!m_queue.isEmpty()) { - m_current = m_queue.dequeue(); - if (m_current.reply) { - m_state = Send; - QTimer::singleShot(0, [writeAdu]() { writeAdu(); }); - } else { - QTimer::singleShot(0, [this]() { processQueue(); }); - } - } else { - m_state = Idle; - } - break; + Q_Q(QModbusRtuSerialMaster); - case Send: - // send timeout will always happen unless canceled by very quick bytesWritten - if (m_current.reply.isNull()) { - scheduleNextRequest(); - } else if (m_current.bytesWritten < m_current.adu.size()) { - qCDebug(QT_MODBUS) << "(RTU client) Send failed:" << m_current.requestPdu; - - if (m_current.numberOfRetries <= 0) { - if (m_current.reply) { - m_current.reply->setError(QModbusDevice::TimeoutError, - QModbusClient::tr("Request timeout.")); - } - m_current = QueueElement(); - scheduleNextRequest(); - } else { - m_serialPort->clear(QSerialPort::AllDirections); - QTimer::singleShot(m_interFrameDelayMilliseconds, [writeAdu]() { writeAdu(); }); - } - } else { - qCDebug(QT_MODBUS) << "(RTU client) Send successful:" << m_current.requestPdu; - m_state = Receive; - m_responseTimer.start(m_responseTimeoutDuration); - } - break; + if (m_state == Idle && !m_queue.isEmpty()) { + m_state = WaitingForReplay; + QTimer::singleShot(delay, q, [this]() { processQueue(); }); + } + } - case Receive: - // receive timeout will only happen after successful send - qCDebug(QT_MODBUS) << "(RTU client) Receive timeout:" << m_current.requestPdu; - if (m_current.reply.isNull()) { - scheduleNextRequest(); - } else if (m_current.numberOfRetries <= 0) { - if (m_current.reply) { - m_current.reply->setError(QModbusDevice::TimeoutError, - QModbusClient::tr("Response timeout.")); - } - scheduleNextRequest(); - } else { - m_state = Send; - m_serialPort->clear(QSerialPort::AllDirections); - QTimer::singleShot(m_interFrameDelayMilliseconds, [writeAdu]() { writeAdu(); }); - } - break; + void processQueue() + { + m_responseBuffer.clear(); + m_serialPort->clear(QSerialPort::AllDirections); - case Idle: - default: - Q_ASSERT_X(false, "processQueue", QByteArray("unexpected state: ").append(m_state)); - break; + if (m_queue.isEmpty()) + return; + auto ¤t = m_queue.first(); + + if (current.reply.isNull()) { + m_queue.dequeue(); + m_state = Idle; + scheduleNextRequest(m_interFrameDelayMilliseconds); + } else { + current.bytesWritten = 0; + current.numberOfRetries--; + m_serialPort->write(current.adu); + + qCDebug(QT_MODBUS) << "(RTU client) Sent Serial PDU:" << current.requestPdu; + qCDebug(QT_MODBUS_LOW).noquote() << "(RTU client) Sent Serial ADU: 0x" + current.adu + .toHex(); } } bool canMatchRequestAndResponse(const QModbusResponse &response, int sendingServer) const { - if (m_current.reply.isNull()) + if (m_queue.isEmpty()) + return false; + const auto ¤t = m_queue.first(); + + if (current.reply.isNull()) return false; // reply deleted - if (m_current.reply->serverAddress() != sendingServer) + if (current.reply->serverAddress() != sendingServer) return false; // server mismatch - if (m_current.requestPdu.functionCode() != response.functionCode()) + if (current.requestPdu.functionCode() != response.functionCode()) return false; // request for different function code return true; } @@ -397,18 +427,18 @@ public: return false; } - QTimer m_sendTimer; - QTimer m_responseTimer; - - QueueElement m_current; - QByteArray responseBuffer; + Timer m_responseTimer; + QByteArray m_responseBuffer; QQueue<QueueElement> m_queue; QSerialPort *m_serialPort = nullptr; int m_interFrameDelayMilliseconds = 2; // A approximated value of 1.750 msec. + int m_turnaroundDelay = 100; // Recommended value is between 100 and 200 msec. }; QT_END_NAMESPACE +#include "qmodbusrtuserialmaster_p.h" + #endif // QMODBUSSERIALMASTER_P_H diff --git a/src/serialbus/qmodbusrtuserialslave.cpp b/src/serialbus/qmodbusrtuserialslave.cpp index 899bd8d..36a845c 100644 --- a/src/serialbus/qmodbusrtuserialslave.cpp +++ b/src/serialbus/qmodbusrtuserialslave.cpp @@ -158,6 +158,6 @@ QModbusResponse QModbusRtuSerialSlave::processRequest(const QModbusPdu &request) return QModbusServer::processRequest(request); } -#include "moc_qmodbusrtuserialslave.cpp" - QT_END_NAMESPACE + +#include "moc_qmodbusrtuserialslave.cpp" diff --git a/src/serialbus/qmodbusrtuserialslave_p.h b/src/serialbus/qmodbusrtuserialslave_p.h index e41aa7a..da59e5e 100644 --- a/src/serialbus/qmodbusrtuserialslave_p.h +++ b/src/serialbus/qmodbusrtuserialslave_p.h @@ -74,7 +74,7 @@ public: Q_Q(QModbusRtuSerialSlave); m_serialPort = new QSerialPort(q); - QObject::connect(m_serialPort, &QSerialPort::readyRead, [this]() { + QObject::connect(m_serialPort, &QSerialPort::readyRead, q, [this]() { if (m_interFrameTimer.isValid() && m_interFrameTimer.elapsed() > m_interFrameDelayMilliseconds @@ -90,7 +90,7 @@ public: m_interFrameTimer.start(); - const int size = m_serialPort->size(); + const qint64 size = m_serialPort->size(); m_requestBuffer += m_serialPort->read(size); const QModbusSerialAdu adu(QModbusSerialAdu::Rtu, m_requestBuffer); @@ -218,7 +218,7 @@ public: return; } - int writtenBytes = m_serialPort->write(result); + qint64 writtenBytes = m_serialPort->write(result); if ((writtenBytes == -1) || (writtenBytes < result.size())) { qCDebug(QT_MODBUS) << "(RTU server) Cannot write requested response to serial port."; q->setError(QModbusRtuSerialSlave::tr("Could not write response to client"), @@ -281,7 +281,7 @@ public: }); using TypeId = void (QSerialPort::*)(QSerialPort::SerialPortError); - QObject::connect(m_serialPort, static_cast<TypeId>(&QSerialPort::error), + QObject::connect(m_serialPort, static_cast<TypeId>(&QSerialPort::error), q, [this](QSerialPort::SerialPortError error) { if (error == QSerialPort::NoError) return; @@ -330,7 +330,7 @@ public: } }); - QObject::connect(m_serialPort, &QSerialPort::aboutToClose, [this]() { + QObject::connect(m_serialPort, &QSerialPort::aboutToClose, q, [this]() { Q_Q(QModbusRtuSerialSlave); // update state if socket closure was caused by remote side if (q->state() != QModbusDevice::ClosingState) diff --git a/src/serialbus/qmodbustcpclient_p.h b/src/serialbus/qmodbustcpclient_p.h index e4dfa9e..db1d4e4 100644 --- a/src/serialbus/qmodbustcpclient_p.h +++ b/src/serialbus/qmodbustcpclient_p.h @@ -71,7 +71,7 @@ public: m_socket = new QTcpSocket(q); - QObject::connect(m_socket, &QAbstractSocket::connected, [this]() { + QObject::connect(m_socket, &QAbstractSocket::connected, q, [this]() { qCDebug(QT_MODBUS) << "(TCP client) Connected to" << m_socket->peerAddress() << "on port" << m_socket->peerPort(); Q_Q(QModbusTcpClient); @@ -79,7 +79,7 @@ public: q->setState(QModbusDevice::ConnectedState); }); - QObject::connect(m_socket, &QAbstractSocket::disconnected, [this]() { + QObject::connect(m_socket, &QAbstractSocket::disconnected, q, [this]() { qCDebug(QT_MODBUS) << "(TCP client) Connection closed."; Q_Q(QModbusTcpClient); q->setState(QModbusDevice::UnconnectedState); @@ -87,7 +87,7 @@ public: }); using TypeId = void (QAbstractSocket::*)(QAbstractSocket::SocketError); - QObject::connect(m_socket, static_cast<TypeId>(&QAbstractSocket::error), + QObject::connect(m_socket, static_cast<TypeId>(&QAbstractSocket::error), q, [this](QAbstractSocket::SocketError /*error*/) { Q_Q(QModbusTcpClient); @@ -100,7 +100,7 @@ public: QModbusDevice::ConnectionError); }); - QObject::connect(m_socket, &QIODevice::readyRead, [this](){ + QObject::connect(m_socket, &QIODevice::readyRead, q, [this](){ responseBuffer += m_socket->read(m_socket->bytesAvailable()); qCDebug(QT_MODBUS_LOW) << "(TCP client) Response buffer:" << responseBuffer.toHex(); @@ -196,7 +196,7 @@ public: using TypeId = void (QTimer::*)(int); q->connect(q, &QModbusClient::timeoutChanged, element.timer.data(), static_cast<TypeId>(&QTimer::setInterval)); - QObject::connect(element.timer.data(), &QTimer::timeout, [this, writeToSocket, tId]() { + QObject::connect(element.timer.data(), &QTimer::timeout, q, [this, writeToSocket, tId]() { if (!m_transactionStore.contains(tId)) return; diff --git a/src/serialbus/qmodbustcpserver.cpp b/src/serialbus/qmodbustcpserver.cpp index 39aeda8..3a15e9e 100644 --- a/src/serialbus/qmodbustcpserver.cpp +++ b/src/serialbus/qmodbustcpserver.cpp @@ -110,7 +110,7 @@ bool QModbusTcpServer::open() return false; } - if (d->m_tcpServer->listen(QHostAddress(url.host()), url.port())) + if (d->m_tcpServer->listen(QHostAddress(url.host()), quint16(url.port()))) setState(QModbusDevice::ConnectedState); else setError(d->m_tcpServer->errorString(), QModbusDevice::ConnectionError); @@ -171,4 +171,66 @@ QModbusResponse QModbusTcpServer::processRequest(const QModbusPdu &request) return QModbusServer::processRequest(request); } +/*! + Installs an \a observer that can be used to obtain notifications when a + new TCP client connects to this server instance. In addition, the \a observer + can be used to reject the incoming TCP connection. + + QModbusTcpServer takes ownership of the given \a observer. Any previously set + observer will be deleted. The observer can be uninstalled by calling this + function with \c nullptr as parameter. + + \sa QModbusTcpConnectionObserver + \since 5.13 +*/ +void QModbusTcpServer::installConnectionObserver(QModbusTcpConnectionObserver *observer) +{ + Q_D(QModbusTcpServer); + + d->m_observer.reset(observer); +} + +/*! + \class QModbusTcpConnectionObserver + \inmodule QtSerialBus + \since 5.13 + + \brief The QModbusTcpConnectionObserver class represents the interface for + objects that can be passed to \l QModbusTcpServer::installConnectionObserver. + + The interface must be implemented by the developer to be able to monitor + every incoming TCP connection from another Modbus client. + + \sa QModbusTcpServer::installConnectionObserver +*/ + +QModbusTcpConnectionObserver::~QModbusTcpConnectionObserver() +{ +} + +/*! + \fn bool QModbusTcpConnectionObserver::acceptNewConnection(QTcpSocket *newClient) + + This function is a callback for every incoming TCP connection. The user should + provide \a newClient to receive a notification when a new client connection + is established and to determine whether the connection is to be accepted. + + The function should return \c true if the connection is to be accepted. Otherwise, + the socket is closed/rejected. +*/ + +/*! + \fn void QModbusTcpServer::modbusClientDisconnected(QTcpSocket *modbusClient) + + This signal is emitted when a current TCP based \a modbusClient disconnects + from this Modbus TCP server. Note that there might be several TCP clients + connected at the same time. + + Notifications on incoming new connections can be received by installing a + QModbusTcpConnectionObserver via \l installConnectionObserver(). + + \sa installConnectionObserver + \since 5.13 +*/ + QT_END_NAMESPACE diff --git a/src/serialbus/qmodbustcpserver.h b/src/serialbus/qmodbustcpserver.h index 57276a5..cf24d42 100644 --- a/src/serialbus/qmodbustcpserver.h +++ b/src/serialbus/qmodbustcpserver.h @@ -43,6 +43,15 @@ QT_BEGIN_NAMESPACE class QModbusTcpServerPrivate; +class QTcpSocket; + +class Q_SERIALBUS_EXPORT QModbusTcpConnectionObserver +{ +public: + virtual ~QModbusTcpConnectionObserver(); + + virtual bool acceptNewConnection(QTcpSocket *newClient) = 0; +}; class Q_SERIALBUS_EXPORT QModbusTcpServer : public QModbusServer { @@ -53,6 +62,11 @@ public: explicit QModbusTcpServer(QObject *parent = nullptr); ~QModbusTcpServer(); + void installConnectionObserver(QModbusTcpConnectionObserver *observer); + +Q_SIGNALS: + void modbusClientDisconnected(QTcpSocket *modbusClient); + protected: QModbusTcpServer(QModbusTcpServerPrivate &dd, QObject *parent = nullptr); diff --git a/src/serialbus/qmodbustcpserver_p.h b/src/serialbus/qmodbustcpserver_p.h index 90d1eaf..56c45e9 100644 --- a/src/serialbus/qmodbustcpserver_p.h +++ b/src/serialbus/qmodbustcpserver_p.h @@ -48,6 +48,8 @@ #include <private/qmodbusserver_p.h> +#include <memory> + // // W A R N I N G // ------------- @@ -113,9 +115,9 @@ public: void setupTcpServer() { - Q_Q(QModbusTcpServer); - m_tcpServer = new QTcpServer(q); - QObject::connect(m_tcpServer, &QTcpServer::newConnection, [this]() { + m_tcpServer = new QTcpServer(q_func()); + QObject::connect(m_tcpServer, &QTcpServer::newConnection, q_func(), [this]() { + Q_Q(QModbusTcpServer); auto *socket = m_tcpServer->nextPendingConnection(); if (!socket) return; @@ -123,19 +125,29 @@ public: qCDebug(QT_MODBUS) << "(TCP server) Incoming socket from" << socket->peerAddress() << socket->peerName() << socket->peerPort(); + if (m_observer && !m_observer->acceptNewConnection(socket)) { + qCDebug(QT_MODBUS) << "(TCP server) Connection rejected by observer"; + socket->close(); + socket->deleteLater(); + return; + } + connections.append(socket); auto buffer = new QByteArray(); - QObject::connect(socket, &QObject::destroyed, [buffer]() { + QObject::connect(socket, &QObject::destroyed, q, [buffer]() { // cleanup buffer delete buffer; }); - QObject::connect(socket, &QTcpSocket::disconnected, [socket, this]() { + QObject::connect(socket, &QTcpSocket::disconnected, q, [socket, this]() { connections.removeAll(socket); + + Q_Q(QModbusTcpServer); + emit q->modbusClientDisconnected(socket); socket->deleteLater(); }); - QObject::connect(socket, &QTcpSocket::readyRead, [buffer, socket, this]() { + QObject::connect(socket, &QTcpSocket::readyRead, q, [buffer, socket, this]() { if (!socket) return; @@ -193,7 +205,7 @@ public: return; } - int writtenBytes = socket->write(result); + qint64 writtenBytes = socket->write(result); if (writtenBytes == -1 || writtenBytes < result.size()) { qCDebug(QT_MODBUS) << "(TCP server) Cannot write requested response to socket."; forwardError(QModbusTcpServer::tr("Could not write response to client"), @@ -202,7 +214,8 @@ public: } }); }); - QObject::connect(m_tcpServer, &QTcpServer::acceptError, + + QObject::connect(m_tcpServer, &QTcpServer::acceptError, q_func(), [this](QAbstractSocket::SocketError /*sError*/) { Q_Q(QModbusTcpServer); @@ -214,6 +227,8 @@ public: QTcpServer *m_tcpServer; QVector<QTcpSocket *> connections; + std::unique_ptr<QModbusTcpConnectionObserver> m_observer; + static const qint8 mbpaHeaderSize = 7; static const qint16 maxBytesModbusADU = 260; }; diff --git a/src/serialbus/qserialbusglobal.h b/src/serialbus/qtserialbusglobal.h index 736c4a8..31c1f85 100644 --- a/src/serialbus/qserialbusglobal.h +++ b/src/serialbus/qtserialbusglobal.h @@ -34,10 +34,11 @@ ** ****************************************************************************/ -#ifndef QSERIALBUSGLOBAL_H -#define QSERIALBUSGLOBAL_H +#ifndef QTSERIALBUSGLOBAL_H +#define QTSERIALBUSGLOBAL_H #include <QtCore/qglobal.h> +#include <QtSerialBus/qtserialbus-config.h> QT_BEGIN_NAMESPACE @@ -53,4 +54,4 @@ QT_BEGIN_NAMESPACE QT_END_NAMESPACE -#endif // QSERIALBUSGLOBAL_H +#endif // QTSERIALBUSGLOBAL_H diff --git a/src/serialbus/serialbus.pro b/src/serialbus/serialbus.pro index 14583bc..3abba31 100644 --- a/src/serialbus/serialbus.pro +++ b/src/serialbus/serialbus.pro @@ -1,7 +1,8 @@ TARGET = QtSerialBus QT = core-private -QT_FOR_PRIVATE = network serialport +QT_FOR_PRIVATE = network + CONFIG += c++11 QMAKE_DOCS = $$PWD/doc/qtserialbus.qdocconf @@ -12,16 +13,14 @@ PUBLIC_HEADERS += \ qcanbusfactory.h \ qcanbusframe.h \ qcanbus.h \ - qserialbusglobal.h \ + qtserialbusglobal.h \ qmodbusserver.h \ qmodbusdevice.h \ qmodbusdataunit.h \ qmodbusclient.h \ qmodbusreply.h \ - qmodbusrtuserialmaster.h \ qmodbustcpclient.h \ qmodbustcpserver.h \ - qmodbusrtuserialslave.h \ qmodbuspdu.h \ qmodbusdeviceidentification.h @@ -31,10 +30,8 @@ PRIVATE_HEADERS += \ qmodbusserver_p.h \ qmodbusclient_p.h \ qmodbusdevice_p.h \ - qmodbusrtuserialmaster_p.h \ qmodbustcpclient_p.h \ qmodbustcpserver_p.h \ - qmodbusrtuserialslave_p.h \ qmodbus_symbols_p.h \ qmodbuscommevent_p.h \ qmodbusadu_p.h \ @@ -50,13 +47,26 @@ SOURCES += \ qmodbusdataunit.cpp \ qmodbusclient.cpp \ qmodbusreply.cpp \ - qmodbusrtuserialmaster.cpp \ qmodbustcpclient.cpp \ qmodbustcpserver.cpp \ - qmodbusrtuserialslave.cpp \ qmodbuspdu.cpp \ qmodbusdeviceidentification.cpp +qtConfig(modbus-serialport) { + QT_FOR_PRIVATE += serialport + + PUBLIC_HEADERS += \ + qmodbusrtuserialmaster.h \ + qmodbusrtuserialslave.h + + PRIVATE_HEADERS += \ + qmodbusrtuserialmaster_p.h \ + qmodbusrtuserialslave_p.h + + SOURCES += \ + qmodbusrtuserialmaster.cpp \ + qmodbusrtuserialslave.cpp +} HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS MODULE_PLUGIN_TYPES = \ diff --git a/sync.profile b/sync.profile index e7125c6..588f4e1 100644 --- a/sync.profile +++ b/sync.profile @@ -1,3 +1,8 @@ %modules = ( "QtSerialBus" => "$basedir/src/serialbus", ); +%deprecatedheaders = ( + "QtSerialBus" => { + "qserialbusglobal.h" => "QtSerialBus/qtserialbusglobal.h" + }, +); diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 2542d65..268fc74 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -11,8 +11,11 @@ SUBDIRS += cmake \ qmodbusserver \ qmodbuscommevent \ qmodbusadu \ - qmodbusdeviceidentification \ - qmodbusrtuserialmaster + qmodbusdeviceidentification + +QT_FOR_CONFIG += serialbus + +qtConfig(modbus-serialport): SUBDIRS += qmodbusrtuserialmaster qcanbus.depends += plugins qcanbusdevice.depends += plugins diff --git a/tests/auto/qmodbusserver/tst_qmodbusserver.cpp b/tests/auto/qmodbusserver/tst_qmodbusserver.cpp index 6fe284d..ad33337 100644 --- a/tests/auto/qmodbusserver/tst_qmodbusserver.cpp +++ b/tests/auto/qmodbusserver/tst_qmodbusserver.cpp @@ -35,7 +35,9 @@ ****************************************************************************/ #include <QtSerialBus/qmodbusserver.h> +#if QT_CONFIG(modbus_serialport) #include <QtSerialBus/qmodbusrtuserialslave.h> +#endif #include <QtSerialBus/qmodbustcpserver.h> #include <QtSerialBus/qmodbusdeviceidentification.h> @@ -90,7 +92,9 @@ private slots: void testServerAddress() { QCOMPARE(QModbusTcpServer().serverAddress(), 0xff); +#if QT_CONFIG(modbus_serialport) QCOMPARE(QModbusRtuSerialSlave().serverAddress(), 1); +#endif } void testProcessRequestReadWriteSingleMultipleCoils() |