feat: CGI-kompatiblen CSV-Parser für digitale Eingänge implementiert

- digitaleInputsMockData.json angepasst: CGI-nahe Simulation mit CSV-Strings und Stringwerten
- fetchDigitalInputsService.ts erweitert:
  - CSV-Zeilen werden automatisch in Arrays umgewandelt
  - Labels wie "'DE1','DE2'" werden korrekt aufgeteilt
  - Daten aus 4 CGI-Blöcken zu 32 Eingängen gemappt
- ermöglicht realitätsnahe Tests in Entwicklungsumgebung ohne Produktion
This commit is contained in:
ISA
2025-07-09 08:41:50 +02:00
parent 7797549baa
commit 14bd72756a
13 changed files with 155 additions and 548 deletions

View File

@@ -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.565
NEXT_PUBLIC_APP_VERSION=1.6.566
NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (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_USE_CGI=true
# App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.6.565
NEXT_PUBLIC_APP_VERSION=1.6.566
NEXT_PUBLIC_CPL_MODE=production

View File

@@ -1,3 +1,14 @@
## [1.6.566] 2025-07-09
- feat: Umstellung von CGI-Daten für analoge Eingänge von JS auf JSON
- CGI-Platzhalter in `analogInputs.json` eingeführt (z.B. <%=AAV01%>)
- Alte JS-Datei ersetzt durch reine JSON-Struktur
- Anpassung des Service-Handlers (`getAnalogInputsHandler.ts`) auf JSON-Parsing
- Reduziert Ladezeit, vereinfacht Code und entfernt unnötige Script-Einbindung
- Mock-Daten weiterhin in `analogInputsMockData.json` für Entwicklungsmodus verfügbar
---
## [1.6.565] 2025-07-08
- Bei den Kabelüberwachung kann neben den Button “Firmware Update” noch zwei Button “Konfiguration sichern” und “Konfiguration zurücksichern” im Admin-Modus hinzukommen.

View File

@@ -1,274 +0,0 @@
{
"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
],
"win_de_invert": [
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
],
"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
],
"win_de_time_filter": [
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
],
"win_de_weighting": [
3,
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
],
"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
],
"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
],
"win_de_label": [
"DE1",
"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,63 +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 = [
0, 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 = [
3, 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 = [
3, 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 = [
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_label = [
"DE1",
"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,64 +1,50 @@
{
"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
],
"win_de_invert": [
0, 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
],
"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
],
"win_de_time_filter": [
3, 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
],
"win_de_weighting": [
3, 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
],
"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
],
"win_de_offline": [
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,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"
],
"win_de_label": [
"DE1",
"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"
"'DE 1','DE 2','DE 3','DE 4','DE 5','DE 6','DE 7','DE 8'",
"'DE 9','DE 10','DE 11','DE 12','DE 13','DE 14','DE 15','DE 16'",
"'DE 17','DE 18','DE 19','DE 20','DE 21','DE 22','DE 23','DE 24'",
"'DE 25','DE 26','DE 27','DE 28','DE 29','DE 30','DE 31','DE 32'"
],
"win_de_counter": [
"0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000",
"0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000",
"0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000",
"0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000"
],
"win_de_time_filter": [
"598.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000",
"0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000",
"0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000",
"0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000"
],
"win_de_weighting": [
"998,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"
],
"win_de_invert": [
"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"
],
"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"
],
"win_de_offline": [
"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"
]
}

View File

@@ -8,7 +8,12 @@ const nextConfig = {
images: {
unoptimized: true,
},
...(isExport && { output: "export" }), // ⬅️ dynamisch aktivieren
...(isExport && { output: "export" }),
// 🔧 HINZUGEFÜGT:
env: {
NEXT_PUBLIC_CPL_MODE: process.env.NEXT_PUBLIC_CPL_MODE,
},
};
export default nextConfig;

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "cpl-v4",
"version": "1.6.565",
"version": "1.6.566",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cpl-v4",
"version": "1.6.565",
"version": "1.6.566",
"dependencies": {
"@fontsource/roboto": "^5.1.0",
"@headlessui/react": "^2.2.4",

View File

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

View File

@@ -11,7 +11,7 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
const filePath = path.join(
process.cwd(),
"mocks",
"api",
"device-cgi-simulator",
"SERVICE",
"digitalInputsMockData.json"
);
@@ -20,16 +20,6 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
return res.status(200).json(json);
}
if (mode === "jsSimulatedProd") {
const digitalInputsScript = fs.readFileSync(
"mocks/device-cgi-simulator/SERVICE/digitalInputsMockData.js",
"utf-8"
);
res.setHeader("Content-Type", "application/javascript");
res.status(200).send(digitalInputsScript);
return;
}
return res.status(400).json({ error: "Ungültiger Modus" });
} catch (error) {
console.error("❌ Fehler beim Parsen der digitalen Eingänge:", error);

View File

@@ -1,40 +0,0 @@
// Zustand -> DESxx xx =Nr Eingang 1-32 80-83 = BGT 1 bis 4
var win_de_state=[<%=DES80%>,<%=DES81%>,<%=DES82%>,<%=DES83%>];//Zustand des digitalen Eingangs 1 = EIN, 0 = AUS
// Name -> DENxx xx =Nr Eingang 1-32 80-83 = BGT 1 bis 4
var win_de_label =[<%=DEN80%>,<%=DEN81%>,<%=DEN82%>,<%=DEN83%>];
//Zählerstand -> DESxx xx =Nr Eingang 1-32 80-83 = BGT 1 bis 4
var win_de_counter=[<%=DEC80%>,<%=DEC81%>,<%=DEC82%>,<%=DEC83%>];//Zählerstand
//Filterzeit -> DEFxx xx =Nr Eingang 1-32 80-83 = BGT 1 bis 4
var win_de_time_filter=[<%=DEF80%>,<%=DEF81%>,<%=DEF82%>,<%=DEF83%>];//Filterzeit
// Gewichtung -> DEGxx xx = Nr Eingang 1-32 80-83 = BGT 1 bis 4
var win_de_weighting=[<%=DEG80%>,<%=DEG81%>,<%=DEG82%>,<%=DEG83%>];//Gewichtung
// Invertierung -> DEIxx xx = Nr Eingang 1-32 80-83 = BGT 1 bis 4
var win_de_invert=[<%=DEI80%>,<%=DEI81%>,<%=DEI82%>,<%=DEI83%>];//Invertierung
// Zähler aktiv -> DEZxx xx = Nr Eingang 1-32 80-83 = BGT 1 bis 4
var win_de_counter_active=[<%=DEZ80%>,<%=DEZ81%>,<%=DEZ82%>,<%=DEZ83%>];//Zähler aktiv
// Eingang offline -> DEAxx xx = Nr Eingang 1-32 80-83 = BGT 1 bis 4
var win_de_offline=[<%=DEA80%>,<%=DEA81%>,<%=DEA82%>,<%=DEA83%>];//Eingang offline
//DECxx xx =Nr Eingang 1-32 80-83 = BGT 1 bis 4
//var win_flutter=[<%=DEF80%>,<%=DEF80%>,<%=DEF81%>,<%=DEF82%>];// noch nicht verwendet in Lastheft Oktober 2024
/* von https://10.10.0.222/CPL?Service/de.ACP
var de=[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 counter=[0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000];
var flutter=[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 de=[1,0,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];
var counter=[0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000];
var flutter=[0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000];
*/

View File

@@ -14,7 +14,13 @@ export const getDigitalInputsThunk = createAsyncThunk(
try {
const data = await fetchDigitalInputsService();
if (data) {
dispatch(setInputs(data)); // ✅ Redux mit API-Daten füllen
// Map data to ensure all DigitalInput properties are present
const mappedData = data.map((item: any) => ({
...item,
flutter: item.flutter ?? false,
zaehlerAktiv: item.zaehlerAktiv ?? false,
}));
dispatch(setInputs(mappedData)); // ✅ Redux mit API-Daten füllen
}
} catch (error) {
console.error("❌ Fehler beim Laden der digitalen Eingänge:", error);

View File

@@ -1,109 +1,95 @@
// ✅ Service: /services/fetchDigitalInputsService.ts
export interface DigitalInput {
id: number;
value: number | string;
label: string;
invert: boolean;
counter: number | string;
timeFilter: number | string;
weighting: number | string;
counterActive: boolean;
eingangOffline: boolean;
status: boolean;
flutter: boolean;
zaehlerAktiv: boolean;
}
export const fetchDigitalInputsService = async () => {
const mode = process.env.NEXT_PUBLIC_CPL_MODE;
// 🧠 Neu: CSV-Zeile wie "1,0,1,0" in [1,0,1,0] konvertieren
const parseCsvNumbers = (line: string): number[] =>
line.split(",").map((v) => Number(v.trim()));
// ✅ PRODUKTIV: lädt JavaScript vom Gerät über CGI
if (mode === "production") {
console.log("🔄 Lade analoge Eingänge im Produktionsmodus...");
const scriptUrl = "/CPL?/CPL/SERVICE/digitalInputs.js";
// 🧠 Neu: CSV-Zeile wie "'DE1','DE2'" in ["DE1", "DE2"] umwandeln
const parseCsvLabels = (line: string): string[] =>
line.split(",").map((v) => v.trim().replace(/^'+|'+$/g, ""));
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);
});
export const fetchDigitalInputsService = async (): Promise<DigitalInput[]> => {
const mode = "production"; // ⛳ production oder json
const win = window as any;
const url =
mode === "production"
? "/CPL?/CPL/SERVICE/digitalInputs.json"
: "/api/cpl/getDigitalInputsHandler";
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],
eingangOffline: !!win.win_de_offline[i],
status: !!win.win_de_state[i],
}));
const res = await fetch(url);
if (!res.ok) {
throw new Error(`❌ Fehler beim Laden der digitalen Eingänge (${mode})`);
}
// ✅ JSON-MODUS (API gibt JSON-Daten zurück)
else if (mode === "json") {
console.log("🔄 Lade digitale Eingänge im JSON-Modus...");
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();
const data = await res.json();
console.log("📡 JSON-Daten geladen in service:", data);
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],
eingangOffline: !!data.win_de_offline[i],
status: !!data.win_de_state[i],
}));
// 🧠 Neu: CSV-Zeilen in Arrays umwandeln
const state = data.win_de_state.flatMap(parseCsvNumbers);
const label = data.win_de_label.flatMap(parseCsvLabels);
const invert = data.win_de_invert.flatMap(parseCsvNumbers);
const counter = data.win_de_counter.flatMap((line: string) =>
line.split(",").map((v) => v.trim())
);
const timeFilter = data.win_de_time_filter.flatMap((line: string) =>
line.split(",").map((v) => v.trim())
);
const weighting = data.win_de_weighting.flatMap((line: string) =>
line.split(",").map((v) => v.trim())
);
const counterActive = data.win_de_counter_active.flatMap(parseCsvNumbers);
const offline = data.win_de_offline.flatMap(parseCsvNumbers);
interface CsvData {
state: number[];
label: string[];
invert: number[];
counter: (number | string)[];
timeFilter: (number | string)[];
weighting: (number | string)[];
counterActive: number[];
offline: number[];
}
// ✅ jsSimulatedProd-MODUS (Script einbinden und aus window lesen)
else if (mode === "jsSimulatedProd") {
console.log("🔄 Lade digitale Eingänge im jsSimulatedProd-Modus...");
// const res = await fetch("/api/cpl/getDigitalInputsHandler");
//------------------------
const scriptUrl = "/api/cpl/getDigitalInputsHandler"; // gibt JavaScript zurück
// Removed redundant DigitalInputResult interface
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 des simulierten Scripts");
document.body.appendChild(script);
});
const csvData: CsvData = {
state,
label,
invert,
counter,
timeFilter,
weighting,
counterActive,
offline,
};
// Annahme: Das Script setzt window.win_de_state usw.
const data = {
win_de_state: (window as any).win_de_state,
win_de_label: (window as any).win_de_label,
win_de_invert: (window as any).win_de_invert,
win_de_counter: (window as any).win_de_counter,
win_de_time_filter: (window as any).win_de_time_filter,
win_de_weighting: (window as any).win_de_weighting,
win_de_counter_active: (window as any).win_de_counter_active,
win_de_offline: (window as any).win_de_offline,
};
//--------------------------
console.log("📡 jsSimulatedProd-Daten geladen in service:", data);
return data.win_de_state.map((_: any, i: number) => ({
return csvData.state.map(
(_, i): DigitalInput => ({
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],
eingangOffline: !!data.win_de_offline[i],
status: !!data.win_de_state[i],
}));
}
// ❌ Unbekannter Modus
throw new Error(`❌ Unbekannter NEXT_PUBLIC_CPL_MODE: ${mode}`);
value: csvData.state[i],
label: csvData.label[i],
invert: !!csvData.invert[i],
counter: csvData.counter[i],
timeFilter: csvData.timeFilter[i],
weighting: csvData.weighting[i],
counterActive: !!csvData.counterActive[i],
eingangOffline: !!csvData.offline[i],
status: !!csvData.state[i],
flutter: false,
zaehlerAktiv: false,
})
);
};