Files
firestar5683 a82935f326 User interfaces and Android Auto
Integrate onroad views, configurable widgets, local settings, steering-profile contracts, Bluetooth and Android Auto with their device and runtime services.

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
Co-authored-by: Daniel Koepping <elkoled@gmail.com>
Co-authored-by: Danny <dannyirwin31@yahoo.com>
Co-authored-by: DimitriChrysafis <Dimitri.chrysafis@gmail.com>
Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com>
Co-authored-by: Harald Schäfer <harald.the.engineer@gmail.com>
Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu>
Co-authored-by: Shane Smiskol <shane@smiskol.com>
Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com>
Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
Co-authored-by: stef <19478336+stefpi@users.noreply.github.com>
Co-authored-by: whoisdomi <dcantu83@gmail.com>

Co-authored-by: Intelli <contact@intelli.software>
Co-authored-by: pharmacomaniac <blittle65@gmail.com>

Co-authored-by: prabhaavp <prabhaav@msu.edu>
2026-10-14 20:02:39 -05:00

139 lines
6.3 KiB
Python

"""Native display projection of saved choice and selfdrived's actual mode receipt."""
from dataclasses import dataclass
from openpilot.starpilot.conditional_mode.effective_status import EffectiveModeAck, LIFETIME_NS, observation
from openpilot.starpilot.conditional_mode.policy import ModeChoice
from openpilot.starpilot.conditional_mode.preferences import MAX_DOCUMENT_BYTES, PreferenceError, decode_preferences
from openpilot.starpilot.saved_source import read_saved
@dataclass(frozen=True)
class ConditionalDisplay:
choice: ModeChoice
effective_experimental: bool
reason: str
status_code: int
planner_session: str
planner_sequence: int
def configured_choice(params) -> ModeChoice | None:
"""Read saved configuration only; it says nothing about active control."""
try:
raw, readable = read_saved(params, 'ConditionalModeConfig', MAX_DOCUMENT_BYTES)
if not readable:
return None
return ModeChoice.CEM if raw is None else decode_preferences(raw).mode
except (AttributeError, PreferenceError, OSError, TypeError, ValueError):
return None
class ConditionalDisplayProjector:
"""Show fresh acknowledgments while the current selfdrive mode agrees."""
def __init__(self):
self.drive_id = 0
self.session: str | None = None
self.sequence = 0
self.last: EffectiveModeAck | None = None
self.retired: tuple[str, ...] = ()
def reset(self) -> None:
self.drive_id = 0
self.session = None
self.sequence = 0
self.last = None
self.retired = ()
def project(self, state, *, now_ns: int, event_ns: int, selfdrive_ns: int, drive_id: int,
selfdrive_experimental: bool, selfdrive_enabled: bool, long_active: bool,
car_valid: bool, system_long: bool) -> ConditionalDisplay | None:
if type(drive_id) is not int or drive_id <= 0:
self.reset()
return None
if self.drive_id != drive_id:
self.reset()
self.drive_id = drive_id
# A render frame can outlast the control receipt. Its display remains bounded
# separately; callers issuing commands still use the original receipt expiry.
display_fresh = (type(event_ns) is int and type(now_ns) is int and
0 < event_ns <= now_ns <= event_ns + 200_000_000)
value = observation(state, event_ns) if state is not None and display_fresh else None
if (value is None or type(event_ns) is not int or event_ns != value.observed_ns or
type(selfdrive_ns) is not int or not 0 < selfdrive_ns <= now_ns <= selfdrive_ns + 200_000_000 or
value.selfdrive_state_ns > selfdrive_ns + LIFETIME_NS or
type(selfdrive_experimental) is not bool or type(selfdrive_enabled) is not bool or
type(long_active) is not bool or type(car_valid) is not bool or type(system_long) is not bool or
value.drive_id != drive_id or value.effective_experimental is not selfdrive_experimental):
return None
if value.session in self.retired:
return None
if self.session is not None and value.session != self.session:
self.retired = (*self.retired[-7:], self.session)
self.session = value.session
self.sequence = value.sequence
self.last = value
return None # One new-source barrier after selfdrived restart.
if value.session == self.session:
if value.sequence < self.sequence or value.sequence == self.sequence and value != self.last:
return None
if value.sequence > self.sequence and self.last is not None and value.observed_ns <= self.last.observed_ns:
return None
self.session = value.session
self.sequence = value.sequence
self.last = value
if (not value.accepted or not selfdrive_enabled or not long_active or not car_valid or not system_long or
value.planner_session is None or value.reason is None):
return None
return ConditionalDisplay(value.choice, value.effective_experimental, value.reason, value.status_code,
value.planner_session, value.planner_sequence)
class ConditionalPerceptionProjector:
"""Display the planner's current scene without treating it as control approval."""
def __init__(self):
self.drive_id = 0
self.session = None
self.sequence = 0
self.last = None
self.retired = ()
def project(self, state, *, now_ns: int, event_ns: int, drive_id: int,
choice: ModeChoice | None, lateral_active: bool, selfdrive_enabled: bool,
car_valid: bool, system_long: bool) -> ConditionalDisplay | None:
from openpilot.starpilot.conditional_mode.status import observation as scene_observation
if type(drive_id) is not int or drive_id <= 0:
self.__init__()
return None
if drive_id != self.drive_id:
self.__init__()
self.drive_id = drive_id
fresh = (type(now_ns) is int and type(event_ns) is int and
drive_id <= event_ns <= now_ns <= event_ns + 200_000_000)
# Other planner owners can create the envelope before this scene is sampled.
# Bound both clocks for presentation; command consumers retain proposal expiry.
try:
scene_ns = int(state.conditionalMode.observedMonoTime) if state is not None else 0
except (AttributeError, TypeError, ValueError, OverflowError):
scene_ns = 0
scene_fresh = fresh and drive_id <= scene_ns <= now_ns <= scene_ns + 200_000_000
value = scene_observation(state, scene_ns) if scene_fresh else None
if (value is None or value.drive_id != drive_id or
value.choice is not choice or choice not in (ModeChoice.CEM, ModeChoice.CCM) or
value.status not in ('proposed', 'inactive_axis') or not car_valid or not system_long or
not (lateral_active or selfdrive_enabled) or value.session in self.retired):
return None
if self.session is not None and value.session != self.session:
self.retired = (*self.retired[-7:], self.session)
self.session, self.sequence, self.last = value.session, value.sequence, value
return None
if value.session == self.session and (
value.sequence < self.sequence or value.sequence == self.sequence and value != self.last or
value.sequence > self.sequence and self.last is not None and value.observed_ns <= self.last.observed_ns):
return None
self.session, self.sequence, self.last = value.session, value.sequence, value
return ConditionalDisplay(value.choice, False, value.reason, value.status_code, value.session, value.sequence)