@sailingrotevista/rotevista-dash 5.0.4 → 5.0.5

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.
Files changed (2) hide show
  1. package/app.js +82 -22
  2. package/package.json +1 -1
package/app.js CHANGED
@@ -35,7 +35,7 @@ let CONFIG = {
35
35
  const RENDER_INTERVAL_MS = 1000;
36
36
  const TIMEOUT_MS = 5000;
37
37
  const SIM_SAMPLE_INTERVAL = 1000;
38
- const DASH_VERSION = "3.5"; // Major Update: Time-Based storage and GAP handling
38
+ const DASH_VERSION = "3.7"; // Major Update: Time-Based storage and GAP handling
39
39
  const sourceLocks = {};
40
40
 
41
41
  // ==========================================================================
@@ -742,37 +742,97 @@ async function watchConfigChanges() {
742
742
  }
743
743
  }
744
744
 
745
- // --------------------------------------------------------------------------
746
-
747
745
  /**
748
- * Rielaborazione Storica Time-Based (Rimuove accumulo e gestisce il Pruning dinamicamente)
746
+ * manageHistory v3.7 - Aggregazione semantica "Pro-Grade"
747
+ * Integrazioni:
748
+ * 1. Strict undefined check per lastUpdates.
749
+ * 2. Anti-dropout dinamico tarato sul 50% del Reef 1.
750
+ * 3. Clamping di sicurezza (no negativi, no Infinity).
749
751
  */
