feat(simulation): drive stock rover through PX4 offboard
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user