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:
+40
-3
@@ -87,14 +87,51 @@ export function exportFms(name = 'WEBFPL') {
|
||||
}
|
||||
const content = lines.join('\n') + '\n';
|
||||
|
||||
const root = xplaneRoot();
|
||||
const dir = root ? path.join(root, 'Output', 'FMS plans') : path.join(process.cwd(), 'fms-out');
|
||||
const dir = fmsDir();
|
||||
try {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
const file = path.join(dir, `${name}.fms`);
|
||||
fs.writeFileSync(file, content);
|
||||
return { ok: true, file, intoXplane: !!root };
|
||||
return { ok: true, file, intoXplane: !!xplaneRoot() };
|
||||
} catch (e) {
|
||||
return { ok: false, error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
// ---- load saved X-Plane .fms plans (Output/FMS plans) ----
|
||||
function fmsDir() {
|
||||
const root = xplaneRoot();
|
||||
return root ? path.join(root, 'Output', 'FMS plans') : path.join(process.cwd(), 'fms-out');
|
||||
}
|
||||
const FMS_TYPE = { 1: 'APT', 2: 'NDB', 3: 'VOR', 11: 'WPT', 28: 'USR' };
|
||||
|
||||
// List the names of every saved .fms plan (X-Plane's own + our exports).
|
||||
export function listPlans() {
|
||||
try {
|
||||
return fs.readdirSync(fmsDir())
|
||||
.filter((f) => f.toLowerCase().endsWith('.fms'))
|
||||
.map((f) => f.replace(/\.fms$/i, ''))
|
||||
.sort((a, b) => a.localeCompare(b));
|
||||
} catch { return []; }
|
||||
}
|
||||
|
||||
// Parse a saved .fms (v1100/v3) into our waypoints and make it the active plan.
|
||||
export function loadFms(name) {
|
||||
const safe = String(name || '').replace(/[^\w .+-]/g, '');
|
||||
const file = path.join(fmsDir(), `${safe}.fms`);
|
||||
if (!fs.existsSync(file)) return { ok: false, error: `not found: ${safe}` };
|
||||
const wps = [];
|
||||
for (const raw of fs.readFileSync(file, 'utf8').split(/\r?\n/)) {
|
||||
const p = raw.trim().split(/\s+/);
|
||||
// waypoint rows start with a numeric type code: <type> <ident> <alt> <lat> <lon>
|
||||
if (p.length >= 5 && /^\d+$/.test(p[0]) && p[0] !== '1100') {
|
||||
const lat = parseFloat(p[3]), lon = parseFloat(p[4]), alt = parseFloat(p[2]);
|
||||
if (isFinite(lat) && isFinite(lon)) {
|
||||
wps.push({ id: p[1], lat, lon, type: FMS_TYPE[+p[0]] || 'WPT', alt: alt > 0 ? Math.round(alt) : null });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wps.length < 1) return { ok: false, error: 'no waypoints in file' };
|
||||
setPlan({ name: safe.toUpperCase(), waypoints: wps, activeLeg: 1 });
|
||||
return { ok: true, plan, count: wps.length };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user