@sailingrotevista/rotevista-dash 7.0.29 → 7.0.31
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 +12 -8
- package/gauge.js +4 -4
- package/index.js +7 -2
- package/package.json +3 -2
- package/style.css +26 -0
- package/utils.js +5 -2
package/app.js
CHANGED
|
@@ -348,15 +348,17 @@ function processIncomingData(path, val, source, timeMs) {
|
|
|
348
348
|
} else {
|
|
349
349
|
const currentLock = sourceLocks[path];
|
|
350
350
|
const isSameSource = (currentLock.label === source);
|
|
351
|
-
const isLockExpired = (now - currentLock.lastSeen > 12000); // Scadenza a 12 secondi
|
|
351
|
+
const isLockExpired = (now - currentLock.lastSeen > 12000); // Scadenza a 12 secondi
|
|
352
352
|
const hasHigherPriority = (score > currentLock.score);
|
|
353
|
+
// Consente lo switch tra dispositivi di pari priorità (es. AP e YD) se la sorgente precedente è inattiva da > 1.5s
|
|
354
|
+
const isSamePriorityStale = (score === currentLock.score && (now - currentLock.lastSeen > 1500));
|
|
353
355
|
|
|
354
356
|
if (isSameSource) {
|
|
355
357
|
// Stessa sorgente: aggiorna il timestamp e mantiene il blocco
|
|
356
358
|
currentLock.lastSeen = now;
|
|
357
359
|
currentLock.score = score;
|
|
358
|
-
} else if (isLockExpired || hasHigherPriority) {
|
|
359
|
-
// Ruba il blocco se la sorgente precedente è scaduta o
|
|
360
|
+
} else if (isLockExpired || hasHigherPriority || isSamePriorityStale) {
|
|
361
|
+
// Ruba il blocco se la sorgente precedente è scaduta, ha priorità superiore o è inattiva da 1.5s a pari priorità
|
|
360
362
|
sourceLocks[path] = { label: source, score: score, lastSeen: now };
|
|
361
363
|
console.log(`🔌 [Smart Lock] Path "${path}" switched to: ${source} (Score: ${score})`);
|
|
362
364
|
} else {
|
|
@@ -410,19 +412,21 @@ function processIncomingData(path, val, source, timeMs) {
|
|
|
410
412
|
|
|
411
413
|
// --- GESTIONE PRUA VERA / MAGNETICA CON AUTODIVIAZIONE ---
|
|
412
414
|
if (path === "navigation.headingTrue") {
|
|
415
|
+
store.timestamps["navigation.headingTrueNative"] = now; // Marca la presenza di un sensore nativo di prua vera
|
|
416
|
+
store.timestamps["navigation.headingTrue"] = now;
|
|
413
417
|
safePush(store.smoothBuf.hdg, val, now);
|
|
414
418
|
safePush(store.longBuf.hdg, val, now);
|
|
415
419
|
}
|
|
416
420
|
else if (path === "navigation.headingMagnetic") {
|
|
417
|
-
// Se non
|
|
418
|
-
const
|
|
419
|
-
if (!
|
|
421
|
+
// Se non c'è una prua vera NATIVA negli ultimi 5 secondi, converte e registra ad ogni secondo la prua magnetica
|
|
422
|
+
const hasNativeTrueHdg = store.timestamps["navigation.headingTrueNative"] && (now - store.timestamps["navigation.headingTrueNative"] < 5000);
|
|
423
|
+
if (!hasNativeTrueHdg) {
|
|
420
424
|
const variation = store.raw["navigation.magneticVariation"] || 0; // Legge la declinazione magnetica del GPS
|
|
421
425
|
const calculatedTrueHdg = (val + variation + 2 * Math.PI) % (2 * Math.PI);
|
|
422
426
|
|
|
423
|
-
//
|
|
427
|
+
// Registra il valore calcolato mantenendo attivo il watchdog senza bloccare i successivi pacchetti magnetici
|
|
424
428
|
store.raw["navigation.headingTrue"] = calculatedTrueHdg;
|
|
425
|
-
store.timestamps["navigation.headingTrue"] = now;
|
|
429
|
+
store.timestamps["navigation.headingTrue"] = now;
|
|
426
430
|
safePush(store.smoothBuf.hdg, calculatedTrueHdg, now);
|
|
427
431
|
safePush(store.longBuf.hdg, calculatedTrueHdg, now);
|
|
428
432
|
}
|
package/gauge.js
CHANGED
|
@@ -55,10 +55,10 @@ function updateCentralGauge(store, ui, now, isNavigating, sogKts, stwKts, rawAws
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// B. Rotazione dei puntatori analogici di AWA (Apparent) e TWA (True)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
const smAwa = getCircularAverageFromBuffer(store.smoothBuf.awa, 2000, true);
|
|
59
|
+
const smTwa = getCircularAverageFromBuffer(store.smoothBuf.twa, 2000, true);
|
|
60
|
+
if (smAwa) ui.awa.setAttribute('transform', `rotate(${curAwaRot = getShortestRotation(curAwaRot, radToDeg(smAwa.val))}, 200, 200)`);
|
|
61
|
+
if (smTwa) ui.twa.setAttribute('transform', `rotate(${curTwaRot = getShortestRotation(curTwaRot, radToDeg(smTwa.val))}, 200, 200)`);
|
|
62
62
|
|
|
63
63
|
// C. Calcolo dello Scarroccio (Leeway) e orientamento del vettore Track
|
|
64
64
|
if (store.raw["navigation.courseOverGroundTrue"] !== undefined && store.raw["navigation.headingTrue"] !== undefined) {
|
package/index.js
CHANGED
|
@@ -38,6 +38,7 @@ module.exports = function (app) {
|
|
|
38
38
|
// Memoria temporale per rilevare la presenza di sensori nativi sul Cerbo
|
|
39
39
|
let lastNativeTwsTime = 0;
|
|
40
40
|
let lastNativeTwdTime = 0;
|
|
41
|
+
let lastNativeHeadingTrueTime = 0; // Traccia la presenza di bussola vera nativa
|
|
41
42
|
|
|
42
43
|
// Monitoraggio dello scorrere dei blocchi da 30 minuti
|
|
43
44
|
let lastFrozen30mSlot = 0;
|
|
@@ -248,10 +249,14 @@ module.exports = function (app) {
|
|
|
248
249
|
lastNativeTwsTime = now; // Rilevato TWS nativo della centralina!
|
|
249
250
|
manageHistory('tws', smoothedVal * 1.94384);
|
|
250
251
|
}
|
|
252
|
+
else if (path === 'navigation.headingTrue') {
|
|
253
|
+
lastNativeHeadingTrueTime = now; // Rilevata prua vera nativa dalla rete NMEA
|
|
254
|
+
}
|
|
251
255
|
// --- DECODIFICA PRUA MAGNETICA SERVER-SIDE ---
|
|
252
256
|
else if (path === 'navigation.headingMagnetic') {
|
|
253
|
-
|
|
254
|
-
|
|
257
|
+
// Converte in prua vera solo se non c'è una bussola vera nativa attiva negli ultimi 5s
|
|
258
|
+
const hasNativeTrueHdg = (now - lastNativeHeadingTrueTime < 5000);
|
|
259
|
+
if (!hasNativeTrueHdg) {
|
|
255
260
|
const variation = raw['navigation.magneticVariation'] || 0;
|
|
256
261
|
raw['navigation.headingTrue'] = (smoothedVal + variation + 2 * Math.PI) % (2 * Math.PI);
|
|
257
262
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sailingrotevista/rotevista-dash",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.31",
|
|
4
4
|
"description": "Wind Dashboard with navigation and course aids",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"author": "Sailing Rotevista",
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"signalk": {
|
|
16
|
-
"displayName": "Rotevista Wind Dashboard"
|
|
16
|
+
"displayName": "Rotevista Wind Dashboard",
|
|
17
|
+
"appIcon": "icons/dash.png"
|
|
17
18
|
}
|
|
18
19
|
}
|
package/style.css
CHANGED
|
@@ -612,3 +612,29 @@ body.night-mode {
|
|
|
612
612
|
}
|
|
613
613
|
|
|
614
614
|
*/
|
|
615
|
+
|
|
616
|
+
/* ==========================================================================
|
|
617
|
+
14. POSIZIONAMENTO SOVRAPPOSTO LAYERED COMPASS (0% CARICO CPU)
|
|
618
|
+
========================================================================== */
|
|
619
|
+
|
|
620
|
+
/* Contenitore padre relativo */
|
|
621
|
+
.wind-gauge-container {
|
|
622
|
+
position: relative;
|
|
623
|
+
width: 100%;
|
|
624
|
+
height: 100%;
|
|
625
|
+
max-height: 100%;
|
|
626
|
+
display: flex;
|
|
627
|
+
align-items: center;
|
|
628
|
+
justify-content: center;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/* Sovrapposizione assoluta perfetta del livello lancette sopra lo sfondo */
|
|
632
|
+
#wind-gauge-pointers {
|
|
633
|
+
position: absolute;
|
|
634
|
+
top: 0;
|
|
635
|
+
left: 0;
|
|
636
|
+
width: 100%;
|
|
637
|
+
height: 100%;
|
|
638
|
+
object-fit: contain;
|
|
639
|
+
pointer-events: none; /* Permette al tocco del mouse di passare sotto al centro interattivo */
|
|
640
|
+
}
|
package/utils.js
CHANGED
|
@@ -33,10 +33,13 @@ function safeSetText(el, text) {
|
|
|
33
33
|
|
|
34
34
|
// --- 2. MOTORE MATEMATICO: MEDIA CIRCOLARE VETTORIALE ---
|
|
35
35
|
function getCircularAverageFromBuffer(bufferArray, windowMs, signed = false, now, stabilityThreshold = 0.95, stabilityBreakout = 15) {
|
|
36
|
-
now = now || Date.now();
|
|
37
36
|
const len = bufferArray.length;
|
|
38
37
|
if (len === 0) return null;
|
|
39
38
|
|
|
39
|
+
// RIFERIMENTO TEMPORALE IMMUNE DA DESINCRONIZZAZIONE:
|
|
40
|
+
// Usiamo come 'ora attuale' l'ultimo timestamp presente nel buffer stesso,
|
|
41
|
+
// garantendo che la finestra di 2s sia sempre calcolata sui dati NMEA reali arrivati.
|
|
42
|
+
const referenceTime = bufferArray[len - 1].time;
|
|
40
43
|
let sSin = 0, sCos = 0, count = 0;
|
|
41
44
|
let newestTime = 0, oldestTime = 0;
|
|
42
45
|
|
|
@@ -51,7 +54,7 @@ function getCircularAverageFromBuffer(bufferArray, windowMs, signed = false, now
|
|
|
51
54
|
|
|
52
55
|
for (let i = len - 1; i >= 0; i--) {
|
|
53
56
|
const item = bufferArray[i];
|
|
54
|
-
if ((
|
|
57
|
+
if ((referenceTime - item.time) > windowMs) break; // Usa referenceTime invece di 'now'
|
|
55
58
|
|
|
56
59
|
let diffRad = Math.atan2(Math.sin(item.val - pilotRad), Math.cos(item.val - pilotRad));
|
|
57
60
|
let finalSin, finalCos;
|