feat: Implement analysis functionality in simulation module
- Added Analyzer component to perform asset analysis and system-wide metrics. - Integrated analysis store to manage analysis state and data. - Updated Simulation component to include Analyzer and log analysis data. - Refactored heatmap generation to remove unnecessary console logs. - Disabled download option in simulation handler for heatmap generation. - Removed simulation socket initialization from Project component. - Enhanced SCSS styles for simulation dashboard with pointer-events adjustments. - Added comprehensive analysis types for various assets including conveyors, vehicles, and machines. - Deleted package-lock.json to resolve dependency issues.
This commit is contained in:
700
app/src/modules/simulation/analyzer/analyzer.tsx
Normal file
700
app/src/modules/simulation/analyzer/analyzer.tsx
Normal file
@@ -0,0 +1,700 @@
|
||||
import { useEffect, useCallback, useRef } from "react";
|
||||
import { useSceneContext } from "../../scene/sceneContext";
|
||||
import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore";
|
||||
|
||||
function Analyzer() {
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { conveyorStore, machineStore, armBotStore, humanStore, vehicleStore, craneStore, storageUnitStore, analysisStore } = useSceneContext();
|
||||
|
||||
const { conveyors } = conveyorStore();
|
||||
const { machines } = machineStore();
|
||||
const { armBots } = armBotStore();
|
||||
const { humans } = humanStore();
|
||||
const { vehicles } = vehicleStore();
|
||||
const { cranes } = craneStore();
|
||||
const { storageUnits } = storageUnitStore();
|
||||
|
||||
const { setAnalysis, setAnalyzing } = analysisStore();
|
||||
|
||||
const analysisIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const startTimeRef = useRef<string>(new Date().toISOString());
|
||||
|
||||
// ============================================================================
|
||||
// CALCULATION UTILITIES
|
||||
// ============================================================================
|
||||
|
||||
const calculateTimeMetrics = (idleTime: number, activeTime: number, totalErrors: number = 0) => {
|
||||
const totalTime = idleTime + activeTime;
|
||||
const uptime = totalTime > 0 ? (activeTime / totalTime) * 100 : 0;
|
||||
const downtime = idleTime;
|
||||
const utilizationRate = uptime / 100;
|
||||
|
||||
return {
|
||||
uptime,
|
||||
downtime,
|
||||
utilizationRate,
|
||||
mtbf: totalErrors > 0 ? totalTime / totalErrors : totalTime,
|
||||
mttr: totalErrors > 0 ? downtime / totalErrors : 0,
|
||||
};
|
||||
};
|
||||
|
||||
const calculateOEE = (availability: number, performance: number, quality: number) => {
|
||||
return (availability * performance * quality) / 10000; // All in percentages
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// CONVEYOR ANALYSIS
|
||||
// ============================================================================
|
||||
|
||||
const analyzeConveyor = useCallback((conveyor: any): ConveyorAnalysis => {
|
||||
const timeMetrics = calculateTimeMetrics(conveyor.idleTime || 0, conveyor.activeTime || 0);
|
||||
|
||||
const totalTime = (conveyor.idleTime || 0) + (conveyor.activeTime || 0);
|
||||
const materialsProcessed = conveyor.materialsProcessed || 0;
|
||||
|
||||
return {
|
||||
assetId: conveyor.modelUuid,
|
||||
assetName: conveyor.modelName,
|
||||
assetType: "conveyor",
|
||||
|
||||
currentStatus: {
|
||||
isActive: conveyor.isActive || false,
|
||||
isPaused: conveyor.isPaused || false,
|
||||
state: conveyor.state || "idle",
|
||||
speed: conveyor.speed || 0,
|
||||
currentProduct: conveyor.productUuid || null,
|
||||
},
|
||||
|
||||
timeMetrics: {
|
||||
...timeMetrics,
|
||||
idleTime: conveyor.idleTime || 0,
|
||||
activeTime: conveyor.activeTime || 0,
|
||||
totalTime,
|
||||
},
|
||||
|
||||
throughput: {
|
||||
itemsPerHour: totalTime > 0 ? (materialsProcessed / totalTime) * 3600 : 0,
|
||||
itemsPerDay: totalTime > 0 ? (materialsProcessed / totalTime) * 86400 : 0,
|
||||
materialFlowRate: conveyor.speed || 0,
|
||||
capacityUtilization: timeMetrics.utilizationRate * 100,
|
||||
materialsProcessed,
|
||||
averageProcessingTime: materialsProcessed > 0 ? totalTime / materialsProcessed : 0,
|
||||
},
|
||||
|
||||
efficiency: {
|
||||
overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100),
|
||||
availability: timeMetrics.uptime,
|
||||
performance: 100,
|
||||
quality: 100,
|
||||
},
|
||||
|
||||
quality: {
|
||||
errorRate: 0,
|
||||
errorFrequency: 0,
|
||||
successRate: 100,
|
||||
stateTransitions: [],
|
||||
},
|
||||
|
||||
historicalData: [],
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ============================================================================
|
||||
// VEHICLE ANALYSIS
|
||||
// ============================================================================
|
||||
|
||||
const analyzeVehicle = useCallback((vehicle: any): VehicleAnalysis => {
|
||||
const timeMetrics = calculateTimeMetrics(vehicle.idleTime || 0, vehicle.activeTime || 0);
|
||||
|
||||
const totalTime = (vehicle.idleTime || 0) + (vehicle.activeTime || 0);
|
||||
const tripsCompleted = vehicle.tripsCompleted || 0;
|
||||
|
||||
return {
|
||||
assetId: vehicle.modelUuid,
|
||||
assetName: vehicle.modelName,
|
||||
assetType: "vehicle",
|
||||
|
||||
currentStatus: {
|
||||
isActive: vehicle.isActive || false,
|
||||
isPicking: vehicle.isPicking || false,
|
||||
currentPhase: vehicle.currentPhase || "idle",
|
||||
state: vehicle.state || "idle",
|
||||
speed: vehicle.speed || 0,
|
||||
currentLoad: vehicle.currentLoad || 0,
|
||||
currentMaterials: vehicle.currentMaterials || [],
|
||||
},
|
||||
|
||||
timeMetrics: {
|
||||
...timeMetrics,
|
||||
idleTime: vehicle.idleTime || 0,
|
||||
activeTime: vehicle.activeTime || 0,
|
||||
totalTime,
|
||||
averageTripTime: tripsCompleted > 0 ? totalTime / tripsCompleted : 0,
|
||||
},
|
||||
|
||||
throughput: {
|
||||
itemsPerHour: totalTime > 0 ? ((vehicle.currentLoad || 0) / totalTime) * 3600 : 0,
|
||||
itemsPerDay: totalTime > 0 ? ((vehicle.currentLoad || 0) / totalTime) * 86400 : 0,
|
||||
materialFlowRate: vehicle.speed || 0,
|
||||
capacityUtilization: timeMetrics.utilizationRate * 100,
|
||||
tripsCompleted,
|
||||
averageLoadsPerTrip: tripsCompleted > 0 ? (vehicle.totalLoadsDelivered || 0) / tripsCompleted : 0,
|
||||
totalLoadsDelivered: vehicle.totalLoadsDelivered || 0,
|
||||
},
|
||||
|
||||
movementMetrics: {
|
||||
distanceTraveled: vehicle.distanceTraveled || 0,
|
||||
averageSpeedActual: totalTime > 0 ? (vehicle.distanceTraveled || 0) / totalTime : 0,
|
||||
fuelEfficiency: 0,
|
||||
routeEfficiency: 100,
|
||||
},
|
||||
|
||||
efficiency: {
|
||||
overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100),
|
||||
availability: timeMetrics.uptime,
|
||||
performance: 100,
|
||||
quality: 100,
|
||||
loadUtilization: vehicle.point?.action?.loadCapacity > 0 ? ((vehicle.currentLoad || 0) / vehicle.point.action.loadCapacity) * 100 : 0,
|
||||
},
|
||||
|
||||
quality: {
|
||||
errorRate: 0,
|
||||
errorFrequency: 0,
|
||||
successRate: 100,
|
||||
stateTransitions: [],
|
||||
},
|
||||
|
||||
historicalData: [],
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ============================================================================
|
||||
// ROBOTIC ARM ANALYSIS
|
||||
// ============================================================================
|
||||
|
||||
const analyzeRoboticArm = useCallback((armBot: any): RoboticArmAnalysis => {
|
||||
const timeMetrics = calculateTimeMetrics(armBot.idleTime || 0, armBot.activeTime || 0);
|
||||
|
||||
const totalTime = (armBot.idleTime || 0) + (armBot.activeTime || 0);
|
||||
const cyclesCompleted = armBot.cyclesCompleted || 0;
|
||||
|
||||
return {
|
||||
assetId: armBot.modelUuid,
|
||||
assetName: armBot.modelName,
|
||||
assetType: "roboticArm",
|
||||
|
||||
currentStatus: {
|
||||
isActive: armBot.isActive || false,
|
||||
state: armBot.state || "idle",
|
||||
speed: armBot.speed || 0,
|
||||
currentAction: armBot.currentAction || null,
|
||||
},
|
||||
|
||||
timeMetrics: {
|
||||
...timeMetrics,
|
||||
idleTime: armBot.idleTime || 0,
|
||||
activeTime: armBot.activeTime || 0,
|
||||
totalTime,
|
||||
averageCycleTime: cyclesCompleted > 0 ? totalTime / cyclesCompleted : 0,
|
||||
},
|
||||
|
||||
throughput: {
|
||||
itemsPerHour: totalTime > 0 ? (cyclesCompleted / totalTime) * 3600 : 0,
|
||||
itemsPerDay: totalTime > 0 ? (cyclesCompleted / totalTime) * 86400 : 0,
|
||||
materialFlowRate: armBot.speed || 0,
|
||||
capacityUtilization: timeMetrics.utilizationRate * 100,
|
||||
cyclesCompleted,
|
||||
pickAndPlaceCount: cyclesCompleted,
|
||||
},
|
||||
|
||||
efficiency: {
|
||||
overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100),
|
||||
availability: timeMetrics.uptime,
|
||||
performance: 100,
|
||||
quality: 100,
|
||||
cycleTimeEfficiency: 100,
|
||||
},
|
||||
|
||||
quality: {
|
||||
errorRate: 0,
|
||||
errorFrequency: 0,
|
||||
successRate: 100,
|
||||
stateTransitions: [],
|
||||
pickSuccessRate: 100,
|
||||
placeAccuracy: 100,
|
||||
},
|
||||
|
||||
historicalData: [],
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ============================================================================
|
||||
// MACHINE ANALYSIS
|
||||
// ============================================================================
|
||||
|
||||
const analyzeMachine = useCallback((machine: any): MachineAnalysis => {
|
||||
const timeMetrics = calculateTimeMetrics(machine.idleTime || 0, machine.activeTime || 0);
|
||||
|
||||
const totalTime = (machine.idleTime || 0) + (machine.activeTime || 0);
|
||||
const cyclesCompleted = machine.cyclesCompleted || 0;
|
||||
|
||||
return {
|
||||
assetId: machine.modelUuid,
|
||||
assetName: machine.modelName,
|
||||
assetType: "machine",
|
||||
|
||||
currentStatus: {
|
||||
isActive: machine.isActive || false,
|
||||
state: machine.state || "idle",
|
||||
currentAction: machine.currentAction || null,
|
||||
},
|
||||
|
||||
timeMetrics: {
|
||||
...timeMetrics,
|
||||
idleTime: machine.idleTime || 0,
|
||||
activeTime: machine.activeTime || 0,
|
||||
totalTime,
|
||||
averageProcessTime: cyclesCompleted > 0 ? totalTime / cyclesCompleted : 0,
|
||||
},
|
||||
|
||||
throughput: {
|
||||
itemsPerHour: totalTime > 0 ? (cyclesCompleted / totalTime) * 3600 : 0,
|
||||
itemsPerDay: totalTime > 0 ? (cyclesCompleted / totalTime) * 86400 : 0,
|
||||
materialFlowRate: 1,
|
||||
capacityUtilization: timeMetrics.utilizationRate * 100,
|
||||
cyclesCompleted,
|
||||
partsProcessed: cyclesCompleted,
|
||||
},
|
||||
|
||||
efficiency: {
|
||||
overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100),
|
||||
availability: timeMetrics.uptime,
|
||||
performance: 100,
|
||||
quality: 100,
|
||||
targetVsActual: 100,
|
||||
},
|
||||
|
||||
quality: {
|
||||
errorRate: 0,
|
||||
errorFrequency: 0,
|
||||
successRate: 100,
|
||||
stateTransitions: [],
|
||||
defectRate: 0,
|
||||
reworkRate: 0,
|
||||
},
|
||||
|
||||
historicalData: [],
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ============================================================================
|
||||
// STORAGE ANALYSIS
|
||||
// ============================================================================
|
||||
|
||||
const analyzeStorage = useCallback((storage: any): StorageAnalysis => {
|
||||
const timeMetrics = calculateTimeMetrics(storage.idleTime || 0, storage.activeTime || 0);
|
||||
|
||||
const utilizationRate = storage.storageCapacity > 0 ? (storage.currentLoad / storage.storageCapacity) * 100 : 0;
|
||||
|
||||
return {
|
||||
assetId: storage.modelUuid,
|
||||
assetName: storage.modelName,
|
||||
assetType: "storage",
|
||||
|
||||
currentStatus: {
|
||||
isActive: storage.isActive || false,
|
||||
state: storage.state || "idle",
|
||||
currentLoad: storage.currentLoad || 0,
|
||||
storageCapacity: storage.storageCapacity || 0,
|
||||
currentMaterials: storage.currentMaterials || [],
|
||||
},
|
||||
|
||||
timeMetrics: {
|
||||
...timeMetrics,
|
||||
idleTime: storage.idleTime || 0,
|
||||
activeTime: storage.activeTime || 0,
|
||||
totalTime: (storage.idleTime || 0) + (storage.activeTime || 0),
|
||||
},
|
||||
|
||||
capacityMetrics: {
|
||||
utilizationRate,
|
||||
averageOccupancy: utilizationRate,
|
||||
peakOccupancy: storage.peakOccupancy || utilizationRate,
|
||||
turnoverRate: 0,
|
||||
},
|
||||
|
||||
throughput: {
|
||||
itemsPerHour: 0,
|
||||
itemsPerDay: 0,
|
||||
materialFlowRate: 0,
|
||||
capacityUtilization: utilizationRate,
|
||||
storeOperations: storage.storeOperations || 0,
|
||||
retrieveOperations: storage.retrieveOperations || 0,
|
||||
totalOperations: (storage.storeOperations || 0) + (storage.retrieveOperations || 0),
|
||||
},
|
||||
|
||||
efficiency: {
|
||||
overallEffectiveness: calculateOEE(timeMetrics.uptime, utilizationRate, 100),
|
||||
availability: timeMetrics.uptime,
|
||||
performance: utilizationRate,
|
||||
quality: 100,
|
||||
spaceUtilization: utilizationRate,
|
||||
},
|
||||
|
||||
quality: {
|
||||
errorRate: 0,
|
||||
errorFrequency: 0,
|
||||
successRate: 100,
|
||||
stateTransitions: [],
|
||||
},
|
||||
|
||||
occupancyTrends: [],
|
||||
historicalData: [],
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ============================================================================
|
||||
// HUMAN ANALYSIS
|
||||
// ============================================================================
|
||||
|
||||
const analyzeHuman = useCallback((human: any): HumanAnalysis => {
|
||||
const timeMetrics = calculateTimeMetrics(human.idleTime || 0, human.activeTime || 0);
|
||||
|
||||
const totalTime = (human.idleTime || 0) + (human.activeTime || 0);
|
||||
const actionsCompleted = human.actionsCompleted || 0;
|
||||
|
||||
return {
|
||||
assetId: human.modelUuid,
|
||||
assetName: human.modelName,
|
||||
assetType: "human",
|
||||
|
||||
currentStatus: {
|
||||
isActive: human.isActive || false,
|
||||
isScheduled: human.isScheduled || false,
|
||||
currentPhase: human.currentPhase || "idle",
|
||||
state: human.state || "idle",
|
||||
speed: human.speed || 0,
|
||||
currentLoad: human.currentLoad || 0,
|
||||
currentMaterials: human.currentMaterials || [],
|
||||
currentAction: human.currentAction || null,
|
||||
},
|
||||
|
||||
timeMetrics: {
|
||||
...timeMetrics,
|
||||
idleTime: human.idleTime || 0,
|
||||
activeTime: human.activeTime || 0,
|
||||
totalTime,
|
||||
scheduledTime: totalTime,
|
||||
},
|
||||
|
||||
productivityMetrics: {
|
||||
actionsCompleted,
|
||||
actionsPerHour: totalTime > 0 ? (actionsCompleted / totalTime) * 3600 : 0,
|
||||
averageActionTime: actionsCompleted > 0 ? totalTime / actionsCompleted : 0,
|
||||
distanceTraveled: human.distanceTraveled || 0,
|
||||
},
|
||||
|
||||
workloadDistribution: [],
|
||||
|
||||
efficiency: {
|
||||
overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100),
|
||||
availability: timeMetrics.uptime,
|
||||
performance: 100,
|
||||
quality: 100,
|
||||
laborProductivity: totalTime > 0 ? actionsCompleted / (totalTime / 3600) : 0,
|
||||
utilizationRate: timeMetrics.utilizationRate * 100,
|
||||
},
|
||||
|
||||
quality: {
|
||||
errorRate: 0,
|
||||
errorFrequency: 0,
|
||||
successRate: 100,
|
||||
stateTransitions: [],
|
||||
},
|
||||
|
||||
historicalData: [],
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ============================================================================
|
||||
// CRANE ANALYSIS
|
||||
// ============================================================================
|
||||
|
||||
const analyzeCrane = useCallback((crane: any): CraneAnalysis => {
|
||||
const timeMetrics = calculateTimeMetrics(crane.idleTime || 0, crane.activeTime || 0);
|
||||
|
||||
const totalTime = (crane.idleTime || 0) + (crane.activeTime || 0);
|
||||
const cyclesCompleted = crane.cyclesCompleted || 0;
|
||||
|
||||
return {
|
||||
assetId: crane.modelUuid,
|
||||
assetName: crane.modelName,
|
||||
assetType: "crane",
|
||||
|
||||
currentStatus: {
|
||||
isActive: crane.isActive || false,
|
||||
isScheduled: crane.isScheduled || false,
|
||||
isCarrying: crane.isCarrying || false,
|
||||
currentPhase: crane.currentPhase || "idle",
|
||||
state: crane.state || "idle",
|
||||
currentLoad: crane.currentLoad || 0,
|
||||
currentMaterials: crane.currentMaterials || [],
|
||||
currentAction: crane.currentAction || null,
|
||||
},
|
||||
|
||||
timeMetrics: {
|
||||
...timeMetrics,
|
||||
idleTime: crane.idleTime || 0,
|
||||
activeTime: crane.activeTime || 0,
|
||||
totalTime,
|
||||
averageCycleTime: cyclesCompleted > 0 ? totalTime / cyclesCompleted : 0,
|
||||
},
|
||||
|
||||
throughput: {
|
||||
itemsPerHour: totalTime > 0 ? (cyclesCompleted / totalTime) * 3600 : 0,
|
||||
itemsPerDay: totalTime > 0 ? (cyclesCompleted / totalTime) * 86400 : 0,
|
||||
materialFlowRate: 1,
|
||||
capacityUtilization: timeMetrics.utilizationRate * 100,
|
||||
cyclesCompleted,
|
||||
loadsHandled: crane.loadsHandled || 0,
|
||||
averageLoadsPerCycle: cyclesCompleted > 0 ? (crane.loadsHandled || 0) / cyclesCompleted : 0,
|
||||
},
|
||||
|
||||
movementMetrics: {
|
||||
totalLifts: crane.totalLifts || 0,
|
||||
averageLiftHeight: crane.averageLiftHeight || 0,
|
||||
movementEfficiency: 100,
|
||||
},
|
||||
|
||||
efficiency: {
|
||||
overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100),
|
||||
availability: timeMetrics.uptime,
|
||||
performance: 100,
|
||||
quality: 100,
|
||||
loadUtilization: 0,
|
||||
cycleTimeEfficiency: 100,
|
||||
},
|
||||
|
||||
quality: {
|
||||
errorRate: 0,
|
||||
errorFrequency: 0,
|
||||
successRate: 100,
|
||||
stateTransitions: [],
|
||||
liftSuccessRate: 100,
|
||||
positioningAccuracy: 100,
|
||||
},
|
||||
|
||||
historicalData: [],
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ============================================================================
|
||||
// SYSTEM-WIDE ANALYSIS
|
||||
// ============================================================================
|
||||
|
||||
const analyzeSystem = useCallback((allAssets: AssetAnalysis[]): AnalysisSchema => {
|
||||
// Calculate system-wide metrics
|
||||
const totalAssets = allAssets.length;
|
||||
const activeAssets = allAssets.filter((a) => a.currentStatus.isActive).length;
|
||||
const assetsInError = allAssets.filter((a) => a.currentStatus.state === "error").length;
|
||||
|
||||
const avgOEE = allAssets.reduce((sum, a) => sum + a.efficiency.overallEffectiveness, 0) / totalAssets;
|
||||
const avgUtilization = allAssets.reduce((sum, a) => sum + a.timeMetrics.utilizationRate, 0) / totalAssets;
|
||||
const avgIdleTime = allAssets.reduce((sum, a) => sum + a.timeMetrics.idleTime, 0) / totalAssets;
|
||||
const totalDowntime = allAssets.reduce((sum, a) => sum + a.timeMetrics.downtime, 0);
|
||||
|
||||
// Helper to get throughput safely
|
||||
const getThroughput = (asset: AssetAnalysis): number => {
|
||||
if ("throughput" in asset) {
|
||||
return asset.throughput.itemsPerHour;
|
||||
} else if ("productivityMetrics" in asset) {
|
||||
return asset.productivityMetrics.actionsPerHour;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Group by asset type
|
||||
const assetTypeGroups = allAssets.reduce((acc, asset) => {
|
||||
if (!acc[asset.assetType]) {
|
||||
acc[asset.assetType] = [];
|
||||
}
|
||||
acc[asset.assetType].push(asset);
|
||||
return acc;
|
||||
}, {} as Record<string, AssetAnalysis[]>);
|
||||
|
||||
const assetTypePerformance = Object.entries(assetTypeGroups).map(([type, assets]) => ({
|
||||
assetType: type,
|
||||
count: assets.length,
|
||||
averageOEE: assets.reduce((sum, a) => sum + a.efficiency.overallEffectiveness, 0) / assets.length,
|
||||
averageUtilization: (assets.reduce((sum, a) => sum + a.timeMetrics.utilizationRate, 0) / assets.length) * 100,
|
||||
totalThroughput: assets.reduce((sum, a) => sum + getThroughput(a), 0),
|
||||
}));
|
||||
|
||||
// Identify bottlenecks (high utilization + low throughput)
|
||||
const bottlenecks = allAssets
|
||||
.filter((a) => a.timeMetrics.utilizationRate > 0.8)
|
||||
.map((a) => ({
|
||||
assetId: a.assetId,
|
||||
assetName: a.assetName,
|
||||
severity:
|
||||
a.timeMetrics.utilizationRate > 0.95
|
||||
? ("critical" as const)
|
||||
: a.timeMetrics.utilizationRate > 0.9
|
||||
? ("high" as const)
|
||||
: a.timeMetrics.utilizationRate > 0.85
|
||||
? ("medium" as const)
|
||||
: ("low" as const),
|
||||
utilizationRate: a.timeMetrics.utilizationRate * 100,
|
||||
queueLength: 0,
|
||||
impactScore: a.timeMetrics.utilizationRate * 100,
|
||||
}))
|
||||
.sort((a, b) => b.impactScore - a.impactScore);
|
||||
|
||||
return {
|
||||
assets: allAssets,
|
||||
|
||||
systemPerformance: {
|
||||
overallOEE: avgOEE,
|
||||
systemThroughput: allAssets.reduce((sum, a) => sum + getThroughput(a), 0),
|
||||
systemUtilization: avgUtilization * 100,
|
||||
assetTypePerformance,
|
||||
criticalMetrics: {
|
||||
activeAssets,
|
||||
totalAssets,
|
||||
assetsInError,
|
||||
averageIdleTime: avgIdleTime,
|
||||
totalDowntime,
|
||||
},
|
||||
},
|
||||
|
||||
materialFlow: {
|
||||
totalMaterialsInSystem: 0,
|
||||
materialsCompleted: 0,
|
||||
averageResidenceTime: 0,
|
||||
queueLengths: [],
|
||||
bottlenecks,
|
||||
flowContinuity: {
|
||||
overallFlowRate: 0,
|
||||
varianceCoefficient: 0,
|
||||
steadyStateDeviation: 0,
|
||||
},
|
||||
},
|
||||
|
||||
predictiveInsights: {
|
||||
maintenanceAlerts: [],
|
||||
optimizationOpportunities: [],
|
||||
trendAnalysis: [],
|
||||
},
|
||||
|
||||
analysisTimeRange: {
|
||||
startTime: startTimeRef.current,
|
||||
endTime: new Date().toISOString(),
|
||||
duration: Date.now() - new Date(startTimeRef.current).getTime(),
|
||||
},
|
||||
|
||||
metadata: {
|
||||
lastUpdated: new Date().toISOString(),
|
||||
dataPoints: allAssets.length,
|
||||
analysisVersion: "1.0.0",
|
||||
},
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ============================================================================
|
||||
// MAIN ANALYSIS FUNCTION
|
||||
// ============================================================================
|
||||
|
||||
const performAnalysis = useCallback(() => {
|
||||
setAnalyzing(true);
|
||||
|
||||
try {
|
||||
const allAssets: AssetAnalysis[] = [];
|
||||
|
||||
// Analyze all conveyors
|
||||
conveyors.forEach((conveyor) => {
|
||||
allAssets.push(analyzeConveyor(conveyor));
|
||||
});
|
||||
|
||||
// Analyze all vehicles
|
||||
vehicles.forEach((vehicle) => {
|
||||
allAssets.push(analyzeVehicle(vehicle));
|
||||
});
|
||||
|
||||
// Analyze all robotic arms
|
||||
armBots.forEach((armBot) => {
|
||||
allAssets.push(analyzeRoboticArm(armBot));
|
||||
});
|
||||
|
||||
// Analyze all machines
|
||||
machines.forEach((machine) => {
|
||||
allAssets.push(analyzeMachine(machine));
|
||||
});
|
||||
|
||||
// Analyze all storage units
|
||||
storageUnits.forEach((storage) => {
|
||||
allAssets.push(analyzeStorage(storage));
|
||||
});
|
||||
|
||||
// Analyze all humans
|
||||
humans.forEach((human) => {
|
||||
allAssets.push(analyzeHuman(human));
|
||||
});
|
||||
|
||||
// Analyze all cranes
|
||||
cranes.forEach((crane) => {
|
||||
allAssets.push(analyzeCrane(crane));
|
||||
});
|
||||
|
||||
// Perform system-wide analysis
|
||||
const completeAnalysis = analyzeSystem(allAssets);
|
||||
|
||||
setAnalysis(completeAnalysis);
|
||||
} catch (error) {
|
||||
console.error("Analysis error:", error);
|
||||
} finally {
|
||||
setAnalyzing(false);
|
||||
}
|
||||
}, [
|
||||
conveyors,
|
||||
vehicles,
|
||||
armBots,
|
||||
machines,
|
||||
humans,
|
||||
cranes,
|
||||
analyzeConveyor,
|
||||
analyzeVehicle,
|
||||
analyzeRoboticArm,
|
||||
analyzeMachine,
|
||||
analyzeHuman,
|
||||
analyzeCrane,
|
||||
analyzeSystem,
|
||||
setAnalysis,
|
||||
setAnalyzing,
|
||||
]);
|
||||
|
||||
// ============================================================================
|
||||
// EFFECTS
|
||||
// ============================================================================
|
||||
|
||||
// Perform initial analysis and set up interval
|
||||
useEffect(() => {
|
||||
if (!isPlaying) return;
|
||||
// Initial analysis
|
||||
performAnalysis();
|
||||
|
||||
// Set up periodic analysis (every 5 seconds)
|
||||
analysisIntervalRef.current = setInterval(() => {
|
||||
performAnalysis();
|
||||
}, 5000);
|
||||
|
||||
return () => {
|
||||
if (analysisIntervalRef.current) {
|
||||
clearInterval(analysisIntervalRef.current);
|
||||
}
|
||||
};
|
||||
}, [performAnalysis, isPlaying]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default Analyzer;
|
||||
Reference in New Issue
Block a user