Backup
This commit is contained in:
@@ -90,6 +90,13 @@ type TimelineSeg = {
|
|||||||
state: TimelineState;
|
state: TimelineState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ActiveStoppage = {
|
||||||
|
state: "microstop" | "macrostop";
|
||||||
|
startedAt: string;
|
||||||
|
durationSec: number;
|
||||||
|
theoreticalCycleTime: number;
|
||||||
|
};
|
||||||
|
|
||||||
type UploadState = {
|
type UploadState = {
|
||||||
status: "idle" | "parsing" | "uploading" | "success" | "error";
|
status: "idle" | "parsing" | "uploading" | "success" | "error";
|
||||||
message?: string;
|
message?: string;
|
||||||
@@ -246,9 +253,12 @@ export default function MachineDetailClient() {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [cycles, setCycles] = useState<CycleRow[]>([]);
|
const [cycles, setCycles] = useState<CycleRow[]>([]);
|
||||||
const [thresholds, setThresholds] = useState<Thresholds | null>(null);
|
const [thresholds, setThresholds] = useState<Thresholds | null>(null);
|
||||||
|
const [activeStoppage, setActiveStoppage] = useState<ActiveStoppage | null>(null);
|
||||||
const [open, setOpen] = useState<null | "events" | "deviation" | "impact">(null);
|
const [open, setOpen] = useState<null | "events" | "deviation" | "impact">(null);
|
||||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
const [uploadState, setUploadState] = useState<UploadState>({ status: "idle" });
|
const [uploadState, setUploadState] = useState<UploadState>({ status: "idle" });
|
||||||
|
const [nowMs, setNowMs] = useState(() => Date.now());
|
||||||
|
|
||||||
|
|
||||||
const BUCKET = {
|
const BUCKET = {
|
||||||
normal: {
|
normal: {
|
||||||
@@ -308,6 +318,7 @@ export default function MachineDetailClient() {
|
|||||||
setEvents(json.events ?? []);
|
setEvents(json.events ?? []);
|
||||||
setCycles(json.cycles ?? []);
|
setCycles(json.cycles ?? []);
|
||||||
setThresholds(json.thresholds ?? null);
|
setThresholds(json.thresholds ?? null);
|
||||||
|
setActiveStoppage(json.activeStoppage ?? null);
|
||||||
setError(null);
|
setError(null);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -325,6 +336,11 @@ export default function MachineDetailClient() {
|
|||||||
};
|
};
|
||||||
}, [machineId, t]);
|
}, [machineId, t]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setInterval(() => setNowMs(Date.now()), 1000);
|
||||||
|
return () => clearInterval(timer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
async function parseWorkOrdersFile(file: File) {
|
async function parseWorkOrdersFile(file: File) {
|
||||||
const name = file.name.toLowerCase();
|
const name = file.name.toLowerCase();
|
||||||
if (name.endsWith(".csv")) {
|
if (name.endsWith(".csv")) {
|
||||||
@@ -430,6 +446,20 @@ export default function MachineDetailClient() {
|
|||||||
return `${v}`;
|
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) {
|
function timeAgo(ts?: string) {
|
||||||
if (!ts) return t("common.never");
|
if (!ts) return t("common.never");
|
||||||
const diff = Math.floor((Date.now() - new Date(ts).getTime()) / 1000);
|
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 };
|
return { windowSec, segments: segs, start, end };
|
||||||
}, [cycles, cycleTarget, thresholds]);
|
}, [cycles, cycleTarget, thresholds]);
|
||||||
|
|
||||||
|
|||||||
@@ -312,6 +312,34 @@ const rawCycles = await prisma.machineCycle.findMany({
|
|||||||
sku: true,
|
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
|
// chart-friendly: oldest -> newest + numeric timestamps
|
||||||
const cycles = rawCycles
|
const cycles = rawCycles
|
||||||
@@ -340,14 +368,14 @@ const cycles = rawCycles
|
|||||||
latestHeartbeat: machine.heartbeats[0] ?? null,
|
latestHeartbeat: machine.heartbeats[0] ?? null,
|
||||||
latestKpi: machine.kpiSnapshots[0] ?? null,
|
latestKpi: machine.kpiSnapshots[0] ?? null,
|
||||||
effectiveCycleTime,
|
effectiveCycleTime,
|
||||||
|
|
||||||
},
|
},
|
||||||
thresholds: {
|
thresholds: {
|
||||||
stoppageMultiplier: microMultiplier,
|
stoppageMultiplier: microMultiplier,
|
||||||
macroStoppageMultiplier: macroMultiplier,
|
macroStoppageMultiplier: macroMultiplier,
|
||||||
},
|
},
|
||||||
|
activeStoppage,
|
||||||
events,
|
events,
|
||||||
cycles
|
cycles,
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user