#include #include #include #include #include #include #include #include "mainwindow.h" #include #include #include #include #include #include ColoredSquare::ColoredSquare(QWidget *parent) : QWidget(parent), m_color(Qt::gray) { } void ColoredSquare::setColor(const QColor& color) { m_color = color; update(); } void ColoredSquare::paintEvent(QPaintEvent *) { QPainter painter(this); painter.fillRect(rect(), m_color); } Mainwindows::Mainwindows(QWidget *parent) : QWidget(parent), m_modbusClient(nullptr), m_connected(false) { loadConfiguration(); createUIElements(); initModbusConnection(); } void Mainwindows::loadConfiguration() { const QString filePath = QCoreApplication::applicationDirPath() + "/config.ini"; QSettings settings(filePath, QSettings::IniFormat); settings.beginGroup("Connection"); m_ipAddress = settings.value("ip", "127.0.0.1").toString(); m_port = settings.value("port", 502).toInt(); m_responseTimeout = settings.value("responseTimeout", 1000).toInt(); m_connectTimeout = settings.value("connectTimeout", 3000).toInt(); m_pollInterval = settings.value("timeBetweenPolls", 100).toInt(); settings.endGroup(); qDebug() << "ip: " << m_ipAddress << "port: " << m_port; qDebug() << "responseTimeout: " << m_responseTimeout; qDebug() << "connectTimeout: " << m_connectTimeout; qDebug() << "poolInterval: " << m_pollInterval; // Read registers configuration settings.beginGroup("Registers"); m_buttonCount = settings.value("buttonCount", 0).toInt(); m_indicatorCount = settings.value("indicatorCount", 0).toInt(); m_buttonRegisters.clear(); m_indicatorRegisters.clear(); // Load button registers for(int i = 1; i <= m_buttonCount; ++i) { QString buttonKey = QString("button_reg%1").arg(i); int buttonReg = settings.value(buttonKey, 0).toInt(); if(buttonReg > 0) { m_buttonRegisters.append(buttonReg); } } // Load indicator registers for(int i = 1; i <= m_indicatorCount; ++i) { QString indicatorKey = QString("indicator_reg%1").arg(i); int indicatorReg = settings.value(indicatorKey, 0).toInt(); if(indicatorReg > 0) { m_indicatorRegisters.append(indicatorReg); } } settings.endGroup(); qDebug() << "Loaded" << m_buttonCount << "buttons and" << m_indicatorCount << "indicators"; qDebug() << "Button registers:" << m_buttonRegisters; qDebug() << "Indicator registers:" << m_indicatorRegisters; } void Mainwindows::initModbusConnection() { m_modbusClient = new QModbusTcpClient(this); // Set timeouts m_modbusClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, m_ipAddress); m_modbusClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, m_port); m_modbusClient->setTimeout(m_responseTimeout); m_modbusClient->setNumberOfRetries(1); connect(m_modbusClient, &QModbusClient::stateChanged, this, &Mainwindows::onStateChanged); // Initialize poll timer m_pollTimer = new QTimer(this); m_pollTimer->setInterval(m_pollInterval); connect(m_pollTimer, &QTimer::timeout, this, &Mainwindows::onPollTimer); } void Mainwindows::onStateChanged(QModbusDevice::State state) { if (state == QModbusDevice::ConnectedState) { m_connected = true; m_connectButton->setText("Disconnect"); readRegisters(); m_pollTimer->start(); // Start polling when connected } else if (state == QModbusDevice::UnconnectedState) { m_connected = false; m_connectButton->setText("Connect"); m_pollTimer->stop(); // Stop polling when disconnected } } void Mainwindows::onConnectButtonClicked() { if (!m_connected) { if (!m_modbusClient->connectDevice()) { QMessageBox::critical(this, tr("Connection Error"), tr("Could not connect to the Modbus device!")); } // Start connection timeout timer QTimer::singleShot(m_connectTimeout, this, [this]() { if (!m_connected) { m_modbusClient->disconnectDevice(); QMessageBox::warning(this, tr("Connection Timeout"), tr("Connection attempt timed out!")); } }); } else { m_modbusClient->disconnectDevice(); } } void Mainwindows::onPollTimer() { if (m_connected) { readRegisters(); } } void Mainwindows::writeRegister(int index) { if (!m_connected) return; QModbusDataUnit writeUnit(QModbusDataUnit::HoldingRegisters, m_buttonRegisters[index], 1); if (auto *reply = m_modbusClient->sendWriteRequest(writeUnit, 1)) { if (!reply->isFinished()) connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); else delete reply; } } void Mainwindows::readRegisters() { if (!m_connected) return; // Read indicator registers for (int i = 0; i < m_indicatorCount; ++i) { if (i < m_indicatorRegisters.size()) { QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, m_indicatorRegisters[i], 1); if (auto *reply = m_modbusClient->sendReadRequest(readUnit, 1)) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, this, &Mainwindows::onReadReady); } else { delete reply; } } } } // Read button registers for (int i = 0; i < m_buttonCount; ++i) { if (i < m_buttonRegisters.size()) { QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, m_buttonRegisters[i], 1); if (auto *reply = m_modbusClient->sendReadRequest(readUnit, 1)) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, this, &Mainwindows::onReadReady); } else { delete reply; } } } } } void Mainwindows::onReadReady() { auto reply = qobject_cast(sender()); if (!reply) return; if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); int address = unit.startAddress(); int value = unit.value(0); // Handle indicators for (int i = 0; i < m_indicatorRegisters.size(); ++i) { if (address == m_indicatorRegisters[i]) { static const QColor colors[] = {Qt::gray, Qt::black, Qt::green}; m_colorIndices[i] = value % 3; m_squares[i]->setColor(colors[m_colorIndices[i]]); qDebug() << "Indicator" << i << "address:" << address << "value:" << value; break; } } // Handle buttons for (int i = 0; i < m_buttonRegisters.size(); ++i) { if (address == m_buttonRegisters[i]) { QStringList bName = {"Открыть", "Закрыть"}; m_buttons[i]->setText(bName[value & 1]); qDebug() << "Button" << i << "address:" << address << "value:" << value; break; } } } reply->deleteLater(); } void Mainwindows::createUIElements() { auto* mainLayout = new QVBoxLayout(this); // Add connect button at the top m_connectButton = new QPushButton("Connect", this); connect(m_connectButton, &QPushButton::clicked, this, &Mainwindows::onConnectButtonClicked); mainLayout->addWidget(m_connectButton); // Create table with maximum of buttons or indicators int rowCount = qMax(m_buttonCount, m_indicatorCount); m_table = new QTableWidget(rowCount, 2, this); // Setup table properties m_table->verticalHeader()->setVisible(false); m_table->horizontalHeader()->setVisible(false); m_table->setShowGrid(false); m_table->setEditTriggers(QAbstractItemView::NoEditTriggers); m_table->setSelectionMode(QAbstractItemView::NoSelection); // Create UI elements for(int i = 0; i < rowCount; ++i) { if (i < m_buttonCount) { auto* button = new QPushButton(QString("Register %1").arg(m_buttonRegisters[i]), this); m_buttons.append(button); m_table->setCellWidget(i, 0, button); connect(button, &QPushButton::clicked, [this, i]() { writeRegister(i); }); } if (i < m_indicatorCount) { auto* square = new ColoredSquare(this); square->setFixedSize(m_table->rowHeight(i), m_table->rowHeight(i)); m_squares.append(square); m_colorIndices.append(0); m_table->setCellWidget(i, 1, square); } } // Adjust column sizes m_table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed); m_table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed); m_table->setColumnWidth(0, 100); m_table->setColumnWidth(1, 100); mainLayout->addWidget(m_table); mainLayout->setContentsMargins(0, 0, 0, 0); } Mainwindows::~Mainwindows() { if (m_pollTimer) { m_pollTimer->stop(); } if (m_modbusClient && m_modbusClient->state() != QModbusDevice::UnconnectedState) { m_modbusClient->disconnectDevice(); } }