@sailingrotevista/rotevista-dash 7.0.10 → 7.0.12

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 CHANGED
@@ -23,7 +23,7 @@ let CONFIG = {
23
23
  minSpeed: 0.5,
24
24
  stabilityBreakout: 15
25
25
  },
26
- graphs: { reef1: 5, reef2: 10, historyMinutes: 10, samples: 60 },
26
+ graphs: { reef1: 10, reef2: 15, historyMinutes: 10, samples: 60 },
27
27
  scales: {
28
28
  stw: { stdMax: 4, hercSpan: 2, step: 1 },
29
29
  sog: { stdMax: 4, hercSpan: 2, step: 1 },
@@ -98,9 +98,12 @@ const ui = {
98
98
  twdChevron: document.getElementById('twd-wind-chevron'),
99
99
  leewayMask: document.getElementById('leeway-mask-rect'), leewayVal: document.getElementById('leeway-val'),
100
100
  tackHdg: document.getElementById('tack-hdg'), tackCog: document.getElementById('tack-cog'),
101
- status: document.getElementById('status'), hotspot: document.getElementById('fullscreen-hotspot')
101
+ status: document.getElementById('status'), hotspot: document.getElementById('fullscreen-hotspot'),
102
+ tackLabel: document.getElementById('tack-label')
102
103
  };
103
104
 
105
+ let currentTackLabelMode = 'TACK'; // Stato dell'etichetta tattica ('TACK' o 'GYBE') con isteresi
106
+
104
107
  // ==========================================================================
105
108
  // 3. UTILITIES (MATEMATICA, BUFFER E MEMORIA) - [Esportate in utils.js]
106
109
  // ==========================================================================
@@ -238,8 +241,13 @@ function computeTrueWind() {
238
241
  if (tws_water > 0.05) {
239
242
  const twa = Math.atan2(aws * Math.sin(awa), aws * Math.cos(awa) - stw);
240
243
  store.raw["environment.wind.angleTrueWater"] = twa;
244
+
245
+ // Inserimento atomico e sincronizzato di TWA e AWA nei relativi buffer mobili
246
+ // per garantire che le medie vettoriali siano perfettamente allineate in fase
241
247
  safePush(store.smoothBuf.twa, twa, now);
242
248
  safePush(store.longBuf.twa, twa, now);
249
+ safePush(store.smoothBuf.awa, awa, now);
250
+ safePush(store.longBuf.awa, awa, now);
243
251
  }
244
252
 
245
253
  // ==========================================================================
@@ -351,16 +359,16 @@ function processIncomingData(path, val, source, timeMs) {
351
359
  // Evita di eseguire calcoli trigonometrici, allocare oggetti in memoria dinamica
352
360
  // e popolare i buffer smoothBuf/longBuf decine di volte al secondo per singolo sensore.
353
361
  if (!lastPathProcessTimes[path]) lastPathProcessTimes[path] = 0;
354
- if (now - lastPathProcessTimes[path] < 1000) {
362
+ if (now - lastPathProcessTimes[path] < 800) {
355
363
  return; // Esce subito risparmiando cicli di calcolo del browser e batteria del tablet
356
364
  }
357
365
  lastPathProcessTimes[path] = now;
358
366
 
359
367
  // Da qui in poi, l'inserimento nei buffer fisici avviene rigorosamente a 1Hz:
360
- if (path === "environment.wind.angleApparent") {
361
- safePush(store.smoothBuf.awa, val, now);
362
- safePush(store.longBuf.awa, val, now);
363
- }
368
+ //if (path === "environment.wind.angleApparent") {
369
+ // safePush(store.smoothBuf.awa, val, now);
370
+ // safePush(store.longBuf.awa, val, now);
371
+ //}
364
372
 
365
373
  // BUG RISOLTO: Intercetta il TWD nativo e lo spinge nei buffer della bussola radar
366
374
  if (path === "environment.wind.directionTrue") {
@@ -462,7 +470,7 @@ function updateWindTrend() {
462
470
  // Allarme VISIVO: Se siamo in zona di pericolo poppa profonda (> 155°), accendi entrambi i LED di rosso pulsante
463
471
  if (absTwaDeg > 155) {
464
472
  if (gaugeDots.cw) { gaugeDots.cw.classList.add('is-gybing'); gaugeDots.cw.setAttribute('fill', '#ff3b30'); }
465
- if (gaugeDots.ccw) { gaugeDots.ccw.classList.add('is-gybing'); gaugeDots.ccw.setAttribute('fill', '#ff3b30'); }
473
+ if (gaugeDots.ccw) { gaugeDots.ccw.classList.remove('is-gybing'); }
466
474
 
467
475
  // LOGICA MACCHINA A STATI CON ISTERESI:
468
476
  // Rileviamo le mure solo se siamo fuori dalla zona cieca di poppa secca (> 170°).
@@ -537,7 +545,7 @@ function updateWindTrend() {
537
545
  const compassDots = { cw: document.getElementById('trend-dot-cw'), ccw: document.getElementById('trend-dot-ccw') };
538
546
 
539
547
  if (twdNow && twdRef) {
540
- let deltaMeteo = radToDeg((twdNow.val - twdRef.val + Math.PI * 3) % (Math.PI * 2) - Math.PI);
548
+ let deltaMeteo = radToDeg((twdNow.val - twdRef.val + Math.PI * 3) % (2 * Math.PI) - Math.PI);
541
549
  if (Math.abs(deltaMeteo) > 6.0) {
542
550
  const isSouth = store.raw["navigation.position"]?.latitude < 0;
543
551
  let meteoColor = (!isSouth) ? (deltaMeteo < 0 ? "#27ae60" : "#c0392b") : (deltaMeteo > 0 ? "#27ae60" : "#c0392b");
@@ -737,6 +745,17 @@ function startDisplayLoop() {
737
745
  let twObj = getCircularAverageFromBuffer(store.longBuf.twa, CONFIG.averaging.longWindow, true);
738
746
  let twdObj = getCircularAverageFromBuffer(store.longBuf.twd, CONFIG.averaging.longWindow, false);
739
747
 
748
+ // --- GESTIONE DINAMICA ETICHETTA TACK/GYBE CON ISTERESI DI 10 GRADI ---
749
+ if (ui.tackLabel) {
750
+ const absTwa = Math.abs(radToDeg(store.raw["environment.wind.angleTrueWater"] || 0));
751
+ if (currentTackLabelMode === 'TACK' && absTwa > 95) {
752
+ currentTackLabelMode = 'GYBE';
753
+ } else if (currentTackLabelMode === 'GYBE' && absTwa < 85) {
754
+ currentTackLabelMode = 'TACK';
755
+ }
756
+ safeSetText(ui.tackLabel, currentTackLabelMode);
757
+ }
758
+
740
759
  upUI(ui.hdg, hObj, store.raw["navigation.headingTrue"], true);
741
760
  upUI(ui.cog, cObj, store.raw["navigation.courseOverGroundTrue"], true);
742
761
  upUI(ui.awaAvg, awObj, store.raw["environment.wind.angleApparent"], false);
@@ -1277,7 +1296,7 @@ document.addEventListener('visibilitychange', () => {
1277
1296
  // Se era già chiuso o in errore, proviamo a riconnettere subito
1278
1297
  connect();
1279
1298
  } else {
1280
- // Se risulta "OPEN" ma potrebbe essere una connessione fantasma,
1299
+ // Se resulta "OPEN" ma potrebbe essere una connessione fantasma,
1281
1300
  // la chiudiamo forzatamente per scatenare la riconnessione pulita e il download della cronologia
1282
1301
  console.log("🔌 [Watchdog] Riavvio precauzionale del WebSocket per evitare connessioni fantasma.");
1283
1302
  socket.close();
package/index.html CHANGED
@@ -49,8 +49,8 @@
49
49
  </div>
50
50
 
51
51
  <div class="data-box box-tack">
52
- <div class="label-row"><span class="label">TACK</span></div>
53
- <div class="dual-value-container">
52
+ <div class="label-row"><span class="label" id="tack-label">TACK</span></div>
53
+ <div class="dual-value-container">
54
54
  <div class="dual-value-col tack-hdg-col">
55
55
  <span class="dual-label">HDG</span>
56
56
  <span class="value dual-val" id="tack-hdg">---&deg;</span>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sailingrotevista/rotevista-dash",
3
- "version": "7.0.10",
3
+ "version": "7.0.12",
4
4
  "description": "Wind Dashboard with navigation and course aids",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
package/style.css CHANGED
@@ -463,8 +463,31 @@ body.night-mode {
463
463
  .night-mode .is-focused .scale-labels { color: #ff3333 !important; }
464
464
 
465
465
  /* --- 10.4 WIND GAUGE (STRUMENTO CENTRALE) --- */
466
- .night-mode #wind-gauge circle { fill: #000 !important; stroke: #220000 !important; }
467
- .night-mode #wind-gauge circle:nth-of-type(2), .night-mode #fullscreen-hotspot { fill: #050000 !important; stroke: #330000 !important; }
466
+ .night-mode #wind-gauge circle:not(#trend-gauge-cw):not(#trend-gauge-ccw) {
467
+ fill: #000000 !important;
468
+ stroke: #220000 !important;
469
+ }
470
+ .night-mode #wind-gauge circle:nth-of-type(2), .night-mode #fullscreen-hotspot {
471
+ fill: #050000 !important;
472
+ stroke: #330000 !important;
473
+ }
474
+
475
+ /* Stile notturno dedicato per i pallini di tendenza della bussola centrale */
476
+ .night-mode #trend-gauge-cw,
477
+ .night-mode #trend-gauge-ccw {
478
+ fill: #440000 !important; /* Rosso scurissimo a riposo per preservare la vista notturna */
479
+ stroke: none !important;
480
+ transition: fill 0.2s ease, filter 0.2s ease;
481
+ }
482
+
483
+ /* Accensione brillante con effetto alone (glow) quando lampeggiano (attivi) o in allarme strambata */
484
+ .night-mode #trend-gauge-cw.is-trending,
485
+ .night-mode #trend-gauge-ccw.is-trending,
486
+ .night-mode #trend-gauge-cw.is-gybing,
487
+ .night-mode #trend-gauge-ccw.is-gybing {
488
+ fill: #ff0000 !important;
489
+ filter: drop-shadow(0 0 4px #ff0000) !important;
490
+ }
468
491
  .night-mode #center-glow feDropShadow { flood-color: #ff0000 !important; flood-opacity: 0.6 !important; }
469
492
  .night-mode #boat-icon { fill: #220000 !important; stroke: #ff0000 !important; stroke-width: 0.8px !important; opacity: 1 !important; }
470
493
  .night-mode #aws-display-group text { fill: #800000 !important; }
@@ -533,11 +556,11 @@ body.night-mode {
533
556
  }
534
557
 
535
558
  /* --- OVERRIDE NIGHT MODE PER IL WIND RADAR --- */
536
- .night-mode #wind-radar circle {
559
+ .night-mode #wind-radar circle:not(.is-trending) {
537
560
  fill: #000000 !important;
538
561
  stroke: #220000 !important;
539
562
  }
540
- .night-mode #wind-radar circle:nth-of-type(2),
563
+ .night-mode #wind-radar circle:nth-of-type(2):not(.is-trending),
541
564
  .night-mode #radar-hotspot {
542
565
  fill: #050000 !important;
543
566
  stroke: #330000 !important;
package/weather-radar.js CHANGED
@@ -138,13 +138,14 @@ function renderRadar() {
138
138
  radarDataList.push({
139
139
  twdMin: (twdDeg - 20 + 360) % 360,
140
140
  twdMax: (twdDeg + 20 + 360) % 360,
141
- twsPeak: store.futureForecast.tws,
141
+ twsPeak: store.futureForecast.gust, // Chirurgico: Usiamo il Gust come picco per visualizzare l'arco delle raffiche future
142
142
  isFuture: true
143
143
  });
144
144
  } else {
145
145
  radarDataList.push(null);
146
146
  }
147
147
 
148
+
148
149
  // 2. ANELLO 1: Presente Mobile (Real-Time Client-Side)
149
150
  const activeRing = calculateActive30mRing();
150
151
  radarDataList.push(activeRing);
@@ -237,6 +238,16 @@ function renderRadar() {
237
238
  borderPath.setAttribute("stroke-linecap", "round");
238
239
  borderPath.setAttribute("opacity", opacityValue);
239
240
  ringsContainer.appendChild(borderPath);
241
+ } else {
242
+ const borderPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
243
+ borderPath.setAttribute("d", pathData);
244
+ borderPath.setAttribute("fill", "none");
245
+ const isNight = document.body.classList.contains('night-mode');
246
+ borderPath.setAttribute("stroke", isNight ? "#220000" : "#90a4ae");
247
+ borderPath.setAttribute("stroke-width", BORDER_STROKE_WIDTH);
248
+ borderPath.setAttribute("stroke-linecap", "round");
249
+ borderPath.setAttribute("opacity", "0.6");
250
+ ringsContainer.appendChild(borderPath);
240
251
  }
241
252
 
242
253
  const mainPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
@@ -245,7 +256,7 @@ function renderRadar() {
245
256
  mainPath.setAttribute("stroke", strokeColor);
246
257
  mainPath.setAttribute("stroke-width", ARC_STROKE_WIDTH);
247
258
  mainPath.setAttribute("stroke-linecap", "round");
248
- mainPath.setAttribute("opacity", data.isFuture ? "0.5" : opacityValue);
259
+ mainPath.setAttribute("opacity", data.isFuture ? "0.7" : opacityValue);
249
260
  mainPath.id = data.isFuture ? "" : (index === 1 ? "active-present-arc" : "");
250
261
  ringsContainer.appendChild(mainPath);
251
262
  });