From 1ca5f001619ed691b5dff460f7f0f502ef8b89e6 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Thu, 8 May 2025 09:18:32 +0530 Subject: [PATCH] Add loading state and integrate simulation analysis components --- .../ui/analysis/ThroughputSummary.tsx | 2 +- .../analysis/simulationAnalysis.tsx | 12 + .../analysis/throughPut/throughPut.tsx | 31 +++ .../armInstance/roboticArmInstance.tsx | 216 +++++++++++++++++- app/src/modules/simulation/simulation.tsx | 3 + .../determineExecutionMachineSequences.ts | 101 ++++++++ 6 files changed, 360 insertions(+), 5 deletions(-) create mode 100644 app/src/modules/simulation/analysis/simulationAnalysis.tsx create mode 100644 app/src/modules/simulation/analysis/throughPut/throughPut.tsx create mode 100644 app/src/modules/simulation/simulator/functions/determineExecutionMachineSequences.ts diff --git a/app/src/components/ui/analysis/ThroughputSummary.tsx b/app/src/components/ui/analysis/ThroughputSummary.tsx index 6a0ad98..57eabd1 100644 --- a/app/src/components/ui/analysis/ThroughputSummary.tsx +++ b/app/src/components/ui/analysis/ThroughputSummary.tsx @@ -16,7 +16,7 @@ const ProductionCapacity = ({ const partialFillPercent = ((progressPercent / 100) * totalBars - barsToFill) * 100; - const isLoading = false; + const isLoading = true; return (
diff --git a/app/src/modules/simulation/analysis/simulationAnalysis.tsx b/app/src/modules/simulation/analysis/simulationAnalysis.tsx new file mode 100644 index 0000000..531c103 --- /dev/null +++ b/app/src/modules/simulation/analysis/simulationAnalysis.tsx @@ -0,0 +1,12 @@ +import React from 'react' +import ThroughPut from './throughPut/throughPut' + +function SimulationAnalysis() { + return ( + <> + + + ) +} + +export default SimulationAnalysis diff --git a/app/src/modules/simulation/analysis/throughPut/throughPut.tsx b/app/src/modules/simulation/analysis/throughPut/throughPut.tsx new file mode 100644 index 0000000..71e82f8 --- /dev/null +++ b/app/src/modules/simulation/analysis/throughPut/throughPut.tsx @@ -0,0 +1,31 @@ +import React, { useEffect } from 'react' +import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; +import { useProductStore } from '../../../../store/simulation/useProductStore'; +import { determineExecutionMachineSequences } from '../../simulator/functions/determineExecutionMachineSequences'; +export default function ThroughPut() { + const { selectedProduct } = useSelectedProduct(); + const { products,getProductById } = useProductStore() + + useEffect(() => { + let productData = getProductById(selectedProduct.productId) + if (productData) { + let productSequenceData = determineExecutionMachineSequences([productData]) + console.log('productSequenceData: ', productSequenceData); + if (productSequenceData?.length > 0) { + let totalItems = 0; // 👈 initialize total count + productSequenceData.map((sequence, index) => { + totalItems += sequence.length; + // sequence.map((item, idx) => {}); + }); + console.log(`Total items across all sequences: ${totalItems}`); // 👈 FINAL TOTAL ✅ + } else { + console.log('No productSequenceData found!'); + } + } + }, [products]) + + return ( + <> + + ) +} \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 7cd3634..a412714 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -1,12 +1,13 @@ import React, { useEffect, useRef, useState } from 'react' import IKInstance from '../ikInstance/ikInstance'; import RoboticArmAnimator from '../animator/roboticArmAnimator'; -import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; +import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb"; import { useThree } from "@react-three/fiber"; import * as THREE from "three"; import MaterialAnimator from '../animator/materialAnimator'; +import { clear } from 'console'; function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { @@ -20,12 +21,21 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { const groupRef = useRef(null); const pauseTimeRef = useRef(null); const isPausedRef = useRef(false); + const isSpeedRef = useRef(null); + const isIdleRef = useRef(false); let startTime: number; //zustand const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore(); const { isPlaying } = usePlayButtonStore(); const { isReset } = useResetButtonStore(); const { isPaused } = usePauseButtonStore(); + const { speed } = useAnimationPlaySpeed(); + + const activeSecondsElapsed = useRef(0); + const activeTimerId = useRef | null>(null); + + const idleSecondsElapsed = useRef(0); + const idleTimerId = useRef | null>(null); function firstFrame() { startTime = performance.now(); @@ -87,9 +97,15 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } useEffect(() => { + isPausedRef.current = isPaused; }, [isPaused]); + useEffect(() => { + + isSpeedRef.current = speed; + }, [speed]); + useEffect(() => { if (isReset || !isPlaying) { logStatus(armBot.modelUuid, "Simulation Play Reset Successfully") @@ -102,6 +118,12 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { isPausedRef.current = false pauseTimeRef.current = null startTime = 0 + clearInterval(activeTimerId.current!); + clearInterval(idleTimerId.current!); + activeTimerId.current = null; + activeSecondsElapsed.current = 0; + idleSecondsElapsed.current = 0; + idleTimerId.current = null; const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone ); if (targetBones && isPlaying) { @@ -114,6 +136,135 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } }, [isReset, isPlaying]) + useEffect(() => { + if (!isPlaying) return; + + const intervalMs = 1000 / isSpeedRef.current; + console.log('intervalMs: ', intervalMs); + + if (!armBot.isActive && armBot.state == "idle" && (currentPhase == "rest" || currentPhase == "init") && !isIdleRef.current) { + isIdleRef.current = true + + // Stop the timer + // 🚨 1. Clear Active Timer + if (activeTimerId.current) { + clearInterval(activeTimerId.current); + activeTimerId.current = null; + } + console.log(`✅ Active Cycle completed in ${activeSecondsElapsed.current} seconds`); + + // 🚨 2. Reset active timer seconds after logging + activeSecondsElapsed.current = 0; + + // 🚨 3. Start Idle Timer (clean old idle timer first) + if (idleTimerId.current) { + clearInterval(idleTimerId.current); + idleTimerId.current = null; + } + + idleSecondsElapsed.current = 0; + idleTimerId.current = setInterval(() => { + if (!isPausedRef.current) { + idleSecondsElapsed.current += 1; + console.log(`🕒 Idle Timer: ${idleSecondsElapsed.current} seconds`); + } + }, intervalMs); + } else if (armBot.isActive && armBot.state != "idle" && currentPhase !== "rest" && armBot.currentAction && isIdleRef.current) { + isIdleRef.current = false + + if (armBot.currentAction) { + // 🚨 Clear Idle Timer + if (idleTimerId.current) { + clearInterval(idleTimerId.current); + idleTimerId.current = null; + } + console.log(`🕒 Idle Cycle completed in: ${idleSecondsElapsed.current} seconds`); + idleSecondsElapsed.current = 0; + + // 🚨 Start Active Timer + if (activeTimerId.current) { + clearInterval(activeTimerId.current); + } + activeSecondsElapsed.current = 0; + activeTimerId.current = setInterval(() => { + if (!isPausedRef.current) { + activeSecondsElapsed.current += 1 + console.log(`🕒 Active Timer: ${activeSecondsElapsed.current} seconds`); + } + }, intervalMs); + } + } + + }, [armBot, currentPhase, isPlaying]) + + + // useEffect(() => { + // if (!isPlaying) return; + // const now = () => performance.now(); + + + // const startActiveTimer = () => { + // let lastTime = now(); + + // const update = () => { + // if (!isPausedRef.current) { + // const currentTime = now(); + // const delta = currentTime - lastTime; + // activeSecondsElapsed.current += delta / 1000; + // console.log(`🕒 Active Timer: ${activeSecondsElapsed.current.toFixed(2)} seconds`); + // lastTime = currentTime; + // } else { + + // lastTime = now(); + // } + + // if (!isIdleRef.current) { + // requestAnimationFrame(update); + // } + // }; + + // activeSecondsElapsed.current = 0; + // update(); + // }; + + // const startIdleTimer = () => { + // let lastTime = now(); + + // const update = () => { + // if (!isPausedRef.current) { + // const currentTime = now(); + // const delta = currentTime - lastTime; + // idleSecondsElapsed.current += delta / 1000; + // console.log(`🕒 Idle Timer: ${idleSecondsElapsed.current.toFixed(2)} seconds`); + // lastTime = currentTime; + // } else { + // lastTime = now(); + // } + + // if (isIdleRef.current) { + // requestAnimationFrame(update); + // } + // }; + + // idleSecondsElapsed.current = 0; + // update(); + // }; + + // // State transition logic + // if (!armBot.isActive && armBot.state === "idle" && (currentPhase === "rest" || currentPhase === "init") && !isIdleRef.current) { + // isIdleRef.current = true; + // console.log(`✅ Active Cycle completed in ${activeSecondsElapsed.current.toFixed(2)} seconds`); + // startIdleTimer(); + // } + // else if (armBot.isActive && armBot.state !== "idle" && currentPhase !== "rest" && armBot.currentAction && isIdleRef.current) { + // isIdleRef.current = false; + // console.log(`🕒 Idle Cycle completed in: ${idleSecondsElapsed.current.toFixed(2)} seconds`); + // startActiveTimer(); + // } + + // }, [armBot, currentPhase, isPlaying, isIdleRef.current, isPausedRef.current]); + + useEffect(() => { const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid); if (targetMesh) { @@ -136,6 +287,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } //Waiting for trigger. else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { + logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction") const timeoutId = setTimeout(() => { addCurrentAction(armBot.modelUuid, armBot.point.actions[0].actionUuid, 'Material 2'); @@ -145,9 +297,30 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { //Moving to pickup point else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) { if (armBot.currentAction) { + setArmBotActive(armBot.modelUuid, true); setArmBotState(armBot.modelUuid, "running"); setCurrentPhase("rest-to-start"); + // // 🚨 Clear Idle Timer + // if (idleTimerId.current) { + // clearInterval(idleTimerId.current); + // idleTimerId.current = null; + // } + // console.log(`🕒 Idle Cycle completed in: ${idleSecondsElapsed.current} seconds`); + // idleSecondsElapsed.current = 0; + + // // 🚨 Start Active Timer + // if (activeTimerId.current) { + // clearInterval(activeTimerId.current); + // } + // activeSecondsElapsed.current = 0; + // activeTimerId.current = setInterval(() => { + // if (!isPausedRef.current) { + // activeSecondsElapsed.current += 1; + // console.log(`🕒 Active Timer: ${activeSecondsElapsed.current} seconds`); + // } + // }, 1000); + const startPoint = armBot.point.actions[0].process.startPoint; if (startPoint) { let curve = createCurveBetweenTwoPoints(targetBones.position, new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2])); @@ -177,6 +350,12 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { pauseTimeRef.current = null isPausedRef.current = false startTime = 0 + clearInterval(activeTimerId.current!); + clearInterval(idleTimerId.current!); + activeTimerId.current = null; + activeSecondsElapsed.current = 0; + idleSecondsElapsed.current = 0; + idleTimerId.current = null; removeCurrentAction(armBot.modelUuid) } @@ -215,10 +394,39 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } else if (armBot.isActive && armBot.state == "running" && currentPhase == "end-to-rest") { logStatus(armBot.modelUuid, "Callback triggered: rest, cycle completed."); + // Stop the timer + // 🚨 1. Clear Active Timer + // if (activeTimerId.current) { + // clearInterval(activeTimerId.current); + // activeTimerId.current = null; + // } + // console.log(`✅ Active Cycle completed in ${activeSecondsElapsed.current} seconds`); + + // // 🚨 2. Reset active timer seconds after logging + // activeSecondsElapsed.current = 0; + + // // 🚨 3. Start Idle Timer (clean old idle timer first) + // if (idleTimerId.current) { + // clearInterval(idleTimerId.current); + // idleTimerId.current = null; + // } + + // idleSecondsElapsed.current = 0; + // idleTimerId.current = setInterval(() => { + // if (!isPausedRef.current) { + // idleSecondsElapsed.current += 1; + // console.log(`🕒 Idle Timer: ${idleSecondsElapsed.current} seconds`); + // } + // }, 1000); + + setArmBotActive(armBot.modelUuid, false) setArmBotState(armBot.modelUuid, "idle") setCurrentPhase("rest"); setPath([]) + + //end active calculation + removeCurrentAction(armBot.modelUuid) } } @@ -228,12 +436,12 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { return ( <> {!isReset && isPlaying && ( - <> + <> - - )} + + )} ) diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 7fe3c50..f918f56 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -12,6 +12,7 @@ import Simulator from './simulator/simulator'; import Products from './products/products'; import Trigger from './triggers/trigger'; import useModuleStore from '../../store/useModuleStore'; +import SimulationAnalysis from './analysis/simulationAnalysis'; function Simulation() { const { activeModule } = useModuleStore(); @@ -53,6 +54,8 @@ function Simulation() { + + } diff --git a/app/src/modules/simulation/simulator/functions/determineExecutionMachineSequences.ts b/app/src/modules/simulation/simulator/functions/determineExecutionMachineSequences.ts new file mode 100644 index 0000000..49e3cc6 --- /dev/null +++ b/app/src/modules/simulation/simulator/functions/determineExecutionMachineSequences.ts @@ -0,0 +1,101 @@ +import { extractTriggersFromPoint } from "./extractTriggersFromPoint"; + +export function determineExecutionMachineSequences(products: productsSchema): EventsSchema[][] { + const pointToEventMap = new Map(); + const allPoints: PointsScheme[] = []; + + // First pass: map points to their corresponding events + products.forEach(product => { + product.eventDatas.forEach(event => { + if (event.type === 'transfer') { + event.points.forEach(point => { + pointToEventMap.set(point.uuid, event); + allPoints.push(point); + }); + } else if ( + event.type === 'vehicle' || + event.type === 'machine' || + event.type === 'storageUnit' || + event.type === 'roboticArm' + ) { + pointToEventMap.set(event.point.uuid, event); + allPoints.push(event.point); + } + }); + }); + + // Build dependency graph + const dependencyGraph = new Map(); + const reverseDependencyGraph = new Map(); + const triggeredPoints = new Set(); + + allPoints.forEach(point => { + const triggers = extractTriggersFromPoint(point); + const dependencies: string[] = []; + + triggers.forEach(trigger => { + const targetUuid = trigger.triggeredAsset?.triggeredPoint?.pointUuid; + if (targetUuid && pointToEventMap.has(targetUuid)) { + dependencies.push(targetUuid); + triggeredPoints.add(targetUuid); + + if (!reverseDependencyGraph.has(targetUuid)) { + reverseDependencyGraph.set(targetUuid, []); + } + reverseDependencyGraph.get(targetUuid)!.push(point.uuid); + } + }); + + dependencyGraph.set(point.uuid, dependencies); + }); + + // Find root points (points that trigger others but are not triggered themselves) + const rootPoints = allPoints.filter(point => { + const hasOutgoingTriggers = extractTriggersFromPoint(point).some( + t => t.triggeredAsset?.triggeredPoint?.pointUuid + ); + return hasOutgoingTriggers && !triggeredPoints.has(point.uuid); + }); + + const executionSequences: EventsSchema[][] = []; + + function buildSequence(startUuid: string): EventsSchema[] { + const sequence: EventsSchema[] = []; + const visited = new Set(); + + function traverse(uuid: string) { + if (visited.has(uuid)) return; + visited.add(uuid); + + const event = pointToEventMap.get(uuid); + if (event && !sequence.includes(event)) { + sequence.push(event); + } + + const nextPoints = dependencyGraph.get(uuid) || []; + nextPoints.forEach(nextUuid => traverse(nextUuid)); + } + + traverse(startUuid); + return sequence; + } + + // Build sequences from root points + rootPoints.forEach(root => { + executionSequences.push(buildSequence(root.uuid)); + }); + + // Handle any isolated triggered points + const processedEvents = new Set( + executionSequences.flat().map(event => event) + ); + + allPoints.forEach(point => { + const event = pointToEventMap.get(point.uuid); + if (triggeredPoints.has(point.uuid) && event && !processedEvents.has(event)) { + executionSequences.push(buildSequence(point.uuid)); + } + }); + + return executionSequences; +}