@sailingrotevista/rotevista-dash 4.0.12 → 4.0.14
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 +47 -35
- package/index.js +21 -6
- package/package.json +1 -1
package/app.js
CHANGED
|
@@ -17,7 +17,7 @@ let CONFIG = {
|
|
|
17
17
|
depthWarning: 5.0
|
|
18
18
|
},
|
|
19
19
|
// Gestione parametri di stabilità e medie
|
|
20
|
-
|
|
20
|
+
averaging: {
|
|
21
21
|
smoothWindow: 2000, // Smoothing puntatori (2s)
|
|
22
22
|
longWindow: 30000, // Finestra per i valori MEAN (30s)
|
|
23
23
|
stabilityTolerance: 2000, // Millisecondi per considerare il buffer "pieno"
|
|
@@ -146,7 +146,7 @@ function getCircularAverageFromBuffer(bufferArray, windowMs, signed = false) {
|
|
|
146
146
|
let historyDuration = (validData.length > 2) ? (validData[validData.length - 1].time - validData[0].time) : 0;
|
|
147
147
|
|
|
148
148
|
// Un dato è stabile se abbiamo abbastanza storia e la coerenza vettoriale R è alta
|
|
149
|
-
let isStable = (historyDuration > 10000) && (R > CONFIG.
|
|
149
|
+
let isStable = (historyDuration > 10000) && (R > CONFIG.averaging.stabilityThreshold);
|
|
150
150
|
let avgRad = Math.atan2(sSin, sCos);
|
|
151
151
|
|
|
152
152
|
// Calcolo della Deviazione Standard Circolare (±) in gradi
|
|
@@ -401,7 +401,7 @@ const upUI = (el, obj, instantRaw, isCompass = false) => {
|
|
|
401
401
|
|
|
402
402
|
let diff = Math.abs((radToDeg(instantRaw) - radToDeg(obj.val) + 540) % 360 - 180);
|
|
403
403
|
// Allarme lampeggio solo se in navigazione E (R bassa O deviazione alta O salto istantaneo brusco)
|
|
404
|
-
if (isNavigating && (!obj.stable || obj.dev > CONFIG.
|
|
404
|
+
if (isNavigating && (!obj.stable || obj.dev > CONFIG.averaging.stabilityBreakout || diff > CONFIG.averaging.stabilityBreakout)) el.classList.add('unstable-data');
|
|
405
405
|
else el.classList.remove('unstable-data');
|
|
406
406
|
}
|
|
407
407
|
};
|
|
@@ -420,7 +420,7 @@ function startDisplayLoop() {
|
|
|
420
420
|
const sogKts = msToKts(store.raw["navigation.speedOverGround"] || 0);
|
|
421
421
|
|
|
422
422
|
// Verifica stato navigazione basato su soglia impostata
|
|
423
|
-
isNavigating = stwKts > CONFIG.
|
|
423
|
+
isNavigating = stwKts > CONFIG.averaging.minSpeed || sogKts > CONFIG.averaging.minSpeed;
|
|
424
424
|
|
|
425
425
|
// --- TIER LIVE (1s): CONTROLLO TIMEOUT DATI ---
|
|
426
426
|
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 +488,7 @@ function startDisplayLoop() {
|
|
|
488
488
|
if (store.raw["navigation.courseOverGroundTrue"] !== undefined && store.raw["navigation.headingTrue"] !== undefined) {
|
|
489
489
|
let driftDeg = radToDeg((store.raw["navigation.courseOverGroundTrue"] - store.raw["navigation.headingTrue"] + Math.PI * 3) % (Math.PI * 2) - Math.PI);
|
|
490
490
|
// Azzeramento sotto soglia minima impostata
|
|
491
|
-
smoothedLeeway = (sogKts < CONFIG.
|
|
491
|
+
smoothedLeeway = (sogKts < CONFIG.averaging.minSpeed) ? 0 : (smoothedLeeway * 0.9) + (driftDeg * 0.1);
|
|
492
492
|
curTrackRot = getShortestRotation(curTrackRot, smoothedLeeway); ui.track.setAttribute('transform', `rotate(${curTrackRot}, 200, 200)`);
|
|
493
493
|
ui.leewayVal.style.color = (Math.abs(sogKts - stwKts) > 0.5 && Math.abs(smoothedLeeway) > 7) ? "#e67e22" : "";
|
|
494
494
|
updateLeewayDisplay(Math.max(-20, Math.min(20, smoothedLeeway)));
|
|
@@ -504,11 +504,11 @@ function startDisplayLoop() {
|
|
|
504
504
|
|
|
505
505
|
// TIER SLOW (3s) - Medie Lunghe e Calcolo TACK
|
|
506
506
|
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.
|
|
507
|
+
let hObj = getCircularAverageFromBuffer(store.longBuf.hdg, CONFIG.averaging.longWindow * 2, false);
|
|
508
|
+
let cObj = getCircularAverageFromBuffer(store.longBuf.cog, CONFIG.averaging.longWindow, false);
|
|
509
|
+
let awObj = getCircularAverageFromBuffer(store.longBuf.awa, CONFIG.averaging.longWindow, true);
|
|
510
|
+
let twObj = getCircularAverageFromBuffer(store.longBuf.twa, CONFIG.averaging.longWindow, true);
|
|
511
|
+
let twdObj = getCircularAverageFromBuffer(store.longBuf.twd, CONFIG.averaging.longWindow, false);
|
|
512
512
|
|
|
513
513
|
upUI(ui.hdg, hObj, store.raw["navigation.headingTrue"], true);
|
|
514
514
|
upUI(ui.cog, cObj, store.raw["navigation.courseOverGroundTrue"], true);
|
|
@@ -519,7 +519,7 @@ function startDisplayLoop() {
|
|
|
519
519
|
// --- LOGICA TACK STRATEGICA (Riflessione geometrica su TWD) ---
|
|
520
520
|
if (hObj && twdObj) {
|
|
521
521
|
const tH = radToDeg((2 * twdObj.val - hObj.val + Math.PI * 2) % (Math.PI * 2));
|
|
522
|
-
const unstableH = !hObj.stable || !twdObj.stable || hObj.dev > CONFIG.
|
|
522
|
+
const unstableH = !hObj.stable || !twdObj.stable || hObj.dev > CONFIG.averaging.stabilityBreakout || twdObj.dev > CONFIG.averaging.stabilityBreakout;
|
|
523
523
|
|
|
524
524
|
if (!isNavigating) {
|
|
525
525
|
ui.tackHdg.innerHTML = "---°"; ui.tackHdg.classList.remove('unstable-data');
|
|
@@ -532,7 +532,7 @@ function startDisplayLoop() {
|
|
|
532
532
|
|
|
533
533
|
if (cObj) {
|
|
534
534
|
const tC = radToDeg((2 * twdObj.val - cObj.val + Math.PI * 2) % (Math.PI * 2));
|
|
535
|
-
const unstableC = !cObj.stable || !twdObj.stable || cObj.dev > CONFIG.
|
|
535
|
+
const unstableC = !cObj.stable || !twdObj.stable || cObj.dev > CONFIG.averaging.stabilityBreakout || twdObj.dev > CONFIG.averaging.stabilityBreakout;
|
|
536
536
|
|
|
537
537
|
if (!isNavigating) {
|
|
538
538
|
ui.tackCog.innerHTML = "---°"; ui.tackCog.classList.remove('unstable-data');
|
|
@@ -559,45 +559,57 @@ function startDisplayLoop() {
|
|
|
559
559
|
// ==========================================================================
|
|
560
560
|
// 8. CONFIGURAZIONE E GRAFICI UTILS
|
|
561
561
|
// ==========================================================================
|
|
562
|
-
|
|
563
|
-
/**
|
|
564
|
-
* Recupera la configurazione dal server Signal K.
|
|
565
|
-
* Prova i percorsi API ufficiali e quelli scoped (@sailingrotevista).
|
|
566
|
-
* Converte i testi in numeri per garantire la precisione dei calcoli.
|
|
567
|
-
*/
|
|
568
562
|
/**
|
|
569
|
-
* Recupera la configurazione dal server
|
|
570
|
-
* Utilizza un approccio semplificato e converte i dati in numeri reali.
|
|
563
|
+
* Recupera la configurazione dal server con log di debug estesi.
|
|
571
564
|
*/
|
|
572
565
|
async function fetchServerConfig() {
|
|
573
566
|
try {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
567
|
+
const response = await fetch('/rotevista-config');
|
|
568
|
+
if (!response.ok) throw new Error(`HTTP Error: ${response.status}`);
|
|
569
|
+
|
|
570
|
+
const data = await response.json();
|
|
571
|
+
|
|
572
|
+
// DEBUG 1: Visualizza i dati esattamente come arrivano dal server
|
|
573
|
+
console.log("📥 [DEBUG] Dati grezzi ricevuti dal server:", data);
|
|
579
574
|
|
|
580
|
-
// FUNZIONE DI PARSING: Trasforma eventuali "20" (stringhe) in 20 (numeri)
|
|
581
575
|
const parse = (obj) => {
|
|
582
576
|
for (let k in obj) {
|
|
583
|
-
if (typeof obj[k] === 'object') parse(obj[k]);
|
|
577
|
+
if (typeof obj[k] === 'object' && obj[k] !== null) parse(obj[k]);
|
|
584
578
|
else if (!isNaN(obj[k]) && typeof obj[k] === 'string' && obj[k] !== "")
|
|
585
579
|
obj[k] = parseFloat(obj[k]);
|
|
586
580
|
}
|
|
587
581
|
return obj;
|
|
588
582
|
};
|
|
589
583
|
|
|
590
|
-
const
|
|
584
|
+
const actual = parse(JSON.parse(JSON.stringify(data))); // Cloniamo per sicurezza
|
|
585
|
+
|
|
586
|
+
// DEBUG 2: Visualizza i dati dopo la conversione numerica
|
|
587
|
+
console.log("⚙️ [DEBUG] Dati convertiti (numeric):", actual);
|
|
591
588
|
|
|
592
|
-
// ASSEGNAZIONE
|
|
593
|
-
if (
|
|
594
|
-
if (
|
|
595
|
-
|
|
596
|
-
|
|
589
|
+
// ASSEGNAZIONE E FUSIONE (Mappatura dei blocchi)
|
|
590
|
+
if (actual.alarms) Object.assign(CONFIG.alarms, actual.alarms);
|
|
591
|
+
if (actual.graphs) Object.assign(CONFIG.graphs, actual.graphs);
|
|
592
|
+
|
|
593
|
+
// Gestione specifica per averaging (il blocco più critico)
|
|
594
|
+
if (actual.averaging) {
|
|
595
|
+
Object.assign(CONFIG.averaging, actual.averaging);
|
|
596
|
+
// DEBUG 3: Tabella comparativa per verificare minSpeed e soglie
|
|
597
|
+
console.table({
|
|
598
|
+
"Parametro": ["longWindow", "minSpeed", "stabilityThreshold", "stabilityBreakout"],
|
|
599
|
+
"Valore Attuale": [
|
|
600
|
+
CONFIG.averaging.longWindow,
|
|
601
|
+
CONFIG.averaging.minSpeed,
|
|
602
|
+
CONFIG.averaging.stabilityThreshold,
|
|
603
|
+
CONFIG.averaging.stabilityBreakout
|
|
604
|
+
]
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (actual.scales) Object.assign(CONFIG.scales, actual.scales);
|
|
597
609
|
|
|
598
|
-
console.log("✅ Configurazione
|
|
610
|
+
console.log("✅ [SUCCESS] Configurazione sincronizzata correttamente.");
|
|
599
611
|
} catch (err) {
|
|
600
|
-
console.warn("⚠️
|
|
612
|
+
console.warn("⚠️ [WARNING] Utilizzo default locali. Motivo:", err.message);
|
|
601
613
|
}
|
|
602
614
|
}
|
|
603
615
|
|
package/index.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* ==========================================================================
|
|
3
3
|
* Rotevista Dash Configuration Plugin
|
|
4
4
|
* ==========================================================================
|
|
5
|
-
* Definisce l'interfaccia di configurazione in Signal K Admin
|
|
6
|
-
*
|
|
5
|
+
* Definisce l'interfaccia di configurazione in Signal K Admin e crea
|
|
6
|
+
* l'endpoint pubblico per la comunicazione con la Dashboard.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
module.exports = function (app) {
|
|
@@ -12,9 +12,25 @@ module.exports = function (app) {
|
|
|
12
12
|
plugin.name = 'Rotevista Dash Configuration';
|
|
13
13
|
plugin.description = 'Configure boat-specific tactical and safety parameters for the Dashboard';
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* plugin.start: Inizializza il plugin e crea la rotta API per la Dashboard.
|
|
17
|
+
*/
|
|
18
|
+
plugin.start = function (options) {
|
|
19
|
+
// Esponiamo i settings su un endpoint dedicato per bypassare i blocchi 401.
|
|
20
|
+
app.get('/rotevista-config', (req, res) => {
|
|
21
|
+
res.json(options);
|
|
22
|
+
});
|
|
17
23
|
|
|
24
|
+
app.debug('Rotevista Dashboard Config Endpoint active at /rotevista-config');
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
plugin.stop = function () {
|
|
28
|
+
// Pulizia risorse allo spegnimento del plugin.
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* plugin.schema: Definisce l'interfaccia grafica in Signal K Admin.
|
|
33
|
+
*/
|
|
18
34
|
plugin.schema = {
|
|
19
35
|
type: 'object',
|
|
20
36
|
title: 'Rotevista Dashboard Settings',
|
|
@@ -62,7 +78,6 @@ module.exports = function (app) {
|
|
|
62
78
|
title: 'Strategic Timeline (Minutes)',
|
|
63
79
|
description: "Total duration shown in the charts. Vertical grid lines mark 1-minute intervals for short durations and 5-minute intervals for long ones.",
|
|
64
80
|
default: 5,
|
|
65
|
-
// Menu a tendina per evitare inserimenti errati
|
|
66
81
|
enum: [5, 10, 15, 30, 60]
|
|
67
82
|
}
|
|
68
83
|
}
|
|
@@ -76,7 +91,7 @@ module.exports = function (app) {
|
|
|
76
91
|
longWindow: {
|
|
77
92
|
type: 'number',
|
|
78
93
|
title: 'Decision Stability Window (ms)',
|
|
79
|
-
description: "The time range used to calculate MEAN values. A longer window (e.g. 30s) provides a solid base for strategy, while a shorter one reacts faster to
|
|
94
|
+
description: "The time range used to calculate MEAN values. A longer window (e.g. 30s) provides a solid base for strategy, while a shorter one reacts faster to every oscillation.",
|
|
80
95
|
default: 30000
|
|
81
96
|
},
|
|
82
97
|
smoothWindow: {
|