import { Component, OnInit, ElementRef, ViewChild, AfterViewInit, OnDestroy } from '@angular/core'; import { Chart, registerables } from 'chart.js'; import { SensorService } from '../../../../cores/services/sensor.service'; import { ApiResponse, ParameterSensor } from '../../../../cores/interface/sensor-data'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; Chart.register(...registerables); const parameterColors: { [key: string]: string } = { vicitemperature: '#8F5A62', vicihumidity: '#16423C', viciluminosity: '#DF9B55', soiltemperature: '#FF6347', soilhumidity: '#00BFFF', soilconductivity: '#A52A2A', soilph: '#228B22', soilnitrogen: '#FEDC56', soilphosphorus: '#B80F0A', soilpotassium: '#6F2DA8', }; @Component({ selector: 'app-graph', standalone: true, imports: [CommonModule, FormsModule], templateUrl: './graph.component.html', styleUrls: ['./graph.component.scss'] }) export class GraphComponent implements OnInit, AfterViewInit, OnDestroy { @ViewChild('myChartDHT', { static: false }) dhtChartElement!: ElementRef; @ViewChild('myChartNPK1', { static: false }) npk1ChartElement!: ElementRef; @ViewChild('myChartNPK2', { static: false }) npk2ChartElement!: ElementRef; isLoadingDHT: boolean = true; isLoadingNPK1: boolean = true; isLoadingNPK2: boolean = true; isNoDataDHT: boolean = false; isNoDataNPK1: boolean = false; isNoDataNPK2: boolean = false; allNoData: boolean = false; sensorParameters: { [key: string]: string[] } = { dht: ['vicitemperature', 'vicihumidity', 'viciluminosity'], npk1: ['soiltemperature', 'soilhumidity', 'soilconductivity', 'soilph', 'soilnitrogen', 'soilphosphorus', 'soilpotassium'], npk2: ['soiltemperature', 'soilhumidity', 'soilconductivity', 'soilph', 'soilnitrogen', 'soilphosphorus', 'soilpotassium'], }; parameterDisplayNames: { [key: string]: string } = { vicitemperature: 'Temperature (°C)', vicihumidity: 'Humidity (%)', viciluminosity: 'Luminosity (lux)', soiltemperature: 'Soil Temperature (°C)', soilhumidity: 'Soil Humidity (%)', soilconductivity: 'Conductivity (μS/cm)', soilph: 'pH', soilnitrogen: 'Nitrogen (mg/l)', soilphosphorus: 'Phosphorus (mg/l)', soilpotassium: 'Potassium (mg/l)', }; charts: { [key: string]: Chart | undefined } = { dht: undefined, npk1: undefined, npk2: undefined, }; selectedNPK1: string = 'npk'; selectedNPK2: string = 'npk'; private resizeListener!: () => void; constructor(private sensorService: SensorService) {} ngOnInit(): void { this.resizeListener = this.onResize.bind(this); window.addEventListener('resize', this.resizeListener); this.updateCharts(); } ngAfterViewInit(): void { this.onResize(); } ngOnDestroy(): void { window.removeEventListener('resize', this.resizeListener); } onResize(): void { Object.values(this.charts).forEach(chart => { if (chart) { chart.destroy(); } }); this.updateCharts(); } updateCharts(): void { this.isLoadingDHT = this.isLoadingNPK1 = this.isLoadingNPK2 = true; const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, '0'); const day = String(today.getDate()).padStart(2, '0'); const startEnd = `${year}-${month}-${day}`; // const startEnd = '2024-10-24'; const timeRange = 'HOURLY'; // Fetch data for DHT this.sensorService.getSensorData('dht', '', startEnd, timeRange).subscribe({ next: (response) => { if (response.statusCode === 200 && response.data.dht?.length > 0) { this.createChart(this.dhtChartElement.nativeElement, response, 'dht', 'npk'); this.isNoDataDHT = false; this.allNoData = false; } else { this.isNoDataDHT = true; } this.isLoadingDHT = false; }, error: () => { this.isLoadingDHT = false; this.isNoDataDHT = true; } }); // Fetch data for NPK1 this.sensorService.getSensorData('npk1', '', startEnd, timeRange).subscribe({ next: (response) => { if (response.statusCode === 200 && response.data.npk1?.length > 0) { this.createChart(this.npk1ChartElement.nativeElement, response, 'npk1', this.selectedNPK1); this.isNoDataNPK1 = false; this.allNoData = false; } else { this.isNoDataNPK1 = true; } this.isLoadingNPK1 = false; }, error: () => { this.isLoadingNPK1 = false; this.isNoDataNPK1 = true; } }); // Fetch data for NPK2 this.sensorService.getSensorData('npk2', '', startEnd, timeRange).subscribe({ next: (response) => { if (response.statusCode === 200 && response.data.npk2?.length > 0) { this.createChart(this.npk2ChartElement.nativeElement, response, 'npk2', this.selectedNPK2); this.isNoDataNPK2 = false; this.allNoData = false; } else { this.isNoDataNPK2 = true; } this.isLoadingNPK2 = false; }, error: () => { this.isLoadingNPK2 = false; this.isNoDataNPK2 = true; } }); } createChart(canvas: HTMLCanvasElement, response: ApiResponse, sensor: string, selectedOption: string): void { const ctx = canvas.getContext('2d'); const parameters = this.sensorParameters[sensor]; if (!ctx) { console.error('Failed to get canvas context for sensor:', sensor); return; } let datasets: any[] = []; if (sensor === 'dht') { // Handle DHT parameters directly datasets = ['vicitemperature', 'vicihumidity', 'viciluminosity'].map(parameter => { const { data, labels } = this.getDataFromResponse(response, sensor, parameter); if (data.length === 0) { console.warn(`No data found for parameter: ${parameter}`); return null; } const displayName = this.parameterDisplayNames[parameter] || parameter; const borderColor = parameterColors[parameter] || '#000000'; const backgroundColor = `${borderColor}4D`; return { label: displayName, data, borderColor, borderWidth: 1.5, fill: true, backgroundColor, tension: 0.5, pointRadius: 0, pointHoverRadius: 0, }; }).filter(dataset => dataset !== null); } else { // Handle NPK1 and NPK2 as before datasets = parameters.map(parameter => { const { data, labels } = this.getDataFromResponse(response, sensor, parameter); if (data.length === 0) { console.warn(`No data found for parameter: ${parameter}`); return null; } const displayName = this.parameterDisplayNames[parameter] || parameter; const borderColor = parameterColors[parameter] || '#000000'; const backgroundColor = `${borderColor}4D`; // Filter datasets based on the selected option if (selectedOption === 'npk' && ['soilphosphorus', 'soilnitrogen', 'soilpotassium'].includes(parameter)) { return { label: displayName, data, borderColor, borderWidth: 1.5, fill: true, backgroundColor, tension: 0.5, pointRadius: 0, pointHoverRadius: 0, }; } else if (selectedOption === 'others' && !['soilphosphorus', 'soilnitrogen', 'soilpotassium'].includes(parameter)) { return { label: displayName, data, borderColor, borderWidth: 1.5, fill: true, backgroundColor, tension: 0.5, pointRadius: 0, pointHoverRadius: 0, }; } return null; }).filter(dataset => dataset !== null); } if (datasets.length === 0) { console.warn('No valid datasets to render for sensor:', sensor); return; } if (this.charts[sensor]) { this.charts[sensor]?.destroy(); } this.charts[sensor] = new Chart(ctx, { type: 'line', data: { labels: this.getLabels(response, sensor), datasets, }, options: { responsive: true, maintainAspectRatio: false, plugins: { tooltip: { enabled: true, mode: 'nearest', intersect: false, callbacks: { label: (tooltipItem) => { const paramLabel = tooltipItem.dataset.label; const value = tooltipItem.formattedValue; return `${paramLabel}: ${value}`; } } }, legend: { display: true } }, scales: { x: { grid: { display: false }, beginAtZero: true }, y: { grid: { display: false }, beginAtZero: true } } } }); } getDataFromResponse(response: ApiResponse, sensor: string, parameter: string): { data: number[], labels: string[] } { const sensorData = response.data[sensor as keyof typeof response.data]; const data: number[] = []; const labels: string[] = []; if (sensorData) { sensorData.forEach(item => { data.push(item[parameter as keyof ParameterSensor] ?? 0); labels.push(`${item.hour}.00`); }); } else { console.error('No data found for sensor:', sensor); } return { data, labels }; } getLabels(response: ApiResponse, sensor: string): string[] { const sensorData = response.data[sensor as keyof typeof response.data]; return sensorData ? sensorData.map(item => `${item.hour}.00`) : []; } }