FrogPilot features - Backup and restore FrogPilot/toggles

This commit is contained in:
FrogAi
2024-06-09 07:56:18 -07:00
parent aa6274f2b1
commit f28f1d1509
4 changed files with 234 additions and 0 deletions
@@ -1,4 +1,6 @@
import datetime
import filecmp
import glob
import os
import shutil
import subprocess
@@ -18,6 +20,52 @@ def run_cmd(cmd, success_msg, fail_msg):
print(f"Unexpected error occurred: {e}")
class FrogPilotFunctions:
@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)}")
build_metadata = get_build_metadata()
branch = build_metadata.channel
commit = build_metadata.openpilot.git_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}/"]
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")
for key in params.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}/"]
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):
def convert_param(key, action_func):
+3
View File
@@ -78,6 +78,9 @@ def frogpilot_thread(frogpilot_toggles):
elif update_toggles:
FrogPilotVariables.update_frogpilot_params(started)
if time_validated and not started:
frogpilot_functions.backup_toggles()
update_toggles = False
if now.second == 0 or not time_validated:
+159
View File
@@ -1,5 +1,7 @@
#include <cassert>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
@@ -276,6 +278,163 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
}
});
// 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::string command = "mkdir -p " + fullBackupPath + " && rsync -av /data/openpilot/ " + fullBackupPath + "/";
int result = std::system(command.c_str());
if (result == 0) {
frogpilotBackup->setValue(tr("Success!"));
} else {
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::string command = "rsync -av --delete --exclude='.overlay_consistent' " + sourcePath + "/ " + targetPath + "/";
int result = std::system(command.c_str());
if (result == 0) {
std::ofstream consistentFile(consistentFilePath);
if (consistentFile) {
consistentFile.close();
} else {
frogpilotBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
return;
}
Hardware::reboot();
} else {
frogpilotBackup->setValue(tr("Failed..."));
std::this_thread::sleep_for(std::chrono::seconds(3));
frogpilotBackup->setValue("");
}
}).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::string command = "mkdir -p " + fullBackupPath + " && rsync -av /data/params/d/ " + fullBackupPath;
int result = std::system(command.c_str());
if (result == 0) {
toggleBackup->setValue(tr("Success!"));
} else {
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::string command = "rsync -av --delete " + sourcePath + " " + targetPath;
int result = std::system(command.c_str());
if (result == 0) {
toggleBackup->setValue(tr("Success!"));
updateFrogPilotToggles();
} else {
toggleBackup->setValue(tr("Failed..."));
}
std::this_thread::sleep_for(std::chrono::seconds(3));
toggleBackup->setValue("");
}).detach();
}
}
});
addItem(toggleBackup);
// power buttons
QHBoxLayout *power_layout = new QHBoxLayout();
power_layout->setSpacing(30);
+24
View File
@@ -2,7 +2,10 @@
import datetime
import os
import signal
import subprocess
import sys
import threading
import time
import traceback
from cereal import log
@@ -16,12 +19,33 @@ from openpilot.system.manager.process import ensure_running
from openpilot.system.manager.process_config import managed_processes
from openpilot.system.athena.registration import register, UNREGISTERED_DONGLE_ID
from openpilot.common.swaglog import cloudlog, add_file_handler
from openpilot.common.time import system_time_valid
from openpilot.system.version import get_build_metadata, terms_version, training_version
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_functions import FrogPilotFunctions
def frogpilot_boot_functions(frogpilot_functions):
while not system_time_valid():
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
def manager_init(frogpilot_functions) -> None:
frogpilot_boot = threading.Thread(target=frogpilot_boot_functions, args=(frogpilot_functions,))
frogpilot_boot.start()
save_bootlog()
build_metadata = get_build_metadata()