feat(api): Zeitraum und Eingang als Pflichtparameter für AnalogInputs-API eingeführt

- API-Handler für /api/cpl/getAnalogInputsHistory überarbeitet
- `zeitraum` (DIA0, DIA1, DIA2) und `eingang` (1–8) sind jetzt Pflichtfelder
- Bei fehlenden oder ungültigen Parametern strukturierte Fehlerantwort mit Beispielen
- Daten werden nun gezielt pro Eingang und Zeitraum geladen (z. B. AE3 + DIA1)
- Bessere Fehlerbehandlung bei nicht vorhandenen Dateien
This commit is contained in:
ISA
2025-07-11 11:50:15 +02:00
parent 1f1e532233
commit 2d3e070830
8 changed files with 160 additions and 34 deletions

View File

@@ -6,6 +6,6 @@ NEXT_PUBLIC_USE_MOCK_BACKEND_LOOP_START=false
NEXT_PUBLIC_EXPORT_STATIC=false NEXT_PUBLIC_EXPORT_STATIC=false
NEXT_PUBLIC_USE_CGI=false NEXT_PUBLIC_USE_CGI=false
# App-Versionsnummer # App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.6.589 NEXT_PUBLIC_APP_VERSION=1.6.593
NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter) NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter)

View File

@@ -5,5 +5,5 @@ NEXT_PUBLIC_CPL_API_PATH=/CPL
NEXT_PUBLIC_EXPORT_STATIC=true NEXT_PUBLIC_EXPORT_STATIC=true
NEXT_PUBLIC_USE_CGI=true NEXT_PUBLIC_USE_CGI=true
# App-Versionsnummer # App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.6.589 NEXT_PUBLIC_APP_VERSION=1.6.593
NEXT_PUBLIC_CPL_MODE=production NEXT_PUBLIC_CPL_MODE=production

View File

