From f231d87ae354a720bb5fee1132124e243fea0ffe Mon Sep 17 00:00:00 2001 From: mdares Date: Sun, 11 Jan 2026 22:07:01 +0000 Subject: [PATCH] Backup --- .../[machineId]/MachineDetailClient.tsx | 32 ++++++++++ app/api/machines/[machineId]/route.ts | 64 +++++++++++++------ 2 files changed, 78 insertions(+), 18 deletions(-) diff --git a/app/(app)/machines/[machineId]/MachineDetailClient.tsx b/app/(app)/machines/[machineId]/MachineDetailClient.tsx index ae26d13..cfbc8d9 100644 --- a/app/(app)/machines/[machineId]/MachineDetailClient.tsx +++ b/app/(app)/machines/[machineId]/MachineDetailClient.tsx @@ -90,6 +90,13 @@ type TimelineSeg = { state: TimelineState; }; +type ActiveStoppage = { + state: "microstop" | "macrostop"; + startedAt: string; + durationSec: number; + theoreticalCycleTime: number; +}; + type UploadState = { status: "idle" | "parsing" | "uploading" | "success" | "error"; message?: string; @@ -246,9 +253,12 @@ export default function MachineDetailClient() { const [error, setError] = useState(null); const [cycles, setCycles] = useState([]); const [thresholds, setThresholds] = useState(null); + const [activeStoppage, setActiveStoppage] = useState(null); const [open, setOpen] = useState(null); const fileInputRef = useRef(null); const [uploadState, setUploadState] = useState({ status: "idle" }); + const [nowMs, setNowMs] = useState(() => Date.now()); + const BUCKET = { normal: { @@ -308,6 +318,7 @@ export default function MachineDetailClient() { setEvents(json.events ?? []); setCycles(json.cycles ?? []); setThresholds(json.thresholds ?? null); + setActiveStoppage(json.activeStoppage ?? null); setError(null); setLoading(false); } catch { @@ -325,6 +336,11 @@ export default function MachineDetailClient() { }; }, [machineId, t]); + useEffect(() => { + const timer = setInterval(() => setNowMs(Date.now()), 1000); + return () => clearInterval(timer); + }, []); + async function parseWorkOrdersFile(file: File) { const name = file.name.toLowerCase(); if (name.endsWith(".csv")) { @@ -430,6 +446,20 @@ export default function MachineDetailClient() { return `${v}`; } + function formatDurationShort(totalSec?: number | null) { + if (totalSec === null || totalSec === undefined || Number.isNaN(totalSec)) { + return t("common.na"); + } + const sec = Math.max(0, Math.floor(totalSec)); + const h = Math.floor(sec / 3600); + const m = Math.floor((sec % 3600) / 60); + const s = sec % 60; + if (h > 0) return `${h}h ${m}m`; + if (m > 0) return `${m}m ${s}s`; + return `${s}s`; + } + + function timeAgo(ts?: string) { if (!ts) return t("common.never"); const diff = Math.floor((Date.now() - new Date(ts).getTime()) / 1000); @@ -815,6 +845,8 @@ export default function MachineDetailClient() { }); } + + return { windowSec, segments: segs, start, end }; }, [cycles, cycleTarget, thresholds]); diff --git a/app/api/machines/[machineId]/route.ts b/app/api/machines/[machineId]/route.ts index aff17ce..73e9a1b 100644 --- a/app/api/machines/[machineId]/route.ts +++ b/app/api/machines/[machineId]/route.ts @@ -312,6 +312,34 @@ const rawCycles = await prisma.machineCycle.findMany({ sku: true, }, }); +const latestCycle = rawCycles[0] ?? null; + +let activeStoppage: { + state: "microstop" | "macrostop"; + startedAt: string; + durationSec: number; + theoreticalCycleTime: number; +} | null = null; + +if (latestCycle?.ts && effectiveCycleTime && effectiveCycleTime > 0) { + const elapsedSec = (Date.now() - latestCycle.ts.getTime()) / 1000; + const microThresholdSec = effectiveCycleTime * microMultiplier; + const macroThresholdSec = effectiveCycleTime * macroMultiplier; + + if (elapsedSec >= microThresholdSec) { + const isMacro = elapsedSec >= macroThresholdSec; + const state = isMacro ? "macrostop" : "microstop"; + const thresholdSec = isMacro ? macroThresholdSec : microThresholdSec; + const startedAtMs = latestCycle.ts.getTime() + thresholdSec * 1000; + + activeStoppage = { + state, + startedAt: new Date(startedAtMs).toISOString(), + durationSec: Math.max(0, Math.floor(elapsedSec - thresholdSec)), + theoreticalCycleTime: effectiveCycleTime, + }; + } +} // chart-friendly: oldest -> newest + numeric timestamps const cycles = rawCycles @@ -331,23 +359,23 @@ const cycles = rawCycles return NextResponse.json({ - ok: true, - machine: { - id: machine.id, - name: machine.name, - code: machine.code, - location: machine.location, - latestHeartbeat: machine.heartbeats[0] ?? null, - latestKpi: machine.kpiSnapshots[0] ?? null, - effectiveCycleTime, - - }, - thresholds: { - stoppageMultiplier: microMultiplier, - macroStoppageMultiplier: macroMultiplier, - }, - events, - cycles - }); + ok: true, + machine: { + id: machine.id, + name: machine.name, + code: machine.code, + location: machine.location, + latestHeartbeat: machine.heartbeats[0] ?? null, + latestKpi: machine.kpiSnapshots[0] ?? null, + effectiveCycleTime, + }, + thresholds: { + stoppageMultiplier: microMultiplier, + macroStoppageMultiplier: macroMultiplier, + }, + activeStoppage, + events, + cycles, +}); }