/****************************************************************************
**
** 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 "ui_mainwindow.h"#include "settingsdialog.h"#include "writeregistermodel.h"#include <QModbusTcpClient>#include <QModbusRtuSerialMaster>#include <QStandardItemModel>#include <QStatusBar>#include <QUrl>enum ModbusConnection {
Serial,
Tcp
};
MainWindow::MainWindow(QWidget*parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, lastRequest(nullptr)
, modbusDevice(nullptr)
{
ui->setupUi(this);
m_settingsDialog =new SettingsDialog(this);
initActions();
writeModel =new WriteRegisterModel(this);
writeModel->setStartAddress(ui->writeAddress->value());
writeModel->setNumberOfValues(ui->writeSize->currentText());
ui->writeValueTable->setModel(writeModel);
ui->writeValueTable->hideColumn(2);
connect(writeModel,&WriteRegisterModel::updateViewport, ui->writeValueTable->viewport(),static_cast<void (QWidget::*)()>(&QWidget::update));
ui->writeTable->addItem(tr("Coils"),QModbusDataUnit::Coils);
ui->writeTable->addItem(tr("Discrete Inputs"),QModbusDataUnit::DiscreteInputs);
ui->writeTable->addItem(tr("Input Registers"),QModbusDataUnit::InputRegisters);
ui->writeTable->addItem(tr("Holding Registers"),QModbusDataUnit::HoldingRegisters);
ui->connectType->setCurrentIndex(0);
on_connectType_currentIndexChanged(0);
auto model =newQStandardItemModel(10,1,this);
for (int i =0; i <10; ++i)
model->setItem(i,newQStandardItem(QStringLiteral("%1").arg(i +1)));
ui->writeSize->setModel(model);
ui->writeSize->setCurrentText("10");
connect(ui->writeSize,&QComboBox::currentTextChanged, writeModel,&WriteRegisterModel::setNumberOfValues);
auto valueChanged =static_cast<void (QSpinBox::*)(int)> (&QSpinBox::valueChanged);
connect(ui->writeAddress, valueChanged, writeModel,&WriteRegisterModel::setStartAddress);
connect(ui->writeAddress, valueChanged,this,[this, model](int i) {
int lastPossibleIndex =0;
constint currentIndex = ui->writeSize->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->writeSize->setCurrentIndex(lastPossibleIndex);
});
}
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->disconnectDevice();
delete modbusDevice;
modbusDevice = nullptr;
}
auto type =static_cast<ModbusConnection> (index);
if (type == Serial) {
modbusDevice =newQModbusRtuSerialMaster(this);
} elseif (type == Tcp) {
modbusDevice =newQModbusTcpClient(this);
if (ui->portEdit->text().isEmpty())
ui->portEdit->setText(QLatin1Literal("127.0.0.1:502"));
}
connect(modbusDevice,&QModbusClient::errorOccurred,[this](QModbusDevice::Error) {
statusBar()->showMessage(modbusDevice->errorString(),5000);
});
if (!modbusDevice) {
ui->connectButton->setDisabled(true);
if (type == Serial)
statusBar()->showMessage(tr("Could not create Modbus master."),5000);
else
statusBar()->showMessage(tr("Could not create Modbus client."),5000);
} else {
connect(modbusDevice,&QModbusClient::stateChanged,this,&MainWindow::onStateChanged);
}
}
void MainWindow::on_connectButton_clicked()
{
if (!modbusDevice)
return;
statusBar()->clearMessage();
if (modbusDevice->state() !=QModbusDevice::ConnectedState) {
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 {
constQUrl url =QUrl::fromUserInput(ui->portEdit->text());
modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
}
modbusDevice->setTimeout(m_settingsDialog->settings().responseTime);
modbusDevice->setNumberOfRetries(m_settingsDialog->settings().numberOfRetries);
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"));
elseif (state ==QModbusDevice::ConnectedState)
ui->connectButton->setText(tr("Disconnect"));
}
void MainWindow::on_readButton_clicked()
{
if (!modbusDevice)
return;
ui->readValue->clear();
statusBar()->clearMessage();
if (auto*reply = modbusDevice->sendReadRequest(readRequest(), ui->serverEdit->value())) {
if (!reply->isFinished())
connect(reply,&QModbusReply::finished,this,&MainWindow::readReady);
elsedelete reply; // broadcast replies return immediately
} else {
statusBar()->showMessage(tr("Read error: ") + modbusDevice->errorString(),5000);
}
}
void MainWindow::readReady()
{
auto reply = qobject_cast<QModbusReply*>(sender());
if (!reply)
return;
if (reply->error() ==QModbusDevice::NoError) {
constQModbusDataUnit unit = reply->result();
for (uint i =0; i < unit.valueCount(); i++) {
constQString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i)
.arg(QString::number(unit.value(i),
unit.registerType() <=QModbusDataUnit::Coils ?10 : 16));
ui->readValue->addItem(entry);
}
} elseif (reply->error() ==QModbusDevice::ProtocolError) {
statusBar()->showMessage(tr("Read response error: %1 (Mobus 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::on_writeButton_clicked()
{
if (!modbusDevice)
return;
statusBar()->clearMessage();
QModbusDataUnit writeUnit = writeRequest();
QModbusDataUnit::RegisterType table = writeUnit.registerType();
for (uint i =0; i < writeUnit.valueCount(); i++) {
if (table ==QModbusDataUnit::Coils)
writeUnit.setValue(i, writeModel->m_coils[i + writeUnit.startAddress()]);
else
writeUnit.setValue(i, writeModel->m_holdingRegisters[i + writeUnit.startAddress()]);
}
if (auto*reply = modbusDevice->sendWriteRequest(writeUnit, ui->serverEdit->value())) {
if (!reply->isFinished()) {
connect(reply,&QModbusReply::finished,this,[this, reply]() {
if (reply->error() ==QModbusDevice::ProtocolError) {
statusBar()->showMessage(tr("Write response error: %1 (Mobus exception: 0x%2)")
.arg(reply->errorString()).arg(reply->rawResult().exceptionCode(),-1,16),5000);
} elseif (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 {
// broadcast replies return immediately
reply->deleteLater();
}
} else {
statusBar()->showMessage(tr("Write error: ") + modbusDevice->errorString(),5000);
}
}
void MainWindow::on_readWriteButton_clicked()
{
if (!modbusDevice)
return;
ui->readValue->clear();
statusBar()->clearMessage();
QModbusDataUnit writeUnit = writeRequest();
QModbusDataUnit::RegisterType table = writeUnit.registerType();
for (uint i =0; i < writeUnit.valueCount(); i++) {
if (table ==QModbusDataUnit::Coils)
writeUnit.setValue(i, writeModel->m_coils[i + writeUnit.startAddress()]);
else
writeUnit.setValue(i, writeModel->m_holdingRegisters[i + writeUnit.startAddress()]);
}
if (auto*reply = modbusDevice->sendReadWriteRequest(readRequest(), writeUnit,
ui->serverEdit->value())) {
if (!reply->isFinished())
connect(reply,&QModbusReply::finished,this,&MainWindow::readReady);
elsedelete reply; // broadcast replies return immediately
} else {
statusBar()->showMessage(tr("Read error: ") + modbusDevice->errorString(),5000);
}
}
void MainWindow::on_writeTable_currentIndexChanged(int index)
{
const bool coilsOrHolding = index ==0|| index ==3;
if (coilsOrHolding) {
ui->writeValueTable->setColumnHidden(1, index !=0);
ui->writeValueTable->setColumnHidden(2, index !=3);
ui->writeValueTable->resizeColumnToContents(0);
}
ui->readWriteButton->setEnabled(index ==3);
ui->writeButton->setEnabled(coilsOrHolding);
ui->writeGroupBox->setEnabled(coilsOrHolding);
}
QModbusDataUnit MainWindow::readRequest() const
{
constauto table =static_cast<QModbusDataUnit::RegisterType> (ui->writeTable->currentData().toInt());
int startAddress = ui->readAddress->value();
Q_ASSERT(startAddress >=0&& startAddress <10);
// do not go beyond 10 entriesint numberOfEntries =qMin(ui->readSize->currentText().toInt(),10- startAddress);
returnQModbusDataUnit(table, startAddress, numberOfEntries);
}
QModbusDataUnit MainWindow::writeRequest() const
{
constauto table =static_cast<QModbusDataUnit::RegisterType> (ui->writeTable->currentData().toInt());
int startAddress = ui->writeAddress->value();
Q_ASSERT(startAddress >=0&& startAddress <10);
// do not go beyond 10 entriesint numberOfEntries =qMin(ui->writeSize->currentText().toInt(),10- startAddress);
returnQModbusDataUnit(table, startAddress, numberOfEntries);
}