feat(simulation): drive stock rover through PX4 offboard
This commit is contained in:
@@ -12,8 +12,10 @@ let resolvePolygonRunRoute;
|
||||
let PolygonRunContractError;
|
||||
let decodePolygonWorkerStatus;
|
||||
let decodePolygonVehicleState;
|
||||
let decodePolygonCommandAcceptance;
|
||||
let fetchPolygonWorkerStatus;
|
||||
let fetchPolygonVehicleState;
|
||||
let sendPolygonRoverCommand;
|
||||
let startPolygonWorker;
|
||||
let stopPolygonWorker;
|
||||
let PolygonWorkerContractError;
|
||||
@@ -38,8 +40,10 @@ before(async () => {
|
||||
({
|
||||
decodePolygonWorkerStatus,
|
||||
decodePolygonVehicleState,
|
||||
decodePolygonCommandAcceptance,
|
||||
fetchPolygonWorkerStatus,
|
||||
fetchPolygonVehicleState,
|
||||
sendPolygonRoverCommand,
|
||||
startPolygonWorker,
|
||||
stopPolygonWorker,
|
||||
PolygonWorkerContractError,
|
||||
@@ -223,6 +227,28 @@ function vehicleState(overrides = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
function commandAcceptance(overrides = {}) {
|
||||
return {
|
||||
schema_version: "missioncore.command-acceptance/v1",
|
||||
run_id: "s1c-6cb1495-20260724t180000z-aabbcc",
|
||||
command_id: "cmd-aabbcc",
|
||||
sequence: 1,
|
||||
issued_at_sim_ns: 7_000_000_000,
|
||||
valid_until_sim_ns: 7_250_000_000,
|
||||
speed_mps: 1,
|
||||
steering_normalized: -0.55,
|
||||
authority_scope: "virtual-only",
|
||||
delivery: {
|
||||
provider: "px4-ros2-offboard",
|
||||
mode: "speed-steering",
|
||||
armed: true,
|
||||
offboard: true,
|
||||
ttl_expired_count: 0,
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test("Polygon is a worker-gated product root and keeps a compatible direct route", () => {
|
||||
assert.deepEqual(resolvePolygonRunRoute("?workspace=polygon-run"), {
|
||||
active: true,
|
||||
@@ -321,11 +347,14 @@ test("Polygon fetchers use bounded same-origin GET endpoints", async () => {
|
||||
test("Polygon Live decodes only D-only virtual diagnostic state", () => {
|
||||
const status = decodePolygonWorkerStatus(workerStatus());
|
||||
const live = decodePolygonVehicleState(vehicleState());
|
||||
const command = decodePolygonCommandAcceptance(commandAcceptance());
|
||||
|
||||
assert.equal(status.available, true);
|
||||
assert.equal(status.activeRunId, live.runId);
|
||||
assert.equal(status.isolation.artifactPolicy, "d-only");
|
||||
assert.deepEqual(live.position, { x: 1.25, y: -0.5, z: 0.18 });
|
||||
assert.equal(command.offboard, true);
|
||||
assert.equal(command.steeringNormalized, -0.55);
|
||||
assert.throws(
|
||||
() => decodePolygonWorkerStatus(workerStatus({
|
||||
authority: {
|
||||
@@ -344,6 +373,12 @@ test("Polygon Live decodes only D-only virtual diagnostic state", () => {
|
||||
})),
|
||||
PolygonWorkerContractError,
|
||||
);
|
||||
assert.throws(
|
||||
() => decodePolygonCommandAcceptance(commandAcceptance({
|
||||
authority_scope: "physical",
|
||||
})),
|
||||
PolygonWorkerContractError,
|
||||
);
|
||||
});
|
||||
|
||||
test("Polygon Live uses same-origin gateway and idempotent lifecycle requests", async () => {
|
||||
@@ -356,6 +391,7 @@ test("Polygon Live uses same-origin gateway and idempotent lifecycle requests",
|
||||
const fetcher = async (url, init) => {
|
||||
calls.push({ url: String(url), init });
|
||||
if (String(url).endsWith("/live")) return jsonResponse(vehicleState());
|
||||
if (String(url).endsWith("/commands")) return jsonResponse(commandAcceptance());
|
||||
if (String(url).endsWith("/stop")) return jsonResponse(stopped);
|
||||
return jsonResponse(workerStatus());
|
||||
};
|
||||
@@ -363,6 +399,12 @@ test("Polygon Live uses same-origin gateway and idempotent lifecycle requests",
|
||||
await fetchPolygonWorkerStatus({ fetcher });
|
||||
await fetchPolygonVehicleState({ fetcher });
|
||||
await startPolygonWorker({ idempotencyKey: "start-001", fetcher });
|
||||
await sendPolygonRoverCommand("s1c-6cb1495-20260724t180000z-aabbcc", {
|
||||
speedMps: 1,
|
||||
steeringNormalized: -0.55,
|
||||
idempotencyKey: "command-001",
|
||||
fetcher,
|
||||
});
|
||||
await stopPolygonWorker("s1c-6cb1495-20260724t180000z-aabbcc", {
|
||||
idempotencyKey: "stop-001",
|
||||
fetcher,
|
||||
@@ -372,9 +414,15 @@ test("Polygon Live uses same-origin gateway and idempotent lifecycle requests",
|
||||
"/api/v1/polygon/worker",
|
||||
"/api/v1/polygon/worker/live",
|
||||
"/api/v1/polygon/worker/runs",
|
||||
"/api/v1/polygon/worker/runs/s1c-6cb1495-20260724t180000z-aabbcc/commands",
|
||||
"/api/v1/polygon/worker/runs/s1c-6cb1495-20260724t180000z-aabbcc/stop",
|
||||
]);
|
||||
assert.equal(calls[2].init.headers["Idempotency-Key"], "start-001");
|
||||
assert.equal(calls[3].init.headers["Idempotency-Key"], "stop-001");
|
||||
assert.equal(calls[3].init.headers["Idempotency-Key"], "command-001");
|
||||
assert.equal(calls[4].init.headers["Idempotency-Key"], "stop-001");
|
||||
assert.equal(calls[2].init.body, JSON.stringify({ scenario_id: "stock-rover-ackermann" }));
|
||||
assert.equal(calls[3].init.body, JSON.stringify({
|
||||
speed_mps: 1,
|
||||
steering_normalized: -0.55,
|
||||
}));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user