221 lines
5.9 KiB
TypeScript
221 lines
5.9 KiB
TypeScript
"use client";
|
|
|
|
import React, { useEffect, useRef } from "react";
|
|
import { useSelector } from "react-redux";
|
|
import { RootState } from "../../../../../../redux/store";
|
|
import {
|
|
Chart as ChartJS,
|
|
LineElement,
|
|
PointElement,
|
|
LinearScale,
|
|
TimeScale,
|
|
Title,
|
|
Tooltip,
|
|
Legend,
|
|
Filler,
|
|
} from "chart.js";
|
|
|
|
import "chartjs-adapter-date-fns";
|
|
import { de } from "date-fns/locale";
|
|
import { differenceInHours, parseISO } from "date-fns";
|
|
|
|
ChartJS.register(
|
|
LineElement,
|
|
PointElement,
|
|
LinearScale,
|
|
TimeScale,
|
|
Title,
|
|
Tooltip,
|
|
Legend,
|
|
Filler
|
|
);
|
|
import { getColor } from "../../../../../../utils/colors";
|
|
import { PulseLoader } from "react-spinners";
|
|
|
|
const usePreviousData = (data: any[]) => {
|
|
const ref = useRef<any[]>([]);
|
|
useEffect(() => {
|
|
ref.current = data;
|
|
}, [data]);
|
|
return ref.current;
|
|
};
|
|
|
|
const LoopMeasurementChart = () => {
|
|
const canvasRef = useRef<HTMLCanvasElement | null>(null);
|
|
const chartInstance = useRef<ChartJS | null>(null);
|
|
|
|
const {
|
|
loopMeasurementCurveChartData,
|
|
selectedMode,
|
|
unit,
|
|
isFullScreen,
|
|
isLoading,
|
|
vonDatum,
|
|
bisDatum,
|
|
} = useSelector((state: RootState) => state.kabelueberwachungChartSlice);
|
|
|
|
const previousData = usePreviousData(loopMeasurementCurveChartData);
|
|
|
|
// Vergleichsfunktion
|
|
const isEqual = (a: any[], b: any[]): boolean => {
|
|
if (a.length !== b.length) return false;
|
|
for (let i = 0; i < a.length; i++) {
|
|
if (
|
|
a[i].t !== b[i].t ||
|
|
a[i].i !== b[i].i ||
|
|
a[i].m !== b[i].m ||
|
|
a[i].g !== b[i].g ||
|
|
a[i].a !== b[i].a
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// ⏱ Zeitspanne in Stunden berechnen
|
|
const from = vonDatum ? parseISO(vonDatum) : null;
|
|
const to = bisDatum ? parseISO(bisDatum) : null;
|
|
const durationInHours = from && to ? differenceInHours(to, from) : 9999;
|
|
const timeUnit: "hour" | "day" = durationInHours <= 48 ? "hour" : "day";
|
|
|
|
useEffect(() => {
|
|
import("chartjs-plugin-zoom").then((zoomPlugin) => {
|
|
if (!ChartJS.registry.plugins.get("zoom")) {
|
|
ChartJS.register(zoomPlugin.default);
|
|
}
|
|
|
|
if (!canvasRef.current) return;
|
|
const ctx = canvasRef.current.getContext("2d");
|
|
if (!ctx) return;
|
|
|
|
if (isEqual(loopMeasurementCurveChartData, previousData)) {
|
|
return; // keine echte Datenänderung → nicht neu zeichnen
|
|
}
|
|
|
|
if (chartInstance.current) {
|
|
chartInstance.current.destroy();
|
|
}
|
|
|
|
const chartData = {
|
|
labels: loopMeasurementCurveChartData
|
|
.map((entry) => new Date(entry.t))
|
|
.reverse(),
|
|
datasets: [
|
|
{
|
|
label: "Messwert Minimum",
|
|
data: loopMeasurementCurveChartData.map((e) => e.i).reverse(),
|
|
borderColor: "lightgrey",
|
|
backgroundColor: "rgba(211,211,211,0.5)",
|
|
borderWidth: 2,
|
|
pointRadius: 0,
|
|
pointHoverRadius: 10,
|
|
tension: 0.1,
|
|
order: 1,
|
|
},
|
|
{
|
|
label: "Messwert Maximum",
|
|
data: loopMeasurementCurveChartData.map((e) => e.a).reverse(),
|
|
borderColor: "lightgrey",
|
|
backgroundColor: "rgba(211,211,211,0.5)",
|
|
borderWidth: 2,
|
|
pointRadius: 0,
|
|
pointHoverRadius: 10,
|
|
tension: 0.1,
|
|
order: 1,
|
|
},
|
|
selectedMode === "DIA0"
|
|
? {
|
|
label: "Messwert",
|
|
data: loopMeasurementCurveChartData.map((e) => e.m).reverse(),
|
|
borderColor: getColor("littwin-blue"),
|
|
backgroundColor: "rgba(59,130,246,0.5)",
|
|
borderWidth: 3,
|
|
pointRadius: 0,
|
|
pointHoverRadius: 10,
|
|
tension: 0.1,
|
|
order: 3,
|
|
}
|
|
: {
|
|
label: "Messwert Durchschnitt",
|
|
data: loopMeasurementCurveChartData.map((e) => e.g).reverse(),
|
|
borderColor: getColor("littwin-blue"),
|
|
backgroundColor: "rgba(59,130,246,0.5)",
|
|
borderWidth: 3,
|
|
pointRadius: 0,
|
|
pointHoverRadius: 10,
|
|
tension: 0.1,
|
|
order: 3,
|
|
},
|
|
],
|
|
};
|
|
|
|
const options = {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: { position: "top" as const },
|
|
tooltip: {
|
|
yAlign: "bottom" as const,
|
|
mode: "index" as const,
|
|
intersect: false,
|
|
},
|
|
zoom: {
|
|
pan: { enabled: true, mode: "x" as const },
|
|
zoom: {
|
|
wheel: { enabled: true },
|
|
pinch: { enabled: true },
|
|
mode: "x" as const,
|
|
},
|
|
},
|
|
},
|
|
scales: {
|
|
x: {
|
|
type: "time" as const,
|
|
time: {
|
|
unit: timeUnit,
|
|
tooltipFormat: "dd.MM.yyyy HH:mm:ss",
|
|
displayFormats: {
|
|
hour: "HH:mm",
|
|
day: "dd.MM.",
|
|
},
|
|
locale: de,
|
|
},
|
|
title: { display: true, text: "Zeit" },
|
|
},
|
|
y: {
|
|
title: { display: true, text: unit },
|
|
ticks: { precision: 0 },
|
|
},
|
|
},
|
|
};
|
|
|
|
chartInstance.current = new ChartJS(ctx, {
|
|
type: "line",
|
|
data: chartData,
|
|
options,
|
|
});
|
|
});
|
|
}, [loopMeasurementCurveChartData, selectedMode, vonDatum, bisDatum]);
|
|
|
|
return (
|
|
<div style={{ width: "100%", height: isFullScreen ? "90%" : "400px" }}>
|
|
{isLoading && (
|
|
<div className="flex items-center justify-center h-96">
|
|
<PulseLoader color="#3B82F6" />
|
|
</div>
|
|
)}
|
|
<canvas
|
|
ref={canvasRef}
|
|
style={{
|
|
width: "100%",
|
|
height: "100%",
|
|
display: isLoading ? "none" : "block",
|
|
}}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default LoopMeasurementChart;
|