feat: Refactor swap handling and enhance delay management in conveyor actions
This commit is contained in:
parent
bdba6447f3
commit
815a9a94ca
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue