WIP: Timer für jeder KÜ separate und nicht eine für alle, aktuell wird prozentzahl bei allen das gleiche angezeigt

This commit is contained in:
ISA
2025-09-08 10:41:46 +02:00
parent d38d3191c5
commit 9c218b2a1d
11 changed files with 164 additions and 47 deletions

View File

@@ -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)

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.865
NEXT_PUBLIC_APP_VERSION=1.6.866
NEXT_PUBLIC_CPL_MODE=production

View File

@@ -1,3 +1,8 @@
## [1.6.866] 2025-09-08
- Test: Jenkinsfile
---
## [1.6.865] 2025-09-08
- test: Jenkinsfile

View File

@@ -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[]) => {

View File

@@ -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<number>(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() {
</div>
)}
{anyAlign && (
{anyCompare && (
<div>
<div className="text-sm text-gray-800 mb-1">
Abgleich läuft (: {fmt(ksz)}) kann bis zu 10 Minuten dauern
Comparison läuft (: {fmt(ksz)}) kann bis zu 10 Minuten dauern
</div>
{(() => {
const { pct } = compute(alignmentStartedAt, ALIGN_MS);
const { pct } = compute(comparisonStartedAt, ALIGN_MS);
return (
<div>
<div className="h-2 w-full bg-gray-200 rounded overflow-hidden">

View File

@@ -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<number>(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 (
<div className="absolute inset-0 z-20 flex items-center justify-center bg-white/70 backdrop-blur-sm">
@@ -56,7 +104,8 @@ export default function SlotActivityOverlay({
<div>
<div className="text-[0.7rem] text-gray-800 mb-1">Schleife</div>
{(() => {
const { pct } = compute(loopStartedAt, LOOP_MS);
const started = loopStartedAtBySlot[slotIndex] ?? loopStartedAt;
const { pct } = compute(started, LOOP_MS);
return (
<div>
<div className="h-2 w-full bg-gray-200 rounded overflow-hidden">
@@ -77,7 +126,8 @@ export default function SlotActivityOverlay({
<div>
<div className="text-[0.7rem] text-gray-800 mb-1">TDR</div>
{(() => {
const { pct } = compute(tdrStartedAt, TDR_MS);
const started = tdrStartedAtBySlot[slotIndex] ?? tdrStartedAt;
const { pct } = compute(started, TDR_MS);
return (
<div>
<div className="h-2 w-full bg-gray-200 rounded overflow-hidden">
@@ -94,11 +144,13 @@ export default function SlotActivityOverlay({
})()}
</div>
)}
{alignActive && (
{comparisonActive && (
<div>
<div className="text-[0.7rem] text-gray-800 mb-1">Abgleich</div>
{(() => {
const { pct } = compute(alignmentStartedAt, ALIGN_MS);
const started =
comparisonStartedAtBySlot[slotIndex] ?? comparisonStartedAt;
const { pct } = compute(started, ALIGN_MS);
return (
<div>
<div className="h-2 w-full bg-gray-200 rounded overflow-hidden">

View File

@@ -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;
}

4
package-lock.json generated
View File

@@ -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",

View File

@@ -1,6 +1,6 @@
{
"name": "cpl-v4",
"version": "1.6.865",
"version": "1.6.866",
"private": true,
"scripts": {
"dev": "next dev -p 3000",

View File

@@ -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%>];
//---------------------------------------------------

View File

@@ -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;