750
- function manageHistory(t, v) {
751
- const n = Date.now();
752
- const interval = (CONFIG.graphs.historyMinutes * 60000) / CONFIG.graphs.samples;
752
+ function manageHistory(type, value) {
753
+ // --- 1. VALIDAZIONE INPUT RIGOROSA ---
754
+ if (value === undefined || value === null || !isFinite(value)) return;
755
+
756
+ const now = Date.now();
757
+ const historyMinutes = Math.max(1, CONFIG.graphs.historyMinutes || 10);
758
+ const samples = Math.max(2, CONFIG.graphs.samples || 60);
759
+ const bucketIntervalMs = (historyMinutes * 60000) / samples;
760
+
761
+ // --- 2. INIT SICURO (Strict Check) ---
762
+ if (!store.graphTempBuf[type]) store.graphTempBuf[type] = [];
763
+ if (!store.histories[type]) store.histories[type] = [];
764
+ if (store.lastUpdates[type] === undefined) store.lastUpdates[type] = 0;
765
+
766
+ const tempBuf = store.graphTempBuf[type];
767
+
768
+ // --- 3. ANTI-DROPOUT DINAMICO (Auto-scaling) ---
769
+ // Ignora cadute a zero se il valore precedente era superiore al 50% del primo Reef
770
+ if ((type === 'tws' || type === 'aws') && value < 0.05 && tempBuf.length > 0) {
771
+ const lastPoint = tempBuf[tempBuf.length - 1];
772
+ const glitchThreshold = (CONFIG.graphs.reef1 || 15) * 0.5;
773
+ if (lastPoint && lastPoint.val > glitchThreshold) return;
774
+ }
753
775
 
754
- if (!store.graphTempBuf[t]) store.graphTempBuf[t] = [];
755
- store.graphTempBuf[t].push(v);
776
+ // --- 4. STORAGE TEMPORANEO ---
777
+ tempBuf.push({ val: value, time: now });
756
778
 
757
- if (n - store.lastUpdates[t] > interval || store.histories[t].length === 0) {
758
- const avg = store.graphTempBuf[t].reduce((a, b) => a + b, 0) / store.graphTempBuf[t].length;
779
+ // Controllo finestra temporale
780
+ const bucketReady = (now - store.lastUpdates[type] > bucketIntervalMs) || store.histories[type].length === 0;
781
+ if (!bucketReady) return;
759
782
 
760
- // NUOVO STORAGE TIME-BASED
761
- store.histories[t].push({ time: n, val: avg });
783
+ // --- 5. AGGREGAZIONE SEMANTICA ---
784
+ let finalValue = value;
762
785
 
763
- // PRUNING DINAMICO
764
- const maxViewportMinutes = CONFIG.graphs.historyMinutes * 2;
765
- const maxHistoryMs = (maxViewportMinutes * 60000) + 30000;
786
+ if (tempBuf.length > 0) {
787
+ // A. VENTO -> SUSTAINED PEAK (EMA Time-Aware)
788
+ if (type === 'tws' || type === 'aws') {
789
+ const tauMs = 2500;
790
+ let ema = tempBuf[0].val;
791
+ let maxSustained = ema;
766
792
 
767
- while (store.histories[t].length > 0 && (n - store.histories[t][0].time) > maxHistoryMs) {
768
- store.histories[t].shift();
793
+ for (let i = 1; i < tempBuf.length; i++) {
794
+ const dt = Math.max(1, tempBuf[i].time - tempBuf[i - 1].time);
795
+ const alpha = 1 - Math.exp(-dt / tauMs);
796
+ ema = (tempBuf[i].val * alpha) + (ema * (1 - alpha));
797
+ if (isFinite(ema) && ema > maxSustained) maxSustained = ema;
798
+ }
799
+ finalValue = maxSustained;
800
+ }
801
+ // B. PROFONDITÀ -> MINIMO
802
+ else if (type === 'depth') {
803
+ const vals = tempBuf.map(p => p.val).filter(v => isFinite(v));
804
+ if (vals.length > 0) finalValue = Math.min(...vals);
805
+ }
806
+ // C. VELOCITÀ -> MEDIA
807
+ else {
808
+ const vals = tempBuf.map(p => p.val).filter(v => isFinite(v));
809
+ if (vals.length > 0) {
810
+ const sum = vals.reduce((a, b) => a + b, 0);
811
+ finalValue = sum / tempBuf.length;
812
+ }
769
813
  }
814
+ }
815
+
816
+ // --- 6. CLAMPING E VALIDAZIONE FINALE ---
817
+ // Protezione contro valori negativi (fisicamente impossibili per questi dati) e non finiti
818
+ if (!isFinite(finalValue)) return;
819
+ finalValue = Math.max(0, finalValue);
820
+
821
+ // --- 7. STORAGE STORICO (Keys: val, time) ---
822
+ store.histories[type].push({ val: finalValue, time: now });
770
823
 
771
- store.graphTempBuf[t] = [];
772
- store.lastUpdates[t] = n;
824
+ // --- 8. PRUNING DINAMICO ---
825
+ const maxViewportMinutes = historyMinutes * 2;
826
+ const maxHistoryMs = (maxViewportMinutes * 60000) + 60000;
827
+
828
+ while (store.histories[type].length > 0 && (now - store.histories[type][0].time) > maxHistoryMs) {
829
+ store.histories[type].shift();
773
830
  }
774
- }
775
831
 
832
+ // Reset per il prossimo bucket
833
+ store.graphTempBuf[type] = [];
834
+ store.lastUpdates[type] = now;
835
+ }
776
836
  function calculateScale(type, data, mode) {
777
837
  const s = CONFIG.scales[type]; let aMin = Math.min(...data), aMax = Math.max(...data);
778
838
  if (mode === 'hercules') {
@@ -1069,7 +1129,7 @@ async function init() {
1069
1129
  await fetchServerConfig();
1070
1130
  startDisplayLoop();
1071
1131
  connect();
1072
- setInterval(watchConfigChanges, 10000);
1132
+ setInterval(watchConfigChanges, 10000);
1073
1133
  }
1074
1134
 
1075
1135
  window.addEventListener('load', init);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sailingrotevista/rotevista-dash",
3
- "version": "5.0.4",
3
+ "version": "5.0.5",
4
4
  "description": "Wind Dashboard with navigation and course aids",
5
5
  "main": "index.js",
6
6
  "publishConfig": {