feat(simulation): drive stock rover through PX4 offboard

This commit is contained in:
DCCONSTRUCTIONS
2026-07-24 21:47:40 +03:00
parent 53fd1e3bfc
commit 101707d54c
12 changed files with 1329 additions and 15 deletions
+57 -2
View File
@@ -18,7 +18,11 @@ from k1link.simulation import (
RunKind,
RunState,
)
from k1link.web.polygon_api import StartStockRoverRequest, build_polygon_router
from k1link.web.polygon_api import (
AckermannCommandRequest,
StartStockRoverRequest,
build_polygon_router,
)
SHA_A = "a" * 64
SHA_B = "b" * 64
@@ -241,6 +245,35 @@ class _FakeWorkerGateway:
self.active_run_id = None
return self._status()
def command(
self,
*,
run_id: str,
speed_mps: float,
steering_normalized: float,
idempotency_key: str,
) -> dict[str, Any]:
self.calls.append(("command", idempotency_key))
assert run_id == self.active_run_id
return {
"schema_version": "missioncore.command-acceptance/v1",
"run_id": run_id,
"command_id": "cmd-test",
"sequence": 1,
"issued_at_sim_ns": 1_000_000,
"valid_until_sim_ns": 251_000_000,
"speed_mps": speed_mps,
"steering_normalized": steering_normalized,
"authority_scope": "virtual-only",
"delivery": {
"provider": "px4-ros2-offboard",
"mode": "speed-steering",
"armed": True,
"offboard": True,
"ttl_expired_count": 0,
},
}
def _status(self) -> dict[str, Any]:
return {
"schema_version": "missioncore.simulation-worker-status/v1",
@@ -297,12 +330,34 @@ def test_polygon_worker_api_fails_closed_then_proxies_virtual_only_lifecycle() -
live = _endpoint(enabled, "/api/v1/polygon/worker/live", "GET")()
assert live["run_id"] == running["active_run_id"]
assert live["source"]["quality"] == "diagnostic"
command = _endpoint(
enabled,
"/api/v1/polygon/worker/runs/{run_id}/commands",
"POST",
)(
run_id=running["active_run_id"],
request=AckermannCommandRequest(speed_mps=1.0, steering_normalized=-0.25),
idempotency_key="command-001",
)
assert command["authority_scope"] == "virtual-only"
assert command["delivery"]["offboard"] is True
stopped = _endpoint(enabled, "/api/v1/polygon/worker/runs/{run_id}/stop", "POST")(
run_id=running["active_run_id"],
idempotency_key="stop-001",
)
assert stopped["active_run_id"] is None
assert worker.calls == [("start", "start-001"), ("stop", "stop-001")]
assert worker.calls == [
("start", "start-001"),
("command", "command-001"),
("stop", "stop-001"),
]
def test_polygon_worker_command_rejects_values_outside_lab_envelope() -> None:
with pytest.raises(ValueError):
AckermannCommandRequest(speed_mps=1.51, steering_normalized=0)
with pytest.raises(ValueError):
AckermannCommandRequest(speed_mps=0, steering_normalized=-1.01)
def test_polygon_worker_status_is_explicitly_unavailable_when_not_registered() -> None: