Imported existing sources
This commit is contained in:
103
.gitignore
vendored
103
.gitignore
vendored
@ -1,52 +1,73 @@
|
|||||||
# C++ objects and libs
|
# This file is used to ignore files which are generated
|
||||||
*.slo
|
# ----------------------------------------------------------------------------
|
||||||
*.lo
|
|
||||||
*.o
|
*~
|
||||||
|
*.autosave
|
||||||
*.a
|
*.a
|
||||||
*.la
|
*.core
|
||||||
*.lai
|
*.moc
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
*.so
|
*.so
|
||||||
*.so.*
|
*.so.*
|
||||||
*.dll
|
*_pch.h.cpp
|
||||||
*.dylib
|
*_resource.rc
|
||||||
|
*.qm
|
||||||
# Qt-es
|
.#*
|
||||||
object_script.*.Release
|
*.*#
|
||||||
object_script.*.Debug
|
core
|
||||||
*_plugin_import.cpp
|
!core/
|
||||||
|
tags
|
||||||
|
.DS_Store
|
||||||
|
.directory
|
||||||
|
*.debug
|
||||||
|
Makefile*
|
||||||
|
*.prl
|
||||||
|
*.app
|
||||||
|
moc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
Thumbs.db
|
||||||
|
*.res
|
||||||
|
*.rc
|
||||||
/.qmake.cache
|
/.qmake.cache
|
||||||
/.qmake.stash
|
/.qmake.stash
|
||||||
*.pro.user
|
|
||||||
*.pro.user.*
|
|
||||||
*.qbs.user
|
|
||||||
*.qbs.user.*
|
|
||||||
*.moc
|
|
||||||
moc_*.cpp
|
|
||||||
moc_*.h
|
|
||||||
qrc_*.cpp
|
|
||||||
ui_*.h
|
|
||||||
*.qmlc
|
|
||||||
*.jsc
|
|
||||||
Makefile*
|
|
||||||
*build-*
|
|
||||||
*.qm
|
|
||||||
*.prl
|
|
||||||
|
|
||||||
# Qt unit tests
|
# qtcreator generated files
|
||||||
target_wrapper.*
|
*.pro.user*
|
||||||
|
|
||||||
# QtCreator
|
# xemacs temporary files
|
||||||
*.autosave
|
*.flc
|
||||||
|
|
||||||
# QtCreator Qml
|
# Vim temporary files
|
||||||
*.qmlproject.user
|
.*.swp
|
||||||
*.qmlproject.user.*
|
|
||||||
|
|
||||||
# QtCreator CMake
|
# Visual Studio generated files
|
||||||
CMakeLists.txt.user*
|
*.ib_pdb_index
|
||||||
|
*.idb
|
||||||
|
*.ilk
|
||||||
|
*.pdb
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.vcproj
|
||||||
|
*vcproj.*.*.user
|
||||||
|
*.ncb
|
||||||
|
*.sdf
|
||||||
|
*.opensdf
|
||||||
|
*.vcxproj
|
||||||
|
*vcxproj.*
|
||||||
|
|
||||||
# QtCreator 4.8< compilation database
|
# MinGW generated files
|
||||||
compile_commands.json
|
*.Debug
|
||||||
|
*.Release
|
||||||
|
|
||||||
|
# Python byte code
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
# --------
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
||||||
# QtCreator local machine specific files for imported projects
|
|
||||||
*creator.user*
|
|
||||||
|
32
main.cpp
Normal file
32
main.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Qt includes
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
// local includes
|
||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication app{argc, argv};
|
||||||
|
|
||||||
|
qSetMessagePattern(QStringLiteral("%{time dd.MM.yyyy HH:mm:ss.zzz} "
|
||||||
|
"["
|
||||||
|
"%{if-debug}D%{endif}"
|
||||||
|
"%{if-info}I%{endif}"
|
||||||
|
"%{if-warning}W%{endif}"
|
||||||
|
"%{if-critical}C%{endif}"
|
||||||
|
"%{if-fatal}F%{endif}"
|
||||||
|
"] "
|
||||||
|
"%{function}(): "
|
||||||
|
"%{message}"));
|
||||||
|
|
||||||
|
QCoreApplication::setOrganizationDomain(QStringLiteral("brunner.ninja"));
|
||||||
|
QCoreApplication::setOrganizationName(QStringLiteral("feedc0de enterprises"));
|
||||||
|
QCoreApplication::setApplicationName(QStringLiteral("qmodbustester"));
|
||||||
|
QCoreApplication::setApplicationVersion(QStringLiteral("1.0"));
|
||||||
|
|
||||||
|
MainWindow mainWindow;
|
||||||
|
mainWindow.show();
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
319
mainwindow.cpp
Normal file
319
mainwindow.cpp
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QModbusTcpClient>
|
||||||
|
#include <QModbusReply>
|
||||||
|
#include <QMetaEnum>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
// local includes
|
||||||
|
#include "modbustablemodel.h"
|
||||||
|
|
||||||
|
// utilities
|
||||||
|
namespace {
|
||||||
|
QDebug operator<<(QDebug debug, QModbusDataUnit::RegisterType registerType);
|
||||||
|
QString toString(QModbusDataUnit::RegisterType registerType);
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
QMainWindow{parent},
|
||||||
|
m_ui{std::make_unique<Ui::MainWindow>()},
|
||||||
|
m_modbus{std::make_unique<QModbusTcpClient>(this)},
|
||||||
|
m_model{std::make_unique<ModbusTableModel>(this)}
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
m_ui->spinBoxTimeout->setValue(m_modbus->timeout());
|
||||||
|
connect(m_modbus.get(), &QModbusClient::timeoutChanged, m_ui->spinBoxTimeout, &QSpinBox::setValue);
|
||||||
|
connect(m_ui->spinBoxTimeout, &QSpinBox::valueChanged, m_modbus.get(), &QModbusClient::setTimeout);
|
||||||
|
|
||||||
|
m_ui->spinBoxRetries->setValue(m_modbus->numberOfRetries());
|
||||||
|
//connect(m_modbus.get(), &QModbusClient::numberOfRetriesChanged, m_ui->spinBoxRetries, &QSpinBox::setValue);
|
||||||
|
connect(m_ui->spinBoxRetries, &QSpinBox::valueChanged, m_modbus.get(), &QModbusClient::setNumberOfRetries);
|
||||||
|
|
||||||
|
modbusStateChanged(m_modbus->state());
|
||||||
|
|
||||||
|
connect(m_modbus.get(), &QModbusClient::errorOccurred,
|
||||||
|
this, &MainWindow::modbusErrorOccured);
|
||||||
|
|
||||||
|
connect(m_modbus.get(), &QModbusClient::stateChanged,
|
||||||
|
this, &MainWindow::modbusStateChanged);
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto addItem = [&](const auto &text, const auto &value){
|
||||||
|
m_ui->comboBoxType->addItem(text, QVariant::fromValue<QModbusDataUnit::RegisterType>(value));
|
||||||
|
};
|
||||||
|
addItem(tr("Discrete Inputs"), QModbusDataUnit::DiscreteInputs);
|
||||||
|
addItem(tr("Coils"), QModbusDataUnit::Coils);
|
||||||
|
addItem(tr("Input Registers"), QModbusDataUnit::InputRegisters);
|
||||||
|
addItem(tr("Holding Registers"), QModbusDataUnit::HoldingRegisters);
|
||||||
|
}
|
||||||
|
m_ui->comboBoxType->setCurrentIndex(
|
||||||
|
m_ui->comboBoxType->findData(
|
||||||
|
QVariant::fromValue<QModbusDataUnit::RegisterType>(QModbusDataUnit::HoldingRegisters)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
connect(m_ui->pushButtonConnect, &QAbstractButton::pressed, this, &MainWindow::connectPressed);
|
||||||
|
connect(m_ui->pushButtonRequest, &QAbstractButton::pressed, this, &MainWindow::requestPressed);
|
||||||
|
|
||||||
|
m_ui->tableView->setModel(m_model.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow() = default;
|
||||||
|
|
||||||
|
void MainWindow::connectPressed()
|
||||||
|
{
|
||||||
|
if (m_reply)
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
tr("Another request is still pending!"),
|
||||||
|
tr("Another request is still pending!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (const auto state = m_modbus->state())
|
||||||
|
{
|
||||||
|
case QModbusDevice::ConnectedState:
|
||||||
|
m_modbus->disconnectDevice();
|
||||||
|
break;
|
||||||
|
case QModbusDevice::UnconnectedState:
|
||||||
|
m_modbus->setConnectionParameter(QModbusDevice::NetworkAddressParameter, m_ui->lineEditServer->text());
|
||||||
|
m_modbus->setConnectionParameter(QModbusDevice::NetworkPortParameter, m_ui->spinBoxPort->value());
|
||||||
|
if (!m_modbus->connectDevice())
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
tr("Modbus client is in wrong state"),
|
||||||
|
tr("Modbus client is in wrong state:\n\n%0")
|
||||||
|
.arg(QMetaEnum::fromType<QModbusDevice::State>().valueToKey(state))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::requestPressed()
|
||||||
|
{
|
||||||
|
if (const auto state = m_modbus->state(); state != QModbusDevice::ConnectedState)
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
tr("Modbus client is in wrong state"),
|
||||||
|
tr("Modbus client is in wrong state:\n\n%0")
|
||||||
|
.arg(QMetaEnum::fromType<QModbusDevice::State>().valueToKey(state))
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_reply)
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
tr("Another request is still pending!"),
|
||||||
|
tr("Another request is still pending!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto registerType = m_ui->comboBoxType->currentData();
|
||||||
|
if (!registerType.isValid() || !registerType.canConvert<QModbusDataUnit::RegisterType>())
|
||||||
|
{
|
||||||
|
qDebug() << registerType << registerType.typeName() << registerType.canConvert<int>() << registerType.canConvert<QModbusDataUnit::RegisterType>();
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
tr("Invalid register type selected!"),
|
||||||
|
tr("Invalid register type selected!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_timer.start();
|
||||||
|
|
||||||
|
QModbusDataUnit dataUnit(registerType.value<QModbusDataUnit::RegisterType>(), m_ui->spinBoxRegister->value(), m_ui->spinBoxCount->value());
|
||||||
|
qDebug() << m_ui->spinBoxSlave->value() << dataUnit.registerType() << dataUnit.startAddress() << dataUnit.valueCount();
|
||||||
|
if (m_reply = std::unique_ptr<QModbusReply>(m_modbus->sendReadRequest(std::move(dataUnit), m_ui->spinBoxSlave->value())))
|
||||||
|
{
|
||||||
|
updateRequestFields();
|
||||||
|
|
||||||
|
if (m_reply->isFinished())
|
||||||
|
replyFinished();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ui->labelRequestStatus->setText(tr("Pending..."));
|
||||||
|
if (!m_ui->checkBoxAutorefresh->isChecked())
|
||||||
|
m_model->setResult({});
|
||||||
|
connect(m_reply.get(), &QModbusReply::finished, this, &MainWindow::replyFinished);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_ui->checkBoxAutorefresh->setChecked(false);
|
||||||
|
m_model->setResult({});
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
tr("Request sending failed!"),
|
||||||
|
tr("Request sending failed:\n\n%s").arg(m_modbus->errorString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::modbusErrorOccured(int error)
|
||||||
|
{
|
||||||
|
const auto typedError = QModbusDevice::Error(error);
|
||||||
|
qWarning() << typedError << m_modbus->errorString();
|
||||||
|
|
||||||
|
statusBar()->showMessage(tr("Modbus client failed with %0: %1")
|
||||||
|
.arg(typedError)
|
||||||
|
.arg(m_modbus->errorString()),
|
||||||
|
5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::modbusStateChanged(int state)
|
||||||
|
{
|
||||||
|
qDebug() << QModbusDevice::State(state);
|
||||||
|
|
||||||
|
m_ui->labelConnectionStatus->setText(QMetaEnum::fromType<QModbusDevice::State>().valueToKey(state));
|
||||||
|
|
||||||
|
if (state == QModbusDevice::ConnectedState)
|
||||||
|
m_ui->pushButtonConnect->setText(tr("Disconnect"));
|
||||||
|
else if (state == QModbusDevice::UnconnectedState)
|
||||||
|
{
|
||||||
|
m_ui->pushButtonConnect->setText(tr("Connect"));
|
||||||
|
if (!m_reply)
|
||||||
|
m_ui->labelRequestStatus->setText(tr("Idle"));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ui->lineEditServer->setEnabled(state == QModbusDevice::UnconnectedState);
|
||||||
|
m_ui->spinBoxPort->setEnabled(state == QModbusDevice::UnconnectedState);
|
||||||
|
m_ui->pushButtonConnect->setEnabled(state == QModbusDevice::ConnectedState || state == QModbusDevice::UnconnectedState);
|
||||||
|
m_ui->pushButtonRequest->setEnabled(state == QModbusDevice::ConnectedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::replyFinished()
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_reply);
|
||||||
|
|
||||||
|
if (!m_reply->isFinished())
|
||||||
|
{
|
||||||
|
qWarning() << "not yet finished?!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto elapsed = m_timer.elapsed();
|
||||||
|
|
||||||
|
if (const QModbusDevice::Error error = m_reply->error(); error == QModbusDevice::NoError)
|
||||||
|
{
|
||||||
|
const auto result = m_reply->result();
|
||||||
|
|
||||||
|
if (result.isValid())
|
||||||
|
{
|
||||||
|
m_model->setResult(result);
|
||||||
|
|
||||||
|
m_ui->labelRequestStatus->setText(tr("Succeeded!"));
|
||||||
|
|
||||||
|
statusBar()->showMessage(tr("Showing %0 from %1 to %2 (%3 registers) (took %4ms)")
|
||||||
|
.arg(toString(result.registerType()))
|
||||||
|
.arg(result.startAddress())
|
||||||
|
.arg(result.startAddress() + result.valueCount())
|
||||||
|
.arg(result.valueCount())
|
||||||
|
.arg(elapsed),
|
||||||
|
5000);
|
||||||
|
|
||||||
|
if (m_ui->checkBoxAutorefresh->isChecked())
|
||||||
|
QTimer::singleShot(m_ui->spinBoxDelay->value(), this, &MainWindow::requestPressed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qWarning() << "result is invalid!";
|
||||||
|
|
||||||
|
m_model->setResult({});
|
||||||
|
|
||||||
|
m_ui->checkBoxAutorefresh->setChecked(false);
|
||||||
|
|
||||||
|
m_ui->labelRequestStatus->setText(tr("Failed!"));
|
||||||
|
|
||||||
|
const auto msg = tr("Request finished without any indication for an error but still the result is not valid! (took %0ms)")
|
||||||
|
.arg(elapsed);
|
||||||
|
|
||||||
|
statusBar()->showMessage(msg, 5000);
|
||||||
|
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
tr("Request failed!"),
|
||||||
|
msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qWarning() << error << m_reply->errorString();
|
||||||
|
|
||||||
|
if (const auto result = m_reply->result(); result.isValid())
|
||||||
|
{
|
||||||
|
qDebug() << "registerType =" << result.registerType();
|
||||||
|
qDebug() << "startAddress =" << result.startAddress();
|
||||||
|
qDebug() << "valueCount =" << result.valueCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_model->setResult({});
|
||||||
|
|
||||||
|
m_ui->checkBoxAutorefresh->setChecked(false);
|
||||||
|
|
||||||
|
m_ui->labelRequestStatus->setText(tr("Failed!"));
|
||||||
|
|
||||||
|
const auto msg = tr("Request failed with %0: %1 (took %2ms)")
|
||||||
|
.arg(error)
|
||||||
|
.arg(m_reply->errorString())
|
||||||
|
.arg(elapsed);
|
||||||
|
|
||||||
|
statusBar()->showMessage(msg, 5000);
|
||||||
|
|
||||||
|
if (m_ui->checkBoxShowMsgBoxes->isChecked())
|
||||||
|
QMessageBox::warning(this,
|
||||||
|
tr("Request failed!"),
|
||||||
|
msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_reply = nullptr;
|
||||||
|
updateRequestFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::updateRequestFields()
|
||||||
|
{
|
||||||
|
m_ui->spinBoxSlave->setEnabled(!m_reply);
|
||||||
|
m_ui->comboBoxType->setEnabled(!m_reply);
|
||||||
|
m_ui->spinBoxRegister->setEnabled(!m_reply);
|
||||||
|
m_ui->spinBoxCount->setEnabled(!m_reply);
|
||||||
|
m_ui->pushButtonRequest->setEnabled(m_modbus->state() == QModbusDevice::ConnectedState && !m_reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
QDebug operator<<(QDebug debug, QModbusDataUnit::RegisterType registerType)
|
||||||
|
{
|
||||||
|
QDebugStateSaver saver(debug);
|
||||||
|
|
||||||
|
debug.nospace() << "QModbusDataUnit::RegisterType(";
|
||||||
|
|
||||||
|
switch (registerType)
|
||||||
|
{
|
||||||
|
case QModbusDataUnit::RegisterType::Invalid: debug << "Invalid"; break;
|
||||||
|
case QModbusDataUnit::RegisterType::DiscreteInputs: debug << "DiscreteInputs"; break;
|
||||||
|
case QModbusDataUnit::RegisterType::Coils: debug << "Coils"; break;
|
||||||
|
case QModbusDataUnit::RegisterType::InputRegisters: debug << "InputRegisters"; break;
|
||||||
|
case QModbusDataUnit::RegisterType::HoldingRegisters: debug << "HoldingRegisters"; break;
|
||||||
|
default: debug << int(registerType); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return debug << ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
QString toString(QModbusDataUnit::RegisterType registerType)
|
||||||
|
{
|
||||||
|
switch (registerType)
|
||||||
|
{
|
||||||
|
case QModbusDataUnit::RegisterType::Invalid: return MainWindow::tr("Invalid");
|
||||||
|
case QModbusDataUnit::RegisterType::DiscreteInputs: return MainWindow::tr("DiscreteInputs");
|
||||||
|
case QModbusDataUnit::RegisterType::Coils: return MainWindow::tr("Coils");
|
||||||
|
case QModbusDataUnit::RegisterType::InputRegisters: return MainWindow::tr("InputRegisters");
|
||||||
|
case QModbusDataUnit::RegisterType::HoldingRegisters: return MainWindow::tr("HoldingRegisters");
|
||||||
|
default: return MainWindow::tr("Unknown RegisterType(%0)").arg(int(registerType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
41
mainwindow.h
Normal file
41
mainwindow.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// system includes
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
|
||||||
|
// forward declares
|
||||||
|
class QModbusTcpClient;
|
||||||
|
class QModbusReply;
|
||||||
|
namespace Ui { class MainWindow; }
|
||||||
|
class ModbusTableModel;
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void connectPressed();
|
||||||
|
void requestPressed();
|
||||||
|
|
||||||
|
void modbusErrorOccured(int error);
|
||||||
|
void modbusStateChanged(int state);
|
||||||
|
|
||||||
|
void replyFinished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateRequestFields();
|
||||||
|
|
||||||
|
const std::unique_ptr<Ui::MainWindow> m_ui;
|
||||||
|
const std::unique_ptr<QModbusTcpClient> m_modbus;
|
||||||
|
const std::unique_ptr<ModbusTableModel> m_model;
|
||||||
|
std::unique_ptr<QModbusReply> m_reply;
|
||||||
|
QElapsedTimer m_timer;
|
||||||
|
};
|
335
mainwindow.ui
Normal file
335
mainwindow.ui
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
<?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>947</width>
|
||||||
|
<height>736</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Qt Modbus Tester</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,1">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayoutConnection">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelConnection">
|
||||||
|
<property name="text">
|
||||||
|
<string><b>Connection:</b></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelServer">
|
||||||
|
<property name="text">
|
||||||
|
<string>Server:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>lineEditServer</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineEditServer">
|
||||||
|
<property name="text">
|
||||||
|
<string>192.168.0.75</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelPort">
|
||||||
|
<property name="text">
|
||||||
|
<string>Port:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>spinBoxPort</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="spinBoxPort">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>502</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelSlave">
|
||||||
|
<property name="text">
|
||||||
|
<string>Slave:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>spinBoxSlave</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="spinBoxSlave">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButtonConnect">
|
||||||
|
<property name="text">
|
||||||
|
<string>Connect</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelConnectionStatus">
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">Connection status</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayoutErrorHandling">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelErrorHandling">
|
||||||
|
<property name="text">
|
||||||
|
<string><b>Error handling:</b></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelTimeout">
|
||||||
|
<property name="text">
|
||||||
|
<string>Timeout:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>spinBoxTimeout</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="spinBoxTimeout">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelRetries">
|
||||||
|
<property name="text">
|
||||||
|
<string>Retries:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>spinBoxRetries</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="spinBoxRetries">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="checkBoxShowMsgBoxes">
|
||||||
|
<property name="text">
|
||||||
|
<string>Show messageboxes</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayoutDataUnit">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelDataUnit">
|
||||||
|
<property name="text">
|
||||||
|
<string><b>Data unit:</b></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelType">
|
||||||
|
<property name="text">
|
||||||
|
<string>Type:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>comboBoxType</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="comboBoxType"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelRegister">
|
||||||
|
<property name="text">
|
||||||
|
<string>Register:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>spinBoxRegister</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="spinBoxRegister">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelCount">
|
||||||
|
<property name="text">
|
||||||
|
<string>Count:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>spinBoxCount</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="spinBoxCount">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="checkBoxAutorefresh">
|
||||||
|
<property name="text">
|
||||||
|
<string>Autorefresh</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelDelay">
|
||||||
|
<property name="text">
|
||||||
|
<string>Delay:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>spinBoxDelay</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="spinBoxDelay">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string>ms</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pushButtonRequest">
|
||||||
|
<property name="text">
|
||||||
|
<string>Request</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelRequestStatus">
|
||||||
|
<property name="text">
|
||||||
|
<string>Idle</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="tableView"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menubar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>947</width>
|
||||||
|
<height>20</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QStatusBar" name="statusbar"/>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>checkBoxAutorefresh</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>spinBoxDelay</receiver>
|
||||||
|
<slot>setEnabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>518</x>
|
||||||
|
<y>103</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>630</x>
|
||||||
|
<y>104</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
128
modbustablemodel.cpp
Normal file
128
modbustablemodel.cpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#include "modbustablemodel.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
enum {
|
||||||
|
ColumnDecimal,
|
||||||
|
ColumnHex,
|
||||||
|
ColumnBinary,
|
||||||
|
ColumnAscii,
|
||||||
|
ColumnCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModbusTableModel::setResult(const QModbusDataUnit &result)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_result = result;
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex ModbusTableModel::index(int row, int column, const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(!parent.isValid());
|
||||||
|
return createIndex(row, column);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex ModbusTableModel::parent(const QModelIndex &child) const
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int ModbusTableModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(!parent.isValid());
|
||||||
|
return m_result.isValid() ? m_result.valueCount() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ModbusTableModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(!parent.isValid());
|
||||||
|
return ColumnCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ModbusTableModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(index.isValid());
|
||||||
|
Q_ASSERT(m_result.isValid());
|
||||||
|
Q_ASSERT(index.row() >= 0);
|
||||||
|
Q_ASSERT(index.row() < m_result.valueCount());
|
||||||
|
Q_ASSERT(index.column() >= 0);
|
||||||
|
Q_ASSERT(index.column() < ColumnCount);
|
||||||
|
|
||||||
|
const auto &values = m_result.values();
|
||||||
|
Q_ASSERT(index.row() < values.size());
|
||||||
|
|
||||||
|
const auto &value = values[index.row()];
|
||||||
|
|
||||||
|
switch (role)
|
||||||
|
{
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
switch (index.column())
|
||||||
|
{
|
||||||
|
case ColumnDecimal: return QString::number(value);
|
||||||
|
case ColumnHex: return QString::number(value, 16).rightJustified(4, '0').insert(2, ' ');
|
||||||
|
case ColumnBinary: return QString::number(value, 2) .rightJustified(16, '0').insert(8, ' ');
|
||||||
|
case ColumnAscii: return QString{QChar{(value&0xFF00)>>8}} + QChar{value&0x00FF};
|
||||||
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
|
break;
|
||||||
|
case Qt::EditRole:
|
||||||
|
switch (index.column())
|
||||||
|
{
|
||||||
|
case ColumnDecimal:
|
||||||
|
case ColumnHex:
|
||||||
|
case ColumnBinary:
|
||||||
|
case ColumnAscii:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ModbusTableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
switch (orientation)
|
||||||
|
{
|
||||||
|
case Qt::Horizontal:
|
||||||
|
Q_ASSERT(section >= 0);
|
||||||
|
Q_ASSERT(section < ColumnCount);
|
||||||
|
|
||||||
|
switch (role)
|
||||||
|
{
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
case Qt::EditRole:
|
||||||
|
switch (section)
|
||||||
|
{
|
||||||
|
case ColumnDecimal: return tr("Decimal");
|
||||||
|
case ColumnHex: return tr("Hex");
|
||||||
|
case ColumnBinary: return tr("Binary");
|
||||||
|
case ColumnAscii: return tr("Ascii");
|
||||||
|
}
|
||||||
|
__builtin_unreachable();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Qt::Vertical:
|
||||||
|
Q_ASSERT(m_result.isValid());
|
||||||
|
Q_ASSERT(section >= 0);
|
||||||
|
Q_ASSERT(section < m_result.valueCount());
|
||||||
|
|
||||||
|
switch (role)
|
||||||
|
{
|
||||||
|
case Qt::DisplayRole: return QString::number(m_result.startAddress() + section);
|
||||||
|
case Qt::EditRole: return m_result.startAddress() + section;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
23
modbustablemodel.h
Normal file
23
modbustablemodel.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Qt includes
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
#include <QModbusDataUnit>
|
||||||
|
|
||||||
|
class ModbusTableModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using QAbstractTableModel::QAbstractTableModel;
|
||||||
|
|
||||||
|
void setResult(const QModbusDataUnit &result);
|
||||||
|
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
|
||||||
|
QModelIndex parent(const QModelIndex &child) const override;
|
||||||
|
int rowCount(const QModelIndex &parent) const override;
|
||||||
|
int columnCount(const QModelIndex &parent) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QModbusDataUnit m_result;
|
||||||
|
};
|
16
qtmodbustester.pro
Normal file
16
qtmodbustester.pro
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
QT = core network serialbus gui widgets
|
||||||
|
|
||||||
|
CONFIG += c++latest
|
||||||
|
|
||||||
|
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000
|
||||||
|
|
||||||
|
SOURCES += main.cpp \
|
||||||
|
mainwindow.cpp \
|
||||||
|
modbustablemodel.cpp
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
mainwindow.ui
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
mainwindow.h \
|
||||||
|
modbustablemodel.h
|
Reference in New Issue
Block a user