/** * data-binder.js — apre una singola sottoscrizione SignalK delta locale * e smista i valori ai tile per path. * * Espone window.dataBinder con: * subscribe(path, fn) → unsubscribe() * getLatest(path) → ultimo valore visto (o null) * onConnState(fn) → notifica connesso/disconnesso */ (function () { const subs = new Map(); // path -> Set const latest = new Map(); // path -> { value, ts } const stateListeners = new Set(); let ws = null; let reconnectTimer = null; function notify(path, value) { latest.set(path, { value, ts: Date.now() }); const ss = subs.get(path); if (!ss) return; for (const fn of ss) { try { fn(value); } catch (e) { console.warn(e); } } } function notifyState(s) { for (const fn of stateListeners) { try { fn(s); } catch (_) {} } } function connect() { const url = `ws://${location.host}/signalk/v1/stream?subscribe=all`; try { ws = new WebSocket(url); } catch (err) { console.error('[BINDER] WS create:', err); scheduleReconnect(); return; } ws.onopen = () => { notifyState({ connected: true }); console.log('[BINDER] WS connected'); }; ws.onerror = (e) => console.warn('[BINDER] WS error', e); ws.onclose = () => { notifyState({ connected: false }); scheduleReconnect(); }; ws.onmessage = (ev) => { let msg; try { msg = JSON.parse(ev.data); } catch { return; } if (!msg.updates) return; for (const u of msg.updates) { if (!u.values) continue; for (const v of u.values) { if (!v.path) continue; notify(v.path, v.value); // espandi position in lat/lon per chi si iscrive a quelli if (v.path === 'navigation.position' && v.value && typeof v.value === 'object') { if (v.value.latitude != null) notify('navigation.position.latitude', v.value.latitude); if (v.value.longitude != null) notify('navigation.position.longitude', v.value.longitude); } } } }; } function scheduleReconnect() { if (reconnectTimer) return; reconnectTimer = setTimeout(() => { reconnectTimer = null; connect(); }, 3000); } function subscribe(path, fn) { if (!subs.has(path)) subs.set(path, new Set()); subs.get(path).add(fn); const last = latest.get(path); if (last) { try { fn(last.value); } catch (_) {} } return () => subs.get(path)?.delete(fn); } function getLatest(path) { return latest.get(path)?.value ?? null; } function onConnState(fn) { stateListeners.add(fn); return () => stateListeners.delete(fn); } connect(); window.dataBinder = { subscribe, getLatest, onConnState }; })();