/**************************************************************************** ** Copyright (c) 2025 Evgeny Teterin (nayk) ** All right reserved. ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be ** included in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ** ****************************************************************************/ #include "main_window.h" #include "./ui_main_window.h" #include #include #include #include #include #include "application_config.h" namespace client { //=========================================================== //============================================================================== MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); initialize(); updateControls(); } //============================================================================== MainWindow::~MainWindow() { socket.disconnectFromHost(); delete ui; } //============================================================================== void MainWindow::initialize() { setWindowTitle( QApplication::applicationName() ); Application::initializeLog(ui->textEditLog); connect(ui->pushButtonConnect, &QPushButton::clicked, this, &MainWindow::btnConnect); connect(ui->pushButtonSend, &QPushButton::clicked, this, &MainWindow::btnSend); connect(&socket, &QTcpSocket::connected, this, &MainWindow::socketConnected); connect(&socket, &QTcpSocket::disconnected, this, &MainWindow::socketDisconnected); QString ipRange {"(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])"}; QRegularExpression ipRegex ("^" + ipRange + "\\." + ipRange + "\\." + ipRange + "\\." + ipRange + "$"); ui->lineEditIp->setValidator( new QRegularExpressionValidator(ipRegex, this) ); ui->lineEditIp->setText("127.0.0.1"); QRegularExpression hexRegex("([a-fA-F0-9]{2})([\\s][a-fA-F0-9]{2})*[\\s]?"); ui->lineEditData->setValidator( new QRegularExpressionValidator(hexRegex, this) ); } //============================================================================== void MainWindow::updateControls() { bool connect = isConnected(); ui->pushButtonSend->setEnabled(connect); ui->lineEditIp->setEnabled(!connect); ui->spinBoxPort->setEnabled(!connect); ui->pushButtonConnect->setText(connect ? tr("Закрыть") : tr("Открыть")); } //============================================================================== bool MainWindow::isConnected() const { return socket.state() == QTcpSocket::SocketState::ConnectedState; } //============================================================================== void MainWindow::btnConnect() { ui->pushButtonConnect->setEnabled(false); ui->pushButtonSend->setEnabled(false); ui->lineEditData->setEnabled(false); ui->lineEditIp->setEnabled(false); ui->spinBoxPort->setEnabled(false); QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); if (isConnected()) { Application::addToLog( ui->textEditLog, tr("Закрытие соединения с сервером...")); socket.disconnectFromHost(); if (socket.state() != QAbstractSocket::UnconnectedState) { if (!socket.waitForDisconnected(timeout)) { Application::addToLog( ui->textEditLog, tr("Не удалось закрыть соединение: %1") .arg(socket.errorString()), Application::LogTypeError); } } } else { Application::addToLog( ui->textEditLog, tr("Открытие соединения с сервером...")); socket.connectToHost( QHostAddress(ui->lineEditIp->text()), static_cast(ui->spinBoxPort->value()) ); if (socket.state() != QTcpSocket::SocketState::ConnectedState) { if (!socket.waitForConnected(timeout)) { Application::addToLog( ui->textEditLog, tr("Не удалось открыть соединение: %1") .arg(socket.errorString()), Application::LogTypeError); } } } ui->pushButtonConnect->setEnabled(true); ui->lineEditData->setEnabled(true); updateControls(); } //============================================================================== void MainWindow::btnSend() { if (!isConnected()) return; QStringList list = ui->lineEditData->text().split(' ', Qt::SkipEmptyParts); QByteArray sendData; for (const QString &str: std::as_const(list) ) { sendData.append( static_cast( str.toInt(nullptr, 16) ) ); } if (sendData.isEmpty()) return; ui->pushButtonSend->setEnabled(false); Application::addToLog( ui->textEditLog, tr("Отправка массива %1 байт").arg(sendData.size())); qsizetype sendCount {0}; QElapsedTimer timer; timer.start(); while ((sendCount < sendData.size()) && (timer.elapsed() < timeout)) { qsizetype count = socket.write(sendData.mid( static_cast(sendCount) )); if (count < 0) { Application::addToLog( ui->textEditLog, tr("Ошибка отправки данных"), Application::LogTypeError ); } else if (count > 0) { Application::addToLog( ui->textEditLog, tr("Отправка данных: %1 (%2 байт)") .arg( QString( sendData.mid( static_cast(sendCount), static_cast(count) ) .toHex(' ')) ).arg(count), Application::LogTypeData ); sendCount += count; } } if (sendCount < sendData.size()) { Application::addToLog( ui->textEditLog, tr("Таймаут отправки данных. Всего отправлено %1 байт из %2") .arg(sendCount).arg(sendData.size()), Application::LogTypeError ); } if (sendCount == 0) { updateControls(); return; } Application::addToLog( ui->textEditLog, tr("Приём массива %1 байт").arg(sendData.size())); if (!socket.waitForReadyRead(timeout)) { Application::addToLog( ui->textEditLog, tr("Таймаут ожидания приёма данных"), Application::LogTypeError ); updateControls(); return; } QByteArray readData; timer.restart(); while ((readData.size() < sendData.size()) && (timer.elapsed() < timeout)) { QByteArray buf = socket.read( sendData.size() - readData.size() ); if (!buf.isEmpty()) { Application::addToLog( ui->textEditLog, tr("Приём данных: %1 (%2 байт)") .arg( QString(buf.toHex(' ')) ).arg(buf.size()), Application::LogTypeData ); readData.append(buf); } } if (readData.size() < sendData.size()) { Application::addToLog( ui->textEditLog, tr("Таймаут приёма данных. Всего принято %1 байт из %2") .arg(readData.size()).arg(sendData.size()), Application::LogTypeError ); } updateControls(); } //============================================================================== void MainWindow::socketConnected() { Application::addToLog( ui->textEditLog, tr("Соединение открыто. Ip: %1, Порт: %2") .arg(ui->lineEditIp->text()).arg(ui->spinBoxPort->value()), Application::LogTypeWarning); updateControls(); } //============================================================================== void MainWindow::socketDisconnected() { Application::addToLog( ui->textEditLog, tr("Соединение закрыто"), Application::LogTypeWarning); ui->textEditLog->append(""); updateControls(); } //============================================================================== } // namespace //===============================================================