From 772ef50af55fef29c54b32137251bb84f3d1f226 Mon Sep 17 00:00:00 2001 From: Ismail Ali Date: Sun, 23 Feb 2025 11:06:15 +0100 Subject: [PATCH] feat: OPC-UA Einstellungen in eigenen Redux Slice ausgelagert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OPC-UA bezogene Variablen aus `variablesSlice` entfernt und in `opcuaSettingsSlice` ausgelagert - Neue Redux Actions für: - `setOpcUaZustand` (OPC-UA Zustand setzen) - `setOpcUaEncryption` (Verschlüsselung setzen) - `setOpcUaActiveClientCount` (Anzahl aktiver Clients setzen) - `setOpcUaNodesetName` (Nodeset-Name setzen) - `addOpcUaUser` & `removeOpcUaUser` (Benutzerverwaltung) - `loadWindowVariables.ts` angepasst, um OPC-UA-Daten in `opcuaSettingsSlice` zu speichern - Benutzerverwaltung optimiert: - Manuell hinzugefügte Benutzer bleiben erhalten - Benutzer werden nur aktualisiert, wenn sich `window.win_opcUaUsers` ändert - Keine automatische Statusumschaltung mehr beim OPC-UA-Server-Button Jetzt ist die OPC-UA Verwaltung sauber getrennt und stabil! 🚀 --- .../GeneralSettings.tsx | 1 - .../OPCUAInterfaceSettings.tsx | 154 +++++++++++------- config/webVersion.ts | 2 +- redux/slices/opcuaSettingsSlice.ts | 76 +++++++++ redux/store.ts | 2 + utils/loadWindowVariables.ts | 83 +++++++++- 6 files changed, 256 insertions(+), 62 deletions(-) create mode 100644 redux/slices/opcuaSettingsSlice.ts diff --git a/components/main/settingsPageComponents/GeneralSettings.tsx b/components/main/settingsPageComponents/GeneralSettings.tsx index e329ebc..5153a65 100644 --- a/components/main/settingsPageComponents/GeneralSettings.tsx +++ b/components/main/settingsPageComponents/GeneralSettings.tsx @@ -11,7 +11,6 @@ const GeneralSettings = () => { const systemSettings = useSelector( (state: RootState) => state.systemSettings ); - console.log("Redux SystemSettings:", systemSettings); const [name, setName] = useState(systemSettings.deviceName || ""); const [ip, setIp] = useState(systemSettings.ip || ""); diff --git a/components/main/settingsPageComponents/OPCUAInterfaceSettings.tsx b/components/main/settingsPageComponents/OPCUAInterfaceSettings.tsx index 3197071..a4d026f 100644 --- a/components/main/settingsPageComponents/OPCUAInterfaceSettings.tsx +++ b/components/main/settingsPageComponents/OPCUAInterfaceSettings.tsx @@ -1,57 +1,62 @@ +"use client"; // /components/main/settingsPageComponents/OPCUAInterfaceSettings.tsx import React, { useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { RootState } from "../../../redux/store"; +import { + setOpcUaEncryption, + toggleOpcUaServer, + setOpcUaNodesetName, + addOpcUaUser, + removeOpcUaUser, +} from "../../../redux/slices/opcuaSettingsSlice"; export default function OPCUAInterfaceSettings() { - const [isEnabled, setIsEnabled] = useState(true); - const [encryption, setEncryption] = useState("None"); - const [users, setUsers] = useState([ - { id: 1, username: "admin", password: "admin123" }, - { id: 2, username: "user1", password: "user123" }, - ]); - const [newUser, setNewUser] = useState({ username: "", password: "" }); + const dispatch = useDispatch(); + const opcuaSettings = useSelector((state: RootState) => state.opcuaSettings); - const toggleServer = () => setIsEnabled(!isEnabled); - const updateEncryption = (event) => setEncryption(event.target.value); - const addUser = () => { - if (newUser.username && newUser.password) { - setUsers([...users, { id: users.length + 1, ...newUser }]); - setNewUser({ username: "", password: "" }); + // Lokale Zustände für das neue Benutzerformular + const [newUsername, setNewUsername] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [nodesetName, setNodesetName] = useState( + opcuaSettings.opcUaNodesetName + ); + + const handleAddUser = () => { + if (newUsername.trim() && newPassword.trim()) { + dispatch(addOpcUaUser({ username: newUsername, password: newPassword })); + setNewUsername(""); + setNewPassword(""); } }; - const removeUser = (id) => setUsers(users.filter((user) => user.id !== id)); + + const handleNodesetUpdate = () => { + dispatch(setOpcUaNodesetName(nodesetName)); + }; return ( -
-

OPCUA Server Einstellungen

+
+

OPCUA Server Einstellungen

- {/* Server Aktivierung */} -
- + {/* ✅ Server Aktivierung */} +
+ +
- {/* Verschlüsselung */} + {/* ✅ Verschlüsselung */}
- +
- {/* Benutzer & Passwörter */} + {/* ✅ OPCUA Zustand (nur Anzeige) */} +
+ +
+ {opcuaSettings.opcUaZustand} +
+
+ + {/* ✅ Aktive Clients (nur Anzeige) */} +
+ +
+ {opcuaSettings.opcUaActiveClientCount} +
+
+ + {/* ✅ Nodeset Name */} +
+ +
+ setNodesetName(e.target.value)} + /> + +
+
+ + {/* ✅ Benutzerverwaltung */}

