diff --git a/.env.development b/.env.development index 97f4d3e..5b87f7e 100644 --- a/.env.development +++ b/.env.development @@ -6,6 +6,6 @@ NEXT_PUBLIC_USE_MOCK_BACKEND_LOOP_START=false NEXT_PUBLIC_EXPORT_STATIC=false NEXT_PUBLIC_USE_CGI=false # App-Versionsnummer -NEXT_PUBLIC_APP_VERSION=1.6.538 +NEXT_PUBLIC_APP_VERSION=1.6.539 NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter) diff --git a/.env.production b/.env.production index 641475c..4713886 100644 --- a/.env.production +++ b/.env.production @@ -5,5 +5,5 @@ NEXT_PUBLIC_CPL_API_PATH=/CPL NEXT_PUBLIC_EXPORT_STATIC=true NEXT_PUBLIC_USE_CGI=true # App-Versionsnummer -NEXT_PUBLIC_APP_VERSION=1.6.538 +NEXT_PUBLIC_APP_VERSION=1.6.539 NEXT_PUBLIC_CPL_MODE=production \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5757781..ead0de8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +## [1.6.539] – 2025-07-07 + +- feat: fetch-Services für Spannung und Temperatur für Dev- und Prod-Modus angepasst + +- fetchSystemspannung5VplusService: Channel 110 (+5V), prod = /cpl?/dashboard.html +- fetchSystemspannung15VplusService: Channel 108 (+15V) +- fetchSystemspannung15VminusService: Channel 114 (-15V) +- fetchSystemspannung98VminusService: Channel 115 (-98V) +- fetchTemperaturAdWandlerService: Channel 116 (Temperatur AD-Wandler) +- fetchTemperaturProzessorService: Channel 117 (Temperatur Prozessor) + +→ Dev-Mode verwendet API-Handler (/api/cpl/...) +→ Production-Mode nutzt CGI-kompatible URLs (/cpl?/dashboard.html&...) + +Fehlerbehandlung integriert, Struktur für Wiederverwendung vereinheitlicht + +--- ## [1.6.538] – 2025-07-03 - feat: Detailansicht auf dynamische Redux-Datenquellen umgestellt diff --git a/components/main/reports/MeldungenTabelle.tsx b/components/main/reports/MeldungenTabelle.tsx new file mode 100644 index 0000000..1589cc0 --- /dev/null +++ b/components/main/reports/MeldungenTabelle.tsx @@ -0,0 +1,51 @@ +type Meldung = { + t: string; + s: number; + c: string; + m: string; + i: string; + v: string; +}; + +export default function MeldungenTabelle({ + messages, +}: { + messages: Meldung[]; +}) { + return ( +
+ + + + + + + + + + + + {messages.map((msg, index) => ( + + + + + + + + ))} + +
PrioZeitstempelQuelleMeldungStatus
+
+
{msg.t}{msg.i}{msg.m}{msg.v}
+ {messages.length === 0 && ( +
+ Keine Meldungen im gewählten Zeitraum vorhanden. +
+ )} +
+ ); +} diff --git a/components/main/reports/MeldungenView.tsx b/components/main/reports/MeldungenView.tsx new file mode 100644 index 0000000..57c4d2f --- /dev/null +++ b/components/main/reports/MeldungenView.tsx @@ -0,0 +1,81 @@ +"use client"; +import React, { useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { getMessagesThunk } from "@/redux/thunks/getMessagesThunk"; +import type { AppDispatch } from "@/redux/store"; +import type { RootState } from "@/redux/store"; +import DateRangePickerMeldungen from "./DateRangePickerMeldungen"; +import MeldungenTabelle from "./MeldungenTabelle"; + +type Meldung = { + t: string; + s: number; + c: string; + m: string; + i: string; + v: string; +}; + +export default function MeldungenView() { + const dispatch = useDispatch(); + const messages = useSelector((state: RootState) => state.messages.data); + + const [sourceFilter, setSourceFilter] = useState("Alle"); + + const today = new Date(); + const prior30 = new Date(); + prior30.setDate(today.getDate() - 30); + + const formatDate = (d: Date) => d.toISOString().split("T")[0]; + + const [fromDate, setFromDate] = useState(formatDate(prior30)); + const [toDate, setToDate] = useState(formatDate(today)); + + useEffect(() => { + dispatch(getMessagesThunk({ fromDate, toDate })); + }, [dispatch, fromDate, toDate]); + + const filteredMessages = + sourceFilter === "Alle" + ? messages + : messages.filter((m: Meldung) => m.i === sourceFilter); + + const allSources = Array.from( + new Set(messages.map((m: Meldung) => m.i)) + ).sort(); + + return ( +
+

Berichte

+ +
+ + + +
+ + +
+ ); +} diff --git a/components/main/system/SystemOverviewGrid.tsx b/components/main/system/SystemOverviewGrid.tsx index 0f4e3e1..fc4515f 100644 --- a/components/main/system/SystemOverviewGrid.tsx +++ b/components/main/system/SystemOverviewGrid.tsx @@ -20,9 +20,9 @@ export const SystemOverviewGrid = ({ voltages, onOpenDetail }: Props) => { {formatValue(value)} {unit}

diff --git a/components/main/system/SystemView.tsx b/components/main/system/SystemView.tsx new file mode 100644 index 0000000..3623871 --- /dev/null +++ b/components/main/system/SystemView.tsx @@ -0,0 +1,78 @@ +// components/main/system/system.tsx +"use client"; +import React, { useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { AppDispatch, RootState } from "@/redux/store"; +import { getSystemVoltTempThunk } from "@/redux/thunks/getSystemVoltTempThunk"; +import { SystemOverviewGrid } from "@/components/main/system/SystemOverviewGrid"; +import { SystemCharts } from "@/components/main/system/SystemCharts"; +import { DetailModal } from "@/components/main/system/DetailModal"; +import type { HistoryEntry } from "@/components/main/system/SystemCharts"; +import { getSystemspannung5VplusThunk } from "@/redux/thunks/getSystemspannung5VplusThunk"; +import { getSystemspannung15VplusThunk } from "@/redux/thunks/getSystemspannung15VplusThunk"; +import { getSystemspannung15VminusThunk } from "@/redux/thunks/getSystemspannung15VminusThunk"; +import { getSystemspannung98VminusThunk } from "@/redux/thunks/getSystemspannung98VminusThunk"; +import { getTemperaturAdWandlerThunk } from "@/redux/thunks/getTemperaturAdWandlerThunk"; +import { getTemperaturProzessorThunk } from "@/redux/thunks/getTemperaturProzessorThunk"; + +const SystemPage = () => { + const dispatch = useDispatch(); + const voltages = useSelector( + (state: RootState) => state.systemVoltTemp.voltages + ); + + const history = useSelector( + (state: RootState) => state.systemVoltTemp.history + ) as HistoryEntry[]; + + const [selectedKey, setSelectedKey] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); + const [zeitraum, setZeitraum] = useState<"DIA0" | "DIA1" | "DIA2">("DIA1"); + + useEffect(() => { + dispatch(getSystemVoltTempThunk()); + + const interval = setInterval(() => { + dispatch(getSystemVoltTempThunk()); + }, 5000); + return () => clearInterval(interval); + }, [dispatch]); + + useEffect(() => { + dispatch(getSystemspannung5VplusThunk(zeitraum)); + dispatch(getSystemspannung15VplusThunk(zeitraum)); + dispatch(getSystemspannung15VminusThunk(zeitraum)); + dispatch(getSystemspannung98VminusThunk(zeitraum)); + dispatch(getTemperaturAdWandlerThunk(zeitraum)); + dispatch(getTemperaturProzessorThunk(zeitraum)); + }, [dispatch, zeitraum]); + + const handleOpenDetail = (key: string) => { + setSelectedKey(key); + setIsModalOpen(true); + }; + + const handleCloseDetail = () => { + setSelectedKey(null); + setIsModalOpen(false); + }; + + return ( +
+

+ System Spannungen & Temperaturen +

+ + + +
+ ); +}; + +export default SystemPage; diff --git a/package-lock.json b/package-lock.json index 9a0e091..290c5b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cpl-v4", - "version": "1.6.538", + "version": "1.6.539", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cpl-v4", - "version": "1.6.538", + "version": "1.6.539", "dependencies": { "@fontsource/roboto": "^5.1.0", "@iconify-icons/ri": "^1.2.10", diff --git a/package.json b/package.json index eeadc76..861fcf3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cpl-v4", - "version": "1.6.538", + "version": "1.6.539", "private": true, "scripts": { "dev": "next dev", diff --git a/pages/_app.tsx b/pages/_app.tsx index 1065d6d..f7c9703 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -3,6 +3,8 @@ import { useEffect, useState } from "react"; import { Provider } from "react-redux"; +import { PersistGate } from "redux-persist/integration/react"; +import { persistor } from "@/redux/store"; import store, { useAppDispatch } from "@/redux/store"; import { AppProps } from "next/app"; @@ -36,9 +38,11 @@ import "@/styles/globals.css"; function MyApp({ Component, pageProps }: AppProps) { return ( - - - + + + + + ); } diff --git a/pages/meldungen.tsx b/pages/meldungen.tsx index b61c858..64b572d 100644 --- a/pages/meldungen.tsx +++ b/pages/meldungen.tsx @@ -1,119 +1,7 @@ -"use client"; // /pages/meldungen.tsx -import React, { useState, useEffect } from "react"; -import DateRangePickerMeldungen from "@/components/main/reports/DateRangePickerMeldungen"; -import { useSelector, useDispatch } from "react-redux"; -import { getMessagesThunk } from "@/redux/thunks/getMessagesThunk"; -import type { AppDispatch } from "@/redux/store"; +"use client"; +import MeldungenView from "@/components/main/reports/MeldungenView"; -type Meldung = { - t: string; - s: number; - c: string; - m: string; - i: string; - v: string; -}; export default function Messages() { - const dispatch = useDispatch(); - type RootState = { - messages: { - data: Meldung[]; - }; - // add other slices if needed - }; - const { data: messages } = useSelector((state: RootState) => state.messages); - - const [sourceFilter, setSourceFilter] = useState("Alle"); - - // Datum initialisieren: von = heute - 30 Tage, bis = heute - const today = new Date(); - const prior30 = new Date(); - prior30.setDate(today.getDate() - 30); - - const formatDate = (d: Date) => d.toISOString().split("T")[0]; - - const [fromDate, setFromDate] = useState(formatDate(prior30)); - const [toDate, setToDate] = useState(formatDate(today)); - - useEffect(() => { - dispatch(getMessagesThunk({ fromDate, toDate })); - }, [dispatch, fromDate, toDate]); - - const filteredMessages = - sourceFilter === "Alle" - ? messages - : messages.filter((m: Meldung) => m.i === sourceFilter); - - const allSources = Array.from( - new Set(messages.map((m: Meldung) => m.i)) - ).sort(); - - return ( -
-

Berichte

- -
- - - -
- -
- - - - - - - - {/* statt Status */} - - - - {filteredMessages.map((msg: Meldung, index: number) => ( - - - - - - {/* NEU */} - - ))} - -
PrioZeitstempelQuelleMeldungWert
-
-
{msg.t}{msg.i}{msg.m}{msg.v}
- {messages.length === 0 && ( -
- Keine Meldungen im gewählten Zeitraum vorhanden. -
- )} -
-
- ); + return ; } diff --git a/pages/system.tsx b/pages/system.tsx index f51ea4d..154a017 100644 --- a/pages/system.tsx +++ b/pages/system.tsx @@ -1,78 +1,7 @@ // pages/system.tsx "use client"; -import React, { useEffect, useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { AppDispatch, RootState } from "../redux/store"; -import { getSystemVoltTempThunk } from "../redux/thunks/getSystemVoltTempThunk"; -import { SystemOverviewGrid } from "@/components/main/system/SystemOverviewGrid"; -import { SystemCharts } from "@/components/main/system/SystemCharts"; -import { DetailModal } from "@/components/main/system/DetailModal"; -import type { HistoryEntry } from "@/components/main/system/SystemCharts"; -import { getSystemspannung5VplusThunk } from "@/redux/thunks/getSystemspannung5VplusThunk"; -import { getSystemspannung15VplusThunk } from "@/redux/thunks/getSystemspannung15VplusThunk"; -import { getSystemspannung15VminusThunk } from "@/redux/thunks/getSystemspannung15VminusThunk"; -import { getSystemspannung98VminusThunk } from "@/redux/thunks/getSystemspannung98VminusThunk"; -import { getTemperaturAdWandlerThunk } from "@/redux/thunks/getTemperaturAdWandlerThunk"; -import { getTemperaturProzessorThunk } from "@/redux/thunks/getTemperaturProzessorThunk"; +import SystemView from "@/components/main/system/SystemView"; -const SystemPage = () => { - const dispatch = useDispatch(); - const voltages = useSelector( - (state: RootState) => state.systemVoltTemp.voltages - ); - - const history = useSelector( - (state: RootState) => state.systemVoltTemp.history - ) as HistoryEntry[]; - - const [selectedKey, setSelectedKey] = useState(null); - const [isModalOpen, setIsModalOpen] = useState(false); - const [zeitraum, setZeitraum] = useState<"DIA0" | "DIA1" | "DIA2">("DIA1"); - - useEffect(() => { - dispatch(getSystemVoltTempThunk()); - - const interval = setInterval(() => { - dispatch(getSystemVoltTempThunk()); - }, 5000); - return () => clearInterval(interval); - }, [dispatch]); - - useEffect(() => { - dispatch(getSystemspannung5VplusThunk(zeitraum)); - dispatch(getSystemspannung15VplusThunk(zeitraum)); - dispatch(getSystemspannung15VminusThunk(zeitraum)); - dispatch(getSystemspannung98VminusThunk(zeitraum)); - dispatch(getTemperaturAdWandlerThunk(zeitraum)); - dispatch(getTemperaturProzessorThunk(zeitraum)); - }, [dispatch, zeitraum]); - - const handleOpenDetail = (key: string) => { - setSelectedKey(key); - setIsModalOpen(true); - }; - - const handleCloseDetail = () => { - setSelectedKey(null); - setIsModalOpen(false); - }; - - return ( -
-

