From 3d3a5de4097ade84e192313a3d62be69f8305f18 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Tue, 10 Feb 2026 22:58:59 -0500 Subject: [PATCH] [TIZI/TICI] ui: dynamic ICBM status (#1684) * [TIZI/TICI] ui: dynamic ICBM status * wat * make sure to init * make sure it exists * send it for all --- .../ui/sunnypilot/onroad/hud_renderer.py | 80 ++++++++++++++++++- selfdrive/ui/sunnypilot/ui_state.py | 1 + 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/sunnypilot/onroad/hud_renderer.py b/selfdrive/ui/sunnypilot/onroad/hud_renderer.py index d8ba4b8bf..86fcf3c69 100644 --- a/selfdrive/ui/sunnypilot/onroad/hud_renderer.py +++ b/selfdrive/ui/sunnypilot/onroad/hud_renderer.py @@ -6,9 +6,8 @@ See the LICENSE.md file in the root directory for more details. """ import pyray as rl +from openpilot.common.constants import CV from openpilot.selfdrive.ui.mici.onroad.torque_bar import TorqueBar -from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.selfdrive.ui.onroad.hud_renderer import HudRenderer from openpilot.selfdrive.ui.sunnypilot.onroad.developer_ui import DeveloperUiRenderer from openpilot.selfdrive.ui.sunnypilot.onroad.road_name import RoadNameRenderer from openpilot.selfdrive.ui.sunnypilot.onroad.rocket_fuel import RocketFuel @@ -17,6 +16,11 @@ from openpilot.selfdrive.ui.sunnypilot.onroad.smart_cruise_control import SmartC from openpilot.selfdrive.ui.sunnypilot.onroad.turn_signal import TurnSignalController from openpilot.selfdrive.ui.sunnypilot.onroad.circular_alerts import CircularAlertsRenderer from openpilot.selfdrive.ui.sunnypilot.onroad.speed_renderer import SpeedRenderer +from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus +from openpilot.selfdrive.ui.onroad.hud_renderer import HudRenderer, UI_CONFIG, FONT_SIZES, COLORS, CRUISE_DISABLED_CHAR +from openpilot.system.ui.lib.application import gui_app +from openpilot.system.ui.lib.multilang import tr +from openpilot.system.ui.lib.text_measure import measure_text_cached class HudRendererSP(HudRenderer): @@ -32,7 +36,21 @@ class HudRendererSP(HudRenderer): self.speed_renderer = SpeedRenderer() self._torque_bar = TorqueBar(scale=3.0, always=True) + self.pcm_cruise_speed: bool = True + self.show_icbm_status: bool = False + self.icbm_active_counter: int = 0 + self.speed_cluster: float = 0.0 + self.speed_conv: float = CV.MS_TO_KPH if ui_state.is_metric else CV.MS_TO_MPH + def _update_state(self) -> None: + if ui_state.sm.recv_frame["carState"] < ui_state.started_frame: + return + + if ui_state.CP_SP is not None: + self.pcm_cruise_speed = ui_state.CP_SP.pcmCruiseSpeed + self.speed_conv = CV.MS_TO_KPH if ui_state.is_metric else CV.MS_TO_MPH + self.speed_cluster = ui_state.sm['carState'].cruiseState.speedCluster * self.speed_conv + super()._update_state() self.road_name_renderer.update() self.speed_limit_renderer.update() @@ -41,6 +59,64 @@ class HudRendererSP(HudRenderer): self.circular_alerts_renderer.update() self.speed_renderer.update() + def _get_icbm_status(self): + if not self.pcm_cruise_speed and ui_state.sm['carControl'].enabled: + if round(self.set_speed) != round(self.speed_cluster): + self.icbm_active_counter = 3 * gui_app.target_fps # 3 seconds usually + elif self.icbm_active_counter > 0: + self.icbm_active_counter -= 1 + else: + self.icbm_active_counter = 0 + + self.show_icbm_status = self.icbm_active_counter > 0 + + def _draw_set_speed(self, rect: rl.Rectangle) -> None: + self._get_icbm_status() + + set_speed_width = UI_CONFIG.set_speed_width_metric if ui_state.is_metric else UI_CONFIG.set_speed_width_imperial + x = rect.x + 60 + (UI_CONFIG.set_speed_width_imperial - set_speed_width) // 2 + y = rect.y + 45 + + set_speed_rect = rl.Rectangle(x, y, set_speed_width, UI_CONFIG.set_speed_height) + rl.draw_rectangle_rounded(set_speed_rect, 0.35, 10, COLORS.BLACK_TRANSLUCENT) + rl.draw_rectangle_rounded_lines_ex(set_speed_rect, 0.35, 10, 6, COLORS.BORDER_TRANSLUCENT) + + max_color = COLORS.GREY + set_speed_color = COLORS.DARK_GREY + if self.is_cruise_set: + set_speed_color = COLORS.WHITE + if ui_state.status == UIStatus.ENGAGED: + max_color = COLORS.ENGAGED + elif ui_state.status == UIStatus.DISENGAGED: + max_color = COLORS.DISENGAGED + elif ui_state.status == UIStatus.OVERRIDE: + max_color = COLORS.OVERRIDE + + max_str_size = 60 if self.show_icbm_status else 40 + max_str_y = 15 if self.show_icbm_status else 27 + + max_text = str(round(self.speed_cluster)) if self.show_icbm_status else tr("MAX") + max_text_width = measure_text_cached(self._font_semi_bold, max_text, max_str_size).x + rl.draw_text_ex( + self._font_semi_bold, + max_text, + rl.Vector2(x + (set_speed_width - max_text_width) / 2, y + max_str_y), + max_str_size, + 0, + max_color, + ) + + set_speed_text = CRUISE_DISABLED_CHAR if not self.is_cruise_set else str(round(self.set_speed)) + speed_text_width = measure_text_cached(self._font_bold, set_speed_text, FONT_SIZES.set_speed).x + rl.draw_text_ex( + self._font_bold, + set_speed_text, + rl.Vector2(x + (set_speed_width - speed_text_width) / 2, y + 77), + FONT_SIZES.set_speed, + 0, + set_speed_color, + ) + def _draw_current_speed(self, rect: rl.Rectangle) -> None: self.speed_renderer.render(rect) diff --git a/selfdrive/ui/sunnypilot/ui_state.py b/selfdrive/ui/sunnypilot/ui_state.py index 6403157d5..369909812 100644 --- a/selfdrive/ui/sunnypilot/ui_state.py +++ b/selfdrive/ui/sunnypilot/ui_state.py @@ -26,6 +26,7 @@ class OnroadTimerStatus(Enum): class UIStateSP: def __init__(self): + self.CP_SP: custom.CarParamsSP | None = None self.params = Params() self.sm_services_ext = [ "modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP",