diff --git a/CMakeLists.txt b/CMakeLists.txt index c19de02e..5248ab81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ option(DEBUG "Enable debug mode" OFF) option(RELEASE "Enable release mode" OFF) option(docker_cross "Cross compile option for docker container" OFF) set(KIPR_VERSION_MAJOR 1) -set(KIPR_VERSION_MINOR 2) +set(KIPR_VERSION_MINOR 3) set(KIPR_VERSION_PATCH 0) cmake_minimum_required(VERSION 2.8.11) diff --git a/include/botui/AboutWidget.h b/include/botui/AboutWidget.h index 16c42e63..f755d6f7 100644 --- a/include/botui/AboutWidget.h +++ b/include/botui/AboutWidget.h @@ -3,7 +3,7 @@ #include "StandardWidget.h" // Include here if needed for inheritance #include - +#include namespace Ui { class AboutWidget; @@ -29,9 +29,13 @@ public slots: void developerList(); void eventModeBackground(int checked); +private slots: + void rebootBox(); + private: Ui::AboutWidget *ui; QProcess proc; + QMessageBox *msgBox; }; #endif diff --git a/include/botui/BackupWidget.h b/include/botui/BackupWidget.h index 24661246..8aa161b0 100644 --- a/include/botui/BackupWidget.h +++ b/include/botui/BackupWidget.h @@ -3,6 +3,8 @@ #include "Device.h" #include "StandardWidget.h" +#include +#include namespace Ui { @@ -11,19 +13,30 @@ namespace Ui class BackupWidget : public StandardWidget { -Q_OBJECT + Q_OBJECT public: BackupWidget(Device *device, QWidget *widget = 0); ~BackupWidget(); public slots: void backupoption(); - //void cleardrive(); + // void cleardrive(); void restore(); - + +private slots: + void updateFinished(int exitCode, QProcess::ExitStatus exitStatus); + void restoreFinished(int, QProcess::ExitStatus exitStatus); + private: + bool isAlreadyMounted(const QString &device, const QString &mountPoint); + bool mountUsb(const QString device, const QDir dir); + bool unmountUsb(const QString device); + static const QDir mountDir; Ui::BackupWidget *ui; + QProcess *backup_process; + QProcess *restore_process; + void handleStandardOutput(); + void handleStandardError(); }; - #endif diff --git a/src/AboutWidget.cpp b/src/AboutWidget.cpp index 2bddec54..e3ec5bec 100644 --- a/src/AboutWidget.cpp +++ b/src/AboutWidget.cpp @@ -7,12 +7,14 @@ #include "RootController.h" #include "DeveloperListWidget.h" #include +#include #include #include - +#include +#include AboutWidget::AboutWidget(Device *device, QWidget *parent) : StandardWidget(device, parent), - ui(new Ui::AboutWidget) + ui(new Ui::AboutWidget), msgBox(nullptr) { ui->setupUi(this); // Setup the UI @@ -94,13 +96,79 @@ AboutWidget::AboutWidget(Device *device, QWidget *parent) connect(ui->developerList, SIGNAL(clicked()), SLOT(developerList())); connect(ui->toggleSwitch, SIGNAL(stateChanged(int)), this, SLOT(eventModeBackground(int))); + connect(ui->toggleSwitch, SIGNAL(stateChanged(int)), this, SLOT(rebootBox())); } AboutWidget::~AboutWidget() { + if (msgBox) + { + msgBox->deleteLater(); // or delete msgBox; if you want to directly delete + } delete ui; } +void AboutWidget::rebootBox() +{ + qDebug() << "In rebootBox()"; + + if (!msgBox) + { + // Create the QMessageBox + msgBox = new QMessageBox(this); + msgBox->setWindowTitle("Switch Event Mode"); + msgBox->setMaximumSize(500, 480); // Limit the size of the QMessageBox + msgBox->setStandardButtons(QMessageBox::NoButton); + + // Create QLabel for the GIF + QLabel *gifLabel = new QLabel(); + gifLabel->setAlignment(Qt::AlignCenter); // Center the GIF label + + // Create QLabel for the message text + QLabel *messageLabel = new QLabel("Switching Event Mode Now..."); + messageLabel->setAlignment(Qt::AlignCenter); // Center the message label + + // Create a container widget and a new vertical layout + QWidget *container = new QWidget(); + QVBoxLayout *vLayout = new QVBoxLayout(container); + + // Add the GIF label and message label to the vertical layout + vLayout->addWidget(gifLabel); + vLayout->addWidget(messageLabel); + + // Adjust the vertical layout spacing and margins + vLayout->setSpacing(10); + vLayout->setContentsMargins(10, 10, 10, 10); + + // Set the layout of the container + container->setLayout(vLayout); + + // Access the internal layout of the QMessageBox + QGridLayout *msgBoxLayout = qobject_cast(msgBox->layout()); + if (msgBoxLayout) + { + msgBoxLayout->addWidget(container, 0, 0, 1, msgBoxLayout->columnCount()); + } + else + { + qDebug() << "msgBoxLayout is nullptr!"; // Debugging message if layout is nullptr + } + + // Setup and start the GIF movie + QMovie *movie = new QMovie("://qml/botguy_noMargin.gif"); + movie->setScaledSize(QSize(200, 240)); + gifLabel->setMovie(movie); + movie->start(); + + // Show the QMessageBox non-blocking + msgBox->setText(""); // Hide the default text to avoid duplication + } + msgBox->show(); + + // Debug information + qDebug() << "Message box displayed, starting event mode change sequence..."; +} + QString AboutWidget::getRaspberryPiType() { QProcess process; @@ -115,12 +183,11 @@ QString AboutWidget::getRaspberryPiType() { qDebug() << "Successfully got Raspberry Pi Type:" << output.trimmed(); // Trim output to remove whitespace - - if(output.trimmed() == "a020d3" || output.trimmed() == "a020d4") + if (output.trimmed() == "a020d3" || output.trimmed() == "a020d4") { piType = "3B+"; } - else if(output.trimmed() == "a02082" || output.trimmed() == "a22082" || output.trimmed() == "a32082" || output.trimmed() == "a52082" || output.trimmed() == "a22083") + else if (output.trimmed() == "a02082" || output.trimmed() == "a22082" || output.trimmed() == "a32082" || output.trimmed() == "a52082" || output.trimmed() == "a22083") { piType = "3B"; } @@ -192,22 +259,30 @@ void AboutWidget::eventModeBackground(int checked) qDebug() << "Checked: " << checked; ui->toggleSwitch->setEnabled(false); - + // rebootBox(); if (checked == 2) // Enable Event Mode { setEventModeState("true"); emit eventModeEnabled(); NetworkManager::ref().deactivateAP(); - ui->toggleSwitch->setEnabled(true); } else // Disable Event Mode { setEventModeState("false"); emit eventModeDisabled(); NetworkManager::ref().enableAP(); - ui->toggleSwitch->setEnabled(true); } + + QTimer::singleShot(3000, this, [this]() + { + if (msgBox) + { + msgBox->hide(); + delete msgBox; + msgBox = nullptr; + } + ui->toggleSwitch->setEnabled(true); }); } void AboutWidget::developerList() diff --git a/src/BackupWidget.cpp b/src/BackupWidget.cpp index 5cbcda3a..e3651809 100644 --- a/src/BackupWidget.cpp +++ b/src/BackupWidget.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "MenuBar.h" #include "StatusBar.h" @@ -23,41 +24,236 @@ #include "LockScreen.h" #include "SystemUtils.h" +QStringList errorFiles; BackupWidget::BackupWidget(Device *device, QWidget *widget) - : StandardWidget(device, widget), - ui(new Ui::BackupWidget) + : StandardWidget(device, widget), + ui(new Ui::BackupWidget) { - ui->setupUi(this); - performStandardSetup("Backup"); - - ui->updateConsole->setVisible(false); - - connect(ui->backupoption, SIGNAL(clicked()), SLOT(backupoption())); - connect(ui->restore, SIGNAL(clicked()), SLOT(restore())); - //connect(ui->cleardrive, SIGNAL(clicked()), SLOT(cleardrive())); + ui->setupUi(this); + performStandardSetup("Backup"); + + ui->backupConsole->setVisible(true); + + connect(ui->backupoption, SIGNAL(clicked()), SLOT(backupoption())); + connect(ui->restore, SIGNAL(clicked()), SLOT(restore())); + // connect(ui->cleardrive, SIGNAL(clicked()), SLOT(cleardrive())); } BackupWidget::~BackupWidget() { - delete ui; + delete ui; } - +const QDir BackupWidget::mountDir = QDir("/mnt"); void BackupWidget::backupoption() { - QProcess backup_process; - backup_process.startCommand("sh /home/kipr/wombat-os/Backup/backup.sh"); - ui->updateConsole->insertPlainText("Backup Complete"); - QMessageBox::warning(this, "Backup complete", "Backup complete"); + + // Mount USB drive + if (!this->mountUsb("/dev/sda1", BackupWidget::mountDir) && !this->mountUsb("/dev/sdb1", BackupWidget::mountDir)) + QMessageBox::warning(this, "USB not found", "Failed to mount USB device\n Please insert USB device and try again"); + else + { + // Verify with user that they want to do the backup + if (QMessageBox::question(this, "Backup?", + QString("Are you sure you want to backup your programs?"), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) + return; + + // Disable buttons + ui->backupoption->setEnabled(false); + ui->restore->setEnabled(false); + StandardWidget::disableMenuBar(); + + ui->BackupRestoreLabel->setText("Backup in progress..."); + backup_process = new QProcess(); + + // Capture output and error from backup.sh + connect(backup_process, &QProcess::readyReadStandardOutput, this, &BackupWidget::handleStandardOutput); + connect(backup_process, &QProcess::readyReadStandardError, this, &BackupWidget::handleStandardError); + + QString command = QString("/home/kipr/wombat-os/Backup/backup.sh"); + backup_process->start("sh", QStringList() << "-c" << command); + ui->backupConsole->setProcess(backup_process); + connect(backup_process, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(updateFinished(int, QProcess::ExitStatus))); + } +} + +bool BackupWidget::isAlreadyMounted(const QString &device, const QString &mountPoint) +{ + QFile file("/proc/mounts"); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + qDebug() << "Failed to open /proc/mounts to check mounted devices."; + return false; + } + QString normalizedMountPoint = QDir::cleanPath(mountPoint); + qDebug() << "Checking if device:" << device << "is mounted at:" << normalizedMountPoint; + + QTextStream in(&file); + qDebug() << "in[0]: " << in.readLine(); + while (!in.atEnd()) + { + QString line = in.readLine(); + QStringList parts = line.split(" "); + if (parts.size() >= 2) + { + QString mountedDevice = parts[0]; + QString mountedPath = QDir::cleanPath(parts[1]); + qDebug() << "Found mounted device:" << mountedDevice << "at path:" << mountedPath; + + if (mountedDevice == device && mountedPath == normalizedMountPoint) + { + qDebug() << "Device is already mounted."; + return true; // Device is already mounted at the target path + } + } + } + qDebug() << "Device is not mounted."; + return false; +} + +bool BackupWidget::mountUsb(const QString device, const QDir dir) +{ + // QProcess proc; + // proc.start("mount", QStringList() << device << dir.absolutePath()); + // return proc.waitForFinished(5000) && proc.exitCode() == 0; + + // QProcess proc; + // proc.start("sudo", QStringList() << "mount" << device << dir.absolutePath()); + // if (!proc.waitForFinished(5000)) + // { + // qDebug() << "Mount process timed out for device:" << device; + // return false; + // } + // if (proc.exitCode() != 0) + // { + // qDebug() << "Mount failed for device:" << device + // << ", Error:" << proc.readAllStandardError(); + // return false; + // } + // return true; + + // Check if already mounted + if (isAlreadyMounted(device, dir.absolutePath())) + { + qDebug() << "Device already mounted:" << device << "at" << dir.absolutePath(); + return true; + } + // Proceed with mounting + QProcess proc; + proc.start("sudo", QStringList() << "mount" << device << dir.absolutePath()); + if (!proc.waitForFinished(5000)) + { + qDebug() << "Mount process timed out for device:" << device; + return false; + } + if (proc.exitCode() != 0) + { + qDebug() << "Mount failed for device:" << device + << ", Error:" << proc.readAllStandardError(); + return false; + } + + qDebug() << "Device successfully mounted:" << device << "at" << dir.absolutePath(); + return true; +} + +bool BackupWidget::unmountUsb(const QString device) +{ + QProcess proc; + proc.start("umount", QStringList() << device); + return proc.waitForFinished(5000) && proc.exitCode() == 0; +} + +void BackupWidget::updateFinished(int, QProcess::ExitStatus exitStatus) +{ + QMessageBox::warning(this, "Backup complete", "Backup complete"); + StandardWidget::enableMenuBar(); + + // Check to see if the update failed + if (backup_process->exitStatus() != QProcess::NormalExit) + { + ui->backupConsole->insertPlainText("\n Update Failed (Crashed): \n The update script has crashed with an error. \n Contact KIPR tech support for assistance if the problem persists \n"); + ui->backupConsole->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor); + } + + ui->backupConsole->append("-------------------------"); + ui->backupConsole->append("-------------------------"); + ui->backupConsole->append("\n"); + ui->backupConsole->append("WARNING - The following files were not backed up:\n"); + // Log only the file paths from errorFiles (if any) + if (!errorFiles.isEmpty()) + { + qDebug() << "Backup error files: "; + foreach (const QString &filePath, errorFiles) + { + qDebug() << filePath; // Logs each file path individually + ui->backupConsole->append(filePath); + ui->backupConsole->append(""); // Add a new line after each file path + } + } + + // Cleanup process + ui->backupConsole->setProcess(0); + delete backup_process; + + // Re-enable buttons + ui->backupoption->setEnabled(true); + ui->restore->setEnabled(true); +} + +void BackupWidget::restoreFinished(int, QProcess::ExitStatus exitStatus) +{ + qDebug() << "Beginning restoreFinished with exitstatus:" << exitStatus; + ui->backupConsole->insertPlainText("Restore Complete"); + QMessageBox::warning(this, "Restore complete", "Restore complete"); + StandardWidget::enableMenuBar(); + + // Check to see if the update failed + if (restore_process->exitStatus() != QProcess::NormalExit) + { + ui->backupConsole->insertPlainText("\n Update Failed (Crashed): \n The Restore script has crashed with an error. \n Contact KIPR tech support for assistance if the problem persists \n"); + ui->backupConsole->moveCursor(QTextCursor::End, QTextCursor::KeepAnchor); + } + + qDebug() << "Before restore cleanup process"; + // Cleanup process + ui->backupConsole->setProcess(0); + + + // Re-enable buttons + ui->backupoption->setEnabled(true); + ui->restore->setEnabled(true); } void BackupWidget::restore() { - QProcess restore_process; - restore_process.startDetached("/bin/sh", QStringList()<< "/home/kipr/wombat-os/Backup/restore.sh"); - restore_process.waitForFinished(); // sets current thread to sleep and waits for Restore end - ui->updateConsole->insertPlainText("Restore Complete"); - QMessageBox::warning(this, "Restore complete", "Restore Complete"); + + // Mount USB drive + if (!this->mountUsb("/dev/sda1", BackupWidget::mountDir) && !this->mountUsb("/dev/sdb1", BackupWidget::mountDir)) + QMessageBox::warning(this, "USB not found", "Failed to mount USB device\n Please insert USB device and try again"); + else + { + // Verify with user that they want to do the backup + if (QMessageBox::question(this, "Restore?", + QString("Are you sure you want to restore your programs?\n This will overwrite all existing programs."), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) + return; + + // Disable buttons + ui->backupoption->setEnabled(false); + ui->restore->setEnabled(false); + StandardWidget::disableMenuBar(); + + ui->BackupRestoreLabel->setText("Restore in progress..."); + restore_process = new QProcess(); + + QString command = QString("/home/kipr/wombat-os/Backup/restore.sh"); + restore_process->start("sudo", QStringList() << "sh" << "-c" << command); + ui->backupConsole->setProcess(restore_process); + + connect(restore_process, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(restoreFinished(int, QProcess::ExitStatus))); + } } /*void BackupWidget::cleardrive() @@ -65,6 +261,48 @@ void BackupWidget::restore() QProcess clear_process; clear_process.startDetached("/bin/sh", QStringList()<< "/home/pi/got2/Backup/ClearDrive.sh"); clear_process.waitForFinished(); // sets current thread to sleep and waits for Clear end - ui->updateConsole->insertPlainText("Backup Complete"); + ui->backupConsole->insertPlainText("Backup Complete"); QMessageBox::warning(this, "complete", "Clear Completed"); }*/ +void BackupWidget::handleStandardOutput() +{ + QString output = backup_process->readAllStandardOutput(); + qDebug() << "Backup output: " << output; + // Optionally display the output in the UI, like a text box or console window + ui->backupConsole->append(output); // Example if you want to display the output in a QTextEdit or similar widget +} + +void BackupWidget::handleStandardError() +{ + QString error = backup_process->readAllStandardError(); + + if (error.contains("Invalid argument")) + { + error.replace("Invalid argument", "Special character found, not backed up."); + } + else if (error.contains("cannot create directory")) + { + error.replace("cannot create directory", "Special character found, not backed up."); + } + + // Regular expression to extract the file path between single quotes or any other pattern + QRegularExpression filePathRegex("/mnt/[^ ]+([^\n]*)"); // Match paths starting with /mnt/ + + QRegularExpressionMatchIterator i = filePathRegex.globalMatch(error); // Find all matches + while (i.hasNext()) + { + QRegularExpressionMatch match = i.next(); + QString filePath = match.captured(0); // Capture the full file path + + // Remove the '/mnt' prefix from the file path + filePath.remove(0, 4); + + // Add the file path to the error list only + if (!filePath.isEmpty()) + { + errorFiles.append(filePath); // Only store the file path, no error message + } + } + qDebug() << "Backup error: " << error; + ui->backupConsole->append(error); // Display the error in a QTextEdit or similar widget +} \ No newline at end of file diff --git a/src/Config.cpp b/src/Config.cpp index 5ca9046d..4c1220af 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -10,7 +10,6 @@ namespace botui { - const QString pathToKISS = "/home/kipr/Documents/KISS/"; // "/home/kipr/Documents/KISS/" - + const QString pathToKISS = "/home/kipr/Documents/KISS/"; // "/home/kipr/Documents/KISS/" + } - diff --git a/src/FileUtils.cpp b/src/FileUtils.cpp index 8db98040..0f423d68 100644 --- a/src/FileUtils.cpp +++ b/src/FileUtils.cpp @@ -8,48 +8,47 @@ bool FileUtils::copy(const QString &path, const QString &newPath) { QFileInfo input(path); - if(!input.exists()) return false; - - if(input.isDir()) { - if(!QDir(newPath).mkpath(input.fileName())) { + if (!input.exists()) + return false; + + if (input.isDir()) + { + if (!QDir(newPath).mkpath(input.fileName())) + { qWarning() << "QDir(newPath).mkpath(input.fileName())" - << "failed. (newPath =" << newPath << ")"; + << "failed. (newPath =" << newPath << ")"; return false; } QFileInfoList entries = QDir(input.absoluteFilePath()).entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); qDebug() << QDir(input.absoluteFilePath()).entryList(QDir::NoDot | QDir::NoDotDot); - foreach(const QFileInfo &entry, entries) { - if(!copy(entry.absoluteFilePath(), newPath + "/" + input.fileName())) { + foreach (const QFileInfo &entry, entries) + { + if (!copy(entry.absoluteFilePath(), newPath + "/" + input.fileName())) + { return false; } } return true; - } else if(input.isFile()) { + } + else if (input.isFile()) + { return QFile::copy(path, newPath + "/" + input.fileName()); } - + return false; } + bool FileUtils::rm(const QString &path) { - QFileInfo input(path); - if(input.isDir()) { - qDebug() << "In directory" << input.absoluteFilePath(); - QFileInfoList entries = QDir(input.absoluteFilePath()).entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); - qDebug() << QDir(input.absoluteFilePath()).entryList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs); - foreach(const QFileInfo &entry, entries) { - qDebug() << "Recursively removing" << entry.absoluteFilePath(); - if(!rm(entry.absoluteFilePath())) { - return false; - } - } - qDebug() << "Removing" << path; - return QDir().rmdir(input.absoluteFilePath()); - } else if(input.isFile()) { - qDebug() << "Removing" << path; - return QFile::remove(path); + QDir dir(path); + + if (dir.removeRecursively()) + { + qDebug() << "Directory removed successfully."; } - - return false; -} \ No newline at end of file + else + { + qDebug() << "Failed to remove directory."; + } +} diff --git a/src/NetworkSettingsWidget.cpp b/src/NetworkSettingsWidget.cpp index 432ef910..0dc3ed78 100644 --- a/src/NetworkSettingsWidget.cpp +++ b/src/NetworkSettingsWidget.cpp @@ -65,6 +65,8 @@ NetworkSettingsWidget::NetworkSettingsWidget(Device *device, QWidget *parent) ui->ConnectButton->setVisible(true); ui->ManageButton->setVisible(true); + // Get Network Mode + QTimer *updateTimer = new QTimer(this); QObject::connect(updateTimer, SIGNAL(timeout()), SLOT(updateInformation())); updateTimer->start(10000); diff --git a/ui/BackupWidget.ui b/ui/BackupWidget.ui index 064c8e68..072f4d4e 100644 --- a/ui/BackupWidget.ui +++ b/ui/BackupWidget.ui @@ -13,36 +13,18 @@ Home - - - 6 - - - 0 - - - 6 - - - 0 - + + + + + + + + - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + 450