diff --git a/.env.development b/.env.development index a1c73bb..ed51f7b 100644 --- a/.env.development +++ b/.env.development @@ -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.707 +NEXT_PUBLIC_APP_VERSION=1.6.708 NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter) diff --git a/.env.production b/.env.production index 64a0ae3..c8f865a 100644 --- a/.env.production +++ b/.env.production @@ -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.707 +NEXT_PUBLIC_APP_VERSION=1.6.708 NEXT_PUBLIC_CPL_MODE=production \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ba20c49..4e68cb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [1.6.708] – 2025-08-13 + +- feat: Slot Nummer anzeigen bei Events + +--- ## [1.6.707] – 2025-08-13 - feat: Meldung für Events darstellen (Kalibrierung, TDR ud Schleifenmessung) diff --git a/components/common/GlobalActivityOverlay.tsx b/components/common/GlobalActivityOverlay.tsx index 8bca54c..6ca7797 100644 --- a/components/common/GlobalActivityOverlay.tsx +++ b/components/common/GlobalActivityOverlay.tsx @@ -1,5 +1,5 @@ "use client"; -import React from "react"; +import React, { useEffect, useState } from "react"; import { useAppSelector } from "@/redux/store"; export default function GlobalActivityOverlay() { @@ -9,9 +9,11 @@ export default function GlobalActivityOverlay() { const ksx = useAppSelector((s) => s.deviceEvents.ksx); const ksy = useAppSelector((s) => s.deviceEvents.ksy); const ksz = useAppSelector((s) => s.deviceEvents.ksz); - - const active = anyLoop || anyTdr || anyAlign; - if (!active) return null; + const loopStartedAt = useAppSelector((s) => s.deviceEvents.loopStartedAt); + const tdrStartedAt = useAppSelector((s) => s.deviceEvents.tdrStartedAt); + const alignmentStartedAt = useAppSelector( + (s) => s.deviceEvents.alignmentStartedAt + ); const fmt = (arr: number[]) => arr @@ -19,16 +21,121 @@ export default function GlobalActivityOverlay() { .filter((n) => n !== 0) .join(", "); - const messages: string[] = []; - if (anyLoop) messages.push(`Schleifenmessung läuft… (KÜ: ${fmt(ksx)})`); - if (anyTdr) messages.push(`TDR-Messung läuft… (KÜ: ${fmt(ksy)})`); - if (anyAlign) messages.push(`Abgleich läuft… (KÜ: ${fmt(ksz)})`); + // Simple 1s ticker so progress bars advance while overlay is shown + const [now, setNow] = useState(Date.now()); + useEffect(() => { + const active = anyLoop || anyTdr || anyAlign; + if (!active) return; + const id = setInterval(() => setNow(Date.now()), 1000); + return () => clearInterval(id); + }, [anyLoop, anyTdr, anyAlign]); + + const active = anyLoop || anyTdr || anyAlign; + if (!active) return null; + + const clamp = (v: number, min = 0, max = 1) => + Math.max(min, Math.min(max, v)); + const formatEta = (ms: number) => { + if (ms <= 0) return "gleich fertig"; + const totalSec = Math.ceil(ms / 1000); + const m = Math.floor(totalSec / 60); + const s = totalSec % 60; + return m > 0 ? `${m}:${s.toString().padStart(2, "0")} min` : `${s} s`; + }; + + const compute = (startedAt: number | null, durationMs: number) => { + if (!startedAt) return { pct: 0, remaining: durationMs }; + const elapsed = now - startedAt; + const pct = clamp(elapsed / durationMs) * 100; + const remaining = Math.max(0, durationMs - Math.max(0, elapsed)); + return { pct, remaining }; + }; + + // Durations + const LOOP_MS = 2 * 60 * 1000; // ~2 min + const TDR_MS = 30 * 1000; // ~30 s + const ALIGN_MS = 10 * 60 * 1000; // ~10 min return (
-
-
Bitte warten…
-
{messages.join(" · ")}
+
+
Bitte warten…
+
+ {anyLoop && ( +
+
+ Schleifenmessung läuft… (KÜ: {fmt(ksx)}) +
+ {(() => { + const { pct, remaining } = compute(loopStartedAt, LOOP_MS); + return ( +
+
+
+
+
+ {Math.round(pct)}% · ~{formatEta(remaining)} +
+
+ ); + })()} +
+ )} + + {anyTdr && ( +
+
+ TDR-Messung läuft… (KÜ: {fmt(ksy)}) +
+ {(() => { + const { pct, remaining } = compute(tdrStartedAt, TDR_MS); + return ( +
+
+
+
+
+ {Math.round(pct)}% · ~{formatEta(remaining)} +
+
+ ); + })()} +
+ )} + + {anyAlign && ( +
+
+ Abgleich läuft… (KÜ: {fmt(ksz)}) +
+ {(() => { + const { pct, remaining } = compute( + alignmentStartedAt, + ALIGN_MS + ); + return ( +
+
+
+
+
+ {Math.round(pct)}% · ~{formatEta(remaining)} +
+
+ ); + })()} +
+ )} +
); diff --git a/docs/TODO.md b/docs/TODO.md index b0750fe..3ac27ec 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -69,3 +69,5 @@ in Rot, wenn Schleifenfehler ansteht - [ ] TODO: Abgleich Dauer entsprechend progress balken einbauen - [ ] TODO: Benutzer passwort ändern - [ ] TODO: PlayWright +- ISO Abgleich 10 Minuten +- diff --git a/mocks/device-cgi-simulator/SERVICE/kabelueberwachungMockData.js b/mocks/device-cgi-simulator/SERVICE/kabelueberwachungMockData.js index 4252ed5..8ef859f 100644 --- a/mocks/device-cgi-simulator/SERVICE/kabelueberwachungMockData.js +++ b/mocks/device-cgi-simulator/SERVICE/kabelueberwachungMockData.js @@ -260,7 +260,7 @@ var win_fallSensors = [ // Event Schleifenmessung KSX var loopMeasurementEvent = [ - 0, 0, 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, 0, 0, ]; //Event TDR-Messung diff --git a/package-lock.json b/package-lock.json index f915c51..4b0c6c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cpl-v4", - "version": "1.6.707", + "version": "1.6.708", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cpl-v4", - "version": "1.6.707", + "version": "1.6.708", "dependencies": { "@fontsource/roboto": "^5.1.0", "@headlessui/react": "^2.2.4", diff --git a/package.json b/package.json index 321eb12..889a347 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cpl-v4", - "version": "1.6.707", + "version": "1.6.708", "private": true, "scripts": { "dev": "next dev", diff --git a/redux/slices/deviceEventsSlice.ts b/redux/slices/deviceEventsSlice.ts index 5639609..4e35c24 100644 --- a/redux/slices/deviceEventsSlice.ts +++ b/redux/slices/deviceEventsSlice.ts @@ -7,6 +7,9 @@ export interface DeviceEventsState { anyLoopActive: boolean; anyTdrActive: boolean; anyAlignmentActive: boolean; + loopStartedAt: number | null; // unix ms timestamp when KSX became active + tdrStartedAt: number | null; // unix ms timestamp when KSY became active + alignmentStartedAt: number | null; // unix ms timestamp when KSZ became active } const ZERO32 = Array.from({ length: 32 }, () => 0); @@ -18,6 +21,9 @@ const initialState: DeviceEventsState = { anyLoopActive: false, anyTdrActive: false, anyAlignmentActive: false, + loopStartedAt: null, + tdrStartedAt: null, + alignmentStartedAt: null, }; export const deviceEventsSlice = createSlice({ @@ -28,6 +34,9 @@ export const deviceEventsSlice = createSlice({ state, action: PayloadAction<{ ksx?: number[]; ksy?: number[]; ksz?: number[] }> ) { + const prevLoop = state.anyLoopActive; + const prevTdr = state.anyTdrActive; + const prevAlign = state.anyAlignmentActive; const to32 = (arr?: number[]) => { if (!Array.isArray(arr)) return ZERO32.slice(); const a = arr @@ -42,6 +51,16 @@ export const deviceEventsSlice = createSlice({ state.anyLoopActive = state.ksx.some((v) => v === 1); state.anyTdrActive = state.ksy.some((v) => v === 1); state.anyAlignmentActive = state.ksz.some((v) => v === 1); + + // Transition detection to set/reset startedAt timestamps + if (!prevLoop && state.anyLoopActive) state.loopStartedAt = Date.now(); + if (prevLoop && !state.anyLoopActive) state.loopStartedAt = null; + if (!prevTdr && state.anyTdrActive) state.tdrStartedAt = Date.now(); + if (prevTdr && !state.anyTdrActive) state.tdrStartedAt = null; + if (!prevAlign && state.anyAlignmentActive) + state.alignmentStartedAt = Date.now(); + if (prevAlign && !state.anyAlignmentActive) + state.alignmentStartedAt = null; }, resetEvents(state) { state.ksx = ZERO32.slice(); @@ -50,6 +69,9 @@ export const deviceEventsSlice = createSlice({ state.anyLoopActive = false; state.anyTdrActive = false; state.anyAlignmentActive = false; + state.loopStartedAt = null; + state.tdrStartedAt = null; + state.alignmentStartedAt = null; }, }, });