diff --git a/app/src/modules/simulation/analyzer/analyzer.tsx b/app/src/modules/simulation/analyzer/analyzer.tsx index 1d6e597..b23f8ef 100644 --- a/app/src/modules/simulation/analyzer/analyzer.tsx +++ b/app/src/modules/simulation/analyzer/analyzer.tsx @@ -4,7 +4,7 @@ import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore"; function Analyzer() { const { isPlaying } = usePlayButtonStore(); - const { conveyorStore, machineStore, armBotStore, humanStore, vehicleStore, craneStore, storageUnitStore, analysisStore } = useSceneContext(); + const { conveyorStore, machineStore, armBotStore, humanStore, vehicleStore, craneStore, storageUnitStore, materialStore, analysisStore } = useSceneContext(); const { conveyors } = conveyorStore(); const { machines } = machineStore(); @@ -13,418 +13,923 @@ function Analyzer() { const { vehicles } = vehicleStore(); const { cranes } = craneStore(); const { storageUnits } = storageUnitStore(); + const { materials } = materialStore(); - const { setAnalysis, setAnalyzing } = analysisStore(); + const { setAnalysis, setAnalyzing, analysis } = analysisStore(); + const historicalDataRef = useRef>({}); + const materialHistoryRef = useRef([]); + const eventTimestampsRef = useRef>({}); + const queueLengthsRef = useRef>({}); + const performanceHistoryRef = useRef>({}); const analysisIntervalRef = useRef(null); const startTimeRef = useRef(new Date().toISOString()); + const errorCountsRef = useRef>({}); + const completedActionsRef = useRef>({}); + const stateTransitionsRef = useRef>({}); // ============================================================================ // CALCULATION UTILITIES // ============================================================================ - const calculateTimeMetrics = (idleTime: number, activeTime: number, totalErrors: number = 0) => { + const calculateTimeMetrics = (idleTime: number, activeTime: number, assetId: string) => { const totalTime = idleTime + activeTime; const uptime = totalTime > 0 ? (activeTime / totalTime) * 100 : 0; const downtime = idleTime; const utilizationRate = uptime / 100; + const errorCount = errorCountsRef.current[assetId] || 0; + return { uptime, downtime, utilizationRate, - mtbf: totalErrors > 0 ? totalTime / totalErrors : totalTime, - mttr: totalErrors > 0 ? downtime / totalErrors : 0, + mtbf: errorCount > 0 ? totalTime / errorCount : totalTime || 0, + mttr: errorCount > 0 ? downtime / errorCount : 0, }; }; const calculateOEE = (availability: number, performance: number, quality: number) => { - return (availability * performance * quality) / 10000; // All in percentages + return (availability * performance * quality) / 10000; + }; + + const trackStateTransition = (assetId: string, fromState: string, toState: string, time: number) => { + if (!stateTransitionsRef.current[assetId]) { + stateTransitionsRef.current[assetId] = []; + } + stateTransitionsRef.current[assetId].push({ + fromState, + toState, + timestamp: Date.now(), + duration: time, + }); + }; + + const getStateTransitions = (assetId: string) => { + const transitions = stateTransitionsRef.current[assetId] || []; + const grouped = transitions.reduce((acc, t) => { + const key = `${t.fromState}-${t.toState}`; + if (!acc[key]) { + acc[key] = { fromState: t.fromState, toState: t.toState, count: 0, totalTime: 0 }; + } + acc[key].count++; + acc[key].totalTime += t.duration; + return acc; + }, {} as Record); + + return Object.values(grouped).map((g: any) => ({ + fromState: g.fromState, + toState: g.toState, + count: g.count, + averageTime: g.totalTime / g.count, + })); + }; + + // Count materials currently on an asset + const getMaterialsOnAsset = (assetId: string) => { + return materials.filter((m) => m.current.modelUuid === assetId && m.isActive); + }; + + // Calculate actual performance vs ideal + const calculatePerformanceRate = (actualTime: number, idealTime: number) => { + if (idealTime === 0 || actualTime === 0) return 100; + return Math.min((idealTime / actualTime) * 100, 100); }; // ============================================================================ - // CONVEYOR ANALYSIS + // ENHANCED UTILITY FUNCTIONS // ============================================================================ - const analyzeConveyor = useCallback((conveyor: any): ConveyorAnalysis => { - const timeMetrics = calculateTimeMetrics(conveyor.idleTime || 0, conveyor.activeTime || 0); + const calculateAdvancedTimeMetrics = (idleTime: number, activeTime: number, assetId: string, errorCount: number = 0) => { + const totalTime = idleTime + activeTime; + const uptime = totalTime > 0 ? (activeTime / totalTime) * 100 : 0; + const downtime = idleTime; + const utilizationRate = uptime / 100; - const totalTime = (conveyor.idleTime || 0) + (conveyor.activeTime || 0); - const materialsProcessed = conveyor.materialsProcessed || 0; + // Enhanced reliability calculations + const mtbf = errorCount > 0 ? totalTime / errorCount : totalTime; + const mttr = errorCount > 0 ? downtime / errorCount : 0; + const reliability = Math.exp(-errorCount / Math.max(1, totalTime / 3600)) * 100; // Reliability per hour + + // Calculate schedule adherence if scheduled data exists + const scheduledTime = totalTime; // Could be enhanced with actual schedule data + const scheduleAdherence = scheduledTime > 0 ? (activeTime / scheduledTime) * 100 : 100; return { - assetId: conveyor.modelUuid, - assetName: conveyor.modelName, - assetType: "conveyor", + uptime, + downtime, + utilizationRate, + mtbf, + mttr, + reliability, + totalTime, + scheduleAdherence, + }; + }; - currentStatus: { - isActive: conveyor.isActive || false, - isPaused: conveyor.isPaused || false, - state: conveyor.state || "idle", - speed: conveyor.speed || 0, - currentProduct: conveyor.productUuid || null, - }, + const calculateQualityMetrics = (assetId: string, totalOperations: number, defects: number, historicalData: any[] = []) => { + const errorCount = errorCountsRef.current[assetId] || 0; + const firstPassYield = totalOperations > 0 ? ((totalOperations - defects) / totalOperations) * 100 : 100; + const defectRate = totalOperations > 0 ? (defects / totalOperations) * 100 : 0; + const scrapRate = defects > 0 ? defects * 0.1 : 0; // Assuming 10% scrap rate + const reworkRate = defects > 0 ? defects * 0.9 : 0; // Assuming 90% rework - timeMetrics: { - ...timeMetrics, - idleTime: conveyor.idleTime || 0, - activeTime: conveyor.activeTime || 0, - totalTime, - }, + // Calculate defect trends + let defectTrend = "stable"; + if (historicalData.length > 5) { + const recentDefects = historicalData.slice(-5).filter((d) => d.defects > 0).length; + const olderDefects = historicalData.slice(-10, -5).filter((d) => d.defects > 0).length; + if (recentDefects > olderDefects * 1.5) defectTrend = "increasing"; + else if (recentDefects < olderDefects * 0.5) defectTrend = "decreasing"; + } - throughput: { - itemsPerHour: totalTime > 0 ? (materialsProcessed / totalTime) * 3600 : 0, - itemsPerDay: totalTime > 0 ? (materialsProcessed / totalTime) * 86400 : 0, - materialFlowRate: conveyor.speed || 0, - capacityUtilization: timeMetrics.utilizationRate * 100, + return { + errorRate: totalOperations > 0 ? (errorCount / totalOperations) * 100 : 0, + errorFrequency: (errorCount / Math.max(1, totalOperations)) * 1000, // Errors per 1000 operations + successRate: firstPassYield, + firstPassYield, + defectRate, + scrapRate, + reworkRate, + defectTrend, + defectsPerMillion: totalOperations > 0 ? (defects / totalOperations) * 1000000 : 0, + stateTransitions: getStateTransitions(assetId), + }; + }; + + const calculateMaterialFlowMetricsForAsset = (assetId: string) => { + const materialsOnAsset = getMaterialsOnAsset(assetId); + const materialHistory = materialHistoryRef.current.filter((m) => m.material.current?.modelUuid === assetId || m.material.previous?.modelUuid === assetId); + + // Calculate flow metrics + const wip = materialsOnAsset.length; + 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); + + 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); + + return { + wip, + throughput, + avgLeadTime, + totalMaterialsProcessed: materialHistory.length, + currentMaterials: materialsOnAsset.length, + avgCycleTime: avgLeadTime / 1000, + materialVelocity: velocity, + inventoryTurns: throughput > 0 ? throughput / Math.max(1, wip) : 0, + }; + }; + + const calculatePerformanceMetrics = (actualOutput: number, idealOutput: number, actualTime: number, idealTime: number, assetType: string) => { + if (idealTime === 0 || actualTime === 0) + return { + performanceRate: 100, + timeEfficiency: 100, + productivity: 0, + outputPerHour: 0, + }; + + const performanceRate = idealOutput > 0 ? Math.min((actualOutput / idealOutput) * 100, 100) : 100; + const timeEfficiency = idealTime > 0 ? Math.min((idealTime / actualTime) * 100, 100) : 100; + const productivity = actualTime > 0 ? actualOutput / actualTime : 0; + const outputPerHour = actualTime > 0 ? (actualOutput / actualTime) * 3600 : 0; + + // Asset type specific adjustments + const efficiencyAdjustment = + { + conveyor: 1.0, + vehicle: 0.95, + machine: 0.9, + roboticArm: 0.92, + human: 0.85, + crane: 0.88, + storage: 0.98, + }[assetType] || 1.0; + + return { + performanceRate: performanceRate * efficiencyAdjustment, + timeEfficiency: timeEfficiency * efficiencyAdjustment, + productivity, + outputPerHour, + efficiencyAdjustment, + }; + }; + + const calculateCostMetrics = (assetId: string, assetType: EventType, activeTime: number, totalOutput: number) => { + // Cost parameters (hourly rates in $) + const costParams = { + conveyor: { hourlyRate: 50, maintenanceCost: 0.5, energyCost: 5 }, + vehicle: { hourlyRate: 75, maintenanceCost: 2, energyCost: 10 }, + machine: { hourlyRate: 100, maintenanceCost: 5, energyCost: 15 }, + roboticArm: { hourlyRate: 120, maintenanceCost: 8, energyCost: 20 }, + human: { hourlyRate: 35, maintenanceCost: 0.1, energyCost: 0 }, + crane: { hourlyRate: 150, maintenanceCost: 10, energyCost: 25 }, + storage: { hourlyRate: 25, maintenanceCost: 1, energyCost: 2 }, + }; + + const params = costParams[assetType] || costParams.conveyor; + const hoursOperated = activeTime / 3600; + const errorCount = errorCountsRef.current[assetId] || 0; + + const operatingCost = hoursOperated * params.hourlyRate; + const maintenanceCost = errorCount * params.maintenanceCost; + const energyCost = hoursOperated * params.energyCost; + const totalCost = operatingCost + maintenanceCost + energyCost; + + const costPerUnit = totalOutput > 0 ? totalCost / totalOutput : 0; + const costPerHour = totalCost / Math.max(1, hoursOperated); + const roi = totalOutput > 0 ? (totalOutput * 10) / totalCost : 0; // Assuming $10 value per unit + + return { + operatingCost, + maintenanceCost, + energyCost, + totalCost, + costPerUnit, + costPerHour, + roi, + valueAdded: totalOutput * 10 - totalCost, + }; + }; + + const calculateEnergyMetrics = (assetType: EventType, activeTime: number, distanceTraveled: number = 0) => { + // Energy consumption rates in kW + const energyRates = { + conveyor: 7.5, + vehicle: 15 + distanceTraveled * 0.1, // Base + distance-based + machine: 20, + roboticArm: 10, + human: 0, + crane: 25, + storage: 2, + }; + + const rate = energyRates[assetType] || 5; + const hoursActive = activeTime / 3600; + const energyConsumed = hoursActive * rate; // kWh + const energyEfficiency = 85 - Math.random() * 10; // Simulated efficiency 75-85% + const carbonFootprint = energyConsumed * 0.5; // kg CO2 per kWh (0.5 kg/kWh average) + const energyCost = energyConsumed * 0.12; // $0.12 per kWh + + return { + energyConsumed, + energyEfficiency, + carbonFootprint, + powerUsage: rate, + energyCost, + energyPerUnit: 0, // Will be calculated with output + }; + }; + + // ============================================================================ + // ENHANCED CONVEYOR ANALYSIS + // ============================================================================ + + const analyzeConveyor = useCallback( + (conveyor: ConveyorStatus): ConveyorAnalysis => { + const errorCount = errorCountsRef.current[conveyor.modelUuid] || 0; + const timeMetrics = calculateAdvancedTimeMetrics(conveyor.idleTime || 0, conveyor.activeTime || 0, conveyor.modelUuid, errorCount); + + const materialFlow = calculateMaterialFlowMetricsForAsset(conveyor.modelUuid); + const materialsProcessed = materialFlow.totalMaterialsProcessed; + const defects = errorCountsRef.current[`${conveyor.modelUuid}_defects`] || 0; + const qualityMetrics = calculateQualityMetrics(conveyor.modelUuid, materialsProcessed, defects); + + // Performance calculations + const conveyorLength = 10; // meters + const idealSpeed = conveyor.speed; + const actualSpeed = conveyor.isActive ? conveyor.speed : 0; + const idealThroughput = idealSpeed > 0 ? (idealSpeed * 3600) / (conveyorLength / 2) : 0; // items per hour + const actualThroughput = materialFlow.throughput * 3600; + + const performance = calculatePerformanceMetrics( materialsProcessed, - averageProcessingTime: materialsProcessed > 0 ? totalTime / materialsProcessed : 0, - }, + idealThroughput * (timeMetrics.totalTime / 3600), + timeMetrics.totalTime, + materialsProcessed * 2, // 2 seconds per item ideal + "conveyor" + ); - efficiency: { - overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100), - availability: timeMetrics.uptime, - performance: 100, - quality: 100, - }, + const costMetrics = calculateCostMetrics(conveyor.modelUuid, "conveyor", conveyor.activeTime || 0, materialsProcessed); - quality: { - errorRate: 0, - errorFrequency: 0, - successRate: 100, - stateTransitions: [], - }, + const energyMetrics = calculateEnergyMetrics("conveyor", conveyor.activeTime || 0); - historicalData: [], - }; - }, []); + // Update historical data + const timestamp = new Date().toISOString(); + if (!historicalDataRef.current[conveyor.modelUuid]) { + historicalDataRef.current[conveyor.modelUuid] = []; + } + historicalDataRef.current[conveyor.modelUuid].push({ + timestamp, + isActive: conveyor.isActive, + speed: conveyor.speed, + state: conveyor.state, + materialsCount: materialFlow.currentMaterials, + throughput: actualThroughput, + performance: performance.performanceRate, + }); + + // Calculate queue if applicable + const queueLength = materialFlow.wip; + if (!queueLengthsRef.current[conveyor.modelUuid]) { + queueLengthsRef.current[conveyor.modelUuid] = []; + } + queueLengthsRef.current[conveyor.modelUuid].push({ + timestamp: Date.now(), + length: queueLength, + }); + + return { + assetId: conveyor.modelUuid, + assetName: conveyor.modelName, + assetType: "conveyor", + + currentStatus: { + isActive: conveyor.isActive, + isPaused: conveyor.isPaused, + state: conveyor.state, + speed: conveyor.speed, + currentProduct: conveyor.productUuid, + currentMaterials: materialFlow.currentMaterials, + queueLength, + }, + + timeMetrics: { + ...timeMetrics, + idleTime: conveyor.idleTime || 0, + activeTime: conveyor.activeTime || 0, + totalTime: timeMetrics.totalTime, + availability: timeMetrics.uptime, + reliability: timeMetrics.reliability, + scheduleAdherence: timeMetrics.scheduleAdherence, + }, + + throughput: { + itemsPerHour: actualThroughput, + itemsPerDay: actualThroughput * 24, + materialFlowRate: conveyor.speed * 60, + capacityUtilization: timeMetrics.utilizationRate * 100, + materialsProcessed, + averageProcessingTime: materialFlow.avgCycleTime, + wip: materialFlow.wip, + throughputEfficiency: (actualThroughput / Math.max(1, idealThroughput)) * 100, + bottleneckIndex: timeMetrics.utilizationRate > 0.9 ? 1 : 0, + }, + + efficiency: { + overallEffectiveness: calculateOEE(timeMetrics.uptime, performance.performanceRate, qualityMetrics.firstPassYield), + availability: timeMetrics.uptime, + performance: performance.performanceRate, + quality: qualityMetrics.firstPassYield, + timeEfficiency: performance.timeEfficiency, + energyEfficiency: energyMetrics.energyEfficiency, + costEfficiency: 100 - costMetrics.costPerUnit * 10, // Inverse relationship + scheduleEfficiency: timeMetrics.scheduleAdherence, + }, + + quality: qualityMetrics, + + // Add cost metrics + costMetrics: { + operatingCost: costMetrics.operatingCost, + maintenanceCost: costMetrics.maintenanceCost, + energyCost: energyMetrics.energyCost, + totalCost: costMetrics.totalCost, + costPerUnit: costMetrics.costPerUnit, + costPerHour: costMetrics.costPerHour, + roi: costMetrics.roi, + valueAdded: costMetrics.valueAdded, + }, + + // Add energy metrics + energyMetrics: { + energyConsumed: energyMetrics.energyConsumed, + energyEfficiency: energyMetrics.energyEfficiency, + carbonFootprint: energyMetrics.carbonFootprint, + powerUsage: energyMetrics.powerUsage, + energyCost: energyMetrics.energyCost, + energyPerUnit: materialsProcessed > 0 ? energyMetrics.energyConsumed / materialsProcessed : 0, + }, + + // Material flow metrics + materialFlow: { + wip: materialFlow.wip, + throughput: materialFlow.throughput, + avgCycleTime: materialFlow.avgCycleTime, + materialVelocity: materialFlow.materialVelocity, + inventoryTurns: materialFlow.inventoryTurns, + leadTimeVariance: 0, // Could be calculated with more data + }, + + historicalData: historicalDataRef.current[conveyor.modelUuid] || [], + }; + }, + [materials, analysis] + ); // ============================================================================ - // VEHICLE ANALYSIS + // ENHANCED VEHICLE ANALYSIS (apply similar enhancements to other assets) // ============================================================================ - const analyzeVehicle = useCallback((vehicle: any): VehicleAnalysis => { - const timeMetrics = calculateTimeMetrics(vehicle.idleTime || 0, vehicle.activeTime || 0); + const analyzeVehicle = useCallback( + (vehicle: VehicleStatus): VehicleAnalysis => { + const errorCount = errorCountsRef.current[vehicle.modelUuid] || 0; + const timeMetrics = calculateAdvancedTimeMetrics(vehicle.idleTime || 0, vehicle.activeTime || 0, vehicle.modelUuid, errorCount); - const totalTime = (vehicle.idleTime || 0) + (vehicle.activeTime || 0); - const tripsCompleted = vehicle.tripsCompleted || 0; + const materialFlow = calculateMaterialFlowMetricsForAsset(vehicle.modelUuid); + const tripsCompleted = completedActionsRef.current[vehicle.modelUuid] || 0; + const totalLoadsDelivered = completedActionsRef.current[`${vehicle.modelUuid}_loads`] || 0; + const defects = errorCountsRef.current[`${vehicle.modelUuid}_defects`] || 0; + const qualityMetrics = calculateQualityMetrics(vehicle.modelUuid, totalLoadsDelivered, defects); - return { - assetId: vehicle.modelUuid, - assetName: vehicle.modelName, - assetType: "vehicle", + // Performance calculations + const loadCapacity = vehicle.point?.action?.loadCapacity || 1; + const avgLoad = tripsCompleted > 0 ? totalLoadsDelivered / tripsCompleted : 0; + const loadUtilization = (avgLoad / loadCapacity) * 100; - 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 || [], - }, + const idealTripTime = 60; + const actualTripTime = tripsCompleted > 0 ? timeMetrics.totalTime / tripsCompleted : 0; + const idealThroughput = loadCapacity * (3600 / idealTripTime); + const actualThroughput = materialFlow.throughput * 3600; - timeMetrics: { - ...timeMetrics, - idleTime: vehicle.idleTime || 0, - activeTime: vehicle.activeTime || 0, - totalTime, - averageTripTime: tripsCompleted > 0 ? totalTime / tripsCompleted : 0, - }, + const performance = calculatePerformanceMetrics(totalLoadsDelivered, idealThroughput * (timeMetrics.totalTime / 3600), timeMetrics.totalTime, idealTripTime * tripsCompleted, "vehicle"); - 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, + // Route efficiency + const optimalDistance = 100; + const actualDistance = vehicle.distanceTraveled || 0; + const routeEfficiency = actualDistance > 0 ? Math.min((optimalDistance / actualDistance) * 100, 100) : 100; + + const costMetrics = calculateCostMetrics(vehicle.modelUuid, "vehicle", vehicle.activeTime || 0, totalLoadsDelivered); + + const energyMetrics = calculateEnergyMetrics("vehicle", vehicle.activeTime || 0, actualDistance); + + // Update historical data + const timestamp = new Date().toISOString(); + if (!historicalDataRef.current[vehicle.modelUuid]) { + historicalDataRef.current[vehicle.modelUuid] = []; + } + historicalDataRef.current[vehicle.modelUuid].push({ + timestamp, + phase: vehicle.currentPhase, + load: vehicle.currentLoad, + distanceTraveled: actualDistance, + state: vehicle.state, + performance: performance.performanceRate, + speed: vehicle.speed, 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, - }, + return { + assetId: vehicle.modelUuid, + assetName: vehicle.modelName, + assetType: "vehicle", - 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, - }, + currentStatus: { + isActive: vehicle.isActive, + isPicking: vehicle.isPicking, + currentPhase: vehicle.currentPhase, + state: vehicle.state, + speed: vehicle.speed, + currentLoad: vehicle.currentLoad, + currentMaterials: vehicle.currentMaterials, + distanceTraveled: actualDistance, + currentRouteEfficiency: routeEfficiency, + }, - quality: { - errorRate: 0, - errorFrequency: 0, - successRate: 100, - stateTransitions: [], - }, + timeMetrics: { + ...timeMetrics, + idleTime: vehicle.idleTime || 0, + activeTime: vehicle.activeTime || 0, + totalTime: timeMetrics.totalTime, + averageTripTime: actualTripTime, + availability: timeMetrics.uptime, + reliability: timeMetrics.reliability, + }, - historicalData: [], - }; - }, []); + throughput: { + itemsPerHour: actualThroughput, + itemsPerDay: actualThroughput * 24, + materialFlowRate: vehicle.speed, + capacityUtilization: timeMetrics.utilizationRate * 100, + tripsCompleted, + averageLoadsPerTrip: avgLoad, + totalLoadsDelivered, + throughputEfficiency: (actualThroughput / Math.max(1, idealThroughput)) * 100, + wip: materialFlow.wip, + bottleneckIndex: timeMetrics.utilizationRate > 0.85 ? 1 : 0, + }, + movementMetrics: { + distanceTraveled: actualDistance, + averageSpeedActual: timeMetrics.totalTime > 0 ? actualDistance / timeMetrics.totalTime : 0, + fuelEfficiency: actualDistance > 0 ? totalLoadsDelivered / actualDistance : 0, + routeEfficiency, + idleDistance: vehicle.idleTime || 0, + totalTrips: tripsCompleted, + averageTripDistance: tripsCompleted > 0 ? actualDistance / tripsCompleted : 0, + distanceEfficiency: routeEfficiency, + }, + + efficiency: { + overallEffectiveness: calculateOEE(timeMetrics.uptime, performance.performanceRate, qualityMetrics.firstPassYield), + availability: timeMetrics.uptime, + performance: performance.performanceRate, + quality: qualityMetrics.firstPassYield, + loadUtilization, + timeEfficiency: performance.timeEfficiency, + energyEfficiency: energyMetrics.energyEfficiency, + routeEfficiency, + costEfficiency: 100 - costMetrics.costPerUnit * 5, + }, + + quality: { + ...qualityMetrics, + onTimeDelivery: tripsCompleted > 0 ? ((tripsCompleted - defects) / tripsCompleted) * 100 : 100, + damageRate: defects > 0 ? (defects / totalLoadsDelivered) * 100 : 0, + accuracyRate: 100 - qualityMetrics.defectRate, + }, + + costMetrics: { + operatingCost: costMetrics.operatingCost, + maintenanceCost: costMetrics.maintenanceCost, + energyCost: energyMetrics.energyCost, + totalCost: costMetrics.totalCost, + costPerMile: actualDistance > 0 ? costMetrics.totalCost / actualDistance : 0, + costPerTrip: tripsCompleted > 0 ? costMetrics.totalCost / tripsCompleted : 0, + costPerLoad: totalLoadsDelivered > 0 ? costMetrics.totalCost / totalLoadsDelivered : 0, + roi: costMetrics.roi, + valueAdded: costMetrics.valueAdded, + }, + + energyMetrics: { + energyConsumed: energyMetrics.energyConsumed, + energyEfficiency: energyMetrics.energyEfficiency, + carbonFootprint: energyMetrics.carbonFootprint, + powerUsage: energyMetrics.powerUsage, + energyCost: energyMetrics.energyCost, + energyPerMile: actualDistance > 0 ? energyMetrics.energyConsumed / actualDistance : 0, + energyPerTrip: tripsCompleted > 0 ? energyMetrics.energyConsumed / tripsCompleted : 0, + }, + + historicalData: historicalDataRef.current[vehicle.modelUuid] || [], + }; + }, + [materials, analysis] + ); // ============================================================================ // ROBOTIC ARM ANALYSIS // ============================================================================ - const analyzeRoboticArm = useCallback((armBot: any): RoboticArmAnalysis => { - const timeMetrics = calculateTimeMetrics(armBot.idleTime || 0, armBot.activeTime || 0); + const analyzeRoboticArm = useCallback( + (armBot: ArmBotStatus): RoboticArmAnalysis => { + const timeMetrics = calculateTimeMetrics(armBot.idleTime || 0, armBot.activeTime || 0, armBot.modelUuid); - const totalTime = (armBot.idleTime || 0) + (armBot.activeTime || 0); - const cyclesCompleted = armBot.cyclesCompleted || 0; + const totalTime = (armBot.idleTime || 0) + (armBot.activeTime || 0); + const cyclesCompleted = completedActionsRef.current[armBot.modelUuid] || 0; - return { - assetId: armBot.modelUuid, - assetName: armBot.modelName, - assetType: "roboticArm", + // Calculate cycle time efficiency + const idealCycleTime = 5; // 5 seconds ideal + const actualCycleTime = cyclesCompleted > 0 ? totalTime / cyclesCompleted : 0; + const cycleTimeEfficiency = calculatePerformanceRate(actualCycleTime, idealCycleTime); - currentStatus: { - isActive: armBot.isActive || false, - state: armBot.state || "idle", - speed: armBot.speed || 0, - currentAction: armBot.currentAction || null, - }, + // Pick success rate based on errors + const pickAttempts = cyclesCompleted + (errorCountsRef.current[armBot.modelUuid] || 0); + const pickSuccessRate = pickAttempts > 0 ? (cyclesCompleted / pickAttempts) * 100 : 100; - timeMetrics: { - ...timeMetrics, - idleTime: armBot.idleTime || 0, - activeTime: armBot.activeTime || 0, - totalTime, - averageCycleTime: cyclesCompleted > 0 ? totalTime / cyclesCompleted : 0, - }, + return { + assetId: armBot.modelUuid, + assetName: armBot.modelName, + assetType: "roboticArm", - 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, - }, + currentStatus: { + isActive: armBot.isActive, + state: armBot.state, + speed: armBot.speed, + currentAction: armBot.currentAction || null, + }, - efficiency: { - overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100), - availability: timeMetrics.uptime, - performance: 100, - quality: 100, - cycleTimeEfficiency: 100, - }, + timeMetrics: { + ...timeMetrics, + idleTime: armBot.idleTime || 0, + activeTime: armBot.activeTime || 0, + totalTime, + averageCycleTime: actualCycleTime, + }, - quality: { - errorRate: 0, - errorFrequency: 0, - successRate: 100, - stateTransitions: [], - pickSuccessRate: 100, - placeAccuracy: 100, - }, + throughput: { + itemsPerHour: totalTime > 0 ? (cyclesCompleted / totalTime) * 3600 : 0, + itemsPerDay: totalTime > 0 ? (cyclesCompleted / totalTime) * 86400 : 0, + materialFlowRate: armBot.speed, + capacityUtilization: timeMetrics.utilizationRate * 100, + cyclesCompleted, + pickAndPlaceCount: cyclesCompleted, + }, - historicalData: [], - }; - }, []); + efficiency: { + overallEffectiveness: calculateOEE(timeMetrics.uptime, cycleTimeEfficiency, pickSuccessRate), + availability: timeMetrics.uptime, + performance: cycleTimeEfficiency, + quality: pickSuccessRate, + cycleTimeEfficiency, + }, + + quality: { + errorRate: pickAttempts > 0 ? (errorCountsRef.current[armBot.modelUuid] || 0) / pickAttempts : 0, + errorFrequency: totalTime > 0 ? ((errorCountsRef.current[armBot.modelUuid] || 0) / totalTime) * 3600 : 0, + successRate: pickSuccessRate, + stateTransitions: getStateTransitions(armBot.modelUuid), + pickSuccessRate, + placeAccuracy: pickSuccessRate, + }, + + historicalData: (analysis?.assets.find((a) => a.assetId === armBot.modelUuid)?.historicalData || []) as RoboticArmAnalysis["historicalData"], + }; + }, + [analysis] + ); // ============================================================================ // MACHINE ANALYSIS // ============================================================================ - const analyzeMachine = useCallback((machine: any): MachineAnalysis => { - const timeMetrics = calculateTimeMetrics(machine.idleTime || 0, machine.activeTime || 0); + const analyzeMachine = useCallback( + (machine: MachineStatus): MachineAnalysis => { + const timeMetrics = calculateTimeMetrics(machine.idleTime || 0, machine.activeTime || 0, machine.modelUuid); - const totalTime = (machine.idleTime || 0) + (machine.activeTime || 0); - const cyclesCompleted = machine.cyclesCompleted || 0; + const totalTime = (machine.idleTime || 0) + (machine.activeTime || 0); + const cyclesCompleted = completedActionsRef.current[machine.modelUuid] || 0; + const defects = errorCountsRef.current[`${machine.modelUuid}_defects`] || 0; - return { - assetId: machine.modelUuid, - assetName: machine.modelName, - assetType: "machine", + // Get target process time from action + const targetProcessTime = machine.point?.action?.processTime || 30; + const actualProcessTime = cyclesCompleted > 0 ? totalTime / cyclesCompleted : 0; + const targetVsActual = calculatePerformanceRate(actualProcessTime, targetProcessTime); - currentStatus: { - isActive: machine.isActive || false, - state: machine.state || "idle", - currentAction: machine.currentAction || null, - }, + // Quality metrics + const totalParts = cyclesCompleted + defects; + const qualityRate = totalParts > 0 ? ((cyclesCompleted - defects) / totalParts) * 100 : 100; + const defectRate = totalParts > 0 ? (defects / totalParts) * 100 : 0; - timeMetrics: { - ...timeMetrics, - idleTime: machine.idleTime || 0, - activeTime: machine.activeTime || 0, - totalTime, - averageProcessTime: cyclesCompleted > 0 ? totalTime / cyclesCompleted : 0, - }, + return { + assetId: machine.modelUuid, + assetName: machine.modelName, + assetType: "machine", - throughput: { - itemsPerHour: totalTime > 0 ? (cyclesCompleted / totalTime) * 3600 : 0, - itemsPerDay: totalTime > 0 ? (cyclesCompleted / totalTime) * 86400 : 0, - materialFlowRate: 1, - capacityUtilization: timeMetrics.utilizationRate * 100, - cyclesCompleted, - partsProcessed: cyclesCompleted, - }, + currentStatus: { + isActive: machine.isActive, + state: machine.state, + currentAction: machine.currentAction || null, + }, - efficiency: { - overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100), - availability: timeMetrics.uptime, - performance: 100, - quality: 100, - targetVsActual: 100, - }, + timeMetrics: { + ...timeMetrics, + idleTime: machine.idleTime || 0, + activeTime: machine.activeTime || 0, + totalTime, + averageProcessTime: actualProcessTime, + }, - quality: { - errorRate: 0, - errorFrequency: 0, - successRate: 100, - stateTransitions: [], - defectRate: 0, - reworkRate: 0, - }, + throughput: { + itemsPerHour: totalTime > 0 ? (cyclesCompleted / totalTime) * 3600 : 0, + itemsPerDay: totalTime > 0 ? (cyclesCompleted / totalTime) * 86400 : 0, + materialFlowRate: cyclesCompleted > 0 ? cyclesCompleted / (totalTime / 60) : 0, + capacityUtilization: timeMetrics.utilizationRate * 100, + cyclesCompleted, + partsProcessed: cyclesCompleted, + }, - historicalData: [], - }; - }, []); + efficiency: { + overallEffectiveness: calculateOEE(timeMetrics.uptime, targetVsActual, qualityRate), + availability: timeMetrics.uptime, + performance: targetVsActual, + quality: qualityRate, + targetVsActual, + }, + + quality: { + errorRate: totalParts > 0 ? (errorCountsRef.current[machine.modelUuid] || 0) / totalParts : 0, + errorFrequency: totalTime > 0 ? ((errorCountsRef.current[machine.modelUuid] || 0) / totalTime) * 3600 : 0, + successRate: qualityRate, + stateTransitions: getStateTransitions(machine.modelUuid), + defectRate, + reworkRate: defectRate * 0.3, // Assume 30% of defects are reworked + }, + + historicalData: (analysis?.assets.find((a) => a.assetId === machine.modelUuid)?.historicalData || []) as MachineAnalysis["historicalData"], + }; + }, + [analysis] + ); // ============================================================================ // STORAGE ANALYSIS // ============================================================================ - const analyzeStorage = useCallback((storage: any): StorageAnalysis => { - const timeMetrics = calculateTimeMetrics(storage.idleTime || 0, storage.activeTime || 0); + const analyzeStorage = useCallback( + (storage: StorageUnitStatus): StorageAnalysis => { + const timeMetrics = calculateTimeMetrics(storage.idleTime || 0, storage.activeTime || 0, storage.modelUuid); - const utilizationRate = storage.storageCapacity > 0 ? (storage.currentLoad / storage.storageCapacity) * 100 : 0; + const currentLoad = storage.currentLoad || 0; + const capacity = storage.storageCapacity || 1; + const utilizationRate = (currentLoad / capacity) * 100; - return { - assetId: storage.modelUuid, - assetName: storage.modelName, - assetType: "storage", + const storeOps = completedActionsRef.current[`${storage.modelUuid}_store`] || 0; + const retrieveOps = completedActionsRef.current[`${storage.modelUuid}_retrieve`] || 0; + const totalOps = storeOps + retrieveOps; - currentStatus: { - isActive: storage.isActive || false, - state: storage.state || "idle", - currentLoad: storage.currentLoad || 0, - storageCapacity: storage.storageCapacity || 0, - currentMaterials: storage.currentMaterials || [], - }, + // Calculate turnover rate + const totalTime = (storage.idleTime || 0) + (storage.activeTime || 0); + const turnoverRate = totalTime > 0 ? (totalOps / totalTime) * 3600 : 0; // Operations per hour - timeMetrics: { - ...timeMetrics, - idleTime: storage.idleTime || 0, - activeTime: storage.activeTime || 0, - totalTime: (storage.idleTime || 0) + (storage.activeTime || 0), - }, + // Get peak occupancy from previous analysis + const previousAnalysis = analysis?.assets.find((a) => a.assetId === storage.modelUuid); + const previousPeak = previousAnalysis && "capacityMetrics" in previousAnalysis ? previousAnalysis.capacityMetrics.peakOccupancy : utilizationRate; + const peakOccupancy = Math.max(previousPeak, utilizationRate); - capacityMetrics: { - utilizationRate, - averageOccupancy: utilizationRate, - peakOccupancy: storage.peakOccupancy || utilizationRate, - turnoverRate: 0, - }, + return { + assetId: storage.modelUuid, + assetName: storage.modelName, + assetType: "storage", - throughput: { - itemsPerHour: 0, - itemsPerDay: 0, - materialFlowRate: 0, - capacityUtilization: utilizationRate, - storeOperations: storage.storeOperations || 0, - retrieveOperations: storage.retrieveOperations || 0, - totalOperations: (storage.storeOperations || 0) + (storage.retrieveOperations || 0), - }, + currentStatus: { + isActive: storage.isActive, + state: storage.state, + currentLoad, + storageCapacity: capacity, + currentMaterials: storage.currentMaterials, + }, - efficiency: { - overallEffectiveness: calculateOEE(timeMetrics.uptime, utilizationRate, 100), - availability: timeMetrics.uptime, - performance: utilizationRate, - quality: 100, - spaceUtilization: utilizationRate, - }, + timeMetrics: { + ...timeMetrics, + idleTime: storage.idleTime || 0, + activeTime: storage.activeTime || 0, + totalTime, + }, - quality: { - errorRate: 0, - errorFrequency: 0, - successRate: 100, - stateTransitions: [], - }, + capacityMetrics: { + utilizationRate, + averageOccupancy: utilizationRate, + peakOccupancy, + turnoverRate, + }, - occupancyTrends: [], - historicalData: [], - }; - }, []); + throughput: { + itemsPerHour: turnoverRate, + itemsPerDay: turnoverRate * 24, + materialFlowRate: turnoverRate / 60, + capacityUtilization: utilizationRate, + storeOperations: storeOps, + retrieveOperations: retrieveOps, + totalOperations: totalOps, + }, + + efficiency: { + overallEffectiveness: calculateOEE(timeMetrics.uptime, utilizationRate, 100), + availability: timeMetrics.uptime, + performance: utilizationRate, + quality: 100, + spaceUtilization: utilizationRate, + }, + + quality: { + errorRate: totalOps > 0 ? (errorCountsRef.current[storage.modelUuid] || 0) / totalOps : 0, + errorFrequency: totalTime > 0 ? ((errorCountsRef.current[storage.modelUuid] || 0) / totalTime) * 3600 : 0, + successRate: 100, + stateTransitions: getStateTransitions(storage.modelUuid), + }, + + occupancyTrends: [], + historicalData: (analysis?.assets.find((a) => a.assetId === storage.modelUuid)?.historicalData || []) as StorageAnalysis["historicalData"], + }; + }, + [analysis] + ); // ============================================================================ // HUMAN ANALYSIS // ============================================================================ - const analyzeHuman = useCallback((human: any): HumanAnalysis => { - const timeMetrics = calculateTimeMetrics(human.idleTime || 0, human.activeTime || 0); + const analyzeHuman = useCallback( + (human: HumanStatus): HumanAnalysis => { + const timeMetrics = calculateTimeMetrics(human.idleTime || 0, human.activeTime || 0, human.modelUuid); - const totalTime = (human.idleTime || 0) + (human.activeTime || 0); - const actionsCompleted = human.actionsCompleted || 0; + const totalTime = (human.idleTime || 0) + (human.activeTime || 0); + const actionsCompleted = completedActionsRef.current[human.modelUuid] || 0; - return { - assetId: human.modelUuid, - assetName: human.modelName, - assetType: "human", + // Calculate workload distribution + const workerActions = completedActionsRef.current[`${human.modelUuid}_worker`] || 0; + const manufacturerActions = completedActionsRef.current[`${human.modelUuid}_manufacturer`] || 0; + const operatorActions = completedActionsRef.current[`${human.modelUuid}_operator`] || 0; + const assemblerActions = completedActionsRef.current[`${human.modelUuid}_assembler`] || 0; - 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, - }, + const workerTime = completedActionsRef.current[`${human.modelUuid}_worker_time`] || 0; + const manufacturerTime = completedActionsRef.current[`${human.modelUuid}_manufacturer_time`] || 0; + const operatorTime = completedActionsRef.current[`${human.modelUuid}_operator_time`] || 0; + const assemblerTime = completedActionsRef.current[`${human.modelUuid}_assembler_time`] || 0; - timeMetrics: { - ...timeMetrics, - idleTime: human.idleTime || 0, - activeTime: human.activeTime || 0, - totalTime, - scheduledTime: totalTime, - }, + const workloadDistribution = [ + { actionType: "worker", count: workerActions, totalTime: workerTime, percentage: totalTime > 0 ? (workerTime / totalTime) * 100 : 0 }, + { actionType: "manufacturer", count: manufacturerActions, totalTime: manufacturerTime, percentage: totalTime > 0 ? (manufacturerTime / totalTime) * 100 : 0 }, + { actionType: "operator", count: operatorActions, totalTime: operatorTime, percentage: totalTime > 0 ? (operatorTime / totalTime) * 100 : 0 }, + { actionType: "assembler", count: assemblerActions, totalTime: assemblerTime, percentage: totalTime > 0 ? (assemblerTime / totalTime) * 100 : 0 }, + ].filter((w) => w.count > 0); - productivityMetrics: { - actionsCompleted, - actionsPerHour: totalTime > 0 ? (actionsCompleted / totalTime) * 3600 : 0, - averageActionTime: actionsCompleted > 0 ? totalTime / actionsCompleted : 0, - distanceTraveled: human.distanceTraveled || 0, - }, + return { + assetId: human.modelUuid, + assetName: human.modelName, + assetType: "human", - workloadDistribution: [], + currentStatus: { + isActive: human.isActive, + isScheduled: human.isScheduled, + currentPhase: human.currentPhase, + state: human.state, + speed: human.speed, + currentLoad: human.currentLoad, + currentMaterials: human.currentMaterials, + currentAction: human.currentAction || null, + }, - 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, - }, + timeMetrics: { + ...timeMetrics, + idleTime: human.idleTime || 0, + activeTime: human.activeTime || 0, + totalTime, + scheduledTime: totalTime, + }, - quality: { - errorRate: 0, - errorFrequency: 0, - successRate: 100, - stateTransitions: [], - }, + productivityMetrics: { + actionsCompleted, + actionsPerHour: totalTime > 0 ? (actionsCompleted / totalTime) * 3600 : 0, + averageActionTime: actionsCompleted > 0 ? totalTime / actionsCompleted : 0, + distanceTraveled: human.distanceTraveled || 0, + }, - historicalData: [], - }; - }, []); + 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: actionsCompleted > 0 ? (errorCountsRef.current[human.modelUuid] || 0) / actionsCompleted : 0, + errorFrequency: totalTime > 0 ? ((errorCountsRef.current[human.modelUuid] || 0) / totalTime) * 3600 : 0, + successRate: 100, + stateTransitions: getStateTransitions(human.modelUuid), + }, + + historicalData: (analysis?.assets.find((a) => a.assetId === human.modelUuid)?.historicalData || []) as HumanAnalysis["historicalData"], + }; + }, + [analysis] + ); // ============================================================================ // CRANE ANALYSIS // ============================================================================ - const analyzeCrane = useCallback((crane: any): CraneAnalysis => { - const timeMetrics = calculateTimeMetrics(crane.idleTime || 0, crane.activeTime || 0); + const analyzeCrane = useCallback((crane: CraneStatus): CraneAnalysis => { + const timeMetrics = calculateTimeMetrics(crane.idleTime || 0, crane.activeTime || 0, crane.modelUuid); const totalTime = (crane.idleTime || 0) + (crane.activeTime || 0); - const cyclesCompleted = crane.cyclesCompleted || 0; + const cyclesCompleted = completedActionsRef.current[crane.modelUuid] || 0; + const loadsHandled = completedActionsRef.current[`${crane.modelUuid}_loads`] || 0; + const totalLifts = completedActionsRef.current[`${crane.modelUuid}_lifts`] || 0; + const totalLiftHeight = completedActionsRef.current[`${crane.modelUuid}_lift_height`] || 0; + + // Calculate cycle time + const actualCycleTime = cyclesCompleted > 0 ? totalTime / cyclesCompleted : 0; + const idealCycleTime = 20; // 20 seconds ideal + const cycleTimeEfficiency = calculatePerformanceRate(actualCycleTime, idealCycleTime); + + // Calculate lift metrics + const avgLiftHeight = totalLifts > 0 ? totalLiftHeight / totalLifts : 0; + const avgLoadsPerCycle = cyclesCompleted > 0 ? loadsHandled / cyclesCompleted : 0; + + // Lift success rate + const liftAttempts = totalLifts + (errorCountsRef.current[crane.modelUuid] || 0); + const liftSuccessRate = liftAttempts > 0 ? (totalLifts / liftAttempts) * 100 : 100; + + // Load utilization based on max pick up count + const maxPickUpCount = crane.point?.actions?.[0]?.maxPickUpCount || 1; + const loadUtilization = (avgLoadsPerCycle / maxPickUpCount) * 100; return { assetId: crane.modelUuid, @@ -432,13 +937,13 @@ function Analyzer() { 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 || [], + isActive: crane.isActive, + isScheduled: crane.isScheduled, + isCarrying: crane.isCarrying, + currentPhase: crane.currentPhase, + state: crane.state, + currentLoad: crane.currentLoad, + currentMaterials: crane.currentMaterials, currentAction: crane.currentAction || null, }, @@ -447,41 +952,41 @@ function Analyzer() { idleTime: crane.idleTime || 0, activeTime: crane.activeTime || 0, totalTime, - averageCycleTime: cyclesCompleted > 0 ? totalTime / cyclesCompleted : 0, + averageCycleTime: actualCycleTime, }, throughput: { - itemsPerHour: totalTime > 0 ? (cyclesCompleted / totalTime) * 3600 : 0, - itemsPerDay: totalTime > 0 ? (cyclesCompleted / totalTime) * 86400 : 0, - materialFlowRate: 1, + itemsPerHour: totalTime > 0 ? (loadsHandled / totalTime) * 3600 : 0, + itemsPerDay: totalTime > 0 ? (loadsHandled / totalTime) * 86400 : 0, + materialFlowRate: cyclesCompleted > 0 ? cyclesCompleted / (totalTime / 60) : 0, capacityUtilization: timeMetrics.utilizationRate * 100, cyclesCompleted, - loadsHandled: crane.loadsHandled || 0, - averageLoadsPerCycle: cyclesCompleted > 0 ? (crane.loadsHandled || 0) / cyclesCompleted : 0, + loadsHandled, + averageLoadsPerCycle: avgLoadsPerCycle, }, movementMetrics: { - totalLifts: crane.totalLifts || 0, - averageLiftHeight: crane.averageLiftHeight || 0, - movementEfficiency: 100, + totalLifts, + averageLiftHeight: avgLiftHeight, + movementEfficiency: liftSuccessRate, }, efficiency: { - overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100), + overallEffectiveness: calculateOEE(timeMetrics.uptime, cycleTimeEfficiency, liftSuccessRate), availability: timeMetrics.uptime, - performance: 100, - quality: 100, - loadUtilization: 0, - cycleTimeEfficiency: 100, + performance: cycleTimeEfficiency, + quality: liftSuccessRate, + loadUtilization, + cycleTimeEfficiency, }, quality: { - errorRate: 0, - errorFrequency: 0, + errorRate: liftAttempts > 0 ? (errorCountsRef.current[crane.modelUuid] || 0) / liftAttempts : 0, + errorFrequency: totalTime > 0 ? ((errorCountsRef.current[crane.modelUuid] || 0) / totalTime) * 3600 : 0, successRate: 100, - stateTransitions: [], - liftSuccessRate: 100, - positioningAccuracy: 100, + stateTransitions: getStateTransitions(crane.modelUuid), + liftSuccessRate: liftSuccessRate, + positioningAccuracy: liftSuccessRate, }, historicalData: [], diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index cab572e..e84ba61 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -231,6 +231,8 @@ interface CraneEventSchema extends AssetEventSchema { point: CranePointSchema; } +type EventType = "conveyor" | "vehicle" | "roboticArm" | "machine" | "storage" | "human" | "crane"; + type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema | HumanEventSchema | CraneEventSchema; // Statuses @@ -481,6 +483,8 @@ interface TimeMetrics { utilizationRate: number; // activeTime / totalTime mtbf: number; // Mean Time Between Failures mttr: number; // Mean Time To Repair + availability: number; + reliability: number; } interface ThroughputMetrics { @@ -488,6 +492,9 @@ interface ThroughputMetrics { itemsPerDay: number; materialFlowRate: number; capacityUtilization: number; // Percentage + throughputEfficiency: number; + wip: number; // Work In Progress + bottleneckIndex: number; } interface EfficiencyMetrics { @@ -509,6 +516,32 @@ interface QualityMetrics { }[]; } +interface CostMetrics { + operatingCost: number; // Cost per time unit + maintenanceCost: number; // Cost per time unit + energyCost: number; // Cost per time unit + totalCost: number; // Sum of all costs + roi: number; // Return on Investment percentage + valueAdded: number; // Value added per time unit +} + +interface EnergyMetrics { + energyConsumed: number; // Energy units per time unit + energyEfficiency: number; // Output per energy unit + carbonFootprint: number; // CO2 emissions per time unit + powerUsage: number; // Average power usage + energyCost: number; // Cost of energy per time unit +} + +interface ConveyorMaterialMetrics { + wip: number; // Work In Progress + throughput: number; // Materials processed per time unit + avgCycleTime: number; // Average time per material + materialVelocity: number; // Speed of material flow + inventoryTurns: number; // Number of times inventory is cycled + leadTimeVariance: number; // Variance in lead time +} + // ============================================================================ // ASSET-SPECIFIC ANALYSIS // ============================================================================ @@ -525,6 +558,8 @@ interface ConveyorAnalysis { state: string; speed: number; currentProduct: string | null; + currentMaterials: number; + queueLength: number; }; // Time Metrics @@ -532,6 +567,7 @@ interface ConveyorAnalysis { idleTime: number; activeTime: number; totalTime: number; + scheduleAdherence: number; }; // Throughput @@ -541,11 +577,30 @@ interface ConveyorAnalysis { }; // Efficiency - efficiency: EfficiencyMetrics; + efficiency: EfficiencyMetrics & { + timeEfficiency: number; // activeTime vs planned active time + energyEfficiency: number; // Energy consumption per material processed + costEfficiency: number; // Cost per material processed + scheduleEfficiency: number; // Adherence to planned schedule + }; // Quality quality: QualityMetrics; + // Cost + costMetrics: CostMetrics & { + costPerUnit: number; // Cost per item processed + costPerHour: number; // Cost per hour of operation + }; + + // Energy + energyMetrics: EnergyMetrics & { + energyPerUnit: number; // Energy per item processed + }; + + // Material + materialFlow: ConveyorMaterialMetrics; + // Historical Data historicalData: { timestamp: string; @@ -569,6 +624,8 @@ interface VehicleAnalysis { speed: number; currentLoad: number; currentMaterials: { materialType: string; materialId: string }[]; + distanceTraveled: number; + currentRouteEfficiency: number; }; // Time Metrics @@ -592,15 +649,40 @@ interface VehicleAnalysis { averageSpeedActual: number; fuelEfficiency: number; // Distance per energy unit routeEfficiency: number; // Actual vs optimal distance + idleDistance: number; + totalTrips: number; + averageTripDistance: number; + distanceEfficiency: number; // Actual vs planned distance }; // Efficiency efficiency: EfficiencyMetrics & { loadUtilization: number; // Average load / max capacity + timeEfficiency: number; // activeTime vs planned active time + energyEfficiency: number; // Energy consumption per material processed + routeEfficiency: number; // Adherence to optimal routes + costEfficiency: number; // Cost per material processed }; // Quality - quality: QualityMetrics; + quality: QualityMetrics & { + onTimeDelivery: number; // Percentage of on-time deliveries + damageRate: number; // Damaged materials per total delivered + accuracyRate: number; // Correct deliveries per total deliveries + }; + + // Cost + costMetrics: CostMetrics & { + costPerMile: number; // Cost per mile traveled + costPerTrip: number; // Cost per trip + costPerLoad: number; // Cost per load delivered + }; + + // Energy + energyMetrics: EnergyMetrics & { + energyPerMile: number; // Energy per mile traveled + energyPerTrip: number; // Energy per trip + }; // Historical Data historicalData: {