diff --git a/app/package-lock.json b/app/package-lock.json index d9ce5b7..91c994b 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -2031,7 +2031,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2043,7 +2043,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -4242,6 +4242,26 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@testing-library/jest-dom": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", @@ -4353,25 +4373,25 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "devOptional": true }, "node_modules/@turf/along": { "version": "7.2.0", @@ -9213,7 +9233,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true }, "node_modules/cross-env": { "version": "7.0.3", @@ -10229,7 +10249,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.3.1" } @@ -15632,7 +15652,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -21288,7 +21308,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -21331,7 +21351,7 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, + "devOptional": true, "dependencies": { "acorn": "^8.11.0" }, @@ -21343,7 +21363,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/tsconfig-paths": { "version": "3.15.0", @@ -21839,7 +21859,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "devOptional": true }, "node_modules/v8-to-istanbul": { "version": "8.1.1", @@ -22932,7 +22952,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx index 7a6962d..8f1e22d 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx @@ -62,7 +62,7 @@ function StorageMechanics() { const handleActionTypeChange = (option: string) => { if (!selectedAction.actionId || !selectedPointData) return; - const event = peekUpdateAction(selectedProduct.productUuid, selectedAction.actionId, { actionType: option as "store" | "retrieve" }); + const event = peekUpdateAction(selectedProduct.productUuid, selectedAction.actionId, { actionType: option === "store" ? "store" : "retrieve" } as StorageAction); if (event) { updateBackend(event); @@ -184,7 +184,13 @@ function StorageMechanics() {
- +
diff --git a/app/src/modules/scene/sceneContext.tsx b/app/src/modules/scene/sceneContext.tsx index 2967ee9..2cec554 100644 --- a/app/src/modules/scene/sceneContext.tsx +++ b/app/src/modules/scene/sceneContext.tsx @@ -29,6 +29,7 @@ import { createHumanStore, HumanStoreType } from "../../store/simulation/useHuma import { createCraneStore, CraneStoreType } from "../../store/simulation/useCraneStore"; import { createSimulationDashboardStore, SimulationDashboardStoreType } from "../../store/simulation/useSimulationDashBoardStore"; +import { createAnalysisStore, AnalysisStoreType } from "../../store/simulation/useAnalysisStore"; import { createThreadsStore, ThreadStoreType } from "../../store/collaboration/useThreadStore"; @@ -66,6 +67,7 @@ type SceneContextValue = { craneStore: CraneStoreType; simulationDashBoardStore: SimulationDashboardStoreType; + analysisStore: AnalysisStoreType; threadStore: ThreadStoreType; @@ -123,6 +125,7 @@ export function SceneProvider({ const craneStore = useMemo(() => createCraneStore(), []); const simulationDashBoardStore = useMemo(() => createSimulationDashboardStore(), []); + const analysisStore = useMemo(() => createAnalysisStore(), []); const threadStore = useMemo(() => createThreadsStore(), []); @@ -157,6 +160,7 @@ export function SceneProvider({ humanStore.getState().clearHumans(); craneStore.getState().clearCranes(); simulationDashBoardStore.getState().clearBlocks(); + analysisStore.getState().clearAnalysis(); threadStore.getState().clearThreads(); collabUsersStore.getState().clearCollabUsers(); humanEventManagerRef.current.humanStates = []; @@ -184,6 +188,7 @@ export function SceneProvider({ humanStore, craneStore, simulationDashBoardStore, + analysisStore, threadStore, collabUsersStore, ] @@ -215,6 +220,7 @@ export function SceneProvider({ humanStore, craneStore, simulationDashBoardStore, + analysisStore, threadStore, collabUsersStore, humanEventManagerRef, @@ -248,6 +254,7 @@ export function SceneProvider({ humanStore, craneStore, simulationDashBoardStore, + analysisStore, threadStore, collabUsersStore, clearStores, diff --git a/app/src/modules/simulation/analysis/simulationAnalysis.tsx b/app/src/modules/simulation/analysis/simulationAnalysis.tsx index 2cfda53..efc02e1 100644 --- a/app/src/modules/simulation/analysis/simulationAnalysis.tsx +++ b/app/src/modules/simulation/analysis/simulationAnalysis.tsx @@ -1,16 +1,15 @@ -import ProductionCapacityData from './productionCapacity/productionCapacityData' -import ThroughPutData from './throughPut/throughPutData' -import ROIData from './ROI/roiData' +import ProductionCapacityData from "./productionCapacity/productionCapacityData"; +import ThroughPutData from "./throughPut/throughPutData"; +import ROIData from "./ROI/roiData"; function SimulationAnalysis() { - return ( <> - ) + ); } -export default SimulationAnalysis +export default SimulationAnalysis; diff --git a/app/src/modules/simulation/analyzer/analyzer.tsx b/app/src/modules/simulation/analyzer/analyzer.tsx new file mode 100644 index 0000000..1d6e597 --- /dev/null +++ b/app/src/modules/simulation/analyzer/analyzer.tsx @@ -0,0 +1,700 @@ +import { useEffect, useCallback, useRef } from "react"; +import { useSceneContext } from "../../scene/sceneContext"; +import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore"; + +function Analyzer() { + const { isPlaying } = usePlayButtonStore(); + const { conveyorStore, machineStore, armBotStore, humanStore, vehicleStore, craneStore, storageUnitStore, analysisStore } = useSceneContext(); + + const { conveyors } = conveyorStore(); + const { machines } = machineStore(); + const { armBots } = armBotStore(); + const { humans } = humanStore(); + const { vehicles } = vehicleStore(); + const { cranes } = craneStore(); + const { storageUnits } = storageUnitStore(); + + const { setAnalysis, setAnalyzing } = analysisStore(); + + const analysisIntervalRef = useRef(null); + const startTimeRef = useRef(new Date().toISOString()); + + // ============================================================================ + // CALCULATION UTILITIES + // ============================================================================ + + const calculateTimeMetrics = (idleTime: number, activeTime: number, totalErrors: number = 0) => { + const totalTime = idleTime + activeTime; + const uptime = totalTime > 0 ? (activeTime / totalTime) * 100 : 0; + const downtime = idleTime; + const utilizationRate = uptime / 100; + + return { + uptime, + downtime, + utilizationRate, + mtbf: totalErrors > 0 ? totalTime / totalErrors : totalTime, + mttr: totalErrors > 0 ? downtime / totalErrors : 0, + }; + }; + + const calculateOEE = (availability: number, performance: number, quality: number) => { + return (availability * performance * quality) / 10000; // All in percentages + }; + + // ============================================================================ + // CONVEYOR ANALYSIS + // ============================================================================ + + const analyzeConveyor = useCallback((conveyor: any): ConveyorAnalysis => { + const timeMetrics = calculateTimeMetrics(conveyor.idleTime || 0, conveyor.activeTime || 0); + + const totalTime = (conveyor.idleTime || 0) + (conveyor.activeTime || 0); + const materialsProcessed = conveyor.materialsProcessed || 0; + + return { + assetId: conveyor.modelUuid, + assetName: conveyor.modelName, + assetType: "conveyor", + + currentStatus: { + isActive: conveyor.isActive || false, + isPaused: conveyor.isPaused || false, + state: conveyor.state || "idle", + speed: conveyor.speed || 0, + currentProduct: conveyor.productUuid || null, + }, + + timeMetrics: { + ...timeMetrics, + idleTime: conveyor.idleTime || 0, + activeTime: conveyor.activeTime || 0, + totalTime, + }, + + throughput: { + itemsPerHour: totalTime > 0 ? (materialsProcessed / totalTime) * 3600 : 0, + itemsPerDay: totalTime > 0 ? (materialsProcessed / totalTime) * 86400 : 0, + materialFlowRate: conveyor.speed || 0, + capacityUtilization: timeMetrics.utilizationRate * 100, + materialsProcessed, + averageProcessingTime: materialsProcessed > 0 ? totalTime / materialsProcessed : 0, + }, + + efficiency: { + overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100), + availability: timeMetrics.uptime, + performance: 100, + quality: 100, + }, + + quality: { + errorRate: 0, + errorFrequency: 0, + successRate: 100, + stateTransitions: [], + }, + + historicalData: [], + }; + }, []); + + // ============================================================================ + // VEHICLE ANALYSIS + // ============================================================================ + + const analyzeVehicle = useCallback((vehicle: any): VehicleAnalysis => { + const timeMetrics = calculateTimeMetrics(vehicle.idleTime || 0, vehicle.activeTime || 0); + + const totalTime = (vehicle.idleTime || 0) + (vehicle.activeTime || 0); + const tripsCompleted = vehicle.tripsCompleted || 0; + + return { + assetId: vehicle.modelUuid, + assetName: vehicle.modelName, + assetType: "vehicle", + + currentStatus: { + isActive: vehicle.isActive || false, + isPicking: vehicle.isPicking || false, + currentPhase: vehicle.currentPhase || "idle", + state: vehicle.state || "idle", + speed: vehicle.speed || 0, + currentLoad: vehicle.currentLoad || 0, + currentMaterials: vehicle.currentMaterials || [], + }, + + timeMetrics: { + ...timeMetrics, + idleTime: vehicle.idleTime || 0, + activeTime: vehicle.activeTime || 0, + totalTime, + averageTripTime: tripsCompleted > 0 ? totalTime / tripsCompleted : 0, + }, + + throughput: { + itemsPerHour: totalTime > 0 ? ((vehicle.currentLoad || 0) / totalTime) * 3600 : 0, + itemsPerDay: totalTime > 0 ? ((vehicle.currentLoad || 0) / totalTime) * 86400 : 0, + materialFlowRate: vehicle.speed || 0, + capacityUtilization: timeMetrics.utilizationRate * 100, + tripsCompleted, + averageLoadsPerTrip: tripsCompleted > 0 ? (vehicle.totalLoadsDelivered || 0) / tripsCompleted : 0, + totalLoadsDelivered: vehicle.totalLoadsDelivered || 0, + }, + + movementMetrics: { + distanceTraveled: vehicle.distanceTraveled || 0, + averageSpeedActual: totalTime > 0 ? (vehicle.distanceTraveled || 0) / totalTime : 0, + fuelEfficiency: 0, + routeEfficiency: 100, + }, + + efficiency: { + overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100), + availability: timeMetrics.uptime, + performance: 100, + quality: 100, + loadUtilization: vehicle.point?.action?.loadCapacity > 0 ? ((vehicle.currentLoad || 0) / vehicle.point.action.loadCapacity) * 100 : 0, + }, + + quality: { + errorRate: 0, + errorFrequency: 0, + successRate: 100, + stateTransitions: [], + }, + + historicalData: [], + }; + }, []); + + // ============================================================================ + // ROBOTIC ARM ANALYSIS + // ============================================================================ + + const analyzeRoboticArm = useCallback((armBot: any): RoboticArmAnalysis => { + const timeMetrics = calculateTimeMetrics(armBot.idleTime || 0, armBot.activeTime || 0); + + const totalTime = (armBot.idleTime || 0) + (armBot.activeTime || 0); + const cyclesCompleted = armBot.cyclesCompleted || 0; + + return { + assetId: armBot.modelUuid, + assetName: armBot.modelName, + assetType: "roboticArm", + + currentStatus: { + isActive: armBot.isActive || false, + state: armBot.state || "idle", + speed: armBot.speed || 0, + currentAction: armBot.currentAction || null, + }, + + timeMetrics: { + ...timeMetrics, + idleTime: armBot.idleTime || 0, + activeTime: armBot.activeTime || 0, + totalTime, + averageCycleTime: cyclesCompleted > 0 ? totalTime / cyclesCompleted : 0, + }, + + throughput: { + itemsPerHour: totalTime > 0 ? (cyclesCompleted / totalTime) * 3600 : 0, + itemsPerDay: totalTime > 0 ? (cyclesCompleted / totalTime) * 86400 : 0, + materialFlowRate: armBot.speed || 0, + capacityUtilization: timeMetrics.utilizationRate * 100, + cyclesCompleted, + pickAndPlaceCount: cyclesCompleted, + }, + + efficiency: { + overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100), + availability: timeMetrics.uptime, + performance: 100, + quality: 100, + cycleTimeEfficiency: 100, + }, + + quality: { + errorRate: 0, + errorFrequency: 0, + successRate: 100, + stateTransitions: [], + pickSuccessRate: 100, + placeAccuracy: 100, + }, + + historicalData: [], + }; + }, []); + + // ============================================================================ + // MACHINE ANALYSIS + // ============================================================================ + + const analyzeMachine = useCallback((machine: any): MachineAnalysis => { + const timeMetrics = calculateTimeMetrics(machine.idleTime || 0, machine.activeTime || 0); + + const totalTime = (machine.idleTime || 0) + (machine.activeTime || 0); + const cyclesCompleted = machine.cyclesCompleted || 0; + + return { + assetId: machine.modelUuid, + assetName: machine.modelName, + assetType: "machine", + + currentStatus: { + isActive: machine.isActive || false, + state: machine.state || "idle", + currentAction: machine.currentAction || null, + }, + + timeMetrics: { + ...timeMetrics, + idleTime: machine.idleTime || 0, + activeTime: machine.activeTime || 0, + totalTime, + averageProcessTime: cyclesCompleted > 0 ? totalTime / cyclesCompleted : 0, + }, + + throughput: { + itemsPerHour: totalTime > 0 ? (cyclesCompleted / totalTime) * 3600 : 0, + itemsPerDay: totalTime > 0 ? (cyclesCompleted / totalTime) * 86400 : 0, + materialFlowRate: 1, + capacityUtilization: timeMetrics.utilizationRate * 100, + cyclesCompleted, + partsProcessed: cyclesCompleted, + }, + + efficiency: { + overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100), + availability: timeMetrics.uptime, + performance: 100, + quality: 100, + targetVsActual: 100, + }, + + quality: { + errorRate: 0, + errorFrequency: 0, + successRate: 100, + stateTransitions: [], + defectRate: 0, + reworkRate: 0, + }, + + historicalData: [], + }; + }, []); + + // ============================================================================ + // STORAGE ANALYSIS + // ============================================================================ + + const analyzeStorage = useCallback((storage: any): StorageAnalysis => { + const timeMetrics = calculateTimeMetrics(storage.idleTime || 0, storage.activeTime || 0); + + const utilizationRate = storage.storageCapacity > 0 ? (storage.currentLoad / storage.storageCapacity) * 100 : 0; + + return { + assetId: storage.modelUuid, + assetName: storage.modelName, + assetType: "storage", + + currentStatus: { + isActive: storage.isActive || false, + state: storage.state || "idle", + currentLoad: storage.currentLoad || 0, + storageCapacity: storage.storageCapacity || 0, + currentMaterials: storage.currentMaterials || [], + }, + + timeMetrics: { + ...timeMetrics, + idleTime: storage.idleTime || 0, + activeTime: storage.activeTime || 0, + totalTime: (storage.idleTime || 0) + (storage.activeTime || 0), + }, + + capacityMetrics: { + utilizationRate, + averageOccupancy: utilizationRate, + peakOccupancy: storage.peakOccupancy || utilizationRate, + turnoverRate: 0, + }, + + throughput: { + itemsPerHour: 0, + itemsPerDay: 0, + materialFlowRate: 0, + capacityUtilization: utilizationRate, + storeOperations: storage.storeOperations || 0, + retrieveOperations: storage.retrieveOperations || 0, + totalOperations: (storage.storeOperations || 0) + (storage.retrieveOperations || 0), + }, + + efficiency: { + overallEffectiveness: calculateOEE(timeMetrics.uptime, utilizationRate, 100), + availability: timeMetrics.uptime, + performance: utilizationRate, + quality: 100, + spaceUtilization: utilizationRate, + }, + + quality: { + errorRate: 0, + errorFrequency: 0, + successRate: 100, + stateTransitions: [], + }, + + occupancyTrends: [], + historicalData: [], + }; + }, []); + + // ============================================================================ + // HUMAN ANALYSIS + // ============================================================================ + + const analyzeHuman = useCallback((human: any): HumanAnalysis => { + const timeMetrics = calculateTimeMetrics(human.idleTime || 0, human.activeTime || 0); + + const totalTime = (human.idleTime || 0) + (human.activeTime || 0); + const actionsCompleted = human.actionsCompleted || 0; + + return { + assetId: human.modelUuid, + assetName: human.modelName, + assetType: "human", + + currentStatus: { + isActive: human.isActive || false, + isScheduled: human.isScheduled || false, + currentPhase: human.currentPhase || "idle", + state: human.state || "idle", + speed: human.speed || 0, + currentLoad: human.currentLoad || 0, + currentMaterials: human.currentMaterials || [], + currentAction: human.currentAction || null, + }, + + timeMetrics: { + ...timeMetrics, + idleTime: human.idleTime || 0, + activeTime: human.activeTime || 0, + totalTime, + scheduledTime: totalTime, + }, + + productivityMetrics: { + actionsCompleted, + actionsPerHour: totalTime > 0 ? (actionsCompleted / totalTime) * 3600 : 0, + averageActionTime: actionsCompleted > 0 ? totalTime / actionsCompleted : 0, + distanceTraveled: human.distanceTraveled || 0, + }, + + workloadDistribution: [], + + efficiency: { + overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100), + availability: timeMetrics.uptime, + performance: 100, + quality: 100, + laborProductivity: totalTime > 0 ? actionsCompleted / (totalTime / 3600) : 0, + utilizationRate: timeMetrics.utilizationRate * 100, + }, + + quality: { + errorRate: 0, + errorFrequency: 0, + successRate: 100, + stateTransitions: [], + }, + + historicalData: [], + }; + }, []); + + // ============================================================================ + // CRANE ANALYSIS + // ============================================================================ + + const analyzeCrane = useCallback((crane: any): CraneAnalysis => { + const timeMetrics = calculateTimeMetrics(crane.idleTime || 0, crane.activeTime || 0); + + const totalTime = (crane.idleTime || 0) + (crane.activeTime || 0); + const cyclesCompleted = crane.cyclesCompleted || 0; + + return { + assetId: crane.modelUuid, + assetName: crane.modelName, + assetType: "crane", + + currentStatus: { + isActive: crane.isActive || false, + isScheduled: crane.isScheduled || false, + isCarrying: crane.isCarrying || false, + currentPhase: crane.currentPhase || "idle", + state: crane.state || "idle", + currentLoad: crane.currentLoad || 0, + currentMaterials: crane.currentMaterials || [], + currentAction: crane.currentAction || null, + }, + + timeMetrics: { + ...timeMetrics, + idleTime: crane.idleTime || 0, + activeTime: crane.activeTime || 0, + totalTime, + averageCycleTime: cyclesCompleted > 0 ? totalTime / cyclesCompleted : 0, + }, + + throughput: { + itemsPerHour: totalTime > 0 ? (cyclesCompleted / totalTime) * 3600 : 0, + itemsPerDay: totalTime > 0 ? (cyclesCompleted / totalTime) * 86400 : 0, + materialFlowRate: 1, + capacityUtilization: timeMetrics.utilizationRate * 100, + cyclesCompleted, + loadsHandled: crane.loadsHandled || 0, + averageLoadsPerCycle: cyclesCompleted > 0 ? (crane.loadsHandled || 0) / cyclesCompleted : 0, + }, + + movementMetrics: { + totalLifts: crane.totalLifts || 0, + averageLiftHeight: crane.averageLiftHeight || 0, + movementEfficiency: 100, + }, + + efficiency: { + overallEffectiveness: calculateOEE(timeMetrics.uptime, 100, 100), + availability: timeMetrics.uptime, + performance: 100, + quality: 100, + loadUtilization: 0, + cycleTimeEfficiency: 100, + }, + + quality: { + errorRate: 0, + errorFrequency: 0, + successRate: 100, + stateTransitions: [], + liftSuccessRate: 100, + positioningAccuracy: 100, + }, + + historicalData: [], + }; + }, []); + + // ============================================================================ + // SYSTEM-WIDE ANALYSIS + // ============================================================================ + + const analyzeSystem = useCallback((allAssets: AssetAnalysis[]): AnalysisSchema => { + // Calculate system-wide metrics + const totalAssets = allAssets.length; + const activeAssets = allAssets.filter((a) => a.currentStatus.isActive).length; + const assetsInError = allAssets.filter((a) => a.currentStatus.state === "error").length; + + const avgOEE = allAssets.reduce((sum, a) => sum + a.efficiency.overallEffectiveness, 0) / totalAssets; + const avgUtilization = allAssets.reduce((sum, a) => sum + a.timeMetrics.utilizationRate, 0) / totalAssets; + const avgIdleTime = allAssets.reduce((sum, a) => sum + a.timeMetrics.idleTime, 0) / totalAssets; + const totalDowntime = allAssets.reduce((sum, a) => sum + a.timeMetrics.downtime, 0); + + // Helper to get throughput safely + const getThroughput = (asset: AssetAnalysis): number => { + if ("throughput" in asset) { + return asset.throughput.itemsPerHour; + } else if ("productivityMetrics" in asset) { + return asset.productivityMetrics.actionsPerHour; + } + return 0; + }; + + // Group by asset type + const assetTypeGroups = allAssets.reduce((acc, asset) => { + if (!acc[asset.assetType]) { + acc[asset.assetType] = []; + } + acc[asset.assetType].push(asset); + return acc; + }, {} as Record); + + const assetTypePerformance = Object.entries(assetTypeGroups).map(([type, assets]) => ({ + assetType: type, + count: assets.length, + averageOEE: assets.reduce((sum, a) => sum + a.efficiency.overallEffectiveness, 0) / assets.length, + averageUtilization: (assets.reduce((sum, a) => sum + a.timeMetrics.utilizationRate, 0) / assets.length) * 100, + totalThroughput: assets.reduce((sum, a) => sum + getThroughput(a), 0), + })); + + // Identify bottlenecks (high utilization + low throughput) + const bottlenecks = allAssets + .filter((a) => a.timeMetrics.utilizationRate > 0.8) + .map((a) => ({ + assetId: a.assetId, + assetName: a.assetName, + severity: + a.timeMetrics.utilizationRate > 0.95 + ? ("critical" as const) + : a.timeMetrics.utilizationRate > 0.9 + ? ("high" as const) + : a.timeMetrics.utilizationRate > 0.85 + ? ("medium" as const) + : ("low" as const), + utilizationRate: a.timeMetrics.utilizationRate * 100, + queueLength: 0, + impactScore: a.timeMetrics.utilizationRate * 100, + })) + .sort((a, b) => b.impactScore - a.impactScore); + + return { + assets: allAssets, + + systemPerformance: { + overallOEE: avgOEE, + systemThroughput: allAssets.reduce((sum, a) => sum + getThroughput(a), 0), + systemUtilization: avgUtilization * 100, + assetTypePerformance, + criticalMetrics: { + activeAssets, + totalAssets, + assetsInError, + averageIdleTime: avgIdleTime, + totalDowntime, + }, + }, + + materialFlow: { + totalMaterialsInSystem: 0, + materialsCompleted: 0, + averageResidenceTime: 0, + queueLengths: [], + bottlenecks, + flowContinuity: { + overallFlowRate: 0, + varianceCoefficient: 0, + steadyStateDeviation: 0, + }, + }, + + predictiveInsights: { + maintenanceAlerts: [], + optimizationOpportunities: [], + trendAnalysis: [], + }, + + analysisTimeRange: { + startTime: startTimeRef.current, + endTime: new Date().toISOString(), + duration: Date.now() - new Date(startTimeRef.current).getTime(), + }, + + metadata: { + lastUpdated: new Date().toISOString(), + dataPoints: allAssets.length, + analysisVersion: "1.0.0", + }, + }; + }, []); + + // ============================================================================ + // MAIN ANALYSIS FUNCTION + // ============================================================================ + + const performAnalysis = useCallback(() => { + setAnalyzing(true); + + try { + const allAssets: AssetAnalysis[] = []; + + // Analyze all conveyors + conveyors.forEach((conveyor) => { + allAssets.push(analyzeConveyor(conveyor)); + }); + + // Analyze all vehicles + vehicles.forEach((vehicle) => { + allAssets.push(analyzeVehicle(vehicle)); + }); + + // Analyze all robotic arms + armBots.forEach((armBot) => { + allAssets.push(analyzeRoboticArm(armBot)); + }); + + // Analyze all machines + machines.forEach((machine) => { + allAssets.push(analyzeMachine(machine)); + }); + + // Analyze all storage units + storageUnits.forEach((storage) => { + allAssets.push(analyzeStorage(storage)); + }); + + // Analyze all humans + humans.forEach((human) => { + allAssets.push(analyzeHuman(human)); + }); + + // Analyze all cranes + cranes.forEach((crane) => { + allAssets.push(analyzeCrane(crane)); + }); + + // Perform system-wide analysis + const completeAnalysis = analyzeSystem(allAssets); + + setAnalysis(completeAnalysis); + } catch (error) { + console.error("Analysis error:", error); + } finally { + setAnalyzing(false); + } + }, [ + conveyors, + vehicles, + armBots, + machines, + humans, + cranes, + analyzeConveyor, + analyzeVehicle, + analyzeRoboticArm, + analyzeMachine, + analyzeHuman, + analyzeCrane, + analyzeSystem, + setAnalysis, + setAnalyzing, + ]); + + // ============================================================================ + // EFFECTS + // ============================================================================ + + // Perform initial analysis and set up interval + useEffect(() => { + if (!isPlaying) return; + // Initial analysis + performAnalysis(); + + // Set up periodic analysis (every 5 seconds) + analysisIntervalRef.current = setInterval(() => { + performAnalysis(); + }, 5000); + + return () => { + if (analysisIntervalRef.current) { + clearInterval(analysisIntervalRef.current); + } + }; + }, [performAnalysis, isPlaying]); + + return null; +} + +export default Analyzer; diff --git a/app/src/modules/simulation/human/instances/instance/actions/workerInstance.tsx b/app/src/modules/simulation/human/instances/instance/actions/workerInstance.tsx index 6711c07..7a41ce9 100644 --- a/app/src/modules/simulation/human/instances/instance/actions/workerInstance.tsx +++ b/app/src/modules/simulation/human/instances/instance/actions/workerInstance.tsx @@ -144,6 +144,7 @@ function WorkerInstance({ human }: { readonly human: HumanStatus }) { setPath(toDrop); setCurrentPhase(human.modelUuid, "pickup-drop"); setHumanState(human.modelUuid, "running"); + setHumanActive(human.modelUuid, true); setCurrentAnimation(human.modelUuid, "walk_with_box", true, true, true); humanStatus(human.modelUuid, "Started from pickup point, heading to drop point"); } diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 65fa29b..cefba91 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -15,12 +15,18 @@ import useModuleStore from "../../store/ui/useModuleStore"; import SimulationAnalysis from "./analysis/simulationAnalysis"; import { useSceneContext } from "../scene/sceneContext"; import HeatMap from "./heatMap/heatMap"; +import Analyzer from "./analyzer/analyzer"; function Simulation() { const { activeModule } = useModuleStore(); - const { eventStore, productStore } = useSceneContext(); + const { eventStore, productStore, analysisStore } = useSceneContext(); const { events } = eventStore(); const { products } = productStore(); + const { analysis } = analysisStore(); + + useEffect(() => { + console.log("analysis: ", analysis); + }, [analysis]); useEffect(() => { // console.log('events: ', events); @@ -58,6 +64,8 @@ function Simulation() { + + diff --git a/app/src/modules/simulation/simulator/functions/generateHeatmapOutput.ts b/app/src/modules/simulation/simulator/functions/generateHeatmapOutput.ts index 933ca5e..e2357ff 100644 --- a/app/src/modules/simulation/simulator/functions/generateHeatmapOutput.ts +++ b/app/src/modules/simulation/simulator/functions/generateHeatmapOutput.ts @@ -93,6 +93,5 @@ export async function generateHeatmapOutput({ }) ); - console.log('fileResults: ', fileResults); return fileResults; } diff --git a/app/src/modules/simulation/simulator/simulationHandler.tsx b/app/src/modules/simulation/simulator/simulationHandler.tsx index 1cfb70e..4fb5984 100644 --- a/app/src/modules/simulation/simulator/simulationHandler.tsx +++ b/app/src/modules/simulation/simulator/simulationHandler.tsx @@ -170,15 +170,14 @@ const SimulationHandler = () => { width, height, version: selectedVersion.versionId, - download: true, + download: false, }).then((bakedResult) => { - console.log("bakedResult: ", bakedResult); heatMapImageApi( projectId || "", selectedVersion?.versionId || "", selectedProduct?.productUuid, bakedResult - ).then((img) => console.log("getImg: ", img)); + ); }); } } diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index ed44aa4..a8856fc 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -80,7 +80,6 @@ const Project: React.FC = () => { if (projectId) { echo.warn("Validating token"); initializeBuilderSocket(projectId); - initializeSimulationSocket(projectId); echo.success("Builder socket initialized"); } else { navigate("/"); diff --git a/app/src/services/simulation/products/heatMapImageApi.ts b/app/src/services/simulation/products/heatMapImageApi.ts index c3e9191..19ad9d3 100644 --- a/app/src/services/simulation/products/heatMapImageApi.ts +++ b/app/src/services/simulation/products/heatMapImageApi.ts @@ -6,7 +6,6 @@ export const heatMapImageApi = async ( productUuid: string, heatmaps: any ) => { - console.log('heatmaps: ', heatmaps); try { const response = await fetch(`${url_Backend_dwinzo}/api/V1/SimulatedImage`, { method: "PATCH", diff --git a/app/src/store/simulation/useAnalysisStore.ts b/app/src/store/simulation/useAnalysisStore.ts new file mode 100644 index 0000000..f3d6c31 --- /dev/null +++ b/app/src/store/simulation/useAnalysisStore.ts @@ -0,0 +1,131 @@ +import { create } from "zustand"; +import { immer } from "zustand/middleware/immer"; + +interface AnalysisStore { + analysis: AnalysisSchema | null; + isAnalyzing: boolean; + lastUpdateTime: string | null; + + // Actions + setAnalysis: (analysis: AnalysisSchema) => void; + updateAssetAnalysis: (assetId: string, assetAnalysis: AssetAnalysis) => void; + addHistoricalDataPoint: (assetId: string, dataPoint: any) => void; + clearAnalysis: () => void; + setAnalyzing: (isAnalyzing: boolean) => void; + + // Getters + getAssetAnalysis: (assetId: string) => AssetAnalysis | undefined; + getAssetsByType: (assetType: string) => AssetAnalysis[]; + getBottlenecks: () => any[]; + getCriticalAssets: () => AssetAnalysis[]; + getSystemMetrics: () => any; +} + +export const createAnalysisStore = () => { + return create()( + immer((set, get) => ({ + analysis: null, + isAnalyzing: false, + lastUpdateTime: null, + + // Set complete analysis + setAnalysis: (analysis: AnalysisSchema) => { + set((state) => { + state.analysis = analysis; + state.lastUpdateTime = new Date().toISOString(); + }); + }, + + // Update single asset analysis + updateAssetAnalysis: (assetId: string, assetAnalysis: AssetAnalysis) => { + set((state) => { + if (state.analysis) { + const index = state.analysis.assets.findIndex((a) => a.assetId === assetId); + if (index !== -1) { + state.analysis.assets[index] = assetAnalysis; + } else { + state.analysis.assets.push(assetAnalysis); + } + state.lastUpdateTime = new Date().toISOString(); + } + }); + }, + + // Add historical data point to asset + addHistoricalDataPoint: (assetId: string, dataPoint: any) => { + set((state) => { + if (state.analysis) { + const asset = state.analysis.assets.find((a) => a.assetId === assetId); + if (asset) { + asset.historicalData.push(dataPoint); + + // Keep only last 1000 data points + if (asset.historicalData.length > 1000) { + asset.historicalData.shift(); + } + } + } + }); + }, + + // Clear all analysis + clearAnalysis: () => { + set((state) => { + state.analysis = null; + state.lastUpdateTime = null; + }); + }, + + // Set analyzing state + setAnalyzing: (isAnalyzing: boolean) => { + set((state) => { + state.isAnalyzing = isAnalyzing; + }); + }, + + // Get analysis for specific asset + getAssetAnalysis: (assetId: string) => { + const state = get(); + return state.analysis?.assets.find((a) => a.assetId === assetId); + }, + + // Get all assets of a specific type + getAssetsByType: (assetType: string) => { + const state = get(); + return state.analysis?.assets.filter((a) => a.assetType === assetType) || []; + }, + + // Get bottlenecks + getBottlenecks: () => { + const state = get(); + return state.analysis?.materialFlow.bottlenecks || []; + }, + + // Get critical assets (in error state or low efficiency) + getCriticalAssets: () => { + const state = get(); + if (!state.analysis) return []; + + return state.analysis.assets.filter((asset) => { + return asset.currentStatus.state === "error" || asset.efficiency.overallEffectiveness < 50; + }); + }, + + // Get system-wide metrics summary + getSystemMetrics: () => { + const state = get(); + if (!state.analysis) return null; + + return { + systemPerformance: state.analysis.systemPerformance, + materialFlow: state.analysis.materialFlow, + predictiveInsights: state.analysis.predictiveInsights, + criticalAssets: get().getCriticalAssets().length, + bottlenecks: state.analysis.materialFlow.bottlenecks.length, + }; + }, + })) + ); +}; + +export type AnalysisStoreType = ReturnType; diff --git a/app/src/styles/components/simulationDashboard/_simulationDashBoard.scss b/app/src/styles/components/simulationDashboard/_simulationDashBoard.scss index 84fba4e..73aac03 100644 --- a/app/src/styles/components/simulationDashboard/_simulationDashBoard.scss +++ b/app/src/styles/components/simulationDashboard/_simulationDashBoard.scss @@ -10,6 +10,11 @@ position: absolute; top: 0; left: 0; + pointer-events: none; + + * > { + pointer-events: auto; + } .control-panel { margin-bottom: 20px; @@ -111,7 +116,6 @@ .element-button-container { position: relative; - } .element-container { @@ -261,8 +265,6 @@ padding: 20px; color: #ffffff; - - background: var(--background-color); backdrop-filter: blur(20px); border-radius: 20px; @@ -294,7 +296,6 @@ } } - &.data-model-panel { left: 20px; top: 50px; @@ -325,9 +326,6 @@ min-width: 300px; max-height: 84vh; overflow: auto; - - - } } @@ -344,7 +342,6 @@ justify-content: space-between; align-items: center; gap: 6px; - } .form-label { @@ -536,4 +533,4 @@ background-color: rgba(85, 85, 85, 1); } } -} \ No newline at end of file +} diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index caca98d..cab572e 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -468,3 +468,550 @@ type PillarJibCrane = { }; type CraneConstraints = PillarJibCrane; + +// Analysis Schema Types + +// ============================================================================ +// BASE METRIC TYPES +// ============================================================================ + +interface TimeMetrics { + uptime: number; // Percentage + downtime: number; // Total time in seconds + utilizationRate: number; // activeTime / totalTime + mtbf: number; // Mean Time Between Failures + mttr: number; // Mean Time To Repair +} + +interface ThroughputMetrics { + itemsPerHour: number; + itemsPerDay: number; + materialFlowRate: number; + capacityUtilization: number; // Percentage +} + +interface EfficiencyMetrics { + overallEffectiveness: number; // OEE percentage + availability: number; // Percentage + performance: number; // Percentage + quality: number; // Percentage +} + +interface QualityMetrics { + errorRate: number; // Errors per total actions + errorFrequency: number; // Errors per time unit + successRate: number; // Successful completions percentage + stateTransitions: { + fromState: string; + toState: string; + count: number; + averageTime: number; + }[]; +} + +// ============================================================================ +// ASSET-SPECIFIC ANALYSIS +// ============================================================================ + +interface ConveyorAnalysis { + assetId: string; + assetName: string; + assetType: "conveyor"; + + // Real-time Status + currentStatus: { + isActive: boolean; + isPaused: boolean; + state: string; + speed: number; + currentProduct: string | null; + }; + + // Time Metrics + timeMetrics: TimeMetrics & { + idleTime: number; + activeTime: number; + totalTime: number; + }; + + // Throughput + throughput: ThroughputMetrics & { + materialsProcessed: number; + averageProcessingTime: number; + }; + + // Efficiency + efficiency: EfficiencyMetrics; + + // Quality + quality: QualityMetrics; + + // Historical Data + historicalData: { + timestamp: string; + isActive: boolean; + speed: number; + materialsCount: number; + }[]; +} + +interface VehicleAnalysis { + assetId: string; + assetName: string; + assetType: "vehicle"; + + // Real-time Status + currentStatus: { + isActive: boolean; + isPicking: boolean; + currentPhase: string; + state: string; + speed: number; + currentLoad: number; + currentMaterials: { materialType: string; materialId: string }[]; + }; + + // Time Metrics + timeMetrics: TimeMetrics & { + idleTime: number; + activeTime: number; + totalTime: number; + averageTripTime: number; + }; + + // Throughput + throughput: ThroughputMetrics & { + tripsCompleted: number; + averageLoadsPerTrip: number; + totalLoadsDelivered: number; + }; + + // Movement Metrics + movementMetrics: { + distanceTraveled: number; + averageSpeedActual: number; + fuelEfficiency: number; // Distance per energy unit + routeEfficiency: number; // Actual vs optimal distance + }; + + // Efficiency + efficiency: EfficiencyMetrics & { + loadUtilization: number; // Average load / max capacity + }; + + // Quality + quality: QualityMetrics; + + // Historical Data + historicalData: { + timestamp: string; + phase: string; + load: number; + distanceTraveled: number; + }[]; +} + +interface RoboticArmAnalysis { + assetId: string; + assetName: string; + assetType: "roboticArm"; + + // Real-time Status + currentStatus: { + isActive: boolean; + state: string; + speed: number; + currentAction: { + actionUuid: string; + actionName: string; + materialType: string | null; + materialId: string | null; + } | null; + }; + + // Time Metrics + timeMetrics: TimeMetrics & { + idleTime: number; + activeTime: number; + totalTime: number; + averageCycleTime: number; + }; + + // Throughput + throughput: ThroughputMetrics & { + cyclesCompleted: number; + pickAndPlaceCount: number; + }; + + // Efficiency + efficiency: EfficiencyMetrics & { + cycleTimeEfficiency: number; // Actual vs target cycle time + }; + + // Quality + quality: QualityMetrics & { + pickSuccessRate: number; + placeAccuracy: number; + }; + + // Historical Data + historicalData: { + timestamp: string; + cycleTime: number; + actionType: string; + }[]; +} + +interface MachineAnalysis { + assetId: string; + assetName: string; + assetType: "machine"; + + // Real-time Status + currentStatus: { + isActive: boolean; + state: string; + currentAction: { + actionUuid: string; + actionName: string; + materialType: string | null; + materialId: string | null; + } | null; + }; + + // Time Metrics + timeMetrics: TimeMetrics & { + idleTime: number; + activeTime: number; + totalTime: number; + averageProcessTime: number; + }; + + // Throughput + throughput: ThroughputMetrics & { + cyclesCompleted: number; + partsProcessed: number; + }; + + // Efficiency + efficiency: EfficiencyMetrics & { + targetVsActual: number; // Actual process time vs target + }; + + // Quality + quality: QualityMetrics & { + defectRate: number; + reworkRate: number; + }; + + // Historical Data + historicalData: { + timestamp: string; + processTime: number; + partsProcessed: number; + }[]; +} + +interface StorageAnalysis { + assetId: string; + assetName: string; + assetType: "storage"; + + // Real-time Status + currentStatus: { + isActive: boolean; + state: string; + currentLoad: number; + storageCapacity: number; + currentMaterials: { materialType: string; materialId: string }[]; + }; + + // Time Metrics + timeMetrics: TimeMetrics & { + idleTime: number; + activeTime: number; + totalTime: number; + }; + + // Capacity Metrics + capacityMetrics: { + utilizationRate: number; // currentLoad / capacity + averageOccupancy: number; + peakOccupancy: number; + turnoverRate: number; // Store/retrieve frequency + }; + + // Throughput + throughput: ThroughputMetrics & { + storeOperations: number; + retrieveOperations: number; + totalOperations: number; + }; + + // Efficiency + efficiency: EfficiencyMetrics & { + spaceUtilization: number; + }; + + // Quality + quality: QualityMetrics; + + // Occupancy Trends + occupancyTrends: { + timestamp: string; + occupancy: number; + utilizationRate: number; + }[]; + + // Historical Data + historicalData: { + timestamp: string; + currentLoad: number; + operation: "store" | "retrieve"; + }[]; +} + +interface HumanAnalysis { + assetId: string; + assetName: string; + assetType: "human"; + + // Real-time Status + currentStatus: { + isActive: boolean; + isScheduled: boolean; + currentPhase: string; + state: string; + speed: number; + currentLoad: number; + currentMaterials: { materialType: string; materialId: string }[]; + currentAction: { + actionUuid: string; + actionName: string; + } | null; + }; + + // Time Metrics + timeMetrics: TimeMetrics & { + idleTime: number; + activeTime: number; + totalTime: number; + scheduledTime: number; + }; + + // Productivity Metrics + productivityMetrics: { + actionsCompleted: number; + actionsPerHour: number; + averageActionTime: number; + distanceTraveled: number; + }; + + // Workload Distribution + workloadDistribution: { + actionType: string; + count: number; + totalTime: number; + percentage: number; + }[]; + + // Efficiency + efficiency: EfficiencyMetrics & { + laborProductivity: number; // Output per hour + utilizationRate: number; // Active / scheduled time + }; + + // Quality + quality: QualityMetrics; + + // Historical Data + historicalData: { + timestamp: string; + actionType: string; + duration: number; + distanceTraveled: number; + }[]; +} + +interface CraneAnalysis { + assetId: string; + assetName: string; + assetType: "crane"; + + // Real-time Status + currentStatus: { + isActive: boolean; + isScheduled: boolean; + isCarrying: boolean; + currentPhase: string; + state: string; + currentLoad: number; + currentMaterials: { materialType: string; materialId: string }[]; + currentAction: { + actionUuid: string; + actionName: string; + materialType: string | null; + materialId: string | null; + } | null; + }; + + // Time Metrics + timeMetrics: TimeMetrics & { + idleTime: number; + activeTime: number; + totalTime: number; + averageCycleTime: number; + }; + + // Throughput + throughput: ThroughputMetrics & { + cyclesCompleted: number; + loadsHandled: number; + averageLoadsPerCycle: number; + }; + + // Movement Metrics + movementMetrics: { + totalLifts: number; + averageLiftHeight: number; + movementEfficiency: number; + }; + + // Efficiency + efficiency: EfficiencyMetrics & { + loadUtilization: number; + cycleTimeEfficiency: number; + }; + + // Quality + quality: QualityMetrics & { + liftSuccessRate: number; + positioningAccuracy: number; + }; + + // Historical Data + historicalData: { + timestamp: string; + cycleTime: number; + loadsHandled: number; + }[]; +} + +// ============================================================================ +// SYSTEM-WIDE ANALYSIS +// ============================================================================ + +interface MaterialFlowAnalysis { + totalMaterialsInSystem: number; + materialsCompleted: number; + averageResidenceTime: number; // Time materials spend in system + + // Queue Analysis + queueLengths: { + assetId: string; + assetName: string; + queueLength: number; + averageWaitTime: number; + }[]; + + // Bottleneck Identification + bottlenecks: { + assetId: string; + assetName: string; + severity: "critical" | "high" | "medium" | "low"; + utilizationRate: number; + queueLength: number; + impactScore: number; + }[]; + + // Flow Continuity + flowContinuity: { + overallFlowRate: number; + varianceCoefficient: number; + steadyStateDeviation: number; + }; +} + +interface SystemPerformance { + overallOEE: number; + systemThroughput: number; + systemUtilization: number; + + // By Asset Type + assetTypePerformance: { + assetType: string; + count: number; + averageOEE: number; + averageUtilization: number; + totalThroughput: number; + }[]; + + // Critical Metrics + criticalMetrics: { + activeAssets: number; + totalAssets: number; + assetsInError: number; + averageIdleTime: number; + totalDowntime: number; + }; +} + +interface PredictiveInsights { + maintenanceAlerts: { + assetId: string; + assetName: string; + assetType: string; + alertType: "preventive" | "predictive" | "critical"; + estimatedTimeToFailure: number; + confidence: number; + recommendation: string; + }[]; + + optimizationOpportunities: { + area: "throughput" | "efficiency" | "quality" | "cost"; + description: string; + potentialImprovement: number; + priority: "high" | "medium" | "low"; + }[]; + + trendAnalysis: { + metric: string; + trend: "increasing" | "decreasing" | "stable"; + changeRate: number; + forecast: number[]; + }[]; +} + +// ============================================================================ +// MAIN ANALYSIS SCHEMA +// ============================================================================ + +type AssetAnalysis = ConveyorAnalysis | VehicleAnalysis | RoboticArmAnalysis | MachineAnalysis | StorageAnalysis | HumanAnalysis | CraneAnalysis; + +interface AnalysisSchema { + // Individual Asset Analysis + assets: AssetAnalysis[]; + + // System-Wide Metrics + systemPerformance: SystemPerformance; + + // Material Flow + materialFlow: MaterialFlowAnalysis; + + // Predictive Insights + predictiveInsights: PredictiveInsights; + + // Time Range + analysisTimeRange: { + startTime: string; + endTime: string; + duration: number; + }; + + // Metadata + metadata: { + lastUpdated: string; + dataPoints: number; + analysisVersion: string; + }; +} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index fd879bc..0000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Dwinzo_Demo", - "lockfileVersion": 3, - "requires": true, - "packages": {} -}