Keep traffic mode icon stable on C4

This commit is contained in:
firestar5683
2026-11-01 16:44:22 -06:00
parent c0bbed7762
commit e7cca0a136
6 changed files with 26 additions and 23 deletions
+4 -15
View File
@@ -5,7 +5,7 @@ import pyray as rl
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.starpilot.ui.onroad_state import AlertSize, OnroadState, is_lane_change_notice, personality_bounds
from openpilot.starpilot.ui.onroad_widget_style import draw_widget_frame
from openpilot.starpilot.ui.presentation import BitmapFonts, FontRole, Profile
from openpilot.starpilot.ui.presentation import BitmapFonts, Profile
from openpilot.system.ui.lib.application import gui_app
HIGHLIGHT_TIME = 2.5
@@ -47,19 +47,8 @@ class PersonalityWidget:
width, height = right - left, bottom - top
rect = rl.Rectangle(left, top, width, height)
draw_widget_frame(rect, state.customization, profile, 'following_distance')
scale = width / 60
label = ({'active': 'TRF', 'paused': 'PAUSE', 'off': 'OFF'}.get(state.traffic_display.state)
if state.traffic_display is not None else None)
icon = rl.Rectangle(rect.x, rect.y, width, height - 14 * scale if label else height)
self.draw(icon, personality=state.personality, has_lead=state.lead_detected, longitudinal_active=state.cruise_active,
traffic_mode=state.traffic_mode, gas_override=state.gas_pressed_override, visible=routine)
if label and self.fonts is not None and state.alert.size != AlertSize.FULL:
size = 11 * scale * Profile.COMPACT.font_scale / profile.font_scale
measured = self.fonts.measure(label, FontRole.SEMI_BOLD, size)
color = ((255, 155, 63, 255) if state.traffic_display.state == 'paused' else
(200, 32, 48, 255) if state.traffic_mode else (112, 192, 216, 255))
self.fonts.draw(label, FontRole.SEMI_BOLD, size, rect.x + (width - measured.width) / 2,
rect.y + height - 14 * scale, rl.Color(*color))
self.draw(rect, personality=state.personality, has_lead=state.lead_detected, longitudinal_active=state.cruise_active,
traffic_mode=state.traffic_selected or state.traffic_mode, gas_override=state.gas_pressed_override, visible=routine)
def draw(self, rect: rl.Rectangle, *, personality: int, has_lead: bool, longitudinal_active: bool,
traffic_mode: bool = False, gas_override: bool = False, visible: bool = True, fit: bool = True) -> None:
@@ -82,7 +71,7 @@ class PersonalityWidget:
if not visible and now - self._personality_changed_time < HIGHLIGHT_TIME:
self._personality_changed_time = now
highlight = now - self._personality_changed_time < HIGHLIGHT_TIME
blink = 0.35 / 0.9 if gas_override and longitudinal_active and now % 0.375 > 0.1875 else 1.0
blink = 0.35 / 0.9 if gas_override and longitudinal_active and not traffic_mode and now % 0.375 > 0.1875 else 1.0
count = 1 if traffic_mode else personality + 1
for i, ((white, green), y, (active_f, green_f)) in enumerate(zip(self._distance, (122, 136, 152), self._distance_filters, strict=True)):
active = active_f.update(float(i < count))
+1
View File
@@ -195,6 +195,7 @@ class OnroadState:
right_hand_drive: bool = False
switchback_mode: bool = False
traffic_mode: bool = False
traffic_selected: bool = False
traffic_display: TrafficDisplay | None = None
aol_latched: bool | None = None
lateral_active: bool = False
+1 -1
View File
@@ -452,7 +452,7 @@ class StarShellSession:
invoke=lambda: guarded(device.toggle_screen_off))
if not hasattr(self, 'mode_actions'):
self.mode_actions = ModeActionPublisher()
for key, label, mode, requested in ((TRAFFIC, "Traffic Mode", 'traffic', state.traffic_mode),
for key, label, mode, requested in ((TRAFFIC, "Traffic Mode", 'traffic', state.traffic_selected or state.traffic_mode),
(SWITCHBACK, "Switchback Mode", 'switchback', getattr(state, 'switchback_mode', False))):
now_ns = time.monotonic_ns()
allowed = (self._favorite_authority() and not ui_state.params.get_bool("SafeMode") and
@@ -693,6 +693,7 @@ class RuntimeSnapshotAdapter:
self._switchback.sample_state(slc, drive_id=drive_id, now_ns=now_ns,
event_ns=int(sm.logMonoTime['slcState']))),
traffic_mode=bool(traffic is not None and traffic.state == 'active'),
traffic_selected=bool(started and self._traffic.drive_id == drive_id and self._traffic.requested),
traffic_display=traffic,
torque_utilization=torque.utilization if torque.utilization is not None else 0.0,
torque_source_available=torque.utilization is not None,
@@ -99,18 +99,25 @@ def test_gas_override_blinks_only_selected_bars_and_clears_on_disengagement(disp
@pytest.mark.parametrize('profile', list(Profile))
def test_traffic_label_and_accent_restore_selected_personality(display, profile):
def test_traffic_accent_is_stable_without_labels_and_restores_personality(display, profile):
display.fonts.profile = profile
state = road(personality=2, traffic_mode=True, traffic_display=TrafficDisplay('active', 'TRAFFIC'))
draws = settle(display, state)
assert draws[2].args[4] == (200, 32, 48)
assert draws[4].args[3] == pytest.approx(.35, abs=.001)
assert all(draws[i].args[3] < .001 for i in (3, 5, 7))
assert display.fonts.draw.call_args.args[0] == 'TRF'
for status, label in (('paused', 'PAUSE'), ('off', 'OFF')):
draws = settle(display, replace(state, traffic_mode=False, traffic_display=TrafficDisplay(status, '')))
assert all(draws[i].args[3] == pytest.approx(.9, abs=.001) for i in (2, 4, 6))
assert display.fonts.draw.call_args.args[0] == label
original_rect = draws[2].args[1]
for status in ('paused', 'unavailable_source', 'unavailable_profile', None):
draws = settle(display, replace(state, traffic_mode=False, traffic_selected=True,
gas_pressed_override=True,
traffic_display=TrafficDisplay(status, '') if status else None))
assert draws[2].args[4] == (200, 32, 48)
assert draws[2].args[3] == pytest.approx(.9, abs=.001)
rect = draws[2].args[1]
assert (rect.x, rect.y, rect.width, rect.height) == (original_rect.x, original_rect.y, original_rect.width, original_rect.height)
draws = settle(display, replace(state, traffic_mode=False, traffic_selected=False, traffic_display=TrafficDisplay('off', 'OFF')))
assert all(draws[i].args[3] == pytest.approx(.9, abs=.001) for i in (2, 4, 6))
display.fonts.draw.assert_not_called()
def test_large_default_sits_left_of_driver_monitor_and_custom_positions_stay_put(display):
@@ -74,15 +74,19 @@ class TestTrafficStatus(unittest.TestCase):
def test_actual_wire_states_and_clearing(self):
active = receipt(self.settings_hash, self.map_hash)
self.assertEqual(self.project(active).state, 'active')
self.assertTrue(self.reader.requested)
self.assertEqual(self.project(active).state, 'active') # Same frame remains fresh, not a new source.
self.assertIsNone(self.project(active, now_mono_ns=NOW + 101_000_000))
self.assertTrue(self.reader.requested)
self.assertIsNone(self.project(active, now_boot_ns=BOOT + 400_000_000))
paused = receipt(self.settings_hash, self.map_hash, sequence=2, observed=NOW + 10_000_000,
reason='authority_unavailable', effective=False)
self.assertEqual(self.project(paused, now_mono_ns=NOW + 10_000_000, long_active=False).state, 'paused')
self.assertTrue(self.reader.requested)
off = receipt(self.settings_hash, self.map_hash, sequence=3, observed=NOW + 20_000_000,
reason='off', accepted=False, effective=False)
self.assertEqual(self.project(off, now_mono_ns=NOW + 20_000_000).state, 'off')
self.assertFalse(self.reader.requested)
unavailable = receipt(self.settings_hash, self.map_hash, sequence=4, observed=NOW + 30_000_000, ready=False)
self.assertEqual(self.project(unavailable, now_mono_ns=NOW + 30_000_000).state, 'unavailable_profile')
self.assertIsNone(self.project(active)) # Old sequence cannot resurrect active status.
@@ -133,7 +137,7 @@ class TestTrafficStatus(unittest.TestCase):
rail = MiciSidebarWidgets(fonts)
with mock.patch.object(rail.personality, 'draw'):
rail._personality(rl.Rectangle(476, 160, 60, 80), shown)
self.assertEqual(fonts.draw.call_args.args[:3], ('TRF', FontRole.SEMI_BOLD, 11))
fonts.draw.assert_not_called()
large = OnroadView.__new__(OnroadView)
large.personality = mock.Mock()
large.fonts = fonts
@@ -143,6 +147,7 @@ class TestTrafficStatus(unittest.TestCase):
large._traffic_badge(replace(shown, viewport_width=width), 101)
self.assertEqual(fonts.draw.call_args.args[:5], ('TRAFFIC', FontRole.SEMI_BOLD, 25, expected_x, 101))
self.assertFalse(adapter.build(ShellMode.ONROAD, now_ns=NOW + 101_000_000).onroad.traffic_mode)
self.assertTrue(adapter.build(ShellMode.ONROAD, now_ns=NOW + 101_000_000).onroad.traffic_selected)
self.assertIsNone(adapter.build(ShellMode.ONROAD, now_ns=NOW + 101_000_000).onroad.traffic_display)