From 9746cfdf800f06a9ddfdbfeb39188fe81f45cbbf Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Sat, 10 May 2025 16:26:54 +0530 Subject: [PATCH] Implement storage retrieval functionality: add useRetrieveHandler, update action handling in StorageMechanics, and modify related components for improved material management. --- .../mechanics/storageMechanics.tsx | 20 +- .../actionHandler/useRetrieveHandler.ts | 207 ++++++++++++++++++ .../actionHandler/useStoreHandler.ts | 1 - .../storageUnit/useStorageUnitActions.ts | 9 + .../simulation/actions/useActionHandler.ts | 2 +- .../modules/simulation/products/products.tsx | 2 +- .../armInstance/roboticArmInstance.tsx | 4 + .../instances/animator/MaterialAnimator.tsx | 2 +- .../triggers/connector/triggerConnector.tsx | 16 ++ .../instances/instance/vehicleInstance.tsx | 18 +- .../store/simulation/useStorageUnitStore.ts | 25 ++- app/src/store/simulation/useVehicleStore.ts | 6 +- app/src/types/simulationTypes.d.ts | 2 +- 13 files changed, 279 insertions(+), 35 deletions(-) create mode 100644 app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx index 1c55bc2..3a57cfe 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx @@ -31,7 +31,8 @@ function StorageMechanics() { ) as StoragePointSchema | undefined; if (point && "action" in point) { setSelectedPointData(point); - setActiveOption(point.action.actionType as "store" | "spawn"); + const uiOption = point.action.actionType === "retrieve" ? "spawn" : point.action.actionType; + setActiveOption(uiOption as "store" | "spawn"); setSelectedAction(point.action.actionUuid, point.action.actionName); } } @@ -46,7 +47,8 @@ function StorageMechanics() { ) as StoragePointSchema | undefined; if (point && "action" in point) { setSelectedPointData(point); - setActiveOption(point.action.actionType as "store" | "spawn"); + const uiOption = point.action.actionType === "retrieve" ? "spawn" : point.action.actionType; + setActiveOption(uiOption as "store" | "spawn"); setSelectedAction(point.action.actionUuid, point.action.actionName); } } else { @@ -70,11 +72,12 @@ function StorageMechanics() { const handleActionTypeChange = (option: string) => { if (!selectedEventData || !selectedPointData) return; - const validOption = option as "store" | "spawn"; - setActiveOption(validOption); + const internalOption = actionTypeMap[option as keyof typeof actionTypeMap] as "store" | "retrieve"; + + setActiveOption(option as "store" | "spawn"); const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { - actionType: validOption, + actionType: internalOption, }); if (event) { @@ -87,7 +90,7 @@ function StorageMechanics() { updateSelectedPointData(); } - if (validOption === "spawn") { + if (option === "spawn") { const storageUnit = getStorageUnitById(selectedEventData.data.modelUuid); if (storageUnit) { const materialType = selectedPointData.action.materialType || "Default material"; @@ -211,6 +214,11 @@ function StorageMechanics() { options: ["store", "spawn"], }; + const actionTypeMap = { + spawn: "retrieve", + store: "store" + }; + return ( <> {selectedEventData && ( diff --git a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts new file mode 100644 index 0000000..846789e --- /dev/null +++ b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts @@ -0,0 +1,207 @@ +import { useCallback, useState, useEffect, useRef } from "react"; +import * as THREE from 'three'; +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 { useStorageUnitStore } from "../../../../../store/simulation/useStorageUnitStore"; +import { useArmBotStore } from "../../../../../store/simulation/useArmBotStore"; +import { useVehicleStore } from "../../../../../store/simulation/useVehicleStore"; +import { usePlayButtonStore, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore"; + +export function useRetrieveHandler() { + const { addMaterial } = useMaterialStore(); + const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid } = useProductStore(); + const { getStorageUnitById, getLastMaterial } = useStorageUnitStore(); + const { selectedProduct } = useSelectedProduct(); + const { getArmBotById, addCurrentAction } = useArmBotStore(); + const { getVehicleById } = useVehicleStore(); + const { isPlaying } = usePlayButtonStore(); + const { isPaused } = usePauseButtonStore(); + const { isReset } = useResetButtonStore(); + + const [activeRetrievals, setActiveRetrievals] = useState>(new Map()); + + const [initialDelayComplete, setInitialDelayComplete] = useState(false); + const delayTimerRef = useRef(null); + + const retrieveLogStatus = (materialUuid: string, status: string) => { + // console.log(`${materialUuid}, ${status}`); + } + + const createNewMaterial = useCallback((materialId: string, materialType: string, action: StorageAction) => { + const modelUuid = getModelUuidByActionUuid(selectedProduct.productId, action.actionUuid); + const pointUuid = getPointUuidByActionUuid(selectedProduct.productId, action.actionUuid); + if (!modelUuid || !pointUuid) return null; + const currentTime = performance.now(); + + if (action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid && + action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid && + action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid + ) { + const newMaterial: MaterialSchema = { + materialId: materialId, + materialName: `${materialType}-${Date.now()}`, + materialType: materialType, + isActive: false, + isVisible: false, + isPaused: false, + isRendered: true, + startTime: currentTime, + previous:{ + modelUuid: modelUuid, + pointUuid: pointUuid, + actionUuid: action.actionUuid + }, + current: { + modelUuid: action.triggers[0].triggeredAsset.triggeredModel.modelUuid, + pointUuid: action.triggers[0].triggeredAsset.triggeredPoint.pointUuid, + actionUuid: action.triggers[0].triggeredAsset.triggeredAction.actionUuid + }, + }; + + addMaterial(newMaterial); + return newMaterial; + } + return null; + }, [addMaterial, getModelUuidByActionUuid, getPointUuidByActionUuid, selectedProduct.productId]); + + useEffect(() => { + if (isPlaying && !initialDelayComplete) { + delayTimerRef.current = setTimeout(() => { + setInitialDelayComplete(true); + }, 3000); + } + + return () => { + if (delayTimerRef.current) { + clearTimeout(delayTimerRef.current); + } + }; + }, [isPlaying, initialDelayComplete]); + + useEffect(() => { + if (isReset || isPaused) { + setInitialDelayComplete(false); + if (delayTimerRef.current) { + clearTimeout(delayTimerRef.current); + } + } + }, [isReset, isPaused]); + + useFrame(() => { + if (!isPlaying || isPaused || isReset || !initialDelayComplete) return; + + const currentTime = performance.now(); + const completedActions: string[] = []; + let hasChanges = false; + + activeRetrievals.forEach((retrieval, actionUuid) => { + if (retrieval.isProcessing) return; + + const storageUnit = getStorageUnitById( + getModelUuidByActionUuid(selectedProduct.productId, retrieval.action.actionUuid) || '' + ); + + if (!storageUnit || storageUnit.currentLoad <= 0) { + completedActions.push(actionUuid); + hasChanges = true; + return; + } + + if (retrieval.action.triggers.length === 0 || !retrieval.action.triggers[0].triggeredAsset) { + return; + } + + const triggeredModel = getEventByModelUuid( + selectedProduct.productId, + retrieval.action.triggers[0].triggeredAsset.triggeredModel.modelUuid + ); + + if (!triggeredModel) return; + + let isIdle = false; + + if (triggeredModel.type === 'roboticArm') { + const armBot = getArmBotById(triggeredModel.modelUuid); + isIdle = armBot && !armBot.isActive && armBot.state === 'idle' && !armBot.currentAction || false; + } else if (triggeredModel.type === 'vehicle') { + const vehicle = getVehicleById(triggeredModel.modelUuid); + isIdle = vehicle && !vehicle.isActive && vehicle.state === 'idle' || false; + } + + if (isIdle) { + setActiveRetrievals(prev => { + const newRetrievals = new Map(prev); + newRetrievals.set(actionUuid, { + ...retrieval, + isProcessing: true, + lastCheckTime: currentTime + }); + return newRetrievals; + }); + + const lastMaterial = getLastMaterial(storageUnit.modelUuid); + if (lastMaterial) { + const material = createNewMaterial( + lastMaterial.materialId, + lastMaterial.materialType, + storageUnit.point.action + ); + + if (material && triggeredModel.type === 'roboticArm') { + addCurrentAction( + triggeredModel.modelUuid, + retrieval.action.triggers[0].triggeredAsset.triggeredAction?.actionUuid || '', + material.materialType, + material.materialId + ); + } + } + + setActiveRetrievals(prev => { + const newRetrievals = new Map(prev); + newRetrievals.set(actionUuid, { + ...retrieval, + isProcessing: false, + lastCheckTime: currentTime + }); + return newRetrievals; + }); + } + }); + + if (hasChanges || completedActions.length > 0) { + setActiveRetrievals(prev => { + const newRetrievals = new Map(prev); + completedActions.forEach(actionUuid => newRetrievals.delete(actionUuid)); + return newRetrievals; + }); + } + }); + + const handleRetrieve = useCallback((action: StorageAction) => { + if (!action || action.actionType !== 'retrieve') return; + + setActiveRetrievals(prev => { + const newRetrievals = new Map(prev); + newRetrievals.set(action.actionUuid, { + action, + isProcessing: false, + lastCheckTime: performance.now() + }); + return newRetrievals; + }); + }, []); + + useEffect(() => { + if (isReset) { + setActiveRetrievals(new Map()); + setInitialDelayComplete(false); + } + }, [isReset]); + + return { + handleRetrieve, + }; +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/storageUnit/actionHandler/useStoreHandler.ts b/app/src/modules/simulation/actions/storageUnit/actionHandler/useStoreHandler.ts index 92c1575..0ceb1dc 100644 --- a/app/src/modules/simulation/actions/storageUnit/actionHandler/useStoreHandler.ts +++ b/app/src/modules/simulation/actions/storageUnit/actionHandler/useStoreHandler.ts @@ -21,7 +21,6 @@ export function useStoreHandler() { if (!material) return; const modelUuid = getModelUuidByActionUuid(selectedProduct.productId, action.actionUuid); - if (!modelUuid) return; removeMaterial(material.materialId); diff --git a/app/src/modules/simulation/actions/storageUnit/useStorageUnitActions.ts b/app/src/modules/simulation/actions/storageUnit/useStorageUnitActions.ts index 07b45f3..6f5fb19 100644 --- a/app/src/modules/simulation/actions/storageUnit/useStorageUnitActions.ts +++ b/app/src/modules/simulation/actions/storageUnit/useStorageUnitActions.ts @@ -1,13 +1,19 @@ import { useEffect, useCallback } from 'react'; import { useStoreHandler } from './actionHandler/useStoreHandler'; +import { useRetrieveHandler } from './actionHandler/useRetrieveHandler'; export function useStorageActions() { const { handleStore } = useStoreHandler(); + const { handleRetrieve } = useRetrieveHandler(); const handleStoreAction = useCallback((action: StorageAction, materialId: string) => { handleStore(action, materialId); }, [handleStore]); + const handleRetrieveAction = useCallback((action: StorageAction) => { + handleRetrieve(action); + }, [handleRetrieve]); + const handleStorageAction = useCallback((action: StorageAction, materialId: string) => { if (!action) return; @@ -15,6 +21,9 @@ export function useStorageActions() { case 'store': handleStoreAction(action, materialId); break; + case 'retrieve': + handleRetrieveAction(action); + break; default: console.warn(`Unknown storage action type: ${action.actionType}`); } diff --git a/app/src/modules/simulation/actions/useActionHandler.ts b/app/src/modules/simulation/actions/useActionHandler.ts index a4c5d1a..cd9fffc 100644 --- a/app/src/modules/simulation/actions/useActionHandler.ts +++ b/app/src/modules/simulation/actions/useActionHandler.ts @@ -35,7 +35,7 @@ export function useActionHandler() { case 'process': handleMachineAction(action as MachineAction, materialId as string); break; - case 'store': + case 'store': case 'retrieve': handleStorageAction(action as StorageAction, materialId as string); break; default: diff --git a/app/src/modules/simulation/products/products.tsx b/app/src/modules/simulation/products/products.tsx index 53b0b30..9536caa 100644 --- a/app/src/modules/simulation/products/products.tsx +++ b/app/src/modules/simulation/products/products.tsx @@ -104,7 +104,7 @@ function Products() { if (event.type === 'storageUnit') { addStorageUnit(selectedProduct.productId, event); - if (event.point.action.actionType === 'spawn') { + if (event.point.action.actionType === 'retrieve') { const storageAction = event.point.action as StorageAction; const materials = Array.from({ length: storageAction.storageCapacity }, () => ({ materialType: storageAction.materialType || 'Default material', diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index f169ad2..a26635f 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -10,6 +10,7 @@ import { useMaterialStore } from '../../../../../store/simulation/useMaterialSto import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; import { useProductStore } from '../../../../../store/simulation/useProductStore'; import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; +import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore'; import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler'; @@ -28,6 +29,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { const { setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore(); const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore(); + const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore(); const { setIsVisible, getMaterialById } = useMaterialStore(); const { selectedProduct } = useSelectedProduct(); const { getActionByUuid, getEventByActionUuid, getEventByModelUuid } = useProductStore(); @@ -58,6 +60,8 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { removeLastMaterial(previousModel.modelUuid); } else if (previousModel.type === 'storageUnit') { // storage unit logic + removeLastStorageMaterial(previousModel.modelUuid); + updateCurrentLoad(previousModel.modelUuid, -1) } } else { setIsVisible(armBot.currentAction.materialId, false); diff --git a/app/src/modules/simulation/storageUnit/instances/animator/MaterialAnimator.tsx b/app/src/modules/simulation/storageUnit/instances/animator/MaterialAnimator.tsx index 259b455..8839704 100644 --- a/app/src/modules/simulation/storageUnit/instances/animator/MaterialAnimator.tsx +++ b/app/src/modules/simulation/storageUnit/instances/animator/MaterialAnimator.tsx @@ -9,7 +9,7 @@ const MaterialAnimator = ({ const meshRef = useRef(null!); const [hasLoad, setHasLoad] = useState(false); const { scene } = useThree(); - const padding = 0.1; + const padding = 0; useEffect(() => { setHasLoad(storage.currentLoad > 0); diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx index b59d9f1..557831c 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -131,6 +131,22 @@ function TriggerConnector() { }); } } + // Handle StorageUnit point + else if (event.type === "storageUnit" && 'point' in event) { + const point = event.point; + if (point.action?.triggers) { + point.action.triggers.forEach(trigger => { + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + } + } }); setConnections(newConnections); diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index 88d2b4e..b58e510 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -177,23 +177,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) 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 === 'spawn') { - // const newMaterial: MaterialSchema = { - // materialId: THREE.MathUtils.generateUUID(), - // materialName: `${storageUnit.point.action.materialType}-${Date.now()}`, - // materialType: storageUnit.point.action.materialType || 'Default material', - // isActive: false, - // isVisible: true, - // isPaused: false, - // isRendered: true, - // startTime: performance.now(), - // current: { - // modelUuid: storageUnit.modelUuid, - // pointUuid: storageUnit.point.uuid, - // actionUuid: storageUnit.point.action.actionUuid - // }, - // }; - } else if (storageUnit.point.action.actionType === 'store') { + if (storageUnit.point.action.actionType === 'store') { handleMaterialDropToStorage( agvDetail.modelUuid, agvDetail.currentLoad, diff --git a/app/src/store/simulation/useStorageUnitStore.ts b/app/src/store/simulation/useStorageUnitStore.ts index c75b1a3..171f674 100644 --- a/app/src/store/simulation/useStorageUnitStore.ts +++ b/app/src/store/simulation/useStorageUnitStore.ts @@ -22,7 +22,8 @@ interface StorageUnitStore { addCurrentMaterial: (modelUuid: string, materialType: string, materialId: string) => void; setCurrentMaterials: (modelUuid: string, materials: { materialType: string; materialId: string; }[]) => void; - removeLastMaterial: (modelUuid: string) => string | undefined; + getLastMaterial: (modelUuid: string) => { materialId: string; materialType: string; } | undefined; + removeLastMaterial: (modelUuid: string) => { materialId: string; materialType: string; } | undefined; clearCurrentMaterials: (modelUuid: string) => void; getStorageUnitById: (modelUuid: string) => StorageUnitStatus | undefined; @@ -140,20 +141,36 @@ export const useStorageUnitStore = create()( }); }, + getLastMaterial: (modelUuid) => { + let removedMaterial: { materialId: string; materialType: string; } | undefined; + set((state) => { + const storage = state.storageUnits.find((s) => s.modelUuid === modelUuid); + if (storage) { + if (storage.currentMaterials.length > 0) { + const material = storage.currentMaterials[storage.currentMaterials.length - 1]; + if (material) { + removedMaterial = { materialId: material.materialId, materialType: material.materialType }; + } + } + } + }); + return removedMaterial; + }, + removeLastMaterial: (modelUuid) => { - let materialId: string | undefined; + let removedMaterial: { materialId: string; materialType: string; } | undefined; set((state) => { const storage = state.storageUnits.find((s) => s.modelUuid === modelUuid); if (storage) { if (storage.currentMaterials.length > 0) { const material = storage.currentMaterials.pop(); if (material) { - materialId = material.materialId + removedMaterial = { materialId: material.materialId, materialType: material.materialType }; } } } }); - return materialId; + return removedMaterial; }, clearCurrentMaterials: (modelUuid) => { diff --git a/app/src/store/simulation/useVehicleStore.ts b/app/src/store/simulation/useVehicleStore.ts index f026edd..0b2ccc4 100644 --- a/app/src/store/simulation/useVehicleStore.ts +++ b/app/src/store/simulation/useVehicleStore.ts @@ -153,19 +153,19 @@ export const useVehicleStore = create()( }, removeLastMaterial: (modelUuid) => { - let materialId: { materialId: string; materialType: string; } | undefined; + 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) { const material = vehicle.currentMaterials.pop(); if (material) { - materialId = { materialId: material.materialId, materialType: material.materialType }; + removedMaterial = { materialId: material.materialId, materialType: material.materialType }; } } } }); - return materialId; + return removedMaterial; }, clearCurrentMaterials: (modelUuid) => { diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index afb3ca6..d9b2395 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -124,7 +124,7 @@ interface MachineAction { interface StorageAction { actionUuid: string; actionName: string; - actionType: "store" | "spawn"; + actionType: "store" | "retrieve"; materialType?: string; storageCapacity: number; triggers: TriggerSchema[];