diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b52b6d..70e3040 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,17 @@ Alle Änderungen und Versionen des CPLv4.0 Frontends chronologisch dokumentiert.
---
+## [1.6.339] – 2025-05-02
+
+### Hinzugefügt
+
+- Modal für analoge Eingänge: Einstellungen für Offset, Faktor, Bezeichnung und Loggerintervall jetzt verfügbar
+- Änderungen werden in der Entwicklungsumgebung über die zentrale Mock-API `/api/cpl/updateAnalogInputsSettingsAPIHandler` gespeichert
+- Unterstützt Speichern im Format `var xyz = [ ... ];` mit Kommentaren vor und nach dem Block
+- Speichern löst automatischen Reload aus zur Anzeige der neuen Werte
+
+---
+
## [1.6.338] – 2025-05-02
### Verbesserungen
diff --git a/README.md b/README.md
index 7ca9aeb..d54aa86 100644
--- a/README.md
+++ b/README.md
@@ -117,6 +117,10 @@ Beispielaufruf im DEV-Modus (über UI gesteuert, nicht manuell notwendig):
- Neue Chart.js Visualisierung:
- Beim Klick auf einen Eingang in der Tabelle wird nur dessen Verlaufskurve angezeigt
- Der Verlauf bezieht sich auf die letzten 24 Stunden, dargestellt mit deutscher Zeitachse
+- **Bearbeitbare Felder im Modal:** Name, Offset, Faktor, Loggerintervall
+- Änderungen werden in der Entwicklungsumgebung direkt in `/apiMockData/SERVICE/analogeEingaengeMockData.js` gespeichert
+- Speicherung erfolgt einzeilig im Format `var xyz = [1, 2, 3];`, Kommentare im File bleiben erhalten
+- Nach dem Speichern wird die Seite automatisch neu geladen
- Entwicklung mit Mock-Daten:
- In der Entwicklungsumgebung werden Mock-Daten über den API-Handler `/api/cpl/fetchAnalogInputsHistory` geladen
diff --git a/apiMockData/SERVICE/analogeEingaengeMockData.js b/apiMockData/SERVICE/analogeEingaengeMockData.js
index c9bffdf..f785a12 100644
--- a/apiMockData/SERVICE/analogeEingaengeMockData.js
+++ b/apiMockData/SERVICE/analogeEingaengeMockData.js
@@ -1,23 +1,12 @@
// /apiMockData/SERVICE/analogeEingaengeMockData.js
-var win_analogInputsValues = [
- 4.771072, 5.665244, 0.005467, -0.007468, 0.000002, 0.000001, 0.000001,
- 0.000007,
-];
-var win_analogInputsNames = [
- "AE 1",
- "AE 2",
- "AE 3",
- "AE 4",
- "AE 5",
- "AE 6",
- "AE 7",
- "AE 8",
-];
+
+var win_analogInputsValues = [4.771072, 5.665244, 0.005467, -0.007468, 0.000002, 0.000001, 0.000001, 0.000007];
+var win_analogInputsNames = ["AE5", "AE 2", "AE 3", "AE 4", "AE 5", "AE 6", "AE 7", "AE 8"];
+var win_analogInputsOffset = [5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
+var win_analogInputsFactor = [5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
+var win_analogInputsloggerIntervall = [5, 10, 10, 10, 10, 10, 10, 10];
var win_analogInputsUnits = ["V", "V", "V", "V", "mA", "mA", "mA", "mA"];
-var win_analogInputsFactor = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
-var win_analogInputsOffset = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
var win_analogInputsWeighting = [0, 0, 0, 0, 0, 0, 0, 0];
-var win_analogInputsloggerIntervall = [10, 10, 10, 10, 10, 10, 10, 10];
/*
ID (z. B. 1, 2, ... 8) → Identifikation des Eingangs
diff --git a/components/main/analogeEingaenge/AnalogInputsChart.tsx b/components/main/analogeEingaenge/AnalogInputsChart.tsx
index 6855122..2f7bba8 100644
--- a/components/main/analogeEingaenge/AnalogInputsChart.tsx
+++ b/components/main/analogeEingaenge/AnalogInputsChart.tsx
@@ -1,4 +1,4 @@
-"use client";
+"use client"; // /components/main/analogeEingaenge/AnalogInputsChart.tsx
import React, { useEffect } from "react";
import { Line } from "react-chartjs-2";
import {
diff --git a/components/main/analogeEingaenge/AnalogInputsSettingsModal.tsx b/components/main/analogeEingaenge/AnalogInputsSettingsModal.tsx
new file mode 100644
index 0000000..167ddb6
--- /dev/null
+++ b/components/main/analogeEingaenge/AnalogInputsSettingsModal.tsx
@@ -0,0 +1,133 @@
+"use client"; // /components/main/analogeEingaenge/AnalogInputsSettingsModal.tsx
+import React, { useEffect, useState } from "react";
+
+interface Props {
+ selectedInput: any;
+ isOpen: boolean;
+ onClose: () => void;
+}
+
+export default function AnalogInputsSettingsModal({
+ selectedInput,
+ isOpen,
+ onClose,
+}: Props) {
+ const [name, setName] = useState("");
+ const [offset, setOffset] = useState(0);
+ const [factor, setFactor] = useState(1);
+ const [loggerInterval, setLoggerInterval] = useState(10);
+ const [isSaving, setIsSaving] = useState(false);
+
+ useEffect(() => {
+ if (selectedInput && isOpen) {
+ setName(selectedInput.name || "");
+ setOffset(selectedInput.offset || 0);
+ setFactor(selectedInput.factor || 1);
+ setLoggerInterval(selectedInput.loggerInterval || 10);
+ }
+ }, [selectedInput, isOpen]);
+
+ if (!isOpen || !selectedInput) return null;
+
+ const handleSave = async () => {
+ setIsSaving(true);
+ try {
+ await fetch("/api/cpl/updateAnalogInputsSettingsAPIHandler", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ updates: [
+ {
+ key: "win_analogInputsNames",
+ index: selectedInput.id - 1,
+ value: name,
+ },
+ {
+ key: "win_analogInputsOffset",
+ index: selectedInput.id - 1,
+ value: parseFloat(offset.toString()),
+ },
+ {
+ key: "win_analogInputsFactor",
+ index: selectedInput.id - 1,
+ value: parseFloat(factor.toString()),
+ },
+ {
+ key: "win_analogInputsloggerIntervall",
+ index: selectedInput.id - 1,
+ value: parseInt(loggerInterval.toString()),
+ },
+ ],
+ }),
+ });
+ alert("Einstellungen gespeichert.");
+ onClose();
+ location.reload();
+ } catch (err) {
+ alert("Fehler beim Speichern der Einstellungen.");
+ console.error(err);
+ } finally {
+ setIsSaving(false);
+ }
+ };
+
+ return (
+
+
+
+ Eingang {selectedInput.id} – Einstellungen
+
+
+
+
setName(e.target.value)}
+ />
+
+
+
setOffset(parseFloat(e.target.value))}
+ />
+
+
+
setFactor(parseFloat(e.target.value))}
+ />
+
+
+
setLoggerInterval(parseInt(e.target.value))}
+ />
+
+
+
+
+
+
+
+ );
+}
diff --git a/components/main/analogeEingaenge/AnalogeEingaengeTable.tsx b/components/main/analogeEingaenge/AnalogeEingaengeTable.tsx
index 1075966..45ed2e8 100644
--- a/components/main/analogeEingaenge/AnalogeEingaengeTable.tsx
+++ b/components/main/analogeEingaenge/AnalogeEingaengeTable.tsx
@@ -1,13 +1,19 @@
-"use client";
+"use client"; // /components/main/analogeEingaenge/AnalogeEingaengeTable.tsx
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { RootState, AppDispatch } from "../../../redux/store";
import { fetchAnalogeEingaengeThunk } from "../../../redux/thunks/fetchAnalogeEingaengeThunk";
+import { Icon } from "@iconify/react";
+import settingsIcon from "@iconify/icons-mdi/settings";
export default function AnalogeEingaengeTable({
setSelectedId,
+ setSelectedInput,
+ setIsSettingsModalOpen,
}: {
setSelectedId: (id: number) => void;
+ setSelectedInput: (input: any) => void;
+ setIsSettingsModalOpen: (open: boolean) => void;
}) {
const dispatch = useDispatch();
@@ -29,20 +35,44 @@ export default function AnalogeEingaengeTable({
Eingang |
Messwert |
Bezeichnung |
+ Aktion |
{Object.values(analogeEingaenge)
.filter((e) => e?.id !== null && e?.id !== undefined)
.map((e, index) => (
- setSelectedId(e.id!)}
- >
- | {e.id ?? "-"} |
- {e.value ?? "-"} |
- {e.name || "----"} |
+
+ | setSelectedId(e.id!)}
+ >
+ {e.id ?? "-"}
+ |
+ setSelectedId(e.id!)}
+ >
+ {e.value ?? "-"}
+ |
+ setSelectedId(e.id!)}
+ >
+ {e.name || "----"}
+ |
+
+
+ |
))}
diff --git a/config/webVersion.ts b/config/webVersion.ts
index 759b428..743300d 100644
--- a/config/webVersion.ts
+++ b/config/webVersion.ts
@@ -6,5 +6,5 @@
2: Patch oder Hotfix (Bugfixes oder kleine Änderungen).
*/
-const webVersion = "1.6.338";
+const webVersion = "1.6.339";
export default webVersion;
diff --git a/pages/analogeEingaenge.tsx b/pages/analogeEingaenge.tsx
index 34aaf06..c6125b2 100644
--- a/pages/analogeEingaenge.tsx
+++ b/pages/analogeEingaenge.tsx
@@ -1,13 +1,15 @@
"use client"; ///pages/analogeEingaenge.tsx
-
import React, { useState, useEffect } from "react";
import AnalogeEingaengeTabelle from "../components/main/analogeEingaenge/AnalogeEingaengeTable";
import AnalogInputsChart from "../components/main/analogeEingaenge/AnalogInputsChart";
+import AnalogInputsSettingsModal from "../components/main/analogeEingaenge/AnalogInputsSettingsModal";
import { fetchAnalogeEingaengeThunk } from "../redux/thunks/fetchAnalogeEingaengeThunk";
import { useAppDispatch } from "../redux/store";
function AnalogeEingaenge() {
const [selectedId, setSelectedId] = useState(null);
+ const [selectedInput, setSelectedInput] = useState(null);
+ const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
const dispatch = useAppDispatch();
useEffect(() => {
@@ -28,7 +30,11 @@ function AnalogeEingaenge() {
Analoge Eingänge
-
+
@@ -39,6 +45,12 @@ function AnalogeEingaenge() {
+
+ setIsSettingsModalOpen(false)}
+ />
);
}
diff --git a/pages/api/cpl/updateAnalogInputsSettingsAPIHandler.ts b/pages/api/cpl/updateAnalogInputsSettingsAPIHandler.ts
new file mode 100644
index 0000000..20c64ec
--- /dev/null
+++ b/pages/api/cpl/updateAnalogInputsSettingsAPIHandler.ts
@@ -0,0 +1,88 @@
+// /pages/api/cpl/updateAnalogInputsSettingsAPIHandler.ts
+import { NextApiRequest, NextApiResponse } from "next";
+import path from "path";
+import fs from "fs/promises";
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ if (req.method !== "POST") {
+ return res.status(405).json({ error: "Method not allowed" });
+ }
+
+ const filePath = path.join(
+ process.cwd(),
+ "apiMockData",
+ "SERVICE",
+ "analogeEingaengeMockData.js"
+ );
+
+ try {
+ const { updates } = req.body;
+
+ if (!Array.isArray(updates)) {
+ return res.status(400).json({ error: "Ungültige Datenstruktur" });
+ }
+
+ const raw = await fs.readFile(filePath, "utf-8");
+
+ // Teile den Inhalt in:
+ // 1. Vor dem ersten var
+ // 2. Die var-Zuweisungen (Arrays)
+ // 3. Nach dem letzten var (z. B. Block-Kommentare)
+
+ const varRegex = /var\s+(\w+)\s*=\s*\[([\s\S]*?)\];/g;
+ let match;
+
+ const updateMap: Record = {};
+ const variableLines: string[] = [];
+ let matchStartIndexes: number[] = [];
+
+ while ((match = varRegex.exec(raw)) !== null) {
+ const [fullMatch, key, valuesBlock] = match;
+ const values = valuesBlock
+ .split(",")
+ .map((v) => v.trim())
+ .filter((v) => v.length > 0);
+
+ updateMap[key] = values;
+ variableLines.push(fullMatch);
+ matchStartIndexes.push(match.index);
+ }
+
+ // Kommentare vor und nach den Variablen extrahieren
+ const firstVarIndex = matchStartIndexes[0] ?? 0;
+ const lastVarMatch = variableLines[variableLines.length - 1] ?? "";
+ const lastVarIndex = raw.lastIndexOf(lastVarMatch);
+ const commentsBefore = raw.slice(0, firstVarIndex).trim();
+ const commentsAfter = raw.slice(lastVarIndex + lastVarMatch.length).trim();
+
+ // Updates anwenden
+ for (const { key, index, value } of updates) {
+ if (!updateMap[key]) {
+ console.warn(`⚠️ Schlüssel '${key}' fehlt – wird übersprungen`);
+ continue;
+ }
+ updateMap[key][index] =
+ typeof value === "string" ? `"${value}"` : value.toString();
+ }
+
+ // Arrays immer einzeilig schreiben
+ const variablesBlock = Object.entries(updateMap)
+ .map(([key, values]) => `var ${key} = [${values.join(", ")}];`)
+ .join("\n");
+
+ const finalContent =
+ [commentsBefore, variablesBlock, commentsAfter]
+ .filter(Boolean)
+ .join("\n\n") + "\n";
+
+ await fs.writeFile(filePath, finalContent, "utf-8");
+
+ res.status(200).json({ message: "Mockdaten gespeichert." });
+ } catch (err) {
+ console.error("❌ Fehler beim Schreiben:", err);
+ res.status(500).json({ error: "Interner Serverfehler" });
+ }
+}