system/ui: fix timeout logc and add pre-defined alerts (#35417)

fix timeout logc and add pre-defined alerts
pull/35418/head
Dean Lee 2 weeks ago committed by GitHub
parent 1935871267
commit 74541e677c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 115
      system/ui/onroad/alert_renderer.py

@ -1,14 +1,15 @@
import numpy as np import time
import pyray as rl import pyray as rl
from dataclasses import dataclass from dataclasses import dataclass
from cereal import messaging, log from cereal import messaging, log
from openpilot.system.hardware import TICI
from openpilot.system.ui.lib.application import gui_app, FontWeight from openpilot.system.ui.lib.application import gui_app, FontWeight
# Constants # Constants
ALERT_COLORS = { ALERT_COLORS = {
log.SelfdriveState.AlertStatus.normal: rl.Color(0, 0, 0, 150), # Black log.SelfdriveState.AlertStatus.normal: rl.Color(0, 0, 0, 220), # Black
log.SelfdriveState.AlertStatus.userPrompt: rl.Color(0xFE, 0x8C, 0x34, 100), # Orange log.SelfdriveState.AlertStatus.userPrompt: rl.Color(0xFE, 0x8C, 0x34, 220), # Orange
log.SelfdriveState.AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 150), # Red log.SelfdriveState.AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 220), # Red
} }
ALERT_HEIGHTS = { ALERT_HEIGHTS = {
@ -16,6 +17,7 @@ ALERT_HEIGHTS = {
log.SelfdriveState.AlertSize.mid: 420, log.SelfdriveState.AlertSize.mid: 420,
} }
ALERT_BORDER_RADIUS = 30
SELFDRIVE_STATE_TIMEOUT = 5 # Seconds SELFDRIVE_STATE_TIMEOUT = 5 # Seconds
SELFDRIVE_UNRESPONSIVE_TIMEOUT = 10 # Seconds SELFDRIVE_UNRESPONSIVE_TIMEOUT = 10 # Seconds
@ -28,95 +30,80 @@ class Alert:
size: log.SelfdriveState.AlertSize = log.SelfdriveState.AlertSize.none size: log.SelfdriveState.AlertSize = log.SelfdriveState.AlertSize.none
status: log.SelfdriveState.AlertStatus = log.SelfdriveState.AlertStatus.normal status: log.SelfdriveState.AlertStatus = log.SelfdriveState.AlertStatus.normal
def is_equal(self, other: 'Alert') -> bool:
"""Check if two alerts are equal.""" # Pre-defined alert instances
return ( ALERT_STARTUP_PENDING = Alert(
self.text1 == other.text1 text1="openpilot Unavailable",
and self.text2 == other.text2 text2="Waiting to start",
and self.alert_type == other.alert_type alert_type="selfdriveWaiting",
and self.size == other.size size=log.SelfdriveState.AlertSize.mid,
and self.status == other.status status=log.SelfdriveState.AlertStatus.normal,
) )
ALERT_CRITICAL_TIMEOUT = Alert(
text1="TAKE CONTROL IMMEDIATELY",
text2="System Unresponsive",
alert_type="selfdriveUnresponsive",
size=log.SelfdriveState.AlertSize.full,
status=log.SelfdriveState.AlertStatus.critical,
)
ALERT_CRITICAL_REBOOT = Alert(
text1="System Unresponsive",
text2="Reboot Device",
alert_type="selfdriveUnresponsivePermanent",
size=log.SelfdriveState.AlertSize.full,
status=log.SelfdriveState.AlertStatus.critical,
)
class AlertRenderer: class AlertRenderer:
def __init__(self): def __init__(self):
"""Initialize the alert renderer.""" """Initialize the alert renderer."""
self.alert: Alert = Alert() self.alert: Alert = Alert()
# TODO: use ui_state to determine when to start
self.started_frame: int = 0 self.started_frame: int = 0
self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL) self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL)
self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD) self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD)
self.font_metrics_cache: dict[tuple[str, int, str], rl.Vector2] = {} self.font_metrics_cache: dict[tuple[str, int, str], rl.Vector2] = {}
def clear(self) -> None: def update_state(self, sm: messaging.SubMaster) -> None:
"""Reset the alert to its default state."""
self.alert = Alert()
def update_state(self, sm: messaging.SubMaster, started_frame: int) -> None:
"""Update alert state based on SubMaster data.""" """Update alert state based on SubMaster data."""
self.started_frame = started_frame self.alert = self.get_alert(sm)
new_alert = self.get_alert(sm)
if not self.alert.is_equal(new_alert):
self.alert = new_alert
def get_alert(self, sm: messaging.SubMaster) -> Alert: def get_alert(self, sm: messaging.SubMaster) -> Alert:
"""Generate the current alert based on selfdrive state.""" """Generate the current alert based on selfdrive state."""
if not sm.valid['selfdriveState']:
return Alert()
ss = sm['selfdriveState'] ss = sm['selfdriveState']
selfdrive_frame = sm.recv_frame['selfdriveState']
alert_status = self._get_enum_value(ss.alertStatus, log.SelfdriveState.AlertStatus)
# Return current alert if selfdrive state is recent # Check if waiting to start
if selfdrive_frame >= self.started_frame: if sm.recv_frame['selfdriveState'] < self.started_frame:
return Alert( return ALERT_STARTUP_PENDING
# Handle selfdrive timeout
ss_missing = time.monotonic() - sm.recv_time['selfdriveState']
if TICI:
if ss_missing > SELFDRIVE_STATE_TIMEOUT:
if ss.enabled and (ss_missing - SELFDRIVE_STATE_TIMEOUT) < SELFDRIVE_UNRESPONSIVE_TIMEOUT:
return ALERT_CRITICAL_TIMEOUT
return ALERT_CRITICAL_REBOOT
# Return current alert from selfdrive state
return Alert(
text1=ss.alertText1, text1=ss.alertText1,
text2=ss.alertText2, text2=ss.alertText2,
alert_type=ss.alertType, alert_type=ss.alertType,
size=self._get_enum_value(ss.alertSize, log.SelfdriveState.AlertSize), size=self._get_enum_value(ss.alertSize, log.SelfdriveState.AlertSize),
status=alert_status, status=self._get_enum_value(ss.alertStatus, log.SelfdriveState.AlertStatus))
)
# Handle selfdrive timeout
ss_missing = (np.uint64(rl.get_time() * 1e9) - sm.recv_time['selfdriveState']) / 1e9
if selfdrive_frame < self.started_frame:
return Alert(
text1="openpilot Unavailable",
text2="Waiting to start",
alert_type="selfdriveWaiting",
size=log.SelfdriveState.AlertSize.mid,
status=log.SelfdriveState.AlertStatus.normal,
)
elif ss_missing > SELFDRIVE_STATE_TIMEOUT:
if ss.enabled and (ss_missing - SELFDRIVE_STATE_TIMEOUT) < SELFDRIVE_UNRESPONSIVE_TIMEOUT:
return Alert(
text1="TAKE CONTROL IMMEDIATELY",
text2="System Unresponsive",
alert_type="selfdriveUnresponsive",
size=log.SelfdriveState.AlertSize.full,
status=log.SelfdriveState.AlertStatus.critical,
)
return Alert(
text1="System Unresponsive",
text2="Reboot Device",
alert_type="selfdriveUnresponsivePermanent",
size=log.SelfdriveState.AlertSize.mid,
status=log.SelfdriveState.AlertStatus.normal,
)
return Alert()
def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster) -> None: def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster) -> None:
"""Render the alert within the specified rectangle.""" """Render the alert within the specified rectangle."""
self.update_state(sm, sm.recv_frame['selfdriveState']) self.update_state(sm)
alert_size = self._get_enum_value(self.alert.size, log.SelfdriveState.AlertSize) alert_size = self._get_enum_value(self.alert.size, log.SelfdriveState.AlertSize)
if alert_size == log.SelfdriveState.AlertSize.none: if alert_size == log.SelfdriveState.AlertSize.none:
return return
# Calculate alert rectangle # Calculate alert rectangle
margin = 0 if alert_size == log.SelfdriveState.AlertSize.full else 40 margin = 0 if alert_size == log.SelfdriveState.AlertSize.full else 40
radius = 0 if alert_size == log.SelfdriveState.AlertSize.full else 30
height = ALERT_HEIGHTS.get(alert_size, rect.height) height = ALERT_HEIGHTS.get(alert_size, rect.height)
alert_rect = rl.Rectangle( alert_rect = rl.Rectangle(
rect.x + margin, rect.x + margin,
@ -129,7 +116,7 @@ class AlertRenderer:
alert_status = self._get_enum_value(self.alert.status, log.SelfdriveState.AlertStatus) alert_status = self._get_enum_value(self.alert.status, log.SelfdriveState.AlertStatus)
color = ALERT_COLORS.get(alert_status, ALERT_COLORS[log.SelfdriveState.AlertStatus.normal]) color = ALERT_COLORS.get(alert_status, ALERT_COLORS[log.SelfdriveState.AlertStatus.normal])
if alert_size != log.SelfdriveState.AlertSize.full: if alert_size != log.SelfdriveState.AlertSize.full:
roundness = radius / (min(alert_rect.width, alert_rect.height) / 2) roundness = ALERT_BORDER_RADIUS / (min(alert_rect.width, alert_rect.height) / 2)
rl.draw_rectangle_rounded(alert_rect, roundness, 10, color) rl.draw_rectangle_rounded(alert_rect, roundness, 10, color)
else: else:
rl.draw_rectangle_rec(alert_rect, color) rl.draw_rectangle_rec(alert_rect, color)

Loading…
Cancel
Save