diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx index 9787d00..cc2efb6 100644 --- a/app/src/components/ui/simulation/simulationPlayer.tsx +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -38,18 +38,12 @@ 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); + setIsPaused(false); setSpeed(1); + setPlaySimulation(false); // local state reset }; const handlePlayStop = () => { setIsPaused(!isPaused); @@ -58,6 +52,7 @@ const SimulationPlayer: React.FC = () => { const handleExit = () => { setPlaySimulation(false); setIsPlaying(false); + setIsPaused(false); setActiveTool("cursor"); }; @@ -279,10 +274,11 @@ const SimulationPlayer: React.FC = () => { {index < intervals.length - 1 && (
= ((index + 1) / totalSegments) * 100 + className={`line ${ + progress >= ((index + 1) / totalSegments) * 100 ? "filled" : "" - }`} + }`} >
)} diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useDefaultHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useDefaultHandler.ts new file mode 100644 index 0000000..e69de29 diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts index 20d82bf..8f639e6 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts @@ -4,28 +4,44 @@ import { useFrame } from "@react-three/fiber"; import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; +import { usePlayButtonStore, useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore"; -export function useSpawnHandler() { - const { addMaterial } = useMaterialStore(); - const { getModelUuidByActionUuid, getPointUuidByActionUuid } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); - const lastSpawnTime = useRef(null); - const startTime = useRef(null); - const spawnCountRef = useRef(0); - const spawnParams = useRef<{ +interface SpawnInstance { + lastSpawnTime: number | null; + startTime: number; + spawnCount: number; + params: { material: string; intervalMs: number; totalCount: number; action: ConveyorAction; - } | null>(null); + }; + pauseStartTime: number; + remainingTime: number; + isPaused: boolean; +} - const clearCurrentSpawn = useCallback(() => { - lastSpawnTime.current = null; - startTime.current = null; - spawnCountRef.current = 0; - spawnParams.current = null; +export function useSpawnHandler() { + const { addMaterial } = useMaterialStore(); + const { getModelUuidByActionUuid, getPointUuidByActionUuid } = useProductStore(); + const { isPlaying } = usePlayButtonStore(); + const { isPaused } = usePauseButtonStore(); + const { speed } = useAnimationPlaySpeed(); + const { isReset } = useResetButtonStore(); + const { selectedProduct } = useSelectedProduct(); + + const activeSpawns = useRef>(new Map()); + + const clearAllSpawns = useCallback(() => { + activeSpawns.current.clear(); }, []); + useEffect(() => { + if (isReset) { + clearAllSpawns(); + } + }, [isReset, clearAllSpawns]); + const spawnLogStatus = (materialUuid: string, status: string) => { // console.log(`${materialUuid}, ${status}`); } @@ -64,78 +80,121 @@ export function useSpawnHandler() { addMaterial(newMaterial); return newMaterial; - }, [addMaterial]); + }, [addMaterial, getModelUuidByActionUuid, getPointUuidByActionUuid, selectedProduct.productId]); + + useEffect(() => { + const currentTime = performance.now(); + + activeSpawns.current.forEach((spawn) => { + if (isPaused && !spawn.isPaused) { + if (spawn.lastSpawnTime === null) { + spawn.remainingTime = Math.max(0, spawn.params.intervalMs - (currentTime - spawn.startTime)); + } else { + spawn.remainingTime = Math.max(0, spawn.params.intervalMs - (currentTime - spawn.lastSpawnTime)); + } + spawn.pauseStartTime = currentTime; + spawn.isPaused = true; + } else if (!isPaused && spawn.isPaused) { + if (spawn.remainingTime > 0) { + if (spawn.lastSpawnTime === null) { + spawn.startTime = currentTime - (spawn.params.intervalMs - spawn.remainingTime); + } else { + spawn.lastSpawnTime = currentTime - (spawn.params.intervalMs - spawn.remainingTime); + } + } + spawn.isPaused = false; + spawn.pauseStartTime = 0; + spawn.remainingTime = 0; + } + }); + }, [isPaused]); useFrame(() => { - if (!spawnParams.current || !startTime.current) return; + if (!isPlaying || isPaused || isReset) return; const currentTime = performance.now(); - const { material, intervalMs, totalCount, action } = spawnParams.current; - const isFirstSpawn = lastSpawnTime.current === null; - const elapsed = currentTime - startTime.current; + const completedActions: string[] = []; - // First spawn - if (isFirstSpawn) { - if (elapsed >= intervalMs) { - const createdMaterial = createNewMaterial(material, action); - if (createdMaterial) { - spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`); + activeSpawns.current.forEach((spawn, actionUuid) => { + const { material, intervalMs, totalCount, action } = spawn.params; + const isFirstSpawn = spawn.lastSpawnTime === null; + + // First spawn + if (isFirstSpawn) { + const elapsed = currentTime - spawn.startTime; + if (elapsed >= intervalMs) { + const createdMaterial = createNewMaterial(material, action); + if (createdMaterial) { + spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`); + } + spawn.lastSpawnTime = currentTime; + spawn.spawnCount = 1; + + if (totalCount <= 1) { + completedActions.push(actionUuid); + } } - lastSpawnTime.current = currentTime; - spawnCountRef.current = 1; + return; + } - if (totalCount <= 1) { - clearCurrentSpawn(); + // Subsequent spawns + if (spawn.lastSpawnTime !== null) { + const timeSinceLast = currentTime - spawn.lastSpawnTime; + if (timeSinceLast >= intervalMs) { + const count = spawn.spawnCount + 1; + const createdMaterial = createNewMaterial(material, action); + if (createdMaterial) { + spawnLogStatus(createdMaterial.materialId, `[${timeSinceLast.toFixed(2)}ms] Spawned ${material} (${count}/${totalCount})`); + } + spawn.lastSpawnTime = currentTime; + spawn.spawnCount = count; + + if (count >= totalCount) { + completedActions.push(actionUuid); + } } } - return; - } + }); - // Subsequent spawns - if (lastSpawnTime.current !== null) { - const timeSinceLast = currentTime - lastSpawnTime.current; - if (timeSinceLast >= intervalMs) { - const count = spawnCountRef.current + 1; - const createdMaterial = createNewMaterial(material, action); - if (createdMaterial) { - spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (${count}/${totalCount})`); - } - lastSpawnTime.current = currentTime; - spawnCountRef.current = count; - - if (count >= totalCount) { - clearCurrentSpawn(); - } - } - } + completedActions.forEach(actionUuid => { + activeSpawns.current.delete(actionUuid); + }); }); const handleSpawn = useCallback((action: ConveyorAction) => { if (!action || action.actionType !== 'spawn') return; - const { material, spawnInterval = 0, spawnCount = 1 } = action; + const { material, spawnInterval = 0, spawnCount = 1, actionUuid } = action; const intervalMs = spawnInterval * 1000; - clearCurrentSpawn(); + if (activeSpawns.current.has(actionUuid)) { + activeSpawns.current.delete(actionUuid); + } - spawnParams.current = { - material, - intervalMs, - totalCount: spawnCount, - action: action - }; - - startTime.current = performance.now(); - }, [clearCurrentSpawn]); + activeSpawns.current.set(actionUuid, { + lastSpawnTime: null, + startTime: performance.now(), + spawnCount: 0, + params: { + material, + intervalMs, + totalCount: spawnCount, + action: action + }, + pauseStartTime: 0, + remainingTime: 0, + isPaused: false + }); + }, []); useEffect(() => { return () => { - clearCurrentSpawn(); + clearAllSpawns(); }; - }, [clearCurrentSpawn]); + }, [clearAllSpawns]); return { handleSpawn, - clearCurrentSpawn + clearCurrentSpawn: clearAllSpawns }; } \ 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 744ff4c..f93ef4f 100644 --- a/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts +++ b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts @@ -5,7 +5,8 @@ export function useConveyorActions() { const { handleSpawn, clearCurrentSpawn } = useSpawnHandler(); const handleDefaultAction = useCallback((action: ConveyorAction) => { - // console.log(`Default conveyor action ${action.actionUuid}`); + console.log('action: ', action); + console.log(`Default conveyor action ${action.actionUuid}`); }, []); const handleSpawnAction = useCallback((action: ConveyorAction) => { diff --git a/app/src/modules/simulation/actions/useActionHandler.ts b/app/src/modules/simulation/actions/useActionHandler.ts index bf1a41c..28a5359 100644 --- a/app/src/modules/simulation/actions/useActionHandler.ts +++ b/app/src/modules/simulation/actions/useActionHandler.ts @@ -1,3 +1,4 @@ +import { usePlayButtonStore, useResetButtonStore } from "../../../store/usePlayButtonStore"; import { useConveyorActions } from "./conveyor/useConveyorActions"; import { useMachineActions } from "./machine/useMachineActions"; import { useRoboticArmActions } from "./roboticArm/useRoboticArmActions"; @@ -6,6 +7,8 @@ import { useVehicleActions } from "./vehicle/useVehicleActions"; import { useCallback, useEffect } from "react"; export function useActionHandler() { + const { isReset } = useResetButtonStore(); + const { isPlaying } = usePlayButtonStore(); const { handleConveyorAction, cleanup: cleanupConveyor } = useConveyorActions(); const { handleVehicleAction, cleanup: cleanupVehicle } = useVehicleActions(); const { handleRoboticArmAction, cleanup: cleanupRoboticArm } = useRoboticArmActions(); @@ -52,7 +55,7 @@ export function useActionHandler() { return () => { cleanup(); }; - }, [cleanup]); + }, [cleanup, isReset, isPlaying]); return { handleAction, diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index 29a6db7..84b6e7c 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -1,276 +1,265 @@ import React, { useEffect, useRef, useState } from "react"; import * as THREE from "three"; import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; -import useModuleStore, { - useSubModuleStore, -} from "../../../../../store/useModuleStore"; +import useModuleStore, { useSubModuleStore } from "../../../../../store/useModuleStore"; import { TransformControls } from "@react-three/drei"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; -import { - useSelectedEventSphere, - useSelectedEventData, -} from "../../../../../store/simulation/useSimulationStore"; +import { useSelectedEventSphere, useSelectedEventData, } from "../../../../../store/simulation/useSimulationStore"; import { useThree } from "@react-three/fiber"; +import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore"; function PointsCreator() { - const { gl, raycaster, scene, pointer, camera } = useThree(); - const { subModule } = useSubModuleStore(); - const { events, updatePoint, getPointByUuid, getEventByModelUuid } = - useEventsStore(); - const { activeModule } = useModuleStore(); - const transformRef = useRef(null); - const [transformMode, setTransformMode] = useState< - "translate" | "rotate" | null - >(null); - const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); - const { - selectedEventSphere, - setSelectedEventSphere, - clearSelectedEventSphere, - } = useSelectedEventSphere(); - const { setSelectedEventData, clearSelectedEventData } = - useSelectedEventData(); + const { gl, raycaster, scene, pointer, camera } = useThree(); + const { subModule } = useSubModuleStore(); + const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore(); + const { activeModule } = useModuleStore(); + const transformRef = useRef(null); + const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); + const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); + const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere(); + const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData(); + const { isPlaying } = usePlayButtonStore(); - useEffect(() => { - if (selectedEventSphere) { - const eventData = getEventByModelUuid( - selectedEventSphere.userData.modelUuid - ); + useEffect(() => { + if (selectedEventSphere) { + const eventData = getEventByModelUuid( + selectedEventSphere.userData.modelUuid + ); - if (eventData) { - setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid); - } else { - clearSelectedEventData(); - } - } else { - clearSelectedEventData(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedEventSphere]); - - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - const keyCombination = detectModifierKeys(e); - if (!selectedEventSphere) return; - if (keyCombination === "G") { - setTransformMode((prev) => (prev === "translate" ? null : "translate")); - } - if (keyCombination === "R") { - setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); - } - }; - - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [selectedEventSphere]); - - const updatePointToState = (selectedEventSphere: THREE.Mesh) => { - let point = JSON.parse( - JSON.stringify( - getPointByUuid( - selectedEventSphere.userData.modelUuid, - selectedEventSphere.userData.pointUuid - ) - ) - ); - if (point) { - point.position = [ - selectedEventSphere.position.x, - selectedEventSphere.position.y, - selectedEventSphere.position.z, - ]; - updatePoint( - selectedEventSphere.userData.modelUuid, - selectedEventSphere.userData.pointUuid, - point - ); - } - }; - - useEffect(() => { - const canvasElement = gl.domElement; - - let drag = false; - let isMouseDown = false; - - const onMouseDown = () => { - isMouseDown = true; - drag = false; - }; - - const onMouseUp = () => { - if (selectedEventSphere && !drag) { - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster - .intersectObjects(scene.children, true) - .filter((intersect) => intersect.object.name === "Event-Sphere"); - if (intersects.length === 0) { - clearSelectedEventSphere(); - setTransformMode(null); + if (eventData) { + setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid); + } else { + clearSelectedEventData(); + } + } else { + clearSelectedEventData(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedEventSphere]); + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + const keyCombination = detectModifierKeys(e); + if (!selectedEventSphere) return; + if (keyCombination === "G") { + setTransformMode((prev) => (prev === "translate" ? null : "translate")); + } + if (keyCombination === "R") { + setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [selectedEventSphere]); + + const updatePointToState = (selectedEventSphere: THREE.Mesh) => { + let point = JSON.parse( + JSON.stringify( + getPointByUuid( + selectedEventSphere.userData.modelUuid, + selectedEventSphere.userData.pointUuid + ) + ) + ); + if (point) { + point.position = [ + selectedEventSphere.position.x, + selectedEventSphere.position.y, + selectedEventSphere.position.z, + ]; + updatePoint( + selectedEventSphere.userData.modelUuid, + selectedEventSphere.userData.pointUuid, + point + ); } - } }; - const onMouseMove = () => { - if (isMouseDown) { - drag = true; - } - }; + useEffect(() => { + const canvasElement = gl.domElement; - if (subModule === "mechanics") { - canvasElement.addEventListener("mousedown", onMouseDown); - canvasElement.addEventListener("mouseup", onMouseUp); - canvasElement.addEventListener("mousemove", onMouseMove); - } + let drag = false; + let isMouseDown = false; - return () => { - canvasElement.removeEventListener("mousedown", onMouseDown); - canvasElement.removeEventListener("mouseup", onMouseUp); - canvasElement.removeEventListener("mousemove", onMouseMove); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [gl, subModule, selectedEventSphere]); + const onMouseDown = () => { + isMouseDown = true; + drag = false; + }; - return ( - <> - {activeModule === "simulation" && ( + const onMouseUp = () => { + if (selectedEventSphere && !drag) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter((intersect) => intersect.object.name === "Event-Sphere"); + if (intersects.length === 0) { + clearSelectedEventSphere(); + setTransformMode(null); + } + } + }; + + const onMouseMove = () => { + if (isMouseDown) { + drag = true; + } + }; + + if (subModule === "mechanics") { + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + } + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [gl, subModule, selectedEventSphere]); + + return ( <> - - {events.map((event, index) => { - if (event.type === "transfer") { - return ( - - {event.points.map((point, j) => ( - (sphereRefs.current[point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere( - sphereRefs.current[point.uuid] - ); - }} - key={`${index}-${point.uuid}`} - position={new THREE.Vector3(...point.position)} - userData={{ - modelUuid: event.modelUuid, - pointUuid: point.uuid, - }} - > - - - - ))} - - ); - } else if (event.type === "vehicle") { - return ( - - (sphereRefs.current[event.point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere( - sphereRefs.current[event.point.uuid] - ); - }} - position={new THREE.Vector3(...event.point.position)} - userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, - }} - > - - - - - ); - } else if (event.type === "roboticArm") { - return ( - - (sphereRefs.current[event.point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere( - sphereRefs.current[event.point.uuid] - ); - }} - position={new THREE.Vector3(...event.point.position)} - userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, - }} - > - - - - - ); - } else if (event.type === "machine") { - return ( - - (sphereRefs.current[event.point.uuid] = el!)} - onClick={(e) => { - e.stopPropagation(); - setSelectedEventSphere( - sphereRefs.current[event.point.uuid] - ); - }} - position={new THREE.Vector3(...event.point.position)} - userData={{ - modelUuid: event.modelUuid, - pointUuid: event.point.uuid, - }} - > - - - - - ); - } else { - return null; - } - })} - - {selectedEventSphere && transformMode && ( - { - updatePointToState(selectedEventSphere); - }} - /> - )} + {activeModule === "simulation" && ( + <> + + {events.map((event, index) => { + if (event.type === "transfer") { + return ( + + {event.points.map((point, j) => ( + (sphereRefs.current[point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere( + sphereRefs.current[point.uuid] + ); + }} + key={`${index}-${point.uuid}`} + position={new THREE.Vector3(...point.position)} + userData={{ + modelUuid: event.modelUuid, + pointUuid: point.uuid, + }} + > + + + + ))} + + ); + } else if (event.type === "vehicle") { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere( + sphereRefs.current[event.point.uuid] + ); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} + > + + + + + ); + } else if (event.type === "roboticArm") { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere( + sphereRefs.current[event.point.uuid] + ); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} + > + + + + + ); + } else if (event.type === "machine") { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere( + sphereRefs.current[event.point.uuid] + ); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} + > + + + + + ); + } else { + return null; + } + })} + + {selectedEventSphere && transformMode && ( + { + updatePointToState(selectedEventSphere); + }} + /> + )} + + )} - )} - - ); + ); } export default PointsCreator; diff --git a/app/src/modules/simulation/machine/instances/animator/machineAnimator.tsx b/app/src/modules/simulation/machine/instances/animator/machineAnimator.tsx index 6ef2b0e..c4b4b44 100644 --- a/app/src/modules/simulation/machine/instances/animator/machineAnimator.tsx +++ b/app/src/modules/simulation/machine/instances/animator/machineAnimator.tsx @@ -34,16 +34,12 @@ const MachineAnimator = ({ currentPhase, handleCallBack, processingTime, machine useEffect(() => { isPlayingRef.current = isPlaying; }, [isPlaying]); - useEffect(() => { - isResetRef.current = isReset; - }, [isReset]); + useEffect(() => { - if (isReset || !isPlaying) { reset(); - setReset(false); startTimeRef.current = 0; isPausedRef.current = false; pauseTimeRef.current = 0; @@ -53,49 +49,53 @@ const MachineAnimator = ({ currentPhase, handleCallBack, processingTime, machine } }, [isReset, isPlaying]) + + useEffect(() => { if (currentPhase === 'processing' && !animationStarted.current && machineUuid) { animationStarted.current = true; startTimeRef.current = performance.now(); animationFrameId.current = requestAnimationFrame(step); } + }, [currentPhase]); function step(time: number) { - if (!isPausedRef.current || !isResetRef.current) { - if (animationFrameId.current) { + + if (isPausedRef.current) { + if (!pauseTimeRef.current) { + pauseTimeRef.current = performance.now(); + } + animationFrameId.current = requestAnimationFrame(step); + return; + } + + if (pauseTimeRef.current) { + const pauseDuration = performance.now() - pauseTimeRef.current; + startTimeRef.current += pauseDuration; + pauseTimeRef.current = null; + } + + const elapsed = time - startTimeRef.current; + const processedTime = (processingTime * 1000) / speed; + + if (elapsed < processedTime) { + machineStatus(machineUuid, "Machine is currently processing the task"); + animationFrameId.current = requestAnimationFrame(step); + } else { + animationStarted.current = false; + if (animationFrameId.current !== null) { + removeCurrentAction(machineUuid); cancelAnimationFrame(animationFrameId.current); animationFrameId.current = null; - } - if (isPausedRef.current) { - if (!pauseTimeRef.current) { - pauseTimeRef.current = performance.now(); - } - animationFrameId.current = requestAnimationFrame(step); - return; - } - - if (pauseTimeRef.current) { - const pauseDuration = performance.now() - pauseTimeRef.current; - startTimeRef.current += pauseDuration; - pauseTimeRef.current = null; - } - - const elapsed = time - startTimeRef.current; - const processedTime = processingTime * 1000; - if (elapsed < processedTime) { - machineStatus(machineUuid, "Machine is currently processing the task"); - animationFrameId.current = requestAnimationFrame(step); - } else { - removeCurrentAction(machineUuid); - animationStarted.current = false; handleCallBack(); - } + } } + return null; } diff --git a/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx index 714bcdb..b3d4498 100644 --- a/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx +++ b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx @@ -10,10 +10,10 @@ function MachineInstance({ machineDetail }: any) { const { machines, addCurrentAction, setMachineState, setMachineActive } = useMachineStore(); const reset = () => { + setCurrentPhase("idle"); setMachineState(machineDetail.modelUuid, 'idle'); setMachineActive(machineDetail.modelUuid, false); isIncrememtable.current = true; - setCurrentPhase("idle"); } const increment = () => { if (isIncrememtable.current) { @@ -31,7 +31,7 @@ function MachineInstance({ machineDetail }: any) { if (!machineDetail.isActive && machineDetail.state === "idle" && currentPhase == "idle" && !machineDetail.currentAction) { setTimeout(() => { increment(); - }, 2000); + }, 5000); machineStatus(machineDetail.modelUuid, 'Machine is idle and waiting for next instruction.') } else if (!machineDetail.isActive && machineDetail.state === "idle" && currentPhase == "idle" && machineDetail.currentAction) { setCurrentPhase("processing"); @@ -39,8 +39,6 @@ function MachineInstance({ machineDetail }: any) { setMachineActive(machineDetail.modelUuid, true); machineStatus(machineDetail.modelUuid, "Machine started processing") } - } else { - reset(); } }, [currentPhase, isPlaying, machines]) @@ -60,6 +58,7 @@ function MachineInstance({ machineDetail }: any) { return ( <> + ) } diff --git a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx index 07c0ee0..c0a021d 100644 --- a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx +++ b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx @@ -1,26 +1,35 @@ 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'; interface MaterialAnimatorProps { matRef: React.RefObject; material: MaterialSchema; - speed: number; // units per second + currentSpeed: number; onAnimationComplete?: () => void; } function MaterialAnimator({ matRef, material, - speed, + currentSpeed, onAnimationComplete }: MaterialAnimatorProps) { const { scene } = useThree(); const [targetPosition, setTargetPosition] = useState(null); const [isAnimating, setIsAnimating] = useState(false); - const animationStartTime = useRef(0); - const startPosition = useRef(new THREE.Vector3()); - const totalDistance = useRef(0); + const animationState = useRef({ + startTime: 0, + startPosition: new THREE.Vector3(), + totalDistance: 0, + pausedTime: 0, + isPaused: false, + lastFrameTime: 0 + }); + + const { isPlaying } = usePlayButtonStore(); + const { isPaused } = usePauseButtonStore(); const getWorldPosition = (uuid: string): THREE.Vector3 | null => { const obj = scene.getObjectByProperty('uuid', uuid); @@ -30,37 +39,58 @@ function MaterialAnimator({ return position; }; + // Handle target position changes and play state useEffect(() => { - if (!material.next?.pointUuid) { - setTargetPosition(null); + if (!isPlaying || !material.next?.pointUuid) { setIsAnimating(false); return; } const newTarget = getWorldPosition(material.next.pointUuid); if (newTarget && matRef.current) { - startPosition.current.copy(matRef.current.position); - totalDistance.current = startPosition.current.distanceTo(newTarget); - animationStartTime.current = performance.now(); + animationState.current.startPosition.copy(matRef.current.position); + animationState.current.totalDistance = animationState.current.startPosition.distanceTo(newTarget); + animationState.current.startTime = performance.now() - animationState.current.pausedTime; + animationState.current.pausedTime = 0; + animationState.current.isPaused = false; setTargetPosition(newTarget); setIsAnimating(true); } - }, [material.next?.pointUuid]); + }, [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); + } + } + }, [isPaused]); useFrame(() => { - if (!matRef.current || !targetPosition || !isAnimating) return; + if (!matRef.current || !targetPosition || !isAnimating || animationState.current.isPaused || !isPlaying) { + return; + } + + 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); - // Calculate exact position based on constant speed - const elapsed = (performance.now() - animationStartTime.current) / 1000; - const progress = Math.min(1, (speed * elapsed) / totalDistance.current); - matRef.current.position.lerpVectors( - startPosition.current, + animationState.current.startPosition, targetPosition, progress ); - // Check if animation is complete if (progress >= 1) { matRef.current.position.copy(targetPosition); setIsAnimating(false); diff --git a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx index 8e2706a..338a45c 100644 --- a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx +++ b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx @@ -5,12 +5,16 @@ import { useProductStore } from '../../../../../store/simulation/useProductStore import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; import { MaterialModel } from '../material/materialModel'; import { useThree } from '@react-three/fiber'; +import { useAnimationPlaySpeed } from '../../../../../store/usePlayButtonStore'; +import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler'; function MaterialInstance({ material }: { material: MaterialSchema }) { const matRef: any = useRef(); const { scene } = useThree(); - const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid } = useProductStore(); + const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid, getActionByUuid } = useProductStore(); const { selectedProduct } = useSelectedProduct(); + const { speed } = useAnimationPlaySpeed(); + const { triggerPointActions } = useTriggerHandler(); const getWorldPositionFromScene = (pointUuid: string): THREE.Vector3 | null => { const pointObj = scene.getObjectByProperty("uuid", pointUuid); @@ -21,32 +25,32 @@ function MaterialInstance({ material }: { material: MaterialSchema }) { return worldPosition; }; - const { position, rotation, speed } = useMemo(() => { + const { position, rotation, currentSpeed } = useMemo(() => { if (!material.current?.pointUuid) { - return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), speed: 1 }; + return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 }; } const modelUuid = getModelUuidByPointUuid(selectedProduct.productId, material.current.pointUuid); if (!modelUuid) { - return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), speed: 1 }; + return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 }; } - const speed = getCurrentSpeed(selectedProduct.productId, modelUuid); + const currentSpeed = getCurrentSpeed(selectedProduct.productId, modelUuid); 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), speed: 1 }; + return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 }; } const position = getWorldPositionFromScene(point.uuid); if (position) { - return { position: position, rotation: new THREE.Vector3(0, 0, 0), speed: 1 }; + return { position: position, rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 }; } return { position: new THREE.Vector3(...point.position), rotation: new THREE.Vector3(...point.rotation), - speed: speed || 1 + currentSpeed: currentSpeed || 1 }; }, [material, getPointByUuid]); @@ -82,16 +86,25 @@ function MaterialInstance({ material }: { material: MaterialSchema }) { // console.log('material: ', material); }, [material]) + const callTrigger = () => { + const action = getActionByUuid(selectedProduct.productId, material.current.actionUuid) + if (action) { + triggerPointActions(action); + } + } + return ( <> - + {material.isRendered && + + } { console.log('123');}} + currentSpeed={currentSpeed * speed} + onAnimationComplete={() => { callTrigger() }} /> ) diff --git a/app/src/modules/simulation/materials/instances/materialInstances.tsx b/app/src/modules/simulation/materials/instances/materialInstances.tsx index 1864f0f..199e604 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/materials/materials.tsx b/app/src/modules/simulation/materials/materials.tsx index 432d815..06ed353 100644 --- a/app/src/modules/simulation/materials/materials.tsx +++ b/app/src/modules/simulation/materials/materials.tsx @@ -1,11 +1,27 @@ -import React from 'react' +import React, { useEffect } from 'react' import MaterialInstances from './instances/materialInstances' +import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore'; +import { useMaterialStore } from '../../../store/simulation/useMaterialStore'; function Materials() { + const { clearMaterials } = useMaterialStore(); + const { isPlaying } = usePlayButtonStore(); + const { isReset } = useResetButtonStore(); + + useEffect(() => { + if (isReset || !isPlaying) { + clearMaterials(); + } + }, [isReset, isPlaying]); + return ( <> - + {isPlaying && + + + + } ) diff --git a/app/src/modules/simulation/simulator/simulator.tsx b/app/src/modules/simulation/simulator/simulator.tsx index 320760f..c271286 100644 --- a/app/src/modules/simulation/simulator/simulator.tsx +++ b/app/src/modules/simulation/simulator/simulator.tsx @@ -1,21 +1,23 @@ import { useEffect } from 'react'; import { useProductStore } from '../../../store/simulation/useProductStore'; import { useActionHandler } from '../actions/useActionHandler'; +import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore'; function Simulator() { const { products } = useProductStore(); const { handleAction } = useActionHandler(); + const { isPlaying } = usePlayButtonStore(); + const { isReset } = useResetButtonStore(); useEffect(() => { + if (!isPlaying || isReset) return; + const executionOrder = determineExecutionOrder(products); - executionOrder.forEach(point => { - if ('actions' in point) { - handleAction(point.actions[0]); - } else { - handleAction(point.action); - } + executionOrder.map(point => { + const action = 'actions' in point ? point.actions[0] : point.action; + handleAction(action); }); - }, [products, handleAction]); + }, [products, handleAction, isPlaying, isReset]); function determineExecutionOrder(products: productsSchema): PointsScheme[] { // Create maps for all events and points diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx index f9fc30f..6afcc69 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -10,6 +10,7 @@ import { handleAddEventToProduct } from "../../events/points/functions/handleAdd import { QuadraticBezierLine } from "@react-three/drei"; import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; import { useDeleteTool } from "../../../../store/store"; +import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; interface ConnectionLine { id: string; @@ -29,6 +30,7 @@ function TriggerConnector() { const [helperlineColor, setHelperLineColor] = useState("red"); const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3; end: THREE.Vector3; mid: THREE.Vector3; } | null>(null); const { deleteTool } = useDeleteTool(); + const { isPlaying } = usePlayButtonStore(); const [firstSelectedPoint, setFirstSelectedPoint] = useState<{ productId: string; @@ -424,7 +426,7 @@ function TriggerConnector() { }; return ( - + {connections.map((connection) => { const startPoint = getWorldPositionFromScene(connection.startPointUuid); const endPoint = getWorldPositionFromScene(connection.endPointUuid); diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts index e29f9eb..7541522 100644 --- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts +++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts @@ -1,13 +1,128 @@ import { useCallback, useEffect, useRef } 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 } = useProductStore(); + const { getActionByUuid, getEventByTriggerUuid, getEventByModelUuid } = useProductStore(); const { handleAction } = useActionHandler(); + const { getMaterialByCurrentModelUuid, setCurrentLocation, setNextLocation } = useMaterialStore(); + const { selectedProduct } = useSelectedProduct(); - const handleTrigger = (trigger: TriggerSchema) => { + const handleTrigger = (trigger: TriggerSchema, actionUuid: string) => { + // const fromEvent = getEventByTriggerUuid(selectedProduct.productId, trigger.triggerUuid); + // console.log('fromEvent: ', fromEvent); + + // const toEvent = getEventByModelUuid(selectedProduct.productId, trigger.triggeredAsset?.triggeredModel.modelUuid || ''); + // console.log('toEvent: ', toEvent); + + // 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) { + + // setCurrentLocation(material.materialId, material.next); + + // 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 + + // } else if (toEvent?.type === 'machine') { + // // Transfer to Machine + + // } else if (toEvent?.type === 'roboticArm') { + // // Transfer to Robotic Arm + + // } else if (toEvent?.type === 'storageUnit') { + // // Transfer to Storage Unit + + // } + // } else if (fromEvent?.type === 'vehicle') { + // if (toEvent?.type === 'transfer') { + // // Vehicle to Transfer + + // } else if (toEvent?.type === 'vehicle') { + // // Vehicle to Vehicle + + // } else if (toEvent?.type === 'machine') { + // // Vehicle to Machine + + // } else if (toEvent?.type === 'roboticArm') { + // // Vehicle to Robotic Arm + + // } else if (toEvent?.type === 'storageUnit') { + // // Vehicle to Storage Unit + + // } + // } else if (fromEvent?.type === 'machine') { + // if (toEvent?.type === 'transfer') { + // // Machine to Transfer + + // } else if (toEvent?.type === 'vehicle') { + // // Machine to Vehicle + + // } else if (toEvent?.type === 'machine') { + // // Machine to Machine + + // } else if (toEvent?.type === 'roboticArm') { + // // Machine to Robotic Arm + + // } else if (toEvent?.type === 'storageUnit') { + // // Machine to Storage Unit + + // } + // } 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 === 'machine') { + // // Robotic Arm to Machine + + // } else if (toEvent?.type === 'roboticArm') { + // // Robotic Arm to Robotic Arm + + // } 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 (toEvent?.type === 'vehicle') { + // // Storage Unit to Vehicle + + // } else if (toEvent?.type === 'machine') { + // // Storage Unit to Machine + + // } else if (toEvent?.type === 'roboticArm') { + // // Storage Unit to Robotic Arm + + // } else if (toEvent?.type === 'storageUnit') { + // // Storage Unit to Storage Unit + + // } + // } } const triggerPointActions = useCallback((action: Action) => { @@ -18,7 +133,7 @@ export function useTriggerHandler() { case 'onStart': break; case 'onComplete': - handleTrigger(trigger); + handleTrigger(trigger, action.actionUuid); break; case 'onStop': break; diff --git a/app/src/modules/simulation/vehicle/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/materialAnimator.tsx new file mode 100644 index 0000000..4b9eb1c --- /dev/null +++ b/app/src/modules/simulation/vehicle/instances/animator/materialAnimator.tsx @@ -0,0 +1,61 @@ +import { useEffect, useRef, useState } from 'react'; +import { useThree, useFrame } from '@react-three/fiber'; +import * as THREE from 'three'; +import { MaterialModel } from '../../../materials/instances/material/materialModel'; +import { Html } from '@react-three/drei'; + +type MaterialAnimatorProps = { + agvDetail: VehicleStatus; +}; + + +const MaterialAnimator = ({ agvDetail }: MaterialAnimatorProps) => { + const meshRef = useRef(null!); + const [hasLoad, setHasLoad] = useState(false); + const { scene } = useThree(); + const offset = new THREE.Vector3(0, 0.85, 0); + const [htmlPosition, setHtmlPosition] = useState<[number, number, number]>([0, 0, 0]); + const [htmlRotation, setHtmlRotation] = useState<[number, number, number]>([0, 0, 0]); + + useEffect(() => { + setHasLoad(agvDetail.currentLoad > 0); + }, [agvDetail.currentLoad]); + + useFrame(() => { + if (!hasLoad || !meshRef.current) return; + + const agvModel = scene.getObjectByProperty("uuid", agvDetail.modelUuid) as THREE.Object3D; + if (agvModel) { + const worldPosition = offset.clone().applyMatrix4(agvModel.matrixWorld); + meshRef.current.position.copy(worldPosition); + setHtmlPosition([worldPosition.x, worldPosition.y, worldPosition.z]); + meshRef.current.rotation.copy(agvModel.rotation); + setHtmlRotation([agvModel.rotation.x, agvModel.rotation.y, agvModel.rotation.z]); + } + }); + + return ( + <> + {hasLoad && ( + <> + + + + {agvDetail.currentLoad} + + + + )} + + ); +}; + + +export default MaterialAnimator; diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 70c3e9d..460997a 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -199,7 +199,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai return ( <> {currentPath.length > 0 && ( - <> + {currentPath.map((point, index) => ( @@ -207,7 +207,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai ))} - + )} ); diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index 412a4ba..d0e2afd 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -5,11 +5,13 @@ import { NavMeshQuery } from '@recast-navigation/core'; import { useNavMesh } from '../../../../../store/store'; import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; +import MaterialAnimator from '../animator/materialAnimator'; function VehicleInstance({ agvDetail }: any) { const { navMesh } = useNavMesh(); + const vehicleRef: any = useRef(); const { isPlaying } = usePlayButtonStore(); - const { vehicles, setVehicleActive, setVehicleState, incrementVehicleLoad } = useVehicleStore(); + const { vehicles, setVehicleActive, setVehicleState, incrementVehicleLoad, setMaterialType } = useVehicleStore(); const [currentPhase, setCurrentPhase] = useState('stationed'); const [path, setPath] = useState<[number, number, number][]>([]); let isIncrememtable = useRef(true); @@ -44,8 +46,8 @@ function VehicleInstance({ agvDetail }: any) { const increment = () => { if (isIncrememtable.current) { - incrementVehicleLoad(agvDetail.modelUuid, 10); + setMaterialType(agvDetail.modelUuid, 'Material 1') isIncrememtable.current = false; } } @@ -71,7 +73,7 @@ function VehicleInstance({ agvDetail }: any) { }, 5000); - if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) { + if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity && agvDetail.materialType) { const toDrop = computePath( agvDetail.point.action.pickUpPoint.position, agvDetail.point.action.unLoadPoint.position @@ -118,6 +120,7 @@ function VehicleInstance({ agvDetail }: any) { setVehicleState(agvDetail.modelUuid, 'idle'); setVehicleActive(agvDetail.modelUuid, false); setPath([]); + setMaterialType(agvDetail.modelUuid, null) vehicleStatus(agvDetail.modelUuid, 'Reached pickup point again, cycle complete'); } } @@ -132,6 +135,7 @@ function VehicleInstance({ agvDetail }: any) { agvDetail={agvDetail} reset={reset} /> + ); } diff --git a/app/src/store/simulation/useMaterialStore.ts b/app/src/store/simulation/useMaterialStore.ts index ba75460..ec63ff3 100644 --- a/app/src/store/simulation/useMaterialStore.ts +++ b/app/src/store/simulation/useMaterialStore.ts @@ -6,6 +6,7 @@ type MaterialsStore = { addMaterial: (material: MaterialSchema) => MaterialSchema | undefined; removeMaterial: (materialId: string) => MaterialSchema | undefined; + clearMaterials: () => void; updateMaterial: (materialId: string, updates: Partial) => MaterialSchema | undefined; setCurrentLocation: ( @@ -35,6 +36,7 @@ type MaterialsStore = { setIsRendered: (materialId: string, isRendered: boolean) => MaterialSchema | undefined; getMaterialById: (materialId: string) => MaterialSchema | undefined; + getMaterialByCurrentModelUuid: (currentModelUuid: string) => MaterialSchema | undefined; getMaterialsByPoint: (pointUuid: string) => MaterialSchema[]; getMaterialsByModel: (modelUuid: string) => MaterialSchema[]; }; @@ -63,6 +65,12 @@ export const useMaterialStore = create()( return updatedMaterial; }, + clearMaterials: () => { + set((state) => { + state.materials = []; + }); + }, + updateMaterial: (materialId, updates) => { let updatedMaterial: MaterialSchema | undefined; set((state) => { @@ -186,6 +194,10 @@ 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); + }, getMaterialsByPoint: (pointUuid) => { return get().materials.filter(m => diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index bf99007..cdbc26c 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -61,6 +61,8 @@ type ProductsStore = { // Helper functions getProductById: (productId: string) => { productName: string; productId: string; eventDatas: EventsSchema[] } | undefined; getEventByModelUuid: (productId: string, modelUuid: string) => EventsSchema | undefined; + getEventByTriggerUuid: (productId: string, triggerUuid: string) => EventsSchema | undefined; + 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; getModelUuidByPointUuid: (productId: string, actionUuid: string) => (string) | undefined; @@ -540,6 +542,53 @@ export const useProductStore = create()( return product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); }, + 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) { + if (point.action?.triggers?.some(t => t.triggerUuid === triggerUuid)) { + return event; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point) { + if (point.action?.triggers?.some((t: any) => t.triggerUuid === triggerUuid)) { + return event; + } + } else if ('actions' in point) { + for (const action of point.actions) { + if (action.triggers?.some((t: any) => t.triggerUuid === triggerUuid)) { + return event; + } + } + } + } + } + return undefined; + }, + + 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)) { + return event; + } + } else if ('point' in event) { + if ((event as any).point?.uuid === pointUuid) { + return event; + } + } + } + return undefined; + }, + getPointByUuid: (productId, modelUuid, pointUuid) => { const event = get().getEventByModelUuid(productId, modelUuid); if (!event) return undefined;