diff --git a/components/main/analogeEingaenge/AnalogeEingaengeTable.tsx b/components/main/analogeEingaenge/AnalogeEingaengeTable.tsx index 408b6d2..655a84a 100644 --- a/components/main/analogeEingaenge/AnalogeEingaengeTable.tsx +++ b/components/main/analogeEingaenge/AnalogeEingaengeTable.tsx @@ -1,19 +1,20 @@ "use client"; // components/main/analogeEingaenge/AnalogeEingaengeTable.tsx -import React, { useState } from "react"; -import { useSelector } from "react-redux"; -import { RootState } from "../../../redux/store"; -import { useFetchAnalogeEingaenge } from "./hooks/useFetchAnalogeEingaenge"; +import React, { useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { RootState, AppDispatch } from "../../../redux/store"; +import { fetchAnalogeEingaengeThunk } from "../../../redux/thunks/fetchAnalogeEingaengeThunk"; export default function AnalogeEingaengeTable() { - useFetchAnalogeEingaenge(); + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(fetchAnalogeEingaengeThunk()); // ✅ Holt die API-Daten nur im Client + }, [dispatch]); const analogeEingaenge = useSelector( (state: RootState) => state.analogeEingaenge ); - console.log("Aktuelle Redux-Daten:", analogeEingaenge); - - // State für das geöffnete Modal const [selectedEingang, setSelectedEingang] = useState(null); const openSettingsModal = (eingang: any) => { @@ -115,71 +116,6 @@ export default function AnalogeEingaengeTable() { defaultValue="Spg." readOnly /> - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- - -
-
- - -
diff --git a/components/main/analogeEingaenge/hooks/useFetchAnalogeEingaenge.ts b/components/main/analogeEingaenge/hooks/useFetchAnalogeEingaenge.ts deleted file mode 100644 index b84cff3..0000000 --- a/components/main/analogeEingaenge/hooks/useFetchAnalogeEingaenge.ts +++ /dev/null @@ -1,30 +0,0 @@ -// components/main/analogeEingaenge/hooks/useFetchAnalogeEingaenge.ts -import { useEffect } from "react"; -import { useAppDispatch } from "../../../../redux/store"; // ✅ Typisierte Dispatch-Funktion verwenden -import { - loadFromWindow, - setAnalogeEingaenge, -} from "../../../../redux/slices/analogeEingaengeSlice"; - -export const useFetchAnalogeEingaenge = () => { - const dispatch = useAppDispatch(); // ✅ Verwende typisierten Dispatch - - useEffect(() => { - dispatch(loadFromWindow()); // Initial Mock-Daten aus `window` laden - - const interval = setInterval(async () => { - try { - dispatch(loadFromWindow()); // 🔄 Mock-Daten regelmäßig neu laden - - const response = await fetch("/api/get-embedded-data"); - if (!response.ok) throw new Error("Fehler beim Abrufen der Daten"); - const data = await response.json(); - dispatch(setAnalogeEingaenge(data)); // ✅ API-Daten in Redux speichern - } catch (error) { - console.error("Fehler beim Abruf der Sensordaten:", error); - } - }, 5000); // Alle 5 Sekunden neue Daten abrufen - - return () => clearInterval(interval); - }, [dispatch]); -}; diff --git a/config/webVersion.ts b/config/webVersion.ts index df1d166..c3fc93f 100644 --- a/config/webVersion.ts +++ b/config/webVersion.ts @@ -6,5 +6,5 @@ 2: Patch oder Hotfix (Bugfixes oder kleine Änderungen). */ -const webVersion = "1.6.135"; +const webVersion = "1.6.136"; export default webVersion; diff --git a/draww.io/test.drawio b/draww.io/test.drawio new file mode 100644 index 0000000..e69de29 diff --git a/pages/_app.tsx b/pages/_app.tsx index 276ada5..8257c55 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,24 +1,35 @@ "use client"; // pages/_app.tsx + import { useEffect, useState } from "react"; +import { Provider } from "react-redux"; +import store, { useAppDispatch } from "../redux/store"; +import { fetchAnalogeEingaengeThunk } from "../redux/thunks/fetchAnalogeEingaengeThunk"; import { loadWindowVariables } from "../utils/loadWindowVariables"; import Header from "../components/header/Header"; import Navigation from "../components/navigation/Navigation"; import Footer from "../components/footer/Footer"; +import WindowVariablesInitializer from "../components/WindowVariablesInitializer"; import "../styles/globals.css"; -import { Provider } from "react-redux"; +import { AppProps } from "next/app"; import { setVariables } from "../redux/slices/variablesSlice"; -import { setSystemSettings } from "../redux/slices/systemSettingsSlice"; // ✅ System-Settings +import { setSystemSettings } from "../redux/slices/systemSettingsSlice"; import { setOpcUaZustand, setOpcUaActiveClientCount, setOpcUaNodesetName, -} from "../redux/slices/opcuaSettingsSlice"; // ✅ OPC-UA Settings -import store from "../redux/store"; -import { AppProps } from "next/app"; -import WindowVariablesInitializer from "../components/WindowVariablesInitializer"; +} from "../redux/slices/opcuaSettingsSlice"; function MyApp({ Component, pageProps }: AppProps) { + return ( + + + + ); +} + +function AppContent({ Component, pageProps }: AppProps) { + const dispatch = useAppDispatch(); const [sessionExpired, setSessionExpired] = useState(false); useEffect(() => { @@ -27,9 +38,10 @@ function MyApp({ Component, pageProps }: AppProps) { const variables = await loadWindowVariables(); if (!variables) throw new Error("Sitzungsfehler"); - // ✅ OPC-UA Werte, System-Settings und last20Messages separat speichern + console.log("✅ Window-Variablen geladen:", variables); + const { - last20Messages, // Entfernen für eigenes Redux-Slice + last20Messages, opcUaZustand, opcUaActiveClientCount, opcUaNodesetName, @@ -44,16 +56,10 @@ function MyApp({ Component, pageProps }: AppProps) { ntp3, ntpTimezone, ntpActive, - de, - de_label, - de_state, - da_state, - da_bezeichnung, ...restVariables } = variables; - // ✅ Speichere System-Settings in systemSettingsSlice - store.dispatch( + dispatch( setSystemSettings({ deviceName, mac1, @@ -69,54 +75,55 @@ function MyApp({ Component, pageProps }: AppProps) { }) ); - // ✅ Speichere OPC-UA Einstellungen in opcuaSettingsSlice - store.dispatch(setOpcUaZustand(opcUaZustand || "Offline")); - store.dispatch(setOpcUaActiveClientCount(opcUaActiveClientCount || 0)); - store.dispatch( - setOpcUaNodesetName(opcUaNodesetName || "DefaultNodeset") - ); + dispatch(setOpcUaZustand(opcUaZustand || "Offline")); + dispatch(setOpcUaActiveClientCount(opcUaActiveClientCount || 0)); + dispatch(setOpcUaNodesetName(opcUaNodesetName || "DefaultNodeset")); - // ✅ Speichere alle anderen Variablen in variablesSlice - store.dispatch(setVariables(restVariables)); + dispatch(setVariables(restVariables)); setSessionExpired(false); } catch (error) { - console.error("Fehler beim Laden der Sitzung:", error); + console.error("❌ Fehler beim Laden der Sitzung:", error); setSessionExpired(true); } }; if (typeof window !== "undefined") { - loadAndStoreVariables(); // Initiales Laden + loadAndStoreVariables(); - // Intervall zum Aktualisieren des Redux-Stores alle 10 Sekunden const intervalId = setInterval(loadAndStoreVariables, 10000); - - // Bereinigen des Intervalls, wenn die Komponente unmountet wird return () => clearInterval(intervalId); } }, []); + useEffect(() => { + if (typeof window !== "undefined") { + dispatch(fetchAnalogeEingaengeThunk()); + const interval = setInterval(() => { + dispatch(fetchAnalogeEingaengeThunk()); + }, 10000); + return () => clearInterval(interval); + } + }, [dispatch]); + return ( - +
-
-
-
- -
- {sessionExpired && ( -
- Ihre Sitzung ist abgelaufen oder die Verbindung ist - unterbrochen. Bitte laden Sie die Seite neu. -
- )} - -
-
-
+
+
+ +
+ {sessionExpired && ( +
+ ❌ Ihre Sitzung ist abgelaufen oder die Verbindung ist + unterbrochen. Bitte laden Sie die Seite neu. +
+ )} + +
- +
+
); } diff --git a/public/CPLmockData/SERVICE/ae.js b/public/CPLmockData/SERVICE/ae.js index 6a38dbc..6a181ef 100644 --- a/public/CPLmockData/SERVICE/ae.js +++ b/public/CPLmockData/SERVICE/ae.js @@ -1,5 +1,5 @@ // public/CPLmockData/SERVICE/ae.js -var win_analogeEingaenge1 = [1, 0, "Sensor1", 1, 1, 0, 1]; // Eingang 1 +var win_analogeEingaenge1 = [1, 0, "Sensor2", 1, 1, 0, 1]; // Eingang 1 var win_analogeEingaenge2 = [2, 22.91, "Feuchtigkeit", 1, 1, 1, 0]; // Eingang 2 var win_analogeEingaenge3 = [3, 0, "Test", 1, 1, 0, 1]; // Eingang 3 var win_analogeEingaenge4 = [4, 0, "test2", 1, 1, 0, 1]; // Eingang 4 diff --git a/redux/slices/testSlice.ts b/redux/slices/testSlice.ts new file mode 100644 index 0000000..f7f626f --- /dev/null +++ b/redux/slices/testSlice.ts @@ -0,0 +1,22 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +interface TestState { + testData: any[]; +} + +const initialState: TestState = { + testData: [], +}; + +const testSlice = createSlice({ + name: "test", + initialState, + reducers: { + setTestData: (state, action: PayloadAction) => { + state.testData = action.payload; + }, + }, +}); + +export const { setTestData } = testSlice.actions; +export default testSlice.reducer; diff --git a/redux/store.ts b/redux/store.ts index 7e3217c..a49e0e1 100644 --- a/redux/store.ts +++ b/redux/store.ts @@ -11,9 +11,9 @@ import dashboardReducer from "./slices/dashboardSlice"; import systemSettingsReducer from "./slices/systemSettingsSlice"; import opcuaSettingsReducer from "./slices/opcuaSettingsSlice"; import digitalOutputsReducer from "./slices/digitalOutputsSlice"; -import analogeEingaengeReducer from "./slices/analogeEingaengeSlice"; import brushReducer from "./slices/brushSlice"; import tdrChartReducer from "./slices/tdrChartSlice"; +import analogeEingaengeReducer from "./slices/analogeEingaengeSlice"; const store = configureStore({ reducer: { diff --git a/redux/thunks/fetchAnalogeEingaengeThunk.ts b/redux/thunks/fetchAnalogeEingaengeThunk.ts new file mode 100644 index 0000000..42b82a0 --- /dev/null +++ b/redux/thunks/fetchAnalogeEingaengeThunk.ts @@ -0,0 +1,22 @@ +// /redux/thunks/fetchAnalogeEingaengeThunk.ts +import { createAsyncThunk } from "@reduxjs/toolkit"; +import { fetchAnalogeEingaenge } from "../../services/fetchAnalogeEingaenge"; +import { setAnalogeEingaenge } from "../slices/analogeEingaengeSlice"; + +/** + * Holt die analogen Eingänge von der API und speichert sie in Redux. + */ +export const fetchAnalogeEingaengeThunk = createAsyncThunk( + "analogeEingaenge/fetchAnalogeEingaenge", + async (_, { dispatch }) => { + if (typeof window === "undefined") return; // Server-Side Execution blockieren + try { + const data = await fetchAnalogeEingaenge(); + if (data) { + dispatch(setAnalogeEingaenge(data)); // ✅ Redux mit API-Daten füllen + } + } catch (error) { + console.error("❌ Fehler beim Laden der analogen Eingänge:", error); + } + } +); diff --git a/redux/thunks/fetchLoopChartDataThunk.ts b/redux/thunks/fetchLoopChartDataThunk.ts new file mode 100644 index 0000000..382a832 --- /dev/null +++ b/redux/thunks/fetchLoopChartDataThunk.ts @@ -0,0 +1,56 @@ +// /redux/thunks/fetchLoopChartDataThunk.ts +import { createAsyncThunk } from "@reduxjs/toolkit"; +import { fetchLoopChartData } from "../../services/fetchLoopChartData"; +import { setLoopMeasurementCurveChartData } from "../slices/kabelueberwachungChartSlice"; + +/** + * Holt die neuesten Daten von der API und speichert sie in Redux. + */ +export const fetchLoopChartDataThunk = createAsyncThunk( + "kabelueberwachungChart/fetchLoopChartData", + async ( + { + mode, + type, + slotNumber, + vonDatum, + bisDatum, + }: { + mode: "DIA0" | "DIA1" | "DIA2"; + type: number; + slotNumber: number; + vonDatum: string; + bisDatum: string; + }, + { dispatch } + ) => { + const data = await fetchLoopChartData( + mode, + type, + slotNumber, + vonDatum, + bisDatum + ); + if (data) { + dispatch(setLoopMeasurementCurveChartData(data)); + } + } +); + +/** + * Starte automatisches Polling (alle 10 Sekunden). + */ +export const startLoopChartDataPolling = () => (dispatch: any) => { + setInterval(() => { + console.log("🔄 Daten werden aktualisiert..."); + dispatch( + fetchLoopChartDataThunk({ + mode: "DIA0", + type: 4, + slotNumber: 6, + vonDatum: "2024-02-01", + bisDatum: "2024-02-10", + }) + ); + }, 10000); +}; diff --git a/services/fetchAnalogeEingaenge.ts b/services/fetchAnalogeEingaenge.ts new file mode 100644 index 0000000..0da6579 --- /dev/null +++ b/services/fetchAnalogeEingaenge.ts @@ -0,0 +1,62 @@ +// /services/fetchAnalogeEingaenge.ts +/** + * Bestimmt die richtige API-URL basierend auf der Umgebung. + */ +const getApiUrl = () => { + if (typeof window === "undefined") { + console.error("❌ `window` ist nicht verfügbar (Server-Side Rendering)"); + return null; // Server-Side darf nicht weiter ausführen + } + + return process.env.NODE_ENV === "development" + ? `${window.location.origin}/CPLmockData/SERVICE/ae.js` + : `${window.location.origin}/CPL/SERVICE/ae.js`; +}; + +/** + * Holt die analogen Eingänge aus der richtigen Quelle. + */ +export const fetchAnalogeEingaenge = async () => { + try { + const apiUrl = getApiUrl(); + if (!apiUrl) return null; // ❌ Falls SSR aktiv ist, nicht ausführen + + console.log(`📡 API-Request an: ${apiUrl}`); + + const response = await fetch(apiUrl); + if (!response.ok) { + throw new Error(`❌ Fehler: ${response.status} ${response.statusText}`); + } + + const rawData = await response.text(); + console.log("✅ Rohdaten erfolgreich geladen:", rawData); + + // **JavaScript-Variablen als Skript einfügen** + const script = document.createElement("script"); + script.innerHTML = rawData; + document.body.appendChild(script); + + // **Daten umwandeln** + const formattedData: Record = {}; + for (let i = 1; i <= 8; i++) { + const varName = `win_analogeEingaenge${i}`; + if (window[varName]) { + formattedData[varName] = { + id: window[varName][0], + value: window[varName][1], + name: window[varName][2], + uW: window[varName][3] === 1, + uG: window[varName][4] === 1, + oW: window[varName][5] === 1, + oG: window[varName][6] === 1, + }; + } + } + + console.log("✅ Formatierte Daten:", formattedData); + return formattedData; + } catch (error) { + console.error("❌ Fehler beim Laden der analogen Eingänge:", error); + return null; + } +}; diff --git a/services/fetchLoopChartData.ts b/services/fetchLoopChartData.ts new file mode 100644 index 0000000..f3c9c2a --- /dev/null +++ b/services/fetchLoopChartData.ts @@ -0,0 +1,76 @@ +// /services/fetchLoopChartData.ts +/** + * Erstellt die richtige API-URL basierend auf Umgebung, Mode, Type und Slot-Nummer. + */ +const getApiUrl = ( + mode: "DIA0" | "DIA1" | "DIA2", + type: number, + slotNumber: number, + vonDatum: string, + bisDatum: string +) => { + if (!slotNumber) { + console.error("⚠️ Slot-Nummer nicht gesetzt!"); + return ""; + } + + const typeFolder = + type === 3 ? "isolationswiderstand" : "schleifenwiderstand"; + + return process.env.NODE_ENV === "development" + ? `/CPLmockData/kuesChartData/slot${slotNumber}/${typeFolder}/${mode}.json` + : `${window.location.origin}/CPL?seite.ACP&${mode}=${formatDate( + vonDatum + )};${formatDate(bisDatum)};${slotNumber};${type};`; +}; + +/** + * Wandelt ein Datum von "YYYY-MM-DD" zu "YYYY;MM;DD" um (für die API-URL). + */ +const formatDate = (dateString: string) => { + const dateParts = dateString.split("-"); + return `${dateParts[0]};${dateParts[1]};${dateParts[2]}`; +}; + +/** + * Holt die Messwerte vom Embedded-System oder einer JSON-Datei. + */ +export const fetchLoopChartData = async ( + mode: "DIA0" | "DIA1" | "DIA2", + type: number, + slotNumber: number, + vonDatum: string, + bisDatum: string +) => { + try { + const apiUrl = getApiUrl(mode, type, slotNumber, vonDatum, bisDatum); + if (!apiUrl) { + throw new Error( + "❌ Keine gültige API-URL! in /services/fetchLoopChartdata.ts" + ); + } + + console.log( + `📡 Fetching data from in /services/fetchLoopChartdata.ts: ${apiUrl}` + ); + const response = await fetch(apiUrl); + + if (!response.ok) { + throw new Error(`❌ Fehler: ${response.status} ${response.statusText}`); + } + + const data = await response.json(); + console.log( + "✅ Daten erfolgreich geladen in /services/fetchLoopChartdata.ts:", + data + ); + + return data; + } catch (error) { + console.error( + "❌ Fehler beim Laden der Schleifenmesskurvendaten in /services/fetchLoopChartdata.ts:", + error + ); + return null; + } +}; diff --git a/utils/loadLoopChartData.ts b/utils/loadLoopChartData.ts index 8cb0161..da98e91 100644 --- a/utils/loadLoopChartData.ts +++ b/utils/loadLoopChartData.ts @@ -1,3 +1,4 @@ +// /utils/loadLoopChartData.ts import { createLoopChart } from "./chartUtils"; export const loadLoopChartData = (