Files
NODEDC_MISSION_CORE/apps/control-station/test/boardObservation.test.mjs
T
DCCONSTRUCTIONS be58d589e2 feat(fleet): add board observation center with live sources and maps
Add adaptive per-vehicle layouts, full-panel dragging, source controls and automatic preview recovery for RealSense, Insta360 X4 and XGRIDS K1 observation views. Integrate cached Cesium map layers through the private NAS gateway link, retain bounded sensor command receipts, and document validation and operational handoff.
2026-09-10 22:12:05 +03:00

44 lines
3.4 KiB
JavaScript

import assert from 'node:assert/strict';
import {after,before,test} from 'node:test';
import {createServer} from 'vite';
let server,layout,source,fanout;
before(async()=>{
server=await createServer({appType:'custom',logLevel:'silent',server:{middlewareMode:true}});
layout=await server.ssrLoadModule('/src/core/fleet/observationLayout.ts');
source=await server.ssrLoadModule('../../packages/sensor-ui/src/observation.ts');
({observationRerun:fanout}=await server.ssrLoadModule('@xgrids-k1/frontend/sensors/observationRerun.ts'));
});
after(async()=>{await server?.close();});
test('layout keeps independent identities and restores absent devices without confusing equal model names',()=>{
const a=source.observationKey('camera-1','left'),b=source.observationKey('camera-2','left'),c=source.observationKey('camera-1','right');
assert.notEqual(a,b);assert.notEqual(a,c);
const value={...layout.emptyObservationLayout(),order:[b,a],hidden:[a],layers:{[a]:'depth'}};
assert.deepEqual(layout.orderedObservationIDs(value,[a,c]),[a,c]);
assert.deepEqual(layout.orderedObservationIDs(value,[a,b,c]),[b,a,c]);
assert.deepEqual(layout.moveObservation(value,[a,b,c],c,b).order,[c,b,a]);
assert.deepEqual(value.hidden,[a]);
});
test('untrusted browser layout is bounded and validates revisions and split numbers',()=>{
const decoded=layout.decodeObservationLayout({version:1,order:['a','a',null,8],hidden:'all',layers:{a:'depth',b:[]},splits:{a:Infinity,b:-8,c:98},arrangement:'bad'});
assert.deepEqual(decoded.order,['a']);assert.deepEqual(decoded.hidden,[]);assert.deepEqual(decoded.splits,{b:20,c:80});assert.deepEqual(decoded.layers,{a:'depth'});assert.equal(decoded.arrangement,'auto');
assert.deepEqual(layout.decodeObservationLayout({version:2}),layout.emptyObservationLayout());
});
test('camera observation exposes only advertised layers with one viewport and RGB default',()=>{
const views=source.cameraObservationViews({name:'RealSense',layers:['depth','private-channel','color','points']});
assert.equal(views.length,1);assert.deepEqual(views[0].layers.map(v=>v.value),['color','depth','points']);
assert.deepEqual(source.cameraObservationViews({name:'unknown',layers:[]}),[]);
});
test('two Rerun views share bytes, apply independent blueprints once and dispose both hosts',async()=>{
const fetchBefore=globalThis.fetch;globalThis.fetch=async url=>new Response(Uint8Array.of(String(url).includes('plan')?2:1));
const sent=[[],[]],closed=[],started=[];let count=0;
const factory=mount=>{const index=count++;return {ready:Promise.resolve({mount,viewer:{start:async()=>started.push(index),open_channel:name=>({ready:true,send_rrd:bytes=>sent[index].push([name,[...bytes]]),close:()=>{}}),get_active_recording_id:()=> 'recording-1',override_panel_state:()=>{},set_active_timeline:()=>{}}}),dispose:()=>closed.push(index)};};
try{
const host=fanout(factory,{current:{id:'top'}})({id:'3d'});const {viewer,mount}=await host.ready;
await viewer.start(null,mount,{});const channel=viewer.open_channel('rrd');channel.send_rrd(Uint8Array.of(9,8));
assert.deepEqual(sent[0],[['rrd',[9,8]]]);assert.deepEqual(sent[1],[['rrd',[9,8]]]);
viewer.get_active_recording_id();viewer.get_active_recording_id();
assert.deepEqual(sent[0].at(-1),['observation-perspective',[1]]);assert.deepEqual(sent[1].at(-1),['observation-plan',[2]]);
assert.equal(sent[0].length,2);assert.deepEqual(started,[0,1]);host.dispose();assert.deepEqual(closed,[0,1]);
}finally{globalThis.fetch=fetchBefore;}
});