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 };