@sailingrotevista/rotevista-dash 7.0.15 → 7.0.17

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 +43 -29
  2. package/index.js +98 -68
  3. package/package.json +1 -1
package/app.js CHANGED
@@ -23,7 +23,7 @@ let CONFIG = {
23
23
  minSpeed: 0.5,
24
24
  stabilityBreakout: 15
25
25
  },
26
- graphs: { reef1: 10, reef2: 15, historyMinutes: 10, samples: 60 },
26
+ graphs: { reef1: 10, reef2: 15, historyMinutes: 5, samples: 60 },
27
27
  scales: {
28
28
  stw: { stdMax: 4, hercSpan: 2, step: 1 },
29
29
  sog: { stdMax: 4, hercSpan: 2, step: 1 },
@@ -964,11 +964,11 @@ function manageHistory(type, value) {
964
964
  const bucketReady = (now - store.lastUpdates[type] > bucketIntervalMs) || store.histories[type].length === 0;
965
965
  if (!bucketReady) return;
966
966
 
967
- // --- 5. AGGREGAZIONE SEMANTICA ---
967
+ // --- 5. AGGREGAZIONE SEMANTICA (Ottimizzata a zero-allocazioni di array intermedi) ---
968
968
  let finalValue = value;
969
969
 
970
970
  if (tempBuf.length > 0) {
971
- // A. VENTO -> SUSTAINED PEAK (EMA Time-Aware)
971
+ // A. VENTO -> SUSTAINED PEAK (EMA Time-Aware) (0 allocazioni)
972
972
  if (type === 'tws' || type === 'aws') {
973
973
  const tauMs = 2500;
974
974
  let ema = tempBuf[0].val;
@@ -982,17 +982,32 @@ function manageHistory(type, value) {
982
982
  }
983
983
  finalValue = maxSustained;
984
984
  }
985
- // B. PROFONDITÀ -> MINIMO
985
+ // B. PROFONDITÀ -> MINIMO (In-place loop, 0 allocazioni)
986
986
  else if (type === 'depth') {
987
- const vals = tempBuf.map(p => p.val).filter(v => isFinite(v));
988
- if (vals.length > 0) finalValue = Math.min(...vals);
987
+ let minVal = Infinity;
988
+ for (let i = 0; i < tempBuf.length; i++) {
989
+ const v = tempBuf[i].val;
990
+ if (isFinite(v) && v < minVal) {
991
+ minVal = v;
992
+ }
993
+ }
994
+ if (minVal !== Infinity) {
995
+ finalValue = minVal;
996
+ }
989
997
  }
990
- // C. VELOCITÀ -> MEDIA
998
+ // C. VELOCITÀ -> MEDIA (In-place loop, 0 allocazioni)
991
999
  else {
992
- const vals = tempBuf.map(p => p.val).filter(v => isFinite(v));
993
- if (vals.length > 0) {
994
- const sum = vals.reduce((a, b) => a + b, 0);
995
- finalValue = sum / tempBuf.length;
1000
+ let sum = 0;
1001
+ let count = 0;
1002
+ for (let i = 0; i < tempBuf.length; i++) {
1003
+ const v = tempBuf[i].val;
1004
+ if (isFinite(v)) {
1005
+ sum += v;
1006
+ count++;
1007
+ }
1008
+ }
1009
+ if (count > 0) {
1010
+ finalValue = sum / count;
996
1011
  }
997
1012
  }
998
1013
  }
@@ -1201,9 +1216,15 @@ function connect() {
1201
1216
  socket.onclose = () => {
1202
1217
  if (!simulationMode) {
1203
1218
  ui.status.className = "offline";
1204
- ui.status.innerText = "RECONNECTING...";
1205
- setTimeout(connect, reconnectDelay);
1206
- reconnectDelay = Math.min(reconnectDelay * 1.5, 10000);
1219
+ ui.status.innerText = "OFFLINE";
1220
+
1221
+ // Tentiamo la riconnessione automatica solo se lo schermo è attivo e visibile,
1222
+ // evitando cicli di loop di rete infiniti in background mentre il tablet dorme
1223
+ if (document.visibilityState === "visible") {
1224
+ ui.status.innerText = "RECONNECTING...";
1225
+ setTimeout(connect, reconnectDelay);
1226
+ reconnectDelay = Math.min(reconnectDelay * 1.5, 10000);
1227
+ }
1207
1228
  }
1208
1229
  };
1209
1230
  } catch (e) {
@@ -1319,24 +1340,17 @@ async function init() {
1319
1340
  }
1320
1341
  }
1321
1342
 
1322
- // Watchdog per il risveglio dallo stato di sospensione / cambio scheda
1343
+ // Watchdog attivo per la gestione intelligente dello standby e il massimo risparmio energetico
1323
1344
  document.addEventListener('visibilitychange', () => {
1324
1345
  if (document.visibilityState === 'visible') {
1325
- console.log("🔌 [Watchdog] Tab ritornato visibile. Verifica connessione...");
1326
-
1327
- // Se il socket esiste ma non è attivo o se vogliamo forzare la pulizia delle connessioni fantasma:
1346
+ console.log("🔌 [Watchdog] Schermo sbloccato / Tab visibile. Riconnessione immediata e allineamento storico...");
1347
+ // Al risveglio stabiliamo una nuova connessione pulita che scaricherà lo storico accumulato in background dal server
1348
+ connect();
1349
+ } else if (document.visibilityState === 'hidden') {
1350
+ console.log("🔌 [Watchdog] Schermo bloccato / Tab in background. Chiusura WebSocket preventiva per salvaguardare la batteria.");
1351
+ // Tagliamo attivamente la connessione per congelare all'istante l'attività di rete ed i consumi del browser
1328
1352
  if (socket) {
1329
- if (socket.readyState !== WebSocket.OPEN) {
1330
- // Se era già chiuso o in errore, proviamo a riconnettere subito
1331
- connect();
1332
- } else {
1333
- // Se resulta "OPEN" ma potrebbe essere una connessione fantasma,
1334
- // la chiudiamo forzatamente per scatenare la riconnessione pulita e il download della cronologia
1335
- console.log("🔌 [Watchdog] Riavvio precauzionale del WebSocket per evitare connessioni fantasma.");
1336
- socket.close();
1337
- }
1338
- } else {
1339
- connect();
1353
+ socket.close();
1340
1354
  }
1341
1355
  }
1342
1356
  });
package/index.js CHANGED
@@ -351,81 +351,111 @@ module.exports = function (app) {
351
351
  let isTwdType = (type === 'twd');
352
352
 
353
353
  if (tempBuf.length > 0) {
354
- // ==========================================================================
355
- // CASO PARTICOLARE: DIREZIONE VENTO (TWD) -> MEDIA PESATA E LIMITI MIN/MAX
356
- // ==========================================================================
357
- if (isTwdType) {
358
- // 1. Identifica il vento massimo del minuto (in m/s)
359
- const twsVals = tempBuf.map(p => p.tws || 0);
360
- const maxTwsInMinute = Math.max(...twsVals, 0);
361
-
362
- // 2. Calcola la soglia dinamica di pressione: max tra 40% del picco e soglia di calma piatta (in m/s)
363
- const calmThresholdMs = CALM_THRESHOLD_KTS / 1.94384;
364
- const pressureThreshold = Math.max(maxTwsInMinute * PRESSURE_FILTER_RATIO, calmThresholdMs);
365
-
366
- // 3. Esclude le direzioni registrate quando il vento era debole (sotto la soglia di pressione)
367
- const activePoints = tempBuf.filter(p => (p.tws || 0) >= pressureThreshold);
368
- const pointsToUse = activePoints.length > 0 ? activePoints : tempBuf; // Fallback se tutto è calma piatta
369
-
370
- // 4. Media Vettoriale Pesata (TWS * sin, TWS * cos)
371
- let sumSin = 0;
372
- let sumCos = 0;
373
- let totalWeight = 0;
374
- pointsToUse.forEach(p => {
375
- const weight = Math.max(p.tws || 0.1, 0.05); // Evita pesi a zero
376
- sumSin += weight * Math.sin(p.val);
377
- sumCos += weight * Math.cos(p.val);
378
- totalWeight += weight;
379
- });
380
- const avgAngle = Math.atan2(sumSin, sumCos);
381
- const finalAvg = (avgAngle + Math.PI * 2) % (Math.PI * 2);
382
-
383
- // 5. Calcolo di Min e Max angolare del minuto (rispetto alla media per gestire l'oltrepasso di 0/360°)
384
- let diffs = pointsToUse.map(p => {
385
- let diff = p.val - finalAvg;
386
- return Math.atan2(Math.sin(diff), Math.cos(diff)); // Srotolamento tra -PI e +PI
387
- });
388
-
389
- const minDiff = Math.min(...diffs);
390
- const maxDiff = Math.max(...diffs);
391
-
392
- const finalMin = (finalAvg + minDiff + Math.PI * 2) % (Math.PI * 2);
393
- const finalMax = (finalAvg + maxDiff + Math.PI * 2) % (Math.PI * 2);
394
-
395
- finalValue = {
396
- val: finalAvg,
397
- min: finalMin,
398
- max: finalMax
399
- };
354
+ // ==========================================================================
355
+ // CASO PARTICOLARE: DIREZIONE VENTO (TWD) -> MEDIA PESATA E LIMITI MIN/MAX
356
+ // ==========================================================================
357
+ if (isTwdType) {
358
+ // 1. Identifica il vento massimo del minuto (in m/s) - Loop in-place (0 allocazioni)
359
+ let maxTwsInMinute = 0;
360
+ for (let i = 0; i < tempBuf.length; i++) {
361
+ const tws = tempBuf[i].tws || 0;
362
+ if (tws > maxTwsInMinute) maxTwsInMinute = tws;
400
363
  }
401
- // A. VENTO VELOCITÀ -> SUSTAINED PEAK (EMA Time-Aware)
402
- else if (type === 'tws' || type === 'aws') {
403
- const tauMs = 2500;
404
- let ema = tempBuf[0].val;
405
- let maxSustained = ema;
406
-
407
- for (let i = 1; i < tempBuf.length; i++) {
408
- const dt = Math.max(1, tempBuf[i].time - tempBuf[i-1].time);
409
- const alpha = 1 - Math.exp(-dt / tauMs);
410
- ema = (tempBuf[i].val * alpha) + (ema * (1 - alpha));
411
- if (isFinite(ema) && ema > maxSustained) maxSustained = ema;
364
+
365
+ // 2. Calcola la soglia dinamica di pressione
366
+ const calmThresholdMs = CALM_THRESHOLD_KTS / 1.94384;
367
+ const pressureThreshold = Math.max(maxTwsInMinute * PRESSURE_FILTER_RATIO, calmThresholdMs);
368
+
369
+ // 3. Determina in un unico passaggio se ci sono punti attivi sopra la soglia (0 allocazioni)
370
+ let hasActivePoints = false;
371
+ for (let i = 0; i < tempBuf.length; i++) {
372
+ if ((tempBuf[i].tws || 0) >= pressureThreshold) {
373
+ hasActivePoints = true;
374
+ break;
412
375
  }
413
- finalValue = maxSustained;
414
376
  }
415
- // B. PROFONDITÀ -> MINIMO
416
- else if (type === 'depth') {
417
- const vals = tempBuf.map(p => p.val).filter(v => isFinite(v));
418
- if (vals.length > 0) finalValue = Math.min(...vals);
377
+
378
+ // 4. Media Vettoriale Pesata (0 allocazioni di array)
379
+ let sumSin = 0;
380
+ let sumCos = 0;
381
+ let totalWeight = 0;
382
+ for (let i = 0; i < tempBuf.length; i++) {
383
+ const p = tempBuf[i];
384
+ if (hasActivePoints && (p.tws || 0) < pressureThreshold) continue;
385
+
386
+ const weight = Math.max(p.tws || 0.1, 0.05);
387
+ sumSin += weight * Math.sin(p.val);
388
+ sumCos += weight * Math.cos(p.val);
389
+ totalWeight += weight;
419
390
  }
420
- // C. VELOCITÀ BARCA / ALTRO -> MEDIA
421
- else {
422
- const vals = tempBuf.map(p => p.val).filter(v => isFinite(v));
423
- if (vals.length > 0) {
424
- const sum = vals.reduce((a, b) => a + b, 0);
425
- finalValue = sum / tempBuf.length;
391
+ const avgAngle = Math.atan2(sumSin, sumCos);
392
+ const finalAvg = (avgAngle + Math.PI * 2) % (Math.PI * 2);
393
+
394
+ // 5. Calcolo di Min e Max angolare del minuto (0 allocazioni di array)
395
+ let minDiff = 0;
396
+ let maxDiff = 0;
397
+ let firstDiff = true;
398
+ for (let i = 0; i < tempBuf.length; i++) {
399
+ const p = tempBuf[i];
400
+ if (hasActivePoints && (p.tws || 0) < pressureThreshold) continue;
401
+
402
+ const diff = Math.atan2(Math.sin(p.val - finalAvg), Math.cos(p.val - finalAvg));
403
+ if (firstDiff) {
404
+ minDiff = diff;
405
+ maxDiff = diff;
406
+ firstDiff = false;
407
+ } else {
408
+ if (diff < minDiff) minDiff = diff;
409
+ if (diff > maxDiff) maxDiff = diff;
426
410
  }
427
411
  }
412
+
413
+ const finalMin = (finalAvg + minDiff + Math.PI * 2) % (Math.PI * 2);
414
+ const finalMax = (finalAvg + maxDiff + Math.PI * 2) % (Math.PI * 2);
415
+
416
+ finalValue = {
417
+ val: finalAvg,
418
+ min: finalMin,
419
+ max: finalMax
420
+ };
428
421
  }
422
+ // A. VENTO VELOCITÀ -> SUSTAINED PEAK (EMA Time-Aware) (Inalterato per minimizzare le modifiche)
423
+ else if (type === 'tws' || type === 'aws') {
424
+ const tauMs = 2500;
425
+ let ema = tempBuf[0].val;
426
+ let maxSustained = ema;
427
+
428
+ for (let i = 1; i < tempBuf.length; i++) {
429
+ const dt = Math.max(1, tempBuf[i].time - tempBuf[i-1].time);
430
+ const alpha = 1 - Math.exp(-dt / tauMs);
431
+ ema = (tempBuf[i].val * alpha) + (ema * (1 - alpha));
432
+ if (isFinite(ema) && ema > maxSustained) maxSustained = ema;
433
+ }
434
+ finalValue = maxSustained;
435
+ }
436
+ // B. PROFONDITÀ -> MINIMO (Ottimizzato a 0 allocazioni)
437
+ else if (type === 'depth') {
438
+ let minVal = Infinity;
439
+ for (let i = 0; i < tempBuf.length; i++) {
440
+ const v = tempBuf[i].val;
441
+ if (isFinite(v) && v < minVal) minVal = v;
442
+ }
443
+ if (minVal !== Infinity) finalValue = minVal;
444
+ }
445
+ // C. VELOCITÀ BARCA / ALTRO -> MEDIA (Ottimizzato a 0 allocazioni)
446
+ else {
447
+ let sum = 0;
448
+ let count = 0;
449
+ for (let i = 0; i < tempBuf.length; i++) {
450
+ const v = tempBuf[i].val;
451
+ if (isFinite(v)) {
452
+ sum += v;
453
+ count++;
454
+ }
455
+ }
456
+ if (count > 0) finalValue = sum / count;
457
+ }
458
+ }
429
459
 
430
460
  // Validazione e clamping di sicurezza (non si applica all'oggetto TWD)
431
461
  if (!isTwdType) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sailingrotevista/rotevista-dash",
3
- "version": "7.0.15",
3
+ "version": "7.0.17",
4
4
  "description": "Wind Dashboard with navigation and course aids",
5
5
  "main": "index.js",
6
6
  "publishConfig": {