Files
Dwinzo_Demo/app/src/modules/simulation/analyzer/analyzer.tsx

1629 lines
74 KiB
TypeScript
Raw Normal View History

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, materialStore, analysisStore } = useSceneContext();
const { conveyors } = conveyorStore();
const { machines } = machineStore();
const { armBots } = armBotStore();
const { humans } = humanStore();
const { vehicles } = vehicleStore();
const { cranes } = craneStore();
const { storageUnits } = storageUnitStore();
const { materials, getMaterialsByModel } = materialStore();
const { setAnalysis, setAnalyzing, analysis } = analysisStore();
const historicalDataRef = useRef<Record<string, any[]>>({});
const materialHistoryRef = useRef<MaterialHistoryEntry[]>([]);
const queueLengthsRef = useRef<Record<string, { timestamp: number; length: number }[]>>({});
const analysisIntervalRef = useRef<NodeJS.Timeout | null>(null);
const startTimeRef = useRef<string>(new Date().toISOString());
const errorCountsRef = useRef<Record<string, number>>({});
const completedActionsRef = useRef<Record<string, number>>({});
const stateTransitionsRef = useRef<Record<string, any[]>>({});
// ============================================================================
// ENHANCED UTILITY FUNCTIONS
// ============================================================================
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;
// 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 {
uptime,
downtime,
utilizationRate,
mtbf,
mttr,
reliability,
totalTime,
scheduleAdherence,
};
};
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
// 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";
}
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 = getMaterialsByModel(assetId).length;
const materialHistory = materialHistoryRef.current.filter((m) => m.material.current?.modelUuid === assetId || m.material.previous?.modelUuid === 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);
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,
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
};
};
const calculateOEE = (availability: number, performance: number, quality: number) => {
return (availability * performance * quality) / 10000;
};
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<string, any>);
return Object.values(grouped).map((g: any) => ({
fromState: g.fromState,
toState: g.toState,
count: g.count,
averageTime: g.totalTime / g.count,
}));
};
// ============================================================================
// 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,
idealThroughput * (timeMetrics.totalTime / 3600),
timeMetrics.totalTime,
materialsProcessed * 2, // 2 seconds per item ideal
"conveyor"
);
const costMetrics = calculateCostMetrics(conveyor.modelUuid, "conveyor", conveyor.activeTime || 0, materialsProcessed);
const energyMetrics = calculateEnergyMetrics("conveyor", conveyor.activeTime || 0);
// Update historical data
const timestamp = new Date().toISOString();
const newEntry = {
timestamp,
isActive: conveyor.isActive,
speed: conveyor.speed,
state: conveyor.state,
materialsCount: materialFlow.currentMaterials,
throughput: actualThroughput,
performance: performance.performanceRate,
};
const currentData = historicalDataRef.current[conveyor.modelUuid] || [];
historicalDataRef.current[conveyor.modelUuid] = [...currentData, newEntry].slice(-100);
// Calculate queue if applicable
const queueLength = materialFlow.wip;
const currentQueueData = queueLengthsRef.current[conveyor.modelUuid] || [];
queueLengthsRef.current[conveyor.modelUuid] = [...currentQueueData, { timestamp: Date.now(), length: queueLength }].slice(-100);
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]
);
// ============================================================================
// ENHANCED VEHICLE ANALYSIS (apply similar enhancements to other assets)
// ============================================================================
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 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);
// Performance calculations
const loadCapacity = vehicle.point?.action?.loadCapacity || 1;
const avgLoad = tripsCompleted > 0 ? totalLoadsDelivered / tripsCompleted : 0;
const loadUtilization = (avgLoad / loadCapacity) * 100;
const idealTripTime = 60;
const actualTripTime = tripsCompleted > 0 ? timeMetrics.totalTime / tripsCompleted : 0;
const idealThroughput = loadCapacity * (3600 / idealTripTime);
const actualThroughput = materialFlow.throughput * 3600;
const performance = calculatePerformanceMetrics(totalLoadsDelivered, idealThroughput * (timeMetrics.totalTime / 3600), timeMetrics.totalTime, idealTripTime * tripsCompleted, "vehicle");
// 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();
const currentData = historicalDataRef.current[vehicle.modelUuid] || [];
historicalDataRef.current[vehicle.modelUuid] = [...currentData, {
timestamp,
phase: vehicle.currentPhase,
load: vehicle.currentLoad,
distanceTraveled: actualDistance,
state: vehicle.state,
performance: performance.performanceRate,
speed: vehicle.speed,
tripsCompleted,
}].slice(-100);
return {
assetId: vehicle.modelUuid,
assetName: vehicle.modelName,
assetType: "vehicle",
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,
},
timeMetrics: {
...timeMetrics,
idleTime: vehicle.idleTime || 0,
activeTime: vehicle.activeTime || 0,
totalTime: timeMetrics.totalTime,
averageTripTime: actualTripTime,
availability: timeMetrics.uptime,
reliability: timeMetrics.reliability,
},
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]
);
// ============================================================================
// ENHANCED ROBOTIC ARM ANALYSIS
// ============================================================================
const analyzeRoboticArm = useCallback(
(armBot: ArmBotStatus): RoboticArmAnalysis => {
const errorCount = errorCountsRef.current[armBot.modelUuid] || 0;
const timeMetrics = calculateAdvancedTimeMetrics(armBot.idleTime || 0, armBot.activeTime || 0, armBot.modelUuid, errorCount);
const materialFlow = calculateMaterialFlowMetricsForAsset(armBot.modelUuid);
const cyclesCompleted = completedActionsRef.current[armBot.modelUuid] || 0;
const pickAndPlaceCount = completedActionsRef.current[`${armBot.modelUuid}_pickplace`] || cyclesCompleted;
const defects = errorCountsRef.current[`${armBot.modelUuid}_defects`] || 0;
const qualityMetrics = calculateQualityMetrics(armBot.modelUuid, pickAndPlaceCount, defects);
// Performance calculations
const idealCycleTime = 5; // 5 seconds ideal
const actualCycleTime = cyclesCompleted > 0 ? timeMetrics.totalTime / cyclesCompleted : 0;
const idealThroughput = (3600 / idealCycleTime) * 1; // 1 item per cycle
const actualThroughput = materialFlow.throughput * 3600;
const performance = calculatePerformanceMetrics(pickAndPlaceCount, idealThroughput * (timeMetrics.totalTime / 3600), timeMetrics.totalTime, idealCycleTime * cyclesCompleted, "roboticArm");
const costMetrics = calculateCostMetrics(armBot.modelUuid, "roboticArm", armBot.activeTime || 0, pickAndPlaceCount);
const energyMetrics = calculateEnergyMetrics("roboticArm", armBot.activeTime || 0);
// Calculate success rates
const pickAttempts = pickAndPlaceCount + (errorCountsRef.current[armBot.modelUuid] || 0);
const pickSuccessRate = pickAttempts > 0 ? (pickAndPlaceCount / pickAttempts) * 100 : 100;
const placeAccuracy = pickSuccessRate * 0.98; // Assuming 98% of successful picks are placed correctly
// Update historical data
const timestamp = new Date().toISOString();
const newEntry = {
timestamp,
cycleTime: actualCycleTime,
actionType: armBot.currentAction?.actionName || "unknown",
isActive: armBot.isActive,
state: armBot.state,
speed: armBot.speed,
cyclesCompleted,
successRate: pickSuccessRate,
performance: performance.performanceRate,
};
const currentData = historicalDataRef.current[armBot.modelUuid] || [];
historicalDataRef.current[armBot.modelUuid] = [...currentData, newEntry].slice(-100);
return {
assetId: armBot.modelUuid,
assetName: armBot.modelName,
assetType: "roboticArm",
currentStatus: {
isActive: armBot.isActive,
state: armBot.state,
speed: armBot.speed,
currentAction: armBot.currentAction || null,
},
timeMetrics: {
...timeMetrics,
idleTime: armBot.idleTime || 0,
activeTime: armBot.activeTime || 0,
totalTime: timeMetrics.totalTime,
averageCycleTime: actualCycleTime,
availability: timeMetrics.uptime,
reliability: timeMetrics.reliability,
},
throughput: {
itemsPerHour: actualThroughput,
itemsPerDay: actualThroughput * 24,
materialFlowRate: armBot.speed,
capacityUtilization: timeMetrics.utilizationRate * 100,
cyclesCompleted,
pickAndPlaceCount,
throughputEfficiency: (actualThroughput / Math.max(1, idealThroughput)) * 100,
wip: materialFlow.wip,
bottleneckIndex: timeMetrics.utilizationRate > 0.85 ? 1 : 0,
},
efficiency: {
overallEffectiveness: calculateOEE(timeMetrics.uptime, performance.performanceRate, qualityMetrics.firstPassYield),
availability: timeMetrics.uptime,
performance: performance.performanceRate,
quality: qualityMetrics.firstPassYield,
cycleTimeEfficiency: performance.timeEfficiency,
},
quality: {
...qualityMetrics,
pickSuccessRate,
placeAccuracy,
},
// Add cost metrics
costMetrics: {
operatingCost: costMetrics.operatingCost,
maintenanceCost: costMetrics.maintenanceCost,
energyCost: energyMetrics.energyCost,
totalCost: costMetrics.totalCost,
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: pickAndPlaceCount > 0 ? energyMetrics.energyConsumed / pickAndPlaceCount : 0,
},
historicalData: historicalDataRef.current[armBot.modelUuid] || [],
};
},
[materials, analysis]
);
// ============================================================================
// ENHANCED MACHINE ANALYSIS
// ============================================================================
const analyzeMachine = useCallback(
(machine: MachineStatus): MachineAnalysis => {
const errorCount = errorCountsRef.current[machine.modelUuid] || 0;
const timeMetrics = calculateAdvancedTimeMetrics(machine.idleTime || 0, machine.activeTime || 0, machine.modelUuid, errorCount);
const materialFlow = calculateMaterialFlowMetricsForAsset(machine.modelUuid);
const cyclesCompleted = completedActionsRef.current[machine.modelUuid] || 0;
const partsProcessed = completedActionsRef.current[`${machine.modelUuid}_parts`] || cyclesCompleted;
const defects = errorCountsRef.current[`${machine.modelUuid}_defects`] || 0;
const qualityMetrics = calculateQualityMetrics(machine.modelUuid, partsProcessed, defects);
// Performance calculations
const targetProcessTime = machine.point?.action?.processTime || 30;
const actualProcessTime = cyclesCompleted > 0 ? timeMetrics.totalTime / cyclesCompleted : 0;
const idealThroughput = (3600 / targetProcessTime) * 1; // 1 part per process
const actualThroughput = materialFlow.throughput * 3600;
const performance = calculatePerformanceMetrics(partsProcessed, idealThroughput * (timeMetrics.totalTime / 3600), timeMetrics.totalTime, targetProcessTime * cyclesCompleted, "machine");
const costMetrics = calculateCostMetrics(machine.modelUuid, "machine", machine.activeTime || 0, partsProcessed);
const energyMetrics = calculateEnergyMetrics("machine", machine.activeTime || 0);
// Quality calculations
const totalParts = partsProcessed + defects;
const defectRate = totalParts > 0 ? (defects / totalParts) * 100 : 0;
const reworkRate = defectRate * 0.3; // Assume 30% of defects are reworked
const scrapRate = defectRate * 0.7; // Assume 70% are scrapped
// Update historical data
const timestamp = new Date().toISOString();
const currentData = historicalDataRef.current[machine.modelUuid] || [];
historicalDataRef.current[machine.modelUuid] = [...currentData, {
timestamp,
processTime: actualProcessTime,
partsProcessed,
isActive: machine.isActive,
state: machine.state,
defectRate,
performance: performance.performanceRate,
}].slice(-100);
return {
assetId: machine.modelUuid,
assetName: machine.modelName,
assetType: "machine",
currentStatus: {
isActive: machine.isActive,
state: machine.state,
currentAction: machine.currentAction || null,
},
timeMetrics: {
...timeMetrics,
idleTime: machine.idleTime || 0,
activeTime: machine.activeTime || 0,
totalTime: timeMetrics.totalTime,
averageProcessTime: actualProcessTime,
availability: timeMetrics.uptime,
reliability: timeMetrics.reliability,
},
throughput: {
itemsPerHour: actualThroughput,
itemsPerDay: actualThroughput * 24,
materialFlowRate: partsProcessed > 0 ? partsProcessed / (timeMetrics.totalTime / 60) : 0,
capacityUtilization: timeMetrics.utilizationRate * 100,
cyclesCompleted,
partsProcessed,
throughputEfficiency: (actualThroughput / Math.max(1, idealThroughput)) * 100,
wip: materialFlow.wip,
bottleneckIndex: timeMetrics.utilizationRate > 0.85 ? 1 : 0,
},
efficiency: {
overallEffectiveness: calculateOEE(timeMetrics.uptime, performance.performanceRate, qualityMetrics.firstPassYield),
availability: timeMetrics.uptime,
performance: performance.performanceRate,
quality: qualityMetrics.firstPassYield,
targetVsActual: performance.timeEfficiency,
},
quality: {
...qualityMetrics,
defectRate,
reworkRate,
scrapRate,
},
// Add cost metrics
costMetrics: {
operatingCost: costMetrics.operatingCost,
maintenanceCost: costMetrics.maintenanceCost,
energyCost: energyMetrics.energyCost,
totalCost: costMetrics.totalCost,
roi: costMetrics.roi,
valueAdded: costMetrics.valueAdded,
costPerUnit: partsProcessed > 0 ? costMetrics.totalCost / partsProcessed : 0,
},
// Add energy metrics
energyMetrics: {
energyConsumed: energyMetrics.energyConsumed,
energyEfficiency: energyMetrics.energyEfficiency,
carbonFootprint: energyMetrics.carbonFootprint,
powerUsage: energyMetrics.powerUsage,
energyCost: energyMetrics.energyCost,
energyPerUnit: partsProcessed > 0 ? energyMetrics.energyConsumed / partsProcessed : 0,
},
historicalData: historicalDataRef.current[machine.modelUuid] || [],
};
},
[materials, analysis]
);
// ============================================================================
// ENHANCED STORAGE ANALYSIS
// ============================================================================
const analyzeStorage = useCallback(
(storage: StorageUnitStatus): StorageAnalysis => {
const errorCount = errorCountsRef.current[storage.modelUuid] || 0;
const timeMetrics = calculateAdvancedTimeMetrics(storage.idleTime || 0, storage.activeTime || 0, storage.modelUuid, errorCount);
const currentLoad = storage.currentLoad || 0;
const capacity = storage.storageCapacity || 1;
const utilizationRate = (currentLoad / capacity) * 100;
const storeOps = completedActionsRef.current[`${storage.modelUuid}_store`] || 0;
const retrieveOps = completedActionsRef.current[`${storage.modelUuid}_retrieve`] || 0;
const totalOps = storeOps + retrieveOps;
const qualityMetrics = calculateQualityMetrics(storage.modelUuid, totalOps, errorCount);
// Calculate turnover rate
const turnoverRate = timeMetrics.totalTime > 0 ? (totalOps / timeMetrics.totalTime) * 3600 : 0;
// Occupancy trends
const timestamp = new Date().toISOString();
if (!historicalDataRef.current[storage.modelUuid]) {
historicalDataRef.current[storage.modelUuid] = [];
}
historicalDataRef.current[storage.modelUuid].push({
timestamp,
currentLoad,
utilizationRate,
operation: storeOps > retrieveOps ? "store" : "retrieve",
totalOps,
state: storage.state,
});
// Calculate peak occupancy from historical data
const occupancyData = historicalDataRef.current[storage.modelUuid] || [];
const peakOccupancy = occupancyData.length > 0 ? Math.max(...occupancyData.map((d) => d.utilizationRate)) : utilizationRate;
const averageOccupancy = occupancyData.length > 0 ? occupancyData.reduce((sum, d) => sum + d.utilizationRate, 0) / occupancyData.length : utilizationRate;
// Calculate occupancy trends for the last hour
const hourAgo = Date.now() - 3600000;
const recentOccupancy = occupancyData.filter((d) => new Date(d.timestamp).getTime() > hourAgo).map((d) => d.utilizationRate);
const occupancyTrend = recentOccupancy.length > 1 ? ((recentOccupancy[recentOccupancy.length - 1] - recentOccupancy[0]) / recentOccupancy[0]) * 100 : 0;
const costMetrics = calculateCostMetrics(storage.modelUuid, "storage", storage.activeTime || 0, totalOps);
const energyMetrics = calculateEnergyMetrics("storage", storage.activeTime || 0);
return {
assetId: storage.modelUuid,
assetName: storage.modelName,
assetType: "storage",
currentStatus: {
isActive: storage.isActive,
state: storage.state,
currentLoad,
storageCapacity: capacity,
currentMaterials: storage.currentMaterials,
},
timeMetrics: {
...timeMetrics,
idleTime: storage.idleTime || 0,
activeTime: storage.activeTime || 0,
totalTime: timeMetrics.totalTime,
availability: timeMetrics.uptime,
reliability: timeMetrics.reliability,
},
capacityMetrics: {
utilizationRate,
averageOccupancy,
peakOccupancy,
turnoverRate,
occupancyTrend,
},
throughput: {
itemsPerHour: turnoverRate,
itemsPerDay: turnoverRate * 24,
materialFlowRate: turnoverRate / 60,
capacityUtilization: utilizationRate,
storeOperations: storeOps,
retrieveOperations: retrieveOps,
totalOperations: totalOps,
throughputEfficiency: (totalOps / Math.max(1, capacity * 24)) * 100,
wip: currentLoad,
bottleneckIndex: utilizationRate > 0.9 ? 1 : 0,
},
efficiency: {
overallEffectiveness: calculateOEE(timeMetrics.uptime, utilizationRate, qualityMetrics.firstPassYield),
availability: timeMetrics.uptime,
performance: utilizationRate,
quality: qualityMetrics.firstPassYield,
spaceUtilization: utilizationRate,
},
quality: qualityMetrics,
// Add cost metrics
costMetrics: {
operatingCost: costMetrics.operatingCost,
maintenanceCost: costMetrics.maintenanceCost,
energyCost: energyMetrics.energyCost,
totalCost: costMetrics.totalCost,
roi: costMetrics.roi,
valueAdded: costMetrics.valueAdded,
costPerUnit: totalOps > 0 ? costMetrics.totalCost / totalOps : 0,
costPerStorageHour: capacity > 0 ? costMetrics.totalCost / (capacity * (timeMetrics.totalTime / 3600)) : 0,
},
// Add energy metrics
energyMetrics: {
energyConsumed: energyMetrics.energyConsumed,
energyEfficiency: energyMetrics.energyEfficiency,
carbonFootprint: energyMetrics.carbonFootprint,
powerUsage: energyMetrics.powerUsage,
energyCost: energyMetrics.energyCost,
energyPerUnit: totalOps > 0 ? energyMetrics.energyConsumed / totalOps : 0,
},
occupancyTrends: occupancyData.slice(-100).map((d) => ({
timestamp: d.timestamp,
occupancy: d.currentLoad,
utilizationRate: d.utilizationRate,
})),
historicalData: historicalDataRef.current[storage.modelUuid] || [],
};
},
[analysis]
);
// ============================================================================
// ENHANCED HUMAN ANALYSIS
// ============================================================================
const analyzeHuman = useCallback(
(human: HumanStatus): HumanAnalysis => {
const errorCount = errorCountsRef.current[human.modelUuid] || 0;
const timeMetrics = calculateAdvancedTimeMetrics(human.idleTime || 0, human.activeTime || 0, human.modelUuid, errorCount);
const actionsCompleted = completedActionsRef.current[human.modelUuid] || 0;
const distanceTraveled = human.distanceTraveled || 0;
const currentLoad = human.currentLoad || 0;
const loadCapacity = human.point?.actions?.[0]?.loadCapacity || 1;
const loadUtilization = (currentLoad / loadCapacity) * 100;
// Workload distribution calculations
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;
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;
const totalActionTime = workerTime + manufacturerTime + operatorTime + assemblerTime;
const workloadDistribution = [
{ actionType: "worker", count: workerActions, totalTime: workerTime, percentage: totalActionTime > 0 ? (workerTime / totalActionTime) * 100 : 0 },
{ actionType: "manufacturer", count: manufacturerActions, totalTime: manufacturerTime, percentage: totalActionTime > 0 ? (manufacturerTime / totalActionTime) * 100 : 0 },
{ actionType: "operator", count: operatorActions, totalTime: operatorTime, percentage: totalActionTime > 0 ? (operatorTime / totalActionTime) * 100 : 0 },
{ actionType: "assembler", count: assemblerActions, totalTime: assemblerTime, percentage: totalActionTime > 0 ? (assemblerTime / totalActionTime) * 100 : 0 },
].filter((w) => w.count > 0);
// Performance calculations
const idealActionsPerHour = 60; // 60 actions per hour ideal
const actualActionsPerHour = timeMetrics.totalTime > 0 ? (actionsCompleted / timeMetrics.totalTime) * 3600 : 0;
const performanceRate = Math.min((actualActionsPerHour / idealActionsPerHour) * 100, 100);
const qualityMetrics = calculateQualityMetrics(human.modelUuid, actionsCompleted, errorCount);
const costMetrics = calculateCostMetrics(human.modelUuid, "human", human.activeTime || 0, actionsCompleted);
const energyMetrics = calculateEnergyMetrics("human", human.activeTime || 0);
// Update historical data
const timestamp = new Date().toISOString();
const newEntry = {
timestamp,
actionType: human.currentAction?.actionName || "unknown",
duration: timeMetrics.totalTime,
distanceTraveled,
isActive: human.isActive,
state: human.state,
load: currentLoad,
performance: performanceRate,
};
const currentData = historicalDataRef.current[human.modelUuid] || [];
historicalDataRef.current[human.modelUuid] = [...currentData, newEntry].slice(-100);
return {
assetId: human.modelUuid,
assetName: human.modelName,
assetType: "human",
currentStatus: {
isActive: human.isActive,
isScheduled: human.isScheduled,
currentPhase: human.currentPhase,
state: human.state,
speed: human.speed,
currentLoad,
currentMaterials: human.currentMaterials,
currentAction: human.currentAction || null,
loadUtilization,
},
timeMetrics: {
...timeMetrics,
idleTime: human.idleTime || 0,
activeTime: human.activeTime || 0,
totalTime: timeMetrics.totalTime,
scheduledTime: timeMetrics.totalTime,
availability: timeMetrics.uptime,
reliability: timeMetrics.reliability,
},
productivityMetrics: {
actionsCompleted,
actionsPerHour: actualActionsPerHour,
averageActionTime: actionsCompleted > 0 ? timeMetrics.totalTime / actionsCompleted : 0,
distanceTraveled,
averageSpeed: timeMetrics.totalTime > 0 ? distanceTraveled / timeMetrics.totalTime : 0,
loadEfficiency: loadUtilization,
},
workloadDistribution,
efficiency: {
overallEffectiveness: calculateOEE(timeMetrics.uptime, performanceRate, qualityMetrics.firstPassYield),
availability: timeMetrics.uptime,
performance: performanceRate,
quality: qualityMetrics.firstPassYield,
laborProductivity: actualActionsPerHour,
utilizationRate: timeMetrics.utilizationRate * 100,
},
quality: qualityMetrics,
// Add cost metrics
costMetrics: {
operatingCost: costMetrics.operatingCost,
maintenanceCost: costMetrics.maintenanceCost,
energyCost: energyMetrics.energyCost,
totalCost: costMetrics.totalCost,
roi: costMetrics.roi,
valueAdded: costMetrics.valueAdded,
costPerAction: actionsCompleted > 0 ? costMetrics.totalCost / actionsCompleted : 0,
costPerHour: costMetrics.costPerHour,
},
// Add energy metrics
energyMetrics: {
energyConsumed: energyMetrics.energyConsumed,
energyEfficiency: energyMetrics.energyEfficiency,
carbonFootprint: energyMetrics.carbonFootprint,
powerUsage: energyMetrics.powerUsage,
energyCost: energyMetrics.energyCost,
energyPerAction: actionsCompleted > 0 ? energyMetrics.energyConsumed / actionsCompleted : 0,
},
historicalData: historicalDataRef.current[human.modelUuid] || [],
};
},
[analysis]
);
// ============================================================================
// ENHANCED CRANE ANALYSIS
// ============================================================================
const analyzeCrane = useCallback(
(crane: CraneStatus): CraneAnalysis => {
const errorCount = errorCountsRef.current[crane.modelUuid] || 0;
const timeMetrics = calculateAdvancedTimeMetrics(crane.idleTime || 0, crane.activeTime || 0, crane.modelUuid, errorCount);
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;
const qualityMetrics = calculateQualityMetrics(crane.modelUuid, loadsHandled, errorCount);
// Performance calculations
const idealCycleTime = 20; // 20 seconds ideal
const actualCycleTime = cyclesCompleted > 0 ? timeMetrics.totalTime / cyclesCompleted : 0;
const idealThroughput = (3600 / idealCycleTime) * 1; // 1 load per cycle
const actualThroughput = cyclesCompleted > 0 ? (loadsHandled / timeMetrics.totalTime) * 3600 : 0;
const performance = calculatePerformanceMetrics(loadsHandled, idealThroughput * (timeMetrics.totalTime / 3600), timeMetrics.totalTime, idealCycleTime * cyclesCompleted, "crane");
// Lift metrics
const avgLiftHeight = totalLifts > 0 ? totalLiftHeight / totalLifts : 0;
const avgLoadsPerCycle = cyclesCompleted > 0 ? loadsHandled / cyclesCompleted : 0;
// Success rates
const liftAttempts = totalLifts + errorCount;
const liftSuccessRate = liftAttempts > 0 ? (totalLifts / liftAttempts) * 100 : 100;
const positioningAccuracy = liftSuccessRate * 0.98; // Slightly lower than success rate
// Load utilization
const maxPickUpCount = crane.point?.actions?.[0]?.maxPickUpCount || 1;
const loadUtilization = (avgLoadsPerCycle / maxPickUpCount) * 100;
const costMetrics = calculateCostMetrics(crane.modelUuid, "crane", crane.activeTime || 0, loadsHandled);
const energyMetrics = calculateEnergyMetrics("crane", crane.activeTime || 0);
// Movement efficiency calculation
const movementEfficiency = liftSuccessRate * 0.8 + positioningAccuracy * 0.2; // Weighted average
// Update historical data
const timestamp = new Date().toISOString();
const newEntry = {
timestamp,
cycleTime: actualCycleTime,
loadsHandled,
isActive: crane.isActive,
state: crane.state,
isCarrying: crane.isCarrying,
loadUtilization,
performance: performance.performanceRate,
};
const currentData = historicalDataRef.current[crane.modelUuid] || [];
historicalDataRef.current[crane.modelUuid] = [...currentData, newEntry].slice(-100);
return {
assetId: crane.modelUuid,
assetName: crane.modelName,
assetType: "crane",
currentStatus: {
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,
loadUtilization,
},
timeMetrics: {
...timeMetrics,
idleTime: crane.idleTime || 0,
activeTime: crane.activeTime || 0,
totalTime: timeMetrics.totalTime,
averageCycleTime: actualCycleTime,
availability: timeMetrics.uptime,
reliability: timeMetrics.reliability,
},
throughput: {
itemsPerHour: actualThroughput,
itemsPerDay: actualThroughput * 24,
materialFlowRate: loadsHandled > 0 ? loadsHandled / (timeMetrics.totalTime / 60) : 0,
capacityUtilization: timeMetrics.utilizationRate * 100,
cyclesCompleted,
loadsHandled,
averageLoadsPerCycle: avgLoadsPerCycle,
throughputEfficiency: (actualThroughput / Math.max(1, idealThroughput)) * 100,
wip: crane.currentLoad,
bottleneckIndex: timeMetrics.utilizationRate > 0.85 ? 1 : 0,
},
movementMetrics: {
totalLifts,
averageLiftHeight: avgLiftHeight,
movementEfficiency,
loadEfficiency: loadUtilization,
cycleEfficiency: performance.timeEfficiency,
},
efficiency: {
overallEffectiveness: calculateOEE(timeMetrics.uptime, performance.performanceRate, qualityMetrics.firstPassYield),
availability: timeMetrics.uptime,
performance: performance.performanceRate,
quality: qualityMetrics.firstPassYield,
loadUtilization,
cycleTimeEfficiency: performance.timeEfficiency,
},
quality: {
...qualityMetrics,
liftSuccessRate,
positioningAccuracy,
},
// Add cost metrics
costMetrics: {
operatingCost: costMetrics.operatingCost,
maintenanceCost: costMetrics.maintenanceCost,
energyCost: energyMetrics.energyCost,
totalCost: costMetrics.totalCost,
roi: costMetrics.roi,
valueAdded: costMetrics.valueAdded,
costPerLift: totalLifts > 0 ? costMetrics.totalCost / totalLifts : 0,
costPerCycle: cyclesCompleted > 0 ? costMetrics.totalCost / cyclesCompleted : 0,
},
// Add energy metrics
energyMetrics: {
energyConsumed: energyMetrics.energyConsumed,
energyEfficiency: energyMetrics.energyEfficiency,
carbonFootprint: energyMetrics.carbonFootprint,
powerUsage: energyMetrics.powerUsage,
energyCost: energyMetrics.energyCost,
energyPerLift: totalLifts > 0 ? energyMetrics.energyConsumed / totalLifts : 0,
},
historicalData: historicalDataRef.current[crane.modelUuid] || [],
};
},
[analysis]
);
// ============================================================================
// COMPREHENSIVE SYSTEM-WIDE ANALYSIS
// ============================================================================
const analyzeSystem = useCallback(
(allAssets: AssetAnalysis[]): AnalysisSchema => {
// System-wide calculations
const totalAssets = allAssets.length;
const activeAssets = allAssets.filter((a) => a.currentStatus.isActive).length;
const assetsInError = allAssets.filter((a) => a.currentStatus.state === "error").length;
const assetsIdle = allAssets.filter((a) => !a.currentStatus.isActive && a.currentStatus.state === "idle").length;
// Calculate system OEE (weighted average by throughput)
const totalThroughput = allAssets.reduce((sum, asset) => {
if ("throughput" in asset) {
return sum + asset.throughput.itemsPerHour;
} else if ("productivityMetrics" in asset) {
return sum + asset.productivityMetrics.actionsPerHour;
}
return sum;
}, 0);
const weightedOEE = allAssets.reduce((sum, asset) => {
const weight = ("throughput" in asset ? asset.throughput.itemsPerHour : "productivityMetrics" in asset ? asset.productivityMetrics.actionsPerHour : 1) / Math.max(1, totalThroughput);
return sum + asset.efficiency.overallEffectiveness * weight;
}, 0);
// Calculate system utilization (average of all assets)
const avgUtilization =
allAssets.reduce((sum, a) => {
if ("timeMetrics" in a) {
return sum + a.timeMetrics.utilizationRate;
}
return sum;
}, 0) / totalAssets;
// Calculate total idle and active time
const totalIdleTime = allAssets.reduce((sum, a) => {
if ("timeMetrics" in a) {
return sum + a.timeMetrics.idleTime;
}
return sum;
}, 0);
const totalActiveTime = allAssets.reduce((sum, a) => {
if ("timeMetrics" in a) {
return sum + a.timeMetrics.activeTime;
}
return sum;
}, 0);
const totalSystemTime = totalIdleTime + totalActiveTime;
const systemUptime = totalSystemTime > 0 ? (totalActiveTime / totalSystemTime) * 100 : 0;
// Calculate total costs and value
const totalCost = allAssets.reduce((sum, a) => {
if ("costMetrics" in a) {
return sum + a.costMetrics.totalCost;
}
return sum;
}, 0);
const totalValueAdded = allAssets.reduce((sum, a) => {
if ("costMetrics" in a) {
return sum + a.costMetrics.valueAdded;
}
return sum;
}, 0);
const totalEnergyConsumed = allAssets.reduce((sum, a) => {
if ("energyMetrics" in a) {
return sum + a.energyMetrics.energyConsumed;
}
return sum;
}, 0);
// Group by asset type for detailed analysis
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]) => {
const typeThroughput = assets.reduce((sum, a) => {
if ("throughput" in a) {
return sum + a.throughput.itemsPerHour;
} else if ("productivityMetrics" in a) {
return sum + a.productivityMetrics.actionsPerHour;
}
return sum;
}, 0);
const typeOEE = assets.reduce((sum, a) => sum + a.efficiency.overallEffectiveness, 0) / assets.length;
const typeUtilization =
assets.reduce((sum, a) => {
if ("timeMetrics" in a) {
return sum + a.timeMetrics.utilizationRate;
}
return sum;
}, 0) / assets.length;
const typeCost = assets.reduce((sum, a) => {
if ("costMetrics" in a) {
return sum + a.costMetrics.totalCost;
}
return sum;
}, 0);
return {
assetType: type,
count: assets.length,
averageOEE: typeOEE,
averageUtilization: typeUtilization * 100,
totalThroughput: typeThroughput,
totalCost: typeCost,
throughputPerAsset: typeThroughput / assets.length,
costPerAsset: typeCost / assets.length,
};
});
// Material Flow Analysis
const totalMaterialsInSystem = materials.filter((m) => m.isActive).length;
const completedMaterials = materialHistoryRef.current.length;
const averageResidenceTime =
materialHistoryRef.current.length > 0
? materialHistoryRef.current.reduce((sum, entry) => {
const residenceTime = new Date(entry.removedAt).getTime() - (entry.material.startTime || 0);
return sum + (residenceTime || 0);
}, 0) / materialHistoryRef.current.length
: 0;
// Bottleneck Identification
const bottlenecks = allAssets
.map((asset) => {
let utilizationRate = 0;
let queueLength = 0;
if ("timeMetrics" in asset) {
utilizationRate = asset.timeMetrics.utilizationRate;
}
if ("currentStatus" in asset && "queueLength" in asset.currentStatus) {
queueLength = asset.currentStatus.queueLength as number;
}
const impactScore = utilizationRate * 100 + queueLength * 10;
let severity: "critical" | "high" | "medium" | "low" = "low";
if (utilizationRate > 0.95) severity = "critical";
else if (utilizationRate > 0.9) severity = "high";
else if (utilizationRate > 0.85) severity = "medium";
return {
assetId: asset.assetId,
assetName: asset.assetName,
severity,
utilizationRate: utilizationRate * 100,
queueLength,
impactScore,
};
})
.filter((b) => b.utilizationRate > 80 || b.queueLength > 5)
.sort((a, b) => b.impactScore - a.impactScore);
// Queue Analysis
const queueLengths = allAssets
.filter((asset) => "currentStatus" in asset && "queueLength" in asset.currentStatus)
.map((asset) => ({
assetId: asset.assetId,
assetName: asset.assetName,
queueLength: (asset.currentStatus as any).queueLength || 0,
averageWaitTime: 0, // Could be calculated with more data
}))
.filter((q) => q.queueLength > 0);
// Flow Continuity Analysis
const throughputOverTime = Object.values(historicalDataRef.current)
.flat()
.filter((d) => d.timestamp)
.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
const flowRates = throughputOverTime.map((d) => d.throughput || d.actionsPerHour || 0);
const meanFlowRate = flowRates.length > 0 ? flowRates.reduce((sum, rate) => sum + rate, 0) / flowRates.length : 0;
const variance = flowRates.length > 0 ? flowRates.reduce((sum, rate) => sum + Math.pow(rate - meanFlowRate, 2), 0) / flowRates.length : 0;
const varianceCoefficient = meanFlowRate > 0 ? Math.sqrt(variance) / meanFlowRate : 0;
// Predictive Insights
const maintenanceAlerts = allAssets
.filter((asset) => {
const errorCount = errorCountsRef.current[asset.assetId] || 0;
const uptime = "timeMetrics" in asset ? asset.timeMetrics.uptime : 100;
return uptime < 95 || errorCount > 3;
})
.map((asset) => ({
assetId: asset.assetId,
assetName: asset.assetName,
assetType: asset.assetType,
alertType:
(errorCountsRef.current[asset.assetId] || 0) > 5 ? ("critical" as const) : (errorCountsRef.current[asset.assetId] || 0) > 3 ? ("predictive" as const) : ("preventive" as const),
estimatedTimeToFailure: Math.max(0, 100 - ((asset.timeMetrics as any)?.reliability || 100)) * 1000,
confidence: Math.min(90 + (errorCountsRef.current[asset.assetId] || 0) * 2, 100),
recommendation: (errorCountsRef.current[asset.assetId] || 0) > 5 ? "Immediate maintenance required" : "Schedule preventive maintenance",
}));
// Optimization Opportunities
const optimizationOpportunities = [
...bottlenecks
.filter((b) => b.severity === "critical" || b.severity === "high")
.map((b) => ({
area: "throughput" as const,
description: `High utilization bottleneck at ${b.assetName} (${b.utilizationRate.toFixed(1)}% utilization)`,
potentialImprovement: Math.min(100 - b.utilizationRate, 20),
priority: b.severity === "critical" ? ("high" as const) : ("medium" as const),
})),
{
area: "efficiency" as const,
description: `System OEE at ${weightedOEE.toFixed(1)}% - target improvements available`,
potentialImprovement: Math.max(0, 85 - weightedOEE),
priority: weightedOEE < 80 ? ("high" as const) : ("medium" as const),
},
{
area: "cost" as const,
description: `Total system cost: $${totalCost.toFixed(2)}`,
potentialImprovement: Math.min(20, totalValueAdded > 0 ? (totalCost / totalValueAdded) * 100 : 0),
priority: totalValueAdded < totalCost ? ("high" as const) : ("medium" as const),
},
];
// Trend Analysis
const trendAnalysis = assetTypePerformance.map((type) => {
const historicalTypeData = allAssets
.filter((a) => a.assetType === type.assetType)
.flatMap((a) => historicalDataRef.current[a.assetId] || [])
.filter((d) => d.performance !== undefined);
const recentPerformance = historicalTypeData.slice(-10).map((d) => d.performance);
const olderPerformance = historicalTypeData.slice(-20, -10).map((d) => d.performance);
const recentAvg = recentPerformance.length > 0 ? recentPerformance.reduce((sum, p) => sum + p, 0) / recentPerformance.length : 0;
const olderAvg = olderPerformance.length > 0 ? olderPerformance.reduce((sum, p) => sum + p, 0) / olderPerformance.length : 0;
let trend: "increasing" | "decreasing" | "stable" = "stable";
let changeRate = 0;
if (olderAvg > 0 && recentAvg > 0) {
changeRate = ((recentAvg - olderAvg) / olderAvg) * 100;
if (Math.abs(changeRate) > 5) {
trend = changeRate > 0 ? "increasing" : "decreasing";
}
}
return {
metric: `${type.assetType} Performance`,
trend,
changeRate,
forecast: [recentAvg * 1.02, recentAvg * 1.01, recentAvg], // Simple forecast
};
});
return {
assets: allAssets,
systemPerformance: {
overallOEE: weightedOEE,
systemThroughput: totalThroughput,
systemUtilization: avgUtilization * 100,
assetTypePerformance,
criticalMetrics: {
activeAssets,
totalAssets,
assetsInError,
assetsIdle,
averageIdleTime: totalIdleTime / totalAssets,
totalDowntime: totalIdleTime,
systemUptime,
},
},
materialFlow: {
totalMaterialsInSystem,
materialsCompleted: completedMaterials,
averageResidenceTime,
queueLengths,
bottlenecks: bottlenecks.slice(0, 10), // Top 10 bottlenecks
flowContinuity: {
overallFlowRate: meanFlowRate,
varianceCoefficient,
steadyStateDeviation: varianceCoefficient > 0.3 ? varianceCoefficient * 100 : 0,
},
},
predictiveInsights: {
maintenanceAlerts: maintenanceAlerts.slice(0, 5), // Top 5 alerts
optimizationOpportunities: optimizationOpportunities.slice(0, 5), // Top 5 opportunities
trendAnalysis: trendAnalysis.filter((t) => t.trend !== "stable" || Math.abs(t.changeRate) > 1),
},
analysisTimeRange: {
startTime: startTimeRef.current,
endTime: new Date().toISOString(),
duration: Date.now() - new Date(startTimeRef.current).getTime(),
},
metadata: {
lastUpdated: new Date().toISOString(),
dataPoints: allAssets.length + materials.length + materialHistoryRef.current.length,
analysisVersion: "1.0.0",
totalCost,
totalValueAdded,
totalEnergyConsumed,
energyCost: totalEnergyConsumed * 0.12, // $0.12 per kWh
carbonFootprint: totalEnergyConsumed * 0.5, // 0.5 kg CO2 per kWh
},
};
},
[materials]
);
// ============================================================================
// 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);
}
};
}, [isPlaying, conveyors, vehicles, armBots, machines, humans, cranes, materials]);
return null;
}
export default Analyzer;