@sailingrotevista/rotevista-dash 6.0.1 → 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.
- package/app.js +131 -1
- package/index.html +1 -1
- 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 = 15000;
|
|
37
37
|
const SIM_SAMPLE_INTERVAL = 1000;
|
|
38
|
-
const DASH_VERSION = "
|
|
38
|
+
const DASH_VERSION = "6.0"; // Major Update: Smart Source Locking & Breathing Hercules Scale
|
|
39
39
|
|
|
40
40
|
// ==========================================================================
|
|
41
41
|
// 2. STATO GLOBALE E RIFERIMENTI UI
|
|
@@ -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