diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx index c7bd8bc..997d078 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx @@ -28,22 +28,11 @@ function RoboticArmMechanics() { selectedEventData.data.modelUuid, selectedEventData.selectedPoint ) as RoboticArmPointSchema | undefined; - const action = getActionByUuid(selectedProduct.productId, selectedAction.actionId) as RoboticArmPointSchema["actions"][0] | undefined; - if (action) { - if (point?.actions) { - setSelectedPointData(point); - if (point.actions.length > 0 && action) { - setActiveOption(action.actionType as "default" | "pickAndPlace"); - setSelectedAction(selectedAction.actionId, selectedAction.actionName); - } - } - } else { - if (point?.actions) { - setSelectedPointData(point); - if (point.actions.length > 0) { - setActiveOption(point.actions[0].actionType as "default" | "pickAndPlace"); - setSelectedAction(point.actions[0].actionUuid, point.actions[0].actionName); - } + if (point?.actions) { + setSelectedPointData(point); + if (point.actions.length > 0) { + setActiveOption(point.actions[0].actionType as "default" | "pickAndPlace"); + setSelectedAction(point.actions[0].actionUuid, point.actions[0].actionName); } } } else { diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx index cc2efb6..0aeae66 100644 --- a/app/src/components/ui/simulation/simulationPlayer.tsx +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -38,6 +38,14 @@ const SimulationPlayer: React.FC = () => { const { isReset, setReset } = useResetButtonStore(); const { subModule } = useSubModuleStore(); + useEffect(() => { + if (isReset) { + setTimeout(()=>{ + setReset(false); + },0) + } + }, [isReset]) + // Button functions const handleReset = () => { setReset(true); diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts new file mode 100644 index 0000000..f49a816 --- /dev/null +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useDelayHandler.ts @@ -0,0 +1,105 @@ +import { useCallback, useEffect, useRef } from "react"; +import { useFrame } from "@react-three/fiber"; +import { usePlayButtonStore, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore"; +import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; + +interface DelayInstance { + delayEndTime: number; + materialId?: string; + action: ConveyorAction; + isPaused: boolean; + remainingTime: number; +} + +export function useDelayHandler() { + const { isPlaying } = usePlayButtonStore(); + const { isPaused } = usePauseButtonStore(); + const { isReset } = useResetButtonStore(); + const { setIsPaused } = useMaterialStore(); + const activeDelays = useRef>(new Map()); + + const cleanupDelay = useCallback(() => { + activeDelays.current.clear(); + }, []); + + useEffect(() => { + if (isReset) { + cleanupDelay(); + } + }, [isReset, cleanupDelay]); + + const delayLogStatus = (materialUuid: string, status: string) => { + // console.log(`${materialUuid}, ${status}`); + } + + useEffect(() => { + const currentTime = performance.now(); + + activeDelays.current.forEach((delay) => { + if (isPaused && !delay.isPaused) { + delay.remainingTime = Math.max(0, delay.delayEndTime - currentTime); + delay.isPaused = true; + } else if (!isPaused && delay.isPaused) { + delay.delayEndTime = currentTime + delay.remainingTime; + delay.isPaused = false; + delay.remainingTime = 0; + } + }); + }, [isPaused]); + + useFrame(() => { + if (!isPlaying || isPaused) return; + + const currentTime = performance.now(); + const completedDelays: string[] = []; + + activeDelays.current.forEach((delay, key) => { + if (!delay.isPaused && currentTime >= delay.delayEndTime && delay.materialId) { + delayLogStatus(delay.materialId, `Delay completed}`); + setIsPaused(delay.materialId, false); + completedDelays.push(key); + } + }); + + completedDelays.forEach(key => { + activeDelays.current.delete(key); + }); + }); + + const handleDelay = useCallback((action: ConveyorAction, materialId?: string) => { + if (!action || action.actionType !== 'delay' || !isPlaying || !materialId) return; + + const delayMs = (action.delay || 0) * 1000; + if (delayMs <= 0) return; + + const key = materialId ? `${materialId}-${action.actionUuid}` : action.actionUuid; + + // If this material already has a delay, cancel it + if (activeDelays.current.has(key)) { + activeDelays.current.delete(key); + } + + activeDelays.current.set(key, { + delayEndTime: performance.now() + delayMs, + materialId, + action, + isPaused: false, + remainingTime: 0 + }); + + delayLogStatus(materialId, `Started ${delayMs * 1000}s delay`); + setIsPaused(materialId, true); + + }, [isPlaying]); + + useEffect(() => { + return () => { + cleanupDelay(); + }; + }, [cleanupDelay]); + + return { + handleDelay, + cleanupDelay + }; +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts index 8f639e6..e12e538 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts @@ -5,6 +5,7 @@ import { useMaterialStore } from "../../../../../store/simulation/useMaterialSto import { useProductStore } from "../../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; import { usePlayButtonStore, useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from "../../../../../store/usePlayButtonStore"; +import { useConveyorStore } from "../../../../../store/simulation/useConveyorStore"; interface SpawnInstance { lastSpawnTime: number | null; @@ -23,6 +24,7 @@ interface SpawnInstance { export function useSpawnHandler() { const { addMaterial } = useMaterialStore(); + const { getConveyorById } = useConveyorStore(); const { getModelUuidByActionUuid, getPointUuidByActionUuid } = useProductStore(); const { isPlaying } = usePlayButtonStore(); const { isPaused } = usePauseButtonStore(); @@ -57,11 +59,12 @@ export function useSpawnHandler() { materialType: materialType, isActive: false, isVisible: true, + isPaused: false, isRendered: true, current: { modelUuid: modelUuid, pointUuid: pointUuid, - actionUuid: action?.actionUuid || '' + actionUuid: action.actionUuid }, weight: 1, cost: 1 @@ -74,7 +77,6 @@ export function useSpawnHandler() { newMaterial.next = { modelUuid: action.triggers[0].triggeredAsset?.triggeredModel.modelUuid, pointUuid: action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid, - actionUuid: action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid } } @@ -82,6 +84,15 @@ export function useSpawnHandler() { return newMaterial; }, [addMaterial, getModelUuidByActionUuid, getPointUuidByActionUuid, selectedProduct.productId]); + + const getConveyorPausedState = useCallback((action: ConveyorAction) => { + const modelUuid = getModelUuidByActionUuid(selectedProduct.productId, action.actionUuid); + if (!modelUuid) return false; + + const conveyor = getConveyorById(modelUuid); + return conveyor?.isPaused ?? false; + }, [getConveyorById, getModelUuidByActionUuid, selectedProduct.productId]); + useEffect(() => { const currentTime = performance.now(); diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts new file mode 100644 index 0000000..bd3a6fc --- /dev/null +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useSwapHandler.ts @@ -0,0 +1,32 @@ +import { useCallback } from "react"; +import { useMaterialStore } from "../../../../../store/simulation/useMaterialStore"; +import { useProductStore } from "../../../../../store/simulation/useProductStore"; +import { useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; +import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore"; + +export function useSwapHandler() { + const { addMaterial, getMaterialById, setMaterial } = useMaterialStore(); + const { getPointUuidByActionUuid } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + const { isPlaying } = usePlayButtonStore(); + + const swapLogStatus = (materialUuid: string, status: string) => { + // console.log(`${materialUuid}, ${status}`); + } + + const handleSwap = useCallback((action: ConveyorAction, materialId?: string) => { + if (!action || action.actionType !== 'swap' || !isPlaying || !materialId) return; + + const { material: newMaterialType } = action; + const material = getMaterialById(materialId); + if (!material) return; + + setMaterial(material.materialId, newMaterialType); + swapLogStatus(material.materialId, `Swapped to ${newMaterialType}`); + + }, [addMaterial, getMaterialById, getPointUuidByActionUuid, isPlaying, setMaterial, selectedProduct.productId]); + + return { + handleSwap, + }; +} \ No newline at end of file diff --git a/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts index f93ef4f..3846b99 100644 --- a/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts +++ b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts @@ -1,32 +1,33 @@ -import { useEffect, useCallback, useRef } from "react"; +import { useEffect, useCallback } from "react"; import { useSpawnHandler } from "./actionHandler/useSpawnHandler"; +import { useSwapHandler } from "./actionHandler/useSwapHandler"; +import { useDelayHandler } from "./actionHandler/useDelayHandler"; export function useConveyorActions() { const { handleSpawn, clearCurrentSpawn } = useSpawnHandler(); + const { handleSwap } = useSwapHandler(); + const { handleDelay, cleanupDelay } = useDelayHandler(); const handleDefaultAction = useCallback((action: ConveyorAction) => { - console.log('action: ', action); - console.log(`Default conveyor action ${action.actionUuid}`); }, []); const handleSpawnAction = useCallback((action: ConveyorAction) => { handleSpawn(action); }, [handleSpawn]); - const handleSwapAction = useCallback((action: ConveyorAction) => { - console.log(`Swapping to material ${action.material}`); - }, []); + const handleSwapAction = useCallback((action: ConveyorAction, materialId?: string) => { + handleSwap(action, materialId); + }, [handleSwap]); - const handleDelayAction = useCallback((action: ConveyorAction) => { - const delayMs = (action.delay || 0) * 1000; - console.log(`Delaying for ${delayMs}ms`); - }, []); + const handleDelayAction = useCallback((action: ConveyorAction, materialId?: string) => { + handleDelay(action, materialId); + }, [handleDelay]); const handleDespawnAction = useCallback((action: ConveyorAction) => { console.log(`Despawning material`); }, []); - const handleConveyorAction = useCallback((action: ConveyorAction) => { + const handleConveyorAction = useCallback((action: ConveyorAction, materialId?: string) => { if (!action) return; switch (action.actionType) { @@ -37,10 +38,10 @@ export function useConveyorActions() { handleSpawnAction(action); break; case 'swap': - handleSwapAction(action); + handleSwapAction(action, materialId); break; case 'delay': - handleDelayAction(action); + handleDelayAction(action, materialId); break; case 'despawn': handleDespawnAction(action); @@ -52,7 +53,8 @@ export function useConveyorActions() { const cleanup = useCallback(() => { clearCurrentSpawn(); - }, [clearCurrentSpawn]); + cleanupDelay(); + }, [clearCurrentSpawn, cleanupDelay]); useEffect(() => { return () => { diff --git a/app/src/modules/simulation/actions/useActionHandler.ts b/app/src/modules/simulation/actions/useActionHandler.ts index 28a5359..91a3c84 100644 --- a/app/src/modules/simulation/actions/useActionHandler.ts +++ b/app/src/modules/simulation/actions/useActionHandler.ts @@ -15,13 +15,13 @@ export function useActionHandler() { const { handleMachineAction, cleanup: cleanupMachine } = useMachineActions(); const { handleStorageAction, cleanup: cleanupStorage } = useStorageActions(); - const handleAction = useCallback((action: Action) => { + const handleAction = useCallback((action: Action, materialId?: string) => { if (!action) return; try { switch (action.actionType) { case 'default': case 'spawn': case 'swap': case 'delay': case 'despawn': - handleConveyorAction(action as ConveyorAction); + handleConveyorAction(action as ConveyorAction, materialId as string); break; case 'travel': handleVehicleAction(action as VehicleAction); diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx index 9c9d612..f1fc34d 100644 --- a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx @@ -1,6 +1,33 @@ -import React from 'react' +import React, { useEffect } from 'react' +import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore'; +import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore'; +import { useResetButtonStore } from '../../../../../store/usePlayButtonStore'; + +function ConveyorInstance({ conveyor }: { conveyor: ConveyorStatus }) { + const { materials, getMaterialsByCurrentModelUuid } = useMaterialStore(); + const { isReset } = useResetButtonStore(); + + const { setConveyorPaused } = useConveyorStore(); + + useEffect(() => { + const conveyorMaterials = getMaterialsByCurrentModelUuid(conveyor.modelUuid); + if (conveyorMaterials && conveyorMaterials?.length > 0) { + + const hasPausedMaterials = conveyorMaterials.some(material => material.isPaused); + + if (hasPausedMaterials) { + setConveyorPaused(conveyor.modelUuid, true); + } else { + setConveyorPaused(conveyor.modelUuid, false); + } + } + + }, [materials, conveyor.modelUuid, getMaterialsByCurrentModelUuid, setConveyorPaused, isReset]); + + useEffect(() => { + // console.log('conveyor: ', conveyor); + }, [conveyor]) -function ConveyorInstance() { return ( <> diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx index 3f53784..4bc4252 100644 --- a/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx @@ -1,11 +1,17 @@ import React from 'react' import ConveyorInstance from './conveyorInstance/conveyorInstance' +import { useConveyorStore } from '../../../../store/simulation/useConveyorStore' function ConveyorInstances() { + + const { conveyors } = useConveyorStore(); + return ( <> - + {conveyors.map((conveyor: ConveyorStatus) => + + )} ) diff --git a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx index c0a021d..c78eac7 100644 --- a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx +++ b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState, useRef } from 'react'; import * as THREE from 'three'; import { useFrame, useThree } from '@react-three/fiber'; import { usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; +import { useConveyorStore } from '../../../../../store/simulation/useConveyorStore'; interface MaterialAnimatorProps { matRef: React.RefObject; @@ -19,6 +20,7 @@ function MaterialAnimator({ const { scene } = useThree(); const [targetPosition, setTargetPosition] = useState(null); const [isAnimating, setIsAnimating] = useState(false); + const { getConveyorById } = useConveyorStore(); const animationState = useRef({ startTime: 0, startPosition: new THREE.Vector3(), @@ -29,7 +31,10 @@ function MaterialAnimator({ }); const { isPlaying } = usePlayButtonStore(); - const { isPaused } = usePauseButtonStore(); + const { isPaused: isGlobalPaused } = usePauseButtonStore(); + + const conveyor = getConveyorById(material.current.modelUuid); + const shouldPause = isGlobalPaused || material.isPaused || conveyor?.isPaused; const getWorldPosition = (uuid: string): THREE.Vector3 | null => { const obj = scene.getObjectByProperty('uuid', uuid); @@ -39,7 +44,6 @@ function MaterialAnimator({ return position; }; - // Handle target position changes and play state useEffect(() => { if (!isPlaying || !material.next?.pointUuid) { setIsAnimating(false); @@ -58,22 +62,21 @@ function MaterialAnimator({ } }, [material.next?.pointUuid, isPlaying]); - // Handle pause/unpause useEffect(() => { - if (isPaused) { + if (shouldPause) { + // Pause the animation animationState.current.isPaused = true; setIsAnimating(false); - // Record the time when paused animationState.current.pausedTime = performance.now() - animationState.current.startTime; } else { + // Resume the animation animationState.current.isPaused = false; if (isPlaying && targetPosition && !isAnimating) { - // Resume from where we left off animationState.current.startTime = performance.now() - animationState.current.pausedTime; setIsAnimating(true); } } - }, [isPaused]); + }, [shouldPause, isPlaying]); useFrame(() => { if (!matRef.current || !targetPosition || !isAnimating || animationState.current.isPaused || !isPlaying) { @@ -81,7 +84,6 @@ function MaterialAnimator({ } const currentTime = performance.now(); - // Calculate elapsed time since animation start, minus any paused time const elapsed = (currentTime - animationState.current.startTime) / 1000; const progress = Math.min(1, (currentSpeed * elapsed) / animationState.current.totalDistance); @@ -95,6 +97,14 @@ function MaterialAnimator({ matRef.current.position.copy(targetPosition); setIsAnimating(false); onAnimationComplete?.(); + animationState.current = { + startTime: 0, + startPosition: new THREE.Vector3(), + totalDistance: 0, + pausedTime: 0, + isPaused: false, + lastFrameTime: 0 + }; } }); diff --git a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx index 338a45c..6cbc0f0 100644 --- a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx +++ b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx @@ -11,7 +11,7 @@ import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHa function MaterialInstance({ material }: { material: MaterialSchema }) { const matRef: any = useRef(); const { scene } = useThree(); - const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid, getActionByUuid } = useProductStore(); + const { getModelUuidByPointUuid, getPointByUuid, getEventByModelUuid, getActionByUuid, getTriggerByUuid, getActionByPointUuid } = useProductStore(); const { selectedProduct } = useSelectedProduct(); const { speed } = useAnimationPlaySpeed(); const { triggerPointActions } = useTriggerHandler(); @@ -39,12 +39,12 @@ function MaterialInstance({ material }: { material: MaterialSchema }) { const point = getPointByUuid(selectedProduct.productId, modelUuid, material.current.pointUuid); if (!point) { - return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 }; + return { position: new THREE.Vector3(0, 0, 0), rotation: new THREE.Vector3(0, 0, 0), currentSpeed: currentSpeed || 1 }; } const position = getWorldPositionFromScene(point.uuid); if (position) { - return { position: position, rotation: new THREE.Vector3(0, 0, 0), currentSpeed: 1 }; + return { position: position, rotation: new THREE.Vector3(0, 0, 0), currentSpeed: currentSpeed || 1 }; } return { @@ -54,6 +54,7 @@ function MaterialInstance({ material }: { material: MaterialSchema }) { }; }, [material, getPointByUuid]); + function getCurrentSpeed(productId: string, modelUuid: string) { const event = getEventByModelUuid(productId, modelUuid) if (event) { @@ -84,13 +85,45 @@ function MaterialInstance({ material }: { material: MaterialSchema }) { useEffect(() => { // console.log('material: ', material); + if (material.current && material.next) { + // console.log('current: ', material.current.pointUuid); + // console.log('next: ', material.next.pointUuid); + } }, [material]) const callTrigger = () => { - const action = getActionByUuid(selectedProduct.productId, material.current.actionUuid) - if (action) { - triggerPointActions(action); + if (!material.next) return; + const fromModel = getEventByModelUuid(selectedProduct.productId, material.next.modelUuid); + if (!fromModel) return; + const fromPoint = getPointByUuid(selectedProduct.productId, fromModel.modelUuid, material.next.pointUuid); + if (!fromPoint) return; + + if (fromModel.type === 'transfer') { + const toModel = getEventByModelUuid(selectedProduct.productId, material.next.modelUuid); + if (!toModel) return; + if (toModel.type === 'transfer') { + const action = getActionByPointUuid(selectedProduct.productId, material.next.pointUuid); + if (action) { + triggerPointActions(action, material.materialId); + } + } else if (toModel?.type === 'vehicle') { + // Transfer to Vehicle + + } else if (toModel?.type === 'machine') { + // Transfer to Machine + + } else if (toModel?.type === 'roboticArm') { + // Transfer to Robotic Arm + + } else if (toModel?.type === 'storageUnit') { + // Transfer to Storage Unit + } + } else if (fromModel.type === 'vehicle') { + } else if (fromModel.type === 'machine') { + } else if (fromModel.type === 'roboticArm') { + } else if (fromModel.type === 'storageUnit') { } + } return ( diff --git a/app/src/modules/simulation/materials/instances/material/materialModel.tsx b/app/src/modules/simulation/materials/instances/material/materialModel.tsx index f27541b..2093584 100644 --- a/app/src/modules/simulation/materials/instances/material/materialModel.tsx +++ b/app/src/modules/simulation/materials/instances/material/materialModel.tsx @@ -32,7 +32,7 @@ export function MaterialModel({ materialType, matRef, ...props }: ModelProps) { ); diff --git a/app/src/modules/simulation/materials/instances/materialInstances.tsx b/app/src/modules/simulation/materials/instances/materialInstances.tsx index 199e604..1864f0f 100644 --- a/app/src/modules/simulation/materials/instances/materialInstances.tsx +++ b/app/src/modules/simulation/materials/instances/materialInstances.tsx @@ -6,7 +6,7 @@ function MaterialInstances() { const { materials } = useMaterialStore(); useEffect(() => { - console.log('materials: ', materials); + // console.log('materials: ', materials); }, [materials]) return ( diff --git a/app/src/modules/simulation/products/products.tsx b/app/src/modules/simulation/products/products.tsx index 54dacf4..e1e8fa6 100644 --- a/app/src/modules/simulation/products/products.tsx +++ b/app/src/modules/simulation/products/products.tsx @@ -7,12 +7,14 @@ import { upsertProductOrEventApi } from '../../../services/simulation/UpsertProd import { getAllProductsApi } from '../../../services/simulation/getallProductsApi'; import { useVehicleStore } from '../../../store/simulation/useVehicleStore'; import { useArmBotStore } from '../../../store/simulation/useArmBotStore'; +import { useConveyorStore } from '../../../store/simulation/useConveyorStore'; function Products() { const { products, getProductById, addProduct, setProducts } = useProductStore(); const { selectedProduct, setSelectedProduct } = useSelectedProduct(); const { addVehicle, clearvehicles } = useVehicleStore(); const { addArmBot, clearArmBots } = useArmBotStore(); + const { addConveyor, clearConveyors } = useConveyorStore(); useEffect(() => { const email = localStorage.getItem('email') @@ -59,6 +61,20 @@ function Products() { } }, [selectedProduct, products]); + useEffect(() => { + if (selectedProduct.productId) { + const product = getProductById(selectedProduct.productId); + if (product) { + clearConveyors(); + product.eventDatas.forEach(events => { + if (events.type === 'transfer') { + addConveyor(selectedProduct.productId, events); + } + }); + } + } + }, [selectedProduct, products]); + return ( <> diff --git a/app/src/modules/simulation/roboticArm/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/materialAnimator.tsx index 893f759..b458d32 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/materialAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/materialAnimator.tsx @@ -1,17 +1,16 @@ import { useFrame } from '@react-three/fiber'; import React, { useEffect, useRef, useState } from 'react'; import * as THREE from 'three'; -import { useLogger } from '../../../../../components/ui/log/LoggerContext'; +import { MaterialModel } from '../../../materials/instances/material/materialModel'; type MaterialAnimatorProps = { ikSolver: any; - armBot: any; + armBot: ArmBotStatus; currentPhase: string; }; export default function MaterialAnimator({ ikSolver, armBot, currentPhase }: MaterialAnimatorProps) { - const sphereRef = useRef(null); - const boneWorldPosition = new THREE.Vector3(); + const sphereRef = useRef(null); const [isRendered, setIsRendered] = useState(false); useEffect(() => { @@ -41,7 +40,8 @@ export default function MaterialAnimator({ ikSolver, armBot, currentPhase }: Mat const direction = new THREE.Vector3(); direction.subVectors(boneWorldPos, boneTargetWorldPos).normalize(); const downwardDirection = direction.clone().negate(); - const adjustedPosition = boneWorldPos.clone().addScaledVector(downwardDirection, -0.25); + + const adjustedPosition = boneWorldPos.clone().addScaledVector(downwardDirection, -0.01); //set position sphereRef.current.position.copy(adjustedPosition); @@ -56,10 +56,10 @@ export default function MaterialAnimator({ ikSolver, armBot, currentPhase }: Mat return ( <> {isRendered && ( - - - - + )} ); diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index 34f191b..3ca7665 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -1,39 +1,34 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useFrame } from '@react-three/fiber'; import * as THREE from 'three'; -import { Line } from '@react-three/drei'; -import { - useAnimationPlaySpeed, - usePauseButtonStore, - usePlayButtonStore, - useResetButtonStore -} from '../../../../../store/usePlayButtonStore'; +import { Line, Text } from '@react-three/drei'; +import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; + +type PointWithDegree = { + position: [number, number, number]; + degree: number; +}; + +function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone, armBot, path }: any) { -function RoboticArmAnimator({ - HandleCallback, - restPosition, - ikSolver, - targetBone, - armBot, - path -}: any) { const progressRef = useRef(0); const curveRef = useRef(null); - const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); - const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]); - const [customCurvePoints, setCustomCurvePoints] = useState(null); - let curveHeight = 1.75 const totalDistanceRef = useRef(0); const startTimeRef = useRef(null); const segmentDistancesRef = useRef([]); + const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); + const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]); + const [circlePointsWithDegrees, setCirclePointsWithDegrees] = useState([]); + const [customCurvePoints, setCustomCurvePoints] = useState(null); + let curveHeight = 1.75 + const CIRCLE_RADIUS = 1.6 + // Zustand stores const { isPlaying } = usePlayButtonStore(); const { isPaused } = usePauseButtonStore(); const { isReset } = useResetButtonStore(); const { speed } = useAnimationPlaySpeed(); - const CIRCLE_RADIUS = 1.6 - // Update path state whenever `path` prop changes useEffect(() => { setCurrentPath(path); @@ -47,7 +42,7 @@ function RoboticArmAnimator({ //Handle Reset Animation useEffect(() => { - if (isReset) { + if (isReset || !isPlaying) { progressRef.current = 0; curveRef.current = null; setCurrentPath([]); @@ -76,6 +71,29 @@ function RoboticArmAnimator({ } return points; } + + //Generate CirclePoints with Angle + function generateRingPointsWithDegrees(radius: number, segments: number, initialRotation: [number, number, number]) { + const points: { position: [number, number, number]; degree: number }[] = []; + for (let i = 0; i < segments; i++) { + const angleRadians = (i / segments) * Math.PI * 2; + const x = Math.cos(angleRadians) * radius; + const z = Math.sin(angleRadians) * radius; + const degree = (angleRadians * 180) / Math.PI; // Convert radians to degrees + points.push({ + position: [x, 1.5, z], + degree, + }); + } + return points; + } + + // Handle circle points based on armBot position + useEffect(() => { + const points = generateRingPointsWithDegrees(CIRCLE_RADIUS, 64, armBot.rotation); + setCirclePointsWithDegrees(points) + }, [armBot.rotation]); + // Function for find nearest Circlepoints Index const findNearestIndex = (nearestPoint: [number, number, number], points: [number, number, number][], epsilon = 1e-6) => { for (let i = 0; i < points.length; i++) { @@ -100,6 +118,30 @@ function RoboticArmAnimator({ }, circlePoints[0]); }; + // Helper function to collect points and check forbidden degrees + const collectArcPoints = (startIdx: number, endIdx: number, clockwise: boolean) => { + const totalSegments = 64; + const arcPoints: [number, number, number][] = []; + let i = startIdx; + + while (i !== (endIdx + (clockwise ? 1 : -1) + totalSegments) % totalSegments) { + const { degree, position } = circlePointsWithDegrees[i]; + // Skip over + arcPoints.push(position); + i = (i + (clockwise ? 1 : -1) + totalSegments) % totalSegments; + } + return arcPoints; + }; + + //Range to restrict angle + const hasForbiddenDegrees = (arc: [number, number, number][]) => { + return arc.some(p => { + const idx = findNearestIndex(p, circlePoints); + const degree = circlePointsWithDegrees[idx]?.degree || 0; + return degree >= 271 && degree <= 300; // Forbidden range: 271° to 300° + }); + }; + // Handle nearest points and final path (including arc points) useEffect(() => { if (circlePoints.length > 0 && currentPath.length > 0) { @@ -116,31 +158,23 @@ function RoboticArmAnimator({ const indexOfNearestStart = findNearestIndex(nearestToStart, circlePoints); const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints); - const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + 64) % 64; - const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + 64) % 64; - const clockwiseIsShorter = clockwiseDistance <= counterClockwiseDistance; + const totalSegments = 64; + const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + totalSegments) % totalSegments; + const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + totalSegments) % totalSegments; + + // Try both directions + const arcClockwise = collectArcPoints(indexOfNearestStart, indexOfNearestEnd, true); + const arcCounterClockwise = collectArcPoints(indexOfNearestStart, indexOfNearestEnd, false); + + const clockwiseForbidden = hasForbiddenDegrees(arcClockwise); + const counterClockwiseForbidden = hasForbiddenDegrees(arcCounterClockwise); let arcPoints: [number, number, number][] = []; - if (clockwiseIsShorter) { - if (indexOfNearestStart <= indexOfNearestEnd) { - arcPoints = circlePoints.slice(indexOfNearestStart, indexOfNearestEnd + 1); - } else { - arcPoints = [ - ...circlePoints.slice(indexOfNearestStart, 64), - ...circlePoints.slice(0, indexOfNearestEnd + 1) - ]; - } + if (!clockwiseForbidden && (clockwiseDistance <= counterClockwiseDistance || counterClockwiseForbidden)) { + arcPoints = arcClockwise; } else { - if (indexOfNearestStart >= indexOfNearestEnd) { - for (let i = indexOfNearestStart; i !== (indexOfNearestEnd - 1 + 64) % 64; i = (i - 1 + 64) % 64) { - arcPoints.push(circlePoints[i]); - } - } else { - for (let i = indexOfNearestStart; i !== (indexOfNearestEnd - 1 + 64) % 64; i = (i - 1 + 64) % 64) { - arcPoints.push(circlePoints[i]); - } - } + arcPoints = arcCounterClockwise; } const pathVectors = [ @@ -153,9 +187,7 @@ function RoboticArmAnimator({ new THREE.Vector3(end[0], end[1], end[2]) ]; - const pathSegments: [THREE.Vector3, THREE.Vector3][] = []; - for (let i = 0; i < pathVectors.length - 1; i++) { pathSegments.push([pathVectors[i], pathVectors[i + 1]]); } @@ -165,13 +197,7 @@ function RoboticArmAnimator({ const totalDistance = segmentDistances.reduce((sum, d) => sum + d, 0); totalDistanceRef.current = totalDistance; - const movementSpeed = speed * armBot.speed; - const totalMoveTime = totalDistance / movementSpeed; - - const segmentTimes = segmentDistances.map(distance => (distance / totalDistance) * totalMoveTime); - setCustomCurvePoints(pathVectors); - } }, [circlePoints, currentPath]); @@ -182,7 +208,6 @@ function RoboticArmAnimator({ const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); if (!bone) return; - if (isPlaying) { if (isReset) { bone.position.copy(restPosition); @@ -227,12 +252,17 @@ function RoboticArmAnimator({ ikSolver.update(); } } else if (!isPlaying && currentPath.length === 0) { + progressRef.current = 0; + startTimeRef.current = null; + setCurrentPath([]); + setCustomCurvePoints([]); bone.position.copy(restPosition); + } ikSolver.update(); }); - + //Helper to Visible the Circle and Curve return ( <> {customCurvePoints && customCurvePoints?.length >= 2 && currentPath && isPlaying && ( @@ -245,10 +275,18 @@ function RoboticArmAnimator({ /> )} - - - - + + {/* Green ring */} + + + + + + + ); } diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 21bbc7e..7cd3634 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -5,7 +5,6 @@ import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '.. import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb"; import { useThree } from "@react-three/fiber"; -import useModuleStore from '../../../../../store/useModuleStore'; import * as THREE from "three"; import MaterialAnimator from '../animator/materialAnimator'; @@ -24,16 +23,15 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { let startTime: number; //zustand const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore(); - const { activeModule } = useModuleStore(); const { isPlaying } = usePlayButtonStore(); - const { isReset, setReset } = useResetButtonStore(); + const { isReset } = useResetButtonStore(); const { isPaused } = usePauseButtonStore(); - function firstFrame() { startTime = performance.now(); step(); } + function step() { if (isPausedRef.current) { if (!pauseTimeRef.current) { @@ -87,6 +85,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.") } } + useEffect(() => { isPausedRef.current = isPaused; }, [isPaused]); @@ -98,6 +97,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { setArmBotState(armBot.modelUuid, "idle") setCurrentPhase("init"); setPath([]) + setIkSolver(null); removeCurrentAction(armBot.modelUuid) isPausedRef.current = false pauseTimeRef.current = null @@ -117,17 +117,17 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { useEffect(() => { const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid); if (targetMesh) { - targetMesh.visible = activeModule !== "simulation" + targetMesh.visible = (!isPlaying) } const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone); - if (isPlaying) { + if (!isReset && isPlaying) { //Moving armBot from initial point to rest position. if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") { - setArmBotActive(armBot.modelUuid, true) - setArmBotState(armBot.modelUuid, "running") - setCurrentPhase("init-to-rest"); if (targetBones) { - let curve = createCurveBetweenTwoPoints(targetBones.position, targetBones.position) + setArmBotActive(armBot.modelUuid, true) + setArmBotState(armBot.modelUuid, "running") + setCurrentPhase("init-to-rest"); + let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition) if (curve) { setPath(curve.points.map(point => [point.x, point.y, point.z])); } @@ -138,13 +138,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction") const timeoutId = setTimeout(() => { - addCurrentAction(armBot.modelUuid, armBot.point.actions[0].actionUuid); + addCurrentAction(armBot.modelUuid, armBot.point.actions[0].actionUuid, 'Material 2'); }, 3000); return () => clearTimeout(timeoutId); } + //Moving to pickup point else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) { if (armBot.currentAction) { - setArmBotActive(armBot.modelUuid, true); setArmBotState(armBot.modelUuid, "running"); setCurrentPhase("rest-to-start"); @@ -158,15 +158,29 @@ 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) { requestAnimationFrame(firstFrame); } + //Moving to drop point to restPosition else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "dropping" && armBot.currentAction) { requestAnimationFrame(firstFrame); } + } else { + logStatus(armBot.modelUuid, "Simulation Play Exited") + setArmBotActive(armBot.modelUuid, false) + setArmBotState(armBot.modelUuid, "idle") + setCurrentPhase("init"); + setIkSolver(null); + setPath([]) + isPausedRef.current = false + pauseTimeRef.current = null + isPausedRef.current = false + startTime = 0 + removeCurrentAction(armBot.modelUuid) } - }, [currentPhase, armBot, isPlaying, ikSolver]) + }, [currentPhase, armBot, isPlaying, isReset, ikSolver]) function createCurveBetweenTwoPoints(p1: any, p2: any) { @@ -213,9 +227,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { return ( <> - - + {!isReset && isPlaying && ( + <> + + + + )} ) diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx index be41a95..4bd05a6 100644 --- a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx @@ -11,7 +11,7 @@ type IKInstanceProps = { modelUrl: string; ikSolver: any; setIkSolver: any - armBot: any; + armBot: ArmBotStatus; groupRef: any; }; function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKInstanceProps) { @@ -57,12 +57,6 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns rotationMin: new THREE.Vector3(0, 0, 0), rotationMax: new THREE.Vector3(2, 0, 0), }, - // { - // index: 1, - // enabled: true, - // rotationMin: new THREE.Vector3(0, -Math.PI, 0), - // rotationMax: new THREE.Vector3(0, Math.PI, 0), - // }, { index: 1, enabled: true, limitation: new THREE.Vector3(0, 1, 0) }, { index: 0, enabled: false, limitation: new THREE.Vector3(0, 0, 0) }, ], @@ -76,7 +70,7 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns setSelectedArm(OOI.Target_Bone); - scene.add(helper); + // scene.add(helper); }, [cloned, gltf, setIkSolver]); @@ -86,10 +80,10 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns setSelectedArm(groupRef.current?.getObjectByName(targetBoneName)) }}> {/* {selectedArm && } */} diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx index a609ad5..91ccb55 100644 --- a/app/src/modules/simulation/roboticArm/roboticArm.tsx +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -1,24 +1,37 @@ -import { useEffect } from "react"; -import RoboticArmInstances from "./instances/roboticArmInstances"; +import { useEffect, useState } from "react"; import { useArmBotStore } from "../../../store/simulation/useArmBotStore"; -import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; +import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import ArmBotUI from "../ui/arm/armBotUI"; +import RoboticArmInstances from "./instances/roboticArmInstances"; function RoboticArm() { - const { armBots } = useArmBotStore(); + const { armBots, getArmBotById } = useArmBotStore(); const { selectedEventSphere } = useSelectedEventSphere(); - const { selectedEventData } = useSelectedEventData(); + const { isPlaying } = usePlayButtonStore(); + const [isArmBotSelected, setIsArmBotSelected] = useState(false); useEffect(() => { // console.log('armBots: ', armBots); }, [armBots]) + useEffect(() => { + if (selectedEventSphere) { + const selectedArmBot = getArmBotById(selectedEventSphere.userData.modelUuid); + if (selectedArmBot) { + setIsArmBotSelected(true); + } else { + setIsArmBotSelected(false); + } + } + }, [selectedEventSphere]) + return ( <> - {selectedEventSphere && selectedEventData?.data.type === "roboticArm" && + {isArmBotSelected && !isPlaying && < ArmBotUI /> } @@ -26,4 +39,4 @@ function RoboticArm() { ); } -export default RoboticArm; +export default RoboticArm; \ No newline at end of file diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts index 7541522..967a23a 100644 --- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts +++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts @@ -1,131 +1,128 @@ -import { useCallback, useEffect, useRef } from 'react'; +import { useCallback } from 'react'; import { useActionHandler } from '../../actions/useActionHandler'; import { useProductStore } from '../../../../store/simulation/useProductStore'; import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; import { useMaterialStore } from '../../../../store/simulation/useMaterialStore'; export function useTriggerHandler() { - const { getActionByUuid, getEventByTriggerUuid, getEventByModelUuid } = useProductStore(); + const { getEventByTriggerUuid, getEventByModelUuid } = useProductStore(); const { handleAction } = useActionHandler(); - const { getMaterialByCurrentModelUuid, setCurrentLocation, setNextLocation } = useMaterialStore(); + const { setCurrentLocation, setNextLocation, getMaterialById } = useMaterialStore(); const { selectedProduct } = useSelectedProduct(); - const handleTrigger = (trigger: TriggerSchema, actionUuid: string) => { + const handleTrigger = (trigger: TriggerSchema, action: Action, materialId: string) => { - // const fromEvent = getEventByTriggerUuid(selectedProduct.productId, trigger.triggerUuid); - // console.log('fromEvent: ', fromEvent); + const fromEvent = getEventByTriggerUuid(selectedProduct.productId, trigger.triggerUuid); - // const toEvent = getEventByModelUuid(selectedProduct.productId, trigger.triggeredAsset?.triggeredModel.modelUuid || ''); - // console.log('toEvent: ', toEvent); + const toEvent = getEventByModelUuid(selectedProduct.productId, trigger.triggeredAsset?.triggeredModel.modelUuid || ''); - // if (fromEvent?.type === 'transfer') { - // if (toEvent?.type === 'transfer') { - // // console.log('toEvent: ', toEvent.type); - // // Transfer to Transfer - // const action = getActionByUuid(selectedProduct.productId, trigger.triggeredAsset?.triggeredAction?.actionUuid || ''); - // if (action && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { - // const material = getMaterialByCurrentModelUuid(fromEvent.modelUuid); - // if (material) { - // if (material.next && - // action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid && - // action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid) { + if (fromEvent?.type === 'transfer') { + if (toEvent?.type === 'transfer') { + // Transfer to Transfer + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { + const material = getMaterialById(materialId); + if (material) { + if (material.next) { - // setCurrentLocation(material.materialId, material.next); + setCurrentLocation(material.materialId, { + modelUuid: material.next.modelUuid, + pointUuid: material.next.pointUuid, + actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, + }); - // setNextLocation(material.materialId, { - // modelUuid: toEvent.modelUuid, - // pointUuid: action.triggers[0].triggeredAsset?.triggeredPoint?.pointUuid, - // actionUuid: action.triggers[0].triggeredAsset?.triggeredAction?.actionUuid - // }); - // } - // handleAction(action); - // } - // } - // } else if (toEvent?.type === 'vehicle') { - // // Transfer to Vehicle + setNextLocation(material.materialId, { + modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid, + pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + }); + } + handleAction(action, materialId); + } + } + } else if (toEvent?.type === 'vehicle') { + // Transfer to Vehicle - // } else if (toEvent?.type === 'machine') { - // // Transfer to Machine + } else if (toEvent?.type === 'machine') { + // Transfer to Machine - // } else if (toEvent?.type === 'roboticArm') { - // // Transfer to Robotic Arm + } else if (toEvent?.type === 'roboticArm') { + // Transfer to Robotic Arm - // } else if (toEvent?.type === 'storageUnit') { - // // Transfer to Storage Unit + } else if (toEvent?.type === 'storageUnit') { + // Transfer to Storage Unit - // } - // } else if (fromEvent?.type === 'vehicle') { - // if (toEvent?.type === 'transfer') { - // // Vehicle to Transfer + } + } else if (fromEvent?.type === 'vehicle') { + if (toEvent?.type === 'transfer') { + // Vehicle to Transfer - // } else if (toEvent?.type === 'vehicle') { - // // Vehicle to Vehicle + } else if (toEvent?.type === 'vehicle') { + // Vehicle to Vehicle - // } else if (toEvent?.type === 'machine') { - // // Vehicle to Machine + } else if (toEvent?.type === 'machine') { + // Vehicle to Machine - // } else if (toEvent?.type === 'roboticArm') { - // // Vehicle to Robotic Arm + } else if (toEvent?.type === 'roboticArm') { + // Vehicle to Robotic Arm - // } else if (toEvent?.type === 'storageUnit') { - // // Vehicle to Storage Unit + } else if (toEvent?.type === 'storageUnit') { + // Vehicle to Storage Unit - // } - // } else if (fromEvent?.type === 'machine') { - // if (toEvent?.type === 'transfer') { - // // Machine to Transfer + } + } else if (fromEvent?.type === 'machine') { + if (toEvent?.type === 'transfer') { + // Machine to Transfer - // } else if (toEvent?.type === 'vehicle') { - // // Machine to Vehicle + } else if (toEvent?.type === 'vehicle') { + // Machine to Vehicle - // } else if (toEvent?.type === 'machine') { - // // Machine to Machine + } else if (toEvent?.type === 'machine') { + // Machine to Machine - // } else if (toEvent?.type === 'roboticArm') { - // // Machine to Robotic Arm + } else if (toEvent?.type === 'roboticArm') { + // Machine to Robotic Arm - // } else if (toEvent?.type === 'storageUnit') { - // // Machine to Storage Unit + } else if (toEvent?.type === 'storageUnit') { + // Machine to Storage Unit - // } - // } else if (fromEvent?.type === 'roboticArm') { - // if (toEvent?.type === 'transfer') { - // // Robotic Arm to Transfer + } + } else if (fromEvent?.type === 'roboticArm') { + if (toEvent?.type === 'transfer') { + // Robotic Arm to Transfer - // } else if (toEvent?.type === 'vehicle') { - // // Robotic Arm to Vehicle + } else if (toEvent?.type === 'vehicle') { + // Robotic Arm to Vehicle - // } else if (toEvent?.type === 'machine') { - // // Robotic Arm to Machine + } else if (toEvent?.type === 'machine') { + // Robotic Arm to Machine - // } else if (toEvent?.type === 'roboticArm') { - // // Robotic Arm to Robotic Arm + } else if (toEvent?.type === 'roboticArm') { + // Robotic Arm to Robotic Arm - // } else if (toEvent?.type === 'storageUnit') { - // // Robotic Arm to Storage Unit + } else if (toEvent?.type === 'storageUnit') { + // Robotic Arm to Storage Unit - // } - // } else if (fromEvent?.type === 'storageUnit') { - // if (toEvent?.type === 'transfer') { - // // Storage Unit to Transfer + } + } else if (fromEvent?.type === 'storageUnit') { + if (toEvent?.type === 'transfer') { + // Storage Unit to Transfer - // } else if (toEvent?.type === 'vehicle') { - // // Storage Unit to Vehicle + } else if (toEvent?.type === 'vehicle') { + // Storage Unit to Vehicle - // } else if (toEvent?.type === 'machine') { - // // Storage Unit to Machine + } else if (toEvent?.type === 'machine') { + // Storage Unit to Machine - // } else if (toEvent?.type === 'roboticArm') { - // // Storage Unit to Robotic Arm + } else if (toEvent?.type === 'roboticArm') { + // Storage Unit to Robotic Arm - // } else if (toEvent?.type === 'storageUnit') { - // // Storage Unit to Storage Unit + } else if (toEvent?.type === 'storageUnit') { + // Storage Unit to Storage Unit - // } - // } + } + } } - const triggerPointActions = useCallback((action: Action) => { + const triggerPointActions = useCallback((action: Action, materialId: string) => { if (!action) return; action.triggers.forEach(trigger => { @@ -133,7 +130,7 @@ export function useTriggerHandler() { case 'onStart': break; case 'onComplete': - handleTrigger(trigger, action.actionUuid); + handleTrigger(trigger, action, materialId); break; case 'onStop': break; diff --git a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts index dae90ee..e1f7fbf 100644 --- a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts +++ b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts @@ -3,166 +3,200 @@ import * as THREE from "three"; import { ThreeEvent, useThree } from "@react-three/fiber"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { - useSelectedEventData, - useSelectedProduct, + useSelectedEventData, + useSelectedProduct, } from "../../../../store/simulation/useSimulationStore"; type OnUpdateCallback = (object: THREE.Object3D) => void; export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { - const { getEventByModelUuid, updateAction, getActionByUuid } = - useProductStore(); - const { selectedEventData } = useSelectedEventData(); - const { selectedProduct } = useSelectedProduct(); - const { camera, gl, controls } = useThree(); - const activeObjRef = useRef(null); - const planeRef = useRef( - new THREE.Plane(new THREE.Vector3(0, 1, 0), 0) - ); - const offsetRef = useRef(new THREE.Vector3()); - const initialPositionRef = useRef(new THREE.Vector3()); + const { getEventByModelUuid, updateAction, getActionByUuid } = + useProductStore(); + const { selectedEventData } = useSelectedEventData(); + const { selectedProduct } = useSelectedProduct(); + const { camera, gl, controls } = useThree(); + const activeObjRef = useRef(null); + const planeRef = useRef( + new THREE.Plane(new THREE.Vector3(0, 1, 0), 0) + ); + const offsetRef = useRef(new THREE.Vector3()); + const initialPositionRef = useRef(new THREE.Vector3()); - const raycaster = new THREE.Raycaster(); - const pointer = new THREE.Vector2(); - const [objectWorldPos, setObjectWorldPos] = useState(new THREE.Vector3()); + const raycaster = new THREE.Raycaster(); + const pointer = new THREE.Vector2(); + const [objectWorldPos, setObjectWorldPos] = useState(new THREE.Vector3()); - const handlePointerDown = (e: ThreeEvent) => { - e.stopPropagation(); + const handlePointerDown = (e: ThreeEvent) => { + e.stopPropagation(); - let obj: THREE.Object3D | null = e.object; + let obj: THREE.Object3D | null = e.object; - // Traverse up until we find modelUuid in userData - while (obj && !obj.userData?.modelUuid) { - obj = obj.parent; - } + // Traverse up until we find modelUuid in userData + while (obj && !obj.userData?.modelUuid) { + obj = obj.parent; + } - if (!obj) return; + if (!obj) return; - // Disable orbit controls while dragging - if (controls) (controls as any).enabled = false; + // Disable orbit controls while dragging + if (controls) (controls as any).enabled = false; - activeObjRef.current = obj; - initialPositionRef.current.copy(obj.position); + activeObjRef.current = obj; + initialPositionRef.current.copy(obj.position); - // Get world position - setObjectWorldPos(obj.getWorldPosition(objectWorldPos)); + // Get world position + setObjectWorldPos(obj.getWorldPosition(objectWorldPos)); - // Set plane at the object's Y level - planeRef.current.set(new THREE.Vector3(0, 1, 0), -objectWorldPos.y); + // Set plane at the object's Y level + planeRef.current.set(new THREE.Vector3(0, 1, 0), -objectWorldPos.y); - // Convert pointer to NDC - const rect = gl.domElement.getBoundingClientRect(); - pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1; - pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1; + // Convert pointer to NDC + const rect = gl.domElement.getBoundingClientRect(); + pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1; + pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1; - // Raycast to intersection - raycaster.setFromCamera(pointer, camera); - const intersection = new THREE.Vector3(); - raycaster.ray.intersectPlane(planeRef.current, intersection); + // Raycast to intersection + raycaster.setFromCamera(pointer, camera); + const intersection = new THREE.Vector3(); + raycaster.ray.intersectPlane(planeRef.current, intersection); - // Calculate offset - offsetRef.current.copy(objectWorldPos).sub(intersection); + // Calculate offset + offsetRef.current.copy(objectWorldPos).sub(intersection); - // Start listening for drag - gl.domElement.addEventListener("pointermove", handlePointerMove); - gl.domElement.addEventListener("pointerup", handlePointerUp); - }; + // Start listening for drag + gl.domElement.addEventListener("pointermove", handlePointerMove); + gl.domElement.addEventListener("pointerup", handlePointerUp); + }; - const handlePointerMove = (e: PointerEvent) => { - if (!activeObjRef.current) return; - if (selectedEventData?.data.type === "roboticArm") { - const selectedArmBot = getEventByModelUuid( - selectedProduct.productId, - selectedEventData.data.modelUuid - ); - if (!selectedArmBot) return; - // Check if Shift key is pressed - const isShiftKeyPressed = e.shiftKey; + const handlePointerMove = (e: PointerEvent) => { + if (!activeObjRef.current) return; + if (selectedEventData?.data.type === "roboticArm") { + const selectedArmBot = getEventByModelUuid( + selectedProduct.productId, + selectedEventData.data.modelUuid + ); + if (!selectedArmBot) return; + // Check if Shift key is pressed + const isShiftKeyPressed = e.shiftKey; - // Get the mouse position relative to the canvas - const rect = gl.domElement.getBoundingClientRect(); - pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1; - pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1; + // Get the mouse position relative to the canvas + const rect = gl.domElement.getBoundingClientRect(); + pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1; + pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1; - // Update raycaster to point to the mouse position - raycaster.setFromCamera(pointer, camera); + // Update raycaster to point to the mouse position + raycaster.setFromCamera(pointer, camera); - // Create a vector to store intersection point - const intersection = new THREE.Vector3(); - const intersects = raycaster.ray.intersectPlane( - planeRef.current, - intersection - ); - if (!intersects) return; + // Create a vector to store intersection point + const intersection = new THREE.Vector3(); + const intersects = raycaster.ray.intersectPlane( + planeRef.current, + intersection + ); + if (!intersects) return; - // Add offset for dragging - intersection.add(offsetRef.current); + // Add offset for dragging + intersection.add(offsetRef.current); - // Get the parent's world matrix if exists - const parent = activeObjRef.current.parent; - const targetPosition = new THREE.Vector3(); + // Get the parent's world matrix if exists + const parent = activeObjRef.current.parent; + const targetPosition = new THREE.Vector3(); - // OnPointerDown - initialPositionRef.current.copy(objectWorldPos); + // OnPointerDown + initialPositionRef.current.copy(objectWorldPos); - // OnPointerMove - if (isShiftKeyPressed) { - const { x: initialX, y: initialY } = initialPositionRef.current; - const { x: objectX, z: objectZ } = objectWorldPos; + // OnPointerMove + if (isShiftKeyPressed) { + const { x: initialX, y: initialY } = initialPositionRef.current; + const { x: objectX, z: objectZ } = objectWorldPos; - const deltaX = intersection.x - initialX; + const deltaX = intersection.x - initialX; - targetPosition.set(objectX, initialY + deltaX, objectZ); - } else { - // For free movement - targetPosition.copy(intersection); - } + targetPosition.set(objectX, initialY + deltaX, objectZ); + } else { + // For free movement + targetPosition.copy(intersection); + } - // CONSTRAIN MOVEMENT HERE: - const centerX = selectedArmBot.position[0]; - const centerZ = selectedArmBot.position[2]; - const minDistance = 1.2; // 1.4 radius - const maxDistance = 2; // 2 radius + // CONSTRAIN MOVEMENT HERE: + const centerX = selectedArmBot.position[0]; + const centerZ = selectedArmBot.position[2]; + const minDistance = 1.2; + const maxDistance = 2; - const deltaX = targetPosition.x - centerX; - const deltaZ = targetPosition.z - centerZ; - const distance = Math.sqrt(deltaX * deltaX + deltaZ * deltaZ); + const delta = new THREE.Vector3(targetPosition.x - centerX, 0, targetPosition.z - centerZ); - if (distance < minDistance || distance > maxDistance) { - const angle = Math.atan2(deltaZ, deltaX); - const clampedDistance = Math.min( - Math.max(distance, minDistance), - maxDistance - ); + // Create quaternion from rotation + const robotEuler = new THREE.Euler(selectedArmBot.rotation[0], selectedArmBot.rotation[1], selectedArmBot.rotation[2]); + const robotQuaternion = new THREE.Quaternion().setFromEuler(robotEuler); - targetPosition.x = centerX + Math.cos(angle) * clampedDistance; - targetPosition.z = centerZ + Math.sin(angle) * clampedDistance; - } - targetPosition.y = Math.min(Math.max(targetPosition.y, 0.6), 1.9); - // Convert world position to local if object is nested inside a parent - if (parent) { - parent.worldToLocal(targetPosition); - } + // Inverse rotate + const inverseQuaternion = robotQuaternion.clone().invert(); + delta.applyQuaternion(inverseQuaternion); - // Update object position + // Angle in robot local space + let relativeAngle = Math.atan2(delta.z, delta.x); + let angleDeg = (relativeAngle * 180) / Math.PI; + if (angleDeg < 0) { + angleDeg += 360; + } - activeObjRef.current.position.copy(targetPosition); - } - }; + // Clamp angle + if (angleDeg < 0 || angleDeg > 270) { + const distanceTo90 = Math.abs(angleDeg - 0); + const distanceTo270 = Math.abs(angleDeg - 270); + if (distanceTo90 < distanceTo270) { + angleDeg = 0; + } else { + return; + } + relativeAngle = (angleDeg * Math.PI) / 180; + } - const handlePointerUp = () => { - if (controls) (controls as any).enabled = true; + // Distance clamp + const distance = delta.length(); + const clampedDistance = Math.min(Math.max(distance, minDistance), maxDistance); - if (activeObjRef.current) { - // Pass the updated position to the onUpdate callback to persist it - onUpdate(activeObjRef.current); - } + // Calculate local target + const finalLocal = new THREE.Vector3( + Math.cos(relativeAngle) * clampedDistance, + 0, + Math.sin(relativeAngle) * clampedDistance + ); - gl.domElement.removeEventListener("pointermove", handlePointerMove); - gl.domElement.removeEventListener("pointerup", handlePointerUp); + // Rotate back to world space + finalLocal.applyQuaternion(robotQuaternion); - activeObjRef.current = null; - }; + targetPosition.x = centerX + finalLocal.x; + targetPosition.z = centerZ + finalLocal.z; - return { handlePointerDown }; + + // Clamp Y axis if needed + targetPosition.y = Math.min(Math.max(targetPosition.y, 0.6), 1.9); + + // Convert to local if parent exists + if (parent) { + parent.worldToLocal(targetPosition); + } + + // Update the object position + activeObjRef.current.position.copy(targetPosition); + } + }; + + const handlePointerUp = () => { + if (controls) (controls as any).enabled = true; + + if (activeObjRef.current) { + // Pass the updated position to the onUpdate callback to persist it + onUpdate(activeObjRef.current); + } + + gl.domElement.removeEventListener("pointermove", handlePointerMove); + gl.domElement.removeEventListener("pointerup", handlePointerUp); + + activeObjRef.current = null; + }; + + return { handlePointerDown }; } diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 460997a..e2e236a 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -10,7 +10,7 @@ interface VehicleAnimatorProps { handleCallBack: () => void; reset: () => void; currentPhase: string; - agvUuid: number; + agvUuid: string; agvDetail: VehicleStatus; } diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index d0e2afd..2eb9e6e 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -7,7 +7,7 @@ import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; import MaterialAnimator from '../animator/materialAnimator'; -function VehicleInstance({ agvDetail }: any) { +function VehicleInstance({ agvDetail }: { agvDetail: VehicleStatus }) { const { navMesh } = useNavMesh(); const vehicleRef: any = useRef(); const { isPlaying } = usePlayButtonStore(); @@ -74,28 +74,32 @@ function VehicleInstance({ agvDetail }: any) { if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity && agvDetail.materialType) { - const toDrop = computePath( - agvDetail.point.action.pickUpPoint.position, - agvDetail.point.action.unLoadPoint.position - ); - setPath(toDrop); - setCurrentPhase('pickup-drop'); - setVehicleState(agvDetail.modelUuid, 'running'); - setVehicleActive(agvDetail.modelUuid, true); - vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point'); + if (agvDetail.point.action.pickUpPoint && agvDetail.point.action.unLoadPoint) { + const toDrop = computePath( + agvDetail.point.action.pickUpPoint.position, + agvDetail.point.action.unLoadPoint.position + ); + setPath(toDrop); + setCurrentPhase('pickup-drop'); + setVehicleState(agvDetail.modelUuid, 'running'); + setVehicleActive(agvDetail.modelUuid, true); + vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point'); + } } } else if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'dropping' && agvDetail.currentLoad === 0) { - const dropToPickup = computePath( - agvDetail.point.action.unLoadPoint.position, - agvDetail.point.action.pickUpPoint.position - ); - setPath(dropToPickup); - setCurrentPhase('drop-pickup'); - setVehicleState(agvDetail.modelUuid, 'running'); - setVehicleActive(agvDetail.modelUuid, true); - vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point'); + if (agvDetail.point.action.pickUpPoint && agvDetail.point.action.unLoadPoint) { + const dropToPickup = computePath( + agvDetail.point.action.unLoadPoint.position, + agvDetail.point.action.pickUpPoint.position + ); + setPath(dropToPickup); + setCurrentPhase('drop-pickup'); + setVehicleState(agvDetail.modelUuid, 'running'); + setVehicleActive(agvDetail.modelUuid, true); + vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point'); - isIncrememtable.current = true; + isIncrememtable.current = true; + } } } else { reset() diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx index 68f40cf..0b5a5a5 100644 --- a/app/src/modules/simulation/vehicle/vehicles.tsx +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -1,26 +1,37 @@ -import { useEffect } from "react"; -import VehicleInstances from "./instances/vehicleInstances"; +import { useEffect, useState } from "react"; import { useVehicleStore } from "../../../store/simulation/useVehicleStore"; -import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; -import VehicleUI from "../ui/vehicle/vehicleUI"; +import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import VehicleUI from "../ui/vehicle/vehicleUI"; +import VehicleInstances from "./instances/vehicleInstances"; function Vehicles() { - const { vehicles } = useVehicleStore(); + const { vehicles, getVehicleById } = useVehicleStore(); const { selectedEventSphere } = useSelectedEventSphere(); - const { selectedEventData } = useSelectedEventData(); const { isPlaying } = usePlayButtonStore(); + const [isVehicleSelected, setIsVehicleSelected] = useState(false); useEffect(() => { // console.log('vehicles: ', vehicles); }, [vehicles]) + useEffect(() => { + if (selectedEventSphere) { + const selectedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid); + if (selectedVehicle) { + setIsVehicleSelected(true); + } else { + setIsVehicleSelected(false); + } + } + }, [selectedEventSphere]) + return ( <> - {selectedEventSphere && selectedEventData?.data.type === "vehicle" && !isPlaying && + {isVehicleSelected && !isPlaying && < VehicleUI /> } diff --git a/app/src/store/simulation/useArmBotStore.ts b/app/src/store/simulation/useArmBotStore.ts index 79ad835..bd81e61 100644 --- a/app/src/store/simulation/useArmBotStore.ts +++ b/app/src/store/simulation/useArmBotStore.ts @@ -12,7 +12,7 @@ interface ArmBotStore { ) => void; clearArmBots: () => void; - addCurrentAction: (modelUuid: string, actionUuid: string) => void; + addCurrentAction: (modelUuid: string, actionUuid: string, materialType: string) => void; removeCurrentAction: (modelUuid: string) => void; addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void; @@ -75,7 +75,7 @@ export const useArmBotStore = create()( }); }, - addCurrentAction: (modelUuid, actionUuid) => { + addCurrentAction: (modelUuid, actionUuid, materialType) => { set((state) => { const armBot = state.armBots.find(a => a.modelUuid === modelUuid); if (armBot) { @@ -84,7 +84,7 @@ export const useArmBotStore = create()( armBot.currentAction = { actionUuid: action.actionUuid, actionName: action.actionName, - materialType: null + materialType: materialType }; } } diff --git a/app/src/store/simulation/useConveyorStore.ts b/app/src/store/simulation/useConveyorStore.ts index 862ce79..6d920eb 100644 --- a/app/src/store/simulation/useConveyorStore.ts +++ b/app/src/store/simulation/useConveyorStore.ts @@ -14,6 +14,7 @@ interface ConveyorStore { setConveyorActive: (modelUuid: string, isActive: boolean) => void; setConveyorState: (modelUuid: string, newState: ConveyorStatus['state']) => void; + setConveyorPaused: (modelUuid: string, isPaused: boolean) => void; incrementActiveTime: (modelUuid: string, incrementBy: number) => void; incrementIdleTime: (modelUuid: string, incrementBy: number) => void; @@ -37,6 +38,7 @@ export const useConveyorStore = create()( ...event, productId, isActive: false, + isPaused: false, idleTime: 0, activeTime: 0, state: 'idle', @@ -84,6 +86,15 @@ export const useConveyorStore = create()( }); }, + setConveyorPaused: (modelUuid, isPaused) => { + set((state) => { + const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); + if (conveyor) { + conveyor.isPaused = isPaused; + } + }); + }, + incrementActiveTime: (modelUuid, incrementBy) => { set((state) => { const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); diff --git a/app/src/store/simulation/useMaterialStore.ts b/app/src/store/simulation/useMaterialStore.ts index ec63ff3..2421f53 100644 --- a/app/src/store/simulation/useMaterialStore.ts +++ b/app/src/store/simulation/useMaterialStore.ts @@ -23,20 +23,22 @@ type MaterialsStore = { location?: { modelUuid: string; pointUuid: string; - actionUuid: string; } | null ) => MaterialSchema | undefined; + setMaterial: (materialId: string, materialType: string) => MaterialSchema | undefined; setStartTime: (materialId: string, startTime: string) => MaterialSchema | undefined; setEndTime: (materialId: string, endTime: string) => MaterialSchema | undefined; setCost: (materialId: string, cost: number) => MaterialSchema | undefined; setWeight: (materialId: string, weight: number) => MaterialSchema | undefined; setIsActive: (materialId: string, isActive: boolean) => MaterialSchema | undefined; setIsVisible: (materialId: string, isVisible: boolean) => MaterialSchema | undefined; + setIsPaused: (materialId: string, isPlaying: boolean) => MaterialSchema | undefined; setIsRendered: (materialId: string, isRendered: boolean) => MaterialSchema | undefined; getMaterialById: (materialId: string) => MaterialSchema | undefined; - getMaterialByCurrentModelUuid: (currentModelUuid: string) => MaterialSchema | undefined; + getMaterialsByCurrentModelUuid: (currentModelUuid: string) => MaterialSchema[] | undefined; + getMaterialByCurrentPointUuid: (currentPointUuid: string) => MaterialSchema | undefined; getMaterialsByPoint: (pointUuid: string) => MaterialSchema[]; getMaterialsByModel: (modelUuid: string) => MaterialSchema[]; }; @@ -107,6 +109,18 @@ export const useMaterialStore = create()( return updatedMaterial; }, + setMaterial: (materialId, materialType) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.materialType = materialType; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, + setStartTime: (materialId, startTime) => { let updatedMaterial: MaterialSchema | undefined; set((state) => { @@ -179,6 +193,18 @@ export const useMaterialStore = create()( return updatedMaterial; }, + setIsPaused: (materialId, isPaused) => { + let updatedMaterial: MaterialSchema | undefined; + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + material.isPaused = isPaused; + updatedMaterial = JSON.parse(JSON.stringify(material)); + }; + }); + return updatedMaterial; + }, + setIsRendered: (materialId, isRendered) => { let updatedMaterial: MaterialSchema | undefined; set((state) => { @@ -194,9 +220,13 @@ export const useMaterialStore = create()( getMaterialById: (materialId) => { return get().materials.find(m => m.materialId === materialId); }, - - getMaterialByCurrentModelUuid: (currentModelUuid) => { - return get().materials.find(m => m.current?.modelUuid === currentModelUuid); + + getMaterialsByCurrentModelUuid: (currentModelUuid) => { + return get().materials.filter(m => m.current?.modelUuid === currentModelUuid); + }, + + getMaterialByCurrentPointUuid: (currentPointlUuid) => { + return get().materials.find(m => m.current?.pointUuid === currentPointlUuid); }, getMaterialsByPoint: (pointUuid) => { diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index cdbc26c..b90fd7d 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -65,6 +65,7 @@ type ProductsStore = { getEventByPointUuid: (productId: string, pointUuid: string) => EventsSchema | undefined; getPointByUuid: (productId: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined; getActionByUuid: (productId: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined; + getActionByPointUuid: (productId: string, pointUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined; getModelUuidByPointUuid: (productId: string, actionUuid: string) => (string) | undefined; getModelUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined; getPointUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined; @@ -545,7 +546,7 @@ export const useProductStore = create()( getEventByTriggerUuid: (productId, triggerUuid) => { const product = get().getProductById(productId); if (!product) return undefined; - + for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { @@ -574,7 +575,7 @@ export const useProductStore = create()( getEventByPointUuid: (productId, pointUuid) => { const product = get().getProductById(productId); if (!product) return undefined; - + for (const event of product.eventDatas) { if ('points' in event) { if ((event as ConveyorEventSchema).points.some(p => p.uuid === pointUuid)) { @@ -625,6 +626,27 @@ export const useProductStore = create()( return undefined; }, + getActionByPointUuid: (productId, pointUuid) => { + const product = get().products.find(p => p.productId === productId); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.uuid === pointUuid) { + return point.action; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if (point.uuid === pointUuid) { + return point.action; + } + } + } + return undefined; + }, + getModelUuidByPointUuid: (productId, pointUuid) => { const product = get().products.find(p => p.productId === productId); if (!product) return undefined; diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index f863c1e..754d151 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -125,7 +125,6 @@ interface StorageAction { actionUuid: string; actionName: string; actionType: "store"; - materials: { materialName: string; materialId: string; }[]; storageCapacity: number; triggers: TriggerSchema[]; } @@ -146,6 +145,7 @@ type productsSchema = { interface ConveyorStatus extends ConveyorEventSchema { productId: string; isActive: boolean; + isPaused: boolean; idleTime: number; activeTime: number; } @@ -190,6 +190,7 @@ interface StorageUnitStatus extends StorageEventSchema { idleTime: number; activeTime: number; currentLoad: number; + materials?: { materialName: string; materialId: string; }[]; } interface MaterialSchema { @@ -198,6 +199,7 @@ interface MaterialSchema { materialType: string; isActive: boolean; isVisible: boolean; + isPaused: boolean; isRendered: boolean; startTime?: string; endTime?: string; @@ -213,7 +215,6 @@ interface MaterialSchema { next?: { modelUuid: string; pointUuid: string; - actionUuid: string; }; }