wip digitale Eingänge sind sichtbar aber keine Werte in Modal

This commit is contained in:
ISA
2025-06-20 06:50:40 +02:00
parent 8d67b08d7f
commit 7ff1c4aaaf
20 changed files with 229 additions and 376 deletions

View File

@@ -6,5 +6,5 @@ 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.425 NEXT_PUBLIC_APP_VERSION=1.6.426
NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsmock (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter) NEXT_PUBLIC_CPL_MODE=jsmock # json (Entwicklungsumgebung) oder jsmock (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter)

View File

@@ -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.425 NEXT_PUBLIC_APP_VERSION=1.6.426
NEXT_PUBLIC_CPL_MODE=production NEXT_PUBLIC_CPL_MODE=production

View File

@@ -118,7 +118,7 @@ export default function InputModal({ selectedInput, closeInputModal, isOpen }) {
return; return;
} }
const res = await fetch("/api/cpl/updateDigitaleEingaenge", { const res = await fetch("/api/cpl/updateDigitalInputs", {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify(updates), body: JSON.stringify(updates),

View File

@@ -0,0 +1,63 @@
// @/mocks/device-cgi-simulator/SERVICE/digitalInputsMockData.js
var win_de_state = [
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, 0,
];
var win_de_invert = [
1, 0, 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,
];
var win_de_counter = [
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,
];
var win_de_time_filter = [
1500, 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,
];
var win_de_weighting = [
600, 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,
];
var win_de_counter_active = [
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,
];
var win_de_offline = [
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, 0, 0,
];
var win_de_label = [
"DE112",
"DE2",
"DE3",
"DE4",
"DE5",
"DE6",
"DE7",
"DE8",
"DE9",
"DE10",
"DE11",
"DE12",
"DE13",
"DE14",
"DE15",
"DE16",
"DE17",
"DE18",
"DE19",
"DE20",
"DE21",
"DE22",
"DE23",
"DE24",
"DE25",
"DE26",
"DE27",
"DE28",
"DE29",
"DE30",
"DE31",
"DE32",
];

View File

@@ -1,2 +0,0 @@
win_da_state = [1, 0, 1, 0];
win_da_bezeichnung = ["Ausgang11", "Ausgang2", "Ausgang3", "Ausgang4"];

View File

@@ -1,274 +0,0 @@
// auto-generated from update API
var win_de_state = [
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,
0
];
var win_de_invert = [
1,
0,
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
];
var win_de_counter = [
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
];
var win_de_time_filter = [
1500,
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
];
var win_de_weighting = [
600,
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
];
var win_de_counter_active = [
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
];
var win_de_offline = [
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,
0,
0
];
var win_de_label = [
"DE112",
"DE2",
"DE3",
"DE4",
"DE5",
"DE6",
"DE7",
"DE8",
"DE9",
"DE10",
"DE11",
"DE12",
"DE13",
"DE14",
"DE15",
"DE16",
"DE17",
"DE18",
"DE19",
"DE20",
"DE21",
"DE22",
"DE23",
"DE24",
"DE25",
"DE26",
"DE27",
"DE28",
"DE29",
"DE30",
"DE31",
"DE32"
];

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "cpl-v4", "name": "cpl-v4",
"version": "1.6.425", "version": "1.6.426",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "cpl-v4", "name": "cpl-v4",
"version": "1.6.425", "version": "1.6.426",
"dependencies": { "dependencies": {
"@fontsource/roboto": "^5.1.0", "@fontsource/roboto": "^5.1.0",
"@iconify-icons/ri": "^1.2.10", "@iconify-icons/ri": "^1.2.10",

View File

@@ -1,6 +1,6 @@
{ {
"name": "cpl-v4", "name": "cpl-v4",
"version": "1.6.425", "version": "1.6.426",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",

View File

@@ -1,26 +0,0 @@
// /pages/api/cpl/digitaleEingaengeAPIHandler.ts
import { NextApiRequest, NextApiResponse } from "next";
import path from "path";
import fs from "fs/promises";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const filePath = path.join(
process.cwd(),
"apiMockData",
"SERVICE",
"digitaleEingaengeMockData.js"
);
try {
const data = await fs.readFile(filePath, "utf-8");
res.setHeader("Content-Type", "text/javascript"); // wichtig!
res.status(200).send(data);
} catch (error) {
res.status(404).json({ error: "File not found" });
}
}

View File

@@ -0,0 +1,63 @@
// /pages/api/cpl/getDgitalInputsHandler.ts
import fs from "fs";
import path from "path";
import type { NextApiRequest, NextApiResponse } from "next";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const mode = process.env.NEXT_PUBLIC_CPL_MODE;
if (mode === "json") {
const filePath = path.join(
process.cwd(),
"mocks",
"api",
"SERVICE",
"digitalInputsMockData.json"
);
const fileContent = fs.readFileSync(filePath, "utf-8");
const json = JSON.parse(fileContent);
return res.status(200).json(json);
}
if (mode === "jsmock") {
const jsPath = path.join(
process.cwd(),
"mocks/device-cgi-simulator/SERVICE/digitalInputsMockData.js"
);
const fileContent = fs.readFileSync(jsPath, "utf-8");
const extractArray = (name: string) => {
const match = fileContent.match(
new RegExp(`var\\s+${name}\\s*=\\s*\\[([\\s\\S]*?)\\];`)
);
if (!match) throw new Error(`Feld ${name} nicht gefunden`);
return match[1]
.split(",")
.map((s) => s.trim().replace(/^["']|["']$/g, ""));
};
const data = {
win_de_state: extractArray("win_de_state").map(Number),
win_de_invert: extractArray("win_de_invert").map(Number),
win_de_counter: extractArray("win_de_counter").map(Number),
win_de_time_filter: extractArray("win_de_time_filter").map(Number),
win_de_weighting: extractArray("win_de_weighting").map(Number),
win_de_counter_active: extractArray("win_de_counter_active").map(
Number
),
win_de_offline: extractArray("win_de_offline").map(Number),
win_de_label: extractArray("win_de_label"),
};
return res.status(200).json(data);
}
return res.status(400).json({ error: "Ungültiger Modus" });
} catch (error) {
console.error("❌ Fehler beim Parsen der digitalen Eingänge:", error);
return res
.status(500)
.json({ error: "Fehler beim Parsen der digitalen Eingänge" });
}
}

View File

@@ -1,4 +1,4 @@
// /pages/api/cpl/updateDigitaleEingaenge.ts // /pages/api/cpl/updateDigitalInputs.ts
import type { NextApiRequest, NextApiResponse } from "next"; import type { NextApiRequest, NextApiResponse } from "next";
import path from "path"; import path from "path";
import fs from "fs"; import fs from "fs";
@@ -8,7 +8,7 @@ const mockFilePath = path.join(
process.cwd(), process.cwd(),
"apiMockData", "apiMockData",
"SERVICE", "SERVICE",
"digitaleEingaengeMockData.js" "digitalInputsMockData.js"
); );
// Funktion zum Parsen des JS-Datei-Inhalts in ein eval-fähiges Objekt // Funktion zum Parsen des JS-Datei-Inhalts in ein eval-fähiges Objekt

View File

@@ -6,7 +6,7 @@ import { AppDispatch, RootState } from "../redux/store";
import InputModal from "../components/main/einausgaenge/modals/InputModal"; import InputModal from "../components/main/einausgaenge/modals/InputModal";
import { fetchDigitaleEingaengeThunk } from "../redux/thunks/fetchDigitaleEingaengeThunk"; import { fetchDigitalInputsThunk } from "@/redux/thunks/fetchDigitalInputsThunk";
import { fetchDigitalOutputsThunk } from "../redux/thunks/fetchDigitalOutputsThunk"; import { fetchDigitalOutputsThunk } from "../redux/thunks/fetchDigitalOutputsThunk";
import DigitalInputs from "../components/main/einausgaenge/DigitalInputs"; import DigitalInputs from "../components/main/einausgaenge/DigitalInputs";
@@ -26,11 +26,11 @@ const EinAusgaenge: React.FC = () => {
const [isOutputModalOpen, setIsOutputModalOpen] = useState(false); const [isOutputModalOpen, setIsOutputModalOpen] = useState(false);
useEffect(() => { useEffect(() => {
dispatch(fetchDigitaleEingaengeThunk()); dispatch(fetchDigitalInputsThunk());
dispatch(fetchDigitalOutputsThunk()); dispatch(fetchDigitalOutputsThunk());
const interval = setInterval(() => { const interval = setInterval(() => {
dispatch(fetchDigitaleEingaengeThunk()); dispatch(fetchDigitalInputsThunk());
dispatch(fetchDigitalOutputsThunk()); dispatch(fetchDigitalOutputsThunk());
}, 10000); }, 10000);

View File

@@ -1,12 +1,12 @@
"use client"; // /pages/digitalOutputs.tsx "use client"; // /pages/digitalOutputs.tsx
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { AppDispatch, RootState } from "../redux/store"; import { AppDispatch, RootState } from "@/redux/store";
import DigitalOutputsModal from "../components/main/einausgaenge/modals/DigitalOutputsModal"; import DigitalOutputsModal from "../components/main/einausgaenge/modals/DigitalOutputsModal";
import { fetchDigitaleEingaengeThunk } from "../redux/thunks/fetchDigitaleEingaengeThunk"; import { fetchDigitalInputsThunk } from "@/redux/thunks/fetchDigitalInputsThunk";
import { fetchDigitalOutputsThunk } from "../redux/thunks/fetchDigitalOutputsThunk"; import { fetchDigitalOutputsThunk } from "@/redux/thunks/fetchDigitalOutputsThunk";
import DigitalOutputs from "../components/main/einausgaenge/DigitalOutputsWidget"; import DigitalOutputs from "../components/main/einausgaenge/DigitalOutputsWidget";
@@ -24,7 +24,7 @@ const EinAusgaenge: React.FC = () => {
dispatch(fetchDigitalOutputsThunk()); dispatch(fetchDigitalOutputsThunk());
const interval = setInterval(() => { const interval = setInterval(() => {
dispatch(fetchDigitaleEingaengeThunk()); dispatch(fetchDigitalInputsThunk());
dispatch(fetchDigitalOutputsThunk()); dispatch(fetchDigitalOutputsThunk());
}, 500); }, 500);

View File

@@ -1,17 +1,18 @@
// @/redux/thunks/fetchDigitalInputsThunk.ts
import { createAsyncThunk } from "@reduxjs/toolkit"; import { createAsyncThunk } from "@reduxjs/toolkit";
import { fetchDigitaleEingaengeService } from "../../services/fetchDigitaleEingaengeService"; import { fetchDigitalInputsService } from "@/services/fetchDigitalInputsService";
import { setInputs } from "../slices/digitalInputsSlice"; import { setInputs } from "@/redux/slices/digitalInputsSlice";
/** /**
* Holt digitale Eingänge von der API und speichert sie in Redux. * Holt digitale Eingänge von der API und speichert sie in Redux.
*/ */
export const fetchDigitaleEingaengeThunk = createAsyncThunk( export const fetchDigitalInputsThunk = createAsyncThunk(
"digitalInputs/fetchDigitaleEingaenge", "digitalInputs/fetchDigitalInputs",
async (_, { dispatch }) => { async (_, { dispatch }) => {
if (typeof window === "undefined") return; if (typeof window === "undefined") return;
try { try {
const data = await fetchDigitaleEingaengeService(); const data = await fetchDigitalInputsService();
if (data) { if (data) {
dispatch(setInputs(data)); // ✅ Redux mit API-Daten füllen dispatch(setInputs(data)); // ✅ Redux mit API-Daten füllen
} }

View File

@@ -0,0 +1,79 @@
// ✅ Service: /services/fetchDigitalInputsService.ts
export const fetchDigitalInputsService = async () => {
const mode = process.env.NEXT_PUBLIC_CPL_MODE;
// ✅ PRODUKTIV: lädt JavaScript vom Gerät über CGI
if (mode === "production") {
const scriptUrl = "/CPL?/CPL/SERVICE/digitalInputs.js";
await new Promise<void>((resolve, reject) => {
const script = document.createElement("script");
script.src = scriptUrl;
script.async = true;
script.onload = () => resolve();
script.onerror = () =>
reject("❌ Fehler beim Laden der digitalen Eingänge (production)");
document.body.appendChild(script);
});
const win = window as any;
return Array.from({ length: 32 }, (_, i) => ({
id: i + 1,
value: win.win_de_state[i],
label: win.win_de_label[i],
invert: !!win.win_de_invert[i],
counter: win.win_de_counter[i],
timeFilter: win.win_de_time_filter[i],
weighting: win.win_de_weighting[i],
counterActive: !!win.win_de_counter_active[i],
offline: !!win.win_de_offline[i],
}));
}
// ✅ JSON-MODUS (API gibt JSON-Daten zurück)
if (mode === "json") {
const res = await fetch("/api/cpl/getDigitalInputsHandler");
if (!res.ok)
throw new Error("❌ Fehler beim Laden der digitalen Eingänge (json)");
const data = await res.json();
return data.win_de_state.map((_: any, i: number) => ({
id: i + 1,
value: data.win_de_state[i],
label: data.win_de_label[i],
invert: !!data.win_de_invert[i],
counter: data.win_de_counter[i],
timeFilter: data.win_de_time_filter[i],
weighting: data.win_de_weighting[i],
counterActive: !!data.win_de_counter_active[i],
offline: !!data.win_de_offline[i],
}));
}
// ✅ JSMOCK-MODUS (Script einbinden und aus window lesen)
if (mode === "jsmock") {
const res = await fetch("/api/cpl/getDigitalInputsHandler");
if (!res.ok)
throw new Error("❌ Fehler beim Laden der digitalen Eingänge (json)");
const data = await res.json();
return data.win_de_state.map((_: any, i: number) => ({
id: i + 1,
value: data.win_de_state[i],
label: data.win_de_label[i],
invert: !!data.win_de_invert[i],
counter: data.win_de_counter[i],
timeFilter: data.win_de_time_filter[i],
weighting: data.win_de_weighting[i],
counterActive: !!data.win_de_counter_active[i],
offline: !!data.win_de_offline[i],
}));
}
// ❌ Unbekannter Modus
throw new Error(`❌ Unbekannter NEXT_PUBLIC_CPL_MODE: ${mode}`);
};

View File

@@ -1,51 +0,0 @@
// ✅ Service: /services/fetchDigitaleEingaengeService.ts
export const fetchDigitaleEingaengeService = async () => {
try {
if (typeof window === "undefined") return null;
// ✅ de.js nur bei Bedarf nachladen (Pfad abhängig von Umgebung)
const scriptSrc =
process.env.NEXT_PUBLIC_NODE_ENV === "production"
? "/CPL?/CPL/SERVICE/de.js"
: "/api/cpl/digitaleEingaengeAPIHandler";
await new Promise<void>((resolve, reject) => {
const script = document.createElement("script");
script.src = scriptSrc;
script.async = true;
script.onload = () => resolve();
script.onerror = () => reject("❌ Fehler beim Laden von de.js");
document.body.appendChild(script);
});
const win = window as any;
if (!Array.isArray(win.win_de_state)) {
console.warn("⚠️ win_de_state ist nicht vorhanden oder kein Array");
return [];
}
const formattedData = win.win_de_state.map((status, index) => ({
id: index + 1,
label: win.win_de_label?.[index] || `DE${index + 1}`,
name: win.win_de_label?.[index] || "",
status: status === 1,
counter: win.win_de_counter?.[index] || 0,
flutter: win.win_flutter?.[index] || 0,
invertierung: win.win_de_invert?.[index] === 1,
// 🔽 NEU:
filterzeit: win.win_de_time_filter?.[index] || 0,
gewichtung: win.win_de_weighting?.[index] || 0,
zaehlerAktiv: win.win_de_counter_active?.[index] === 1,
eingangOffline: win.win_de_offline?.[index] === 1,
}));
return formattedData;
} catch (error) {
console.error("❌ Fehler beim Laden der digitalen Eingänge:", error);
return null;
}
};

View File

@@ -18,9 +18,9 @@ export async function loadWindowVariables(): Promise<{
if (pathname.includes("kabelueberwachung")) { if (pathname.includes("kabelueberwachung")) {
scripts.push(isDev ? "kabelueberwachungAPIHandler" : "kueData.js"); scripts.push(isDev ? "kabelueberwachungAPIHandler" : "kueData.js");
} else if (pathname.includes("digitalInputs")) { } else if (pathname.includes("digitalInputs")) {
scripts.push(isDev ? "digitaleEingaengeAPIHandler" : "de.js"); scripts.push(isDev ? "getDigitalInputsHandler" : "digitalInputs.js");
} else if (pathname.includes("digitalOutputs")) { } else if (pathname.includes("digitalOutputs")) {
scripts.push(isDev ? "digitalOutputsAPIHandler" : "da.js"); scripts.push(isDev ? "digitalOutputsAPIHandler" : "digitalOutputs.js");
} else if (pathname.includes("analogInputs")) { } else if (pathname.includes("analogInputs")) {
scripts.push(isDev ? "getAnalogInputsHandler" : "analogInputs.js"); scripts.push(isDev ? "getAnalogInputsHandler" : "analogInputs.js");
} else if (pathname.includes("dashboard")) { } else if (pathname.includes("dashboard")) {