Files
mbIoUniversal/Src/mainwindow.cpp
2025-07-04 02:53:27 +07:00

338 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <QTableWidget>
#include <QHeaderView>
#include <QTextCodec>
#include <QVBoxLayout>
#include <QThread>
#include <QPainter>
#include <QSettings>
#include "mainwindow.h"
#include <QCoreApplication>
#include <QModbusTcpClient>
#include <QModbusDataUnit>
#include <QUrl>
#include <QMessageBox>
#include <QDebug>
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) {
// Устанавливаем кодировку для всего приложения
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
// Устанавливаем шрифт с поддержкой кириллицы
QFont font("Arial", 10); // или можно использовать "MS Shell Dlg 2"
this->setFont(font);
loadConfiguration();
createUIElements();
initModbusConnection();
}
void Mainwindows::loadConfiguration() {
const QString filePath = QCoreApplication::applicationDirPath() + "/config.ini";
QSettings settings(filePath, QSettings::IniFormat);
// Устанавливаем кодировку для чтения файла конфигурации
settings.setIniCodec("UTF-8");
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();
m_buttonBits.clear();
// Load button registers and bits
for(int i = 1; i <= m_buttonCount; ++i) {
QString buttonRegKey = QString("button_reg%1").arg(i);
QString buttonBitKey = QString("button_bit%1").arg(i);
int buttonReg = settings.value(buttonRegKey, 0).toInt();
int buttonBit = settings.value(buttonBitKey, 0).toInt();
if(buttonReg > 0 && buttonBit >= 0 && buttonBit < 16) {
m_buttonRegisters.append(buttonReg);
m_buttonBits.append(buttonBit);
}
}
// 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();
// Load button labels
settings.beginGroup("Registers");
for(int i = 1; i <= m_buttonCount; ++i) {
QString labelKey = QString("button_label%1").arg(i);
QString label = settings.value(labelKey).toString();
if(label.isEmpty()) {
label = QString("Button %1").arg(i);
}
m_buttonLabels.append(label);
}
// Load indicator labels
for(int i = 1; i <= m_indicatorCount; ++i) {
QString labelKey = QString("indicator_label%1").arg(i);
QString label = settings.value(labelKey).toString();
if(label.isEmpty()) {
label = QString("Indicator %1").arg(i);
}
m_indicatorLabels.append(label);
}
settings.endGroup();
}
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::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<QModbusReply *>(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};
static const QString states[] = {"?", "Закрыт", "Открыт"};
m_colorIndices[i] = value % 3;
m_squares[i]->setColor(colors[m_colorIndices[i]]);
if (auto* item = m_table->item(i, 3)) {
item->setText(states[m_colorIndices[i]]);
}
break;
}
}
// Handle buttons
for (int i = 0; i < m_buttonRegisters.size(); ++i) {
if (address == m_buttonRegisters[i]) {
m_buttonValues[i] = value;
bool bitState = (value >> m_buttonBits[i]) & 1;
m_buttons[i]->setText(QString("%1")
.arg(bitState ? "Закрыть" : "Открыть"));
}
}
}
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, 4, this); // Changed from 3 to 4 columns
// 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);
// Устанавливаем шрифт для таблицы
QFont tableFont("Arial", 10);
m_table->setFont(tableFont);
// Create UI elements
for(int i = 0; i < rowCount; ++i) {
// Add label in first column
QString label;
if (i < m_buttonCount) {
label = m_buttonLabels[i];
} else if (i < m_indicatorCount) {
label = m_indicatorLabels[i];
}
m_table->setItem(i, 0, new QTableWidgetItem(label));
if (i < m_buttonCount) {
auto* button = new QPushButton(QString("Register %1 Bit %2").arg(m_buttonRegisters[i]).arg(m_buttonBits[i]), this);
button->setFont(tableFont); // Устанавливаем шрифт для кнопок
m_buttons.append(button);
m_buttonValues.append(0);
m_table->setCellWidget(i, 1, button); // Changed from column 0 to 1
connect(button, &QPushButton::clicked, [this, i]() {
QModbusDataUnit writeUnit(QModbusDataUnit::HoldingRegisters, m_buttonRegisters[i], 1);
int currentValue = m_buttonValues[i];
int newValue = currentValue ^ (1 << m_buttonBits[i]); // Toggle specific bit
m_buttonValues[i] = newValue;
writeUnit.setValue(0, newValue);
if (auto *reply = m_modbusClient->sendWriteRequest(writeUnit, 1)) {
if (!reply->isFinished())
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
else
delete reply;
}
});
}
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, 2, square);
// Add status text cell
const auto statusItem = new QTableWidgetItem("?");
statusItem->setTextAlignment(Qt::AlignCenter);
m_table->setItem(i, 3, statusItem);
}
}
// Adjust column sizes
m_table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
m_table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
m_table->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
m_table->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
m_table->setColumnWidth(0, 150); // Label column
m_table->setColumnWidth(1, 100); // Button column
m_table->setColumnWidth(2, 50); // Square column
m_table->setColumnWidth(3, 100); // Status text column
mainLayout->addWidget(m_table);
mainLayout->setContentsMargins(0, 0, 0, 0);
// Устанавливаем фиксированный размер окна на основе размера таблицы
int totalWidth = m_table->horizontalHeader()->length() + 2; // +2 для рамки
int totalHeight = m_table->verticalHeader()->length() + m_connectButton->height() + 2;
setFixedSize(totalWidth, totalHeight);
}
Mainwindows::~Mainwindows() {
if (m_pollTimer) {
m_pollTimer->stop();
}
if (m_modbusClient && m_modbusClient->state() != QModbusDevice::UnconnectedState) {
m_modbusClient->disconnectDevice();
}
}