- System Spannungen & Temperaturen -

- - - -
- ); -}; - -export default SystemPage; +export default function SystemPage() { + return ; +} diff --git a/public/CPL/js/abfrage.js b/public/CPL/js/abfrage.js deleted file mode 100644 index 0b21960..0000000 --- a/public/CPL/js/abfrage.js +++ /dev/null @@ -1,17 +0,0 @@ -// window-Funktion, damit sie global im Browser verfügbar ist -window.startAbfrage = function (fromDate, toDate, quelle, typ) { - // Datum formatieren: yyyy;MM;dd - const format = (dateStr) => { - const d = new Date(dateStr); - return `${d.getFullYear()};${d.getMonth() + 1};${d.getDate()}`; - }; - - const von = format(fromDate); - const bis = format(toDate); - - // Beispiel-Kanal: 110 = +5V - const diaURL = `/cpl?/dashboard.html&DIA1=${von};${bis};${quelle};${typ};`; - - // Neue Seite öffnen - window.open(diaURL, "_blank"); -}; diff --git a/redux/store.ts b/redux/store.ts index 396a95b..c2d1ddf 100644 --- a/redux/store.ts +++ b/redux/store.ts @@ -1,6 +1,7 @@ // /redux/store.ts import { configureStore } from "@reduxjs/toolkit"; -import { useDispatch } from "react-redux"; +import { persistReducer, persistStore } from "redux-persist"; +import storage from "redux-persist/lib/storage"; // = localStorage import authReducer from "./slices/authSlice"; import kueChartModeReducer from "./slices/kueChartModeSlice"; import webVersionReducer from "./slices/webVersionSlice"; @@ -35,48 +36,78 @@ import systemspannung15VplusReducer from "./slices/systemspannung15VplusSlice"; import systemspannung98VminusReducer from "./slices/systemspannung98VminusSlice"; import temperaturAdWandlerReducer from "./slices/temperaturAdWandlerSlice"; import temperaturProzessorReducer from "./slices/temperaturProzessorSlice"; +import { combineReducers } from "@reduxjs/toolkit"; +import { useDispatch, useSelector, TypedUseSelectorHook } from "react-redux"; -const store = configureStore({ - reducer: { - authSlice: authReducer, - kueChartModeSlice: kueChartModeReducer, - webVersionSlice: webVersionReducer, - digitalInputsSlice: digitalInputsReducer, - kabelueberwachungChartSlice: kabelueberwachungChartReducer, - last20MessagesSlice: last20MessagesReducer, - systemSettingsSlice: systemSettingsReducer, - opcuaSettingsSlice: opcuaSettingsReducer, - digitalOutputsSlice: digitalOutputsReducer, - analogInputs: analogInputsSlice, - brushSlice: brushReducer, - tdrChartSlice: tdrChartReducer, - tdrReferenceChartSlice: tdrReferenceChartReducer, - loopChartSlice: loopChartReducer, - tdmChartSlice: tdmChartReducer, - tdrDataByIdSlice: tdrDataByIdReducer, - kueDataSlice: kueDataReducer, - selectedChartDataSlice: selectedChartDataReducer, - tdmSingleChartSlice: tdmSingleChartReducer, - tdrReferenceChartDataBySlotSlice: tdrReferenceChartDataBySlotReducer, - loopChartType: loopChartTypeSlice, - systemVoltTemp: systemVoltTempReducer, - analogInputsHistory: analogInputsHistoryReducer, - selectedAnalogInput: selectedAnalogInputReducer, - messages: messagesReducer, - firmwareUpdate: firmwareUpdateReducer, - confirmModal: confirmModalReducer, - firmwareProgress: firmwareProgressReducer, - systemspannung5Vplus: systemspannung5VplusReducer, - systemspannung15Vminus: systemspannung15VminusReducer, - systemspannung15Vplus: systemspannung15VplusReducer, - systemspannung98Vminus: systemspannung98VminusReducer, - temperaturAdWandler: temperaturAdWandlerReducer, - temperaturProzessor: temperaturProzessorReducer, - }, +//--------------------------------------- +// 🧠 Nur diese Slices werden persistiert +const persistConfig = { + key: "root", + storage, + whitelist: [ + "systemspannung5Vplus", + "systemspannung15Vplus", + "systemspannung15Vminus", + "systemspannung98Vminus", + "temperaturAdWandler", + "temperaturProzessor", + ], +}; + +const rootReducer = combineReducers({ + systemspannung5Vplus: systemspannung5VplusReducer, + systemspannung15Vplus: systemspannung15VplusReducer, + systemspannung15Vminus: systemspannung15VminusReducer, + systemspannung98Vminus: systemspannung98VminusReducer, + temperaturAdWandler: temperaturAdWandlerReducer, + temperaturProzessor: temperaturProzessorReducer, + // Die restlichen Slices werden nicht persistiert, aber können hier auch aufgenommen werden, + // falls sie Teil des globalen States sein sollen: + authSlice: authReducer, + kueChartModeSlice: kueChartModeReducer, + webVersionSlice: webVersionReducer, + digitalInputsSlice: digitalInputsReducer, + kabelueberwachungChartSlice: kabelueberwachungChartReducer, + last20MessagesSlice: last20MessagesReducer, + systemSettingsSlice: systemSettingsReducer, + opcuaSettingsSlice: opcuaSettingsReducer, + digitalOutputsSlice: digitalOutputsReducer, + analogInputs: analogInputsSlice, + brushSlice: brushReducer, + tdrChartSlice: tdrChartReducer, + tdrReferenceChartSlice: tdrReferenceChartReducer, + loopChartSlice: loopChartReducer, + tdmChartSlice: tdmChartReducer, + tdrDataByIdSlice: tdrDataByIdReducer, + kueDataSlice: kueDataReducer, + selectedChartDataSlice: selectedChartDataReducer, + tdmSingleChartSlice: tdmSingleChartReducer, + tdrReferenceChartDataBySlotSlice: tdrReferenceChartDataBySlotReducer, + loopChartType: loopChartTypeSlice, + systemVoltTemp: systemVoltTempReducer, + analogInputsHistory: analogInputsHistoryReducer, + selectedAnalogInput: selectedAnalogInputReducer, + messages: messagesReducer, + firmwareUpdate: firmwareUpdateReducer, + confirmModal: confirmModalReducer, + firmwareProgress: firmwareProgressReducer, }); +const persistedReducer = persistReducer(persistConfig, rootReducer); + +//------------------------------------------- + +export const store = configureStore({ + reducer: persistedReducer, +}); + +export const persistor = persistStore(store); + +// Typisierte Hooks export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; -export const useAppDispatch: () => AppDispatch = useDispatch; // ✅ Typisierte Dispatch-Funktion + +export const useAppDispatch = () => useDispatch(); +export const useAppSelector: TypedUseSelectorHook = useSelector; export default store;