mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
80fbb4f6db
Register vehicle controls, planner modes, UI actions and Galaxy services after their providers exist. Wire the shared runtime entrypoints and cover their combined behavior. 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: Kacper Rączy <gfw.kra@gmail.com> Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu> 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: haraschax <6804392+haraschax@users.noreply.github.com> Co-authored-by: prabhaavp <prabhaav@msu.edu>
129 lines
7.1 KiB
Python
129 lines
7.1 KiB
Python
import unittest
|
|
import numpy as np
|
|
from types import SimpleNamespace as N
|
|
from unittest.mock import patch
|
|
|
|
from openpilot.cereal import messaging, log
|
|
from openpilot.common.prefix import OpenpilotPrefix
|
|
from openpilot.selfdrive.selfdrived.alertmanager import AlertManager
|
|
from openpilot.selfdrive.selfdrived.events import Alert, ET, Priority, VisualAlert
|
|
from openpilot.selfdrive.selfdrived.selfdrived import SelfdriveD
|
|
from openpilot.selfdrive.ui.soundd import Soundd, AudibleAlert
|
|
from openpilot.starpilot.audio.axis_alerts import AxisAlerts
|
|
from openpilot.starpilot.audio.availability_warning import AvailabilityWarningDelivery
|
|
|
|
class TestAvailabilityWarningDelivery(unittest.TestCase):
|
|
def fixture_attr(self, obj, name, value):
|
|
replacement = patch.object(obj, name, value, create=True)
|
|
replacement.start()
|
|
self.addCleanup(replacement.stop)
|
|
|
|
def publisher(self, pm):
|
|
sd = SelfdriveD.__new__(SelfdriveD)
|
|
sd.enabled = sd.active = False
|
|
self.fixture_attr(sd, 'state_machine', N(state=log.SelfdriveState.OpenpilotState.disabled))
|
|
self.fixture_attr(sd, 'events', N(contains=lambda _: False))
|
|
sd.experimental_mode = False
|
|
sd.personality = log.LongitudinalPersonality.standard
|
|
sd.AM = AlertManager()
|
|
sd.pm = pm
|
|
sd.conditional_replay = False
|
|
sd.aol_session_id = 'delivery-session'
|
|
self.fixture_attr(sd, 'sm', {'deviceState': N(started=True, startedMonoTime=1)})
|
|
sd.availability_warning_delivery = AvailabilityWarningDelivery()
|
|
return sd
|
|
|
|
def issue(self, sd, now):
|
|
alert = Alert('Steering Paused', 'Communication Issue', log.SelfdriveState.AlertStatus.normal,
|
|
log.SelfdriveState.AlertSize.small, Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 0.)
|
|
alert.alert_type, alert.event_type = 'aolAvailability/warning', ET.WARNING
|
|
sd.AM.add_many(10, [alert])
|
|
sd.AM.process_alerts(10, set())
|
|
sd.availability_warning_delivery.observe(session=sd.aol_session_id, drive=1, now_ns=now,
|
|
selected=True, requested_until_ns=now + 400_000_000)
|
|
|
|
def sound(self):
|
|
sound = Soundd.__new__(Soundd)
|
|
sound.axis_alerts = AxisAlerts()
|
|
sound.current_alert = sound.current_sound = AudibleAlert.none
|
|
sound.current_sound_frame = 0
|
|
sound.pending_stop = False
|
|
sound.selfdrive_timeout_alert = False
|
|
sound.loaded_sounds = {AudibleAlert.prompt: np.full(16, 0.25, dtype=np.float32),
|
|
AudibleAlert.none: np.zeros(16, dtype=np.float32)}
|
|
sound.current_volume = 1.0
|
|
sound.saved_volumes = {}
|
|
self.fixture_attr(sound, 'pack_loader', N(is_builtin=lambda _: False))
|
|
return sound
|
|
|
|
def test_actual_serialized_publisher_latest_conflation_delivers_after_five_ms_recovery(self):
|
|
with OpenpilotPrefix():
|
|
pm = messaging.PubMaster(['selfdriveState'])
|
|
sm = messaging.SubMaster(['selfdriveState']) # production conflate=True
|
|
sd = self.publisher(pm)
|
|
now = 10_000_000_000
|
|
self.issue(sd, now)
|
|
with patch('openpilot.selfdrive.selfdrived.selfdrived.time.monotonic_ns', return_value=now):
|
|
sd.publish_selfdriveState(N(), axis_published=True)
|
|
sd.AM.process_alerts(11, {ET.WARNING})
|
|
for delta in (5_000_000, 10_000_000, 20_000_000, 30_000_000, 40_000_000):
|
|
with patch('openpilot.selfdrive.selfdrived.selfdrived.time.monotonic_ns', return_value=now + delta):
|
|
sd.publish_selfdriveState(N(), axis_published=True)
|
|
sm.update(1000) # first soundd read after all producer publications
|
|
self.assertTrue(sm.updated['selfdriveState'])
|
|
self.assertEqual(sm['selfdriveState'].alertType, '')
|
|
self.assertEqual(str(sm['selfdriveState'].alertSound), 'prompt')
|
|
self.assertFalse(sm['selfdriveState'].enabled or sm['selfdriveState'].active)
|
|
sound = self.sound()
|
|
with patch('openpilot.selfdrive.ui.soundd.time.monotonic_ns', return_value=now + 50_000_000):
|
|
sound.get_audible_alert(sm) # actual 20 Hz first receive boundary, no audio device
|
|
self.assertEqual(sound.current_alert, AudibleAlert.prompt)
|
|
self.assertEqual(sound.current_sound_frame, 0)
|
|
pcm = sound.get_sound_data(16)
|
|
self.assertTrue(np.all(pcm > 0))
|
|
self.assertEqual(sound.current_sound_frame, 16)
|
|
with patch('openpilot.selfdrive.ui.soundd.time.monotonic_ns', return_value=now + 100_000_000):
|
|
sound.get_audible_alert(sm)
|
|
self.assertEqual(sound.current_sound_frame, 16) # repeated prompt cannot restart the sample
|
|
self.assertTrue(np.all(sound.get_sound_data(16) == 0))
|
|
|
|
def test_deadline_does_not_extend_on_healthy_frames(self):
|
|
d = AvailabilityWarningDelivery()
|
|
d.observe(session='s', drive=1, now_ns=100, selected=True, requested_until_ns=400_000_100)
|
|
d.observe(session='s', drive=1, now_ns=300_000_100, selected=False, requested_until_ns=999_000_100)
|
|
self.assertTrue(d.pending(session='s', drive=1, now_ns=400_000_100, audible_none=True, priority=0, low_priority=1))
|
|
self.assertFalse(d.pending(session='s', drive=1, now_ns=400_000_101, audible_none=True, priority=0, low_priority=1))
|
|
|
|
def test_never_issued_stays_quiet(self):
|
|
d = AvailabilityWarningDelivery()
|
|
d.observe(session='s', drive=1, now_ns=100, selected=False, requested_until_ns=400_000_100)
|
|
self.assertFalse(d.pending(session='s', drive=1, now_ns=100, audible_none=True, priority=0, low_priority=1))
|
|
|
|
def test_urgent_and_existing_audible_preempt(self):
|
|
d = AvailabilityWarningDelivery()
|
|
d.observe(session='s', drive=1, now_ns=100, selected=True, requested_until_ns=400_000_100)
|
|
self.assertFalse(d.pending(session='s', drive=1, now_ns=101, audible_none=True, priority=2, low_priority=1))
|
|
self.assertFalse(d.pending(session='s', drive=1, now_ns=101, audible_none=False, priority=0, low_priority=1))
|
|
|
|
def test_context_changes_between_observation_and_publication_deny(self):
|
|
d = AvailabilityWarningDelivery()
|
|
d.observe(session='s', drive=1, now_ns=100, selected=True, requested_until_ns=400_000_100)
|
|
for session,drive in [('new',1),('s',2),('s',0),(None,1)]:
|
|
with self.subTest(session=session,drive=drive):
|
|
self.assertFalse(d.pending(session=session, drive=drive, now_ns=101, audible_none=True, priority=0, low_priority=1))
|
|
|
|
def test_clock_rollback_and_offroad_reset(self):
|
|
d = AvailabilityWarningDelivery()
|
|
d.observe(session='s', drive=1, now_ns=100, selected=True, requested_until_ns=400_000_100)
|
|
d.observe(session='s', drive=1, now_ns=99, selected=False, requested_until_ns=400_000_100)
|
|
self.assertFalse(d.pending(session='s', drive=1, now_ns=101, audible_none=True, priority=0, low_priority=1))
|
|
d.observe(session='s', drive=1, now_ns=102, selected=True, requested_until_ns=400_000_102)
|
|
d.observe(session='s', drive=0, now_ns=103, selected=False, requested_until_ns=400_000_102)
|
|
self.assertFalse(d.pending(session='s', drive=1, now_ns=104, audible_none=True, priority=0, low_priority=1))
|
|
|
|
def test_new_episode_after_deadline_can_issue(self):
|
|
d = AvailabilityWarningDelivery()
|
|
d.observe(session='s', drive=1, now_ns=100, selected=True, requested_until_ns=400_000_100)
|
|
d.observe(session='s', drive=1, now_ns=500_000_100, selected=True, requested_until_ns=900_000_100)
|
|
self.assertTrue(d.pending(session='s', drive=1, now_ns=550_000_100, audible_none=True, priority=0, low_priority=1))
|