From 3e6c973f3bf5d23bd285e9122239ade46dfdb5ee Mon Sep 17 00:00:00 2001 From: ISA Date: Wed, 18 Jun 2025 14:06:23 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Unterst=C3=BCtzung=20f=C3=BCr=20JSON-?= =?UTF-8?q?=20und=20Production-Modus=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - API-Handler `updateDigitalOutputsHandler` überarbeitet: - JSON-Dateien werden jetzt korrekt im gültigen Format gespeichert (`{ key: value }`) - Schreibzugriff im production-Modus blockiert - JS-Mock-Struktur vorbereitet (noch nicht aktiv getestet) - Verzeichnisstruktur vereinheitlicht: - JSON-Mocks unter `/mocks/api/SERVICE/` - CGI-Platzhalter unter `/public/CPL/` - JSMock-Ordner für CPL-Simulation vorbereitet (`/mocks/js-simulator/`) - README.md um Betriebsmodi erweitert (`NEXT_PUBLIC_CPL_MODE` mit `json`, `jsmock`, `production`) - `.env`-Dateien angepasst zur besseren Modussteuerung --- .env.development | 2 +- .env.production | 2 +- .../SERVICE/digitaleAusgaengeMockData.js | 2 +- .../modals/DigitalOutputsModal.tsx | 2 +- .../SERVICE/digitaleAusgaengeMockData.json | 14 --- .../api/SERVICE/digitaleAusgaengeMockData.js | 2 + .../SERVICE/digitaleAusgaengeMockData.json | 16 +++- package-lock.json | 4 +- package.json | 2 +- pages/api/cpl/digitalOutputsAPIHandler.ts | 9 +- pages/api/cpl/updateDigitalOutputsHandler.ts | 93 ++++++++++++------- .../CPL/SERVICE/{da.js => digitalOutputs.js} | 0 services/fetchDigitalOutputsService.ts | 59 ++++++++---- 13 files changed, 129 insertions(+), 78 deletions(-) delete mode 100644 mock-api-json/SERVICE/digitaleAusgaengeMockData.json create mode 100644 mocks/api/SERVICE/digitaleAusgaengeMockData.js rename public/CPL/SERVICE/{da.js => digitalOutputs.js} (100%) diff --git a/.env.development b/.env.development index 5052694..8bea584 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.415 +NEXT_PUBLIC_APP_VERSION=1.6.416 NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsmock (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter) diff --git a/.env.production b/.env.production index b9cd8a7..cc7aebd 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.415 +NEXT_PUBLIC_APP_VERSION=1.6.416 NEXT_PUBLIC_CPL_MODE=production \ No newline at end of file diff --git a/apiMockData/SERVICE/digitaleAusgaengeMockData.js b/apiMockData/SERVICE/digitaleAusgaengeMockData.js index ddb9b9b..5b00803 100644 --- a/apiMockData/SERVICE/digitaleAusgaengeMockData.js +++ b/apiMockData/SERVICE/digitaleAusgaengeMockData.js @@ -1,2 +1,2 @@ -win_da_state = [1, 0, 1, 0]; +win_da_state = [0, 0, 1, 0]; win_da_bezeichnung = ["Ausgang1", "Ausgang2", "Ausgang3", "Ausgang4"]; diff --git a/components/main/einausgaenge/modals/DigitalOutputsModal.tsx b/components/main/einausgaenge/modals/DigitalOutputsModal.tsx index 126fcea..02301cc 100644 --- a/components/main/einausgaenge/modals/DigitalOutputsModal.tsx +++ b/components/main/einausgaenge/modals/DigitalOutputsModal.tsx @@ -66,7 +66,7 @@ export default function DigitalOutputsModal({ // 💡 Modal wird nicht automatisch geschlossen — da Seite neu lädt. } else { // 🧪 Lokaler Entwicklungsmodus - const res = await fetch("/api/cpl/updateDigitalOutputs", { + const res = await fetch("/api/cpl/updateDigitalOutputsHandler", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ outputs: updatedOutputs }), diff --git a/mock-api-json/SERVICE/digitaleAusgaengeMockData.json b/mock-api-json/SERVICE/digitaleAusgaengeMockData.json deleted file mode 100644 index 5a453ce..0000000 --- a/mock-api-json/SERVICE/digitaleAusgaengeMockData.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "win_da_state": [ - 0, - 0, - 1, - 0 - ], - "win_da_bezeichnung": [ - "Ausgang1", - "Ausgang2", - "Ausgang3", - "Ausgang4" - ] -} \ No newline at end of file diff --git a/mocks/api/SERVICE/digitaleAusgaengeMockData.js b/mocks/api/SERVICE/digitaleAusgaengeMockData.js new file mode 100644 index 0000000..93b9d31 --- /dev/null +++ b/mocks/api/SERVICE/digitaleAusgaengeMockData.js @@ -0,0 +1,2 @@ +win_da_state = [1, 1, 1, 0]; +win_da_bezeichnung = ["Ausgang1", "Ausgang2", "Ausgang3", "Ausgang4"]; diff --git a/mocks/api/SERVICE/digitaleAusgaengeMockData.json b/mocks/api/SERVICE/digitaleAusgaengeMockData.json index b30e361..57f97d6 100644 --- a/mocks/api/SERVICE/digitaleAusgaengeMockData.json +++ b/mocks/api/SERVICE/digitaleAusgaengeMockData.json @@ -1,4 +1,14 @@ { - "win_da_state": [1, 0, 1, 0], - "win_da_bezeichnung": ["Ausgang1", "Ausgang2", "Ausgang3", "Ausgang4"] -} + "win_da_state": [ + 1, + 0, + 1, + 0 + ], + "win_da_bezeichnung": [ + "Ausgang1", + "Ausgang2", + "Ausgang3", + "Ausgang4" + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 96a4573..7332aa2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cpl-v4", - "version": "1.6.415", + "version": "1.6.416", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cpl-v4", - "version": "1.6.415", + "version": "1.6.416", "dependencies": { "@fontsource/roboto": "^5.1.0", "@iconify-icons/ri": "^1.2.10", diff --git a/package.json b/package.json index f7be987..07b4478 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cpl-v4", - "version": "1.6.415", + "version": "1.6.416", "private": true, "scripts": { "dev": "next dev", diff --git a/pages/api/cpl/digitalOutputsAPIHandler.ts b/pages/api/cpl/digitalOutputsAPIHandler.ts index adcf092..0a45bed 100644 --- a/pages/api/cpl/digitalOutputsAPIHandler.ts +++ b/pages/api/cpl/digitalOutputsAPIHandler.ts @@ -1,4 +1,4 @@ -// /pages/api/cpl/digitalOutputsAPIHandler.ts +// /pages/api/cpl/analogeEingaengeAPIHandler.ts import { NextApiRequest, NextApiResponse } from "next"; import path from "path"; @@ -10,13 +10,16 @@ export default async function handler( ) { const filePath = path.join( process.cwd(), - "apiMockData", + "mocks", + "api", "SERVICE", - "digitaleAusgaengeMockData.js" + "digitaleAusgaengeMockData.json" ); try { const data = await fs.readFile(filePath, "utf-8"); + + res.setHeader("Content-Type", "text/javascript"); res.status(200).send(data); } catch (error) { res.status(404).json({ error: "File not found" }); diff --git a/pages/api/cpl/updateDigitalOutputsHandler.ts b/pages/api/cpl/updateDigitalOutputsHandler.ts index 8312d8d..46e6aa8 100644 --- a/pages/api/cpl/updateDigitalOutputsHandler.ts +++ b/pages/api/cpl/updateDigitalOutputsHandler.ts @@ -1,46 +1,75 @@ -// /pages/api/cpl/updateDigitalOutputsHandler.ts +// /pages/api/cpl/updateDigitalOutputs.ts -import { NextApiRequest, NextApiResponse } from "next"; +import fs from "fs"; import path from "path"; -import fs from "fs/promises"; -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { +export default function handler(req, res) { if (req.method !== "POST") { - return res.status(405).json({ error: "Method not allowed" }); + return res.status(405).json({ message: "Nur POST erlaubt" }); } - const filePath = path.join( - process.cwd(), - "apiMockdata", - "SERVICE", - "digitaleAusgaengeMockData.js" - ); + const { outputs } = req.body; - try { - const { outputs } = req.body; + if (!Array.isArray(outputs)) { + return res.status(400).json({ error: "Ungültige Datenstruktur" }); + } - if (!Array.isArray(outputs) || outputs.length !== 4) { - return res - .status(400) - .json({ error: "Ungültiges Datenformat (4 Einträge erwartet)" }); + const mode = process.env.NEXT_PUBLIC_CPL_MODE; + + const jsonData = { + win_da_state: outputs.map((o) => (o.status ? 1 : 0)), + win_da_bezeichnung: outputs.map((o) => o.label), + }; + + let filePath = ""; + + if (mode === "json") { + filePath = path.join( + process.cwd(), + "mocks", + "api", + "SERVICE", + "digitaleAusgaengeMockData.json" + ); + + try { + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + fs.writeFileSync(filePath, JSON.stringify(jsonData, null, 2), "utf-8"); + + console.log("✅ Datei (JSON) geschrieben:", filePath); + res.status(200).json({ success: true }); + } catch (err) { + console.error("❌ Fehler beim Schreiben der JSON-Datei:", err); + res.status(500).json({ error: "Fehler beim Schreiben der Datei" }); } + } else if (mode === "jsmock") { + filePath = path.join( + process.cwd(), + "mocks", + "js-simulator", + "SERVICE", + "digitaleAusgaengeMockData.js" + ); - const stateArray = outputs.map((o) => (o.status ? 1 : 0)).join(", "); - const labelArray = outputs.map((o) => `"${o.label}"`).join(", "); + const jsContent = + `win_da_state = [${jsonData.win_da_state.join(", ")}];\n` + + `win_da_bezeichnung = [${jsonData.win_da_bezeichnung + .map((s) => `"${s}"`) + .join(", ")}];\n`; - const fileContent = `win_da_state = [${stateArray}];\nwin_da_bezeichnung = [${labelArray}];\n`; + try { + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + fs.writeFileSync(filePath, jsContent, "utf-8"); - await fs.writeFile(filePath, fileContent, "utf-8"); - - return res - .status(200) - .json({ message: "Mockdaten erfolgreich gespeichert." }); - } catch (error) { - return res - .status(500) - .json({ error: "Speichern fehlgeschlagen", detail: error }); + console.log("✅ Datei (JS-Mock) geschrieben:", filePath); + res.status(200).json({ success: true }); + } catch (err) { + console.error("❌ Fehler beim Schreiben der JS-Datei:", err); + res.status(500).json({ error: "Fehler beim Schreiben der JS-Datei" }); + } + } else { + res + .status(403) + .json({ error: "In production ist Schreiben nicht erlaubt" }); } } diff --git a/public/CPL/SERVICE/da.js b/public/CPL/SERVICE/digitalOutputs.js similarity index 100% rename from public/CPL/SERVICE/da.js rename to public/CPL/SERVICE/digitalOutputs.js diff --git a/services/fetchDigitalOutputsService.ts b/services/fetchDigitalOutputsService.ts index 63f2edc..ced780d 100644 --- a/services/fetchDigitalOutputsService.ts +++ b/services/fetchDigitalOutputsService.ts @@ -1,19 +1,44 @@ // /services/fetchDigitalOutputsService.ts export const fetchDigitalOutputsService = async () => { - if (typeof window === "undefined") return []; + const mode = process.env.NEXT_PUBLIC_CPL_MODE; - const scriptSrc = - process.env.NEXT_PUBLIC_NODE_ENV === "production" - ? "/CPL?/CPL/SERVICE/da.js" - : "/api/cpl/digitalOutputsAPIHandler"; + if (mode === "json") { + const res = await fetch("/api/cpl/digitalOutputsAPIHandler"); + if (!res.ok) + throw new Error("❌ Fehler beim Laden der digitalen Ausgänge (JSON)"); + + const data = await res.json(); + const state = data.win_da_state; + const labels = data.win_da_bezeichnung; + + if (!Array.isArray(state)) { + console.warn("⚠️ win_da_state fehlt oder ist ungültig:", state); + return []; + } + + return state.slice(0, 4).map((status: number, index: number) => ({ + id: index + 1, + label: + Array.isArray(labels) && labels[index] + ? labels[index] + : `Ausgang ${index + 1}`, + status: status === 1, + })); + } + + // jsmock oder production + const scriptUrl = + mode === "production" + ? "/CPL?/CPL/SERVICE/digitalOutputs.js" + : "/CPLmockData/SERVICE/digitalOutputsMockData.js"; await new Promise((resolve, reject) => { const script = document.createElement("script"); - script.src = scriptSrc; + script.src = scriptUrl; script.async = true; script.onload = () => resolve(); - script.onerror = () => reject("❌ Fehler beim Laden von da.js"); + script.onerror = () => reject("❌ Fehler beim Laden der digitalOutputs.js"); document.body.appendChild(script); }); @@ -26,16 +51,12 @@ export const fetchDigitalOutputsService = async () => { return []; } - const outputs = state - .slice(0, 4) // ✅ Nur die 4 Ausgänge verwenden - .map((status: number, index: number) => ({ - id: index + 1, - label: - Array.isArray(labels) && labels[index] - ? labels[index] - : `Ausgang ${index + 1}`, - status: status === 1, - })); - - return outputs; + return state.slice(0, 4).map((status: number, index: number) => ({ + id: index + 1, + label: + Array.isArray(labels) && labels[index] + ? labels[index] + : `Ausgang ${index + 1}`, + status: status === 1, + })); };