summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarsten Heimrich <karsten.heimrich@qt.io>2021-02-15 17:05:33 +0100
committerKarsten Heimrich <karsten.heimrich@qt.io>2021-03-01 12:04:57 +0100
commitc88db0ee12ca941d9b37b8e6e5b4f94ac293d866 (patch)
treee91299d4115ae6362f537d3780e64e8c73c9bcd2
parent09927894825d97197ffa656576e6be780371dda0 (diff)
Add Modbus custom function code example
Task-number: QTBUG-91212 Change-Id: I5df9e33186acb5216335ec1010f6694bcbab9376 Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
-rw-r--r--examples/serialbus/modbus/CMakeLists.txt2
-rw-r--r--examples/serialbus/modbus/custom/CMakeLists.txt58
-rw-r--r--examples/serialbus/modbus/custom/custom.pro25
-rw-r--r--examples/serialbus/modbus/custom/images.qrc7
-rw-r--r--examples/serialbus/modbus/custom/images/application-exit.pngbin0 -> 11200 bytes
-rw-r--r--examples/serialbus/modbus/custom/images/connect.pngbin0 -> 15374 bytes
-rw-r--r--examples/serialbus/modbus/custom/images/disconnect.pngbin0 -> 15092 bytes
-rw-r--r--examples/serialbus/modbus/custom/images/settings.pngbin0 -> 16039 bytes
-rw-r--r--examples/serialbus/modbus/custom/main.cpp66
-rw-r--r--examples/serialbus/modbus/custom/mainwindow.cpp314
-rw-r--r--examples/serialbus/modbus/custom/mainwindow.h105
-rw-r--r--examples/serialbus/modbus/custom/mainwindow.ui650
-rw-r--r--examples/serialbus/modbus/custom/modbusclient.cpp134
-rw-r--r--examples/serialbus/modbus/custom/modbusclient.h71
-rw-r--r--examples/serialbus/modbus/custom/modbusserver.cpp131
-rw-r--r--examples/serialbus/modbus/custom/modbusserver.h68
-rw-r--r--examples/serialbus/modbus/custom/registermodel.cpp150
-rw-r--r--examples/serialbus/modbus/custom/registermodel.h85
-rw-r--r--examples/serialbus/modbus/modbus.pro3
19 files changed, 1868 insertions, 1 deletions
diff --git a/examples/serialbus/modbus/CMakeLists.txt b/examples/serialbus/modbus/CMakeLists.txt
index 8d63950..2e6b6cb 100644
--- a/examples/serialbus/modbus/CMakeLists.txt
+++ b/examples/serialbus/modbus/CMakeLists.txt
@@ -1,5 +1,7 @@
add_subdirectory(master)
add_subdirectory(slave)
+add_subdirectory(custom)
+
if(QT_FEATURE_modbus_serialport)
add_subdirectory(adueditor)
endif()
diff --git a/examples/serialbus/modbus/custom/CMakeLists.txt b/examples/serialbus/modbus/custom/CMakeLists.txt
new file mode 100644
index 0000000..cfe398f
--- /dev/null
+++ b/examples/serialbus/modbus/custom/CMakeLists.txt
@@ -0,0 +1,58 @@
+cmake_minimum_required(VERSION 3.14)
+project(customcommand LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/serialbus/modbus/custom")
+
+find_package(Qt6 COMPONENTS Core)
+find_package(Qt6 COMPONENTS Gui)
+find_package(Qt6 COMPONENTS SerialBus)
+find_package(Qt6 COMPONENTS Widgets)
+
+qt_add_executable(customcommand
+ main.cpp
+ mainwindow.cpp mainwindow.h mainwindow.ui
+ modbusclient.cpp modbusclient.h
+ modbusserver.cpp modbusserver.h
+ registermodel.cpp registermodel.h
+)
+
+set_target_properties(customcommand PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(customcommand PRIVATE
+ Qt::Core
+ Qt::Gui
+ Qt::SerialBus
+ Qt::Widgets
+)
+
+set(custom_resource_files
+ "images/connect.png"
+ "images/disconnect.png"
+ "images/settings.png"
+)
+
+qt6_add_resources(customcommand "custom"
+ PREFIX
+ "/"
+ FILES
+ ${custom_resource_files}
+)
+
+install(TARGETS customcommand
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/serialbus/modbus/custom/custom.pro b/examples/serialbus/modbus/custom/custom.pro
new file mode 100644
index 0000000..c849c1a
--- /dev/null
+++ b/examples/serialbus/modbus/custom/custom.pro
@@ -0,0 +1,25 @@
+QT += serialbus widgets
+
+TARGET = customcommand
+TEMPLATE = app
+CONFIG += c++11
+
+SOURCES += main.cpp \
+ mainwindow.cpp \
+ modbusclient.cpp \
+ modbusserver.cpp \
+ registermodel.cpp
+
+HEADERS += \
+ mainwindow.h \
+ modbusclient.h \
+ modbusserver.h \
+ registermodel.h
+
+FORMS += \
+ mainwindow.ui
+
+RESOURCES += images.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/serialbus/modbus/custom
+INSTALLS += target
diff --git a/examples/serialbus/modbus/custom/images.qrc b/examples/serialbus/modbus/custom/images.qrc
new file mode 100644
index 0000000..d212059
--- /dev/null
+++ b/examples/serialbus/modbus/custom/images.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/connect.png</file>
+ <file>images/disconnect.png</file>
+ <file>images/settings.png</file>
+ </qresource>
+</RCC>
diff --git a/examples/serialbus/modbus/custom/images/application-exit.png b/examples/serialbus/modbus/custom/images/application-exit.png
new file mode 100644
index 0000000..32be6b3
--- /dev/null
+++ b/examples/serialbus/modbus/custom/images/application-exit.png
Binary files differ
diff --git a/examples/serialbus/modbus/custom/images/connect.png b/examples/serialbus/modbus/custom/images/connect.png
new file mode 100644
index 0000000..dd5a51e
--- /dev/null
+++ b/examples/serialbus/modbus/custom/images/connect.png
Binary files differ
diff --git a/examples/serialbus/modbus/custom/images/disconnect.png b/examples/serialbus/modbus/custom/images/disconnect.png
new file mode 100644
index 0000000..fd58f7a
--- /dev/null
+++ b/examples/serialbus/modbus/custom/images/disconnect.png
Binary files differ
diff --git a/examples/serialbus/modbus/custom/images/settings.png b/examples/serialbus/modbus/custom/images/settings.png
new file mode 100644
index 0000000..3d1042e
--- /dev/null
+++ b/examples/serialbus/modbus/custom/images/settings.png
Binary files differ
diff --git a/examples/serialbus/modbus/custom/main.cpp b/examples/serialbus/modbus/custom/main.cpp
new file mode 100644
index 0000000..f42e918
--- /dev/null
+++ b/examples/serialbus/modbus/custom/main.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtSerialBus module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+
+#include <QApplication>
+#include <QLoggingCategory>
+
+int main(int argc, char *argv[])
+{
+ // Uncomment the following line to enable logging
+ // QLoggingCategory::setFilterRules(QStringLiteral("qt.modbus* = true"));
+
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.show();
+
+ return a.exec();
+}
diff --git a/examples/serialbus/modbus/custom/mainwindow.cpp b/examples/serialbus/modbus/custom/mainwindow.cpp
new file mode 100644
index 0000000..cd93d78
--- /dev/null
+++ b/examples/serialbus/modbus/custom/mainwindow.cpp
@@ -0,0 +1,314 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtSerialBus module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+#include "registermodel.h"
+#include "ui_mainwindow.h"
+
+#include <QtWidgets>
+#include <QtSerialBus>
+
+MainWindow::MainWindow(QWidget* parent)
+ : QMainWindow(parent)
+ , ui(new Ui::MainWindow)
+{
+ ui->setupUi(this);
+ ui->statusbar->setSizeGripEnabled(false);
+
+ setupConnections();
+ setupClientContainer();
+ setupServerContainer();
+}
+
+MainWindow::~MainWindow()
+{
+ m_client.disconnectDevice();
+ m_server.disconnectDevice();
+
+ delete ui;
+}
+
+void MainWindow::setupConnections()
+{
+ // actions
+ connect(ui->actionConnect, &QAction::triggered, this, &MainWindow::onConnectButtonClicked);
+ connect(ui->actionDisconnect, &QAction::triggered, this, &MainWindow::onConnectButtonClicked);
+ connect(ui->actionExit, &QAction::triggered, this, &QMainWindow::close);
+
+ // buttons
+ connect(ui->readButton, &QPushButton::clicked, this, &MainWindow::onReadButtonClicked);
+ connect(ui->writeButton, &QPushButton::clicked, this, &MainWindow::onWriteButtonClicked);
+ connect(ui->connectButton, &QPushButton::clicked, this, &MainWindow::onConnectButtonClicked);
+
+ // client
+ connect(&m_client, &QModbusServer::stateChanged, this, &MainWindow::onStateChanged);
+ connect(&m_client, &QModbusServer::errorOccurred, [this](QModbusDevice::Error) {
+ statusBar()->showMessage(m_client.errorString(), 5000);
+ });
+
+ // server
+ connect(&m_server, &QModbusServer::dataWritten, this, &MainWindow::updateWidgets);
+ connect(&m_server, &QModbusServer::errorOccurred, [this](QModbusDevice::Error) {
+ statusBar()->showMessage(m_server.errorString(), 5000);
+ });
+}
+
+void MainWindow::onConnectButtonClicked()
+{
+ bool intendToConnect = (m_server.state() == QModbusDevice::UnconnectedState
+ && m_client.state() == QModbusDevice::UnconnectedState);
+
+ if (intendToConnect) {
+ const QUrl url = QUrl::fromUserInput(ui->addressPort->text());
+ m_server.setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
+ m_server.setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
+ m_server.setServerAddress(ui->serverAddress->text().toInt());
+
+ if (m_server.connectDevice()) {
+ m_client.setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
+ m_client.setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
+
+ if (m_client.connectDevice()) {
+ ui->actionConnect->setEnabled(false);
+ ui->actionDisconnect->setEnabled(true);
+ } else {
+ statusBar()->showMessage(tr("Client connect failed: ") + m_client.errorString(), 5000);
+ }
+ } else {
+ statusBar()->showMessage(tr("Server connect failed: ") + m_server.errorString(), 5000);
+ }
+ } else {
+ m_client.disconnectDevice();
+ m_server.disconnectDevice();
+ ui->actionConnect->setEnabled(true);
+ ui->actionDisconnect->setEnabled(false);
+ }
+}
+
+void MainWindow::onStateChanged(int state)
+{
+ ui->serverAddress->setEnabled(state == QModbusDevice::UnconnectedState);
+ ui->actionConnect->setEnabled(state == QModbusDevice::UnconnectedState);
+ ui->actionDisconnect->setEnabled(state == QModbusDevice::ConnectedState);
+
+ if (state == QModbusDevice::UnconnectedState)
+ ui->connectButton->setText(tr("Connect"));
+ else if (state == QModbusDevice::ConnectedState)
+ ui->connectButton->setText(tr("Disconnect"));
+}
+
+// -- client
+
+void MainWindow::onReadReady()
+{
+ auto reply = qobject_cast<QModbusReply*>(sender());
+ if (!reply)
+ return;
+
+ if (reply->error() == QModbusDevice::NoError) {
+ const QModbusDataUnit unit = reply->result();
+ for (int i = 0, total = int(unit.valueCount()); i < total; ++i) {
+ auto st = unit.startAddress();
+ m_model->setData(m_model->index(unit.startAddress() + i, 1),
+ QString::number(unit.value(i), 16), Qt::EditRole);
+ }
+ } else if (reply->error() == QModbusDevice::ProtocolError) {
+ statusBar()->showMessage(tr("Read response error: %1 (Modbus exception: 0x%2)").
+ arg(reply->errorString()).
+ arg(reply->rawResult().exceptionCode(), -1, 16), 5000);
+ } else {
+ statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)").
+ arg(reply->errorString()).
+ arg(reply->error(), -1, 16), 5000);
+ }
+
+ reply->deleteLater();
+}
+
+void MainWindow::onReadButtonClicked()
+{
+ QModbusRequest readRequest {
+ QModbusPdu::FunctionCode(ModbusClient::CustomRead),
+ quint16(ui->startAddress->value()),
+ qMin(ui->numberOfRegisters->currentText().toUShort(),
+ quint16(10 - ui->startAddress->value())) // do not go beyond 10 entries
+ };
+
+ if (auto* reply = m_client.sendRawRequest(readRequest, ui->serverAddress->value())) {
+ if (!reply->isFinished())
+ connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady);
+ } else {
+ statusBar()->showMessage(tr("Read error: ") + m_client.errorString(), 5000);
+ }
+}
+
+void MainWindow::onWriteButtonClicked()
+{
+ QModbusDataUnit unit {
+ QModbusDataUnit::HoldingRegisters,
+ ui->startAddress->value(),
+ qMin(ui->numberOfRegisters->currentText().toUShort(),
+ quint16(10 - ui->startAddress->value())) // do not go beyond 10 entries
+ };
+
+ for (qsizetype i = 0, total = int(unit.valueCount()); i < total; ++i)
+ unit.setValue(i, m_model->m_registers[i + unit.startAddress()]);
+
+ const quint8 byteCount = unit.valueCount() * 2;
+ QModbusRequest writeRequest {
+ QModbusPdu::FunctionCode(ModbusClient::CustomWrite),
+ quint16(unit.startAddress()),
+ quint16(unit.valueCount()), byteCount, unit.values()
+ };
+
+ if (auto *reply = m_client.sendRawRequest(writeRequest, ui->serverAddress->value())) {
+ if (!reply->isFinished()) {
+ connect(reply, &QModbusReply::finished, this, [this, reply]() {
+ if (reply->error() == QModbusDevice::ProtocolError) {
+ statusBar()->showMessage(tr("Write response error: %1 (Modbus exception: 0x%2)")
+ .arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16),
+ 5000);
+ } else if (reply->error() != QModbusDevice::NoError) {
+ statusBar()->showMessage(tr("Write response error: %1 (code: 0x%2)").
+ arg(reply->errorString()).arg(reply->error(), -1, 16), 5000);
+ }
+
+ reply->deleteLater();
+ }
+ );
+ }
+ } else {
+ statusBar()->showMessage(tr("Write error: ") + m_client.errorString(), 5000);
+ }
+}
+
+void MainWindow::setupClientContainer()
+{
+ m_model = new RegisterModel(this);
+ m_model->setStartAddress(ui->startAddress->value());
+ m_model->setNumberOfValues(ui->numberOfRegisters->currentText());
+
+ ui->registersTable->setModel(m_model);
+ connect(m_model, &RegisterModel::updateViewport, ui->registersTable->viewport(),
+ QOverload<>::of(&QWidget::update));
+
+ auto model = new QStandardItemModel(10, 1, this);
+ for (int i = 0; i < 10; ++i)
+ model->setItem(i, new QStandardItem(QStringLiteral("%1").arg(i + 1)));
+ ui->numberOfRegisters->setModel(model);
+ ui->numberOfRegisters->setCurrentText("10");
+ connect(ui->numberOfRegisters, &QComboBox::currentTextChanged,
+ m_model, &RegisterModel::setNumberOfValues);
+
+ connect(ui->startAddress, &QSpinBox::valueChanged, m_model, &RegisterModel::setStartAddress);
+ connect(ui->startAddress, &QSpinBox::valueChanged, this, [this, model](int i) {
+ int lastPossibleIndex = 0;
+ const int currentIndex = ui->numberOfRegisters->currentIndex();
+ for (int ii = 0; ii < 10; ++ii) {
+ if (ii < (10 - i)) {
+ lastPossibleIndex = ii;
+ model->item(ii)->setEnabled(true);
+ } else {
+ model->item(ii)->setEnabled(false);
+ }
+ }
+ if (currentIndex > lastPossibleIndex)
+ ui->numberOfRegisters->setCurrentIndex(lastPossibleIndex);
+ }
+ );
+}
+
+// -- server
+
+void MainWindow::setRegister(const QString &value)
+{
+ const QString objectName = QObject::sender()->objectName();
+ if (m_registers.contains(objectName)) {
+ const quint16 id = quint16(QObject::sender()->property("ID").toUInt());
+
+ bool ok = false;
+ ok = m_server.setData(QModbusDataUnit::HoldingRegisters, id, value.toUShort(&ok, 16));
+ if (!ok)
+ statusBar()->showMessage(tr("Could not set register: ") + m_server.errorString(), 5000);
+ }
+}
+
+void MainWindow::updateWidgets(QModbusDataUnit::RegisterType table, int address, int size)
+{
+ if (table != QModbusDataUnit::HoldingRegisters)
+ return;
+
+ QString text;
+ quint16 value;
+ for (int i = 0; i < size; ++i) {
+ m_server.data(table, quint16(address + i), &value);
+ m_registers.value(QStringLiteral("register_%1").arg(address + i))->setText(text.setNum(value, 16));
+ }
+}
+
+void MainWindow::setupServerContainer()
+{
+ QRegularExpression regexp(QStringLiteral("register_(?<ID>\\d+)"));
+ const QList<QLineEdit *> qle = ui->server->findChildren<QLineEdit *>(regexp);
+ for (QLineEdit *lineEdit : qle) {
+ const quint16 id = regexp.match(lineEdit->objectName()).captured(QStringLiteral("ID")).toInt();
+
+ lineEdit->setProperty("ID", id);
+ m_registers.insert(lineEdit->objectName(), lineEdit);
+ lineEdit->setValidator(new QRegularExpressionValidator(
+ QRegularExpression(QStringLiteral("[0-9a-f]{0,4}"), QRegularExpression::CaseInsensitiveOption),
+ this)
+ );
+
+ bool ok;
+ m_server.setData(QModbusDataUnit::HoldingRegisters, id, lineEdit->text().toUShort(&ok, 16));
+ connect(lineEdit, &QLineEdit::textChanged, this, &MainWindow::setRegister);
+ }
+}
diff --git a/examples/serialbus/modbus/custom/mainwindow.h b/examples/serialbus/modbus/custom/mainwindow.h
new file mode 100644
index 0000000..e538da3
--- /dev/null
+++ b/examples/serialbus/modbus/custom/mainwindow.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtSerialBus module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "modbusclient.h"
+#include "modbusserver.h"
+
+#include <QHash>
+#include <QMainWindow>
+
+QT_BEGIN_NAMESPACE
+
+class QLineEdit;
+
+namespace Ui {
+class MainWindow;
+}
+
+QT_END_NAMESPACE
+
+class RegisterModel;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = nullptr);
+ ~MainWindow();
+
+private Q_SLOTS:
+ void onConnectButtonClicked();
+ void onStateChanged(int state);
+
+ void onReadReady();
+ void onReadButtonClicked();
+ void onWriteButtonClicked();
+
+ void setRegister(const QString &value);
+ void updateWidgets(QModbusDataUnit::RegisterType table, int address, int size);
+
+private:
+ void setupConnections();
+ void setupClientContainer();
+ void setupServerContainer();
+
+private:
+ Ui::MainWindow *ui = nullptr;
+ RegisterModel *m_model = nullptr;
+
+ ModbusClient m_client;
+ ModbusServer m_server;
+ QHash<QString, QLineEdit *> m_registers;
+};
+
+#endif // MAINWINDOW_H
diff --git a/examples/serialbus/modbus/custom/mainwindow.ui b/examples/serialbus/modbus/custom/mainwindow.ui
new file mode 100644
index 0000000..d8e6099
--- /dev/null
+++ b/examples/serialbus/modbus/custom/mainwindow.ui
@@ -0,0 +1,650 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>620</width>
+ <height>482</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Address &amp; Port:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="addressPort">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>127.0.0.1:502</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Maximum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>13</width>
+ <height>17</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="label_28">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Server Address:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QSpinBox" name="serverAddress">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>247</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="5">
+ <widget class="QPushButton" name="connectButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Connect</string>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="6">
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Custom Function code (Read):</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_9">
+ <property name="font">
+ <font>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>0X41</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" rowspan="2">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="3" rowspan="2">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>The Modbus specification reserves two ranges of user-defined function codes, i.e. 0x41 to 0x48 and from 0x64 to 0x6e.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Custom Function code (Write):</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label_10">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>0X42</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QGroupBox" name="server">
+ <property name="title">
+ <string>Server</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="address">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Address</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Registers</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_21">
+ <property name="text">
+ <string>0</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="register_0">
+ <property name="maxLength">
+ <number>32767</number>
+ </property>
+ <property name="placeholderText">
+ <string>Hexadecimal A-F, a-f, 0-9.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_22">
+ <property name="text">
+ <string>1</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="register_1">
+ <property name="maxLength">
+ <number>32767</number>
+ </property>
+ <property name="placeholderText">
+ <string>Hexadecimal A-F, a-f, 0-9.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_23">
+ <property name="text">
+ <string>2</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="register_2">
+ <property name="maxLength">
+ <number>32767</number>
+ </property>
+ <property name="placeholderText">
+ <string>Hexadecimal A-F, a-f, 0-9.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>3</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="register_3">
+ <property name="maxLength">
+ <number>32767</number>
+ </property>
+ <property name="placeholderText">
+ <string>Hexadecimal A-F, a-f, 0-9.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>4</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLineEdit" name="register_4">
+ <property name="maxLength">
+ <number>32767</number>
+ </property>
+ <property name="placeholderText">
+ <string>Hexadecimal A-F, a-f, 0-9.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>5</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QLineEdit" name="register_5">
+ <property name="maxLength">
+ <number>32767</number>
+ </property>
+ <property name="placeholderText">
+ <string>Hexadecimal A-F, a-f, 0-9.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>6</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QLineEdit" name="register_6">
+ <property name="maxLength">
+ <number>32767</number>
+ </property>
+ <property name="placeholderText">
+ <string>Hexadecimal A-F, a-f, 0-9.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>7</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="1">
+ <widget class="QLineEdit" name="register_7">
+ <property name="maxLength">
+ <number>32767</number>
+ </property>
+ <property name="placeholderText">
+ <string>Hexadecimal A-F, a-f, 0-9.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>8</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="1">
+ <widget class="QLineEdit" name="register_8">
+ <property name="maxLength">
+ <number>32767</number>
+ </property>
+ <property name="placeholderText">
+ <string>Hexadecimal A-F, a-f, 0-9.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="10" column="0">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>9</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="10" column="1">
+ <widget class="QLineEdit" name="register_9">
+ <property name="maxLength">
+ <number>32767</number>
+ </property>
+ <property name="placeholderText">
+ <string>Hexadecimal A-F, a-f, 0-9.</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Client</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Start address:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="startAddress">
+ <property name="maximum">
+ <number>9</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Number of registers:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="numberOfRegisters">
+ <property name="currentIndex">
+ <number>9</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>5</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>8</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>9</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>10</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QTreeView" name="registersTable">
+ <property name="showDropIndicator" stdset="0">
+ <bool>true</bool>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="uniformRowHeights">
+ <bool>true</bool>
+ </property>
+ <property name="itemsExpandable">
+ <bool>false</bool>
+ </property>
+ <property name="expandsOnDoubleClick">
+ <bool>false</bool>
+ </property>
+ <attribute name="headerVisible">
+ <bool>true</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>13</width>
+ <height>17</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="readButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Read</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="writeButton">
+ <property name="text">
+ <string>Write</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>620</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menuDevice">
+ <property name="title">
+ <string>Device</string>
+ </property>
+ <addaction name="actionConnect"/>
+ <addaction name="actionDisconnect"/>
+ <addaction name="separator"/>
+ <addaction name="actionExit"/>
+ </widget>
+ <addaction name="menuDevice"/>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ <action name="actionExit">
+ <property name="text">
+ <string>Quit</string>
+ </property>
+ </action>
+ <action name="actionConnect">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="images.qrc">
+ <normaloff>:/images/connect.png</normaloff>:/images/connect.png</iconset>
+ </property>
+ <property name="text">
+ <string>Connect</string>
+ </property>
+ </action>
+ <action name="actionDisconnect">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="icon">
+ <iconset resource="images.qrc">
+ <normaloff>:/images/disconnect.png</normaloff>:/images/disconnect.png</iconset>
+ </property>
+ <property name="text">
+ <string>Disconnect</string>
+ </property>
+ </action>
+ </widget>
+ <resources>
+ <include location="images.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/examples/serialbus/modbus/custom/modbusclient.cpp b/examples/serialbus/modbus/custom/modbusclient.cpp
new file mode 100644
index 0000000..faba17f
--- /dev/null
+++ b/examples/serialbus/modbus/custom/modbusclient.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtSerialBus module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "modbusclient.h"
+
+// The read response must contain the start address (quint16), the byte count
+// (quint8) and at least one register value (quint16).
+static constexpr int ReadHeaderSize {3};
+static constexpr int MinimumReadResponseSize {ReadHeaderSize + 2}; // 2 payload bytes
+
+// The response must contain the start address (quint16) and the number of
+// registers written (quint16).
+static constexpr int WriteResponseSize {4};
+
+ModbusClient::ModbusClient(QObject *parent)
+ : QModbusTcpClient(parent)
+{
+ QModbusResponse::registerDataSizeCalculator(CustomRead, [](const QModbusResponse &response) {
+ if (!response.isValid())
+ return -1;
+
+ if (response.dataSize() < MinimumReadResponseSize)
+ return -1;
+ return ReadHeaderSize + quint8(response.data().at(ReadHeaderSize - 1));
+ });
+
+ QModbusResponse::registerDataSizeCalculator(CustomWrite, [](const QModbusResponse &response) {
+ if (!response.isValid())
+ return -1;
+ return (response.dataSize() != WriteResponseSize ? -1 : WriteResponseSize);
+ });
+}
+
+static bool collateBytes(const QModbusPdu &response, QModbusDataUnit *data)
+{
+ if (response.dataSize() < MinimumReadResponseSize)
+ return false;
+
+ quint16 address; quint8 byteCount;
+ response.decodeData(&address, &byteCount);
+
+ if (byteCount % 2 != 0)
+ return false;
+
+ if (data) {
+ QDataStream stream(response.data().remove(0, 3));
+
+ QList<quint16> values;
+ const quint8 itemCount = byteCount / 2;
+ for (int i = 0; i < itemCount; i++) {
+ quint16 tmp;
+ stream >> tmp;
+ values.append(tmp);
+ }
+ *data = {QModbusDataUnit::HoldingRegisters, address, values};
+ }
+ return true;
+}
+
+static bool collateMultipleValues(const QModbusPdu &response, QModbusDataUnit *data)
+{
+ if (response.dataSize() != WriteResponseSize)
+ return false;
+
+ quint16 address, count;
+ response.decodeData(&address, &count);
+
+ if (count < 1 || count > 10)
+ return false;
+
+ if (data)
+ *data = {QModbusDataUnit::HoldingRegisters, address, count};
+ return true;
+}
+
+bool ModbusClient::processPrivateResponse(const QModbusResponse &response, QModbusDataUnit *data)
+{
+ if (!response.isValid())
+ return QModbusClient::processPrivateResponse(response, data);
+
+ if (CustomRead == response.functionCode())
+ return collateBytes(response, data);
+
+ if (CustomWrite == response.functionCode())
+ return collateMultipleValues(response, data);
+ return QModbusClient::processPrivateResponse(response, data);
+}
diff --git a/examples/serialbus/modbus/custom/modbusclient.h b/examples/serialbus/modbus/custom/modbusclient.h
new file mode 100644
index 0000000..a803839
--- /dev/null
+++ b/examples/serialbus/modbus/custom/modbusclient.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtSerialBus module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QModbusTcpClient>
+
+#ifndef MODBUSCLIENT_H
+#define MODBUSCLIENT_H
+
+class ModbusClient : public QModbusTcpClient
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(ModbusClient)
+
+public:
+ ModbusClient(QObject *parent = nullptr);
+
+ static constexpr QModbusPdu::FunctionCode CustomRead {QModbusPdu::FunctionCode(0x41)};
+ static constexpr QModbusPdu::FunctionCode CustomWrite {QModbusPdu::FunctionCode(0x42)};
+
+private:
+ bool processPrivateResponse(const QModbusResponse &response, QModbusDataUnit *data) override;
+};
+
+#endif // MODBUSCLIENT_H
diff --git a/examples/serialbus/modbus/custom/modbusserver.cpp b/examples/serialbus/modbus/custom/modbusserver.cpp
new file mode 100644
index 0000000..e80a472
--- /dev/null
+++ b/examples/serialbus/modbus/custom/modbusserver.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtSerialBus module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "modbusserver.h"
+#include "modbusclient.h"
+
+// The request must contain the start address (quint16) and the number of
+// registers to read (quint16).
+static constexpr int ReadRequestSize {4};
+
+// The request must contain the start address (quint16), the number of
+// registers to write (quint16), the byte count (quint8) and at least one
+// register value (quint16).
+static constexpr int WriteHeaderSize {5};
+static constexpr int MinimumWriteRequestSize {WriteHeaderSize + 2}; // 2 payload bytes
+
+ModbusServer::ModbusServer(QObject *parent)
+ : QModbusTcpServer(parent)
+{
+ setMap({{QModbusDataUnit::HoldingRegisters, {QModbusDataUnit::HoldingRegisters, 0, 10}}});
+
+ QModbusRequest::registerDataSizeCalculator(ModbusClient::CustomRead, [](const QModbusRequest &request) {
+ if (!request.isValid())
+ return -1;
+ return (request.dataSize() != ReadRequestSize ? -1 : ReadRequestSize);
+ });
+
+ QModbusRequest::registerDataSizeCalculator(ModbusClient::CustomWrite, [](const QModbusRequest &request) {
+ if (!request.isValid())
+ return -1;
+
+ if (request.dataSize() < MinimumWriteRequestSize)
+ return -1;
+ return WriteHeaderSize + quint8(request.data().at(WriteHeaderSize - 1));
+ });
+}
+
+QModbusResponse ModbusServer::processPrivateRequest(const QModbusPdu &request)
+{
+ if (!request.isValid())
+ return QModbusServer::processPrivateRequest(request);
+
+ if (ModbusClient::CustomRead == request.functionCode()) {
+ quint16 startAddress, count;
+ request.decodeData(&startAddress, &count);
+
+ QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, startAddress, count);
+ if (!data(&unit)) {
+ return QModbusExceptionResponse(request.functionCode(),
+ QModbusExceptionResponse::IllegalDataAddress);
+ }
+ return QModbusResponse(request.functionCode(), startAddress, quint8(count * 2), unit.values());
+ }
+
+ if (ModbusClient::CustomWrite == request.functionCode()) {
+ quint8 byteCount;
+ quint16 startAddress, numberOfRegisters;
+ request.decodeData(&startAddress, &numberOfRegisters, &byteCount);
+
+ if (byteCount % 2 != 0) {
+ return QModbusExceptionResponse(request.functionCode(),
+ QModbusExceptionResponse::IllegalDataValue);
+ }
+
+ const QByteArray pduData = request.data().remove(0, WriteHeaderSize);
+ QDataStream stream(pduData);
+
+ QList<quint16> values;
+ for (int i = 0; i < numberOfRegisters; i++) {
+ quint16 tmp;
+ stream >> tmp;
+ values.append(tmp);
+ }
+
+ if (!writeData({QModbusDataUnit::HoldingRegisters, startAddress, values})) {
+ return QModbusExceptionResponse(request.functionCode(),
+ QModbusExceptionResponse::ServerDeviceFailure);
+ }
+
+ return QModbusResponse(request.functionCode(), startAddress, numberOfRegisters);
+ }
+
+ return QModbusServer::processPrivateRequest(request);
+}
diff --git a/examples/serialbus/modbus/custom/modbusserver.h b/examples/serialbus/modbus/custom/modbusserver.h
new file mode 100644
index 0000000..cae690e
--- /dev/null
+++ b/examples/serialbus/modbus/custom/modbusserver.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtSerialBus module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QModbusTcpServer>
+
+#ifndef MODBUSSERVER_H
+#define MODBUSSERVER_H
+
+class ModbusServer : public QModbusTcpServer
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(ModbusServer)
+
+public:
+ ModbusServer(QObject *parent = nullptr);
+
+private:
+ QModbusResponse processPrivateRequest(const QModbusPdu &request) override;
+};
+
+#endif // MODBUSSERVER_H
diff --git a/examples/serialbus/modbus/custom/registermodel.cpp b/examples/serialbus/modbus/custom/registermodel.cpp
new file mode 100644
index 0000000..7e0f52f
--- /dev/null
+++ b/examples/serialbus/modbus/custom/registermodel.cpp
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtSerialBus module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "registermodel.h"
+
+enum { NumColumn = 0, RegistersColumn = 1, ColumnCount = 2, RowCount = 10 };
+
+RegisterModel::RegisterModel(QObject *parent)
+ : QAbstractTableModel(parent)
+ , m_registers(RowCount, 0u)
+{
+}
+
+int RegisterModel::rowCount(const QModelIndex &/*parent*/) const
+{
+ return RowCount;
+}
+
+int RegisterModel::columnCount(const QModelIndex &/*parent*/) const
+{
+ return ColumnCount;
+}
+
+QVariant RegisterModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
+ return QVariant();
+
+ Q_ASSERT(m_registers.count() == RowCount);
+
+ if (index.column() == NumColumn && role == Qt::DisplayRole)
+ return QString::number(index.row());
+
+ if (index.column() == RegistersColumn && role == Qt::DisplayRole)
+ return QString("0x%1").arg(QString::number(m_registers.at(index.row()), 16));
+
+ return QVariant();
+}
+
+QVariant RegisterModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (role != Qt::DisplayRole)
+ return QVariant();
+
+ if (orientation == Qt::Horizontal) {
+ switch (section) {
+ case NumColumn:
+ return tr("Address");
+ case RegistersColumn:
+ return tr("Registers");
+ default:
+ break;
+ }
+ }
+ return QVariant();
+}
+
+bool RegisterModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
+ return false;
+
+ Q_ASSERT(m_registers.count() == RowCount);
+
+ if (index.column() == RegistersColumn && role == Qt::EditRole) {
+ bool result = false;
+ quint16 newValue = value.toString().toUShort(&result, 16);
+ if (result)
+ m_registers[index.row()] = newValue;
+
+ emit dataChanged(index, index);
+ return result;
+ }
+
+ return false;
+}
+
+Qt::ItemFlags RegisterModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
+ return QAbstractTableModel::flags(index);
+
+ Qt::ItemFlags flags = QAbstractTableModel::flags(index);
+ if ((index.row() < m_address) || (index.row() >= (m_address + m_number)))
+ flags &= ~Qt::ItemIsEnabled;
+
+ if (index.column() == RegistersColumn)
+ return flags | Qt::ItemIsEditable;
+
+ return flags;
+}
+
+void RegisterModel::setStartAddress(int address)
+{
+ m_address = address;
+ emit updateViewport();
+}
+
+void RegisterModel::setNumberOfValues(const QString &number)
+{
+ m_number = number.toInt();
+ emit updateViewport();
+}
diff --git a/examples/serialbus/modbus/custom/registermodel.h b/examples/serialbus/modbus/custom/registermodel.h
new file mode 100644
index 0000000..1c06ddd
--- /dev/null
+++ b/examples/serialbus/modbus/custom/registermodel.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtSerialBus module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef REGISTERMODEL_H
+#define REGISTERMODEL_H
+
+#include <QAbstractItemModel>
+
+class RegisterModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ RegisterModel(QObject *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
+ bool setData(const QModelIndex &index, const QVariant &value, int role) override;
+
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+
+public slots:
+ void setStartAddress(int address);
+ void setNumberOfValues(const QString &number);
+
+signals:
+ void updateViewport();
+
+public:
+ int m_number = 0;
+ int m_address = 0;
+ QList<quint16> m_registers;
+};
+
+#endif // REGISTERMODEL_H
diff --git a/examples/serialbus/modbus/modbus.pro b/examples/serialbus/modbus/modbus.pro
index a15b828..9e94be7 100644
--- a/examples/serialbus/modbus/modbus.pro
+++ b/examples/serialbus/modbus/modbus.pro
@@ -3,4 +3,5 @@ TEMPLATE = subdirs
SUBDIRS += \
master \
slave \
- adueditor
+ adueditor \
+ custom