docs: Zusatzfunktionen (Kai, 25.06.2025) in TODO.md ergänzt
This commit is contained in:
@@ -6,5 +6,5 @@ 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.444
|
||||
NEXT_PUBLIC_APP_VERSION=1.6.445
|
||||
NEXT_PUBLIC_CPL_MODE=jsSimulatedProd # 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_USE_CGI=true
|
||||
# App-Versionsnummer
|
||||
NEXT_PUBLIC_APP_VERSION=1.6.444
|
||||
NEXT_PUBLIC_APP_VERSION=1.6.445
|
||||
NEXT_PUBLIC_CPL_MODE=production
|
||||
@@ -1,3 +1,8 @@
|
||||
## [1.6.445] – 2025-06-25
|
||||
|
||||
- docs: README
|
||||
|
||||
---
|
||||
## [1.6.444] – 2025-06-23
|
||||
|
||||
- feat: digitalOutputs separate jsSimulatedProd mode
|
||||
|
||||
@@ -18,7 +18,13 @@ import handleAdminLogin from "./handlers/handleAdminLogin";
|
||||
|
||||
ReactModal.setAppElement("#__next");
|
||||
|
||||
function SettingModal({ showModal, onClose }) {
|
||||
function SettingModal({
|
||||
showModal,
|
||||
onClose,
|
||||
}: {
|
||||
showModal: boolean;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const { isAdminLoggedIn, logoutAdmin } = useAdminAuth(showModal);
|
||||
|
||||
const { formValues, setFormValues } = useSystemSettings(showModal);
|
||||
@@ -72,9 +78,21 @@ function SettingModal({ showModal, onClose }) {
|
||||
const [ntp2, setNtp2] = useState(ntp2_Redux || "");
|
||||
const [ntp3, setNtp3] = useState(ntp3_Redux || "");
|
||||
const [ntpTimezone, setNtpTimezone] = useState(ntpTimezone_Redux || "");
|
||||
const [active, setActive] = useState(active_Redux || "");
|
||||
const [active, setActive] = useState<boolean>(
|
||||
typeof active_Redux === "boolean" ? active_Redux : active_Redux === "true"
|
||||
);
|
||||
|
||||
const [originalValues, setOriginalValues] = useState({});
|
||||
const [originalValues, setOriginalValues] = useState({
|
||||
name: name,
|
||||
ip: ip,
|
||||
subnet: subnet,
|
||||
gateway: gateway,
|
||||
ntp1: ntp1,
|
||||
ntp2: ntp2,
|
||||
ntp3: ntp3,
|
||||
ntpTimezone: ntpTimezone,
|
||||
active: active,
|
||||
});
|
||||
const currentValues = {
|
||||
name,
|
||||
ip,
|
||||
@@ -228,12 +246,14 @@ function SettingModal({ showModal, onClose }) {
|
||||
</div>
|
||||
<div>
|
||||
<label className="block font-medium">NTP Active:</label>
|
||||
<input
|
||||
type="text"
|
||||
<select
|
||||
className="border border-gray-300 rounded p-1 xl:p-2 w-full"
|
||||
value={active}
|
||||
onChange={(e) => setActive(e.target.value)}
|
||||
/>
|
||||
value={active ? "true" : "false"}
|
||||
onChange={(e) => setActive(e.target.value === "true")}
|
||||
>
|
||||
<option value="true">true</option>
|
||||
<option value="false">false</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -241,13 +261,13 @@ function SettingModal({ showModal, onClose }) {
|
||||
<div className="flex flex-col md:flex-row justify-between mt-4 gap-2">
|
||||
<button
|
||||
className="bg-littwin-blue text-white px-3 py-1 xl:px-4 xl:py-2 rounded w-full md:w-auto"
|
||||
onClick={handleReboot}
|
||||
onClick={() => handleReboot()}
|
||||
>
|
||||
Neustart CPL
|
||||
</button>
|
||||
<button
|
||||
onClick={() =>
|
||||
isAdminLoggedIn ? handleAdminLogout() : setShowLoginForm(true)
|
||||
isAdminLoggedIn ? logoutAdmin() : setShowLoginForm(true)
|
||||
}
|
||||
className="bg-littwin-blue text-white px-3 py-1 xl:px-4 xl:py-2 rounded w-full md:w-auto"
|
||||
>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useState, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../../redux/store";
|
||||
export function useSystemSettings(showModal: boolean) {
|
||||
const settings = useSelector((state: RootState) => state.systemSettings);
|
||||
const settings = useSelector((state: RootState) => state.systemSettingsSlice);
|
||||
const [formValues, setFormValues] = useState(settings);
|
||||
const [originalValues, setOriginalValues] = useState({});
|
||||
|
||||
|
||||
@@ -16,13 +16,24 @@ export function generateKeyAndIV() {
|
||||
}
|
||||
|
||||
// Generiert einen verschlüsselten Token
|
||||
export function generateToken(user) {
|
||||
const payload = {
|
||||
interface User {
|
||||
username: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
interface TokenPayload {
|
||||
username: string;
|
||||
role: string;
|
||||
exp: number;
|
||||
}
|
||||
|
||||
export function generateToken(user: User): string {
|
||||
const payload: TokenPayload = {
|
||||
username: user.username,
|
||||
role: user.role,
|
||||
exp: Date.now() + 5 * 60 * 1000, // Ablaufzeit: 5 Minuten
|
||||
};
|
||||
const token = JSON.stringify(payload);
|
||||
const token: string = JSON.stringify(payload);
|
||||
const { key, iv } = generateKeyAndIV();
|
||||
return CryptoJS.AES.encrypt(token, key, { iv }).toString();
|
||||
}
|
||||
|
||||
@@ -38,7 +38,11 @@ export default function AnalogInputsChart({
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const { data, isLoading, error } = useSelector(
|
||||
(state: RootState) => state.analogInputsHistory
|
||||
);
|
||||
) as {
|
||||
data: { [key: string]: any[] };
|
||||
isLoading: boolean;
|
||||
error: any;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getAnalogInputsHistoryThunk());
|
||||
@@ -95,7 +99,7 @@ export default function AnalogInputsChart({
|
||||
x: {
|
||||
type: "time" as const,
|
||||
time: {
|
||||
unit: "hour",
|
||||
unit: "hour" as const,
|
||||
tooltipFormat: "HH:mm 'Uhr' dd.MM.",
|
||||
displayFormats: {
|
||||
hour: "HH:mm",
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
"use client"; // /components/main/analogInputs/AnalogInputsTable.tsx
|
||||
|
||||
export type AnalogInput = {
|
||||
id: number;
|
||||
value: number;
|
||||
label: string;
|
||||
unit?: string; // Make unit optional if it may not exist
|
||||
offset: number;
|
||||
factor: number;
|
||||
loggerInterval: number;
|
||||
weighting: number;
|
||||
};
|
||||
|
||||
import React, { useEffect } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { RootState, AppDispatch } from "@/redux/store";
|
||||
|
||||
@@ -9,7 +9,13 @@ import outputIcon from "@iconify/icons-mdi/output";
|
||||
import switchIcon from "@iconify/icons-ion/switch";
|
||||
import { setDigitalOutputs } from "@/redux/slices/digitalOutputsSlice";
|
||||
|
||||
export default function DigitalOutputsWidget({ openOutputModal }) {
|
||||
interface DigitalOutputsWidgetProps {
|
||||
openOutputModal: (output: any) => void;
|
||||
}
|
||||
|
||||
export default function DigitalOutputsWidget({
|
||||
openOutputModal,
|
||||
}: DigitalOutputsWidgetProps) {
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const digitalOutputs = useSelector(
|
||||
(state: RootState) => state.digitalOutputsSlice.outputs
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useState, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../../redux/store";
|
||||
export function useSystemSettings(showModal: boolean) {
|
||||
const settings = useSelector((state: RootState) => state.systemSettings);
|
||||
const settings = useSelector((state: RootState) => state.systemSettingsSlice);
|
||||
const [formValues, setFormValues] = useState(settings);
|
||||
const [originalValues, setOriginalValues] = useState({});
|
||||
|
||||
|
||||
@@ -16,13 +16,22 @@ export function generateKeyAndIV() {
|
||||
}
|
||||
|
||||
// Generiert einen verschlüsselten Token
|
||||
export function generateToken(user) {
|
||||
const payload = {
|
||||
interface UserPayload {
|
||||
username: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
interface TokenPayload extends UserPayload {
|
||||
exp: number;
|
||||
}
|
||||
|
||||
export function generateToken(user: UserPayload): string {
|
||||
const payload: TokenPayload = {
|
||||
username: user.username,
|
||||
role: user.role,
|
||||
exp: Date.now() + 5 * 60 * 1000, // Ablaufzeit: 5 Minuten
|
||||
};
|
||||
const token = JSON.stringify(payload);
|
||||
const token: string = JSON.stringify(payload);
|
||||
const { key, iv } = generateKeyAndIV();
|
||||
return CryptoJS.AES.encrypt(token, key, { iv }).toString();
|
||||
}
|
||||
|
||||
@@ -4,16 +4,22 @@ import { Icon } from "@iconify/react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../redux/store";
|
||||
|
||||
const VersionInfo: React.FC = () => {
|
||||
type VersionInfoProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const VersionInfo: React.FC<VersionInfoProps> = ({ className = "" }) => {
|
||||
const appVersion =
|
||||
useSelector((state: RootState) => state.systemSettingsSlice.appVersion) ||
|
||||
"Unbekannt";
|
||||
const webVersion = useSelector(
|
||||
(state: RootState) => state.webVersionSlice.appVersion
|
||||
); // Webversion aus Redux holen
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="bg-gray-50 p-4 rounded-lg shadow-sm border border-gray-200 w-full laptop:p-2">
|
||||
<div
|
||||
className={`bg-gray-50 rounded-lg shadow-sm border border-gray-200 w-full laptop:p-2 ${className}`}
|
||||
>
|
||||
<h2 className="text-lg font-semibold text-gray-700 mb-2">
|
||||
Versionsinformationen
|
||||
</h2>
|
||||
|
||||
11
docs/TODO.md
11
docs/TODO.md
@@ -1,3 +1,12 @@
|
||||
## ✅ TODOs
|
||||
|
||||
- [ ] TODO: In digitale Eingänge Modal Out of Service geht nicht in json , jsSimulatedProd und production. readOnly?
|
||||
## 🔄 Zusatzfunktionen (prio niedrig) – Kai, 25. Juni 2025
|
||||
|
||||
- [ ] Messwertanzeige eines Eingangs: Zeitraum einstellbar, Bezeichnung und Einheit in die Messkurve, Datum in die Messkurve
|
||||
- [ ] Startseite Meldungen: Spalte Quelle wie bei Berichte (nicht "Modul")
|
||||
- [ ] Startseite Spaltenreihenfolge: Pop (farbiger Punkt), Zeitstempel, Quelle, Meldung, Status
|
||||
- [ ] Berichte Spaltenreihenfolge: Pop (farbiger Punkt), Zeitstempel, Quelle, Meldung, Status
|
||||
- [ ] Filter für Quelle
|
||||
- [ ] System: Button bei jedem Messwert für Detailansicht (inkl. Kurve im Popup und Zeitauswahl)
|
||||
- [ ] Einstellungen – Benutzerverwaltung: Admin kann Adminpasswort ändern
|
||||
- [ ] Einstellungen – OPC: Anzahl der aktuellen Clients (ggf. KAS-Variable einbauen)
|
||||
|
||||
@@ -4,11 +4,11 @@ var win_analogInputsValues = [
|
||||
4.771072, 5.665244, 0.005467, -0.007468, 0.000002, 0.000001, 0.000001,
|
||||
0.070007,
|
||||
];
|
||||
var win_analogInputsLabels = ["AE 1", "AE 2", "AE 3", "AE 4", "AE 5", "AE 6", "AE 7", "AE 8", ];
|
||||
var win_analogInputsOffset = [10.995, 0.0, 0.0, 0, 0.0, 0.0, 0.0, 0.0];
|
||||
var win_analogInputsFactor = [11.996, 1.0, 1.0, 1, 1.0, 1.0, 1.0, 1.0];
|
||||
var win_analogInputsLoggerIntervall = [6, 10, 10, 10, 10, 10, 10, 10];
|
||||
var win_analogInputsUnits = ["mA", "V", "V", "V", "mA", "mA", "mA", "mA"];
|
||||
var win_analogInputsLabels = ["AE 11", "AE 2", "AE 3", "AE 4", "AE 5", "AE 6", "AE 7", "AE 8", ];
|
||||
var win_analogInputsOffset = [21.999, 0.0, 0.0, 0, 0.0, 0.0, 0.0, 0.0];
|
||||
var win_analogInputsFactor = [21.999, 1.0, 1.0, 1, 1.0, 1.0, 1.0, 1.0];
|
||||
var win_analogInputsLoggerIntervall = [21, 10, 10, 10, 10, 10, 10, 10];
|
||||
var win_analogInputsUnits = ["V", "V", "V", "V", "mA", "mA", "mA", "mA"];
|
||||
var win_analogInputsWeighting = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
|
||||
/*
|
||||
|
||||
63
mocks/device-cgi-simulator/SERVICE/analogInputsMockData.json
Normal file
63
mocks/device-cgi-simulator/SERVICE/analogInputsMockData.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"win_analogInputsValues": [
|
||||
"126.812080",
|
||||
"5.680176",
|
||||
"-0.015003",
|
||||
"0.009538",
|
||||
"-0.000002",
|
||||
"0.000003",
|
||||
"-0.000005",
|
||||
"0.000000"
|
||||
],
|
||||
"win_analogInputsLabels": [
|
||||
"'AE 1'",
|
||||
"'AE 2'",
|
||||
"'AE 3'",
|
||||
"'AE 4'",
|
||||
"'AE 5'",
|
||||
"'AE 6'",
|
||||
"'AE 7'",
|
||||
"'AE 8'"
|
||||
],
|
||||
"win_analogInputsUnits": [
|
||||
"'V'",
|
||||
"'V'",
|
||||
"'V'",
|
||||
"'V'",
|
||||
"'mA'",
|
||||
"'mA'",
|
||||
"'mA'",
|
||||
"'mA'"
|
||||
],
|
||||
"win_analogInputsFactor": [
|
||||
"21.999",
|
||||
"1.000",
|
||||
"1.000",
|
||||
"1.000",
|
||||
"1.000",
|
||||
"1.000",
|
||||
"1.000",
|
||||
"1.000"
|
||||
],
|
||||
"win_analogInputsOffset": [
|
||||
"21.999",
|
||||
"0.000",
|
||||
"0.000",
|
||||
"0.000",
|
||||
"0.000",
|
||||
"0.000",
|
||||
"0.000",
|
||||
"0.000"
|
||||
],
|
||||
"win_analogInputsWeighting": ["0", "0", "0", "0", "0", "0", "0", "0"],
|
||||
"win_analogInputsLoggerIntervall": [
|
||||
"21",
|
||||
"10",
|
||||
"10",
|
||||
"10",
|
||||
"10",
|
||||
"10",
|
||||
"10",
|
||||
"10"
|
||||
]
|
||||
}
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.444",
|
||||
"version": "1.6.445",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.444",
|
||||
"version": "1.6.445",
|
||||
"dependencies": {
|
||||
"@fontsource/roboto": "^5.1.0",
|
||||
"@iconify-icons/ri": "^1.2.10",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.444",
|
||||
"version": "1.6.445",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -44,7 +44,8 @@ function AppContent({
|
||||
}): JSX.Element {
|
||||
const dispatch = useAppDispatch();
|
||||
const [sessionExpired, setSessionExpired] = useState(false);
|
||||
|
||||
const mode = "DIA0"; // oder aus Router oder Session
|
||||
const type = 0; // Beispiel: 0 für "loop", 1 für "iso" (bitte ggf. anpassen)
|
||||
useEffect(() => {
|
||||
let intervalId: NodeJS.Timeout;
|
||||
const pathname = window.location.pathname;
|
||||
@@ -71,11 +72,19 @@ function AppContent({
|
||||
} else if (pathname.includes("tdrRef")) {
|
||||
dispatch(getAllTDRReferenceChartThunk());
|
||||
} else if (pathname.includes("tdrSlot")) {
|
||||
dispatch(getReferenceCurveBySlotThunk());
|
||||
dispatch(getReferenceCurveBySlotThunk(1));
|
||||
} else if (pathname.includes("tdrId")) {
|
||||
dispatch(getTDRChartDataByIdThunk());
|
||||
dispatch(getTDRChartDataByIdThunk(1));
|
||||
} else if (pathname.includes("loopChart")) {
|
||||
dispatch(getLoopChartDataThunk());
|
||||
dispatch(
|
||||
getLoopChartDataThunk({
|
||||
mode,
|
||||
type,
|
||||
slotNumber: 1, // Beispielwert, ggf. anpassen
|
||||
vonDatum: new Date().toISOString().split("T")[0], // Beispiel: heutiges Datum
|
||||
bisDatum: new Date().toISOString().split("T")[0], // Beispiel: heutiges Datum
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -19,12 +19,13 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
}
|
||||
|
||||
if (mode === "jsSimulatedProd") {
|
||||
const analogInputsScript = fs.readFileSync(
|
||||
"mocks/device-cgi-simulator/SERVICE/analogInputsMockData.js",
|
||||
"utf-8"
|
||||
const filePath = path.join(
|
||||
process.cwd(),
|
||||
"mocks/device-cgi-simulator/SERVICE/analogInputsMockData.json"
|
||||
);
|
||||
res.setHeader("Content-Type", "application/javascript");
|
||||
res.status(200).send(analogInputsScript);
|
||||
const jsonContent = fs.readFileSync(filePath, "utf-8");
|
||||
const data = JSON.parse(jsonContent);
|
||||
res.status(200).json(data);
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export default function handler(req, res) {
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { slot } = req.query;
|
||||
setTimeout(() => {
|
||||
res
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// pages/api/cpl/tdrMessungStartenMockHandler.ts
|
||||
export default function handler(req, res) {
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { slot } = req.query;
|
||||
setTimeout(() => {
|
||||
res.status(200).json({ message: `TDR simuliert für Slot ${slot}` });
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
export default function handler(req, res) {
|
||||
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (req.method !== "POST") {
|
||||
return res.status(405).json({ message: "Nur POST erlaubt" });
|
||||
}
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
// /pages/api/cpl/updateKueSettingsDataAPIHandler.ts
|
||||
import path from "path";
|
||||
import fs from "fs/promises";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
let { key, value, slot } = req.query;
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { key, value, slot } = req.query;
|
||||
|
||||
if (!key || slot === undefined) {
|
||||
return res.status(400).json({ error: "Missing key or slot parameter." });
|
||||
if (
|
||||
typeof key !== "string" ||
|
||||
typeof value !== "string" ||
|
||||
typeof slot !== "string"
|
||||
) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: "Missing or invalid key, value, or slot parameter." });
|
||||
}
|
||||
|
||||
const mockFilePath = path.join(
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
import path from "path";
|
||||
import fs from "fs/promises";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const updates = req.body?.updates;
|
||||
|
||||
if (!Array.isArray(updates) || updates.length === 0) {
|
||||
@@ -34,7 +38,7 @@ export default async function handler(req, res) {
|
||||
.map((v) => (v === "" ? "0" : v))
|
||||
.slice(0, 32);
|
||||
|
||||
values[Number(slot)] = Number(value);
|
||||
values[Number(slot)] = String(Number(value));
|
||||
|
||||
const newLine = `var ${key} = [\n ${values.join(", ")}\n];`;
|
||||
fileContent = fileContent.replace(regex, newLine);
|
||||
|
||||
@@ -10,10 +10,10 @@ import { getKueDataThunk } from "../redux/thunks/getKueDataThunk";
|
||||
function Kabelueberwachung() {
|
||||
const dispatch: AppDispatch = useDispatch();
|
||||
const searchParams = useSearchParams(); // URL-Parameter holen
|
||||
const initialRack = parseInt(searchParams.get("rack")) || 1; // Rack-Nummer aus URL oder 1
|
||||
const initialRack = parseInt(searchParams.get("rack") ?? "1") || 1; // Rack-Nummer aus URL oder 1
|
||||
|
||||
const [activeRack, setActiveRack] = useState(initialRack); // Nutze initialRack als Startwert
|
||||
const [alarmStatus, setAlarmStatus] = useState([]); // Alarmstatus
|
||||
const [alarmStatus, setAlarmStatus] = useState<boolean[]>([]); // Alarmstatus
|
||||
|
||||
// Redux-Variablen aus dem Store abrufen
|
||||
const {
|
||||
@@ -25,21 +25,23 @@ function Kabelueberwachung() {
|
||||
kueResidence,
|
||||
kueCableBreak,
|
||||
kueGroundFault,
|
||||
} = useSelector((state) => state.kueDataSlice);
|
||||
} = useSelector((state: RootState) => state.kueDataSlice);
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// 🚀 **TDR-Daten bereits in Redux abrufen**
|
||||
// Redux-Variablen abrufen
|
||||
const tdrData = useSelector((state) => state.tdrChartSlice.data);
|
||||
const loading = useSelector((state) => state.tdrChartSlice.loading);
|
||||
const error = useSelector((state) => state.tdrChartSlice.error);
|
||||
const tdrData = useSelector((state: RootState) => state.tdrChartSlice.data);
|
||||
const loading = useSelector(
|
||||
(state: RootState) => state.tdrChartSlice.loading
|
||||
);
|
||||
const error = useSelector((state: RootState) => state.tdrChartSlice.error);
|
||||
//----------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Alarmstatus basierend auf Redux-Variablen berechnen
|
||||
const updateAlarmStatus = () => {
|
||||
const updatedAlarmStatus = kueIso.map((_, index) => {
|
||||
return (
|
||||
return !!(
|
||||
(kueAlarm1 && kueAlarm1[index]) ||
|
||||
(kueAlarm2 && kueAlarm2[index]) ||
|
||||
(kueCableBreak && kueCableBreak[index]) ||
|
||||
@@ -66,7 +68,8 @@ function Kabelueberwachung() {
|
||||
}));
|
||||
//console.log("Alle Module:", allModules);
|
||||
|
||||
const racks = {
|
||||
type RackKey = "rack1" | "rack2" | "rack3" | "rack4";
|
||||
const racks: Record<RackKey, typeof allModules> = {
|
||||
rack1: allModules.slice(0, 8),
|
||||
rack2: allModules.slice(8, 16),
|
||||
rack3: allModules.slice(16, 24),
|
||||
@@ -92,7 +95,11 @@ function Kabelueberwachung() {
|
||||
); */
|
||||
|
||||
// Funktion zum Wechseln des Racks
|
||||
const changeRack = (rack) => {
|
||||
interface ChangeRackFn {
|
||||
(rack: number): void;
|
||||
}
|
||||
|
||||
const changeRack: ChangeRackFn = (rack) => {
|
||||
setActiveRack(rack);
|
||||
console.log(`Aktives Rack geändert zu: ${rack}`);
|
||||
};
|
||||
@@ -144,23 +151,22 @@ function Kabelueberwachung() {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-row space-x-8 xl:space-x-0 2xl:space-x-8 qhd:space-x-16 ml-[5%] mt-[5%]">
|
||||
{racks[`rack${activeRack}`].map((slot, index) => {
|
||||
const slotIndex = index + (activeRack - 1) * 8;
|
||||
return (
|
||||
<div key={index} className="flex">
|
||||
<Kue705FO
|
||||
isolationswert={slot.isolationswert}
|
||||
schleifenwiderstand={slot.schleifenwiderstand}
|
||||
modulName={slot.modulName}
|
||||
kueOnline={slot.kueOnlineStatus}
|
||||
alarmStatus={slot.alarmStatus}
|
||||
slotIndex={slotIndex}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{racks[`rack${activeRack}` as RackKey].map((slot, index) => {
|
||||
const slotIndex = index + (activeRack - 1) * 8;
|
||||
return (
|
||||
<div key={index} className="flex">
|
||||
<Kue705FO
|
||||
isolationswert={slot.isolationswert}
|
||||
schleifenwiderstand={slot.schleifenwiderstand}
|
||||
modulName={slot.modulName}
|
||||
kueOnline={slot.kueOnlineStatus}
|
||||
alarmStatus={slot.alarmStatus}
|
||||
slotIndex={slotIndex}
|
||||
tdrLocation={[]} // TODO: Replace with actual tdrLocation data if available
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
72
public/CPL/SERVICE/analogInputs.json
Normal file
72
public/CPL/SERVICE/analogInputs.json
Normal file
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"win_analogInputsValues": [
|
||||
"<%=AAV01%>",
|
||||
"<%=AAV02%>",
|
||||
"<%=AAV03%>",
|
||||
"<%=AAV04%>",
|
||||
"<%=AAV05%>",
|
||||
"<%=AAV06%>",
|
||||
"<%=AAV07%>",
|
||||
"<%=AAV08%>"
|
||||
],
|
||||
"win_analogInputsLabels": [
|
||||
"<%=ACN01%>",
|
||||
"<%=ACN02%>",
|
||||
"<%=ACN03%>",
|
||||
"<%=ACN04%>",
|
||||
"<%=ACN05%>",
|
||||
"<%=ACN06%>",
|
||||
"<%=ACN07%>",
|
||||
"<%=ACN08%>"
|
||||
],
|
||||
"win_analogInputsUnits": [
|
||||
"<%=ACU01%>",
|
||||
"<%=ACU02%>",
|
||||
"<%=ACU03%>",
|
||||
"<%=ACU04%>",
|
||||
"<%=ACU05%>",
|
||||
"<%=ACU06%>",
|
||||
"<%=ACU07%>",
|
||||
"<%=ACU08%>"
|
||||
],
|
||||
"win_analogInputsFactor": [
|
||||
"<%=ACF01%>",
|
||||
"<%=ACF02%>",
|
||||
"<%=ACF03%>",
|
||||
"<%=ACF04%>",
|
||||
"<%=ACF05%>",
|
||||
"<%=ACF06%>",
|
||||
"<%=ACF07%>",
|
||||
"<%=ACF08%>"
|
||||
],
|
||||
"win_analogInputsOffset": [
|
||||
"<%=ACO01%>",
|
||||
"<%=ACO02%>",
|
||||
"<%=ACO03%>",
|
||||
"<%=ACO04%>",
|
||||
"<%=ACO05%>",
|
||||
"<%=ACO06%>",
|
||||
"<%=ACO07%>",
|
||||
"<%=ACO08%>"
|
||||
],
|
||||
"win_analogInputsWeighting": [
|
||||
"<%=ACS01%>",
|
||||
"<%=ACS02%>",
|
||||
"<%=ACS03%>",
|
||||
"<%=ACS04%>",
|
||||
"<%=ACS05%>",
|
||||
"<%=ACS06%>",
|
||||
"<%=ACS07%>",
|
||||
"<%=ACS08%>"
|
||||
],
|
||||
"win_analogInputsLoggerIntervall": [
|
||||
"<%=ACL01%>",
|
||||
"<%=ACL02%>",
|
||||
"<%=ACL03%>",
|
||||
"<%=ACL04%>",
|
||||
"<%=ACL05%>",
|
||||
"<%=ACL06%>",
|
||||
"<%=ACL07%>",
|
||||
"<%=ACL08%>"
|
||||
]
|
||||
}
|
||||
@@ -2,61 +2,56 @@
|
||||
|
||||
export const fetchAnalogInputsService = async () => {
|
||||
const mode = process.env.NEXT_PUBLIC_CPL_MODE;
|
||||
// ✅ PRODUKTIV: lädt JavaScript vom Gerät über CGI
|
||||
// ✅ PRODUKTIV: lädt JSON-Datei vom Gerät über CGI
|
||||
if (mode === "production") {
|
||||
console.log("🔄 Lade analoge Eingänge im Produktionsmodus...");
|
||||
const scriptUrl = "/CPL?/CPL/SERVICE/analogInputs.js";
|
||||
console.log(
|
||||
"🔄 Lade analoge Eingänge im Produktionsmodus (JSON über CPL?)..."
|
||||
);
|
||||
|
||||
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 analogInputs.js");
|
||||
document.body.appendChild(script);
|
||||
const res = await fetch("/CPL?/CPL/SERVICE/analogInputs.json", {
|
||||
headers: { Accept: "application/json" },
|
||||
});
|
||||
|
||||
const win = window as any;
|
||||
if (!res.ok) {
|
||||
throw new Error("❌ Fehler beim Laden der analogInputs.json");
|
||||
}
|
||||
|
||||
return Array.from({ length: 8 }, (_, i) => ({
|
||||
const data = await res.json();
|
||||
|
||||
return data.win_analogInputsValues.map((value: string, i: number) => ({
|
||||
id: i + 1,
|
||||
value: parseFloat(win.win_analogInputsValues[i]),
|
||||
label: win.win_analogInputsLabels[i],
|
||||
unit: win.win_analogInputsUnits[i],
|
||||
offset: parseFloat(win.win_analogInputsOffset[i]),
|
||||
factor: parseFloat(win.win_analogInputsFactor[i]),
|
||||
loggerInterval: parseInt(win.win_analogInputsLoggerIntervall[i]),
|
||||
weighting: parseInt(win.win_analogInputsWeighting[i]),
|
||||
value: parseFloat(value),
|
||||
label: data.win_analogInputsLabels[i].replace(/'/g, ""),
|
||||
unit: data.win_analogInputsUnits[i].replace(/'/g, ""),
|
||||
offset: parseFloat(data.win_analogInputsOffset[i]),
|
||||
factor: parseFloat(data.win_analogInputsFactor[i]),
|
||||
loggerInterval: parseInt(data.win_analogInputsLoggerIntervall[i]),
|
||||
weighting: parseInt(data.win_analogInputsWeighting[i]),
|
||||
}));
|
||||
}
|
||||
|
||||
// ✅ jsSimulatedProd-MODUS (Script einbinden und aus window lesen)
|
||||
else if (mode === "jsSimulatedProd") {
|
||||
console.log("🔄 Lade simulierte analoge Eingänge im Produktionsmodus...");
|
||||
const scriptUrl = "/api/cpl/getAnalogInputsHandler"; // gibt JavaScript zurück
|
||||
console.log("🧪 Lade analoge Eingänge im Simulationsmodus (JSON)");
|
||||
|
||||
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 res = await fetch("/api/cpl/getAnalogInputsHandler");
|
||||
if (!res.ok)
|
||||
throw new Error("❌ Fehler beim Laden der analogen Eingänge (Mock)");
|
||||
|
||||
const win = window as any;
|
||||
const data = await res.json();
|
||||
|
||||
return Array.from({ length: 8 }, (_, i) => ({
|
||||
return data.win_analogInputsValues.map((value: string, i: number) => ({
|
||||
id: i + 1,
|
||||
value: parseFloat(win.win_analogInputsValues[i]),
|
||||
label: win.win_analogInputsLabels[i],
|
||||
unit: win.win_analogInputsUnits[i],
|
||||
offset: parseFloat(win.win_analogInputsOffset[i]),
|
||||
factor: parseFloat(win.win_analogInputsFactor[i]),
|
||||
loggerInterval: parseInt(win.win_analogInputsLoggerIntervall[i]),
|
||||
weighting: parseInt(win.win_analogInputsWeighting[i]),
|
||||
value: parseFloat(value),
|
||||
label: data.win_analogInputsLabels[i].replace(/'/g, ""),
|
||||
unit: data.win_analogInputsUnits[i].replace(/'/g, ""),
|
||||
offset: parseFloat(data.win_analogInputsOffset[i]),
|
||||
factor: parseFloat(data.win_analogInputsFactor[i]),
|
||||
loggerInterval: parseInt(data.win_analogInputsLoggerIntervall[i]),
|
||||
weighting: parseInt(data.win_analogInputsWeighting[i]),
|
||||
}));
|
||||
}
|
||||
|
||||
// ✅ JSON-MODUS (API gibt JSON-Daten zurück)
|
||||
else if (mode === "json") {
|
||||
console.log("🔄 Lade analoge Eingänge im JSON-Modus...");
|
||||
|
||||
@@ -7,7 +7,10 @@ export async function checkSession(): Promise<boolean> {
|
||||
|
||||
if (typeof window === "undefined") return false;
|
||||
|
||||
return window[expectedGlobalVar] !== undefined;
|
||||
return (
|
||||
(window as unknown as Record<string, unknown>)[expectedGlobalVar] !==
|
||||
undefined
|
||||
);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -54,13 +54,13 @@ export async function loadWindowVariables(): Promise<{
|
||||
const win = window as unknown as CustomWindow;
|
||||
|
||||
const variablesObj: Record<string, any> = requiredVars.reduce(
|
||||
(acc, variable) => {
|
||||
(acc: Record<string, any>, variable: string) => {
|
||||
if (win[variable] !== undefined) {
|
||||
acc[variable.replace("win_", "")] = win[variable];
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
{} as Record<string, any>
|
||||
);
|
||||
|
||||
const kueData =
|
||||
|
||||
Reference in New Issue
Block a user