227 lines
7.7 KiB
TypeScript
227 lines
7.7 KiB
TypeScript
"use client"; // /components/main/kabelueberwachung/kue705FO/Charts/TDRChart/TDRChart.tsx
|
||
|
||
import React, { useEffect, useRef } from "react";
|
||
import { RootState } from "../../../../../../redux/store";
|
||
import { useSelector, useDispatch } from "react-redux";
|
||
import { AppDispatch } from "../../../../../../redux/store";
|
||
import { Chart, registerables } from "chart.js";
|
||
import "chartjs-adapter-date-fns";
|
||
import { getColor } from "../../../../../../utils/colors";
|
||
import { getReferenceCurveBySlotThunk } from "../../../../../../redux/thunks/getReferenceCurveBySlotThunk";
|
||
|
||
const TDRChart: React.FC<{ isFullScreen: boolean }> = ({ isFullScreen }) => {
|
||
const dispatch = useDispatch<AppDispatch>();
|
||
|
||
//---------------------------------
|
||
const chartRef = useRef<HTMLCanvasElement>(null);
|
||
const chartInstance = useRef<Chart | null>(null);
|
||
|
||
// 🟢 **Hole den ausgewählten Slot und Messkurve aus Redux**
|
||
const selectedId = useSelector(
|
||
(state: RootState) => state.tdrDataByIdSlice.selectedId
|
||
);
|
||
const selectedSlot = useSelector(
|
||
(state: RootState) => state.kueChartModeSlice.selectedSlot
|
||
);
|
||
const selectedChartType = useSelector(
|
||
(state: RootState) => state.kueChartModeSlice.activeMode
|
||
);
|
||
const tdrDataById = useSelector(
|
||
(state: RootState) => state.tdrDataByIdSlice.dataById
|
||
);
|
||
//--------------------------------
|
||
|
||
//--------------------------------
|
||
// Kombinierte Logik: ID hat Vorrang, sonst Initial-Daten für Slot
|
||
const tdrChartData =
|
||
selectedId !== null && tdrDataById[selectedId]
|
||
? tdrDataById[selectedId]
|
||
: [];
|
||
//--------------------------------
|
||
const referenceChartData = useSelector((state: RootState) =>
|
||
selectedSlot !== null
|
||
? state.tdrReferenceChartDataBySlotSlice.referenceData[selectedSlot] || []
|
||
: []
|
||
);
|
||
//--------------------------------
|
||
useEffect(() => {
|
||
if (selectedSlot !== null) {
|
||
dispatch(getReferenceCurveBySlotThunk(selectedSlot));
|
||
}
|
||
}, [selectedSlot, dispatch]);
|
||
|
||
//--------------------------------
|
||
const tdmChartData = useSelector(
|
||
(state: RootState) => state.tdmSingleChartSlice.data
|
||
);
|
||
const pinDistance =
|
||
selectedId !== null && Array.isArray(tdmChartData?.[selectedSlot ?? -1])
|
||
? tdmChartData[selectedSlot!].find((entry) => entry.id === selectedId)
|
||
?.d ?? null
|
||
: null;
|
||
|
||
//--------------------------------
|
||
useEffect(() => {
|
||
import("chartjs-plugin-zoom").then((zoomPlugin) => {
|
||
Chart.register(...registerables, zoomPlugin.default);
|
||
|
||
if (chartRef.current && tdrChartData.length > 0) {
|
||
if (chartInstance.current) {
|
||
chartInstance.current.destroy();
|
||
}
|
||
|
||
const ctx = chartRef.current.getContext("2d");
|
||
if (ctx) {
|
||
chartInstance.current = new Chart(ctx, {
|
||
type: "line",
|
||
data: {
|
||
datasets: [
|
||
{
|
||
/* label: `Modul ${
|
||
selectedSlot !== null ? selectedSlot + 1 : "?"
|
||
}`, */
|
||
label: `TDR Kurve`,
|
||
data: tdrChartData,
|
||
borderColor: getColor("littwin-blue"),
|
||
backgroundColor: "rgba(59,130,246,0.5)",
|
||
borderWidth: 1,
|
||
tension: 0.1,
|
||
pointRadius: 0,
|
||
pointHoverRadius: 5,
|
||
parsing: {
|
||
xAxisKey: "d",
|
||
yAxisKey: "p",
|
||
},
|
||
},
|
||
{
|
||
label: "Referenzkurve",
|
||
data: referenceChartData,
|
||
borderColor: "rgba(51,51,51,1)", // Dunkelgrau (nicht tiefschwarz)
|
||
backgroundColor: "rgba(51,51,51,0.3)", // Leicht transparent
|
||
borderWidth: 1,
|
||
// borderDash: [5, 5],
|
||
pointRadius: 0,
|
||
pointHoverRadius: 5,
|
||
pointBackgroundColor: "gray",
|
||
tension: 0.1,
|
||
parsing: {
|
||
xAxisKey: "d",
|
||
yAxisKey: "p",
|
||
},
|
||
},
|
||
{
|
||
label: "Fehlerstelle",
|
||
data:
|
||
pinDistance !== null && typeof pinDistance === "number"
|
||
? [{ d: pinDistance, p: 0 }]
|
||
: [],
|
||
borderColor: "red",
|
||
borderWidth: 1,
|
||
backgroundColor: "rgba(220,38,38,0.5)",
|
||
pointRadius: 10,
|
||
pointStyle: "triangle", // Hier den korrekten Stil setzen
|
||
showLine: false,
|
||
clip: false, // Wenn du die Fehlerstelle sichtbar sehen möchtest
|
||
parsing: {
|
||
xAxisKey: "d",
|
||
yAxisKey: "p",
|
||
},
|
||
order: 9999,
|
||
pointHoverRadius: 15,
|
||
// Hier die Reihenfolge der Marker bestimmen
|
||
},
|
||
],
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
animation: {
|
||
duration: 150, // 150 ms Animation
|
||
},
|
||
scales: {
|
||
x: {
|
||
type: "linear",
|
||
title: {
|
||
display: true,
|
||
text: "Entfernung (m)",
|
||
},
|
||
},
|
||
y: {
|
||
title: {
|
||
display: true,
|
||
text: "Pegel",
|
||
},
|
||
},
|
||
},
|
||
plugins: {
|
||
tooltip: {
|
||
yAlign: "bottom", // Tooltip oberhalb des Punktes
|
||
callbacks: {
|
||
title: () => "Fehlerstelle", // Kein Titel
|
||
label: function (context) {
|
||
const rawData = context.raw as { d: number; p: number };
|
||
|
||
// 👇 Unterscheide zwischen Mess- und Referenzkurve
|
||
if (context.dataset.label === "Referenzkurve") {
|
||
return [
|
||
`Referenzwert`,
|
||
`Entfernung: ${rawData.d.toFixed(0)} Meter`,
|
||
`Pegel: ${rawData.p.toFixed(2)}`,
|
||
];
|
||
} else {
|
||
return [
|
||
`Messwert`,
|
||
`Entfernung: ${rawData.d.toFixed(0)} Meter`,
|
||
`Pegel: ${rawData.p.toFixed(2)}`,
|
||
];
|
||
}
|
||
},
|
||
},
|
||
},
|
||
|
||
zoom: {
|
||
pan: {
|
||
enabled: true,
|
||
mode: "xy",
|
||
},
|
||
zoom: {
|
||
wheel: {
|
||
enabled: true,
|
||
},
|
||
pinch: {
|
||
enabled: true,
|
||
},
|
||
mode: "xy",
|
||
},
|
||
},
|
||
},
|
||
},
|
||
});
|
||
}
|
||
}
|
||
});
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
JSON.stringify(tdrChartData), // eslint-disable-next-line react-hooks/exhaustive-deps
|
||
JSON.stringify(referenceChartData),
|
||
selectedSlot,
|
||
selectedChartType,
|
||
]);
|
||
//--------------------------------------
|
||
|
||
return (
|
||
<div style={{ width: "100%", height: isFullScreen ? "90%" : "28rem" }}>
|
||
{tdrChartData.length === 0 ? (
|
||
<div className="flex items-center justify-center h-full text-gray-500 italic">
|
||
⚠️ Keine Daten verfügbar für diesen Slot
|
||
</div>
|
||
) : (
|
||
<canvas ref={chartRef} style={{ width: "100%", height: "100%" }} />
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default TDRChart;
|