@sailingrotevista/rotevista-dash 4.0.13 → 4.0.15
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 +44 -46
- package/package.json +1 -1
package/app.js
CHANGED
|
@@ -12,36 +12,23 @@
|
|
|
12
12
|
// 1. CONFIGURAZIONE E DEFAULT
|
|
13
13
|
// ==========================================================================
|
|
14
14
|
let CONFIG = {
|
|
15
|
-
alarms: {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
longWindow: 30000, // Finestra per i valori MEAN (30s)
|
|
23
|
-
stabilityTolerance: 2000, // Millisecondi per considerare il buffer "pieno"
|
|
24
|
-
stabilityThreshold: 0.85, // Soglia coerenza R per il lampeggio (0.7 - 0.98)
|
|
25
|
-
minSpeed: 0, // Nodi minimi per attivare gli allarmi di instabilità
|
|
15
|
+
alarms: { depthDanger: 2.5, depthWarning: 5.0 },
|
|
16
|
+
averaging: {
|
|
17
|
+
smoothWindow: 2000,
|
|
18
|
+
longWindow: 30000,
|
|
19
|
+
stabilityTolerance: 2000,
|
|
20
|
+
stabilityThreshold: 0.85,
|
|
21
|
+
minSpeed: 0.5,
|
|
26
22
|
stabilityBreakout: 15
|
|
27
23
|
},
|
|
28
|
-
|
|
29
|
-
graphs: {
|
|
30
|
-
reef1: 15.0, // Soglia primo reef (Orange)
|
|
31
|
-
reef2: 20.0, // Soglia secondo reef (Red)
|
|
32
|
-
historyMinutes: 5, // Finestra temporale visualizzata
|
|
33
|
-
samples: 60 // Numero di punti di campionamento
|
|
34
|
-
},
|
|
35
|
-
// Configurazioni scale automatiche
|
|
24
|
+
graphs: { reef1: 15.0, reef2: 20.0, historyMinutes: 5, samples: 60 },
|
|
36
25
|
scales: {
|
|
37
26
|
stw: { stdMax: 12, hercSpan: 4, step: 2 },
|
|
38
27
|
sog: { stdMax: 12, hercSpan: 4, step: 2 },
|
|
39
28
|
tws: { stdMax: 25, hercSpan: 10, step: 5 },
|
|
40
29
|
depth: { stdMax: 20, hercSpan: 10, step: 10 }
|
|
41
30
|
},
|
|
42
|
-
server: {
|
|
43
|
-
fallbackIp: "192.168.111.240:3000"
|
|
44
|
-
}
|
|
31
|
+
server: { fallbackIp: "192.168.111.240:3000" }
|
|
45
32
|
};
|
|
46
33
|
|
|
47
34
|
const RENDER_INTERVAL_MS = 1000;
|
|
@@ -146,7 +133,7 @@ function getCircularAverageFromBuffer(bufferArray, windowMs, signed = false) {
|
|
|
146
133
|
let historyDuration = (validData.length > 2) ? (validData[validData.length - 1].time - validData[0].time) : 0;
|
|
147
134
|
|
|
148
135
|
// Un dato è stabile se abbiamo abbastanza storia e la coerenza vettoriale R è alta
|
|
149
|
-
let isStable = (historyDuration > 10000) && (R > CONFIG.
|
|
136
|
+
let isStable = (historyDuration > 10000) && (R > CONFIG.averaging.stabilityThreshold);
|
|
150
137
|
let avgRad = Math.atan2(sSin, sCos);
|
|
151
138
|
|
|
152
139
|
// Calcolo della Deviazione Standard Circolare (±) in gradi
|
|
@@ -401,7 +388,7 @@ const upUI = (el, obj, instantRaw, isCompass = false) => {
|
|
|
401
388
|
|
|
402
389
|
let diff = Math.abs((radToDeg(instantRaw) - radToDeg(obj.val) + 540) % 360 - 180);
|
|
403
390
|
// Allarme lampeggio solo se in navigazione E (R bassa O deviazione alta O salto istantaneo brusco)
|
|
404
|
-
if (isNavigating && (!obj.stable || obj.dev > CONFIG.
|
|
391
|
+
if (isNavigating && (!obj.stable || obj.dev > CONFIG.averaging.stabilityBreakout || diff > CONFIG.averaging.stabilityBreakout)) el.classList.add('unstable-data');
|
|
405
392
|
else el.classList.remove('unstable-data');
|
|
406
393
|
}
|
|
407
394
|
};
|
|
@@ -420,7 +407,7 @@ function startDisplayLoop() {
|
|
|
420
407
|
const sogKts = msToKts(store.raw["navigation.speedOverGround"] || 0);
|
|
421
408
|
|
|
422
409
|
// Verifica stato navigazione basato su soglia impostata
|
|
423
|
-
isNavigating = stwKts > CONFIG.
|
|
410
|
+
isNavigating = stwKts > CONFIG.averaging.minSpeed || sogKts > CONFIG.averaging.minSpeed;
|
|
424
411
|
|
|
425
412
|
// --- TIER LIVE (1s): CONTROLLO TIMEOUT DATI ---
|
|
426
413
|
const watch = { "navigation.speedThroughWater": ui.stw, "navigation.speedOverGround": ui.sog, "navigation.headingTrue": ui.hdg, "navigation.courseOverGroundTrue": ui.cog, "environment.wind.speedApparent": ui.awsSvg, "environment.depth.belowTransducer": ui.depth, "environment.wind.speedTrue": ui.tws };
|
|
@@ -488,7 +475,7 @@ function startDisplayLoop() {
|
|
|
488
475
|
if (store.raw["navigation.courseOverGroundTrue"] !== undefined && store.raw["navigation.headingTrue"] !== undefined) {
|
|
489
476
|
let driftDeg = radToDeg((store.raw["navigation.courseOverGroundTrue"] - store.raw["navigation.headingTrue"] + Math.PI * 3) % (Math.PI * 2) - Math.PI);
|
|
490
477
|
// Azzeramento sotto soglia minima impostata
|
|
491
|
-
smoothedLeeway = (sogKts < CONFIG.
|
|
478
|
+
smoothedLeeway = (sogKts < CONFIG.averaging.minSpeed) ? 0 : (smoothedLeeway * 0.9) + (driftDeg * 0.1);
|
|
492
479
|
curTrackRot = getShortestRotation(curTrackRot, smoothedLeeway); ui.track.setAttribute('transform', `rotate(${curTrackRot}, 200, 200)`);
|
|
493
480
|
ui.leewayVal.style.color = (Math.abs(sogKts - stwKts) > 0.5 && Math.abs(smoothedLeeway) > 7) ? "#e67e22" : "";
|
|
494
481
|
updateLeewayDisplay(Math.max(-20, Math.min(20, smoothedLeeway)));
|
|
@@ -504,11 +491,11 @@ function startDisplayLoop() {
|
|
|
504
491
|
|
|
505
492
|
// TIER SLOW (3s) - Medie Lunghe e Calcolo TACK
|
|
506
493
|
if (lastAvgUIUpdate % 3 === 0) {
|
|
507
|
-
let hObj = getCircularAverageFromBuffer(store.longBuf.hdg, CONFIG.
|
|
508
|
-
let cObj = getCircularAverageFromBuffer(store.longBuf.cog, CONFIG.
|
|
509
|
-
let awObj = getCircularAverageFromBuffer(store.longBuf.awa, CONFIG.
|
|
510
|
-
let twObj = getCircularAverageFromBuffer(store.longBuf.twa, CONFIG.
|
|
511
|
-
let twdObj = getCircularAverageFromBuffer(store.longBuf.twd, CONFIG.
|
|
494
|
+
let hObj = getCircularAverageFromBuffer(store.longBuf.hdg, CONFIG.averaging.longWindow * 2, false);
|
|
495
|
+
let cObj = getCircularAverageFromBuffer(store.longBuf.cog, CONFIG.averaging.longWindow, false);
|
|
496
|
+
let awObj = getCircularAverageFromBuffer(store.longBuf.awa, CONFIG.averaging.longWindow, true);
|
|
497
|
+
let twObj = getCircularAverageFromBuffer(store.longBuf.twa, CONFIG.averaging.longWindow, true);
|
|
498
|
+
let twdObj = getCircularAverageFromBuffer(store.longBuf.twd, CONFIG.averaging.longWindow, false);
|
|
512
499
|
|
|
513
500
|
upUI(ui.hdg, hObj, store.raw["navigation.headingTrue"], true);
|
|
514
501
|
upUI(ui.cog, cObj, store.raw["navigation.courseOverGroundTrue"], true);
|
|
@@ -519,7 +506,7 @@ function startDisplayLoop() {
|
|
|
519
506
|
// --- LOGICA TACK STRATEGICA (Riflessione geometrica su TWD) ---
|
|
520
507
|
if (hObj && twdObj) {
|
|
521
508
|
const tH = radToDeg((2 * twdObj.val - hObj.val + Math.PI * 2) % (Math.PI * 2));
|
|
522
|
-
const unstableH = !hObj.stable || !twdObj.stable || hObj.dev > CONFIG.
|
|
509
|
+
const unstableH = !hObj.stable || !twdObj.stable || hObj.dev > CONFIG.averaging.stabilityBreakout || twdObj.dev > CONFIG.averaging.stabilityBreakout;
|
|
523
510
|
|
|
524
511
|
if (!isNavigating) {
|
|
525
512
|
ui.tackHdg.innerHTML = "---°"; ui.tackHdg.classList.remove('unstable-data');
|
|
@@ -532,7 +519,7 @@ function startDisplayLoop() {
|
|
|
532
519
|
|
|
533
520
|
if (cObj) {
|
|
534
521
|
const tC = radToDeg((2 * twdObj.val - cObj.val + Math.PI * 2) % (Math.PI * 2));
|
|
535
|
-
const unstableC = !cObj.stable || !twdObj.stable || cObj.dev > CONFIG.
|
|
522
|
+
const unstableC = !cObj.stable || !twdObj.stable || cObj.dev > CONFIG.averaging.stabilityBreakout || twdObj.dev > CONFIG.averaging.stabilityBreakout;
|
|
536
523
|
|
|
537
524
|
if (!isNavigating) {
|
|
538
525
|
ui.tackCog.innerHTML = "---°"; ui.tackCog.classList.remove('unstable-data');
|
|
@@ -560,37 +547,48 @@ function startDisplayLoop() {
|
|
|
560
547
|
// 8. CONFIGURAZIONE E GRAFICI UTILS
|
|
561
548
|
// ==========================================================================
|
|
562
549
|
/**
|
|
563
|
-
* Recupera la configurazione
|
|
564
|
-
* Questo bypassa i blocchi di sicurezza standard di Signal K.
|
|
550
|
+
* Recupera la configurazione e forza la sovrascrittura di ogni parametro.
|
|
565
551
|
*/
|
|
566
552
|
async function fetchServerConfig() {
|
|
567
553
|
try {
|
|
568
554
|
const response = await fetch('/rotevista-config');
|
|
569
|
-
if (!response.ok) throw new Error(`
|
|
570
|
-
|
|
555
|
+
if (!response.ok) throw new Error(`Server offline o rotta non trovata`);
|
|
571
556
|
const data = await response.json();
|
|
572
557
|
|
|
573
|
-
// Funzione di
|
|
558
|
+
// 1. Funzione di pulizia: trasforma stringhe in numeri
|
|
574
559
|
const parse = (obj) => {
|
|
575
560
|
for (let k in obj) {
|
|
576
|
-
if (typeof obj[k] === 'object') parse(obj[k]);
|
|
561
|
+
if (typeof obj[k] === 'object' && obj[k] !== null) parse(obj[k]);
|
|
577
562
|
else if (!isNaN(obj[k]) && typeof obj[k] === 'string' && obj[k] !== "")
|
|
578
563
|
obj[k] = parseFloat(obj[k]);
|
|
579
564
|
}
|
|
580
565
|
return obj;
|
|
581
566
|
};
|
|
582
|
-
|
|
583
567
|
const actual = parse(data);
|
|
584
568
|
|
|
585
|
-
//
|
|
569
|
+
// 2. Mappatura Forzata (Deep Merge)
|
|
586
570
|
if (actual.alarms) Object.assign(CONFIG.alarms, actual.alarms);
|
|
587
|
-
|
|
588
|
-
if (actual.
|
|
589
|
-
|
|
571
|
+
|
|
572
|
+
if (actual.graphs) {
|
|
573
|
+
Object.assign(CONFIG.graphs, actual.graphs);
|
|
574
|
+
console.log(`📈 GRAFICI: Durata ${CONFIG.graphs.historyMinutes}m | Reef1: ${CONFIG.graphs.reef1}kts | Reef2: ${CONFIG.graphs.reef2}kts`);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (actual.averaging) {
|
|
578
|
+
Object.assign(CONFIG.averaging, actual.averaging);
|
|
579
|
+
console.log(`⏱️ STABILITÀ: MinSpeed ${CONFIG.averaging.minSpeed}kts | Breakout: ${CONFIG.averaging.stabilityBreakout}°`);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (actual.scales) {
|
|
583
|
+
// Per le scale facciamo un merge profondo per ogni box
|
|
584
|
+
for (let key in actual.scales) {
|
|
585
|
+
if (CONFIG.scales[key]) Object.assign(CONFIG.scales[key], actual.scales[key]);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
590
588
|
|
|
591
|
-
console.log("✅ Configurazione sincronizzata
|
|
589
|
+
console.log("✅ Configurazione sincronizzata con successo.");
|
|
592
590
|
} catch (err) {
|
|
593
|
-
console.warn("⚠️ Utilizzo default locali
|
|
591
|
+
console.warn("⚠️ Utilizzo default locali. Motivo:", err.message);
|
|
594
592
|
}
|
|
595
593
|
}
|
|
596
594
|
|