mainwindow.cpp Example File

modbus/slave/mainwindow.cpp
/**************************************************************************** ** ** Copyright (C) 2017 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 "settingsdialog.h" #include "ui_mainwindow.h" #include <QModbusRtuSerialSlave> #include <QModbusTcpServer> #include <QRegularExpression> #include <QStatusBar> #include <QUrl> enum ModbusConnection { Serial, Tcp }; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , modbusDevice(nullptr) { ui->setupUi(this); setupWidgetContainers(); ui->connectType->setCurrentIndex(0); on_connectType_currentIndexChanged(0); m_settingsDialog = new SettingsDialog(this); initActions(); } MainWindow::~MainWindow() { if (modbusDevice) modbusDevice->disconnectDevice(); delete modbusDevice; delete ui; } void MainWindow::initActions() { ui->actionConnect->setEnabled(true); ui->actionDisconnect->setEnabled(false); ui->actionExit->setEnabled(true); ui->actionOptions->setEnabled(true); connect(ui->actionConnect, &QAction::triggered, this, &MainWindow::on_connectButton_clicked); connect(ui->actionDisconnect, &QAction::triggered, this, &MainWindow::on_connectButton_clicked); connect(ui->actionExit, &QAction::triggered, this, &QMainWindow::close); connect(ui->actionOptions, &QAction::triggered, m_settingsDialog, &QDialog::show); } void MainWindow::on_connectType_currentIndexChanged(int index) { if (modbusDevice) { modbusDevice->disconnect(); delete modbusDevice; modbusDevice = nullptr; } ModbusConnection type = static_cast<ModbusConnection> (index); if (type == Serial) { modbusDevice = new QModbusRtuSerialSlave(this); } else if (type == Tcp) { modbusDevice = new QModbusTcpServer(this); if (ui->portEdit->text().isEmpty()) ui->portEdit->setText(QLatin1Literal("127.0.0.1:502")); } ui->listenOnlyBox->setEnabled(type == Serial); if (!modbusDevice) { ui->connectButton->setDisabled(true); if (type == Serial) statusBar()->showMessage(tr("Could not create Modbus slave."), 5000); else statusBar()->showMessage(tr("Could not create Modbus server."), 5000); } else { QModbusDataUnitMap reg; reg.insert(QModbusDataUnit::Coils, { QModbusDataUnit::Coils, 0, 10 }); reg.insert(QModbusDataUnit::DiscreteInputs, { QModbusDataUnit::DiscreteInputs, 0, 10 }); reg.insert(QModbusDataUnit::InputRegisters, { QModbusDataUnit::InputRegisters, 0, 10 }); reg.insert(QModbusDataUnit::HoldingRegisters, { QModbusDataUnit::HoldingRegisters, 0, 10 }); modbusDevice->setMap(reg); connect(modbusDevice, &QModbusServer::dataWritten, this, &MainWindow::updateWidgets); connect(modbusDevice, &QModbusServer::stateChanged, this, &MainWindow::onStateChanged); connect(modbusDevice, &QModbusServer::errorOccurred, this, &MainWindow::handleDeviceError); connect(ui->listenOnlyBox, &QCheckBox::toggled, this, [this](bool toggled) { if (modbusDevice) modbusDevice->setValue(QModbusServer::ListenOnlyMode, toggled); }); emit ui->listenOnlyBox->toggled(ui->listenOnlyBox->isChecked()); connect(ui->setBusyBox, &QCheckBox::toggled, this, [this](bool toggled) { if (modbusDevice) modbusDevice->setValue(QModbusServer::DeviceBusy, toggled ? 0xffff : 0x0000); }); emit ui->setBusyBox->toggled(ui->setBusyBox->isChecked()); setupDeviceData(); } } void MainWindow::handleDeviceError(QModbusDevice::Error newError) { if (newError == QModbusDevice::NoError || !modbusDevice) return; statusBar()->showMessage(modbusDevice->errorString(), 5000); } void MainWindow::on_connectButton_clicked() { bool intendToConnect = (modbusDevice->state() == QModbusDevice::UnconnectedState); statusBar()->clearMessage(); if (intendToConnect) { if (static_cast<ModbusConnection> (ui->connectType->currentIndex()) == Serial) { modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, ui->portEdit->text()); modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, m_settingsDialog->settings().parity); modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, m_settingsDialog->settings().baud); modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, m_settingsDialog->settings().dataBits); modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, m_settingsDialog->settings().stopBits); } else { const QUrl url = QUrl::fromUserInput(ui->portEdit->text()); modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port()); modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host()); } modbusDevice->setServerAddress(ui->serverEdit->text().toInt()); if (!modbusDevice->connectDevice()) { statusBar()->showMessage(tr("Connect failed: ") + modbusDevice->errorString(), 5000); } else { ui->actionConnect->setEnabled(false); ui->actionDisconnect->setEnabled(true); } } else { modbusDevice->disconnectDevice(); ui->actionConnect->setEnabled(true); ui->actionDisconnect->setEnabled(false); } } void MainWindow::onStateChanged(int state) { bool connected = (state != QModbusDevice::UnconnectedState); ui->actionConnect->setEnabled(!connected); ui->actionDisconnect->setEnabled(connected); if (state == QModbusDevice::UnconnectedState) ui->connectButton->setText(tr("Connect")); else if (state == QModbusDevice::ConnectedState) ui->connectButton->setText(tr("Disconnect")); } void MainWindow::coilChanged(int id) { QAbstractButton *button = coilButtons.button(id); bitChanged(id, QModbusDataUnit::Coils, button->isChecked()); } void MainWindow::discreteInputChanged(int id) { QAbstractButton *button = discreteButtons.button(id); bitChanged(id, QModbusDataUnit::DiscreteInputs, button->isChecked()); } void MainWindow::bitChanged(int id, QModbusDataUnit::RegisterType table, bool value) { if (!modbusDevice) return; if (!modbusDevice->setData(table, id, value)) statusBar()->showMessage(tr("Could not set data: ") + modbusDevice->errorString(), 5000); } void MainWindow::setRegister(const QString &value) { if (!modbusDevice) return; const QString objectName = QObject::sender()->objectName(); if (registers.contains(objectName)) { bool ok = true; const int id = QObject::sender()->property("ID").toInt(); if (objectName.startsWith(QStringLiteral("inReg"))) ok = modbusDevice->setData(QModbusDataUnit::InputRegisters, id, value.toInt(&ok, 16)); else if (objectName.startsWith(QStringLiteral("holdReg"))) ok = modbusDevice->setData(QModbusDataUnit::HoldingRegisters, id, value.toInt(&ok, 16)); if (!ok) statusBar()->showMessage(tr("Could not set register: ") + modbusDevice->errorString(), 5000); } } void MainWindow::updateWidgets(QModbusDataUnit::RegisterType table, int address, int size) { for (int i = 0; i < size; ++i) { quint16 value; QString text; switch (table) { case QModbusDataUnit::Coils: modbusDevice->data(QModbusDataUnit::Coils, address + i, &value); coilButtons.button(address + i)->setChecked(value); break; case QModbusDataUnit::HoldingRegisters: modbusDevice->data(QModbusDataUnit::HoldingRegisters, address + i, &value); registers.value(QStringLiteral("holdReg_%1").arg(address + i))->setText(text .setNum(value, 16)); break; default: break; } } } // -- private void MainWindow::setupDeviceData() { if (!modbusDevice) return; for (int i = 0; i < coilButtons.buttons().count(); ++i) modbusDevice->setData(QModbusDataUnit::Coils, i, coilButtons.button(i)->isChecked()); for (int i = 0; i < discreteButtons.buttons().count(); ++i) { modbusDevice->setData(QModbusDataUnit::DiscreteInputs, i, discreteButtons.button(i)->isChecked()); } bool ok; for (QLineEdit *widget : qAsConst(registers)) { if (widget->objectName().startsWith(QStringLiteral("inReg"))) { modbusDevice->setData(QModbusDataUnit::InputRegisters, widget->property("ID").toInt(), widget->text().toInt(&ok, 16)); } else if (widget->objectName().startsWith(QStringLiteral("holdReg"))) { modbusDevice->setData(QModbusDataUnit::HoldingRegisters, widget->property("ID").toInt(), widget->text().toInt(&ok, 16)); } } } void MainWindow::setupWidgetContainers() { coilButtons.setExclusive(false); discreteButtons.setExclusive(false); QRegularExpression regexp(QStringLiteral("coils_(?<ID>\\d+)")); const QList<QCheckBox *> coils = findChildren<QCheckBox *>(regexp); for (QCheckBox *cbx : coils) coilButtons.addButton(cbx, regexp.match(cbx->objectName()).captured("ID").toInt()); connect(&coilButtons, SIGNAL(buttonClicked(int)), this, SLOT(coilChanged(int))); regexp.setPattern(QStringLiteral("disc_(?<ID>\\d+)")); const QList<QCheckBox *> discs = findChildren<QCheckBox *>(regexp); for (QCheckBox *cbx : discs) discreteButtons.addButton(cbx, regexp.match(cbx->objectName()).captured("ID").toInt()); connect(&discreteButtons, SIGNAL(buttonClicked(int)), this, SLOT(discreteInputChanged(int))); regexp.setPattern(QLatin1String("(in|hold)Reg_(?<ID>\\d+)")); const QList<QLineEdit *> qle = findChildren<QLineEdit *>(regexp); for (QLineEdit *lineEdit : qle) { registers.insert(lineEdit->objectName(), lineEdit); lineEdit->setProperty("ID", regexp.match(lineEdit->objectName()).captured("ID").toInt()); lineEdit->setValidator(new QRegExpValidator(QRegExp(QStringLiteral("[0-9a-f]{0,4}"), Qt::CaseInsensitive), this)); connect(lineEdit, &QLineEdit::textChanged, this, &MainWindow::setRegister); } }