mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-07-17 23:32:07 +08:00
fixes
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import json
|
||||
from types import SimpleNamespace
|
||||
|
||||
from openpilot.starpilot import starpilot_process
|
||||
|
||||
|
||||
class FakeParams:
|
||||
def __init__(self):
|
||||
self.writes = []
|
||||
|
||||
def put(self, key, value):
|
||||
self.writes.append((key, value))
|
||||
|
||||
|
||||
class FakeThreadManager:
|
||||
def run_with_lock(self, *args, **kwargs):
|
||||
return None
|
||||
|
||||
|
||||
class FakeThemeManager:
|
||||
def update_active_theme(self, *args, **kwargs):
|
||||
return None
|
||||
|
||||
|
||||
class FakeModelManager:
|
||||
def randomize_selected_model(self):
|
||||
return None
|
||||
|
||||
|
||||
def test_transition_offroad_skips_invalid_gps_persist():
|
||||
params = FakeParams()
|
||||
planner = SimpleNamespace(gps_position={
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"bearing": 0.0,
|
||||
"speed": 0.0,
|
||||
"hasFix": False,
|
||||
"updatedAtMonotonic": 1.0,
|
||||
"updatedAtSec": 1.0,
|
||||
})
|
||||
toggles = SimpleNamespace(lock_doors_timer=0, random_themes=False)
|
||||
|
||||
starpilot_process.transition_offroad(
|
||||
planner,
|
||||
FakeModelManager(),
|
||||
FakeThemeManager(),
|
||||
FakeThreadManager(),
|
||||
False,
|
||||
None,
|
||||
params,
|
||||
toggles,
|
||||
)
|
||||
|
||||
assert params.writes == []
|
||||
|
||||
|
||||
def test_transition_offroad_persists_valid_gps():
|
||||
params = FakeParams()
|
||||
gps_position = {
|
||||
"latitude": 41.0,
|
||||
"longitude": -87.0,
|
||||
"bearing": 90.0,
|
||||
"speed": 12.0,
|
||||
"hasFix": True,
|
||||
"updatedAtMonotonic": 1.0,
|
||||
"updatedAtSec": 2.0,
|
||||
}
|
||||
planner = SimpleNamespace(gps_position=gps_position)
|
||||
toggles = SimpleNamespace(lock_doors_timer=0, random_themes=False)
|
||||
|
||||
starpilot_process.transition_offroad(
|
||||
planner,
|
||||
FakeModelManager(),
|
||||
FakeThemeManager(),
|
||||
FakeThreadManager(),
|
||||
False,
|
||||
None,
|
||||
params,
|
||||
toggles,
|
||||
)
|
||||
|
||||
assert params.writes == [("LastGPSPosition", json.dumps(gps_position))]
|
||||
@@ -144,7 +144,8 @@ def sync_drive_stats(params, session):
|
||||
print(f"Failed to sync drive stats: {exception}")
|
||||
|
||||
def transition_offroad(starpilot_planner, model_manager, theme_manager, thread_manager, time_validated, sm, params, starpilot_toggles):
|
||||
params.put("LastGPSPosition", json.dumps(starpilot_planner.gps_position))
|
||||
if gps_position_valid(starpilot_planner.gps_position):
|
||||
params.put("LastGPSPosition", json.dumps(starpilot_planner.gps_position))
|
||||
|
||||
if starpilot_toggles.lock_doors_timer != 0:
|
||||
thread_manager.run_with_lock(lock_doors, (starpilot_toggles.lock_doors_timer, sm, params), report=False)
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import json
|
||||
|
||||
from openpilot.common.params import ParamKeyType
|
||||
from openpilot.starpilot.system.the_pond import the_pond
|
||||
|
||||
|
||||
class FakeParamsBackend:
|
||||
def __init__(self, key_types=None, default_values=None, values=None):
|
||||
self.key_types = key_types or {}
|
||||
self.default_values = default_values or {}
|
||||
self.values = values or {}
|
||||
self.writes = []
|
||||
|
||||
def get_key_type(self, key):
|
||||
return self.key_types[key]
|
||||
|
||||
def get_default_value(self, key):
|
||||
return self.default_values.get(key)
|
||||
|
||||
def put(self, key, value):
|
||||
self.writes.append((key, value))
|
||||
self.values[key] = value
|
||||
|
||||
def get(self, key, block=False):
|
||||
return self.values.get(key)
|
||||
|
||||
|
||||
def test_params_compat_accepts_json_strings_for_json_keys():
|
||||
backend = FakeParamsBackend(
|
||||
key_types={"FavoriteDestinations": ParamKeyType.JSON},
|
||||
default_values={"FavoriteDestinations": []},
|
||||
)
|
||||
compat = the_pond.ParamsCompat(backend)
|
||||
|
||||
compat.put("FavoriteDestinations", json.dumps([{"name": "Home"}]))
|
||||
|
||||
assert backend.writes == [("FavoriteDestinations", [{"name": "Home"}])]
|
||||
|
||||
|
||||
def test_navigation_last_position_uses_recent_persisted_fix(monkeypatch):
|
||||
recent_payload = json.dumps({
|
||||
"latitude": 41.0,
|
||||
"longitude": -87.0,
|
||||
"hasFix": True,
|
||||
"updatedAtSec": 10_000.0,
|
||||
})
|
||||
memory_backend = FakeParamsBackend(values={"LastGPSPosition": ""})
|
||||
persisted_backend = FakeParamsBackend(values={"LastGPSPosition": recent_payload})
|
||||
|
||||
monkeypatch.setattr(the_pond, "params_memory", the_pond.ParamsCompat(memory_backend))
|
||||
monkeypatch.setattr(the_pond, "params", the_pond.ParamsCompat(persisted_backend))
|
||||
monkeypatch.setattr(the_pond.time, "time", lambda: 10_300.0)
|
||||
monkeypatch.setattr(the_pond, "system_time_valid", lambda: True)
|
||||
|
||||
position = the_pond._get_navigation_last_position()
|
||||
|
||||
assert position["latitude"] == 41.0
|
||||
assert position["longitude"] == -87.0
|
||||
|
||||
|
||||
def test_navigation_last_position_rejects_stale_persisted_fix(monkeypatch):
|
||||
stale_payload = json.dumps({
|
||||
"latitude": 41.0,
|
||||
"longitude": -87.0,
|
||||
"hasFix": True,
|
||||
"updatedAtSec": 10_000.0,
|
||||
})
|
||||
memory_backend = FakeParamsBackend(values={"LastGPSPosition": ""})
|
||||
persisted_backend = FakeParamsBackend(values={"LastGPSPosition": stale_payload})
|
||||
|
||||
monkeypatch.setattr(the_pond, "params_memory", the_pond.ParamsCompat(memory_backend))
|
||||
monkeypatch.setattr(the_pond, "params", the_pond.ParamsCompat(persisted_backend))
|
||||
monkeypatch.setattr(the_pond.time, "time", lambda: 10_000.0 + the_pond.NAVIGATION_PERSISTED_LOCATION_MAX_AGE_SECONDS + 1.0)
|
||||
monkeypatch.setattr(the_pond, "system_time_valid", lambda: True)
|
||||
|
||||
assert the_pond._get_navigation_last_position() is None
|
||||
@@ -313,6 +313,20 @@ class ParamsCompat:
|
||||
typed_value = ""
|
||||
else:
|
||||
typed_value = str(value)
|
||||
elif expected_type == ParamKeyType.JSON:
|
||||
if isinstance(value, bytes):
|
||||
value = value.decode("utf-8", errors="replace")
|
||||
|
||||
if isinstance(value, str):
|
||||
stripped = value.strip()
|
||||
if not stripped:
|
||||
typed_value = self._params.get_default_value(key)
|
||||
else:
|
||||
typed_value = json.loads(stripped)
|
||||
elif isinstance(value, tuple):
|
||||
typed_value = list(value)
|
||||
else:
|
||||
typed_value = value
|
||||
|
||||
self._params.put(key, typed_value)
|
||||
|
||||
@@ -466,7 +480,8 @@ KEYS = {
|
||||
}
|
||||
|
||||
NAVIGATION_MEMORY_LOCATION_STALE_SECONDS = 10.0
|
||||
NAVIGATION_PERSISTED_LOCATION_BOOT_SKEW_SECONDS = 5.0
|
||||
NAVIGATION_PERSISTED_LOCATION_FUTURE_SKEW_SECONDS = 60.0
|
||||
NAVIGATION_PERSISTED_LOCATION_MAX_AGE_SECONDS = 24 * 60 * 60
|
||||
|
||||
TMUX_LOGS_PATH = Path("/data/tmux_logs")
|
||||
|
||||
@@ -524,7 +539,7 @@ def _last_gps_position_is_live(payload):
|
||||
return (time.monotonic() - updated_at_monotonic) <= NAVIGATION_MEMORY_LOCATION_STALE_SECONDS
|
||||
|
||||
|
||||
def _last_gps_position_is_current_boot(payload):
|
||||
def _last_gps_position_is_recent(payload):
|
||||
if not isinstance(payload, dict):
|
||||
return False
|
||||
|
||||
@@ -537,11 +552,14 @@ def _last_gps_position_is_current_boot(payload):
|
||||
return False
|
||||
|
||||
now_sec = time.time()
|
||||
boot_started_at_sec = now_sec - time.monotonic()
|
||||
if updated_at_sec > (now_sec + 60.0):
|
||||
if updated_at_sec > (now_sec + NAVIGATION_PERSISTED_LOCATION_FUTURE_SKEW_SECONDS):
|
||||
return False
|
||||
|
||||
return updated_at_sec >= (boot_started_at_sec - NAVIGATION_PERSISTED_LOCATION_BOOT_SKEW_SECONDS)
|
||||
if not system_time_valid():
|
||||
return True
|
||||
|
||||
age_sec = now_sec - updated_at_sec
|
||||
return 0.0 <= age_sec <= NAVIGATION_PERSISTED_LOCATION_MAX_AGE_SECONDS
|
||||
|
||||
|
||||
def _get_navigation_last_position():
|
||||
@@ -550,7 +568,7 @@ def _get_navigation_last_position():
|
||||
return memory_position
|
||||
|
||||
persisted_position = _parse_last_gps_position(params.get("LastGPSPosition", encoding="utf8") or "")
|
||||
if _last_gps_position_is_current_boot(persisted_position):
|
||||
if _last_gps_position_is_recent(persisted_position):
|
||||
return persisted_position
|
||||
|
||||
return None
|
||||
@@ -3640,7 +3658,7 @@ def setup(app):
|
||||
destination,
|
||||
)
|
||||
params.put("NavDestination", json.dumps(destination))
|
||||
params.put("ApiCache_NavDestinations", json.dumps(recent_destinations))
|
||||
params.put("ApiCache_NavDestinations", recent_destinations)
|
||||
return {"message": "Destination set"}
|
||||
|
||||
@app.route("/api/navigation/favorite", methods=["DELETE"])
|
||||
@@ -3662,7 +3680,7 @@ def setup(app):
|
||||
)
|
||||
]
|
||||
|
||||
params.put("FavoriteDestinations", json.dumps(favorites))
|
||||
params.put("FavoriteDestinations", favorites)
|
||||
return jsonify(message="Destination removed from favorites!")
|
||||
|
||||
@app.route("/api/navigation/favorite", methods=["GET"])
|
||||
@@ -3675,7 +3693,7 @@ def setup(app):
|
||||
f["id"] = hashlib.sha1(raw.encode()).hexdigest()
|
||||
changed = True
|
||||
if changed:
|
||||
params.put("FavoriteDestinations", json.dumps(favorites))
|
||||
params.put("FavoriteDestinations", favorites)
|
||||
return jsonify(favorites=favorites)
|
||||
|
||||
@app.route("/api/navigation/favorite", methods=["POST"])
|
||||
@@ -3690,7 +3708,7 @@ def setup(app):
|
||||
if not any(f.get("id") == new_fav["id"] for f in existing):
|
||||
existing.append(new_fav)
|
||||
|
||||
params.put("FavoriteDestinations", json.dumps(existing))
|
||||
params.put("FavoriteDestinations", existing)
|
||||
return {"message": "Destination added to favorites!"}
|
||||
|
||||
@app.route("/api/navigation/favorite/rename", methods=["POST"])
|
||||
@@ -3740,7 +3758,7 @@ def setup(app):
|
||||
if not found:
|
||||
return jsonify({"error": "Favorite not found"}), 404
|
||||
|
||||
params.put("FavoriteDestinations", json.dumps(existing_favorites))
|
||||
params.put("FavoriteDestinations", existing_favorites)
|
||||
return jsonify(message="Favorite updated successfully!")
|
||||
|
||||
@app.route("/api/navigation_key", methods=["DELETE"])
|
||||
|
||||
Reference in New Issue
Block a user