mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-28 19:53:42 +08:00
FrogPilot features - Backup and restore FrogPilot/toggles
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import datetime
|
||||
import filecmp
|
||||
import glob
|
||||
import http.client
|
||||
import os
|
||||
import shutil
|
||||
@@ -11,7 +13,8 @@ import urllib.request
|
||||
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.common.numpy_fast import clip, interp, mean
|
||||
from openpilot.common.params_pyx import Params, UnknownKeyName
|
||||
from openpilot.common.params_pyx import Params, ParamKeyType, UnknownKeyName
|
||||
from openpilot.common.time import system_time_valid
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
|
||||
def delete_file(file):
|
||||
@@ -47,6 +50,45 @@ def run_cmd(cmd, success_msg, fail_msg):
|
||||
except Exception as e:
|
||||
print(f"Unexpected error occurred: {e}")
|
||||
|
||||
def backup_directory(backup, destination, success_msg, fail_msg):
|
||||
os.makedirs(destination, exist_ok=True)
|
||||
try:
|
||||
run_cmd(['sudo', 'cp', '-a', os.path.join(backup, '.'), destination], success_msg, fail_msg)
|
||||
except OSError as e:
|
||||
if e.errno == 28:
|
||||
print("Not enough space to perform the backup.")
|
||||
else:
|
||||
print(f"Failed to backup due to unexpected error: {e}")
|
||||
|
||||
def cleanup_backups(directory, limit):
|
||||
backups = sorted(glob.glob(os.path.join(directory, "*_auto")), key=os.path.getmtime, reverse=True)
|
||||
for old_backup in backups[limit:]:
|
||||
subprocess.run(['sudo', 'rm', '-rf', old_backup], check=True)
|
||||
print(f"Deleted oldest backup: {os.path.basename(old_backup)}")
|
||||
|
||||
def backup_frogpilot(build_metadata):
|
||||
backup_path = "/data/backups"
|
||||
cleanup_backups(backup_path, 4)
|
||||
|
||||
branch = build_metadata.channel
|
||||
commit = build_metadata.openpilot.git_commit_date[12:-16]
|
||||
|
||||
backup_dir = f"{backup_path}/{branch}_{commit}_auto"
|
||||
backup_directory(BASEDIR, backup_dir, f"Successfully backed up FrogPilot to {backup_dir}.", f"Failed to backup FrogPilot to {backup_dir}.")
|
||||
|
||||
def backup_toggles(params, params_storage):
|
||||
for key in params.all_keys():
|
||||
if params.get_key_type(key) & ParamKeyType.FROGPILOT_STORAGE:
|
||||
value = params.get(key)
|
||||
if value is not None:
|
||||
params_storage.put(key, value)
|
||||
|
||||
backup_path = "/data/toggle_backups"
|
||||
cleanup_backups(backup_path, 9)
|
||||
|
||||
backup_dir = f"{backup_path}/{datetime.datetime.now().strftime('%Y-%m-%d_%I-%M%p').lower()}_auto"
|
||||
backup_directory("/data/params/d", backup_dir, f"Successfully backed up toggles to {backup_dir}.", f"Failed to backup toggles to {backup_dir}.")
|
||||
|
||||
def convert_params(params, params_storage):
|
||||
def convert_param(key, action_func):
|
||||
try:
|
||||
@@ -71,9 +113,19 @@ def convert_params(params, params_storage):
|
||||
print("Params successfully converted!")
|
||||
params_storage.put_int_nonblocking("ParamConversionVersion", version)
|
||||
|
||||
def frogpilot_boot_functions(params, params_storage):
|
||||
def frogpilot_boot_functions(build_metadata, params, params_storage):
|
||||
convert_params(params, params_storage)
|
||||
|
||||
while not system_time_valid():
|
||||
print("Waiting for system time to become valid...")
|
||||
time.sleep(1)
|
||||
|
||||
try:
|
||||
backup_frogpilot(build_metadata)
|
||||
backup_toggles(params, params_storage)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Backup failed: {e}")
|
||||
|
||||
def setup_frogpilot(build_metadata):
|
||||
remount_root = ['sudo', 'mount', '-o', 'remount,rw', '/']
|
||||
run_cmd(remount_root, "File system remounted as read-write.", "Failed to remount file system.")
|
||||
|
||||
@@ -9,12 +9,13 @@ from openpilot.common.time import system_time_valid
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
|
||||
from openpilot.selfdrive.frogpilot.controls.frogpilot_planner import FrogPilotPlanner
|
||||
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_functions import is_url_pingable
|
||||
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_functions import backup_toggles, is_url_pingable
|
||||
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_variables import FrogPilotVariables
|
||||
|
||||
OFFLINE = log.DeviceState.NetworkType.none
|
||||
|
||||
locks = {
|
||||
"backup_toggles": threading.Lock(),
|
||||
"time_checks": threading.Lock(),
|
||||
"update_frogpilot_params": threading.Lock(),
|
||||
}
|
||||
@@ -58,6 +59,7 @@ def frogpilot_thread():
|
||||
|
||||
params = Params()
|
||||
params_memory = Params("/dev/shm/params")
|
||||
params_storage = Params("/persist/params")
|
||||
|
||||
frogpilot_planner = FrogPilotPlanner()
|
||||
|
||||
@@ -91,6 +93,9 @@ def frogpilot_thread():
|
||||
elif update_toggles:
|
||||
run_thread_with_lock("update_frogpilot_params", locks["update_frogpilot_params"], FrogPilotVariables.update_frogpilot_params, (started,))
|
||||
|
||||
if time_validated and not started:
|
||||
run_thread_with_lock("backup_toggles", locks["backup_toggles"], backup_toggles, (params, params_storage))
|
||||
|
||||
update_toggles = False
|
||||
|
||||
started_previously = started
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
@@ -274,8 +276,172 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
btn->setEnabled(offroad);
|
||||
}
|
||||
}
|
||||
for (FrogPilotButtonsControl *btn : findChildren<FrogPilotButtonsControl *>()) {
|
||||
btn->setEnabled(offroad);
|
||||
}
|
||||
});
|
||||
|
||||
// Backup FrogPilot
|
||||
std::vector<QString> frogpilotBackupOptions{tr("BACKUP"), tr("DELETE"), tr("RESTORE")};
|
||||
FrogPilotButtonsControl *frogpilotBackupBtn = new FrogPilotButtonsControl(tr("FrogPilot Backups"), tr("Backup, delete, or restore your FrogPilot backups."), "", frogpilotBackupOptions);
|
||||
connect(frogpilotBackupBtn, &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([=]() {
|
||||
frogpilotBackupBtn->setEnabled(false);
|
||||
frogpilotBackupBtn->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());
|
||||
frogpilotBackupBtn->setValue(result == 0 ? tr("Success!") : tr("Failed..."));
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
frogpilotBackupBtn->setValue("");
|
||||
frogpilotBackupBtn->setEnabled(true);
|
||||
}).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([=]() {
|
||||
frogpilotBackupBtn->setEnabled(false);
|
||||
frogpilotBackupBtn->setValue(tr("Deleting..."));
|
||||
|
||||
QDir dirToDelete(backupDir.absoluteFilePath(selection));
|
||||
|
||||
frogpilotBackupBtn->setValue(dirToDelete.removeRecursively() ? tr("Deleted!") : tr("Failed..."));
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
frogpilotBackupBtn->setValue("");
|
||||
frogpilotBackupBtn->setEnabled(true);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
} else if (id == 2) {
|
||||
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([=]() {
|
||||
frogpilotBackupBtn->setEnabled(false);
|
||||
frogpilotBackupBtn->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 -l --exclude='.overlay_consistent' " + sourcePath + "/ " + targetPath + "/";
|
||||
int result = std::system(command.c_str());
|
||||
|
||||
if (result == 0) {
|
||||
std::ofstream consistentFile(consistentFilePath);
|
||||
if (consistentFile) {
|
||||
frogpilotBackupBtn->setValue(tr("Restored!"));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
frogpilotBackupBtn->setValue(tr("Rebooting..."));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
consistentFile.close();
|
||||
Hardware::reboot();
|
||||
} else {
|
||||
frogpilotBackupBtn->setValue(tr("Failed..."));
|
||||
}
|
||||
} else {
|
||||
frogpilotBackupBtn->setValue(tr("Failed..."));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
frogpilotBackupBtn->setValue("");
|
||||
frogpilotBackupBtn->setEnabled(true);
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
});
|
||||
addItem(frogpilotBackupBtn);
|
||||
|
||||
// Backup toggles
|
||||
std::vector<QString> toggleBackupOptions{tr("BACKUP"), tr("DELETE"), tr("RESTORE")};
|
||||
FrogPilotButtonsControl *toggleBackupBtn = new FrogPilotButtonsControl(tr("Toggle Backups"), tr("Backup, delete, or restore your toggle backups."), "", toggleBackupOptions);
|
||||
connect(toggleBackupBtn, &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([=]() {
|
||||
toggleBackupBtn->setEnabled(false);
|
||||
toggleBackupBtn->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());
|
||||
toggleBackupBtn->setValue(result == 0 ? tr("Success!") : tr("Failed..."));
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
toggleBackupBtn->setValue("");
|
||||
toggleBackupBtn->setEnabled(true);
|
||||
}).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([=]() {
|
||||
toggleBackupBtn->setEnabled(false);
|
||||
toggleBackupBtn->setValue(tr("Deleting..."));
|
||||
|
||||
QDir dirToDelete(backupDir.absoluteFilePath(selection));
|
||||
|
||||
toggleBackupBtn->setValue(dirToDelete.removeRecursively() ? tr("Deleted!") : tr("Failed..."));
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
toggleBackupBtn->setValue("");
|
||||
toggleBackupBtn->setEnabled(true);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
} else if (id == 2) {
|
||||
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([=]() {
|
||||
toggleBackupBtn->setEnabled(false);
|
||||
toggleBackupBtn->setValue(tr("Restoring..."));
|
||||
|
||||
std::string sourcePath = backupDir.absolutePath().toStdString() + "/" + selection.toStdString() + "/";
|
||||
std::string targetPath = "/data/params/d/";
|
||||
std::string command = "rsync -av --delete -l " + sourcePath + " " + targetPath;
|
||||
int result = std::system(command.c_str());
|
||||
|
||||
if (result == 0) {
|
||||
toggleBackupBtn->setValue(tr("Success!"));
|
||||
updateFrogPilotToggles();
|
||||
} else {
|
||||
toggleBackupBtn->setValue(tr("Failed..."));
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
toggleBackupBtn->setValue("");
|
||||
toggleBackupBtn->setEnabled(true);
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
});
|
||||
addItem(toggleBackupBtn);
|
||||
|
||||
// power buttons
|
||||
QHBoxLayout *power_layout = new QHBoxLayout();
|
||||
power_layout->setSpacing(30);
|
||||
|
||||
@@ -37,7 +37,7 @@ def manager_init() -> None:
|
||||
if build_metadata.release_channel:
|
||||
params.clear_all(ParamKeyType.DEVELOPMENT_ONLY)
|
||||
|
||||
threading.Thread(target=frogpilot_boot_functions, args=(params, params_storage,)).start()
|
||||
threading.Thread(target=frogpilot_boot_functions, args=(build_metadata, params, params_storage,)).start()
|
||||
|
||||
default_params: list[tuple[str, str | bytes]] = [
|
||||
("AlwaysOnDM", "0"),
|
||||
|
||||
Reference in New Issue
Block a user