From 508c88dce2018df696e1cb389f5d00053f00cc65 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 4 Jul 2025 17:15:10 +0530 Subject: [PATCH] feat: Implement human event handling in copy and duplication controls - Added support for handling "Human" type events in the copy and duplication controls. - Created a new HumanEventSchema to manage human-related events, including position, rotation, and action details. - Updated the HumanAnimator to improve rotation handling using quaternions for smoother transitions. - Enhanced the HumanInstance to ensure proper synchronization of human positions and rotations. - Integrated human event handling in the trigger handler for various interactions, including vehicle and machine actions. - Introduced material drop functionality from vehicles to humans, allowing for dynamic interactions in the simulation. --- .../mechanics/humanMechanics.tsx | 2 +- .../builder/asset/models/model/model.tsx | 4 +- .../selectionControls/copyPasteControls.tsx | 28 + .../selectionControls/duplicationControls.tsx | 28 + .../instances/animator/humanAnimator.tsx | 42 +- .../instances/instance/humanInstance.tsx | 38 +- .../human/instances/instance/humanUi.tsx | 1 - .../triggerHandler/useTriggerHandler.ts | 683 ++++++++++++++++-- .../instances/instance/vehicleInstance.tsx | 81 ++- 9 files changed, 821 insertions(+), 86 deletions(-) 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 1e7c423..9f2fd2d 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/humanMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/humanMechanics.tsx @@ -30,7 +30,7 @@ function HumanMechanics() { const { projectId } = useParams(); useEffect(() => { - if (selectedEventData) { + if (selectedEventData && selectedEventData.data.type === "human") { const point = getPointByUuid( selectedProduct.productUuid, selectedEventData.data.modelUuid, diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index f3baadd..ff5e0bf 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -16,6 +16,7 @@ import { getUserData } from '../../../../../functions/getUserData'; import { useSceneContext } from '../../../../scene/sceneContext'; import { useVersionContext } from '../../../version/versionContext'; import { SkeletonUtils } from 'three-stdlib'; +import { useAnimationPlaySpeed } from '../../../../../store/usePlayButtonStore'; function Model({ asset }: { readonly asset: Asset }) { const { camera, controls, gl } = useThree(); @@ -23,6 +24,7 @@ function Model({ asset }: { readonly asset: Asset }) { const { toggleView } = useToggleView(); const { subModule } = useSubModuleStore(); const { activeModule } = useModuleStore(); + const { speed } = useAnimationPlaySpeed(); const { assetStore, eventStore, productStore } = useSceneContext(); const { removeAsset, setAnimations, resetAnimation, setAnimationComplete, setCurrentAnimation: setAnmationAnimation } = assetStore(); const { setTop } = useTopData(); @@ -289,7 +291,7 @@ function Model({ asset }: { readonly asset: Asset }) { useFrame((_, delta) => { if (mixerRef.current) { - mixerRef.current.update(delta); + mixerRef.current.update(delta * speed); } }); diff --git a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx index 3b9d232..29a3007 100644 --- a/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/copyPasteControls.tsx @@ -332,6 +332,34 @@ const CopyPasteControls = ({ position: storageEvent.point.position, rotation: storageEvent.point.rotation }; + } else if (obj.userData.eventData.type === "Human") { + const humanEvent: HumanEventSchema = { + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + position: newFloorItem.position, + rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + state: "idle", + type: "human", + speed: 1, + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]], + rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "worker", + loadCapacity: 1, + triggers: [] + } + } + } + addEvent(humanEvent); + eventData.point = { + uuid: humanEvent.point.uuid, + position: humanEvent.point.position, + rotation: humanEvent.point.rotation + }; } newFloorItem.eventData = eventData; diff --git a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx index 5194952..b4a1916 100644 --- a/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/duplicationControls.tsx @@ -306,6 +306,34 @@ const DuplicationControls = ({ position: storageEvent.point.position, rotation: storageEvent.point.rotation }; + } else if (obj.userData.eventData.type === "Human") { + const humanEvent: HumanEventSchema = { + modelUuid: newFloorItem.modelUuid, + modelName: newFloorItem.modelName, + position: newFloorItem.position, + rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + state: "idle", + type: "human", + speed: 1, + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [updatedEventData.point.position[0], updatedEventData.point.position[1], updatedEventData.point.position[2]], + rotation: [updatedEventData.point.rotation[0], updatedEventData.point.rotation[1], updatedEventData.point.rotation[2]], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Action 1", + actionType: "worker", + loadCapacity: 1, + triggers: [] + } + } + } + addEvent(humanEvent); + eventData.point = { + uuid: humanEvent.point.uuid, + position: humanEvent.point.position, + rotation: humanEvent.point.rotation + }; } newFloorItem.eventData = eventData; diff --git a/app/src/modules/simulation/human/instances/animator/humanAnimator.tsx b/app/src/modules/simulation/human/instances/animator/humanAnimator.tsx index 8e818fa..89d7a82 100644 --- a/app/src/modules/simulation/human/instances/animator/humanAnimator.tsx +++ b/app/src/modules/simulation/human/instances/animator/humanAnimator.tsx @@ -100,17 +100,18 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start const end = new THREE.Vector3(...currentPath[index + 1]); const segmentDistance = distances[index]; - const currentDirection = new THREE.Vector3().subVectors(end, start).normalize(); - const targetAngle = Math.atan2(currentDirection.x, currentDirection.z); - const currentAngle = object.rotation.y; + const targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0))); + const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI); + targetQuaternion.multiply(y180); - let angleDifference = targetAngle - currentAngle; - if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI; - if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI; + const angle = object.quaternion.angleTo(targetQuaternion); + if (angle < 0.01) { + object.quaternion.copy(targetQuaternion); + } else { + object.quaternion.slerp(targetQuaternion, delta * rotationSpeed * speed * human.speed * 5); + } - const maxRotationStep = (rotationSpeed * speed * human.speed) * delta; - object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep); - const isAligned = Math.abs(angleDifference) < 0.01; + const isAligned = angle < 0.01; if (isAligned) { progressRef.current += delta * (speed * human.speed); @@ -133,23 +134,26 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start if (progressRef.current >= totalDistance) { if (restRotation && objectRotation) { - const targetEuler = new THREE.Euler( - objectRotation[0], - objectRotation[1] + Math.PI, - objectRotation[2] - ); - const targetQuaternion = new THREE.Quaternion().setFromEuler(targetEuler); - object.quaternion.slerp(targetQuaternion, delta * (rotationSpeed * speed * human.speed)); - if (object.quaternion.angleTo(targetQuaternion) < 0.01) { + const targetEuler = new THREE.Euler(0, objectRotation[1], 0); + + const baseQuaternion = new THREE.Quaternion().setFromEuler(targetEuler); + const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI); + const targetQuaternion = baseQuaternion.multiply(y180); + + const angle = object.quaternion.angleTo(targetQuaternion); + if (angle < 0.01) { object.quaternion.copy(targetQuaternion); - object.rotation.copy(targetEuler); setRestingRotation(false); + } else { + object.quaternion.slerp(targetQuaternion, delta * rotationSpeed * speed * human.speed * 4); } + if (human.currentMaterials.length > 0) { setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true); } else { setCurrentAnimation(human.modelUuid, 'idle', true, true, true); } + return; } } @@ -170,7 +174,7 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start <> {currentPath.length > 0 && ( // helper - + {currentPath.map((point, index) => ( diff --git a/app/src/modules/simulation/human/instances/instance/humanInstance.tsx b/app/src/modules/simulation/human/instances/instance/humanInstance.tsx index 214c1e6..12d2d61 100644 --- a/app/src/modules/simulation/human/instances/instance/humanInstance.tsx +++ b/app/src/modules/simulation/human/instances/instance/humanInstance.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import * as THREE from 'three'; +import { useThree } from '@react-three/fiber'; import { NavMeshQuery } from '@recast-navigation/core'; import { useNavMesh } from '../../../../../store/builder/store'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; @@ -13,6 +14,7 @@ import MaterialAnimator from '../animator/materialAnimator'; function HumanInstance({ human }: { human: HumanStatus }) { const { navMesh } = useNavMesh(); const { isPlaying } = usePlayButtonStore(); + const { scene } = useThree(); const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext(); const { removeMaterial, setEndTime } = materialStore(); const { getStorageUnitById } = storageUnitStore(); @@ -92,6 +94,11 @@ function HumanInstance({ human }: { human: HumanStatus }) { cancelAnimationFrame(animationFrameIdRef.current) animationFrameIdRef.current = null } + const object = scene.getObjectByProperty('uuid', human.modelUuid); + if (object && human) { + object.position.set(human.position[0], human.position[1], human.position[2]); + object.rotation.set(human.rotation[0], human.rotation[1], human.rotation[2]); + } } useEffect(() => { @@ -292,6 +299,7 @@ function HumanInstance({ human }: { human: HumanStatus }) { } function handleMaterialDropToStorageUnit(model: StorageEventSchema) { + const humanAsset = getAssetById(human.modelUuid); if (model && humanAsset?.animationState?.current !== 'drop') { setCurrentAnimation(human.modelUuid, 'drop', true, false, false); } @@ -322,6 +330,7 @@ function HumanInstance({ human }: { human: HumanStatus }) { action: HumanAction ) { const storageUnit = getStorageUnitById(storageUnitId); + const humanAsset = getAssetById(human.modelUuid); if (!storageUnit || humanCurrentLoad <= 0 || storageUnit.currentLoad >= storageMaxCapacity) { return; @@ -340,7 +349,7 @@ function HumanInstance({ human }: { human: HumanStatus }) { setCurrentAnimation(human.modelUuid, 'drop', true, false, false); const waitForNextDrop = () => { - if (humanAsset?.animationState?.isCompleted) { + if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) { loopMaterialDropToStorage( humanId, humanCurrentLoad, @@ -357,6 +366,7 @@ function HumanInstance({ human }: { human: HumanStatus }) { } function handleMaterialDropToConveyor(model: ConveyorEventSchema) { + const humanAsset = getAssetById(human.modelUuid); if (humanAsset?.animationState?.current !== 'drop') { setCurrentAnimation(human.modelUuid, 'drop', true, false, false); } @@ -386,6 +396,7 @@ function HumanInstance({ human }: { human: HumanStatus }) { action: HumanAction ) { const conveyor = getConveyorById(conveyorId); + const humanAsset = getAssetById(human.modelUuid); if (!conveyor || humanCurrentLoad <= 0) { return; @@ -420,12 +431,13 @@ function HumanInstance({ human }: { human: HumanStatus }) { } function handleMaterialDropToArmBot(model: RoboticArmEventSchema) { + const humanAsset = getAssetById(human.modelUuid); if (humanAsset?.animationState?.current !== 'drop') { setCurrentAnimation(human.modelUuid, 'drop', true, false, false); } const checkAnimation = () => { - if (humanAsset?.animationState?.isCompleted) { + if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) { const armBot = getArmBotById(model.modelUuid); if (armBot && armBot.state === 'idle' && !armBot.isActive) { loopMaterialDropToArmBot( @@ -449,6 +461,7 @@ function HumanInstance({ human }: { human: HumanStatus }) { action: HumanAction ) { const armBot = getArmBotById(armBotId); + const humanAsset = getAssetById(human.modelUuid); if (!armBot || armBot.state !== 'idle' || armBot.isActive || humanCurrentLoad <= 0) { return; @@ -488,12 +501,13 @@ function HumanInstance({ human }: { human: HumanStatus }) { } function handleMaterialDropToVehicle(model: VehicleEventSchema) { + const humanAsset = getAssetById(human.modelUuid); if (humanAsset?.animationState?.current !== 'drop') { setCurrentAnimation(human.modelUuid, 'drop', true, false, false); } const checkAnimation = () => { - if (humanAsset?.animationState?.isCompleted) { + if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) { const vehicle = getVehicleById(model.modelUuid); if (vehicle && vehicle.state === 'idle' && !vehicle.isActive) { loopMaterialDropToVehicle( @@ -517,6 +531,7 @@ function HumanInstance({ human }: { human: HumanStatus }) { action: HumanAction ) { const vehicle = getVehicleById(vehicleId); + const humanAsset = getAssetById(human.modelUuid); if (!vehicle || vehicle.state !== 'idle' || vehicle.isActive || humanCurrentLoad <= 0) { return; @@ -535,10 +550,10 @@ function HumanInstance({ human }: { human: HumanStatus }) { setCurrentAnimation(human.modelUuid, 'drop', true, false, false); const waitForNextTransfer = () => { - const currentArmBot = getVehicleById(vehicleId); - if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) { + const currentVehicle = getVehicleById(vehicleId); + if (currentVehicle && currentVehicle.state === 'idle' && !currentVehicle.isActive) { if (humanAsset?.animationState?.isCompleted) { - loopMaterialDropToArmBot( + loopMaterialDropToVehicle( humanId, humanCurrentLoad, vehicleId, @@ -556,12 +571,13 @@ function HumanInstance({ human }: { human: HumanStatus }) { } function handleMaterialDropToMachine(model: MachineEventSchema) { + const humanAsset = getAssetById(human.modelUuid); if (humanAsset?.animationState?.current !== 'drop') { setCurrentAnimation(human.modelUuid, 'drop', true, false, false); } const checkAnimation = () => { - if (humanAsset?.animationState?.isCompleted) { + if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) { const machine = getMachineById(model.modelUuid); if (machine && machine.state === 'idle' && !machine.isActive) { loopMaterialDropToMachine( @@ -585,6 +601,7 @@ function HumanInstance({ human }: { human: HumanStatus }) { action: HumanAction ) { const machine = getMachineById(machineId); + const humanAsset = getAssetById(human.modelUuid); if (!machine || machine.state !== 'idle' || machine.isActive || humanCurrentLoad <= 0) { return; @@ -603,10 +620,10 @@ function HumanInstance({ human }: { human: HumanStatus }) { setCurrentAnimation(human.modelUuid, 'drop', true, false, false); const waitForNextTransfer = () => { - const currentArmBot = getMachineById(machineId); - if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) { + const currentMachine = getMachineById(machineId); + if (currentMachine && currentMachine.state === 'idle' && !currentMachine.isActive) { if (humanAsset?.animationState?.isCompleted) { - loopMaterialDropToArmBot( + loopMaterialDropToMachine( humanId, humanCurrentLoad, machineId, @@ -624,6 +641,7 @@ function HumanInstance({ human }: { human: HumanStatus }) { } function handleMaterialDropByDefault(droppedMaterial: number) { + const humanAsset = getAssetById(human.modelUuid); if (humanAsset?.animationState?.current !== 'drop') { setCurrentAnimation(human.modelUuid, 'drop', true, false, false); } diff --git a/app/src/modules/simulation/human/instances/instance/humanUi.tsx b/app/src/modules/simulation/human/instances/instance/humanUi.tsx index f3688c5..64b1f84 100644 --- a/app/src/modules/simulation/human/instances/instance/humanUi.tsx +++ b/app/src/modules/simulation/human/instances/instance/humanUi.tsx @@ -219,7 +219,6 @@ function HumanUi() { {selectedHumanData && ( { + const armBot = getArmBotById(trigger.triggeredAsset?.triggeredModel.modelUuid); + if (armBot) { + if (armBot.isActive === false && armBot.state === 'idle') { + const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); + if (vehicle) { + if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) { + // Handle current action from vehicle + setIsPaused(materialId, true); handleAction(action, materialId); + + } else { + + // Handle current action using Event Manager + setIsPaused(materialId, true); + + addVehicleToMonitor(vehicle.modelUuid, + () => { + handleAction(action, materialId); + } + ) } - ) + } + } else { + setIsPaused(materialId, true); + addArmBotToMonitor(armBot.modelUuid, () => { + const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (vehicle) { + if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) { + // Handle current action from vehicle + setIsPaused(materialId, true); + handleAction(action, materialId); + + } else { + + // Handle current action using Event Manager + setIsPaused(materialId, true); + + addVehicleToMonitor(vehicle.modelUuid, + () => { + handleAction(action, materialId); + } + ) + } + } + }) } } } else if (model?.type === 'machine') { @@ -194,26 +222,25 @@ export function useTriggerHandler() { } } else { setIsPaused(materialId, true); - addArmBotToMonitor(armBot.modelUuid, - () => { - const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); - if (machine) { - if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) { - setIsPaused(materialId, true); - handleAction(action, materialId); - } else { + addArmBotToMonitor(armBot.modelUuid, () => { + const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (machine) { + if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) { + setIsPaused(materialId, true); + handleAction(action, materialId); + } else { - // Handle current action using Event Manager - setIsPaused(materialId, true); + // Handle current action using Event Manager + setIsPaused(materialId, true); - addMachineToMonitor(machine.modelUuid, - () => { - handleAction(action, materialId); - } - ) - } + addMachineToMonitor(machine.modelUuid, + () => { + handleAction(action, materialId); + } + ) } } + } ); } } @@ -289,23 +316,206 @@ export function useTriggerHandler() { if (action) { if (human) { + if (action && action.triggers.length > 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 (human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < human.point.action.loadCapacity) { + if (model?.type === 'vehicle') { + const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid); + if (human) { + if (human.isActive === false && human.state === 'idle') { + const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); + if (vehicle) { + if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) { + // Handle current action from vehicle + setIsVisible(materialId, false); + setIsPaused(materialId, true); + handleAction(action, materialId); - setIsVisible(materialId, false); + } else { - // Handle current action from vehicle - handleAction(action, materialId); + // Handle current action using Event Manager + setIsPaused(materialId, true); + addVehicleToMonitor(vehicle.modelUuid, + () => { + setIsVisible(materialId, false); + handleAction(action, materialId); + } + ) + } + } + } else { + setIsPaused(materialId, true); + addHumanToMonitor(human.modelUuid, () => { + const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (vehicle) { + if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) { + // Handle current action from vehicle + setIsVisible(materialId, false); + setIsPaused(materialId, true); + handleAction(action, materialId); + + } else { + + // Handle current action using Event Manager + setIsPaused(materialId, true); + + addVehicleToMonitor(vehicle.modelUuid, + () => { + setIsVisible(materialId, false); + handleAction(action, materialId); + } + ) + } + } + }) + } + } + } else if (model?.type === 'transfer') { + const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid); + if (human) { + if (human.isActive === false && human.state === 'idle') { + const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); + if (conveyor) { + if (!conveyor.isPaused) { + // Handle current action from vehicle + setIsVisible(materialId, false); + setIsPaused(materialId, true); + handleAction(action, materialId); + + } else { + + // Handle current action using Event Manager + setIsPaused(materialId, true); + + addConveyorToMonitor(conveyor.modelUuid, + () => { + setIsVisible(materialId, false); + handleAction(action, materialId); + } + ) + } + } + } else { + setIsPaused(materialId, true); + addHumanToMonitor(human.modelUuid, () => { + const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (conveyor) { + if (!conveyor.isPaused) { + // Handle current action from vehicle + setIsVisible(materialId, false); + setIsPaused(materialId, true); + handleAction(action, materialId); + + } else { + + // Handle current action using Event Manager + setIsPaused(materialId, true); + + addConveyorToMonitor(conveyor.modelUuid, + () => { + setIsVisible(materialId, false); + handleAction(action, materialId); + } + ) + } + } + }) + } + } + } else if (model?.type === 'machine') { + const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid); + if (human) { + if (human.isActive === false && human.state === 'idle') { + const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); + if (machine) { + if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) { + setIsPaused(materialId, true); + setIsVisible(materialId, false); + handleAction(action, materialId); + } else { + + // Handle current action using Event Manager + setIsPaused(materialId, true); + + addMachineToMonitor(machine.modelUuid, + () => { + setIsVisible(materialId, false); + handleAction(action, materialId); + } + ) + } + } + } else { + setIsPaused(materialId, true); + addHumanToMonitor(human.modelUuid, () => { + const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (machine) { + if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) { + setIsPaused(materialId, true); + setIsVisible(materialId, false); + handleAction(action, materialId); + } else { + + // Handle current action using Event Manager + setIsPaused(materialId, true); + + addMachineToMonitor(machine.modelUuid, + () => { + setIsVisible(materialId, false); + handleAction(action, materialId); + } + ) + } + } + } + ); + } + } + } else { + if (human.isActive === false && human.state === 'idle') { + + // Handle current action from arm bot + setIsPaused(materialId, true); + setIsVisible(materialId, false); + handleAction(action, materialId); + + } else { + + // Handle current action using Event Manager + setIsPaused(materialId, true); + addHumanToMonitor(human.modelUuid, + () => { + setIsVisible(materialId, false); + handleAction(action, materialId) + } + ); + + } + } } else { + if (human.isActive === false && human.state === 'idle') { - // Handle current action using Event Manager - addHumanToMonitor(human.modelUuid, () => { - + // Handle current action from arm bot + setIsPaused(materialId, true); setIsVisible(materialId, false); - handleAction(action, materialId); - }) + + } else { + + // Handle current action using Event Manager + setIsPaused(materialId, true); + addHumanToMonitor(human.modelUuid, + () => { + setIsVisible(materialId, false); + handleAction(action, materialId) + } + ); + + } } } } @@ -378,18 +588,25 @@ export function useTriggerHandler() { setNextLocation(material.materialId, null); - setIsVisible(materialId, false); if (action && armBot) { if (armBot.isActive === false && armBot.state === 'idle') { // Handle current action from arm bot + setIsVisible(materialId, false); + handleAction(action, materialId); } else { - // Event Manager Needed + addArmBotToMonitor(armBot.modelUuid, + () => { + setIsVisible(materialId, false); + + handleAction(action, materialId); + } + ) } } @@ -438,6 +655,49 @@ export function useTriggerHandler() { } else if (toEvent?.type === 'human') { // Vehicle to Human + if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { + const material = getMaterialById(materialId); + if (material) { + const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid); + const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid); + + setPreviousLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }) + + setCurrentLocation(material.materialId, { + modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid, + pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, + }); + + setNextLocation(material.materialId, null); + + + if (action && human) { + + if (human.isActive === false && human.state === 'idle') { + + // Handle current action from arm bot + setIsVisible(materialId, false); + + handleAction(action, materialId); + + } else { + + addHumanToMonitor(human.modelUuid, + () => { + setIsVisible(materialId, false); + + handleAction(action, materialId); + } + ) + } + } + } + } } } else if (fromEvent?.type === 'machine') { if (toEvent?.type === 'transfer') { @@ -546,7 +806,133 @@ export function useTriggerHandler() { } else if (toEvent?.type === 'human') { // Machine to Human + if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { + const material = getMaterialById(materialId); + setIsPaused(materialId, true); + + if (material) { + const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid); + const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid); + + setPreviousLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }) + + setCurrentLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, + }); + + setNextLocation(material.materialId, null); + + setIsVisible(materialId, false); + + if (action && human) { + + if (human.isActive === false && human.state === 'idle') { + + // Handle current action from arm bot + const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (model?.type === 'transfer') { + const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (conveyor) { + const previousModel = getEventByModelUuid(selectedProduct.productUuid, material.previous?.modelUuid || ''); + if (previousModel) { + if (previousModel.type === 'transfer' && previousModel.modelUuid === model.modelUuid) { + handleAction(action, materialId) + } else { + addConveyorToMonitor(conveyor.modelUuid, + () => { + handleAction(action, materialId) + } + ) + } + } else { + handleAction(action, materialId) + } + // handleAction(action, materialId) + } + } else if (model?.type === 'vehicle') { + const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (vehicle) { + if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) { + // Handle current action from vehicle + setIsPaused(materialId, true); + handleAction(action, materialId); + + } else { + + // Handle current action using Event Manager + setIsPaused(materialId, true); + + addVehicleToMonitor(vehicle.modelUuid, + () => { + handleAction(action, materialId); + } + ) + } + } + } else { + handleAction(action, materialId) + } + + } else { + + // Handle current action using Event Manager + + addHumanToMonitor(human.modelUuid, () => { + const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (model?.type === 'transfer') { + const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (conveyor) { + const previousModel = getEventByModelUuid(selectedProduct.productUuid, material.previous?.modelUuid || ''); + if (previousModel) { + if (previousModel.type === 'transfer' && previousModel.modelUuid === model.modelUuid) { + handleAction(action, materialId) + } else { + addConveyorToMonitor(conveyor.modelUuid, + () => { + handleAction(action, materialId) + } + ) + } + } else { + handleAction(action, materialId) + } + } + } else if (model?.type === 'vehicle') { + const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || ''); + if (vehicle) { + if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) { + // Handle current action from vehicle + setIsPaused(materialId, true); + handleAction(action, materialId); + + } else { + + // Handle current action using Event Manager + setIsPaused(materialId, true); + + addVehicleToMonitor(vehicle.modelUuid, + () => { + handleAction(action, materialId); + } + ) + } + } + } else { + handleAction(action, materialId) + } + } + ); + } + } + } + } } } else if (fromEvent?.type === 'roboticArm') { if (toEvent?.type === 'transfer') { @@ -821,7 +1207,7 @@ export function useTriggerHandler() { } } } else if (toEvent?.type === 'human') { - // Robotic Arm to Vehicle + // Robotic Arm to Human if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { const material = getMaterialById(materialId); if (material) { @@ -895,7 +1281,6 @@ export function useTriggerHandler() { if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { const material = getMaterialById(materialId); if (material) { - const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid); setPreviousLocation(material.materialId, { modelUuid: material.current.modelUuid, @@ -909,18 +1294,120 @@ export function useTriggerHandler() { actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, }); - setIsVisible(materialId, true); + const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid); - if (action && + if (action && action.triggers.length > 0 && action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid && - action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid - ) { - setNextLocation(material.materialId, { - modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid, - pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid, - }); + action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid && + action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid) { + const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); - handleAction(action, materialId); + if (model?.type === 'roboticArm') { + + handleAction(action, material.materialId); + + } else if (model?.type === 'vehicle') { + const nextAction = getActionByUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredAction.actionUuid); + + if (action) { + handleAction(action, material.materialId); + + const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); + + setPreviousLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }) + + setCurrentLocation(material.materialId, { + modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid, + pointUuid: trigger.triggeredAsset?.triggeredPoint?.pointUuid, + actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, + }); + + setNextLocation(material.materialId, null); + + if (nextAction) { + + if (vehicle) { + + if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) { + + setPreviousLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }) + + setCurrentLocation(material.materialId, { + modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '', + pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid || '', + actionUuid: action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid || '', + }); + + setNextLocation(material.materialId, null); + + setIsVisible(materialId, false); + setIsPaused(materialId, false); + + // Handle current action from vehicle + handleAction(nextAction, materialId); + + } else { + + // Handle current action using Event Manager + setIsPaused(materialId, true); + + addVehicleToMonitor(vehicle.modelUuid, + () => { + setPreviousLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }) + + setCurrentLocation(material.materialId, { + modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '', + pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid || '', + actionUuid: action.triggers[0]?.triggeredAsset?.triggeredAction?.actionUuid || '', + }); + + setNextLocation(material.materialId, null); + + setIsPaused(materialId, false); + setIsVisible(materialId, false); + handleAction(nextAction, materialId); + } + ) + } + } + } + } + + } else if (model?.type === 'transfer') { + const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid); + if (conveyor) { + setNextLocation(material.materialId, { + modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid, + pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid, + }) + setIsPaused(material.materialId, false); + setIsVisible(material.materialId, true); + handleAction(action, material.materialId); + } + } else { + setNextLocation(material.materialId, { + modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid, + pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid, + }) + + handleAction(action, material.materialId); + } + } else if (action) { + setNextLocation(material.materialId, null) + + handleAction(action, material.materialId); } } @@ -929,9 +1416,96 @@ export function useTriggerHandler() { } else if (toEvent?.type === 'vehicle') { // Human to Vehicle + if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { + const material = getMaterialById(materialId); + if (material) { + + setIsPaused(material.materialId, false); + + const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid); + const vehicle = getVehicleById(trigger.triggeredAsset?.triggeredModel.modelUuid); + + setNextLocation(material.materialId, null); + + if (action) { + + if (vehicle) { + + if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) { + + setIsVisible(materialId, false); + + setPreviousLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }) + + setCurrentLocation(material.materialId, { + modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid, + pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, + }); + + // Handle current action from vehicle + handleAction(action, materialId); + + } else { + + // Event Manager Needed + + } + } + } + + } + } + } else if (toEvent?.type === 'machine') { // Human to Machine + if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { + const material = getMaterialById(materialId); + if (material) { + + // setIsPaused(material.materialId, false); + + const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid); + const machine = getMachineById(trigger.triggeredAsset?.triggeredModel.modelUuid); + setNextLocation(material.materialId, null); + + if (action) { + + if (machine) { + + if (machine.isActive === false && machine.state === 'idle') { + + setIsVisible(materialId, false); + + setPreviousLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }) + + setCurrentLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, + }); + + // Handle current action from machine + handleAction(action, materialId); + + } else { + + // Event Manager Needed + + } + } + } + } + } } else if (toEvent?.type === 'roboticArm') { // Human to Robotic Arm @@ -1013,6 +1587,9 @@ export function useTriggerHandler() { } } } + } else if (toEvent?.type === 'human') { + // Human to Human + } } } diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index 09b498c..5c49901 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -13,9 +13,10 @@ import VehicleAnimator from '../animator/vehicleAnimator'; function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) { const { navMesh } = useNavMesh(); const { isPlaying } = usePlayButtonStore(); - const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, productStore } = useSceneContext(); + const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, productStore } = useSceneContext(); const { removeMaterial, setEndTime } = materialStore(); const { getStorageUnitById } = storageUnitStore(); + const { getHumanById } = humanStore(); const { getArmBotById } = armBotStore(); const { getConveyorById } = conveyorStore(); const { triggerPointActions } = useTriggerHandler(); @@ -246,6 +247,11 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) if (action) { handleMaterialDropToStorageUnit(model); } + } else if (model.type === 'human') { + const action = getActionByUuid(selectedProduct.productUuid, agvDetail.point.action.actionUuid); + if (action) { + handleMaterialDropToHuman(model); + } } } else { const droppedMaterial = agvDetail.currentLoad; @@ -259,6 +265,79 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) } } + function handleMaterialDropToHuman(model: HumanEventSchema) { + if (model) { + if (model.point.action.actionType === 'worker') { + loopMaterialDropToHuman( + agvDetail.modelUuid, + agvDetail.currentLoad, + agvDetail.point.action.unLoadDuration, + model.modelUuid, + model.point.action.loadCapacity, + agvDetail.point.action + ); + } + } + } + + function loopMaterialDropToHuman( + vehicleId: string, + vehicleCurrentLoad: number, + unLoadDuration: number, + humanId: string, + storageMaxCapacity: number, + action: VehicleAction + ) { + startTime = performance.now(); + const fixedInterval = ((unLoadDuration / vehicleCurrentLoad) * (1000 / isSpeedRef.current)); + + const unloadLoop = () => { + if (isPausedRef.current) { + pauseTimeRef.current ??= performance.now(); + requestAnimationFrame(unloadLoop); + return; + } + + if (pauseTimeRef.current) { + const pauseDuration = performance.now() - pauseTimeRef.current; + startTime += pauseDuration; + pauseTimeRef.current = null; + } + + const elapsedTime = performance.now() - startTime; + const human = getHumanById(humanId); + + if (elapsedTime >= fixedInterval) { + if (human && agvDetail && + human.currentLoad < storageMaxCapacity && + vehicleCurrentLoad > 0) { + + decrementVehicleLoad(vehicleId, 1); + vehicleCurrentLoad -= 1; + + const material = removeLastMaterial(vehicleId); + if (material) { + + triggerPointActions(action, material.materialId); + + } + + if (vehicleCurrentLoad > 0 && human.currentLoad < storageMaxCapacity) { + startTime = performance.now(); + requestAnimationFrame(unloadLoop); + } + } + } else { + requestAnimationFrame(unloadLoop); + } + }; + + const human = getHumanById(humanId); + if (human && vehicleCurrentLoad > 0 && human?.currentLoad < storageMaxCapacity) { + unloadLoop(); + } + } + function handleMaterialDropToStorageUnit(model: StorageEventSchema) { if (model) { if (model.point.action.actionType === 'store') {