refactor: construct application layers from supplied sources

This commit is contained in:
Sameh Khamis committed 2026-09-14 14:20:01 -07:00
1 parent cd704d0e9b
commit 8160ac5fe4
53 files changed
+1152 -400

No files matched your search

+16 -20
View File
@@ -8,15 +8,10 @@ import {
getSelectedEntityContext,
isContextRecordActive,
} from '../data/contextStore.js';
import { getNextIssPass } from '../data/satellites.js';
import { CCTV_FOCUS_RESULT } from '../data/cctv.js';
import { CCTV_FOCUS_RESULT } from '../layers/cctv/index.js';
import { contextModeWord } from '../contextModePolicy.js';
import { createAnalystEngine } from '../data/analystEngine.js';
import { layerFeedState } from '../data/manager.js';
import militaryAwarenessLayer, {
collectAircraftProximityWindow,
contactsWindowFromSnapshot,
} from '../data/militaryAwareness.js';
import { initCameraVerbs, moveCamera, flyRoute, interruptCameraMotion, adjustOrbitRange } from '../cameraVerbs.js';
import { cachedGroundFloor, warmGroundFloor } from '../data/groundFloor.js';
import { isPickedWorldPosition } from '../data/scenePick.js';
@@ -612,7 +607,7 @@ export function createGevActionRunner({ viewer, styleManager, dataManager, scene
};
}
const contactsWindow = ['contacts', 'flights'].includes(mode)
? activeContactsWindow()
? activeContactsWindow(dataManager)
: null;
return {
...withContextModeVocabulary(result),
@@ -792,7 +787,7 @@ export function createGevActionRunner({ viewer, styleManager, dataManager, scene
}
if (name === 'next_iss_pass') {
return nextIssPass(viewer, args);
return nextIssPass(viewer, dataManager, args);
}
if (name === 'analyst_query') {
@@ -2007,7 +2002,7 @@ function compassDir(azDeg) {
return COMPASS_16[Math.round(((((azDeg % 360) + 360) % 360)) / 22.5) % 16];
}
function nextIssPass(viewer, args) {
function nextIssPass(viewer, dataManager, args) {
let latDeg = Number.isFinite(args.latitude) ? args.latitude : null;
let lonDeg = Number.isFinite(args.longitude) ? args.longitude : null;
if (latDeg == null || lonDeg == null) {
@@ -2017,7 +2012,7 @@ function nextIssPass(viewer, args) {
lonDeg = Cesium.Math.toDegrees(carto.longitude);
}
const minElevDeg = Number.isFinite(args.minElevationDeg) ? args.minElevationDeg : 10;
const result = getNextIssPass({ latDeg, lonDeg, minElevDeg });
const result = dataManager?.layers?.get('satellites')?.module?.getNextIssPass?.({ latDeg, lonDeg, minElevDeg }) ?? { status: 'no-tle' };
if (result.status === 'no-tle') {
return {
ok: false,
@@ -2337,7 +2332,7 @@ function getCurrentViewState(viewer, styleManager, dataManager, sceneDirector =
...withContextModeVocabulary(styleManager.getContextModeState()),
// The numbers on the operator's Contacts panel, so a window/count
// question can be answered from what they are looking at.
...(activeContactsWindow() ? { contactsWindow: activeContactsWindow() } : {}),
...(activeContactsWindow(dataManager) ? { contactsWindow: activeContactsWindow(dataManager) } : {}),
}
: null,
cockpit: typeof styleManager.getCockpitState === 'function'
@@ -2408,19 +2403,19 @@ async function getEntityContext(viewer, dataManager, styleManager, args = {}, se
* @param {object} result The general engine's result, reused for scope text.
* @returns {object|null} A unified-count payload, or null.
*/
function aircraftProximityWindowForQuery(args, result) {
function aircraftProximityWindowForQuery(dataManager, args, result) {
const scope = args?.scope;
if (String(scope?.kind || '').toLowerCase() !== 'radius') return null;
const layers = Array.isArray(args.layers) ? args.layers : [];
if (!layers.some((layer) => layer === 'flights' || layer === 'military')) return null;
const snapshot = militaryAwarenessLayer.getContextSnapshot?.();
const snapshot = dataManager?.layers?.get('military-awareness')?.module?.getContextSnapshot?.();
const subject = snapshot?.subject;
if (!subject?.position) return null;
// An explicit centre only qualifies when it IS the subject; otherwise the
// operator asked about somewhere else and must get that answer.
if (scope.center && !centerMatchesSubject(scope.center, subject.position)) return null;
const radiusM = Number.isFinite(scope.km) ? scope.km * 1000 : (snapshot.radiusM || 250_000);
const window = collectAircraftProximityWindow(subject.position, { radiusM, subject });
const window = dataManager.layers.get('military-awareness').module.collectAircraftProximityWindow(subject.position, { radiusM, subject });
if (!window) return null;
const label = subject.label || subject.id || 'the selected contact';
const radiusKm = Math.round(radiusM / 1000);
@@ -2459,7 +2454,7 @@ function aircraftProximityWindowForQuery(args, result) {
military: window.military.length,
aircraft: window.aircraft,
},
...(activeContactsWindow() ? { contactsWindow: activeContactsWindow() } : {}),
...(activeContactsWindow(dataManager) ? { contactsWindow: activeContactsWindow(dataManager) } : {}),
};
}
@@ -3235,9 +3230,10 @@ const VIEWPORT_LOADED_LAYERS = new Set(['flights']);
* diverge no matter which surface asks.
* @returns {object|null} Panel-equivalent window counts.
*/
function activeContactsWindow() {
function activeContactsWindow(dataManager) {
try {
return contactsWindowFromSnapshot(militaryAwarenessLayer.getContextSnapshot?.());
const layer = dataManager?.layers?.get('military-awareness')?.module;
return layer?.contactsWindowFromSnapshot?.(layer.getContextSnapshot?.()) ?? null;
} catch {
return null;
}
@@ -3263,7 +3259,7 @@ function analystProviders(viewer, dataManager, { recordLimitByLayer = null, plac
* @returns {{lat: number, lon: number, label: string|null}|null} Subject centre.
*/
getContextSubject() {
const snapshot = militaryAwarenessLayer.getContextSnapshot?.();
const snapshot = dataManager?.layers?.get('military-awareness')?.module?.getContextSnapshot?.();
const subject = snapshot?.subject;
if (!subject?.position) return null;
const carto = Cesium.Cartographic.fromCartesian(subject.position);
@@ -3343,10 +3339,10 @@ async function runAnalystQuery(viewer, dataManager, args = {}, placeSearch = una
// differ. The generic record/scope engine still owns explicit regions and
// arbitrary points — only "how many aircraft around <this contact>" is
// unified, because that is the question the panel is already answering.
const entityWindow = aircraftProximityWindowForQuery(args, result);
const entityWindow = aircraftProximityWindowForQuery(dataManager, args, result);
if (entityWindow) return entityWindow;
const contactsWindow = activeContactsWindow();
const contactsWindow = activeContactsWindow(dataManager);
const aircraftQueried = (result.coverage?.layersQueried || [])
.some((l) => l.layerKey === 'flights' || l.layerKey === 'military');
// Both numbers, and which one answers the question. The window counts have
+33 -15
View File
@@ -2838,7 +2838,7 @@ async function withAwareness(harness, run) {
: originals.cartoFrom(value)
);
try {
return await run();
return await run(awareness);
} finally {
awareness.getContextSnapshot = originals.snapshot;
flightsLayer.getNearby = originals.flightsNearby;
@@ -2847,7 +2847,7 @@ async function withAwareness(harness, run) {
}
}
function analystRunner() {
function analystRunner(awareness) {
const flights = {
id: 'flights',
// Deliberately a DIFFERENT population from the proximity window: this is
@@ -2869,7 +2869,7 @@ function analystRunner() {
viewer,
styleManager: {},
dataManager: {
layers: new Map([['flights', { module: flights }]]),
layers: new Map([['flights', { module: flights }], ['military-awareness', { module: awareness }]]),
isEnabled: (id) => id === 'flights',
getAll: () => [{ id: 'flights', name: 'Live Flights', enabled: true, stats: { count: 1 } }],
},
@@ -2883,8 +2883,8 @@ test('front5: a nearby ask centres on the Contacts SUBJECT, not the selected dat
flights: Array.from({ length: 111 }, (_, i) => ({ id: `F${i}`, icao24: `f${i}`, distance: 1000 * i })),
military: Array.from({ length: 5 }, (_, i) => ({ id: `M${i}`, icao24: `m${i}`, distance: 500 * i })),
});
await withAwareness(harness, async () => {
const runner = analystRunner();
await withAwareness(harness, async (awareness) => {
const runner = analystRunner(awareness);
const result = await runner('analyst_query', {
layers: ['flights', 'military'],
// The centre the model reached for in the field: the selected datacenter.
@@ -2913,13 +2913,13 @@ test('front5: the spoken count and the panel window are ONE number by constructi
flights: Array.from({ length: 111 }, (_, i) => ({ id: `F${i}`, icao24: `f${i}` })),
military: Array.from({ length: 5 }, (_, i) => ({ id: `M${i}`, icao24: `m${i}` })),
});
await withAwareness(harness, async () => {
await withAwareness(harness, async (awareness) => {
// What the PANEL computes for this subject...
const panel = collectAircraftProximityWindow(harness.snapshot.subject.position, {
subject: harness.snapshot.subject,
});
// ...and what VOICE answers for the same subject.
const spoken = await analystRunner()('analyst_query', {
const spoken = await analystRunner(awareness)('analyst_query', {
layers: ['flights', 'military'],
scope: { kind: 'radius', km: 250 },
});
@@ -2936,8 +2936,8 @@ test('front5: the spoken count and the panel window are ONE number by constructi
test('front5: Contacts active with NO subject falls back to the view, not an empty panel', async () => {
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
const harness = awarenessSubjectHarness({ subject: null });
await withAwareness(harness, async () => {
const result = await analystRunner()('analyst_query', {
await withAwareness(harness, async (awareness) => {
const result = await analystRunner(awareness)('analyst_query', {
layers: ['flights'],
scope: { kind: 'radius', km: 250 },
});
@@ -2954,8 +2954,8 @@ test('front5: an explicit region still uses the region engine while Contacts is
subject: { id: 'a1b2c3', label: 'N546PC' },
flights: Array.from({ length: 111 }, (_, i) => ({ id: `F${i}`, icao24: `f${i}` })),
});
await withAwareness(harness, async () => {
const result = await analystRunner()('analyst_query', {
await withAwareness(harness, async (awareness) => {
const result = await analystRunner(awareness)('analyst_query', {
layers: ['flights'],
scope: { kind: 'region', name: 'Texas' },
});
@@ -2988,8 +2988,8 @@ test('front5: the box DIAGONAL is not the subject — 1.32 km away is somewhere
// separation is 1.32 km. This is the case the coordinator flagged: a centre
// far enough to be a different place, slipping through on the diagonal.
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
await withAwareness(subjectWindowHarness(), async () => {
const result = await analystRunner()('analyst_query', {
await withAwareness(subjectWindowHarness(), async (awareness) => {
const result = await analystRunner(awareness)('analyst_query', {
layers: ['flights', 'military'],
scope: { kind: 'radius', km: 250, center: { lat: 29.9 + 0.009, lon: -97.9 + 0.009 } },
});
@@ -3009,8 +3009,8 @@ test('front5: 0.99 km due EAST is the subject, though a degree box rejects it',
// 0.99 km — inside 1 km — yet over the 0.01 box threshold. A box would send
// the operator a different, smaller number for a centre that IS the contact.
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
await withAwareness(subjectWindowHarness(), async () => {
const result = await analystRunner()('analyst_query', {
await withAwareness(subjectWindowHarness(), async (awareness) => {
const result = await analystRunner(awareness)('analyst_query', {
layers: ['flights', 'military'],
scope: { kind: 'radius', km: 250, center: { lat: 29.9, lon: -97.9 + 0.0103 } },
});
@@ -3164,3 +3164,21 @@ test('ALPR common names toggle only the registered camera layer through the norm
}
}
});
test('ISS voice lookup uses the registered satellite instance', async () => {
const calls = [];
const viewer = {
clock: { onTick: { addEventListener: () => () => {} } },
scene: { canvas: { addEventListener() {}, removeEventListener() {} } },
camera: { moveEnd: { addEventListener() {} } },
};
const runner = createGevActionRunner({ viewer, styleManager: {}, dataManager: {
layers: new Map([['satellites', { module: { getNextIssPass(query) {
calls.push(query);
return { status: 'none' };
} } }]]),
} });
const result = await runner('next_iss_pass', { latitude: 30, longitude: -97, minElevationDeg: 15 });
assert.deepEqual(calls, [{ latDeg: 30, lonDeg: -97, minElevDeg: 15 }]);
assert.match(result.error, /No ISS pass above 15/);
});