Files
StarPilot/selfdrive/frogpilot/frogpilot_functions.py
T
2025-01-28 12:53:28 -07:00

231 lines
9.7 KiB
Python

#!/usr/bin/env python3
from pathlib import Path
import datetime
import filecmp
import glob
import json
import shutil
import subprocess
import tarfile
import threading
import time
from openpilot.common.basedir import BASEDIR
from openpilot.common.params_pyx import ParamKeyType
from openpilot.common.time import system_time_valid
from openpilot.system.hardware import HARDWARE
from openpilot.selfdrive.frogpilot.assets.theme_manager import HOLIDAY_THEME_PATH, ThemeManager
from openpilot.selfdrive.frogpilot.frogpilot_utilities import run_cmd
from openpilot.selfdrive.frogpilot.frogpilot_variables import MODELS_PATH, THEME_SAVE_PATH, FrogPilotVariables, get_frogpilot_toggles, params
def backup_directory(backup, destination, success_message, fail_message, minimum_backup_size=0, compressed=False):
if compressed:
destination_compressed = destination.with_suffix(".tar.gz")
if destination_compressed.exists():
print("Backup already exists. Aborting")
return
in_progress_destination = destination.parent / (destination.name + "_in_progress")
run_cmd(["sudo", "rsync", "-avq", f"{backup}/.", in_progress_destination], "", fail_message)
in_progress_compressed = destination_compressed.with_suffix(".tar.gz_in_progress")
with tarfile.open(in_progress_compressed, "w:gz") as tar:
tar.add(in_progress_destination, arcname=destination.name)
run_cmd(["sudo", "rm", "-rf", in_progress_destination], success_message, fail_message)
in_progress_compressed.rename(destination_compressed)
compressed_backup_size = destination_compressed.stat().st_size
if minimum_backup_size == 0 or compressed_backup_size < minimum_backup_size:
params.put_int("MinimumBackupSize", compressed_backup_size)
else:
if destination.exists():
print("Backup already exists. Aborting")
return
run_cmd(["sudo", "rsync", "-avq", f"{backup}/.", destination], success_message, fail_message)
def cleanup_backups(directory, limit, success_message, fail_message, compressed=False):
directory.mkdir(parents=True, exist_ok=True)
backups = sorted(directory.glob("*_auto*"), key=lambda x: x.stat().st_mtime, reverse=True)
for backup in backups[:]:
if backup.name.endswith("_in_progress") or backup.name.endswith("_in_progress.tar.gz"):
run_cmd(["sudo", "rm", "-rf", backup], "", fail_message)
backups.remove(backup)
for oldest_backup in backups[limit:]:
if oldest_backup.is_dir():
run_cmd(["sudo", "rm", "-rf", oldest_backup], success_message, fail_message)
else:
run_cmd(["sudo", "rm", oldest_backup], success_message, fail_message)
def backup_frogpilot(build_metadata):
backup_path = Path("/data/backups")
maximum_backups = 5
cleanup_backups(backup_path, maximum_backups, "Successfully cleaned up old FrogPilot backups", "Failed to cleanup old FrogPilot backups", compressed=True)
_, _, free = shutil.disk_usage(backup_path)
minimum_backup_size = params.get_int("MinimumBackupSize")
if free > minimum_backup_size * maximum_backups:
directory = Path(BASEDIR)
destination_directory = backup_path / f"{build_metadata.channel}_{build_metadata.openpilot.git_commit_date[12:-16]}_auto"
backup_directory(directory, destination_directory, f"Successfully backed up FrogPilot to {destination_directory}", f"Failed to backup FrogPilot to {destination_directory}", minimum_backup_size, compressed=True)
def backup_toggles(params_storage):
for key in params.all_keys():
if params.get_key_type(key) & ParamKeyType.PERSISTENT:
value = params.get(key)
if value is not None:
params_storage.put(key, value)
backup_path = Path("/data/toggle_backups")
maximum_backups = 10
cleanup_backups(backup_path, maximum_backups, "Successfully cleaned up old toggle backups", "Failed to cleanup old toggle backups")
directory = Path("/data/params/d")
destination_directory = backup_path / f"{datetime.datetime.now().strftime('%Y-%m-%d_%I-%M%p').lower()}_auto"
backup_directory(directory, destination_directory, f"Successfully backed up toggles to {destination_directory}", f"Failed to backup toggles to {destination_directory}")
def convert_params(params_storage):
print("Starting to convert params")
def update_values(keys, mappings):
for key in keys:
for original, replacement in mappings.items():
if params.get(key, encoding='utf-8') == original:
params.put(key, replacement)
if params_storage.get(key, encoding='utf-8') == original:
params_storage.put(key, replacement)
priority_keys = ["SLCPriority1", "SLCPriority2", "SLCPriority3"]
update_values(priority_keys, {"Offline Maps": "Map Data"})
bottom_key = ["StartupMessageBottom"]
update_values(bottom_key, {"so I do what I want 🐸": "Human-tested, frog-approved 🐸"})
top_key = ["StartupMessageTop"]
update_values(top_key, {"Hippity hoppity this is my property": "Hop in and buckle up!"})
models = [
("Certified Herbalist", "CertifiedHerbalistDrives", "CertifiedHerbalistScore"),
("Dissolved Oxygen", "DissolvedOxygenDrives", "DissolvedOxygenScore"),
("Duck Amigo", "DuckAmigoDrives", "DuckAmigoScore"),
("Los Angeles", "LosAngelesDrives", "LosAngelesScore"),
("North Dakota", "NorthDakotaDrives", "NorthDakotaScore"),
("Notre Dame", "NotreDameDrives", "NotreDameScore"),
("Radical Turtle", "RadicalTurtleDrives", "RadicalTurtleScore"),
("Recertified Herbalist", "RecertifiedHerbalistDrives", "RecertifiedHerbalistScore"),
("SecretGoodOpenpilot", "SecretGoodOpenpilotDrives", "SecretGoodOpenpilotScore"),
("WD-40", "WD40Drives", "WD40Score")
]
try:
model_drives_and_scores = json.loads(params.get("ModelDrivesAndScores") or "{}")
except Exception as error:
print(f"Error parsing ModelDrivesAndScores JSON: {error}. Initializing empty structure")
model_drives_and_scores = {}
for model, drives_param, score_param in models:
drives = params.get_int(drives_param)
score = params.get_int(score_param)
if drives > 0 or score > 0:
model_drives_and_scores[model] = {
"Drives": drives,
"Score": score
}
params.remove(drives_param)
params_storage.remove(drives_param)
params.remove(score_param)
params_storage.remove(score_param)
params.put("ModelDrivesAndScores", json.dumps(model_drives_and_scores))
print("Param conversion completed")
def frogpilot_boot_functions(build_metadata, params_storage):
if params.get_bool("HasAcceptedTerms"):
params_storage.clear_all()
source = THEME_SAVE_PATH / "distance_icons"
destination = THEME_SAVE_PATH / "theme_packs"
if source.exists():
for item in source.iterdir():
if item.is_dir():
destination_path = destination / item.name / "distance_icons"
destination_path.mkdir(parents=True, exist_ok=True)
for sub_item in item.iterdir():
destination_file = destination_path / sub_item.name
if destination_file.exists():
destination_file.unlink()
shutil.move(sub_item, destination_path)
if not any(item.iterdir()):
item.rmdir()
if not any(source.iterdir()):
source.rmdir()
FrogPilotVariables().update(holiday_theme="stock", started=False)
ThemeManager().update_active_theme(time_validated=system_time_valid(), frogpilot_toggles=get_frogpilot_toggles(), boot_run=True)
def backup_thread():
while not system_time_valid():
print("Waiting for system time to become valid...")
time.sleep(1)
subprocess.run(["pkill", "-SIGUSR1", "-f", "system.updated.updated"], check=False)
backup_frogpilot(build_metadata)
backup_toggles(params_storage)
threading.Thread(target=backup_thread, daemon=True).start()
def setup_frogpilot(build_metadata):
run_cmd(["sudo", "mount", "-o", "remount,rw", "/persist"], "Successfully remounted /persist as read-write", "Failed to remount /persist")
Path(MODELS_PATH).mkdir(parents=True, exist_ok=True)
Path(THEME_SAVE_PATH).mkdir(parents=True, exist_ok=True)
for source_suffix, destination_suffix in [
("world_frog_day/colors", "theme_packs/frog/colors"),
("world_frog_day/distance_icons", "theme_packs/frog-animated/distance_icons"),
("world_frog_day/icons", "theme_packs/frog-animated/icons"),
("world_frog_day/signals", "theme_packs/frog/signals"),
("world_frog_day/sounds", "theme_packs/frog/sounds"),
]:
source = Path(HOLIDAY_THEME_PATH) / source_suffix
destination = Path(THEME_SAVE_PATH) / destination_suffix
destination.mkdir(parents=True, exist_ok=True)
shutil.copytree(source, destination, dirs_exist_ok=True)
for source_suffix, destination_suffix in [
("world_frog_day/steering_wheel/wheel.png", "steering_wheels/frog.png"),
]:
source = Path(HOLIDAY_THEME_PATH) / source_suffix
destination = Path(THEME_SAVE_PATH) / destination_suffix
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, destination)
run_cmd(["sudo", "mount", "-o", "remount,rw", "/"], "Successfully remounted the file system as read-write", "Failed to remount the file system")
boot_logo_location = Path("/usr/comma/bg.jpg")
frogpilot_boot_logo = Path(__file__).parent / "assets/other_images/frogpilot_boot_logo.png"
if not filecmp.cmp(frogpilot_boot_logo, boot_logo_location, shallow=False):
run_cmd(["sudo", "cp", frogpilot_boot_logo, boot_logo_location], "Successfully replaced boot logo", "Failed to replace boot logo")
if build_metadata.channel == "FrogPilot-Development":
subprocess.run(["sudo", "python3", "/persist/frogsgomoo.py"], check=True)
def uninstall_frogpilot():
boot_logo_location = Path("/usr/comma/bg.jpg")
stock_boot_logo = Path(__file__).parent / "assets/other_images/original_bg.jpg"
run_cmd(["sudo", "cp", stock_boot_logo, boot_logo_location], "Successfully restored the stock boot logo", "Failed to restore the stock boot logo")
HARDWARE.uninstall()