From d482560e7b9c9e61e9d3943ee47b6869afbb42d0 Mon Sep 17 00:00:00 2001 From: Alekseev Date: Fri, 4 Jul 2025 02:07:18 +0700 Subject: [PATCH] first commit, correct read, correct config ini --- .gitignore | 3 + CMakeLists.txt | 89 +++++++++++++++ Inc/mainwindow.h | 68 +++++++++++ Src/main.cpp | 20 ++++ Src/mainwindow.cpp | 276 +++++++++++++++++++++++++++++++++++++++++++++ config.ini | 17 +++ 6 files changed, 473 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 Inc/mainwindow.h create mode 100644 Src/main.cpp create mode 100644 Src/mainwindow.cpp create mode 100644 config.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6694cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/cmake-build-release/ +/cmake-build-debug/ +/.idea/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..109a4f3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,89 @@ +cmake_minimum_required(VERSION 3.23) +project(mbIoUniversal LANGUAGES CXX) + +# Set the C++ standard to C++17 +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOUIC ON) # Enable automatic processing of .ui files +set(CMAKE_AUTORCC ON) # Enable automatic processing of .qrc files +set(CMAKE_AUTOMOC ON) # Enable automatic processing of MOC files + +if (WIN32) + set(CMAKE_PREFIX_PATH "C:/Qt/5.15.2/mingw81_64/lib/cmake") +endif () + +set(QT_COMPONENTS Core Gui Widgets Network Svg OpenGL SerialBus SerialPort Charts) + +find_package(Qt5 COMPONENTS + ${QT_COMPONENTS} + REQUIRED) + +add_definitions(-DQT_DEPRECATED_WARNINGS) + +include_directories(Inc) +file(GLOB_RECURSE PROJECT_SOURCES SOURCES "Src/*.*" "Inc/*.*") + +# List your UI form files +set(PROJECT_FORMS +# windows.ui +) + +# List your resource files +set(PROJECT_RESOURCES +# resources.qrc +) + +# Add the executable target +add_executable(${PROJECT_NAME} + ${PROJECT_SOURCES} + ${PROJECT_HEADERS} + ${PROJECT_FORMS} + ${PROJECT_RESOURCES} +) + +foreach(QT_LIB ${QT_COMPONENTS}) + target_link_libraries(${PROJECT_NAME} Qt5::${QT_LIB}) +endforeach(QT_LIB) + +if (WIN32) + set(DEBUG_SUFFIX) + if (MSVC AND CMAKE_BUILD_TYPE MATCHES "Debug") + set(DEBUG_SUFFIX "d") + endif () + set(QT_INSTALL_PATH "${CMAKE_PREFIX_PATH}") + if (NOT EXISTS "${QT_INSTALL_PATH}/bin") + set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..") + if (NOT EXISTS "${QT_INSTALL_PATH}/bin") + set(QT_INSTALL_PATH "${QT_INSTALL_PATH}/..") + endif () + endif () + set(MINGW_BIN_DIR "C:/PROGRA~1/JETBRA~1/CLion/bin/mingw/bin") # <-- Укажите правильный путь + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${MINGW_BIN_DIR}/libstdc++-6.dll" + "$") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${MINGW_BIN_DIR}/libgcc_s_seh-1.dll" + "$") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${MINGW_BIN_DIR}/libwinpthread-1.dll" + "$") + if (EXISTS "${QT_INSTALL_PATH}/plugins/") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory + "$/plugins/") + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${QT_INSTALL_PATH}/plugins/" + "$/plugins/") + endif () + foreach (QT_LIB ${QT_COMPONENTS}) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + "${QT_INSTALL_PATH}/bin/Qt5${QT_LIB}${DEBUG_SUFFIX}.dll" + "$") + endforeach (QT_LIB) +endif () diff --git a/Inc/mainwindow.h b/Inc/mainwindow.h new file mode 100644 index 0000000..ee9ac0b --- /dev/null +++ b/Inc/mainwindow.h @@ -0,0 +1,68 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class ColoredSquare : public QWidget { + Q_OBJECT +public: + explicit ColoredSquare(QWidget *parent = nullptr); + void setColor(const QColor& color); +protected: + void paintEvent(QPaintEvent *event) override; +private: + QColor m_color; +}; + +class Mainwindows : public QWidget { + Q_OBJECT + +public: + explicit Mainwindows(QWidget *parent = nullptr); + ~Mainwindows() override; + +private slots: + void onStateChanged(QModbusDevice::State state); + void onConnectButtonClicked(); + void onReadReady(); + void onPollTimer(); + +private: + void createUIElements(); + void initModbusConnection(); + void writeRegister(int index); + void readRegisters(); + void loadConfiguration(); + + QTableWidget* m_table; + QString m_ipAddress; + int m_port; + QVector m_buttonRegisters; + QVector m_indicatorRegisters; + int m_buttonCount; + int m_indicatorCount; + QVector m_buttons; + QVector m_squares; + QVector m_colorIndices; + + QModbusTcpClient* m_modbusClient; + QPushButton* m_connectButton; + bool m_connected; + int m_responseTimeout; + int m_connectTimeout; + int m_pollInterval; + QTimer* m_pollTimer; +}; + +#endif // MAINWINDOW_H diff --git a/Src/main.cpp b/Src/main.cpp new file mode 100644 index 0000000..cced488 --- /dev/null +++ b/Src/main.cpp @@ -0,0 +1,20 @@ +#include "mainwindow.h" +#include +#include +#include + +int main(int argc, char *argv[]) { + QApplication give_me_a_name(argc, argv); + //app->setStyle(QStyleFactory::create("Fusion")); + //app->setStyle(QStyleFactory::create("windowsvista")); + QApplication::setStyle("windowsvista"); + //app->setStyle(QStyleFactory::create("Windows")); + //app->setStyle(QStyleFactory::create("Plastique")); + + qDebug() << QApplication::style(); + + auto *window = new Mainwindows(); + window->show(); + //window->init(); + QApplication::exec(); +} diff --git a/Src/mainwindow.cpp b/Src/mainwindow.cpp new file mode 100644 index 0000000..bbacc4e --- /dev/null +++ b/Src/mainwindow.cpp @@ -0,0 +1,276 @@ +#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(); + } +} diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..359f06e --- /dev/null +++ b/config.ini @@ -0,0 +1,17 @@ +[Connection] +ip=192.168.8.222 +port=502 +responseTimeout=1000 +connectTimeout=2000 +timeBetweenPolls=100 + +[Registers] +buttonCount=3 +indicatorCount=4 +button_reg1=3 +button_reg2=4 +button_reg3=5 +indicator_reg1=13 +indicator_reg2=14 +indicator_reg3=15 +indicator_reg4=16