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