From f37750023f487eefebf46aeb14f0242a4bf42a1f Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Thu, 15 May 2025 14:36:57 +0530 Subject: [PATCH] Refactor FloorItemsGroup to integrate environment data retrieval and improve asset loading progress tracking Enhance useRetrieveHandler to manage retrieval timing based on animation speed and arm bot state Update materials component to streamline imports and improve readability Modify roboticArmInstance to change state handling from 'idle' to 'running' during active phases Fix useTriggerHandler to ensure actions are handled correctly when material is null Refactor vehicleInstance to improve material drop handling and integrate conveyor and robotic arm interactions Add getLastMaterial method to useVehicleStore for better material management in vehicle instances --- .../properties/GlobalProperties.tsx | 571 +++++++++--------- .../builder/groups/floorItemsGroup.tsx | 129 ++-- .../actionHandler/useRetrieveHandler.ts | 145 ++++- .../simulation/materials/materials.tsx | 2 +- .../armInstance/roboticArmInstance.tsx | 8 +- .../triggerHandler/useTriggerHandler.ts | 4 + .../instances/instance/vehicleInstance.tsx | 182 ++++-- app/src/store/simulation/useVehicleStore.ts | 17 + 8 files changed, 640 insertions(+), 418 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index cf8ac60..0422e2b 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -4,305 +4,304 @@ import InputToggle from "../../../ui/inputs/InputToggle"; import { AIIcon } from "../../../icons/ExportCommonIcons"; import LabeledButton from "../../../ui/inputs/LabledButton"; import { - useAzimuth, - useElevation, - useLimitDistance, - useRenderDistance, - useResetCamera, - useRoofVisibility, - useSelectedWallItem, - useShadows, - useSocketStore, - useTileDistance, - useToggleView, - useWallVisibility, + useAzimuth, + useElevation, + useLimitDistance, + useRenderDistance, + useResetCamera, + useRoofVisibility, + useSelectedWallItem, + useShadows, + useSocketStore, + useTileDistance, + useToggleView, + useWallVisibility, } from "../../../../store/builder/store"; import { setEnvironment } from "../../../../services/factoryBuilder/environment/setEnvironment"; import * as CONSTANTS from "../../../../types/world/worldConstants"; import { validateBBox } from "@turf/helpers"; const GlobalProperties: React.FC = () => { - const { toggleView, setToggleView } = useToggleView(); - const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); - const { roofVisibility, setRoofVisibility } = useRoofVisibility(); - const { wallVisibility, setWallVisibility } = useWallVisibility(); - const { shadows, setShadows } = useShadows(); - const { resetCamera, setResetCamera } = useResetCamera(); - const { elevation, setElevation } = useElevation(); - const { azimuth, setAzimuth } = useAzimuth(); - const { renderDistance, setRenderDistance } = useRenderDistance(); - const { setPlaneValue, setGridValue, planeValue, gridValue } = - useTileDistance(); - useEffect(() => {}, [gridValue, planeValue]); - const { socket } = useSocketStore(); - const { limitDistance, setLimitDistance } = useLimitDistance(); - const [distance, setDistance] = useState(40); - useEffect(() => {}, [limitDistance]); + const { toggleView, setToggleView } = useToggleView(); + const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); + const { roofVisibility, setRoofVisibility } = useRoofVisibility(); + const { wallVisibility, setWallVisibility } = useWallVisibility(); + const { shadows, setShadows } = useShadows(); + const { resetCamera, setResetCamera } = useResetCamera(); + const { elevation, setElevation } = useElevation(); + const { azimuth, setAzimuth } = useAzimuth(); + const { renderDistance, setRenderDistance } = useRenderDistance(); + const { setPlaneValue, setGridValue, planeValue, gridValue } = useTileDistance(); + const { socket } = useSocketStore(); + const { limitDistance, setLimitDistance } = useLimitDistance(); + const [distance, setDistance] = useState(40); - const [limitGridDistance, setLimitGridDistance] = useState(false); - const [gridDistance, setGridDistance] = useState(3); + const [limitGridDistance, setLimitGridDistance] = useState(false); + const [gridDistance, setGridDistance] = useState(3); - const optimizeScene = async (value: any) => { - const email = localStorage.getItem("email"); - const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; + const optimizeScene = async (value: any) => { + const email = localStorage.getItem("email"); + const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; - const data = await setEnvironment( - organization, - localStorage.getItem("userId")!, - wallVisibility, - roofVisibility, - shadows, - 30, - true - ); - setRenderDistance(30); - setLimitDistance(true); - }; - const limitRenderDistance = async () => { - const email = localStorage.getItem("email"); - const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; + const data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + wallVisibility, + roofVisibility, + shadows, + 30, + true + ); + setRenderDistance(30); + setLimitDistance(true); + }; - if (limitDistance) { - let data = await setEnvironment( - organization, - localStorage.getItem("userId")!, - wallVisibility, - roofVisibility, - shadows, - 75, - !limitDistance - ); - setRenderDistance(75); - } else { - let data = await setEnvironment( - organization, - localStorage.getItem("userId")!, - wallVisibility, - roofVisibility, - shadows, - renderDistance, - !limitDistance - ); + const limitRenderDistance = async () => { + const email = localStorage.getItem("email"); + const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; + + if (limitDistance) { + let data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + wallVisibility, + roofVisibility, + shadows, + 75, + !limitDistance + ); + setRenderDistance(75); + } else { + let data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + wallVisibility, + roofVisibility, + shadows, + renderDistance, + !limitDistance + ); + } + setLimitDistance(!limitDistance); + }; + + function updateDistance(value: number) { + setDistance(value); + setRenderDistance(value); } - setLimitDistance(!limitDistance); - }; - - function updateDistance(value: number) { - setDistance(value); - setRenderDistance(value); - } - function updateGridDistance(value: number) { - setGridDistance(value); - // setGridValue({ size: value * 100, divisions: (value * 100) / 4 }); - // setPlaneValue({ height: value * 100, width: value * 100 }); - } - function updatedGrid(value: number) { - // console.log(" (value * 100) / 4 : ", (value * 100) / 4); - setGridValue({ size: value * 100, divisions: (value * 100) / 4 }); - setPlaneValue({ height: value * 100, width: value * 100 }); - } - - const updatedDist = async (value: number) => { - const email = localStorage.getItem("email"); - const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; - setRenderDistance(value); - // setDistance(value); - const data = await setEnvironment( - organization, - localStorage.getItem("userId")!, - wallVisibility, - roofVisibility, - shadows, - value, - limitDistance - ); - }; - - // Function to toggle roof visibility - const changeRoofVisibility = async () => { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - //using REST - const data = await setEnvironment( - organization, - localStorage.getItem("userId")!, - wallVisibility, - !roofVisibility, - shadows, - renderDistance, - limitDistance - ); - // - - //using Socket - // const visData = { - // organization: organization, - // userId: localStorage.getItem('userId')!, - // wallVisibility: wallVisibility, - // roofVisibility: !roofVisibility, - // shadowVisibility: shadows, - // socketId: socket.id - // }; - // socket.emit('v1:Environment:set', visData) - - setRoofVisibility(!roofVisibility); // Toggle roof visibility - }; - // Function to toggle wall visibility - const changeWallVisibility = async () => { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - //using REST - const data = await setEnvironment( - organization, - localStorage.getItem("userId")!, - !wallVisibility, - roofVisibility, - shadows, - renderDistance, - limitDistance - ); - // - - //using Socket - // const visData = { - // organization: organization, - // userId: localStorage.getItem('userId')!, - // wallVisibility: !wallVisibility, - // roofVisibility: roofVisibility, - // shadowVisibility: shadows, - // socketId: socket.id - // }; - // socket.emit('v1:Environment:set', visData) - - setWallVisibility(!wallVisibility); // Toggle wall visibility - }; - - const shadowVisibility = async () => { - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - //using REST - const data = await setEnvironment( - organization, - localStorage.getItem("userId")!, - wallVisibility, - roofVisibility, - !shadows, - renderDistance, - limitDistance - ); - // - - //using Socket - // const visData = { - // organization: organization, - // userId: localStorage.getItem('userId')!, - // wallVisibility: wallVisibility, - // roofVisibility: roofVisibility, - // shadowVisibility: !shadows, - // socketId: socket.id - // }; - // socket.emit('v1:Environment:set', visData) - - setShadows(!shadows); - }; - const toggleResetCamera = () => { - if (!toggleView) { - setResetCamera(true); // Trigger reset camera action + function updateGridDistance(value: number) { + setGridDistance(value); + // setGridValue({ size: value * 100, divisions: (value * 100) / 4 }); + // setPlaneValue({ height: value * 100, width: value * 100 }); + } + function updatedGrid(value: number) { + // console.log(" (value * 100) / 4 : ", (value * 100) / 4); + setGridValue({ size: value * 100, divisions: (value * 100) / 4 }); + setPlaneValue({ height: value * 100, width: value * 100 }); } - }; - // function changeRenderDistance(e: any) { - // if (parseInt(e.target.value) < 20) { - // setRenderDistance(20); - // } else if (parseInt(e.target.value) > 75) { - // setRenderDistance(75); - // } else { - // setRenderDistance(parseInt(e.target.value)); - // } - // } - return ( -
-
-
Environment
-
- - Optimize + const updatedDist = async (value: number) => { + const email = localStorage.getItem("email"); + const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; + setRenderDistance(value); + // setDistance(value); + const data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + wallVisibility, + roofVisibility, + shadows, + value, + limitDistance + ); + }; + + // Function to toggle roof visibility + const changeRoofVisibility = async () => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + + //using REST + const data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + wallVisibility, + !roofVisibility, + shadows, + renderDistance, + limitDistance + ); + // + + //using Socket + // const visData = { + // organization: organization, + // userId: localStorage.getItem('userId')!, + // wallVisibility: wallVisibility, + // roofVisibility: !roofVisibility, + // shadowVisibility: shadows, + // socketId: socket.id + // }; + // socket.emit('v1:Environment:set', visData) + + setRoofVisibility(!roofVisibility); // Toggle roof visibility + }; + // Function to toggle wall visibility + const changeWallVisibility = async () => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + //using REST + const data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + !wallVisibility, + roofVisibility, + shadows, + renderDistance, + limitDistance + ); + // + + //using Socket + // const visData = { + // organization: organization, + // userId: localStorage.getItem('userId')!, + // wallVisibility: !wallVisibility, + // roofVisibility: roofVisibility, + // shadowVisibility: shadows, + // socketId: socket.id + // }; + // socket.emit('v1:Environment:set', visData) + + setWallVisibility(!wallVisibility); // Toggle wall visibility + }; + + const shadowVisibility = async () => { + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + //using REST + const data = await setEnvironment( + organization, + localStorage.getItem("userId")!, + wallVisibility, + roofVisibility, + !shadows, + renderDistance, + limitDistance + ); + // + + //using Socket + // const visData = { + // organization: organization, + // userId: localStorage.getItem('userId')!, + // wallVisibility: wallVisibility, + // roofVisibility: roofVisibility, + // shadowVisibility: !shadows, + // socketId: socket.id + // }; + // socket.emit('v1:Environment:set', visData) + + setShadows(!shadows); + }; + + const toggleResetCamera = () => { + if (!toggleView) { + setResetCamera(true); // Trigger reset camera action + } + }; + + // function changeRenderDistance(e: any) { + // if (parseInt(e.target.value) < 20) { + // setRenderDistance(20); + // } else if (parseInt(e.target.value) > 75) { + // setRenderDistance(75); + // } else { + // setRenderDistance(parseInt(e.target.value)); + // } + // } + return ( +
+
+
Environment
+
+ + Optimize +
+ +
+ + + + {/* */} + + +
+ {/* //visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor} */} + { + // setLimitDistance(!limitDistance); + // // setDistance(75); + // // setRenderDistance(75); + // }} + onClick={async () => { + await limitRenderDistance(); // Call the function here + }} + /> + updateDistance(value)} + onPointerUp={updatedDist} + key={"6"} + /> + + {/*
+ { + setLimitGridDistance(!limitGridDistance); + }} + /> + updateGridDistance(value)} + onPointerUp={updatedGrid} + /> */} +
- -
- - - - {/* */} - - -
- {/* //visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor} */} - { - // setLimitDistance(!limitDistance); - // // setDistance(75); - // // setRenderDistance(75); - // }} - onClick={async () => { - await limitRenderDistance(); // Call the function here - }} - /> - updateDistance(value)} - onPointerUp={updatedDist} - key={"6"} - /> - - {/*
- { - setLimitGridDistance(!limitGridDistance); - }} - /> - updateGridDistance(value)} - onPointerUp={updatedGrid} - /> */} -
-
- ); + ); }; export default GlobalProperties; diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index aacd04c..58f8f05 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -27,6 +27,7 @@ import addAssetModel from "../geomentries/assets/addAssetModel"; import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; import useModuleStore from "../../../store/useModuleStore"; import { useEventsStore } from "../../../store/simulation/useEventsStore"; +import { findEnvironment } from "../../../services/factoryBuilder/environment/findEnvironment"; const assetManagerWorker = new Worker( new URL( @@ -77,71 +78,77 @@ const FloorItemsGroup = ({ const email = localStorage.getItem("email"); const organization = email!.split("@")[1].split(".")[0]; - let totalAssets = 0; - let loadedAssets = 0; + findEnvironment( + organization, + localStorage.getItem("userId")! + ).then((evnironMentData) => { - const updateLoadingProgress = (progress: number) => { - if (progress < 100) { - setLoadingProgress(progress); - } else if (progress === 100) { - setTimeout(() => { - setLoadingProgress(100); + let totalAssets = 0; + let loadedAssets = 0; + + const updateLoadingProgress = (progress: number) => { + if (progress < 100) { + setLoadingProgress(progress); + } else if (progress === 100) { setTimeout(() => { - setLoadingProgress(0); - }, 1500); - }, 1000); - } - }; - - getFloorAssets(organization).then((data) => { - if (data.length > 0) { - const uniqueItems = (data as Types.FloorItems).filter( - (item, index, self) => - index === self.findIndex((t) => t.modelfileID === item.modelfileID) - ); - totalAssets = uniqueItems.length; - if (totalAssets === 0) { - updateLoadingProgress(100); - return; + setLoadingProgress(100); + setTimeout(() => { + setLoadingProgress(0); + }, 1500); + }, 1000); } - gltfLoaderWorker.postMessage({ floorItems: uniqueItems }); - } else { - gltfLoaderWorker.postMessage({ floorItems: [] }); - loadInitialFloorItems( - itemsGroup, - setFloorItems, - addEvent, - renderDistance - ); - updateLoadingProgress(100); - } - }); + }; - gltfLoaderWorker.onmessage = async (event) => { - if (event.data.message === "gltfLoaded" && event.data.modelBlob) { - const blobUrl = URL.createObjectURL(event.data.modelBlob); - - loader.load(blobUrl, (gltf) => { - URL.revokeObjectURL(blobUrl); - THREE.Cache.remove(blobUrl); - THREE.Cache.add(event.data.modelID, gltf); - - loadedAssets++; - const progress = Math.round((loadedAssets / totalAssets) * 100); - updateLoadingProgress(progress); - - if (loadedAssets === totalAssets) { - loadInitialFloorItems( - itemsGroup, - setFloorItems, - addEvent, - renderDistance - ); + getFloorAssets(organization).then((data) => { + if (data.length > 0) { + const uniqueItems = (data as Types.FloorItems).filter( + (item, index, self) => + index === self.findIndex((t) => t.modelfileID === item.modelfileID) + ); + totalAssets = uniqueItems.length; + if (totalAssets === 0) { updateLoadingProgress(100); + return; } - }); - } - }; + gltfLoaderWorker.postMessage({ floorItems: uniqueItems }); + } else { + gltfLoaderWorker.postMessage({ floorItems: [] }); + loadInitialFloorItems( + itemsGroup, + setFloorItems, + addEvent, + evnironMentData.renderDistance + ); + updateLoadingProgress(100); + } + }); + + gltfLoaderWorker.onmessage = async (event) => { + if (event.data.message === "gltfLoaded" && event.data.modelBlob) { + const blobUrl = URL.createObjectURL(event.data.modelBlob); + + loader.load(blobUrl, (gltf) => { + URL.revokeObjectURL(blobUrl); + THREE.Cache.remove(blobUrl); + THREE.Cache.add(event.data.modelID, gltf); + + loadedAssets++; + const progress = Math.round((loadedAssets / totalAssets) * 100); + updateLoadingProgress(progress); + + if (loadedAssets === totalAssets) { + loadInitialFloorItems( + itemsGroup, + setFloorItems, + addEvent, + evnironMentData.renderDistance + ); + updateLoadingProgress(100); + } + }); + } + }; + }) }, []); useEffect(() => { @@ -363,10 +370,10 @@ const FloorItemsGroup = ({ if (!event.dataTransfer?.files[0]) return; if (selectedItem.id !== "" && event.dataTransfer?.files[0] && selectedItem.category !== 'Fenestration') { - + state.pointer.x = (event.clientX / window.innerWidth) * 2 - 1; state.pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; - + addAssetModel( raycaster, state.camera, diff --git a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts index 09731a6..87e8ee1 100644 --- a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts +++ b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts @@ -6,20 +6,22 @@ import { useSelectedProduct } from "../../../../../store/simulation/useSimulatio import { useStorageUnitStore } from "../../../../../store/simulation/useStorageUnitStore"; import { useArmBotStore } from "../../../../../store/simulation/useArmBotStore"; import { useVehicleStore } from "../../../../../store/simulation/useVehicleStore"; -import { usePlayButtonStore, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore"; +import { usePlayButtonStore, usePauseButtonStore, useResetButtonStore, useAnimationPlaySpeed } from "../../../../../store/usePlayButtonStore"; export function useRetrieveHandler() { const { addMaterial } = useMaterialStore(); - const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid } = useProductStore(); + const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid, getActionByUuid } = useProductStore(); const { getStorageUnitById, getLastMaterial, updateCurrentLoad, removeLastMaterial } = useStorageUnitStore(); const { getVehicleById, incrementVehicleLoad, addCurrentMaterial } = useVehicleStore(); const { selectedProduct } = useSelectedProduct(); const { getArmBotById, addCurrentAction } = useArmBotStore(); const { isPlaying } = usePlayButtonStore(); + const { speed } = useAnimationPlaySpeed(); const { isPaused } = usePauseButtonStore(); const { isReset } = useResetButtonStore(); const [activeRetrievals, setActiveRetrievals] = useState>(new Map()); + const retrievalTimeRef = useRef>(new Map()); const [initialDelayComplete, setInitialDelayComplete] = useState(false); const delayTimerRef = useRef(null); @@ -125,7 +127,26 @@ export function useRetrieveHandler() { const armBot = getArmBotById(triggeredModel.modelUuid); isIdle = (armBot && !armBot.isActive && armBot.state === 'idle' && !armBot.currentAction) || false; - if (isIdle) { + if (!armBot) return; + if (!retrievalTimeRef.current.has(actionUuid) && isIdle) { + retrievalTimeRef.current.set(actionUuid, currentTime); + return; + } + + const idleStartTime = retrievalTimeRef.current.get(actionUuid); + const minIdleTimeBeforeFirstRetrieval = 5000 / speed; + const minDelayBetweenRetrievals = 5000 / speed; + + const canProceedFirstRetrieval = idleStartTime !== undefined && + (currentTime - idleStartTime) >= minIdleTimeBeforeFirstRetrieval; + + const lastRetrievalTime = retrievalTimeRef.current.get(`${actionUuid}_last`) ?? null; + const canProceedSubsequent = lastRetrievalTime === null || + (currentTime - lastRetrievalTime) >= minDelayBetweenRetrievals; + + const canProceed = lastRetrievalTime === null ? canProceedFirstRetrieval : canProceedSubsequent; + + if (isIdle && canProceed) { setActiveRetrievals(prev => { const newRetrievals = new Map(prev); newRetrievals.set(actionUuid, { @@ -138,42 +159,97 @@ export function useRetrieveHandler() { const lastMaterial = getLastMaterial(storageUnit.modelUuid); if (lastMaterial) { - const material = createNewMaterial( - lastMaterial.materialId, - lastMaterial.materialType, - storageUnit.point.action - ); - if (material) { - addCurrentAction( - triggeredModel.modelUuid, - retrieval.action.triggers[0].triggeredAsset.triggeredAction?.actionUuid ?? '', - material.materialType, - material.materialId - ); - retrieveLogStatus(material.materialName, `is being picked by ${armBot?.modelName}`); + if (retrieval.action.triggers[0].triggeredAsset.triggeredAction?.actionUuid) { + const action = getActionByUuid(selectedProduct.productId, retrieval.action.triggers[0].triggeredAsset.triggeredAction.actionUuid); + if (action && action.triggers.length > 0 && action.triggers[0].triggeredAsset?.triggeredModel.modelUuid) { + const model = getEventByModelUuid(selectedProduct.productId, action.triggers[0].triggeredAsset.triggeredModel.modelUuid); + if (model) { + if (model.type === 'vehicle') { + const vehicle = getVehicleById(model.modelUuid); + if (vehicle && !vehicle.isActive && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) { + + const material = createNewMaterial( + lastMaterial.materialId, + lastMaterial.materialType, + storageUnit.point.action + ); + if (material) { + + addCurrentAction( + triggeredModel.modelUuid, + retrieval.action.triggers[0].triggeredAsset.triggeredAction?.actionUuid ?? '', + material.materialType, + material.materialId + ); + retrieveLogStatus(material.materialName, `is being picked by ${armBot?.modelName}`); + } + } + } else { + const material = createNewMaterial( + lastMaterial.materialId, + lastMaterial.materialType, + storageUnit.point.action + ); + if (material) { + + addCurrentAction( + triggeredModel.modelUuid, + retrieval.action.triggers[0].triggeredAsset.triggeredAction?.actionUuid ?? '', + material.materialType, + material.materialId + ); + retrieveLogStatus(material.materialName, `is being picked by ${armBot?.modelName}`); + } + } + setActiveRetrievals(prev => { + const newRetrievals = new Map(prev); + newRetrievals.set(actionUuid, { + ...retrieval, + isProcessing: false, + lastCheckTime: currentTime, + }); + return newRetrievals; + }); + + retrievalTimeRef.current.set(actionUuid, currentTime); + + } + } } - } - setActiveRetrievals(prev => { - const newRetrievals = new Map(prev); - newRetrievals.set(actionUuid, { - ...retrieval, - isProcessing: false, - lastCheckTime: currentTime - }); - return newRetrievals; - }); + } + } else if (!isIdle) { + retrievalTimeRef.current.delete(actionUuid); } } else if (triggeredModel.type === 'vehicle') { const vehicle = getVehicleById(triggeredModel.modelUuid); - isIdle = (vehicle && !vehicle.isActive && vehicle.state === 'idle' && vehicle.isPicking) || false; + isIdle = (vehicle && !vehicle.isActive && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) || false; if (!vehicle) return; - // const loadDuration = vehicle.point.action.unLoadDuration; - if (isIdle) { + const loadDuration = vehicle.point.action.unLoadDuration; + let minDelayBetweenRetrievals = (loadDuration * 1000) / speed; + const minIdleTimeBeforeFirstRetrieval = 3000 / speed; + + if (!retrievalTimeRef.current.has(actionUuid) && isIdle) { + retrievalTimeRef.current.set(actionUuid, currentTime); + return; + } + + const idleStartTime = retrievalTimeRef.current.get(actionUuid); + const lastRetrievalTime = retrievalTimeRef.current.get(`${actionUuid}_last`) ?? null; + + const canProceedFirstRetrieval = idleStartTime !== undefined && + (currentTime - idleStartTime) >= minIdleTimeBeforeFirstRetrieval; + + const canProceedSubsequent = lastRetrievalTime === null || + (currentTime - lastRetrievalTime) >= minDelayBetweenRetrievals; + + const canProceed = lastRetrievalTime === null ? canProceedFirstRetrieval : canProceedSubsequent; + + if (isIdle && canProceed) { setActiveRetrievals(prev => { const newRetrievals = new Map(prev); newRetrievals.set(actionUuid, { @@ -208,12 +284,17 @@ export function useRetrieveHandler() { newRetrievals.set(actionUuid, { ...retrieval, isProcessing: false, - lastCheckTime: currentTime + lastCheckTime: currentTime, }); return newRetrievals; }); - } + retrievalTimeRef.current.set(actionUuid, currentTime); + retrievalTimeRef.current.set(`${actionUuid}_last`, currentTime); + } else if (!isIdle) { + retrievalTimeRef.current.delete(actionUuid); + retrievalTimeRef.current.delete(`${actionUuid}_last`); + } } }); @@ -234,7 +315,7 @@ export function useRetrieveHandler() { newRetrievals.set(action.actionUuid, { action, isProcessing: false, - lastCheckTime: performance.now() + lastCheckTime: performance.now(), }); return newRetrievals; }); diff --git a/app/src/modules/simulation/materials/materials.tsx b/app/src/modules/simulation/materials/materials.tsx index 06ed353..ccc6b33 100644 --- a/app/src/modules/simulation/materials/materials.tsx +++ b/app/src/modules/simulation/materials/materials.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react' +import { useEffect } from 'react' import MaterialInstances from './instances/materialInstances' import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore'; import { useMaterialStore } from '../../../store/simulation/useMaterialStore'; diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index a26635f..0925b04 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -228,11 +228,11 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { logStatus(armBot.modelUuid, "Moving armBot from rest point to start position.") } // Moving to Pick to Drop position - else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "picking" && armBot.currentAction) { + else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "picking" && armBot.currentAction) { requestAnimationFrame(firstFrame); } //Moving to drop point to restPosition - else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "dropping" && armBot.currentAction) { + else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "dropping" && armBot.currentAction) { requestAnimationFrame(firstFrame); } } else { @@ -270,14 +270,14 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { else if (armBot.isActive && armBot.state == "running" && currentPhase == "rest-to-start") { logStatus(armBot.modelUuid, "Callback triggered: pick."); setArmBotActive(armBot.modelUuid, false) - setArmBotState(armBot.modelUuid, "idle") + setArmBotState(armBot.modelUuid, "running") setCurrentPhase("picking"); setPath([]) } else if (armBot.isActive && armBot.state == "running" && currentPhase == "start-to-end") { logStatus(armBot.modelUuid, "Callback triggered: drop."); setArmBotActive(armBot.modelUuid, false) - setArmBotState(armBot.modelUuid, "idle") + setArmBotState(armBot.modelUuid, "running") setCurrentPhase("dropping"); setPath([]) } diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts index 7db5730..9936535 100644 --- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts +++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts @@ -414,6 +414,10 @@ export function useTriggerHandler() { handleAction(action, material.materialId); } + } else if (action) { + setNextLocation(material.materialId, null) + + handleAction(action, material.materialId); } } diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index 4031713..df64dbe 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -5,6 +5,8 @@ import { NavMeshQuery } from '@recast-navigation/core'; import { useNavMesh } from '../../../../../store/builder/store'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; +import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; +import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore'; import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore'; import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore'; import { useProductStore } from '../../../../../store/simulation/useProductStore'; @@ -20,10 +22,12 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) const { isPlaying } = usePlayButtonStore(); const { removeMaterial, setEndTime } = useMaterialStore(); const { getStorageUnitById } = useStorageUnitStore(); + const { getArmBotById } = useArmBotStore(); + const { getConveyorById } = useConveyorStore(); const { triggerPointActions } = useTriggerHandler(); const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = useProductStore(); const { selectedProduct } = useSelectedProduct(); - const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial } = useVehicleStore(); + const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial, getLastMaterial } = useVehicleStore(); const [currentPhase, setCurrentPhase] = useState('stationed'); const [path, setPath] = useState<[number, number, number][]>([]); const pauseTimeRef = useRef(null); @@ -147,9 +151,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) } } - - - function startUnloadingProcess() { if (agvDetail.point.action.triggers.length > 0) { const trigger = getTriggerByUuid(selectedProduct.productId, agvDetail.point.action.triggers[0].triggerUuid); @@ -159,19 +160,19 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) if (model.type === 'transfer') { const action = getActionByUuid(selectedProduct.productId, agvDetail.point.action.actionUuid); if (action) { - handleMaterialDropToConveyor(action); + handleMaterialDropToConveyor(model); } } else if (model.type === 'machine') { // } else if (model.type === 'roboticArm') { const action = getActionByUuid(selectedProduct.productId, agvDetail.point.action.actionUuid); if (action) { - handleMaterialDropToArmBot(action); + handleMaterialDropToArmBot(model); } } else if (model.type === 'storageUnit') { const action = getActionByUuid(selectedProduct.productId, agvDetail.point.action.actionUuid); if (action) { - handleMaterialDropToStorageUnit(action); + handleMaterialDropToStorageUnit(model); } } } else { @@ -186,25 +187,22 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) } } - function handleMaterialDropToStorageUnit(action: Action) { - if (action.triggers.length > 0 && action.triggers[0].triggeredAsset?.triggeredModel.modelUuid) { - const storageUnit = getStorageUnitById(action.triggers[0].triggeredAsset?.triggeredModel.modelUuid); - if (storageUnit) { - if (storageUnit.point.action.actionType === 'store') { - handleMaterialDropToStorage( - agvDetail.modelUuid, - agvDetail.currentLoad, - agvDetail.point.action.unLoadDuration, - storageUnit.modelUuid, - storageUnit.point.action.storageCapacity, - agvDetail.point.action - ); - } + function handleMaterialDropToStorageUnit(model: StorageEventSchema) { + if (model) { + if (model.point.action.actionType === 'store') { + loopMaterialDropToStorage( + agvDetail.modelUuid, + agvDetail.currentLoad, + agvDetail.point.action.unLoadDuration, + model.modelUuid, + model.point.action.storageCapacity, + agvDetail.point.action + ); } } } - function handleMaterialDropToStorage( + function loopMaterialDropToStorage( vehicleId: string, vehicleCurrentLoad: number, unLoadDuration: number, @@ -262,24 +260,140 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) } } - function handleMaterialDropToConveyor(action: Action) { - if (agvDetail.currentLoad > 1) { - // - } else if (agvDetail.currentLoad === 1 && agvDetail.currentMaterials.length === 1) { - triggerPointActions(action, agvDetail.currentMaterials[0].materialId); - decrementVehicleLoad(agvDetail.modelUuid, 1); - removeLastMaterial(agvDetail.modelUuid); + function handleMaterialDropToConveyor(model: ConveyorEventSchema) { + const conveyor = getConveyorById(model.modelUuid); + if (conveyor) { + loopMaterialDropToConveyor( + agvDetail.modelUuid, + agvDetail.currentLoad, + conveyor.modelUuid, + agvDetail.point.action.unLoadDuration, + agvDetail.point.action + ); } } - function handleMaterialDropToArmBot(action: Action) { - if (agvDetail.currentLoad > 1) { - // - } else if (agvDetail.currentLoad === 1 && agvDetail.currentMaterials.length === 1) { - triggerPointActions(action, agvDetail.currentMaterials[0].materialId); + function loopMaterialDropToConveyor( + vehicleId: string, + vehicleCurrentLoad: number, + conveyorId: string, + unLoadDuration: number, + action: VehicleAction + ) { + startTime = performance.now(); + const fixedInterval = unLoadDuration * (1000 / speed); + + const dropLoop = () => { + if (isPausedRef.current) { + pauseTimeRef.current ??= performance.now(); + requestAnimationFrame(dropLoop); + return; + } + + if (pauseTimeRef.current) { + const pauseDuration = performance.now() - pauseTimeRef.current; + startTime += pauseDuration; + pauseTimeRef.current = null; + } + + const elapsedTime = performance.now() - startTime; + const conveyor = getConveyorById(conveyorId); + if (elapsedTime >= fixedInterval) { + if (conveyor && !conveyor.isPaused && vehicleCurrentLoad > 0) { + decrementVehicleLoad(vehicleId, 1); + vehicleCurrentLoad -= 1; + + const material = removeLastMaterial(vehicleId); + if (material) { + triggerPointActions(action, material.materialId); + } + + if (vehicleCurrentLoad > 0) { + startTime = performance.now(); + requestAnimationFrame(dropLoop); + } + } else if (!conveyor?.isActive) { + requestAnimationFrame(dropLoop); + } + } else { + requestAnimationFrame(dropLoop); + } + }; + dropLoop(); + } + + function handleMaterialDropToArmBot(model: RoboticArmEventSchema) { + const armBot = getArmBotById(model.modelUuid); + if (armBot && armBot.state === 'idle' && !armBot.isActive) { + loopMaterialDropToArmBot( + agvDetail.modelUuid, + agvDetail.currentLoad, + agvDetail.point.action.unLoadDuration, + model.modelUuid, + agvDetail.point.action + ); } } + function loopMaterialDropToArmBot( + vehicleId: string, + vehicleCurrentLoad: number, + unLoadDuration: number, + armBotId: string, + action: VehicleAction + ) { + startTime = performance.now(); + const armBot = getArmBotById(armBotId); + + if (!armBot || armBot.state !== 'idle' || armBot.isActive || vehicleCurrentLoad <= 0) { + return; + } + + const checkIdleDuration = () => { + if (isPausedRef.current) { + pauseTimeRef.current ??= performance.now(); + requestAnimationFrame(checkIdleDuration); + return; + } + + if (pauseTimeRef.current) { + const pauseDuration = performance.now() - pauseTimeRef.current; + startTime += pauseDuration; + pauseTimeRef.current = null; + } + + const elapsedTime = performance.now() - startTime; + + if (elapsedTime >= unLoadDuration * (1000 / speed)) { + const material = getLastMaterial(vehicleId); + if (material) { + vehicleCurrentLoad -= 1; + + triggerPointActions(action, material.materialId); + + if (vehicleCurrentLoad > 0) { + setTimeout(() => { + const waitForNextTransfer = () => { + const currentArmBot = getArmBotById(armBotId); + if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) { + startTime = performance.now(); + loopMaterialDropToArmBot(vehicleId, vehicleCurrentLoad, unLoadDuration, armBotId, action); + } else { + requestAnimationFrame(waitForNextTransfer); + } + }; + waitForNextTransfer(); + }, 0) + } + } + } else { + requestAnimationFrame(checkIdleDuration); + } + }; + + checkIdleDuration(); + } + function handleMaterialDropByDefault(droppedMaterial: number) { if (isPausedRef.current) { pauseTimeRef.current ??= performance.now(); diff --git a/app/src/store/simulation/useVehicleStore.ts b/app/src/store/simulation/useVehicleStore.ts index e69c233..b492c7e 100644 --- a/app/src/store/simulation/useVehicleStore.ts +++ b/app/src/store/simulation/useVehicleStore.ts @@ -25,6 +25,7 @@ interface VehiclesStore { addCurrentMaterial: (modelUuid: string, materialType: string, materialId: string) => void; setCurrentMaterials: (modelUuid: string, materials: { materialType: string; materialId: string; }[]) => void; removeLastMaterial: (modelUuid: string) => { materialId: string; materialType: string; } | undefined; + getLastMaterial: (modelUuid: string) => { materialId: string; materialType: string; } | undefined; clearCurrentMaterials: (modelUuid: string) => void; incrementActiveTime: (modelUuid: string, incrementBy: number) => void; incrementIdleTime: (modelUuid: string, incrementBy: number) => void; @@ -179,6 +180,22 @@ export const useVehicleStore = create()( return removedMaterial; }, + getLastMaterial: (modelUuid) => { + let removedMaterial: { materialId: string; materialType: string; } | undefined; + set((state) => { + const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid); + if (vehicle) { + if (vehicle.currentMaterials.length > 0) { + removedMaterial = { + materialId: vehicle.currentMaterials[vehicle.currentMaterials.length - 1].materialId, + materialType: vehicle.currentMaterials[vehicle.currentMaterials.length - 1].materialType + }; + } + } + }); + return removedMaterial; + }, + clearCurrentMaterials: (modelUuid) => { set((state) => { const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid);