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_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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
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
|
## [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
4
package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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}.`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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