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

368 lines
12 KiB
TypeScript
Raw Normal View History

import { Component, OnInit, ElementRef, ViewChild, AfterViewInit, OnDestroy, OnChanges, Input, SimpleChanges, ChangeDetectorRef } 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',
2024-11-04 04:09:04 +00:00
soilhumidity: '#0389b5',
soilconductivity: '#A52A2A',
soilph: '#228B22',
2024-11-04 04:09:04 +00:00
soilnitrogen: '#fece48',
soilphosphorus: '#B80F0A',
2024-11-04 04:09:04 +00:00
soilpotassium: '#4c1f74',
};
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, OnChanges{
@ViewChild('myChartDHT', { static: false }) dhtChartElement!: ElementRef<HTMLCanvasElement>;
@ViewChild('myChartNPK1', { static: false }) npk1ChartElement!: ElementRef<HTMLCanvasElement>;
@ViewChild('myChartNPK2', { static: false }) npk2ChartElement!: ElementRef<HTMLCanvasElement>;
@Input() interval: string = 'hourly';
selectedInterval: string = 'HOURLY';
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: 'Temperatur Udara (°C)',
vicihumidity: 'Kelembaban Udara (%)',
viciluminosity: 'Intensitas Cahaya (lux)',
soiltemperature: 'Temperatur Tanah (°C)',
soilhumidity: 'Kelembaban Tanah (%)',
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, private cdr: ChangeDetectorRef) {}
2024-09-17 06:50:34 +00:00
ngOnInit(): void {
this.resizeListener = this.onResize.bind(this);
window.addEventListener('resize', this.resizeListener);
this.updateCharts();
}
ngOnChanges(changes: SimpleChanges): void {
2024-11-06 02:28:54 +00:00
if (changes['interval'] && changes['interval'].previousValue !== changes['interval'].currentValue) {
this.selectedInterval = changes['interval'].currentValue;
}
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();
}
getDate(): string {
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');
2024-11-04 04:09:04 +00:00
return `${year}-${month}-${day}`;
}
2024-11-04 04:09:04 +00:00
fetchDHTData(timeRange: string): void {
const startEnd = this.getDate();
this.sensorService.getSensorData('dht', 'npk', startEnd, timeRange).subscribe({
next: (response) => {
this.isLoadingDHT = false;
if (response.statusCode === 200 && response.data.dht?.length > 0) {
this.createChart(this.dhtChartElement.nativeElement, response, 'dht', 'npk');
this.isNoDataDHT = false;
} else {
this.isNoDataDHT = true;
}
},
error: () => {
this.isLoadingDHT = false;
this.isNoDataDHT = true;
}
});
}
2024-11-04 04:09:04 +00:00
fetchNPK1Data(timeRange: string): void {
const startEnd = this.getDate();
this.sensorService.getSensorData('npk1', this.selectedNPK1, startEnd, timeRange).subscribe({
next: (response) => {
2024-11-04 04:09:04 +00:00
this.isLoadingNPK1 = false;
if (response.statusCode === 200 && response.data.npk1?.length > 0) {
this.createChart(this.npk1ChartElement.nativeElement, response, 'npk1', this.selectedNPK1);
this.isNoDataNPK1 = false;
} else {
this.isNoDataNPK1 = true;
}
},
error: () => {
this.isLoadingNPK1 = false;
this.isNoDataNPK1 = true;
}
});
}
fetchNPK2Data(savedTimeRange: string): void {
const startEnd = this.getDate();
this.sensorService.getSensorData('npk2', this.selectedNPK2, startEnd, savedTimeRange).subscribe({
next: (response) => {
console.log(savedTimeRange);
2024-11-04 04:09:04 +00:00
this.isLoadingNPK2 = false;
if (response.statusCode === 200 && response.data.npk2?.length > 0) {
this.createChart(this.npk2ChartElement.nativeElement, response, 'npk2', this.selectedNPK2);
this.isNoDataNPK2 = false;
} else {
this.isNoDataNPK2 = true;
}
},
error: () => {
this.isLoadingNPK2 = false;
this.isNoDataNPK2 = true;
}
});
}
2024-11-04 04:09:04 +00:00
updateCharts(): void {
2024-11-06 02:28:54 +00:00
const interval = this.selectedInterval;
Object.keys(this.charts).forEach(key => {
if (this.charts[key]) {
this.charts[key]?.destroy();
this.charts[key] = undefined;
}
});
this.fetchDHTData(interval);
this.fetchNPK1Data(interval);
this.fetchNPK2Data(interval);
}
2024-11-04 04:09:04 +00:00
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];
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') {
datasets = ['vicitemperature', 'viciluminosity', 'vicihumidity'].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`;
const pointRadius = data.length === 1 ? 5 : 0;
const pointHoverRadius = data.length === 1 ? 7 : 0;
return {
label: displayName,
data,
borderColor,
borderWidth: 1.5,
fill: true,
backgroundColor,
tension: 0.5,
pointRadius,
pointHoverRadius,
};
}).filter(dataset => dataset !== null);
} else {
datasets = parameters
.filter(parameter => {
if (selectedOption === 'npk') {
return ['soilphosphorus', 'soilnitrogen', 'soilpotassium'].includes(parameter);
} else if (selectedOption === 'others') {
return !['soilphosphorus', 'soilnitrogen', 'soilpotassium'].includes(parameter);
}
return true;
})
.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`;
const pointRadius = data.length === 1 ? 5 : 0;
const pointHoverRadius = data.length === 1 ? 7 : 0;
return {
label: displayName,
data,
borderColor,
borderWidth: 1.5,
fill: true,
backgroundColor,
tension: 0.5,
pointRadius,
pointHoverRadius,
};
})
.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,
aspectRatio: 2,
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,
ticks: {
callback: (value, index, values) => {
const labels = this.getLabels(response, sensor);
return labels[index] || value;
}
}
},
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);
if(this.interval === 'HOURLY') {
labels.push(`${item.hour}.00`);
}
else if (this.interval === 'DAILY') {
labels.push(item.day);
}
});
} 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.map(item => {
if (this.interval === 'HOURLY' || this.selectedInterval === 'HOURLY') {
return `${item.hour}.00`;
} else if (this.interval === 'DAILY') {
const day = item.day;
return this.convertDateToDay(day);
} else {
return '';
}
});
2024-09-17 06:50:34 +00:00
}
convertDateToDay(dateString: string): string {
const date = new Date(dateString);
const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thrusday', 'Friday', 'Saturday'];
return days[date.getDay()];
}
2024-09-17 06:50:34 +00:00
}