diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx index fd9f418..25a55e5 100644 --- a/app/src/components/ui/simulation/simulationPlayer.tsx +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -38,18 +38,10 @@ 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); - // setSpeed(1); + setReset(!isReset); + setSpeed(1); }; const handlePlayStop = () => { setIsPaused(!isPaused); @@ -279,10 +271,11 @@ const SimulationPlayer: React.FC = () => { {index < intervals.length - 1 && (
= ((index + 1) / totalSegments) * 100 - ? "filled" - : "" - }`} + className={`line ${ + progress >= ((index + 1) / totalSegments) * 100 + ? "filled" + : "" + }`} >
)} diff --git a/app/src/modules/collaboration/functions/setCameraView.ts b/app/src/modules/collaboration/functions/setCameraView.ts index 8bae59b..456f622 100644 --- a/app/src/modules/collaboration/functions/setCameraView.ts +++ b/app/src/modules/collaboration/functions/setCameraView.ts @@ -28,6 +28,4 @@ export default async function setCameraView({ controls?.setLookAt(...newPosition.toArray(), newPosition.x, 0, newPosition.z, true); } - // Optionally you can log - console.log(`Camera view updated by ${username ?? 'unknown user'}`); } diff --git a/app/src/modules/simulation/actions/conveyor/actionHandler/spawnActionHandler.ts b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts similarity index 82% rename from app/src/modules/simulation/actions/conveyor/actionHandler/spawnActionHandler.ts rename to app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts index c28f1d0..776b539 100644 --- a/app/src/modules/simulation/actions/conveyor/actionHandler/spawnActionHandler.ts +++ b/app/src/modules/simulation/actions/conveyor/actionHandler/useSpawnHandler.ts @@ -7,7 +7,7 @@ import { useSelectedProduct } from "../../../../../store/simulation/useSimulatio export function useSpawnHandler() { const { addMaterial } = useMaterialStore(); - const { getModelUuidByActionUuid } = useProductStore(); + const { getModelUuidByActionUuid, getPointUuidByActionUuid } = useProductStore(); const { selectedProduct } = useSelectedProduct(); const lastSpawnTime = useRef(null); const startTime = useRef(null); @@ -16,7 +16,7 @@ export function useSpawnHandler() { material: string; intervalMs: number; totalCount: number; - point: ConveyorPointSchema; + action: ConveyorAction; } | null>(null); const clearCurrentSpawn = useCallback(() => { @@ -27,12 +27,13 @@ export function useSpawnHandler() { }, []); const spawnLogStatus = (materialUuid: string, status: string) => { - // console.log(`${materialUuid}, ${status}`); + console.log(`${materialUuid}, ${status}`); } - const createNewMaterial = useCallback((materialType: string, point: ConveyorPointSchema) => { - const modelUuid = getModelUuidByActionUuid(selectedProduct.productId, point.action.actionUuid); - if (!modelUuid || !point.uuid) return; + const createNewMaterial = useCallback((materialType: string, action: ConveyorAction) => { + const modelUuid = getModelUuidByActionUuid(selectedProduct.productId, action.actionUuid); + const pointUuid = getPointUuidByActionUuid(selectedProduct.productId, action.actionUuid); + if (!modelUuid || !pointUuid) return; const newMaterial: MaterialSchema = { materialId: THREE.MathUtils.generateUUID(), @@ -43,8 +44,8 @@ export function useSpawnHandler() { isRendered: true, current: { modelUuid: modelUuid, - pointUuid: point.uuid, - actionUuid: point.action?.actionUuid || '' + pointUuid: pointUuid, + actionUuid: action?.actionUuid || '' }, weight: 1, cost: 1 @@ -58,14 +59,14 @@ export function useSpawnHandler() { if (!spawnParams.current || !startTime.current) return; const currentTime = performance.now(); - const { material, intervalMs, totalCount, point } = spawnParams.current; + const { material, intervalMs, totalCount, action } = spawnParams.current; const isFirstSpawn = lastSpawnTime.current === null; const elapsed = currentTime - startTime.current; // First spawn if (isFirstSpawn) { if (elapsed >= intervalMs) { - const createdMaterial = createNewMaterial(material, point); + const createdMaterial = createNewMaterial(material, action); if (createdMaterial) { spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`); } @@ -84,9 +85,9 @@ export function useSpawnHandler() { const timeSinceLast = currentTime - lastSpawnTime.current; if (timeSinceLast >= intervalMs) { const count = spawnCountRef.current + 1; - const createdMaterial = createNewMaterial(material, point); + const createdMaterial = createNewMaterial(material, action); if (createdMaterial) { - spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (1/${totalCount})`); + spawnLogStatus(createdMaterial.materialId, `[${elapsed.toFixed(2)}ms] Spawned ${material} (${count}/${totalCount})`); } lastSpawnTime.current = currentTime; spawnCountRef.current = count; @@ -98,10 +99,10 @@ export function useSpawnHandler() { } }); - const handleSpawn = useCallback((point: ConveyorPointSchema) => { - if (!point.action || point.action.actionType !== 'spawn') return; + const handleSpawn = useCallback((action: ConveyorAction) => { + if (!action || action.actionType !== 'spawn') return; - const { material, spawnInterval = 0, spawnCount = 1 } = point.action; + const { material, spawnInterval = 0, spawnCount = 1 } = action; const intervalMs = spawnInterval * 1000; clearCurrentSpawn(); @@ -110,7 +111,7 @@ export function useSpawnHandler() { material, intervalMs, totalCount: spawnCount, - point: point + action: action }; startTime.current = performance.now(); diff --git a/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts index e10e115..a61e3c4 100644 --- a/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts +++ b/app/src/modules/simulation/actions/conveyor/useConveyorActions.ts @@ -1,40 +1,66 @@ -import { useEffect } from "react"; -import { useSpawnHandler } from "./actionHandler/spawnActionHandler"; +import { useEffect, useCallback, useRef } from "react"; +import { useSpawnHandler } from "./actionHandler/useSpawnHandler"; -// Conveyor Actions -export function useConveyorActions(point: ConveyorPointSchema) { - const { handleSpawn } = useSpawnHandler(); +export function useConveyorActions() { + const { handleSpawn, clearCurrentSpawn } = useSpawnHandler(); + + const handleDefaultAction = useCallback((action: ConveyorAction) => { + 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 handleDelayAction = useCallback((action: ConveyorAction) => { + const delayMs = (action.delay || 0) * 1000; + console.log(`Delaying for ${delayMs}ms`); + }, []); + + const handleDespawnAction = useCallback((action: ConveyorAction) => { + console.log(`Despawning material`); + }, []); + + const handleConveyorAction = useCallback((action: ConveyorAction) => { + if (!action) return; + + switch (action.actionType) { + case 'default': + handleDefaultAction(action); + break; + case 'spawn': + handleSpawnAction(action); + break; + case 'swap': + handleSwapAction(action); + break; + case 'delay': + handleDelayAction(action); + break; + case 'despawn': + handleDespawnAction(action); + break; + default: + console.warn(`Unknown conveyor action type: ${action.actionType}`); + } + }, [handleDefaultAction, handleSpawnAction, handleSwapAction, handleDelayAction, handleDespawnAction]); + + const cleanup = useCallback(() => { + clearCurrentSpawn(); + }, [clearCurrentSpawn]); useEffect(() => { - if (!point.action) return; - - const { actionType, material, delay, spawnInterval, spawnCount } = point.action; - - const handleAction = () => { - switch (actionType) { - case 'default': - console.log(`Default conveyor action at point ${point.uuid}`); - break; - case 'spawn': - console.log(`Spawning material ${material} at point ${point.uuid}`); - handleSpawn(point); - break; - case 'swap': - console.log(`Swapping to material ${material} at point ${point.uuid}`); - break; - case 'delay': - console.log(`Delaying for ${delay}ms at point ${point.uuid}`); - break; - case 'despawn': - console.log(`Despawning material at point ${point.uuid}`); - break; - } - }; - - handleAction(); - return () => { - // Cleanup if needed + cleanup(); }; - }, [point]); + }, [cleanup]); + + return { + handleConveyorAction, + cleanup + }; } \ No newline at end of file diff --git a/app/src/modules/simulation/actions/machine/useMachineActions.ts b/app/src/modules/simulation/actions/machine/useMachineActions.ts index 0ac9865..38e8ce3 100644 --- a/app/src/modules/simulation/actions/machine/useMachineActions.ts +++ b/app/src/modules/simulation/actions/machine/useMachineActions.ts @@ -1,23 +1,35 @@ -import { useEffect } from 'react'; +import { useEffect, useCallback, useRef, useState } from 'react'; + +export function useMachineActions() { + + const handleProcessAction = useCallback((action: MachineAction) => { + if (!action || action.actionType !== 'process') return; + console.log(`Machine processing for ${action.processTime}ms`); + }, []); + + const handleMachineAction = useCallback((action: MachineAction) => { + if (!action) return; + + switch (action.actionType) { + case 'process': + handleProcessAction(action); + break; + default: + console.warn(`Unknown machine action type: ${action.actionType}`); + } + }, [handleProcessAction]); + + const cleanup = useCallback(() => { + }, []); -// Machine Actions -export function useMachineActions(point: MachinePointSchema) { useEffect(() => { - if (!point.action) return; - - const { actionType, processTime, swapMaterial } = point.action; - - const handleAction = () => { - if (actionType === 'process') { - console.log(`Machine processing for ${processTime}ms at point ${point.uuid}`); - if (swapMaterial) { - console.log(`Swapping material to ${swapMaterial}`); - } - } - }; - return () => { - // Cleanup if needed + cleanup(); }; - }, [point]); + }, [cleanup]); + + return { + handleMachineAction, + cleanup, + }; } \ No newline at end of file diff --git a/app/src/modules/simulation/actions/roboticArm/useRoboticArmActions.ts b/app/src/modules/simulation/actions/roboticArm/useRoboticArmActions.ts index 50a6196..37990db 100644 --- a/app/src/modules/simulation/actions/roboticArm/useRoboticArmActions.ts +++ b/app/src/modules/simulation/actions/roboticArm/useRoboticArmActions.ts @@ -1,26 +1,32 @@ -import { useEffect } from 'react'; +import { useEffect, useCallback } from 'react'; + +export function useRoboticArmActions() { + + const handlePickAndPlace = useCallback((action: RoboticArmAction) => { + console.log(`Robotic arm pick and place`); + }, []); + + const handleRoboticArmAction = useCallback((action: RoboticArmAction) => { + switch (action.actionType) { + case 'pickAndPlace': + handlePickAndPlace(action); + break; + default: + console.warn(`Unknown robotic arm action type: ${action.actionType}`); + } + }, [handlePickAndPlace]); + + const cleanup = useCallback(() => { + }, []); -// Robotic Arm Actions -export function useRoboticArmActions(point: RoboticArmPointSchema) { useEffect(() => { - point.actions.forEach(action => { - const { actionType, process } = action; - - const handleAction = () => { - if (actionType === 'pickAndPlace') { - console.log(`Robotic arm pick and place at point ${point.uuid}`); - if (process.startPoint) { - console.log(`Start point: ${process.startPoint}`); - } - if (process.endPoint) { - console.log(`End point: ${process.endPoint}`); - } - } - }; - }); - return () => { - // Cleanup if needed + cleanup(); }; - }, [point]); + }, [cleanup]); + + return { + handleRoboticArmAction, + cleanup + }; } \ No newline at end of file diff --git a/app/src/modules/simulation/actions/storageUnit/useStorageUnitActions.ts b/app/src/modules/simulation/actions/storageUnit/useStorageUnitActions.ts index 6ffa8ad..bc1ab94 100644 --- a/app/src/modules/simulation/actions/storageUnit/useStorageUnitActions.ts +++ b/app/src/modules/simulation/actions/storageUnit/useStorageUnitActions.ts @@ -1,22 +1,33 @@ -import { useEffect } from 'react'; +import { useEffect, useCallback, useRef, useState } from 'react'; + +export function useStorageActions() { + const handleStoreAction = useCallback((action: StorageAction) => { + if (!action || action.actionType !== 'store') return; + }, []); + + const handleStorageAction = useCallback((action: StorageAction) => { + if (!action) return; + + switch (action.actionType) { + case 'store': + handleStoreAction(action); + break; + default: + console.warn(`Unknown storage action type: ${action.actionType}`); + } + }, [handleStoreAction]); + + const cleanup = useCallback(() => { + }, []); -// Storage Actions -export function useStorageActions(point: StoragePointSchema) { useEffect(() => { - if (!point.action) return; - - const { actionType, materials, storageCapacity } = point.action; - - const handleAction = () => { - if (actionType === 'store') { - console.log(`Storage action at point ${point.uuid}`); - console.log(`Materials: ${materials.map(m => m.materialName).join(', ')}`); - console.log(`Capacity: ${storageCapacity}`); - } - }; - return () => { - // Cleanup if needed + cleanup(); }; - }, [point]); + }, [cleanup]); + + return { + handleStorageAction, + cleanup + }; } \ No newline at end of file diff --git a/app/src/modules/simulation/actions/useActionHandler.ts b/app/src/modules/simulation/actions/useActionHandler.ts index f49381f..dbffd69 100644 --- a/app/src/modules/simulation/actions/useActionHandler.ts +++ b/app/src/modules/simulation/actions/useActionHandler.ts @@ -3,23 +3,76 @@ import { useMachineActions } from "./machine/useMachineActions"; import { useRoboticArmActions } from "./roboticArm/useRoboticArmActions"; import { useStorageActions } from "./storageUnit/useStorageUnitActions"; import { useVehicleActions } from "./vehicle/useVehicleActions"; +import { useCallback, useEffect } from "react"; -// Master hook that selects the appropriate action handler -export function useActionHandler(point: PointsScheme) { - // if ('actions' in point) { - // // Robotic Arm - // useRoboticArmActions(point); - // } else if (point.action.actionType === 'travel') { - // // Vehicle - // useVehicleActions(point as VehiclePointSchema); - // } else if (point.action.actionType === 'process') { - // // Machine - // useMachineActions(point as MachinePointSchema); - // } else if (point.action.actionType === 'store') { - // // Storage - // useStorageActions(point as StoragePointSchema); - // } else { - // // Conveyor - // useConveyorActions(point as ConveyorPointSchema); - // } +export function useActionHandler() { + // Initialize all action handlers + const { handleConveyorAction, cleanup: cleanupConveyor } = useConveyorActions(); + const { handleVehicleAction, cleanup: cleanupVehicle } = useVehicleActions(); + const { handleRoboticArmAction, cleanup: cleanupRoboticArm } = useRoboticArmActions(); + const { handleMachineAction, cleanup: cleanupMachine } = useMachineActions(); + const { handleStorageAction, cleanup: cleanupStorage } = useStorageActions(); + + // Main handler function + const handleAction = useCallback((action: Action) => { + if (!action) return; + + try { + switch (action.actionType) { + case 'default': case 'spawn': case 'swap': case 'delay': case 'despawn': + handleConveyorAction(action as ConveyorAction); + break; + case 'travel': + handleVehicleAction(action as VehicleAction); + break; + case 'pickAndPlace': + handleRoboticArmAction(action as RoboticArmAction); + break; + case 'process': + handleMachineAction(action as MachineAction); + break; + case 'store': + handleStorageAction(action as StorageAction); + break; + default: + console.warn(`Unknown action type: ${(action as Action).actionType}`); + } + } catch (error) { + console.error('Error handling action:', error); + // Consider adding error recovery or notification here + } + }, [ + handleConveyorAction, + handleVehicleAction, + handleRoboticArmAction, + handleMachineAction, + handleStorageAction + ]); + + // Cleanup all actions + const cleanup = useCallback(() => { + cleanupConveyor(); + cleanupVehicle(); + cleanupRoboticArm(); + cleanupMachine(); + cleanupStorage(); + }, [ + cleanupConveyor, + cleanupVehicle, + cleanupRoboticArm, + cleanupMachine, + cleanupStorage + ]); + + // Auto cleanup on unmount + useEffect(() => { + return () => { + cleanup(); + }; + }, [cleanup]); + + return { + handleAction, + cleanup + }; } \ No newline at end of file diff --git a/app/src/modules/simulation/actions/vehicle/useVehicleActions.ts b/app/src/modules/simulation/actions/vehicle/useVehicleActions.ts index 7a6ce4d..313fb5d 100644 --- a/app/src/modules/simulation/actions/vehicle/useVehicleActions.ts +++ b/app/src/modules/simulation/actions/vehicle/useVehicleActions.ts @@ -1,26 +1,37 @@ -import { useEffect } from 'react'; +import { useEffect, useCallback } from 'react'; + +export function useVehicleActions() { + + const handleTravelAction = useCallback((action: VehicleAction) => { + if (!action || action.actionType !== 'travel') return; + + console.log(`Vehicle travel action ${action.actionUuid}`); + + }, []); + + const handleVehicleAction = useCallback((action: VehicleAction) => { + if (!action) return; + + switch (action.actionType) { + case 'travel': + handleTravelAction(action); + break; + default: + console.warn(`Unknown vehicle action type: ${action.actionType}`); + } + }, [handleTravelAction]); + + const cleanup = useCallback(() => { + }, []); -// Vehicle Actions -export function useVehicleActions(point: VehiclePointSchema) { useEffect(() => { - if (!point.action) return; - - const { actionType, unLoadDuration, loadCapacity, steeringAngle } = point.action; - - const handleAction = () => { - if (actionType === 'travel') { - console.log(`Vehicle travel action at point ${point.uuid}`); - if (point.action.pickUpPoint) { - console.log(`Pick up at: ${JSON.stringify(point.action.pickUpPoint)}`); - } - if (point.action.unLoadPoint) { - console.log(`Unload at: ${JSON.stringify(point.action.unLoadPoint)}`); - } - } - }; - return () => { - // Cleanup if needed + cleanup(); }; - }, [point]); + }, [cleanup]); + + return { + handleVehicleAction, + cleanup + }; } \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index ceccd40..fd649c6 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -3,240 +3,260 @@ import { useFrame } from '@react-three/fiber'; import * as THREE from 'three'; import { Line } from '@react-three/drei'; import { - useAnimationPlaySpeed, - usePauseButtonStore, - usePlayButtonStore, - useResetButtonStore + useAnimationPlaySpeed, + usePauseButtonStore, + usePlayButtonStore, + useResetButtonStore } from '../../../../../store/usePlayButtonStore'; function RoboticArmAnimator({ - HandleCallback, - restPosition, - ikSolver, - targetBone, - armBot, - logStatus, - path + HandleCallback, + restPosition, + ikSolver, + targetBone, + armBot, + logStatus, + 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([]); - // Zustand stores - const { isPlaying } = usePlayButtonStore(); - const { isPaused } = usePauseButtonStore(); - const { isReset, setReset } = useResetButtonStore(); - const { speed } = useAnimationPlaySpeed(); + 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([]); + // Zustand stores + const { isPlaying } = usePlayButtonStore(); + const { isPaused } = usePauseButtonStore(); + const { isReset, setReset } = useResetButtonStore(); + const { speed } = useAnimationPlaySpeed(); - // Update path state whenever `path` prop changes - useEffect(() => { - setCurrentPath(path); - }, [path]); + // Update path state whenever `path` prop changes + useEffect(() => { + setCurrentPath(path); + }, [path]); - // Handle circle points based on armBot position - useEffect(() => { - const points = generateRingPoints(1.6, 64) - setCirclePoints(points); - }, [armBot.position]); + // Handle circle points based on armBot position + useEffect(() => { + const points = generateRingPoints(1.6, 64) + setCirclePoints(points); + }, [armBot.position]); - useEffect(() => { - if (isReset || !isPlaying) { - progressRef.current = 0; - curveRef.current = null; - setCurrentPath([]); - setCustomCurvePoints(null); - totalDistanceRef.current = 0; - startTimeRef.current = null; - segmentDistancesRef.current = []; - setReset(false); + //Handle Reset Animation + useEffect(() => { + if (isReset ) { + progressRef.current = 0; + curveRef.current = null; + setCurrentPath([]); + setCustomCurvePoints(null); + totalDistanceRef.current = 0; + startTimeRef.current = null; + segmentDistancesRef.current = []; + if (!ikSolver) return + const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); + if (!bone) return; + bone.position.copy(restPosition) + ikSolver.update(); + } + }, [isReset, isPlaying]) + + //Generate Circle Points + function generateRingPoints(radius: any, segments: any) { + const points: [number, number, number][] = []; + for (let i = 0; i < segments; i++) { + // Calculate angle for current segment + const angle = (i / segments) * Math.PI * 2; + // Calculate x and z coordinates (y remains the same for a flat ring) + const x = Math.cos(angle) * radius; + const z = Math.sin(angle) * radius; + points.push([x, 1.5, z]); + } + return points; } - }, [isReset, isPlaying]) + // 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++) { + const [x, y, z] = points[i]; + if ( + Math.abs(x - nearestPoint[0]) < epsilon && + Math.abs(y - nearestPoint[1]) < epsilon && + Math.abs(z - nearestPoint[2]) < epsilon + ) { + return i; // Found the matching index + } + } + return -1; // Not found + }; - - function generateRingPoints(radius: any, segments: any) { - const points: [number, number, number][] = []; - for (let i = 0; i < segments; i++) { - // Calculate angle for current segment - const angle = (i / segments) * Math.PI * 2; - // Calculate x and z coordinates (y remains the same for a flat ring) - const x = Math.cos(angle) * radius; - const z = Math.sin(angle) * radius; - points.push([x, 1.5, z]); - } - return points; - } - - const findNearestIndex = (nearestPoint: [number, number, number], points: [number, number, number][], epsilon = 1e-6) => { - for (let i = 0; i < points.length; i++) { - const [x, y, z] = points[i]; - if ( - Math.abs(x - nearestPoint[0]) < epsilon && - Math.abs(y - nearestPoint[1]) < epsilon && - Math.abs(z - nearestPoint[2]) < epsilon - ) { - return i; // Found the matching index - } - } - return -1; // Not found - }; - - // Handle nearest points and final path (including arc points) - useEffect(() => { - if (circlePoints.length > 0 && currentPath.length > 0) { - - const start = currentPath[0]; - const end = currentPath[currentPath.length - 1]; - - const raisedStart = [start[0], start[1] + 0.5, start[2]] as [number, number, number]; - const raisedEnd = [end[0], end[1] + 0.5, end[2]] as [number, number, number]; - - const findNearest = (target: [number, number, number]) => { + //function to find nearest Circlepoints + const findNearest = (target: [number, number, number]) => { return circlePoints.reduce((nearest, point) => { - const distance = Math.hypot(target[0] - point[0], target[1] - point[1], target[2] - point[2]); - const nearestDistance = Math.hypot(target[0] - nearest[0], target[1] - nearest[1], target[2] - nearest[2]); - return distance < nearestDistance ? point : nearest; + const distance = Math.hypot(target[0] - point[0], target[1] - point[1], target[2] - point[2]); + const nearestDistance = Math.hypot(target[0] - nearest[0], target[1] - nearest[1], target[2] - nearest[2]); + return distance < nearestDistance ? point : nearest; }, circlePoints[0]); - }; + }; + + // Handle nearest points and final path (including arc points) + useEffect(() => { + if (circlePoints.length > 0 && currentPath.length > 0) { - const nearestToStart = findNearest(raisedStart); - const nearestToEnd = findNearest(raisedEnd); + const start = currentPath[0]; + const end = currentPath[currentPath.length - 1]; - const indexOfNearestStart = findNearestIndex(nearestToStart, circlePoints); - const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints); + const raisedStart = [start[0], start[1] + 0.5, start[2]] as [number, number, number]; + const raisedEnd = [end[0], end[1] + 0.5, end[2]] as [number, number, number]; - const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + 64) % 64; - const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + 64) % 64; - const clockwiseIsShorter = clockwiseDistance <= counterClockwiseDistance; + // const findNearest = (target: [number, number, number]) => { + // return circlePoints.reduce((nearest, point) => { + // const distance = Math.hypot(target[0] - point[0], target[1] - point[1], target[2] - point[2]); + // const nearestDistance = Math.hypot(target[0] - nearest[0], target[1] - nearest[1], target[2] - nearest[2]); + // return distance < nearestDistance ? point : nearest; + // }, circlePoints[0]); + // }; - let arcPoints: [number, number, number][] = []; + const nearestToStart = findNearest(raisedStart); + const nearestToEnd = findNearest(raisedEnd); + + 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; + + 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) + ]; + } + } 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]); + } + } + } + + const pathVectors = [ + new THREE.Vector3(start[0], start[1], start[2]), + new THREE.Vector3(start[0], curveHeight, start[2]), + new THREE.Vector3(nearestToStart[0], curveHeight, nearestToStart[2]), + ...arcPoints.map(point => new THREE.Vector3(point[0], curveHeight, point[2])), + new THREE.Vector3(nearestToEnd[0], curveHeight, nearestToEnd[2]), + new THREE.Vector3(end[0], curveHeight, end[2]), + 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]]); + } + + const segmentDistances = pathSegments.map(([p1, p2]) => p1.distanceTo(p2)); + segmentDistancesRef.current = segmentDistances; + 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); - if (clockwiseIsShorter) { - if (indexOfNearestStart <= indexOfNearestEnd) { - arcPoints = circlePoints.slice(indexOfNearestStart, indexOfNearestEnd + 1); - } else { - arcPoints = [ - ...circlePoints.slice(indexOfNearestStart, 64), - ...circlePoints.slice(0, indexOfNearestEnd + 1) - ]; } - } 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]); - } + }, [circlePoints, currentPath]); + + // Frame update for animation + useFrame((state, delta) => { + if (!ikSolver) return; + + const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); + if (!bone) return; + + if (isPlaying) { + if (isReset) { + bone.position.copy(restPosition); + setCustomCurvePoints([]); + ikSolver.update(); + } + if (!isPaused && customCurvePoints && customCurvePoints.length > 0) { + const distances = segmentDistancesRef.current; + const totalDistance = totalDistanceRef.current; + + progressRef.current += delta * (speed * armBot.speed); + const coveredDistance = progressRef.current; + + let index = 0; + let accumulatedDistance = 0; + + while (index < distances.length && coveredDistance > accumulatedDistance + distances[index]) { + accumulatedDistance += distances[index]; + index++; + } + + if (index < distances.length) { + const startPoint = customCurvePoints[index]; + const endPoint = customCurvePoints[index + 1]; + const segmentDistance = distances[index]; + const t = (coveredDistance - accumulatedDistance) / segmentDistance; + if (startPoint && endPoint) { + const position = startPoint.clone().lerp(endPoint, t); + bone.position.copy(position); + } + } + + if (progressRef.current >= totalDistance) { + HandleCallback(); + setCurrentPath([]); + setCustomCurvePoints([]); + curveRef.current = null; + progressRef.current = 0; + startTimeRef.current = null; + } + + ikSolver.update(); + } + } else if (!isPlaying && currentPath.length === 0) { + bone.position.copy(restPosition); } - } - - const pathVectors = [ - new THREE.Vector3(start[0], start[1], start[2]), - new THREE.Vector3(start[0], curveHeight, start[2]), - new THREE.Vector3(nearestToStart[0], curveHeight, nearestToStart[2]), - ...arcPoints.map(point => new THREE.Vector3(point[0], curveHeight, point[2])), - new THREE.Vector3(nearestToEnd[0], curveHeight, nearestToEnd[2]), - new THREE.Vector3(end[0], curveHeight, end[2]), - 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]]); - } - - const segmentDistances = pathSegments.map(([p1, p2]) => p1.distanceTo(p2)); - segmentDistancesRef.current = segmentDistances; - 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]); - - // Frame update for animation - useFrame((state, delta) => { - if (!ikSolver) return; - - const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); - if (!bone) return; - - if (isPlaying) { - if (!isPaused && customCurvePoints && customCurvePoints.length > 0) { - const distances = segmentDistancesRef.current; // distances between each pair of points - const totalDistance = totalDistanceRef.current; - - progressRef.current += delta * (speed * armBot.speed); - const coveredDistance = progressRef.current; - - let index = 0; - let accumulatedDistance = 0; - - // Find which segment we are currently in - while (index < distances.length && coveredDistance > accumulatedDistance + distances[index]) { - accumulatedDistance += distances[index]; - index++; - } - if (index < distances.length) { - const startPoint = customCurvePoints[index]; - const endPoint = customCurvePoints[index + 1]; - const segmentDistance = distances[index]; - const t = (coveredDistance - accumulatedDistance) / segmentDistance; - if (startPoint && endPoint) { - const position = startPoint.clone().lerp(endPoint, t); - bone.position.copy(position); - } - } - if (progressRef.current >= totalDistance) { - HandleCallback(); - setCurrentPath([]); - setCustomCurvePoints([]); - curveRef.current = null; - progressRef.current = 0; - startTimeRef.current = null; - } - ikSolver.update(); - } - } else if ((!isPlaying && currentPath.length === 0) || isReset) { - bone.position.copy(restPosition); - ikSolver.update(); - } + }); - }); - return ( - <> - {customCurvePoints && customCurvePoints?.length >= 2 && currentPath && isPlaying && ( - - [p.x, p.y, p.z] as [number, number, number])} - color="green" - lineWidth={5} - dashed={false} - /> - - )} - - - - - - ); + return ( + <> + {customCurvePoints && customCurvePoints?.length >= 2 && currentPath && isPlaying && ( + + [p.x, p.y, p.z] as [number, number, number])} + color="green" + lineWidth={5} + dashed={false} + /> + + )} + + + + + + ); } export default RoboticArmAnimator; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 60d4c33..c67d6f6 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -97,24 +97,24 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { useEffect(() => { if (isReset || !isPlaying) { logStatus(armBot.modelUuid, "Simulation Play Reset Successfully") - removeCurrentAction(armBot.modelUuid) setArmBotActive(armBot.modelUuid, false) setArmBotState(armBot.modelUuid, "idle") setCurrentPhase("init"); + setPath([]) + removeCurrentAction(armBot.modelUuid) isPausedRef.current = false pauseTimeRef.current = null startTime = 0 - const targetBones = ikSolver?.mesh.skeleton.bones.find( - (b: any) => b.name === targetBone + const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone ); - if (targetBones) { + if (targetBones && isPlaying) { let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition) if (curve) { setPath(curve.points.map(point => [point.x, point.y, point.z])); + logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.") } } - setReset(false); - logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.") + // setReset(false); } }, [isReset, isPlaying]) @@ -127,7 +127,6 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { (b: any) => b.name === targetBone ); if (isPlaying) { - //Moving armBot from initial point to rest position. if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") { @@ -204,16 +203,16 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { // logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.") } } else { - logStatus(armBot.modelUuid, "Simulation Play Exited") - setArmBotActive(armBot.modelUuid, false) - setArmBotState(armBot.modelUuid, "idle") - setCurrentPhase("init"); - setPath([]) - isPausedRef.current = false - pauseTimeRef.current = null - isPausedRef.current = false - startTime = 0 - removeCurrentAction(armBot.modelUuid) + // logStatus(armBot.modelUuid, "Simulation Play Exited") + // setArmBotActive(armBot.modelUuid, false) + // setArmBotState(armBot.modelUuid, "idle") + // setCurrentPhase("init"); + // setPath([]) + // isPausedRef.current = false + // pauseTimeRef.current = null + // isPausedRef.current = false + // startTime = 0 + // removeCurrentAction(armBot.modelUuid) } }, [currentPhase, armBot, isPlaying, ikSolver]) @@ -260,6 +259,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } const logStatus = (id: string, status: string) => { + // } diff --git a/app/src/modules/simulation/simulator/simulator.tsx b/app/src/modules/simulation/simulator/simulator.tsx index b62e75a..320760f 100644 --- a/app/src/modules/simulation/simulator/simulator.tsx +++ b/app/src/modules/simulation/simulator/simulator.tsx @@ -1,114 +1,21 @@ +import { useEffect } from 'react'; import { useProductStore } from '../../../store/simulation/useProductStore'; import { useActionHandler } from '../actions/useActionHandler'; function Simulator() { const { products } = useProductStore(); + const { handleAction } = useActionHandler(); - const executionOrder = determineExecutionOrder(products); - - executionOrder.forEach(point => { - // useActionHandler(point); - }); - - function determineExecutionSequences(products: productsSchema): PointsScheme[][] { - // Create maps for all points - const pointMap = new Map(); - const allPoints: PointsScheme[] = []; - - // First pass: collect all points - products.forEach(product => { - product.eventDatas.forEach(event => { - if (event.type === 'transfer') { - event.points.forEach(point => { - pointMap.set(point.uuid, point); - allPoints.push(point); - }); - } else if (event.type === 'vehicle' || - event.type === 'machine' || - event.type === 'storageUnit' || - event.type === 'roboticArm') { - pointMap.set(event.point.uuid, event.point); - allPoints.push(event.point); - } - }); - }); - - // Build complete dependency graph - const dependencyGraph = new Map(); - const reverseDependencyGraph = new Map(); - const triggeredPoints = new Set(); - - allPoints.forEach(point => { - const triggers = extractTriggersFromPoint(point); - const dependencies: string[] = []; - - triggers.forEach(trigger => { - const targetUuid = trigger.triggeredAsset?.triggeredPoint?.pointUuid; - if (targetUuid && pointMap.has(targetUuid)) { - dependencies.push(targetUuid); - triggeredPoints.add(targetUuid); - - if (!reverseDependencyGraph.has(targetUuid)) { - reverseDependencyGraph.set(targetUuid, []); - } - reverseDependencyGraph.get(targetUuid)!.push(point.uuid); - } - }); - - dependencyGraph.set(point.uuid, dependencies); - }); - - // Identify independent root points (points that trigger others but aren't triggered themselves) - const rootPoints = allPoints.filter(point => { - const hasOutgoingTriggers = extractTriggersFromPoint(point).some( - t => t.triggeredAsset?.triggeredPoint?.pointUuid - ); - return hasOutgoingTriggers && !triggeredPoints.has(point.uuid); - }); - - // For each root point, build its complete trigger chain - const executionSequences: PointsScheme[][] = []; - - function buildSequence(startUuid: string): PointsScheme[] { - const sequence: PointsScheme[] = []; - const visited = new Set(); - - function traverse(uuid: string) { - if (visited.has(uuid)) return; - visited.add(uuid); - - const point = pointMap.get(uuid); - if (point) { - sequence.push(point); - } - - // Follow forward dependencies - const nextPoints = dependencyGraph.get(uuid) || []; - nextPoints.forEach(nextUuid => traverse(nextUuid)); - } - - traverse(startUuid); - return sequence; - } - - // Build sequences for all root points - rootPoints.forEach(root => { - executionSequences.push(buildSequence(root.uuid)); - }); - - // Handle any triggered points not reachable from roots (isolated chains) - const processedPoints = new Set( - executionSequences.flat().map(p => p.uuid) - ); - - allPoints.forEach(point => { - if (triggeredPoints.has(point.uuid) && !processedPoints.has(point.uuid)) { - executionSequences.push(buildSequence(point.uuid)); + useEffect(() => { + const executionOrder = determineExecutionOrder(products); + executionOrder.forEach(point => { + if ('actions' in point) { + handleAction(point.actions[0]); + } else { + handleAction(point.action); } }); - - return executionSequences; - } + }, [products, handleAction]); function determineExecutionOrder(products: productsSchema): PointsScheme[] { // Create maps for all events and points @@ -223,6 +130,106 @@ function Simulator() { return triggeredPoints; } + function determineExecutionSequences(products: productsSchema): PointsScheme[][] { + // Create maps for all points + const pointMap = new Map(); + const allPoints: PointsScheme[] = []; + + // First pass: collect all points + products.forEach(product => { + product.eventDatas.forEach(event => { + if (event.type === 'transfer') { + event.points.forEach(point => { + pointMap.set(point.uuid, point); + allPoints.push(point); + }); + } else if (event.type === 'vehicle' || + event.type === 'machine' || + event.type === 'storageUnit' || + event.type === 'roboticArm') { + pointMap.set(event.point.uuid, event.point); + allPoints.push(event.point); + } + }); + }); + + // Build complete dependency graph + const dependencyGraph = new Map(); + const reverseDependencyGraph = new Map(); + const triggeredPoints = new Set(); + + allPoints.forEach(point => { + const triggers = extractTriggersFromPoint(point); + const dependencies: string[] = []; + + triggers.forEach(trigger => { + const targetUuid = trigger.triggeredAsset?.triggeredPoint?.pointUuid; + if (targetUuid && pointMap.has(targetUuid)) { + dependencies.push(targetUuid); + triggeredPoints.add(targetUuid); + + if (!reverseDependencyGraph.has(targetUuid)) { + reverseDependencyGraph.set(targetUuid, []); + } + reverseDependencyGraph.get(targetUuid)!.push(point.uuid); + } + }); + + dependencyGraph.set(point.uuid, dependencies); + }); + + // Identify independent root points (points that trigger others but aren't triggered themselves) + const rootPoints = allPoints.filter(point => { + const hasOutgoingTriggers = extractTriggersFromPoint(point).some( + t => t.triggeredAsset?.triggeredPoint?.pointUuid + ); + return hasOutgoingTriggers && !triggeredPoints.has(point.uuid); + }); + + // For each root point, build its complete trigger chain + const executionSequences: PointsScheme[][] = []; + + function buildSequence(startUuid: string): PointsScheme[] { + const sequence: PointsScheme[] = []; + const visited = new Set(); + + function traverse(uuid: string) { + if (visited.has(uuid)) return; + visited.add(uuid); + + const point = pointMap.get(uuid); + if (point) { + sequence.push(point); + } + + // Follow forward dependencies + const nextPoints = dependencyGraph.get(uuid) || []; + nextPoints.forEach(nextUuid => traverse(nextUuid)); + } + + traverse(startUuid); + return sequence; + } + + // Build sequences for all root points + rootPoints.forEach(root => { + executionSequences.push(buildSequence(root.uuid)); + }); + + // Handle any triggered points not reachable from roots (isolated chains) + const processedPoints = new Set( + executionSequences.flat().map(p => p.uuid) + ); + + allPoints.forEach(point => { + if (triggeredPoints.has(point.uuid) && !processedPoints.has(point.uuid)) { + executionSequences.push(buildSequence(point.uuid)); + } + }); + + return executionSequences; + } + function extractTriggersFromPoint(point: PointsScheme): TriggerSchema[] { if ('actions' in point) { return point.actions.flatMap(action => action.triggers); diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts new file mode 100644 index 0000000..6e1f095 --- /dev/null +++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts @@ -0,0 +1,13 @@ +import { useCallback } from 'react'; +import { useActionHandler } from '../../actions/useActionHandler'; + +export function useTriggerHandler() { + + const triggerPointActions = useCallback((point: PointsScheme) => { + if (!point) return; + }, []); + + return { + triggerPointActions + }; +} \ No newline at end of file diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index c24ec63..3ed534e 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -64,6 +64,7 @@ type ProductsStore = { 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; getModelUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined; + getPointUuidByActionUuid: (productId: string, actionUuid: string) => (string) | undefined; getTriggerByUuid: (productId: string, triggerUuid: string) => TriggerSchema | undefined; getIsEventInProduct: (productId: string, modelUuid: string) => boolean; }; @@ -598,6 +599,30 @@ export const useProductStore = create()( return undefined; }, + getPointUuidByActionUuid: (productId, actionUuid) => { + 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.action?.actionUuid === actionUuid) { + return point.uuid; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action?.actionUuid === actionUuid) { + return point.uuid; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) return point.uuid; + } + } + } + return undefined; + }, + getTriggerByUuid: (productId, triggerUuid) => { 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 a33056e..3cd7267 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -203,4 +203,56 @@ interface MaterialSchema { }; } -type MaterialsSchema = MaterialSchema[]; \ No newline at end of file +type MaterialsSchema = MaterialSchema[]; + + +interface ConveyorAction { + actionUuid: string; + actionName: string; + actionType: "default" | "spawn" | "swap" | "delay" | "despawn"; + material: string; + delay: number; + spawnInterval: number; + spawnCount: number; + triggers: TriggerSchema[]; +} + +interface VehicleAction { + actionUuid: string; + actionName: string; + actionType: "travel"; + unLoadDuration: number; + loadCapacity: number; + steeringAngle: number; + pickUpPoint: { position: { x: number; y: number, z: number }, rotation: { x: number; y: number, z: number } } | null; + unLoadPoint: { position: { x: number; y: number, z: number }, rotation: { x: number; y: number, z: number } } | null; + triggers: TriggerSchema[]; +} + +interface RoboticArmAction { + actionUuid: string; + actionName: string; + actionType: "pickAndPlace"; + process: { startPoint: [number, number, number] | null; endPoint: [number, number, number] | null; }; + triggers: TriggerSchema[]; +} + +interface MachineAction { + actionUuid: string; + actionName: string; + actionType: "process"; + processTime: number; + swapMaterial: string; + triggers: TriggerSchema[]; +} + +interface StorageAction { + actionUuid: string; + actionName: string; + actionType: "store"; + materials: { materialName: string; materialId: string; }[]; + storageCapacity: number; + triggers: TriggerSchema[]; +} + +type Action = ConveyorAction | VehicleAction | RoboticArmAction | MachineAction | StorageAction;