feat: Refactor swap handling and enhance delay management in conveyor actions

This commit is contained in:
Jerald-Golden-B 2025-05-06 12:35:51 +05:30
parent bdba6447f3
commit 815a9a94ca
9 changed files with 138 additions and 48 deletions

View File

@ -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<Map<string, DelayInstance>>(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
};
}

View File

@ -3,10 +3,9 @@ import { useMaterialStore } from "../../../../../store/simulation/useMaterialSto
import { useProductStore } from "../../../../../store/simulation/useProductStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore";
import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore";
import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore"; import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore";
import * as THREE from 'three';
export function useSwapHandler() { export function useSwapHandler() {
const { addMaterial, getMaterialByCurrentPointUuid, setMaterial } = useMaterialStore(); const { addMaterial, getMaterialById, setMaterial } = useMaterialStore();
const { getPointUuidByActionUuid } = useProductStore(); const { getPointUuidByActionUuid } = useProductStore();
const { selectedProduct } = useSelectedProduct(); const { selectedProduct } = useSelectedProduct();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
@ -15,22 +14,17 @@ export function useSwapHandler() {
// console.log(`${materialUuid}, ${status}`); // console.log(`${materialUuid}, ${status}`);
} }
const handleSwap = useCallback((action: ConveyorAction) => { const handleSwap = useCallback((action: ConveyorAction, materialId?: string) => {
if (!action || action.actionType !== 'swap' || !isPlaying) return; if (!action || action.actionType !== 'swap' || !isPlaying || !materialId) return;
const { material: newMaterialType, actionUuid } = action; const { material: newMaterialType } = action;
const pointUuid = getPointUuidByActionUuid(selectedProduct.productId, actionUuid); const material = getMaterialById(materialId);
if (!material) return;
if (!pointUuid) return; setMaterial(material.materialId, newMaterialType);
swapLogStatus(material.materialId, `Swapped to ${newMaterialType}`);
const currentMaterial = getMaterialByCurrentPointUuid(pointUuid); }, [addMaterial, getMaterialById, getPointUuidByActionUuid, isPlaying, setMaterial, selectedProduct.productId]);
if (currentMaterial) {
setMaterial(currentMaterial.materialId, newMaterialType);
swapLogStatus(currentMaterial.materialId, `Swapped to ${newMaterialType}`);
}
}, [addMaterial, getMaterialByCurrentPointUuid, getPointUuidByActionUuid, isPlaying, setMaterial, selectedProduct.productId]);
return { return {
handleSwap, handleSwap,

View File

@ -1,10 +1,12 @@
import { useEffect, useCallback, useRef } from "react"; import { useEffect, useCallback } from "react";
import { useSpawnHandler } from "./actionHandler/useSpawnHandler"; import { useSpawnHandler } from "./actionHandler/useSpawnHandler";
import { useSwapHandler } from "./actionHandler/useSwapHandler"; import { useSwapHandler } from "./actionHandler/useSwapHandler";
import { useDelayHandler } from "./actionHandler/useDelayHandler";
export function useConveyorActions() { export function useConveyorActions() {
const { handleSpawn, clearCurrentSpawn } = useSpawnHandler(); const { handleSpawn, clearCurrentSpawn } = useSpawnHandler();
const { handleSwap } = useSwapHandler(); const { handleSwap } = useSwapHandler();
const { handleDelay, cleanupDelay } = useDelayHandler();
const handleDefaultAction = useCallback((action: ConveyorAction) => { const handleDefaultAction = useCallback((action: ConveyorAction) => {
console.log(`Default conveyor action ${action.actionUuid}`); console.log(`Default conveyor action ${action.actionUuid}`);
@ -14,20 +16,19 @@ export function useConveyorActions() {
handleSpawn(action); handleSpawn(action);
}, [handleSpawn]); }, [handleSpawn]);
const handleSwapAction = useCallback((action: ConveyorAction) => { const handleSwapAction = useCallback((action: ConveyorAction, materialId?: string) => {
handleSwap(action); handleSwap(action, materialId);
}, [handleSwap]); }, [handleSwap]);
const handleDelayAction = useCallback((action: ConveyorAction) => { const handleDelayAction = useCallback((action: ConveyorAction, materialId?: string) => {
const delayMs = (action.delay || 0) * 1000; handleDelay(action, materialId);
console.log(`Delaying for ${delayMs}ms`); }, [handleDelay]);
}, []);
const handleDespawnAction = useCallback((action: ConveyorAction) => { const handleDespawnAction = useCallback((action: ConveyorAction) => {
console.log(`Despawning material`); console.log(`Despawning material`);
}, []); }, []);
const handleConveyorAction = useCallback((action: ConveyorAction) => { const handleConveyorAction = useCallback((action: ConveyorAction, materialId?: string) => {
if (!action) return; if (!action) return;
switch (action.actionType) { switch (action.actionType) {
@ -38,10 +39,10 @@ export function useConveyorActions() {
handleSpawnAction(action); handleSpawnAction(action);
break; break;
case 'swap': case 'swap':
handleSwapAction(action); handleSwapAction(action, materialId);
break; break;
case 'delay': case 'delay':
handleDelayAction(action); handleDelayAction(action, materialId);
break; break;
case 'despawn': case 'despawn':
handleDespawnAction(action); handleDespawnAction(action);
@ -53,7 +54,8 @@ export function useConveyorActions() {
const cleanup = useCallback(() => { const cleanup = useCallback(() => {
clearCurrentSpawn(); clearCurrentSpawn();
}, [clearCurrentSpawn]); cleanupDelay();
}, [clearCurrentSpawn, cleanupDelay]);
useEffect(() => { useEffect(() => {
return () => { return () => {

View File

@ -15,13 +15,13 @@ export function useActionHandler() {
const { handleMachineAction, cleanup: cleanupMachine } = useMachineActions(); const { handleMachineAction, cleanup: cleanupMachine } = useMachineActions();
const { handleStorageAction, cleanup: cleanupStorage } = useStorageActions(); const { handleStorageAction, cleanup: cleanupStorage } = useStorageActions();
const handleAction = useCallback((action: Action) => { const handleAction = useCallback((action: Action, materialId?: string) => {
if (!action) return; if (!action) return;
try { try {
switch (action.actionType) { switch (action.actionType) {
case 'default': case 'spawn': case 'swap': case 'delay': case 'despawn': case 'default': case 'spawn': case 'swap': case 'delay': case 'despawn':
handleConveyorAction(action as ConveyorAction); handleConveyorAction(action as ConveyorAction, materialId as string);
break; break;
case 'travel': case 'travel':
handleVehicleAction(action as VehicleAction); handleVehicleAction(action as VehicleAction);

View File

@ -39,7 +39,6 @@ function MaterialAnimator({
return position; return position;
}; };
// Handle target position changes and play state
useEffect(() => { useEffect(() => {
if (!isPlaying || !material.next?.pointUuid) { if (!isPlaying || !material.next?.pointUuid) {
setIsAnimating(false); setIsAnimating(false);
@ -58,17 +57,14 @@ function MaterialAnimator({
} }
}, [material.next?.pointUuid, isPlaying]); }, [material.next?.pointUuid, isPlaying]);
// Handle pause/unpause
useEffect(() => { useEffect(() => {
if (isPaused) { if (isPaused) {
animationState.current.isPaused = true; animationState.current.isPaused = true;
setIsAnimating(false); setIsAnimating(false);
// Record the time when paused
animationState.current.pausedTime = performance.now() - animationState.current.startTime; animationState.current.pausedTime = performance.now() - animationState.current.startTime;
} else { } else {
animationState.current.isPaused = false; animationState.current.isPaused = false;
if (isPlaying && targetPosition && !isAnimating) { if (isPlaying && targetPosition && !isAnimating) {
// Resume from where we left off
animationState.current.startTime = performance.now() - animationState.current.pausedTime; animationState.current.startTime = performance.now() - animationState.current.pausedTime;
setIsAnimating(true); setIsAnimating(true);
} }
@ -81,7 +77,6 @@ function MaterialAnimator({
} }
const currentTime = performance.now(); const currentTime = performance.now();
// Calculate elapsed time since animation start, minus any paused time
const elapsed = (currentTime - animationState.current.startTime) / 1000; const elapsed = (currentTime - animationState.current.startTime) / 1000;
const progress = Math.min(1, (currentSpeed * elapsed) / animationState.current.totalDistance); const progress = Math.min(1, (currentSpeed * elapsed) / animationState.current.totalDistance);
@ -95,6 +90,14 @@ function MaterialAnimator({
matRef.current.position.copy(targetPosition); matRef.current.position.copy(targetPosition);
setIsAnimating(false); setIsAnimating(false);
onAnimationComplete?.(); onAnimationComplete?.();
animationState.current = {
startTime: 0,
startPosition: new THREE.Vector3(),
totalDistance: 0,
pausedTime: 0,
isPaused: false,
lastFrameTime: 0
}
} }
}); });

View File

@ -103,7 +103,7 @@ function MaterialInstance({ material }: { material: MaterialSchema }) {
if (toModel.type === 'transfer') { if (toModel.type === 'transfer') {
const action = getActionByPointUuid(selectedProduct.productId, material.next.pointUuid); const action = getActionByPointUuid(selectedProduct.productId, material.next.pointUuid);
if (action) { if (action) {
triggerPointActions(action); triggerPointActions(action, material.materialId);
} }
} else if (toModel?.type === 'vehicle') { } else if (toModel?.type === 'vehicle') {
// Transfer to Vehicle // Transfer to Vehicle

View File

@ -32,7 +32,7 @@ export function MaterialModel({ materialType, matRef, ...props }: ModelProps) {
<group ref={matRef} {...props} dispose={null}> <group ref={matRef} {...props} dispose={null}>
<primitive <primitive
object={cloned} object={cloned}
scale={[0.25, 0.25, 0.25]} scale={[0.4, 0.4, 0.4]}
/> />
</group> </group>
); );

View File

@ -1,16 +1,16 @@
import { useCallback, useEffect, useRef } from 'react'; import { useCallback } from 'react';
import { useActionHandler } from '../../actions/useActionHandler'; import { useActionHandler } from '../../actions/useActionHandler';
import { useProductStore } from '../../../../store/simulation/useProductStore'; import { useProductStore } from '../../../../store/simulation/useProductStore';
import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore';
import { useMaterialStore } from '../../../../store/simulation/useMaterialStore'; import { useMaterialStore } from '../../../../store/simulation/useMaterialStore';
export function useTriggerHandler() { export function useTriggerHandler() {
const { getActionByUuid, getEventByTriggerUuid, getEventByModelUuid } = useProductStore(); const { getEventByTriggerUuid, getEventByModelUuid } = useProductStore();
const { handleAction } = useActionHandler(); const { handleAction } = useActionHandler();
const { getMaterialByCurrentModelUuid, setCurrentLocation, setNextLocation } = useMaterialStore(); const { setCurrentLocation, setNextLocation, getMaterialById } = useMaterialStore();
const { selectedProduct } = useSelectedProduct(); const { selectedProduct } = useSelectedProduct();
const handleTrigger = (trigger: TriggerSchema, action: Action) => { const handleTrigger = (trigger: TriggerSchema, action: Action, materialId: string) => {
const fromEvent = getEventByTriggerUuid(selectedProduct.productId, trigger.triggerUuid); const fromEvent = getEventByTriggerUuid(selectedProduct.productId, trigger.triggerUuid);
@ -20,7 +20,7 @@ export function useTriggerHandler() {
if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
// Transfer to Transfer // Transfer to Transfer
if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialByCurrentModelUuid(fromEvent.modelUuid); const material = getMaterialById(materialId);
if (material) { if (material) {
if (material.next) { if (material.next) {
@ -35,7 +35,7 @@ export function useTriggerHandler() {
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
}); });
} }
handleAction(action); handleAction(action, materialId);
} }
} }
} else if (toEvent?.type === 'vehicle') { } 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; if (!action) return;
action.triggers.forEach(trigger => { action.triggers.forEach(trigger => {
@ -130,7 +130,7 @@ export function useTriggerHandler() {
case 'onStart': case 'onStart':
break; break;
case 'onComplete': case 'onComplete':
handleTrigger(trigger, action); handleTrigger(trigger, action, materialId);
break; break;
case 'onStop': case 'onStop':
break; break;

View File

@ -36,7 +36,7 @@ type MaterialsStore = {
setIsRendered: (materialId: string, isRendered: boolean) => MaterialSchema | undefined; setIsRendered: (materialId: string, isRendered: boolean) => MaterialSchema | undefined;
getMaterialById: (materialId: string) => MaterialSchema | undefined; getMaterialById: (materialId: string) => MaterialSchema | undefined;
getMaterialByCurrentModelUuid: (currentModelUuid: string) => MaterialSchema | undefined; getMaterialsByCurrentModelUuid: (currentModelUuid: string) => MaterialSchema[] | undefined;
getMaterialByCurrentPointUuid: (currentPointUuid: string) => MaterialSchema | undefined; getMaterialByCurrentPointUuid: (currentPointUuid: string) => MaterialSchema | undefined;
getMaterialsByPoint: (pointUuid: string) => MaterialSchema[]; getMaterialsByPoint: (pointUuid: string) => MaterialSchema[];
getMaterialsByModel: (modelUuid: string) => MaterialSchema[]; getMaterialsByModel: (modelUuid: string) => MaterialSchema[];
@ -207,11 +207,11 @@ export const useMaterialStore = create<MaterialsStore>()(
getMaterialById: (materialId) => { getMaterialById: (materialId) => {
return get().materials.find(m => m.materialId === materialId); return get().materials.find(m => m.materialId === materialId);
}, },
getMaterialByCurrentModelUuid: (currentModelUuid) => { getMaterialsByCurrentModelUuid: (currentModelUuid) => {
return get().materials.find(m => m.current?.modelUuid === currentModelUuid); return get().materials.filter(m => m.current?.modelUuid === currentModelUuid);
}, },
getMaterialByCurrentPointUuid: (currentPointlUuid) => { getMaterialByCurrentPointUuid: (currentPointlUuid) => {
return get().materials.find(m => m.current?.pointUuid === currentPointlUuid); return get().materials.find(m => m.current?.pointUuid === currentPointlUuid);
}, },