From 14bf0896a7c4b058e773caddbe7e8f6a2715e8ff Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Sat, 20 Dec 2025 10:30:56 +0530 Subject: [PATCH] feat: implement simulation analyzer for detailed simulation performance and flow analysis. --- .../modules/simulation/analyzer/analyzer.tsx | 100 ++++++++++-------- 1 file changed, 56 insertions(+), 44 deletions(-) diff --git a/app/src/modules/simulation/analyzer/analyzer.tsx b/app/src/modules/simulation/analyzer/analyzer.tsx index b5c6544..df1f4da 100644 --- a/app/src/modules/simulation/analyzer/analyzer.tsx +++ b/app/src/modules/simulation/analyzer/analyzer.tsx @@ -1,6 +1,6 @@ import { useEffect, useCallback, useRef } from "react"; import { useSceneContext } from "../../scene/sceneContext"; -import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore"; +import { useAnimationPlaySpeed, usePlayButtonStore } from "../../../store/ui/usePlayButtonStore"; function Analyzer() { const { isPlaying } = usePlayButtonStore(); @@ -14,6 +14,7 @@ function Analyzer() { const { cranes } = craneStore(); const { storageUnits } = storageUnitStore(); const { materials, getMaterialsByModel } = materialStore(); + const { speed } = useAnimationPlaySpeed(); const { setAnalysis, setAnalyzing, analysis } = analysisStore(); @@ -222,6 +223,9 @@ function Analyzer() { useEffect(() => { if (!isPlaying) { resetAllRefs(); + } else { + // Reset start time when simulation starts + startTimeRef.current = new Date().toISOString(); } }, [isPlaying]); @@ -288,32 +292,35 @@ function Analyzer() { const calculateMaterialFlowMetricsForAsset = (assetId: string) => { const materialsOnAsset = getMaterialsByModel(assetId).length; - const materialHistory = materialHistoryRef.current.filter((m) => m.material.current?.modelUuid === assetId || m.material.previous?.modelUuid === assetId); + + // Use removals as the history of processed items + // This fixes the issue where items per hour was 0 because materialHistoryRef was empty + const removals = materialRemovalsRef.current[assetId] || []; // Calculate flow metrics const wip = materialsOnAsset; - const throughput = (materialHistory.length / (Date.now() - new Date(startTimeRef.current).getTime())) * 1000; - // Calculate lead times - const leadTimes = materialHistory - .map((m) => { - if (m.removedAt && m.material.startTime) { - return new Date(m.removedAt).getTime() - m.material.startTime; - } - return 0; - }) - .filter((t) => t > 0); + // Calculate throughput (items per second) + // Ensure we don't divide by zero + const durationMs = Date.now() - new Date(startTimeRef.current).getTime(); + + // Normalize by simulation speed to get "simulation time" throughput + const currentSpeed = Math.max(1, speed); + const throughput = durationMs > 1000 ? ((removals.length / durationMs) * 1000) / currentSpeed : 0; + + // Calculate lead times (processing times on this asset) + const leadTimes = removals.map((m) => m.processingTime || 0).filter((t) => t > 0); const avgLeadTime = leadTimes.length > 0 ? leadTimes.reduce((sum, t) => sum + t, 0) / leadTimes.length : 0; - // Calculate velocity - const velocity = materialHistory.length / Math.max(1, wip); + // Calculate velocity (turnover) + const velocity = removals.length / Math.max(1, wip); return { wip, throughput, avgLeadTime, - totalMaterialsProcessed: materialHistory.length, + totalMaterialsProcessed: removals.length, currentMaterials: materialsOnAsset, avgCycleTime: avgLeadTime / 1000, materialVelocity: velocity, @@ -670,32 +677,37 @@ function Analyzer() { /** * Update throughput snapshot for an asset */ - const updateThroughputSnapshot = useCallback((assetId: string) => { - const timestamp = Date.now(); - const timeWindow = 60; // 60 seconds + const updateThroughputSnapshot = useCallback( + (assetId: string) => { + const timestamp = Date.now(); + const timeWindow = 60; // 60 seconds - if (!throughputSnapshotsRef.current[assetId]) { - throughputSnapshotsRef.current[assetId] = []; - } + if (!throughputSnapshotsRef.current[assetId]) { + throughputSnapshotsRef.current[assetId] = []; + } - // Count items processed in the last time window - const removals = materialRemovalsRef.current[assetId] || []; - const recentRemovals = removals.filter((r) => timestamp - r.timestamp <= timeWindow * 1000); - const itemsProcessed = recentRemovals.length; - const rate = (itemsProcessed / timeWindow) * 3600; // items per hour + // Count items processed in the last time window + const removals = materialRemovalsRef.current[assetId] || []; + const recentRemovals = removals.filter((r) => timestamp - r.timestamp <= timeWindow * 1000); + const itemsProcessed = recentRemovals.length; + // Normalize by speed + const currentSpeed = Math.max(1, speed); + const rate = ((itemsProcessed / timeWindow) * 3600) / currentSpeed; // items per hour - throughputSnapshotsRef.current[assetId].push({ - timestamp, - itemsProcessed, - timeWindow, - rate, - }); + throughputSnapshotsRef.current[assetId].push({ + timestamp, + itemsProcessed, + timeWindow, + rate, + }); - // Keep only last 100 snapshots - if (throughputSnapshotsRef.current[assetId].length > 100) { - throughputSnapshotsRef.current[assetId] = throughputSnapshotsRef.current[assetId].slice(-100); - } - }, []); + // Keep only last 100 snapshots + if (throughputSnapshotsRef.current[assetId].length > 100) { + throughputSnapshotsRef.current[assetId] = throughputSnapshotsRef.current[assetId].slice(-100); + } + }, + [speed] + ); /** * Update performance snapshot for an asset @@ -948,7 +960,7 @@ function Analyzer() { historicalData: historicalDataRef.current[conveyor.modelUuid] || [], }; }, - [materials, analysis] + [materials, analysis, speed] ); // ============================================================================ @@ -1099,7 +1111,7 @@ function Analyzer() { historicalData: historicalDataRef.current[vehicle.modelUuid] || [], }; }, - [materials, analysis] + [materials, analysis, speed] ); // ============================================================================ @@ -1224,7 +1236,7 @@ function Analyzer() { historicalData: historicalDataRef.current[armBot.modelUuid] || [], }; }, - [materials, analysis] + [materials, analysis, speed] ); // ============================================================================ @@ -1349,7 +1361,7 @@ function Analyzer() { historicalData: historicalDataRef.current[machine.modelUuid] || [], }; }, - [materials, analysis] + [materials, analysis, speed] ); // ============================================================================ @@ -1486,7 +1498,7 @@ function Analyzer() { historicalData: historicalDataRef.current[storage.modelUuid] || [], }; }, - [analysis] + [analysis, speed] ); // ============================================================================ @@ -1624,7 +1636,7 @@ function Analyzer() { historicalData: historicalDataRef.current[human.modelUuid] || [], }; }, - [analysis] + [analysis, speed] ); // ============================================================================ @@ -1774,7 +1786,7 @@ function Analyzer() { historicalData: historicalDataRef.current[crane.modelUuid] || [], }; }, - [analysis] + [analysis, speed] ); // ============================================================================