@sailingrotevista/rotevista-dash 6.0.2 → 6.0.3

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 (3) hide show
  1. package/app.js +130 -0
  2. package/index.html +1 -1
  3. package/package.json +1 -1
package/app.js CHANGED
@@ -852,6 +852,136 @@ async function fetchServerHistory() {
852
852
  }
853
853
  }
854
854
 
855
+ /**
856
+ * Gestione dinamica delle scale dei grafici (Involucro Elastico Snapped e Safety Zoom)
857
+ * Implementa lo "Snap a Griglia" basato sull'hercSpan senza padding per tutti i sensori.
858
+ */
859
+ function calculateScale(type, data, mode) {
860
+ const s = CONFIG.scales[type];
861
+ const currentVal = data[data.length - 1];
862
+
863
+ // Fallback di emergenza se il buffer è momentaneamente vuoto
864
+ if (currentVal === undefined || currentVal === null) {
865
+ return { min: 0, max: s ? s.stdMax : 10 };
866
+ }
867
+
868
+ // Inizializzazione della memoria delle scale nello store se non esiste
869
+ if (!store.herculesScales) store.herculesScales = {};
870
+ if (!store.herculesScales[type]) {
871
+ store.herculesScales[type] = { min: 0, max: s ? s.stdMax : 10 };
872
+ }
873
+ let currentScale = store.herculesScales[type];
874
+
875
+ // ==========================================================================
876
+ // 1. SEZIONE PROFONDITÀ (DEDICATA A 2 MINUTI DI SICUREZZA, MINIMO FISSO A 0)
877
+ // ==========================================================================
878
+ if (type === 'depth') {
879
+ const shallowThreshold = Math.max(s.stdMax, 10); // Es. 20m
880
+ if (store.depthProtectedActive === undefined) store.depthProtectedActive = false;
881
+
882
+ const now = Date.now();
883
+ const depthSafetyWindowMs = 120000; // 2 minuti di stabilizzazione fissi per la sicurezza
884
+
885
+ // Estrazione dati reali degli ultimi 2 minuti con timestamp
886
+ const recentPoints = store.histories.depth.filter(p => (now - p.time) <= depthSafetyWindowMs);
887
+ const recentVals = recentPoints.map(p => p.val);
888
+
889
+ const localMax = recentVals.length > 0 ? Math.max(...recentVals) : currentVal;
890
+ const localMin = recentVals.length > 0 ? Math.min(...recentVals) : currentVal;
891
+
892
+ // --- 1.1 NORMALE PROFONDITÀ (CON SOGLIA STANDARD E FILTRO 2 MINUTI) ---
893
+ if (mode !== 'hercules') {
894
+ if (!store.depthProtectedActive && currentVal <= shallowThreshold) {
895
+ store.depthProtectedActive = true;
896
+ }
897
+ if (store.depthProtectedActive) {
898
+ if (localMin > shallowThreshold) {
899
+ store.depthProtectedActive = false;
900
+ }
901
+ }
902
+ if (store.depthProtectedActive) {
903
+ return { min: 0, max: shallowThreshold };
904
+ }
905
+ const maxHistorico = Math.max(...data);
906
+ return { min: 0, max: Math.max(s.stdMax, Math.ceil(maxHistorico / s.step) * s.step) };
907
+ }
908
+
909
+ // --- 1.2 HERCULES PROFONDITÀ (0 IN BASSO, SNAP SUL MAX SENZA PADDING) ---
910
+ if (mode === 'hercules') {
911
+ const roundStep = s.hercSpan; // Passo di griglia selezionato dall'utente
912
+
913
+ let targetMin = 0;
914
+ let targetMax = Math.ceil(localMax / roundStep) * roundStep;
915
+
916
+ // Impediamo una scala inferiore a 4 metri per sicurezza visiva
917
+ const absoluteMinSpan = 4;
918
+ if (targetMax < absoluteMinSpan) {
919
+ targetMax = absoluteMinSpan;
920
+ }
921
+
922
+ // Regola asimmetrica per il MAX (Espansione istantanea, contrazione a 2 minuti)
923
+ if (currentVal > currentScale.max) {
924
+ currentScale.max = targetMax;
925
+ } else {
926
+ const allStableInTarget = recentVals.every(val => val <= targetMax);
927
+ if (allStableInTarget) {
928
+ currentScale.max = targetMax;
929
+ }
930
+ }
931
+
932
+ currentScale.min = 0;
933
+ return { min: currentScale.min, max: currentScale.max };
934
+ }
935
+ }
936
+
937
+ // ==========================================================================
938
+ // 2. ALTRI GRAFICI (STW, SOG, TWS): COMPORTAMENTO ADATTIVO SU TERZO DEL GRAFICO
939
+ // ==========================================================================
940
+
941
+ // --- 2.1 MODALITÀ NORMALE ---
942
+ if (mode !== 'hercules') {
943
+ const maxHistorico = Math.max(...data);
944
+ return { min: 0, max: Math.max(s.stdMax, Math.ceil(maxHistorico / s.step) * s.step) };
945
+ }
946
+
947
+ // --- 2.2 MODALITÀ HERCULES AD ALTO CONTRASTO (SNAP SENZA PADDING) ---
948
+ if (mode === 'hercules') {
949
+ const roundStep = s.hercSpan; // Passo di griglia (ex hercSpan)
950
+
951
+ const oneThirdCount = Math.max(1, Math.floor(data.length / 3));
952
+ const recentThirdData = data.slice(-oneThirdCount);
953
+
954
+ const localMin = Math.min(...recentThirdData);
955
+ const localMax = Math.max(...recentThirdData);
956
+
957
+ // Applichiamo lo snap diretto ai multipli della griglia
958
+ let targetMin = Math.max(0, Math.floor(localMin / roundStep) * roundStep);
959
+ let targetMax = Math.ceil(localMax / roundStep) * roundStep;
960
+
961
+ // Se lo span calcolato è nullo (es. velocità costante), forziamo lo span minimo
962
+ if (targetMax - targetMin === 0) {
963
+ targetMax = targetMin + roundStep;
964
+ }
965
+
966
+ // APPLICAZIONE REGOLE ASIMMETRICHE ANTI-CLIPPING
967
+ // A. Espansione ISTANTANEA in alto o in basso (sicurezza)
968
+ if (currentVal < currentScale.min || currentVal > currentScale.max) {
969
+ currentScale.min = Math.min(currentScale.min, targetMin);
970
+ currentScale.max = Math.max(currentScale.max, targetMax);
971
+ }
972
+ // B. Contrazione RITARDATA (solo se tutto il terzo recente si è assestato nel target)
973
+ else {
974
+ const allStableInTarget = recentThirdData.every(val => val >= targetMin && val <= targetMax);
975
+ if (allStableInTarget) {
976
+ currentScale.min = targetMin;
977
+ currentScale.max = targetMax;
978
+ }
979
+ }
980
+
981
+ return { min: currentScale.min, max: currentScale.max };
982
+ }
983
+ }
984
+
855
985
  function updateScaleLabels(t, min, max) {
856
986
  const el = document.getElementById(t + '-scale');
857
987
  if (el) el.innerHTML = `<span>${Math.round(max)}</span><span>${Math.round((min+max)/2)}</span><span>${Math.round(min)}</span>`;
package/index.html CHANGED
@@ -210,6 +210,6 @@
210
210
  </div>
211
211
  </div>
212
212
 
213
- <script src="app.js"></script>
213
+ <script src="app.js?v=6.0"></script>
214
214
  </body>
215
215
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sailingrotevista/rotevista-dash",
3
- "version": "6.0.2",
3
+ "version": "6.0.3",
4
4
  "description": "Wind Dashboard with navigation and course aids",
5
5
  "main": "index.js",
6
6
  "publishConfig": {