Preperations for modbus rtu

This commit is contained in:
2025-04-07 17:33:46 +02:00
parent 53bde3d77d
commit 2a238e68ed
6 changed files with 289 additions and 77 deletions

View File

@ -6,10 +6,10 @@
// Qt includes
#include <QDebug>
#include <QModbusTcpClient>
#include <QModbusClient>
#include <QMessageBox>
ChangeValuesDialog::ChangeValuesDialog(QModbusTcpClient &modbus, int serverAddress, QModbusDataUnit::RegisterType registerType, QWidget *parent) :
ChangeValuesDialog::ChangeValuesDialog(QModbusClient &modbus, int serverAddress, QModbusDataUnit::RegisterType registerType, QWidget *parent) :
QDialog{parent},
m_ui{std::make_unique<Ui::ChangeValuesDialog>()},
m_modbus{modbus}

View File

@ -11,7 +11,7 @@
#include "changevaluesmodel.h"
// forward declares
class QModbusTcpClient;
class QModbusClient;
class QModbusReply;
namespace Ui {class ChangeValuesDialog; }
@ -20,7 +20,7 @@ class ChangeValuesDialog : public QDialog
Q_OBJECT
public:
explicit ChangeValuesDialog(QModbusTcpClient &modbus, int serverAddress, QModbusDataUnit::RegisterType registerType, QWidget *parent = nullptr);
explicit ChangeValuesDialog(QModbusClient &modbus, int serverAddress, QModbusDataUnit::RegisterType registerType, QWidget *parent = nullptr);
~ChangeValuesDialog() override;
private slots:
@ -29,7 +29,7 @@ private slots:
private:
const std::unique_ptr<Ui::ChangeValuesDialog> m_ui;
QModbusTcpClient &m_modbus;
QModbusClient &m_modbus;
ChangeValuesModel m_model;
std::unique_ptr<QModbusReply> m_reply;
};

View File

