351 lines
10 KiB
JavaScript
351 lines
10 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const ARCHIVE_FILE = path.join(__dirname, 'hourly_archive.json');
|
|
|
|
// Cache dati OpenMeteo condivisi (evita chiamate duplicate)
|
|
let sharedWeatherData = {
|
|
forecast: null,
|
|
waves: null,
|
|
units: null, // Unità di misura globali
|
|
lastUpdate: null,
|
|
updateInterval: 2 * 60 * 1000 // 2 minuti
|
|
};
|
|
|
|
// Archivio dati orari
|
|
let hourlyArchive = {
|
|
temperature: [],
|
|
windSpeed: [],
|
|
windDirection: [],
|
|
waveHeight: [],
|
|
wavePeriod: [],
|
|
waveDirection: [],
|
|
humidity: [],
|
|
pressure: []
|
|
};
|
|
|
|
/**
|
|
* Carica l'archivio da file
|
|
*/
|
|
function loadArchive() {
|
|
try {
|
|
if (fs.existsSync(ARCHIVE_FILE)) {
|
|
const data = fs.readFileSync(ARCHIVE_FILE, 'utf8');
|
|
const parsed = JSON.parse(data);
|
|
// Valida struttura archivio
|
|
if (parsed && typeof parsed === 'object') {
|
|
hourlyArchive = {
|
|
temperature: Array.isArray(parsed.temperature) ? parsed.temperature : [],
|
|
windSpeed: Array.isArray(parsed.windSpeed) ? parsed.windSpeed : [],
|
|
windDirection: Array.isArray(parsed.windDirection) ? parsed.windDirection : [],
|
|
waveHeight: Array.isArray(parsed.waveHeight) ? parsed.waveHeight : [],
|
|
wavePeriod: Array.isArray(parsed.wavePeriod) ? parsed.wavePeriod : [],
|
|
waveDirection: Array.isArray(parsed.waveDirection) ? parsed.waveDirection : [],
|
|
humidity: Array.isArray(parsed.humidity) ? parsed.humidity : [],
|
|
pressure: Array.isArray(parsed.pressure) ? parsed.pressure : []
|
|
};
|
|
console.log('[GraphsCore] Archivio caricato');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('[GraphsCore] Errore caricamento archivio:', error.message);
|
|
// Resetta archivio se corrotto
|
|
hourlyArchive = {
|
|
temperature: [], windSpeed: [], windDirection: [],
|
|
waveHeight: [], wavePeriod: [], waveDirection: [],
|
|
humidity: [], pressure: []
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Salva l'archivio su file
|
|
*/
|
|
function saveArchive() {
|
|
try {
|
|
fs.writeFileSync(ARCHIVE_FILE, JSON.stringify(hourlyArchive, null, 2));
|
|
} catch (error) {
|
|
console.error('[GraphsCore] Errore salvataggio archivio:', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Aggiorna i dati meteo condivisi
|
|
* @param {object} forecastData - Dati forecast da OpenMeteo
|
|
* @param {object} wavesData - Dati onde da OpenMeteo
|
|
*/
|
|
function updateSharedWeatherData(forecastData, wavesData) {
|
|
if (forecastData) {
|
|
sharedWeatherData.forecast = forecastData;
|
|
}
|
|
if (wavesData) {
|
|
sharedWeatherData.waves = wavesData;
|
|
}
|
|
|
|
// Aggiorna unità se disponibili
|
|
if (forecastData?.units || wavesData?.units) {
|
|
sharedWeatherData.units = {
|
|
forecast: forecastData?.units || sharedWeatherData.units?.forecast || {},
|
|
waves: wavesData?.units || sharedWeatherData.units?.waves || {}
|
|
};
|
|
}
|
|
|
|
sharedWeatherData.lastUpdate = Date.now();
|
|
}
|
|
|
|
/**
|
|
* Ottiene i dati meteo condivisi
|
|
* @returns {object} Dati meteo attuali
|
|
*/
|
|
function getSharedWeatherData() {
|
|
return {
|
|
forecast: sharedWeatherData.forecast,
|
|
waves: sharedWeatherData.waves,
|
|
units: sharedWeatherData.units,
|
|
lastUpdate: sharedWeatherData.lastUpdate,
|
|
isValid: sharedWeatherData.lastUpdate &&
|
|
(Date.now() - sharedWeatherData.lastUpdate) < sharedWeatherData.updateInterval * 2
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Ottiene le unità di misura globali
|
|
*/
|
|
function getUnits() {
|
|
return sharedWeatherData.units || {
|
|
forecast: {
|
|
temperature: '°C',
|
|
humidity: '%',
|
|
pressure: 'hPa',
|
|
windSpeed: 'km/h',
|
|
windDirection: '°'
|
|
},
|
|
waves: {
|
|
waveHeight: 'm',
|
|
wavePeriod: 's',
|
|
waveDirection: '°'
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Formatta un valore con la sua unità
|
|
*/
|
|
function formatValue(value, unitKey, category = 'forecast') {
|
|
if (value === null || value === undefined) return 'n/d';
|
|
const units = getUnits();
|
|
const unit = units[category]?.[unitKey] || '';
|
|
return `${value}${unit}`;
|
|
}
|
|
|
|
/**
|
|
* Verifica se i dati condivisi sono ancora validi
|
|
*/
|
|
function isWeatherDataValid() {
|
|
if (!sharedWeatherData.lastUpdate) return false;
|
|
return (Date.now() - sharedWeatherData.lastUpdate) < sharedWeatherData.updateInterval;
|
|
}
|
|
|
|
/**
|
|
* Archivia un punto dati orario
|
|
*/
|
|
function archiveHourlyData(data) {
|
|
if (!data || typeof data !== 'object') {
|
|
console.warn('[GraphsCore] archiveHourlyData: dati non validi');
|
|
return;
|
|
}
|
|
|
|
const timestamp = new Date().toISOString();
|
|
const maxPoints = 168; // 7 giorni di dati orari
|
|
|
|
const addPoint = (arr, value) => {
|
|
if (value === null || value === undefined || Number.isNaN(value)) return;
|
|
arr.push({ timestamp, value });
|
|
if (arr.length > maxPoints) arr.shift();
|
|
};
|
|
|
|
addPoint(hourlyArchive.temperature, data.temperature);
|
|
addPoint(hourlyArchive.windSpeed, data.windSpeed);
|
|
addPoint(hourlyArchive.windDirection, data.windDirection);
|
|
addPoint(hourlyArchive.waveHeight, data.waveHeight);
|
|
addPoint(hourlyArchive.wavePeriod, data.wavePeriod);
|
|
addPoint(hourlyArchive.waveDirection, data.waveDirection);
|
|
addPoint(hourlyArchive.humidity, data.humidity);
|
|
addPoint(hourlyArchive.pressure, data.pressure);
|
|
|
|
saveArchive();
|
|
console.log('[GraphsCore] Dati orari archiviati');
|
|
}
|
|
|
|
/**
|
|
* Ottiene i dati per un grafico specifico
|
|
* @param {string} parameter - temperatura, vento, onde, etc.
|
|
* @param {number} hours - ultimi N ore (default 24)
|
|
*/
|
|
function getGraphData(parameter, hours = 24) {
|
|
const paramMap = {
|
|
'temperature': hourlyArchive.temperature,
|
|
'windSpeed': hourlyArchive.windSpeed,
|
|
'windDirection': hourlyArchive.windDirection,
|
|
'waveHeight': hourlyArchive.waveHeight,
|
|
'wavePeriod': hourlyArchive.wavePeriod,
|
|
'waveDirection': hourlyArchive.waveDirection,
|
|
'humidity': hourlyArchive.humidity,
|
|
'pressure': hourlyArchive.pressure
|
|
};
|
|
|
|
const data = paramMap[parameter] || [];
|
|
const cutoff = Date.now() - (hours * 60 * 60 * 1000);
|
|
|
|
return data.filter(point => new Date(point.timestamp).getTime() > cutoff);
|
|
}
|
|
|
|
/**
|
|
* Genera dati formattati per Chart.js
|
|
*/
|
|
function formatForChart(parameter, hours = 24) {
|
|
const data = getGraphData(parameter, hours);
|
|
|
|
return {
|
|
labels: data.map(p => {
|
|
const d = new Date(p.timestamp);
|
|
return `${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`;
|
|
}),
|
|
datasets: [{
|
|
label: getParameterLabel(parameter),
|
|
data: data.map(p => p.value),
|
|
borderColor: getParameterColor(parameter),
|
|
backgroundColor: getParameterColor(parameter, 0.2),
|
|
tension: 0.3,
|
|
fill: true
|
|
}]
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Label leggibili per i parametri
|
|
*/
|
|
function getParameterLabel(param) {
|
|
const labels = {
|
|
'temperature': 'Temperatura (°C)',
|
|
'windSpeed': 'Velocità Vento (km/h)',
|
|
'windDirection': 'Direzione Vento (°)',
|
|
'waveHeight': 'Altezza Onde (m)',
|
|
'wavePeriod': 'Periodo Onde (s)',
|
|
'waveDirection': 'Direzione Onde (°)',
|
|
'humidity': 'Umidità (%)',
|
|
'pressure': 'Pressione (hPa)'
|
|
};
|
|
return labels[param] || param;
|
|
}
|
|
|
|
/**
|
|
* Colori per i grafici
|
|
*/
|
|
function getParameterColor(param, alpha = 1) {
|
|
const colors = {
|
|
'temperature': `rgba(255, 99, 132, ${alpha})`,
|
|
'windSpeed': `rgba(54, 162, 235, ${alpha})`,
|
|
'windDirection': `rgba(75, 192, 192, ${alpha})`,
|
|
'waveHeight': `rgba(153, 102, 255, ${alpha})`,
|
|
'wavePeriod': `rgba(255, 159, 64, ${alpha})`,
|
|
'waveDirection': `rgba(255, 205, 86, ${alpha})`,
|
|
'humidity': `rgba(201, 203, 207, ${alpha})`,
|
|
'pressure': `rgba(100, 149, 237, ${alpha})`
|
|
};
|
|
return colors[param] || `rgba(128, 128, 128, ${alpha})`;
|
|
}
|
|
|
|
/**
|
|
* Ottiene tutti i dati disponibili per dashboard
|
|
*/
|
|
function getAllGraphsData(hours = 24) {
|
|
return {
|
|
temperature: formatForChart('temperature', hours),
|
|
windSpeed: formatForChart('windSpeed', hours),
|
|
waveHeight: formatForChart('waveHeight', hours),
|
|
humidity: formatForChart('humidity', hours)
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Statistiche sull'archivio
|
|
*/
|
|
function getArchiveStats() {
|
|
return {
|
|
temperature: hourlyArchive.temperature.length,
|
|
windSpeed: hourlyArchive.windSpeed.length,
|
|
waveHeight: hourlyArchive.waveHeight.length,
|
|
oldestData: getOldestTimestamp(),
|
|
newestData: getNewestTimestamp()
|
|
};
|
|
}
|
|
|
|
function getOldestTimestamp() {
|
|
const all = [
|
|
...hourlyArchive.temperature,
|
|
...hourlyArchive.windSpeed,
|
|
...hourlyArchive.waveHeight
|
|
];
|
|
if (all.length === 0) return null;
|
|
return all.reduce((oldest, p) =>
|
|
new Date(p.timestamp) < new Date(oldest.timestamp) ? p : oldest
|
|
).timestamp;
|
|
}
|
|
|
|
function getNewestTimestamp() {
|
|
const all = [
|
|
...hourlyArchive.temperature,
|
|
...hourlyArchive.windSpeed,
|
|
...hourlyArchive.waveHeight
|
|
];
|
|
if (all.length === 0) return null;
|
|
return all.reduce((newest, p) =>
|
|
new Date(p.timestamp) > new Date(newest.timestamp) ? p : newest
|
|
).timestamp;
|
|
}
|
|
|
|
/**
|
|
* Pulisce l'archivio
|
|
*/
|
|
function clearArchive() {
|
|
hourlyArchive = {
|
|
temperature: [],
|
|
windSpeed: [],
|
|
windDirection: [],
|
|
waveHeight: [],
|
|
wavePeriod: [],
|
|
waveDirection: [],
|
|
humidity: [],
|
|
pressure: []
|
|
};
|
|
saveArchive();
|
|
console.log('[GraphsCore] Archivio pulito');
|
|
}
|
|
|
|
// Carica archivio all'avvio
|
|
loadArchive();
|
|
|
|
module.exports = {
|
|
// Gestione dati condivisi
|
|
updateSharedWeatherData,
|
|
getSharedWeatherData,
|
|
isWeatherDataValid,
|
|
|
|
// Unità di misura
|
|
getUnits,
|
|
formatValue,
|
|
|
|
// Archivio orario
|
|
archiveHourlyData,
|
|
getGraphData,
|
|
formatForChart,
|
|
getAllGraphsData,
|
|
getArchiveStats,
|
|
clearArchive,
|
|
|
|
// Utility
|
|
getParameterLabel,
|
|
getParameterColor
|
|
};
|