feat: KVZ API JSON Data
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.661
|
NEXT_PUBLIC_APP_VERSION=1.6.662
|
||||||
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.661
|
NEXT_PUBLIC_APP_VERSION=1.6.662
|
||||||
NEXT_PUBLIC_CPL_MODE=production
|
NEXT_PUBLIC_CPL_MODE=production
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
## [1.6.662] – 2025-07-31
|
||||||
|
|
||||||
|
- Feat: KVz Bereich in EinstellungsModal in KÜs Modal
|
||||||
|
|
||||||
|
---
|
||||||
## [1.6.661] – 2025-07-31
|
## [1.6.661] – 2025-07-31
|
||||||
|
|
||||||
- feat: TDR starten Button in KÜ Chart
|
- feat: TDR starten Button in KÜ Chart
|
||||||
|
|||||||
@@ -1,25 +1,49 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "../../../redux/store";
|
||||||
|
|
||||||
// components/main/fall-detection-sensors/FallSensors.tsx
|
// components/main/fall-detection-sensors/FallSensors.tsx
|
||||||
const FallSensors = () => {
|
interface FallSensorsProps {
|
||||||
const sensors = [
|
slotIndex: number;
|
||||||
{ id: "KVZ1", status: "inactive" },
|
}
|
||||||
{ id: "KVZ2", status: "active" },
|
|
||||||
{ id: "KVZ3", status: "active" },
|
const FallSensors: React.FC<FallSensorsProps> = ({ slotIndex }) => {
|
||||||
{ id: "KVZ4", status: "active" },
|
const { kvzStatus } = useSelector((state: RootState) => state.kueDataSlice);
|
||||||
];
|
|
||||||
|
// Nur 4 LEDs für den spezifischen Slot anzeigen
|
||||||
|
const leds = Array.from({ length: 4 }, (_, ledIndex) => {
|
||||||
|
const arrayIndex = slotIndex * 4 + ledIndex;
|
||||||
|
const ledValue = kvzStatus?.[arrayIndex];
|
||||||
|
|
||||||
|
// LED Status: 1 = grün, 0 = rot, 2 = grau, undefined = grau
|
||||||
|
if (ledValue === 1) return "green";
|
||||||
|
if (ledValue === 0) return "red";
|
||||||
|
return "gray"; // für 2 oder undefined
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-1 p-1 border rounded bg-gray-200">
|
<div className="flex justify-center items-center gap-2 p-3 border rounded-lg bg-gray-100 shadow-sm">
|
||||||
{sensors.map((sensor) => (
|
{leds.map((ledStatus, ledIndex) => {
|
||||||
<div key={sensor.id} className="flex flex-col items-center gap-1">
|
// LED Farben: grün (1), rot (0), grau (2)
|
||||||
<span className="text-[0.5rem]">{sensor.id}</span>
|
let bgColor = "bg-gray-400"; // Standard grau
|
||||||
|
let statusText = "Unbekannt";
|
||||||
|
|
||||||
|
if (ledStatus === "green") {
|
||||||
|
bgColor = "bg-green-500";
|
||||||
|
statusText = "Ein";
|
||||||
|
} else if (ledStatus === "red") {
|
||||||
|
bgColor = "bg-red-500";
|
||||||
|
statusText = "Aus";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
<div
|
<div
|
||||||
className={`w-4 h-4 flex items-center justify-center rounded-full border ${
|
key={ledIndex}
|
||||||
sensor.status === "active" ? "bg-green-400" : "bg-red-400"
|
className={`w-3 h-3 rounded-full border-2 border-gray-300 shadow-sm ${bgColor} transition-all duration-200 hover:scale-110 flex-shrink-0`}
|
||||||
}`}
|
title={`Slot ${slotIndex} LED${ledIndex + 1}: ${statusText}`}
|
||||||
></div>
|
/>
|
||||||
</div>
|
);
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import useKueVersion from "./hooks/useKueVersion";
|
|||||||
import useIsoDisplay from "./hooks/useIsoDisplay";
|
import useIsoDisplay from "./hooks/useIsoDisplay";
|
||||||
import useLoopDisplay from "./hooks/useLoopDisplay";
|
import useLoopDisplay from "./hooks/useLoopDisplay";
|
||||||
import useModulName from "./hooks/useModulName";
|
import useModulName from "./hooks/useModulName";
|
||||||
|
import { useAdminAuth } from "../../settingsPageComponents/hooks/useAdminAuth";
|
||||||
|
|
||||||
//--------handlers----------------
|
//--------handlers----------------
|
||||||
// Keep needed imports
|
// Keep needed imports
|
||||||
@@ -48,6 +49,9 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { kueName } = useSelector((state: RootState) => state.kueDataSlice);
|
const { kueName } = useSelector((state: RootState) => state.kueDataSlice);
|
||||||
|
|
||||||
|
// Admin authentication hook for security - using showModal as true for continuous auth check
|
||||||
|
const { isAdminLoggedIn } = useAdminAuth(true);
|
||||||
|
|
||||||
const [activeButton, setActiveButton] = useState<"Schleife" | "TDR" | "ISO">(
|
const [activeButton, setActiveButton] = useState<"Schleife" | "TDR" | "ISO">(
|
||||||
"Schleife"
|
"Schleife"
|
||||||
);
|
);
|
||||||
@@ -79,7 +83,9 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
|
|||||||
kueOverflow: kueOverflowRaw,
|
kueOverflow: kueOverflowRaw,
|
||||||
kuePSTmMinus96V, // <- richtig, weil so im State vorhanden
|
kuePSTmMinus96V, // <- richtig, weil so im State vorhanden
|
||||||
tdrActive, // <- TDR aktiv Status hinzugefügt
|
tdrActive, // <- TDR aktiv Status hinzugefügt
|
||||||
win_fallSensorsActive, // <- KVz aktiv Status hinzugefügt
|
kvzPresence, // <- KVz Presence Array hinzugefügt
|
||||||
|
kvzActive, // <- KVz Active Array hinzugefügt
|
||||||
|
kvzStatus, // <- KVz LED Status Array hinzugefügt
|
||||||
} = useSelector((state: RootState) => state.kueDataSlice);
|
} = useSelector((state: RootState) => state.kueDataSlice);
|
||||||
|
|
||||||
//---------------------------------------------
|
//---------------------------------------------
|
||||||
@@ -228,8 +234,17 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
|
|||||||
// TDR aktiv Status für diesen Slot prüfen
|
// TDR aktiv Status für diesen Slot prüfen
|
||||||
const isTdrActiveForSlot = tdrActive?.[slotIndex] === 1;
|
const isTdrActiveForSlot = tdrActive?.[slotIndex] === 1;
|
||||||
|
|
||||||
// KVz aktiv Status für diesen Slot prüfen
|
// KVz aktiv Status für diesen Slot prüfen - nur wenn Admin authentifiziert ist, KVz vorhanden ist UND aktiviert ist
|
||||||
const isKvzActiveForSlot = win_fallSensorsActive?.[slotIndex] === 1;
|
const isKvzActiveForSlot =
|
||||||
|
kvzPresence?.[slotIndex] === 1 &&
|
||||||
|
kvzActive?.[slotIndex] === 1 &&
|
||||||
|
isAdminLoggedIn;
|
||||||
|
|
||||||
|
// KVz LED Status abrufen (4 LEDs pro Slot)
|
||||||
|
const getKvzLedStatus = (ledIndex: number) => {
|
||||||
|
const arrayIndex = slotIndex * 4 + ledIndex;
|
||||||
|
return kvzStatus?.[arrayIndex] === 1;
|
||||||
|
};
|
||||||
|
|
||||||
// Removed useChartData(loopMeasurementCurveChartData) as the state was unused
|
// Removed useChartData(loopMeasurementCurveChartData) as the state was unused
|
||||||
|
|
||||||
@@ -453,7 +468,7 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
|
|||||||
{/* KVz Panel - Anzeige ganz unten, nur wenn KVz aktiv ist */}
|
{/* KVz Panel - Anzeige ganz unten, nur wenn KVz aktiv ist */}
|
||||||
{showKvzPanel && isKvzActiveForSlot && (
|
{showKvzPanel && isKvzActiveForSlot && (
|
||||||
<div className=" bg-gray-400 mt-4 border p-1">
|
<div className=" bg-gray-400 mt-4 border p-1">
|
||||||
<FallSensors />
|
<FallSensors slotIndex={slotIndex} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from "../../../../../redux/store";
|
import { RootState, useAppDispatch } from "../../../../../redux/store";
|
||||||
import { setKueData } from "../../../../../redux/slices/kueDataSlice";
|
import { updateKvzData } from "../../../../../redux/thunks/kvzThunks";
|
||||||
import { useAdminAuth } from "../../../settingsPageComponents/hooks/useAdminAuth";
|
import { useAdminAuth } from "../../../settingsPageComponents/hooks/useAdminAuth";
|
||||||
|
|
||||||
type KvzData = {
|
type KvzData = {
|
||||||
@@ -11,12 +11,6 @@ type KvzData = {
|
|||||||
kvzSettings: string;
|
kvzSettings: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
__kvzCache?: Record<string, { data: KvzData; kvzActive: boolean }>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
slot: number;
|
slot: number;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
@@ -24,153 +18,93 @@ interface Props {
|
|||||||
|
|
||||||
export default function KvzModalView({ slot, onClose }: Props) {
|
export default function KvzModalView({ slot, onClose }: Props) {
|
||||||
const { isAdminLoggedIn } = useAdminAuth(true);
|
const { isAdminLoggedIn } = useAdminAuth(true);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const kvzSlice = useSelector((state: RootState) => state.kueDataSlice);
|
const kvzSlice = useSelector((state: RootState) => state.kueDataSlice);
|
||||||
|
|
||||||
const cacheKey = `kvz_slot_${slot}`;
|
// KVZ System: 32 Slots mit je 4 LEDs
|
||||||
if (typeof window !== "undefined") {
|
const isKvzPresent = kvzSlice.kvzPresence?.[slot] === 1;
|
||||||
window.__kvzCache = window.__kvzCache || {};
|
const isKvzActive = kvzSlice.kvzActive?.[slot] === 1;
|
||||||
}
|
|
||||||
const cachedKvz =
|
|
||||||
typeof window !== "undefined"
|
|
||||||
? window.__kvzCache?.[cacheKey] ?? null
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const [kvzData] = useState(
|
// LED Status für diesen Slot (4 LEDs pro Slot)
|
||||||
() =>
|
const getKvzLedStatus = (ledIndex: number) => {
|
||||||
cachedKvz?.data || {
|
const arrayIndex = slot * 4 + ledIndex;
|
||||||
kvzSettings: "", // Placeholder für zukünftige Einstellungen
|
return kvzSlice.kvzStatus?.[arrayIndex] === 1;
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const [kvzActive, setKvzActive] = useState(
|
|
||||||
() => cachedKvz?.kvzActive ?? kvzSlice.win_fallSensorsActive?.[slot] === 1
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateCache = (data: typeof kvzData, active = kvzActive) => {
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
(window.__kvzCache ??= {})[cacheKey] = {
|
|
||||||
data,
|
|
||||||
kvzActive: active,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKvzToggle = () => {
|
const [localKvzActive, setLocalKvzActive] = useState(() => isKvzActive);
|
||||||
const newState = !kvzActive;
|
|
||||||
setKvzActive(newState);
|
|
||||||
updateCache(kvzData, newState);
|
|
||||||
|
|
||||||
// Redux State sofort aktualisieren für UI-Update
|
// Synchronisiere localState mit Redux State
|
||||||
const updatedKvzActive = [...(kvzSlice.win_fallSensorsActive || [])];
|
React.useEffect(() => {
|
||||||
updatedKvzActive[slot] = newState ? 1 : 0;
|
setLocalKvzActive(isKvzActive);
|
||||||
dispatch(setKueData({ win_fallSensorsActive: updatedKvzActive }));
|
}, [isKvzActive]);
|
||||||
|
|
||||||
const isDev = window.location.hostname === "localhost";
|
const handleKvzToggle = async () => {
|
||||||
const slotParam = `KVZ${slot}=${newState ? 1 : 0}`;
|
const newState = !localKvzActive;
|
||||||
|
setLocalKvzActive(newState);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// API Update mit neuem Thunk - kvzActive statt kvzPresence
|
||||||
|
await dispatch(
|
||||||
|
updateKvzData([{ key: "kvzActive", slot, value: newState ? 1 : 0 }])
|
||||||
|
);
|
||||||
|
|
||||||
const reloadAfterConfirm = () => {
|
|
||||||
const msg = newState
|
const msg = newState
|
||||||
? "✅ KVz wurde aktiviert."
|
? "✅ KVz wurde aktiviert."
|
||||||
: "⚠️ KVz wurde deaktiviert.";
|
: "⚠️ KVz wurde deaktiviert.";
|
||||||
alert(msg);
|
alert(msg);
|
||||||
location.reload();
|
location.reload();
|
||||||
};
|
} catch (error) {
|
||||||
|
console.error("Fehler beim KVz-Toggle:", error);
|
||||||
if (isDev) {
|
alert("Fehler beim Umschalten der KVz-Funktion.");
|
||||||
const updates = [
|
// State zurücksetzen bei Fehler
|
||||||
{ key: "win_fallSensorsActive", slot, value: newState ? 1 : 0 },
|
setLocalKvzActive(!newState);
|
||||||
];
|
|
||||||
|
|
||||||
fetch("/api/cpl/updateKvzSettingsDataAPIHandler", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ updates }),
|
|
||||||
})
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then(() => {
|
|
||||||
console.log("KVz-Aktiv-Status gespeichert.");
|
|
||||||
reloadAfterConfirm();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Fehler beim Speichern von KVz aktiv:", err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const url = `${window.location.origin}/CPL?/kabelueberwachung.html&${slotParam}`;
|
|
||||||
fetch(url)
|
|
||||||
.then((res) => {
|
|
||||||
if (!res.ok) throw new Error("KVz-Befehl fehlgeschlagen");
|
|
||||||
console.log("KVz aktiviert/deaktiviert:", res.status);
|
|
||||||
reloadAfterConfirm();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Fehler beim KVz-Befehl:", err);
|
|
||||||
alert("Fehler beim Umschalten der KVz-Funktion.");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = () => {
|
|
||||||
const isDev = window.location.hostname === "localhost";
|
|
||||||
|
|
||||||
if (isDev) {
|
|
||||||
const updates = [
|
|
||||||
{ key: "win_fallSensorsActive", slot, value: kvzActive ? 1 : 0 },
|
|
||||||
// Hier können später weitere KVz-Einstellungen hinzugefügt werden
|
|
||||||
];
|
|
||||||
|
|
||||||
fetch("/api/cpl/updateKvzSettingsDataAPIHandler", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ updates }),
|
|
||||||
})
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then(() => {
|
|
||||||
alert("KVz-Einstellungen erfolgreich gespeichert.");
|
|
||||||
if (typeof onClose === "function") onClose();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Fehler beim Speichern:", err);
|
|
||||||
alert("Speichern fehlgeschlagen.");
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Originaler Webservice-Teil für Produktionsumgebung
|
|
||||||
alert("KVz-Einstellungen gespeichert.");
|
|
||||||
if (typeof onClose === "function") onClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCache(kvzData, kvzActive);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4 text-sm">
|
<div className="p-4 text-sm">
|
||||||
{/* KVz-Funktion */}
|
{/* KVz-Funktion - nur anzeigen wenn KVZ vorhanden ist */}
|
||||||
{isAdminLoggedIn && (
|
{isAdminLoggedIn && isKvzPresent && (
|
||||||
<div className="mb-4 mt-4 grid grid-cols-3 items-center gap-2 w-full">
|
<div className="mb-4 mt-4 grid grid-cols-3 items-center gap-2 w-full">
|
||||||
<span className="text-sm font-medium">KVz-Funktion:</span>
|
<span className="text-sm font-medium">KVz-Funktion:</span>
|
||||||
<div className="col-span-2 flex items-center gap-4">
|
<div className="col-span-2 flex items-center gap-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
role="switch"
|
role="switch"
|
||||||
aria-checked={kvzActive}
|
aria-checked={localKvzActive}
|
||||||
onClick={handleKvzToggle}
|
onClick={handleKvzToggle}
|
||||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors duration-200 ${
|
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors duration-200 ${
|
||||||
kvzActive ? "bg-littwin-blue" : "bg-gray-300"
|
localKvzActive ? "bg-littwin-blue" : "bg-gray-300"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform duration-200 ${
|
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform duration-200 ${
|
||||||
kvzActive ? "translate-x-6" : "translate-x-1"
|
localKvzActive ? "translate-x-6" : "translate-x-1"
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<span className="text-sm text-gray-600">
|
<span className="text-sm text-gray-600">
|
||||||
{kvzActive ? "aktiviert" : "deaktiviert"}
|
{localKvzActive ? "aktiviert" : "deaktiviert"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Meldung wenn KVZ nicht vorhanden */}
|
||||||
|
{!isKvzPresent && (
|
||||||
|
<div className="mb-4 mt-4 p-4 bg-yellow-50 border border-yellow-200 rounded">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-yellow-600">⚠️</span>
|
||||||
|
<p className="text-sm text-yellow-800">
|
||||||
|
<strong>Kein KVZ-Gerät vorhanden</strong>
|
||||||
|
<br />
|
||||||
|
Für Slot {slot + 1} ist kein KVZ-Gerät installiert oder
|
||||||
|
konfiguriert.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Zukünftige KVz-Einstellungen können hier hinzugefügt werden */}
|
{/* Zukünftige KVz-Einstellungen können hier hinzugefügt werden */}
|
||||||
{!isAdminLoggedIn && (
|
{!isAdminLoggedIn && (
|
||||||
<div className="mt-6 mb-4">
|
<div className="mt-6 mb-4">
|
||||||
|
|||||||
@@ -29,6 +29,17 @@ export function useAdminAuth(showModal: boolean) {
|
|||||||
function logoutAdmin() {
|
function logoutAdmin() {
|
||||||
sessionStorage.removeItem("token");
|
sessionStorage.removeItem("token");
|
||||||
localStorage.setItem("isAdminLoggedIn", "false");
|
localStorage.setItem("isAdminLoggedIn", "false");
|
||||||
|
|
||||||
|
// KVz localStorage-Werte löschen für alle Slots
|
||||||
|
const keysToRemove = [];
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
const key = localStorage.key(i);
|
||||||
|
if (key && key.startsWith("kvz_slot_")) {
|
||||||
|
keysToRemove.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keysToRemove.forEach((key) => localStorage.removeItem(key));
|
||||||
|
|
||||||
setAdminLoggedIn(false);
|
setAdminLoggedIn(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
142
docs/KVZ/kvz-system-understanding.md
Normal file
142
docs/KVZ/kvz-system-understanding.md
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
# KVZ System - Mein aktuelles Verständnis
|
||||||
|
|
||||||
|
## System Übersicht
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Kabelüberwachung System (32 Slots)"
|
||||||
|
Slot0["Slot 0<br/>Kabelüberwachung"]
|
||||||
|
Slot1["Slot 1<br/>Kabelüberwachung"]
|
||||||
|
Slot2["Slot 2<br/>Kabelüberwachung"]
|
||||||
|
Slot3["Slot 3<br/>Kabelüberwachung"]
|
||||||
|
SlotDots["..."]
|
||||||
|
Slot31["Slot 31<br/>Kabelüberwachung"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "KVZ Geräte (Optional pro Slot)"
|
||||||
|
KVZ0["KVZ Gerät<br/>für Slot 0"]
|
||||||
|
KVZ1["KVZ Gerät<br/>für Slot 1"]
|
||||||
|
KVZ2["KVZ Gerät<br/>für Slot 2"]
|
||||||
|
KVZ3["KVZ Gerät<br/>für Slot 3"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "KVZ LEDs (4 pro KVZ Gerät)"
|
||||||
|
subgraph "KVZ0 LEDs"
|
||||||
|
LED0_0["LED 1"]
|
||||||
|
LED0_1["LED 2"]
|
||||||
|
LED0_2["LED 3"]
|
||||||
|
LED0_3["LED 4"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "KVZ1 LEDs"
|
||||||
|
LED1_0["LED 1"]
|
||||||
|
LED1_1["LED 2"]
|
||||||
|
LED1_2["LED 3"]
|
||||||
|
LED1_3["LED 4"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Slot0 -.-> KVZ0
|
||||||
|
Slot1 -.-> KVZ1
|
||||||
|
Slot2 -.-> KVZ2
|
||||||
|
Slot3 -.-> KVZ3
|
||||||
|
|
||||||
|
KVZ0 --> LED0_0
|
||||||
|
KVZ0 --> LED0_1
|
||||||
|
KVZ0 --> LED0_2
|
||||||
|
KVZ0 --> LED0_3
|
||||||
|
|
||||||
|
KVZ1 --> LED1_0
|
||||||
|
KVZ1 --> LED1_1
|
||||||
|
KVZ1 --> LED1_2
|
||||||
|
KVZ1 --> LED1_3
|
||||||
|
```
|
||||||
|
|
||||||
|
## Redux Data Structure - Mein Verständnis
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
subgraph "Redux Store"
|
||||||
|
subgraph "kvzPresence Array (32 Elemente)"
|
||||||
|
P0["Index 0: 1<br/>(KVZ vorhanden)"]
|
||||||
|
P1["Index 1: 0<br/>(KVZ nicht vorhanden)"]
|
||||||
|
P2["Index 2: 0"]
|
||||||
|
P3["Index 3: 0"]
|
||||||
|
PDots["..."]
|
||||||
|
P31["Index 31: 0"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "kvzStatus Array (128 Elemente)"
|
||||||
|
subgraph "Slot 0 LEDs (Index 0-3)"
|
||||||
|
S0_0["Index 0: 1 (grün)"]
|
||||||
|
S0_1["Index 1: 0 (rot)"]
|
||||||
|
S0_2["Index 2: 1 (grün)"]
|
||||||
|
S0_3["Index 3: 0 (rot)"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Slot 1 LEDs (Index 4-7)"
|
||||||
|
S1_0["Index 4: 0"]
|
||||||
|
S1_1["Index 5: 0"]
|
||||||
|
S1_2["Index 6: 0"]
|
||||||
|
S1_3["Index 7: 0"]
|
||||||
|
end
|
||||||
|
|
||||||
|
StatusDots["...weitere 120 Elemente"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## UI Darstellung - Mein aktuelles Verständnis
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
subgraph "FallSensors UI Component"
|
||||||
|
subgraph "Aktuelle Implementierung (FALSCH?)"
|
||||||
|
UI1["KVZ1: 🟢<br/>(kvzPresence[0] = 1)"]
|
||||||
|
UI2["KVZ2: 🔴<br/>(kvzPresence[1] = 0)"]
|
||||||
|
UI3["KVZ3: 🔴<br/>(kvzPresence[2] = 0)"]
|
||||||
|
UI4["KVZ4: 🔴<br/>(kvzPresence[3] = 0)"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph "Problem"
|
||||||
|
Problem["Alle KVZ zeigen den gleichen Status<br/>basierend auf kvzPresence Array<br/>→ NICHT korrekt!"]
|
||||||
|
end
|
||||||
|
|
||||||
|
UI1 -.-> Problem
|
||||||
|
UI2 -.-> Problem
|
||||||
|
UI3 -.-> Problem
|
||||||
|
UI4 -.-> Problem
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fragen zu meinem Verständnis
|
||||||
|
|
||||||
|
1. **KVZ Geräte Zuordnung**:
|
||||||
|
|
||||||
|
- Ist ein KVZ-Gerät einem Slot zugeordnet oder unabhängig?
|
||||||
|
- Wie viele KVZ-Geräte gibt es insgesamt?
|
||||||
|
|
||||||
|
2. **UI KVZ1-KVZ4**:
|
||||||
|
|
||||||
|
- Repräsentieren KVZ1-KVZ4 in der UI die ersten 4 Slots (0-3)?
|
||||||
|
- Oder sind es 4 separate, unabhängige KVZ-Geräte?
|
||||||
|
|
||||||
|
3. **LED Status Mapping**:
|
||||||
|
|
||||||
|
- Welche LED von welchem KVZ soll in KVZ1, KVZ2, KVZ3, KVZ4 angezeigt werden?
|
||||||
|
- Soll jedes UI-KVZ eine andere LED des gleichen Geräts zeigen?
|
||||||
|
- Oder soll jedes UI-KVZ ein anderes KVZ-Gerät repräsentieren?
|
||||||
|
|
||||||
|
4. **kvzStatus Array**:
|
||||||
|
- Wie soll das 128-Element Array für die UI-Darstellung genutzt werden?
|
||||||
|
- Welche Indizes entsprechen welchen UI-Elementen?
|
||||||
|
|
||||||
|
## Verdacht
|
||||||
|
|
||||||
|
Ich vermute, dass mein aktueller Ansatz falsch ist, weil:
|
||||||
|
|
||||||
|
- KVZ2 sollte nicht eine Kopie von KVZ1 Status sein
|
||||||
|
- Jedes KVZ in der UI sollte einen eigenen, unabhängigen Status haben
|
||||||
|
- Die Zuordnung zwischen Redux Arrays und UI ist unklar
|
||||||
|
|
||||||
|
**Bitte korrigieren Sie mein Verständnis! 🤔**
|
||||||
141
docs/KVZ/mock-data.md
Normal file
141
docs/KVZ/mock-data.md
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
# KVZ Mock Data - Dokumentation
|
||||||
|
|
||||||
|
## Mock-Daten Struktur
|
||||||
|
|
||||||
|
Die KVZ Mock-Daten befinden sich in `mocks/kvzData.json` und haben folgende Struktur:
|
||||||
|
|
||||||
|
### Beispiel-Daten
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kvzPresence": [1, 0, 1, 0, ...], // 32 Elemente
|
||||||
|
"kvzStatus": [1, 0, 1, 0, ...], // 128 Elemente
|
||||||
|
"timestamp": "2025-01-31T12:00:00.000Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### kvzPresence Array (32 Elemente)
|
||||||
|
|
||||||
|
- **Index 0-31**: Repräsentiert Slots 0-31
|
||||||
|
- **Wert 1**: KVZ-Gerät vorhanden
|
||||||
|
- **Wert 0**: KVZ-Gerät nicht vorhanden
|
||||||
|
|
||||||
|
**Aktuelle Mock-Daten:**
|
||||||
|
|
||||||
|
- Slot 0: KVZ vorhanden (1)
|
||||||
|
- Slot 1: KVZ nicht vorhanden (0)
|
||||||
|
- Slot 2: KVZ vorhanden (1)
|
||||||
|
- Slot 3-31: KVZ nicht vorhanden (0)
|
||||||
|
|
||||||
|
### kvzStatus Array (128 Elemente)
|
||||||
|
|
||||||
|
- **Index Berechnung**: `slotIndex * 4 + ledIndex`
|
||||||
|
- **4 LEDs pro Slot**: LED 0, LED 1, LED 2, LED 3
|
||||||
|
- **Wert 1**: LED aktiv (grün)
|
||||||
|
- **Wert 0**: LED inaktiv (rot)
|
||||||
|
|
||||||
|
**Aktuelle Mock-Daten:**
|
||||||
|
|
||||||
|
- Slot 0 LEDs (Index 0-3): [1, 0, 1, 0] → LED1=grün, LED2=rot, LED3=grün, LED4=rot
|
||||||
|
- Slot 1 LEDs (Index 4-7): [0, 0, 0, 0] → Alle LEDs rot (KVZ nicht vorhanden)
|
||||||
|
- Slot 2 LEDs (Index 8-11): [1, 1, 0, 1] → LED1=grün, LED2=grün, LED3=rot, LED4=grün
|
||||||
|
- Slot 3-31: Alle LEDs rot (0)
|
||||||
|
|
||||||
|
## API Endpunkte
|
||||||
|
|
||||||
|
### GET /api/kvz/data
|
||||||
|
|
||||||
|
Holt alle KVZ-Daten.
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kvzPresence": [...],
|
||||||
|
"kvzStatus": [...],
|
||||||
|
"timestamp": "2025-01-31T12:00:00.000Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### POST /api/kvz/data
|
||||||
|
|
||||||
|
Ersetzt komplette KVZ-Daten.
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"kvzPresence": [...],
|
||||||
|
"kvzStatus": [...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### POST /api/kvz/updateSettings
|
||||||
|
|
||||||
|
Aktualisiert spezifische KVZ-Einstellungen.
|
||||||
|
|
||||||
|
**Request Body:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"updates": [
|
||||||
|
{
|
||||||
|
"key": "kvzPresence",
|
||||||
|
"slot": 0,
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "kvzStatus",
|
||||||
|
"slot": 0,
|
||||||
|
"ledIndex": 1,
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Service Functions
|
||||||
|
|
||||||
|
### fetchKvzData()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { fetchKvzData } from "../services/fetchKvzDataService";
|
||||||
|
|
||||||
|
const data = await fetchKvzData();
|
||||||
|
console.log(data.kvzPresence); // [1, 0, 1, 0, ...]
|
||||||
|
```
|
||||||
|
|
||||||
|
### updateKvzSettings()
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { updateKvzSettings } from "../services/fetchKvzDataService";
|
||||||
|
|
||||||
|
await updateKvzSettings([
|
||||||
|
{ key: "kvzPresence", slot: 0, value: 1 },
|
||||||
|
{ key: "kvzStatus", slot: 0, ledIndex: 1, value: 1 },
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Redux Integration
|
||||||
|
|
||||||
|
Die Mock-Daten können in Redux geladen werden:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// In einem Thunk oder useEffect
|
||||||
|
const data = await fetchKvzData();
|
||||||
|
dispatch(
|
||||||
|
setKueData({
|
||||||
|
kvzPresence: data.kvzPresence,
|
||||||
|
kvzStatus: data.kvzStatus,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testen
|
||||||
|
|
||||||
|
Die Mock-Daten ermöglichen es:
|
||||||
|
|
||||||
|
1. **KVZ-Geräte zu simulieren** (Slots 0 und 2 haben KVZ)
|
||||||
|
2. **LED-Status zu testen** (verschiedene Kombinationen)
|
||||||
|
3. **API-Updates zu testen** (Presence und Status ändern)
|
||||||
|
4. **UI-Verhalten zu validieren** (bedingte Anzeige, Farben)
|
||||||
24
mocks/device-cgi-simulator/kvz/kvzData.json
Normal file
24
mocks/device-cgi-simulator/kvz/kvzData.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"kvzPresence": [
|
||||||
|
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0
|
||||||
|
],
|
||||||
|
"kvzActive": [
|
||||||
|
1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0
|
||||||
|
],
|
||||||
|
"kvzStatus": [
|
||||||
|
1, 0, 2, 1, 2, 0, 1, 0, 2, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0
|
||||||
|
],
|
||||||
|
"timestamp": "2025-07-31T11:39:22.951Z",
|
||||||
|
"description": {
|
||||||
|
"kvzPresence": "32 Slots: 1=KVZ Gerät vorhanden, 0=nicht vorhanden. Slots 0,2 haben KVZ-Geräte",
|
||||||
|
"kvzActive": "32 Slots: 1=KVZ aktiviert, 0=deaktiviert. Nur Slot 0 ist aktiviert",
|
||||||
|
"kvzStatus": "128 LEDs: 4 LEDs pro Slot. Slot 0: [1,0,1,0], Slot 2: [1,1,0,1] (aber Slot 2 ist deaktiviert)"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.661",
|
"version": "1.6.662",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.661",
|
"version": "1.6.662",
|
||||||
"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.661",
|
"version": "1.6.662",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { getAllTDRReferenceChartThunk } from "@/redux/thunks/getAllTDRReferenceC
|
|||||||
import { getTDRChartDataByIdThunk } from "@/redux/thunks/getTDRChartDataByIdThunk";
|
import { getTDRChartDataByIdThunk } from "@/redux/thunks/getTDRChartDataByIdThunk";
|
||||||
import { getLoopChartDataThunk } from "@/redux/thunks/getLoopChartDataThunk";
|
import { getLoopChartDataThunk } from "@/redux/thunks/getLoopChartDataThunk";
|
||||||
import { getAuthThunks } from "@/redux/thunks/getAuthThunks";
|
import { getAuthThunks } from "@/redux/thunks/getAuthThunks";
|
||||||
|
import { loadKvzData } from "@/redux/thunks/kvzThunks";
|
||||||
import Modal from "react-modal";
|
import Modal from "react-modal";
|
||||||
import { ToastContainer } from "react-toastify";
|
import { ToastContainer } from "react-toastify";
|
||||||
import "react-toastify/dist/ReactToastify.css";
|
import "react-toastify/dist/ReactToastify.css";
|
||||||
@@ -78,6 +79,10 @@ function AppContent({
|
|||||||
|
|
||||||
const loadAndDispatch = () => {
|
const loadAndDispatch = () => {
|
||||||
dispatch(getAuthThunks());
|
dispatch(getAuthThunks());
|
||||||
|
|
||||||
|
// KVZ-Daten beim Start laden
|
||||||
|
dispatch(loadKvzData());
|
||||||
|
|
||||||
if (pathname.includes("kabelueberwachung")) {
|
if (pathname.includes("kabelueberwachung")) {
|
||||||
dispatch(getKueDataThunk());
|
dispatch(getKueDataThunk());
|
||||||
} else if (pathname.includes("analogInputs")) {
|
} else if (pathname.includes("analogInputs")) {
|
||||||
|
|||||||
101
pages/api/kvz/data.ts
Normal file
101
pages/api/kvz/data.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// pages/api/kvz/data.ts
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export interface KvzData {
|
||||||
|
kvzPresence: number[];
|
||||||
|
kvzActive: number[];
|
||||||
|
kvzStatus: number[];
|
||||||
|
timestamp: string;
|
||||||
|
description?: {
|
||||||
|
kvzPresence: string;
|
||||||
|
kvzActive: string;
|
||||||
|
kvzStatus: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse<KvzData | { error: string }>
|
||||||
|
) {
|
||||||
|
if (req.method === "GET") {
|
||||||
|
try {
|
||||||
|
const filePath = path.join(
|
||||||
|
process.cwd(),
|
||||||
|
"mocks",
|
||||||
|
"device-cgi-simulator",
|
||||||
|
"kvz",
|
||||||
|
"kvzData.json"
|
||||||
|
);
|
||||||
|
const fileContents = fs.readFileSync(filePath, "utf8");
|
||||||
|
const kvzData: KvzData = JSON.parse(fileContents);
|
||||||
|
|
||||||
|
// Update timestamp to current time
|
||||||
|
kvzData.timestamp = new Date().toISOString();
|
||||||
|
|
||||||
|
res.status(200).json(kvzData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Lesen der KVZ-Daten:", error);
|
||||||
|
res.status(500).json({ error: "Fehler beim Laden der KVZ-Daten" });
|
||||||
|
}
|
||||||
|
} else if (req.method === "POST") {
|
||||||
|
try {
|
||||||
|
const { kvzPresence, kvzActive, kvzStatus } = req.body;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Array.isArray(kvzPresence) ||
|
||||||
|
!Array.isArray(kvzActive) ||
|
||||||
|
!Array.isArray(kvzStatus)
|
||||||
|
) {
|
||||||
|
return res.status(400).json({ error: "Ungültige Datenstruktur" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kvzPresence.length !== 32) {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ error: "kvzPresence muss 32 Elemente haben" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kvzActive.length !== 32) {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ error: "kvzActive muss 32 Elemente haben" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kvzStatus.length !== 128) {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ error: "kvzStatus muss 128 Elemente haben" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = path.join(
|
||||||
|
process.cwd(),
|
||||||
|
"mocks",
|
||||||
|
"device-cgi-simulator",
|
||||||
|
"kvz",
|
||||||
|
"kvzData.json"
|
||||||
|
);
|
||||||
|
const fileContents = fs.readFileSync(filePath, "utf8");
|
||||||
|
const existingData: KvzData = JSON.parse(fileContents);
|
||||||
|
|
||||||
|
const updatedData: KvzData = {
|
||||||
|
...existingData,
|
||||||
|
kvzPresence,
|
||||||
|
kvzActive,
|
||||||
|
kvzStatus,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.writeFileSync(filePath, JSON.stringify(updatedData, null, 2));
|
||||||
|
|
||||||
|
res.status(200).json(updatedData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Speichern der KVZ-Daten:", error);
|
||||||
|
res.status(500).json({ error: "Fehler beim Speichern der KVZ-Daten" });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.setHeader("Allow", ["GET", "POST"]);
|
||||||
|
res.status(405).json({ error: `Method ${req.method} not allowed` });
|
||||||
|
}
|
||||||
|
}
|
||||||
77
pages/api/kvz/updateSettings.ts
Normal file
77
pages/api/kvz/updateSettings.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// pages/api/kvz/updateSettings.ts
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { KvzData } from "./data";
|
||||||
|
|
||||||
|
interface UpdateRequest {
|
||||||
|
updates: Array<{
|
||||||
|
key: "kvzPresence" | "kvzActive" | "kvzStatus";
|
||||||
|
slot?: number;
|
||||||
|
ledIndex?: number;
|
||||||
|
value: number;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse<KvzData | { error: string }>
|
||||||
|
) {
|
||||||
|
if (req.method !== "POST") {
|
||||||
|
res.setHeader("Allow", ["POST"]);
|
||||||
|
return res.status(405).json({ error: `Method ${req.method} not allowed` });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { updates }: UpdateRequest = req.body;
|
||||||
|
|
||||||
|
if (!Array.isArray(updates)) {
|
||||||
|
return res.status(400).json({ error: "Updates muss ein Array sein" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = path.join(
|
||||||
|
process.cwd(),
|
||||||
|
"mocks",
|
||||||
|
"device-cgi-simulator",
|
||||||
|
"kvz",
|
||||||
|
"kvzData.json"
|
||||||
|
);
|
||||||
|
const fileContents = fs.readFileSync(filePath, "utf8");
|
||||||
|
const kvzData: KvzData = JSON.parse(fileContents);
|
||||||
|
|
||||||
|
// Updates anwenden
|
||||||
|
updates.forEach((update) => {
|
||||||
|
const { key, slot, ledIndex, value } = update;
|
||||||
|
|
||||||
|
if (key === "kvzPresence" && typeof slot === "number") {
|
||||||
|
if (slot >= 0 && slot < 32) {
|
||||||
|
kvzData.kvzPresence[slot] = value;
|
||||||
|
}
|
||||||
|
} else if (key === "kvzActive" && typeof slot === "number") {
|
||||||
|
if (slot >= 0 && slot < 32) {
|
||||||
|
kvzData.kvzActive[slot] = value;
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
key === "kvzStatus" &&
|
||||||
|
typeof slot === "number" &&
|
||||||
|
typeof ledIndex === "number"
|
||||||
|
) {
|
||||||
|
const arrayIndex = slot * 4 + ledIndex;
|
||||||
|
if (arrayIndex >= 0 && arrayIndex < 128) {
|
||||||
|
kvzData.kvzStatus[arrayIndex] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Timestamp aktualisieren
|
||||||
|
kvzData.timestamp = new Date().toISOString();
|
||||||
|
|
||||||
|
// Datei speichern
|
||||||
|
fs.writeFileSync(filePath, JSON.stringify(kvzData, null, 2));
|
||||||
|
|
||||||
|
res.status(200).json(kvzData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Update der KVZ-Einstellungen:", error);
|
||||||
|
res.status(500).json({ error: "Fehler beim Update der KVZ-Einstellungen" });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,6 +44,10 @@ interface KueDataState {
|
|||||||
memoryInterval: number[];
|
memoryInterval: number[];
|
||||||
// Fallsensors
|
// Fallsensors
|
||||||
win_fallSensorsActive: number[];
|
win_fallSensorsActive: number[];
|
||||||
|
// KVZ System - 32 Slots mit je 4 LEDs
|
||||||
|
kvzPresence: number[]; // 32 Werte: 1 = KVZ vorhanden, 0 = nicht vorhanden
|
||||||
|
kvzActive: number[]; // 32 Werte: 1 = KVZ aktiviert, 0 = deaktiviert
|
||||||
|
kvzStatus: number[]; // 128 Werte: 4 LEDs pro Slot (32 * 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: KueDataState = {
|
const initialState: KueDataState = {
|
||||||
@@ -89,6 +93,10 @@ const initialState: KueDataState = {
|
|||||||
memoryInterval: [],
|
memoryInterval: [],
|
||||||
// Fallsensors
|
// Fallsensors
|
||||||
win_fallSensorsActive: [], // Fallsensors aktiv
|
win_fallSensorsActive: [], // Fallsensors aktiv
|
||||||
|
// KVZ System - 32 Slots mit je 4 LEDs
|
||||||
|
kvzPresence: new Array(32).fill(0), // 32 Slots: 1 = KVZ vorhanden, 0 = nicht vorhanden
|
||||||
|
kvzActive: new Array(32).fill(0), // 32 Slots: 1 = KVZ aktiviert, 0 = deaktiviert
|
||||||
|
kvzStatus: new Array(128).fill(0), // 128 LEDs: 4 LEDs pro Slot (32 * 4)
|
||||||
};
|
};
|
||||||
|
|
||||||
const kueDataSlice = createSlice({
|
const kueDataSlice = createSlice({
|
||||||
|
|||||||
67
redux/thunks/kvzThunks.ts
Normal file
67
redux/thunks/kvzThunks.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// redux/thunks/kvzThunks.ts
|
||||||
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
|
import {
|
||||||
|
fetchKvzData,
|
||||||
|
updateKvzSettings,
|
||||||
|
} from "../../services/fetchKvzDataService";
|
||||||
|
import { setKueData } from "../slices/kueDataSlice";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lädt KVZ-Daten von der API und aktualisiert Redux Store
|
||||||
|
*/
|
||||||
|
export const loadKvzData = createAsyncThunk(
|
||||||
|
"kvz/loadData",
|
||||||
|
async (_, { dispatch }) => {
|
||||||
|
try {
|
||||||
|
const data = await fetchKvzData();
|
||||||
|
|
||||||
|
// KVZ-Daten in Redux Store laden
|
||||||
|
dispatch(
|
||||||
|
setKueData({
|
||||||
|
kvzPresence: data.kvzPresence,
|
||||||
|
kvzActive: data.kvzActive,
|
||||||
|
kvzStatus: data.kvzStatus,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Laden der KVZ-Daten:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aktualisiert KVZ-Einstellungen und synchronisiert Redux Store
|
||||||
|
*/
|
||||||
|
export const updateKvzData = createAsyncThunk(
|
||||||
|
"kvz/updateData",
|
||||||
|
async (
|
||||||
|
updates: Array<{
|
||||||
|
key: "kvzPresence" | "kvzActive" | "kvzStatus";
|
||||||
|
slot?: number;
|
||||||
|
ledIndex?: number;
|
||||||
|
value: number;
|
||||||
|
}>,
|
||||||
|
{ dispatch }
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const data = await updateKvzSettings(updates);
|
||||||
|
|
||||||
|
// Aktualisierte Daten in Redux Store laden
|
||||||
|
dispatch(
|
||||||
|
setKueData({
|
||||||
|
kvzPresence: data.kvzPresence,
|
||||||
|
kvzActive: data.kvzActive,
|
||||||
|
kvzStatus: data.kvzStatus,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Update der KVZ-Daten:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
53
services/fetchKvzDataService.ts
Normal file
53
services/fetchKvzDataService.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// services/fetchKvzDataService.ts
|
||||||
|
import { KvzData } from "../pages/api/kvz/data";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holt KVZ-Daten vom Server
|
||||||
|
*/
|
||||||
|
export const fetchKvzData = async (): Promise<KvzData> => {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/kvz/data");
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: KvzData = await response.json();
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Laden der KVZ-Daten:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aktualisiert KVZ-Einstellungen auf dem Server
|
||||||
|
*/
|
||||||
|
export const updateKvzSettings = async (
|
||||||
|
updates: Array<{
|
||||||
|
key: "kvzPresence" | "kvzActive" | "kvzStatus";
|
||||||
|
slot?: number;
|
||||||
|
ledIndex?: number;
|
||||||
|
value: number;
|
||||||
|
}>
|
||||||
|
): Promise<KvzData> => {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/kvz/updateSettings", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ updates }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: KvzData = await response.json();
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Update der KVZ-Einstellungen:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user