diff --git a/.env.development b/.env.development index 080ef19..36210b8 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.865 +NEXT_PUBLIC_APP_VERSION=1.6.866 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 75272ca..0c3c00d 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.865 +NEXT_PUBLIC_APP_VERSION=1.6.866 NEXT_PUBLIC_CPL_MODE=production \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0164667..d14af0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [1.6.866] – 2025-09-08 + +- Test: Jenkinsfile + +--- ## [1.6.865] – 2025-09-08 - test: Jenkinsfile diff --git a/components/common/DeviceEventsBridge.tsx b/components/common/DeviceEventsBridge.tsx index 2a8bc87..961c68c 100644 --- a/components/common/DeviceEventsBridge.tsx +++ b/components/common/DeviceEventsBridge.tsx @@ -7,7 +7,7 @@ declare global { interface Window { loopMeasurementEvent?: number[]; tdrMeasurementEvent?: number[]; - alignmentEvent?: number[]; + comparisonEvent?: number[]; // renamed from alignmentEvent } } @@ -25,8 +25,8 @@ export default function DeviceEventsBridge() { const ksy = Array.isArray(window.tdrMeasurementEvent) ? window.tdrMeasurementEvent : undefined; - const ksz = Array.isArray(window.alignmentEvent) - ? window.alignmentEvent + const ksz = Array.isArray(window.comparisonEvent) + ? window.comparisonEvent : undefined; // Build a stable signature of first 32 values per array const to32 = (a?: number[]) => { diff --git a/components/common/GlobalActivityOverlay.tsx b/components/common/GlobalActivityOverlay.tsx index 512d47e..d888310 100644 --- a/components/common/GlobalActivityOverlay.tsx +++ b/components/common/GlobalActivityOverlay.tsx @@ -5,14 +5,14 @@ import { useAppSelector } from "@/redux/store"; export default function GlobalActivityOverlay() { const anyLoop = useAppSelector((s) => s.deviceEvents.anyLoopActive); const anyTdr = useAppSelector((s) => s.deviceEvents.anyTdrActive); - const anyAlign = useAppSelector((s) => s.deviceEvents.anyAlignmentActive); + const anyCompare = useAppSelector((s) => s.deviceEvents.anyComparisonActive); const ksx = useAppSelector((s) => s.deviceEvents.ksx); const ksy = useAppSelector((s) => s.deviceEvents.ksy); const ksz = useAppSelector((s) => s.deviceEvents.ksz); const loopStartedAt = useAppSelector((s) => s.deviceEvents.loopStartedAt); const tdrStartedAt = useAppSelector((s) => s.deviceEvents.tdrStartedAt); - const alignmentStartedAt = useAppSelector( - (s) => s.deviceEvents.alignmentStartedAt + const comparisonStartedAt = useAppSelector( + (s) => s.deviceEvents.comparisonStartedAt ); const fmt = (arr: number[]) => @@ -24,13 +24,13 @@ export default function GlobalActivityOverlay() { // Simple 1s ticker so progress bars advance while overlay is shown const [now, setNow] = useState(Date.now()); useEffect(() => { - const active = anyLoop || anyTdr || anyAlign; + const active = anyLoop || anyTdr || anyCompare; if (!active) return; const id = setInterval(() => setNow(Date.now()), 1000); return () => clearInterval(id); - }, [anyLoop, anyTdr, anyAlign]); + }, [anyLoop, anyTdr, anyCompare]); - const active = anyLoop || anyTdr || anyAlign; + const active = anyLoop || anyTdr || anyCompare; if (!active) return null; const clamp = (v: number, min = 0, max = 1) => @@ -102,13 +102,13 @@ export default function GlobalActivityOverlay() { )} - {anyAlign && ( + {anyCompare && (
- Abgleich läuft… (KÜ: {fmt(ksz)}) kann bis zu 10 Minuten dauern + Comparison läuft… (KÜ: {fmt(ksz)}) kann bis zu 10 Minuten dauern
{(() => { - const { pct } = compute(alignmentStartedAt, ALIGN_MS); + const { pct } = compute(comparisonStartedAt, ALIGN_MS); return (
diff --git a/components/main/kabelueberwachung/kue705FO/SlotActivityOverlay.tsx b/components/main/kabelueberwachung/kue705FO/SlotActivityOverlay.tsx index 598df8e..f3f577d 100644 --- a/components/main/kabelueberwachung/kue705FO/SlotActivityOverlay.tsx +++ b/components/main/kabelueberwachung/kue705FO/SlotActivityOverlay.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useState } from "react"; -import { useAppSelector } from "@/redux/store"; +import { useAppSelector, useAppDispatch } from "@/redux/store"; +import { initPersistedTimings } from "@/redux/slices/deviceEventsSlice"; export default function SlotActivityOverlay({ slotIndex, @@ -12,22 +13,69 @@ export default function SlotActivityOverlay({ const ksz = useAppSelector((s) => s.deviceEvents.ksz); const loopStartedAt = useAppSelector((s) => s.deviceEvents.loopStartedAt); const tdrStartedAt = useAppSelector((s) => s.deviceEvents.tdrStartedAt); - const alignmentStartedAt = useAppSelector( - (s) => s.deviceEvents.alignmentStartedAt + const comparisonStartedAt = useAppSelector( + (s) => s.deviceEvents.comparisonStartedAt ); + const loopStartedAtBySlot = useAppSelector( + (s) => s.deviceEvents.loopStartedAtBySlot + ); + const tdrStartedAtBySlot = useAppSelector( + (s) => s.deviceEvents.tdrStartedAtBySlot + ); + const comparisonStartedAtBySlot = useAppSelector( + (s) => s.deviceEvents.comparisonStartedAtBySlot + ); + const dispatch = useAppDispatch(); const loopActive = Array.isArray(ksx) && ksx[slotIndex] === 1; const tdrActive = Array.isArray(ksy) && ksy[slotIndex] === 1; - const alignActive = Array.isArray(ksz) && ksz[slotIndex] === 1; + const comparisonActive = Array.isArray(ksz) && ksz[slotIndex] === 1; + + // Load persisted timings only once (on mount) + useEffect(() => { + try { + const raw = localStorage.getItem("deviceEventsTimingsV1"); + if (raw) { + const parsed = JSON.parse(raw); + dispatch( + initPersistedTimings({ + loop: parsed.loop, + tdr: parsed.tdr, + compare: parsed.align || parsed.compare, + }) + ); + } + } catch (e) { + // eslint-disable-next-line no-console + console.warn("Failed to load persisted timings", e); + } + }, [dispatch]); + + // Persist whenever arrays change + useEffect(() => { + try { + localStorage.setItem( + "deviceEventsTimingsV1", + JSON.stringify({ + loop: loopStartedAtBySlot, + tdr: tdrStartedAtBySlot, + compare: comparisonStartedAtBySlot, + }) + ); + } catch (e) { + // eslint-disable-next-line no-console + console.warn("Failed to persist timings", e); + } + }, [loopStartedAtBySlot, tdrStartedAtBySlot, comparisonStartedAtBySlot]); // Progress ticker const [now, setNow] = useState(Date.now()); useEffect(() => { - const any = loopActive || tdrActive || alignActive; + const any = loopActive || tdrActive || comparisonActive; if (!any) return; const id = setInterval(() => setNow(Date.now()), 1000); return () => clearInterval(id); - }, [loopActive, tdrActive, alignActive]); + }, [loopActive, tdrActive, comparisonActive]); const clamp = (v: number, min = 0, max = 1) => Math.max(min, Math.min(max, v)); @@ -43,7 +91,7 @@ export default function SlotActivityOverlay({ const TDR_MS = 30 * 1000; // ~30 s const ALIGN_MS = 10 * 60 * 1000; // ~10 min - if (!loopActive && !tdrActive && !alignActive) return null; + if (!loopActive && !tdrActive && !comparisonActive) return null; return (
@@ -56,7 +104,8 @@ export default function SlotActivityOverlay({
Schleife
{(() => { - const { pct } = compute(loopStartedAt, LOOP_MS); + const started = loopStartedAtBySlot[slotIndex] ?? loopStartedAt; + const { pct } = compute(started, LOOP_MS); return (
@@ -77,7 +126,8 @@ export default function SlotActivityOverlay({
TDR
{(() => { - const { pct } = compute(tdrStartedAt, TDR_MS); + const started = tdrStartedAtBySlot[slotIndex] ?? tdrStartedAt; + const { pct } = compute(started, TDR_MS); return (
@@ -94,11 +144,13 @@ export default function SlotActivityOverlay({ })()}
)} - {alignActive && ( + {comparisonActive && (
Abgleich
{(() => { - const { pct } = compute(alignmentStartedAt, ALIGN_MS); + const started = + comparisonStartedAtBySlot[slotIndex] ?? comparisonStartedAt; + const { pct } = compute(started, ALIGN_MS); return (
diff --git a/mocks/device-cgi-simulator/SERVICE/kabelueberwachungMockData.js b/mocks/device-cgi-simulator/SERVICE/kabelueberwachungMockData.js index b6538ee..8cf9573 100644 --- a/mocks/device-cgi-simulator/SERVICE/kabelueberwachungMockData.js +++ b/mocks/device-cgi-simulator/SERVICE/kabelueberwachungMockData.js @@ -269,7 +269,12 @@ var tdrMeasurementEvent = [ 0, 0, 0, 0, 0, 0, ]; //Event Abgleich -var alignmentEvent = [ - 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 comparisonEvent = [ + // renamed from alignmentEvent + 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, ]; +// expose for browser simulation +if (typeof window !== "undefined") { + window.comparisonEvent = comparisonEvent; +} diff --git a/package-lock.json b/package-lock.json index 16176d0..879db1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cpl-v4", - "version": "1.6.865", + "version": "1.6.866", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cpl-v4", - "version": "1.6.865", + "version": "1.6.866", "dependencies": { "@fontsource/roboto": "^5.1.0", "@headlessui/react": "^2.2.4", diff --git a/package.json b/package.json index 800d36b..061c3d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cpl-v4", - "version": "1.6.865", + "version": "1.6.866", "private": true, "scripts": { "dev": "next dev -p 3000", diff --git a/public/CPL/SERVICE/kueData.js b/public/CPL/SERVICE/kueData.js index cc50767..8d3c74b 100644 --- a/public/CPL/SERVICE/kueData.js +++ b/public/CPL/SERVICE/kueData.js @@ -35,8 +35,8 @@ var win_tdrLocation=[<%=KTF80%>,<%=KTF81%>,<%=KTF82%>,<%=KTF83%>];//Entfernung B var loopMeasurementEvent=[<%=KSX80%>, <%=KSX81%>, <%=KSX82%>, <%=KSX83%>]; //Event TDR-Messung var tdrMeasurementEvent=[<%=KSY80%>, <%=KSY81%>, <%=KSY82%>, <%=KSY83%>]; -//Event Abgleich -var alignmentEvent=[<%=KSZ80%>, <%=KSZ81%>, <%=KSZ82%>, <%=KSZ83%>]; +//Event Comparison (ehem. Abgleich) +var comparisonEvent=[<%=KSZ80%>, <%=KSZ81%>, <%=KSZ82%>, <%=KSZ83%>]; //--------------------------------------------------- diff --git a/redux/slices/deviceEventsSlice.ts b/redux/slices/deviceEventsSlice.ts index 4e35c24..c677fd0 100644 --- a/redux/slices/deviceEventsSlice.ts +++ b/redux/slices/deviceEventsSlice.ts @@ -3,13 +3,17 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; export interface DeviceEventsState { ksx: number[]; // 32 Slots: Schleifenmessung aktiv ksy: number[]; // 32 Slots: TDR-Messung aktiv - ksz: number[]; // 32 Slots: Abgleich aktiv + ksz: number[]; // 32 Slots: Comparison (ehem. Abgleich) aktiv anyLoopActive: boolean; anyTdrActive: boolean; - anyAlignmentActive: boolean; + anyComparisonActive: boolean; // renamed from anyAlignmentActive 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 + comparisonStartedAt: number | null; // renamed from alignmentStartedAt + // per-slot start timestamps (persistable) + loopStartedAtBySlot: (number | null)[]; + tdrStartedAtBySlot: (number | null)[]; + comparisonStartedAtBySlot: (number | null)[]; // renamed from alignmentStartedAtBySlot } const ZERO32 = Array.from({ length: 32 }, () => 0); @@ -20,10 +24,13 @@ const initialState: DeviceEventsState = { ksz: ZERO32.slice(), anyLoopActive: false, anyTdrActive: false, - anyAlignmentActive: false, + anyComparisonActive: false, loopStartedAt: null, tdrStartedAt: null, - alignmentStartedAt: null, + comparisonStartedAt: null, + loopStartedAtBySlot: Array.from({ length: 32 }, () => null), + tdrStartedAtBySlot: Array.from({ length: 32 }, () => null), + comparisonStartedAtBySlot: Array.from({ length: 32 }, () => null), }; export const deviceEventsSlice = createSlice({ @@ -36,7 +43,10 @@ export const deviceEventsSlice = createSlice({ ) { const prevLoop = state.anyLoopActive; const prevTdr = state.anyTdrActive; - const prevAlign = state.anyAlignmentActive; + const prevCompare = state.anyComparisonActive; + const prevKsx = state.ksx.slice(); + const prevKsy = state.ksy.slice(); + const prevKsz = state.ksz.slice(); const to32 = (arr?: number[]) => { if (!Array.isArray(arr)) return ZERO32.slice(); const a = arr @@ -50,17 +60,36 @@ export const deviceEventsSlice = createSlice({ state.ksz = to32(action.payload.ksz); state.anyLoopActive = state.ksx.some((v) => v === 1); state.anyTdrActive = state.ksy.some((v) => v === 1); - state.anyAlignmentActive = state.ksz.some((v) => v === 1); + state.anyComparisonActive = state.ksz.some((v) => v === 1); - // Transition detection to set/reset startedAt timestamps + // Global transition detection 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; + if (!prevCompare && state.anyComparisonActive) + state.comparisonStartedAt = Date.now(); + if (prevCompare && !state.anyComparisonActive) + state.comparisonStartedAt = null; + + // Per-slot transition detection + for (let i = 0; i < 32; i++) { + if (prevKsx[i] === 0 && state.ksx[i] === 1) { + state.loopStartedAtBySlot[i] = Date.now(); + } else if (prevKsx[i] === 1 && state.ksx[i] === 0) { + state.loopStartedAtBySlot[i] = null; + } + if (prevKsy[i] === 0 && state.ksy[i] === 1) { + state.tdrStartedAtBySlot[i] = Date.now(); + } else if (prevKsy[i] === 1 && state.ksy[i] === 0) { + state.tdrStartedAtBySlot[i] = null; + } + if (prevKsz[i] === 0 && state.ksz[i] === 1) { + state.comparisonStartedAtBySlot[i] = Date.now(); + } else if (prevKsz[i] === 1 && state.ksz[i] === 0) { + state.comparisonStartedAtBySlot[i] = null; + } + } }, resetEvents(state) { state.ksx = ZERO32.slice(); @@ -68,13 +97,39 @@ export const deviceEventsSlice = createSlice({ state.ksz = ZERO32.slice(); state.anyLoopActive = false; state.anyTdrActive = false; - state.anyAlignmentActive = false; + state.anyComparisonActive = false; state.loopStartedAt = null; state.tdrStartedAt = null; - state.alignmentStartedAt = null; + state.comparisonStartedAt = null; + state.loopStartedAtBySlot = Array.from({ length: 32 }, () => null); + state.tdrStartedAtBySlot = Array.from({ length: 32 }, () => null); + state.comparisonStartedAtBySlot = Array.from({ length: 32 }, () => null); + }, + initPersistedTimings( + state, + action: PayloadAction<{ + loop?: (number | null)[]; + tdr?: (number | null)[]; + compare?: (number | null)[]; // renamed key + }> + ) { + const normalize = (arr?: (number | null)[]) => { + const out: (number | null)[] = Array.isArray(arr) + ? arr.slice(0, 32).map((v) => (typeof v === "number" ? v : null)) + : []; + while (out.length < 32) out.push(null); + return out; + }; + if (action.payload.loop) + state.loopStartedAtBySlot = normalize(action.payload.loop); + if (action.payload.tdr) + state.tdrStartedAtBySlot = normalize(action.payload.tdr); + if (action.payload.compare) + state.comparisonStartedAtBySlot = normalize(action.payload.compare); }, }, }); -export const { setEvents, resetEvents } = deviceEventsSlice.actions; +export const { setEvents, resetEvents, initPersistedTimings } = + deviceEventsSlice.actions; export default deviceEventsSlice.reducer;