TDR Chart von Dropdown Menü Auwahl zeichnen

This commit is contained in:
ISA
2025-03-24 15:00:20 +01:00
parent 69bcbf519d
commit 68614df0cd
14 changed files with 13428 additions and 152 deletions

View File

@@ -0,0 +1,20 @@
digraph TDRReduxStructure {
rankdir=LR;
node [shape=record, fontname=Helvetica];
SlotData [label="{ SlotData | + slotIndex: number\\l+ tdrList: TDRMeasurement[]\\l }"];
TDRMeasurement [label="{ TDRMeasurement | + id: number\\l+ t: string\\l+ d: number\\l+ p: number\\l+ s: number\\l+ a: number\\l }"];
TDMChartSlice [label="{ tdmChartSlice (Redux) | + data: SlotData[]\\l }"];
TDRChartSlice [label="{ tdrChartDataByIdSlice (Redux) | + data: Record<id, TDRChartPoint[]>\\l }"];
fetchAllTDMData [label="fetchAllTDMData.ts", shape=note];
fetchTDRChartThunk [label="fetchTDRChartDataByIdThunk.ts", shape=note];
fetchTDRChartService [label="fetchTDRChartDataById.ts", shape=note];
kabelueberwachung [label="kabelueberwachung.tsx", shape=note];
SlotData -> TDRMeasurement [arrowhead="open", label="has many"];
fetchAllTDMData -> TDMChartSlice;
fetchTDRChartThunk -> TDRChartSlice;
fetchTDRChartThunk -> fetchTDRChartService;
kabelueberwachung -> fetchAllTDMData;
kabelueberwachung -> fetchTDRChartThunk;
}

View File

