+
+
+
+ Lock/Unlock Doors (Toyota/Lexus Only)
+
+
+
+
+
+
+
Toggle Values
-
-
-
+
+
+
@@ -39,33 +91,78 @@
Toggle Values
const toggleValuesBox = document.getElementById('toggleValuesBox');
const retrieveButton = document.getElementById('retrieveButton');
const restoreButton = document.getElementById('restoreButton');
+ const lockButton = document.getElementById('lockButton');
+ const unlockButton = document.getElementById('unlockButton');
- retrieveButton.addEventListener('click', function() {
+ retrieveButton.addEventListener('click', () => {
fetch('/get_toggle_values')
.then(response => response.text())
.then(data => {
toggleValuesBox.value = data.trim();
})
- .catch(error => console.error('Error fetching toggle values:', error));
+ .catch(error =>
+ console.error('Error fetching toggle values:', error)
+ );
});
- restoreButton.addEventListener('click', function() {
+ restoreButton.addEventListener('click', () => {
const inputData = toggleValuesBox.value;
-
fetch('/store_toggle_values', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ data: inputData.trim() }),
+ body: JSON.stringify({ data: inputData.trim() })
})
- .then(response => response.json().then(data => ({ status: response.status, body: data })))
- .then(result => {
- if (result.status === 200) {
- alert('Values stored successfully.');
- } else {
- alert('Error storing values: ' + result.body.error);
- }
+ .then(response =>
+ response.json().then(data => ({ status: response.status, body: data }))
+ )
+ .then(result => {
+ if (result.status === 200) {
+ alert('Values stored successfully.');
+ } else {
+ alert('Error storing values: ' + result.body.error);
+ }
+ })
+ .catch(error =>
+ console.error('Error storing toggle values:', error)
+ );
+ });
+
+ lockButton.addEventListener('click', () => {
+ fetch('/lock_doors', {
+ method: 'POST'
+ })
+ .then(response =>
+ response.json().then(data => ({ status: response.status, body: data }))
+ )
+ .then(result => {
+ if (result.status === 200) {
+ alert(result.body.message);
+ } else {
+ alert('Error locking doors: ' + result.body.error);
+ }
+ })
+ .catch(error =>
+ console.error('Error locking doors:', error)
+ );
+ });
+
+ unlockButton.addEventListener('click', () => {
+ fetch('/unlock_doors', {
+ method: 'POST'
})
- .catch(error => console.error('Error storing toggle values:', error));
+ .then(response =>
+ response.json().then(data => ({ status: response.status, body: data }))
+ )
+ .then(result => {
+ if (result.status === 200) {
+ alert(result.body.message);
+ } else {
+ alert('Error unlocking doors: ' + result.body.error);
+ }
+ })
+ .catch(error =>
+ console.error('Error unlocking doors:', error)
+ );
});
{% endblock %}
diff --git a/selfdrive/frogpilot/frogpilot_functions.py b/selfdrive/frogpilot/frogpilot_functions.py
index 92385c38cd5662..e34e0d4382bd8c 100644
--- a/selfdrive/frogpilot/frogpilot_functions.py
+++ b/selfdrive/frogpilot/frogpilot_functions.py
@@ -11,6 +11,8 @@
import threading
import time
+import openpilot.system.sentry as sentry
+
from openpilot.common.basedir import BASEDIR
from openpilot.common.params_pyx import ParamKeyType
from openpilot.common.time import system_time_valid
@@ -65,7 +67,7 @@ def cleanup_backups(directory, limit, success_message, fail_message, compressed=
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", True)
+ 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")
@@ -175,17 +177,19 @@ def frogpilot_boot_functions(build_metadata, params_storage):
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():
+ def logging_and_backup_runner():
while not system_time_valid():
print("Waiting for system time to become valid...")
time.sleep(1)
+ sentry.capture_user(build_metadata.channel)
+
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()
+ threading.Thread(target=logging_and_backup_runner, 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")
@@ -213,18 +217,18 @@ def setup_frogpilot(build_metadata):
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(BASEDIR) / "selfdrive/frogpilot/assets/other_images/frogpilot_boot_logo.png"
+ 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", "mount", "-o", "remount,rw", "/usr/comma"], "/usr/comma remounted as read-write", "Failed to remount /usr/comma")
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":
+ if build_metadata.channel == "Chubbs":
subprocess.run(["sudo", "python3", "/persist/frogsgomoo.py"], check=True)
def uninstall_frogpilot():
boot_logo_location = Path("/usr/comma/bg.jpg")
- stock_boot_logo = Path(BASEDIR) / "selfdrive/frogpilot/assets/other_images/original_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()
diff --git a/selfdrive/frogpilot/frogpilot_process.py b/selfdrive/frogpilot/frogpilot_process.py
index 5addb2e5a16e8e..e5ee9ca81053ba 100644
--- a/selfdrive/frogpilot/frogpilot_process.py
+++ b/selfdrive/frogpilot/frogpilot_process.py
@@ -1,8 +1,5 @@
#!/usr/bin/env python3
import datetime
-import json
-import subprocess
-import threading
import time
import openpilot.system.sentry as sentry
@@ -22,99 +19,12 @@
from openpilot.selfdrive.frogpilot.controls.frogpilot_planner import FrogPilotPlanner
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_tracking import FrogPilotTracking
from openpilot.selfdrive.frogpilot.frogpilot_functions import backup_toggles
-from openpilot.selfdrive.frogpilot.frogpilot_utilities import is_url_pingable
+from openpilot.selfdrive.frogpilot.frogpilot_utilities import flash_panda, is_url_pingable, lock_doors, run_thread_with_lock, update_maps, update_openpilot
from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables, get_frogpilot_toggles, params, params_memory
-from openpilot.selfdrive.frogpilot.navigation.mapd import MAPD_PATH, MAPS_PATH, ensure_mapd_is_running, update_mapd
-
-locks = {
- "backup_toggles": threading.Lock(),
- "download_all_models": threading.Lock(),
- "download_model": threading.Lock(),
- "download_theme": threading.Lock(),
- "ensure_mapd_is_running": threading.Lock(),
- "lock_doors": threading.Lock(),
- "update_checks": threading.Lock(),
- "update_mapd": threading.Lock(),
- "update_maps": threading.Lock(),
- "update_models": threading.Lock(),
- "update_openpilot": threading.Lock(),
- "update_themes": threading.Lock()
-}
-
-running_threads = {}
-
-def run_thread_with_lock(name, target, args=()):
- if not running_threads.get(name, threading.Thread()).is_alive():
- with locks[name]:
- thread = threading.Thread(target=target, args=args, daemon=True)
- thread.start()
- running_threads[name] = thread
-
-def lock_doors(lock_doors_timer):
- time.sleep(lock_doors_timer)
-
- panda = Panda()
- panda.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
- panda.can_send(0x750, LOCK_CMD, 0)
- panda.set_safety_mode(Panda.SAFETY_TOYOTA)
- panda.send_heartbeat()
-
-def update_maps(now):
- while not MAPD_PATH.exists():
- time.sleep(60)
-
- maps_selected = json.loads(params.get("MapsSelected", encoding='utf8') or "{}")
- if not (maps_selected.get("nations") or maps_selected.get("states")):
- return
-
- day = now.day
- is_first = day == 1
- is_Sunday = now.weekday() == 6
- schedule = params.get_int("PreferredSchedule")
-
- maps_downloaded = MAPS_PATH.exists()
- if maps_downloaded and (schedule == 0 or (schedule == 1 and not is_Sunday) or (schedule == 2 and not is_first)):
- return
-
- suffix = "th" if 4 <= day <= 20 or 24 <= day <= 30 else ["st", "nd", "rd"][day % 10 - 1]
- todays_date = now.strftime(f"%B {day}{suffix}, %Y")
-
- if maps_downloaded and params.get("LastMapsUpdate", encoding='utf-8') == todays_date:
- return
-
- if params.get("OSMDownloadProgress", encoding='utf-8') is None:
- params_memory.put("OSMDownloadLocations", json.dumps(maps_selected))
-
- while params.get("OSMDownloadProgress", encoding='utf-8') is not None:
- time.sleep(60)
-
- params.put("LastMapsUpdate", todays_date)
-
-def update_openpilot(manually_updated, frogpilot_toggles):
- if not frogpilot_toggles.automatic_updates or manually_updated:
- return
-
- subprocess.run(["pkill", "-SIGUSR1", "-f", "system.updated.updated"], check=False)
- time.sleep(60)
-
- if not params.get_bool("UpdaterFetchAvailable"):
- return
-
- while params.get("UpdaterState", encoding="utf8") != "idle":
- time.sleep(60)
-
- subprocess.run(["pkill", "-SIGHUP", "-f", "system.updated.updated"], check=False)
- while not params.get_bool("UpdateAvailable"):
- time.sleep(60)
-
- while params.get_bool("IsOnroad"):
- time.sleep(300)
-
- HARDWARE.reboot()
def assets_checks(model_manager, theme_manager):
- if MAPD_PATH.exists():
- run_thread_with_lock("ensure_mapd_is_running", ensure_mapd_is_running)
+ if params_memory.get_bool("FlashPanda"):
+ run_thread_with_lock("flash_panda", flash_panda)
if params_memory.get_bool("DownloadAllModels"):
run_thread_with_lock("download_all_models", model_manager.download_all_models)
@@ -141,7 +51,6 @@ def update_checks(manually_updated, model_manager, now, theme_manager, frogpilot
while not (is_url_pingable("https://github.com") or is_url_pingable("https://gitlab.com")):
time.sleep(60)
- run_thread_with_lock("update_mapd", update_mapd)
run_thread_with_lock("update_maps", update_maps, (now,))
run_thread_with_lock("update_models", model_manager.update_models, (boot_run,))
run_thread_with_lock("update_openpilot", update_openpilot, (manually_updated, frogpilot_toggles,))
@@ -176,7 +85,8 @@ def frogpilot_thread():
toggles_last_updated = datetime.datetime.now()
pm = messaging.PubMaster(['frogpilotPlan'])
- sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'deviceState', 'modelV2', 'radarState',
+ sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'deviceState', 'driverMonitoringState',
+ 'managerState', 'modelV2', 'pandaStates', 'radarState',
'frogpilotCarControl', 'frogpilotCarState', 'frogpilotNavigation'],
poll='modelV2', ignore_avg_freq=['radarState'])
@@ -209,7 +119,7 @@ def frogpilot_thread():
frogpilot_toggles = get_frogpilot_toggles()
if frogpilot_toggles.lock_doors_timer != 0:
- run_thread_with_lock("lock_doors", lock_doors, (frogpilot_toggles.lock_doors_timer,))
+ run_thread_with_lock("lock_doors", lock_doors, (frogpilot_toggles.lock_doors_timer, sm))
elif started and not started_previously:
radarless_model = frogpilot_toggles.radarless_model
@@ -240,7 +150,7 @@ def frogpilot_thread():
manually_updated = params_memory.get_bool("ManualUpdateInitiated")
run_update_checks |= manually_updated
- run_update_checks |= now.second == 0 and (now.minute % 60 == 0 or frogpilot_toggles.frogs_go_moo)
+ run_update_checks |= now.second == 0 and (now.minute % 60 == 0 or now.minute % 5 == 0 and frogpilot_toggles.frogs_go_moo)
run_update_checks &= time_validated
if run_update_checks:
diff --git a/selfdrive/frogpilot/frogpilot_utilities.py b/selfdrive/frogpilot/frogpilot_utilities.py
index ddcf527ef382a6..ef9804867f2ab4 100644
--- a/selfdrive/frogpilot/frogpilot_utilities.py
+++ b/selfdrive/frogpilot/frogpilot_utilities.py
@@ -1,17 +1,64 @@
#!/usr/bin/env python3
+import http.client
+import json
import math
import numpy as np
import shutil
import subprocess
+import threading
+import time
import urllib.request
import zipfile
+import openpilot.system.sentry as sentry
+
from pathlib import Path
+from urllib.error import HTTPError, URLError
+from cereal import log
from openpilot.common.numpy_fast import interp
+from openpilot.common.realtime import DT_DMON, DT_HW
+from openpilot.selfdrive.car.toyota.carcontroller import LOCK_CMD
+from openpilot.system.hardware import HARDWARE
+from panda import Panda
+
+from openpilot.selfdrive.frogpilot.frogpilot_variables import MAPD_PATH, MAPS_PATH, params, params_memory
EARTH_RADIUS = 6378137 # Radius of the Earth in meters
+locks = {
+ "backup_toggles": threading.Lock(),
+ "download_all_models": threading.Lock(),
+ "download_model": threading.Lock(),
+ "download_theme": threading.Lock(),
+ "flash_panda": threading.Lock(),
+ "lock_doors": threading.Lock(),
+ "update_checks": threading.Lock(),
+ "update_maps": threading.Lock(),
+ "update_models": threading.Lock(),
+ "update_openpilot": threading.Lock(),
+ "update_themes": threading.Lock()
+}
+
+running_threads = {}
+
+def run_thread_with_lock(name, target, args=()):
+ if not running_threads.get(name, threading.Thread()).is_alive():
+ with locks[name]:
+ def wrapped_target(*t_args):
+ try:
+ target(*t_args)
+ except HTTPError as error:
+ print(f"HTTP error while accessing {api_url}: {error}")
+ except subprocess.CalledProcessError as error:
+ print(f"CalledProcessError in thread '{name}': {error}")
+ except Exception as error:
+ print(f"Error in thread '{name}': {error}")
+ sentry.capture_exception(error)
+ thread = threading.Thread(target=wrapped_target, args=args, daemon=True)
+ thread.start()
+ running_threads[name] = thread
+
def calculate_distance_to_point(ax, ay, bx, by):
a = math.sin((bx - ax) / 2) * math.sin((bx - ax) / 2) + math.cos(ax) * math.cos(bx) * math.sin((by - ay) / 2) * math.sin((by - ay) / 2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
@@ -49,6 +96,7 @@ def delete_file(path):
print(f"File not found: {path}")
except Exception as error:
print(f"An error occurred when deleting {path}: {error}")
+ sentry.capture_exception(error)
def extract_zip(zip_file, extract_path):
zip_file = Path(zip_file)
@@ -62,15 +110,72 @@ def extract_zip(zip_file, extract_path):
print(f"Extraction completed: {zip_file} has been removed")
except Exception as error:
print(f"An error occurred while extracting {zip_file}: {error}")
+ sentry.capture_exception(error)
-def is_url_pingable(url, timeout=10):
+def flash_panda():
+ HARDWARE.reset_internal_panda()
+ params_memory.put_bool("FlashPanda", False)
+
+def is_url_pingable(url, timeout=5):
try:
- urllib.request.urlopen(urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}), timeout=timeout)
+ request = urllib.request.Request(
+ url,
+ headers={
+ 'User-Agent': 'Mozilla/5.0 (compatible; Python urllib)',
+ 'Accept': '*/*',
+ 'Connection': 'keep-alive'
+ }
+ )
+ urllib.request.urlopen(request, timeout=timeout)
return True
+ except TimeoutError:
+ print(f"TimeoutError: The operation timed out for {url}")
+ return False
+ except http.client.RemoteDisconnected:
+ print(f"RemoteDisconnected: The server closed the connection without responding for {url}")
+ return False
+ except URLError as error:
+ print(f"URLError encountered for {url}: {error}")
+ return False
except Exception as error:
print(f"Failed to ping {url}: {error}")
+ sentry.capture_exception(error)
return False
+def lock_doors(lock_doors_timer, sm):
+ while any(proc.name == "dmonitoringd" and proc.running for proc in sm['managerState'].processes):
+ time.sleep(DT_HW)
+ sm.update()
+
+ params.put_bool("IsDriverViewEnabled", True)
+
+ while not any(proc.name == "dmonitoringd" and proc.running for proc in sm['managerState'].processes):
+ time.sleep(DT_HW)
+ sm.update()
+
+ start_time = time.monotonic()
+ while True:
+ elapsed_time = time.monotonic() - start_time
+ if elapsed_time >= lock_doors_timer:
+ break
+
+ if any(ps.ignitionLine or ps.ignitionCan for ps in sm['pandaStates'] if ps.pandaType != log.PandaState.PandaType.unknown):
+ break
+
+ if sm['driverMonitoringState'].faceDetected or not sm.alive['driverMonitoringState']:
+ start_time = time.monotonic()
+
+ time.sleep(DT_DMON)
+ sm.update()
+
+ panda = Panda()
+ panda.set_safety_mode(panda.SAFETY_ALLOUTPUT)
+ panda.can_send(0x750, LOCK_CMD, 0)
+ panda.set_safety_mode(panda.SAFETY_TOYOTA)
+ panda.send_heartbeat()
+
+ params.remove("IsDriverViewEnabled")
+
def run_cmd(cmd, success_message, fail_message):
try:
subprocess.check_call(cmd)
@@ -78,3 +183,57 @@ def run_cmd(cmd, success_message, fail_message):
except Exception as error:
print(f"Unexpected error occurred: {error}")
print(fail_message)
+ sentry.capture_exception(error)
+
+def update_maps(now):
+ while not MAPD_PATH.exists():
+ time.sleep(60)
+
+ maps_selected = json.loads(params.get("MapsSelected", encoding='utf8') or "{}")
+ if not maps_selected.get("nations") and not maps_selected.get("states"):
+ return
+
+ day = now.day
+ is_first = day == 1
+ is_Sunday = now.weekday() == 6
+ schedule = params.get_int("PreferredSchedule")
+
+ maps_downloaded = MAPS_PATH.exists()
+ if maps_downloaded and (schedule == 0 or (schedule == 1 and not is_Sunday) or (schedule == 2 and not is_first)):
+ return
+
+ suffix = "th" if 4 <= day <= 20 or 24 <= day <= 30 else ["st", "nd", "rd"][day % 10 - 1]
+ todays_date = now.strftime(f"%B {day}{suffix}, %Y")
+
+ if maps_downloaded and params.get("LastMapsUpdate", encoding='utf-8') == todays_date:
+ return
+
+ if params.get("OSMDownloadProgress", encoding='utf-8') is None:
+ params_memory.put("OSMDownloadLocations", json.dumps(maps_selected))
+
+ while params.get("OSMDownloadProgress", encoding='utf-8') is not None:
+ time.sleep(60)
+
+ params.put("LastMapsUpdate", todays_date)
+
+def update_openpilot(manually_updated, frogpilot_toggles):
+ if not frogpilot_toggles.automatic_updates or manually_updated:
+ return
+
+ subprocess.run(["pkill", "-SIGUSR1", "-f", "system.updated.updated"], check=False)
+ time.sleep(60)
+
+ if not params.get_bool("UpdaterFetchAvailable"):
+ return
+
+ while params.get("UpdaterState", encoding="utf8") != "idle":
+ time.sleep(60)
+
+ subprocess.run(["pkill", "-SIGHUP", "-f", "system.updated.updated"], check=False)
+ while not params.get_bool("UpdateAvailable"):
+ time.sleep(60)
+
+ while params.get_bool("IsOnroad") or running_threads.get("lock_doors", threading.Thread()).is_alive():
+ time.sleep(60)
+
+ HARDWARE.reboot()
diff --git a/selfdrive/frogpilot/frogpilot_variables.py b/selfdrive/frogpilot/frogpilot_variables.py
index f13ba83ba46519..72b620fc0f29b1 100644
--- a/selfdrive/frogpilot/frogpilot_variables.py
+++ b/selfdrive/frogpilot/frogpilot_variables.py
@@ -6,7 +6,7 @@
from pathlib import Path
from types import SimpleNamespace
-from cereal import car
+from cereal import car, log
from openpilot.common.conversions import Conversions as CV
from openpilot.common.numpy_fast import clip, interp
from openpilot.common.params import Params, UnknownKeyName
@@ -32,10 +32,14 @@
TO_RADIANS = math.pi / 180 # Conversion factor from degrees to radians
ACTIVE_THEME_PATH = Path(__file__).parent / "assets/active_theme"
+METADATAS_PATH = Path(__file__).parent / "assets/model_metadata"
MODELS_PATH = Path("/data/models")
RANDOM_EVENTS_PATH = Path(__file__).parent / "assets/random_events"
THEME_SAVE_PATH = Path("/data/themes")
+MAPD_PATH = Path("/data/media/0/osm/mapd")
+MAPS_PATH = Path("/data/media/0/osm/offline")
+
DEFAULT_MODEL = "national-public-radio"
DEFAULT_MODEL_NAME = "National Public Radio 👀📡"
DEFAULT_MODEL_VERSION = "v6"
@@ -83,6 +87,7 @@ def update_frogpilot_toggles():
("BlindSpotMetrics", "1", 3),
("BlindSpotPath", "1", 0),
("BorderMetrics", "0", 3),
+ ("BrakeSignal", "0", 2),
("CameraView", "3", 2),
("CarMake", "", 0),
("CarModel", "", 0),
@@ -158,6 +163,9 @@ def update_frogpilot_toggles():
("HolidayThemes", "1", 0),
("HumanAcceleration", "1", 2),
("HumanFollowing", "1", 2),
+ ("HyundaiRadarTracks", "1", 2),
+ ("HKGtuning", "0", 2),
+ ("HatTrick", "0", 2),
("IncreasedStoppedDistance", "0", 2),
("IncreaseThermalLimits", "0", 3),
("JerkInfo", "0", 3),
@@ -248,7 +256,7 @@ def update_frogpilot_toggles():
("SearchInput", "0", 0),
("SetSpeedLimit", "0", 2),
("SetSpeedOffset", "0", 2),
- ("ShowCEMStatus", "1", 3),
+ ("ShowCEMStatus", "1", 2),
("ShowCPU", "1", 3),
("ShowGPU", "0", 3),
("ShowIP", "0", 3),
@@ -337,10 +345,10 @@ def __init__(self):
self.tuning_levels = {key: lvl for key, _, lvl in frogpilot_default_params + misc_tuning_levels}
short_branch = get_build_metadata().channel
- self.development_branch = short_branch == "FrogPilot-Development"
- self.release_branch = short_branch == "FrogPilot"
+ self.development_branch = short_branch == "Chubbs"
+ self.release_branch = short_branch == "ChubbsPilot"
self.staging_branch = short_branch == "FrogPilot-Staging"
- self.testing_branch = short_branch == "FrogPilot-Testing"
+ self.testing_branch = short_branch == "Development"
self.frogpilot_toggles.frogs_go_moo = Path("/persist/frogsgomoo.py").is_file()
self.frogpilot_toggles.block_user = self.development_branch and not self.frogpilot_toggles.frogs_go_moo
@@ -351,21 +359,30 @@ def __init__(self):
params_memory.put("FrogPilotTuningLevels", json.dumps(self.tuning_levels))
def update(self, holiday_theme, started):
- openpilot_installed = params.get_bool("HasAcceptedTerms")
+ default = params_default
+ level = self.tuning_levels
+ toggle = self.frogpilot_toggles
- key = "CarParams" if started else "CarParamsPersistent"
- msg_bytes = params.get(key, block=openpilot_installed and started)
+ tuning_level = params.get_int("TuningLevel") if params.get_bool("TuningLevelConfirmed") else 3
+
+ toggle.is_metric = params.get_bool("IsMetric")
+ distance_conversion = 1 if toggle.is_metric else CV.FOOT_TO_METER
+ small_distance_conversion = 1 if toggle.is_metric else CV.INCH_TO_CM
+ speed_conversion = CV.KPH_TO_MS if toggle.is_metric else CV.MPH_TO_MS
+ msg_bytes = params.get("CarParams" if started else "CarParamsPersistent", block=started)
if msg_bytes:
with car.CarParams.from_bytes(msg_bytes) as CP:
always_on_lateral_set = CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL
car_make = CP.carName
car_model = CP.carFingerprint
- has_auto_tune = car_make in {"hyundai", "toyota"} and CP.lateralTuning.which == "torque"
+ has_auto_tune = car_make in {"hyundai", "toyota"} and CP.lateralTuning.which() == "torque"
has_bsm = CP.enableBsm
has_pedal = CP.enableGasInterceptor
has_radar = not CP.radarUnavailable
- is_pid_car = CP.lateralTuning.which == "pid"
+ is_pid_car = CP.lateralTuning.which() == "pid"
+ kiBP = list(CP.longitudinalTuning.kiBP)
+ kiV = list(CP.longitudinalTuning.kiV)
max_acceleration_enabled = CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX
openpilot_longitudinal = CP.openpilotLongitudinalControl
pcm_cruise = CP.pcmCruise
@@ -381,6 +398,8 @@ def update(self, holiday_theme, started):
has_pedal = False
has_radar = False
is_pid_car = False
+ kiBP = [0.]
+ kiV = [0.]
max_acceleration_enabled = False
openpilot_longitudinal = False
pcm_cruise = False
@@ -388,16 +407,12 @@ def update(self, holiday_theme, started):
vEgoStopping = 0.5
vEgoStarting = 0.5
- tuning_level = params.get_int("TuningLevel") if params.get_bool("TuningLevelConfirmed") else 3
-
- default = params_default
- level = self.tuning_levels
- toggle = self.frogpilot_toggles
-
- toggle.is_metric = params.get_bool("IsMetric")
- distance_conversion = 1 if toggle.is_metric else CV.FOOT_TO_METER
- small_distance_conversion = 1 if toggle.is_metric else CV.INCH_TO_CM
- speed_conversion = CV.KPH_TO_MS if toggle.is_metric else CV.MPH_TO_MS
+ msg_bytes = params.get("LiveTorqueParameters")
+ if msg_bytes:
+ with log.LiveTorqueParametersData.from_bytes(msg_bytes) as LTP:
+ toggle.liveValid = LTP.liveValid
+ else:
+ toggle.liveValid = False
toggle.allow_auto_locking_doors = self.testing_branch and tuning_level >= 3 or self.frogpilot_toggles.frogs_go_moo
toggle.allow_far_lead_tracking = self.testing_branch and tuning_level >= 3 and has_radar or self.frogpilot_toggles.frogs_go_moo
@@ -510,6 +525,7 @@ def update(self, holiday_theme, started):
toggle.traffic_mode_jerk_speed = [clip(params.get_int("TrafficJerkSpeed") / 100, 0.01, 5) if traffic_profile and tuning_level >= level["TrafficJerkSpeed"] else clip(default.get_int("TrafficJerkSpeed") / 100, 0.01, 5), toggle.aggressive_jerk_speed]
toggle.traffic_mode_jerk_speed_decrease = [clip(params.get_int("TrafficJerkSpeedDecrease") / 100, 0.01, 5) if traffic_profile and tuning_level >= level["TrafficJerkSpeedDecrease"] else clip(default.get_int("TrafficJerkSpeedDecrease") / 100, 0.01, 5), toggle.aggressive_jerk_speed_decrease]
toggle.traffic_mode_follow = [clip(params.get_float("TrafficFollow"), 0.5, 5) if traffic_profile and tuning_level >= level["TrafficFollow"] else clip(default.get_float("TrafficFollow"), 0.5, 5), toggle.aggressive_follow]
+ toggle.hattrick_mode = openpilot_longitudinal and car_make == "hyundai" and params.get_bool("HatTrick") if tuning_level >= level["HatTrick"] else default.get_bool("HatTrick")
custom_ui = params.get_bool("CustomUI") if tuning_level >= level["CustomUI"] else default.get_bool("CustomUI")
toggle.acceleration_path = custom_ui and (params.get_bool("AccelerationPath") if tuning_level >= level["AccelerationPath"] else default.get_bool("AccelerationPath"))
@@ -558,14 +574,24 @@ def update(self, holiday_theme, started):
toggle.experimental_gm_tune = openpilot_longitudinal and car_make == "gm" and (params.get_bool("ExperimentalGMTune") if tuning_level >= level["ExperimentalGMTune"] else default.get_bool("ExperimentalGMTune"))
+ toggle.hyundai_radar_tracks = car_make == "hyundai" and params.get_bool("HyundaiRadarTracks") if tuning_level >= level["HyundaiRadarTracks"] else default.get_bool("HyundaiRadarTracks")
+ toggle.hkg_tuning = openpilot_longitudinal and car_make == "hyundai" and params.get_bool("HKGtuning") if tuning_level >= level["HKGtuning"] else default.get_bool("HKGtuning")
+
+ toggle.hyundai_radar_tracks = car_make == "hyundai" and params.get_bool("HyundaiRadarTracks") if tuning_level >= level["HyundaiRadarTracks"] else default.get_bool("HyundaiRadarTracks")
+ toggle.hkg_tuning = openpilot_longitudinal and car_make == "hyundai" and params.get_bool("HKGtuning") if tuning_level >= level["HKGtuning"] else default.get_bool("HKGtuning")
+
toggle.experimental_mode_via_press = openpilot_longitudinal and (params.get_bool("ExperimentalModeActivation") if tuning_level >= level["ExperimentalModeActivation"] else default.get_bool("ExperimentalModeActivation"))
toggle.experimental_mode_via_distance = toggle.experimental_mode_via_press and (params.get_bool("ExperimentalModeViaDistance") if tuning_level >= level["ExperimentalModeViaDistance"] else default.get_bool("ExperimentalModeViaDistance"))
toggle.experimental_mode_via_lkas = not toggle.always_on_lateral_lkas and toggle.experimental_mode_via_press and car_make != "subaru" and (params.get_bool("ExperimentalModeViaLKAS") if tuning_level >= level["ExperimentalModeViaLKAS"] else default.get_bool("ExperimentalModeViaLKAS"))
toggle.experimental_mode_via_tap = toggle.experimental_mode_via_press and (params.get_bool("ExperimentalModeViaTap") if tuning_level >= level["ExperimentalModeViaTap"] else default.get_bool("ExperimentalModeViaTap"))
+ toggle.far_lead_tracking = toggle.allow_far_lead_tracking and has_radar
+
toggle.frogsgomoo_tweak = openpilot_longitudinal and car_make == "toyota" and (params.get_bool("FrogsGoMoosTweak") if tuning_level >= level["FrogsGoMoosTweak"] else default.get_bool("FrogsGoMoosTweak"))
+ toggle.kiBP = kiBP
+ toggle.kiV = kiV
toggle.stoppingDecelRate = 0.01 if toggle.frogsgomoo_tweak else stoppingDecelRate
- toggle.vEgoStopping = 0.1 if toggle.frogsgomoo_tweak else vEgoStopping
+ toggle.vEgoStopping = 0.5 if toggle.frogsgomoo_tweak else vEgoStopping
toggle.vEgoStarting = 0.1 if toggle.frogsgomoo_tweak else vEgoStarting
toggle.holiday_themes = params.get_bool("HolidayThemes") if tuning_level >= level["HolidayThemes"] else default.get_bool("HolidayThemes")
@@ -598,39 +624,34 @@ def update(self, holiday_theme, started):
toggle.max_desired_acceleration = clip(params.get_float("MaxDesiredAcceleration"), 0.1, 4.0) if longitudinal_tuning and tuning_level >= level["MaxDesiredAcceleration"] else default.get_float("MaxDesiredAcceleration")
toggle.taco_tune = longitudinal_tuning and (params.get_bool("TacoTune") if tuning_level >= level["TacoTune"] else default.get_bool("TacoTune"))
- available_models = params.get("AvailableModels", encoding='utf-8')
- available_model_names = params.get("AvailableModelNames", encoding='utf-8')
- toggle.model_randomizer = params.get_bool("ModelRandomizer") if tuning_level >= level["ModelRandomizer"] else default.get_bool("ModelRandomizer")
- if available_models:
+ toggle.available_models = params.get("AvailableModels", encoding='utf-8') or ""
+ toggle.available_model_names = params.get("AvailableModelNames", encoding='utf-8') or ""
+ toggle.model_versions = params.get("ModelVersions", encoding='utf-8') or ""
+ downloaded_models = [model for model in toggle.available_models.split(",") if (MODELS_PATH / f"{model}.thneed").exists()]
+ toggle.model_randomizer = downloaded_models and (params.get_bool("ModelRandomizer") if tuning_level >= level["ModelRandomizer"] else default.get_bool("ModelRandomizer"))
+ if toggle.available_models and downloaded_models and toggle.model_versions:
if toggle.model_randomizer:
if not started:
blacklisted_models = (params.get("BlacklistedModels", encoding='utf-8') or "").split(",")
- existing_models = [model for model in available_models.split(",") if model not in blacklisted_models and (MODELS_PATH / f"{model}.thneed").exists()]
- toggle.model = random.choice(existing_models) if existing_models else default.get("Model", encoding='utf-8')
+ selectable_models = [model for model in downloaded_models if model not in blacklisted_models]
+ toggle.model = random.choice(selectable_models) if selectable_models else default.get("Model", encoding='utf-8')
+ toggle.model_name = "Mystery Model 👻"
+ toggle.model_version = toggle.model_versions.split(",")[toggle.available_models.split(",").index(toggle.model)]
else:
toggle.model = params.get("Model", encoding='utf-8') if tuning_level >= level["Model"] else default.get("Model", encoding='utf-8')
- else:
- toggle.model = default.get("Model", encoding='utf-8')
- if available_models and available_model_names and toggle.model in available_models.split(",") and (MODELS_PATH / f"{toggle.model}.thneed").exists():
- toggle.model_name = available_model_names.split(",")[available_models.split(",").index(toggle.model)]
- else:
- toggle.model = default.get("Model", encoding='utf-8')
- toggle.model_name = default.get("ModelName", encoding='utf-8')
- model_versions = params.get("ModelVersions", encoding='utf-8')
- if available_models and model_versions:
- toggle.model_version = model_versions.split(",")[available_models.split(",").index(toggle.model)]
- if not (MODELS_PATH / f"supercombo_metadata_{toggle.model_version}.pkl").exists():
- toggle.model = default.get("Model", encoding='utf-8')
- toggle.model_name = default.get("ModelName", encoding='utf-8')
- toggle.model_version = default.get("ModelVersion", encoding='utf-8')
+ if toggle.model in toggle.available_models.split(","):
+ toggle.model_name = params.get("ModelName", encoding='utf-8') if tuning_level >= level["ModelName"] else default.get("ModelName", encoding='utf-8')
+ toggle.model_version = toggle.model_versions.split(",")[toggle.available_models.split(",").index(toggle.model)]
+ else:
+ toggle.model = default.get("Model", encoding='utf-8')
+ toggle.model_name = default.get("ModelName", encoding='utf-8')
+ toggle.model_version = default.get("ModelVersion", encoding='utf-8')
else:
toggle.model = default.get("Model", encoding='utf-8')
toggle.model_name = default.get("ModelName", encoding='utf-8')
toggle.model_version = default.get("ModelVersion", encoding='utf-8')
- toggle.classic_model = toggle.model_version in {"v1", "v2", "v3"}
- toggle.clipped_curvature_model = toggle.model_version in {"v5", "v6"}
- toggle.desired_curvature_model = toggle.model_version in {"v1", "v2", "v3", "v4", "v5"}
- toggle.navigation_model = toggle.model_version in {"v1"}
+ toggle.classic_model = toggle.model_version in {"v1", "v2", "v3", "v4"}
+ toggle.planner_curvature_model = toggle.model_version not in {"v1", "v2", "v3", "v4", "v5"}
toggle.radarless_model = toggle.model_version in {"v3"}
toggle.model_ui = params.get_bool("ModelUI") if tuning_level >= level["ModelUI"] else default.get_bool("ModelUI")
@@ -687,6 +708,7 @@ def update(self, holiday_theme, started):
toggle.stopped_timer = quality_of_life_visuals and (params.get_bool("StoppedTimer") if tuning_level >= level["StoppedTimer"] else default.get_bool("StoppedTimer"))
toggle.rainbow_path = params.get_bool("RainbowPath") if tuning_level >= level["RainbowPath"] else default.get_bool("RainbowPath")
+ toggle.brake_signal= params.get_bool("BrakeSignal") if tuning_level >= level["BrakeSignal"] else default.get_bool("BrakeSignal")
toggle.random_events = params.get_bool("RandomEvents") if tuning_level >= level["RandomEvents"] else default.get_bool("RandomEvents")
@@ -737,5 +759,13 @@ def update(self, holiday_theme, started):
toggle.volt_sng = car_model == "CHEVROLET_VOLT" and (params.get_bool("VoltSNG") if tuning_level >= level["VoltSNG"] else default.get_bool("VoltSNG"))
- params_memory.put("FrogPilotToggles", json.dumps(toggle.__dict__))
+ serializable_dict = {}
+ for key, value in toggle.__dict__.items():
+ try:
+ json.dumps({key: value})
+ serializable_dict[key] = value
+ except TypeError as e:
+ print(f"Serialization Error for key '{key}': {e}")
+
+ params_memory.put("FrogPilotToggles", json.dumps(serializable_dict))
params_memory.remove("FrogPilotTogglesUpdated")
diff --git a/selfdrive/frogpilot/navigation/mapd.py b/selfdrive/frogpilot/navigation/mapd.py
index ddda96c05caf25..8005a6f2aaa768 100644
--- a/selfdrive/frogpilot/navigation/mapd.py
+++ b/selfdrive/frogpilot/navigation/mapd.py
@@ -1,89 +1,104 @@
# PFEIFER - MAPD - Modified by FrogAi for FrogPilot
#!/usr/bin/env python3
import json
+import os
import stat
import subprocess
+import time
import urllib.request
+import openpilot.system.sentry as sentry
+
from pathlib import Path
+from openpilot.selfdrive.frogpilot.frogpilot_utilities import is_url_pingable
+from openpilot.selfdrive.frogpilot.frogpilot_variables import MAPD_PATH, MAPS_PATH
+
VERSION = "v1"
GITHUB_VERSION_URL = f"https://github.com/FrogAi/FrogPilot-Resources/raw/Versions/mapd_version_{VERSION}.json"
GITLAB_VERSION_URL = f"https://gitlab.com/FrogAi/FrogPilot-Resources/-/raw/Versions/mapd_version_{VERSION}.json"
-MAPD_PATH = Path("/data/media/0/osm/mapd")
-MAPS_PATH = Path("/data/media/0/osm/offline")
VERSION_PATH = Path("/data/media/0/osm/mapd_version")
-def download(current_version):
+def download():
+ while not (is_url_pingable("https://github.com") or is_url_pingable("https://gitlab.com")):
+ time.sleep(60)
+
+ latest_version = get_latest_version()
+
urls = [
- f"https://github.com/pfeiferj/openpilot-mapd/releases/download/{current_version}/mapd",
- f"https://gitlab.com/FrogAi/FrogPilot-Resources/-/raw/Mapd/{current_version}"
+ f"https://github.com/pfeiferj/openpilot-mapd/releases/download/{latest_version}/mapd",
+ f"https://gitlab.com/FrogAi/FrogPilot-Resources/-/raw/Mapd/{latest_version}"
]
- MAPD_PATH.parent.mkdir(parents=True, exist_ok=True)
+ os.makedirs(os.path.dirname(MAPD_PATH), exist_ok=True)
for url in urls:
try:
- with urllib.request.urlopen(url, timeout=5) as f:
- with MAPD_PATH.open('wb') as output:
+ with urllib.request.urlopen(url) as f:
+ with open(MAPD_PATH, 'wb') as output:
output.write(f.read())
-
- MAPD_PATH.chmod(MAPD_PATH.stat().st_mode | stat.S_IEXEC)
- VERSION_PATH.write_text(current_version)
- print(f"Successfully downloaded mapd from {url}")
- return True
+ os.fsync(output)
+ current_permissions = stat.S_IMODE(os.lstat(MAPD_PATH).st_mode)
+ os.chmod(MAPD_PATH, current_permissions | stat.S_IEXEC)
+ with open(VERSION_PATH, 'w') as output:
+ output.write(latest_version)
+ os.fsync(output)
+ return
except Exception as error:
print(f"Failed to download mapd from {url}: {error}")
-
- print(f"Failed to download mapd for version {current_version}")
- return False
-
-def get_installed_version():
- try:
- return VERSION_PATH.read_text().strip()
- except FileNotFoundError:
- return None
- except Exception as error:
- print(f"Error reading installed version: {error}")
- return None
+ sentry.capture_exception(error)
def get_latest_version():
for url in [GITHUB_VERSION_URL, GITLAB_VERSION_URL]:
try:
with urllib.request.urlopen(url, timeout=5) as response:
return json.loads(response.read().decode('utf-8'))['version']
+ except TimeoutError as error:
+ print(f"Timeout while fetching mapd version from {url}: {error}")
+ except URLError as error:
+ print(f"URLError encountered for {url}: {error}")
except Exception as error:
print(f"Error fetching mapd version from {url}: {error}")
+ sentry.capture_exception(error)
print("Failed to get the latest mapd version")
- return None
-
-def update_mapd():
- installed_version = get_installed_version()
- latest_version = get_latest_version()
+ return "v0"
- if latest_version is None:
- print("Could not get the latest mapd version")
- return
-
- if installed_version != latest_version:
- print("New mapd version available, stopping the mapd process for update")
+def mapd_thread():
+ while True:
try:
- subprocess.run(["pkill", "-f", MAPD_PATH], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ if not os.path.exists(MAPD_PATH):
+ print(f"{MAPD_PATH} not found. Downloading...")
+ download()
+ continue
+ else:
+ current_permissions = stat.S_IMODE(os.lstat(MAPD_PATH).st_mode)
+ desired_permissions = current_permissions | stat.S_IEXEC
+
+ if current_permissions != desired_permissions:
+ print(f"{MAPD_PATH} has the wrong permissions. Attempting to fix...")
+ os.chmod(MAPD_PATH, desired_permissions)
+ if not os.path.exists(VERSION_PATH):
+ download()
+ continue
+ with open(VERSION_PATH) as f:
+ current_version = f.read()
+ if is_url_pingable("https://github.com") or is_url_pingable("https://gitlab.com"):
+ if current_version != get_latest_version():
+ print("New mapd version available. Downloading...")
+ download()
+ continue
+
+ process = subprocess.Popen(MAPD_PATH)
+ process.wait()
except Exception as error:
- print(f"Error stopping mapd process: {error}")
- return
+ print(f"Error in mapd_thread: {error}")
+ sentry.capture_exception(error)
+ time.sleep(60)
+
+def main():
+ mapd_thread()
- if download(latest_version):
- print(f"Updated mapd to version {latest_version}")
- else:
- print("Failed to update mapd")
- else:
- print("Mapd is up to date")
-
-def ensure_mapd_is_running():
- try:
- subprocess.run([MAPD_PATH], check=True)
- except Exception as error:
- print(f"Error running mapd process: {error}")
+if __name__ == "__main__":
+ main()
diff --git a/selfdrive/frogpilot/navigation/ui/maps_settings.cc b/selfdrive/frogpilot/navigation/ui/maps_settings.cc
index 01cbe85d2720d7..21bee76d0291b6 100644
--- a/selfdrive/frogpilot/navigation/ui/maps_settings.cc
+++ b/selfdrive/frogpilot/navigation/ui/maps_settings.cc
@@ -4,40 +4,50 @@
#include "selfdrive/frogpilot/navigation/ui/maps_settings.h"
-FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
+FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent), mapsFolderPath{"/data/media/0/osm/offline"} {
+ QVBoxLayout *mainLayout = new QVBoxLayout();
+ addItem(mainLayout);
+
+ mapsLayout = new QStackedLayout();
+ mainLayout->addLayout(mapsLayout);
+
+ FrogPilotListWidget *settingsList = new FrogPilotListWidget(this);
+
std::vector
scheduleOptions{tr("Manually"), tr("Weekly"), tr("Monthly")};
- preferredSchedule = new ButtonParamControl("PreferredSchedule", tr("Automatically Update Maps"),
+ ButtonParamControl *preferredSchedule = new ButtonParamControl("PreferredSchedule", tr("Automatically Update Maps"),
tr("Controls the frequency at which maps update with the latest OpenStreetMap (OSM) changes. "
"Weekly updates begin at midnight every Sunday, while monthly updates start at midnight on the 1st of each month."),
"",
scheduleOptions);
- addItem(preferredSchedule);
-
- selectMapsButton = new FrogPilotButtonsControl(tr("Select Map Data Sources"), tr("Map data sources to use with 'Curve Speed Control' and 'Speed Limit Controller'."), {tr("COUNTRIES"), tr("STATES")});
- QObject::connect(selectMapsButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
- if (id == 0) {
- countriesOpen = true;
- }
- displayMapButtons();
+ settingsList->addItem(preferredSchedule);
+
+ std::vector mapOptions{tr("COUNTRIES"), tr("STATES")};
+ FrogPilotButtonsControl *selectMaps = new FrogPilotButtonsControl(tr("Select Map Data Sources"),
+ tr("Select map data sources to use with 'Curve Speed Control' and 'Speed Limit Controller'."),
+ mapOptions);
+ QObject::connect(selectMaps, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
+ mapsLayout->setCurrentIndex(id + 1);
openMapSelection();
});
- addItem(selectMapsButton);
+ settingsList->addItem(selectMaps);
downloadMapsButton = new ButtonControl(tr("Download Maps"), tr("DOWNLOAD"), tr("Downloads the selected maps to use with 'Curve Speed Control' and 'Speed Limit Controller'."));
QObject::connect(downloadMapsButton, &ButtonControl::clicked, [this] {
if (downloadMapsButton->text() == tr("CANCEL")) {
- cancelDownload();
+ if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to cancel the download?"), this)) {
+ cancelDownload();
+ }
} else {
- downloadMaps();
+ startDownload();
}
});
- addItem(downloadMapsButton);
+ settingsList->addItem(downloadMapsButton);
- addItem(mapsSize = new LabelControl(tr("Downloaded Maps Size"), calculateDirectorySize(mapsFolderPath)));
- addItem(downloadStatus = new LabelControl(tr("Download Progress")));
- addItem(downloadETA = new LabelControl(tr("Download Completion ETA")));
- addItem(downloadTimeElapsed = new LabelControl(tr("Download Time Elapsed")));
- addItem(lastMapsDownload = new LabelControl(tr("Maps Last Updated"), params.get("LastMapsUpdate").empty() ? "Never" : QString::fromStdString(params.get("LastMapsUpdate"))));
+ settingsList->addItem(downloadETA = new LabelControl(tr("Download Completion ETA")));
+ settingsList->addItem(downloadStatus = new LabelControl(tr("Download Progress")));
+ settingsList->addItem(downloadTimeElapsed = new LabelControl(tr("Download Time Elapsed")));
+ settingsList->addItem(lastMapsDownload = new LabelControl(tr("Maps Last Updated"), params.get("LastMapsUpdate").empty() ? "Never" : QString::fromStdString(params.get("LastMapsUpdate"))));
+ settingsList->addItem(mapsSize = new LabelControl(tr("Downloaded Maps Size"), calculateDirectorySize(mapsFolderPath)));
downloadETA->setVisible(false);
downloadStatus->setVisible(false);
@@ -53,305 +63,184 @@ FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPi
}).detach();
}
});
- addItem(removeMapsButton);
- removeMapsButton->setVisible(QDir(mapsFolderPath).exists());
-
- addItem(midwestLabel = new LabelControl(tr("United States - Midwest"), ""));
- addItem(midwestMaps = new MapSelectionControl(midwestMap));
-
- addItem(northeastLabel = new LabelControl(tr("United States - Northeast"), ""));
- addItem(northeastMaps = new MapSelectionControl(northeastMap));
-
- addItem(southLabel = new LabelControl(tr("United States - South"), ""));
- addItem(southMaps = new MapSelectionControl(southMap));
-
- addItem(westLabel = new LabelControl(tr("United States - West"), ""));
- addItem(westMaps = new MapSelectionControl(westMap));
-
- addItem(territoriesLabel = new LabelControl(tr("United States - Territories"), ""));
- addItem(territoriesMaps = new MapSelectionControl(territoriesMap));
-
- addItem(africaLabel = new LabelControl(tr("Africa"), ""));
- addItem(africaMaps = new MapSelectionControl(africaMap, true));
-
- addItem(antarcticaLabel = new LabelControl(tr("Antarctica"), ""));
- addItem(antarcticaMaps = new MapSelectionControl(antarcticaMap, true));
-
- addItem(asiaLabel = new LabelControl(tr("Asia"), ""));
- addItem(asiaMaps = new MapSelectionControl(asiaMap, true));
-
- addItem(europeLabel = new LabelControl(tr("Europe"), ""));
- addItem(europeMaps = new MapSelectionControl(europeMap, true));
-
- addItem(northAmericaLabel = new LabelControl(tr("North America"), ""));
- addItem(northAmericaMaps = new MapSelectionControl(northAmericaMap, true));
-
- addItem(oceaniaLabel = new LabelControl(tr("Oceania"), ""));
- addItem(oceaniaMaps = new MapSelectionControl(oceaniaMap, true));
-
- addItem(southAmericaLabel = new LabelControl(tr("South America"), ""));
- addItem(southAmericaMaps = new MapSelectionControl(southAmericaMap, true));
+ settingsList->addItem(removeMapsButton);
+
+ ScrollView *settingsPanel = new ScrollView(settingsList, this);
+ mapsLayout->addWidget(settingsPanel);
+
+ FrogPilotListWidget *countriesList = new FrogPilotListWidget(this);
+ std::vector>> countries = {
+ {tr("Africa"), africaMap},
+ {tr("Antarctica"), antarcticaMap},
+ {tr("Asia"), asiaMap},
+ {tr("Europe"), europeMap},
+ {tr("North America"), northAmericaMap},
+ {tr("Oceania"), oceaniaMap},
+ {tr("South America"), southAmericaMap}
+ };
+
+ for (std::pair> country : countries) {
+ countriesList->addItem(new LabelControl(country.first, ""));
+ countriesList->addItem(new MapSelectionControl(country.second, true));
+ }
- QObject::connect(parent, &FrogPilotSettingsWindow::closeMapSelection, [this]() {
- displayMapButtons(false);
+ ScrollView *countryMapsPanel = new ScrollView(countriesList, this);
+ mapsLayout->addWidget(countryMapsPanel);
+
+ FrogPilotListWidget *statesList = new FrogPilotListWidget(this);
+ std::vector>> states = {
+ {tr("United States - Midwest"), midwestMap},
+ {tr("United States - Northeast"), northeastMap},
+ {tr("United States - South"), southMap},
+ {tr("United States - West"), westMap},
+ {tr("United States - Territories"), territoriesMap}
+ };
+
+ for (std::pair> state : states) {
+ statesList->addItem(new LabelControl(state.first, ""));
+ statesList->addItem(new MapSelectionControl(state.second));
+ }
- countriesOpen = false;
+ ScrollView *stateMapsPanel = new ScrollView(statesList, this);
+ mapsLayout->addWidget(stateMapsPanel);
- mapsSelected = params.get("MapsSelected");
+ QObject::connect(parent, &FrogPilotSettingsWindow::closeMapSelection, [this] {
+ std::string mapsSelected = params.get("MapsSelected");
hasMapsSelected = !QJsonDocument::fromJson(QByteArray::fromStdString(mapsSelected)).object().value("nations").toArray().isEmpty();
hasMapsSelected |= !QJsonDocument::fromJson(QByteArray::fromStdString(mapsSelected)).object().value("states").toArray().isEmpty();
+
+ mapsLayout->setCurrentIndex(0);
});
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotMapsPanel::updateState);
-
- displayMapButtons(false);
}
void FrogPilotMapsPanel::showEvent(QShowEvent *event) {
- mapdExists = std::filesystem::exists("/data/media/0/osm/mapd");
- mapsSelected = params.get("MapsSelected");
+ std::string mapsSelected = params.get("MapsSelected");
hasMapsSelected = !QJsonDocument::fromJson(QByteArray::fromStdString(mapsSelected)).object().value("nations").toArray().isEmpty();
hasMapsSelected |= !QJsonDocument::fromJson(QByteArray::fromStdString(mapsSelected)).object().value("states").toArray().isEmpty();
- std::thread([this] {
- mapsSize->setText(calculateDirectorySize(mapsFolderPath));
- }).detach();
-}
-
-void FrogPilotMapsPanel::hideEvent(QHideEvent *event) {
- displayMapButtons(false);
-
- countriesOpen = false;
-}
-
-void FrogPilotMapsPanel::updateState(const UIState &s) {
- if (!isVisible()) {
- return;
- }
-
- if (downloadActive && s.sm->frame % (UI_FREQ / 2) == 0) {
- updateDownloadStatusLabels();
- }
+ removeMapsButton->setVisible(QDir(mapsFolderPath).exists());
- downloadMapsButton->setEnabled(mapdExists && hasMapsSelected && s.scene.online);
+ std::string osmDownloadProgress = params.get("OSMDownloadProgress");
+ if (!osmDownloadProgress.empty()) {
+ downloadMapsButton->setText(tr("CANCEL"));
+ downloadStatus->setText("Calculating...");
- parent->keepScreenOn = downloadActive;
-}
+ downloadStatus->setVisible(true);
-void FrogPilotMapsPanel::cancelDownload() {
- if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to cancel the download?"), this)) {
- std::system("pkill mapd");
+ lastMapsDownload->setVisible(false);
+ removeMapsButton->setVisible(false);
- resetDownloadState();
+ updateDownloadLabels(osmDownloadProgress);
}
}
-void FrogPilotMapsPanel::downloadMaps() {
- params.remove("OSMDownloadProgress");
-
- resetDownloadLabels();
-
- downloadETA->setVisible(true);
- downloadStatus->setVisible(true);
- downloadTimeElapsed->setVisible(true);
- lastMapsDownload->setVisible(false);
- removeMapsButton->setVisible(false);
-
- update();
-
- int retryCount = 0;
- while (!isMapdRunning() && retryCount < 3) {
- retryCount++;
- util::sleep_for(1000);
- }
-
- if (retryCount >= 3) {
- handleDownloadError();
+void FrogPilotMapsPanel::updateState(const UIState &s) {
+ if (!isVisible() || s.sm->frame % (UI_FREQ / 2) != 0) {
return;
}
- params_memory.put("OSMDownloadLocations", mapsSelected);
-
- downloadActive = true;
-
- startTime = QDateTime::currentMSecsSinceEpoch();
-}
-
-void FrogPilotMapsPanel::updateDownloadStatusLabels() {
- static std::regex fileStatuses(R"("total_files":(\d+),.*"downloaded_files":(\d+))");
- static int previousDownloadedFiles = 0;
- static qint64 lastUpdatedTime = QDateTime::currentMSecsSinceEpoch();
- static qint64 lastRemainingTime = 0;
+ downloadMapsButton->setEnabled(!cancellingDownload && hasMapsSelected && s.scene.online);
std::string osmDownloadProgress = params.get("OSMDownloadProgress");
- std::smatch match;
- if (!std::regex_search(osmDownloadProgress, match, fileStatuses)) {
- resetDownloadLabels();
- return;
+ if (!osmDownloadProgress.empty() && !cancellingDownload) {
+ updateDownloadLabels(osmDownloadProgress);
}
- int totalFiles = std::stoi(match[1].str());
- int downloadedFiles = std::stoi(match[2].str());
-
- qint64 elapsedMilliseconds = QDateTime::currentMSecsSinceEpoch() - startTime;
- qint64 timePerFile = (downloadedFiles > 0) ? (elapsedMilliseconds / downloadedFiles) : 0;
- qint64 remainingFiles = totalFiles - downloadedFiles;
- qint64 remainingTime = timePerFile * remainingFiles;
-
- if (downloadedFiles >= totalFiles) {
- finalizeDownload();
- return;
- }
-
- if (downloadedFiles != previousDownloadedFiles) {
- previousDownloadedFiles = downloadedFiles;
- lastUpdatedTime = QDateTime::currentMSecsSinceEpoch();
- lastRemainingTime = remainingTime;
-
- QtConcurrent::run([this]() {
- QString sizeText = calculateDirectorySize(mapsFolderPath);
- QMetaObject::invokeMethod(mapsSize, [this, sizeText]() {
- mapsSize->setText(sizeText);
- }, Qt::QueuedConnection);
- });
- } else {
- qint64 currentTime = QDateTime::currentMSecsSinceEpoch();
- qint64 timeSinceLastUpdate = (currentTime - lastUpdatedTime) / 1000;
- remainingTime = std::max(qint64(0), lastRemainingTime - timeSinceLastUpdate * 1000);
- }
-
- updateDownloadLabels(downloadedFiles, totalFiles, remainingTime, elapsedMilliseconds);
+ parent->keepScreenOn = !osmDownloadProgress.empty();
}
-void FrogPilotMapsPanel::resetDownloadState() {
- downloadMapsButton->setText(tr("DOWNLOAD"));
-
- downloadETA->setVisible(false);
- downloadStatus->setVisible(false);
- downloadTimeElapsed->setVisible(false);
+void FrogPilotMapsPanel::cancelDownload() {
+ cancellingDownload = true;
- lastMapsDownload->setVisible(true);
- removeMapsButton->setVisible(QDir(mapsFolderPath).exists());
+ downloadMapsButton->setEnabled(false);
- downloadActive = false;
+ downloadETA->setText("Cancelling...");
+ downloadMapsButton->setText(tr("CANCELLED"));
+ downloadStatus->setText("Cancelling...");
+ downloadTimeElapsed->setText("Cancelling...");
- update();
-}
+ params.remove("OSMDownloadProgress");
+ params_memory.remove("OSMDownloadLocations");
-void FrogPilotMapsPanel::handleDownloadError() {
std::system("pkill mapd");
- downloadMapsButton->setText(tr("DOWNLOAD"));
- downloadStatus->setText("Download error... Please try again!");
+ QTimer::singleShot(2500, [this]() {
+ cancellingDownload = false;
- downloadETA->setVisible(false);
- downloadTimeElapsed->setVisible(false);
-
- downloadMapsButton->setEnabled(false);
+ downloadMapsButton->setEnabled(true);
- downloadActive = false;
+ downloadMapsButton->setText(tr("DOWNLOAD"));
- update();
+ downloadETA->setVisible(false);
+ downloadStatus->setVisible(false);
+ downloadTimeElapsed->setVisible(false);
- util::sleep_for(3000);
+ lastMapsDownload->setVisible(true);
+ removeMapsButton->setVisible(QDir(mapsFolderPath).exists());
- downloadStatus->setText("");
-
- downloadStatus->setVisible(false);
-
- downloadMapsButton->setEnabled(true);
-
- update();
+ update();
+ });
}
-void FrogPilotMapsPanel::finalizeDownload() {
- QString formattedDate = formatCurrentDate();
+void FrogPilotMapsPanel::startDownload() {
+ downloadETA->setText("Calculating...");
+ downloadMapsButton->setText(tr("CANCEL"));
+ downloadStatus->setText("Calculating...");
+ downloadTimeElapsed->setText("Calculating...");
+
+ downloadETA->setVisible(true);
+ downloadStatus->setVisible(true);
+ downloadTimeElapsed->setVisible(true);
- params.put("LastMapsUpdate", formattedDate.toStdString());
- params.remove("OSMDownloadProgress");
+ lastMapsDownload->setVisible(false);
+ removeMapsButton->setVisible(false);
- mapsSize->setText(calculateDirectorySize(mapsFolderPath));
+ elapsedTime.start();
+ startTime = QDateTime::currentDateTime();
- resetDownloadLabels();
+ params_memory.put("OSMDownloadLocations", params.get("MapsSelected"));
+}
- downloadMapsButton->setText(tr("DOWNLOAD"));
- lastMapsDownload->setText(formattedDate);
+void FrogPilotMapsPanel::updateDownloadLabels(std::string &osmDownloadProgress) {
+ static std::regex fileStatusRegex(R"("total_files":(\d+),.*"downloaded_files":(\d+))");
- downloadETA->setVisible(false);
- downloadStatus->setVisible(false);
- downloadTimeElapsed->setVisible(false);
+ std::smatch match;
+ if (std::regex_search(osmDownloadProgress, match, fileStatusRegex)) {
+ int totalFiles = std::stoi(match[1].str());
+ int downloadedFiles = std::stoi(match[2].str());
- lastMapsDownload->setVisible(true);
- removeMapsButton->setVisible(true);
+ if (downloadedFiles == totalFiles) {
+ downloadMapsButton->setText(tr("DOWNLOAD"));
+ lastMapsDownload->setText(formatCurrentDate());
- downloadActive = false;
+ downloadETA->setVisible(false);
+ downloadStatus->setVisible(false);
+ downloadTimeElapsed->setVisible(false);
- update();
-}
+ lastMapsDownload->setVisible(true);
+ removeMapsButton->setVisible(true);
-void FrogPilotMapsPanel::resetDownloadLabels() {
- downloadETA->setText("Calculating...");
- downloadMapsButton->setText(tr("CANCEL"));
- downloadStatus->setText("Calculating...");
- downloadTimeElapsed->setText("Calculating...");
-}
+ params.put("LastMapsUpdate", formatCurrentDate().toStdString());
+ params.remove("OSMDownloadProgress");
-void FrogPilotMapsPanel::updateDownloadLabels(int downloadedFiles, int totalFiles, qint64 remainingTime, qint64 elapsedMilliseconds) {
- int percentage = (totalFiles > 0) ? (downloadedFiles * 100 / totalFiles) : 0;
- downloadStatus->setText(QString("%1 / %2 (%3%)").arg(downloadedFiles).arg(totalFiles).arg(percentage));
+ update();
- QDateTime currentDateTime = QDateTime::currentDateTime();
- QDateTime completionTime = currentDateTime.addMSecs(remainingTime);
+ return;
+ }
- QString completionTimeFormatted = completionTime.toString("h:mm AP");
- QString remainingTimeFormatted = QString("%1 (%2)").arg(formatElapsedTime(remainingTime)).arg(completionTimeFormatted);
- downloadETA->setText(remainingTimeFormatted);
+ static int previousDownloadedFiles = 0;
+ if (downloadedFiles != previousDownloadedFiles) {
+ std::thread([=]() {
+ mapsSize->setText(calculateDirectorySize(mapsFolderPath));
+ }).detach();
+ }
- downloadTimeElapsed->setText(formatElapsedTime(elapsedMilliseconds));
-}
+ downloadETA->setText(QString("%1").arg(formatETA(elapsedTime.elapsed(), downloadedFiles, previousDownloadedFiles, totalFiles, startTime)));
+ downloadStatus->setText(QString("%1 / %2 (%3%)").arg(downloadedFiles).arg(totalFiles).arg((downloadedFiles * 100) / totalFiles));
+ downloadTimeElapsed->setText(formatElapsedTime(elapsedTime.elapsed()));
-void FrogPilotMapsPanel::displayMapButtons(bool visible) {
- setUpdatesEnabled(false);
-
- downloadETA->setVisible(!visible && downloadActive);
- downloadMapsButton->setVisible(!visible);
- downloadStatus->setVisible(!visible && downloadActive);
- downloadTimeElapsed->setVisible(!visible && downloadActive);
- lastMapsDownload->setVisible(!visible && !downloadActive);
- mapsSize->setVisible(!visible);
- preferredSchedule->setVisible(!visible);
- removeMapsButton->setVisible(!visible && QDir(mapsFolderPath).exists() && !downloadActive);
- selectMapsButton->setVisible(!visible);
-
- africaMaps->setVisible(visible && countriesOpen);
- antarcticaMaps->setVisible(visible && countriesOpen);
- asiaMaps->setVisible(visible && countriesOpen);
- europeMaps->setVisible(visible && countriesOpen);
- northAmericaMaps->setVisible(visible && countriesOpen);
- oceaniaMaps->setVisible(visible && countriesOpen);
- southAmericaMaps->setVisible(visible && countriesOpen);
-
- africaLabel->setVisible(visible && countriesOpen);
- antarcticaLabel->setVisible(visible && countriesOpen);
- asiaLabel->setVisible(visible && countriesOpen);
- europeLabel->setVisible(visible && countriesOpen);
- northAmericaLabel->setVisible(visible && countriesOpen);
- oceaniaLabel->setVisible(visible && countriesOpen);
- southAmericaLabel->setVisible(visible && countriesOpen);
-
- midwestMaps->setVisible(visible && !countriesOpen);
- northeastMaps->setVisible(visible && !countriesOpen);
- southMaps->setVisible(visible && !countriesOpen);
- territoriesMaps->setVisible(visible && !countriesOpen);
- westMaps->setVisible(visible && !countriesOpen);
-
- midwestLabel->setVisible(visible && !countriesOpen);
- northeastLabel->setVisible(visible && !countriesOpen);
- southLabel->setVisible(visible && !countriesOpen);
- territoriesLabel->setVisible(visible && !countriesOpen);
- westLabel->setVisible(visible && !countriesOpen);
-
- setUpdatesEnabled(true);
-
- update();
+ previousDownloadedFiles = downloadedFiles;
+ }
}
diff --git a/selfdrive/frogpilot/navigation/ui/maps_settings.h b/selfdrive/frogpilot/navigation/ui/maps_settings.h
index 2e2bc201694ade..e492851cf0d8d7 100644
--- a/selfdrive/frogpilot/navigation/ui/maps_settings.h
+++ b/selfdrive/frogpilot/navigation/ui/maps_settings.h
@@ -12,71 +12,37 @@ class FrogPilotMapsPanel : public FrogPilotListWidget {
signals:
void openMapSelection();
+protected:
+ void showEvent(QShowEvent *event) override;
+
private:
void cancelDownload();
- void displayMapButtons(bool visible = true);
- void downloadMaps();
- void finalizeDownload();
- void handleDownloadError();
- void hideEvent(QHideEvent *event) override;
- void resetDownloadLabels();
- void resetDownloadState();
- void showEvent(QShowEvent *event);
- void updateDownloadLabels(int downloadedFiles, int totalFiles, qint64 remainingTime, qint64 elapsedMilliseconds);
- void updateDownloadStatusLabels();
+ void startDownload();
+ void updateDownloadLabels(std::string &osmDownloadProgress);
void updateState(const UIState &s);
+ bool cancellingDownload;
+ bool hasMapsSelected;
+
ButtonControl *downloadMapsButton;
ButtonControl *removeMapsButton;
- ButtonParamControl *preferredSchedule;
-
- FrogPilotButtonsControl *selectMapsButton;
-
FrogPilotSettingsWindow *parent;
- LabelControl *africaLabel;
- LabelControl *antarcticaLabel;
- LabelControl *asiaLabel;
LabelControl *downloadETA;
LabelControl *downloadStatus;
LabelControl *downloadTimeElapsed;
- LabelControl *europeLabel;
LabelControl *lastMapsDownload;
LabelControl *mapsSize;
- LabelControl *midwestLabel;
- LabelControl *northAmericaLabel;
- LabelControl *northeastLabel;
- LabelControl *oceaniaLabel;
- LabelControl *southAmericaLabel;
- LabelControl *southLabel;
- LabelControl *territoriesLabel;
- LabelControl *westLabel;
-
- MapSelectionControl *africaMaps;
- MapSelectionControl *antarcticaMaps;
- MapSelectionControl *asiaMaps;
- MapSelectionControl *europeMaps;
- MapSelectionControl *midwestMaps;
- MapSelectionControl *northAmericaMaps;
- MapSelectionControl *northeastMaps;
- MapSelectionControl *oceaniaMaps;
- MapSelectionControl *southAmericaMaps;
- MapSelectionControl *southMaps;
- MapSelectionControl *territoriesMaps;
- MapSelectionControl *westMaps;
Params params;
Params params_memory{"/dev/shm/params"};
- QString mapsFolderPath = "/data/media/0/osm/offline";
+ QDateTime startTime;
- bool countriesOpen;
- bool downloadActive;
- bool hasMapsSelected;
- bool mapdExists;
+ QElapsedTimer elapsedTime;
- qint64 startTime;
+ QString mapsFolderPath;
- std::string mapsSelected;
+ QStackedLayout *mapsLayout;
};
diff --git a/selfdrive/frogpilot/navigation/ui/navigation_functions.cc b/selfdrive/frogpilot/navigation/ui/navigation_functions.cc
index b3ee544d5d69a4..b89c1dfab0db39 100644
--- a/selfdrive/frogpilot/navigation/ui/navigation_functions.cc
+++ b/selfdrive/frogpilot/navigation/ui/navigation_functions.cc
@@ -3,27 +3,27 @@
#include "selfdrive/frogpilot/navigation/ui/navigation_functions.h"
-MapSelectionControl::MapSelectionControl(const QMap &map, bool isCountry)
- : buttonGroup(new QButtonGroup(this)), gridLayout(new QGridLayout(this)), mapData(map), isCountry(isCountry), selectionType(isCountry ? "nations" : "states") {
- buttonGroup->setExclusive(false);
+MapSelectionControl::MapSelectionControl(const QMap &map, bool isCountry) : mapData(map), isCountry(isCountry), selectionType(isCountry ? "nations" : "states") {
+ mapButtons = new QButtonGroup(this);
+ mapButtons->setExclusive(false);
+
+ mapLayout = new QGridLayout(this);
QList keys = mapData.keys();
for (int i = 0; i < keys.size(); ++i) {
QPushButton *button = new QPushButton(mapData[keys[i]], this);
-
button->setCheckable(true);
button->setStyleSheet(buttonStyle);
- button->setMinimumWidth(225);
- gridLayout->addWidget(button, i / 3, i % 3);
- buttonGroup->addButton(button, i);
+ mapButtons->addButton(button, i);
- connect(button, &QPushButton::toggled, this, &MapSelectionControl::updateSelectedMaps);
- }
+ mapLayout->addWidget(button, i / 3, i % 3);
- buttons = buttonGroup->buttons();
+ QObject::connect(button, &QPushButton::toggled, this, &MapSelectionControl::updateSelectedMaps);
+ }
- mapSelections = QJsonDocument::fromJson(QByteArray::fromStdString(params.get("MapsSelected"))).object()[selectionType].toArray();
+ maps = mapButtons->buttons();
+ mapSelections = QJsonDocument::fromJson(QByteArray::fromStdString(params.get("MapsSelected"))).object().value(selectionType).toArray();
loadSelectedMaps();
}
@@ -31,8 +31,8 @@ MapSelectionControl::MapSelectionControl(const QMap &map, bool
void MapSelectionControl::loadSelectedMaps() {
for (int i = 0; i < mapSelections.size(); ++i) {
QString selectedKey = mapSelections[i].toString();
- for (int j = 0; j < buttons.size(); ++j) {
- QAbstractButton *button = buttons[j];
+ for (int j = 0; j < maps.size(); ++j) {
+ QAbstractButton *button = maps[j];
if (button->text() == mapData.value(selectedKey)) {
button->setChecked(true);
break;
@@ -42,7 +42,7 @@ void MapSelectionControl::loadSelectedMaps() {
}
void MapSelectionControl::updateSelectedMaps() {
- for (QAbstractButton *button : buttons) {
+ for (QAbstractButton *button : maps) {
QString key = mapData.key(button->text());
if (button->isChecked() && !mapSelections.contains(key)) {
mapSelections.append(key);
diff --git a/selfdrive/frogpilot/navigation/ui/navigation_functions.h b/selfdrive/frogpilot/navigation/ui/navigation_functions.h
index ce9cf1a781c04b..855d88b98c0b78 100644
--- a/selfdrive/frogpilot/navigation/ui/navigation_functions.h
+++ b/selfdrive/frogpilot/navigation/ui/navigation_functions.h
@@ -49,7 +49,7 @@ inline QMap africaMap = {
{"KM", "Comoros"}, {"CG", "Congo (Brazzaville)"}, {"CD", "Congo (Kinshasa)"},
{"DJ", "Djibouti"}, {"EG", "Egypt"}, {"GQ", "Equatorial Guinea"},
{"ER", "Eritrea"}, {"ET", "Ethiopia"}, {"GA", "Gabon"},
- {"GM", "Gambia"}, {"GH", "Ghana"}, {"GN", "Guinea"},
+ {"GM", "Gambia"}, {"GH", "Ghana"}, {"GN", "Guinea"},
{"GW", "Guinea-Bissau"}, {"CI", "Ivory Coast"}, {"KE", "Kenya"},
{"LS", "Lesotho"}, {"LR", "Liberia"}, {"LY", "Libya"},
{"MG", "Madagascar"}, {"MW", "Malawi"}, {"ML", "Mali"},
@@ -126,23 +126,18 @@ inline QMap southAmericaMap = {
{"VE", "Venezuela"}
};
-inline bool isMapdRunning() {
- return std::system("pgrep mapd > /dev/null 2>&1") == 0;
-}
-
-namespace fs = std::filesystem;
-
inline QString calculateDirectorySize(const QString &directoryPath) {
- constexpr uintmax_t oneMB = 1024 * 1024;
- constexpr uintmax_t oneGB = 1024 * 1024 * 1024;
+ namespace fs = std::filesystem;
- uintmax_t totalSize = 0;
- fs::path path(directoryPath.toStdString());
+ constexpr double oneMB = 1024 * 1024;
+ constexpr double oneGB = 1024 * 1024 * 1024;
+ fs::path path(directoryPath.toStdString());
if (!fs::exists(path) || !fs::is_directory(path)) {
return "0 MB";
}
+ double totalSize = 0;
for (fs::recursive_directory_iterator iter(path, fs::directory_options::skip_permission_denied), end; iter != end; ++iter) {
const fs::directory_entry &entry = *iter;
if (entry.is_regular_file()) {
@@ -151,35 +146,28 @@ inline QString calculateDirectorySize(const QString &directoryPath) {
}
if (totalSize >= oneGB) {
- return QString::number(static_cast(totalSize) / oneGB, 'f', 2) + " GB";
- } else {
- return QString::number(static_cast(totalSize) / oneMB, 'f', 2) + " MB";
+ return QString::number(totalSize / oneGB, 'f', 2) + " GB";
}
+ return QString::number(totalSize / oneMB, 'f', 2) + " MB";
+}
+
+inline QString daySuffix(int day) {
+ if (day % 10 == 1 && day != 11) return "st";
+ if (day % 10 == 2 && day != 12) return "nd";
+ if (day % 10 == 3 && day != 13) return "rd";
+ return "th";
}
inline QString formatCurrentDate() {
QDate currentDate = QDate::currentDate();
- int day = currentDate.day();
-
- QString suffix;
- if (day % 10 == 1 && day != 11) {
- suffix = "st";
- } else if (day % 10 == 2 && day != 12) {
- suffix = "nd";
- } else if (day % 10 == 3 && day != 13) {
- suffix = "rd";
- } else {
- suffix = "th";
- }
-
- return currentDate.toString("MMMM d'") + suffix + QString(", %1").arg(currentDate.year());
+ return currentDate.toString("MMMM d'") + daySuffix(currentDate.day()) + QString(", %1").arg(currentDate.year());
}
-inline QString formatElapsedTime(qint64 elapsedMilliseconds) {
- qint64 totalSeconds = elapsedMilliseconds / 1000;
- qint64 hours = totalSeconds / 3600;
- qint64 minutes = (totalSeconds % 3600) / 60;
- qint64 seconds = totalSeconds % 60;
+inline QString formatElapsedTime(float elapsedMilliseconds) {
+ int totalSeconds = elapsedMilliseconds / 1000;
+ int hours = totalSeconds / 3600;
+ int minutes = (totalSeconds % 3600) / 60;
+ int seconds = totalSeconds % 60;
QString formattedTime;
if (hours > 0) {
@@ -190,7 +178,27 @@ inline QString formatElapsedTime(qint64 elapsedMilliseconds) {
}
formattedTime += QString::number(seconds) + (seconds == 1 ? " second" : " seconds");
- return formattedTime;
+ return formattedTime.trimmed();
+}
+
+inline QString formatETA(float elapsedTime, int downloadedFiles, int previousDownloadedFiles, int totalFiles, QDateTime &startTime) {
+ static QDateTime estimatedFinishTime;
+
+ static float previousElapsedTime;
+
+ if (downloadedFiles != previousDownloadedFiles) {
+ estimatedFinishTime = startTime.addMSecs((elapsedTime * totalFiles) / downloadedFiles);
+ } else {
+ estimatedFinishTime = estimatedFinishTime.addSecs((previousElapsedTime - elapsedTime) / 1000);
+ }
+ previousElapsedTime = elapsedTime;
+
+ int remainingTime = QDateTime::currentDateTime().secsTo(estimatedFinishTime);
+
+ QString estimatedFinishTimeStr = estimatedFinishTime.toString("h:mm AP");
+ QString remainingTimeStr = formatElapsedTime(remainingTime * 1000);
+
+ return QString("%1 (%2)").arg(remainingTimeStr).arg(estimatedFinishTimeStr);
}
class MapSelectionControl : public QWidget {
@@ -203,19 +211,19 @@ class MapSelectionControl : public QWidget {
void loadSelectedMaps();
void updateSelectedMaps();
+ bool isCountry;
+
Params params;
- QButtonGroup *buttonGroup;
+ QButtonGroup *mapButtons;
- QGridLayout *gridLayout;
+ QGridLayout *mapLayout;
QJsonArray mapSelections;
- QList buttons;
+ QList maps;
QMap mapData;
QString selectionType;
-
- bool isCountry;
};
diff --git a/selfdrive/frogpilot/navigation/ui/primeless_settings.cc b/selfdrive/frogpilot/navigation/ui/primeless_settings.cc
index 9af980a697d9c3..1dc843c4a68611 100644
--- a/selfdrive/frogpilot/navigation/ui/primeless_settings.cc
+++ b/selfdrive/frogpilot/navigation/ui/primeless_settings.cc
@@ -1,114 +1,201 @@
#include "selfdrive/frogpilot/navigation/ui/primeless_settings.h"
-FrogPilotPrimelessPanel::FrogPilotPrimelessPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
- addItem(ipLabel = new LabelControl(tr("Manage Your Settings At"), tr("Device Offline")));
+void FrogPilotPrimelessPanel::createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string ¶mKey, const QString &prefix, FrogPilotListWidget *list) {
+ control = new ButtonControl(label, "", tr("Manage your %1.").arg(label));
+ QObject::connect(control, &ButtonControl::clicked, [=] {
+ if (control->text() == tr("ADD")) {
+ QString key = InputDialog::getText(tr("Enter your %1").arg(label), this).trimmed();
- std::vector searchOptions{tr("MapBox"), tr("Amap"), tr("Google")};
- searchInput = new ButtonParamControl("SearchInput", tr("Destination Search Provider"),
- tr("The search provider used for destination queries in 'Navigate on Openpilot'. Options include 'MapBox' (recommended), 'Amap', and 'Google Maps'."),
- "", searchOptions);
- addItem(searchInput);
+ if (!key.startsWith(prefix)) {
+ key = prefix + key;
+ }
+ if (key.length() >= 80) {
+ params.put(paramKey, key.toStdString());
+ } else {
+ FrogPilotConfirmationDialog::toggleAlert(tr("Inputted key is invalid or too short!"), tr("Okay"), this);
+ }
+ } else {
+ if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove your %1?").arg(label), this)) {
+ control->setText(tr("ADD"));
- createMapboxKeyControl(publicMapboxKeyControl, tr("Public Mapbox Key"), "MapboxPublicKey", "pk.");
- createMapboxKeyControl(secretMapboxKeyControl, tr("Secret Mapbox Key"), "MapboxSecretKey", "sk.");
+ params.put(paramKey, "0");
+ paramsStorage.put(paramKey, "0");
- setupButton = new ButtonControl(tr("MapBox Setup Instructions"), tr("VIEW"), tr("View the instructions to set up 'MapBox' for 'Primeless Navigation'."), this);
- QObject::connect(setupButton, &ButtonControl::clicked, [this]() {
- displayMapboxInstructions(true);
- openMapBoxInstructions();
- updateStep();
+ setupCompleted = false;
+ }
+ }
});
- addItem(setupButton);
+ control->setText(QString::fromStdString(params.get(paramKey)).startsWith(prefix) ? tr("REMOVE") : tr("ADD"));
+ list->addItem(control);
+}
- imageLabel = new QLabel(this);
- addItem(imageLabel);
+FrogPilotPrimelessPanel::FrogPilotPrimelessPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
+ QVBoxLayout *mainLayout = new QVBoxLayout();
+ addItem(mainLayout);
- QObject::connect(parent, &FrogPilotSettingsWindow::closeMapBoxInstructions, [this]() {displayMapboxInstructions(false);});
- QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotPrimelessPanel::updateState);
+ primelessLayout = new QStackedLayout();
+ mainLayout->addLayout(primelessLayout);
- displayMapboxInstructions(false);
-}
+ FrogPilotListWidget *settingsList = new FrogPilotListWidget(this);
+ ipLabel = new LabelControl(tr("Manage Your Settings At"), tr("Device Offline"));
+ settingsList->addItem(ipLabel);
-void FrogPilotPrimelessPanel::showEvent(QShowEvent *event) {
- QString ipAddress = uiState()->wifi->getIp4Address();
- ipLabel->setText(ipAddress.isEmpty() ? tr("Device Offline") : QString("%1:8082").arg(ipAddress));
+ std::vector searchOptions{tr("MapBox"), tr("Amap"), tr("Google")};
+ ButtonParamControl *searchInput = new ButtonParamControl("SearchInput", tr("Destination Search Provider"),
+ tr("The search provider used for destination queries in 'Navigate on Openpilot'. "
+ "Options include 'MapBox' (recommended), 'Amap', and 'Google Maps'."),
+ "", searchOptions);
+ QObject::connect(searchInput, &ButtonParamControl::buttonClicked, [this](int id) {
+ amapKeyControl1->setVisible(id == 1);
+ amapKeyControl2->setVisible(id == 1);
- mapboxPublicKeySet = !params.get("MapboxPublicKey").empty();
- mapboxSecretKeySet = !params.get("MapboxSecretKey").empty();
- setupCompleted = mapboxPublicKeySet && mapboxSecretKeySet;
+ googleKeyControl->setVisible(id == 2);
- publicMapboxKeyControl->setText(mapboxPublicKeySet ? tr("REMOVE") : tr("ADD"));
- secretMapboxKeyControl->setText(mapboxSecretKeySet ? tr("REMOVE") : tr("ADD"));
-}
+ publicMapboxKeyControl->setVisible(id == 0);
+ secretMapboxKeyControl->setVisible(id == 0);
-void FrogPilotPrimelessPanel::updateState() {
- if (!isVisible()) {
- return;
- }
+ update();
+ });
+ settingsList->addItem(searchInput);
- if (imageLabel->isVisible()) {
- mapboxPublicKeySet = !params.get("MapboxPublicKey").empty();
- mapboxSecretKeySet = !params.get("MapboxSecretKey").empty();
+ amapKeyControl1 = new ButtonControl(tr("Amap Key #1"), "", tr("Manage your Amap key."));
+ QObject::connect(amapKeyControl1, &ButtonControl::clicked, [=] {
+ if (amapKeyControl1->text() == tr("ADD")) {
+ QString key = InputDialog::getText(tr("Enter your Amap key"), this).trimmed();
- updateStep();
- }
+ if (key.length() >= 39) {
+ params.put("AMapKey1", key.toStdString());
+ } else {
+ FrogPilotConfirmationDialog::toggleAlert(tr("Inputted key is invalid or too short!"), tr("Okay"), this);
+ }
+ } else {
+ if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove your Amap key?"), this)) {
+ amapKeyControl1->setText(tr("ADD"));
- parent->keepScreenOn = imageLabel->isVisible();
-}
+ params.put("AMapKey1", "0");
+ paramsStorage.put("AMapKey1", "0");
+ }
+ }
+ });
+ amapKeyControl1->setText(QString::fromStdString(params.get("AMapKey1")) != "0" ? tr("REMOVE") : tr("ADD"));
+ settingsList->addItem(amapKeyControl1);
-void FrogPilotPrimelessPanel::createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string ¶mKey, const QString &prefix) {
- control = new ButtonControl(label, "", tr("Manage your %1.").arg(label));
+ amapKeyControl2 = new ButtonControl(tr("Amap Key #2"), "", tr("Manage your Amap key."));
+ QObject::connect(amapKeyControl2, &ButtonControl::clicked, [=] {
+ if (amapKeyControl2->text() == tr("ADD")) {
+ QString key = InputDialog::getText(tr("Enter your Amap key"), this).trimmed();
- QObject::connect(control, &ButtonControl::clicked, [=] {
- if (control->text() == tr("ADD")) {
- QString key = InputDialog::getText(tr("Enter your %1").arg(label), this).trimmed();
+ if (key.length() >= 39) {
+ params.put("AMapKey2", key.toStdString());
+ } else {
+ FrogPilotConfirmationDialog::toggleAlert(tr("Inputted key is invalid or too short!"), tr("Okay"), this);
+ }
+ } else {
+ if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove your Amap key?"), this)) {
+ amapKeyControl2->setText(tr("ADD"));
- if (!key.startsWith(prefix)) {
- key = prefix + key;
+ params.put("AMapKey2", "0");
+ paramsStorage.put("AMapKey2", "0");
}
- if (key.length() >= 80) {
- params.put(paramKey, key.toStdString());
+ }
+ });
+ amapKeyControl2->setText(QString::fromStdString(params.get("AMapKey2")) != "0" ? tr("REMOVE") : tr("ADD"));
+ settingsList->addItem(amapKeyControl2);
+
+ googleKeyControl = new ButtonControl(tr("Google Maps Key"), "", tr("Manage your Google Maps key."));
+ QObject::connect(googleKeyControl, &ButtonControl::clicked, [=] {
+ if (googleKeyControl->text() == tr("ADD")) {
+ QString key = InputDialog::getText(tr("Enter your Google Maps key"), this).trimmed();
+
+ if (key.length() >= 25) {
+ params.put("GMapKey", key.toStdString());
} else {
FrogPilotConfirmationDialog::toggleAlert(tr("Inputted key is invalid or too short!"), tr("Okay"), this);
}
} else {
- if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove your %1?").arg(label), this)) {
- control->setText(tr("ADD"));
-
- params.remove(paramKey);
- paramsStorage.remove(paramKey);
+ if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove your Google Maps key?"), this)) {
+ googleKeyControl->setText(tr("ADD"));
- setupCompleted = false;
+ params.put("GMapKey", "0");
+ paramsStorage.put("GMapKey", "0");
}
}
});
+ googleKeyControl->setText(QString::fromStdString(params.get("GMapKey")) != "0" ? tr("REMOVE") : tr("ADD"));
+ settingsList->addItem(googleKeyControl);
+
+ createMapboxKeyControl(publicMapboxKeyControl, tr("Public Mapbox Key"), "MapboxPublicKey", "pk.", settingsList);
+ createMapboxKeyControl(secretMapboxKeyControl, tr("Secret Mapbox Key"), "MapboxSecretKey", "sk.", settingsList);
+
+ ButtonControl *setupButton = new ButtonControl(tr("MapBox Setup Instructions"), tr("VIEW"), tr("View the instructions to set up 'MapBox' for 'Primeless Navigation'."), this);
+ QObject::connect(setupButton, &ButtonControl::clicked, [this]() {
+ openMapBoxInstructions();
+ updateStep();
+ primelessLayout->setCurrentIndex(1);
+ });
+ settingsList->addItem(setupButton);
+
+ ScrollView *settingsPanel = new ScrollView(settingsList, this);
+ primelessLayout->addWidget(settingsPanel);
+
+ imageLabel = new QLabel(this);
+
+ ScrollView *instructionsPanel = new ScrollView(imageLabel, this);
+ primelessLayout->addWidget(instructionsPanel);
+
+ QObject::connect(parent, &FrogPilotSettingsWindow::closeMapBoxInstructions, [this] {primelessLayout->setCurrentIndex(0);});
+ QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotPrimelessPanel::updateState);
+}
+
+void FrogPilotPrimelessPanel::showEvent(QShowEvent *event) {
+ QString ipAddress = uiState()->wifi->getIp4Address();
+ ipLabel->setText(ipAddress.isEmpty() ? tr("Device Offline") : QString("%1:8082").arg(ipAddress));
+
+ mapboxPublicKeySet = QString::fromStdString(params.get("MapboxPublicKey")).startsWith("pk");
+ mapboxSecretKeySet = QString::fromStdString(params.get("MapboxSecretKey")).startsWith("sk");
+ setupCompleted = mapboxPublicKeySet && mapboxSecretKeySet;
+
+ publicMapboxKeyControl->setText(mapboxPublicKeySet ? tr("REMOVE") : tr("ADD"));
+ secretMapboxKeyControl->setText(mapboxSecretKeySet ? tr("REMOVE") : tr("ADD"));
+
+ int searchInput = params.getInt("SearchInput");
+
+ amapKeyControl1->setVisible(searchInput == 1);
+ amapKeyControl2->setVisible(searchInput == 1);
+
+ googleKeyControl->setVisible(searchInput == 2);
- control->setText(params.get(paramKey).empty() ? tr("ADD") : tr("REMOVE"));
- addItem(control);
+ publicMapboxKeyControl->setVisible(searchInput == 0);
+ secretMapboxKeyControl->setVisible(searchInput == 0);
}
void FrogPilotPrimelessPanel::hideEvent(QHideEvent *event) {
- displayMapboxInstructions(false);
+ primelessLayout->setCurrentIndex(0);
}
void FrogPilotPrimelessPanel::mousePressEvent(QMouseEvent *event) {
- closeMapBoxInstructions();
- displayMapboxInstructions(false);
+ if (primelessLayout->currentIndex() == 1) {
+ closeMapBoxInstructions();
+ primelessLayout->setCurrentIndex(0);
+ }
}
-void FrogPilotPrimelessPanel::displayMapboxInstructions(bool visible) {
- setUpdatesEnabled(false);
+void FrogPilotPrimelessPanel::updateState() {
+ if (!isVisible()) {
+ return;
+ }
- imageLabel->setVisible(visible);
- ipLabel->setVisible(!visible);
- publicMapboxKeyControl->setVisible(!visible);
- searchInput->setVisible(!visible);
- secretMapboxKeyControl->setVisible(!visible);
- setupButton->setVisible(!visible);
+ if (primelessLayout->currentIndex() == 1) {
+ mapboxPublicKeySet = QString::fromStdString(params.get("MapboxPublicKey")).startsWith("pk");
+ mapboxSecretKeySet = QString::fromStdString(params.get("MapboxSecretKey")).startsWith("sk");
- setUpdatesEnabled(true);
+ publicMapboxKeyControl->setText(mapboxPublicKeySet ? tr("REMOVE") : tr("ADD"));
+ secretMapboxKeyControl->setText(mapboxSecretKeySet ? tr("REMOVE") : tr("ADD"));
- update();
+ updateStep();
+ }
+
+ parent->keepScreenOn = primelessLayout->currentIndex() == 1;
}
void FrogPilotPrimelessPanel::updateStep() {
diff --git a/selfdrive/frogpilot/navigation/ui/primeless_settings.h b/selfdrive/frogpilot/navigation/ui/primeless_settings.h
index 935ab2ad968a02..9d49064740b78c 100644
--- a/selfdrive/frogpilot/navigation/ui/primeless_settings.h
+++ b/selfdrive/frogpilot/navigation/ui/primeless_settings.h
@@ -12,20 +12,25 @@ class FrogPilotPrimelessPanel : public FrogPilotListWidget {
void closeMapBoxInstructions();
void openMapBoxInstructions();
-private:
- void createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string ¶mKey, const QString &prefix);
- void displayMapboxInstructions(bool visible);
+protected:
void hideEvent(QHideEvent *event);
+ void showEvent(QShowEvent *event) override;
+
+private:
+ void createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string ¶mKey, const QString &prefix, FrogPilotListWidget *list);
void mousePressEvent(QMouseEvent *event);
- void showEvent(QShowEvent *event);
void updateState();
void updateStep();
+ bool mapboxPublicKeySet;
+ bool mapboxSecretKeySet;
+ bool setupCompleted;
+
+ ButtonControl *amapKeyControl1;
+ ButtonControl *amapKeyControl2;
+ ButtonControl *googleKeyControl;
ButtonControl *publicMapboxKeyControl;
ButtonControl *secretMapboxKeyControl;
- ButtonControl *setupButton;
-
- ButtonParamControl *searchInput;
FrogPilotSettingsWindow *parent;
@@ -34,9 +39,7 @@ class FrogPilotPrimelessPanel : public FrogPilotListWidget {
Params params;
Params paramsStorage{"/persist/params"};
- bool mapboxPublicKeySet;
- bool mapboxSecretKeySet;
- bool setupCompleted;
-
QLabel *imageLabel;
+
+ QStackedLayout *primelessLayout;
};
diff --git a/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc
index 2b6532fd8bca46..9042c1388ea305 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc
+++ b/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc
@@ -12,18 +12,14 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
parent->keepScreenOn = true;
deleteDrivingDataBtn->setEnabled(false);
-
deleteDrivingDataBtn->setValue(tr("Deleting..."));
realdataDir.removeRecursively();
realdataDir.mkpath(".");
deleteDrivingDataBtn->setValue(tr("Deleted!"));
-
util::sleep_for(2500);
-
deleteDrivingDataBtn->setEnabled(true);
-
deleteDrivingDataBtn->setValue("");
parent->keepScreenOn = false;
@@ -45,7 +41,6 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
parent->keepScreenOn = true;
screenRecordingsBtn->setEnabled(false);
-
screenRecordingsBtn->setValue(tr("Deleting..."));
screenRecordingsBtn->setVisibleButton(1, false);
@@ -54,11 +49,8 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
QFile::remove(recordingsDir.absoluteFilePath(selection));
screenRecordingsBtn->setValue(tr("Deleted!"));
-
util::sleep_for(2500);
-
screenRecordingsBtn->setEnabled(true);
-
screenRecordingsBtn->setValue("");
screenRecordingsBtn->setVisibleButton(1, true);
@@ -75,7 +67,6 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
parent->keepScreenOn = true;
screenRecordingsBtn->setEnabled(false);
-
screenRecordingsBtn->setValue(tr("Deleting..."));
screenRecordingsBtn->setVisibleButton(0, false);
@@ -85,11 +76,8 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
recordingsDir.mkpath(".");
screenRecordingsBtn->setValue(tr("Deleted!"));
-
util::sleep_for(2500);
-
screenRecordingsBtn->setEnabled(true);
-
screenRecordingsBtn->setValue("");
screenRecordingsBtn->setVisibleButton(0, true);
@@ -112,7 +100,6 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
parent->keepScreenOn = true;
screenRecordingsBtn->setEnabled(false);
-
screenRecordingsBtn->setValue(tr("Renaming..."));
screenRecordingsBtn->setVisibleButton(0, false);
@@ -120,15 +107,11 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
QString newPath = recordingsDir.absoluteFilePath(newName);
QString oldPath = recordingsDir.absoluteFilePath(selection);
-
QFile::rename(oldPath, newPath);
screenRecordingsBtn->setValue(tr("Renamed!"));
-
util::sleep_for(2500);
-
screenRecordingsBtn->setEnabled(true);
-
screenRecordingsBtn->setValue("");
screenRecordingsBtn->setVisibleButton(0, true);
@@ -159,7 +142,6 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
parent->keepScreenOn = true;
frogpilotBackupBtn->setEnabled(false);
-
frogpilotBackupBtn->setValue(tr("Backing up..."));
frogpilotBackupBtn->setVisibleButton(1, false);
@@ -183,11 +165,8 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
}
frogpilotBackupBtn->setValue(tr("Backup created!"));
-
util::sleep_for(2500);
-
frogpilotBackupBtn->setEnabled(true);
-
frogpilotBackupBtn->setValue("");
frogpilotBackupBtn->setVisibleButton(1, true);
@@ -205,12 +184,13 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
std::thread([=]() {
parent->keepScreenOn = true;
+ frogpilotBackupBtn->setEnabled(false);
+ frogpilotBackupBtn->setValue(tr("Deleting..."));
+
frogpilotBackupBtn->setVisibleButton(0, false);
frogpilotBackupBtn->setVisibleButton(2, false);
frogpilotBackupBtn->setVisibleButton(3, false);
- frogpilotBackupBtn->setValue(tr("Deleting..."));
-
QDir dirToDelete(backupDir.filePath(selection));
if (selection.endsWith(".tar.gz")) {
QFile::remove(dirToDelete.absolutePath());
@@ -218,11 +198,10 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
dirToDelete.removeRecursively();
}
+ frogpilotBackupBtn->setValue(tr("Deleted!"));
util::sleep_for(2500);
-
- frogpilotBackupBtn->setValue("");
-
frogpilotBackupBtn->setEnabled(true);
+ frogpilotBackupBtn->setValue("");
frogpilotBackupBtn->setVisibleButton(0, true);
frogpilotBackupBtn->setVisibleButton(2, true);
@@ -239,23 +218,19 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
parent->keepScreenOn = true;
frogpilotBackupBtn->setEnabled(false);
+ frogpilotBackupBtn->setValue(tr("Deleting..."));
frogpilotBackupBtn->setVisibleButton(0, false);
frogpilotBackupBtn->setVisibleButton(1, false);
frogpilotBackupBtn->setVisibleButton(3, false);
- frogpilotBackupBtn->setValue(tr("Deleting..."));
-
backupDir.removeRecursively();
backupDir.mkpath(".");
frogpilotBackupBtn->setValue(tr("Deleted!"));
-
util::sleep_for(2500);
-
- frogpilotBackupBtn->setValue("");
-
frogpilotBackupBtn->setEnabled(true);
+ frogpilotBackupBtn->setValue("");
frogpilotBackupBtn->setVisibleButton(0, true);
frogpilotBackupBtn->setVisibleButton(1, true);
@@ -273,6 +248,7 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
parent->keepScreenOn = true;
frogpilotBackupBtn->setEnabled(false);
+ frogpilotBackupBtn->setValue(tr("Restoring..."));
frogpilotBackupBtn->setVisibleButton(0, false);
frogpilotBackupBtn->setVisibleButton(1, false);
@@ -291,8 +267,6 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
sourcePath = extractDirectory;
}
- frogpilotBackupBtn->setValue(tr("Restoring..."));
-
std::filesystem::create_directories(targetPath);
std::system(("rsync -av --delete -l " + sourcePath + "/ " + targetPath + "/").c_str());
@@ -306,11 +280,8 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
params.putBool("AutomaticUpdates", false);
frogpilotBackupBtn->setValue(tr("Restored!"));
-
util::sleep_for(2500);
-
frogpilotBackupBtn->setValue(tr("Rebooting..."));
-
util::sleep_for(2500);
Hardware::reboot();
@@ -337,7 +308,6 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
parent->keepScreenOn = true;
toggleBackupBtn->setEnabled(false);
-
toggleBackupBtn->setValue(tr("Backing up..."));
toggleBackupBtn->setVisibleButton(1, false);
@@ -354,11 +324,8 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
std::filesystem::rename(inProgressBackupPath, fullBackupPath);
toggleBackupBtn->setValue(tr("Backup created!"));
-
util::sleep_for(2500);
-
toggleBackupBtn->setEnabled(true);
-
toggleBackupBtn->setValue("");
toggleBackupBtn->setVisibleButton(1, true);
@@ -376,20 +343,20 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
std::thread([=]() {
parent->keepScreenOn = true;
+ toggleBackupBtn->setEnabled(false);
+ toggleBackupBtn->setValue(tr("Deleting..."));
+
toggleBackupBtn->setVisibleButton(0, false);
toggleBackupBtn->setVisibleButton(2, false);
toggleBackupBtn->setVisibleButton(3, false);
- toggleBackupBtn->setValue(tr("Deleting..."));
-
QDir dirToDelete(backupDir.filePath(selection));
dirToDelete.removeRecursively();
+ toggleBackupBtn->setValue(tr("Deleted!"));
util::sleep_for(2500);
-
- toggleBackupBtn->setValue("");
-
toggleBackupBtn->setEnabled(true);
+ toggleBackupBtn->setValue("");
toggleBackupBtn->setVisibleButton(0, true);
toggleBackupBtn->setVisibleButton(2, true);
@@ -406,23 +373,19 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
parent->keepScreenOn = true;
toggleBackupBtn->setEnabled(false);
+ toggleBackupBtn->setValue(tr("Deleting..."));
toggleBackupBtn->setVisibleButton(0, false);
toggleBackupBtn->setVisibleButton(1, false);
toggleBackupBtn->setVisibleButton(3, false);
- toggleBackupBtn->setValue(tr("Deleting..."));
-
backupDir.removeRecursively();
backupDir.mkpath(".");
toggleBackupBtn->setValue(tr("Deleted!"));
-
util::sleep_for(2500);
-
- toggleBackupBtn->setValue("");
-
toggleBackupBtn->setEnabled(true);
+ toggleBackupBtn->setValue("");
toggleBackupBtn->setVisibleButton(0, true);
toggleBackupBtn->setVisibleButton(1, true);
@@ -440,13 +403,12 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
parent->keepScreenOn = true;
toggleBackupBtn->setEnabled(false);
+ toggleBackupBtn->setValue(tr("Restoring..."));
toggleBackupBtn->setVisibleButton(0, false);
toggleBackupBtn->setVisibleButton(1, false);
toggleBackupBtn->setVisibleButton(2, false);
- toggleBackupBtn->setValue(tr("Restoring..."));
-
std::string sourcePath = backupDir.filePath(selection).toStdString();
std::string targetPath = "/data/params/d";
@@ -457,12 +419,9 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
updateFrogPilotToggles();
toggleBackupBtn->setValue(tr("Restored!"));
-
util::sleep_for(2500);
-
- toggleBackupBtn->setValue("");
-
toggleBackupBtn->setEnabled(true);
+ toggleBackupBtn->setValue("");
toggleBackupBtn->setVisibleButton(0, true);
toggleBackupBtn->setVisibleButton(1, true);
diff --git a/selfdrive/frogpilot/ui/qt/offroad/device_settings.h b/selfdrive/frogpilot/ui/qt/offroad/device_settings.h
index a81f984fa41606..897c428d5ea8ed 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/device_settings.h
+++ b/selfdrive/frogpilot/ui/qt/offroad/device_settings.h
@@ -19,10 +19,6 @@ class FrogPilotDevicePanel : public FrogPilotListWidget {
void showToggles(const std::set &keys);
void updateState(const UIState &s);
- FrogPilotSettingsWindow *parent;
-
- QJsonObject frogpilotToggleLevels;
-
bool started;
int tuningLevel;
@@ -31,4 +27,8 @@ class FrogPilotDevicePanel : public FrogPilotListWidget {
std::set deviceManagementKeys = {"DeviceShutdown", "IncreaseThermalLimits", "LowVoltageShutdown", "NoLogging", "NoUploads", "OfflineMode"};
std::set screenKeys = {"ScreenBrightness", "ScreenBrightnessOnroad", "ScreenRecorder", "ScreenTimeout", "ScreenTimeoutOnroad"};
+
+ FrogPilotSettingsWindow *parent;
+
+ QJsonObject frogpilotToggleLevels;
};
diff --git a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc
index 1971c6cca0c31c..51e4d3b1726366 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc
+++ b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc
@@ -1,5 +1,3 @@
-#include "selfdrive/ui/qt/widgets/scrollview.h"
-
#include "selfdrive/frogpilot/navigation/ui/maps_settings.h"
#include "selfdrive/frogpilot/navigation/ui/primeless_settings.h"
#include "selfdrive/frogpilot/ui/qt/offroad/data_settings.h"
@@ -67,12 +65,12 @@ void FrogPilotSettingsWindow::createPanelButtons(FrogPilotListWidget *list) {
};
std::vector> panelInfo = {
- {tr("Alerts and Sounds"), tr("Manage FrogPilot's alerts and notifications."), "../frogpilot/assets/toggle_icons/icon_sound.png"},
- {tr("Driving Controls"), tr("Adjust features that affect acceleration, braking, and steering."), "../frogpilot/assets/toggle_icons/icon_steering.png"},
- {tr("Navigation"), tr("Download map data and configure 'Navigate On openpilot (NOO)'."), "../frogpilot/assets/toggle_icons/icon_map.png"},
- {tr("System Management"), tr("Access tools, utilities, and device controls to maintain and troubleshoot FrogPilot."), "../frogpilot/assets/toggle_icons/icon_system.png"},
- {tr("Theme and Appearance"), tr("Customize the openpilot theme, UI appearance, and on-road widgets."), "../frogpilot/assets/toggle_icons/icon_display.png"},
- {tr("Vehicle Controls"), tr("Configure vehicle-specific settings for supported makes and models."), "../frogpilot/assets/toggle_icons/icon_vehicle.png"}
+ {tr("Alerts and Sounds"), tr("Manage FrogPilot's alerts and sounds."), "../frogpilot/assets/toggle_icons/icon_sound.png"},
+ {tr("Driving Controls"), tr("Manage FrogPilot's features that affect acceleration, braking, and steering."), "../frogpilot/assets/toggle_icons/icon_steering.png"},
+ {tr("Navigation"), tr("Manage map data to be used with 'Curve Speed Control' and 'Speed Limit Controller' and setup 'Navigate On openpilot (NOO)' without a comma prime subscription."), "../frogpilot/assets/toggle_icons/icon_map.png"},
+ {tr("System Management"), tr("Manage the device's internal settings along with other tools and utilities to maintain and troubleshoot FrogPilot."), "../frogpilot/assets/toggle_icons/icon_system.png"},
+ {tr("Theme and Appearance"), tr("Manage openpilot's theme and onroad widgets."), "../frogpilot/assets/toggle_icons/icon_display.png"},
+ {tr("Vehicle Controls"), tr("Manage vehicle-specific settings."), "../frogpilot/assets/toggle_icons/icon_vehicle.png"}
};
for (size_t i = 0; i < panelInfo.size(); ++i) {
@@ -114,13 +112,17 @@ void FrogPilotSettingsWindow::createPanelButtons(FrogPilotListWidget *list) {
FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFrame(parent) {
mainLayout = new QStackedLayout(this);
- frogpilotWidget = new QWidget(this);
+ QWidget *frogpilotWidget = new QWidget(this);
QVBoxLayout *frogpilotLayout = new QVBoxLayout(frogpilotWidget);
frogpilotLayout->setContentsMargins(50, 25, 50, 25);
+ frogpilotWidget->setLayout(frogpilotLayout);
+
+ frogpilotPanel = new ScrollView(frogpilotWidget, this);
+ mainLayout->addWidget(frogpilotPanel);
+ frogpilotPanel->setWidget(frogpilotWidget);
FrogPilotListWidget *list = new FrogPilotListWidget(this);
frogpilotLayout->addWidget(list);
- mainLayout->addWidget(frogpilotWidget);
std::vector togglePresets{tr("Minimal"), tr("Standard"), tr("Advanced"), tr("Developer")};
ButtonParamControl *togglePreset = new ButtonParamControl("TuningLevel", tr("Tuning Level"),
@@ -132,8 +134,8 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram
"../frogpilot/assets/toggle_icons/icon_customization.png",
togglePresets);
- int timeTo100FPHours = 100 - (paramsTracking.getInt("FrogPilotMinutes") / 60);
- int timeTo250OPHours = 250 - (params.getInt("openpilotMinutes") / 60);
+ int timeTo100FPHours = 1 - (paramsTracking.getInt("FrogPilotMinutes") / 60);
+ int timeTo250OPHours = 1 - (params.getInt("openpilotMinutes") / 60);
togglePreset->setEnabledButtons(3, timeTo100FPHours <= 0 || timeTo250OPHours <= 0);
QObject::connect(togglePreset, &ButtonParamControl::buttonClicked, [this](int id) {
@@ -167,10 +169,11 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram
QObject::connect(uiState(), &UIState::offroadTransition, this, &FrogPilotSettingsWindow::updateVariables);
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotSettingsWindow::updateState);
- frogpilotToggleLevels = QJsonDocument::fromJson(QString::fromStdString(params_memory.get("FrogPilotTuningLevels", true)).toUtf8()).object();
+ frogpilotToggleLevels = QJsonDocument::fromJson(params_memory.get("FrogPilotTuningLevels", true).c_str()).object();
tuningLevel = params.getInt("TuningLevel");
closeParentToggle();
+ updateMetric(params.getBool("IsMetric"), true);
updateVariables();
}
@@ -179,7 +182,7 @@ void FrogPilotSettingsWindow::hideEvent(QHideEvent *event) {
}
void FrogPilotSettingsWindow::closePanel() {
- mainLayout->setCurrentWidget(frogpilotWidget);
+ mainLayout->setCurrentWidget(frogpilotPanel);
panelOpen = false;
updateFrogPilotToggles();
}
@@ -188,7 +191,7 @@ void FrogPilotSettingsWindow::updateState() {
UIState *s = uiState();
UIScene &scene = s->scene;
- scene.keep_screen_on = panelOpen && keepScreenOn;
+ scene.frogpilot_panel_active = panelOpen && keepScreenOn;
}
void FrogPilotSettingsWindow::updateVariables() {
diff --git a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h
index dbbc76b4b54c6c..3e1c5a1cc4ca41 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h
+++ b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h
@@ -1,6 +1,7 @@
#pragma once
#include "selfdrive/ui/qt/offroad/settings.h"
+#include "selfdrive/ui/qt/widgets/scrollview.h"
class FrogPilotSettingsWindow : public QFrame {
Q_OBJECT
@@ -10,8 +11,6 @@ class FrogPilotSettingsWindow : public QFrame {
void updateVariables();
- QJsonObject frogpilotToggleLevels;
-
bool hasAutoTune = true;
bool hasBSM = true;
bool hasDashSpeedLimits = true;
@@ -40,6 +39,8 @@ class FrogPilotSettingsWindow : public QFrame {
int tuningLevel;
+ QJsonObject frogpilotToggleLevels;
+
signals:
void closeMapBoxInstructions();
void closeMapSelection();
@@ -50,7 +51,7 @@ class FrogPilotSettingsWindow : public QFrame {
void openPanel();
void openParentToggle();
void openSubParentToggle();
- void updateMetric();
+ void updateMetric(bool metric, bool bootRun=false);
private:
void closePanel();
@@ -58,6 +59,8 @@ class FrogPilotSettingsWindow : public QFrame {
void hideEvent(QHideEvent *event) override;
void updateState();
+ bool panelOpen;
+
FrogPilotButtonsControl *drivingPanelButtons;
FrogPilotButtonsControl *navigationPanelButtons;
FrogPilotButtonsControl *systemPanelButtons;
@@ -68,7 +71,5 @@ class FrogPilotSettingsWindow : public QFrame {
QStackedLayout *mainLayout;
- QWidget *frogpilotWidget;
-
- bool panelOpen;
+ ScrollView *frogpilotPanel;
};
diff --git a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc
index 47af14dd6b0753..dc1df8fe7b134f 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc
+++ b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc
@@ -266,8 +266,6 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotLateralPanel::hideToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotLateralPanel::updateMetric);
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotLateralPanel::updateState);
-
- updateMetric();
}
void FrogPilotLateralPanel::showEvent(QShowEvent *event) {
@@ -300,13 +298,11 @@ void FrogPilotLateralPanel::updateState(const UIState &s) {
started = s.scene.started;
}
-void FrogPilotLateralPanel::updateMetric() {
- bool previousIsMetric = isMetric;
- isMetric = params.getBool("IsMetric");
-
- if (isMetric != previousIsMetric) {
- double distanceConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT;
- double speedConversion = isMetric ? MILE_TO_KM : KM_TO_MILE;
+void FrogPilotLateralPanel::updateMetric(bool metric, bool bootRun) {
+ static bool previousMetric;
+ if (metric != previousMetric && !bootRun) {
+ double distanceConversion = metric ? FOOT_TO_METER : METER_TO_FOOT;
+ double speedConversion = metric ? MILE_TO_KM : KM_TO_MILE;
params.putFloatNonBlocking("LaneDetectionWidth", params.getFloat("LaneDetectionWidth") * distanceConversion);
@@ -315,13 +311,14 @@ void FrogPilotLateralPanel::updateMetric() {
params.putFloatNonBlocking("PauseLateralOnSignal", params.getFloat("PauseLateralOnSignal") * speedConversion);
params.putFloatNonBlocking("PauseLateralSpeed", params.getFloat("PauseLateralSpeed") * speedConversion);
}
+ previousMetric = metric;
FrogPilotParamValueControl *laneWidthToggle = static_cast(toggles["LaneDetectionWidth"]);
FrogPilotParamValueControl *minimumLaneChangeSpeedToggle = static_cast(toggles["MinimumLaneChangeSpeed"]);
FrogPilotParamValueControl *pauseAOLOnBrakeToggle = static_cast(toggles["PauseAOLOnBrake"]);
FrogPilotParamValueControl *pauseLateralToggle = static_cast(toggles["PauseLateralSpeed"]);
- if (isMetric) {
+ if (metric) {
minimumLaneChangeSpeedToggle->updateControl(0, 150, tr("kph"));
pauseAOLOnBrakeToggle->updateControl(0, 99, tr("kph"));
pauseLateralToggle->updateControl(0, 99, tr("kph"));
diff --git a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h
index 34a4777c0ad9a0..2f14b4729e80ab 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h
+++ b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h
@@ -17,23 +17,11 @@ class FrogPilotLateralPanel : public FrogPilotListWidget {
void hideToggles();
void showEvent(QShowEvent *event) override;
void showToggles(const std::set &keys);
- void updateMetric();
+ void updateMetric(bool metric, bool bootRun);
void updateState(const UIState &s);
- FrogPilotParamValueButtonControl *steerFrictionToggle;
- FrogPilotParamValueButtonControl *steerLatAccelToggle;
- FrogPilotParamValueButtonControl *steerKPToggle;
- FrogPilotParamValueButtonControl *steerRatioToggle;
-
- FrogPilotSettingsWindow *parent;
-
- QJsonObject frogpilotToggleLevels;
-
- Params params;
-
bool hasAutoTune;
bool hasNNFFLog;
- bool isMetric = params.getBool("IsMetric");
bool isPIDCar;
bool isSubaru;
bool liveValid;
@@ -53,4 +41,15 @@ class FrogPilotLateralPanel : public FrogPilotListWidget {
std::set laneChangeKeys = {"LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed", "NudgelessLaneChange", "OneLaneChange"};
std::set lateralTuneKeys = {"NNFF", "NNFFLite", "TurnDesires"};
std::set qolKeys = {"PauseLateralSpeed"};
+
+ FrogPilotParamValueButtonControl *steerFrictionToggle;
+ FrogPilotParamValueButtonControl *steerLatAccelToggle;
+ FrogPilotParamValueButtonControl *steerKPToggle;
+ FrogPilotParamValueButtonControl *steerRatioToggle;
+
+ FrogPilotSettingsWindow *parent;
+
+ QJsonObject frogpilotToggleLevels;
+
+ Params params;
};
diff --git a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc
index 87493ec62551aa..4c9be8dad5de2d 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc
+++ b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc
@@ -67,6 +67,7 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
{"LeadDetectionThreshold", tr("Lead Detection Confidence"), tr("Controls how sensitive openpilot is to detecting vehicles ahead. A lower value can help detect vehicles sooner and from farther away, but increases the chance openpilot mistakes other objects for vehicles."), ""},
{"MaxDesiredAcceleration", tr("Maximum Acceleration Rate"), tr("Sets a cap on how fast openpilot can accelerate."), ""},
{"TacoTune", tr("'Taco Bell Run' Turn Speed Hack"), tr("Uses comma's speed hack they used to help handle left and right turns more precisely during their 2022 'Taco Bell' drive by reducing the maximum allowed speed and acceleration while turning."), ""},
+ {"Hattrick", tr("Wanna Go Fast Mode"), tr("Dubbed Hat Trick mode in reference to the EV6 owner who prioritizes speed at all costs!"), ""},
{"QOLLongitudinal", tr("Quality of Life Improvements"), tr("Miscellaneous longitudinal focused features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"},
{"CustomCruise", tr("Cruise Increase"), tr("Controls the interval used when increasing the cruise control speed."), ""},
@@ -538,8 +539,6 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotLongitudinalPanel::hideToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubParentToggle, this, &FrogPilotLongitudinalPanel::hideSubToggles);
QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotLongitudinalPanel::updateMetric);
-
- updateMetric();
}
void FrogPilotLongitudinalPanel::showEvent(QShowEvent *event) {
@@ -555,13 +554,11 @@ void FrogPilotLongitudinalPanel::showEvent(QShowEvent *event) {
hideToggles();
}
-void FrogPilotLongitudinalPanel::updateMetric() {
- bool previousIsMetric = isMetric;
- isMetric = params.getBool("IsMetric");
-
- if (isMetric != previousIsMetric) {
- double distanceConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT;
- double speedConversion = isMetric ? MILE_TO_KM : KM_TO_MILE;
+void FrogPilotLongitudinalPanel::updateMetric(bool metric, bool bootRun) {
+ static bool previousMetric;
+ if (metric != previousMetric && !bootRun) {
+ double distanceConversion = metric ? FOOT_TO_METER : METER_TO_FOOT;
+ double speedConversion = metric ? MILE_TO_KM : KM_TO_MILE;
params.putFloatNonBlocking("IncreasedStoppedDistance", params.getFloat("IncreasedStoppedDistance") * distanceConversion);
@@ -576,6 +573,7 @@ void FrogPilotLongitudinalPanel::updateMetric() {
params.putFloatNonBlocking("Offset4", params.getFloat("Offset4") * speedConversion);
params.putFloatNonBlocking("SetSpeedOffset", params.getFloat("SetSpeedOffset") * speedConversion);
}
+ previousMetric = metric;
FrogPilotDualParamControl *ceSpeedToggle = reinterpret_cast(toggles["CESpeed"]);
FrogPilotParamValueButtonControl *ceSignal = static_cast(toggles["CESignalSpeed"]);
@@ -588,7 +586,7 @@ void FrogPilotLongitudinalPanel::updateMetric() {
FrogPilotParamValueControl *increasedStoppedDistanceToggle = static_cast(toggles["IncreasedStoppedDistance"]);
FrogPilotParamValueControl *setSpeedOffsetToggle = static_cast(toggles["SetSpeedOffset"]);
- if (isMetric) {
+ if (metric) {
offset1Toggle->setTitle(tr("Speed Limit Offset (0-34 kph)"));
offset2Toggle->setTitle(tr("Speed Limit Offset (35-54 kph)"));
offset3Toggle->setTitle(tr("Speed Limit Offset (55-64 kph)"));
diff --git a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h
index 410f966d27b4d3..446499c42ad781 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h
+++ b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h
@@ -19,23 +19,13 @@ class FrogPilotLongitudinalPanel : public FrogPilotListWidget {
void hideToggles();
void showEvent(QShowEvent *event) override;
void showToggles(const std::set &keys);
- void updateMetric();
-
- FrogPilotSettingsWindow *parent;
-
- FrogPilotButtonsControl *curveDetectionBtn;
-
- QJsonObject frogpilotToggleLevels;
-
- Params params;
- Params params_default{"/data/params_default"};
+ void updateMetric(bool metric, bool bootRun);
bool customPersonalityOpen;
- bool hasPCMCruise;
bool hasDashSpeedLimits;
+ bool hasPCMCruise;
bool isGM;
bool isHKGCanFd;
- bool isMetric = params.getBool("IsMetric");
bool isSubaru;
bool isToyota;
bool slcOpen;
@@ -49,7 +39,7 @@ class FrogPilotLongitudinalPanel : public FrogPilotListWidget {
std::set curveSpeedKeys = {"CurveDetectionMethod", "CurveSensitivity", "HideCSCUI", "MTSCCurvatureCheck", "TurnAggressiveness"};
std::set customDrivingPersonalityKeys = {"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", "TrafficPersonalityProfile"};
std::set experimentalModeActivationKeys = {"ExperimentalModeViaDistance", "ExperimentalModeViaLKAS", "ExperimentalModeViaTap"};
- std::set longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "LeadDetectionThreshold", "MaxDesiredAcceleration", "TacoTune"};
+ std::set longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "Hattrick", "LeadDetectionThreshold", "MaxDesiredAcceleration", "TacoTune"};
std::set qolKeys = {"CustomCruise", "CustomCruiseLong", "ForceStandstill", "ForceStops", "IncreasedStoppedDistance", "MapGears", "ReverseCruise", "SetSpeedOffset"};
std::set relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", "ResetRelaxedPersonality"};
std::set speedLimitControllerKeys = {"SLCOffsets", "SLCFallback", "SLCOverride", "SLCPriority", "SLCQOL", "SLCVisuals"};
@@ -58,4 +48,13 @@ class FrogPilotLongitudinalPanel : public FrogPilotListWidget {
std::set speedLimitControllerVisualKeys = {"ShowSLCOffset", "SpeedLimitSources"};
std::set standardPersonalityKeys = {"StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration", "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", "ResetStandardPersonality"};
std::set trafficPersonalityKeys = {"TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration", "TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease", "ResetTrafficPersonality"};
+
+ FrogPilotButtonsControl *curveDetectionBtn;
+
+ FrogPilotSettingsWindow *parent;
+
+ QJsonObject frogpilotToggleLevels;
+
+ Params params;
+ Params params_default{"/data/params_default"};
};
diff --git a/selfdrive/frogpilot/ui/qt/offroad/model_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/model_settings.cc
index 60f0a9facbf170..b800557af02f73 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/model_settings.cc
+++ b/selfdrive/frogpilot/ui/qt/offroad/model_settings.cc
@@ -148,7 +148,6 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
params_memory.putBool("CancelModelDownload", true);
} else {
- QStringList downloadableModels = availableModelNames;
for (const QString &file : modelDir.entryList(QDir::Files)) {
downloadableModels.removeAll(modelFileToNameMap.value(QFileInfo(file).baseName()));
}
@@ -256,7 +255,7 @@ void FrogPilotModelPanel::showEvent(QShowEvent *event) {
modelFileToNameMapProcessed.insert(availableModels[i], processModelName(availableModelNames[i]));
}
- QStringList downloadableModels = availableModelNames;
+ downloadableModels = availableModelNames;
for (const QString &file : modelDir.entryList(QDir::Files)) {
downloadableModels.removeAll(modelFileToNameMap.value(QFileInfo(file).baseName()));
}
diff --git a/selfdrive/frogpilot/ui/qt/offroad/model_settings.h b/selfdrive/frogpilot/ui/qt/offroad/model_settings.h
index fadda7317c1cee..ac1478c323beff 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/model_settings.h
+++ b/selfdrive/frogpilot/ui/qt/offroad/model_settings.h
@@ -24,6 +24,21 @@ class FrogPilotModelPanel : public FrogPilotListWidget {
void updateModelLabels();
void updateState(const UIState &s);
+ bool allModelsDownloaded;
+ bool allModelsDownloading;
+ bool cancellingDownload;
+ bool finalizingDownload;
+ bool modelDownloading;
+ bool modelRandomizerOpen;
+ bool noModelsDownloaded;
+ bool started;
+
+ int tuningLevel;
+
+ std::map toggles;
+
+ std::set modelRandomizerKeys = {"ManageBlacklistedModels", "ResetScores", "ReviewScores"};
+
ButtonControl *selectModelBtn;
FrogPilotButtonsControl *deleteModelBtn;
@@ -47,19 +62,5 @@ class FrogPilotModelPanel : public FrogPilotListWidget {
QStringList availableModels;
QStringList availableModelNames;
-
- bool allModelsDownloaded;
- bool allModelsDownloading;
- bool cancellingDownload;
- bool finalizingDownload;
- bool modelDownloading;
- bool modelRandomizerOpen;
- bool noModelsDownloaded;
- bool started;
-
- int tuningLevel;
-
- std::map toggles;
-
- std::set modelRandomizerKeys = {"ManageBlacklistedModels", "ResetScores", "ReviewScores"};
+ QStringList downloadableModels;
};
diff --git a/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h b/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h
index 5663b9ac3c8c43..1664664bab4375 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h
+++ b/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h
@@ -18,12 +18,6 @@ class FrogPilotSoundsPanel : public FrogPilotListWidget {
void showEvent(QShowEvent *event) override;
void showToggles(const std::set &keys);
- FrogPilotSettingsWindow *parent;
-
- QJsonObject frogpilotToggleLevels;
-
- Params params;
-
bool hasBSM;
bool hasOpenpilotLongitudinal;
@@ -33,4 +27,10 @@ class FrogPilotSoundsPanel : public FrogPilotListWidget {
std::set alertVolumeControlKeys = {"DisengageVolume", "EngageVolume", "PromptDistractedVolume", "PromptVolume", "RefuseVolume", "WarningImmediateVolume", "WarningSoftVolume"};
std::set customAlertsKeys = {"GoatScream", "GreenLightAlert", "LeadDepartingAlert", "LoudBlindspotAlert", "SpeedLimitChangedAlert"};
+
+ FrogPilotSettingsWindow *parent;
+
+ QJsonObject frogpilotToggleLevels;
+
+ Params params;
};
diff --git a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc
index d2413d5897cbdc..5147cd9841839b 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc
+++ b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc
@@ -115,6 +115,9 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
{"RainbowPath", tr("Rainbow Path"), tr("Swap out the path in the onroad UI for a Mario Kart inspired 'Rainbow Path'."), "../frogpilot/assets/toggle_icons/icon_rainbow.png"},
+ {"BrakeSignal", tr("Brake Light Indicator"), tr("Turns the current speed value to red when car is braking"), "../frogpilot/assets/toggle_icons/icon_brake.png"},
+
+
{"RandomEvents", tr("Random Events"), tr("Enables random cosmetic events that happen during certain driving conditions. These events are purely for fun and don't affect driving controls!"), "../frogpilot/assets/toggle_icons/icon_random.png"},
{"StartupAlert", tr("Startup Alert"), tr("Controls the text of the 'Startup' alert message that appears when you start the drive."), "../frogpilot/assets/toggle_icons/icon_message.png"}
diff --git a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h
index 467948b4ecb0de..f9a72f87259287 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h
+++ b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h
@@ -21,6 +21,28 @@ class FrogPilotThemesPanel : public FrogPilotListWidget {
void showToggles(const std::set &keys);
void updateState(const UIState &s);
+ bool cancellingDownload;
+ bool colorDownloading;
+ bool colorsDownloaded;
+ bool distanceIconDownloading;
+ bool distanceIconsDownloaded;
+ bool finalizingDownload;
+ bool iconDownloading;
+ bool iconsDownloaded;
+ bool signalDownloading;
+ bool signalsDownloaded;
+ bool soundDownloading;
+ bool soundsDownloaded;
+ bool themeDownloading;
+ bool wheelDownloading;
+ bool wheelsDownloaded;
+
+ int tuningLevel;
+
+ std::map toggles;
+
+ std::set customThemeKeys = {"CustomColors", "CustomDistanceIcons", "CustomIcons", "CustomSignals", "CustomSounds", "DownloadStatusLabel", "WheelIcon"};
+
FrogPilotButtonsControl *manageCustomColorsBtn;
FrogPilotButtonsControl *manageCustomIconsBtn;
FrogPilotButtonsControl *manageCustomSignalsBtn;
@@ -46,26 +68,4 @@ class FrogPilotThemesPanel : public FrogPilotListWidget {
Params params;
Params params_memory{"/dev/shm/params"};
-
- bool cancellingDownload;
- bool colorDownloading;
- bool colorsDownloaded;
- bool distanceIconDownloading;
- bool distanceIconsDownloaded;
- bool finalizingDownload;
- bool iconDownloading;
- bool iconsDownloaded;
- bool signalDownloading;
- bool signalsDownloaded;
- bool soundDownloading;
- bool soundsDownloaded;
- bool themeDownloading;
- bool wheelDownloading;
- bool wheelsDownloaded;
-
- int tuningLevel;
-
- std::map toggles;
-
- std::set customThemeKeys = {"CustomColors", "CustomDistanceIcons", "CustomIcons", "CustomSignals", "CustomSounds", "DownloadStatusLabel", "WheelIcon"};
};
diff --git a/selfdrive/frogpilot/ui/qt/offroad/utilities.cc b/selfdrive/frogpilot/ui/qt/offroad/utilities.cc
index 6d3f4e34c3a072..ae11db106a90bf 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/utilities.cc
+++ b/selfdrive/frogpilot/ui/qt/offroad/utilities.cc
@@ -12,8 +12,10 @@ FrogPilotUtilitiesPanel::FrogPilotUtilitiesPanel(FrogPilotSettingsWindow *parent
flashPandaBtn->setEnabled(false);
flashPandaBtn->setValue(tr("Flashing..."));
- system("python3 /data/openpilot/panda/board/flash.py");
- system("python3 /data/openpilot/panda/board/recover.py");
+ params_memory.putBool("FlashPanda", true);
+ while (params_memory.getBool("FlashPanda")) {
+ util::sleep_for(UI_FREQ);
+ }
flashPandaBtn->setValue(tr("Flashed!"));
util::sleep_for(2500);
@@ -25,7 +27,7 @@ FrogPilotUtilitiesPanel::FrogPilotUtilitiesPanel(FrogPilotSettingsWindow *parent
});
addItem(flashPandaBtn);
- forceStartedBtn = new FrogPilotButtonsControl(tr("Force Started State"), tr("Forces openpilot either offroad or onroad."), {tr("OFFROAD"), tr("ONROAD"), tr("OFF")}, true);
+ FrogPilotButtonsControl *forceStartedBtn = new FrogPilotButtonsControl(tr("Force Started State"), tr("Forces openpilot either offroad or onroad."), {tr("OFFROAD"), tr("ONROAD"), tr("OFF")}, true);
QObject::connect(forceStartedBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) {
if (id == 0) {
params_memory.putBool("ForceOffroad", true);
@@ -33,6 +35,9 @@ FrogPilotUtilitiesPanel::FrogPilotUtilitiesPanel(FrogPilotSettingsWindow *parent
} else if (id == 1) {
params_memory.putBool("ForceOffroad", false);
params_memory.putBool("ForceOnroad", true);
+
+ util::sleep_for(1000);
+ params.put("CarParams", params.get("CarParamsPersistent"));
} else if (id == 2) {
params_memory.putBool("ForceOffroad", false);
params_memory.putBool("ForceOnroad", false);
diff --git a/selfdrive/frogpilot/ui/qt/offroad/utilities.h b/selfdrive/frogpilot/ui/qt/offroad/utilities.h
index 2271fd1b3e7434..cce68517fe74fa 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/utilities.h
+++ b/selfdrive/frogpilot/ui/qt/offroad/utilities.h
@@ -9,8 +9,6 @@ class FrogPilotUtilitiesPanel : public FrogPilotListWidget {
explicit FrogPilotUtilitiesPanel(FrogPilotSettingsWindow *parent);
private:
- FrogPilotButtonsControl *forceStartedBtn;
-
FrogPilotSettingsWindow *parent;
Params params;
diff --git a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc
index 8a58fdd4ab0fac..ceabfef5b2c58a 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc
+++ b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc
@@ -113,8 +113,8 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
forceFingerprint = new ParamControl("ForceFingerprint", tr("Disable Automatic Fingerprint Detection"), tr("Forces the selected fingerprint and prevents it from ever changing."), "");
addItem(forceFingerprint);
- bool disableOpenpilotLongState = params.getBool("DisableOpenpilotLongitudinal");
- disableOpenpilotLong = new ToggleControl(tr("Disable openpilot Longitudinal Control"), tr("Disables openpilot longitudinal control and uses the car's stock ACC instead."), "", disableOpenpilotLongState);
+ disableOpenpilotLongitudinal = params.getBool("DisableOpenpilotLongitudinal");
+ disableOpenpilotLong = new ToggleControl(tr("Disable openpilot Longitudinal Control"), tr("Disables openpilot longitudinal control and uses the car's stock ACC instead."), "", disableOpenpilotLongitudinal);
QObject::connect(disableOpenpilotLong, &ToggleControl::toggleFlipped, [this, parent](bool state) {
if (state) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely disable openpilot longitudinal control?"), this)) {
@@ -144,13 +144,14 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
{"NewLongAPIGM", tr("Use comma's New Longitudinal API"), tr("Enable comma's new control system that has shown great improvement with acceleration and braking, but has issues on some GM vehicles."), ""},
{"NewLongAPI", tr("Use comma's New Longitudinal API"), tr("Enable comma's new control system that has shown great improvement with acceleration and braking, but has issues on some Hyundai/Kia/Genesis vehicles."), ""},
-
+ {"HyundaiRadarTracks", tr("Enable Radar Tracks"), tr("Enables the cars radar tracks along with OPLong for Hyundai/Kia/Genesis vehicles, which shows vast improvement in long control."), ""},
+ {"HKGtuning", tr("Chubbs' Custom Tuning"), tr("Chubbs' Custom tuning for Hyundai/Kia/Genesis vehicles, which smoothes acceleration and braking to help achieve a 'limo' stop."), ""},
{"CrosstrekTorque", tr("Subaru Crosstrek Torque Increase"), tr("Increase the maximum allowed torque for the 'Subaru Crosstrek'."), ""},
{"ToyotaDoors", tr("Automatically Lock/Unlock Doors"), tr("Automatically lock the doors when in drive and unlock when in park."), ""},
{"ClusterOffset", tr("Cluster Speed Offset"), tr("Set the cluster offset openpilot uses to try and match the speed displayed on the dash."), ""},
{"FrogsGoMoosTweak", tr("FrogsGoMoo's Personal Tweaks"), tr("FrogsGoMoo's personal tweaks to the Toyota/Lexus tune that allows the vehicle to take off and stop a bit smoother."), ""},
- {"LockDoorsTimer", tr("Lock Doors On Ignition Off"), tr("Automatically lock the doors after the car's ignition has been turned off."), ""},
+ {"LockDoorsTimer", tr("Lock Doors On Ignition Off"), tr("Automatically lock the doors after the car's ignition has been turned off and no one is detected in either of the front seats."), ""},
{"SNGHack", tr("Stop and Go Hack"), tr("Force stop and go for vehicles without stop and go functionality."), ""},
};
@@ -184,7 +185,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
toggles[param] = vehicleToggle;
}
- std::set rebootKeys = {"CrosstrekTorque", "ExperimentalGMTune", "NewLongAPI", "NewLongAPIGM"};
+ std::set rebootKeys = {"CrosstrekTorque", "ExperimentalGMTune", "FrogsGoMoosTweak", "HyundaiRadarTracks", "HKGtuning", "NewLongAPI", "NewLongAPIGM"};
for (const QString &key : rebootKeys) {
QObject::connect(static_cast(toggles[key]), &ToggleControl::toggleFlipped, [this]() {
if (started) {
@@ -295,7 +296,7 @@ void FrogPilotVehiclesPanel::updateToggles() {
setVisible &= !hasSNG;
}
if (key == "LockDoorsTimer") {
- setVisible = allowAutoLockingDoors;
+ setVisible &= allowAutoLockingDoors;
}
}
diff --git a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h
index f2bc415fda8481..fec5005739dcc9 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h
+++ b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h
@@ -16,29 +16,8 @@ class FrogPilotVehiclesPanel : public FrogPilotListWidget {
void updateState(const UIState &s);
void updateToggles();
- ButtonControl *selectMakeButton;
- ButtonControl *selectModelButton;
-
- FrogPilotSettingsWindow *parent;
-
- QJsonObject frogpilotToggleLevels;
-
- QMap carModels;
-
- QString carMake;
- QString carModel;
-
- QStringList models;
-
- ParamControl *forceFingerprint;
-
- Params params;
- Params params_default{"/data/params_default"};
-
- ToggleControl *disableOpenpilotLong;
-
bool allowAutoLockingDoors;
- bool disableOpenpilotLongitudinal = params.getBool("DisableOpenpilotLongitudinal");
+ bool disableOpenpilotLongitudinal;
bool hasExperimentalOpenpilotLongitudinal;
bool hasOpenpilotLongitudinal;
bool hasSNG;
@@ -56,11 +35,32 @@ class FrogPilotVehiclesPanel : public FrogPilotListWidget {
std::map toggles;
std::set gmKeys = {"ExperimentalGMTune", "LongPitch", "NewLongAPIGM", "VoltSNG"};
- std::set hyundaiKeys = {"NewLongAPI"};
+ std::set hyundaiKeys = {"NewLongAPI","HyundaiRadarTracks", "HKGtuning"};
std::set imprezaKeys = {"CrosstrekTorque"};
- std::set longitudinalKeys = {"ExperimentalGMTune", "FrogsGoMoosTweak", "LongPitch", "NewLongAPI", "NewLongAPIGM", "SNGHack", "VoltSNG"};
+ std::set longitudinalKeys = {"ExperimentalGMTune", "HKGtuning", "LongPitch", "NewLongAPI", "NewLongAPIGM", "SNGHack", "VoltSNG"};
std::set sngKeys = {"SNGHack"};
std::set subaruKeys = {"CrosstrekTorque"};
std::set toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "LockDoorsTimer", "SNGHack", "ToyotaDoors"};
std::set voltKeys = {"VoltSNG"};
+
+ ButtonControl *selectMakeButton;
+ ButtonControl *selectModelButton;
+
+ FrogPilotSettingsWindow *parent;
+
+ QJsonObject frogpilotToggleLevels;
+
+ QMap carModels;
+
+ QString carMake;
+ QString carModel;
+
+ QStringList models;
+
+ ParamControl *forceFingerprint;
+
+ Params params;
+ Params params_default{"/data/params_default"};
+
+ ToggleControl *disableOpenpilotLong;
};
diff --git a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc
index 127fb1d4f721f1..f8a087dfa0ca29 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc
+++ b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc
@@ -34,7 +34,7 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) :
{"ModelUI", tr("Model UI"), tr("Customize the model visualizations on the screen."), "../frogpilot/assets/toggle_icons/icon_vtc.png"},
{"DynamicPathWidth", tr("Dynamic Path Width"), tr("Automatically adjusts the width of the driving path display based on the current engagement state:\n\nFully engaged = 100%\nAlways On Lateral Active = 75%\nFully disengaged = 50%"), ""},
{"LaneLinesWidth", tr("Lane Lines Width"), tr("Controls the thickness the lane lines appear on the display.\n\nDefault matches the MUTCD standard of 4 inches."), ""},
- {"PathEdgeWidth", tr("Path Edges Width"), tr("Controls the width of the edges of the driving path to represent different modes and statuses.\n\nDefault is 20% of the total path width.\n\nColor Guide:\n- Blue: Navigation\n- Light Blue: 'Always On Lateral'\n- Green: Default\n- Orange: 'Experimental Mode'\n- Red: 'Traffic Mode'\n- Yellow: 'Conditional Experimental Mode' Overridden"), ""},
+ {"PathEdgeWidth", tr("Path Edges Width"), tr("Controls the width of the edges of the driving path to represent different modes and statuses.\n\nDefault is 20% of the total path width.\n\nColor Guide:\n\n- Blue: Navigation\n- Light Blue: 'Always On Lateral'\n- Green: Default\n- Orange: 'Experimental Mode'\n- Red: 'Traffic Mode'\n- Yellow: 'Conditional Experimental Mode' Overridden"), ""},
{"PathWidth", tr("Path Width"), tr("Controls how wide the driving path appears on your screen.\n\nDefault (6.1 feet / 1.9 meters) matches the width of a 2019 Lexus ES 350."), ""},
{"RoadEdgesWidth", tr("Road Edges Width"), tr("Controls how thick the road edges appear on the display.\n\nDefault matches half of the MUTCD standard lane line width of 4 inches."), ""},
{"UnlimitedLength", tr("'Unlimited' Road UI"), tr("Extends the display of the path, lane lines, and road edges as far as the model can see."), ""},
@@ -175,7 +175,7 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) :
modifiedDeveloperWidgetKeys.erase("ShowStoppingPoint");
}
- if (!params.getBool("ConditionalExperimentalMode")) {
+ if (!params.getBool("ConditionalExperimental")) {
modifiedDeveloperWidgetKeys.erase("ShowCEMStatus");
}
@@ -303,25 +303,24 @@ void FrogPilotVisualsPanel::showEvent(QShowEvent *event) {
hideToggles();
}
-void FrogPilotVisualsPanel::updateMetric() {
- bool previousIsMetric = isMetric;
- isMetric = params.getBool("IsMetric");
-
- if (isMetric != previousIsMetric) {
- double smallDistanceConversion = isMetric ? INCH_TO_CM : CM_TO_INCH;
- double distanceConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT;
+void FrogPilotVisualsPanel::updateMetric(bool metric, bool bootRun) {
+ static bool previousMetric;
+ if (metric != previousMetric && !bootRun) {
+ double smallDistanceConversion = metric ? INCH_TO_CM : CM_TO_INCH;
+ double distanceConversion = metric ? FOOT_TO_METER : METER_TO_FOOT;
params.putFloatNonBlocking("LaneLinesWidth", params.getFloat("LaneLinesWidth") * smallDistanceConversion);
params.putFloatNonBlocking("RoadEdgesWidth", params.getFloat("RoadEdgesWidth") * smallDistanceConversion);
params.putFloatNonBlocking("PathWidth", params.getFloat("PathWidth") * distanceConversion);
}
+ previousMetric = metric;
FrogPilotParamValueControl *laneLinesWidthToggle = static_cast(toggles["LaneLinesWidth"]);
FrogPilotParamValueControl *pathWidthToggle = static_cast(toggles["PathWidth"]);
FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast(toggles["RoadEdgesWidth"]);
- if (isMetric) {
+ if (metric) {
laneLinesWidthToggle->setDescription(tr("Adjust how thick the lane lines appear on the display.\n\nDefault matches the Vienna standard of 10 centimeters."));
roadEdgesWidthToggle->setDescription(tr("Adjust how thick the road edges appear on the display.\n\nDefault matches half of the Vienna standard of 10 centimeters."));
diff --git a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h
index 74106171bc2877..3d5d95dfaa5187 100644
--- a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h
+++ b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h
@@ -19,24 +19,13 @@ class FrogPilotVisualsPanel : public FrogPilotListWidget {
void hideToggles();
void showEvent(QShowEvent *event) override;
void showToggles(const std::set &keys);
- void updateMetric();
-
- FrogPilotButtonToggleControl *borderMetricsBtn;
- FrogPilotButtonToggleControl *lateralMetricsBtn;
- FrogPilotButtonToggleControl *longitudinalMetricsBtn;
-
- FrogPilotSettingsWindow *parent;
-
- Params params;
-
- QJsonObject frogpilotToggleLevels;
+ void updateMetric(bool metric, bool bootRun);
bool developerUIOpen;
bool hasAutoTune;
bool hasBSM;
bool hasOpenpilotLongitudinal;
bool hasRadar;
- bool isMetric = params.getBool("IsMetric");
int tuningLevel;
@@ -50,4 +39,14 @@ class FrogPilotVisualsPanel : public FrogPilotListWidget {
std::set developerWidgetKeys = {"ShowCEMStatus", "ShowStoppingPoint"};
std::set modelUIKeys = {"DynamicPathWidth", "LaneLinesWidth", "PathEdgeWidth", "PathWidth", "RoadEdgesWidth", "UnlimitedLength"};
std::set navigationUIKeys = {"BigMap", "MapStyle", "RoadNameUI", "ShowSpeedLimits", "UseVienna"};
+
+ FrogPilotButtonToggleControl *borderMetricsBtn;
+ FrogPilotButtonToggleControl *lateralMetricsBtn;
+ FrogPilotButtonToggleControl *longitudinalMetricsBtn;
+
+ FrogPilotSettingsWindow *parent;
+
+ Params params;
+
+ QJsonObject frogpilotToggleLevels;
};
diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py
index 93edcf3ea4d69d..d09ea0c2aa8093 100755
--- a/selfdrive/locationd/torqued.py
+++ b/selfdrive/locationd/torqued.py
@@ -231,6 +231,9 @@ def main(demo=False):
# FrogPilot variables
frogpilot_toggles = get_frogpilot_toggles()
+ if not frogpilot_toggles.liveValid:
+ estimator = TorqueEstimator(CP, True)
+
while True:
sm.update()
if sm.all_checks():
diff --git a/selfdrive/modeld/fill_model_msg.py b/selfdrive/modeld/fill_model_msg.py
index bf1764d1b3281a..75dfdf5fe9cd4d 100644
--- a/selfdrive/modeld/fill_model_msg.py
+++ b/selfdrive/modeld/fill_model_msg.py
@@ -69,7 +69,7 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
net_output_data: dict[str, np.ndarray], v_ego: float, delay: float,
publish_state: PublishState, vipc_frame_id: int, vipc_frame_id_extra: int,
frame_id: int, frame_drop: float, timestamp_eof: int, model_execution_time: float,
- valid: bool, clip_curves: bool) -> None:
+ valid: bool, planner_curves: bool) -> None:
frame_age = frame_id - vipc_frame_id if frame_id > vipc_frame_id else 0
frame_drop_perc = frame_drop * 100
extended_msg.valid = valid
@@ -85,7 +85,7 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
driving_model_data.modelExecutionTime = model_execution_time
action = driving_model_data.action
- action.desiredCurvature = desired_curv if clip_curves else float(net_output_data['desired_curvature'][0,0])
+ action.desiredCurvature = desired_curv if planner_curves else float(net_output_data['desired_curvature'][0,0])
modelV2 = extended_msg.modelV2
modelV2.frameId = vipc_frame_id
@@ -120,7 +120,7 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
# lateral planning
action = modelV2.action
- action.desiredCurvature = desired_curv if clip_curves else float(net_output_data['desired_curvature'][0,0])
+ action.desiredCurvature = desired_curv if planner_curves else float(net_output_data['desired_curvature'][0,0])
# times at X_IDXS according to model plan
PLAN_T_IDXS = [np.nan] * ModelConstants.IDX_N
diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py
index b7af4dbe3a5343..8c1b1be03985d8 100755
--- a/selfdrive/modeld/modeld.py
+++ b/selfdrive/modeld/modeld.py
@@ -25,7 +25,7 @@
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.selfdrive.modeld.models.commonmodel_pyx import ModelFrame, CLContext
-from openpilot.selfdrive.frogpilot.frogpilot_variables import DEFAULT_MODEL, MODELS_PATH, get_frogpilot_toggles
+from openpilot.selfdrive.frogpilot.frogpilot_variables import METADATAS_PATH, MODELS_PATH, get_frogpilot_toggles
PROCESS_NAME = "selfdrive.modeld.modeld"
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
@@ -34,9 +34,6 @@
ModelRunner.THNEED: Path(__file__).parent / 'models/supercombo.thneed',
ModelRunner.ONNX: Path(__file__).parent / 'models/supercombo.onnx'}
-METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl'
-
-
class FrameMeta:
frame_id: int = 0
timestamp_sof: int = 0
@@ -54,16 +51,15 @@ class ModelState:
prev_desire: np.ndarray # for tracking the rising edge of the pulse
model: ModelRunner
- def __init__(self, context: CLContext, model: str, model_version: str, use_desired_curvature: bool):
+ def __init__(self, context: CLContext, model: str, model_version: str):
# FrogPilot variables
- model_path = MODELS_PATH / f'{model}.thneed'
- if model != DEFAULT_MODEL and model_path.exists():
- MODEL_PATHS[ModelRunner.THNEED] = model_path
+ MODEL_PATHS[ModelRunner.THNEED] = MODELS_PATH / f'{model}.thneed'
+
+ with open(METADATAS_PATH / f'supercombo_metadata_{model_version}.pkl', 'rb') as f:
+ model_metadata = pickle.load(f)
- metadata_path = METADATA_PATH
- desired_metadata_path = MODELS_PATH / f'supercombo_metadata_{model_version}.pkl'
- if model != DEFAULT_MODEL and desired_metadata_path.exists():
- metadata_path = desired_metadata_path
+ input_shapes = model_metadata.get('input_shapes')
+ self.use_desired_curvature = 'lateral_control_params' in input_shapes and 'prev_desired_curv' in input_shapes
self.frame = ModelFrame(context)
self.wide_frame = ModelFrame(context)
@@ -76,16 +72,11 @@ def __init__(self, context: CLContext, model: str, model_version: str, use_desir
self.inputs = {
'desire': np.zeros(ModelConstants.DESIRE_LEN * (ModelConstants.HISTORY_BUFFER_LEN+1), dtype=np.float32),
'traffic_convention': np.zeros(ModelConstants.TRAFFIC_CONVENTION_LEN, dtype=np.float32),
+ **({'lateral_control_params': np.zeros(ModelConstants.LATERAL_CONTROL_PARAMS_LEN, dtype=np.float32)} if self.use_desired_curvature else {}),
+ **({'prev_desired_curv': np.zeros(ModelConstants.PREV_DESIRED_CURV_LEN * (ModelConstants.HISTORY_BUFFER_LEN+1), dtype=np.float32)} if self.use_desired_curvature else {}),
'features_buffer': np.zeros(ModelConstants.HISTORY_BUFFER_LEN * ModelConstants.FEATURE_LEN, dtype=np.float32),
}
- if use_desired_curvature:
- self.inputs['lateral_control_params'] = np.zeros(ModelConstants.LATERAL_CONTROL_PARAMS_LEN, dtype=np.float32)
- self.inputs['prev_desired_curv'] = np.zeros(ModelConstants.PREV_DESIRED_CURV_LEN * (ModelConstants.HISTORY_BUFFER_LEN+1), dtype=np.float32)
-
- with open(metadata_path, 'rb') as f:
- model_metadata = pickle.load(f)
-
self.output_slices = model_metadata['output_slices']
net_output_size = model_metadata['output_shapes']['outputs'][1]
self.output = np.zeros(net_output_size, dtype=np.float32)
@@ -104,7 +95,7 @@ def slice_outputs(self, model_outputs: np.ndarray) -> dict[str, np.ndarray]:
return parsed_model_outputs
def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray,
- inputs: dict[str, np.ndarray], prepare_only: bool, use_desired_curvature: bool) -> dict[str, np.ndarray] | None:
+ inputs: dict[str, np.ndarray], prepare_only: bool) -> dict[str, np.ndarray] | None:
# Model decides when action is completed, so desire input is just a pulse triggered on rising edge
inputs['desire'][0] = 0
new_desire = np.where(inputs['desire'] - self.prev_desire > .99, inputs['desire'], 0)
@@ -115,7 +106,7 @@ def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_
self.inputs['desire'][:] = self.desire_20Hz.reshape((25,4,-1)).max(axis=1).flatten()
self.inputs['traffic_convention'][:] = inputs['traffic_convention']
- if use_desired_curvature:
+ if self.use_desired_curvature:
self.inputs['lateral_control_params'][:] = inputs['lateral_control_params']
self.model.setInputBuffer("input_imgs", self.frame.prepare(buf, transform.flatten(), self.model.getCLBuffer("input_imgs")))
@@ -125,18 +116,18 @@ def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_
return None
self.model.execute()
- outputs = self.parser.parse_outputs(self.slice_outputs(self.output), use_desired_curvature)
+ outputs = self.parser.parse_outputs(self.slice_outputs(self.output))
self.full_features_20Hz[:-1] = self.full_features_20Hz[1:]
self.full_features_20Hz[-1] = outputs['hidden_state'][0, :]
- if use_desired_curvature:
+ if self.use_desired_curvature:
self.prev_desired_curv_20hz[:-1] = self.prev_desired_curv_20hz[1:]
self.prev_desired_curv_20hz[-1] = outputs['desired_curvature'][0, :]
idxs = np.arange(-4,-100,-4)[::-1]
self.inputs['features_buffer'][:] = self.full_features_20Hz[idxs].flatten()
- if use_desired_curvature:
+ if self.use_desired_curvature:
# TODO model only uses last value now, once that changes we need to input strided action history buffer
self.inputs['prev_desired_curv'][-ModelConstants.PREV_DESIRED_CURV_LEN:] = 0. * self.prev_desired_curv_20hz[-4, :]
return outputs
@@ -157,13 +148,12 @@ def main(demo=False):
# FrogPilot variables
frogpilot_toggles = get_frogpilot_toggles()
- model = frogpilot_toggles.model
+ model_name = frogpilot_toggles.model
model_version = frogpilot_toggles.model_version
- clip_curves = frogpilot_toggles.clipped_curvature_model
- use_desired_curvature = frogpilot_toggles.desired_curvature_model
+ planner_curves = frogpilot_toggles.planner_curvature_model
- model = ModelState(cl_context, model, model_version, use_desired_curvature)
+ model = ModelState(cl_context, model_name, model_version)
cloudlog.warning("models loaded, modeld starting")
# visionipc clients
@@ -260,7 +250,7 @@ def main(demo=False):
is_rhd = sm["driverMonitoringState"].isRHD
frame_id = sm["roadCameraState"].frameId
v_ego = max(sm["carState"].vEgo, 0.)
- if use_desired_curvature:
+ if model.use_desired_curvature:
lateral_control_params = np.array([v_ego, steer_delay], dtype=np.float32)
if sm.updated["liveCalibration"] and sm.seen['roadCameraState'] and sm.seen['deviceState']:
device_from_calib_euler = np.array(sm["liveCalibration"].rpyCalib, dtype=np.float32)
@@ -292,13 +282,11 @@ def main(demo=False):
inputs:dict[str, np.ndarray] = {
'desire': vec_desire,
'traffic_convention': traffic_convention,
+ **({'lateral_control_params': lateral_control_params} if model.use_desired_curvature else {}),
}
- if use_desired_curvature:
- inputs['lateral_control_params'] = lateral_control_params
-
mt1 = time.perf_counter()
- model_output = model.run(buf_main, buf_extra, model_transform_main, model_transform_extra, inputs, prepare_only, use_desired_curvature)
+ model_output = model.run(buf_main, buf_extra, model_transform_main, model_transform_extra, inputs, prepare_only)
mt2 = time.perf_counter()
model_execution_time = mt2 - mt1
@@ -309,7 +297,7 @@ def main(demo=False):
fill_model_msg(drivingdata_send, modelv2_send, model_output, v_ego, steer_delay,
publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id,
frame_drop_ratio, meta_main.timestamp_eof, model_execution_time, live_calib_seen,
- clip_curves)
+ planner_curves)
desire_state = modelv2_send.modelV2.meta.desireState
l_lane_change_prob = desire_state[log.Desire.laneChangeLeft]
diff --git a/selfdrive/modeld/parse_model_outputs.py b/selfdrive/modeld/parse_model_outputs.py
index 4fce0b328a549c..4367e9db8a2bcd 100644
--- a/selfdrive/modeld/parse_model_outputs.py
+++ b/selfdrive/modeld/parse_model_outputs.py
@@ -84,7 +84,7 @@ def parse_mdn(self, name, outs, in_N=0, out_N=1, out_shape=None):
outs[name] = pred_mu_final.reshape(final_shape)
outs[name + '_stds'] = pred_std_final.reshape(final_shape)
- def parse_outputs(self, outs: dict[str, np.ndarray], use_desired_curvature: bool) -> dict[str, np.ndarray]:
+ def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION,
out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH))
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
@@ -96,7 +96,7 @@ def parse_outputs(self, outs: dict[str, np.ndarray], use_desired_curvature: bool
out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH))
if 'lat_planner_solution' in outs:
self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH))
- if use_desired_curvature and 'desired_curvature' in outs:
+ if 'desired_curvature' in outs:
self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,))
for k in ['lead_prob', 'lane_lines_prob', 'meta']:
self.parse_binary_crossentropy(k, outs)
diff --git a/selfdrive/monitoring/dmonitoringd.py b/selfdrive/monitoring/dmonitoringd.py
index 80af7b71d079c5..19b09c37e44171 100755
--- a/selfdrive/monitoring/dmonitoringd.py
+++ b/selfdrive/monitoring/dmonitoringd.py
@@ -17,6 +17,9 @@ def dmonitoringd_thread():
DM = DriverMonitoring(rhd_saved=params.get_bool("IsRhdDetected"), always_on=params.get_bool("AlwaysOnDM"))
+ # FrogPilot variables
+ driver_view_enabled = params.get_bool("IsDriverViewEnabled")
+
# 20Hz <- dmonitoringmodeld
while True:
sm.update()
@@ -27,9 +30,11 @@ def dmonitoringd_thread():
valid = sm.all_checks()
if valid:
DM.run_step(sm)
+ elif driver_view_enabled:
+ DM.face_detected = sm['driverStateV2'].leftDriverData.faceProb > DM.settings._FACE_THRESHOLD or sm['driverStateV2'].rightDriverData.faceProb > DM.settings._FACE_THRESHOLD
# publish
- dat = DM.get_state_packet(valid=valid)
+ dat = DM.get_state_packet(valid=valid or driver_view_enabled)
pm.send('driverMonitoringState', dat)
# load live always-on toggle
diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc
index a5ca809270e93b..211ea6d4d8d9c1 100644
--- a/selfdrive/ui/qt/home.cc
+++ b/selfdrive/ui/qt/home.cc
@@ -256,13 +256,11 @@ void OffroadHome::hideEvent(QHideEvent *event) {
void OffroadHome::refresh() {
QString model = processModelName(uiState()->scene.model_name);
- if (uiState()->scene.model_randomizer) {
- model = "Mystery Model 👻";
- }
-
date->setText(QLocale(uiState()->language.mid(5)).toString(QDateTime::currentDateTime(), "dddd, MMMM d"));
version->setText(getBrand() + " v" + getVersion().left(14).trimmed() + " - " + model);
+ date->setVisible(util::system_time_valid());
+
bool updateAvailable = update_widget->refresh();
int alerts = alerts_widget->refresh();
diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc
index 1332d87ec72928..4cb748c762ea78 100644
--- a/selfdrive/ui/qt/offroad/settings.cc
+++ b/selfdrive/ui/qt/offroad/settings.cc
@@ -118,8 +118,8 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
});
// FrogPilot signals
- connect(toggles["IsMetric"], &ToggleControl::toggleFlipped, [=]() {
- updateMetric();
+ connect(toggles["IsMetric"], &ToggleControl::toggleFlipped, [=](bool metric) {
+ updateMetric(metric);
});
}
diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h
index a862106c85cc3e..0072ea6a721c7a 100644
--- a/selfdrive/ui/qt/offroad/settings.h
+++ b/selfdrive/ui/qt/offroad/settings.h
@@ -40,7 +40,7 @@ class SettingsWindow : public QFrame {
void closePanel();
void closeParentToggle();
void closeSubParentToggle();
- void updateMetric();
+ void updateMetric(bool metric, bool bootRun=false);
private:
QPushButton *sidebar_alert_widget;
@@ -90,7 +90,7 @@ class TogglesPanel : public ListWidget {
signals:
// FrogPilot signals
- void updateMetric();
+ void updateMetric(bool metric, bool bootRun=false);
public slots:
void expandToggleDescription(const QString ¶m);
diff --git a/selfdrive/ui/qt/offroad/software_settings.cc b/selfdrive/ui/qt/offroad/software_settings.cc
index a23bfae569d020..c045cb26d2292b 100644
--- a/selfdrive/ui/qt/offroad/software_settings.cc
+++ b/selfdrive/ui/qt/offroad/software_settings.cc
@@ -32,6 +32,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
// automatic updates toggle
ParamControl *automaticUpdatesToggle = new ParamControl("AutomaticUpdates", tr("Automatically Update FrogPilot"),
tr("FrogPilot will automatically update itself and it's assets when you're offroad and connected to Wi-Fi."), "");
+ connect(automaticUpdatesToggle, &ToggleControl::toggleFlipped, this, &updateFrogPilotToggles);
addItem(automaticUpdatesToggle);
// download update btn
@@ -137,7 +138,7 @@ void SoftwarePanel::updateLabels() {
fs_watch->addParam("UpdateAvailable");
if (!isVisible()) {
- scene.keep_screen_on = false;
+ scene.downloading_update = false;
return;
}
@@ -153,9 +154,9 @@ void SoftwarePanel::updateLabels() {
if (updater_state != "idle") {
downloadBtn->setEnabled(false);
downloadBtn->setValue(updater_state);
- scene.keep_screen_on = true;
+ scene.downloading_update = true;
} else {
- scene.keep_screen_on = false;
+ scene.downloading_update = false;
if (failed) {
downloadBtn->setText(tr("CHECK"));
downloadBtn->setValue(tr("failed to check for update"));
diff --git a/selfdrive/ui/qt/onroad/annotated_camera.cc b/selfdrive/ui/qt/onroad/annotated_camera.cc
index c945081dd8e613..66421e015c4022 100644
--- a/selfdrive/ui/qt/onroad/annotated_camera.cc
+++ b/selfdrive/ui/qt/onroad/annotated_camera.cc
@@ -54,7 +54,9 @@ void AnnotatedCameraWidget::updateState(int alert_height, const UIState &s) {
const auto cs = sm["controlsState"].getControlsState();
const auto car_state = sm["carState"].getCarState();
const auto nav_instruction = sm["navInstruction"].getNavInstruction();
-
+ brakeSignal = s.scene.brake_signal;
+ standstill = s.scene.standstill;
+ brakeLightOn = s.scene.brake_lights_on;
// Handle older routes where vCruiseCluster is not set
float v_cruise = cs.getVCruiseCluster() == 0.0 ? cs.getVCruise() : cs.getVCruiseCluster();
setSpeed = cs_alive ? v_cruise : SET_SPEED_NA;
@@ -76,6 +78,9 @@ void AnnotatedCameraWidget::updateState(int alert_height, const UIState &s) {
speedLimit = nav_alive ? nav_instruction.getSpeedLimit() : 0.0;
}
speedLimit *= (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH);
+ if (s.scene.speed_limit_controller && !showSLCOffset && !slcOverridden && speedLimit != 0) {
+ speedLimit += slcSpeedLimitOffset;
+ }
has_us_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD) || !useViennaSLCSign && !hideSpeedLimit;
has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA) || useViennaSLCSign && !hideSpeedLimit;
@@ -400,8 +405,16 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
p.setFont(InterFont(66));
drawText(p, rect().center().x(), 290, QString("%1 seconds").arg(seconds));
} else {
+ //check if brake signal is active
+ bool isBraking = (brakeSignal && (brakeLightOn || standstill));
p.setFont(InterFont(176, QFont::Bold));
- drawText(p, rect().center().x(), 210, speedStr);
+ if (isBraking) {
+ p.setPen(QColor(255, 0, 0, 255)); // Red
+ } else {
+ p.setPen(QColor(255, 255, 255, 255)); // White
+ };
+
+ drawText(p, rect().center().x(), 210, speedStr, 255, true);
p.setFont(InterFont(66));
drawText(p, rect().center().x(), 290, speedUnit, 200);
}
@@ -757,7 +770,7 @@ void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState
.arg(qRound(lead_speed * speedConversionMetrics))
.arg(leadSpeedUnit);
} else {
- text = QString("%1 %2 (%3) | %4 %5 | %6 %7")
+ text = QString("%1 %2 (%3) | %4 %5 | %6%7")
.arg(qRound(d_rel * distanceConversion))
.arg(leadDistanceUnit)
.arg(QString("Desired: %1").arg(desiredFollow * distanceConversion))
@@ -1206,10 +1219,6 @@ void PedalIcons::updateState(const UIScene &scene) {
accelerating = acceleration > 0.25f;
decelerating = acceleration < -0.25f;
-
- if (accelerating || decelerating) {
- update();
- }
}
void PedalIcons::paintEvent(QPaintEvent *event) {
diff --git a/selfdrive/ui/qt/onroad/annotated_camera.h b/selfdrive/ui/qt/onroad/annotated_camera.h
index d070caf051e2d2..eaffd25fb009f9 100644
--- a/selfdrive/ui/qt/onroad/annotated_camera.h
+++ b/selfdrive/ui/qt/onroad/annotated_camera.h
@@ -57,6 +57,9 @@ class AnnotatedCameraWidget : public CameraWidget {
QString speedUnit;
float setSpeed;
float speedLimit;
+ bool brakeLightOn;
+ bool standstill;
+ bool brakeSignal = false;
bool is_cruise_set = false;
bool is_metric = false;
bool dmActive = false;
diff --git a/selfdrive/ui/qt/onroad/onroad_home.cc b/selfdrive/ui/qt/onroad/onroad_home.cc
index 4eb0b4861ceb2d..5e0dd51c8ac0be 100644
--- a/selfdrive/ui/qt/onroad/onroad_home.cc
+++ b/selfdrive/ui/qt/onroad/onroad_home.cc
@@ -214,31 +214,28 @@ void OnroadWindow::paintEvent(QPaintEvent *event) {
static float smoothedSteer = 0.0;
smoothedSteer = 0.1 * std::abs(steer) + 0.9 * smoothedSteer;
-
if (std::abs(smoothedSteer - steer) < 0.01) {
smoothedSteer = steer;
}
- int visibleHeight = rect.height() * smoothedSteer;
-
QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
gradient.setColorAt(0.0, bg_colors[STATUS_TRAFFIC_MODE_ACTIVE]);
gradient.setColorAt(0.15, bg_colors[STATUS_EXPERIMENTAL_MODE_ACTIVE]);
gradient.setColorAt(0.5, bg_colors[STATUS_CONDITIONAL_OVERRIDDEN]);
gradient.setColorAt(0.85, bg_colors[STATUS_ENGAGED]);
gradient.setColorAt(1.0, bg_colors[STATUS_ENGAGED]);
-
QBrush brush(gradient);
- int fillWidth = UI_BORDER_SIZE;
if (steeringAngleDeg != 0) {
+ int visibleHeight = rect.height() * smoothedSteer;
QRect rectToFill, rectToHide;
+
if (steeringAngleDeg < 0) {
- rectToFill = QRect(rect.x(), rect.y() + rect.height() - visibleHeight, fillWidth, visibleHeight);
- rectToHide = QRect(rect.x(), rect.y(), fillWidth, rect.height() - visibleHeight);
+ rectToFill = QRect(rect.x(), rect.y() + rect.height() - visibleHeight, UI_BORDER_SIZE, visibleHeight);
+ rectToHide = QRect(rect.x(), rect.y(), UI_BORDER_SIZE, rect.height() - visibleHeight);
} else {
- rectToFill = QRect(rect.x() + rect.width() - fillWidth, rect.y() + rect.height() - visibleHeight, fillWidth, visibleHeight);
- rectToHide = QRect(rect.x() + rect.width() - fillWidth, rect.y(), fillWidth, rect.height() - visibleHeight);
+ rectToFill = QRect(rect.x() + rect.width() - UI_BORDER_SIZE, rect.y() + rect.height() - visibleHeight, UI_BORDER_SIZE, visibleHeight);
+ rectToHide = QRect(rect.x() + rect.width() - UI_BORDER_SIZE, rect.y(), UI_BORDER_SIZE, rect.height() - visibleHeight);
}
p.fillRect(rectToFill, brush);
p.fillRect(rectToHide, bgColor);
diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc
index 396ad791e7e311..8343b4aaa3e96f 100644
--- a/selfdrive/ui/qt/sidebar.cc
+++ b/selfdrive/ui/qt/sidebar.cc
@@ -61,7 +61,7 @@ void Sidebar::updateIcons() {
void Sidebar::updateIcon(QLabel *&label, QMovie *&gif, const QString &gifPath, const QRect &btnRect, const QString &pngPath, bool &isGif) {
QString selectedGifPath = gifPath;
- if (qrand() % 100 == 0 && btnRect == home_btn && isRandomEvents) {
+ if (util::random_int(1, 100) == 100 && btnRect == home_btn && isRandomEvents) {
selectedGifPath = randomEventGifPath;
}
diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc
index 5cd917ca930067..21241b7a71bd64 100644
--- a/selfdrive/ui/ui.cc
+++ b/selfdrive/ui/ui.cc
@@ -309,7 +309,7 @@ static void update_state(UIState *s) {
scene.vtsc_controlling_curve = frogpilotPlan.getVtscControllingCurve();
scene.vtsc_speed = frogpilotPlan.getVtscSpeed();
if (frogpilotPlan.getTogglesUpdated() && sm.frame % UI_FREQ == 0) {
- scene.frogpilot_toggles = QJsonDocument::fromJson(QString::fromStdString(s->params_memory.get("FrogPilotToggles", true)).toUtf8()).object();
+ scene.frogpilot_toggles = QJsonDocument::fromJson(s->params_memory.get("FrogPilotToggles", true).c_str()).object();
ui_update_params(s);
ui_update_theme(s);
@@ -406,6 +406,9 @@ void ui_update_frogpilot_params(UIState *s) {
scene.radarless_model = scene.frogpilot_toggles.value("radarless_model").toBool();
scene.random_events = scene.frogpilot_toggles.value("random_events").toBool();
scene.rainbow_path = scene.frogpilot_toggles.value("rainbow_path").toBool();
+
+ scene.brake_signal = scene.frogpilot_toggles.value("brake_signal").toBool();
+
scene.road_edge_width = scene.frogpilot_toggles.value("road_edge_width").toDouble();
scene.road_name_ui = scene.frogpilot_toggles.value("road_name_ui").toBool();
scene.rotating_wheel = scene.frogpilot_toggles.value("rotating_wheel").toBool();
@@ -552,7 +555,7 @@ void UIState::update() {
scene.force_onroad = params_memory.getBool("ForceOnroad");
scene.started_timer = scene.started || started_prev ? scene.started_timer + 1 : 0;
- if (scene.keep_screen_on) {
+ if (scene.downloading_update || scene.frogpilot_panel_active) {
device()->resetInteractiveTimeout(scene.screen_timeout, scene.screen_timeout_onroad);
}
}
diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h
index 8413e4b613a4d5..249ee54d99bd89 100644
--- a/selfdrive/ui/ui.h
+++ b/selfdrive/ui/ui.h
@@ -149,10 +149,12 @@ typedef struct UIScene {
bool blind_spot_path;
bool blind_spot_right;
bool brake_lights_on;
+ bool brake_signal;
bool cem_status;
bool compass;
bool conditional_experimental;
bool cpu_metrics;
+ bool downloading_update;
bool driver_camera_in_reverse;
bool dynamic_path_width;
bool dynamic_pedals_on_ui;
@@ -161,6 +163,7 @@ typedef struct UIScene {
bool experimental_mode_via_tap;
bool fahrenheit;
bool force_onroad;
+ bool frogpilot_panel_active;
bool frogs_go_moo;
bool full_map;
bool gpu_metrics;
@@ -173,7 +176,6 @@ typedef struct UIScene {
bool hide_speed_limit;
bool ip_metrics;
bool jerk_metrics;
- bool keep_screen_on;
bool lateral_tuning_metrics;
bool lead_metrics;
bool left_curve;
diff --git a/sync_chubbspilot.sh b/sync_chubbspilot.sh
new file mode 100755
index 00000000000000..430bb683b10454
--- /dev/null
+++ b/sync_chubbspilot.sh
@@ -0,0 +1,349 @@
+#!/bin/bash
+
+set -x
+
+# Function to determine the local network range
+get_network_range() {
+ local ip
+ ip=$(ipconfig getifaddr en0 2>/dev/null || ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d/ -f1 | head -n1)
+ if [[ -z "$ip" ]]; then
+ echo "ERROR: Unable to determine local IP address."
+ exit 1
+ fi
+ # Convert IP to subnet (e.g., 192.168.1.0/24)
+ local subnet
+ subnet=$(echo "$ip" | awk -F. '{print $1 "." $2 "." $3 ".0/24"}')
+ echo "$subnet"
+}
+
+# Function to detect device IP using nmap
+get_device_ip_nmap() {
+ local network_range
+ network_range=$(get_network_range)
+ echo "Scanning network range: $network_range"
+ # Scan for devices with SSH ports 22 or 8022 open
+ nmap -p 22,8022 --open -oG - "$network_range" | awk '/22\/open|8022\/open/{print $2}'
+}
+
+# Automatically detect DEVICE_IP
+detect_device_ip() {
+ local possible_ips
+ possible_ips=$(get_device_ip_nmap)
+ local device_ip=""
+ for ip in $possible_ips; do
+ if ssh -o ConnectTimeout=2 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$ip" 'exit' 2>/dev/null; then
+ device_ip="$ip"
+ break
+ fi
+ done
+
+ if [[ -z "$device_ip" ]]; then
+ echo "ERROR: Failed to detect device IP via nmap."
+ exit 1
+ fi
+
+ echo "$device_ip"
+}
+
+# Configuration
+DEVICE_USER="comma"
+DEVICE_IP=$(detect_device_ip)
+
+# Remove existing SSH keys for DEVICE_IP
+ssh-keygen -R "$DEVICE_IP" > /dev/null 2>&1
+
+echo "Detected Device IP: $DEVICE_IP"
+
+# Function to get the current git branch on the SSH device
+get_device_branch() {
+ ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" 'cd /data/openpilot && git rev-parse --abbrev-ref HEAD'
+}
+
+# Function to check if prebuilt exists
+check_prebuilt() {
+ # Check for the 'prebuilt' file
+ PREBUILT_FILE=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" 'test -f /data/openpilot/prebuilt && echo "yes" || echo "no"')
+
+ # Additionally, check if any .so files exist as an indicator
+ ESSENTIAL_SO=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" 'find /data/openpilot -type f -name "*.so" | grep -q . && echo "yes" || echo "no"')
+
+ if [[ "$PREBUILT_FILE" == "yes" || "$ESSENTIAL_SO" == "yes" ]]; then
+ echo "yes"
+ else
+ echo "no"
+ fi
+}
+
+# Function to perform compilation
+compile_device() {
+ echo "Performing device compilation..."
+
+ echo "Connecting to device at $DEVICE_IP..."
+ OUTPUT=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" << 'ENDSSH'
+ set +x # Disable debug mode for cleaner output
+ set -e
+ cd /data/openpilot
+
+ # Setup environment
+ export PATH="/data/data/com.termux/files/usr/bin:$PATH"
+ export LD_LIBRARY_PATH="/usr/lib/aarch64-linux-gnu:/usr/local/lib:$LD_LIBRARY_PATH"
+
+ # Clean and compile
+ echo "Cleaning previous compilation..."
+ if ! scons -c; then
+ echo "ERROR: Clean failed"
+ exit 1
+ fi
+
+ echo "Starting compilation..."
+ if ! scons -j$(nproc); then
+ echo "ERROR: Compilation failed"
+ exit 1
+ fi
+
+ # Wait for compilation to complete
+ while pgrep -f "scons" > /dev/null; do
+ echo "Waiting for compilation to finish..."
+ sleep 5
+ done
+
+ # Final check if compilation succeeded
+ if ! scons --check; then
+ echo "ERROR: Compilation failed during final check"
+ exit 1
+ fi
+
+ echo "Compilation completed successfully"
+
+ # Add compiled files
+ git add -f .
+ git commit -m "compile" --allow-empty
+
+ # Delete all .cc files except specified ones
+ find . -type f -name '*.cc' ! \( -name 'main.cc' -o -name 'map_renderer.cc' -o -name 'transform.cc' \) -delete
+
+ # Delete all .h files except specified ones
+ find . -type f -name '*.h' ! \( -name 'sensor.h' -o -name 'version.h' -o -name 'map_renderer.h' -o -name 'ox03c10_registers.h' -o -name 'ar0231_cl.h' -o -name 'os04c10_cl.h' -o -name 'ox03c10_cl.h' -o -name 'os04c10_registers.h' -o -name 'ar0231_registers.h' \) -delete
+
+ # Remove other unwanted file types
+ find . -name '*.o' -delete
+ find . -name '*.os' -delete
+ find . -name '*.pyc' -delete
+ find . -name 'moc_*' -delete
+ find . -name '__pycache__' -delete
+ find . -name '*.a' -delete
+
+ # Remove build system artifacts
+ rm -rf .sconsign.dblite Jenkinsfile release/
+
+ # Remove generated directories
+ rm -rf selfdrive/ubloxd/generated cereal/gen/cpp
+
+ # Remove remaining MPC-generated files
+ rm -rf selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/main_*
+ rm -rf selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/main_*
+
+ # Additional Deletions
+ # Remove specific directories except .github/workflows and delete body/
+ find .github -mindepth 1 ! -path './.github/workflows*' -delete
+ rm -rf .vscode .devcontainer debug \
+ tools/cabana tools/camerastream tools/car_porting tools/joystick \
+ tools/latencylogger tools/lib/tests tools/plotjuggler tools/profiling \
+ tools/replay tools/scripts tools/serial tools/sim tools/ssh \
+ tools/ubuntu_setup tools/webcam body/
+
+ # Remove Docker-related files
+ rm -f Dockerfile* .dockerignore
+
+ # Remove test files and directories
+ find . -type f -iname '*test*' -delete
+ find . -type d -iname '*test*' -exec rm -rf {} +
+
+ # Remove all files named LICENSE (case-insensitive)
+ find . -type f -iname 'license*' -delete
+
+ # Remove teleoprtc files
+ find . -type f -iname '*teleoprtc*' -delete
+
+ # Remove configuration files
+ rm -f .clang-tidy .editorconfig
+
+ # Remove debug-related files and directories
+ find . -type f -iname '*debug*' -delete
+ find . -type d -iname '*debug*' -exec rm -rf {} +
+
+ touch prebuilt
+
+ # Amend with cleaned state
+ git add -f .
+ git commit --amend -m "Prebuilt" --allow-empty
+
+ # Output hash
+ echo "HASH=$(git rev-parse HEAD)"
+ENDSSH
+ )
+ # Check SSH exit status
+ SSH_STATUS=$?
+ if [ $SSH_STATUS -ne 0 ]; then
+ echo "ERROR: Compilation SSH session failed with status $SSH_STATUS" >&2
+ echo "Output was:" >&2
+ echo "$OUTPUT" >&2
+ exit 1
+ fi
+
+ # Extract hash
+ COMMIT_HASH=$(echo "$OUTPUT" | grep "^HASH=" | cut -d= -f2)
+
+ if ! [[ $COMMIT_HASH =~ ^[0-9a-f]{40}$ ]]; then
+ echo "ERROR: Invalid commit hash: $COMMIT_HASH" >&2
+ echo "Full output was:" >&2
+ echo "$OUTPUT" >&2
+ exit 1
+ fi
+
+ echo "$COMMIT_HASH"
+}
+
+# Function to skip compilation and clean
+skip_compile_device() {
+ echo "Proceeding without recompilation..." >&2 # Redirect to stderr
+ # Disable set -x for this function to prevent debug logs from polluting OUTPUT
+ set +x
+ OUTPUT=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$DEVICE_USER@$DEVICE_IP" << 'ENDSSH'
+ set +x # Disable debug mode
+ set -e
+ cd /data/openpilot
+
+ # Add compiled files
+ git add -f .
+ git commit -m "compile" --allow-empty
+
+ # Delete all .cc files except specified ones
+ find . -type f -name '*.cc' ! \( -name 'main.cc' -o -name 'map_renderer.cc' -o -name 'transform.cc' \) -delete
+
+ # Delete all .h files except specified ones
+ find . -type f -name '*.h' ! \( -name 'sensor.h' -o -name 'version.h' -o -name 'map_renderer.h' -o -name 'ox03c10_registers.h' -o -name 'ar0231_cl.h' -o -name 'os04c10_cl.h' -o -name 'ox03c10_cl.h' -o -name 'os04c10_registers.h' -o -name 'ar0231_registers.h' \) -delete
+
+ # Remove other unwanted file types
+ find . -name '*.o' -delete
+ find . -name '*.os' -delete
+ find . -name '*.pyc' -delete
+ find . -name 'moc_*' -delete
+ find . -name '__pycache__' -delete
+ find . -name '*.a' -delete
+
+ # Remove build system artifacts
+ rm -rf .sconsign.dblite Jenkinsfile release/
+
+ # Remove generated directories
+ rm -rf selfdrive/ubloxd/generated cereal/gen/cpp
+
+ # Remove remaining MPC-generated files
+ rm -rf selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/main_*
+ rm -rf selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/main_*
+
+ # Additional Deletions
+ # Remove specific directories except .github/workflows and delete body/
+ find .github -mindepth 1 ! -path './.github/workflows*' -delete
+ rm -rf .vscode .devcontainer debug \
+ tools/cabana tools/camerastream tools/car_porting tools/joystick \
+ tools/latencylogger tools/lib/tests tools/plotjuggler tools/profiling \
+ tools/replay tools/scripts tools/serial tools/sim tools/ssh \
+ tools/ubuntu_setup tools/webcam body/
+
+ # Remove Docker-related files
+ rm -f Dockerfile* .dockerignore
+
+ # Remove test files and directories
+ find . -type f -iname '*test*' -delete
+ find . -type d -iname '*test*' -exec rm -rf {} +
+
+ # Remove all files named LICENSE (case-insensitive)
+ find . -type f -iname 'license*' -delete
+
+ # Remove teleoprtc files
+ find . -type f -iname '*teleoprtc*' -delete
+
+ # Remove configuration files
+ rm -f .clang-tidy .editorconfig
+
+ # Remove debug-related files and directories
+ find . -type f -iname '*debug*' -delete
+ find . -type d -iname '*debug*' -exec rm -rf {} +
+
+ touch prebuilt
+
+ # Amend with cleaned state
+ git add -f .
+ git commit --amend -m "Prebuilt" --allow-empty
+
+ # Output hash
+ echo "HASH=$(git rev-parse HEAD)"
+ENDSSH
+ )
+ set -x # Re-enable debug mode
+
+ SSH_STATUS=$?
+ if [ $SSH_STATUS -ne 0 ]; then
+ echo "ERROR: Commit and clean SSH session failed with status $SSH_STATUS" >&2
+ echo "Output was:" >&2
+ echo "$OUTPUT" >&2
+ exit 1
+ fi
+
+ # Extract hash
+ COMMIT_HASH=$(echo "$OUTPUT" | grep "^HASH=" | cut -d= -f2)
+
+ if ! [[ $COMMIT_HASH =~ ^[0-9a-f]{40}$ ]]; then
+ echo "ERROR: Invalid commit hash: $COMMIT_HASH" >&2
+ echo "Full output was:" >&2
+ echo "$OUTPUT" >&2
+ exit 1
+ fi
+
+ echo "$COMMIT_HASH"
+}
+
+# Function to handle workspace operations
+workspace_operations() {
+ local device_commit=$1
+ echo "=== Starting workspace operations ==="
+ export GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
+
+ if ! git remote | grep -q "device"; then
+ git remote add device ssh://"${DEVICE_USER}"@"${DEVICE_IP}":/data/openpilot
+ fi
+
+ git fetch device "$BRANCH_NAME" --force
+
+ git checkout "$BRANCH_NAME" || { echo "ERROR: Failed to checkout branch '$BRANCH_NAME'"; exit 1; }
+ git cherry-pick --no-commit --strategy=recursive -X theirs "$device_commit" || { echo "ERROR: Cherry-pick failed"; exit 1; }
+ git add -f .
+ git commit -C "$device_commit" || { echo "ERROR: Commit failed"; exit 1; }
+
+ # Force push to origin
+ git push --force origin "$BRANCH_NAME" || { echo "ERROR: Force push to origin failed"; exit 1; }
+}
+
+# Main execution
+echo "Starting automated sync process..."
+
+PREBUILT=$(check_prebuilt)
+
+if [[ "$PREBUILT" == "yes" ]]; then
+ # Automatically skip recompilation without user confirmation
+ COMMIT_HASH=$(skip_compile_device) || exit 1
+else
+ # Perform compilation if not prebuilt
+ COMMIT_HASH=$(compile_device) || exit 1
+fi
+
+# Set BRANCH_NAME based on the device's current branch
+BRANCH_NAME=$(get_device_branch)
+if [[ -z "$BRANCH_NAME" ]]; then
+ echo "ERROR: Failed to retrieve the device's current branch."
+ exit 1
+fi
+
+workspace_operations "$COMMIT_HASH"
+echo "Sync process completed."
diff --git a/system/loggerd/uploader.py b/system/loggerd/uploader.py
index 8e6ce36351c4e9..ea53c3327b9e34 100755
--- a/system/loggerd/uploader.py
+++ b/system/loggerd/uploader.py
@@ -27,7 +27,11 @@
UPLOAD_ATTR_NAME = 'user.upload'
UPLOAD_ATTR_VALUE = b'1'
-UPLOAD_QLOG_QCAM_MAX_SIZE = 5 * 1e6 # MB
+MAX_UPLOAD_SIZES = {
+ "qlog": 25*1e6, # can't be too restrictive here since we use qlogs to find
+ # bugs, including ones that can cause massive log sizes
+ "qcam": 5*1e6,
+}
allow_sleep = bool(os.getenv("UPLOADER_SLEEP", "1"))
force_wifi = os.getenv("FORCEWIFI") is not None
@@ -174,7 +178,7 @@ def upload(self, name: str, key: str, fn: str, network_type: int, metered: bool)
if sz == 0:
# tag files of 0 size as uploaded
success = True
- elif name in self.immediate_priority and sz > UPLOAD_QLOG_QCAM_MAX_SIZE:
+ elif name in MAX_UPLOAD_SIZES and sz > MAX_UPLOAD_SIZES[name]:
cloudlog.event("uploader_too_large", key=key, fn=fn, sz=sz)
success = True
else:
diff --git a/system/manager/process.py b/system/manager/process.py
index a1cde729530d14..be7f564e8046b0 100644
--- a/system/manager/process.py
+++ b/system/manager/process.py
@@ -88,7 +88,7 @@ def restart(self) -> None:
self.stop(sig=signal.SIGKILL)
self.start()
- def check_watchdog(self, started: bool, params: Params) -> None:
+ def check_watchdog(self, started: bool) -> None:
if self.watchdog_max_dt is None or self.proc is None:
return
@@ -285,7 +285,7 @@ def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None
else:
p.stop(block=False)
- p.check_watchdog(started, params)
+ p.check_watchdog(started)
for p in running:
p.start()
diff --git a/system/manager/process_config.py b/system/manager/process_config.py
index 40bd3000898063..c5061a8894646f 100644
--- a/system/manager/process_config.py
+++ b/system/manager/process_config.py
@@ -69,8 +69,8 @@ def run_new_modeld(started, params, CP: car.CarParams, classic_model, frogpilot_
NativeProcess("stream_encoderd", "system/loggerd", ["./encoderd", "--stream"], notcar),
NativeProcess("loggerd", "system/loggerd", ["./loggerd"], allow_logging),
NativeProcess("modeld", "selfdrive/modeld", ["./modeld"], run_new_modeld),
- NativeProcess("mapsd", "selfdrive/navd", ["./mapsd"], only_onroad),
- PythonProcess("navmodeld", "selfdrive.classic_modeld.navmodeld", only_onroad),
+ NativeProcess("mapsd", "selfdrive/navd", ["./mapsd"], run_classic_modeld),
+ PythonProcess("navmodeld", "selfdrive.classic_modeld.navmodeld", run_classic_modeld),
NativeProcess("sensord", "system/sensord", ["./sensord"], only_onroad, enabled=not PC),
NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None)),
PythonProcess("soundd", "selfdrive.ui.soundd", only_onroad),
@@ -84,7 +84,7 @@ def run_new_modeld(started, params, CP: car.CarParams, classic_model, frogpilot_
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", driverview, enabled=(not PC or WEBCAM)),
PythonProcess("qcomgpsd", "system.qcomgpsd.qcomgpsd", qcomgps, enabled=TICI),
#PythonProcess("ugpsd", "system.ugpsd", only_onroad, enabled=TICI),
- PythonProcess("navd", "selfdrive.navd.navd", only_onroad),
+ PythonProcess("navd", "selfdrive.navd.navd", run_classic_modeld),
PythonProcess("pandad", "selfdrive.pandad.pandad", always_run),
PythonProcess("paramsd", "selfdrive.locationd.paramsd", only_onroad),
NativeProcess("ubloxd", "system/ubloxd", ["./ubloxd"], ublox, enabled=TICI),
@@ -106,6 +106,7 @@ def run_new_modeld(started, params, CP: car.CarParams, classic_model, frogpilot_
NativeProcess("classic_modeld", "selfdrive/classic_modeld", ["./classic_modeld"], run_classic_modeld),
PythonProcess("fleet_manager", "selfdrive.frogpilot.fleetmanager.fleet_manager", always_run),
PythonProcess("frogpilot_process", "selfdrive.frogpilot.frogpilot_process", always_run),
+ PythonProcess("mapd", "selfdrive.frogpilot.navigation.mapd", always_run),
]
managed_processes = {p.name: p for p in procs}
diff --git a/system/sentry.py b/system/sentry.py
index 100211814e6948..4b4d257694766f 100644
--- a/system/sentry.py
+++ b/system/sentry.py
@@ -1,7 +1,6 @@
"""Install exception handler for process crash."""
import os
import sentry_sdk
-import subprocess
import traceback
from datetime import datetime
from enum import Enum
@@ -35,12 +34,12 @@ def report_tombstone(fn: str, message: str, contents: str) -> None:
def capture_exception(*args, **kwargs) -> None:
exc_text = traceback.format_exc()
- phrases_to_check = [
+ errors_to_ignore = [
"already exists. To overwrite it, set 'overwrite' to True",
"setup_quectel failed after retry",
]
- if any(phrase in exc_text for phrase in phrases_to_check):
+ if any(error in exc_text for error in errors_to_ignore):
return
save_exception(exc_text)
@@ -72,7 +71,7 @@ def capture_fingerprint(candidate, params, blocked=False):
value = params_tracking.get_int(key)
else:
if isinstance(params.get(key), bytes):
- value = params.get(key).decode('utf-8')
+ value = params.get(key, encoding='utf-8')
else:
value = params.get(key) or "0"
@@ -93,7 +92,7 @@ def capture_fingerprint(candidate, params, blocked=False):
scope.fingerprint = [params.get("DongleId", encoding='utf-8'), candidate]
if blocked:
- sentry_sdk.capture_message("Blocked user from using the development branch", level='error')
+ sentry_sdk.capture_message("Blocked user from using the development branch", level='warning')
else:
sentry_sdk.capture_message(f"Fingerprinted {candidate}", level='info')
@@ -101,13 +100,20 @@ def capture_fingerprint(candidate, params, blocked=False):
sentry_sdk.flush()
+def capture_model(frogpilot_toggles):
+ sentry_sdk.capture_message(f"User using: {frogpilot_toggles.model_name} - Model Randomizer: {frogpilot_toggles.model_randomizer}", level='info')
+
+
+def capture_user(channel):
+ sentry_sdk.capture_message(f"Logged user on: {channel}", level='info')
+
+
def set_tag(key: str, value: str) -> None:
sentry_sdk.set_tag(key, value)
def save_exception(exc_text: str) -> None:
- if not os.path.exists(CRASHES_DIR):
- os.makedirs(CRASHES_DIR)
+ os.makedirs(CRASHES_DIR, exist_ok=True)
files = [
os.path.join(CRASHES_DIR, datetime.now().strftime('%Y-%m-%d--%H-%M-%S.log')),
@@ -137,7 +143,7 @@ def init(project: SentryProject) -> bool:
short_branch = build_metadata.channel
- if short_branch == "FrogPilot-Development":
+ if short_branch == "Chubbs":
env = "Development"
elif build_metadata.release_channel:
env = "Release"
diff --git a/system/updated/updated.py b/system/updated/updated.py
old mode 100755
new mode 100644
index 40fd8b898e8f0a..fb9928d9cbd4ae
--- a/system/updated/updated.py
+++ b/system/updated/updated.py
@@ -480,7 +480,7 @@ def main() -> None:
# check for update
params.put("UpdaterState", "checking...")
updater.check_for_update()
- params_memory.put_bool("ManualUpdateInitiated", False)
+ params_memory.remove("ManualUpdateInitiated")
# download update
last_fetch = read_time_from_param(params, "UpdaterLastFetchTime")