feat: implement simulation analyzer for detailed simulation performance and flow analysis.

This commit is contained in:
2025-12-20 10:30:56 +05:30
parent e9d0a98a49
commit 14bf0896a7

View File

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