// 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 #include MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); ui->statusbar->setSizeGripEnabled(false); ui->addressPort->setToolTip(tr("Enter : 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](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->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(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_(?\\d+)")); const QList qle = ui->server->findChildren(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); } }