285 lines
9.6 KiB
C++
285 lines
9.6 KiB
C++
/****************************************************************************
|
||
** Copyright (c) 2025 Evgeny Teterin (nayk) <nayk@nxt.ru>
|
||
** 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 <QtWidgets/QApplication>
|
||
#include <QtGui/QRegularExpressionValidator>
|
||
#include <QtNetwork/QHostAddress>
|
||
#include <QtCore/QDateTime>
|
||
#include <QtCore/QElapsedTimer>
|
||
|
||
#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<quint16>(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<char>( 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<int>(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<int>(sendCount),
|
||
static_cast<int>(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 //===============================================================
|