@ -7,8 +7,6 @@
int main(int argc, char *argv[])
{
QApplication app{argc, argv};
qSetMessagePattern(QStringLiteral("%{time dd.MM.yyyy HH:mm:ss.zzz} "
"["
"%{if-debug}D%{endif}"
@ -25,6 +23,8 @@ int main(int argc, char *argv[])
QCoreApplication::setApplicationName(QStringLiteral("qmodbustester"));
QCoreApplication::setApplicationVersion(QStringLiteral("1.0"));
QApplication app{argc, argv};
MainWindow mainWindow;
mainWindow.show();

View File

@ -3,7 +3,10 @@
// Qt includes
#include <QDebug>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QModbusTcpClient>
#include <QtSerialBus/QModbusRtuSerialMaster>
#include <QModbusReply>
#include <QMetaEnum>
#include <QMessageBox>
@ -23,26 +26,13 @@ QString toString(QModbusDataUnit::RegisterType registerType);
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_model{std::make_unique<ModbusTableModel>(this)},
m_completerModel{this},
m_completer{&m_completerModel, 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);
qDebug() << m_settings.fileName();
{
const auto addItem = [&](const auto &text, const auto &value){
@ -63,11 +53,44 @@ MainWindow::MainWindow(QWidget *parent) :
connect(m_ui->pushButtonRequest, &QAbstractButton::pressed, this, &MainWindow::requestPressed);
connect(m_ui->pushButtonWrite, &QAbstractButton::pressed, this, &MainWindow::writePressed);
auto completions = m_settings.value("lastHosts").toStringList();
m_completerModel.setStringList(completions);
m_completer.setModel(&m_completerModel);
m_completer.setCaseSensitivity(Qt::CaseInsensitive);
m_ui->lineEditServer->setCompleter(&m_completer);
if (!completions.isEmpty())
m_ui->lineEditServer->setText(completions.first());
m_ui->tableView->setModel(m_model.get());
refreshSerialPorts();
connect(m_ui->toolButtonRefreshSerialports, &QToolButton::pressed, this, &MainWindow::refreshSerialPorts);
{
QMetaEnum e = QMetaEnum::fromType<QSerialPort::Parity>();
for (int i = 0; i < e.keyCount(); i++)
m_ui->comboBoxParity->addItem(e.key(i), QSerialPort::Parity(e.value(i)));
}
{
QMetaEnum e = QMetaEnum::fromType<QSerialPort::StopBits>();
for (int i = 0; i < e.keyCount(); i++)
m_ui->comboBoxStopBits->addItem(e.key(i), QSerialPort::StopBits(e.value(i)));
}
}
MainWindow::~MainWindow() = default;
void MainWindow::refreshSerialPorts()
{
m_ui->comboBoxSerialPort->clear();
for (const auto &port : QSerialPortInfo::availablePorts())
m_ui->comboBoxSerialPort->addItem(port.portName() + " [" + port.manufacturer() + "; " + port.serialNumber() + "]", port.portName());
}
void MainWindow::connectPressed()
{
if (m_reply)
@ -78,30 +101,70 @@ void MainWindow::connectPressed()
return;
}
switch (const auto state = m_modbus->state())
switch (!m_modbus || m_modbus->state() != QModbusDevice::ConnectedState)
{
case QModbusDevice::ConnectedState:
case false:
m_modbus->disconnectDevice();
m_modbus.release()->deleteLater();
break;
case QModbusDevice::UnconnectedState:
m_modbus->setConnectionParameter(QModbusDevice::NetworkAddressParameter, m_ui->lineEditServer->text());
m_modbus->setConnectionParameter(QModbusDevice::NetworkPortParameter, m_ui->spinBoxPort->value());
case true :
switch (m_ui->comboBoxConnectionType->currentIndex())
{
case 0: // tcp
m_modbus = std::make_unique<QModbusTcpClient>(this);
m_modbus->setConnectionParameter(QModbusDevice::NetworkAddressParameter, m_ui->lineEditServer->text());
m_modbus->setConnectionParameter(QModbusDevice::NetworkPortParameter, m_ui->spinBoxPort->value());
break;
case 1: // rtu
m_modbus = std::make_unique<QModbusRtuSerialMaster>(this);
m_modbus->setConnectionParameter(QModbusDevice::SerialPortNameParameter, m_ui->comboBoxSerialPort->currentData().toString());
m_modbus->setConnectionParameter(QModbusDevice::SerialParityParameter, m_ui->comboBoxParity->currentData().value<QSerialPort::Parity>());
m_modbus->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud9600);
m_modbus->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, m_ui->comboBoxStopBits->currentData().value<QSerialPort::StopBits>());
break;
default:
QMessageBox::warning(this, tr("Invalid connection type"), tr("Invalid connection type"));
return;
}
m_modbus->setTimeout(m_ui->spinBoxTimeout->value());
connect(m_modbus.get(), &QModbusClient::timeoutChanged, m_ui->spinBoxTimeout, &QSpinBox::setValue);
connect(m_ui->spinBoxTimeout, &QSpinBox::valueChanged, m_modbus.get(), &QModbusClient::setTimeout);
m_modbus->setNumberOfRetries(m_ui->spinBoxRetries->value());
//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);
if (!m_modbus->connectDevice())
{
QMessageBox::warning(this,
tr("Modbus client could not connect"),
tr("Modbus client could not connect:\n\n%0")
.arg(m_modbus->errorString())
);
}
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 (!m_modbus)
{
QMessageBox::warning(this, tr("No valid serial port openend"), tr("No valid serial port openend"));
return;
}
if (const auto state = m_modbus->state(); state != QModbusDevice::ConnectedState)
{
QMessageBox::warning(this,
@ -134,7 +197,7 @@ void MainWindow::requestPressed()
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())))
if (m_reply = std::unique_ptr<QModbusReply>{m_modbus->sendReadRequest(std::move(dataUnit), m_ui->spinBoxSlave->value())})
{
updateRequestFields();
@ -146,6 +209,8 @@ void MainWindow::requestPressed()
if (!m_ui->checkBoxAutorefresh->isChecked())
m_model->setResult({});
connect(m_reply.get(), &QModbusReply::finished, this, &MainWindow::replyFinished);
connect(m_reply.get(), &QModbusReply::errorOccurred, this, &MainWindow::replyErrorOccurred);
connect(m_reply.get(), &QModbusReply::intermediateErrorOccurred, this, &MainWindow::replyIntermediateErrorOccurred);
}
}
else
@ -189,7 +254,15 @@ void MainWindow::modbusStateChanged(int state)
m_ui->labelConnectionStatus->setText(QMetaEnum::fromType<QModbusDevice::State>().valueToKey(state));
if (state == QModbusDevice::ConnectedState)
{
auto completions = m_settings.value("lastHosts").toStringList();
completions.removeAll(m_ui->lineEditServer->text());
completions.prepend(m_ui->lineEditServer->text());
m_settings.setValue("lastHosts", completions);
m_completerModel.setStringList(completions);
m_ui->pushButtonConnect->setText(tr("Disconnect"));
}
else if (state == QModbusDevice::UnconnectedState)
{
m_ui->pushButtonConnect->setText(tr("Connect"));
@ -238,7 +311,7 @@ void MainWindow::replyFinished()
}
else
{
qWarning() << "result is invalid!";
qWarning() << "result is invalid!" << m_reply->error() << m_reply->errorString();
m_model->setResult({});
@ -290,6 +363,16 @@ void MainWindow::replyFinished()
updateRequestFields();
}
void MainWindow::replyErrorOccurred(QModbusDevice::Error error)
{
qDebug() << error;
}
void MainWindow::replyIntermediateErrorOccurred(QModbusDevice::IntermediateError error)
{
qDebug() << error;
}
void MainWindow::updateRequestFields()
{
m_ui->spinBoxSlave->setEnabled(!m_reply);

View File

@ -6,9 +6,14 @@
// Qt includes
#include <QMainWindow>
#include <QElapsedTimer>
#include <QStringListModel>
#include <QSettings>
#include <QCompleter>
#include <QSettings>
#include <qmodbusdevice.h>
// forward declares
class QModbusTcpClient;
class QModbusClient;
class QModbusReply;
namespace Ui { class MainWindow; }
class ModbusTableModel;
@ -22,6 +27,8 @@ public:
~MainWindow() override;
private slots:
void refreshSerialPorts();
void connectPressed();
void requestPressed();
void writePressed();
@ -30,13 +37,18 @@ private slots:
void modbusStateChanged(int state);
void replyFinished();
void replyErrorOccurred(QModbusDevice::Error error);
void replyIntermediateErrorOccurred(QModbusDevice::IntermediateError error);
private:
void updateRequestFields();
const std::unique_ptr<Ui::MainWindow> m_ui;
const std::unique_ptr<QModbusTcpClient> m_modbus;
QSettings m_settings;
std::unique_ptr<QModbusClient> m_modbus;
const std::unique_ptr<ModbusTableModel> m_model;
std::unique_ptr<QModbusReply> m_reply;
QElapsedTimer m_timer;
QStringListModel m_completerModel;
QCompleter m_completer;
};

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>947</width>
<width>1108</width>
<height>736</height>
</rect>
</property>
@ -25,40 +25,135 @@
</widget>
</item>
<item>
<widget class="QLabel" name="labelServer">
<property name="text">
<string>Server:</string>
</property>
<property name="buddy">
<cstring>lineEditServer</cstring>
</property>
<widget class="QComboBox" name="comboBoxConnectionType">
<item>
<property name="text">
<string>TCP</string>
</property>
</item>
<item>
<property name="text">
<string>Serial</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEditServer">
<property name="text">
<string>192.168.8.116</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>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page">
<layout class="QHBoxLayout">
<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/>
</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>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="labelSerialPort">
<property name="text">
<string>SerialPort:</string>
</property>
<property name="buddy">
<cstring>comboBoxSerialPort</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxSerialPort"/>
</item>
<item>
<widget class="QToolButton" name="toolButtonRefreshSerialports">
<property name="text">
<string>🗘</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelBaudrate">
<property name="text">
<string>Baudrate:</string>
</property>
<property name="buddy">
<cstring>spinBoxBaudrate</cstring>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBoxBaudrate">
<property name="maximum">
<number>4000000</number>
</property>
<property name="value">
<number>9600</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelParity">
<property name="text">
<string>Parity:</string>
</property>
<property name="buddy">
<cstring>comboBoxParity</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxParity"/>
</item>
<item>
<widget class="QLabel" name="labelStopBits">
<property name="text">
<string>StopBits:</string>
</property>
<property name="buddy">
<cstring>comboBoxStopBits</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="comboBoxStopBits"/>
</item>
</layout>
</widget>
</widget>
</item>
<item>
@ -91,7 +186,7 @@
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -134,6 +229,9 @@
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>1000</number>
</property>
</widget>
</item>
<item>
@ -151,6 +249,9 @@
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>3</number>
</property>
</widget>
</item>
<item>
@ -166,7 +267,7 @@
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -277,7 +378,7 @@
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -297,7 +398,7 @@
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -326,8 +427,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>947</width>
<height>20</height>
<width>1108</width>
<height>22</height>
</rect>
</property>
</widget>
@ -351,5 +452,21 @@
</hint>
</hints>
</connection>
<connection>
<sender>comboBoxConnectionType</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>stackedWidget</receiver>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>123</x>
<y>44</y>
</hint>
<hint type="destinationlabel">
<x>225</x>
<y>44</y>
</hint>
</hints>
</connection>
</connections>
</ui>