From fcc5fa64e929eb151568966d109cd4324692374b Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 8 Aug 2025 18:05:25 +0530 Subject: [PATCH] added event handler --- .../actions/pillarJibAction.tsx | 39 ++++++++ .../mechanics/humanMechanics.tsx | 4 +- .../transformControls/transformControls.tsx | 4 +- app/src/modules/scene/scene.tsx | 2 +- app/src/modules/scene/sceneContext.tsx | 4 + .../actionHandler/usePickAndDropHandler.ts | 37 +++++++ .../actions/crane/useCraneActions.ts | 36 +++++++ .../simulation/actions/useActionHandler.ts | 10 +- .../eventManager/useCraneEventManager.ts | 99 +++++++++++++++++++ .../instances/animator/pillarJibAnimator.tsx | 4 +- .../instances/instance/pillarJibInstance.tsx | 32 ++++-- .../triggerHandler/useTriggerHandler.ts | 87 +++++++++++++++- app/src/types/simulationTypes.d.ts | 17 ++++ 13 files changed, 359 insertions(+), 16 deletions(-) create mode 100644 app/src/components/layout/sidebarRight/properties/eventProperties/actions/pillarJibAction.tsx create mode 100644 app/src/modules/simulation/actions/crane/actionHandler/usePickAndDropHandler.ts create mode 100644 app/src/modules/simulation/actions/crane/useCraneActions.ts create mode 100644 app/src/modules/simulation/crane/eventManager/useCraneEventManager.ts diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/pillarJibAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/pillarJibAction.tsx new file mode 100644 index 0000000..e7ff2c5 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/pillarJibAction.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; + +interface WorkerActionProps { + loadCount: { + value: number; + min: number; + max: number; + step: number; + defaultValue: string, + disabled: false, + onChange: (value: number) => void; + }; +} + +const WorkerAction: React.FC = ({ + loadCount +}) => { + return ( +
+ {loadCount && ( + { }} + onChange={(value) => loadCount.onChange(parseInt(value))} + /> + )} +
+ ); +}; + +export default WorkerAction; \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/humanMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/humanMechanics.tsx index fc73e58..9d8e393 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/humanMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/humanMechanics.tsx @@ -6,8 +6,8 @@ import RenameInput from "../../../../../ui/inputs/RenameInput"; import LabledDropdown from "../../../../../ui/inputs/LabledDropdown"; import Trigger from "../trigger/Trigger"; import ActionsList from "../components/ActionsList"; -import WorkerAction from "../actions/workerAction"; -import AssemblyAction from "../actions/assemblyAction"; +import WorkerAction from "../actions/WorkerAction"; +import AssemblyAction from "../actions/AssemblyAction"; import { useSelectedEventData, useSelectedAction } from "../../../../../../store/simulation/useSimulationStore"; import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi"; diff --git a/app/src/modules/scene/controls/transformControls/transformControls.tsx b/app/src/modules/scene/controls/transformControls/transformControls.tsx index e0ce1cf..6d5e7d5 100644 --- a/app/src/modules/scene/controls/transformControls/transformControls.tsx +++ b/app/src/modules/scene/controls/transformControls/transformControls.tsx @@ -3,7 +3,7 @@ import * as THREE from "three"; import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useActiveTool, useSocketStore } from "../../../../store/builder/store"; import { useThree } from "@react-three/fiber"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; // import { setAssetsApi } from "../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi"; @@ -15,6 +15,7 @@ import { useVersionContext } from "../../../builder/version/versionContext"; export default function TransformControl() { const state = useThree(); + const ref = useRef(null); const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); const { setObjectPosition } = useObjectPosition(); @@ -186,6 +187,7 @@ export default function TransformControl() { <> {(selectedFloorItem && transformMode) && { e.scene.background = layout === 'Main Layout' ? null : new Color(0x19191d); }} - gl={{ outputColorSpace: SRGBColorSpace, powerPreference: "high-performance", antialias: true, preserveDrawingBuffer: true }} + gl={{ outputColorSpace: SRGBColorSpace, powerPreference: "high-performance", antialias: true }} > diff --git a/app/src/modules/scene/sceneContext.tsx b/app/src/modules/scene/sceneContext.tsx index ac17198..6dbfce8 100644 --- a/app/src/modules/scene/sceneContext.tsx +++ b/app/src/modules/scene/sceneContext.tsx @@ -45,6 +45,7 @@ type SceneContextValue = { craneStore: CraneStoreType; humanEventManagerRef: React.RefObject; + craneEventManagerRef: React.RefObject; clearStores: () => void; @@ -83,6 +84,7 @@ export function SceneProvider({ const craneStore = useMemo(() => createCraneStore(), []); const humanEventManagerRef = useRef({ humanStates: [] }); + const craneEventManagerRef = useRef({ craneStates: [] }); const clearStores = useMemo(() => () => { assetStore.getState().clearAssets(); @@ -103,6 +105,7 @@ export function SceneProvider({ humanStore.getState().clearHumans(); craneStore.getState().clearCranes(); humanEventManagerRef.current.humanStates = []; + craneEventManagerRef.current.craneStates = []; }, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, undoRedo2DStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, craneStore]); const contextValue = useMemo(() => ( @@ -125,6 +128,7 @@ export function SceneProvider({ humanStore, craneStore, humanEventManagerRef, + craneEventManagerRef, clearStores, layout } diff --git a/app/src/modules/simulation/actions/crane/actionHandler/usePickAndDropHandler.ts b/app/src/modules/simulation/actions/crane/actionHandler/usePickAndDropHandler.ts new file mode 100644 index 0000000..48896f4 --- /dev/null +++ b/app/src/modules/simulation/actions/crane/actionHandler/usePickAndDropHandler.ts @@ -0,0 +1,37 @@ +import { useCallback } from "react"; +import { useSceneContext } from "../../../../scene/sceneContext"; +import { useProductContext } from "../../../products/productContext"; + +export function usePickAndDropHandler() { + const { materialStore, craneStore, productStore } = useSceneContext(); + const { getMaterialById } = materialStore(); + const { getModelUuidByActionUuid } = productStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const { incrementCraneLoad, addCurrentMaterial, addCurrentAction } = craneStore(); + + const pickAndDropLogStatus = (materialUuid: string, status: string) => { + echo.info(`${materialUuid}, ${status}`); + } + + const handlePickAndDrop = useCallback((action: CraneAction, materialId?: string) => { + if (!action || action.actionType !== 'pickAndDrop' || !materialId) return; + + const material = getMaterialById(materialId); + if (!material) return; + + const modelUuid = getModelUuidByActionUuid(selectedProduct.productUuid, action.actionUuid); + if (!modelUuid) return; + + incrementCraneLoad(modelUuid, 1); + addCurrentAction(modelUuid, action.actionUuid); + addCurrentMaterial(modelUuid, material.materialType, material.materialId); + + pickAndDropLogStatus(material.materialName, `performing pickAndDrop action`); + + }, [getMaterialById]); + + return { + handlePickAndDrop, + }; +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/crane/useCraneActions.ts b/app/src/modules/simulation/actions/crane/useCraneActions.ts new file mode 100644 index 0000000..aa7de6f --- /dev/null +++ b/app/src/modules/simulation/actions/crane/useCraneActions.ts @@ -0,0 +1,36 @@ +import { useEffect, useCallback } from 'react'; +import { usePickAndDropHandler } from './actionHandler/usePickAndDropHandler'; + +export function useCraneActions() { + const { handlePickAndDrop } = usePickAndDropHandler(); + + const handleWorkerAction = useCallback((action: CraneAction, materialId: string) => { + handlePickAndDrop(action, materialId); + }, [handlePickAndDrop]); + + const handleCraneAction = useCallback((action: CraneAction, materialId: string) => { + if (!action) return; + + switch (action.actionType) { + case 'pickAndDrop': + handleWorkerAction(action, materialId); + break; + default: + console.warn(`Unknown Human action type: ${action.actionType}`); + } + }, [handleWorkerAction]); + + const cleanup = useCallback(() => { + }, []); + + useEffect(() => { + return () => { + cleanup(); + }; + }, [cleanup]); + + return { + handleCraneAction, + cleanup + }; +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/useActionHandler.ts b/app/src/modules/simulation/actions/useActionHandler.ts index 7352ae1..99d8225 100644 --- a/app/src/modules/simulation/actions/useActionHandler.ts +++ b/app/src/modules/simulation/actions/useActionHandler.ts @@ -8,6 +8,7 @@ import { useRoboticArmActions } from "./roboticArm/useRoboticArmActions"; import { useStorageActions } from "./storageUnit/useStorageUnitActions"; import { useVehicleActions } from "./vehicle/useVehicleActions"; import { useHumanActions } from "./human/useHumanActions"; +import { useCraneActions } from "./crane/useCraneActions"; import { useCallback, useEffect } from "react"; export function useActionHandler() { @@ -19,6 +20,7 @@ export function useActionHandler() { const { handleMachineAction, cleanup: cleanupMachine } = useMachineActions(); const { handleStorageAction, cleanup: cleanupStorage } = useStorageActions(); const { handleHumanAction, cleanup: cleanupHuman } = useHumanActions(); + const { handleCraneAction, cleanup: cleanupCrane } = useCraneActions(); const handleAction = useCallback((action: Action, materialId?: string) => { if (!action) return; @@ -42,6 +44,9 @@ export function useActionHandler() { case 'worker': case 'assembly': handleHumanAction(action as HumanAction, materialId as string); break; + case 'pickAndDrop': + handleCraneAction(action as CraneAction, materialId as string); + break; default: console.warn(`Unknown action type: ${(action as Action).actionType}`); } @@ -49,7 +54,7 @@ export function useActionHandler() { echo.error("Failed to handle action"); console.error("Error handling action:", error); } - }, [handleConveyorAction, handleVehicleAction, handleRoboticArmAction, handleMachineAction, handleStorageAction, handleHumanAction]); + }, [handleConveyorAction, handleVehicleAction, handleRoboticArmAction, handleMachineAction, handleStorageAction, handleHumanAction, handleCraneAction]); const cleanup = useCallback(() => { cleanupConveyor(); @@ -58,7 +63,8 @@ export function useActionHandler() { cleanupMachine(); cleanupStorage(); cleanupHuman(); - }, [cleanupConveyor, cleanupVehicle, cleanupRoboticArm, cleanupMachine, cleanupStorage, cleanupHuman]); + cleanupCrane(); + }, [cleanupConveyor, cleanupVehicle, cleanupRoboticArm, cleanupMachine, cleanupStorage, cleanupHuman, cleanupCrane]); useEffect(() => { return () => { diff --git a/app/src/modules/simulation/crane/eventManager/useCraneEventManager.ts b/app/src/modules/simulation/crane/eventManager/useCraneEventManager.ts new file mode 100644 index 0000000..f8d40d9 --- /dev/null +++ b/app/src/modules/simulation/crane/eventManager/useCraneEventManager.ts @@ -0,0 +1,99 @@ +import { useEffect } from 'react'; +import { useFrame } from '@react-three/fiber'; +import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore'; +import { useSceneContext } from '../../../scene/sceneContext'; +import { useProductContext } from '../../products/productContext'; + +export function useCraneEventManager() { + const { craneStore, productStore, assetStore, craneEventManagerRef } = useSceneContext(); + const { getCraneById, setCurrentPhase, removeCurrentAction } = craneStore(); + const { getAssetById } = assetStore(); + const { getActionByUuid } = productStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + + const { isPlaying } = usePlayButtonStore(); + const { isPaused } = usePauseButtonStore(); + const { isReset } = useResetButtonStore(); + + useEffect(() => { + if ((isReset || !isPlaying) && craneEventManagerRef.current) { + craneEventManagerRef.current.craneStates = []; + } + }, [isReset, isPlaying]); + + const addCraneToMonitor = (craneId: string, callback: () => void, actionUuid: string) => { + const crane = getCraneById(craneId); + const action = getActionByUuid(selectedProduct.productUuid, actionUuid) as CraneAction | undefined; + + if (!crane || !action || action.actionType !== 'pickAndDrop' || !craneEventManagerRef.current) return; + + let state = craneEventManagerRef.current.craneStates.find(c => c.craneId === craneId); + if (!state) { + state = { + craneId, + pendingActions: [], + currentAction: null, + isProcessing: false + }; + craneEventManagerRef.current.craneStates.push(state); + } + + state.pendingActions.push({ + actionUuid, + callback + }); + + if (!state.isProcessing) { + processNextAction(state); + } + }; + + const processNextAction = (state: CraneEventState) => { + if (state.pendingActions.length === 0) { + state.currentAction = null; + return; + } + + state.isProcessing = true; + state.currentAction = state.pendingActions.shift() || null; + }; + + const completeCurrentAction = (state: CraneEventState) => { + processNextAction(state); + }; + + useFrame(() => { + if (!craneEventManagerRef.current || craneEventManagerRef.current.craneStates.length === 0 || !isPlaying || isPaused) return; + + for (const craneState of craneEventManagerRef.current.craneStates) { + if (!craneState.currentAction || !craneState.isProcessing) continue; + + const { craneId, currentAction } = craneState; + const crane = getCraneById(craneId); + const craneAsset = getAssetById(craneId); + const currentCraneAction = getActionByUuid(selectedProduct.productUuid, crane?.currentAction?.actionUuid || '') as CraneAction | undefined; + + if (!crane || !craneAsset || !currentCraneAction) continue; + if (crane.isActive || crane.state !== "idle") continue; + + if (currentCraneAction.actionType === 'pickAndDrop' && crane.currentLoad < currentCraneAction.maxPickUpCount) { + if (currentAction.actionUuid !== crane.currentAction?.actionUuid) { + setCurrentPhase(crane.modelUuid, 'init'); + removeCurrentAction(crane.modelUuid); + } + + craneState.isProcessing = false; + currentAction.callback(); + + setTimeout(() => { + completeCurrentAction(craneState); + }, 1000); + } + } + }, 0); + + return { + addCraneToMonitor + }; +} \ No newline at end of file diff --git a/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx b/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx index 9c7f3ab..fefad50 100644 --- a/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx +++ b/app/src/modules/simulation/crane/instances/animator/pillarJibAnimator.tsx @@ -12,7 +12,7 @@ function PillarJibAnimator({ onAnimationComplete }: { crane: CraneStatus; - points: [THREE.Vector3, THREE.Vector3]; + points: [THREE.Vector3, THREE.Vector3] | null; animationPhase: string; setAnimationPhase: (phase: string) => void; onAnimationComplete: (action: string) => void; @@ -45,7 +45,7 @@ function PillarJibAnimator({ const trolley = model.getObjectByName('trolley'); const hook = model.getObjectByName('hook'); - if (!base || !trolley || !hook) return; + if (!base || !trolley || !hook || !points) return; const baseWorld = new THREE.Vector3(); base.getWorldPosition(baseWorld); diff --git a/app/src/modules/simulation/crane/instances/instance/pillarJibInstance.tsx b/app/src/modules/simulation/crane/instances/instance/pillarJibInstance.tsx index bf0fa43..9c16e78 100644 --- a/app/src/modules/simulation/crane/instances/instance/pillarJibInstance.tsx +++ b/app/src/modules/simulation/crane/instances/instance/pillarJibInstance.tsx @@ -1,13 +1,34 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import * as THREE from 'three' +import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; +import { useSceneContext } from '../../../../scene/sceneContext'; +import { useProductContext } from '../../../products/productContext'; + import PillarJibAnimator from '../animator/pillarJibAnimator' import PillarJibHelper from '../helper/pillarJibHelper' function PillarJibInstance({ crane }: { crane: CraneStatus }) { + const { isPlaying } = usePlayButtonStore(); + const { craneStore, productStore } = useSceneContext(); + const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore(); + const { getCraneById } = craneStore(); + const { selectedProductStore } = useProductContext(); + const { selectedProduct } = selectedProductStore(); + const [currentPhase, setCurrentPhase] = useState('idle'); const [animationPhase, setAnimationPhase] = useState('idle'); + const [points, setPoints] = useState<[THREE.Vector3, THREE.Vector3] | null>(null); - const position1: [number, number, number] = [5, 1, -4]; - const position2: [number, number, number] = [-2, 2, -2]; + useEffect(() => { + if (isPlaying) { + const action = getActionByUuid(selectedProduct.productUuid, crane?.currentAction?.actionUuid || ''); + if (!action || action.actionType !== 'pickAndDrop') return; + + if (!crane.isActive && currentPhase === 'idle' && crane.currentMaterials.length > 0 && action.maxPickUpCount <= crane.currentMaterials.length) { + console.log('crane: ', crane); + + } + } + }, [crane, currentPhase]) const handleAnimationComplete = (action: string) => { if (action === 'picking') { @@ -27,10 +48,7 @@ function PillarJibInstance({ crane }: { crane: CraneStatus }) { 0 && + action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid && + action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid && + action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid) { + const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); + + if (model?.type === 'transfer') { + const crane = getCraneById(trigger.triggeredAsset?.triggeredModel.modelUuid); + if (crane) { + setIsPaused(materialId, true); + setCraneScheduled(crane.modelUuid, true); + const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (conveyor) { + addConveyorToMonitor(conveyor.modelUuid, () => { + addCraneToMonitor(crane.modelUuid, () => { + setIsPaused(materialId, true); + + handleAction(action, materialId); + }, action.actionUuid) + }) + } + } + } + } + } + } + } + } + } } } else if (fromEvent?.type === 'vehicle') { if (toEvent?.type === 'transfer') { @@ -1547,6 +1609,29 @@ export function useTriggerHandler() { } else if (toEvent?.type === 'human') { // Human to Human + } + } else if (fromEvent?.type === 'crane') { + if (toEvent?.type === 'transfer') { + // Crane Unit to Transfer + + } else if (toEvent?.type === 'vehicle') { + // Crane Unit to Vehicle + + } else if (toEvent?.type === 'machine') { + // Crane Unit to Machine + + } else if (toEvent?.type === 'roboticArm') { + // Crane Unit to Robotic Arm + + } else if (toEvent?.type === 'storageUnit') { + // Crane Unit to Storage Unit + + } else if (toEvent?.type === 'human') { + // Crane Unit to Human + + } else if (toEvent?.type === 'crane') { + // Crane Unit to Human + } } } diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index e257ed8..1e90fe8 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -336,6 +336,23 @@ type HumanEventManagerState = { humanStates: HumanEventState[]; }; +type CraneEventState = { + craneId: string; + pendingActions: { + actionUuid: string; + callback: () => void; + }[]; + currentAction: { + actionUuid: string; + callback: () => void; + } | null; + isProcessing: boolean; +}; + +type CraneEventManagerState = { + craneStates: CraneEventState[]; +}; + // Materials