Files
xplane-cockpit/plugins/fms-sync.lua
T
karim 38b048ad41 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>
2026-06-02 02:17:06 +02:00

114 lines
4.0 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- ============================================================================
-- X-Plane Glass Cockpit — FMS two-way sync (FlyWithLua companion)
-- ============================================================================
-- The web cockpit's bridge can't write the FMS via X-Plane's Web API. This
-- script runs INSIDE X-Plane (FlyWithLua) and has the FMS SDK, so it bridges
-- the shared plan <-> the in-sim FMS through two text files:
--
-- <X-Plane>/Output/fms-sync/to_sim.txt written by the bridge (our plan)
-- <X-Plane>/Output/fms-sync/from_sim.txt written here (the sim's plan)
--
-- A 3-decimal lat/lon signature de-dupes both sides so they never loop.
--
-- INSTALL: copy this file to <X-Plane>/Resources/plugins/FlyWithLua/Scripts/
-- (install FlyWithLua NG+ first), then restart X-Plane or run
-- "FlyWithLua > Reload all Lua script files".
-- ============================================================================
local SYNC = SYSTEM_DIRECTORY .. "Output/fms-sync/"
local TO_SIM = SYNC .. "to_sim.txt"
local FROM_SIM = SYNC .. "from_sim.txt"
local last_sig = nil
-- make sure the folder exists (bridge also creates it)
os.execute('mkdir -p "' .. SYNC .. '" 2>/dev/null || mkdir "' .. SYNC .. '" 2>nul')
-- 3-decimal lat/lon signature of a waypoint list ---------------------------
local function sig_of(wps)
local parts = {}
for i = 1, #wps do
parts[i] = string.format("%.3f,%.3f", wps[i].lat, wps[i].lon)
end
return table.concat(parts, ";")
end
local function read_file(p)
local f = io.open(p, "r"); if not f then return nil end
local s = f:read("*a"); f:close(); return s
end
local function write_file(p, s)
local f = io.open(p, "w"); if not f then return end
f:write(s); f:close()
end
-- parse the bridge file: skip "# sig" lines, take "lat lon alt id type" ------
local function parse(txt)
local wps = {}
if not txt then return wps end
for line in txt:gmatch("[^\r\n]+") do
if line:sub(1, 1) ~= "#" then
local lat, lon, alt, id = line:match("^%s*(-?%d+%.?%d*)%s+(-?%d+%.?%d*)%s+(-?%d+)%s+(%S+)")
if lat and lon then
wps[#wps + 1] = { lat = tonumber(lat), lon = tonumber(lon), alt = tonumber(alt) or 0, id = id or "WPT" }
end
end
end
return wps
end
-- read the current in-sim FMS plan ------------------------------------------
local function read_fms()
local wps = {}
local n = XPLMCountFMSEntries()
for i = 0, n - 1 do
-- FlyWithLua: type, id, ref, altitude, lat, lon
local _t, id, _ref, alt, lat, lon = XPLMGetFMSEntryInfo(i)
if lat and lon and (math.abs(lat) > 0.0001 or math.abs(lon) > 0.0001) then
wps[#wps + 1] = { lat = lat, lon = lon, alt = alt or 0, id = (id ~= "" and id) or "WPT" }
end
end
return wps
end
-- write our plan into the in-sim FMS ----------------------------------------
local function apply_to_fms(wps)
local old = XPLMCountFMSEntries()
for i = 1, #wps do
-- lat/lon entries keep our exact coords -> stable round-trip (no drift)
XPLMSetFMSEntryLatLon(i - 1, wps[i].lat, wps[i].lon, math.floor(wps[i].alt or 0))
end
for i = old - 1, #wps, -1 do XPLMClearFMSEntry(i) end -- trim leftovers
if #wps >= 1 then
XPLMSetDisplayedFMSEntry(0)
XPLMSetDestinationFMSEntry(#wps - 1)
end
end
local function serialize(wps)
local lines = { "# " .. sig_of(wps) }
for i = 1, #wps do
lines[#lines + 1] = string.format("%.6f %.6f %d %s WPT", wps[i].lat, wps[i].lon, math.floor(wps[i].alt or 0), wps[i].id)
end
return table.concat(lines, "\n") .. "\n"
end
-- main loop (~1×/sec): whichever side differs from the agreed plan wins ------
function fms_sync_tick()
local to_wps = parse(read_file(TO_SIM))
local tsig = sig_of(to_wps)
local fm_wps = read_fms()
local fsig = sig_of(fm_wps)
if tsig ~= "" and tsig ~= last_sig then
apply_to_fms(to_wps) -- App -> Sim
last_sig = tsig
elseif fsig ~= last_sig then
write_file(FROM_SIM, serialize(fm_wps)) -- Sim -> App
last_sig = fsig
end
end
do_often("fms_sync_tick()")
logMsg("[glass-cockpit] FMS sync active -> " .. SYNC)