@sailingrotevista/rotevista-dash 6.2.2 → 6.2.4
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/index.js +67 -30
- package/package.json +1 -1
- package/radar.html +13 -3
- package/rarad_test.html +233 -0
package/index.js
CHANGED
|
@@ -212,12 +212,11 @@ module.exports = function (app) {
|
|
|
212
212
|
const vmg = Math.abs(stwKts * Math.cos(twa));
|
|
213
213
|
manageHistory('vmg', vmg);
|
|
214
214
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
215
|
+
// Calcoliamo il TWD di fallback solo se non abbiamo visto dati nativi negli ultimi 5 secondi
|
|
216
|
+
if (hdg !== undefined && (now - lastNativeTwdTime > 5000)) {
|
|
217
|
+
const twd = (hdg + twa + 2 * Math.PI) % (2 * Math.PI);
|
|
218
|
+
manageHistory('twd', twd); // BUG RISOLTO: Rimossa la riassegnazione di "const" che mandava in crash il server
|
|
219
|
+
}
|
|
221
220
|
}
|
|
222
221
|
}
|
|
223
222
|
|
|
@@ -338,31 +337,45 @@ module.exports = function (app) {
|
|
|
338
337
|
}
|
|
339
338
|
}
|
|
340
339
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
} else {
|
|
347
|
-
// Salvataggio specifico del TWD contenente l'oggetto { val, min, max, time }
|
|
348
|
-
histories['twd'].push({
|
|
349
|
-
val: finalValue.val,
|
|
350
|
-
min: finalValue.min,
|
|
351
|
-
max: finalValue.max,
|
|
352
|
-
time: now
|
|
353
|
-
});
|
|
340
|
+
// Validazione e clamping di sicurezza (non si applica all'oggetto TWD)
|
|
341
|
+
if (!isTwdType) {
|
|
342
|
+
if (!isFinite(finalValue)) return;
|
|
343
|
+
finalValue = Math.max(0, finalValue);
|
|
344
|
+
histories[type].push({ val: finalValue, time: now });
|
|
354
345
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
346
|
+
// EMISSIONE DEL DELTA: Se abbiamo calcolato il TWS di fallback, lo trasmettiamo a Signal K
|
|
347
|
+
if (type === 'tws' && (now - lastNativeTwsTime > 5000)) {
|
|
348
|
+
emitDelta('environment.wind.speedTrue', finalValue / 1.94384); // Converte nodi in m/s
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
// Salvataggio specifico del TWD contenente l'oggetto { val, min, max, time }
|
|
352
|
+
histories['twd'].push({
|
|
353
|
+
val: finalValue.val,
|
|
354
|
+
min: finalValue.min,
|
|
355
|
+
max: finalValue.max,
|
|
356
|
+
time: now
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// EMISSIONE DEL DELTA: Se abbiamo calcolato il TWD di fallback, lo trasmettiamo a Signal K
|
|
360
|
+
if (now - lastNativeTwdTime > 5000) {
|
|
361
|
+
emitDelta('environment.wind.directionTrue', {
|
|
362
|
+
val: finalValue.val,
|
|
363
|
+
min: finalValue.min,
|
|
364
|
+
max: finalValue.max
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// --- TRIGGER DI CONGELAMENTO ARCO (Ogni :00 e :30 dell'orologio) ---
|
|
369
|
+
const current30mSlot = Math.floor(now / 1800000) * 1800000;
|
|
370
|
+
if (lastFrozen30mSlot === 0) {
|
|
371
|
+
lastFrozen30mSlot = current30mSlot; // Inizializzazione al primo avvio
|
|
372
|
+
} else if (current30mSlot > lastFrozen30mSlot) {
|
|
373
|
+
// Lo slot da 30 minuti precedente (lastFrozen30mSlot) si è appena concluso!
|
|
374
|
+
// Avviamo la procedura di compressione e congelamento dei dati di quel periodo
|
|
375
|
+
freeze30mSlot(lastFrozen30mSlot);
|
|
376
|
+
lastFrozen30mSlot = current30mSlot;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
366
379
|
|
|
367
380
|
// Pruning automatico basato sulle impostazioni di timeline
|
|
368
381
|
// (Forziamo il server a conservare sempre almeno 60 minuti per il TWD!)
|
|
@@ -731,5 +744,29 @@ module.exports = function (app) {
|
|
|
731
744
|
}
|
|
732
745
|
}
|
|
733
746
|
|
|
747
|
+
/**
|
|
748
|
+
* emitDelta: Scrive ed emette un aggiornamento di rotta direttamente nel
|
|
749
|
+
* server principale di Signal K per renderlo disponibile a tutti i client WebSocket.
|
|
750
|
+
*/
|
|
751
|
+
function emitDelta(path, value) {
|
|
752
|
+
if (typeof app.handleMessage === 'function') {
|
|
753
|
+
app.handleMessage(plugin.id, {
|
|
754
|
+
context: 'vessels.self', // BUG RISOLTO: Inserito il contesto Signal K per evitare lo scarto del delta
|
|
755
|
+
updates: [
|
|
756
|
+
{
|
|
757
|
+
source: { label: 'rotevista-dash-plugin' },
|
|
758
|
+
timestamp: new Date().toISOString(),
|
|
759
|
+
values: [
|
|
760
|
+
{
|
|
761
|
+
path: path,
|
|
762
|
+
value: value
|
|
763
|
+
}
|
|
764
|
+
]
|
|
765
|
+
}
|
|
766
|
+
]
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
734
771
|
return plugin;
|
|
735
772
|
};
|
package/package.json
CHANGED
package/radar.html
CHANGED
|
@@ -311,6 +311,16 @@
|
|
|
311
311
|
document.getElementById('debug-tws').innerText = tws.toFixed(1);
|
|
312
312
|
document.getElementById('debug-twa').innerText = Math.round(radToDeg(twa)) + '°';
|
|
313
313
|
document.getElementById('debug-twd').innerText = Math.round(radToDeg(twd)) + '°';
|
|
314
|
+
|
|
315
|
+
// --- ACCUMULO DATI REALI LOCALE (1Hz) ---
|
|
316
|
+
// Popoliamo i buffer locali ogni secondo per garantire il disegno immediato del Presente
|
|
317
|
+
store.twdMinuteBuffer.push({ val: twd, min: twd, max: twd, time: now });
|
|
318
|
+
while(store.twdMinuteBuffer.length > 1800) store.twdMinuteBuffer.shift(); // conserva 30 minuti a 1Hz
|
|
319
|
+
|
|
320
|
+
store.twsMinuteBuffer.push({ val: tws, time: now });
|
|
321
|
+
while(store.twsMinuteBuffer.length > 1800) store.twsMinuteBuffer.shift();
|
|
322
|
+
|
|
323
|
+
renderRadar(); // Rinfresco grafico istantaneo reattivo
|
|
314
324
|
}
|
|
315
325
|
}
|
|
316
326
|
|
|
@@ -352,7 +362,7 @@
|
|
|
352
362
|
document.getElementById('debug-gps').innerText = val.latitude.toFixed(4) + '; ' + val.longitude.toFixed(4);
|
|
353
363
|
}
|
|
354
364
|
|
|
355
|
-
//
|
|
365
|
+
// Sincronizzazione automatica allineata alle chiavi REST storiche reali del server
|
|
356
366
|
if (data.twd) store.twdMinuteBuffer = data.twd;
|
|
357
367
|
if (data.tws) store.twsMinuteBuffer = data.tws;
|
|
358
368
|
}
|
|
@@ -388,7 +398,7 @@
|
|
|
388
398
|
u.values.forEach(v => {
|
|
389
399
|
processIncomingDelta(v.path, v.value, sourceLabel, timeMs);
|
|
390
400
|
|
|
391
|
-
//
|
|
401
|
+
// Ricezione live delle emissioni delta del nostro plugin server
|
|
392
402
|
if (v.path === 'environment.wind.directionTrue') {
|
|
393
403
|
let val, min, max;
|
|
394
404
|
if (v.value && typeof v.value === 'object' && v.value.val !== undefined) {
|
|
@@ -678,7 +688,7 @@
|
|
|
678
688
|
const radius = ringRadii[index];
|
|
679
689
|
const gradId = `chord-gradient-${index}`;
|
|
680
690
|
|
|
681
|
-
//
|
|
691
|
+
// Opacità sempre fissa a 1 per colore solido e brillante (BUG RISOLTO)
|
|
682
692
|
const opacityValue = 1;
|
|
683
693
|
|
|
684
694
|
// Caso A: Calma Piatta
|
package/rarad_test.html
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html lang="it">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Signal K Wind Radar - Server RAM Diagnostic Panel</title>
|
|
8
|
+
<style>
|
|
9
|
+
body {
|
|
10
|
+
background-color: #0c0c0c;
|
|
11
|
+
color: #00ff66;
|
|
12
|
+
font-family: 'Courier New', Courier, monospace;
|
|
13
|
+
padding: 20px;
|
|
14
|
+
margin: 0;
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
h1 {
|
|
19
|
+
color: #ffffff;
|
|
20
|
+
font-size: 1.4rem;
|
|
21
|
+
border-bottom: 2px solid #00ff66;
|
|
22
|
+
padding-bottom: 10px;
|
|
23
|
+
margin-top: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.controls {
|
|
27
|
+
background: #111;
|
|
28
|
+
border: 1px solid #333;
|
|
29
|
+
padding: 15px;
|
|
30
|
+
border-radius: 8px;
|
|
31
|
+
margin-bottom: 20px;
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-wrap: wrap;
|
|
34
|
+
gap: 15px;
|
|
35
|
+
align-items: center;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
input[type="text"] {
|
|
39
|
+
background: #000;
|
|
40
|
+
color: #00ff66;
|
|
41
|
+
border: 1px solid #00ff66;
|
|
42
|
+
padding: 8px 12px;
|
|
43
|
+
font-family: monospace;
|
|
44
|
+
border-radius: 4px;
|
|
45
|
+
width: 250px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
button {
|
|
49
|
+
background: #00ff66;
|
|
50
|
+
color: #000000;
|
|
51
|
+
border: none;
|
|
52
|
+
padding: 8px 15px;
|
|
53
|
+
font-weight: bold;
|
|
54
|
+
font-family: monospace;
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
border-radius: 4px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
button:hover {
|
|
60
|
+
background: #ffffff;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.status-box {
|
|
64
|
+
font-size: 13px;
|
|
65
|
+
line-height: 1.5;
|
|
66
|
+
margin-bottom: 20px;
|
|
67
|
+
padding: 15px;
|
|
68
|
+
background: #141414;
|
|
69
|
+
border: 1px solid #222;
|
|
70
|
+
border-radius: 8px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.grid {
|
|
74
|
+
display: grid;
|
|
75
|
+
grid-template-columns: repeat(auto-fit, minmax(45%, 1fr));
|
|
76
|
+
gap: 20px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.panel {
|
|
80
|
+
background: #111;
|
|
81
|
+
border: 1px solid #333;
|
|
82
|
+
border-radius: 8px;
|
|
83
|
+
padding: 15px;
|
|
84
|
+
box-sizing: border-box;
|
|
85
|
+
display: flex;
|
|
86
|
+
flex-direction: column;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.panel-title {
|
|
90
|
+
color: #ffffff;
|
|
91
|
+
font-weight: bold;
|
|
92
|
+
font-size: 1.1rem;
|
|
93
|
+
margin-bottom: 10px;
|
|
94
|
+
border-bottom: 1px solid #222;
|
|
95
|
+
padding-bottom: 5px;
|
|
96
|
+
text-transform: uppercase;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
pre {
|
|
100
|
+
background: #000000;
|
|
101
|
+
border: 1px solid #222;
|
|
102
|
+
padding: 10px;
|
|
103
|
+
border-radius: 4px;
|
|
104
|
+
overflow: auto;
|
|
105
|
+
max-height: 400px;
|
|
106
|
+
font-size: 11px;
|
|
107
|
+
color: #00ff66;
|
|
108
|
+
margin: 0;
|
|
109
|
+
box-sizing: border-box;
|
|
110
|
+
flex-grow: 1;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.success { color: #00ff66; }
|
|
114
|
+
.danger { color: #ff3333; }
|
|
115
|
+
.warning { color: #ffcc00; }
|
|
116
|
+
</style>
|
|
117
|
+
</head>
|
|
118
|
+
<body>
|
|
119
|
+
|
|
120
|
+
<h1>Signal K Wind Radar - Server RAM Diagnostic Panel (v6.0)</h1>
|
|
121
|
+
|
|
122
|
+
<!-- PANNELLO DI CONTROLLO IP -->
|
|
123
|
+
<div class="controls">
|
|
124
|
+
<label>Indirizzo IP Server SK:</label>
|
|
125
|
+
<input type="text" id="server-ip" value="192.168.111.240:3000">
|
|
126
|
+
<button onclick="runManualDiagnostic()">Esegui Scansione Ora</button>
|
|
127
|
+
<span id="diagnostic-running" style="display: none; color: #ffcc00;">SCANSIONE IN CORSO...</span>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<!-- STATO DELLE RISPOSTE DEL SERVER -->
|
|
131
|
+
<div class="status-box">
|
|
132
|
+
<div style="font-weight: bold; font-size: 15px; margin-bottom: 8px; color: #fff;">Sintesi Connessione REST:</div>
|
|
133
|
+
<div>• Endpoint Configurazione (/rotevista-config): <span id="status-config" class="warning">IN ATTESA...</span></div>
|
|
134
|
+
<div>• Endpoint Storico RAM (/rotevista-history): <span id="status-history" class="warning">IN ATTESA...</span></div>
|
|
135
|
+
<div style="margin-top: 10px; border-top: 1px dashed #222; padding-top: 10px;">
|
|
136
|
+
• Picchi Vento in RAM (histories.tws): <span id="stat-tws" class="warning">--</span> |
|
|
137
|
+
• Direzioni Vento in RAM (histories.twd): <span id="stat-twd" class="warning">--</span> |
|
|
138
|
+
• Archi Bussola in RAM (windRadarSlots): <span id="stat-slots" class="warning">--</span>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<!-- GRIGLIA DUMP MEMORIA GREZZA -->
|
|
143
|
+
<div class="grid">
|
|
144
|
+
|
|
145
|
+
<!-- PANNELLO SINISTRO: DUMP CONFIG -->
|
|
146
|
+
<div class="panel">
|
|
147
|
+
<div class="panel-title">Raw Config Dump (/rotevista-config)</div>
|
|
148
|
+
<pre id="dump-config">In attesa di scansione...</pre>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<!-- PANNELLO DESTRO: DUMP COSTRUZIONE STORICO -->
|
|
152
|
+
<div class="panel">
|
|
153
|
+
<div class="panel-title">Raw History & Radar Dump (/rotevista-history)</div>
|
|
154
|
+
<pre id="dump-history">In attesa di scansione...</pre>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<script>
|
|
160
|
+
// Estrapola l'indirizzo dinamico dall'URL della barra indirizzi se caricato dal server
|
|
161
|
+
window.onload = function() {
|
|
162
|
+
let addr = window.location.host || "192.168.111.240:3000";
|
|
163
|
+
document.getElementById('server-ip').value = addr;
|
|
164
|
+
runManualDiagnostic();
|
|
165
|
+
// Esegue un poll diagnostico automatico ogni 5 secondi
|
|
166
|
+
setInterval(runManualDiagnostic, 5000);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
function getTargetUrl(path) {
|
|
170
|
+
const ip = document.getElementById('server-ip').value;
|
|
171
|
+
return `http://${ip}${path}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function runManualDiagnostic() {
|
|
175
|
+
document.getElementById('diagnostic-running').style.display = 'inline';
|
|
176
|
+
|
|
177
|
+
const configPre = document.getElementById('dump-config');
|
|
178
|
+
const historyPre = document.getElementById('dump-history');
|
|
179
|
+
|
|
180
|
+
const statusConfig = document.getElementById('status-config');
|
|
181
|
+
const statusHistory = document.getElementById('status-history');
|
|
182
|
+
|
|
183
|
+
// 1. SCANSIONE /rotevista-config
|
|
184
|
+
try {
|
|
185
|
+
const resConfig = await fetch(getTargetUrl('/rotevista-config'));
|
|
186
|
+
if (resConfig.ok) {
|
|
187
|
+
const data = await resConfig.json();
|
|
188
|
+
configPre.innerText = JSON.stringify(data, null, 2);
|
|
189
|
+
statusConfig.innerHTML = `<span class="success">RISPOSTA OK (200) - Configurazione Caricata</span>`;
|
|
190
|
+
} else {
|
|
191
|
+
statusConfig.innerHTML = `<span class="danger">ERRORE SERVER (${resConfig.status})</span>`;
|
|
192
|
+
configPre.innerText = "Il server ha risposto con un codice di errore.";
|
|
193
|
+
}
|
|
194
|
+
} catch (err) {
|
|
195
|
+
statusConfig.innerHTML = `<span class="danger">ERRORE DI RETE (Impossibile raggiungere il server)</span>`;
|
|
196
|
+
configPre.innerText = `Errore di connessione:\n${err.message}\n\nConsiglio:\n1. Verifica che l'IP del Cerbo sia corretto.\n2. Verifica che il tablet sia connesso alla rete Wi-Fi della barca.`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 2. SCANSIONE /rotevista-history
|
|
200
|
+
try {
|
|
201
|
+
const resHistory = await fetch(getTargetUrl('/rotevista-history'));
|
|
202
|
+
if (resHistory.ok) {
|
|
203
|
+
const data = await resHistory.json();
|
|
204
|
+
historyPre.innerText = JSON.stringify(data, null, 2);
|
|
205
|
+
statusHistory.innerHTML = `<span class="success">RISPOSTA OK (200) - Storico Caricato</span>`;
|
|
206
|
+
|
|
207
|
+
// Mostra statistiche dei record in RAM sul server
|
|
208
|
+
const countTws = data.tws ? data.tws.length : 0;
|
|
209
|
+
const countTwd = data.twd ? data.twd.length : 0;
|
|
210
|
+
const countSlots = data.windRadarSlots ? data.windRadarSlots.length : 0;
|
|
211
|
+
|
|
212
|
+
document.getElementById('stat-tws').innerHTML = `<span class="success">${countTws} punti</span>`;
|
|
213
|
+
document.getElementById('stat-twd').innerHTML = `<span class="success">${countTwd} punti</span>`;
|
|
214
|
+
document.getElementById('stat-slots').innerHTML = `<span class="success">${countSlots}/12 congelati</span>`;
|
|
215
|
+
|
|
216
|
+
} else {
|
|
217
|
+
statusHistory.innerHTML = `<span class="danger">ERRORE SERVER (${resHistory.status})</span>`;
|
|
218
|
+
historyPre.innerText = "Il server ha risposto con un codice di errore.";
|
|
219
|
+
}
|
|
220
|
+
} catch (err) {
|
|
221
|
+
statusHistory.innerHTML = `<span class="danger">ERRORE DI RETE (Impossibile raggiungere lo storico)</span>`;
|
|
222
|
+
historyPre.innerText = `Errore di connessione:\n${err.message}`;
|
|
223
|
+
|
|
224
|
+
document.getElementById('stat-tws').innerHTML = `<span class="danger">--</span>`;
|
|
225
|
+
document.getElementById('stat-twd').innerHTML = `<span class="danger">--</span>`;
|
|
226
|
+
document.getElementById('stat-slots').innerHTML = `<span class="danger">--</span>`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
document.getElementById('diagnostic-running').style.display = 'none';
|
|
230
|
+
}
|
|
231
|
+
</script>
|
|
232
|
+
</body>
|
|
233
|
+
</html>
|