@sailingrotevista/rotevista-dash 7.0.37 → 7.0.39
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.
- package/app.js +46 -30
- package/index.js +24 -15
- package/package.json +1 -1
- package/weather-radar.js +5 -3
- package/debug_sample_radar.html +0 -887
- package/debug_signalk_connection.html +0 -70
- package/debug_weather_radar.html +0 -233
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
|
|
|
@@ -76,6 +77,7 @@ const sourceLocks = {};
|
|
|
76
77
|
const store = {
|
|
77
78
|
raw: {},
|
|
78
79
|
timestamps: {},
|
|
80
|
+
isNavigating: false, // Stato unificato di navigazione barca in movimento
|
|
79
81
|
depthProtectedActive: false, // Memoria per lo stato della protezione profondità
|
|
80
82
|
herculesScales: {}, // Memoria per i limiti attivi della modalità Hercules
|
|
81
83
|
smoothBuf: { hdg: [], cog: [], awa: [], twa: [], twd: [] },
|
|
@@ -661,14 +663,22 @@ function startDisplayLoop() {
|
|
|
661
663
|
const sogKts = msToKts(store.raw["navigation.speedOverGround"] || 0);
|
|
662
664
|
|
|
663
665
|
isNavigating = stwKts > CONFIG.averaging.minSpeed || sogKts > CONFIG.averaging.minSpeed;
|
|
666
|
+
store.isNavigating = isNavigating; // Sincronizzazione stato nello store condiviso
|
|
664
667
|
|
|
665
668
|
// --- CALCOLO TREND VENTO & ALLARME STRAMBATA ---
|
|
666
669
|
updateWindTrend();
|
|
667
670
|
|
|
668
|
-
// --- AGGIORNAMENTO STATUS CON
|
|
671
|
+
// --- AGGIORNAMENTO STATUS CON WATCHDOG ANTI-ZOMBIE ---
|
|
669
672
|
const isSocketOpen = socket && socket.readyState === WebSocket.OPEN;
|
|
673
|
+
const isDataFlowing = (now - lastMessageReceivedTime) < 6000; // Flusso dati attivo negli ultimi 6s
|
|
670
674
|
|
|
671
|
-
|
|
675
|
+
// Se il socket risulta aperto ma non riceve pacchetti da > 6s (connessione zombie congelata da Android)
|
|
676
|
+
if (isSocketOpen && !isDataFlowing && !document.hidden && !simulationMode) {
|
|
677
|
+
console.warn("🔌 [Watchdog] Rilevato socket zombie (0 byte ricevuti da 6s). Forzo riconnessione...");
|
|
678
|
+
connect();
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (isSocketOpen && isDataFlowing) {
|
|
672
682
|
ui.status.className = "online"; // Colore Verde
|
|
673
683
|
const viewportMinutes = CONFIG.graphs.historyMinutes * (isNavigating ? 1 : 2);
|
|
674
684
|
const requiredMs = viewportMinutes * 60000;
|
|
@@ -687,7 +697,7 @@ function startDisplayLoop() {
|
|
|
687
697
|
}
|
|
688
698
|
} else {
|
|
689
699
|
ui.status.className = "offline"; // Colore Rosso
|
|
690
|
-
ui.status.innerText = "
|
|
700
|
+
ui.status.innerText = isSocketOpen ? "STALL (RECONNECTING...)" : "OFFLINE";
|
|
691
701
|
}
|
|
692
702
|
|
|
693
703
|
// --- WATCHDOG: CONTROLLO TIMEOUT ---
|
|
@@ -1024,18 +1034,7 @@ async function fetchServerHistory() {
|
|
|
1024
1034
|
timestamp: data.futureForecast.timestamp + timeDelta
|
|
1025
1035
|
};
|
|
1026
1036
|
}
|
|
1027
|
-
|
|
1028
|
-
store.twdMinuteBuffer = data.twd.map(p => ({
|
|
1029
|
-
...p,
|
|
1030
|
-
time: p.time + timeDelta
|
|
1031
|
-
}));
|
|
1032
|
-
}
|
|
1033
|
-
if (data.tws) {
|
|
1034
|
-
store.twsMinuteBuffer = data.tws.map(p => ({
|
|
1035
|
-
...p,
|
|
1036
|
-
time: p.time + timeDelta
|
|
1037
|
-
}));
|
|
1038
|
-
}
|
|
1037
|
+
// data.twd e data.tws sono già mappati direttamente in store.histories.twd e store.histories.tws
|
|
1039
1038
|
|
|
1040
1039
|
// Sincronizziamo lo storico dei minuti per il radar, ma lasciamo che il buffer rapido 'store.longBuf.twd'
|
|
1041
1040
|
// parta pulito all'avvio. Si popolerà istantaneamente a 3Hz con i soli dati in tempo reale,
|
|
@@ -1266,6 +1265,17 @@ function startDynamicSimulation() {
|
|
|
1266
1265
|
|
|
1267
1266
|
function connect() {
|
|
1268
1267
|
if (simulationMode) return;
|
|
1268
|
+
|
|
1269
|
+
// Pulizia preventiva del vecchio socket orfano (elimina memory leak ed eventi doppi)
|
|
1270
|
+
if (socket) {
|
|
1271
|
+
socket.onopen = null;
|
|
1272
|
+
socket.onmessage = null;
|
|
1273
|
+
socket.onerror = null;
|
|
1274
|
+
socket.onclose = null;
|
|
1275
|
+
try { socket.close(); } catch(e) {}
|
|
1276
|
+
socket = null;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1269
1279
|
let addr = window.location.host || CONFIG.server.fallbackIp;
|
|
1270
1280
|
try {
|
|
1271
1281
|
// Connessione con subscribe=none per impedire a Signal K l'invio automatico di tutto lo stream di bordo
|
|
@@ -1275,6 +1285,7 @@ function connect() {
|
|
|
1275
1285
|
ui.status.className = "online";
|
|
1276
1286
|
ui.status.innerText = "ONLINE";
|
|
1277
1287
|
reconnectDelay = 1000;
|
|
1288
|
+
lastMessageReceivedTime = Date.now(); // Reset timestamp dati
|
|
1278
1289
|
|
|
1279
1290
|
// Sottoscrizione selettiva bilanciata: applichiamo un filtro a 3 Hz (333ms) per i dati rapidi,
|
|
1280
1291
|
// garantendo fluidità matematica senza ritardi e ottimizzando profondità (1s) e GPS (10s)
|
|
@@ -1316,11 +1327,12 @@ function connect() {
|
|
|
1316
1327
|
};
|
|
1317
1328
|
|
|
1318
1329
|
socket.onmessage = (e) => {
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1330
|
+
lastMessageReceivedTime = Date.now(); // Aggiorna il battito cardiaco del flusso dati
|
|
1331
|
+
const d = JSON.parse(e.data);
|
|
1332
|
+
if (d.updates) {
|
|
1333
|
+
d.updates.forEach(u => {
|
|
1334
|
+
// Sincronizzazione dell'orologio sul tempo reale del server (NMEA/GPS)
|
|
1335
|
+
const timeMs = u.timestamp ? new Date(u.timestamp).getTime() : Date.now();
|
|
1324
1336
|
let sourceLabel = "Unknown";
|
|
1325
1337
|
if (u.$source) {
|
|
1326
1338
|
sourceLabel = u.$source;
|
|
@@ -1467,20 +1479,24 @@ async function init() {
|
|
|
1467
1479
|
}
|
|
1468
1480
|
}
|
|
1469
1481
|
|
|
1470
|
-
//
|
|
1471
|
-
|
|
1472
|
-
if (document.
|
|
1473
|
-
console.log("🔌 [Watchdog]
|
|
1474
|
-
|
|
1482
|
+
// Gestore unificato di riattivazione su Android / iOS (Visibility, Focus, Pageshow, Online)
|
|
1483
|
+
const handleWakeUp = () => {
|
|
1484
|
+
if (!document.hidden) {
|
|
1485
|
+
console.log("🔌 [Watchdog] Risveglio dispositivo / Tab attiva. Riconnessione immediata...");
|
|
1486
|
+
reconnectDelay = 1000;
|
|
1475
1487
|
connect();
|
|
1476
|
-
} else
|
|
1477
|
-
console.log("🔌 [Watchdog] Schermo bloccato / Tab in background. Chiusura
|
|
1478
|
-
// Tagliamo attivamente la connessione per congelare all'istante l'attività di rete ed i consumi del browser
|
|
1488
|
+
} else {
|
|
1489
|
+
console.log("🔌 [Watchdog] Schermo bloccato / Tab in background. Chiusura preventiva WebSocket.");
|
|
1479
1490
|
if (socket) {
|
|
1480
|
-
socket.close();
|
|
1491
|
+
try { socket.close(); } catch(e) {}
|
|
1481
1492
|
}
|
|
1482
1493
|
}
|
|
1483
|
-
}
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1496
|
+
document.addEventListener('visibilitychange', handleWakeUp);
|
|
1497
|
+
window.addEventListener('pageshow', handleWakeUp);
|
|
1498
|
+
window.addEventListener('focus', handleWakeUp);
|
|
1499
|
+
window.addEventListener('online', handleWakeUp);
|
|
1484
1500
|
|
|
1485
1501
|
window.addEventListener('load', init);
|
|
1486
1502
|
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
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
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
|
-
|
|
812
|
-
|
|
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
package/weather-radar.js
CHANGED
|
@@ -59,8 +59,9 @@ function calculateActive30mRing() {
|
|
|
59
59
|
const now = Date.now();
|
|
60
60
|
const start30m = now - 1800000;
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
const
|
|
62
|
+
// Lettura unificata dai buffer storici dello store
|
|
63
|
+
const twdRecent = (store.histories.twd || []).filter(p => p.time >= start30m);
|
|
64
|
+
const twsRecent = (store.histories.tws || []).filter(p => p.time >= start30m);
|
|
64
65
|
|
|
65
66
|
if (twdRecent.length === 0) return null;
|
|
66
67
|
|
|
@@ -264,7 +265,8 @@ function renderRadar() {
|
|
|
264
265
|
// Disegno del LED lampeggiante del meteo-trend
|
|
265
266
|
if (activeRing && !activeRing.isCalm) {
|
|
266
267
|
const twdNow = getCircularAverageFromBuffer(store.longBuf.twd, 60000, false);
|
|
267
|
-
const
|
|
268
|
+
const isNav = (typeof store !== 'undefined' && store.isNavigating);
|
|
269
|
+
const strategicWindowMs = (isNav ? 15 : 60) * 60000;
|
|
268
270
|
const twdRef = getCircularAverageFromBuffer(store.longBuf.twd, strategicWindowMs, false);
|
|
269
271
|
|
|
270
272
|
if (twdNow && twdRef) {
|