mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-06-30 11:02:19 +08:00
system/ui: fix timeout logc and add pre-defined alerts (#35417)
fix timeout logc and add pre-defined alerts
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
import numpy as np
|
||||
import time
|
||||
import pyray as rl
|
||||
from dataclasses import dataclass
|
||||
from cereal import messaging, log
|
||||
from openpilot.system.hardware import TICI
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight
|
||||
|
||||
# Constants
|
||||
ALERT_COLORS = {
|
||||
log.SelfdriveState.AlertStatus.normal: rl.Color(0, 0, 0, 150), # Black
|
||||
log.SelfdriveState.AlertStatus.userPrompt: rl.Color(0xFE, 0x8C, 0x34, 100), # Orange
|
||||
log.SelfdriveState.AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 150), # Red
|
||||
log.SelfdriveState.AlertStatus.normal: rl.Color(0, 0, 0, 220), # Black
|
||||
log.SelfdriveState.AlertStatus.userPrompt: rl.Color(0xFE, 0x8C, 0x34, 220), # Orange
|
||||
log.SelfdriveState.AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 220), # Red
|
||||
}
|
||||
|
||||
ALERT_HEIGHTS = {
|
||||
@@ -16,6 +17,7 @@ ALERT_HEIGHTS = {
|
||||
log.SelfdriveState.AlertSize.mid: 420,
|
||||
}
|
||||
|
||||
ALERT_BORDER_RADIUS = 30
|
||||
SELFDRIVE_STATE_TIMEOUT = 5 # Seconds
|
||||
SELFDRIVE_UNRESPONSIVE_TIMEOUT = 10 # Seconds
|
||||
|
||||
@@ -28,95 +30,80 @@ class Alert:
|
||||
size: log.SelfdriveState.AlertSize = log.SelfdriveState.AlertSize.none
|
||||
status: log.SelfdriveState.AlertStatus = log.SelfdriveState.AlertStatus.normal
|
||||
|
||||
def is_equal(self, other: 'Alert') -> bool:
|
||||
"""Check if two alerts are equal."""
|
||||
return (
|
||||
self.text1 == other.text1
|
||||
and self.text2 == other.text2
|
||||
and self.alert_type == other.alert_type
|
||||
and self.size == other.size
|
||||
and self.status == other.status
|
||||
)
|
||||
|
||||
# Pre-defined alert instances
|
||||
ALERT_STARTUP_PENDING = Alert(
|
||||
text1="openpilot Unavailable",
|
||||
text2="Waiting to start",
|
||||
alert_type="selfdriveWaiting",
|
||||
size=log.SelfdriveState.AlertSize.mid,
|
||||
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:
|
||||
def __init__(self):
|
||||
"""Initialize the alert renderer."""
|
||||
self.alert: Alert = Alert()
|
||||
# TODO: use ui_state to determine when to start
|
||||
self.started_frame: int = 0
|
||||
self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL)
|
||||
self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD)
|
||||
self.font_metrics_cache: dict[tuple[str, int, str], rl.Vector2] = {}
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Reset the alert to its default state."""
|
||||
self.alert = Alert()
|
||||
|
||||
def update_state(self, sm: messaging.SubMaster, started_frame: int) -> None:
|
||||
def update_state(self, sm: messaging.SubMaster) -> None:
|
||||
"""Update alert state based on SubMaster data."""
|
||||
self.started_frame = started_frame
|
||||
new_alert = self.get_alert(sm)
|
||||
if not self.alert.is_equal(new_alert):
|
||||
self.alert = new_alert
|
||||
self.alert = self.get_alert(sm)
|
||||
|
||||
def get_alert(self, sm: messaging.SubMaster) -> Alert:
|
||||
"""Generate the current alert based on selfdrive state."""
|
||||
if not sm.valid['selfdriveState']:
|
||||
return Alert()
|
||||
|
||||
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
|
||||
if selfdrive_frame >= self.started_frame:
|
||||
return Alert(
|
||||
# Check if waiting to start
|
||||
if sm.recv_frame['selfdriveState'] < self.started_frame:
|
||||
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,
|
||||
text2=ss.alertText2,
|
||||
alert_type=ss.alertType,
|
||||
size=self._get_enum_value(ss.alertSize, log.SelfdriveState.AlertSize),
|
||||
status=alert_status,
|
||||
)
|
||||
|
||||
# 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()
|
||||
status=self._get_enum_value(ss.alertStatus, log.SelfdriveState.AlertStatus))
|
||||
|
||||
def draw(self, rect: rl.Rectangle, sm: messaging.SubMaster) -> None:
|
||||
"""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)
|
||||
if alert_size == log.SelfdriveState.AlertSize.none:
|
||||
return
|
||||
|
||||
# Calculate alert rectangle
|
||||
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)
|
||||
alert_rect = rl.Rectangle(
|
||||
rect.x + margin,
|
||||
@@ -129,7 +116,7 @@ class AlertRenderer:
|
||||
alert_status = self._get_enum_value(self.alert.status, log.SelfdriveState.AlertStatus)
|
||||
color = ALERT_COLORS.get(alert_status, ALERT_COLORS[log.SelfdriveState.AlertStatus.normal])
|
||||
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)
|
||||
else:
|
||||
rl.draw_rectangle_rec(alert_rect, color)
|
||||
|
||||
Reference in New Issue
Block a user