mainwindow.cpp Example File | Qt Serial Bus 5.6

Qt Documentation (Technology Preview) | Qt Serial Bus | Modbus Slave example | mainwindow.cpp Example File

Qt 5.6.2 Reference Documentation

# mainwindow.cpp Example File

modbus/slave/mainwindow.cpp

      /****************************************************************************
      **
      ** Copyright (C) 2015 The Qt Company Ltd.
      ** Contact: http://www.qt.io/licensing/
      **
      ** This file is part of the examples of the QtSerialBus module.
      **
      ** $QT_BEGIN_LICENSE:BSD$
      ** 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;
          foreach (QLineEdit *widget, 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);
          foreach (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);
          foreach (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);
          foreach (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);
          }
      }