G1000: two-way sim sync, more PFD/MFD fidelity, authentic dialogs

Sync (FlyWithLua companions in plugins/ + server/fmssync.js):
- FMS flight-plan two-way sync (App <-> in-sim FMS) via fms-sync.lua
- G1000 UI-state publish (page/range/inset) via ui-sync.lua + CDI source,
  baro, map-range follow
- Terrain awareness: elevation grid probe (terrain-probe.lua) -> red/yellow
  MFD overlay vs aircraft altitude

PFD:
- AFCS mode annunciation bar from autopilot _status datarefs
- CDI source GPS/VLOC colouring, BRG1/BRG2 pointers + DME windows, marker beacons
- magenta speed/altitude trend vectors, selected-altitude alerting
- time-based (frame-rate-independent) smoothing for attitude/heading/tapes

MFD:
- nav data bar (DTK/ETE/active leg), airways overlay from earth_awy.dat,
  compass rose anchored to the ownship

Dialogs (NEAREST/FLIGHTPLAN/DIRECT-TO/PROCEDURES):
- flat, square, embedded G1000 look (no shadow/rounded/transparency)
- compact lower-right placement, no close X (softkey toggles), single window
- NEAREST 2-line entries (ILS/VFR, COM freq, runway length), PROC action menu

Service worker: network-first HTML so reloads pick up new builds (cache v2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 02:17:06 +02:00
parent 354ea5d44b
commit 38b048ad41
23 changed files with 1707 additions and 213 deletions
+28 -1
View File
@@ -6,6 +6,7 @@ import { useEffect, useRef, useState, useCallback } from 'react';
export function useXplane() {
const [values, setValues] = useState({});
const [flightPlan, setFlightPlan] = useState({ name: 'ACTIVE', waypoints: [] });
const [terrain, setTerrain] = useState(null); // elevation grid for terrain awareness
const [exportMsg, setExportMsg] = useState(null);
const [connected, setConnected] = useState(false); // socket to bridge
const [xpConnected, setXpConnected] = useState(false); // bridge <-> X-Plane
@@ -37,6 +38,7 @@ export function useXplane() {
}
else if (msg.type === 'status') setXpConnected(!!msg.xpConnected);
else if (msg.type === 'flightplan') setFlightPlan(msg.data);
else if (msg.type === 'terrain') setTerrain(msg.data);
else if (msg.type === 'fp_export_result') setExportMsg(msg);
};
ws.onclose = () => {
@@ -68,9 +70,16 @@ export function useXplane() {
clear: () => send({ type: 'fp_clear' }),
set: (plan) => send({ type: 'fp_set', plan }),
export: (name) => send({ type: 'fp_export', name }),
load: (name) => send({ type: 'fp_load', name }),
};
return { values, flightPlan, exportMsg, connected, xpConnected, command, setDataref, fp };
return { values, flightPlan, terrain, exportMsg, connected, xpConnected, command, setDataref, fp };
}
// List saved .fms flight plans (X-Plane's Output/FMS plans) via the bridge.
export async function fmsList() {
try { const r = await fetch('/api/fms/list'); return r.ok ? r.json() : []; }
catch { return []; }
}
// Search X-Plane's nav database (waypoints/VOR/NDB/airports) via the bridge.
@@ -86,3 +95,21 @@ export async function navSearch(q) {
// Convenience: read a numeric value with a fallback.
export const num = (v, d = 0) => (typeof v === 'number' && isFinite(v) ? v : d);
const v0 = (x) => (Array.isArray(x) ? num(x[0]) : num(x));
// System alerts/annunciations derived from live datarefs — drives the PFD
// CAUTION softkey + the ALERTS window. Each: { t: text, warn: bool (red vs amber) }.
export function systemAlerts(V = {}) {
const out = [];
const rpm = v0(V.engRpm);
const running = rpm > 400;
const oilP = v0(V.oilPress);
const oilT = v0(V.oilTemp); const oilF = oilT > 150 ? oilT : oilT * 9 / 5 + 32;
const volts = v0(V.volts);
const fuelGal = (Array.isArray(V.fuelQty) ? V.fuelQty.reduce((a, b) => a + num(b), 0) : num(V.fuelQty)) / 2.72;
if (running && oilP < 20) out.push({ t: 'OIL PRESSURE', warn: true });
if (oilF > 245) out.push({ t: 'OIL TEMP HIGH', warn: true });
if (Array.isArray(V.fuelQty) && fuelGal < 5) out.push({ t: 'FUEL LOW TOTAL', warn: fuelGal < 2.5 });
if (volts > 1 && volts < 24.5) out.push({ t: 'LOW VOLTS', warn: false });
return out;
}