frontend-smartfarming/agrilink_vocpro/src/app/pages/dashboard/page/graph/graph.component.ts

313 lines
9.7 KiB
TypeScript
Raw Normal View History

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',
};
2024-09-17 06:50:34 +00:00
@Component({
selector: 'app-graph',
standalone: true,
imports: [CommonModule, FormsModule],
2024-09-17 06:50:34 +00:00
templateUrl: './graph.component.html',
styleUrls: ['./graph.component.scss']
2024-09-17 06:50:34 +00:00
})
export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChild('myChartDHT', { static: false }) dhtChartElement!: ElementRef<HTMLCanvasElement>;
@ViewChild('myChartNPK1', { static: false }) npk1ChartElement!: ElementRef<HTMLCanvasElement>;
@ViewChild('myChartNPK2', { static: false }) npk2ChartElement!: ElementRef<HTMLCanvasElement>;
isLoadingDHT: boolean = true;
isLoadingNPK1: boolean = true;
isLoadingNPK2: boolean = true;
isNoDataDHT: boolean = false;
isNoDataNPK1: boolean = false;
isNoDataNPK2: boolean = false;
allNoData: boolean = false;
2024-09-17 06:50:34 +00:00
sensorParameters: { [key: string]: string[] } = {
dht: ['vicitemperature', 'vicihumidity', 'viciluminosity'],
npk1: ['soiltemperature', 'soilhumidity', 'soilconductivity', 'soilph', 'soilnitrogen', 'soilphosphorus', 'soilpotassium'],
npk2: ['soiltemperature', 'soilhumidity', 'soilconductivity', 'soilph', 'soilnitrogen', 'soilphosphorus', 'soilpotassium'],
};
2024-09-17 06:50:34 +00:00
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: 'Kalium (mg/l)',
};
2024-09-17 06:50:34 +00:00
charts: { [key: string]: Chart | undefined } = {
dht: undefined,
npk1: undefined,
npk2: undefined,
};
selectedNPK1: string = 'npk';
selectedNPK2: string = 'npk';
private resizeListener!: () => void;
2024-09-17 06:50:34 +00:00
constructor(private sensorService: SensorService) {}
2024-09-17 06:50:34 +00:00
ngOnInit(): void {
this.resizeListener = this.onResize.bind(this);
window.addEventListener('resize', this.resizeListener);
this.updateCharts();
2024-09-17 06:50:34 +00:00
}
ngAfterViewInit(): void {
this.onResize();
}
2024-09-17 06:50:34 +00:00
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 {
2024-09-17 06:50:34 +00:00
const ctx = canvas.getContext('2d');
const parameters = this.sensorParameters[sensor];
2024-09-17 06:50:34 +00:00
if (!ctx) {
console.error('Failed to get canvas context for sensor:', sensor);
return;
2024-09-17 06:50:34 +00:00
}
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,
2024-09-17 06:50:34 +00:00
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 }
2024-09-17 06:50:34 +00:00
}
}
});
}
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);
}
2024-09-17 06:50:34 +00:00
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`) : [];
2024-09-17 06:50:34 +00:00
}
}