@sailingrotevista/rotevista-dash 7.0.37 → 7.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/app.js +43 -18
  2. package/index.js +24 -15
  3. package/package.json +1 -1
package/app.js CHANGED
@@ -53,6 +53,7 @@ let rotationTrend = 0, meteoTrend = 0;
53
53
  let lastShortAvgVal = null, lastInstantTwa = null;
54
54
  let lastTrendTime = Date.now(), lastGybeAlarmTime = 0, lastTWCompute = 0;
55
55
  let twDirty = false, isNavigating = false, reconnectDelay = 1000;
56
+ let lastMessageReceivedTime = Date.now(); // Watchdog anti-zombie per monitorare il flusso dati reale
56
57
 
57
58
  let pressTimer, isFocusActive = false;
58
59
 
@@ -665,10 +666,17 @@ function startDisplayLoop() {
665
666
  // --- CALCOLO TREND VENTO & ALLARME STRAMBATA ---
666
667
  updateWindTrend();
667
668
 
668
- // --- AGGIORNAMENTO STATUS CON CONTEGGIO MINUTI REALE ---
669
+ // --- AGGIORNAMENTO STATUS CON WATCHDOG ANTI-ZOMBIE ---
669
670
  const isSocketOpen = socket && socket.readyState === WebSocket.OPEN;
671
+ const isDataFlowing = (now - lastMessageReceivedTime) < 6000; // Flusso dati attivo negli ultimi 6s
670
672
 
671
- if (isSocketOpen) {
673
+ // Se il socket risulta aperto ma non riceve pacchetti da > 6s (connessione zombie congelata da Android)
674
+ if (isSocketOpen && !isDataFlowing && !document.hidden && !simulationMode) {
675
+ console.warn("🔌 [Watchdog] Rilevato socket zombie (0 byte ricevuti da 6s). Forzo riconnessione...");
676
+ connect();
677
+ }
678
+
679
+ if (isSocketOpen && isDataFlowing) {
672
680
  ui.status.className = "online"; // Colore Verde
673
681
  const viewportMinutes = CONFIG.graphs.historyMinutes * (isNavigating ? 1 : 2);
674
682
  const requiredMs = viewportMinutes * 60000;
@@ -687,7 +695,7 @@ function startDisplayLoop() {
687
695
  }
688
696
  } else {
689
697
  ui.status.className = "offline"; // Colore Rosso
690
- ui.status.innerText = "OFFLINE"; // Chirurgico: Forza il testo a OFFLINE se il socket è chiuso, evitando scritte verdi in rosso
698
+ ui.status.innerText = isSocketOpen ? "STALL (RECONNECTING...)" : "OFFLINE";
691
699
  }
692
700
 
693
701
  // --- WATCHDOG: CONTROLLO TIMEOUT ---
@@ -1266,6 +1274,17 @@ function startDynamicSimulation() {
1266
1274
 
1267
1275
  function connect() {
1268
1276
  if (simulationMode) return;
1277
+
1278
+ // Pulizia preventiva del vecchio socket orfano (elimina memory leak ed eventi doppi)
1279
+ if (socket) {
1280
+ socket.onopen = null;
1281
+ socket.onmessage = null;
1282
+ socket.onerror = null;
1283
+ socket.onclose = null;
1284
+ try { socket.close(); } catch(e) {}
1285
+ socket = null;
1286
+ }
1287
+
1269
1288
  let addr = window.location.host || CONFIG.server.fallbackIp;
1270
1289
  try {
1271
1290
  // Connessione con subscribe=none per impedire a Signal K l'invio automatico di tutto lo stream di bordo
@@ -1275,6 +1294,7 @@ function connect() {
1275
1294
  ui.status.className = "online";
1276
1295
  ui.status.innerText = "ONLINE";
1277
1296
  reconnectDelay = 1000;
1297
+ lastMessageReceivedTime = Date.now(); // Reset timestamp dati
1278
1298
 
1279
1299
  // Sottoscrizione selettiva bilanciata: applichiamo un filtro a 3 Hz (333ms) per i dati rapidi,
1280
1300
  // garantendo fluidità matematica senza ritardi e ottimizzando profondità (1s) e GPS (10s)
@@ -1316,11 +1336,12 @@ function connect() {
1316
1336
  };
1317
1337
 
1318
1338
  socket.onmessage = (e) => {
1319
- const d = JSON.parse(e.data);
1320
- if (d.updates) {
1321
- d.updates.forEach(u => {
1322
- // Sincronizzazione dell'orologio sul tempo reale del server (NMEA/GPS)
1323
- const timeMs = u.timestamp ? new Date(u.timestamp).getTime() : Date.now();
1339
+ lastMessageReceivedTime = Date.now(); // Aggiorna il battito cardiaco del flusso dati
1340
+ const d = JSON.parse(e.data);
1341
+ if (d.updates) {
1342
+ d.updates.forEach(u => {
1343
+ // Sincronizzazione dell'orologio sul tempo reale del server (NMEA/GPS)
1344
+ const timeMs = u.timestamp ? new Date(u.timestamp).getTime() : Date.now();
1324
1345
  let sourceLabel = "Unknown";
1325
1346
  if (u.$source) {
1326
1347
  sourceLabel = u.$source;
@@ -1467,20 +1488,24 @@ async function init() {
1467
1488
  }
1468
1489
  }
1469
1490
 
1470
- // Watchdog attivo per la gestione intelligente dello standby e il massimo risparmio energetico
1471
- document.addEventListener('visibilitychange', () => {
1472
- if (document.visibilityState === 'visible') {
1473
- console.log("🔌 [Watchdog] Schermo sbloccato / Tab visibile. Riconnessione immediata e allineamento storico...");
1474
- // Al risveglio stabiliamo una nuova connessione pulita che scaricherà lo storico accumulato in background dal server
1491
+ // Gestore unificato di riattivazione su Android / iOS (Visibility, Focus, Pageshow, Online)
1492
+ const handleWakeUp = () => {
1493
+ if (!document.hidden) {
1494
+ console.log("🔌 [Watchdog] Risveglio dispositivo / Tab attiva. Riconnessione immediata...");
1495
+ reconnectDelay = 1000;
1475
1496
  connect();
1476
- } else if (document.visibilityState === 'hidden') {
1477
- console.log("🔌 [Watchdog] Schermo bloccato / Tab in background. Chiusura WebSocket preventiva per salvaguardare la batteria.");
1478
- // Tagliamo attivamente la connessione per congelare all'istante l'attività di rete ed i consumi del browser
1497
+ } else {
1498
+ console.log("🔌 [Watchdog] Schermo bloccato / Tab in background. Chiusura preventiva WebSocket.");
1479
1499
  if (socket) {
1480
- socket.close();
1500
+ try { socket.close(); } catch(e) {}
1481
1501
  }
1482
1502
  }
1483
- });
1503
+ };
1504
+
1505
+ document.addEventListener('visibilitychange', handleWakeUp);
1506
+ window.addEventListener('pageshow', handleWakeUp);
1507
+ window.addEventListener('focus', handleWakeUp);
1508
+ window.addEventListener('online', handleWakeUp);
1484
1509
 
1485
1510
  window.addEventListener('load', init);
1486
1511
  window.addEventListener('pagehide', saveDashboardState);
package/index.js CHANGED
@@ -762,13 +762,13 @@ module.exports = function (app) {
762
762
 
763
763
  lastForecast30mSlot = current30mSlot; // Aggiorna preventivamente lo slot per evitare chiamate simultanee in caso di rallentamento di rete
764
764
 
765
- https.get(url, (res) => {
766
- if (res.statusCode !== 200) {
767
- app.error(`[Open-Meteo] HTTP Error: ${res.statusCode}`);
768
- res.resume();
769
- lastForecast30mSlot = 0; // Reset in caso di errore per permettere un tentativo al prossimo pacchetto GPS
770
- return;
771
- }
765
+ const req = https.get(url, (res) => {
766
+ if (res.statusCode !== 200) {
767
+ app.error(`[Open-Meteo] HTTP Error: ${res.statusCode}`);
768
+ res.resume();
769
+ lastForecast30mSlot = 0; // Reset in caso di errore per permettere un tentativo al prossimo pacchetto GPS
770
+ return;
771
+ }
772
772
 
773
773
  let data = '';
774
774
  res.on('data', (chunk) => { data += chunk; });
@@ -808,15 +808,24 @@ module.exports = function (app) {
808
808
  calculateInterpolatedFuture(forecastList);
809
809
 
810
810
  } catch (err) {
811
- app.error(`[Open-Meteo] Error parsing JSON: ${err.message}`);
812
- lastForecast30mSlot = 0;
811
+ app.error(`[Open-Meteo] Error parsing JSON: ${err.message}`);
812
+ lastForecast30mSlot = 0;
813
+ }
814
+ });
815
+ });
816
+
817
+ // Timeout di sicurezza a 8 secondi contro cadute di linea 4G/Satellite
818
+ req.setTimeout(8000, () => {
819
+ app.error('[Open-Meteo] Request Timeout (8s)');
820
+ req.abort();
821
+ lastForecast30mSlot = 0;
822
+ });
823
+
824
+ req.on('error', (err) => {
825
+ app.error(`[Open-Meteo] Network Error: ${err.message}`);
826
+ lastForecast30mSlot = 0;
827
+ });
813
828
  }
814
- });
815
- }).on('error', (err) => {
816
- app.error(`[Open-Meteo] Network Error: ${err.message}`);
817
- lastForecast30mSlot = 0;
818
- });
819
- }
820
829
 
821
830
  /**
822
831
  * calculateInterpolatedFuture: Esegue l'interpolazione lineare e vettoriale circolare
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sailingrotevista/rotevista-dash",
3
- "version": "7.0.37",
3
+ "version": "7.0.38",
4
4
  "description": "Wind Dashboard with navigation and course aids",
5
5
  "main": "index.js",
6
6
  "publishConfig": {