Source code

Revision control

Copy as Markdown

// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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);
ui->addressPort->setToolTip(tr("Enter <ip address>:<port> pair."));
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, [this](QModbusDevice::Error) {
statusBar()->showMessage(m_client.errorString(), 5000);
});
// server
connect(&m_server, &QModbusServer::dataWritten, this, &MainWindow::updateWidgets);
connect(&m_server, &QModbusServer::errorOccurred, this, [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->addressPort->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 (qsizetype i = 0, total = unit.valueCount(); i < total; ++i) {
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()
{
//! [generate_modbus_request]
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 = unit.valueCount(); i < total; ++i)
unit.setValue(i, m_model->m_registers[i + unit.startAddress()]);
const quint8 byteCount = quint8(unit.valueCount() * 2);
QModbusRequest writeRequest {
QModbusPdu::FunctionCode(ModbusClient::CustomWrite),
quint16(unit.startAddress()),
quint16(unit.valueCount()), byteCount, unit.values()
};
//! [generate_modbus_request]
//! [send_custom_command]
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);
}
//! [send_custom_command]
}
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);
}
}