/
eastopener
/
operationDisk
Обзор
Документация
Войти
/
eastopener
/
operationDisk
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
MainWindow.cpp
770 строк
29 KB
Demin
Before_service
04 мар 2026, 11:40
04 мар 2026, 11:40
0ae2e95
Код
Авторство
О чём код?
#include "MainWindow.h" #include <QVBoxLayout> #include <QHBoxLayout> #include <QFormLayout> #include <windows.h> #include <QTextCodec> #include <locale> #include <codecvt> #include <stdexcept> #include <string> #include <qprogressdialog.h> #include <thread> #include <QApplication> #include <QListWidget> #include <QScrollArea> #include <QFrame> #include <QTimer> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), socketTimer(nullptr), currentSocket(INVALID_SOCKET), operationCancelled(false), currentProgressDialog(nullptr) { setupUI(); } MainWindow::~MainWindow() { if (socketTimer) { socketTimer->stop(); delete socketTimer; } if (currentSocket != INVALID_SOCKET) { closesocket(currentSocket); } } auto showMessage = [](const QString& msg, const QString& title, UINT type = MB_OK) -> int { return MessageBoxW(nullptr, msg.toStdWString().c_str(), title.toStdWString().c_str(), type); }; void MainWindow::setupUI() { QWidget *centralWidget = new QWidget(this); QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget); // 1. Верхняя часть - информация о диске QGroupBox *diskInfoGroup = new QGroupBox("Характеристики диска", this); QFormLayout *diskInfoLayout = new QFormLayout(diskInfoGroup); fileSystemLabel = new QLabel("-", this); totalSpaceLabel = new QLabel("-", this); freeSpaceLabel = new QLabel("-", this); usedSpaceLabel = new QLabel("-", this); diskInfoLayout->addRow("Файловая система:", fileSystemLabel); diskInfoLayout->addRow("Общий размер:", totalSpaceLabel); diskInfoLayout->addRow("Свободное место:", freeSpaceLabel); diskInfoLayout->addRow("Использовано:", usedSpaceLabel); mainLayout->addWidget(diskInfoGroup); // 2. Центральная часть - выбор прошивки QGroupBox *selectionGroup = new QGroupBox("Выбор прошивки", this); QVBoxLayout *selectionMainLayout = new QVBoxLayout(selectionGroup); refreshButton = new QPushButton("Обновить список проектов и дисков", this); selectionMainLayout->addWidget(refreshButton); QFormLayout *selectionLayout = new QFormLayout(); // Заменяем QComboBox на QListWidget для проектов QLabel *projectLabel = new QLabel("Проект:", this); projectListWidget = new QListWidget(this); projectListWidget->setSelectionMode(QAbstractItemView::SingleSelection); projectListWidget->setFixedHeight(100); projectListWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); // Заменяем QComboBox на QListWidget для устройств QLabel *deviceLabel = new QLabel("Устройство:", this); deviceListWidget = new QListWidget(this); deviceListWidget->setSelectionMode(QAbstractItemView::SingleSelection); deviceListWidget->setFixedHeight(100); deviceListWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); // Заменяем QComboBox на QListWidget для прошивок QLabel *firmwareLabel = new QLabel("Прошивка:", this); firmwareListWidget = new QListWidget(this); firmwareListWidget->setSelectionMode(QAbstractItemView::SingleSelection); firmwareListWidget->setFixedHeight(100); firmwareListWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); selectionLayout->addRow(projectLabel, projectListWidget); selectionLayout->addRow(deviceLabel, deviceListWidget); selectionLayout->addRow(firmwareLabel, firmwareListWidget); selectionMainLayout->addLayout(selectionLayout); mainLayout->addWidget(selectionGroup); // 3. Нижняя часть - действия QGroupBox *actionGroup = new QGroupBox("Действия", this); QHBoxLayout *actionLayout = new QHBoxLayout(actionGroup); // Заменяем QComboBox на QListWidget для дисков QLabel *diskLabel = new QLabel("Целевой диск:", this); diskListWidget = new QListWidget(this); diskListWidget->setSelectionMode(QAbstractItemView::SingleSelection); diskListWidget->setFixedHeight(80); diskListWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); copyButton = new QPushButton("Записать прошивку на диск", this); copyButton->setEnabled(false); actionLayout->addWidget(diskLabel); actionLayout->addWidget(diskListWidget, 1); actionLayout->addWidget(copyButton, 1); actionLayout->setSpacing(15); mainLayout->addWidget(actionGroup); setCentralWidget(centralWidget); // Подключение сигналов для ListWidget connect(diskListWidget, &QListWidget::itemClicked, this, [this](QListWidgetItem* item) { if (item && item->data(Qt::UserRole).toString() != "") { updateDiskInfo(); } }); connect(projectListWidget, &QListWidget::itemClicked, this, [this](QListWidgetItem* item) { if (item && item->data(Qt::UserRole).toString() != "") { updateDevices(); } }); connect(deviceListWidget, &QListWidget::itemClicked, this, [this](QListWidgetItem* item) { if (item && item->data(Qt::UserRole).toString() != "") { updateFirmwares(); } }); connect(copyButton, &QPushButton::clicked, this, &MainWindow::checkDriveAndFile); connect(refreshButton, &QPushButton::clicked, this, &MainWindow::refreshAll); // Общая проверка выбора connect(projectListWidget, &QListWidget::itemSelectionChanged, this, &MainWindow::validateSelections); connect(deviceListWidget, &QListWidget::itemSelectionChanged, this, &MainWindow::validateSelections); connect(firmwareListWidget, &QListWidget::itemSelectionChanged, this, &MainWindow::validateSelections); connect(diskListWidget, &QListWidget::itemSelectionChanged, this, &MainWindow::validateSelections); // Инициализация данных refreshAll(); populateDiskList(); } void MainWindow::refreshAll() { resetSelections(); updateProjects(); populateDiskList(); } void MainWindow::resetSelections() { projectListWidget->clear(); addPlaceholderItem(projectListWidget, "-- Выберите проект --"); deviceListWidget->clear(); addPlaceholderItem(deviceListWidget, "-- Выберите устройство --"); firmwareListWidget->clear(); addPlaceholderItem(firmwareListWidget, "-- Выберите прошивку --"); diskListWidget->clear(); addPlaceholderItem(diskListWidget, "-- Выберите диск --"); fileSystemLabel->setText("-"); totalSpaceLabel->setText("-"); freeSpaceLabel->setText("-"); usedSpaceLabel->setText("-"); copyButton->setEnabled(false); } void MainWindow::addPlaceholderItem(QListWidget* listWidget, const QString& text) { QListWidgetItem* item = new QListWidgetItem(text); item->setFlags(item->flags() & ~Qt::ItemIsEnabled); item->setData(Qt::UserRole, ""); listWidget->addItem(item); } QString MainWindow::getSelectedItemText(QListWidget* listWidget) const { QList<QListWidgetItem*> selectedItems = listWidget->selectedItems(); if (selectedItems.isEmpty() || selectedItems.first()->data(Qt::UserRole).toString().isEmpty()) { return ""; } return selectedItems.first()->text(); } QString MainWindow::getSelectedItemData(QListWidget* listWidget) const { QList<QListWidgetItem*> selectedItems = listWidget->selectedItems(); if (selectedItems.isEmpty()) { return ""; } return selectedItems.first()->data(Qt::UserRole).toString(); } bool MainWindow::isRealItemSelected(QListWidget* listWidget) const { return !getSelectedItemText(listWidget).isEmpty(); } void MainWindow::clearAndAddPlaceholder(QListWidget* listWidget, const QString& placeholderText) { listWidget->clear(); addPlaceholderItem(listWidget, placeholderText); } bool MainWindow::validateSelections() { bool projectSelected = isRealItemSelected(projectListWidget); bool deviceSelected = isRealItemSelected(deviceListWidget); bool firmwareSelected = isRealItemSelected(firmwareListWidget); bool diskSelected = isRealItemSelected(diskListWidget); bool allValid = projectSelected && deviceSelected && firmwareSelected && diskSelected; copyButton->setEnabled(allValid); return allValid; } void MainWindow::updateProjects() { projectListWidget->clear(); addPlaceholderItem(projectListWidget, "-- Выберите проект --"); QDir baseDir(basePath); if (baseDir.exists()) { QStringList projects = baseDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QString& project : projects) { QListWidgetItem* item = new QListWidgetItem(project); item->setData(Qt::UserRole, project); projectListWidget->addItem(item); } } else { QMessageBox::warning(this, "Ошибка", "Базовая папка с проектами не найдена: " + basePath); } } void MainWindow::updateDevices() { deviceListWidget->clear(); addPlaceholderItem(deviceListWidget, "-- Выберите устройство --"); firmwareListWidget->clear(); addPlaceholderItem(firmwareListWidget, "-- Выберите прошивку --"); QString projectName = getSelectedItemText(projectListWidget); if (projectName.isEmpty()) return; QDir projectDir(basePath + "/" + projectName); if (projectDir.exists()) { QStringList devices = projectDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); for (const QString& device : devices) { QListWidgetItem* item = new QListWidgetItem(device); item->setData(Qt::UserRole, device); deviceListWidget->addItem(item); } } } void MainWindow::updateFirmwares() { firmwareListWidget->clear(); addPlaceholderItem(firmwareListWidget, "-- Выберите прошивку --"); QString projectName = getSelectedItemText(projectListWidget); QString deviceName = getSelectedItemText(deviceListWidget); if (projectName.isEmpty() || deviceName.isEmpty()) return; QDir deviceDir(basePath + "/" + projectName + "/" + deviceName); if (deviceDir.exists()) { QStringList filters; filters << "*.txt" << "*.bin" << "*.hex" << "*.iso" << "*.img"; QStringList firmwares = deviceDir.entryList(filters, QDir::Files); for (const QString& firmware : firmwares) { QListWidgetItem* item = new QListWidgetItem(firmware); item->setData(Qt::UserRole, firmware); firmwareListWidget->addItem(item); } } } void MainWindow::populateDiskList() { diskListWidget->clear(); addPlaceholderItem(diskListWidget, "-- Выберите съёмный диск --"); // Получаем информацию о логических дисках disks = DiskInfoReader::GetDiskInfo(); // ОЧИЩАЕМ И ЗАПОЛНЯЕМ ТОЛЬКО СЪЁМНЫМИ ДИСКАМИ std::vector<DiskInfo> removableDisks; for (const auto& disk : disks) { std::wstring rootPath = disk.name; UINT driveType = ::GetDriveTypeW(rootPath.c_str()); // ТОЛЬКО СЪЁМНЫЕ ДИСКИ if (driveType == DRIVE_REMOVABLE) { removableDisks.push_back(disk); // В СПИСОК ВЫВОДИМ БУКВУ ДИСКА И НАЗВАНИЕ (ЕСЛИ ЕСТЬ) QString displayText; if (!disk.volumeName.empty() && disk.volumeName != L"Без названия" && disk.volumeName != L"Не удалось получить название") { displayText = QString("%1 [%2]") .arg(QString::fromStdWString(disk.name)) .arg(QString::fromStdWString(disk.volumeName)); } else { displayText = QString::fromStdWString(disk.name); } QListWidgetItem* item = new QListWidgetItem(displayText); item->setData(Qt::UserRole, QString::fromStdWString(disk.name)); diskListWidget->addItem(item); } } // Сохраняем в disks ТОЛЬКО съёмные диски disks = removableDisks; // Если нет съёмных дисков if (disks.empty()) { diskListWidget->clear(); QListWidgetItem* item = new QListWidgetItem("Съёмные диски не найдены"); item->setFlags(item->flags() & ~Qt::ItemIsEnabled); item->setForeground(QBrush(QColor(255, 0, 0))); diskListWidget->addItem(item); } // Если в disks больше одного съёмного диска!!! if (disks.size() > 1) { diskListWidget->clear(); QListWidgetItem* item = new QListWidgetItem("Найдено больше одного кандидата"); item->setFlags(item->flags() & ~Qt::ItemIsEnabled); item->setForeground(QBrush(QColor(0, 255, 0))); diskListWidget->addItem(item); } } void MainWindow::updateDiskInfo() { QList<QListWidgetItem*> selectedItems = diskListWidget->selectedItems(); if (selectedItems.isEmpty()) { fileSystemLabel->setText("-"); totalSpaceLabel->setText("-"); freeSpaceLabel->setText("-"); usedSpaceLabel->setText("-"); return; } QListWidgetItem* item = selectedItems.first(); if (!item || item->data(Qt::UserRole).toString().isEmpty()) { fileSystemLabel->setText("-"); totalSpaceLabel->setText("-"); freeSpaceLabel->setText("-"); usedSpaceLabel->setText("-"); return; } QString diskName = item->text(); QString driveLetter = diskName.left(2); // "E:" std::wstring rootPath = driveLetter.toStdWString() + L"\\"; auto formatSize = [](unsigned long long bytes) -> QString { constexpr double GB = 1024 * 1024 * 1024; constexpr double MB = 1024 * 1024; constexpr double KB = 1024; if (bytes >= GB) { return QString::number(bytes / GB, 'f', 2) + " GB"; } else if (bytes >= MB) { return QString::number(bytes / MB, 'f', 2) + " MB"; } else if (bytes >= KB) { return QString::number(bytes / KB, 'f', 2) + " KB"; } return QString::number(bytes) + " B"; }; // ПОЛУЧАЕМ АКТУАЛЬНЫЕ ДАННЫЕ ПРЯМО СЕЙЧАС wchar_t volumeName[MAX_PATH + 1] = {0}; wchar_t fileSystemName[MAX_PATH + 1] = {0}; ULARGE_INTEGER freeBytesAvailable, totalBytes, totalFreeBytes; ::GetVolumeInformationW(rootPath.c_str(), volumeName, MAX_PATH, NULL, NULL, NULL, fileSystemName, MAX_PATH); ::GetDiskFreeSpaceExW(rootPath.c_str(), &freeBytesAvailable, &totalBytes, &totalFreeBytes); // Отображаем информацию QString fs = QString::fromWCharArray(fileSystemName); if (wcslen(volumeName) > 0) { fileSystemLabel->setText(QString("%1 [%2]") .arg(fs) .arg(QString::fromWCharArray(volumeName))); } else { fileSystemLabel->setText(fs); } totalSpaceLabel->setText(formatSize(totalBytes.QuadPart)); freeSpaceLabel->setText(formatSize(totalFreeBytes.QuadPart)); usedSpaceLabel->setText(formatSize(totalBytes.QuadPart - totalFreeBytes.QuadPart)); validateSelections(); } void MainWindow::checkDriveAndFile() { resetOperationState(); connect(currentProgressDialog, &QProgressDialog::canceled, this, [this]() { operationCancelled = true; // Останавливаем таймер if (socketTimer) { socketTimer->stop(); } // Закрываем сокет if (currentSocket != INVALID_SOCKET) { shutdown(currentSocket, SD_BOTH); closesocket(currentSocket); currentSocket = INVALID_SOCKET; } // Сразу сбрасываем состояние QMetaObject::invokeMethod(this, &MainWindow::resetOperationState, Qt::QueuedConnection); }); if (!validateSelections()) { QMessageBox::warning(this, "Ошибка", "Не все параметры выбраны корректно"); return; } int currentRow = diskListWidget->currentRow() - 1; if (currentRow < 0 || currentRow >= disks.size()) { QMessageBox::critical(this, "Ошибка", "Неверно выбран диск"); return; } const auto& disk = disks[currentRow]; currentDiskLetter = QString::fromStdWString(disk.name).left(1); QString project = getSelectedItemText(projectListWidget); QString device = getSelectedItemText(deviceListWidget); QString firmware = getSelectedItemText(firmwareListWidget); currentFilePath = basePath + "/" + project + "/" + device + "/" + firmware; // Создаем прогресс-диалог currentProgressDialog = new QProgressDialog("Подготовка к копированию...", "Отмена", 0, 100, this); currentProgressDialog->setWindowModality(Qt::WindowModal); currentProgressDialog->setMinimumDuration(0); currentProgressDialog->setAutoClose(true); currentProgressDialog->setValue(0); // Подключаемся к серверу SOCKET clientSocket = INVALID_SOCKET; operationCancelled = false; finalResponse.clear(); try { // Инициализация WinSock WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { throw std::runtime_error("Ошибка инициализации сетевого соединения"); } // Создание сокета clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (clientSocket == INVALID_SOCKET) { throw std::runtime_error("Ошибка создания сокета"); } // Настройка адреса сервера sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(8080); inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr); // Подключение к серверу if (::connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { throw std::runtime_error("Не удалось подключиться к серверу проверки"); } // Устанавливаем сокет в неблокирующий режим u_long mode = 1; // 1 для неблокирующего режима ioctlsocket(clientSocket, FIONBIO, &mode); // Сохраняем сокет currentSocket = clientSocket; // Формируем запрос QString request = currentDiskLetter + "|" + currentFilePath; QByteArray requestData = request.toUtf8(); // Отправляем запрос if (send(clientSocket, requestData.constData(), requestData.size(), 0) == SOCKET_ERROR) { throw std::runtime_error("Ошибка отправки запроса"); } // Создаем и настраиваем таймер для проверки сокета socketTimer = new QTimer(this); socketTimer->setInterval(50); // Проверять каждые 50ms connect(socketTimer, &QTimer::timeout, this, &MainWindow::checkSocketData); connect(currentProgressDialog, &QProgressDialog::canceled, this, [this]() { operationCancelled = true; if (socketTimer) { socketTimer->stop(); } }); // Запускаем таймер socketTimer->start(); // Показываем диалог - UI останется отзывчивым currentProgressDialog->exec(); // После закрытия диалога if (socketTimer) { socketTimer->stop(); delete socketTimer; socketTimer = nullptr; } // Закрываем сокет if (currentSocket != INVALID_SOCKET) { shutdown(currentSocket, SD_BOTH); closesocket(currentSocket); currentSocket = INVALID_SOCKET; } WSACleanup(); // Анализируем полученный ответ if (!finalResponse.isEmpty()) { analyzeServerResponse(finalResponse, currentDiskLetter, currentFilePath); } } catch (const std::exception& e) { if (!operationCancelled) { QMessageBox::critical(this, "Ошибка", QString("Ошибка проверки: %1").arg(e.what())); } // Очистка ресурсов if (socketTimer) { socketTimer->stop(); delete socketTimer; socketTimer = nullptr; } if (clientSocket != INVALID_SOCKET) { closesocket(clientSocket); } if (currentSocket != INVALID_SOCKET) { currentSocket = INVALID_SOCKET; } WSACleanup(); } delete currentProgressDialog; currentProgressDialog = nullptr; } void MainWindow::checkSocketData() { if (operationCancelled || currentSocket == INVALID_SOCKET) { if (socketTimer) { socketTimer->stop(); } return; } char buffer[1024]; int bytesReceived = recv(currentSocket, buffer, sizeof(buffer) - 1, 0); if (bytesReceived == SOCKET_ERROR) { int error = WSAGetLastError(); if (error == WSAEWOULDBLOCK) { // Нет данных - это нормально, ждем следующую проверку return; } // Ошибка соединения if (socketTimer) { socketTimer->stop(); } QMetaObject::invokeMethod(this, [this]() { QMessageBox::critical(this, "Ошибка", "Ошибка соединения с сервером"); if (currentProgressDialog) { currentProgressDialog->reject(); } }, Qt::QueuedConnection); return; } if (bytesReceived > 0) { buffer[bytesReceived] = '\0'; std::string receivedData(buffer); size_t pos = 0; while (pos < receivedData.length()) { size_t endPos = receivedData.find('\n', pos); if (endPos == std::string::npos) { endPos = receivedData.length(); } std::string message = receivedData.substr(pos, endPos - pos); pos = endPos + 1; if (message.empty()) continue; if (message.find("PROGRESS:") == 0) { QString progressMsg = QString::fromStdString(message); QMetaObject::invokeMethod(this, [this, progressMsg]() { handleProgressMessage(progressMsg, *currentProgressDialog); }, Qt::QueuedConnection); } else { // Финальный ответ получен finalResponse = QString::fromStdString(message); if (socketTimer) { socketTimer->stop(); } QMetaObject::invokeMethod(this, [this]() { if (currentProgressDialog) { currentProgressDialog->accept(); } }, Qt::QueuedConnection); return; } } } // Проверяем, не завершилось ли соединение if (bytesReceived == 0) { // Сервер закрыл соединение if (socketTimer) { socketTimer->stop(); } if (finalResponse.isEmpty()) { QMetaObject::invokeMethod(this, [this]() { QMessageBox::warning(this, "Предупреждение", "Сервер закрыл соединение без ответа"); if (currentProgressDialog) { currentProgressDialog->reject(); } }, Qt::QueuedConnection); } } } void MainWindow::handleProgressMessage(const QString& progressMessage, QProgressDialog& progressDialog) { if (progressMessage.startsWith("PROGRESS:")) { int progressValue = progressMessage.mid(9).toInt(); progressDialog.setValue(progressValue); // Обновляем текст прогресса if (progressValue < 100) { progressDialog.setLabelText("Копирование файла..."); } else { progressDialog.setLabelText("Копирование завершено"); } } } void MainWindow::analyzeServerResponse(const QString& response, const QString& diskLetter, const QString& filePath) { if (response.startsWith("DRIVE:EXISTS|FILE:EXISTS|COPY:SUCCESS")) { QString newFilePath; int pathIndex = response.indexOf("|PATH:"); if (pathIndex != -1) { newFilePath = response.mid(pathIndex + 6); } QMessageBox::information(this, "Результат копирования прошивки", QString("Диск %1 и файл найдены!\nКопирование успешно завершено.\n\n" "Диск: %1\n" "Исходный файл: %2\n" "Созданный файл: %3") .arg(diskLetter).arg(filePath).arg(newFilePath)); } else if (response.startsWith("DRIVE:EXISTS|FILE:EXISTS|COPY:FAILED")) { QMessageBox::warning(this, "Результат копирования прошивки", QString("Диск %1 и файл найдены, но копирование не удалось.\n\nДиск: %1\nФайл: %2") .arg(diskLetter).arg(filePath)); } else if (response.startsWith("DRIVE:NOT_EXISTS")) { QMessageBox::critical(this, "Ошибка", QString("Диск %1 не найден").arg(diskLetter)); } else if (response.startsWith("FILE:NOT_EXISTS")) { QMessageBox::critical(this, "Ошибка", QString("Файл не найден: %1").arg(filePath)); } else if (response.startsWith("ERROR:")) { QMessageBox::critical(this, "Ошибка", QString("Ошибка сервера: %1").arg(response.mid(6))); } else { QMessageBox::warning(this, "Результат проверки", QString("Неизвестный ответ от сервера:\n%1").arg(response)); } } void MainWindow::resetOperationState() { // Останавливаем и очищаем таймер if (socketTimer) { socketTimer->stop(); socketTimer->deleteLater(); socketTimer = nullptr; } // Закрываем сокет if (currentSocket != INVALID_SOCKET) { shutdown(currentSocket, SD_BOTH); closesocket(currentSocket); currentSocket = INVALID_SOCKET; } // Сбрасываем флаги operationCancelled = false; finalResponse.clear(); currentDiskLetter.clear(); currentFilePath.clear(); // Удаляем прогресс-диалог if (currentProgressDialog) { currentProgressDialog->deleteLater(); currentProgressDialog = nullptr; } // WSACleanup будет вызываться в конце операции или деструкторе // Не вызываем WSACleanup здесь, так как может потребоваться для новой операции // Разблокируем UI copyButton->setEnabled(true); refreshButton->setEnabled(true); projectListWidget->setEnabled(true); deviceListWidget->setEnabled(true); firmwareListWidget->setEnabled(true); diskListWidget->setEnabled(true); }