Modbus Master example▲
Sélectionnez
/**
**************************************************************************
**
** 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-&
gt;setupUi(this
);
m_settingsDialog =
new
SettingsDialog(this
);
initActions();
writeModel =
new
WriteRegisterModel(this
);
writeModel-&
gt;setStartAddress(ui-&
gt;writeAddress-&
gt;value());
writeModel-&
gt;setNumberOfValues(ui-&
gt;writeSize-&
gt;currentText());
ui-&
gt;writeValueTable-&
gt;setModel(writeModel);
ui-&
gt;writeValueTable-&
gt;hideColumn(2
);
connect(writeModel, &
amp;WriteRegisterModel::
updateViewport, ui-&
gt;writeValueTable-&
gt;viewport(),
static_cast
&
lt;void
(QWidget::
*
)()&
gt;(&
amp;QWidget::
update));
ui-&
gt;writeTable-&
gt;addItem(tr("Coils"
), QModbusDataUnit::
Coils);
ui-&
gt;writeTable-&
gt;addItem(tr("Discrete Inputs"
), QModbusDataUnit::
DiscreteInputs);
ui-&
gt;writeTable-&
gt;addItem(tr("Input Registers"
), QModbusDataUnit::
InputRegisters);
ui-&
gt;writeTable-&
gt;addItem(tr("Holding Registers"
), QModbusDataUnit::
HoldingRegisters);
ui-&
gt;connectType-&
gt;setCurrentIndex(0
);
on_connectType_currentIndexChanged(0
);
auto
model =
new
QStandardItemModel(10
, 1
, this
);
for
(int
i =
0
; i &
lt; 10
; ++
i)
model-&
gt;setItem(i, new
QStandardItem(QStringLiteral("%1"
).arg(i +
1
)));
ui-&
gt;writeSize-&
gt;setModel(model);
ui-&
gt;writeSize-&
gt;setCurrentText("10"
);
connect(ui-&
gt;writeSize,&
amp;QComboBox::
currentTextChanged, writeModel,
&
amp;WriteRegisterModel::
setNumberOfValues);
auto
valueChanged =
static_cast
&
lt;void
(QSpinBox::
*
)(int
)&
gt; (&
amp;QSpinBox::
valueChanged);
connect(ui-&
gt;writeAddress, valueChanged, writeModel, &
amp;WriteRegisterModel::
setStartAddress);
connect(ui-&
gt;writeAddress, valueChanged, this
, [this
, model](int
i) {
int
lastPossibleIndex =
0
;
const
int
currentIndex =
ui-&
gt;writeSize-&
gt;currentIndex();
for
(int
ii =
0
; ii &
lt; 10
; ++
ii) {
if
(ii &
lt; (10
-
i)) {
lastPossibleIndex =
ii;
model-&
gt;item(ii)-&
gt;setEnabled(true
);
}
else
{
model-&
gt;item(ii)-&
gt;setEnabled(false
);
}
}
if
(currentIndex &
gt; lastPossibleIndex)
ui-&
gt;writeSize-&
gt;setCurrentIndex(lastPossibleIndex);
}
);
}
MainWindow::
~
MainWindow()
{
if
(modbusDevice)
modbusDevice-&
gt;disconnectDevice();
delete
modbusDevice;
delete
ui;
}
void
MainWindow::
initActions()
{
ui-&
gt;actionConnect-&
gt;setEnabled(true
);
ui-&
gt;actionDisconnect-&
gt;setEnabled(false
);
ui-&
gt;actionExit-&
gt;setEnabled(true
);
ui-&
gt;actionOptions-&
gt;setEnabled(true
);
connect(ui-&
gt;actionConnect, &
amp;QAction::
triggered,
this
, &
amp;MainWindow::
on_connectButton_clicked);
connect(ui-&
gt;actionDisconnect, &
amp;QAction::
triggered,
this
, &
amp;MainWindow::
on_connectButton_clicked);
connect(ui-&
gt;actionExit, &
amp;QAction::
triggered, this
, &
amp;QMainWindow::
close);
connect(ui-&
gt;actionOptions, &
amp;QAction::
triggered, m_settingsDialog, &
amp;QDialog::
show);
}
void
MainWindow::
on_connectType_currentIndexChanged(int
index)
{
if
(modbusDevice) {
modbusDevice-&
gt;disconnectDevice();
delete
modbusDevice;
modbusDevice =
nullptr
;
}
auto
type =
static_cast
&
lt;ModbusConnection&
gt; (index);
if
(type ==
Serial) {
modbusDevice =
new
QModbusRtuSerialMaster(this
);
}
else
if
(type ==
Tcp) {
modbusDevice =
new
QModbusTcpClient(this
);
if
(ui-&
gt;portEdit-&
gt;text().isEmpty())
ui-&
gt;portEdit-&
gt;setText(QLatin1Literal("127.0.0.1:502"
));
}
connect(modbusDevice, &
amp;QModbusClient::
errorOccurred, [this
](QModbusDevice::
Error) {
statusBar()-&
gt;showMessage(modbusDevice-&
gt;errorString(), 5000
);
}
);
if
(!
modbusDevice) {
ui-&
gt;connectButton-&
gt;setDisabled(true
);
if
(type ==
Serial)
statusBar()-&
gt;showMessage(tr("Could not create Modbus master."
), 5000
);
else
statusBar()-&
gt;showMessage(tr("Could not create Modbus client."
), 5000
);
}
else
{
connect(modbusDevice, &
amp;QModbusClient::
stateChanged,
this
, &
amp;MainWindow::
onStateChanged);
}
}
void
MainWindow::
on_connectButton_clicked()
{
if
(!
modbusDevice)
return
;
statusBar()-&
gt;clearMessage();
if
(modbusDevice-&
gt;state() !=
QModbusDevice::
ConnectedState) {
if
(static_cast
&
lt;ModbusConnection&
gt; (ui-&
gt;connectType-&
gt;currentIndex()) ==
Serial) {
modbusDevice-&
gt;setConnectionParameter(QModbusDevice::
SerialPortNameParameter,
ui-&
gt;portEdit-&
gt;text());
modbusDevice-&
gt;setConnectionParameter(QModbusDevice::
SerialParityParameter,
m_settingsDialog-&
gt;settings().parity);
modbusDevice-&
gt;setConnectionParameter(QModbusDevice::
SerialBaudRateParameter,
m_settingsDialog-&
gt;settings().baud);
modbusDevice-&
gt;setConnectionParameter(QModbusDevice::
SerialDataBitsParameter,
m_settingsDialog-&
gt;settings().dataBits);
modbusDevice-&
gt;setConnectionParameter(QModbusDevice::
SerialStopBitsParameter,
m_settingsDialog-&
gt;settings().stopBits);
}
else
{
const
QUrl url =
QUrl::
fromUserInput(ui-&
gt;portEdit-&
gt;text());
modbusDevice-&
gt;setConnectionParameter(QModbusDevice::
NetworkPortParameter, url.port());
modbusDevice-&
gt;setConnectionParameter(QModbusDevice::
NetworkAddressParameter, url.host());
}
modbusDevice-&
gt;setTimeout(m_settingsDialog-&
gt;settings().responseTime);
modbusDevice-&
gt;setNumberOfRetries(m_settingsDialog-&
gt;settings().numberOfRetries);
if
(!
modbusDevice-&
gt;connectDevice()) {
statusBar()-&
gt;showMessage(tr("Connect failed: "
) +
modbusDevice-&
gt;errorString(), 5000
);
}
else
{
ui-&
gt;actionConnect-&
gt;setEnabled(false
);
ui-&
gt;actionDisconnect-&
gt;setEnabled(true
);
}
}
else
{
modbusDevice-&
gt;disconnectDevice();
ui-&
gt;actionConnect-&
gt;setEnabled(true
);
ui-&
gt;actionDisconnect-&
gt;setEnabled(false
);
}
}
void
MainWindow::
onStateChanged(int
state)
{
bool
connected =
(state !=
QModbusDevice::
UnconnectedState);
ui-&
gt;actionConnect-&
gt;setEnabled(!
connected);
ui-&
gt;actionDisconnect-&
gt;setEnabled(connected);
if
(state ==
QModbusDevice::
UnconnectedState)
ui-&
gt;connectButton-&
gt;setText(tr("Connect"
));
else
if
(state ==
QModbusDevice::
ConnectedState)
ui-&
gt;connectButton-&
gt;setText(tr("Disconnect"
));
}
void
MainWindow::
on_readButton_clicked()
{
if
(!
modbusDevice)
return
;
ui-&
gt;readValue-&
gt;clear();
statusBar()-&
gt;clearMessage();
if
(auto
*
reply =
modbusDevice-&
gt;sendReadRequest(readRequest(), ui-&
gt;serverEdit-&
gt;value())) {
if
(!
reply-&
gt;isFinished())
connect(reply, &
amp;QModbusReply::
finished, this
, &
amp;MainWindow::
readReady);
else
delete
reply; // broadcast replies return immediately
}
else
{
statusBar()-&
gt;showMessage(tr("Read error: "
) +
modbusDevice-&
gt;errorString(), 5000
);
}
}
void
MainWindow::
readReady()
{
auto
reply =
qobject_cast&
lt;QModbusReply *&
gt;(sender());
if
(!
reply)
return
;
if
(reply-&
gt;error() ==
QModbusDevice::
NoError) {
const
QModbusDataUnit unit =
reply-&
gt;result();
for
(uint i =
0
; i &
lt; unit.valueCount(); i++
) {
const
QString entry =
tr("Address: %1, Value: %2"
).arg(unit.startAddress() +
i)
.arg(QString::
number(unit.value(i),
unit.registerType() &
lt;=
QModbusDataUnit::
Coils ? 10
: 16
));
ui-&
gt;readValue-&
gt;addItem(entry);
}
}
else
if
(reply-&
gt;error() ==
QModbusDevice::
ProtocolError) {
statusBar()-&
gt;showMessage(tr("Read response error: %1 (Mobus exception: 0x%2)"
).
arg(reply-&
gt;errorString()).
arg(reply-&
gt;rawResult().exceptionCode(), -
1
, 16
), 5000
);
}
else
{
statusBar()-&
gt;showMessage(tr("Read response error: %1 (code: 0x%2)"
).
arg(reply-&
gt;errorString()).
arg(reply-&
gt;error(), -
1
, 16
), 5000
);
}
reply-&
gt;deleteLater();
}
void
MainWindow::
on_writeButton_clicked()
{
if
(!
modbusDevice)
return
;
statusBar()-&
gt;clearMessage();
QModbusDataUnit writeUnit =
writeRequest();
QModbusDataUnit::
RegisterType table =
writeUnit.registerType();
for
(uint i =
0
; i &
lt; writeUnit.valueCount(); i++
) {
if
(table ==
QModbusDataUnit::
Coils)
writeUnit.setValue(i, writeModel-&
gt;m_coils[i +
writeUnit.startAddress()]);
else
writeUnit.setValue(i, writeModel-&
gt;m_holdingRegisters[i +
writeUnit.startAddress()]);
}
if
(auto
*
reply =
modbusDevice-&
gt;sendWriteRequest(writeUnit, ui-&
gt;serverEdit-&
gt;value())) {
if
(!
reply-&
gt;isFinished()) {
connect(reply, &
amp;QModbusReply::
finished, this
, [this
, reply]() {
if
(reply-&
gt;error() ==
QModbusDevice::
ProtocolError) {
statusBar()-&
gt;showMessage(tr("Write response error: %1 (Mobus exception: 0x%2)"
)
.arg(reply-&
gt;errorString()).arg(reply-&
gt;rawResult().exceptionCode(), -
1
, 16
),
5000
);
}
else
if
(reply-&
gt;error() !=
QModbusDevice::
NoError) {
statusBar()-&
gt;showMessage(tr("Write response error: %1 (code: 0x%2)"
).
arg(reply-&
gt;errorString()).arg(reply-&
gt;error(), -
1
, 16
), 5000
);
}
reply-&
gt;deleteLater();
}
);
}
else
{
// broadcast replies return immediately
reply-&
gt;deleteLater();
}
}
else
{
statusBar()-&
gt;showMessage(tr("Write error: "
) +
modbusDevice-&
gt;errorString(), 5000
);
}
}
void
MainWindow::
on_readWriteButton_clicked()
{
if
(!
modbusDevice)
return
;
ui-&
gt;readValue-&
gt;clear();
statusBar()-&
gt;clearMessage();
QModbusDataUnit writeUnit =
writeRequest();
QModbusDataUnit::
RegisterType table =
writeUnit.registerType();
for
(uint i =
0
; i &
lt; writeUnit.valueCount(); i++
) {
if
(table ==
QModbusDataUnit::
Coils)
writeUnit.setValue(i, writeModel-&
gt;m_coils[i +
writeUnit.startAddress()]);
else
writeUnit.setValue(i, writeModel-&
gt;m_holdingRegisters[i +
writeUnit.startAddress()]);
}
if
(auto
*
reply =
modbusDevice-&
gt;sendReadWriteRequest(readRequest(), writeUnit,
ui-&
gt;serverEdit-&
gt;value())) {
if
(!
reply-&
gt;isFinished())
connect(reply, &
amp;QModbusReply::
finished, this
, &
amp;MainWindow::
readReady);
else
delete
reply; // broadcast replies return immediately
}
else
{
statusBar()-&
gt;showMessage(tr("Read error: "
) +
modbusDevice-&
gt;errorString(), 5000
);
}
}
void
MainWindow::
on_writeTable_currentIndexChanged(int
index)
{
const
bool
coilsOrHolding =
index ==
0
||
index ==
3
;
if
(coilsOrHolding) {
ui-&
gt;writeValueTable-&
gt;setColumnHidden(1
, index !=
0
);
ui-&
gt;writeValueTable-&
gt;setColumnHidden(2
, index !=
3
);
ui-&
gt;writeValueTable-&
gt;resizeColumnToContents(0
);
}
ui-&
gt;readWriteButton-&
gt;setEnabled(index ==
3
);
ui-&
gt;writeButton-&
gt;setEnabled(coilsOrHolding);
ui-&
gt;writeGroupBox-&
gt;setEnabled(coilsOrHolding);
}
QModbusDataUnit MainWindow::
readRequest() const
{
const
auto
table =
static_cast
&
lt;QModbusDataUnit::
RegisterType&
gt; (ui-&
gt;writeTable-&
gt;currentData().toInt());
int
startAddress =
ui-&
gt;readAddress-&
gt;value();
Q_ASSERT(startAddress &
gt;=
0
&
amp;&
amp; startAddress &
lt; 10
);
// do not go beyond 10 entries
int
numberOfEntries =
qMin(ui-&
gt;readSize-&
gt;currentText().toInt(), 10
-
startAddress);
return
QModbusDataUnit(table, startAddress, numberOfEntries);
}
QModbusDataUnit MainWindow::
writeRequest() const
{
const
auto
table =
static_cast
&
lt;QModbusDataUnit::
RegisterType&
gt; (ui-&
gt;writeTable-&
gt;currentData().toInt());
int
startAddress =
ui-&
gt;writeAddress-&
gt;value();
Q_ASSERT(startAddress &
gt;=
0
&
amp;&
amp; startAddress &
lt; 10
);
// do not go beyond 10 entries
int
numberOfEntries =
qMin(ui-&
gt;writeSize-&
gt;currentText().toInt(), 10
-
startAddress);
return
QModbusDataUnit(table, startAddress, numberOfEntries);
}