@sailingrotevista/rotevista-dash 5.0.7 → 5.0.8

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 +47 -39
  2. package/index.js +32 -8
  3. package/package.json +1 -1
package/app.js CHANGED
@@ -927,8 +927,8 @@ function manageHistory(type, value) {
927
927
  }
928
928
 
929
929
  /**
930
- * Gestione dinamica delle scale dei grafici (Involucro Elastico e Safety Zoom)
931
- * Implementa la logica di sicurezza disaccoppiata a 2 minuti per la profondità.
930
+ * Gestione dinamica delle scale dei grafici (Involucro Elastico Snapped e Safety Zoom)
931
+ * Implementa lo "Snap a Griglia" basato sull'hercSpan senza padding per tutti i sensori.
932
932
  */
933
933
  function calculateScale(type, data, mode) {
934
934
  const s = CONFIG.scales[type];
@@ -947,14 +947,14 @@ function calculateScale(type, data, mode) {
947
947
  let currentScale = store.herculesScales[type];
948
948
 
949
949
  // ==========================================================================
950
- // SEZIONE PROFONDITÀ (REGOLA DI SICUREZZA DISACCOPPIATA A 2 MINUTI)
950
+ // 1. SEZIONE PROFONDITÀ (DEDICATA A 2 MINUTI DI SICUREZZA, MINIMO FISSO A 0)
951
951
  // ==========================================================================
952
952
  if (type === 'depth') {
953
953
  const shallowThreshold = Math.max(s.stdMax, 10); // Es. 20m
954
954
  if (store.depthProtectedActive === undefined) store.depthProtectedActive = false;
955
955
 
956
956
  const now = Date.now();
957
- const depthSafetyWindowMs = 120000; // 2 minuti di stabilizzazione fissi per la sicurezza
957
+ const depthSafetyWindowMs = 120000; // 2 minuti fissi per la sicurezza
958
958
 
959
959
  // Estrazione dati reali degli ultimi 2 minuti con timestamp
960
960
  const recentPoints = store.histories.depth.filter(p => (now - p.time) <= depthSafetyWindowMs);
@@ -963,49 +963,40 @@ function calculateScale(type, data, mode) {
963
963
  const localMax = recentVals.length > 0 ? Math.max(...recentVals) : currentVal;
964
964
  const localMin = recentVals.length > 0 ? Math.min(...recentVals) : currentVal;
965
965
 
966
- // --- NORMALE PROFONDITÀ (CON SOGLIA STANDARD E FILTRO 2 MINUTI) ---
966
+ // --- 1.1 NORMALE PROFONDITÀ (CON SOGLIA STANDARD E FILTRO 2 MINUTI) ---
967
967
  if (mode !== 'hercules') {
968
- // Entrata istantanea sotto lo Standard Max (sicurezza immediata)
969
968
  if (!store.depthProtectedActive && currentVal <= shallowThreshold) {
970
969
  store.depthProtectedActive = true;
971
970
  }
972
-
973
- // Uscita ritardata: usciamo solo se il minimo degli ultimi 2 minuti è sopra soglia
974
971
  if (store.depthProtectedActive) {
975
972
  if (localMin > shallowThreshold) {
976
973
  store.depthProtectedActive = false;
977
974
  }
978
975
  }
979
-
980
976
  if (store.depthProtectedActive) {
981
- return { min: 0, max: shallowThreshold }; // Blocco a [0 - 20m]
977
+ return { min: 0, max: shallowThreshold };
982
978
  }
983
-
984
- // Se siamo fuori, scala dinamica normale basata sull'intero buffer passato
985
979
  const maxHistorico = Math.max(...data);
986
980
  return { min: 0, max: Math.max(s.stdMax, Math.ceil(maxHistorico / s.step) * s.step) };
987
981
  }
988
982
 
989
- // --- HERCULES PROFONDITÀ (0 IN BASSO, ZOOM SUL MASSIMO DEI 2 MINUTI) ---
983
+ // --- 1.2 HERCULES PROFONDITÀ (0 IN BASSO, SNAP SUL MAX SENZA PADDING) ---
990
984
  if (mode === 'hercules') {
991
- const padding = 1.0; // 1 metro di margine sopra il fondo
985
+ const roundStep = s.hercSpan; // Passo di griglia selezionato dall'utente
992
986
 
993
- // Calcoliamo i limiti ideali basati solo sugli ultimi 2 minuti
994
987
  let targetMin = 0;
995
- let targetMax = Math.ceil(localMax + padding);
988
+ let targetMax = Math.ceil(localMax / roundStep) * roundStep;
996
989
 
997
- // Impediamo una scala troppo stretta (minimo 4 metri di range totale per sicurezza)
990
+ // Impediamo una scala inferiore a 4 metri per sicurezza visiva
998
991
  const absoluteMinSpan = 4;
999
992
  if (targetMax < absoluteMinSpan) {
1000
993
  targetMax = absoluteMinSpan;
1001
994
  }
1002
995
 
1003
- // Regola asimmetrica di aggiornamento
996
+ // Regola asimmetrica per il MAX (Espansione istantanea, contrazione a 2 minuti)
1004
997
  if (currentVal > currentScale.max) {
1005
- // Se andiamo verso il fondo profondo, allarghiamo istantaneamente
1006
998
  currentScale.max = targetMax;
1007
999
  } else {
1008
- // Stringiamo lo zoom solo se tutti i dati degli ultimi 2 minuti sono inferiori al target
1009
1000
  const allStableInTarget = recentVals.every(val => val <= targetMax);
1010
1001
  if (allStableInTarget) {
1011
1002
  currentScale.max = targetMax;
@@ -1018,34 +1009,51 @@ function calculateScale(type, data, mode) {
1018
1009
  }
1019
1010
 
1020
1011
  // ==========================================================================
1021
- // ALTRI GRAFICI (STW, SOG, TWS): MANTENGONO IL COMPORTAMENTO ORIGINALE
1012
+ // 2. ALTRI GRAFICI (STW, SOG, TWS): COMPORTAMENTO ADATTIVO SU TERZO DEL GRAFICO
1022
1013
  // ==========================================================================
1023
- let aMin = Math.min(...data), aMax = Math.max(...data);
1014
+
1015
+ // --- 2.1 MODALITÀ NORMALE ---
1016
+ if (mode !== 'hercules') {
1017
+ const maxHistorico = Math.max(...data);
1018
+ return { min: 0, max: Math.max(s.stdMax, Math.ceil(maxHistorico / s.step) * s.step) };
1019
+ }
1024
1020
 
1021
+ // --- 2.2 MODALITÀ HERCULES AD ALTO CONTRASTO (SNAP SENZA PADDING) ---
1025
1022
  if (mode === 'hercules') {
1026
- const padding = (type === 'stw' || type === 'sog') ? 0.5 : 1.0;
1023
+ const roundStep = s.hercSpan; // Passo di griglia (ex hercSpan)
1024
+
1025
+ const oneThirdCount = Math.max(1, Math.floor(data.length / 3));
1026
+ const recentThirdData = data.slice(-oneThirdCount);
1027
+
1028
+ const localMin = Math.min(...recentThirdData);
1029
+ const localMax = Math.max(...recentThirdData);
1027
1030
 
1028
- let min = Math.max(0, Math.floor(aMin - padding));
1029
- let max = Math.ceil(aMax + padding);
1031
+ // Applichiamo lo snap diretto ai multipli della griglia
1032
+ let targetMin = Math.max(0, Math.floor(localMin / roundStep) * roundStep);
1033
+ let targetMax = Math.ceil(localMax / roundStep) * roundStep;
1030
1034
 
1031
- // Garantisce lo span minimo
1032
- const currentSpan = max - min;
1033
- if (currentSpan < s.hercSpan) {
1034
- const diff = s.hercSpan - currentSpan;
1035
- min = Math.max(0, min - Math.floor(diff / 2));
1036
- max = min + s.hercSpan;
1035
+ // Se lo span calcolato è nullo (es. velocità costante), forziamo lo span minimo
1036
+ if (targetMax - targetMin === 0) {
1037
+ targetMax = targetMin + roundStep;
1037
1038
  }
1038
1039
 
1039
- // Arrotonda la griglia numerica a step prefissati
1040
- const roundStep = (type === 'stw' || type === 'sog') ? 0.5 : 1.0;
1041
- min = Math.floor(min / roundStep) * roundStep;
1042
- max = Math.ceil(max / roundStep) * roundStep;
1040
+ // APPLICAZIONE REGOLE ASIMMETRICHE ANTI-CLIPPING
1041
+ // A. Espansione ISTANTANEA in alto o in basso (sicurezza)
1042
+ if (currentVal < currentScale.min || currentVal > currentScale.max) {
1043
+ currentScale.min = Math.min(currentScale.min, targetMin);
1044
+ currentScale.max = Math.max(currentScale.max, targetMax);
1045
+ }
1046
+ // B. Contrazione RITARDATA (solo se tutto il terzo recente si è assestato nel target)
1047
+ else {
1048
+ const allStableInTarget = recentThirdData.every(val => val >= targetMin && val <= targetMax);
1049
+ if (allStableInTarget) {
1050
+ currentScale.min = targetMin;
1051
+ currentScale.max = targetMax;
1052
+ }
1053
+ }
1043
1054
 
1044
- return { min, max };
1055
+ return { min: currentScale.min, max: currentScale.max };
1045
1056
  }
1046
-
1047
- // Scala Standard (Autocompressione a scatti)
1048
- return { min: 0, max: Math.max(s.stdMax, Math.ceil(aMax / s.step) * s.step) };
1049
1057
  }
1050
1058
 
1051
1059
  function updateScaleLabels(t, min, max) {
package/index.js CHANGED
@@ -127,10 +127,10 @@ module.exports = function (app) {
127
127
  default: 0.5
128
128
  },
129
129
  stabilityThreshold: {
130
- type: 'number',
131
- title: 'Steering Precision (Sensitivity)',
132
- description: "How strictly the system judges data coherence (0.0 to 1.0). Due to internal smoothing, 0.97-0.98 requires racing precision; 0.93-0.95 is ideal for cruising in waves. Below this, the display rarely alerts for instability.",
133
- default: 0.95
130
+ type: 'number',
131
+ title: 'Steering Precision (Sensitivity)',
132
+ description: "How strictly the system judges data coherence (0.0 to 1.0). Due to internal smoothing, 0.97-0.98 requires racing precision; 0.93-0.95 is ideal for cruising in waves. Below this, the display rarely alerts for instability.",
133
+ default: 0.95
134
134
  },
135
135
  stabilityBreakout: {
136
136
  type: 'number',
@@ -152,7 +152,13 @@ module.exports = function (app) {
152
152
  properties: {
153
153
  stdMax: { type: 'number', title: 'Standard Max', description: "Default top limit of the graph.", default: 12 },
154
154
  step: { type: 'number', title: 'Scale Jump', description: "Amount the scale increases when you exceed the limit.", default: 2 },
155
- hercSpan: { type: 'number', title: 'Hercules Zoom Span', description: "Width of the zoom window around your current speed.", default: 4 }
155
+ hercSpan: {
156
+ type: 'number',
157
+ title: 'Hercules Grid Step (Resolution)',
158
+ description: "Select the multiplier step for the Hercules zoom. The scale boundaries will always snap to multiples of this value.",
159
+ enum: [0.5, 1.0, 2.0, 3.0],
160
+ default: 1.0
161
+ }
156
162
  }
157
163
  },
158
164
  sog: {
@@ -161,7 +167,13 @@ module.exports = function (app) {
161
167
  properties: {
162
168
  stdMax: { type: 'number', title: 'Standard Max', default: 12 },
163
169
  step: { type: 'number', title: 'Scale Jump', default: 2 },
164
- hercSpan: { type: 'number', title: 'Hercules Zoom Span', default: 4 }
170
+ hercSpan: {
171
+ type: 'number',
172
+ title: 'Hercules Grid Step (Resolution)',
173
+ description: "Select the multiplier step for the Hercules zoom. The scale boundaries will always snap to multiples of this value.",
174
+ enum: [0.5, 1.0, 2.0, 3.0],
175
+ default: 1.0
176
+ }
165
177
  }
166
178
  },
167
179
  tws: {
@@ -170,7 +182,13 @@ module.exports = function (app) {
170
182
  properties: {
171
183
  stdMax: { type: 'number', title: 'Standard Max', default: 25 },
172
184
  step: { type: 'number', title: 'Scale Jump', default: 5 },
173
- hercSpan: { type: 'number', title: 'Hercules Zoom Span', default: 10 }
185
+ hercSpan: {
186
+ type: 'number',
187
+ title: 'Hercules Grid Step (Resolution)',
188
+ description: "Select the multiplier step for the Hercules zoom. The scale boundaries will always snap to multiples of this value.",
189
+ enum: [1, 2, 3, 5, 10],
190
+ default: 2
191
+ }
174
192
  }
175
193
  },
176
194
  depth: {
@@ -179,7 +197,13 @@ module.exports = function (app) {
179
197
  properties: {
180
198
  stdMax: { type: 'number', title: 'Standard Max', default: 20 },
181
199
  step: { type: 'number', title: 'Scale Jump', default: 10 },
182
- hercSpan: { type: 'number', title: 'Hercules Zoom Span', default: 10 }
200
+ hercSpan: {
201
+ type: 'number',
202
+ title: 'Hercules Grid Step (Resolution)',
203
+ description: "Select the multiplier step for the Hercules zoom. The scale boundaries will always snap to multiples of this value.",
204
+ enum: [1, 2, 3, 5, 10],
205
+ default: 2
206
+ }
183
207
  }
184
208
  }
185
209
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sailingrotevista/rotevista-dash",
3
- "version": "5.0.7",
3
+ "version": "5.0.8",
4
4
  "description": "Wind Dashboard with navigation and course aids",
5
5
  "main": "index.js",
6
6
  "publishConfig": {