diff --git a/server/bridge.js b/server/bridge.js index 79fd970..5c2a076 100644 --- a/server/bridge.js +++ b/server/bridge.js @@ -79,6 +79,15 @@ async function fetchAllByName(resource, names) { // ---- X-Plane connection --------------------------------------------------- async function resolveIds() { + // one-shot diagnostic: probe a universal dataref so the log shows the API's + // real response shape (helps when a version/format mismatch yields 0 matches). + try { + const probe = 'sim/cockpit2/gauges/indicators/airspeed_kts_pilot'; + const u = `${REST}/datarefs?filter[name]=${encodeURIComponent(probe)}`; + const r = await fetch(u, { headers: { Accept: 'application/json' } }); + const txt = await r.text(); + log(`resolve-probe: GET ${u} -> HTTP ${r.status}; body ${txt.slice(0, 160)}`); + } catch (e) { log(`resolve-probe failed: ${e.message}`); } const drefNames = Object.values(DATAREFS); const cmdNames = Object.values(COMMANDS); state.drefNameToId = await fetchAllByName('datarefs', [ @@ -146,11 +155,18 @@ function connectXPlane() { } }); + // 'error' and 'close' both fire on a failed socket — guard so each socket + // schedules exactly ONE reconnect (otherwise attempts double every cycle and + // the process leaks sockets/timers until it OOMs). + let downHandled = false; const onDown = (why) => { + if (downHandled) return; + downHandled = true; if (state.xpConnected) log(`X-Plane disconnected (${why})`); state.xpConnected = false; broadcast({ type: 'status', xpConnected: false }); if (state.xpSocket === sock) state.xpSocket = null; + try { sock.removeAllListeners(); sock.terminate?.(); } catch {} setTimeout(connectXPlane, 3000); }; sock.on('close', () => onDown('close'));