From 815a9a94ca085e9631b4a3fe0f45cd69783fabd0 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 6 May 2025 12:35:51 +0530 Subject: [PATCH] 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); },