mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-20 07:43:48 +08:00
Offroad Start Navigation Guards
This commit is contained in:
@@ -39,8 +39,8 @@ def _favorite(name, latitude, longitude, **flags):
|
||||
return {"name": name, "latitude": latitude, "longitude": longitude, **flags}
|
||||
|
||||
|
||||
def _card(params):
|
||||
card = HomeInfoCard(params, FakeDriveStats())
|
||||
def _card(params, *, online_provider=lambda: True, gps_provider=lambda: True):
|
||||
card = HomeInfoCard(params, FakeDriveStats(), online_provider=online_provider, gps_provider=gps_provider)
|
||||
card.set_rect(rl.Rectangle(100, 200, 750, 745))
|
||||
card.refresh()
|
||||
return card
|
||||
@@ -127,3 +127,48 @@ def test_refresh_falls_back_without_clearing_an_active_non_favorite_destination(
|
||||
assert not card.quick_start_available
|
||||
assert card.show_records
|
||||
assert card.active_destination["name"] == "Old destination"
|
||||
|
||||
|
||||
def test_quick_start_requires_online_and_gps():
|
||||
params = FakeParams({
|
||||
"MapboxSecretKey": "secret",
|
||||
FAVORITE_DESTINATIONS_KEY: json.dumps([_favorite("Home", 1, 2)]),
|
||||
})
|
||||
|
||||
# Both online and GPS available
|
||||
card = _card(params, online_provider=lambda: True, gps_provider=lambda: True)
|
||||
assert card.quick_start_available
|
||||
assert not card.show_records
|
||||
|
||||
# Offline
|
||||
card_offline = _card(params, online_provider=lambda: False, gps_provider=lambda: True)
|
||||
assert not card_offline.quick_start_available
|
||||
assert card_offline.show_records
|
||||
|
||||
# No GPS fix
|
||||
card_no_gps = _card(params, online_provider=lambda: True, gps_provider=lambda: False)
|
||||
assert not card_no_gps.quick_start_available
|
||||
assert card_no_gps.show_records
|
||||
|
||||
# Fallback GPS check via LastGPSPosition in params
|
||||
params_with_gps = FakeParams({
|
||||
"MapboxSecretKey": "secret",
|
||||
FAVORITE_DESTINATIONS_KEY: json.dumps([_favorite("Home", 1, 2)]),
|
||||
"LastGPSPosition": json.dumps({"latitude": 37.77, "longitude": -122.41, "hasFix": True}),
|
||||
})
|
||||
card_param_gps = HomeInfoCard(params_with_gps, FakeDriveStats(), online_provider=lambda: True)
|
||||
card_param_gps.set_rect(rl.Rectangle(100, 200, 750, 745))
|
||||
card_param_gps.refresh()
|
||||
assert card_param_gps.quick_start_available
|
||||
|
||||
# LastGPSPosition with hasFix=False or Null Island -> unavailable
|
||||
params_bad_gps = FakeParams({
|
||||
"MapboxSecretKey": "secret",
|
||||
FAVORITE_DESTINATIONS_KEY: json.dumps([_favorite("Home", 1, 2)]),
|
||||
"LastGPSPosition": json.dumps({"latitude": 0.0, "longitude": 0.0, "hasFix": False}),
|
||||
})
|
||||
card_bad_gps = HomeInfoCard(params_bad_gps, FakeDriveStats(), online_provider=lambda: True)
|
||||
card_bad_gps.set_rect(rl.Rectangle(100, 200, 750, 745))
|
||||
card_bad_gps.refresh()
|
||||
assert not card_bad_gps.quick_start_available
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from cereal import log
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.selfdrive.ui.widgets.drive_stats import (
|
||||
@@ -33,14 +37,38 @@ TEXT_LEFT_INSET = 30.0
|
||||
TEXT_RIGHT_INSET = 34.0
|
||||
|
||||
|
||||
def _has_valid_gps_payload(raw: Any) -> bool:
|
||||
if not raw:
|
||||
return False
|
||||
try:
|
||||
data = json.loads(raw) if isinstance(raw, (str, bytes)) else raw
|
||||
if isinstance(data, dict):
|
||||
if str(data.get("hasFix")).strip().lower() in ("0", "false", "no", "off", "", "none"):
|
||||
return False
|
||||
lat, lon = float(data.get("latitude", 0.0)), float(data.get("longitude", 0.0))
|
||||
return math.isfinite(lat) and math.isfinite(lon) and (abs(lat) > 1e-6 or abs(lon) > 1e-6)
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
class HomeInfoCard(Widget):
|
||||
"""Paired offroad Home card for Quick Start and Personal Records."""
|
||||
|
||||
def __init__(self, params: Any, drive_stats: Any):
|
||||
def __init__(
|
||||
self,
|
||||
params: Any,
|
||||
drive_stats: Any,
|
||||
*,
|
||||
online_provider: Callable[[], bool] | None = None,
|
||||
gps_provider: Callable[[], bool] | None = None,
|
||||
):
|
||||
super().__init__()
|
||||
self._params = params
|
||||
self._store = NavigationDestinationStore(params)
|
||||
self._drive_stats = drive_stats
|
||||
self._online_provider = online_provider
|
||||
self._gps_provider = gps_provider
|
||||
|
||||
self._show_records = False
|
||||
self._quick_start_available = False
|
||||
@@ -73,6 +101,39 @@ class HomeInfoCard(Widget):
|
||||
self.refresh()
|
||||
super().show_event()
|
||||
|
||||
def _is_online(self) -> bool:
|
||||
if self._online_provider is not None:
|
||||
return bool(self._online_provider())
|
||||
try:
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
if hasattr(ui_state, "sm") and ui_state.sm.valid.get("deviceState", False):
|
||||
return ui_state.sm["deviceState"].networkType != log.DeviceState.NetworkType.none
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
def _has_gps(self) -> bool:
|
||||
if self._gps_provider is not None:
|
||||
return bool(self._gps_provider())
|
||||
|
||||
if _has_valid_gps_payload(self._params.get("LastGPSPosition", encoding="utf-8")):
|
||||
return True
|
||||
|
||||
try:
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
if hasattr(ui_state, "sm") and ui_state.sm.valid.get("gpsLocationExternal", False):
|
||||
gps = ui_state.sm["gpsLocationExternal"]
|
||||
if getattr(gps, "hasFix", False):
|
||||
lat, lon = float(getattr(gps, "latitude", 0.0)), float(getattr(gps, "longitude", 0.0))
|
||||
if math.isfinite(lat) and math.isfinite(lon) and (abs(lat) > 1e-6 or abs(lon) > 1e-6):
|
||||
return True
|
||||
|
||||
if hasattr(ui_state, "params_memory"):
|
||||
return _has_valid_gps_payload(ui_state.params_memory.get("LastGPSPosition", encoding="utf-8"))
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
def refresh(self) -> None:
|
||||
was_available = self._quick_start_available
|
||||
self._active_destination = self._store.active_destination()
|
||||
@@ -80,7 +141,12 @@ class HomeInfoCard(Widget):
|
||||
raw_favorites = self._params.get(FAVORITE_DESTINATIONS_KEY, encoding="utf-8", default="[]")
|
||||
favorites = load_favorite_destinations(raw_favorites)
|
||||
self._favorites = ordered_favorite_destinations(favorites, limit=ROW_COUNT)
|
||||
self._quick_start_available = routing_configured(self._params) and bool(self._favorites)
|
||||
self._quick_start_available = (
|
||||
routing_configured(self._params)
|
||||
and bool(self._favorites)
|
||||
and self._is_online()
|
||||
and self._has_gps()
|
||||
)
|
||||
|
||||
if not self._quick_start_available:
|
||||
self._show_records = True
|
||||
|
||||
Reference in New Issue
Block a user