@sailingrotevista/rotevista-dash 6.0.8 → 6.0.11
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 +143 -44
- package/icons/dash.png +0 -0
- package/index.html +16 -9
- package/manifest.json +2 -2
- package/package.json +1 -1
- package/style.css +1 -3
package/app.js
CHANGED
|
@@ -115,6 +115,15 @@ function getShortestRotation(curr, target) {
|
|
|
115
115
|
return curr + diff;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Scrive il testo nel DOM solo se il valore è realmente cambiato (Evita layout reflow inutili)
|
|
120
|
+
*/
|
|
121
|
+
function safeSetText(el, text) {
|
|
122
|
+
if (el && el.innerText !== text) {
|
|
123
|
+
el.innerText = text;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
118
127
|
/**
|
|
119
128
|
* Inserimento sicuro nel buffer con trigonometria precaricata e pruning automatico
|
|
120
129
|
*/
|
|
@@ -614,7 +623,7 @@ function startDisplayLoop() {
|
|
|
614
623
|
|
|
615
624
|
// --- AGGIORNAMENTO VELOCITÀ SULL'ACQUA (STW) ---
|
|
616
625
|
if (store.raw["navigation.speedThroughWater"] !== undefined) {
|
|
617
|
-
ui.stw
|
|
626
|
+
safeSetText(ui.stw, stwKts.toFixed(1));
|
|
618
627
|
ui.stw.style.color = ""; // Neutro
|
|
619
628
|
manageHistory('stw', stwKts);
|
|
620
629
|
}
|
|
@@ -627,11 +636,11 @@ function startDisplayLoop() {
|
|
|
627
636
|
|
|
628
637
|
const labelSogVmg = document.getElementById('sog-vmg-label');
|
|
629
638
|
if (displayModeSog === 'VMG') {
|
|
630
|
-
ui.sog
|
|
639
|
+
safeSetText(ui.sog, vmgVal.toFixed(1));
|
|
631
640
|
ui.sog.style.setProperty('color', '#00b8d4', 'important'); // Cyan
|
|
632
641
|
if (labelSogVmg) labelSogVmg.textContent = 'VMG';
|
|
633
642
|
} else {
|
|
634
|
-
ui.sog
|
|
643
|
+
safeSetText(ui.sog, sogKts.toFixed(1));
|
|
635
644
|
if (labelSogVmg) labelSogVmg.textContent = 'SOG';
|
|
636
645
|
|
|
637
646
|
if (isNavigating) {
|
|
@@ -650,7 +659,7 @@ function startDisplayLoop() {
|
|
|
650
659
|
|
|
651
660
|
// --- AGGIORNAMENTO PROFONDITÀ (DEPTH) ---
|
|
652
661
|
if (store.raw["environment.depth.belowTransducer"] !== undefined) {
|
|
653
|
-
ui.depth
|
|
662
|
+
safeSetText(ui.depth, store.raw["environment.depth.belowTransducer"].toFixed(1));
|
|
654
663
|
checkDepthAlarm(store.raw["environment.depth.belowTransducer"]);
|
|
655
664
|
manageHistory('depth', store.raw["environment.depth.belowTransducer"]);
|
|
656
665
|
}
|
|
@@ -666,7 +675,7 @@ function startDisplayLoop() {
|
|
|
666
675
|
const labelWind = document.getElementById('tws-aws-label');
|
|
667
676
|
const currentWind = (displayModeTws === 'AWS') ? awsVal : twsVal;
|
|
668
677
|
|
|
669
|
-
ui.tws
|
|
678
|
+
safeSetText(ui.tws, currentWind.toFixed(1));
|
|
670
679
|
if (labelWind) labelWind.textContent = displayModeTws;
|
|
671
680
|
|
|
672
681
|
if (currentWind >= CONFIG.graphs.reef2) {
|
|
@@ -684,7 +693,7 @@ function startDisplayLoop() {
|
|
|
684
693
|
}
|
|
685
694
|
|
|
686
695
|
if (store.raw["environment.wind.speedApparent"] !== undefined) {
|
|
687
|
-
ui.awsSvg
|
|
696
|
+
safeSetText(ui.awsSvg, awsVal.toFixed(1));
|
|
688
697
|
}
|
|
689
698
|
|
|
690
699
|
// --- PUNTATORI ANALOGICI ---
|
|
@@ -702,9 +711,8 @@ function startDisplayLoop() {
|
|
|
702
711
|
updateLeewayDisplay(Math.max(-20, Math.min(20, smoothedLeeway)));
|
|
703
712
|
}
|
|
704
713
|
|
|
705
|
-
// ---
|
|
706
|
-
if (lastAvgUIUpdate++ %
|
|
707
|
-
['stw', 'sog', 'depth', 'tws'].forEach(refreshGraph);
|
|
714
|
+
// --- SLOW TIER (Salvataggio stato ogni 10 secondi) ---
|
|
715
|
+
if (lastAvgUIUpdate++ % 10 === 0) {
|
|
708
716
|
saveDashboardState();
|
|
709
717
|
}
|
|
710
718
|
|
|
@@ -949,6 +957,10 @@ function manageHistory(type, value) {
|
|
|
949
957
|
|
|
950
958
|
// --- 7. STORAGE STORICO ---
|
|
951
959
|
store.histories[type].push({ val: finalValue, time: now });
|
|
960
|
+
|
|
961
|
+
// --- TRIGGER STRATEGIA 1 (EVENT-DRIVEN) ---
|
|
962
|
+
// Ridisegnamo lo strumento SOLO nell'esatto istante in cui viene generato un nuovo punto storico!
|
|
963
|
+
refreshGraph(type);
|
|
952
964
|
|
|
953
965
|
// --- 8. PRUNING DINAMICO ---
|
|
954
966
|
const maxViewportMinutes = historyMinutes * 2;
|
|
@@ -958,9 +970,8 @@ function manageHistory(type, value) {
|
|
|
958
970
|
store.histories[type].shift();
|
|
959
971
|
}
|
|
960
972
|
|
|
961
|
-
// Reset per il prossimo bucket
|
|
973
|
+
// Reset per il prossimo bucket (sincronizzato)
|
|
962
974
|
store.graphTempBuf[type] = [];
|
|
963
|
-
// Spostiamo il timer esattamente al confine del secchiello assoluto appena concluso
|
|
964
975
|
store.lastUpdates[type] = Math.floor(now / bucketIntervalMs) * bucketIntervalMs;
|
|
965
976
|
}
|
|
966
977
|
|
|
@@ -1018,30 +1029,36 @@ function calculateScale(type, data, mode) {
|
|
|
1018
1029
|
return { min: 0, max: Math.max(s.stdMax, Math.ceil(maxHistorico / s.step) * s.step) };
|
|
1019
1030
|
}
|
|
1020
1031
|
|
|
1021
|
-
// --- 1.2 HERCULES PROFONDITÀ (0 IN
|
|
1032
|
+
// --- 1.2 HERCULES PROFONDITÀ (BASE 0 IN ACQUE BASSE, FLUTTUANTE AL LARGO) ---
|
|
1022
1033
|
if (mode === 'hercules') {
|
|
1023
1034
|
const roundStep = s.hercSpan; // Passo di griglia selezionato dall'utente
|
|
1035
|
+
const shallowThreshold = Math.max(s.stdMax, 10); // Es. 20m
|
|
1024
1036
|
|
|
1025
1037
|
let targetMin = 0;
|
|
1038
|
+
// Se siamo in acque profonde, lasciamo che il minimo fluttui per mostrare i micro-dettagli
|
|
1039
|
+
if (localMin > shallowThreshold) {
|
|
1040
|
+
targetMin = Math.max(0, Math.floor(localMin / roundStep) * roundStep);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1026
1043
|
let targetMax = Math.ceil(localMax / roundStep) * roundStep;
|
|
1027
1044
|
|
|
1028
|
-
// Impediamo
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
targetMax = absoluteMinSpan;
|
|
1045
|
+
// Impediamo uno span inferiore a 4 metri per evitare grafici piatti
|
|
1046
|
+
if (targetMax - targetMin < 4) {
|
|
1047
|
+
targetMax = targetMin + 4;
|
|
1032
1048
|
}
|
|
1033
1049
|
|
|
1034
|
-
// Regola asimmetrica per il MAX (Espansione istantanea, contrazione a 2 minuti)
|
|
1035
|
-
if (currentVal > currentScale.max) {
|
|
1036
|
-
currentScale.
|
|
1050
|
+
// Regola asimmetrica per il MIN e il MAX (Espansione istantanea, contrazione a 2 minuti)
|
|
1051
|
+
if (currentVal < currentScale.min || currentVal > currentScale.max) {
|
|
1052
|
+
currentScale.min = Math.min(currentScale.min, targetMin);
|
|
1053
|
+
currentScale.max = Math.max(currentScale.max, targetMax);
|
|
1037
1054
|
} else {
|
|
1038
|
-
const allStableInTarget = recentVals.every(val => val <= targetMax);
|
|
1055
|
+
const allStableInTarget = recentVals.every(val => val >= targetMin && val <= targetMax);
|
|
1039
1056
|
if (allStableInTarget) {
|
|
1057
|
+
currentScale.min = targetMin;
|
|
1040
1058
|
currentScale.max = targetMax;
|
|
1041
1059
|
}
|
|
1042
1060
|
}
|
|
1043
1061
|
|
|
1044
|
-
currentScale.min = 0;
|
|
1045
1062
|
return { min: currentScale.min, max: currentScale.max };
|
|
1046
1063
|
}
|
|
1047
1064
|
}
|
|
@@ -1100,10 +1117,21 @@ function updateScaleLabels(t, min, max) {
|
|
|
1100
1117
|
}
|
|
1101
1118
|
|
|
1102
1119
|
/**
|
|
1103
|
-
* refreshGraph: Recupero dati, switch AWS/TWS e passaggio a motore grafico.
|
|
1120
|
+
* refreshGraph: Recupero dati, switch AWS/TWS/VMG e passaggio a motore grafico.
|
|
1121
|
+
* Redirige e protegge i canali secondari (AWS/VMG) evitando ridisegni inutili.
|
|
1104
1122
|
*/
|
|
1105
1123
|
function refreshGraph(t) {
|
|
1106
|
-
|
|
1124
|
+
// Redirezione di sicurezza dei canali secondari (AWS / VMG) verso i contenitori principali
|
|
1125
|
+
if (t === 'aws') {
|
|
1126
|
+
if (displayModeTws !== 'AWS') return; // Se non stiamo visualizzando AWS a schermo, ignora il rinfresco
|
|
1127
|
+
t = 'tws';
|
|
1128
|
+
}
|
|
1129
|
+
if (t === 'vmg') {
|
|
1130
|
+
if (displayModeSog !== 'VMG') return; // Se non stiamo visualizzando VMG a schermo, ignora il rinfresco
|
|
1131
|
+
t = 'sog';
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
const boxType = t;
|
|
1107
1135
|
let rawData;
|
|
1108
1136
|
|
|
1109
1137
|
if (t === 'tws' && displayModeTws === 'AWS') {
|
|
@@ -1128,6 +1156,7 @@ function refreshGraph(t) {
|
|
|
1128
1156
|
/**
|
|
1129
1157
|
* drawGraph: Motore SVG con Timeline Reale e Gestione GAP
|
|
1130
1158
|
* Risolve i conflitti di orologio (Clock Drift) tra Cerbo GX e Tablet.
|
|
1159
|
+
* Integra la reattività dello spessore della curva in modalità Dual Screen (Focus).
|
|
1131
1160
|
*/
|
|
1132
1161
|
function drawGraph(d, id, min, max, isTws, isHercules) {
|
|
1133
1162
|
const svg = document.getElementById(id);
|
|
@@ -1142,6 +1171,10 @@ function drawGraph(d, id, min, max, isTws, isHercules) {
|
|
|
1142
1171
|
const latestPoint = d[d.length - 1];
|
|
1143
1172
|
const now = latestPoint ? latestPoint.time : Date.now();
|
|
1144
1173
|
|
|
1174
|
+
// --- RILEVAMENTO DINAMICO DUAL SCREEN BLINDATO ---
|
|
1175
|
+
const box = svg.closest('.data-box');
|
|
1176
|
+
const isFocused = isFocusActive && box && box.classList.contains('is-focused');
|
|
1177
|
+
|
|
1145
1178
|
const visibleMinutes = CONFIG.graphs.historyMinutes * (isNavigating ? 1 : 2);
|
|
1146
1179
|
const viewportMs = visibleMinutes * 60000;
|
|
1147
1180
|
const viewportStart = now - viewportMs;
|
|
@@ -1153,15 +1186,20 @@ function drawGraph(d, id, min, max, isTws, isHercules) {
|
|
|
1153
1186
|
const colDepth = "#0088cc", colStw = "#00C851", colSog = "#ffbb33", colVmg = "#00b8d4";
|
|
1154
1187
|
|
|
1155
1188
|
const getColorProps = (val) => {
|
|
1156
|
-
|
|
1189
|
+
// Se siamo in Dual Screen (focus), aumentiamo lo spessore base della curva di un filino (da 1.6 a 2.2)
|
|
1190
|
+
const baseStroke = isFocused ? "4.2" : "1.6";
|
|
1191
|
+
const alertStroke = isFocused ? "4.8" : "2.2";
|
|
1192
|
+
const warnStroke = isFocused ? "4.4" : "1.8";
|
|
1193
|
+
|
|
1194
|
+
let color = colTws, opacity = "0.15", stroke = baseStroke;
|
|
1157
1195
|
if (isTws) {
|
|
1158
1196
|
const baseWind = (displayModeTws === 'AWS') ? colAws : colTws;
|
|
1159
|
-
if (val >= CONFIG.graphs.reef2) { color = colDanger; opacity = "0.55"; stroke =
|
|
1160
|
-
else if (val >= CONFIG.graphs.reef1) { color = colWarning; opacity = "0.45"; stroke =
|
|
1197
|
+
if (val >= CONFIG.graphs.reef2) { color = colDanger; opacity = "0.55"; stroke = alertStroke; }
|
|
1198
|
+
else if (val >= CONFIG.graphs.reef1) { color = colWarning; opacity = "0.45"; stroke = warnStroke; }
|
|
1161
1199
|
else color = baseWind;
|
|
1162
1200
|
} else if (isDepth) {
|
|
1163
|
-
if (val < CONFIG.alarms.depthDanger) { color = colDanger; opacity = "0.55"; stroke =
|
|
1164
|
-
else if (val < CONFIG.alarms.depthWarning) { color = colWarning; opacity = "0.45"; stroke =
|
|
1201
|
+
if (val < CONFIG.alarms.depthDanger) { color = colDanger; opacity = "0.55"; stroke = alertStroke; }
|
|
1202
|
+
else if (val < CONFIG.alarms.depthWarning) { color = colWarning; opacity = "0.45"; stroke = warnStroke; }
|
|
1165
1203
|
else color = colDepth;
|
|
1166
1204
|
} else {
|
|
1167
1205
|
if (id === 'stw-graph') color = colStw;
|
|
@@ -1171,14 +1209,37 @@ function drawGraph(d, id, min, max, isTws, isHercules) {
|
|
|
1171
1209
|
};
|
|
1172
1210
|
|
|
1173
1211
|
let grids = "";
|
|
1174
|
-
//
|
|
1175
|
-
|
|
1176
|
-
[0.25, 0.5, 0.75].forEach(p => grids += `<line x1="0" y1="${h-(p*h)}" x2="${w}" y2="${h-(p*h)}" stroke="rgba(0,0,0,0.12)" stroke-width="0.5" />`);
|
|
1212
|
+
// --- GRIGLIE ORIZZONTALI (Spessore fisso a 0.5px, non deformabile) ---
|
|
1213
|
+
[0.25, 0.5, 0.75].forEach(p => grids += `<line x1="0" y1="${h-(p*h)}" x2="${w}" y2="${h-(p*h)}" stroke="rgba(0,0,0,0.12)" stroke-width="0.5" vector-effect="non-scaling-stroke" />`);
|
|
1177
1214
|
|
|
1215
|
+
// --- GRIGLIE VERTICALI (Spessore fisso a 0.4px, non deformabile) ---
|
|
1178
1216
|
const gridInterval = (visibleMinutes <= 15) ? 1 : 5;
|
|
1179
1217
|
for (let m = gridInterval; m < visibleMinutes; m += gridInterval) {
|
|
1180
1218
|
const x = w - ((m / visibleMinutes) * w);
|
|
1181
|
-
grids += `<line x1="${x}" y1="0" x2="${x}" y2="${h}" stroke="rgba(0,0,0,0.08)" stroke-width="0.
|
|
1219
|
+
grids += `<line x1="${x}" y1="0" x2="${x}" y2="${h}" stroke="rgba(0,0,0,0.08)" stroke-width="0.4" vector-effect="non-scaling-stroke" />`;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// --- DISEGNO LINEE DI SICUREZZA ALLARME PROFONDITÀ (DANGER & WARNING) ---
|
|
1223
|
+
if (isDepth) {
|
|
1224
|
+
const dangerVal = CONFIG.alarms.depthDanger; // Es. 2.5m
|
|
1225
|
+
const warningVal = CONFIG.alarms.depthWarning; // Es. 3.5m
|
|
1226
|
+
const marginX = 4; // Margine per non toccare i bordi esterni
|
|
1227
|
+
|
|
1228
|
+
// Spessore dinamico per gli allarmi: 4.8px in Dual Screen, 1.8px nella griglia normale
|
|
1229
|
+
const alarmStrokeWidth = isFocused ? "4.8" : "1.8";
|
|
1230
|
+
|
|
1231
|
+
// Linea Rossa Viva (Spessore dinamico, con il tuo tratteggio alternato personalizzato)
|
|
1232
|
+
if (dangerVal >= min && dangerVal <= max) {
|
|
1233
|
+
const p = (dangerVal - min) / range;
|
|
1234
|
+
const y = h - (p * h);
|
|
1235
|
+
grids += `<line x1="${marginX}" y1="${y}" x2="${w - marginX}" y2="${y}" stroke="rgba(255, 59, 48, 0.95)" stroke-width="${alarmStrokeWidth}" stroke-dasharray="12, 6, 2, 6" vector-effect="non-scaling-stroke" />`;
|
|
1236
|
+
}
|
|
1237
|
+
// Linea Giallo Oro Viva (Spessore dinamico, con il tuo tratteggio alternato personalizzato)
|
|
1238
|
+
if (warningVal >= min && warningVal <= max) {
|
|
1239
|
+
const p = (warningVal - min) / range;
|
|
1240
|
+
const y = h - (p * h);
|
|
1241
|
+
grids += `<line x1="${marginX}" y1="${y}" x2="${w - marginX}" y2="${y}" stroke="rgba(255, 204, 0, 0.95)" stroke-width="${alarmStrokeWidth}" stroke-dasharray="6 , 2, 6, 12" vector-effect="non-scaling-stroke" />`;
|
|
1242
|
+
}
|
|
1182
1243
|
}
|
|
1183
1244
|
|
|
1184
1245
|
let gradientStops = "", lines = "", areaPath = "";
|
|
@@ -1208,7 +1269,8 @@ function drawGraph(d, id, min, max, isTws, isHercules) {
|
|
|
1208
1269
|
started = false;
|
|
1209
1270
|
}
|
|
1210
1271
|
} else {
|
|
1211
|
-
|
|
1272
|
+
// Curva del grafico: spessore reattivo al focus e protezione da deformazione (non-scaling)
|
|
1273
|
+
lines += `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" style="stroke:${props.color}; stroke-width:${props.stroke}; stroke-linecap:round; shape-rendering:geometricPrecision;" vector-effect="non-scaling-stroke" />`;
|
|
1212
1274
|
if (!started) {
|
|
1213
1275
|
areaPath += `M ${Math.max(0, x1)} ${h} L ${Math.max(0, x1)} ${y1} `;
|
|
1214
1276
|
started = true;
|
|
@@ -1235,8 +1297,20 @@ function toggleFocusMode(type, element) {
|
|
|
1235
1297
|
const container = document.querySelector('.main-container');
|
|
1236
1298
|
const isLeft = ['stw', 'sog', 'hdg', 'cog', 'tack'].includes(type);
|
|
1237
1299
|
isFocusActive = !isFocusActive;
|
|
1238
|
-
if (isFocusActive) {
|
|
1239
|
-
|
|
1300
|
+
if (isFocusActive) {
|
|
1301
|
+
container.classList.add('focus-active', isLeft ? 'focus-side-left' : 'focus-side-right');
|
|
1302
|
+
element.classList.add('is-focused');
|
|
1303
|
+
} else {
|
|
1304
|
+
container.classList.remove('focus-active', 'focus-side-left', 'focus-side-right');
|
|
1305
|
+
document.querySelectorAll('.data-box').forEach(b => b.classList.remove('is-focused'));
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// --- FORZATURA REDRAW IMMEDIATO DUAL SCREEN ---
|
|
1309
|
+
// Quando entriamo o usciamo dal Focus, ridisegnamo subito lo strumento
|
|
1310
|
+
// interessato per applicare istantaneamente il cambio di spessore della linea!
|
|
1311
|
+
if (['stw', 'sog', 'tws', 'depth'].includes(type)) {
|
|
1312
|
+
refreshGraph(type);
|
|
1313
|
+
}
|
|
1240
1314
|
}
|
|
1241
1315
|
|
|
1242
1316
|
['stw', 'sog', 'tws', 'depth'].forEach(type => {
|
|
@@ -1275,8 +1349,10 @@ function toggleFocusMode(type, element) {
|
|
|
1275
1349
|
} else if (!isFocusActive) {
|
|
1276
1350
|
if (type === 'sog') {
|
|
1277
1351
|
displayModeSog = (displayModeSog === 'SOG') ? 'VMG' : 'SOG';
|
|
1352
|
+
refreshGraph('sog'); // --- FORZA RINFRESCO ISTANTANEO AL CAMBIO SOG/VMG ---
|
|
1278
1353
|
} else if (type === 'tws') {
|
|
1279
1354
|
displayModeTws = (displayModeTws === 'TWS') ? 'AWS' : 'TWS';
|
|
1355
|
+
refreshGraph('tws'); // --- FORZA RINFRESCO ISTANTANEO AL CAMBIO TWS/AWS ---
|
|
1280
1356
|
}
|
|
1281
1357
|
el.style.backgroundColor = "rgba(0, 0, 0, 0.05)";
|
|
1282
1358
|
setTimeout(() => el.style.backgroundColor = "", 150);
|
|
@@ -1404,22 +1480,45 @@ async function init() {
|
|
|
1404
1480
|
// RICONNESSIONE RAPIDA AL RISVEGLIO (VISIBILITY WATCHDOG)
|
|
1405
1481
|
// ==========================================================================
|
|
1406
1482
|
|
|
1407
|
-
// Quando sblocchi l'iPad o riapri
|
|
1408
|
-
// WebSocket orfano. Questo farà scattare immediatamente connect() e la sincronizzazione.
|
|
1483
|
+
// Quando sblocchi l'iPad o riapri la scheda, sincronizziamo in modo intelligente
|
|
1409
1484
|
document.addEventListener('visibilitychange', () => {
|
|
1410
1485
|
if (document.visibilityState === 'visible') {
|
|
1411
|
-
|
|
1412
|
-
if (socket) {
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1486
|
+
// 1. Se la connessione è già attiva e sana, scarichiamo solo lo storico senza disconnetterci
|
|
1487
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
1488
|
+
console.log("⏰ Schermo sbloccato: connessione attiva, sincronizzazione dello storico.");
|
|
1489
|
+
fetchServerHistory().then(() => {
|
|
1490
|
+
['stw', 'sog', 'depth', 'tws'].forEach(refreshGraph);
|
|
1491
|
+
}).catch(err => {});
|
|
1492
|
+
}
|
|
1493
|
+
// 2. Se la connessione è persa o morta, forzatura riconnessione rapida
|
|
1494
|
+
else {
|
|
1495
|
+
console.log("⏰ Schermo sbloccato: connessione assente, forzatura riconnessione rapida.");
|
|
1496
|
+
if (socket) {
|
|
1497
|
+
try {
|
|
1498
|
+
socket.close();
|
|
1499
|
+
} catch (e) {
|
|
1500
|
+
connect();
|
|
1501
|
+
}
|
|
1502
|
+
} else {
|
|
1416
1503
|
connect();
|
|
1417
1504
|
}
|
|
1418
|
-
} else {
|
|
1419
|
-
connect();
|
|
1420
1505
|
}
|
|
1421
1506
|
}
|
|
1422
1507
|
});
|
|
1508
|
+
|
|
1509
|
+
// Rileva se il dispositivo è andato in sospensione misurando il ritardo dei secondi
|
|
1510
|
+
let lastHeartbeat = Date.now();
|
|
1511
|
+
setInterval(() => {
|
|
1512
|
+
const now = Date.now();
|
|
1513
|
+
const diff = now - lastHeartbeat;
|
|
1514
|
+
lastHeartbeat = now;
|
|
1515
|
+
|
|
1516
|
+
// Se passano più di 6 secondi tra un ciclo e l'altro (invece di 1 secondo),
|
|
1517
|
+
// significa che il PC era in sospensione. Avviamo il risveglio.
|
|
1518
|
+
if (diff > 6000) {
|
|
1519
|
+
handleWakeUp();
|
|
1520
|
+
}
|
|
1521
|
+
}, 1000);
|
|
1423
1522
|
}
|
|
1424
1523
|
|
|
1425
1524
|
|
package/icons/dash.png
ADDED
|
Binary file
|
package/index.html
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="it">
|
|
3
|
-
<head>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
6
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
7
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
8
|
+
<link rel="manifest" href="manifest.json">
|
|
9
|
+
|
|
10
|
+
<!-- Icona standard per la scheda del browser (Favicon) -->
|
|
11
|
+
<link rel="icon" type="image/png" href="icons/dash.png">
|
|
12
|
+
|
|
13
|
+
<!-- Icona per Bookmark e schermata Home su Apple iPad, iPhone e Mac -->
|
|
14
|
+
<link rel="apple-touch-icon" href="icons/dash.png">
|
|
15
|
+
|
|
16
|
+
<title>Sailing Dashboard Pro</title>
|
|
17
|
+
<link rel="stylesheet" href="style.css">
|
|
18
|
+
</head>
|
|
12
19
|
<body>
|
|
13
20
|
|
|
14
21
|
<div class="main-container">
|
package/manifest.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Signal K Wind Dashboard",
|
|
4
4
|
"icons": [
|
|
5
5
|
{
|
|
6
|
-
"src": "
|
|
6
|
+
"src": "./icons/dash.png",
|
|
7
7
|
"type": "image/png",
|
|
8
8
|
"sizes": "512x512"
|
|
9
9
|
}
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
"scope": "./",
|
|
15
15
|
"theme_color": "#000000",
|
|
16
16
|
"orientation": "landscape"
|
|
17
|
-
}
|
|
17
|
+
}
|
package/package.json
CHANGED
package/style.css
CHANGED
|
@@ -175,8 +175,7 @@ body.night-mode .alarm-danger {
|
|
|
175
175
|
|
|
176
176
|
.focus-active .is-focused .sparkline path,
|
|
177
177
|
.focus-active .is-focused .sparkline line {
|
|
178
|
-
|
|
179
|
-
}
|
|
178
|
+
}
|
|
180
179
|
|
|
181
180
|
.focus-active.focus-side-left {
|
|
182
181
|
grid-template-columns: 3fr 2fr !important;
|
|
@@ -415,7 +414,6 @@ body.night-mode .alarm-danger {
|
|
|
415
414
|
rect[fill="#222"] { fill: #eee !important; }
|
|
416
415
|
#leeway-val { color: #000 !important; }
|
|
417
416
|
|
|
418
|
-
#awa-pointer, #twa-pointer, #track-pointer, #twd-arrow, #twd-boat-wrap { transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1); }
|
|
419
417
|
#wind-gauge { width: 100%; height: 100%; max-height: 100%; object-fit: contain; }
|
|
420
418
|
|
|
421
419
|
/* ==========================================================================
|