From 2542d448f9d891824abdc915ba7a8d920bdd4081 Mon Sep 17 00:00:00 2001 From: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Date: Mon, 27 Jul 2026 12:46:49 -0400 Subject: [PATCH] Big UI: Not your Papaw's Cached Params --- selfdrive/ui/onroad/exp_button.py | 3 +- selfdrive/ui/onroad/model_renderer.py | 3 +- .../ui/onroad/starpilot/developer_sidebar.py | 3 +- .../onroad/starpilot/starpilot_onroad_view.py | 3 +- .../onroad/starpilot/widgets/speed_limit.py | 3 +- selfdrive/ui/ui_state.py | 49 ++++++++++++++++++- 6 files changed, 53 insertions(+), 11 deletions(-) diff --git a/selfdrive/ui/onroad/exp_button.py b/selfdrive/ui/onroad/exp_button.py index d2476c2b3..aca2b1214 100644 --- a/selfdrive/ui/onroad/exp_button.py +++ b/selfdrive/ui/onroad/exp_button.py @@ -1,6 +1,5 @@ import time import pyray as rl -from openpilot.common.params import Params from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app from openpilot.system.ui.widgets import Widget @@ -15,7 +14,7 @@ from openpilot.starpilot.common.experimental_state import ( class ExpButton(Widget): def __init__(self, button_size: int, icon_size: int): super().__init__() - self._params = Params() + self._params = ui_state.params self._experimental_mode: bool = False self._engageable: bool = False diff --git a/selfdrive/ui/onroad/model_renderer.py b/selfdrive/ui/onroad/model_renderer.py index 47d437ac7..a0e326be0 100644 --- a/selfdrive/ui/onroad/model_renderer.py +++ b/selfdrive/ui/onroad/model_renderer.py @@ -4,7 +4,6 @@ import pyray as rl from cereal import messaging, car from dataclasses import dataclass, field from openpilot.common.filter_simple import FirstOrderFilter -from openpilot.common.params import Params from openpilot.common.constants import CV from openpilot.selfdrive.locationd.calibrationd import HEIGHT_INIT from openpilot.selfdrive.ui.lib.starpilot_theme import get_param_color, get_theme_color, get_visual_color, is_stock_color_scheme, with_alpha @@ -88,7 +87,7 @@ class ModelRenderer(Widget): self._rainbow_path = RainbowPath() # Get longitudinal control setting from car parameters - self._params = Params() + self._params = ui_state.params if car_params := self._params.get("CarParams"): cp = messaging.log_from_bytes(car_params, car.CarParams) self._longitudinal_control = cp.openpilotLongitudinalControl diff --git a/selfdrive/ui/onroad/starpilot/developer_sidebar.py b/selfdrive/ui/onroad/starpilot/developer_sidebar.py index 724dfce00..4d85e0bee 100644 --- a/selfdrive/ui/onroad/starpilot/developer_sidebar.py +++ b/selfdrive/ui/onroad/starpilot/developer_sidebar.py @@ -3,7 +3,6 @@ import time import re import json from cereal import car -from openpilot.common.params import Params from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE from openpilot.system.ui.lib.text_measure import measure_text_cached @@ -64,7 +63,7 @@ def _setting_changed(value: float, reference: float) -> bool: class DeveloperSidebar: def __init__(self): - self._params = Params() + self._params = ui_state.params self._font_bold = gui_app.font(FontWeight.SEMI_BOLD) self._last_toggles_check = 0.0 self._cached_metrics = [0] * 7 diff --git a/selfdrive/ui/onroad/starpilot/starpilot_onroad_view.py b/selfdrive/ui/onroad/starpilot/starpilot_onroad_view.py index 58fbc0e2c..398e0c173 100644 --- a/selfdrive/ui/onroad/starpilot/starpilot_onroad_view.py +++ b/selfdrive/ui/onroad/starpilot/starpilot_onroad_view.py @@ -1,7 +1,6 @@ import pyray as rl import time from msgq.visionipc import VisionStreamType -from openpilot.common.params import Params from openpilot.selfdrive.ui.onroad.augmented_road_view import AugmentedRoadView from openpilot.selfdrive.ui.onroad.starpilot.starpilot_border import render_behind, render_overlay, render_background_effects from openpilot.selfdrive.ui.onroad.starpilot.path import render_adjacent_lanes, render_path_edges @@ -31,7 +30,7 @@ AlertSize = log.SelfdriveState.AlertSize class StarPilotOnroadView(AugmentedRoadView): def __init__(self, stream_type: VisionStreamType = VisionStreamType.VISION_STREAM_ROAD): super().__init__(stream_type) - self._params = Params() + self._params = ui_state.params self._font_bold = gui_app.font(FontWeight.BOLD) self._font_medium = gui_app.font(FontWeight.MEDIUM) diff --git a/selfdrive/ui/onroad/starpilot/widgets/speed_limit.py b/selfdrive/ui/onroad/starpilot/widgets/speed_limit.py index da3909a9c..59e73afa7 100644 --- a/selfdrive/ui/onroad/starpilot/widgets/speed_limit.py +++ b/selfdrive/ui/onroad/starpilot/widgets/speed_limit.py @@ -1,6 +1,5 @@ import pyray as rl from typing import Optional -from openpilot.common.params import Params from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.onroad.starpilot.widgets.base import LayoutWidget from openpilot.selfdrive.ui.onroad.starpilot.slc_speed_limit import ( @@ -60,7 +59,7 @@ class SpeedLimitWidget(LayoutWidget): ) if rl.check_collision_point_rec(mouse_pos, hit_rect): current = ui_state.params.get_bool("SpeedLimitSources") - Params().put_bool("SpeedLimitSources", not current) + ui_state.params.put_bool("SpeedLimitSources", not current) return state = _get_slc_state() diff --git a/selfdrive/ui/ui_state.py b/selfdrive/ui/ui_state.py index 59393f5aa..76ef3d756 100644 --- a/selfdrive/ui/ui_state.py +++ b/selfdrive/ui/ui_state.py @@ -18,6 +18,53 @@ BACKLIGHT_OFFROAD = 65 if HARDWARE.get_device_type() == "mici" else 50 USBGPU_POLL_INTERVAL = 1.0 +class CachedParams: + def __init__(self, ttl: float = 1.0): + self._params = Params() + self._cache = {} + self._wrappers = {} + self._ttl = ttl + + def _invalidate(self, key=None): + if key is None: + self._cache.clear() + else: + k_str = key.decode("utf-8") if isinstance(key, bytes) else str(key) + self._cache = {k: v for k, v in self._cache.items() if k[0] != k_str} + + def __getattr__(self, name: str): + if name in self._wrappers: + return self._wrappers[name] + + attr = getattr(self._params, name) + if not callable(attr): + return attr + + if name.startswith("get"): + def get_wrapper(key, *args, **kwargs): + now = time.monotonic() + k_str = key.decode("utf-8") if isinstance(key, bytes) else str(key) + ck = (k_str, name) if not args and not kwargs else (k_str, name, args, tuple(sorted(kwargs.items()))) + val, expiry = self._cache.get(ck, (None, 0.0)) + if now < expiry: + return val + val = attr(key, *args, **kwargs) + self._cache[ck] = (val, now + self._ttl) + return val + self._wrappers[name] = get_wrapper + return get_wrapper + + if name.startswith("put") or name.startswith("remove") or name.startswith("clear"): + def put_wrapper(key=None, *args, **kwargs): + res = attr(key, *args, **kwargs) if key is not None else attr(*args, **kwargs) + self._invalidate(key) + return res + self._wrappers[name] = put_wrapper + return put_wrapper + + return attr + + class UIStatus(Enum): DISENGAGED = "disengaged" ENGAGED = "engaged" @@ -34,7 +81,7 @@ class UIState: return cls._instance def _initialize(self): - self.params = Params() + self.params = CachedParams() self.params_memory = Params(memory=True) self.sm = messaging.SubMaster( [