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 { 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,

View File

@ -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 () => {

View File

@ -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);

View File

@ -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
}
}
});

View File

@ -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

View File

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

View File

@ -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;

View File

@ -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<MaterialsStore>()(
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);
},