446 lines
16 KiB
C++
446 lines
16 KiB
C++
#include "union_modbus.h"
|
|
#include "ui_union_modbus.h"
|
|
|
|
#include "writeregistermodel.h"
|
|
|
|
#include <QtSerialBus/QModbusTcpClient>
|
|
#include <QtSerialBus/QModbusRtuSerialMaster>
|
|
#include <QStandardItemModel>
|
|
#include <QUrl>
|
|
|
|
enum ModbusConnection {
|
|
Serial,
|
|
Tcp
|
|
};
|
|
|
|
union_modbus *this_modbus;
|
|
|
|
static void stepToPeriodRequest()
|
|
{
|
|
this_modbus->readModbus();
|
|
}
|
|
|
|
union_modbus::union_modbus(QWidget *parent) :
|
|
QWidget(parent),
|
|
ui(new Ui::union_modbus)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
initActions();
|
|
|
|
writeModel = new WriteRegisterModel(this);
|
|
writeModel->setStartAddress(ui->spinTXStartAddress->value());
|
|
writeModel->setNumberOfValues(ui->comboTXNumberOfValues->currentText());
|
|
|
|
ui->writeValueTable->setModel(writeModel);
|
|
ui->writeValueTable->hideColumn(2);
|
|
connect(writeModel, &WriteRegisterModel::updateViewport,
|
|
ui->writeValueTable->viewport(), QOverload<>::of(&QWidget::update));
|
|
|
|
ui->comboTable->addItem(tr("Coils"), QModbusDataUnit::Coils);
|
|
ui->comboTable->addItem(tr("Discrete Inputs"), QModbusDataUnit::DiscreteInputs);
|
|
ui->comboTable->addItem(tr("Input Registers"), QModbusDataUnit::InputRegisters);
|
|
ui->comboTable->addItem(tr("Holding Registers"), QModbusDataUnit::HoldingRegisters);
|
|
|
|
this_modbus=this;
|
|
RequestPeriodTimer.setSingleShot(true);
|
|
connect(&RequestPeriodTimer, &QTimer::timeout, this, &stepToPeriodRequest);
|
|
|
|
#if QT_CONFIG(modbus_serialport)
|
|
ui->connectType->setCurrentIndex(0);
|
|
onConnectTypeChanged(0);
|
|
#else
|
|
// lock out the serial port option
|
|
ui->connectType->setCurrentIndex(1);
|
|
onConnectTypeChanged(1);
|
|
ui->connectType->setEnabled(false);
|
|
#endif
|
|
|
|
auto model = new QStandardItemModel(10, 1, this);
|
|
for (int i = 0; i < 100; ++i)
|
|
model->setItem(i, new QStandardItem(QStringLiteral("%1").arg(i + 1)));
|
|
ui->comboTXNumberOfValues->setModel(model);
|
|
|
|
connect(ui->comboTXNumberOfValues, &QComboBox::currentTextChanged,
|
|
writeModel, &WriteRegisterModel::setNumberOfValues);
|
|
ui->comboTXNumberOfValues->setCurrentText("10");
|
|
auto valueChanged = QOverload<int>::of(&QSpinBox::valueChanged);
|
|
connect(ui->spinTXStartAddress, valueChanged, writeModel, &WriteRegisterModel::setStartAddress);
|
|
connect(ui->spinTXStartAddress, valueChanged, this, [this, model](int i) {
|
|
int lastPossibleIndex = 0;
|
|
const int currentIndex = ui->comboTXNumberOfValues->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->comboTXNumberOfValues->setCurrentIndex(lastPossibleIndex);
|
|
});
|
|
}
|
|
|
|
union_modbus::~union_modbus()
|
|
{
|
|
if(modbusDevice->state()==QModbusDevice::ConnectedState)
|
|
{
|
|
modbusDevice->disconnectDevice();
|
|
}
|
|
delete ui;
|
|
}
|
|
|
|
void union_modbus::statusBarClear()
|
|
{
|
|
ui->StatusBar->clear();
|
|
}
|
|
|
|
void union_modbus::initActions()
|
|
{
|
|
connect(ui->buttonConnectOrDisconnect, &QPushButton::clicked,
|
|
this, &union_modbus::onConnectButtonClicked);
|
|
connect(ui->buttonRead, &QPushButton::clicked,
|
|
this, &union_modbus::onReadButtonClicked);
|
|
connect(ui->buttonWrite, &QPushButton::clicked,
|
|
this, &union_modbus::onWriteButtonClicked);
|
|
connect(ui->buttonReadWrite, &QPushButton::clicked,
|
|
this, &union_modbus::onReadWriteButtonClicked);
|
|
connect(ui->comboTable, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
this, &union_modbus::onWriteTableChanged);
|
|
connect(ui->connectType, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
this, &union_modbus::onConnectTypeChanged);
|
|
|
|
statusBarTimeOut.setSingleShot(true);
|
|
connect(&statusBarTimeOut, &QTimer::timeout, this, &union_modbus::statusBarClear);
|
|
}
|
|
|
|
void union_modbus::onConnectTypeChanged(int index)
|
|
{
|
|
if (modbusDevice) {
|
|
modbusDevice->disconnectDevice();
|
|
delete modbusDevice;
|
|
modbusDevice = nullptr;
|
|
}
|
|
|
|
auto type = static_cast<ModbusConnection>(index);
|
|
if (type == Serial) {
|
|
#if QT_CONFIG(modbus_serialport)
|
|
ui->portEdit->clear();
|
|
modbusDevice = new QModbusRtuSerialMaster(this);
|
|
#endif
|
|
} else if (type == Tcp) {
|
|
modbusDevice = new QModbusTcpClient(this);
|
|
if (ui->portEdit->text().isEmpty())
|
|
ui->portEdit->setText(QLatin1String("127.0.0.1:502"));
|
|
}
|
|
|
|
connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
|
|
ui->StatusBar->setText(modbusDevice->errorString());
|
|
statusBarTimeOut.start(5000);
|
|
});
|
|
|
|
if (!modbusDevice) {
|
|
ui->buttonConnectOrDisconnect->setDisabled(true);
|
|
if (type == Serial)
|
|
{
|
|
ui->StatusBar->setText(tr("Could not create Modbus master."));
|
|
statusBarTimeOut.start(5000);
|
|
}
|
|
else
|
|
{
|
|
ui->StatusBar->setText(tr("Could not create Modbus client."));
|
|
statusBarTimeOut.start(5000);
|
|
}
|
|
} else {
|
|
connect(modbusDevice, &QModbusClient::stateChanged,
|
|
this, &union_modbus::onModbusStateChanged);
|
|
}
|
|
}
|
|
void union_modbus::ApplySettings()
|
|
{
|
|
#if QT_CONFIG(modbus_serialport)
|
|
m_settings.parity = ui->conboParity->currentIndex();
|
|
if (m_settings.parity > 0)
|
|
m_settings.parity++;
|
|
m_settings.baud = ui->comboBaudRate->currentText().toInt();
|
|
m_settings.dataBits = ui->comboDataBits->currentText().toInt();
|
|
m_settings.stopBits = ui->comboStopBits->currentText().toInt();
|
|
#endif
|
|
m_settings.responseTime = ui->spinResponseTimeout->value();
|
|
m_settings.numberOfRetries = ui->spinNumberOfRetries->value();
|
|
}
|
|
void union_modbus::onConnectButtonClicked()
|
|
{
|
|
if (!modbusDevice)
|
|
return;
|
|
statusBarTimeOut.stop();
|
|
ui->StatusBar->clear();
|
|
RequestPeriodTimer.stop();
|
|
if (modbusDevice->state() != QModbusDevice::ConnectedState) {
|
|
ApplySettings();
|
|
if (static_cast<ModbusConnection>(ui->connectType->currentIndex()) == Serial) {
|
|
modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
|
|
ui->portEdit->text());
|
|
#if QT_CONFIG(modbus_serialport)
|
|
modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
|
|
m_settings.parity);
|
|
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
|
|
m_settings.baud);
|
|
modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
|
|
m_settings.dataBits);
|
|
modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
|
|
m_settings.stopBits);
|
|
#endif
|
|
} else {
|
|
const QUrl url = QUrl::fromUserInput(ui->portEdit->text());
|
|
modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
|
|
modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
|
|
}
|
|
modbusDevice->setTimeout(m_settings.responseTime);
|
|
modbusDevice->setNumberOfRetries(m_settings.numberOfRetries);
|
|
|
|
if (!modbusDevice->connectDevice()) {
|
|
ui->StatusBar->setText(tr("Connect failed: ") + modbusDevice->errorString());
|
|
statusBarTimeOut.start(5000);
|
|
}
|
|
else
|
|
{
|
|
ui->frameSettings->setEnabled(false);
|
|
ui->frameSerialTCPPort->setEnabled(false);
|
|
}
|
|
} else {
|
|
modbusDevice->disconnectDevice();
|
|
ui->frameSettings->setEnabled(true);
|
|
ui->frameSerialTCPPort->setEnabled(true);
|
|
}
|
|
}
|
|
|
|
void union_modbus::onModbusStateChanged(int state)
|
|
{
|
|
bool connected = (state != QModbusDevice::UnconnectedState);
|
|
|
|
if (state == QModbusDevice::UnconnectedState)
|
|
ui->buttonConnectOrDisconnect->setText(tr("Connect"));
|
|
else if (state == QModbusDevice::ConnectedState)
|
|
ui->buttonConnectOrDisconnect->setText(tr("Disconnect"));
|
|
}
|
|
|
|
void union_modbus::onReadButtonClicked()
|
|
{
|
|
if(CurrentReadButtonPosition)
|
|
{
|
|
if(ui->checkPeriodRequest->checkState()==Qt::Checked)
|
|
{
|
|
ui->buttonRead->setText("Stop Read");
|
|
CurrentReadButtonPosition = false;
|
|
readModbus();
|
|
}
|
|
else
|
|
{
|
|
readModbus();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ui->buttonRead->setText("Read");
|
|
CurrentReadButtonPosition = true;
|
|
RequestPeriodTimer.stop();
|
|
}
|
|
}
|
|
void union_modbus::readModbus()
|
|
{
|
|
if (!modbusDevice)
|
|
return;
|
|
statusBarTimeOut.stop();
|
|
ui->StatusBar->clear();
|
|
if (auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->serverEdit->value())) {
|
|
if (!reply->isFinished())
|
|
connect(reply, &QModbusReply::finished, this, &union_modbus::onReadReady);
|
|
else
|
|
delete reply; // broadcast replies return immediately
|
|
|
|
} else {
|
|
ui->StatusBar->setText(tr("Read error: ") + modbusDevice->errorString());
|
|
statusBarTimeOut.start(5000);
|
|
}
|
|
// if (auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->serverEdit->value())) {
|
|
// if (!reply->isFinished())
|
|
// connect(reply, &QModbusReply::finished, this, &union_modbus::onReadReady);
|
|
// else
|
|
// delete reply; // broadcast replies return immediately
|
|
// } else {
|
|
// ui->StatusBar->setText(tr("Read error: ") + modbusDevice->errorString());
|
|
// statusBarTimeOut.start(5000);
|
|
// }
|
|
// if (auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->serverEdit->value())) {
|
|
// if (!reply->isFinished())
|
|
// connect(reply, &QModbusReply::finished, this, &union_modbus::onReadReady);
|
|
// else
|
|
// delete reply; // broadcast replies return immediately
|
|
// } else {
|
|
// ui->StatusBar->setText(tr("Read error: ") + modbusDevice->errorString());
|
|
// statusBarTimeOut.start(5000);
|
|
// }
|
|
}
|
|
|
|
void union_modbus::onReadReady()
|
|
{
|
|
auto reply = qobject_cast<QModbusReply *>(sender());
|
|
if (!reply)
|
|
return;
|
|
|
|
ui->readValue->clear();
|
|
if (reply->error() == QModbusDevice::NoError) {
|
|
ui->StatusBar->clear();
|
|
const QModbusDataUnit unit = reply->result();
|
|
for (int i = 0, total = int(unit.valueCount()); i < total; ++i) {
|
|
const QString 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);
|
|
}
|
|
if(CurrentReadButtonPosition == false)
|
|
{
|
|
RequestPeriodTimer.start(ui->spinPeriodTime->value());
|
|
}
|
|
reply->deleteLater();
|
|
return;
|
|
} else if (reply->error() == QModbusDevice::ProtocolError) {
|
|
ui->StatusBar->setText(tr("Read response error: %1 (Mobus exception: 0x%2)").
|
|
arg(reply->errorString()).
|
|
arg(reply->rawResult().exceptionCode(), -1, 16));
|
|
statusBarTimeOut.start(5000);
|
|
} else {
|
|
ui->StatusBar->setText(tr("Read response error: %1 (code: 0x%2)").
|
|
arg(reply->errorString()).
|
|
arg(reply->error(), -1, 16));
|
|
statusBarTimeOut.start(5000);
|
|
}
|
|
if(CurrentReadButtonPosition==false)
|
|
onReadButtonClicked();
|
|
reply->deleteLater();
|
|
}
|
|
|
|
void union_modbus::onWriteButtonClicked()
|
|
{
|
|
if (!modbusDevice)
|
|
return;
|
|
statusBarTimeOut.stop();
|
|
ui->StatusBar->clear();
|
|
|
|
QModbusDataUnit writeUnit = writeRequest();
|
|
QModbusDataUnit::RegisterType table = writeUnit.registerType();
|
|
for (int i = 0, total = int(writeUnit.valueCount()); i < total; ++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) {
|
|
ui->StatusBar->setText(tr("Write response error: %1 (Mobus exception: 0x%2)")
|
|
.arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16));
|
|
statusBarTimeOut.start(5000);
|
|
} else if (reply->error() != QModbusDevice::NoError) {
|
|
ui->StatusBar->setText(tr("Write response error: %1 (code: 0x%2)").
|
|
arg(reply->errorString()).arg(reply->error(), -1, 16));
|
|
statusBarTimeOut.start(5000);
|
|
}
|
|
reply->deleteLater();
|
|
});
|
|
} else {
|
|
// broadcast replies return immediately
|
|
reply->deleteLater();
|
|
}
|
|
} else {
|
|
ui->StatusBar->setText(tr("Write error: ") + modbusDevice->errorString());
|
|
statusBarTimeOut.start(5000);
|
|
}
|
|
}
|
|
|
|
void union_modbus::onReadWriteButtonClicked()
|
|
{
|
|
if (!modbusDevice)
|
|
return;
|
|
ui->readValue->clear();
|
|
ui->StatusBar->clear();
|
|
statusBarTimeOut.stop();
|
|
|
|
QModbusDataUnit writeUnit = writeRequest();
|
|
QModbusDataUnit::RegisterType table = writeUnit.registerType();
|
|
for (int i = 0, total = int(writeUnit.valueCount()); i < total; ++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, &union_modbus::onReadReady);
|
|
else
|
|
delete reply; // broadcast replies return immediately
|
|
} else {
|
|
ui->StatusBar->setText(tr("Read error: ") + modbusDevice->errorString());
|
|
statusBarTimeOut.start(5000);
|
|
}
|
|
}
|
|
|
|
void union_modbus::onWriteTableChanged(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->buttonReadWrite->setEnabled(index == 3);
|
|
ui->buttonWrite->setEnabled(coilsOrHolding);
|
|
ui->layoutTX->setEnabled(coilsOrHolding);
|
|
}
|
|
|
|
QModbusDataUnit union_modbus::readRequest() const
|
|
{
|
|
const auto table =
|
|
static_cast<QModbusDataUnit::RegisterType>(ui->comboTable->currentData().toInt());
|
|
|
|
int startAddress = ui->spinRXStartAddress->value();
|
|
Q_ASSERT(startAddress >= 0 && startAddress < 340);
|
|
|
|
// do not go beyond 10 entries
|
|
quint16 numberOfEntries = qMin(ui->comboRXNumberOfValues->currentText().toUShort(), quint16(340 - startAddress));
|
|
return QModbusDataUnit(table, startAddress, numberOfEntries);
|
|
}
|
|
|
|
QModbusDataUnit union_modbus::writeRequest() const
|
|
{
|
|
const auto table =
|
|
static_cast<QModbusDataUnit::RegisterType>(ui->comboTable->currentData().toInt());
|
|
|
|
int startAddress = ui->spinTXStartAddress->value();
|
|
Q_ASSERT(startAddress >= 0 && startAddress < 340);
|
|
|
|
// do not go beyond 10 entries
|
|
quint16 numberOfEntries = qMin(ui->comboTXNumberOfValues->currentText().toUShort(), quint16(340 - startAddress));
|
|
return QModbusDataUnit(table, startAddress, numberOfEntries);
|
|
}
|
|
|
|
void union_modbus::on_checkPeriodRequest_stateChanged(int arg1)
|
|
{
|
|
if (ui->checkPeriodRequest->checkState()==Qt::Checked)
|
|
{
|
|
ui->spinPeriodTime->setEnabled(true);
|
|
}
|
|
else
|
|
{
|
|
ui->spinPeriodTime->setEnabled(false);
|
|
RequestPeriodTimer.stop();
|
|
}
|
|
}
|