From acbbe5bd730fef28f510cb1abe09990f9a038112 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Tue, 6 May 2025 09:32:55 +0530 Subject: [PATCH 1/7] arm points constraints updated --- .../instances/animator/roboticArmAnimator.tsx | 21 +++++++ .../simulation/roboticArm/roboticArm.tsx | 6 +- .../simulation/ui/arm/useDraggableGLTF.ts | 60 ++++++++++++++----- 3 files changed, 68 insertions(+), 19 deletions(-) diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index 34f191b..d96182b 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -76,6 +76,27 @@ function RoboticArmAnimator({ } return points; } + + + function generateRingPointsWithDegrees(radius: number, segments: number) { + const points: { position: [number, number, number]; degree: number }[] = []; + for (let i = 0; i < segments; i++) { + const angleRadians = (i / segments) * Math.PI * 2; + const x = Math.cos(angleRadians) * radius; + const z = Math.sin(angleRadians) * radius; + const degree = (angleRadians * 180) / Math.PI; // Convert radians to degrees + points.push({ + position: [x, 1.5, z], + degree, + }); + } + return points; + } + useEffect(() => { + const points = generateRingPointsWithDegrees(CIRCLE_RADIUS, 64); + + }, [armBot.position]); + // Function for find nearest Circlepoints Index const findNearestIndex = (nearestPoint: [number, number, number], points: [number, number, number][], epsilon = 1e-6) => { for (let i = 0; i < points.length; i++) { diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx index a609ad5..e112884 100644 --- a/app/src/modules/simulation/roboticArm/roboticArm.tsx +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -3,11 +3,13 @@ import RoboticArmInstances from "./instances/roboticArmInstances"; import { useArmBotStore } from "../../../store/simulation/useArmBotStore"; import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; import ArmBotUI from "../ui/arm/armBotUI"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; function RoboticArm() { const { armBots } = useArmBotStore(); const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventData } = useSelectedEventData(); + const { isPlaying } = usePlayButtonStore(); useEffect(() => { // console.log('armBots: ', armBots); @@ -15,10 +17,8 @@ function RoboticArm() { return ( <> - - - {selectedEventSphere && selectedEventData?.data.type === "roboticArm" && + {selectedEventSphere && selectedEventData?.data.type === "roboticArm" && !isPlaying && < ArmBotUI /> } diff --git a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts index dae90ee..b819852 100644 --- a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts +++ b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts @@ -121,31 +121,59 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { // CONSTRAIN MOVEMENT HERE: const centerX = selectedArmBot.position[0]; const centerZ = selectedArmBot.position[2]; - const minDistance = 1.2; // 1.4 radius - const maxDistance = 2; // 2 radius + const minDistance = 1.2; + const maxDistance = 2; - const deltaX = targetPosition.x - centerX; - const deltaZ = targetPosition.z - centerZ; - const distance = Math.sqrt(deltaX * deltaX + deltaZ * deltaZ); + let deltaX = targetPosition.x - centerX; + let deltaZ = targetPosition.z - centerZ; - if (distance < minDistance || distance > maxDistance) { - const angle = Math.atan2(deltaZ, deltaX); - const clampedDistance = Math.min( - Math.max(distance, minDistance), - maxDistance - ); + // Calculate angle in radians + let angle = Math.atan2(deltaZ, deltaX); // [-PI, PI] - targetPosition.x = centerX + Math.cos(angle) * clampedDistance; - targetPosition.z = centerZ + Math.sin(angle) * clampedDistance; + // Convert angle to degrees + let angleDeg = (angle * 180) / Math.PI; // [-180, 180] + + // Normalize to [0, 360] + if (angleDeg < 0) { + angleDeg += 360; } + + // Allow only angles between 90° and 270° + if (angleDeg < 95 || angleDeg > 270) { + // Clamp to nearest boundary (90° or 270°) + const distanceTo90 = Math.abs(angleDeg - 95); + const distanceTo270 = Math.abs(angleDeg - 270); + + if (distanceTo90 < distanceTo270) { + angleDeg = 95; + } else { + angleDeg = 270; + } + + // Update angle in radians + angle = (angleDeg * Math.PI) / 180; + } + + // Clamp distance + let distance = Math.sqrt(deltaX * deltaX + deltaZ * deltaZ); + const clampedDistance = Math.min( + Math.max(distance, minDistance), + maxDistance + ); + + // Set final target position + targetPosition.x = centerX + Math.cos(angle) * clampedDistance; + targetPosition.z = centerZ + Math.sin(angle) * clampedDistance; + + // Clamp Y axis if needed targetPosition.y = Math.min(Math.max(targetPosition.y, 0.6), 1.9); - // Convert world position to local if object is nested inside a parent + + // Convert to local if parent exists if (parent) { parent.worldToLocal(targetPosition); } - // Update object position - + // Update the object position activeObjRef.current.position.copy(targetPosition); } }; From bb7315edeb122945737b23a7003cf6a6b2a7bbd0 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Tue, 6 May 2025 11:30:32 +0530 Subject: [PATCH 2/7] armbot animator movements and path updated --- .../instances/animator/roboticArmAnimator.tsx | 128 +++++++++++++----- .../armInstance/roboticArmInstance.tsx | 11 ++ .../simulation/ui/arm/useDraggableGLTF.ts | 9 +- 3 files changed, 107 insertions(+), 41 deletions(-) diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index d96182b..a1e88cc 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useFrame } from '@react-three/fiber'; import * as THREE from 'three'; -import { Line } from '@react-three/drei'; +import { Line, Text } from '@react-three/drei'; import { useAnimationPlaySpeed, usePauseButtonStore, @@ -94,9 +94,9 @@ function RoboticArmAnimator({ } useEffect(() => { const points = generateRingPointsWithDegrees(CIRCLE_RADIUS, 64); - + }, [armBot.position]); - + // Function for find nearest Circlepoints Index const findNearestIndex = (nearestPoint: [number, number, number], points: [number, number, number][], epsilon = 1e-6) => { for (let i = 0; i < points.length; i++) { @@ -124,7 +124,6 @@ function RoboticArmAnimator({ // Handle nearest points and final path (including arc points) useEffect(() => { if (circlePoints.length > 0 && currentPath.length > 0) { - const start = currentPath[0]; const end = currentPath[currentPath.length - 1]; @@ -137,31 +136,50 @@ function RoboticArmAnimator({ const indexOfNearestStart = findNearestIndex(nearestToStart, circlePoints); const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints); - const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + 64) % 64; - const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + 64) % 64; - const clockwiseIsShorter = clockwiseDistance <= counterClockwiseDistance; + const totalSegments = 64; + const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + totalSegments) % totalSegments; + const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + totalSegments) % totalSegments; + + // Prepare degrees + const degreesList = generateRingPointsWithDegrees(CIRCLE_RADIUS, totalSegments); + + // Helper function to collect points and check forbidden degrees + const collectArcPoints = (startIdx: number, endIdx: number, clockwise: boolean) => { + const arcPoints: [number, number, number][] = []; + let i = startIdx; + + while (i !== (endIdx + (clockwise ? 1 : -1) + totalSegments) % totalSegments) { + const { degree, position } = degreesList[i]; + + // Skip over + arcPoints.push(position); + i = (i + (clockwise ? 1 : -1) + totalSegments) % totalSegments; + } + return arcPoints; + }; + + const hasForbiddenDegrees = (arc: [number, number, number][]) => { + return arc.some(p => { + const idx = findNearestIndex(p, circlePoints); + const degree = degreesList[idx]?.degree || 0; + return degree >= 271 && degree <= 300; // Forbidden range: 271° to 300° + }); + }; + + + // Try both directions + const arcClockwise = collectArcPoints(indexOfNearestStart, indexOfNearestEnd, true); + const arcCounterClockwise = collectArcPoints(indexOfNearestStart, indexOfNearestEnd, false); + + const clockwiseForbidden = hasForbiddenDegrees(arcClockwise); + const counterClockwiseForbidden = hasForbiddenDegrees(arcCounterClockwise); let arcPoints: [number, number, number][] = []; - if (clockwiseIsShorter) { - if (indexOfNearestStart <= indexOfNearestEnd) { - arcPoints = circlePoints.slice(indexOfNearestStart, indexOfNearestEnd + 1); - } else { - arcPoints = [ - ...circlePoints.slice(indexOfNearestStart, 64), - ...circlePoints.slice(0, indexOfNearestEnd + 1) - ]; - } + if (!clockwiseForbidden && (clockwiseDistance <= counterClockwiseDistance || counterClockwiseForbidden)) { + arcPoints = arcClockwise; } else { - if (indexOfNearestStart >= indexOfNearestEnd) { - for (let i = indexOfNearestStart; i !== (indexOfNearestEnd - 1 + 64) % 64; i = (i - 1 + 64) % 64) { - arcPoints.push(circlePoints[i]); - } - } else { - for (let i = indexOfNearestStart; i !== (indexOfNearestEnd - 1 + 64) % 64; i = (i - 1 + 64) % 64) { - arcPoints.push(circlePoints[i]); - } - } + arcPoints = arcCounterClockwise; } const pathVectors = [ @@ -174,9 +192,7 @@ function RoboticArmAnimator({ new THREE.Vector3(end[0], end[1], end[2]) ]; - const pathSegments: [THREE.Vector3, THREE.Vector3][] = []; - for (let i = 0; i < pathVectors.length - 1; i++) { pathSegments.push([pathVectors[i], pathVectors[i + 1]]); } @@ -186,16 +202,12 @@ function RoboticArmAnimator({ const totalDistance = segmentDistances.reduce((sum, d) => sum + d, 0); totalDistanceRef.current = totalDistance; - const movementSpeed = speed * armBot.speed; - const totalMoveTime = totalDistance / movementSpeed; - - const segmentTimes = segmentDistances.map(distance => (distance / totalDistance) * totalMoveTime); - setCustomCurvePoints(pathVectors); - } }, [circlePoints, currentPath]); + + // Frame update for animation useFrame((state, delta) => { if (!ikSolver) return; @@ -266,10 +278,52 @@ function RoboticArmAnimator({ /> )} - - - - + + {/* Green ring */} + + + + + + {/* Markers at 90°, 180°, 270°, 360° */} + {[90, 180, 270, 360].map((degree, index) => { + const rad = (degree * Math.PI) / 180; + const x = CIRCLE_RADIUS * Math.cos(rad); + const z = CIRCLE_RADIUS * Math.sin(rad); + const y = 0; // same plane as the ring (Y axis) + + return ( + + + + + ); + })} + + {/* Optional: Text Labels */} + + {[90, 180, 270, 360].map((degree, index) => { + const rad = (degree * Math.PI) / 180; + const x = CIRCLE_RADIUS * Math.cos(rad); + const z = CIRCLE_RADIUS * Math.sin(rad); + const y = 0.15; // lift the text slightly above the ring + + return ( + + {degree}° + + ); + })} + + + ); } diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 21bbc7e..246d3f8 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -164,6 +164,17 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "dropping" && armBot.currentAction) { requestAnimationFrame(firstFrame); } + }else{ + logStatus(armBot.modelUuid, "Simulation Play Exited") + setArmBotActive(armBot.modelUuid, false) + setArmBotState(armBot.modelUuid, "idle") + setCurrentPhase("init"); + setPath([]) + isPausedRef.current = false + pauseTimeRef.current = null + isPausedRef.current = false + startTime = 0 + removeCurrentAction(armBot.modelUuid) } }, [currentPhase, armBot, isPlaying, ikSolver]) diff --git a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts index b819852..fdb271c 100644 --- a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts +++ b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts @@ -139,15 +139,16 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { } // Allow only angles between 90° and 270° - if (angleDeg < 95 || angleDeg > 270) { + if (angleDeg < 0 || angleDeg > 270) { // Clamp to nearest boundary (90° or 270°) - const distanceTo90 = Math.abs(angleDeg - 95); + const distanceTo90 = Math.abs(angleDeg - 0); const distanceTo270 = Math.abs(angleDeg - 270); if (distanceTo90 < distanceTo270) { - angleDeg = 95; + angleDeg = 0; } else { - angleDeg = 270; + return + // angleDeg = 270; } // Update angle in radians From bdba6447f3a8c795e15ab80435c5700b0eb98b6d Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 6 May 2025 11:30:58 +0530 Subject: [PATCH 3/7] feat: Implement swap handling and enhance action management in conveyor system --- .../conveyor/actionHandler/useSpawnHandler.ts | 3 +- .../conveyor/actionHandler/useSwapHandler.ts | 38 ++++ .../actions/conveyor/useConveyorActions.ts | 7 +- .../instances/instance/materialInstance.tsx | 40 ++++- .../materials/instances/materialInstances.tsx | 2 +- .../armInstance/roboticArmInstance.tsx | 2 +- .../triggerHandler/useTriggerHandler.ts | 167 +++++++++--------- app/src/store/simulation/useArmBotStore.ts | 6 +- app/src/store/simulation/useMaterialStore.ts | 19 +- app/src/store/simulation/useProductStore.ts | 26 ++- app/src/types/simulationTypes.d.ts | 3 +- 11 files changed, 209 insertions(+), 104 deletions(-) create mode 100644 app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts index 8f639e6..da1626b 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts @@ -61,7 +61,7 @@ export function useSpawnHandler() { current: { modelUuid: modelUuid, pointUuid: pointUuid, - actionUuid: action?.actionUuid || '' + actionUuid: action.actionUuid }, weight: 1, cost: 1 @@ -74,7 +74,6 @@ export function useSpawnHandler() { newMaterial.next = { modelUuid: action.triggers[0].triggeredAsset?.triggeredModel.modelUuid, pointUuid: action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid, - actionUuid: action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid } } diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts new file mode 100644 index 0000000..46c9d02 --- /dev/null +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts @@ -0,0 +1,38 @@ +import { useCallback } from "react"; +import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; +import { useProductStore } from "../../../../../store/simulation/useProductStore"; +import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; +import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore"; +import * as THREE from 'three'; + +export function useSwapHandler() { + const { addMaterial, getMaterialByCurrentPointUuid, setMaterial } = useMaterialStore(); + const { getPointUuidByActionUuid } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + const { isPlaying } = usePlayButtonStore(); + + const swapLogStatus = (materialUuid: string, status: string) => { + // console.log(`${materialUuid}, ${status}`); + } + + const handleSwap = useCallback((action: ConveyorAction) => { + if (!action || action.actionType !== 'swap' || !isPlaying) return; + + const { material: newMaterialType, actionUuid } = action; + const pointUuid = getPointUuidByActionUuid(selectedProduct.productId, actionUuid); + + if (!pointUuid) return; + + const currentMaterial = getMaterialByCurrentPointUuid(pointUuid); + + if (currentMaterial) { + setMaterial(currentMaterial.materialId, newMaterialType); + swapLogStatus(currentMaterial.materialId, `Swapped to ${newMaterialType}`); + } + + }, [addMaterial, getMaterialByCurrentPointUuid, getPointUuidByActionUuid, isPlaying, setMaterial, selectedProduct.productId]); + + return { + handleSwap, + }; +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts index f93ef4f..32d54c4 100644 --- a/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts +++ b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts @@ -1,11 +1,12 @@ import { useEffect, useCallback, useRef } from "react"; import { useSpawnHandler } from "./actionHandler/useSpawnHandler"; +import { useSwapHandler } from "./actionHandler/useSwapHandler"; export function useConveyorActions() { const { handleSpawn, clearCurrentSpawn } = useSpawnHandler(); + const { handleSwap } = useSwapHandler(); const handleDefaultAction = useCallback((action: ConveyorAction) => { - console.log('action: ', action); console.log(`Default conveyor action ${action.actionUuid}`); }, []); @@ -14,8 +15,8 @@ export function useConveyorActions() { }, [handleSpawn]); const handleSwapAction = useCallback((action: ConveyorAction) => { - console.log(`Swapping to material ${action.material}`); - }, []); + handleSwap(action); + }, [handleSwap]); const handleDelayAction = useCallback((action: ConveyorAction) => { const delayMs = (action.delay || 0) * 1000; diff --git a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx index 338a45c..52e4862 100644 --- a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx +++ b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx @@ -11,7 +11,7 @@ import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHa function MaterialInstance({ material }: { material: MaterialSchema }) { const matRef: any = useRef(); const { scene } = useThree(); - const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid, getActionByUuid } = useProductStore(); + const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid, getActionByUuid, getTriggerByUuid, getActionByPointUuid } = useProductStore(); const { selectedProduct } = useSelectedProduct(); const { speed } = useAnimationPlaySpeed(); const { triggerPointActions } = useTriggerHandler(); @@ -84,13 +84,45 @@ function MaterialInstance({ material }: { material: MaterialSchema }) { useEffect(() => { // console.log('material: ', material); + if (material.current && material.next) { + // console.log('current: ', material.current.pointUuid); + // console.log('next: ', material.next.pointUuid); + } }, [material]) const callTrigger = () => { - const action = getActionByUuid(selectedProduct.productId, material.current.actionUuid) - if (action) { - triggerPointActions(action); + if (!material.next) return; + const fromModel = getEventByModelUuid(selectedProduct.productId, material.next.modelUuid); + if (!fromModel) return; + const fromPoint = getPointByUuid(selectedProduct.productId, fromModel.modelUuid, material.next.pointUuid); + if (!fromPoint) return; + + if (fromModel.type === 'transfer') { + const toModel = getEventByModelUuid(selectedProduct.productId, material.next.modelUuid); + if (!toModel) return; + if (toModel.type === 'transfer') { + const action = getActionByPointUuid(selectedProduct.productId, material.next.pointUuid); + if (action) { + triggerPointActions(action); + } + } else if (toModel?.type === 'vehicle') { + // Transfer to Vehicle + + } else if (toModel?.type === 'machine') { + // Transfer to Machine + + } else if (toModel?.type === 'roboticArm') { + // Transfer to Robotic Arm + + } else if (toModel?.type === 'storageUnit') { + // Transfer to Storage Unit + } + } else if (fromModel.type === 'vehicle') { + } else if (fromModel.type === 'machine') { + } else if (fromModel.type === 'roboticArm') { + } else if (fromModel.type === 'storageUnit') { } + } return ( diff --git a/app/src/modules/simulation/materials/instances/materialInstances.tsx b/app/src/modules/simulation/materials/instances/materialInstances.tsx index 199e604..1864f0f 100644 --- a/app/src/modules/simulation/materials/instances/materialInstances.tsx +++ b/app/src/modules/simulation/materials/instances/materialInstances.tsx @@ -6,7 +6,7 @@ function MaterialInstances() { const { materials } = useMaterialStore(); useEffect(() => { - console.log('materials: ', materials); + // console.log('materials: ', materials); }, [materials]) return ( diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 21bbc7e..8cb0d13 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -138,7 +138,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { 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); + addCurrentAction(armBot.modelUuid, armBot.point.actions[0].actionUuid, 'Default material'); }, 3000); return () => clearTimeout(timeoutId); } diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts index 7541522..22d5cfd 100644 --- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts +++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts @@ -10,119 +10,116 @@ export function useTriggerHandler() { const { getMaterialByCurrentModelUuid, setCurrentLocation, setNextLocation } = useMaterialStore(); const { selectedProduct } = useSelectedProduct(); - const handleTrigger = (trigger: TriggerSchema, actionUuid: string) => { + const handleTrigger = (trigger: TriggerSchema, action: Action) => { - // const fromEvent = getEventByTriggerUuid(selectedProduct.productId, trigger.triggerUuid); - // console.log('fromEvent: ', fromEvent); + const fromEvent = getEventByTriggerUuid(selectedProduct.productId, trigger.triggerUuid); - // const toEvent = getEventByModelUuid(selectedProduct.productId, trigger.triggeredAsset?.triggeredModel.modelUuid || ''); - // console.log('toEvent: ', toEvent); + const toEvent = getEventByModelUuid(selectedProduct.productId, trigger.triggeredAsset?.triggeredModel.modelUuid || ''); - // if (fromEvent?.type === 'transfer') { - // if (toEvent?.type === 'transfer') { - // // console.log('toEvent: ', toEvent.type); - // // Transfer to Transfer - // const action = getActionByUuid(selectedProduct.productId, trigger.triggeredAsset?.triggeredAction?.actionUuid || ''); - // if (action && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { - // const material = getMaterialByCurrentModelUuid(fromEvent.modelUuid); - // if (material) { - // if (material.next && - // action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid && - // action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid) { + if (fromEvent?.type === 'transfer') { + if (toEvent?.type === 'transfer') { + // Transfer to Transfer + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { + const material = getMaterialByCurrentModelUuid(fromEvent.modelUuid); + if (material) { + if (material.next) { - // setCurrentLocation(material.materialId, material.next); + setCurrentLocation(material.materialId, { + modelUuid: material.next.modelUuid, + pointUuid: material.next.pointUuid, + actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, + }); - // setNextLocation(material.materialId, { - // modelUuid: toEvent.modelUuid, - // pointUuid: action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid, - // actionUuid: action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid - // }); - // } - // handleAction(action); - // } - // } - // } else if (toEvent?.type === 'vehicle') { - // // Transfer to Vehicle + setNextLocation(material.materialId, { + modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid, + pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + }); + } + handleAction(action); + } + } + } else if (toEvent?.type === 'vehicle') { + // Transfer to Vehicle - // } else if (toEvent?.type === 'machine') { - // // Transfer to Machine + } else if (toEvent?.type === 'machine') { + // Transfer to Machine - // } else if (toEvent?.type === 'roboticArm') { - // // Transfer to Robotic Arm + } else if (toEvent?.type === 'roboticArm') { + // Transfer to Robotic Arm - // } else if (toEvent?.type === 'storageUnit') { - // // Transfer to Storage Unit + } else if (toEvent?.type === 'storageUnit') { + // Transfer to Storage Unit - // } - // } else if (fromEvent?.type === 'vehicle') { - // if (toEvent?.type === 'transfer') { - // // Vehicle to Transfer + } + } else if (fromEvent?.type === 'vehicle') { + if (toEvent?.type === 'transfer') { + // Vehicle to Transfer - // } else if (toEvent?.type === 'vehicle') { - // // Vehicle to Vehicle + } else if (toEvent?.type === 'vehicle') { + // Vehicle to Vehicle - // } else if (toEvent?.type === 'machine') { - // // Vehicle to Machine + } else if (toEvent?.type === 'machine') { + // Vehicle to Machine - // } else if (toEvent?.type === 'roboticArm') { - // // Vehicle to Robotic Arm + } else if (toEvent?.type === 'roboticArm') { + // Vehicle to Robotic Arm - // } else if (toEvent?.type === 'storageUnit') { - // // Vehicle to Storage Unit + } else if (toEvent?.type === 'storageUnit') { + // Vehicle to Storage Unit - // } - // } else if (fromEvent?.type === 'machine') { - // if (toEvent?.type === 'transfer') { - // // Machine to Transfer + } + } else if (fromEvent?.type === 'machine') { + if (toEvent?.type === 'transfer') { + // Machine to Transfer - // } else if (toEvent?.type === 'vehicle') { - // // Machine to Vehicle + } else if (toEvent?.type === 'vehicle') { + // Machine to Vehicle - // } else if (toEvent?.type === 'machine') { - // // Machine to Machine + } else if (toEvent?.type === 'machine') { + // Machine to Machine - // } else if (toEvent?.type === 'roboticArm') { - // // Machine to Robotic Arm + } else if (toEvent?.type === 'roboticArm') { + // Machine to Robotic Arm - // } else if (toEvent?.type === 'storageUnit') { - // // Machine to Storage Unit + } else if (toEvent?.type === 'storageUnit') { + // Machine to Storage Unit - // } - // } else if (fromEvent?.type === 'roboticArm') { - // if (toEvent?.type === 'transfer') { - // // Robotic Arm to Transfer + } + } else if (fromEvent?.type === 'roboticArm') { + if (toEvent?.type === 'transfer') { + // Robotic Arm to Transfer - // } else if (toEvent?.type === 'vehicle') { - // // Robotic Arm to Vehicle + } else if (toEvent?.type === 'vehicle') { + // Robotic Arm to Vehicle - // } else if (toEvent?.type === 'machine') { - // // Robotic Arm to Machine + } else if (toEvent?.type === 'machine') { + // Robotic Arm to Machine - // } else if (toEvent?.type === 'roboticArm') { - // // Robotic Arm to Robotic Arm + } else if (toEvent?.type === 'roboticArm') { + // Robotic Arm to Robotic Arm - // } else if (toEvent?.type === 'storageUnit') { - // // Robotic Arm to Storage Unit + } else if (toEvent?.type === 'storageUnit') { + // Robotic Arm to Storage Unit - // } - // } else if (fromEvent?.type === 'storageUnit') { - // if (toEvent?.type === 'transfer') { - // // Storage Unit to Transfer + } + } else if (fromEvent?.type === 'storageUnit') { + if (toEvent?.type === 'transfer') { + // Storage Unit to Transfer - // } else if (toEvent?.type === 'vehicle') { - // // Storage Unit to Vehicle + } else if (toEvent?.type === 'vehicle') { + // Storage Unit to Vehicle - // } else if (toEvent?.type === 'machine') { - // // Storage Unit to Machine + } else if (toEvent?.type === 'machine') { + // Storage Unit to Machine - // } else if (toEvent?.type === 'roboticArm') { - // // Storage Unit to Robotic Arm + } else if (toEvent?.type === 'roboticArm') { + // Storage Unit to Robotic Arm - // } else if (toEvent?.type === 'storageUnit') { - // // Storage Unit to Storage Unit + } else if (toEvent?.type === 'storageUnit') { + // Storage Unit to Storage Unit - // } - // } + } + } } const triggerPointActions = useCallback((action: Action) => { @@ -133,7 +130,7 @@ export function useTriggerHandler() { case 'onStart': break; case 'onComplete': - handleTrigger(trigger, action.actionUuid); + handleTrigger(trigger, action); break; case 'onStop': break; diff --git a/app/src/store/simulation/useArmBotStore.ts b/app/src/store/simulation/useArmBotStore.ts index 79ad835..bd81e61 100644 --- a/app/src/store/simulation/useArmBotStore.ts +++ b/app/src/store/simulation/useArmBotStore.ts @@ -12,7 +12,7 @@ interface ArmBotStore { ) => void; clearArmBots: () => void; - addCurrentAction: (modelUuid: string, actionUuid: string) => void; + addCurrentAction: (modelUuid: string, actionUuid: string, materialType: string) => void; removeCurrentAction: (modelUuid: string) => void; addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void; @@ -75,7 +75,7 @@ export const useArmBotStore = create()( }); }, - addCurrentAction: (modelUuid, actionUuid) => { + addCurrentAction: (modelUuid, actionUuid, materialType) => { set((state) => { const armBot = state.armBots.find(a => a.modelUuid === modelUuid); if (armBot) { @@ -84,7 +84,7 @@ export const useArmBotStore = create()( armBot.currentAction = { actionUuid: action.actionUuid, actionName: action.actionName, - materialType: null + materialType: materialType }; } } diff --git a/app/src/store/simulation/useMaterialStore.ts b/app/src/store/simulation/useMaterialStore.ts index ec63ff3..20476d0 100644 --- a/app/src/store/simulation/useMaterialStore.ts +++ b/app/src/store/simulation/useMaterialStore.ts @@ -23,10 +23,10 @@ type MaterialsStore = { location?: { modelUuid: string; pointUuid: string; - actionUuid: string; } | null ) => MaterialSchema | undefined; + setMaterial: (materialId: string, materialType: string) => MaterialSchema | undefined; setStartTime: (materialId: string, startTime: string) => MaterialSchema | undefined; setEndTime: (materialId: string, endTime: string) => MaterialSchema | undefined; setCost: (materialId: string, cost: number) => MaterialSchema | undefined; @@ -37,6 +37,7 @@ type MaterialsStore = { getMaterialById: (materialId: string) => MaterialSchema | undefined; getMaterialByCurrentModelUuid: (currentModelUuid: string) => MaterialSchema | undefined; + getMaterialByCurrentPointUuid: (currentPointUuid: string) => MaterialSchema | undefined; getMaterialsByPoint: (pointUuid: string) => MaterialSchema[]; getMaterialsByModel: (modelUuid: string) => MaterialSchema[]; }; @@ -107,6 +108,18 @@ export const useMaterialStore = create()( return updatedMaterial; }, + setMaterial: (materialId, materialType) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.materialType = materialType; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, + setStartTime: (materialId, startTime) => { let updatedMaterial: MaterialSchema | undefined; set((state) => { @@ -198,6 +211,10 @@ export const useMaterialStore = create()( getMaterialByCurrentModelUuid: (currentModelUuid) => { return get().materials.find(m => m.current?.modelUuid === currentModelUuid); }, + + getMaterialByCurrentPointUuid: (currentPointlUuid) => { + return get().materials.find(m => m.current?.pointUuid === currentPointlUuid); + }, getMaterialsByPoint: (pointUuid) => { return get().materials.filter(m => diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index cdbc26c..b90fd7d 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -65,6 +65,7 @@ type ProductsStore = { getEventByPointUuid: (productId: string, pointUuid: string) => EventsSchema | undefined; getPointByUuid: (productId: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined; getActionByUuid: (productId: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined; + getActionByPointUuid: (productId: string, pointUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined; getModelUuidByPointUuid: (productId: string, actionUuid: string) => (string) | undefined; getModelUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined; getPointUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined; @@ -545,7 +546,7 @@ export const useProductStore = create()( getEventByTriggerUuid: (productId, triggerUuid) => { const product = get().getProductById(productId); if (!product) return undefined; - + for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { @@ -574,7 +575,7 @@ export const useProductStore = create()( getEventByPointUuid: (productId, pointUuid) => { const product = get().getProductById(productId); if (!product) return undefined; - + for (const event of product.eventDatas) { if ('points' in event) { if ((event as ConveyorEventSchema).points.some(p => p.uuid === pointUuid)) { @@ -625,6 +626,27 @@ export const useProductStore = create()( return undefined; }, + getActionByPointUuid: (productId, pointUuid) => { + const product = get().products.find(p => p.productId === productId); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.uuid === pointUuid) { + return point.action; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if (point.uuid === pointUuid) { + return point.action; + } + } + } + return undefined; + }, + getModelUuidByPointUuid: (productId, pointUuid) => { const product = get().products.find(p => p.productId === productId); if (!product) return undefined; diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index f863c1e..c313db1 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -125,7 +125,6 @@ interface StorageAction { actionUuid: string; actionName: string; actionType: "store"; - materials: { materialName: string; materialId: string; }[]; storageCapacity: number; triggers: TriggerSchema[]; } @@ -190,6 +189,7 @@ interface StorageUnitStatus extends StorageEventSchema { idleTime: number; activeTime: number; currentLoad: number; + materials?: { materialName: string; materialId: string; }[]; } interface MaterialSchema { @@ -213,7 +213,6 @@ interface MaterialSchema { next?: { modelUuid: string; pointUuid: string; - actionUuid: string; }; } From 815a9a94ca085e9631b4a3fe0f45cd69783fabd0 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 6 May 2025 12:35:51 +0530 Subject: [PATCH 4/7] feat: Refactor swap handling and enhance delay management in conveyor actions --- .../conveyor/actionHandler/useDelayHandler.ts | 91 +++++++++++++++++++ .../conveyor/actionHandler/useSwapHandler.ts | 24 ++--- .../actions/conveyor/useConveyorActions.ts | 24 ++--- .../simulation/actions/useActionHandler.ts | 4 +- .../instances/animator/materialAnimator.tsx | 13 ++- .../instances/instance/materialInstance.tsx | 2 +- .../instances/material/materialModel.tsx | 2 +- .../triggerHandler/useTriggerHandler.ts | 16 ++-- app/src/store/simulation/useMaterialStore.ts | 10 +- 9 files changed, 138 insertions(+), 48 deletions(-) create mode 100644 app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts new file mode 100644 index 0000000..bb0e7bd --- /dev/null +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts @@ -0,0 +1,91 @@ +import { useCallback, useEffect, useRef } from "react"; +import { useFrame } from "@react-three/fiber"; +import { usePlayButtonStore, usePauseButtonStore } from "../../../../../store/usePlayButtonStore"; + +interface DelayInstance { + delayEndTime: number; + materialId?: string; + action: ConveyorAction; + isPaused: boolean; + remainingTime: number; +} + +export function useDelayHandler() { + const { isPlaying } = usePlayButtonStore(); + const { isPaused } = usePauseButtonStore(); + const activeDelays = useRef>(new Map()); + + const cleanupDelay = useCallback(() => { + activeDelays.current.clear(); + }, []); + + useEffect(() => { + return () => { + cleanupDelay(); + }; + }, [cleanupDelay]); + + // Handle pause/resume for all delays + useEffect(() => { + const currentTime = performance.now(); + + activeDelays.current.forEach((delay) => { + if (isPaused && !delay.isPaused) { + delay.remainingTime = Math.max(0, delay.delayEndTime - currentTime); + delay.isPaused = true; + } else if (!isPaused && delay.isPaused) { + delay.delayEndTime = currentTime + delay.remainingTime; + delay.isPaused = false; + delay.remainingTime = 0; + } + }); + }, [isPaused]); + + useFrame(() => { + if (!isPlaying || isPaused) return; + + const currentTime = performance.now(); + const completedDelays: string[] = []; + + activeDelays.current.forEach((delay, key) => { + if (!delay.isPaused && currentTime >= delay.delayEndTime) { + console.log(`Delay completed for material ${delay.materialId || 'unknown'}`); + completedDelays.push(key); + } + }); + + completedDelays.forEach(key => { + activeDelays.current.delete(key); + }); + }); + + const handleDelay = useCallback((action: ConveyorAction, materialId?: string) => { + if (!action || action.actionType !== 'delay' || !isPlaying) return; + + const delayMs = (action.delay || 0) * 1000; + if (delayMs <= 0) return; + + const key = materialId ? `${materialId}-${action.actionUuid}` : action.actionUuid; + + // If this material already has a delay, cancel it + if (activeDelays.current.has(key)) { + activeDelays.current.delete(key); + } + + activeDelays.current.set(key, { + delayEndTime: performance.now() + delayMs, + materialId, + action, + isPaused: false, + remainingTime: 0 + }); + + console.log(`Started ${delayMs}ms delay for material ${materialId || 'unknown'}`); + + }, [isPlaying]); + + return { + handleDelay, + cleanupDelay + }; +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts index 46c9d02..bd3a6fc 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts @@ -3,10 +3,9 @@ import { useMaterialStore } from "../../../../../store/simulation/useMaterialSto import { useProductStore } from "../../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore"; -import * as THREE from 'three'; export function useSwapHandler() { - const { addMaterial, getMaterialByCurrentPointUuid, setMaterial } = useMaterialStore(); + const { addMaterial, getMaterialById, setMaterial } = useMaterialStore(); const { getPointUuidByActionUuid } = useProductStore(); const { selectedProduct } = useSelectedProduct(); const { isPlaying } = usePlayButtonStore(); @@ -15,22 +14,17 @@ export function useSwapHandler() { // console.log(`${materialUuid}, ${status}`); } - const handleSwap = useCallback((action: ConveyorAction) => { - if (!action || action.actionType !== 'swap' || !isPlaying) return; + const handleSwap = useCallback((action: ConveyorAction, materialId?: string) => { + if (!action || action.actionType !== 'swap' || !isPlaying || !materialId) return; - const { material: newMaterialType, actionUuid } = action; - const pointUuid = getPointUuidByActionUuid(selectedProduct.productId, actionUuid); + const { material: newMaterialType } = action; + const material = getMaterialById(materialId); + if (!material) return; - if (!pointUuid) return; + setMaterial(material.materialId, newMaterialType); + swapLogStatus(material.materialId, `Swapped to ${newMaterialType}`); - const currentMaterial = getMaterialByCurrentPointUuid(pointUuid); - - if (currentMaterial) { - setMaterial(currentMaterial.materialId, newMaterialType); - swapLogStatus(currentMaterial.materialId, `Swapped to ${newMaterialType}`); - } - - }, [addMaterial, getMaterialByCurrentPointUuid, getPointUuidByActionUuid, isPlaying, setMaterial, selectedProduct.productId]); + }, [addMaterial, getMaterialById, getPointUuidByActionUuid, isPlaying, setMaterial, selectedProduct.productId]); return { handleSwap, diff --git a/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts index 32d54c4..e655e69 100644 --- a/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts +++ b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts @@ -1,10 +1,12 @@ -import { useEffect, useCallback, useRef } from "react"; +import { useEffect, useCallback } from "react"; import { useSpawnHandler } from "./actionHandler/useSpawnHandler"; import { useSwapHandler } from "./actionHandler/useSwapHandler"; +import { useDelayHandler } from "./actionHandler/useDelayHandler"; export function useConveyorActions() { const { handleSpawn, clearCurrentSpawn } = useSpawnHandler(); const { handleSwap } = useSwapHandler(); + const { handleDelay, cleanupDelay } = useDelayHandler(); const handleDefaultAction = useCallback((action: ConveyorAction) => { console.log(`Default conveyor action ${action.actionUuid}`); @@ -14,20 +16,19 @@ export function useConveyorActions() { handleSpawn(action); }, [handleSpawn]); - const handleSwapAction = useCallback((action: ConveyorAction) => { - handleSwap(action); + const handleSwapAction = useCallback((action: ConveyorAction, materialId?: string) => { + handleSwap(action, materialId); }, [handleSwap]); - const handleDelayAction = useCallback((action: ConveyorAction) => { - const delayMs = (action.delay || 0) * 1000; - console.log(`Delaying for ${delayMs}ms`); - }, []); + const handleDelayAction = useCallback((action: ConveyorAction, materialId?: string) => { + handleDelay(action, materialId); + }, [handleDelay]); const handleDespawnAction = useCallback((action: ConveyorAction) => { console.log(`Despawning material`); }, []); - const handleConveyorAction = useCallback((action: ConveyorAction) => { + const handleConveyorAction = useCallback((action: ConveyorAction, materialId?: string) => { if (!action) return; switch (action.actionType) { @@ -38,10 +39,10 @@ export function useConveyorActions() { handleSpawnAction(action); break; case 'swap': - handleSwapAction(action); + handleSwapAction(action, materialId); break; case 'delay': - handleDelayAction(action); + handleDelayAction(action, materialId); break; case 'despawn': handleDespawnAction(action); @@ -53,7 +54,8 @@ export function useConveyorActions() { const cleanup = useCallback(() => { clearCurrentSpawn(); - }, [clearCurrentSpawn]); + cleanupDelay(); + }, [clearCurrentSpawn, cleanupDelay]); useEffect(() => { return () => { diff --git a/app/src/modules/simulation/actions/useActionHandler.ts b/app/src/modules/simulation/actions/useActionHandler.ts index 28a5359..91a3c84 100644 --- a/app/src/modules/simulation/actions/useActionHandler.ts +++ b/app/src/modules/simulation/actions/useActionHandler.ts @@ -15,13 +15,13 @@ export function useActionHandler() { const { handleMachineAction, cleanup: cleanupMachine } = useMachineActions(); const { handleStorageAction, cleanup: cleanupStorage } = useStorageActions(); - const handleAction = useCallback((action: Action) => { + const handleAction = useCallback((action: Action, materialId?: string) => { if (!action) return; try { switch (action.actionType) { case 'default': case 'spawn': case 'swap': case 'delay': case 'despawn': - handleConveyorAction(action as ConveyorAction); + handleConveyorAction(action as ConveyorAction, materialId as string); break; case 'travel': handleVehicleAction(action as VehicleAction); diff --git a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx index c0a021d..f325ac2 100644 --- a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx +++ b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx @@ -39,7 +39,6 @@ function MaterialAnimator({ return position; }; - // Handle target position changes and play state useEffect(() => { if (!isPlaying || !material.next?.pointUuid) { setIsAnimating(false); @@ -58,17 +57,14 @@ function MaterialAnimator({ } }, [material.next?.pointUuid, isPlaying]); - // Handle pause/unpause useEffect(() => { if (isPaused) { animationState.current.isPaused = true; setIsAnimating(false); - // Record the time when paused animationState.current.pausedTime = performance.now() - animationState.current.startTime; } else { animationState.current.isPaused = false; if (isPlaying && targetPosition && !isAnimating) { - // Resume from where we left off animationState.current.startTime = performance.now() - animationState.current.pausedTime; setIsAnimating(true); } @@ -81,7 +77,6 @@ function MaterialAnimator({ } const currentTime = performance.now(); - // Calculate elapsed time since animation start, minus any paused time const elapsed = (currentTime - animationState.current.startTime) / 1000; const progress = Math.min(1, (currentSpeed * elapsed) / animationState.current.totalDistance); @@ -95,6 +90,14 @@ function MaterialAnimator({ matRef.current.position.copy(targetPosition); setIsAnimating(false); onAnimationComplete?.(); + animationState.current = { + startTime: 0, + startPosition: new THREE.Vector3(), + totalDistance: 0, + pausedTime: 0, + isPaused: false, + lastFrameTime: 0 + } } }); diff --git a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx index 52e4862..c44b24b 100644 --- a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx +++ b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx @@ -103,7 +103,7 @@ function MaterialInstance({ material }: { material: MaterialSchema }) { if (toModel.type === 'transfer') { const action = getActionByPointUuid(selectedProduct.productId, material.next.pointUuid); if (action) { - triggerPointActions(action); + triggerPointActions(action, material.materialId); } } else if (toModel?.type === 'vehicle') { // Transfer to Vehicle diff --git a/app/src/modules/simulation/materials/instances/material/materialModel.tsx b/app/src/modules/simulation/materials/instances/material/materialModel.tsx index f27541b..2093584 100644 --- a/app/src/modules/simulation/materials/instances/material/materialModel.tsx +++ b/app/src/modules/simulation/materials/instances/material/materialModel.tsx @@ -32,7 +32,7 @@ export function MaterialModel({ materialType, matRef, ...props }: ModelProps) { ); diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts index 22d5cfd..967a23a 100644 --- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts +++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts @@ -1,16 +1,16 @@ -import { useCallback, useEffect, useRef } from 'react'; +import { useCallback } from 'react'; import { useActionHandler } from '../../actions/useActionHandler'; import { useProductStore } from '../../../../store/simulation/useProductStore'; import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; import { useMaterialStore } from '../../../../store/simulation/useMaterialStore'; export function useTriggerHandler() { - const { getActionByUuid, getEventByTriggerUuid, getEventByModelUuid } = useProductStore(); + const { getEventByTriggerUuid, getEventByModelUuid } = useProductStore(); const { handleAction } = useActionHandler(); - const { getMaterialByCurrentModelUuid, setCurrentLocation, setNextLocation } = useMaterialStore(); + const { setCurrentLocation, setNextLocation, getMaterialById } = useMaterialStore(); const { selectedProduct } = useSelectedProduct(); - const handleTrigger = (trigger: TriggerSchema, action: Action) => { + const handleTrigger = (trigger: TriggerSchema, action: Action, materialId: string) => { const fromEvent = getEventByTriggerUuid(selectedProduct.productId, trigger.triggerUuid); @@ -20,7 +20,7 @@ export function useTriggerHandler() { if (toEvent?.type === 'transfer') { // Transfer to Transfer if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { - const material = getMaterialByCurrentModelUuid(fromEvent.modelUuid); + const material = getMaterialById(materialId); if (material) { if (material.next) { @@ -35,7 +35,7 @@ export function useTriggerHandler() { pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, }); } - handleAction(action); + handleAction(action, materialId); } } } else if (toEvent?.type === 'vehicle') { @@ -122,7 +122,7 @@ export function useTriggerHandler() { } } - const triggerPointActions = useCallback((action: Action) => { + const triggerPointActions = useCallback((action: Action, materialId: string) => { if (!action) return; action.triggers.forEach(trigger => { @@ -130,7 +130,7 @@ export function useTriggerHandler() { case 'onStart': break; case 'onComplete': - handleTrigger(trigger, action); + handleTrigger(trigger, action, materialId); break; case 'onStop': break; diff --git a/app/src/store/simulation/useMaterialStore.ts b/app/src/store/simulation/useMaterialStore.ts index 20476d0..a96191d 100644 --- a/app/src/store/simulation/useMaterialStore.ts +++ b/app/src/store/simulation/useMaterialStore.ts @@ -36,7 +36,7 @@ type MaterialsStore = { setIsRendered: (materialId: string, isRendered: boolean) => MaterialSchema | undefined; getMaterialById: (materialId: string) => MaterialSchema | undefined; - getMaterialByCurrentModelUuid: (currentModelUuid: string) => MaterialSchema | undefined; + getMaterialsByCurrentModelUuid: (currentModelUuid: string) => MaterialSchema[] | undefined; getMaterialByCurrentPointUuid: (currentPointUuid: string) => MaterialSchema | undefined; getMaterialsByPoint: (pointUuid: string) => MaterialSchema[]; getMaterialsByModel: (modelUuid: string) => MaterialSchema[]; @@ -207,11 +207,11 @@ export const useMaterialStore = create()( getMaterialById: (materialId) => { return get().materials.find(m => m.materialId === materialId); }, - - getMaterialByCurrentModelUuid: (currentModelUuid) => { - return get().materials.find(m => m.current?.modelUuid === currentModelUuid); + + getMaterialsByCurrentModelUuid: (currentModelUuid) => { + return get().materials.filter(m => m.current?.modelUuid === currentModelUuid); }, - + getMaterialByCurrentPointUuid: (currentPointlUuid) => { return get().materials.find(m => m.current?.pointUuid === currentPointlUuid); }, From 4ac77e462c2689e1b659236c128364803c684daa Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Tue, 6 May 2025 18:38:27 +0530 Subject: [PATCH 5/7] arm points path upated and points restricted between 270 and 360 --- .../mechanics/roboticArmMechanics.tsx | 21 +- .../instances/material/materialModel.tsx | 2 +- .../instances/animator/materialAnimator.tsx | 18 +- .../instances/animator/roboticArmAnimator.tsx | 153 +++----- .../armInstance/roboticArmInstance.tsx | 39 +- .../instances/ikInstance/ikInstance.tsx | 14 +- .../simulation/roboticArm/roboticArm.tsx | 29 +- .../simulation/ui/arm/useDraggableGLTF.ts | 345 +++++++++--------- 8 files changed, 296 insertions(+), 325 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx index c7bd8bc..997d078 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx @@ -28,22 +28,11 @@ function RoboticArmMechanics() { selectedEventData.data.modelUuid, selectedEventData.selectedPoint ) as RoboticArmPointSchema | undefined; - const action = getActionByUuid(selectedProduct.productId, selectedAction.actionId) as RoboticArmPointSchema["actions"][0] | undefined; - if (action) { - if (point?.actions) { - setSelectedPointData(point); - if (point.actions.length > 0 && action) { - setActiveOption(action.actionType as "default" | "pickAndPlace"); - setSelectedAction(selectedAction.actionId, selectedAction.actionName); - } - } - } else { - if (point?.actions) { - setSelectedPointData(point); - if (point.actions.length > 0) { - setActiveOption(point.actions[0].actionType as "default" | "pickAndPlace"); - setSelectedAction(point.actions[0].actionUuid, point.actions[0].actionName); - } + if (point?.actions) { + setSelectedPointData(point); + if (point.actions.length > 0) { + setActiveOption(point.actions[0].actionType as "default" | "pickAndPlace"); + setSelectedAction(point.actions[0].actionUuid, point.actions[0].actionName); } } } else { diff --git a/app/src/modules/simulation/materials/instances/material/materialModel.tsx b/app/src/modules/simulation/materials/instances/material/materialModel.tsx index f27541b..2093584 100644 --- a/app/src/modules/simulation/materials/instances/material/materialModel.tsx +++ b/app/src/modules/simulation/materials/instances/material/materialModel.tsx @@ -32,7 +32,7 @@ export function MaterialModel({ materialType, matRef, ...props }: ModelProps) { ); diff --git a/app/src/modules/simulation/roboticArm/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/materialAnimator.tsx index 893f759..b458d32 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/materialAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/materialAnimator.tsx @@ -1,17 +1,16 @@ import { useFrame } from '@react-three/fiber'; import React, { useEffect, useRef, useState } from 'react'; import * as THREE from 'three'; -import { useLogger } from '../../../../../components/ui/log/LoggerContext'; +import { MaterialModel } from '../../../materials/instances/material/materialModel'; type MaterialAnimatorProps = { ikSolver: any; - armBot: any; + armBot: ArmBotStatus; currentPhase: string; }; export default function MaterialAnimator({ ikSolver, armBot, currentPhase }: MaterialAnimatorProps) { - const sphereRef = useRef(null); - const boneWorldPosition = new THREE.Vector3(); + const sphereRef = useRef(null); const [isRendered, setIsRendered] = useState(false); useEffect(() => { @@ -41,7 +40,8 @@ export default function MaterialAnimator({ ikSolver, armBot, currentPhase }: Mat const direction = new THREE.Vector3(); direction.subVectors(boneWorldPos, boneTargetWorldPos).normalize(); const downwardDirection = direction.clone().negate(); - const adjustedPosition = boneWorldPos.clone().addScaledVector(downwardDirection, -0.25); + + const adjustedPosition = boneWorldPos.clone().addScaledVector(downwardDirection, -0.01); //set position sphereRef.current.position.copy(adjustedPosition); @@ -56,10 +56,10 @@ export default function MaterialAnimator({ ikSolver, armBot, currentPhase }: Mat return ( <> {isRendered && ( - - - - + )} ); diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index a1e88cc..3ca7665 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -2,38 +2,33 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useFrame } from '@react-three/fiber'; import * as THREE from 'three'; import { Line, Text } from '@react-three/drei'; -import { - useAnimationPlaySpeed, - usePauseButtonStore, - usePlayButtonStore, - useResetButtonStore -} from '../../../../../store/usePlayButtonStore'; +import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; + +type PointWithDegree = { + position: [number, number, number]; + degree: number; +}; + +function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone, armBot, path }: any) { -function RoboticArmAnimator({ - HandleCallback, - restPosition, - ikSolver, - targetBone, - armBot, - path -}: any) { const progressRef = useRef(0); const curveRef = useRef(null); - const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); - const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]); - const [customCurvePoints, setCustomCurvePoints] = useState(null); - let curveHeight = 1.75 const totalDistanceRef = useRef(0); const startTimeRef = useRef(null); const segmentDistancesRef = useRef([]); + const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); + const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]); + const [circlePointsWithDegrees, setCirclePointsWithDegrees] = useState([]); + const [customCurvePoints, setCustomCurvePoints] = useState(null); + let curveHeight = 1.75 + const CIRCLE_RADIUS = 1.6 + // Zustand stores const { isPlaying } = usePlayButtonStore(); const { isPaused } = usePauseButtonStore(); const { isReset } = useResetButtonStore(); const { speed } = useAnimationPlaySpeed(); - const CIRCLE_RADIUS = 1.6 - // Update path state whenever `path` prop changes useEffect(() => { setCurrentPath(path); @@ -47,7 +42,7 @@ function RoboticArmAnimator({ //Handle Reset Animation useEffect(() => { - if (isReset) { + if (isReset || !isPlaying) { progressRef.current = 0; curveRef.current = null; setCurrentPath([]); @@ -77,8 +72,8 @@ function RoboticArmAnimator({ return points; } - - function generateRingPointsWithDegrees(radius: number, segments: number) { + //Generate CirclePoints with Angle + function generateRingPointsWithDegrees(radius: number, segments: number, initialRotation: [number, number, number]) { const points: { position: [number, number, number]; degree: number }[] = []; for (let i = 0; i < segments; i++) { const angleRadians = (i / segments) * Math.PI * 2; @@ -92,10 +87,12 @@ function RoboticArmAnimator({ } return points; } - useEffect(() => { - const points = generateRingPointsWithDegrees(CIRCLE_RADIUS, 64); - }, [armBot.position]); + // Handle circle points based on armBot position + useEffect(() => { + const points = generateRingPointsWithDegrees(CIRCLE_RADIUS, 64, armBot.rotation); + setCirclePointsWithDegrees(points) + }, [armBot.rotation]); // Function for find nearest Circlepoints Index const findNearestIndex = (nearestPoint: [number, number, number], points: [number, number, number][], epsilon = 1e-6) => { @@ -121,9 +118,34 @@ function RoboticArmAnimator({ }, circlePoints[0]); }; + // Helper function to collect points and check forbidden degrees + const collectArcPoints = (startIdx: number, endIdx: number, clockwise: boolean) => { + const totalSegments = 64; + const arcPoints: [number, number, number][] = []; + let i = startIdx; + + while (i !== (endIdx + (clockwise ? 1 : -1) + totalSegments) % totalSegments) { + const { degree, position } = circlePointsWithDegrees[i]; + // Skip over + arcPoints.push(position); + i = (i + (clockwise ? 1 : -1) + totalSegments) % totalSegments; + } + return arcPoints; + }; + + //Range to restrict angle + const hasForbiddenDegrees = (arc: [number, number, number][]) => { + return arc.some(p => { + const idx = findNearestIndex(p, circlePoints); + const degree = circlePointsWithDegrees[idx]?.degree || 0; + return degree >= 271 && degree <= 300; // Forbidden range: 271° to 300° + }); + }; + // Handle nearest points and final path (including arc points) useEffect(() => { if (circlePoints.length > 0 && currentPath.length > 0) { + const start = currentPath[0]; const end = currentPath[currentPath.length - 1]; @@ -140,33 +162,6 @@ function RoboticArmAnimator({ const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + totalSegments) % totalSegments; const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + totalSegments) % totalSegments; - // Prepare degrees - const degreesList = generateRingPointsWithDegrees(CIRCLE_RADIUS, totalSegments); - - // Helper function to collect points and check forbidden degrees - const collectArcPoints = (startIdx: number, endIdx: number, clockwise: boolean) => { - const arcPoints: [number, number, number][] = []; - let i = startIdx; - - while (i !== (endIdx + (clockwise ? 1 : -1) + totalSegments) % totalSegments) { - const { degree, position } = degreesList[i]; - - // Skip over - arcPoints.push(position); - i = (i + (clockwise ? 1 : -1) + totalSegments) % totalSegments; - } - return arcPoints; - }; - - const hasForbiddenDegrees = (arc: [number, number, number][]) => { - return arc.some(p => { - const idx = findNearestIndex(p, circlePoints); - const degree = degreesList[idx]?.degree || 0; - return degree >= 271 && degree <= 300; // Forbidden range: 271° to 300° - }); - }; - - // Try both directions const arcClockwise = collectArcPoints(indexOfNearestStart, indexOfNearestEnd, true); const arcCounterClockwise = collectArcPoints(indexOfNearestStart, indexOfNearestEnd, false); @@ -206,8 +201,6 @@ function RoboticArmAnimator({ } }, [circlePoints, currentPath]); - - // Frame update for animation useFrame((state, delta) => { if (!ikSolver) return; @@ -215,7 +208,6 @@ function RoboticArmAnimator({ const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); if (!bone) return; - if (isPlaying) { if (isReset) { bone.position.copy(restPosition); @@ -260,12 +252,17 @@ function RoboticArmAnimator({ ikSolver.update(); } } else if (!isPlaying && currentPath.length === 0) { + progressRef.current = 0; + startTimeRef.current = null; + setCurrentPath([]); + setCustomCurvePoints([]); bone.position.copy(restPosition); + } ikSolver.update(); }); - + //Helper to Visible the Circle and Curve return ( <> {customCurvePoints && customCurvePoints?.length >= 2 && currentPath && isPlaying && ( @@ -278,50 +275,16 @@ function RoboticArmAnimator({ /> )} - + {/* Green ring */} - {/* Markers at 90°, 180°, 270°, 360° */} - {[90, 180, 270, 360].map((degree, index) => { - const rad = (degree * Math.PI) / 180; - const x = CIRCLE_RADIUS * Math.cos(rad); - const z = CIRCLE_RADIUS * Math.sin(rad); - const y = 0; // same plane as the ring (Y axis) - - return ( - - - - - ); - })} - - {/* Optional: Text Labels */} - - {[90, 180, 270, 360].map((degree, index) => { - const rad = (degree * Math.PI) / 180; - const x = CIRCLE_RADIUS * Math.cos(rad); - const z = CIRCLE_RADIUS * Math.sin(rad); - const y = 0.15; // lift the text slightly above the ring - - return ( - - {degree}° - - ); - })} - diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index ea6521b..035f25e 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -5,7 +5,6 @@ import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '.. import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb"; import { useThree } from "@react-three/fiber"; -import useModuleStore from '../../../../../store/useModuleStore'; import * as THREE from "three"; import MaterialAnimator from '../animator/materialAnimator'; @@ -24,16 +23,15 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { let startTime: number; //zustand const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore(); - const { activeModule } = useModuleStore(); const { isPlaying } = usePlayButtonStore(); - const { isReset, setReset } = useResetButtonStore(); + const { isReset } = useResetButtonStore(); const { isPaused } = usePauseButtonStore(); - function firstFrame() { startTime = performance.now(); step(); } + function step() { if (isPausedRef.current) { if (!pauseTimeRef.current) { @@ -87,6 +85,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.") } } + useEffect(() => { isPausedRef.current = isPaused; }, [isPaused]); @@ -98,6 +97,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { setArmBotState(armBot.modelUuid, "idle") setCurrentPhase("init"); setPath([]) + setIkSolver(null); removeCurrentAction(armBot.modelUuid) isPausedRef.current = false pauseTimeRef.current = null @@ -117,17 +117,17 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { useEffect(() => { const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid); if (targetMesh) { - targetMesh.visible = activeModule !== "simulation" + targetMesh.visible = (!isPlaying) } const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone); - if (isPlaying) { + if (!isReset && isPlaying) { //Moving armBot from initial point to rest position. if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") { - setArmBotActive(armBot.modelUuid, true) - setArmBotState(armBot.modelUuid, "running") - setCurrentPhase("init-to-rest"); if (targetBones) { - let curve = createCurveBetweenTwoPoints(targetBones.position, targetBones.position) + setArmBotActive(armBot.modelUuid, true) + setArmBotState(armBot.modelUuid, "running") + setCurrentPhase("init-to-rest"); + let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition) if (curve) { setPath(curve.points.map(point => [point.x, point.y, point.z])); } @@ -142,9 +142,9 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { }, 3000); return () => clearTimeout(timeoutId); } + //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"); @@ -158,17 +158,20 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } logStatus(armBot.modelUuid, "Moving armBot from rest point to start position.") } + // Moving to Pick to Drop position else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "picking" && armBot.currentAction) { requestAnimationFrame(firstFrame); } + //Moving to drop point to restPosition else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "dropping" && armBot.currentAction) { requestAnimationFrame(firstFrame); } - }else{ + } else { logStatus(armBot.modelUuid, "Simulation Play Exited") setArmBotActive(armBot.modelUuid, false) setArmBotState(armBot.modelUuid, "idle") setCurrentPhase("init"); + setIkSolver(null); setPath([]) isPausedRef.current = false pauseTimeRef.current = null @@ -177,7 +180,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { removeCurrentAction(armBot.modelUuid) } - }, [currentPhase, armBot, isPlaying, ikSolver]) + }, [currentPhase, armBot, isPlaying, isReset, ikSolver]) function createCurveBetweenTwoPoints(p1: any, p2: any) { @@ -224,9 +227,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { return ( <> - - + {!isReset && isPlaying && ( + <> + + + + )} ) diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx index be41a95..4bd05a6 100644 --- a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx @@ -11,7 +11,7 @@ type IKInstanceProps = { modelUrl: string; ikSolver: any; setIkSolver: any - armBot: any; + armBot: ArmBotStatus; groupRef: any; }; function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKInstanceProps) { @@ -57,12 +57,6 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns rotationMin: new THREE.Vector3(0, 0, 0), rotationMax: new THREE.Vector3(2, 0, 0), }, - // { - // index: 1, - // enabled: true, - // rotationMin: new THREE.Vector3(0, -Math.PI, 0), - // rotationMax: new THREE.Vector3(0, Math.PI, 0), - // }, { index: 1, enabled: true, limitation: new THREE.Vector3(0, 1, 0) }, { index: 0, enabled: false, limitation: new THREE.Vector3(0, 0, 0) }, ], @@ -76,7 +70,7 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns setSelectedArm(OOI.Target_Bone); - scene.add(helper); + // scene.add(helper); }, [cloned, gltf, setIkSolver]); @@ -86,10 +80,10 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns setSelectedArm(groupRef.current?.getObjectByName(targetBoneName)) }}> {/* {selectedArm && } */} diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx index e112884..91ccb55 100644 --- a/app/src/modules/simulation/roboticArm/roboticArm.tsx +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -1,24 +1,37 @@ -import { useEffect } from "react"; -import RoboticArmInstances from "./instances/roboticArmInstances"; +import { useEffect, useState } from "react"; import { useArmBotStore } from "../../../store/simulation/useArmBotStore"; -import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; -import ArmBotUI from "../ui/arm/armBotUI"; +import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import ArmBotUI from "../ui/arm/armBotUI"; +import RoboticArmInstances from "./instances/roboticArmInstances"; function RoboticArm() { - const { armBots } = useArmBotStore(); + const { armBots, getArmBotById } = useArmBotStore(); const { selectedEventSphere } = useSelectedEventSphere(); - const { selectedEventData } = useSelectedEventData(); const { isPlaying } = usePlayButtonStore(); + const [isArmBotSelected, setIsArmBotSelected] = useState(false); useEffect(() => { // console.log('armBots: ', armBots); }, [armBots]) + useEffect(() => { + if (selectedEventSphere) { + const selectedArmBot = getArmBotById(selectedEventSphere.userData.modelUuid); + if (selectedArmBot) { + setIsArmBotSelected(true); + } else { + setIsArmBotSelected(false); + } + } + }, [selectedEventSphere]) + return ( <> + - {selectedEventSphere && selectedEventData?.data.type === "roboticArm" && !isPlaying && + + {isArmBotSelected && !isPlaying && < ArmBotUI /> } @@ -26,4 +39,4 @@ function RoboticArm() { ); } -export default RoboticArm; +export default RoboticArm; \ No newline at end of file diff --git a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts index fdb271c..e1f7fbf 100644 --- a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts +++ b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts @@ -3,195 +3,200 @@ import * as THREE from "three"; import { ThreeEvent, useThree } from "@react-three/fiber"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { - useSelectedEventData, - useSelectedProduct, + useSelectedEventData, + useSelectedProduct, } from "../../../../store/simulation/useSimulationStore"; type OnUpdateCallback = (object: THREE.Object3D) => void; export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { - const { getEventByModelUuid, updateAction, getActionByUuid } = - useProductStore(); - const { selectedEventData } = useSelectedEventData(); - const { selectedProduct } = useSelectedProduct(); - const { camera, gl, controls } = useThree(); - const activeObjRef = useRef(null); - const planeRef = useRef( - new THREE.Plane(new THREE.Vector3(0, 1, 0), 0) - ); - const offsetRef = useRef(new THREE.Vector3()); - const initialPositionRef = useRef(new THREE.Vector3()); + const { getEventByModelUuid, updateAction, getActionByUuid } = + useProductStore(); + const { selectedEventData } = useSelectedEventData(); + const { selectedProduct } = useSelectedProduct(); + const { camera, gl, controls } = useThree(); + const activeObjRef = useRef(null); + const planeRef = useRef( + new THREE.Plane(new THREE.Vector3(0, 1, 0), 0) + ); + const offsetRef = useRef(new THREE.Vector3()); + const initialPositionRef = useRef(new THREE.Vector3()); - const raycaster = new THREE.Raycaster(); - const pointer = new THREE.Vector2(); - const [objectWorldPos, setObjectWorldPos] = useState(new THREE.Vector3()); + const raycaster = new THREE.Raycaster(); + const pointer = new THREE.Vector2(); + const [objectWorldPos, setObjectWorldPos] = useState(new THREE.Vector3()); - const handlePointerDown = (e: ThreeEvent) => { - e.stopPropagation(); + const handlePointerDown = (e: ThreeEvent) => { + e.stopPropagation(); - let obj: THREE.Object3D | null = e.object; + let obj: THREE.Object3D | null = e.object; - // Traverse up until we find modelUuid in userData - while (obj && !obj.userData?.modelUuid) { - obj = obj.parent; - } - - if (!obj) return; - - // Disable orbit controls while dragging - if (controls) (controls as any).enabled = false; - - activeObjRef.current = obj; - initialPositionRef.current.copy(obj.position); - - // Get world position - setObjectWorldPos(obj.getWorldPosition(objectWorldPos)); - - // Set plane at the object's Y level - planeRef.current.set(new THREE.Vector3(0, 1, 0), -objectWorldPos.y); - - // Convert pointer to NDC - const rect = gl.domElement.getBoundingClientRect(); - pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1; - pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1; - - // Raycast to intersection - raycaster.setFromCamera(pointer, camera); - const intersection = new THREE.Vector3(); - raycaster.ray.intersectPlane(planeRef.current, intersection); - - // Calculate offset - offsetRef.current.copy(objectWorldPos).sub(intersection); - - // Start listening for drag - gl.domElement.addEventListener("pointermove", handlePointerMove); - gl.domElement.addEventListener("pointerup", handlePointerUp); - }; - - const handlePointerMove = (e: PointerEvent) => { - if (!activeObjRef.current) return; - if (selectedEventData?.data.type === "roboticArm") { - const selectedArmBot = getEventByModelUuid( - selectedProduct.productId, - selectedEventData.data.modelUuid - ); - if (!selectedArmBot) return; - // Check if Shift key is pressed - const isShiftKeyPressed = e.shiftKey; - - // Get the mouse position relative to the canvas - const rect = gl.domElement.getBoundingClientRect(); - pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1; - pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1; - - // Update raycaster to point to the mouse position - raycaster.setFromCamera(pointer, camera); - - // Create a vector to store intersection point - const intersection = new THREE.Vector3(); - const intersects = raycaster.ray.intersectPlane( - planeRef.current, - intersection - ); - if (!intersects) return; - - // Add offset for dragging - intersection.add(offsetRef.current); - - // Get the parent's world matrix if exists - const parent = activeObjRef.current.parent; - const targetPosition = new THREE.Vector3(); - - // OnPointerDown - initialPositionRef.current.copy(objectWorldPos); - - // OnPointerMove - if (isShiftKeyPressed) { - const { x: initialX, y: initialY } = initialPositionRef.current; - const { x: objectX, z: objectZ } = objectWorldPos; - - const deltaX = intersection.x - initialX; - - targetPosition.set(objectX, initialY + deltaX, objectZ); - } else { - // For free movement - targetPosition.copy(intersection); - } - - // CONSTRAIN MOVEMENT HERE: - const centerX = selectedArmBot.position[0]; - const centerZ = selectedArmBot.position[2]; - const minDistance = 1.2; - const maxDistance = 2; - - let deltaX = targetPosition.x - centerX; - let deltaZ = targetPosition.z - centerZ; - - // Calculate angle in radians - let angle = Math.atan2(deltaZ, deltaX); // [-PI, PI] - - // Convert angle to degrees - let angleDeg = (angle * 180) / Math.PI; // [-180, 180] - - // Normalize to [0, 360] - if (angleDeg < 0) { - angleDeg += 360; - } - - // Allow only angles between 90° and 270° - if (angleDeg < 0 || angleDeg > 270) { - // Clamp to nearest boundary (90° or 270°) - const distanceTo90 = Math.abs(angleDeg - 0); - const distanceTo270 = Math.abs(angleDeg - 270); - - if (distanceTo90 < distanceTo270) { - angleDeg = 0; - } else { - return - // angleDeg = 270; + // Traverse up until we find modelUuid in userData + while (obj && !obj.userData?.modelUuid) { + obj = obj.parent; } - // Update angle in radians - angle = (angleDeg * Math.PI) / 180; - } + if (!obj) return; - // Clamp distance - let distance = Math.sqrt(deltaX * deltaX + deltaZ * deltaZ); - const clampedDistance = Math.min( - Math.max(distance, minDistance), - maxDistance - ); + // Disable orbit controls while dragging + if (controls) (controls as any).enabled = false; - // Set final target position - targetPosition.x = centerX + Math.cos(angle) * clampedDistance; - targetPosition.z = centerZ + Math.sin(angle) * clampedDistance; + activeObjRef.current = obj; + initialPositionRef.current.copy(obj.position); - // Clamp Y axis if needed - targetPosition.y = Math.min(Math.max(targetPosition.y, 0.6), 1.9); + // Get world position + setObjectWorldPos(obj.getWorldPosition(objectWorldPos)); - // Convert to local if parent exists - if (parent) { - parent.worldToLocal(targetPosition); - } + // Set plane at the object's Y level + planeRef.current.set(new THREE.Vector3(0, 1, 0), -objectWorldPos.y); - // Update the object position - activeObjRef.current.position.copy(targetPosition); - } - }; + // Convert pointer to NDC + const rect = gl.domElement.getBoundingClientRect(); + pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1; + pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1; - const handlePointerUp = () => { - if (controls) (controls as any).enabled = true; + // Raycast to intersection + raycaster.setFromCamera(pointer, camera); + const intersection = new THREE.Vector3(); + raycaster.ray.intersectPlane(planeRef.current, intersection); - if (activeObjRef.current) { - // Pass the updated position to the onUpdate callback to persist it - onUpdate(activeObjRef.current); - } + // Calculate offset + offsetRef.current.copy(objectWorldPos).sub(intersection); - gl.domElement.removeEventListener("pointermove", handlePointerMove); - gl.domElement.removeEventListener("pointerup", handlePointerUp); + // Start listening for drag + gl.domElement.addEventListener("pointermove", handlePointerMove); + gl.domElement.addEventListener("pointerup", handlePointerUp); + }; - activeObjRef.current = null; - }; + const handlePointerMove = (e: PointerEvent) => { + if (!activeObjRef.current) return; + if (selectedEventData?.data.type === "roboticArm") { + const selectedArmBot = getEventByModelUuid( + selectedProduct.productId, + selectedEventData.data.modelUuid + ); + if (!selectedArmBot) return; + // Check if Shift key is pressed + const isShiftKeyPressed = e.shiftKey; - return { handlePointerDown }; + // Get the mouse position relative to the canvas + const rect = gl.domElement.getBoundingClientRect(); + pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1; + pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1; + + // Update raycaster to point to the mouse position + raycaster.setFromCamera(pointer, camera); + + // Create a vector to store intersection point + const intersection = new THREE.Vector3(); + const intersects = raycaster.ray.intersectPlane( + planeRef.current, + intersection + ); + if (!intersects) return; + + // Add offset for dragging + intersection.add(offsetRef.current); + + // Get the parent's world matrix if exists + const parent = activeObjRef.current.parent; + const targetPosition = new THREE.Vector3(); + + // OnPointerDown + initialPositionRef.current.copy(objectWorldPos); + + // OnPointerMove + if (isShiftKeyPressed) { + const { x: initialX, y: initialY } = initialPositionRef.current; + const { x: objectX, z: objectZ } = objectWorldPos; + + const deltaX = intersection.x - initialX; + + targetPosition.set(objectX, initialY + deltaX, objectZ); + } else { + // For free movement + targetPosition.copy(intersection); + } + + // CONSTRAIN MOVEMENT HERE: + const centerX = selectedArmBot.position[0]; + const centerZ = selectedArmBot.position[2]; + const minDistance = 1.2; + const maxDistance = 2; + + const delta = new THREE.Vector3(targetPosition.x - centerX, 0, targetPosition.z - centerZ); + + // Create quaternion from rotation + const robotEuler = new THREE.Euler(selectedArmBot.rotation[0], selectedArmBot.rotation[1], selectedArmBot.rotation[2]); + const robotQuaternion = new THREE.Quaternion().setFromEuler(robotEuler); + + // Inverse rotate + const inverseQuaternion = robotQuaternion.clone().invert(); + delta.applyQuaternion(inverseQuaternion); + + // Angle in robot local space + let relativeAngle = Math.atan2(delta.z, delta.x); + let angleDeg = (relativeAngle * 180) / Math.PI; + if (angleDeg < 0) { + angleDeg += 360; + } + + // Clamp angle + if (angleDeg < 0 || angleDeg > 270) { + const distanceTo90 = Math.abs(angleDeg - 0); + const distanceTo270 = Math.abs(angleDeg - 270); + if (distanceTo90 < distanceTo270) { + angleDeg = 0; + } else { + return; + } + relativeAngle = (angleDeg * Math.PI) / 180; + } + + // Distance clamp + const distance = delta.length(); + const clampedDistance = Math.min(Math.max(distance, minDistance), maxDistance); + + // Calculate local target + const finalLocal = new THREE.Vector3( + Math.cos(relativeAngle) * clampedDistance, + 0, + Math.sin(relativeAngle) * clampedDistance + ); + + // Rotate back to world space + finalLocal.applyQuaternion(robotQuaternion); + + targetPosition.x = centerX + finalLocal.x; + targetPosition.z = centerZ + finalLocal.z; + + + // Clamp Y axis if needed + targetPosition.y = Math.min(Math.max(targetPosition.y, 0.6), 1.9); + + // Convert to local if parent exists + if (parent) { + parent.worldToLocal(targetPosition); + } + + // Update the object position + activeObjRef.current.position.copy(targetPosition); + } + }; + + const handlePointerUp = () => { + if (controls) (controls as any).enabled = true; + + if (activeObjRef.current) { + // Pass the updated position to the onUpdate callback to persist it + onUpdate(activeObjRef.current); + } + + gl.domElement.removeEventListener("pointermove", handlePointerMove); + gl.domElement.removeEventListener("pointerup", handlePointerUp); + + activeObjRef.current = null; + }; + + return { handlePointerDown }; } From 53912b2597571172af73bcc1d09983ed7395441d Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 6 May 2025 19:12:58 +0530 Subject: [PATCH 6/7] feat: Enhance conveyor and material handling with pause functionality and state management --- .../mechanics/roboticArmMechanics.tsx | 21 +++------ .../ui/simulation/simulationPlayer.tsx | 8 ++++ .../conveyor/actionHandler/useDelayHandler.ts | 32 ++++++++++---- .../conveyor/actionHandler/useSpawnHandler.ts | 12 +++++ .../actions/conveyor/useConveyorActions.ts | 1 - .../conveyorInstance/conveyorInstance.tsx | 31 ++++++++++++- .../conveyor/instances/conveyorInstances.tsx | 8 +++- .../instances/animator/materialAnimator.tsx | 15 +++++-- .../instances/instance/materialInstance.tsx | 5 ++- .../modules/simulation/products/products.tsx | 16 +++++++ .../simulation/roboticArm/roboticArm.tsx | 25 ++++++++--- .../instances/animator/vehicleAnimator.tsx | 2 +- .../instances/instance/vehicleInstance.tsx | 44 ++++++++++--------- .../modules/simulation/vehicle/vehicles.tsx | 25 ++++++++--- app/src/store/simulation/useConveyorStore.ts | 11 +++++ app/src/store/simulation/useMaterialStore.ts | 13 ++++++ app/src/types/simulationTypes.d.ts | 2 + 17 files changed, 202 insertions(+), 69 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx index c7bd8bc..997d078 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx @@ -28,22 +28,11 @@ function RoboticArmMechanics() { selectedEventData.data.modelUuid, selectedEventData.selectedPoint ) as RoboticArmPointSchema | undefined; - const action = getActionByUuid(selectedProduct.productId, selectedAction.actionId) as RoboticArmPointSchema["actions"][0] | undefined; - if (action) { - if (point?.actions) { - setSelectedPointData(point); - if (point.actions.length > 0 && action) { - setActiveOption(action.actionType as "default" | "pickAndPlace"); - setSelectedAction(selectedAction.actionId, selectedAction.actionName); - } - } - } else { - if (point?.actions) { - setSelectedPointData(point); - if (point.actions.length > 0) { - setActiveOption(point.actions[0].actionType as "default" | "pickAndPlace"); - setSelectedAction(point.actions[0].actionUuid, point.actions[0].actionName); - } + if (point?.actions) { + setSelectedPointData(point); + if (point.actions.length > 0) { + setActiveOption(point.actions[0].actionType as "default" | "pickAndPlace"); + setSelectedAction(point.actions[0].actionUuid, point.actions[0].actionName); } } } else { diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx index cc2efb6..0aeae66 100644 --- a/app/src/components/ui/simulation/simulationPlayer.tsx +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -38,6 +38,14 @@ const SimulationPlayer: React.FC = () => { const { isReset, setReset } = useResetButtonStore(); const { subModule } = useSubModuleStore(); + useEffect(() => { + if (isReset) { + setTimeout(()=>{ + setReset(false); + },0) + } + }, [isReset]) + // Button functions const handleReset = () => { setReset(true); diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts index bb0e7bd..f49a816 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts @@ -1,6 +1,7 @@ import { useCallback, useEffect, useRef } from "react"; import { useFrame } from "@react-three/fiber"; -import { usePlayButtonStore, usePauseButtonStore } from "../../../../../store/usePlayButtonStore"; +import { usePlayButtonStore, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore"; +import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; interface DelayInstance { delayEndTime: number; @@ -13,6 +14,8 @@ interface DelayInstance { export function useDelayHandler() { const { isPlaying } = usePlayButtonStore(); const { isPaused } = usePauseButtonStore(); + const { isReset } = useResetButtonStore(); + const { setIsPaused } = useMaterialStore(); const activeDelays = useRef>(new Map()); const cleanupDelay = useCallback(() => { @@ -20,12 +23,15 @@ export function useDelayHandler() { }, []); useEffect(() => { - return () => { + if (isReset) { cleanupDelay(); - }; - }, [cleanupDelay]); + } + }, [isReset, cleanupDelay]); + + const delayLogStatus = (materialUuid: string, status: string) => { + // console.log(`${materialUuid}, ${status}`); + } - // Handle pause/resume for all delays useEffect(() => { const currentTime = performance.now(); @@ -48,8 +54,9 @@ export function useDelayHandler() { const completedDelays: string[] = []; activeDelays.current.forEach((delay, key) => { - if (!delay.isPaused && currentTime >= delay.delayEndTime) { - console.log(`Delay completed for material ${delay.materialId || 'unknown'}`); + if (!delay.isPaused && currentTime >= delay.delayEndTime && delay.materialId) { + delayLogStatus(delay.materialId, `Delay completed}`); + setIsPaused(delay.materialId, false); completedDelays.push(key); } }); @@ -60,7 +67,7 @@ export function useDelayHandler() { }); const handleDelay = useCallback((action: ConveyorAction, materialId?: string) => { - if (!action || action.actionType !== 'delay' || !isPlaying) return; + if (!action || action.actionType !== 'delay' || !isPlaying || !materialId) return; const delayMs = (action.delay || 0) * 1000; if (delayMs <= 0) return; @@ -80,10 +87,17 @@ export function useDelayHandler() { remainingTime: 0 }); - console.log(`Started ${delayMs}ms delay for material ${materialId || 'unknown'}`); + delayLogStatus(materialId, `Started ${delayMs * 1000}s delay`); + setIsPaused(materialId, true); }, [isPlaying]); + useEffect(() => { + return () => { + cleanupDelay(); + }; + }, [cleanupDelay]); + return { handleDelay, cleanupDelay diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts index da1626b..e12e538 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts @@ -5,6 +5,7 @@ import { useMaterialStore } from "../../../../../store/simulation/useMaterialSto import { useProductStore } from "../../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; import { usePlayButtonStore, useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore"; +import { useConveyorStore } from "../../../../../store/simulation/useConveyorStore"; interface SpawnInstance { lastSpawnTime: number | null; @@ -23,6 +24,7 @@ interface SpawnInstance { export function useSpawnHandler() { const { addMaterial } = useMaterialStore(); + const { getConveyorById } = useConveyorStore(); const { getModelUuidByActionUuid, getPointUuidByActionUuid } = useProductStore(); const { isPlaying } = usePlayButtonStore(); const { isPaused } = usePauseButtonStore(); @@ -57,6 +59,7 @@ export function useSpawnHandler() { materialType: materialType, isActive: false, isVisible: true, + isPaused: false, isRendered: true, current: { modelUuid: modelUuid, @@ -81,6 +84,15 @@ export function useSpawnHandler() { return newMaterial; }, [addMaterial, getModelUuidByActionUuid, getPointUuidByActionUuid, selectedProduct.productId]); + + const getConveyorPausedState = useCallback((action: ConveyorAction) => { + const modelUuid = getModelUuidByActionUuid(selectedProduct.productId, action.actionUuid); + if (!modelUuid) return false; + + const conveyor = getConveyorById(modelUuid); + return conveyor?.isPaused ?? false; + }, [getConveyorById, getModelUuidByActionUuid, selectedProduct.productId]); + useEffect(() => { const currentTime = performance.now(); diff --git a/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts index e655e69..3846b99 100644 --- a/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts +++ b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts @@ -9,7 +9,6 @@ export function useConveyorActions() { const { handleDelay, cleanupDelay } = useDelayHandler(); const handleDefaultAction = useCallback((action: ConveyorAction) => { - console.log(`Default conveyor action ${action.actionUuid}`); }, []); const handleSpawnAction = useCallback((action: ConveyorAction) => { diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx index 9c9d612..f1fc34d 100644 --- a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx @@ -1,6 +1,33 @@ -import React from 'react' +import React, { useEffect } from 'react' +import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore'; +import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore'; +import { useResetButtonStore } from '../../../../../store/usePlayButtonStore'; + +function ConveyorInstance({ conveyor }: { conveyor: ConveyorStatus }) { + const { materials, getMaterialsByCurrentModelUuid } = useMaterialStore(); + const { isReset } = useResetButtonStore(); + + const { setConveyorPaused } = useConveyorStore(); + + useEffect(() => { + const conveyorMaterials = getMaterialsByCurrentModelUuid(conveyor.modelUuid); + if (conveyorMaterials && conveyorMaterials?.length > 0) { + + const hasPausedMaterials = conveyorMaterials.some(material => material.isPaused); + + if (hasPausedMaterials) { + setConveyorPaused(conveyor.modelUuid, true); + } else { + setConveyorPaused(conveyor.modelUuid, false); + } + } + + }, [materials, conveyor.modelUuid, getMaterialsByCurrentModelUuid, setConveyorPaused, isReset]); + + useEffect(() => { + // console.log('conveyor: ', conveyor); + }, [conveyor]) -function ConveyorInstance() { return ( <> diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx index 3f53784..4bc4252 100644 --- a/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx @@ -1,11 +1,17 @@ import React from 'react' import ConveyorInstance from './conveyorInstance/conveyorInstance' +import { useConveyorStore } from '../../../../store/simulation/useConveyorStore' function ConveyorInstances() { + + const { conveyors } = useConveyorStore(); + return ( <> - + {conveyors.map((conveyor: ConveyorStatus) => + + )} ) diff --git a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx index f325ac2..c78eac7 100644 --- a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx +++ b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState, useRef } from 'react'; import * as THREE from 'three'; import { useFrame, useThree } from '@react-three/fiber'; import { usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; +import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore'; interface MaterialAnimatorProps { matRef: React.RefObject; @@ -19,6 +20,7 @@ function MaterialAnimator({ const { scene } = useThree(); const [targetPosition, setTargetPosition] = useState(null); const [isAnimating, setIsAnimating] = useState(false); + const { getConveyorById } = useConveyorStore(); const animationState = useRef({ startTime: 0, startPosition: new THREE.Vector3(), @@ -29,7 +31,10 @@ function MaterialAnimator({ }); const { isPlaying } = usePlayButtonStore(); - const { isPaused } = usePauseButtonStore(); + const { isPaused: isGlobalPaused } = usePauseButtonStore(); + + const conveyor = getConveyorById(material.current.modelUuid); + const shouldPause = isGlobalPaused || material.isPaused || conveyor?.isPaused; const getWorldPosition = (uuid: string): THREE.Vector3 | null => { const obj = scene.getObjectByProperty('uuid', uuid); @@ -58,18 +63,20 @@ function MaterialAnimator({ }, [material.next?.pointUuid, isPlaying]); useEffect(() => { - if (isPaused) { + if (shouldPause) { + // Pause the animation animationState.current.isPaused = true; setIsAnimating(false); animationState.current.pausedTime = performance.now() - animationState.current.startTime; } else { + // Resume the animation animationState.current.isPaused = false; if (isPlaying && targetPosition && !isAnimating) { animationState.current.startTime = performance.now() - animationState.current.pausedTime; setIsAnimating(true); } } - }, [isPaused]); + }, [shouldPause, isPlaying]); useFrame(() => { if (!matRef.current || !targetPosition || !isAnimating || animationState.current.isPaused || !isPlaying) { @@ -97,7 +104,7 @@ function MaterialAnimator({ pausedTime: 0, isPaused: false, lastFrameTime: 0 - } + }; } }); diff --git a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx index c44b24b..6cbc0f0 100644 --- a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx +++ b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx @@ -39,12 +39,12 @@ function MaterialInstance({ material }: { material: MaterialSchema }) { const point = getPointByUuid(selectedProduct.productId, modelUuid, material.current.pointUuid); if (!point) { - return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 }; + return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), currentSpeed: currentSpeed || 1 }; } const position = getWorldPositionFromScene(point.uuid); if (position) { - return { position: position, rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 }; + return { position: position, rotation: new THREE.Vector3(0, 0, 0), currentSpeed: currentSpeed || 1 }; } return { @@ -54,6 +54,7 @@ function MaterialInstance({ material }: { material: MaterialSchema }) { }; }, [material, getPointByUuid]); + function getCurrentSpeed(productId: string, modelUuid: string) { const event = getEventByModelUuid(productId, modelUuid) if (event) { diff --git a/app/src/modules/simulation/products/products.tsx b/app/src/modules/simulation/products/products.tsx index 54dacf4..e1e8fa6 100644 --- a/app/src/modules/simulation/products/products.tsx +++ b/app/src/modules/simulation/products/products.tsx @@ -7,12 +7,14 @@ import { upsertProductOrEventApi } from '../../../services/simulation/UpsertProd import { getAllProductsApi } from '../../../services/simulation/getallProductsApi'; import { useVehicleStore } from '../../../store/simulation/useVehicleStore'; import { useArmBotStore } from '../../../store/simulation/useArmBotStore'; +import { useConveyorStore } from '../../../store/simulation/useConveyorStore'; function Products() { const { products, getProductById, addProduct, setProducts } = useProductStore(); const { selectedProduct, setSelectedProduct } = useSelectedProduct(); const { addVehicle, clearvehicles } = useVehicleStore(); const { addArmBot, clearArmBots } = useArmBotStore(); + const { addConveyor, clearConveyors } = useConveyorStore(); useEffect(() => { const email = localStorage.getItem('email') @@ -59,6 +61,20 @@ function Products() { } }, [selectedProduct, products]); + useEffect(() => { + if (selectedProduct.productId) { + const product = getProductById(selectedProduct.productId); + if (product) { + clearConveyors(); + product.eventDatas.forEach(events => { + if (events.type === 'transfer') { + addConveyor(selectedProduct.productId, events); + } + }); + } + } + }, [selectedProduct, products]); + return ( <> diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx index a609ad5..5440abf 100644 --- a/app/src/modules/simulation/roboticArm/roboticArm.tsx +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -1,24 +1,37 @@ -import { useEffect } from "react"; -import RoboticArmInstances from "./instances/roboticArmInstances"; +import { useEffect, useState } from "react"; import { useArmBotStore } from "../../../store/simulation/useArmBotStore"; -import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; +import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import ArmBotUI from "../ui/arm/armBotUI"; +import RoboticArmInstances from "./instances/roboticArmInstances"; function RoboticArm() { - const { armBots } = useArmBotStore(); + const { armBots, getArmBotById } = useArmBotStore(); const { selectedEventSphere } = useSelectedEventSphere(); - const { selectedEventData } = useSelectedEventData(); + const { isPlaying } = usePlayButtonStore(); + const [isArmBotSelected, setIsArmBotSelected] = useState(false); useEffect(() => { // console.log('armBots: ', armBots); }, [armBots]) + useEffect(() => { + if (selectedEventSphere) { + const selectedArmBot = getArmBotById(selectedEventSphere.userData.modelUuid); + if (selectedArmBot) { + setIsArmBotSelected(true); + } else { + setIsArmBotSelected(false); + } + } + }, [selectedEventSphere]) + return ( <> - {selectedEventSphere && selectedEventData?.data.type === "roboticArm" && + {isArmBotSelected && !isPlaying && < ArmBotUI /> } diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 460997a..e2e236a 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -10,7 +10,7 @@ interface VehicleAnimatorProps { handleCallBack: () => void; reset: () => void; currentPhase: string; - agvUuid: number; + agvUuid: string; agvDetail: VehicleStatus; } diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index d0e2afd..2eb9e6e 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -7,7 +7,7 @@ import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; import MaterialAnimator from '../animator/materialAnimator'; -function VehicleInstance({ agvDetail }: any) { +function VehicleInstance({ agvDetail }: { agvDetail: VehicleStatus }) { const { navMesh } = useNavMesh(); const vehicleRef: any = useRef(); const { isPlaying } = usePlayButtonStore(); @@ -74,28 +74,32 @@ function VehicleInstance({ agvDetail }: any) { if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity && agvDetail.materialType) { - const toDrop = computePath( - agvDetail.point.action.pickUpPoint.position, - agvDetail.point.action.unLoadPoint.position - ); - setPath(toDrop); - setCurrentPhase('pickup-drop'); - setVehicleState(agvDetail.modelUuid, 'running'); - setVehicleActive(agvDetail.modelUuid, true); - vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point'); + if (agvDetail.point.action.pickUpPoint && agvDetail.point.action.unLoadPoint) { + const toDrop = computePath( + agvDetail.point.action.pickUpPoint.position, + agvDetail.point.action.unLoadPoint.position + ); + setPath(toDrop); + setCurrentPhase('pickup-drop'); + setVehicleState(agvDetail.modelUuid, 'running'); + setVehicleActive(agvDetail.modelUuid, true); + vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point'); + } } } else if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'dropping' && agvDetail.currentLoad === 0) { - const dropToPickup = computePath( - agvDetail.point.action.unLoadPoint.position, - agvDetail.point.action.pickUpPoint.position - ); - setPath(dropToPickup); - setCurrentPhase('drop-pickup'); - setVehicleState(agvDetail.modelUuid, 'running'); - setVehicleActive(agvDetail.modelUuid, true); - vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point'); + if (agvDetail.point.action.pickUpPoint && agvDetail.point.action.unLoadPoint) { + const dropToPickup = computePath( + agvDetail.point.action.unLoadPoint.position, + agvDetail.point.action.pickUpPoint.position + ); + setPath(dropToPickup); + setCurrentPhase('drop-pickup'); + setVehicleState(agvDetail.modelUuid, 'running'); + setVehicleActive(agvDetail.modelUuid, true); + vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point'); - isIncrememtable.current = true; + isIncrememtable.current = true; + } } } else { reset() diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx index 68f40cf..0b5a5a5 100644 --- a/app/src/modules/simulation/vehicle/vehicles.tsx +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -1,26 +1,37 @@ -import { useEffect } from "react"; -import VehicleInstances from "./instances/vehicleInstances"; +import { useEffect, useState } from "react"; import { useVehicleStore } from "../../../store/simulation/useVehicleStore"; -import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; -import VehicleUI from "../ui/vehicle/vehicleUI"; +import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import VehicleUI from "../ui/vehicle/vehicleUI"; +import VehicleInstances from "./instances/vehicleInstances"; function Vehicles() { - const { vehicles } = useVehicleStore(); + const { vehicles, getVehicleById } = useVehicleStore(); const { selectedEventSphere } = useSelectedEventSphere(); - const { selectedEventData } = useSelectedEventData(); const { isPlaying } = usePlayButtonStore(); + const [isVehicleSelected, setIsVehicleSelected] = useState(false); useEffect(() => { // console.log('vehicles: ', vehicles); }, [vehicles]) + useEffect(() => { + if (selectedEventSphere) { + const selectedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid); + if (selectedVehicle) { + setIsVehicleSelected(true); + } else { + setIsVehicleSelected(false); + } + } + }, [selectedEventSphere]) + return ( <> - {selectedEventSphere && selectedEventData?.data.type === "vehicle" && !isPlaying && + {isVehicleSelected && !isPlaying && < VehicleUI /> } diff --git a/app/src/store/simulation/useConveyorStore.ts b/app/src/store/simulation/useConveyorStore.ts index 862ce79..6d920eb 100644 --- a/app/src/store/simulation/useConveyorStore.ts +++ b/app/src/store/simulation/useConveyorStore.ts @@ -14,6 +14,7 @@ interface ConveyorStore { setConveyorActive: (modelUuid: string, isActive: boolean) => void; setConveyorState: (modelUuid: string, newState: ConveyorStatus['state']) => void; + setConveyorPaused: (modelUuid: string, isPaused: boolean) => void; incrementActiveTime: (modelUuid: string, incrementBy: number) => void; incrementIdleTime: (modelUuid: string, incrementBy: number) => void; @@ -37,6 +38,7 @@ export const useConveyorStore = create()( ...event, productId, isActive: false, + isPaused: false, idleTime: 0, activeTime: 0, state: 'idle', @@ -84,6 +86,15 @@ export const useConveyorStore = create()( }); }, + setConveyorPaused: (modelUuid, isPaused) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + conveyor.isPaused = isPaused; + } + }); + }, + incrementActiveTime: (modelUuid, incrementBy) => { set((state) => { const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); diff --git a/app/src/store/simulation/useMaterialStore.ts b/app/src/store/simulation/useMaterialStore.ts index a96191d..2421f53 100644 --- a/app/src/store/simulation/useMaterialStore.ts +++ b/app/src/store/simulation/useMaterialStore.ts @@ -33,6 +33,7 @@ type MaterialsStore = { setWeight: (materialId: string, weight: number) => MaterialSchema | undefined; setIsActive: (materialId: string, isActive: boolean) => MaterialSchema | undefined; setIsVisible: (materialId: string, isVisible: boolean) => MaterialSchema | undefined; + setIsPaused: (materialId: string, isPlaying: boolean) => MaterialSchema | undefined; setIsRendered: (materialId: string, isRendered: boolean) => MaterialSchema | undefined; getMaterialById: (materialId: string) => MaterialSchema | undefined; @@ -192,6 +193,18 @@ export const useMaterialStore = create()( return updatedMaterial; }, + setIsPaused: (materialId, isPaused) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.isPaused = isPaused; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, + setIsRendered: (materialId, isRendered) => { let updatedMaterial: MaterialSchema | undefined; set((state) => { diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index c313db1..754d151 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -145,6 +145,7 @@ type productsSchema = { interface ConveyorStatus extends ConveyorEventSchema { productId: string; isActive: boolean; + isPaused: boolean; idleTime: number; activeTime: number; } @@ -198,6 +199,7 @@ interface MaterialSchema { materialType: string; isActive: boolean; isVisible: boolean; + isPaused: boolean; isRendered: boolean; startTime?: string; endTime?: string; From a48f7f5823f351444fc4cea2f8f971f65ae7fd83 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 6 May 2025 19:17:17 +0530 Subject: [PATCH 7/7] fix: Update action material from 'Default material' to 'Material 2' in robotic arm instance --- .../roboticArm/instances/armInstance/roboticArmInstance.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 035f25e..7cd3634 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -138,7 +138,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { 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, 'Default material'); + addCurrentAction(armBot.modelUuid, armBot.point.actions[0].actionUuid, 'Material 2'); }, 3000); return () => clearTimeout(timeoutId); }