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:
@@ -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.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)
|
||||
|
||||
|
||||
@@ -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.589
|
||||
NEXT_PUBLIC_APP_VERSION=1.6.593
|
||||
NEXT_PUBLIC_CPL_MODE=production
|
||||
32
CHANGELOG.md
32
CHANGELOG.md
@@ -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
|
||||
|
||||
- feat: Zeitspanne-Funktion mit Von/Bis und Button-Trigger im DetailModal eingebaut
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.589",
|
||||
"version": "1.6.593",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.589",
|
||||
"version": "1.6.593",
|
||||
"dependencies": {
|
||||
"@fontsource/roboto": "^5.1.0",
|
||||
"@headlessui/react": "^2.2.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.589",
|
||||
"version": "1.6.593",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -1,43 +1,77 @@
|
||||
// /pages/api/cpl/getAnalogInputsHistory.ts
|
||||
|
||||
import path from "path";
|
||||
import fs from "fs/promises";
|
||||
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(
|
||||
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 {
|
||||
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 filePath = path.join(
|
||||
process.cwd(),
|
||||
"mocks",
|
||||
"device-cgi-simulator",
|
||||
"chartsData",
|
||||
"analogInputs",
|
||||
`${i}`,
|
||||
`DIA0.json`
|
||||
);
|
||||
console.log(`Lade Mock-Daten für analogInput${i} von: ${filePath}`);
|
||||
const fileContent = await fs.readFile(filePath, "utf-8");
|
||||
const daten = JSON.parse(fileContent);
|
||||
|
||||
try {
|
||||
const fileContent = await fs.readFile(filePath, "utf-8");
|
||||
result[99 + i] = JSON.parse(fileContent); // z. B. 100 für AE1, 101 für AE2
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Mock-Datei für analogInput${i} nicht gefunden oder fehlerhaft.`,
|
||||
error
|
||||
);
|
||||
result[99 + i] = [];
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json(result);
|
||||
res.status(200).json({
|
||||
eingang: eingangId,
|
||||
zeitraum: zeitraumKey,
|
||||
beschreibung:
|
||||
zeitraumKey === "DIA0"
|
||||
? "Alle Messwerte"
|
||||
: zeitraumKey === "DIA1"
|
||||
? "Stündlich aggregierte Messwerte"
|
||||
: "Täglich aggregierte Messwerte",
|
||||
daten,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Laden der analogen Eingänge (Mock):", error);
|
||||
res.status(500).json({ error: "Fehler beim Laden der Mock-Daten." });
|
||||
console.error(
|
||||
`❌ 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}.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
40
redux/slices/analogInputsChartSlice.ts
Normal file
40
redux/slices/analogInputsChartSlice.ts
Normal 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;
|
||||
20
redux/thunks/getAnalogInputsChartDataThunk.ts
Normal file
20
redux/thunks/getAnalogInputsChartDataThunk.ts
Normal 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;
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user