198 lines
6.4 KiB
JavaScript
198 lines
6.4 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { after, before, test } from "node:test";
|
|
|
|
import { createServer } from "vite";
|
|
|
|
let server;
|
|
let xgridsK1Manifest;
|
|
let xgridsK1ObservationSources;
|
|
|
|
before(async () => {
|
|
server = await createServer({
|
|
appType: "custom",
|
|
logLevel: "silent",
|
|
server: { middlewareMode: true },
|
|
});
|
|
({ xgridsK1Manifest } = await server.ssrLoadModule(
|
|
"/src/device-plugins/xgrids-k1/manifest.ts",
|
|
));
|
|
({ xgridsK1ObservationSources } = await server.ssrLoadModule(
|
|
"/src/device-plugins/xgrids-k1/observationSources.ts",
|
|
));
|
|
});
|
|
|
|
after(async () => {
|
|
await server?.close();
|
|
});
|
|
|
|
function model() {
|
|
const activeModel = xgridsK1Manifest.spec.models[0];
|
|
assert.ok(activeModel, "XGRIDS plugin must declare at least one model");
|
|
return activeModel;
|
|
}
|
|
|
|
function declaredState() {
|
|
const activeModel = model();
|
|
return {
|
|
phase: "connected",
|
|
source_mode: "idle",
|
|
k1_ip: "192.168.7.10",
|
|
foxglove_ws_url: "ws://192.168.7.10:8765",
|
|
foxglove_viewer_url: "http://192.168.7.10:8765/vendor-viewer",
|
|
compatibility: {
|
|
profile_id: "xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1",
|
|
camera_preview: "rtsp://192.168.7.10:8554/vendor-preview",
|
|
},
|
|
device_ref: {
|
|
device_id: "device-k1-001",
|
|
model_id: activeModel.id,
|
|
identity_stability: "stable",
|
|
identity_basis: "hardware-identifier",
|
|
},
|
|
device_session: {
|
|
device_session_id: "device-session-001",
|
|
device_id: "device-k1-001",
|
|
compatibility_profile_id: "xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1",
|
|
connectivity: "connected",
|
|
},
|
|
sensor_catalog: {
|
|
schema_version: "missioncore.sensor-catalog/v1alpha2",
|
|
revision: "test-profile",
|
|
streams: [
|
|
{ stream_id: "spatial.point-cloud.live", modality: "point-cloud", availability: "observed" },
|
|
{ stream_id: "camera.preview.live", modality: "encoded-video", availability: "observed" },
|
|
],
|
|
},
|
|
};
|
|
}
|
|
|
|
function streamingState() {
|
|
return {
|
|
...declaredState(),
|
|
phase: "streaming",
|
|
source_mode: "live",
|
|
rerun_grpc_url: "rerun+http://127.0.0.1:9877/proxy",
|
|
acquisition: {
|
|
acquisition_id: "acquisition-001",
|
|
device_id: "device-k1-001",
|
|
device_session_id: "device-session-001",
|
|
compatibility_profile_id: "xgrids.lixelkity-k1.fw-3.0.2.direct-lan.v1",
|
|
control_mode: "operator-manual",
|
|
requested_streams: ["spatial.point-cloud.live"],
|
|
target_host: "127.0.0.1",
|
|
duration_seconds: 0,
|
|
evidence_policy: "required",
|
|
state: "acquiring",
|
|
state_revision: 3,
|
|
},
|
|
};
|
|
}
|
|
|
|
function collectUrlLikeStrings(value, found = []) {
|
|
if (typeof value === "string") {
|
|
if (value.includes("://")) found.push(value);
|
|
return found;
|
|
}
|
|
if (Array.isArray(value)) {
|
|
for (const item of value) collectUrlLikeStrings(item, found);
|
|
return found;
|
|
}
|
|
if (value && typeof value === "object") {
|
|
for (const item of Object.values(value)) collectUrlLikeStrings(item, found);
|
|
}
|
|
return found;
|
|
}
|
|
|
|
test("K1 maps to one primary point cloud and two auxiliary camera channels", () => {
|
|
const sources = xgridsK1ObservationSources(declaredState(), model());
|
|
|
|
assert.equal(sources.length, 3);
|
|
assert.deepEqual(
|
|
sources.map(({ sourceId, semanticChannelId, modality, role }) => ({
|
|
sourceId,
|
|
semanticChannelId,
|
|
modality,
|
|
role,
|
|
})),
|
|
[
|
|
{
|
|
sourceId: "sensor.lidar.primary",
|
|
semanticChannelId: "spatial.point-cloud.live",
|
|
modality: "point-cloud",
|
|
role: "primary",
|
|
},
|
|
{
|
|
sourceId: "sensor.camera.left",
|
|
semanticChannelId: "camera.preview.live",
|
|
modality: "video",
|
|
role: "auxiliary",
|
|
},
|
|
{
|
|
sourceId: "sensor.camera.right",
|
|
semanticChannelId: "camera.preview.live",
|
|
modality: "video",
|
|
role: "auxiliary",
|
|
},
|
|
],
|
|
);
|
|
|
|
assert.equal(new Set(sources.map(({ id }) => id)).size, sources.length);
|
|
assert.ok(sources.every(({ provider }) => provider.pluginId && provider.modelId));
|
|
assert.ok(sources.every(({ provider }) => provider.pluginVersion));
|
|
assert.ok(sources.every(({ binding }) => binding.deviceId === "device-k1-001"));
|
|
assert.ok(sources.every(({ capabilities }) => capabilities.timelineMode === "live-only"));
|
|
});
|
|
|
|
test("observation source ids remain stable while runtime availability changes", () => {
|
|
const declared = xgridsK1ObservationSources(declaredState(), model());
|
|
const streaming = xgridsK1ObservationSources(streamingState(), model());
|
|
|
|
assert.deepEqual(
|
|
streaming.map(({ id }) => id),
|
|
declared.map(({ id }) => id),
|
|
);
|
|
assert.equal(declared[0].availability, "available");
|
|
assert.equal(streaming[0].availability, "streaming");
|
|
assert.equal(streaming[0].previewUrl, "rerun+http://127.0.0.1:9877/proxy");
|
|
});
|
|
|
|
test("camera descriptors stay declared and URL-free until a browser delivery adapter exists", () => {
|
|
const sources = xgridsK1ObservationSources(streamingState(), model());
|
|
const cameras = sources.filter(({ modality }) => modality === "video");
|
|
|
|
assert.equal(cameras.length, 2);
|
|
for (const camera of cameras) {
|
|
assert.equal(camera.availability, "declared");
|
|
assert.equal(camera.transport, "rtsp");
|
|
assert.equal(camera.previewUrl, null);
|
|
assert.equal(camera.capabilities.spatialRegistration, "unresolved");
|
|
assert.equal(camera.capabilities.resizable, true);
|
|
assert.doesNotMatch(camera.endpointLabel ?? "", /:\/\//);
|
|
}
|
|
|
|
assert.deepEqual(collectUrlLikeStrings(sources), [
|
|
"rerun+http://127.0.0.1:9877/proxy",
|
|
]);
|
|
const serialized = JSON.stringify(sources);
|
|
assert.doesNotMatch(serialized, /rtsp:\/\//i);
|
|
assert.doesNotMatch(serialized, /192\.168\.7\.10/);
|
|
assert.doesNotMatch(serialized, /vendor-(?:preview|viewer)/);
|
|
});
|
|
|
|
test("unattested camera catalog entries remain unverified", () => {
|
|
const state = declaredState();
|
|
state.compatibility.profile_id = null;
|
|
state.device_session.compatibility_profile_id = null;
|
|
state.sensor_catalog.streams = state.sensor_catalog.streams.map((stream) =>
|
|
stream.stream_id === "camera.preview.live"
|
|
? { ...stream, availability: "unverified", decode_status: "profile-not-attested" }
|
|
: stream);
|
|
|
|
const cameras = xgridsK1ObservationSources(state, model()).filter(
|
|
({ modality }) => modality === "video",
|
|
);
|
|
assert.equal(cameras.length, 2);
|
|
assert.ok(cameras.every(({ availability }) => availability === "unverified"));
|
|
assert.ok(cameras.every(({ previewUrl }) => previewUrl === null));
|
|
});
|