Benutzer

    - {users.map((user) => ( + {opcuaSettings.users.map((user) => (
  • ))}
-
+ + {/* ✅ Neuen Benutzer hinzufügen */} +
- setNewUser({ ...newUser, username: e.target.value }) - } - className="p-2 border rounded mr-2" + value={newUsername} + onChange={(e) => setNewUsername(e.target.value)} + className="p-2 border rounded w-1/3" /> - setNewUser({ ...newUser, password: e.target.value }) - } - className="p-2 border rounded mr-2" + value={newPassword} + onChange={(e) => setNewPassword(e.target.value)} + className="p-2 border rounded w-1/3" /> diff --git a/config/webVersion.ts b/config/webVersion.ts index 0800972..ecd1f86 100644 --- a/config/webVersion.ts +++ b/config/webVersion.ts @@ -6,5 +6,5 @@ 2: Patch oder Hotfix (Bugfixes oder kleine Änderungen). */ -const webVersion = "1.6.91"; +const webVersion = "1.6.92"; export default webVersion; diff --git a/redux/slices/opcuaSettingsSlice.ts b/redux/slices/opcuaSettingsSlice.ts new file mode 100644 index 0000000..492ea4c --- /dev/null +++ b/redux/slices/opcuaSettingsSlice.ts @@ -0,0 +1,76 @@ +// redux/slices/opcuaSettingsSlice.ts +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +interface OPCUAUser { + id: number; + username: string; + password: string; +} + +interface OPCUASettingsState { + isEnabled: boolean; + encryption: string; + opcUaZustand: string; + opcUaActiveClientCount: number; + opcUaNodesetName: string; + users: OPCUAUser[]; +} + +const initialState: OPCUASettingsState = { + isEnabled: true, + encryption: "None", + opcUaZustand: "Offline", + opcUaActiveClientCount: 0, + opcUaNodesetName: "DefaultNodeset", + users: [ + { id: 1, username: "admin", password: "admin123" }, + { id: 2, username: "user1", password: "user123" }, + ], +}; + +const opcuaSettingsSlice = createSlice({ + name: "opcuaSettings", + initialState, + reducers: { + toggleOpcUaServer(state) { + state.isEnabled = !state.isEnabled; + }, + setOpcUaEncryption(state, action: PayloadAction) { + state.encryption = action.payload; + }, + setOpcUaZustand(state, action: PayloadAction) { + state.opcUaZustand = action.payload; + }, + setOpcUaActiveClientCount(state, action: PayloadAction) { + state.opcUaActiveClientCount = action.payload; + }, + setOpcUaNodesetName(state, action: PayloadAction) { + state.opcUaNodesetName = action.payload; + }, + addOpcUaUser( + state, + action: PayloadAction<{ username: string; password: string }> + ) { + const newUser = { + id: state.users.length + 1, + ...action.payload, + }; + state.users.push(newUser); + }, + removeOpcUaUser(state, action: PayloadAction) { + state.users = state.users.filter((user) => user.id !== action.payload); + }, + }, +}); + +export const { + toggleOpcUaServer, + setOpcUaEncryption, + setOpcUaZustand, + setOpcUaActiveClientCount, + setOpcUaNodesetName, + addOpcUaUser, + removeOpcUaUser, +} = opcuaSettingsSlice.actions; + +export default opcuaSettingsSlice.reducer; diff --git a/redux/store.ts b/redux/store.ts index 75a1543..2d278b6 100644 --- a/redux/store.ts +++ b/redux/store.ts @@ -8,6 +8,7 @@ import digitalInputsReducer from "./slices/digitalInputsSlice"; import kabelueberwachungChartReducer from "./slices/kabelueberwachungChartSlice"; import dashboardReducer from "./slices/dashboardSlice"; import systemSettingsReducer from "./slices/systemSettingsSlice"; +import opcuaSettingsReducer from "./slices/opcuaSettingsSlice"; const store = configureStore({ reducer: { @@ -19,6 +20,7 @@ const store = configureStore({ kabelueberwachungChart: kabelueberwachungChartReducer, dashboard: dashboardReducer, systemSettings: systemSettingsReducer, + opcuaSettings: opcuaSettingsReducer, }, }); diff --git a/utils/loadWindowVariables.ts b/utils/loadWindowVariables.ts index 2b814b5..cedcb12 100644 --- a/utils/loadWindowVariables.ts +++ b/utils/loadWindowVariables.ts @@ -1,5 +1,15 @@ +// /utils/loadWindowVariables.ts import store from "../redux/store"; import { setSystemSettings } from "../redux/slices/systemSettingsSlice"; +import { + toggleOpcUaServer, + setOpcUaEncryption, + setOpcUaZustand, + setOpcUaActiveClientCount, + setOpcUaNodesetName, + addOpcUaUser, + removeOpcUaUser, +} from "../redux/slices/opcuaSettingsSlice"; // ✅ Interface für `window`-Objekt zur TypeScript-Sicherheit interface CustomWindow extends Window { @@ -21,6 +31,16 @@ interface SystemSettings { ntpActive: boolean; } +// ✅ Interface für OPC-UA Einstellungen im Redux-Store +interface OpcUaSettings { + isEnabled: boolean; + encryption: string; + opcUaZustand: string; + opcUaActiveClientCount: number; + opcUaNodesetName: string; + users: { id: number; username: string; password: string }[]; +} + // ✅ Hauptfunktion zum Laden von `window`-Variablen export async function loadWindowVariables(): Promise> { return new Promise((resolve, reject) => { @@ -73,11 +93,13 @@ export async function loadWindowVariables(): Promise> { "win_analogeEingaenge6", "win_analogeEingaenge7", "win_analogeEingaenge8", + "win_da_state", + "win_da_bezeichnung", "win_opcUaZustand", "win_opcUaActiveClientCount", "win_opcUaNodesetName", - "win_da_state", - "win_da_bezeichnung", + "win_opcUaEncryption", // ✅ NEU: Verschlüsselung von OPC-UA + "win_opcUaUsers", // ✅ NEU: OPC-UA Benutzerliste ]; const scripts: string[] = [ @@ -127,6 +149,7 @@ export async function loadWindowVariables(): Promise> { // ✅ Redux mit Systemvariablen aktualisieren loadAndStoreSystemSettings(win); + loadAndStoreOpcUaSettings(win); resolve(variablesObj); }) @@ -155,3 +178,59 @@ const loadAndStoreSystemSettings = (win: CustomWindow) => { store.dispatch(setSystemSettings(settings)); }; + +// ✅ Funktion zum Speichern von OPC-UA Variablen in Redux +const loadAndStoreOpcUaSettings = (win: CustomWindow) => { + // ✅ Aktuellen Redux-Status holen + const currentState = store.getState().opcuaSettings; + + // ✅ Nur setzen, wenn sich der Zustand geändert hat + if (currentState.opcUaZustand !== win.win_opcUaZustand) { + store.dispatch(setOpcUaZustand(win.win_opcUaZustand || "Offline")); + } + + // ✅ Nur setzen, wenn sich die Verschlüsselung geändert hat + if (currentState.encryption !== win.win_opcUaEncryption) { + store.dispatch(setOpcUaEncryption(win.win_opcUaEncryption || "None")); + } + + // ✅ Nur setzen, wenn sich die Anzahl der aktiven Clients geändert hat + if (currentState.opcUaActiveClientCount !== win.win_opcUaActiveClientCount) { + store.dispatch( + setOpcUaActiveClientCount(win.win_opcUaActiveClientCount || 0) + ); + } + + // ✅ Nur setzen, wenn sich der Nodeset-Name geändert hat + if (currentState.opcUaNodesetName !== win.win_opcUaNodesetName) { + store.dispatch( + setOpcUaNodesetName(win.win_opcUaNodesetName || "DefaultNodeset") + ); + } + + // ✅ Benutzer synchronisieren (aber nicht überschreiben, wenn manuell hinzugefügt) + if (Array.isArray(win.win_opcUaUsers) && win.win_opcUaUsers.length > 0) { + const newUsers = win.win_opcUaUsers; + const currentUsers = currentState.users; + + // ✅ Vorhandene Benutzer entfernen, die nicht mehr in `window` sind + currentUsers.forEach((user) => { + if (!newUsers.some((newUser) => newUser.username === user.username)) { + store.dispatch(removeOpcUaUser(user.id)); + } + }); + + // ✅ Nur neue Benutzer hinzufügen, falls sie nicht existieren + newUsers.forEach((user) => { + if ( + !currentUsers.some( + (existingUser) => existingUser.username === user.username + ) + ) { + store.dispatch( + addOpcUaUser({ username: user.username, password: user.password }) + ); + } + }); + } +};