mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-01 22:19:45 +08:00
Offroad/onroad guarding
This commit is contained in:
@@ -835,6 +835,9 @@ class AugmentedRoadView(CameraView):
|
||||
self._reverse_driver_camera_active = False
|
||||
return
|
||||
|
||||
if getattr(self, "_onroad_reentry_pending", False):
|
||||
self._refresh_available_streams()
|
||||
|
||||
if self._update_reverse_driver_camera_state():
|
||||
self.switch_stream(DRIVER_CAM)
|
||||
return
|
||||
@@ -857,7 +860,8 @@ class AugmentedRoadView(CameraView):
|
||||
else:
|
||||
target = ROAD_CAM
|
||||
|
||||
if self.stream_type != target or (self._switching and self._target_stream_type != target):
|
||||
if (getattr(self, "_onroad_reentry_pending", False) or
|
||||
self.stream_type != target or (self._switching and self._target_stream_type != target)):
|
||||
self.switch_stream(target)
|
||||
|
||||
def _update_calibration(self):
|
||||
|
||||
@@ -164,3 +164,34 @@ def test_reverse_activation_cancels_mismatched_pending_switch():
|
||||
assert view._target_client is None
|
||||
assert view._target_stream_type is None
|
||||
assert not view._switching
|
||||
|
||||
|
||||
def test_onroad_transition_marks_camera_reentry(monkeypatch):
|
||||
module = big_cameraview
|
||||
|
||||
class FakeClient:
|
||||
pass
|
||||
|
||||
view = module.CameraView.__new__(module.CameraView)
|
||||
view._name = "camerad"
|
||||
view._stream_type = object()
|
||||
view.client = FakeClient()
|
||||
view.frame = None
|
||||
view.available_streams = []
|
||||
view._target_client = None
|
||||
view._target_stream_type = None
|
||||
view._switching = False
|
||||
view._texture_needs_update = False
|
||||
view._regressive_frame_count = 1
|
||||
view._closed = True
|
||||
view._onroad_reentry_pending = False
|
||||
view._reentry_stream_selected = False
|
||||
view._clear_textures = lambda: None
|
||||
|
||||
monkeypatch.setattr(module, "VisionIpcClient", lambda *_args, **_kwargs: FakeClient())
|
||||
monkeypatch.setattr(module.ui_state, "is_onroad", lambda: True)
|
||||
|
||||
view._offroad_transition()
|
||||
|
||||
assert view._onroad_reentry_pending
|
||||
assert not view._reentry_stream_selected
|
||||
|
||||
@@ -205,6 +205,9 @@ class AugmentedRoadView(CameraView):
|
||||
self._reverse_driver_camera_active = False
|
||||
return
|
||||
|
||||
if getattr(self, "_onroad_reentry_pending", False):
|
||||
self._refresh_available_streams()
|
||||
|
||||
if self._update_reverse_driver_camera_state():
|
||||
target = DRIVER_CAM
|
||||
elif camera_view == CAMERA_VIEW_DRIVER:
|
||||
@@ -225,7 +228,8 @@ class AugmentedRoadView(CameraView):
|
||||
else:
|
||||
target = ROAD_CAM
|
||||
|
||||
if self.stream_type != target or (self._switching and self._target_stream_type != target):
|
||||
if (getattr(self, "_onroad_reentry_pending", False) or
|
||||
self.stream_type != target or (self._switching and self._target_stream_type != target)):
|
||||
self.switch_stream(target)
|
||||
|
||||
def _update_calibration(self):
|
||||
|
||||
@@ -130,6 +130,8 @@ class CameraView(Widget):
|
||||
|
||||
self._placeholder_color: rl.Color | None = None
|
||||
self._closed = False
|
||||
self._onroad_reentry_pending = False
|
||||
self._reentry_stream_selected = False
|
||||
|
||||
if self._use_egl and not self._create_egl_texture():
|
||||
cloudlog.error("CameraView EGL texture creation failed, falling back to texture rendering")
|
||||
@@ -164,12 +166,23 @@ class CameraView(Widget):
|
||||
self._switching = False
|
||||
self._texture_needs_update = True
|
||||
self.last_connection_attempt = 0.0
|
||||
self._onroad_reentry_pending = ui_state.is_onroad()
|
||||
self._reentry_stream_selected = False
|
||||
|
||||
def _set_placeholder_color(self, color: rl.Color):
|
||||
"""Set a placeholder color to be drawn when no frame is available."""
|
||||
self._placeholder_color = color
|
||||
|
||||
def _refresh_available_streams(self) -> None:
|
||||
streams = VisionIpcClient.available_streams(self._name, block=False)
|
||||
if streams:
|
||||
self.available_streams = list(streams)
|
||||
|
||||
def switch_stream(self, stream_type: VisionStreamType) -> None:
|
||||
if getattr(self, "_onroad_reentry_pending", False):
|
||||
self._select_reentry_stream(stream_type)
|
||||
return
|
||||
|
||||
if self._switching:
|
||||
if self._target_stream_type == stream_type:
|
||||
return
|
||||
@@ -194,6 +207,23 @@ class CameraView(Widget):
|
||||
self._target_stream_type = None
|
||||
self._switching = False
|
||||
|
||||
def _select_reentry_stream(self, stream_type: VisionStreamType) -> None:
|
||||
"""Select the desired stream before displaying any post-transition frame."""
|
||||
self._cancel_pending_switch()
|
||||
|
||||
if self._stream_type != stream_type:
|
||||
old_client = self.client
|
||||
self.client = None
|
||||
del old_client
|
||||
self.client = VisionIpcClient(self._name, stream_type, conflate=True)
|
||||
self._stream_type = stream_type
|
||||
|
||||
self.frame = None
|
||||
self._last_frame_id = -1
|
||||
self._regressive_frame_count = 0
|
||||
self._texture_needs_update = True
|
||||
self._reentry_stream_selected = True
|
||||
|
||||
@property
|
||||
def stream_type(self) -> VisionStreamType:
|
||||
return self._stream_type
|
||||
@@ -219,6 +249,8 @@ class CameraView(Widget):
|
||||
self.available_streams.clear()
|
||||
self.client = None
|
||||
self._target_client = None
|
||||
self._onroad_reentry_pending = False
|
||||
self._reentry_stream_selected = False
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
@@ -245,6 +277,10 @@ class CameraView(Widget):
|
||||
if self._switching:
|
||||
self._handle_switch()
|
||||
|
||||
if self._onroad_reentry_pending and not self._reentry_stream_selected:
|
||||
# Standalone CameraView users have no higher-level stream selector.
|
||||
self._select_reentry_stream(self._stream_type)
|
||||
|
||||
if not self._ensure_connection():
|
||||
self._draw_placeholder(rect)
|
||||
return
|
||||
@@ -333,6 +369,8 @@ class CameraView(Widget):
|
||||
self._last_frame_id = content_frame_id
|
||||
self._regressive_frame_count = 0
|
||||
self._texture_needs_update = True
|
||||
self._onroad_reentry_pending = False
|
||||
self._reentry_stream_selected = False
|
||||
return True
|
||||
|
||||
def _render_egl(self, src_rect: rl.Rectangle, dst_rect: rl.Rectangle) -> bool:
|
||||
@@ -475,6 +513,9 @@ class CameraView(Widget):
|
||||
|
||||
# Initialize textures for new stream
|
||||
self._initialize_textures()
|
||||
available_streams = getattr(self.client, "available_streams", None)
|
||||
if available_streams is not None:
|
||||
self.available_streams = available_streams(self._name, block=False)
|
||||
|
||||
def _initialize_textures(self):
|
||||
self._clear_textures()
|
||||
|
||||
@@ -15,6 +15,7 @@ class FakeFrame:
|
||||
def _camera_view():
|
||||
view = big_cameraview.CameraView.__new__(big_cameraview.CameraView)
|
||||
view._name = "camerad"
|
||||
view._stream_type = big_cameraview.VisionStreamType.VISION_STREAM_ROAD
|
||||
view.frame = None
|
||||
view._last_frame_id = -1
|
||||
view._regressive_frame_count = 0
|
||||
@@ -41,6 +42,67 @@ def test_pending_switch_is_cancelled_when_requested_stream_is_current():
|
||||
assert not view._switching
|
||||
|
||||
|
||||
def test_onroad_reentry_selects_requested_stream_before_rendering(monkeypatch):
|
||||
view = _camera_view()
|
||||
view._name = "camerad"
|
||||
view._stream_type = big_cameraview.VisionStreamType.VISION_STREAM_WIDE_ROAD
|
||||
view.client = object()
|
||||
view._target_client = object()
|
||||
view._target_stream_type = big_cameraview.VisionStreamType.VISION_STREAM_ROAD
|
||||
view._switching = True
|
||||
view._onroad_reentry_pending = True
|
||||
view._reentry_stream_selected = False
|
||||
clients = []
|
||||
|
||||
class FakeClient:
|
||||
def __init__(self, name, stream_type, conflate):
|
||||
self.name = name
|
||||
self.stream_type = stream_type
|
||||
self.conflate = conflate
|
||||
clients.append(self)
|
||||
|
||||
monkeypatch.setattr(big_cameraview, "VisionIpcClient", FakeClient)
|
||||
view.switch_stream(big_cameraview.VisionStreamType.VISION_STREAM_ROAD)
|
||||
|
||||
assert len(clients) == 1
|
||||
assert view.client is clients[0]
|
||||
assert view.client.stream_type == big_cameraview.VisionStreamType.VISION_STREAM_ROAD
|
||||
assert view.stream_type == big_cameraview.VisionStreamType.VISION_STREAM_ROAD
|
||||
assert view._target_client is None
|
||||
assert view._target_stream_type is None
|
||||
assert not view._switching
|
||||
assert view._reentry_stream_selected
|
||||
|
||||
|
||||
def test_onroad_reentry_guard_clears_on_first_fresh_frame():
|
||||
view = _camera_view()
|
||||
view._onroad_reentry_pending = True
|
||||
view._reentry_stream_selected = True
|
||||
|
||||
assert view._accept_frame(FakeFrame(frame_id=1, idx=0), packet_frame_id=1)
|
||||
assert not view._onroad_reentry_pending
|
||||
assert not view._reentry_stream_selected
|
||||
|
||||
|
||||
def test_standalone_camera_reentry_selects_configured_stream():
|
||||
view = _camera_view()
|
||||
view._switching = False
|
||||
view._onroad_reentry_pending = True
|
||||
view._reentry_stream_selected = False
|
||||
selected = []
|
||||
placeholders = []
|
||||
view._select_reentry_stream = lambda stream_type: (
|
||||
selected.append(stream_type), setattr(view, "_reentry_stream_selected", True)
|
||||
)
|
||||
view._draw_placeholder = lambda rect: placeholders.append(rect)
|
||||
view._ensure_connection = lambda: False
|
||||
|
||||
view._render(object())
|
||||
|
||||
assert selected == [view._stream_type]
|
||||
assert len(placeholders) == 1
|
||||
|
||||
|
||||
def test_reused_egl_slot_cannot_move_camera_backwards(monkeypatch):
|
||||
monkeypatch.setattr(big_cameraview.cloudlog, "warning", lambda *_args, **_kwargs: None)
|
||||
view = _camera_view()
|
||||
|
||||
Reference in New Issue
Block a user