@@ -5,13 +5,10 @@
# include <QThread>
# include <QPainter>
# include <QSettings>
# include "mainwindow.h"
# include <QCoreApplication>
# include <QModbusTcpClient>
# include <QModbusDataUnit>
# include <QUrl>
# include <QMessageBox>
# include <QDebug>
# include "mainwindow.h"
# include <QCoreApplication>
ColoredSquare : : ColoredSquare ( QWidget * parent ) : QWidget ( parent ) , m_color ( Qt : : gray ) {
}
@@ -26,18 +23,29 @@ void ColoredSquare::paintEvent(QPaintEvent *) {
painter . fillRect ( rect ( ) , m_color ) ;
}
Mainw indows : : Mainw indows ( QWidget * parent )
: QWidget ( parent ) , m_modbusClient ( nullptr ) , m_connected ( false ) {
MainW indows : : MainW indows ( QWidget * parent )
: QWidget ( parent ) , m_modbusClient ( nullptr ) , m_connected ( false ) ,
m_requestCounter ( 0 ) , m_responseCounter ( 0 ) {
// Устанавливаем кодировку для всего приложения
QTextCodec : : setCodecForLocale ( QTextCodec : : codecForName ( " UTF-8 " ) ) ;
// Устанавливаем шрифт с поддержкой кириллицы
QFont font ( " Arial " , 10 ) ; // или можно использовать "MS Shell Dlg 2"
this - > setFont ( font ) ;
loadConfiguration ( ) ;
createUIElements ( ) ;
initModbusConnection ( ) ;
}
void Mainw indows : : loadConfiguration ( ) {
void MainW indows : : 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 ( ) ;
@@ -82,12 +90,32 @@ void Mainwindows::loadConfiguration() {
}
settings . endGroup ( ) ;
qDebug ( ) < < " Loaded " < < m_buttonCount < < " buttons and " < < m_indicatorCount < < " indicators " ;
qDebug ( ) < < " Button r egisters: " < < m_buttonRegisters ;
qDebug ( ) < < " Indicator registers: " < < m_indicatorRegisters ;
// Load button labels
settings . beginGroup ( " R egisters" ) ;
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 Mainw indows : : initModbusConnection ( ) {
void MainW indows : : initModbusConnection ( ) {
delete m_modbusClient ;
m_modbusClient = new QModbusTcpClient ( this ) ;
// Set timeouts
@@ -96,63 +124,75 @@ void Mainwindows::initModbusConnection() {
m_modbusClient - > setTimeout ( m_responseTimeout ) ;
m_modbusClient - > setNumberOfRetries ( 1 ) ;
connect ( m_modbusClient , & QModbusClient : : stateChanged ,
this , & Mainw indows : : onStateChanged ) ;
connect ( m_modbusClient , & QModbusDevice : : stateChanged ,
this , & MainW indows : : onStateChanged ) ;
// Initialize poll timer
if ( ! m_pollTimer ) {
m_pollTimer = new QTimer ( this ) ;
}
m_pollTimer - > setInterval ( m_pollInterval ) ;
connect ( m_pollTimer , & QTimer : : timeout , this , & Mainw indows : : onPollTimer ) ;
connect ( m_pollTimer , & QTimer : : timeout , this , & MainW indows : : onPollTimer ) ;
}
void Mainw indows : : onStateChanged ( QModbusDevice : : State state ) {
void MainW indows : : onStateChanged ( QModbusDevice : : State state ) {
if ( state = = QModbusDevice : : ConnectedState ) {
m_connected = true ;
m_connectButton - > setText ( " Disconnect " ) ;
m_requestCounter = 0 ;
m_responseCounter = 0 ;
updateStatusBar ( ) ;
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
updateStatusBar ( ) ;
}
}
void Mainw indows : : onConnectButtonClicked ( ) {
void MainW indows : : onConnectButtonClicked ( ) {
if ( ! m_connected ) {
m_disconnectRequested = false ;
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 ) {
if ( ! m_connected & & ! m_disconnectRequested ) {
m_modbusClient - > disconnectDevice ( ) ;
QMessageBox : : warning ( this , tr ( " Connection Timeout " ) ,
tr ( " Connection attempt timed out! " ) ) ;
}
} ) ;
} else {
m_disconnectRequested = true ;
m_modbusClient - > disconnectDevice ( ) ;
}
}
void Mainw indows : : onPollTimer ( ) {
void MainW indows : : onPollTimer ( ) {
if ( m_connected ) {
readRegisters ( ) ;
}
}
void Mainw indows : : readRegisters ( ) {
void MainW indows : : readRegisters ( ) {
if ( ! m_connected ) return ;
// Увеличиваем счетчик запросов
m_requestCounter + = m_indicatorCount + m_buttonCount ;
updateStatusBar ( ) ;
// 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 , & Mainw indows : : onReadReady ) ;
connect ( reply , & QModbusReply : : finished , this , & MainW indows : : onReadReady ) ;
} else {
delete reply ;
}
@@ -166,7 +206,7 @@ void Mainwindows::readRegisters() {
QModbusDataUnit readUnit ( QModbusDataUnit : : HoldingRegisters , m_buttonRegisters [ i ] , 1 ) ;
if ( auto * reply = m_modbusClient - > sendReadRequest ( readUnit , 1 ) ) {
if ( ! reply - > isFinished ( ) ) {
connect ( reply , & QModbusReply : : finished , this , & Mainw indows : : onReadReady ) ;
connect ( reply , & QModbusReply : : finished , this , & MainW indows : : onReadReady ) ;
} else {
delete reply ;
}
@@ -175,12 +215,16 @@ void Mainwindows::readRegisters() {
}
}
void Mainw indows : : onReadReady ( ) {
auto reply = qobject_cast < QModbusReply * > ( sender ( ) ) ;
void MainW indows : : onReadReady ( ) {
auto * reply = qobject_cast < QModbusReply * > ( sender ( ) ) ;
if ( ! reply ) return ;
if ( reply - > error ( ) = = QModbusDevice : : NoError ) {
const QModbusDataUnit unit = reply - > result ( ) ;
std : : unique_ptr < QModbusReply > replyPtr ( reply ) ; // Автоматическое освобождение памяти
if ( replyPtr - > error ( ) = = QModbusDevice : : NoError ) {
m_responseCounter + + ;
updateStatusBar ( ) ;
const QModbusDataUnit unit = replyPtr - > result ( ) ;
int address = unit . startAddress ( ) ;
int value = unit . value ( 0 ) ;
@@ -188,8 +232,12 @@ void Mainwindows::onReadReady() {
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 ;
}
}
@@ -199,25 +247,39 @@ void Mainwindows::onReadReady() {
if ( address = = m_buttonRegisters [ i ] ) {
m_buttonValues [ i ] = value ;
bool bitState = ( value > > m_buttonBits [ i ] ) & 1 ;
m_buttons [ i ] - > setText ( QString ( " %1 " )
. arg ( bitState ? " Закрыть " : " Открыть " ) ) ;
m_buttons [ i ] - > setChecked ( bitState ) ; // Устанавливаем состояние кнопки
bool hasIndicator = false ;
// Проверяем, есть ли индикатор для этой кнопки
for ( int j = 0 ; j < m_indicatorRegisters . size ( ) ; + + j ) {
if ( m_indicatorLabels [ j ] . contains ( m_buttonLabels [ i ] , Qt : : CaseInsensitive ) ) {
hasIndicator = true ;
break ;
}
}
m_buttons [ i ] - > setText ( hasIndicator ?
QString ( " %1 " ) . arg ( bitState ? " Выкл " : " Вкл " ) :
QString ( " %1 " ) . arg ( bitState ? " Закрыть " : " Открыть " ) ) ;
}
}
}
reply - > deleteLater ( ) ;
}
void Mainw indows : : createUIElements ( ) {
void MainW indows : : createUIElements ( ) {
auto * mainLayout = new QVBoxLayout ( this ) ;
// Добавляем статусную строку
m_statusLabel = new QLabel ( this ) ;
m_statusLabel - > setFrameStyle ( QFrame : : Panel | QFrame : : Sunken ) ;
updateStatusBar ( ) ;
// Add connect button at the top
m_connectButton = new QPushButton ( " Connect " , this ) ;
connect ( m_connectButton , & QPushButton : : clicked , this , & Mainw indows : : onConnectButtonClicked ) ;
connect ( m_connectButton , & QPushButton : : clicked , this , & MainW indows : : 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 ) ;
m_table = new QTableWidget ( rowCount , 4 , this ) ; // Changed from 3 to 4 columns
// Setup table properties
m_table - > verticalHeader ( ) - > setVisible ( false ) ;
@@ -226,48 +288,101 @@ void Mainwindows::createUIElements() {
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 * container = new QWidget ( ) ;
auto * containerLayout = new QHBoxLayout ( container ) ;
auto * button = new QPushButton ( QString ( " Register %1 Bit %2 " ) . arg ( m_buttonRegisters [ i ] ) . arg ( m_buttonBits [ i ] ) , this ) ;
button - > setFont ( tableFont ) ;
button - > setCheckable ( true ) ;
button - > setMaximumHeight ( button - > height ( ) - 5 ) ;
containerLayout - > addWidget ( button ) ;
containerLayout - > setAlignment ( Qt : : AlignCenter ) ;
containerLayout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
m_buttons . append ( button ) ;
m_buttonValues . append ( 0 ) ;
m_table - > setCellWidget ( i , 0 , button ) ;
connect ( button , & QPushButton : : clicked , [ this , i ] ( ) {
m_table - > setCellWidget ( i , 1 , container ) ;
connect ( button , & QPushButton : : clicked , this , [ 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
int newValue = currentValue ^ ( 1 < < m_buttonBits [ i ] ) ;
m_buttonValues [ i ] = newValue ;
writeUnit . setValue ( 0 , newValue ) ;
if ( auto * reply = m_modbusClient - > sendWriteRequest ( writeUnit , 1 ) ) {
if ( ! reply - > isFinished ( ) )
if ( auto * reply = m_modbusClient - > sendWriteRequest ( writeUnit , 1 ) ) {
if ( ! reply - > isFinished ( ) ) {
connect ( reply , & QModbusReply : : finished , reply , & QModbusReply : : deleteLater ) ;
else
} else {
delete reply ;
}
}
} ) ;
}
if ( i < m_indicatorCount ) {
auto * square = new ColoredSquare ( this ) ;
square - > setFixedSize ( m_table - > rowHeight ( i ) , m_table - > rowHeight ( i ) ) ;
auto * container = new QWidget ( ) ;
auto * containerLayout = new QHBoxLayout ( container ) ;
auto * square = new ColoredSquare ( container ) ;
square - > setFixedSize ( m_table - > rowHeight ( i ) - 4 , m_table - > rowHeight ( i ) - 4 ) ; // Немного уменьшаем размер
containerLayout - > addWidget ( square ) ;
containerLayout - > setAlignment ( Qt : : AlignCenter ) ;
containerLayout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
m_squares . append ( square ) ;
m_colorIndices . append ( 0 ) ;
m_table - > setCellWidget ( i , 1 , square ) ;
m_table - > setCellWidget ( i , 2 , container ) ;
// 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 - > setColumnWidth ( 0 , 100 ) ;
m_table - > setColumnWidth ( 1 , 100 ) ;
m_table - > horizontalHeader ( ) - > setSectionResizeMode ( 2 , QHeaderView : : Fixed ) ;
m_table - > horizontalHeader ( ) - > setSectionResizeMode ( 3 , QHeaderView : : Fixed ) ;
m_table - > setColumnWidth ( 0 , 180 ) ; // 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 - > addWidget ( m_statusLabel ) ;
mainLayout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
// Обновляем расчет размера окна с учетом статусной строки
int totalWidth = m_table - > horizontalHeader ( ) - > length ( ) + 4 ;
int totalHeight = m_table - > verticalHeader ( ) - > length ( ) +
m_connectButton - > height ( ) +
m_statusLabel - > sizeHint ( ) . height ( ) + 20 ;
setFixedSize ( totalWidth , totalHeight ) ;
}
Mainw indows : : ~ Mainwindows ( ) {
void MainW indows : : updateStatusBar ( ) const {
QString status = QString ( " IP: %1 Port: %2 | Requests: %3 Responses: %4 " )
. arg ( m_ipAddress )
. arg ( m_port )
. arg ( m_requestCounter )
. arg ( m_responseCounter ) ;
m_statusLabel - > setText ( status ) ;
}
MainWindows : : ~ MainWindows ( ) {
if ( m_pollTimer ) {
m_pollTimer - > stop ( ) ;
}