bridge: fix exponential reconnect (OOM) + add dataref resolve probe

The X-Plane socket fired both 'error' and 'close' on a failed connect, each
scheduling a reconnect — so attempts doubled every cycle, leaking sockets/timers
until the process OOMed (~3 min with the sim down). Guard onDown so each socket
schedules exactly one reconnect (and tear the dead socket down).

Also log a one-shot resolve-probe (GET /datarefs?filter[name]=airspeed…) on
connect so a Web-API version/format mismatch is visible in the log.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 23:20:59 +02:00
parent 1734a2d7ac
commit 10d4a4facf
+16
View File
@@ -79,6 +79,15 @@ async function fetchAllByName(resource, names) {
// ---- X-Plane connection --------------------------------------------------- // ---- X-Plane connection ---------------------------------------------------
async function resolveIds() { 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 drefNames = Object.values(DATAREFS);
const cmdNames = Object.values(COMMANDS); const cmdNames = Object.values(COMMANDS);
state.drefNameToId = await fetchAllByName('datarefs', [ 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) => { const onDown = (why) => {
if (downHandled) return;
downHandled = true;
if (state.xpConnected) log(`X-Plane disconnected (${why})`); if (state.xpConnected) log(`X-Plane disconnected (${why})`);
state.xpConnected = false; state.xpConnected = false;
broadcast({ type: 'status', xpConnected: false }); broadcast({ type: 'status', xpConnected: false });
if (state.xpSocket === sock) state.xpSocket = null; if (state.xpSocket === sock) state.xpSocket = null;
try { sock.removeAllListeners(); sock.terminate?.(); } catch {}
setTimeout(connectXPlane, 3000); setTimeout(connectXPlane, 3000);
}; };
sock.on('close', () => onDown('close')); sock.on('close', () => onDown('close'));