@sailingrotevista/rotevista-dash 5.0.6 → 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.
- package/app.js +116 -27
- package/index.js +32 -8
- package/package.json +1 -1
package/app.js
CHANGED
|
@@ -33,7 +33,7 @@ let CONFIG = {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
const RENDER_INTERVAL_MS = 1000;
|
|
36
|
-
const TIMEOUT_MS =
|
|
36
|
+
const TIMEOUT_MS = 15000;
|
|
37
37
|
const SIM_SAMPLE_INTERVAL = 1000;
|
|
38
38
|
const DASH_VERSION = "3.8"; // Major Update: Smart Source Locking & Breathing Hercules Scale
|
|
39
39
|
|
|
@@ -75,6 +75,8 @@ const sourceLocks = {};
|
|
|
75
75
|
const store = {
|
|
76
76
|
raw: {},
|
|
77
77
|
timestamps: {},
|
|
78
|
+
depthProtectedActive: false, // Memoria per lo stato della protezione profondità
|
|
79
|
+
herculesScales: {}, // Memoria per i limiti attivi della modalità Hercules
|
|
78
80
|
smoothBuf: { hdg: [], cog: [], awa: [], twa: [], twd: [] },
|
|
79
81
|
longBuf: { hdg: [], cog: [], awa: [], twa: [], twd: [] },
|
|
80
82
|
histories: { stw: [], sog: [], depth: [], tws: [], vmg: [], aws: [] },
|
|
@@ -925,46 +927,133 @@ function manageHistory(type, value) {
|
|
|
925
927
|
}
|
|
926
928
|
|
|
927
929
|
/**
|
|
928
|
-
* Gestione dinamica delle scale dei grafici (Involucro Elastico e Safety Zoom)
|
|
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.
|
|
929
932
|
*/
|
|
930
933
|
function calculateScale(type, data, mode) {
|
|
931
934
|
const s = CONFIG.scales[type];
|
|
932
|
-
|
|
935
|
+
const currentVal = data[data.length - 1];
|
|
933
936
|
|
|
934
|
-
//
|
|
935
|
-
if (
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
937
|
+
// Fallback di emergenza se il buffer è momentaneamente vuoto
|
|
938
|
+
if (currentVal === undefined || currentVal === null) {
|
|
939
|
+
return { min: 0, max: s ? s.stdMax : 10 };
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// Inizializzazione della memoria delle scale nello store se non esiste
|
|
943
|
+
if (!store.herculesScales) store.herculesScales = {};
|
|
944
|
+
if (!store.herculesScales[type]) {
|
|
945
|
+
store.herculesScales[type] = { min: 0, max: s ? s.stdMax : 10 };
|
|
946
|
+
}
|
|
947
|
+
let currentScale = store.herculesScales[type];
|
|
948
|
+
|
|
949
|
+
// ==========================================================================
|
|
950
|
+
// 1. SEZIONE PROFONDITÀ (DEDICATA A 2 MINUTI DI SICUREZZA, MINIMO FISSO A 0)
|
|
951
|
+
// ==========================================================================
|
|
952
|
+
if (type === 'depth') {
|
|
953
|
+
const shallowThreshold = Math.max(s.stdMax, 10); // Es. 20m
|
|
954
|
+
if (store.depthProtectedActive === undefined) store.depthProtectedActive = false;
|
|
955
|
+
|
|
956
|
+
const now = Date.now();
|
|
957
|
+
const depthSafetyWindowMs = 120000; // 2 minuti fissi per la sicurezza
|
|
958
|
+
|
|
959
|
+
// Estrazione dati reali degli ultimi 2 minuti con timestamp
|
|
960
|
+
const recentPoints = store.histories.depth.filter(p => (now - p.time) <= depthSafetyWindowMs);
|
|
961
|
+
const recentVals = recentPoints.map(p => p.val);
|
|
962
|
+
|
|
963
|
+
const localMax = recentVals.length > 0 ? Math.max(...recentVals) : currentVal;
|
|
964
|
+
const localMin = recentVals.length > 0 ? Math.min(...recentVals) : currentVal;
|
|
965
|
+
|
|
966
|
+
// --- 1.1 NORMALE PROFONDITÀ (CON SOGLIA STANDARD E FILTRO 2 MINUTI) ---
|
|
967
|
+
if (mode !== 'hercules') {
|
|
968
|
+
if (!store.depthProtectedActive && currentVal <= shallowThreshold) {
|
|
969
|
+
store.depthProtectedActive = true;
|
|
970
|
+
}
|
|
971
|
+
if (store.depthProtectedActive) {
|
|
972
|
+
if (localMin > shallowThreshold) {
|
|
973
|
+
store.depthProtectedActive = false;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
if (store.depthProtectedActive) {
|
|
977
|
+
return { min: 0, max: shallowThreshold };
|
|
978
|
+
}
|
|
979
|
+
const maxHistorico = Math.max(...data);
|
|
980
|
+
return { min: 0, max: Math.max(s.stdMax, Math.ceil(maxHistorico / s.step) * s.step) };
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
// --- 1.2 HERCULES PROFONDITÀ (0 IN BASSO, SNAP SUL MAX SENZA PADDING) ---
|
|
984
|
+
if (mode === 'hercules') {
|
|
985
|
+
const roundStep = s.hercSpan; // Passo di griglia selezionato dall'utente
|
|
986
|
+
|
|
987
|
+
let targetMin = 0;
|
|
988
|
+
let targetMax = Math.ceil(localMax / roundStep) * roundStep;
|
|
989
|
+
|
|
990
|
+
// Impediamo una scala inferiore a 4 metri per sicurezza visiva
|
|
991
|
+
const absoluteMinSpan = 4;
|
|
992
|
+
if (targetMax < absoluteMinSpan) {
|
|
993
|
+
targetMax = absoluteMinSpan;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// Regola asimmetrica per il MAX (Espansione istantanea, contrazione a 2 minuti)
|
|
997
|
+
if (currentVal > currentScale.max) {
|
|
998
|
+
currentScale.max = targetMax;
|
|
999
|
+
} else {
|
|
1000
|
+
const allStableInTarget = recentVals.every(val => val <= targetMax);
|
|
1001
|
+
if (allStableInTarget) {
|
|
1002
|
+
currentScale.max = targetMax;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
currentScale.min = 0;
|
|
1007
|
+
return { min: currentScale.min, max: currentScale.max };
|
|
940
1008
|
}
|
|
941
1009
|
}
|
|
1010
|
+
|
|
1011
|
+
// ==========================================================================
|
|
1012
|
+
// 2. ALTRI GRAFICI (STW, SOG, TWS): COMPORTAMENTO ADATTIVO SU TERZO DEL GRAFICO
|
|
1013
|
+
// ==========================================================================
|
|
942
1014
|
|
|
943
|
-
// --- MODALITÀ
|
|
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
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// --- 2.2 MODALITÀ HERCULES AD ALTO CONTRASTO (SNAP SENZA PADDING) ---
|
|
944
1022
|
if (mode === 'hercules') {
|
|
945
|
-
const
|
|
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);
|
|
946
1027
|
|
|
947
|
-
|
|
948
|
-
|
|
1028
|
+
const localMin = Math.min(...recentThirdData);
|
|
1029
|
+
const localMax = Math.max(...recentThirdData);
|
|
949
1030
|
|
|
950
|
-
//
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
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;
|
|
1034
|
+
|
|
1035
|
+
// Se lo span calcolato è nullo (es. velocità costante), forziamo lo span minimo
|
|
1036
|
+
if (targetMax - targetMin === 0) {
|
|
1037
|
+
targetMax = targetMin + roundStep;
|
|
956
1038
|
}
|
|
957
1039
|
|
|
958
|
-
//
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
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
|
+
}
|
|
962
1054
|
|
|
963
|
-
return { min, max };
|
|
1055
|
+
return { min: currentScale.min, max: currentScale.max };
|
|
964
1056
|
}
|
|
965
|
-
|
|
966
|
-
// Scala Standard (Autocompressione a scatti)
|
|
967
|
-
return { min: 0, max: Math.max(s.stdMax, Math.ceil(aMax / s.step) * s.step) };
|
|
968
1057
|
}
|
|
969
1058
|
|
|
970
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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: {
|
|
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: {
|
|
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: {
|
|
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: {
|
|
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
|
}
|