@@ -1,3 +1,35 @@
## [1.6.593] 2025-07-11
- fix: Von/Bis-Datum beim Schließen des DetailModals zurücksetzen
- Redux-State für vonDatum und bisDatum wird bei handleClose geleert
- verhindert unerwünschtes Vorfiltern bei erneutem Öffnen des Modals
---
## [1.6.592] 2025-07-11
- fix: Von/Bis-Datum beim Schließen des DetailModals zurücksetzen
- Redux-State für vonDatum und bisDatum wird bei handleClose geleert
- verhindert unerwünschtes Vorfiltern bei erneutem Öffnen des Modals
---
## [1.6.591] 2025-07-11
- fix: Von/Bis-Datum beim Schließen des DetailModals zurücksetzen
- Redux-State für vonDatum und bisDatum wird bei handleClose geleert
- verhindert unerwünschtes Vorfiltern bei erneutem Öffnen des Modals
---
## [1.6.590] 2025-07-11
- fix: Von/Bis-Datum beim Schließen des DetailModals zurücksetzen
- Redux-State für vonDatum und bisDatum wird bei handleClose geleert
- verhindert unerwünschtes Vorfiltern bei erneutem Öffnen des Modals
---
## [1.6.589] 2025-07-11 ## [1.6.589] 2025-07-11
- feat: Zeitspanne-Funktion mit Von/Bis und Button-Trigger im DetailModal eingebaut - feat: Zeitspanne-Funktion mit Von/Bis und Button-Trigger im DetailModal eingebaut

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "cpl-v4", "name": "cpl-v4",
"version": "1.6.589", "version": "1.6.593",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "cpl-v4", "name": "cpl-v4",
"version": "1.6.589", "version": "1.6.593",
"dependencies": { "dependencies": {
"@fontsource/roboto": "^5.1.0", "@fontsource/roboto": "^5.1.0",
"@headlessui/react": "^2.2.4", "@headlessui/react": "^2.2.4",

View File

@@ -1,6 +1,6 @@
{ {
"name": "cpl-v4", "name": "cpl-v4",
"version": "1.6.589", "version": "1.6.593",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",

View File

@@ -1,43 +1,77 @@
// /pages/api/cpl/getAnalogInputsHistory.ts // /pages/api/cpl/getAnalogInputsHistory.ts
import path from "path"; import path from "path";
import fs from "fs/promises"; import fs from "fs/promises";
import type { NextApiRequest, NextApiResponse } from "next"; import type { NextApiRequest, NextApiResponse } from "next";
const erlaubteZeitraeume = ["DIA0", "DIA1", "DIA2"];
const erlaubteEingaenge = [1, 2, 3, 4, 5, 6, 7, 8];
export default async function handler( export default async function handler(
req: NextApiRequest, req: NextApiRequest,
res: NextApiResponse res: NextApiResponse<unknown>
) { ) {
const { zeitraum, eingang } = req.query;
const zeitraumKey =
typeof zeitraum === "string" && erlaubteZeitraeume.includes(zeitraum)
? zeitraum
: null;
const eingangId =
typeof eingang === "string" && !isNaN(Number(eingang))
? Number(eingang)
: null;
// ❌ Fehlerhafte Anfrage
if (!zeitraumKey || !eingangId || !erlaubteEingaenge.includes(eingangId)) {
return res.status(400).json({
error: "❌ Ungültige oder unvollständige Anfrage.",
erwartet: {
zeitraum: "Pflichtfeld. Erlaubt: DIA0 | DIA1 | DIA2",
eingang: "Pflichtfeld. Erlaubt: 1 bis 8 (entspricht AE1 bis AE8)",
},
beispiele: [
"/api/cpl/getAnalogInputsHistory?zeitraum=DIA1&eingang=3",
"/api/cpl/getAnalogInputsHistory?zeitraum=DIA2&eingang=7",
"http://localhost:3000/api/cpl/getAnalogInputsHistory?eingang=1&zeitraum=DIA0",
],
hinweis:
"Die Antwort enthält ein Array mit Messwertobjekten für den gewählten Eingang und Zeitraum.",
});
}
try { try {
const result: Record<number, unknown[]> = {}; const filePath = path.join(
process.cwd(),
"mocks",
"device-cgi-simulator",
"chartsData",
"analogInputs",
`${eingangId}`,
`${zeitraumKey}.json`
);
for (let i = 1; i <= 8; i++) { const fileContent = await fs.readFile(filePath, "utf-8");
const filePath = path.join( const daten = JSON.parse(fileContent);
process.cwd(),
"mocks",
"device-cgi-simulator",
"chartsData",
"analogInputs",
`${i}`,
`DIA0.json`
);
console.log(`Lade Mock-Daten für analogInput${i} von: ${filePath}`);
try { res.status(200).json({
const fileContent = await fs.readFile(filePath, "utf-8"); eingang: eingangId,
result[99 + i] = JSON.parse(fileContent); // z. B. 100 für AE1, 101 für AE2 zeitraum: zeitraumKey,
} catch (error) { beschreibung:
console.warn( zeitraumKey === "DIA0"
`Mock-Datei für analogInput${i} nicht gefunden oder fehlerhaft.`, ? "Alle Messwerte"
error : zeitraumKey === "DIA1"
); ? "Stündlich aggregierte Messwerte"
result[99 + i] = []; : "Täglich aggregierte Messwerte",
} daten,
} });
res.status(200).json(result);
} catch (error) { } catch (error) {
console.error("Fehler beim Laden der analogen Eingänge (Mock):", error); console.error(
res.status(500).json({ error: "Fehler beim Laden der Mock-Daten." }); `❌ Datei nicht gefunden für Eingang ${eingangId} und Zeitraum ${zeitraumKey}:`,
error
);
res.status(404).json({
error: `Keine Daten gefunden für Eingang ${eingangId} mit Zeitraum ${zeitraumKey}.`,
});
} }
} }

View File

@@ -0,0 +1,40 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
export type Zeitraum = "DIA0" | "DIA1" | "DIA2";
interface ChartState {
zeitraum: Zeitraum;
vonDatum: string;
bisDatum: string;
isLoading: boolean;
}
const initialState: ChartState = {
zeitraum: "DIA0",
vonDatum: "",
bisDatum: "",
isLoading: false,
};
const analogInputsChartSlice = createSlice({
name: "analogInputsChart",
initialState,
reducers: {
setZeitraum: (state, action: PayloadAction<Zeitraum>) => {
state.zeitraum = action.payload;
},
setVonDatum: (state, action: PayloadAction<string>) => {
state.vonDatum = action.payload;
},
setBisDatum: (state, action: PayloadAction<string>) => {
state.bisDatum = action.payload;
},
setIsLoading: (state, action: PayloadAction<boolean>) => {
state.isLoading = action.payload;
},
},
});
export const { setZeitraum, setVonDatum, setBisDatum, setIsLoading } =
analogInputsChartSlice.actions;
export default analogInputsChartSlice.reducer;

View File

@@ -0,0 +1,20 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { RootState } from "@/redux/store";
import { Zeitraum } from "@/redux/slices/analogInputsChartSlice";
export const getAnalogInputsChartDataThunk = createAsyncThunk(
"analogInputsChart/fetchChartData",
async (_, { getState }) => {
const state = getState() as RootState;
const { zeitraum, vonDatum, bisDatum } = state.analogInputsChart;
const selectedInput = state.selectedAnalogInput;
if (!selectedInput) return [];
const res = await fetch(
`/api/cpl/getAnalogInputsHistory?inputId=${selectedInput.id}&zeitraum=${zeitraum}&von=${vonDatum}&bis=${bisDatum}`
);
const data = await res.json();
return data;
}
);