FrogPilot features - Backup and restore FrogPilot
This commit is contained in:
parent
12f807007a
commit
9b553646fd
@ -66,6 +66,56 @@ class FrogPilotFunctions:
|
||||
except Exception as e:
|
||||
print(f"Unexpected error occurred: {e}")
|
||||
|
||||
@classmethod
|
||||
def backup_frogpilot(cls):
|
||||
frogpilot_backup_directory = "/data/backups"
|
||||
os.makedirs(frogpilot_backup_directory, exist_ok=True)
|
||||
|
||||
auto_backups = sorted(glob.glob(os.path.join(frogpilot_backup_directory, "*_auto")),
|
||||
key=os.path.getmtime, reverse=True)
|
||||
|
||||
for old_backup in auto_backups[4:]:
|
||||
shutil.rmtree(old_backup)
|
||||
print(f"Deleted oldest FrogPilot backup to maintain limit: {os.path.basename(old_backup)}")
|
||||
|
||||
branch = get_short_branch()
|
||||
commit = get_commit_date()[12:-16]
|
||||
backup_folder_name = f"{branch}_{commit}_auto"
|
||||
backup_path = os.path.join(frogpilot_backup_directory, backup_folder_name)
|
||||
|
||||
if not os.path.exists(backup_path):
|
||||
cmd = ['sudo', 'cp', '-a', f"{BASEDIR}", f"{backup_path}/"]
|
||||
cls.run_cmd(cmd, f"Successfully backed up FrogPilot to {backup_folder_name}.", f"Failed to backup FrogPilot to {backup_folder_name}.")
|
||||
|
||||
@classmethod
|
||||
def backup_toggles(cls):
|
||||
params = Params()
|
||||
params_storage = Params("/persist/params")
|
||||
|
||||
all_keys = params.all_keys()
|
||||
for key in all_keys:
|
||||
value = params.get(key)
|
||||
if value is not None:
|
||||
params_storage.put(key, value)
|
||||
|
||||
toggle_backup_directory = "/data/toggle_backups"
|
||||
os.makedirs(toggle_backup_directory, exist_ok=True)
|
||||
|
||||
auto_backups = sorted(glob.glob(os.path.join(toggle_backup_directory, "*_auto")),
|
||||
key=os.path.getmtime, reverse=True)
|
||||
|
||||
for old_backup in auto_backups[9:]:
|
||||
shutil.rmtree(old_backup)
|
||||
print(f"Deleted oldest toggle backup to maintain limit: {os.path.basename(old_backup)}")
|
||||
|
||||
current_datetime = datetime.datetime.now().strftime("%Y-%m-%d_%I-%M%p").lower()
|
||||
backup_folder_name = f"{current_datetime}_auto"
|
||||
backup_path = os.path.join(toggle_backup_directory, backup_folder_name)
|
||||
|
||||
if not os.path.exists(backup_path):
|
||||
cmd = ['sudo', 'cp', '-a', '/data/params/.', f"{backup_path}/"]
|
||||
cls.run_cmd(cmd, f"Successfully backed up toggles to {backup_folder_name}.", f"Failed to backup toggles to {backup_folder_name}.")
|
||||
|
||||
@classmethod
|
||||
def convert_params(cls, params, params_storage, params_tracking):
|
||||
if params.get("InstallDate") == "November 21, 2023 - 02:10PM":
|
||||
|
@ -82,6 +82,9 @@ def frogpilot_thread(frogpilot_toggles):
|
||||
if FrogPilotVariables.toggles_updated:
|
||||
FrogPilotVariables.update_frogpilot_params(started)
|
||||
|
||||
if not started and time_validated:
|
||||
frogpilot_functions.backup_toggles()
|
||||
|
||||
if not time_validated:
|
||||
time_validated = system_time_valid()
|
||||
if not time_validated:
|
||||
|
@ -33,6 +33,18 @@ def frogpilot_boot_functions(frogpilot_functions):
|
||||
print("Waiting for system time to become valid...")
|
||||
time.sleep(1)
|
||||
|
||||
try:
|
||||
frogpilot_functions.backup_frogpilot()
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Failed to backup FrogPilot. Error: {e}")
|
||||
return
|
||||
|
||||
try:
|
||||
frogpilot_functions.backup_toggles()
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Failed to backup toggles. Error: {e}")
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred: {e}")
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
@ -271,6 +273,186 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
});
|
||||
addItem(translateBtn);
|
||||
|
||||
// Backup FrogPilot
|
||||
std::vector<QString> frogpilotBackupOptions{tr("Backup"), tr("Delete"), tr("Restore")};
|
||||
FrogPilotButtonsControl *frogpilotBackup = new FrogPilotButtonsControl(tr("FrogPilot Backups"), tr("Backup, delete, or restore your FrogPilot backups."), "", frogpilotBackupOptions);
|
||||
|
||||
connect(frogpilotBackup, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
|
||||
QDir backupDir("/data/backups");
|
||||
|
||||
if (id == 0) {
|
||||
QString nameSelection = InputDialog::getText(tr("Name your backup"), this, "", false, 1);
|
||||
if (!nameSelection.isEmpty()) {
|
||||
std::thread([=]() {
|
||||
frogpilotBackup->setValue(tr("Backing up..."));
|
||||
|
||||
std::string fullBackupPath = backupDir.absolutePath().toStdString() + "/" + nameSelection.toStdString();
|
||||
|
||||
std::ostringstream commandStream;
|
||||
commandStream << "mkdir -p " << std::quoted(fullBackupPath)
|
||||
<< " && rsync -av /data/openpilot/ " << std::quoted(fullBackupPath + "/");
|
||||
std::string command = commandStream.str();
|
||||
|
||||
int result = std::system(command.c_str());
|
||||
if (result == 0) {
|
||||
std::cout << "Backup successful to " << fullBackupPath << std::endl;
|
||||
frogpilotBackup->setValue(tr("Success!"));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
frogpilotBackup->setValue("");
|
||||
} else {
|
||||
std::cerr << "Backup failed with error code: " << result << std::endl;
|
||||
frogpilotBackup->setValue(tr("Failed..."));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
frogpilotBackup->setValue("");
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
} else if (id == 1) {
|
||||
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
|
||||
QString selection = MultiOptionDialog::getSelection(tr("Select a backup to delete"), backupNames, "", this);
|
||||
if (!selection.isEmpty()) {
|
||||
if (!ConfirmationDialog::confirm(tr("Are you sure you want to delete this backup?"), tr("Delete"), this)) return;
|
||||
std::thread([=]() {
|
||||
frogpilotBackup->setValue(tr("Deleting..."));
|
||||
QDir dirToDelete(backupDir.absoluteFilePath(selection));
|
||||
if (dirToDelete.removeRecursively()) {
|
||||
frogpilotBackup->setValue(tr("Deleted!"));
|
||||
} else {
|
||||
frogpilotBackup->setValue(tr("Failed..."));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
frogpilotBackup->setValue("");
|
||||
}).detach();
|
||||
}
|
||||
} else {
|
||||
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
|
||||
QString selection = MultiOptionDialog::getSelection(tr("Select a restore point"), backupNames, "", this);
|
||||
if (!selection.isEmpty()) {
|
||||
if (!ConfirmationDialog::confirm(tr("Are you sure you want to restore this version of FrogPilot?"), tr("Restore"), this)) return;
|
||||
std::thread([=]() {
|
||||
frogpilotBackup->setValue(tr("Restoring..."));
|
||||
|
||||
std::string sourcePath = backupDir.absolutePath().toStdString() + "/" + selection.toStdString();
|
||||
std::string targetPath = "/data/safe_staging/finalized";
|
||||
std::string consistentFilePath = targetPath + "/.overlay_consistent";
|
||||
|
||||
std::ostringstream commandStream;
|
||||
commandStream << "rsync -av --delete --exclude='.overlay_consistent' " << std::quoted(sourcePath + "/") << " " << std::quoted(targetPath + "/");
|
||||
std::string command = commandStream.str();
|
||||
int result = std::system(command.c_str());
|
||||
|
||||
if (result == 0) {
|
||||
std::cout << "Restore successful from " << sourcePath << " to " << targetPath << std::endl;
|
||||
std::ofstream consistentFile(consistentFilePath);
|
||||
if (consistentFile) {
|
||||
consistentFile.close();
|
||||
std::cout << ".overlay_consistent file created successfully." << std::endl;
|
||||
} else {
|
||||
std::cerr << "Failed to create .overlay_consistent file." << std::endl;
|
||||
frogpilotBackup->setValue(tr("Failed..."));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
frogpilotBackup->setValue("");
|
||||
}
|
||||
Hardware::reboot();
|
||||
} else {
|
||||
std::cerr << "Restore failed with error code: " << result << std::endl;
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
});
|
||||
addItem(frogpilotBackup);
|
||||
|
||||
// Backup toggles
|
||||
std::vector<QString> toggleBackupOptions{tr("Backup"), tr("Delete"), tr("Restore")};
|
||||
FrogPilotButtonsControl *toggleBackup = new FrogPilotButtonsControl(tr("Toggle Backups"), tr("Backup, delete, or restore your toggle backups."), "", toggleBackupOptions);
|
||||
|
||||
connect(toggleBackup, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
|
||||
QDir backupDir("/data/toggle_backups");
|
||||
|
||||
if (id == 0) {
|
||||
QString nameSelection = InputDialog::getText(tr("Name your backup"), this, "", false, 1);
|
||||
if (!nameSelection.isEmpty()) {
|
||||
std::thread([=]() {
|
||||
toggleBackup->setValue(tr("Backing up..."));
|
||||
|
||||
std::string fullBackupPath = backupDir.absolutePath().toStdString() + "/" + nameSelection.toStdString() + "/";
|
||||
|
||||
std::ostringstream commandStream;
|
||||
commandStream << "mkdir -p " << std::quoted(fullBackupPath)
|
||||
<< " && rsync -av /data/params/d/ " << std::quoted(fullBackupPath);
|
||||
std::string command = commandStream.str();
|
||||
|
||||
int result = std::system(command.c_str());
|
||||
if (result == 0) {
|
||||
std::cout << "Backup successful to " << fullBackupPath << std::endl;
|
||||
toggleBackup->setValue(tr("Success!"));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
toggleBackup->setValue("");
|
||||
} else {
|
||||
std::cerr << "Backup failed with error code: " << result << std::endl;
|
||||
toggleBackup->setValue(tr("Failed..."));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
toggleBackup->setValue("");
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
} else if (id == 1) {
|
||||
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
|
||||
QString selection = MultiOptionDialog::getSelection(tr("Select a backup to delete"), backupNames, "", this);
|
||||
if (!selection.isEmpty()) {
|
||||
if (!ConfirmationDialog::confirm(tr("Are you sure you want to delete this backup?"), tr("Delete"), this)) return;
|
||||
std::thread([=]() {
|
||||
toggleBackup->setValue(tr("Deleting..."));
|
||||
QDir dirToDelete(backupDir.absoluteFilePath(selection));
|
||||
if (dirToDelete.removeRecursively()) {
|
||||
toggleBackup->setValue(tr("Deleted!"));
|
||||
} else {
|
||||
toggleBackup->setValue(tr("Failed..."));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
toggleBackup->setValue("");
|
||||
}).detach();
|
||||
}
|
||||
} else {
|
||||
QStringList backupNames = backupDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
|
||||
QString selection = MultiOptionDialog::getSelection(tr("Select a restore point"), backupNames, "", this);
|
||||
if (!selection.isEmpty()) {
|
||||
if (!ConfirmationDialog::confirm(tr("Are you sure you want to restore this toggle backup?"), tr("Restore"), this)) return;
|
||||
std::thread([=]() {
|
||||
toggleBackup->setValue(tr("Restoring..."));
|
||||
|
||||
std::string sourcePath = backupDir.absolutePath().toStdString() + "/" + selection.toStdString() + "/";
|
||||
std::string targetPath = "/data/params/d/";
|
||||
|
||||
std::ostringstream commandStream;
|
||||
commandStream << "rsync -av --delete " << std::quoted(sourcePath) << " " << std::quoted(targetPath);
|
||||
std::string command = commandStream.str();
|
||||
|
||||
int result = std::system(command.c_str());
|
||||
|
||||
if (result == 0) {
|
||||
std::cout << "Restore successful from " << sourcePath << " to " << targetPath << std::endl;
|
||||
toggleBackup->setValue(tr("Success!"));
|
||||
updateFrogPilotToggles();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
toggleBackup->setValue("");
|
||||
} else {
|
||||
std::cerr << "Restore failed with error code: " << result << std::endl;
|
||||
toggleBackup->setValue(tr("Failed..."));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
toggleBackup->setValue("");
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
});
|
||||
addItem(toggleBackup);
|
||||
|
||||
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
|
||||
for (auto btn : findChildren<ButtonControl *>()) {
|
||||
btn->setEnabled(offroad);
|
||||
|
Loading…
x
Reference in New Issue
Block a user