@@ -7,12 +7,16 @@ import { useSelector } from "react-redux";
import { Chart, registerables } from "chart.js";
import "chartjs-adapter-date-fns";
import { getColor } from "../../../../../../utils/colors";
import TDRChartActionBar from "./TDRChartActionBar";
const TDRChart: React.FC<{ isFullScreen: boolean }> = ({ isFullScreen }) => {
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.tdrDataById.selectedId
);
const selectedSlot = useSelector(
(state: RootState) => state.kueChartMode.selectedSlot
);
@@ -20,7 +24,7 @@ const TDRChart: React.FC<{ isFullScreen: boolean }> = ({ isFullScreen }) => {
(state: RootState) => state.kueChartMode.activeMode
);
const tdrChartData = useSelector((state: RootState) =>
selectedSlot !== null ? state.tdrChart.data[selectedSlot] || [] : []
selectedId !== null ? state.tdrDataById.dataById[selectedId] || [] : []
);
const referenceChartData = useSelector((state: RootState) =>
@@ -142,9 +146,10 @@ const TDRChart: React.FC<{ isFullScreen: boolean }> = ({ isFullScreen }) => {
}
});
}, [JSON.stringify(tdrChartData), selectedSlot, selectedChartType]);
return (
<div style={{ width: "100%", height: isFullScreen ? "90%" : "28rem" }}>
<TDRChartActionBar />
{tdrChartData.length === 0 ? (
<div className="flex items-center justify-center h-full text-gray-500 italic">
Keine Daten verfügbar für diesen Slot

View File

@@ -1,158 +1,61 @@
import React, { useState, useEffect } from "react";
import { useDispatch } from "react-redux";
import { setTDRChartData } from "../../../../../../redux/slices/kabelueberwachungChartSlice";
// /components/main/kabelueberwachung/kue705FO/Charts/TDRChart/TDRChartActionBar.tsx
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "../../../../../../redux/store";
import { fetchTDRChartDataById } from "../../../../../../services/fetchTDRChartDataById";
import {
setTDRChartDataById,
setSelectedTDRId,
} from "../../../../../../redux/slices/tdrDataByIdSlice";
const TDRChartActionBar: React.FC = () => {
const dispatch = useDispatch();
const [jahr, setJahr] = useState(new Date().getFullYear());
const [monat, setMonat] = useState(new Date().getMonth() + 1);
const [dateiListe, setDateiListe] = useState<string[]>([]);
const [ausgewählteDatei, setAusgewählteDatei] = useState<string>("");
const [sortAscending, setSortAscending] = useState(true);
const getYearFolderName = (year: number): string => {
return `Year_${String(year).slice(-2)}`;
};
const tdmChartData = useSelector((state: RootState) => state.tdmChart.data);
const selectedSlot = useSelector(
(state: RootState) => state.kueChartMode.selectedSlot
);
// 📌 Laden der Datei-Liste aus directory.json
useEffect(() => {
const loadDirectory = async () => {
const yearFolder = getYearFolderName(jahr);
const monthFolder = `Month_${monat.toString().padStart(2, "0")}`;
const path = `/CPLmockData/LastTDR/kue_01/${yearFolder}/${monthFolder}/directory.json`;
const idsForSlot =
selectedSlot !== null ? tdmChartData[selectedSlot] ?? [] : [];
try {
const response = await fetch(path);
if (!response.ok) {
console.error(`Fehler beim Laden der Datei.`);
setDateiListe([]);
setAusgewählteDatei("");
return;
}
const [selectedId, setSelectedId] = useState<number | null>(null);
const data = await response.json();
let files = data.files.map(
(file: { filename: string }) => file.filename
);
const handleSelectChange = async (
e: React.ChangeEvent<HTMLSelectElement>
) => {
const id = parseInt(e.target.value);
setSelectedId(id);
if (!sortAscending) {
files.reverse(); // 🔄 Falls bereits sortiert, Reihenfolge umkehren
}
const data = await fetchTDRChartDataById(id);
if (!data) return;
setDateiListe(files);
if (files.length > 0) {
setAusgewählteDatei(files[0]); // 🟢 Automatische Auswahl der ersten Datei
loadAndStoreChartData(files[0]); // 🟢 Chart-Daten sofort aktualisieren
}
} catch (error) {
console.error("Fehler beim Laden der Datei directory.json:", error);
}
};
loadDirectory();
}, [jahr, monat, sortAscending]);
// 📌 Daten für die ausgewählte Datei laden
const handleDateiAuswahl = (event: React.ChangeEvent<HTMLSelectElement>) => {
const selectedFile = event.target.value;
setAusgewählteDatei(selectedFile);
loadAndStoreChartData(selectedFile);
};
// 📌 Sortieren der Datei-Liste und Chart sofort aktualisieren
const handleSortToggle = () => {
setSortAscending(!sortAscending);
setDateiListe((prevListe) => {
const newListe = [...prevListe].reverse(); // 🔄 Reihenfolge umkehren
if (newListe.length > 0) {
setAusgewählteDatei(newListe[0]); // 🟢 Erste Datei nach dem Sortieren auswählen
loadAndStoreChartData(newListe[0]); // 🟢 Chart sofort aktualisieren
}
return newListe;
});
};
// 📌 Daten abrufen und in Redux speichern
const loadAndStoreChartData = async (filename: string) => {
const yearFolder = getYearFolderName(jahr);
const monthFolder = `Month_${monat.toString().padStart(2, "0")}`;
const filePath = `/CPLmockData/LastTDR/kue_01/${yearFolder}/${monthFolder}/${filename}`;
try {
const response = await fetch(filePath);
if (!response.ok) {
console.error(`Fehler beim Laden der Datei.`);
return;
}
const data = await response.json();
dispatch(setTDRChartData(data));
} catch (error) {
console.error("Fehler beim Laden der JSON-Daten:", error);
}
dispatch(setTDRChartDataById({ id, data }));
dispatch(setSelectedTDRId(id)); // 👉 wichtig!
};
return (
<div className="flex flex-wrap justify-end items-center p-2 bg-gray-100 rounded-lg space-x-2">
{/* Jahr Auswahl */}
<label htmlFor="jahrSelect" className="text-sm font-semibold">
Jahr
</label>
<select
id="jahrSelect"
className="border rounded px-2 py-1"
value={jahr}
onChange={(e) => setJahr(Number(e.target.value))}
>
{Array.from({ length: 11 }, (_, i) => 2020 + i).map((year) => (
<option key={year} value={year}>
{year}
</option>
))}
</select>
{/* Monat Auswahl */}
<label htmlFor="monatSelect" className="text-sm font-semibold">
Monat
</label>
<select
id="monatSelect"
className="border rounded px-2 py-1"
value={monat}
onChange={(e) => setMonat(Number(e.target.value))}
>
{Array.from({ length: 12 }, (_, i) => i + 1).map((month) => (
<option key={month} value={month}>
{month.toString().padStart(2, "0")}
</option>
))}
</select>
{/* Datei Auswahl */}
<label htmlFor="dateiSelect" className="text-sm font-semibold">
Auswahl
</label>
<select
id="dateiSelect"
className="border rounded px-2 py-1"
value={ausgewählteDatei}
onChange={handleDateiAuswahl}
>
{dateiListe.map((file, index) => (
<option key={index} value={file}>
{file}
</option>
))}
</select>
{/* Sortieren-Button */}
<button
onClick={handleSortToggle}
className="px-3 py-1 bg-blue-500 text-white rounded text-sm"
>
<i className={`bi bi-arrow-${sortAscending ? "down" : "up"}-short`} />
<span className="ml-1">Sortieren</span>
</button>
{idsForSlot.length > 0 && (
<>
<label htmlFor="tdrIdSelect" className="text-sm font-semibold">
Messung ID
</label>
<select
id="tdrIdSelect"
value={selectedId ?? ""}
onChange={handleSelectChange}
className="border rounded px-2 py-1 text-sm"
>
<option value="">-- Wähle Messung --</option>
{idsForSlot.map((entry) => (
<option key={entry.id} value={entry.id}>
ID {entry.id} {entry.t}
</option>
))}
</select>
</>
)}
</div>
);
};

View File

@@ -6,5 +6,5 @@
2: Patch oder Hotfix (Bugfixes oder kleine Änderungen).
*/
const webVersion = "1.6.151";
const webVersion = "1.6.152";
export default webVersion;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
// /redux/slices/tdmChartSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { fetchAllTDMData } from "../thunks/fetchAllTDMThunk";
import type { TDMEntry } from "../../types"; // optional, wenn ausgelagert

View File

@@ -0,0 +1,34 @@
// redux/slices/tdrDataByIdSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface TDRDataState {
dataById: {
[id: number]: { d: number; p: number }[];
};
selectedId: number | null;
}
const initialState: TDRDataState = {
dataById: {},
selectedId: null,
};
const tdrDataByIdSlice = createSlice({
name: "tdrDataById",
initialState,
reducers: {
setTDRChartDataById: (
state,
action: PayloadAction<{ id: number; data: { d: number; p: number }[] }>
) => {
state.dataById[action.payload.id] = action.payload.data;
},
setSelectedTDRId: (state, action: PayloadAction<number>) => {
state.selectedId = action.payload;
},
},
});
export const { setTDRChartDataById, setSelectedTDRId } =
tdrDataByIdSlice.actions;
export default tdrDataByIdSlice.reducer;

View File

@@ -17,6 +17,7 @@ import digitalInputsReducer from "./slices/digitalInputsSlice";
import tdrReferenceChartReducer from "./slices/tdrReferenceChartSlice";
import loopChartReducer from "./slices/loopChartSlice";
import tdmChartReducer from "./slices/tdmChartSlice";
import tdrDataByIdReducer from "./slices/tdrDataByIdSlice";
const store = configureStore({
reducer: {
@@ -36,6 +37,7 @@ const store = configureStore({
tdrReferenceChart: tdrReferenceChartReducer,
loopChart: loopChartReducer,
tdmChart: tdmChartReducer,
tdrDataById: tdrDataByIdReducer,
},
});

View File

@@ -5,18 +5,18 @@ export const fetchAllTDMDataFromServer = async (): Promise<any[]> => {
const isDev = window.location.hostname === "localhost";
const baseUrl = isDev
? "/CPLmockData/TDM" // z.B. /public/CPLmockData/TDM/slot0.json usw.
: `${window.location.origin}/CPL?Service/empty.acp&TDM`;
const slotRequests = Array.from({ length: 32 }, (_, i) => {
const url = isDev
? `/CPLmockData/TDM/slot${i}.json` // ✅ korrekt für DEV (Dateien im public-Ordner)
: `${window.location.origin}/CPL?Service/empty.acp&TDM=${i}`; // ✅ korrekt für PROD
const slotRequests = Array.from({ length: 32 }, (_, i) =>
fetch(`${baseUrl}=${i}`)
return fetch(url)
.then((res) => (res.ok ? res.json() : null))
.catch((err) => {
console.error(`❌ Fehler bei Slot ${i}:`, err);
return null;
})
);
});
});
return await Promise.all(slotRequests);
};

View File

@@ -0,0 +1,22 @@
// /services/fetchTDRChartDataById.ts
export const fetchTDRChartDataById = async (
id: number
): Promise<any[] | null> => {
const isDev = process.env.NODE_ENV === "development";
const url = isDev
? `http://localhost:3000/CPLmockData/Last100TDR/kue_01/id/${id}.json`
: `${window.location.origin}/CPL?Service/empty.acp&TDR=${id}`;
try {
const response = await fetch(url);
if (!response.ok)
throw new Error(`Fehler beim Laden der TDR-Daten für ID ${id}`);
return await response.json();
} catch (error) {
console.error("❌ Fehler in fetchTDRChartDataById:", error);
return null;
}
};