diff --git a/CetHub/CetHub.pro b/CetHub/CetHub.pro index 3ca28b3..f521e23 100644 --- a/CetHub/CetHub.pro +++ b/CetHub/CetHub.pro @@ -1,4 +1,4 @@ -QT += core gui +QT += core gui network greaterThan(QT_MAJOR_VERSION, 4): QT += widgets @@ -20,6 +20,7 @@ SOURCES += \ hubmainwidget.cpp HEADERS += \ + downloadmanager.h \ hubmainwidget.h FORMS += \ diff --git a/CetHub/downloadmanager.h b/CetHub/downloadmanager.h new file mode 100644 index 0000000..b907e07 --- /dev/null +++ b/CetHub/downloadmanager.h @@ -0,0 +1,198 @@ +#ifndef DOWNLOADMANAGER_H +#define DOWNLOADMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Downloader : public QObject +{ + Q_OBJECT +public: + explicit Downloader(QObject *parent = nullptr) : QObject(parent), m_manager(new QNetworkAccessManager(this)) {} + +// void downloadFile(const QUrl &url, const QString &destinationFilePath) +// { +// m_destinationFilePath = destinationFilePath; + +// QNetworkRequest request(url); + +// // Устанавливаем время ожидания тайм-аута передачи сети +// request.setTransferTimeout(5000); // 5000 миллисекунд (5 секунд) +// m_reply = m_manager->get(request); + +// connect(m_reply, &QNetworkReply::downloadProgress, this, &Downloader::onDownloadProgress); +// connect(m_reply, &QNetworkReply::finished, this, &Downloader::onDownloadFinished); + +// // Обрабатываем ошибки сети +// connect(m_reply, &QNetworkReply::error, this, &Downloader::onErrorOccurred); + +//// connect(m_reply, &QNetworkReply::error, this, &Downloader::onDownloadError);//QNetworkReply::errorOccurred(QNetworkReply::NetworkError) + +// //connect(m_reply, QOverload::of(&QNetworkReply::error), this, &Downloader::onError); + +// } + +signals: + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void downloadFinished(bool success); + void downloadError(const QString &errorString); + +private slots: + void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) + { + emit downloadProgress(bytesReceived, bytesTotal); + } + + void onDownloadFinished() + { + if (m_reply->error() == QNetworkReply::NoError) + { + qDebug() << "finish download"; + // Проверяем статус HTTP ответа + if (m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 404) + { + emit downloadError("File not found."); + emit downloadFinished(false); + return; + } + + QFile file(m_destinationFilePath); + if (file.open(QIODevice::WriteOnly)) + { + file.write(m_reply->readAll()); + file.close(); + emit downloadFinished(true); + } + else + { + emit downloadError("Failed to open destination file for writing."); + emit downloadFinished(false); + } + } + else + { + qDebug() << "finish download and error"; + emit downloadError(m_reply->errorString()); + emit downloadFinished(false); + } + + m_reply->deleteLater(); + } + + void onErrorOccurred(QNetworkReply::NetworkError code) + { + qDebug() << "Error occurred:" << m_reply->errorString(); + qDebug() << "Network error occurred:" << code; + emit downloadError(m_reply->errorString()); + emit downloadFinished(false); + } + +// void onDownloadError(QNetworkReply::NetworkError code) +// { +// qDebug() << "Network error occurred:" << code; +// emit downloadError(m_reply->errorString()); +// emit downloadFinished(false); +// } +private: + QNetworkAccessManager *m_manager; + QNetworkReply *m_reply; + QString m_destinationFilePath; + +public: + void downloadFile(const QUrl &url, const QString &destinationFilePath) + { + m_destinationFilePath = destinationFilePath; + + QNetworkRequest request(url); + + // Устанавливаем время ожидания тайм-аута передачи сети + //request.setTransferTimeout(5000); // 5000 миллисекунд (5 секунд) + m_reply = m_manager->get(request); + + connect(m_reply, &QNetworkReply::downloadProgress, this, &Downloader::onDownloadProgress); + connect(m_reply, &QNetworkReply::finished, this, &Downloader::onDownloadFinished); + + // Обрабатываем ошибки сети + connect(m_reply, QOverload::of(&QNetworkReply::error), this, &Downloader::onErrorOccurred); + +// connect(m_reply, &QNetworkReply::error, this, &Downloader::onDownloadError);//QNetworkReply::errorOccurred(QNetworkReply::NetworkError) + + //connect(m_reply, QOverload::of(&QNetworkReply::error), this, &Downloader::onError); + + } +}; + + + + +//enum UpdateErrorCode { +// UpdateNoError = 0, +// DownloadError = 1, +// RenameError = 2, +// DownloadCancelled = 3 +//}; + +//class DownloadManager : public QObject { +// Q_OBJECT +//public: +// DownloadManager(QObject *parent = nullptr) : QObject(parent) { +// connect(&manager, &QNetworkAccessManager::finished, this, &DownloadManager::onDownloadFinished); +// } + +// void downloadFile(const QUrl &url, const QString &outputFileName) { +// QNetworkRequest request(url); +// currentDownload = manager.get(request); +// outputFile.setFileName(outputFileName); +// if (!outputFile.open(QIODevice::WriteOnly)) { +// emit downloadError(DownloadError); +// return; +// } + +// connect(currentDownload, &QNetworkReply::downloadProgress, this, &DownloadManager::onDownloadProgress); +// connect(currentDownload, &QNetworkReply::readyRead, this, [this]() { +// outputFile.write(currentDownload->readAll()); +// }); +// } + +// void cancelDownload() { +// if (currentDownload) { +// currentDownload->abort(); +// } +// } + + + +//signals: +// void downloadProgress(qint64 bytesRead, qint64 totalBytes); +// void downloadError(int errorCode); +// void downloadFinished(); + +//private slots: +// void onDownloadFinished(QNetworkReply *reply) { +// if (reply->error()) { +// emit downloadError(DownloadError); +// } else { +// outputFile.close(); +// emit downloadFinished(); +// } +// } + +// void onDownloadProgress(qint64 bytesRead, qint64 totalBytes) { +// emit downloadProgress(bytesRead, totalBytes); +// } + +//private: +// QNetworkAccessManager manager; +// QNetworkReply *currentDownload = nullptr; +// QFile outputFile; +//}; + + +#endif // DOWNLOADMANAGER_H diff --git a/CetHub/hubmainwidget.cpp b/CetHub/hubmainwidget.cpp index 6724c38..7aa13ba 100644 --- a/CetHub/hubmainwidget.cpp +++ b/CetHub/hubmainwidget.cpp @@ -2,11 +2,12 @@ #include "ui_hubmainwidget.h" #include "QtNetwork/QNetworkReply" -HubMainWidget::HubMainWidget(QWidget *parent) +HubMainWidget::HubMainWidget(QWidget *parent, QApplication *app) : QWidget(parent) , ui(new Ui::HubMainWidget) { ui->setupUi(this); + parentApp = app; connect(&sysTimer, &QTimer::timeout, this, [this]() { ui->dateTimeEdit->setTime(QTime::currentTime()); @@ -14,80 +15,122 @@ HubMainWidget::HubMainWidget(QWidget *parent) }); sysTimer.start(1000); - getModules(); + setupTableHeaders(); + + getModules(); } bool HubMainWidget::getModules() { - dllInfo UnionCom; - UnionCom.libName = "Терминал CAN, RS, Modbus"; + QStringList appDep; + appInfo UnionCom; + UnionCom.appName = "Терминал CAN, RS, Modbus"; UnionCom.fileName = "UnionCom"; - UnionCom.initFuncName = "init"; - UnionCom.adr = "https://git.arktika.cyou/Tenocha/UnionComDLL"; + UnionCom.fileDir = QDir::currentPath() + "/UnionCom/"; + UnionCom.sourceAdr = "https://git.arktika.cyou/Tenocha/UnionComDLL"; UnionCom.downloadAdr = "https://git.arktika.cyou/Tenocha/UnionComDLL/archive/Release.zip"; - connectLibrary(UnionCom); + connectModule(UnionCom); - dllInfo M3KTE_TERM; - M3KTE_TERM.libName = "МЗКТЕ Терминал"; + appInfo M3KTE_TERM; + M3KTE_TERM.appName = "МЗКТЕ Терминал"; M3KTE_TERM.fileName = "M3KTE_TERM"; - M3KTE_TERM.initFuncName = "init"; - M3KTE_TERM.adr = "https://git.arktika.cyou/Tenocha/M3KTE_TERM"; + M3KTE_TERM.fileDir = QDir::currentPath() + "/M3KTE_TERM/"; + M3KTE_TERM.sourceAdr = "https://git.arktika.cyou/Tenocha/M3KTE_TERM"; M3KTE_TERM.downloadAdr = "https://git.arktika.cyou/Tenocha/M3KTE_TERM/releases/download/Pre-release/M3KTETERM.rar"; - connectLibrary(M3KTE_TERM); + connectModule(M3KTE_TERM); - dllInfo LineRingerLib; - LineRingerLib.libName = "Поиск modbus устройств"; - LineRingerLib.fileName = "LineRingerLib32Bit"; - LineRingerLib.initFuncName = "init"; - LineRingerLib.adr = "https://git.arktika.cyou/Tenocha/LineRingerDLL"; + appInfo LineRingerLib; + LineRingerLib.appName = "Поиск modbus устройств"; + LineRingerLib.fileName = "LineRinger"; + LineRingerLib.fileDir = QDir::currentPath() + "/LineRinge/"; + LineRingerLib.sourceAdr = "https://git.arktika.cyou/Tenocha/LineRingerDLL"; LineRingerLib.downloadAdr = "https://git.arktika.cyou/Tenocha/LineRingerDLL/archive/Release.zip"; - connectLibrary(LineRingerLib); + connectModule(LineRingerLib); + + appInfo CanGaroo; + CanGaroo.appName = "CanGaroo"; + CanGaroo.fileName = "cangaroo"; + CanGaroo.fileDir = QDir::currentPath() + "/CanGaroo/"; + CanGaroo.sourceAdr = "https://git.arktika.cyou/Tenocha/LineRingerDLL"; + CanGaroo.downloadAdr = "https://git.arktika.cyou/Tenocha/LineRingerDLL/archive/Release.zip"; + connectModule(CanGaroo); + + //LogsViewS + appInfo LogsViewS; + LogsViewS.appName = "LogsViewS"; + LogsViewS.fileName = "LogsViewS"; + LogsViewS.fileDir = QDir::currentPath() + "/LogsViewS/"; + LogsViewS.sourceAdr = "http://git.arktika.cyou/Tenocha/LineRingerDLL"; + LogsViewS.downloadAdr = "http://git.arktika.cyou/all_public/LogViewNovel/raw/branch/master/LogsView_64/LogsViewS_64.exe"; + connectModule(LogsViewS); + + ui->appTable->resizeColumnsToContents(); + setupTableHeaders(); return true; } -bool HubMainWidget::connectLibrary(dllInfo modul) +void HubMainWidget::setupTableHeaders() { - QLibrary myLib(modul.fileName); - myLib.load(); - //QString check = myLib.errorString(); - unsigned newRow = ui->libraryTable->rowCount(); - ui->libraryTable->insertRow(newRow); - ui->libraryTable->setItem(newRow, 0, new QTableWidgetItem(modul.libName)); - ui->libraryTable->setItem(newRow, 1, new QTableWidgetItem(modul.fileName)); - QTableWidgetItem *item = new QTableWidgetItem(); - item->setText(""); - QLabel *url = new QLabel(); - url->setText(tr("Source").arg(modul.adr.toString())); - url->setTextFormat(Qt::RichText); - url->setTextInteractionFlags(Qt::TextBrowserInteraction); - url->setOpenExternalLinks(true); - ui->libraryTable->setCellWidget(newRow, 3, url); - //ui->libraryTable->setItem(newRow, 2, new QTableWidgetItem(modul.adr)); - if(myLib.isLoaded()) - { - typedef QWidget* (*DLL_Init_Function)(QWidget*); - DLL_Init_Function DLL_Init = (DLL_Init_Function) myLib.resolve(modul.initFuncName.toUtf8()); - if(DLL_Init) - { - item->setCheckState(Qt::Checked); - ui->libraryTable->setItem(newRow, 2, item); - modul.widget = DLL_Init(nullptr); - connectedModules.append(modul); - ui->appBox->addItem(modul.libName); - ui->libraryTable->resizeColumnsToContents(); - return true; - } - } - else - { - item->setCheckState(Qt::Unchecked); - ui->libraryTable->setItem(newRow, 2, item); - connectedModules.append(modul); - ui->appBox->addItem(modul.libName); - ui->libraryTable->resizeColumnsToContents(); - } - return false; + // Устанавливаем заголовки для каждой колонки + QStringList headers; + headers << "Наличие файла" // колонка 0 + << "Название программы" // колонка 1 + << "Название файла" // колонка 2 + << "Источник"; // колонка 3 + ui->appTable->setHorizontalHeaderLabels(headers); +} + +void HubMainWidget::connectModule(appInfo &info) +{ + // 1. Проверка наличия файла .exe в директории + QString dirPath = info.fileDir; // предполагается, что это уже полный путь + QString filePath = dirPath + "/" + info.fileName; + + // Проверяем наличие файла с расширением .exe + QString exeFilePath = filePath + ".exe"; + + info.isFileExists = QFile::exists(exeFilePath); + + // 2. Создаём новую строку таблицы + int row = ui->appTable->rowCount(); + ui->appTable->insertRow(row); + + // 3. Создаём виджеты и заполняем ячейки таблицы + + // 1) Наличие файла (QCheckBox) + QCheckBox *fileCheckBox = new QCheckBox(); + fileCheckBox->setChecked(info.isFileExists); + QWidget *fileCellWidget = new QWidget(); + QHBoxLayout *fileLayout = new QHBoxLayout(fileCellWidget); + fileLayout->addWidget(fileCheckBox); + fileLayout->setAlignment(Qt::AlignCenter); + fileLayout->setContentsMargins(0,0,0,0); + fileCellWidget->setLayout(fileLayout); + ui->appTable->setCellWidget(row, 0, fileCellWidget); + + // 2) Название программы (QString) + QTableWidgetItem *nameItem = new QTableWidgetItem(info.appName); + ui->appTable->setItem(row, 1, nameItem); + + // 3) Название файла (QString) + QTableWidgetItem *fileNameItem = new QTableWidgetItem(info.fileName); + ui->appTable->setItem(row, 2, fileNameItem); + + // 4) Источник файла (QString из sourceAdr) + QLabel *sourceLabel = new QLabel(); + QString linkText = QString("Source").arg(info.sourceAdr.toString()); + sourceLabel->setText(linkText); + sourceLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + sourceLabel->setOpenExternalLinks(true); + + ui->appTable->setCellWidget(row, 3, sourceLabel); + + // 4. Добавляем структуру в QVector + connectedModules.append(info); + + // 5. Добавляем название программы + ui->appBox->addItem(info.appName); } HubMainWidget::~HubMainWidget() @@ -98,27 +141,161 @@ HubMainWidget::~HubMainWidget() void HubMainWidget::on_openOrDownloadButton_clicked() { - if(ui->appBox->count()!=0) + int index = ui->appBox->currentIndex(); + + if (index < 0 || index >= connectedModules.size()) + return; // Защита + + auto &module = connectedModules.at(index); + QString filePath = module.fileDir + "/" + module.fileName + ".exe"; + + if (QFile::exists(filePath)) { - if(connectedModules.at(ui->appBox->currentIndex()).widget != nullptr) - { - connectedModules.at(ui->appBox->currentIndex()).widget->show(); - } - else - { - QDesktopServices::openUrl(connectedModules.at(ui->appBox->currentIndex()).adr); +// // Файл есть, запускаем + QProcess process; + process.setWorkingDirectory(module.fileDir); + process.setProgram(filePath); + process.startDetached(); + } + else + { + // Получаем путь к директории + QDir dir(module.fileDir); + + // Проверяем, существует ли директория + if (!dir.exists()) { + // Создаём директорию (включая все недостающие промежуточные папки) + if (!dir.mkpath(".")) { + QMessageBox::warning(this, "Ошибка", "Не удалось создать директорию: " + module.fileDir); + return; // Выходим из функции, т.к. сохранить файл невозможно + } } + + // Путь для сохранения файла (включая имя файла и расширение) + QString saveFilePath = filePath; + + // Создаем объект Downloader + Downloader downloader; + + // Создаем диалог прогресса + QProgressDialog progressDialog; + progressDialog.setWindowTitle("Downloading"); + progressDialog.setLabelText("Downloading file:\n" + module.downloadAdr.toString() + "..."); + progressDialog.setCancelButton(new QPushButton("Cancel")); + progressDialog.setMinimumDuration(200); + + // Связываем прогресс скачивания с диалогом + QObject::connect(&downloader, &Downloader::downloadProgress, [&](qint64 bytesReceived, qint64 bytesTotal) { + progressDialog.setMaximum(bytesTotal); + progressDialog.setValue(bytesReceived); + }); + + // Обработка завершения скачивания + QObject::connect(&downloader, &Downloader::downloadFinished, [&](bool success) { + if (success) { + QMessageBox::information(nullptr, "Загрузка завершена", "Файл успешно скачан:\n" + saveFilePath); + QWidget *statusWidget = ui->appTable->cellWidget(index, 0); + if (statusWidget) { + QCheckBox *statusCheckBox = statusWidget->findChild(); + if (statusCheckBox) { + statusCheckBox->setChecked(true); // файл есть + } + on_appBox_currentIndexChanged(ui->appBox->currentIndex()); + } + } else { + QMessageBox::warning(nullptr, "Ошибка", "Ошибка при скачивании файла"); + QFile::remove(saveFilePath); // удаляем поврежденный или неполный файл + if (dir.exists()) { + if (!dir.removeRecursively()) { + QMessageBox::warning(this, "Ошибка", "Не удалось удалить созданную директорию: " + module.fileDir); + } + } + } + progressDialog.setValue(progressDialog.maximum()); + progressDialog.close(); + }); + + // Запускаем скачивание + qDebug() << "Start download:" << module.downloadAdr.toString() << " to " << saveFilePath; + downloader.downloadFile(module.downloadAdr, saveFilePath); + + // Показываем диалог прогресса + progressDialog.exec(); + + +// // Файл отсутствует, скачиваем +// QUrl downloadUrl(module.downloadAdr); +// QNetworkRequest request(downloadUrl); +// QNetworkReply *reply = networkManager->get(request); + +// // Ожидаем завершения скачивания +// QEventLoop loop; +// connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); +// loop.exec(); + +// if (reply->error() == QNetworkReply::NoError) +// { +// QByteArray data = reply->readAll(); + +// // Получаем путь к директории +// QDir dir(module.fileDir); + +// // Проверяем, существует ли директория +// if (!dir.exists()) { +// // Создаём директорию (включая все недостающие промежуточные папки) +// if (!dir.mkpath(".")) { +// QMessageBox::warning(this, "Ошибка", "Не удалось создать директорию: " + module.fileDir); +// return; // Выходим из функции, т.к. сохранить файл невозможно +// } +// } + +// // Записываем файл +// QFile file(filePath); +// if (file.open(QIODevice::WriteOnly)) +// { +// file.write(data); +// file.close(); +// QMessageBox::information(this, "Загрузка завершена", "Файл успешно скачан."); +// // После скачивания можно запустить файл +// //QProcess::startDetached(filePath); +// // Обновляем статус наличия файла (чекбокс) +// QWidget *statusWidget = ui->appTable->cellWidget(index, 0); +// if (statusWidget) { +// QCheckBox *statusCheckBox = statusWidget->findChild(); +// if (statusCheckBox) { +// statusCheckBox->setChecked(true); // файл есть +// } +// } +// connectedModules[index].isFileExists = true; +// on_appBox_currentIndexChanged(ui->appBox->currentIndex()); +// } +// else +// { +// QMessageBox::warning(this, "Ошибка", "Не удалось сохранить файл."); +// } +// } +// else +// { +// QMessageBox::warning(this, "Ошибка загрузки", reply->errorString()); +// } + +// reply->deleteLater(); } } void HubMainWidget::on_appBox_currentIndexChanged(int index) { - if(connectedModules.at(index).widget == nullptr) - { - ui->openOrDownloadButton->setText("Скачать"); - } - else + if (index < 0 || index >= connectedModules.size()) + return; // Защита от выхода за границы + + const appInfo &info = connectedModules.at(index); + + if (info.isFileExists) { ui->openOrDownloadButton->setText("Открыть"); } + else + { + ui->openOrDownloadButton->setText("Скачать"); + } } diff --git a/CetHub/hubmainwidget.h b/CetHub/hubmainwidget.h index 7e57af1..0284d60 100644 --- a/CetHub/hubmainwidget.h +++ b/CetHub/hubmainwidget.h @@ -4,11 +4,28 @@ #include #include #include -#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + #include #include + +#include + +#include "windows.h" +#include "downloadmanager.h" + #include -#include "QtNetwork/QNetworkAccessManager" QT_BEGIN_NAMESPACE namespace Ui { class HubMainWidget; } @@ -19,7 +36,7 @@ class HubMainWidget : public QWidget Q_OBJECT public: - HubMainWidget(QWidget *parent = nullptr); + HubMainWidget(QWidget *parent = nullptr, QApplication *app = nullptr); ~HubMainWidget(); private slots: @@ -29,21 +46,24 @@ private slots: private: QTimer sysTimer; + QApplication *parentApp; + QNetworkAccessManager *networkManager = new QNetworkAccessManager(this); - struct dllInfo + struct appInfo { - QWidget* widget = nullptr; - QString libName; - QString fileName; - QString initFuncName; - QUrl adr; + QString appName = "appName"; + QString fileName = "fileName"; + bool isFileExists = false; + QString fileDir = "fileDir"; + QUrl sourceAdr; QUrl downloadAdr; }; - QVectorconnectedModules; + QVectorconnectedModules; bool getModules(); - bool connectLibrary(dllInfo modul); + void connectModule(appInfo &info); + void setupTableHeaders(); Ui::HubMainWidget *ui; }; diff --git a/CetHub/hubmainwidget.ui b/CetHub/hubmainwidget.ui index b76c91b..74d93a0 100644 --- a/CetHub/hubmainwidget.ui +++ b/CetHub/hubmainwidget.ui @@ -80,7 +80,7 @@ - + true @@ -90,6 +90,9 @@ QAbstractItemView::NoEditTriggers + + 4 + true @@ -97,28 +100,12 @@ false - true + false - - - Modul - - - - - DLL - - - - - Finded - - - - - Link - - + + + + diff --git a/CetHub/main.cpp b/CetHub/main.cpp index c287a6b..1ad1acd 100644 --- a/CetHub/main.cpp +++ b/CetHub/main.cpp @@ -5,7 +5,8 @@ int main(int argc, char *argv[]) { QApplication a(argc, argv); - HubMainWidget w; - w.show(); + a.addLibraryPath(QDir::currentPath()); + HubMainWidget *w = new HubMainWidget(nullptr, &a); + w->show(); return a.exec(); }