#include "union_modbus.h" #include "ui_union_modbus.h" #include "writeregistermodel.h" #include #include #include #include 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::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::of(&QComboBox::currentIndexChanged), this, &union_modbus::onWriteTableChanged); connect(ui->connectType, QOverload::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(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(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(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(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(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(); } }