gods-eye-view/src/cockpitTracking.js

93 lines
3.3 KiB
JavaScript

/*
* Cockpit aircraft metadata itself is resolved by `resolveTrackedAircraftInfo`
* in `cockpitMath.js` — the landed owner of that rule. It keeps layer
* precedence as the fallback when no normalized `gevTrackedId` is available,
* so tracking paths that never stamp one keep working; `readAircraftInfo` is
* pinned to it by `cockpitMarkup.test.mjs`. This module owns only the
* multi-step ENTRY TRANSACTION (adopt → enter → roll back).
*/
/** Force the prior aircraft layer to reacquire Cesium and durable tracking ownership. */
export function restoreAircraftTrackingOwner(layer, id, { origin = 'programmatic' } = {}) {
if (!layer?.trackById || !id) return false;
layer.stopTracking?.({ origin });
return Boolean(layer.trackById(id, { origin }));
}
/** Normalize Cockpit aircraft metadata into a stable tracking target. */
export function aircraftTrackingTarget(info) {
if (!info?.layerId || !(info.icao24 || info.id)) return null;
return {
layerId: info.layerId,
id: String(info.icao24 || info.id),
};
}
/** Enter Cockpit and restore the pre-transaction tracker after every failure form. */
export function enterCockpitWithTracking({
cockpitView,
selectedLayer = null,
selectedTarget = null,
currentLayer = null,
rollbackLayer = null,
rollbackTarget = undefined,
selectionOrigin = 'programmatic',
}) {
const currentTarget = aircraftTrackingTarget(cockpitView?.readAircraftInfo?.());
const restoreTarget = rollbackTarget === undefined ? currentTarget : rollbackTarget;
const key = (target) => target?.layerId && target?.id
? `${target.layerId}:${target.id}`
: null;
let activeLayer = currentLayer;
let activeTarget = currentTarget;
let entryError = null;
let entered = false;
let entryThrew = false;
try {
const selectingTarget = Boolean(selectedLayer && selectedTarget?.id);
if (selectingTarget) {
activeLayer = selectedLayer;
activeTarget = selectedTarget;
if (!selectedLayer.trackById?.(selectedTarget.id, { origin: selectionOrigin })) {
entryError = new Error('Selected aircraft could not be tracked for Cockpit entry');
}
}
if (!entryError) entered = Boolean(cockpitView.enter());
} catch (error) {
entryThrew = true;
entryError = error instanceof Error ? error : new Error(String(error));
}
if (!entered && key(activeTarget) !== key(restoreTarget)) {
try {
// Undo the explicit attempted selection through the same persistence
// authority, then republish the prior target with that origin. A
// programmatic rollback would fix the camera while leaving the failed
// target durable in local/share state.
activeLayer?.stopTracking?.({ origin: selectionOrigin });
if (restoreTarget && !restoreAircraftTrackingOwner(
rollbackLayer,
restoreTarget.id,
{ origin: selectionOrigin },
)) {
entryError ||= new Error('Prior aircraft tracking could not be restored');
}
} catch (error) {
entryError ||= error instanceof Error ? error : new Error(String(error));
}
}
if (entryThrew) {
try {
cockpitView.exit?.({ restoreTracking: false });
} catch {
// Tracker rollback above remains authoritative if partial Cockpit cleanup fails.
}
}
return {
entered,
error: entered ? null : entryError?.message || 'Cockpit entry was unavailable',
};
}