mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-20 07:43:48 +08:00
BigUI : Nav on device, maybe?
This commit is contained in:
@@ -2,10 +2,9 @@ from __future__ import annotations
|
||||
from collections.abc import Callable
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.lib.application import MousePos, gui_app, FontWeight
|
||||
from openpilot.system.ui.lib.application import MousePos
|
||||
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanelType, StarPilotPanelInfo, FrameCachedParams
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.sounds import StarPilotSoundsLayout
|
||||
@@ -13,6 +12,7 @@ from openpilot.selfdrive.ui.layouts.settings.starpilot.driving_model import Star
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.longitudinal import StarPilotLongitudinalLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.lateral import StarPilotLateralLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.maps import StarPilotMapsLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.navigation import StarPilotNavigationLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.system_settings import StarPilotSystemLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.appearance import StarPilotAppearanceLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.vehicle import StarPilotVehicleSettingsLayout
|
||||
@@ -32,9 +32,9 @@ class StarPilotLayout(Widget):
|
||||
"buttons": [("Driving Model", "DRIVING_MODEL", "aicar"), ("Gas / Brake", "LONGITUDINAL", "road"), ("Steering", "LATERAL", "steering")],
|
||||
},
|
||||
{
|
||||
"title": "Map Data",
|
||||
"title": "Navigation & Maps",
|
||||
"icon": "navigate",
|
||||
"panel": "MAPS",
|
||||
"buttons": [("Map Data", "MAPS", "navigate"), ("Navigation", "NAVIGATION", "road")],
|
||||
},
|
||||
{
|
||||
"title": "System",
|
||||
@@ -75,6 +75,7 @@ class StarPilotLayout(Widget):
|
||||
StarPilotPanelType.LONGITUDINAL: StarPilotPanelInfo(tr_noop("Gas / Brake"), StarPilotLongitudinalLayout()),
|
||||
StarPilotPanelType.LATERAL: StarPilotPanelInfo(tr_noop("Steering"), StarPilotLateralLayout()),
|
||||
StarPilotPanelType.MAPS: StarPilotPanelInfo(tr_noop("Map Data"), StarPilotMapsLayout()),
|
||||
StarPilotPanelType.NAVIGATION: StarPilotPanelInfo(tr_noop("Navigation"), StarPilotNavigationLayout()),
|
||||
StarPilotPanelType.VISUALS: StarPilotPanelInfo(tr_noop("Appearance"), StarPilotAppearanceLayout()),
|
||||
StarPilotPanelType.VEHICLE: StarPilotPanelInfo(tr_noop("Vehicle Settings"), StarPilotVehicleSettingsLayout()),
|
||||
}
|
||||
@@ -85,6 +86,7 @@ class StarPilotLayout(Widget):
|
||||
StarPilotPanelType.SYSTEM,
|
||||
StarPilotPanelType.LATERAL,
|
||||
StarPilotPanelType.MAPS,
|
||||
StarPilotPanelType.NAVIGATION,
|
||||
StarPilotPanelType.VISUALS,
|
||||
StarPilotPanelType.VEHICLE,
|
||||
)
|
||||
@@ -177,6 +179,7 @@ class StarPilotLayout(Widget):
|
||||
"LONGITUDINAL": StarPilotPanelType.LONGITUDINAL,
|
||||
"LATERAL": StarPilotPanelType.LATERAL,
|
||||
"MAPS": StarPilotPanelType.MAPS,
|
||||
"NAVIGATION": StarPilotPanelType.NAVIGATION,
|
||||
"VISUALS": StarPilotPanelType.VISUALS,
|
||||
"VEHICLE": StarPilotPanelType.VEHICLE,
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -71,6 +71,7 @@ class StarPilotPanelType(IntEnum):
|
||||
VISUALS = 8
|
||||
VEHICLE = 10
|
||||
SYSTEM = 12
|
||||
NAVIGATION = 13
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.navigation import (
|
||||
MapboxSearchClient,
|
||||
_NavigationParams,
|
||||
_add_favorite_destination,
|
||||
_favorite_destination_id,
|
||||
_favorite_payload_for_galaxy,
|
||||
_load_favorite_destinations,
|
||||
_remove_favorite_destination,
|
||||
_update_favorite_destination,
|
||||
)
|
||||
|
||||
|
||||
def test_favorites_get_stable_ids_and_deduplicate_by_id():
|
||||
favorite = {
|
||||
"name": "Home",
|
||||
"latitude": "41.881832",
|
||||
"longitude": "-87.623177",
|
||||
"routeId": "main",
|
||||
}
|
||||
expected_id = _favorite_destination_id(favorite)
|
||||
|
||||
added = _add_favorite_destination("[]", favorite)
|
||||
duplicate = _add_favorite_destination(json.dumps(added), {**favorite, "id": expected_id})
|
||||
|
||||
assert added == [{**favorite, "latitude": 41.881832, "longitude": -87.623177, "id": expected_id}]
|
||||
assert duplicate == added
|
||||
assert _load_favorite_destinations(json.dumps(added)) == added
|
||||
|
||||
|
||||
def test_favorite_id_preserves_galaxy_hash_input_for_numeric_coordinates():
|
||||
favorite = {"name": "One", "latitude": 1.0, "longitude": 2.0}
|
||||
expected = hashlib.sha1(b"2.0,1.0||One").hexdigest()
|
||||
|
||||
assert _favorite_destination_id(favorite) == expected
|
||||
|
||||
|
||||
def test_new_favorites_use_galaxys_default_route_id_contract():
|
||||
destination = {"name": "Home", "latitude": 1.0, "longitude": 2.0}
|
||||
|
||||
assert _favorite_payload_for_galaxy(destination)["routeId"] == "main"
|
||||
assert _favorite_payload_for_galaxy({**destination, "routeId": "alternate"})["routeId"] == "alternate"
|
||||
|
||||
|
||||
def test_favorite_special_locations_are_mutually_exclusive():
|
||||
raw = json.dumps([
|
||||
{"id": "home", "name": "Home", "latitude": 1, "longitude": 2, "is_home": True},
|
||||
{"id": "work", "name": "Work", "latitude": 3, "longitude": 4},
|
||||
])
|
||||
|
||||
updated = _update_favorite_destination(raw, {"id": "work"}, is_work=True)
|
||||
|
||||
assert updated is not None
|
||||
assert updated[0].get("is_home") is True
|
||||
assert updated[1].get("is_work") is True
|
||||
assert updated[1].get("is_home") is None
|
||||
|
||||
|
||||
def test_favorite_rename_can_target_legacy_route_id_without_coordinates():
|
||||
raw = json.dumps([{
|
||||
"name": "Old name",
|
||||
"routeId": "route-1",
|
||||
"latitude": 1,
|
||||
"longitude": 2,
|
||||
}])
|
||||
|
||||
updated = _update_favorite_destination(raw, {"routeId": "route-1"}, name="New name")
|
||||
|
||||
assert updated is not None
|
||||
assert updated[0]["name"] == "New name"
|
||||
|
||||
|
||||
def test_favorite_can_be_removed_by_legacy_payload_identity():
|
||||
raw = json.dumps([
|
||||
{"name": "Keep", "latitude": 1, "longitude": 2},
|
||||
{"name": "Remove", "latitude": 3, "longitude": 4},
|
||||
])
|
||||
|
||||
updated = _remove_favorite_destination(raw, {"name": "Remove", "latitude": 3, "longitude": 4})
|
||||
|
||||
assert [favorite["name"] for favorite in updated] == ["Keep"]
|
||||
|
||||
|
||||
def test_navigation_params_commits_destination_and_clears_runtime_state():
|
||||
class FakeParams:
|
||||
def __init__(self):
|
||||
self.values = {}
|
||||
self.removed = []
|
||||
|
||||
def get(self, key, encoding=None, default=None):
|
||||
value = self.values.get(key, default)
|
||||
if encoding == "utf-8" and isinstance(value, bytes):
|
||||
return value.decode("utf-8")
|
||||
return value
|
||||
|
||||
def put(self, key, value):
|
||||
self.values[key] = value
|
||||
|
||||
def remove(self, key):
|
||||
self.removed.append(key)
|
||||
self.values.pop(key, None)
|
||||
|
||||
params = FakeParams()
|
||||
memory = FakeParams()
|
||||
navigation = _NavigationParams(params, memory)
|
||||
|
||||
destination = navigation.set_destination({"name": "Home", "latitude": 1, "longitude": 2})
|
||||
assert navigation.clear_navigation() is True
|
||||
|
||||
assert destination["place_name"] == "Home"
|
||||
assert params.values["ApiCache_NavDestinations"][0]["place_name"] == "Home"
|
||||
assert "NavDestination" in params.removed
|
||||
assert memory.removed == ["NavInstructionState", "NavInstructionCollapsed"]
|
||||
|
||||
|
||||
class FakeResponse:
|
||||
def __init__(self, payload, status_code=200):
|
||||
self._payload = payload
|
||||
self.status_code = status_code
|
||||
|
||||
def raise_for_status(self):
|
||||
if self.status_code >= 400:
|
||||
raise RuntimeError(f"HTTP {self.status_code}")
|
||||
|
||||
def json(self):
|
||||
return self._payload
|
||||
|
||||
|
||||
class FakeSession:
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
def get(self, url, *, params, timeout):
|
||||
self.calls.append((url, params, timeout))
|
||||
if url.endswith("/suggest"):
|
||||
return FakeResponse({
|
||||
"suggestions": [{
|
||||
"name": "OpenAI",
|
||||
"full_address": "OpenAI, San Francisco, CA",
|
||||
"mapbox_id": "place.openai",
|
||||
}],
|
||||
})
|
||||
return FakeResponse({
|
||||
"features": [{
|
||||
"properties": {"name": "OpenAI", "full_address": "OpenAI, San Francisco, CA"},
|
||||
"geometry": {"coordinates": [-122.401, 37.789]},
|
||||
}],
|
||||
})
|
||||
|
||||
|
||||
def test_mapbox_search_and_retrieve_use_public_token_only():
|
||||
session = FakeSession()
|
||||
client = MapboxSearchClient(session=session)
|
||||
|
||||
results = client.search("openai", "public-token", "session-token", proximity=(-122.4, 37.8), language="en")
|
||||
resolved = client.resolve(results[0], "public-token", "session-token")
|
||||
|
||||
assert results[0].name == "OpenAI"
|
||||
assert results[0].latitude is None
|
||||
assert resolved.to_destination() == {
|
||||
"name": "OpenAI",
|
||||
"place_name": "OpenAI",
|
||||
"latitude": 37.789,
|
||||
"longitude": -122.401,
|
||||
}
|
||||
assert session.calls[0][1]["access_token"] == "public-token"
|
||||
assert "secret" not in session.calls[0][1]
|
||||
@@ -0,0 +1,46 @@
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.main_panel import StarPilotLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.navigation import StarPilotNavigationLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanelType
|
||||
|
||||
|
||||
def test_navigation_and_maps_is_the_parent_folder_for_map_data_and_navigation():
|
||||
category = next(item for item in StarPilotLayout.CATEGORIES if item["title"] == "Navigation & Maps")
|
||||
|
||||
assert "panel" not in category
|
||||
assert category["buttons"] == [
|
||||
("Map Data", "MAPS", "navigate"),
|
||||
("Navigation", "NAVIGATION", "road"),
|
||||
]
|
||||
assert all(item["title"] != "Map Data" for item in StarPilotLayout.CATEGORIES)
|
||||
assert StarPilotPanelType.NAVIGATION.value == 13
|
||||
|
||||
|
||||
def test_navigation_start_is_the_summary_action_not_a_duplicate_rail_target():
|
||||
layout = object.__new__(StarPilotNavigationLayout)
|
||||
layout._draft_destination = {
|
||||
"name": "Home",
|
||||
"place_name": "Home",
|
||||
"latitude": 1.0,
|
||||
"longitude": 2.0,
|
||||
}
|
||||
layout._selected_favorite = None
|
||||
layout._favorites = []
|
||||
|
||||
action_ids = [action[0] for action in layout._action_definitions()]
|
||||
|
||||
assert action_ids == ["action:favorite", "action:home", "action:work"]
|
||||
|
||||
|
||||
def test_rejected_search_invalidates_an_in_flight_request_generation():
|
||||
layout = object.__new__(StarPilotNavigationLayout)
|
||||
layout._search_generation = 3
|
||||
layout._query = "previous"
|
||||
layout._search_results = []
|
||||
layout._search_error = ""
|
||||
layout._draft_destination = None
|
||||
layout._selected_favorite = None
|
||||
|
||||
layout._start_search("ab")
|
||||
|
||||
assert layout._search_generation == 4
|
||||
assert layout._search_error
|
||||
Reference in New Issue
Block a user