Add: SERVICE JSON Files

This commit is contained in:
ISA
2025-06-25 15:26:00 +02:00
parent 53c2a02224
commit 845c2dd658
23 changed files with 242 additions and 53 deletions

View File

@@ -6,7 +6,7 @@ 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.446
NEXT_PUBLIC_APP_VERSION=1.6.447
NEXT_PUBLIC_CPL_MODE=jsSimulatedProd # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter)
#### Feature-Flags ####

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.446
NEXT_PUBLIC_APP_VERSION=1.6.447
NEXT_PUBLIC_CPL_MODE=production

View File

@@ -1,3 +1,8 @@
## [1.6.447] 2025-06-25
- feat: Zeitraum einstellbar in AnalogInputsChart mit Zoom- und Pan-Funktion umgesetzt
---
## [1.6.446] 2025-06-25
- docs: Zusatzfunktionen (Kai, 25.06.2025) in TODO.md ergänzt

View File

@@ -36,6 +36,9 @@ export default function AnalogInputsChart({
}: {
selectedId: number | null;
}) {
const selectedInput = useSelector(
(state: RootState) => state.selectedAnalogInput
);
const zoomEnabled =
process.env.NEXT_PUBLIC_FEATURE_MESSWERTANZEIGE_EINGANG === "true";
const dispatch = useDispatch<AppDispatch>();
@@ -82,7 +85,9 @@ export default function AnalogInputsChart({
const chartData = {
datasets: [
{
label: `Messkurve Messwerteingang ${selectedId}`,
label: `Messkurve ${selectedInput?.label ?? "Eingang"} (${
selectedInput?.unit ?? ""
})`,
data: inputData.map((point: any) => ({
x: point.t,
y: point.m,
@@ -145,7 +150,7 @@ export default function AnalogInputsChart({
y: {
title: {
display: true,
text: "Messwert",
text: `Messwert (${selectedInput?.unit ?? ""})`,
},
},
},

View File

@@ -18,6 +18,7 @@ import { getAnalogInputsThunk } from "@/redux/thunks/getAnalogInputsThunk";
import { Icon } from "@iconify/react";
import settingsIcon from "@iconify/icons-mdi/settings";
import waveformIcon from "@iconify/icons-mdi/waveform";
import { setSelectedAnalogInput } from "@/redux/slices/selectedAnalogInputSlice";
export default function AnalogInputsTable({
setSelectedId,
@@ -39,9 +40,10 @@ export default function AnalogInputsTable({
(state: RootState) => state.analogInputs ?? []
);
const handleSelect = (id: number) => {
const handleSelect = (id: number, input: AnalogInput) => {
setSelectedId(id);
setActiveId(id);
dispatch(setSelectedAnalogInput(input)); // 🧠 hier kommt die Bezeichnung in Redux
};
return (
@@ -79,7 +81,7 @@ export default function AnalogInputsTable({
>
<td
className="border p-2"
onClick={() => handleSelect(e.id!)}
onClick={() => handleSelect(e.id!, e)}
>
<div className="flex items-center gap-1 ">
<Icon
@@ -91,7 +93,7 @@ export default function AnalogInputsTable({
</td>
<td
className="border p-2 text-right"
onClick={() => handleSelect(e.id!)}
onClick={() => handleSelect(e.id!, e)}
>
{typeof e.value === "number" ? e.value.toFixed(2) : "-"}
</td>
@@ -99,7 +101,7 @@ export default function AnalogInputsTable({
<td className="border p-2">{e.unit || "-"}</td>
<td
className="border p-2"
onClick={() => handleSelect(e.id!)}
onClick={() => handleSelect(e.id!, e)}
>
{e.label || "----"}
</td>
@@ -107,7 +109,7 @@ export default function AnalogInputsTable({
<td className="border p-2 text-center">
<button
onClick={() => {
handleSelect(e.id!);
handleSelect(e.id!, e);
setSelectedInput(e);
setIsSettingsModalOpen(true);
}}

View File

@@ -2,13 +2,15 @@
## 🔄 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)
- [ ] TODO:Messwertanzeige eines Eingangs: Zeitraum einstellbar, Bezeichnung und Einheit in die Messkurve, Datum in die Messkurve
- Einheit in eckigen Klammern und Chart Daten mit Parameter DIA0 usw.
- [ ] FIXME: Kabelüberwachung vertikal angezeigt
- [ ] TODO: Startseite Meldungen: Spalte Quelle wie bei Berichte (nicht "Modul")
- [ ] TODO: Startseite Spaltenreihenfolge: Pop (farbiger Punkt), Zeitstempel, Quelle, Meldung, Status
- [ ] TODO: Berichte Spaltenreihenfolge: Pop (farbiger Punkt), Zeitstempel, Quelle, Meldung, Status
- [ ] TODO: Filter für Quelle
- [ ] TODO: System: Button bei jedem Messwert für Detailansicht (inkl. Kurve im Popup und Zeitauswahl)
- [ ] TODO: Einstellungen Benutzerverwaltung: Admin kann Adminpasswort ändern
- [ ] TODO: Einstellungen OPC: Anzahl der aktuellen Clients (ggf. KAS-Variable einbauen)
![Zusatzfunktionen Kai 25.06.2025](./TODOsScreenshots/Zusatzfunktionen_25-06-2025.png)

4
package-lock.json generated
View File

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

View File

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

View File

@@ -1,4 +1,4 @@
"use client"; ///pages/analogInputs.tsx
"use client"; ///pages/analogeEingaenge.tsx
import React, { useState, useEffect } from "react";
import AnalogInputsTable from "../components/main/analogInputs/AnalogInputsTable";
import AnalogInputsChart from "../components/main/analogInputs/AnalogInputsChart";

View File

@@ -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") || 1; // Rack-Nummer aus URL oder 1
const initialRack = parseInt(searchParams.get("rack")) || 1; // Rack-Nummer aus URL oder 1
const [activeRack, setActiveRack] = useState(initialRack); // Nutze initialRack als Startwert
const [alarmStatus, setAlarmStatus] = useState<boolean[]>([]); // Alarmstatus
const [alarmStatus, setAlarmStatus] = useState([]); // Alarmstatus
// Redux-Variablen aus dem Store abrufen
const {
@@ -25,23 +25,21 @@ function Kabelueberwachung() {
kueResidence,
kueCableBreak,
kueGroundFault,
} = useSelector((state: RootState) => state.kueDataSlice);
} = useSelector((state) => state.kueDataSlice);
//----------------------------------------------------------------
// 🚀 **TDR-Daten bereits in Redux abrufen**
// Redux-Variablen abrufen
const tdrData = useSelector((state: RootState) => state.tdrChartSlice.data);
const loading = useSelector(
(state: RootState) => state.tdrChartSlice.loading
);
const error = useSelector((state: RootState) => state.tdrChartSlice.error);
const tdrData = useSelector((state) => state.tdrChartSlice.data);
const loading = useSelector((state) => state.tdrChartSlice.loading);
const error = useSelector((state) => 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]) ||
@@ -68,8 +66,7 @@ function Kabelueberwachung() {
}));
//console.log("Alle Module:", allModules);
type RackKey = "rack1" | "rack2" | "rack3" | "rack4";
const racks: Record<RackKey, typeof allModules> = {
const racks = {
rack1: allModules.slice(0, 8),
rack2: allModules.slice(8, 16),
rack3: allModules.slice(16, 24),
@@ -95,11 +92,7 @@ function Kabelueberwachung() {
); */
// Funktion zum Wechseln des Racks
interface ChangeRackFn {
(rack: number): void;
}
const changeRack: ChangeRackFn = (rack) => {
const changeRack = (rack) => {
setActiveRack(rack);
console.log(`Aktives Rack geändert zu: ${rack}`);
};
@@ -151,7 +144,8 @@ function Kabelueberwachung() {
</button>
))}
</div>
{racks[`rack${activeRack}` as RackKey].map((slot, index) => {
<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">
@@ -162,12 +156,12 @@ function Kabelueberwachung() {
kueOnline={slot.kueOnlineStatus}
alarmStatus={slot.alarmStatus}
slotIndex={slotIndex}
tdrLocation={[]} // TODO: Replace with actual tdrLocation data if available
/>
</div>
);
})}
</div>
</div>
);
}

View File

@@ -0,0 +1,3 @@
{
"win_last20Messages": "<%=SAM01%>"
}

View File

@@ -0,0 +1,17 @@
{
"win_appVersion": "<%=SAV00%>",
"win_deviceName": "<%=SAN01%>",
"win_mac1": "<%=SEM01%>",
"win_ip": "<%=SEI01%>",
"win_subnet": "<%=SES01%>",
"win_gateway": "<%=SEG01%>",
"win_cplInternalTimestamp": "<%=SCL01%>",
"win_ntp1": "<%=STP01%>",
"win_ntp2": "<%=STP02%>",
"win_ntp3": "<%=STP03%>",
"win_ntpTimezone": "<%=STT00%>",
"win_ntpActive": "<%=STA00%>",
"win_opcState": "<%=SOS01%>",
"win_opcSessions": "<%=SOC01%>",
"win_opcName": "<%=SON01%>"
}

View File

@@ -0,0 +1,20 @@
{
"win_de_state": ["<%=DES80%>", "<%=DES81%>", "<%=DES82%>", "<%=DES83%>"],
"win_de_label": ["<%=DEN80%>", "<%=DEN81%>", "<%=DEN82%>", "<%=DEN83%>"],
"win_de_counter": ["<%=DEC80%>", "<%=DEC81%>", "<%=DEC82%>", "<%=DEC83%>"],
"win_de_time_filter": [
"<%=DEF80%>",
"<%=DEF81%>",
"<%=DEF82%>",
"<%=DEF83%>"
],
"win_de_weighting": ["<%=DEG80%>", "<%=DEG81%>", "<%=DEG82%>", "<%=DEG83%>"],
"win_de_invert": ["<%=DEI80%>", "<%=DEI81%>", "<%=DEI82%>", "<%=DEI83%>"],
"win_de_counter_active": [
"<%=DEZ80%>",
"<%=DEZ81%>",
"<%=DEZ82%>",
"<%=DEZ83%>"
],
"win_de_offline": ["<%=DEA80%>", "<%=DEA81%>", "<%=DEA82%>", "<%=DEA83%>"]
}

View File

@@ -0,0 +1,4 @@
{
"win_da_state": ["<%=DAS01%>", "<%=DAS02%>", "<%=DAS03%>", "<%=DAS04%>"],
"win_da_bezeichnung": ["<%=DAN01%>", "<%=DAN02%>", "<%=DAN03%>", "<%=DAN04%>"]
}

View File

@@ -0,0 +1,42 @@
{
"win_kueOnline": ["<%=KSO80%>", "<%=KSO81%>", "<%=KSO82%>", "<%=KSO83%>"],
"win_kueVersion": ["<%=KSV80%>", "<%=KSV81%>", "<%=KSV82%>", "<%=KSV83%>"],
"win_kuePSTmMinus96V": [
"<%=KSS80%>",
"<%=KSS81%>",
"<%=KSS82%>",
"<%=KSS83%>"
],
"win_kueCableBreak": ["<%=KSC80%>", "<%=KSC81%>", "<%=KSC82%>", "<%=KSC83%>"],
"win_kueGroundFault": [
"<%=KSG80%>",
"<%=KSG81%>",
"<%=KSG82%>",
"<%=KSG83%>"
],
"win_kueOverflow": ["<%=KIW80%>", "<%=KIW81%>", "<%=KIW82%>", "<%=KIW83%>"],
"win_kueIso": ["<%=KIM80%>", "<%=KIM81%>", "<%=KIM82%>", "<%=KIM83%>"],
"win_kueAlarm1": ["<%=KIA80%>", "<%=KIA81%>", "<%=KIA82%>", "<%=KIA83%>"],
"win_kueLimit1": ["<%=KIG80%>", "<%=KIG81%>", "<%=KIG82%>", "<%=KIG83%>"],
"win_kueResidence": ["<%=KRM80%>", "<%=KRM81%>", "<%=KRM82%>", "<%=KRM83%>"],
"win_kueAlarm2": ["<%=KRA80%>", "<%=KRA81%>", "<%=KRA82%>", "<%=KRA83%>"],
"win_kueLimit2Low": ["<%=KRG80%>", "<%=KRG81%>", "<%=KRG82%>", "<%=KRG83%>"],
"win_kueDelay1": ["<%=KID80%>", "<%=KID81%>", "<%=KID82%>", "<%=KID83%>"],
"win_kueLoopInterval": [
"<%=KRI80%>",
"<%=KRI81%>",
"<%=KRI82%>",
"<%=KRI83%>"
],
"win_kueID": ["<%=KSI80%>", "<%=KSI81%>", "<%=KSI82%>", "<%=KSI83%>"],
"win_kueName": ["<%=KSA80%>", "<%=KSA81%>", "<%=KSA82%>", "<%=KSA83%>"],
"win_tdrActive": ["<%=KTX80%>", "<%=KTX81%>", "<%=KTX82%>", "<%=KTX83%>"],
"win_tdrAtten": ["<%=KTD80%>", "<%=KTD81%>", "<%=KTD82%>", "<%=KTD83%>"],
"win_tdrSpeed": ["<%=KTS80%>", "<%=KTS81%>", "<%=KTS82%>", "<%=KTS83%>"],
"win_tdrTrigger": ["<%=KTE80%>", "<%=KTE81%>", "<%=KTE82%>", "<%=KTE83%>"],
"win_tdrPulse": ["<%=KTP80%>", "<%=KTP81%>", "<%=KTP82%>", "<%=KTP83%>"],
"win_tdrAmp": ["<%=KTA80%>", "<%=KTA81%>", "<%=KTA82%>", "<%=KTA83%>"],
"win_tdrLast": ["<%=KTL80%>", "<%=KTL81%>", "<%=KTL82%>", "<%=KTL83%>"],
"win_tdrLocation": ["<%=KTF80%>", "<%=KTF81%>", "<%=KTF82%>", "<%=KTF83%>"],
"win_memoryInterval": ["<%=KIL80%>", "<%=KIL81%>", "<%=KIL82%>", "<%=KIL83%>"]
}

View File

@@ -0,0 +1,50 @@
{
"kueNodeID": [
"<%=KN001%>",
"<%=KN101%>",
"<%=KN201%>",
"<%=KN301%>",
"<%=KN401%>",
"<%=KN501%>",
"<%=KN601%>",
"<%=KN701%>",
"<%=KN801%>",
"<%=KN901%>"
],
"kueLinkID": [
"<%=KP001%>",
"<%=KP101%>",
"<%=KP201%>",
"<%=KP301%>",
"<%=KP401%>",
"<%=KP501%>",
"<%=KP601%>",
"<%=KP701%>",
"<%=KP801%>",
"<%=KP901%>"
],
"kueLinkLength": [
"<%=KQ001%>",
"<%=KQ101%>",
"<%=KQ201%>",
"<%=KQ301%>",
"<%=KQ401%>",
"<%=KQ501%>",
"<%=KQ601%>",
"<%=KQ701%>",
"<%=KQ801%>",
"<%=KQ901%>"
],
"kueLinkStatus": [
"<%=KU001%>",
"<%=KU101%>",
"<%=KU201%>",
"<%=KU301%>",
"<%=KU401%>",
"<%=KU501%>",
"<%=KU601%>",
"<%=KU701%>",
"<%=KU801%>",
"<%=KU901%>"
]
}

View File

@@ -0,0 +1,5 @@
{
"win_opcUaZustand": "<%=SOS01%>",
"win_opcUaActiveClientCount": "<%=SOC01%>",
"win_opcUaNodesetName": "<%=SON01%>"
}

View File

@@ -0,0 +1,3 @@
{
"win_systemVoltTemp": ["AAV09", "AAV11", "AAV15", "AAV16", "AAV17", "AAV18"]
}

BIN
redux/slices.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,35 @@
// /redux/slices/selectedAnalogInputSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
export interface SelectedAnalogInput {
id: number;
label: string;
unit?: string;
value?: number;
offset?: number;
factor?: number;
loggerInterval?: number;
weighting?: number;
}
const initialState: SelectedAnalogInput | null = null;
const selectedAnalogInputSlice = createSlice({
name: "selectedAnalogInput",
initialState,
reducers: {
setSelectedAnalogInput: (
state,
action: PayloadAction<SelectedAnalogInput>
) => {
return action.payload;
},
clearSelectedAnalogInput: () => {
return null;
},
},
});
export const { setSelectedAnalogInput, clearSelectedAnalogInput } =
selectedAnalogInputSlice.actions;
export default selectedAnalogInputSlice.reducer;

View File

@@ -24,6 +24,7 @@ import tdrReferenceChartDataBySlotReducer from "./slices/tdrReferenceChartDataBy
import loopChartTypeSlice from "./slices/loopChartTypeSlice";
import systemVoltTempReducer from "./slices/systemVoltTempSlice";
import analogInputsHistoryReducer from "./slices/analogInputsHistorySlice";
import selectedAnalogInputReducer from "./slices/selectedAnalogInputSlice";
const store = configureStore({
reducer: {
@@ -50,6 +51,7 @@ const store = configureStore({
loopChartType: loopChartTypeSlice,
systemVoltTemp: systemVoltTempReducer,
analogInputsHistory: analogInputsHistoryReducer,
selectedAnalogInput: selectedAnalogInputReducer,
},
});

BIN
redux/thunks.zip Normal file

Binary file not shown.

BIN
redux/types.zip Normal file

Binary file not shown.