diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 0db803f..29b0627 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -225,7 +225,6 @@ async function handleModelLoad( eventData as SimulationTypes.ConveyorEventsSchema ]); - console.log('data: ', data); socket.emit("v2:model-asset:add", data); } else if (res.type === "Vehicle") { @@ -365,7 +364,7 @@ async function handleModelLoad( uuid: pointUUID, position: res.points.position as [number, number, number], rotation: res.points.rotation as [number, number, number], - actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 1, processes: [] }, + actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 0.2, processes: [] }, triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' }, connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] }, } diff --git a/app/src/modules/scene/controls/selection/copyPasteControls.tsx b/app/src/modules/scene/controls/selection/copyPasteControls.tsx index 84d6047..1f83513 100644 --- a/app/src/modules/scene/controls/selection/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selection/copyPasteControls.tsx @@ -332,8 +332,8 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas } else if (eventData.type === 'StaticMachine' && eventData) { const createStaticMachinePoint = () => { const pointUUID = THREE.MathUtils.generateUUID(); - const vehiclePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points; - const hasActions = vehiclePoint?.actions !== undefined; + const staticMachinePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points; + const hasActions = staticMachinePoint?.actions !== undefined; const defaultAction = { uuid: THREE.MathUtils.generateUUID(), @@ -344,11 +344,11 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas return { uuid: pointUUID, - position: vehiclePoint?.position, - // rotation: vehiclePoint?.rotation, + position: staticMachinePoint?.position, + rotation: staticMachinePoint?.rotation, actions: hasActions ? { - ...vehiclePoint.actions, + ...staticMachinePoint.actions, uuid: THREE.MathUtils.generateUUID() } : defaultAction, @@ -410,8 +410,8 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas } else if (eventData.type === 'ArmBot' && eventData) { const createArmBotPoint = () => { const pointUUID = THREE.MathUtils.generateUUID(); - const vehiclePoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points; - const hasActions = vehiclePoint?.actions !== undefined; + const armBotPoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points; + const hasActions = armBotPoint?.actions !== undefined; const defaultAction = { uuid: THREE.MathUtils.generateUUID(), @@ -422,18 +422,19 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas return { uuid: pointUUID, - position: vehiclePoint?.position, - // rotation: vehiclePoint?.rotation, + position: armBotPoint?.position, + rotation: armBotPoint?.rotation, actions: hasActions ? { - ...vehiclePoint.actions, - uuid: THREE.MathUtils.generateUUID() + ...armBotPoint.actions, + uuid: THREE.MathUtils.generateUUID(), + processes: [] } : defaultAction, triggers: { uuid: THREE.MathUtils.generateUUID(), - name: vehiclePoint.triggers.name, - type: vehiclePoint.triggers.type, + name: armBotPoint.triggers.name, + type: armBotPoint.triggers.type, }, connections: { source: { modelUUID: obj.uuid, pointUUID }, diff --git a/app/src/modules/scene/controls/selection/duplicationControls.tsx b/app/src/modules/scene/controls/selection/duplicationControls.tsx index 5ce0603..915cb61 100644 --- a/app/src/modules/scene/controls/selection/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selection/duplicationControls.tsx @@ -246,7 +246,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb return { uuid: pointUUID, position: vehiclePoint?.position, - // rotation: vehiclePoint?.rotation, + rotation: vehiclePoint?.rotation, actions: hasActions ? { ...vehiclePoint.actions, @@ -311,8 +311,8 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb } else if (eventData.type === 'StaticMachine' && eventData) { const createStaticMachinePoint = () => { const pointUUID = THREE.MathUtils.generateUUID(); - const vehiclePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points; - const hasActions = vehiclePoint?.actions !== undefined; + const staticMachinePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points; + const hasActions = staticMachinePoint?.actions !== undefined; const defaultAction = { uuid: THREE.MathUtils.generateUUID(), @@ -323,11 +323,11 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb return { uuid: pointUUID, - position: vehiclePoint?.position, - // rotation: vehiclePoint?.rotation, + position: staticMachinePoint?.position, + rotation: staticMachinePoint?.rotation, actions: hasActions ? { - ...vehiclePoint.actions, + ...staticMachinePoint.actions, uuid: THREE.MathUtils.generateUUID() } : defaultAction, @@ -389,8 +389,8 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb } else if (eventData.type === 'ArmBot' && eventData) { const createArmBotPoint = () => { const pointUUID = THREE.MathUtils.generateUUID(); - const vehiclePoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points; - const hasActions = vehiclePoint?.actions !== undefined; + const armBotPoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points; + const hasActions = armBotPoint?.actions !== undefined; const defaultAction = { uuid: THREE.MathUtils.generateUUID(), @@ -401,18 +401,19 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb return { uuid: pointUUID, - position: vehiclePoint?.position, - // rotation: vehiclePoint?.rotation, + position: armBotPoint?.position, + rotation: armBotPoint?.rotation, actions: hasActions ? { - ...vehiclePoint.actions, - uuid: THREE.MathUtils.generateUUID() + ...armBotPoint.actions, + uuid: THREE.MathUtils.generateUUID(), + processes: [] } : defaultAction, triggers: { uuid: THREE.MathUtils.generateUUID(), - name: vehiclePoint.triggers.name, - type: vehiclePoint.triggers.type, + name: armBotPoint.triggers.name, + type: armBotPoint.triggers.type, }, connections: { source: { modelUUID: obj.uuid, pointUUID }, diff --git a/app/src/modules/simulation/armbot/ArmBot.tsx b/app/src/modules/simulation/armbot/ArmBot.tsx index 2e77206..a6d8d23 100644 --- a/app/src/modules/simulation/armbot/ArmBot.tsx +++ b/app/src/modules/simulation/armbot/ArmBot.tsx @@ -15,27 +15,38 @@ interface ArmBotState { actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; } +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} + interface ArmBotProps { armBots: ArmBotState[]; setArmBots: React.Dispatch>; + setStaticMachines: React.Dispatch>; } -const ArmBot = ({ armBots, setArmBots }: ArmBotProps) => { +const ArmBot = ({ armBots, setArmBots, setStaticMachines }: ArmBotProps) => { const { activeModule } = useModuleStore(); const { scene } = useThree(); const { simulationStates } = useSimulationStates(); useEffect(() => { const filtered = simulationStates.filter((s): s is SimulationTypes.ArmBotEventsSchema => s.type === "ArmBot"); - const initialStates: ArmBotState[] = filtered.map(bot => ({ - uuid: bot.modeluuid, - position: bot.position, - rotation: bot.rotation, - status: "idle", - material: "default", - triggerId: '', - actions: bot.points.actions - })); + const initialStates: ArmBotState[] = filtered + .filter(bot => bot.points.connections.targets.length > 0) + .map(bot => ({ + uuid: bot.modeluuid, + position: bot.position, + rotation: bot.rotation, + status: "idle", + material: "default", + triggerId: '', + actions: bot.points.actions + })); setArmBots(initialStates); }, [simulationStates]); @@ -57,6 +68,7 @@ const ArmBot = ({ armBots, setArmBots }: ArmBotProps) => { index={i} armBot={bot} setArmBots={setArmBots} + setStaticMachines={setStaticMachines} /> ))} diff --git a/app/src/modules/simulation/armbot/ArmBotInstances.tsx b/app/src/modules/simulation/armbot/ArmBotInstances.tsx index 8a1380b..10a49f6 100644 --- a/app/src/modules/simulation/armbot/ArmBotInstances.tsx +++ b/app/src/modules/simulation/armbot/ArmBotInstances.tsx @@ -30,13 +30,22 @@ interface ArmBotState { }; } +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} + interface ArmbotInstancesProps { index: number; armBot: ArmBotState; setArmBots: React.Dispatch>; + setStaticMachines: React.Dispatch>; } -export const ArmbotInstances: React.FC = ({ index, armBot, setArmBots }) => { +export const ArmbotInstances: React.FC = ({ index, armBot, setArmBots, setStaticMachines }) => { const { scene } = useThree(); const [processes, setProcesses] = useState([]); @@ -61,7 +70,7 @@ export const ArmbotInstances: React.FC = ({ index, armBot, setArmBots((prevArmBots) => { return prevArmBots.map(bot => { if (bot.uuid === armBot.uuid) { - return { ...bot, status }; + return { ...bot, status, triggerId: status === 'idle' ? '' : armBot.triggerId }; } return bot; }); @@ -72,10 +81,13 @@ export const ArmbotInstances: React.FC = ({ index, armBot, ); diff --git a/app/src/modules/simulation/armbot/IKAnimationController.tsx b/app/src/modules/simulation/armbot/IKAnimationController.tsx index 538f0aa..1044540 100644 --- a/app/src/modules/simulation/armbot/IKAnimationController.tsx +++ b/app/src/modules/simulation/armbot/IKAnimationController.tsx @@ -2,6 +2,35 @@ import { useEffect, useMemo, useState, useRef } from "react"; import { useFrame } from "@react-three/fiber"; import * as THREE from "three"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import { useSimulationStates } from "../../../store/store"; + + +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} + +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + actions: { + uuid: string; + name: string; + speed: number; + processes: { + triggerId: string; + startPoint: string; + endPoint: string; + }[]; + }; +} type IKAnimationControllerProps = { ikSolver: any; @@ -16,6 +45,8 @@ type IKAnimationControllerProps = { uuid: string; logStatus: (status: string) => void; groupRef: React.RefObject; + armBot: ArmBotState; + setStaticMachines: React.Dispatch>; updateArmBotStatus: (status: string) => void; } @@ -27,6 +58,8 @@ const IKAnimationController = ({ uuid, logStatus, groupRef, + armBot, + setStaticMachines, updateArmBotStatus }: IKAnimationControllerProps) => { const [progress, setProgress] = useState(0); @@ -36,6 +69,7 @@ const IKAnimationController = ({ const restSpeed = 0.1; const restPosition = new THREE.Vector3(0, 2, 1.6); const { isPlaying } = usePlayButtonStore(); + const { simulationStates } = useSimulationStates(); // Track previous states for comparison const prevStateRef = useRef({ @@ -120,54 +154,56 @@ const IKAnimationController = ({ }; const processedCurves = useMemo(() => { - return process.map((p) => { - const tempLift = 0.5; - const localStart = groupRef.current?.worldToLocal(p.startPoint.clone().add(new THREE.Vector3(0, tempLift, 0))); - const localEnd = groupRef.current?.worldToLocal(p.endPoint.clone().add(new THREE.Vector3(0, tempLift, 0))); + if (isPlaying) + return process.map((p) => { + const tempLift = 0.5; + const localStart = groupRef.current?.worldToLocal(p.startPoint.clone().add(new THREE.Vector3(0, tempLift, 0))); + const localEnd = groupRef.current?.worldToLocal(p.endPoint.clone().add(new THREE.Vector3(0, tempLift, 0))); - if (localStart && localEnd) { + if (localStart && localEnd) { - const mid = new THREE.Vector3( - (localStart.x + localEnd.x) / 1, - Math.max(localStart.y, localEnd.y) + 0.8, - (localStart.z + localEnd.z) / 0.9 - ); + const mid = new THREE.Vector3( + (localStart.x + localEnd.x) / 1, + Math.max(localStart.y, localEnd.y) + 0.8, + (localStart.z + localEnd.z) / 0.9 + ); - const points = [ - restPosition.clone(), - localStart.clone(), - mid.clone(), - localEnd.clone(), - restPosition.clone(), - ]; - const curve = new THREE.CatmullRomCurve3(points); - const restToStartDist = points[0].distanceTo(points[1]); - const startToEndDist = points[1].distanceTo(points[3]); - const endToRestDist = points[3].distanceTo(points[4]); + const points = [ + restPosition.clone(), + localStart.clone(), + mid.clone(), + localEnd.clone(), + restPosition.clone(), + ]; + const curve = new THREE.CatmullRomCurve3(points); + const restToStartDist = points[0].distanceTo(points[1]); + const startToEndDist = points[1].distanceTo(points[3]); + const endToRestDist = points[3].distanceTo(points[4]); - const totalDist = restToStartDist + startToEndDist + endToRestDist; - const restToStartRange = [0, restToStartDist / totalDist]; - const startToEndRange = [ - restToStartRange[1], - restToStartRange[1] + startToEndDist / totalDist, - ]; - const endToRestRange = [startToEndRange[1], 1]; + const totalDist = restToStartDist + startToEndDist + endToRestDist; + const restToStartRange = [0, restToStartDist / totalDist]; + const startToEndRange = [ + restToStartRange[1], + restToStartRange[1] + startToEndDist / totalDist, + ]; + const endToRestRange = [startToEndRange[1], 1]; - return { - trigger: p.triggerId, - curve, - speed: p.speed, - restToStartRange, - startToEndRange, - endToRestRange, - }; - } - }); - }, [process, groupRef]); + return { + trigger: p.triggerId, + curve, + speed: p.speed, + restToStartRange, + startToEndRange, + endToRestRange, + }; + } + }); + }, [process, groupRef, isPlaying]); const activeCurve = useMemo(() => { - return processedCurves.find((c) => c?.trigger === selectedTrigger); - }, [processedCurves, selectedTrigger]); + if (isPlaying && processedCurves) + return processedCurves.find((c) => c?.trigger === selectedTrigger); + }, [processedCurves, selectedTrigger, isPlaying]); // Initial movement to rest position useFrame((_, delta) => { @@ -195,7 +231,7 @@ const IKAnimationController = ({ // Main animation loop useFrame((_, delta) => { - if (!ikSolver || !activeCurve || isInitializing) return; + if (!ikSolver || !activeCurve || isInitializing || !isPlaying) return; const { curve, speed, restToStartRange, startToEndRange, endToRestRange } = activeCurve; const targetBone = ikSolver.mesh.skeleton.bones.find( @@ -213,6 +249,35 @@ const IKAnimationController = ({ } else if (progress >= startToEndRange[0] && progress < startToEndRange[1]) { currentSpeed = speed; currentStatus = "moving"; // Moving between points + if (1 - progress < 0.05) { + // Find the process that matches the current trigger + const currentProcess = process.find(p => p.triggerId === selectedTrigger); + if (currentProcess) { + const triggerId = currentProcess.triggerId; + + const endPoint = armBot.actions.processes.find((process) => process.triggerId === triggerId)?.endPoint; + + // Search simulationStates for a StaticMachine that has a point matching this endPointId + const matchedStaticMachine = simulationStates.find( + (state) => + state.type === "StaticMachine" && + state.points?.uuid === endPoint// check for static machine with matching point uuid + ) as any; + + if (matchedStaticMachine) { + setStaticMachines((machines) => { + return machines.map((machine) => { + if (machine.uuid === matchedStaticMachine.modeluuid) { + return { ...machine, status: "running" }; + } else { + return machine; + } + }); + }); + } + } + + } } else if (progress >= endToRestRange[0] && progress < 1) { currentSpeed = restSpeed; currentStatus = "moving"; // Returning to rest diff --git a/app/src/modules/simulation/armbot/IkInstances.tsx b/app/src/modules/simulation/armbot/IkInstances.tsx index 7b02941..50b8ffb 100644 --- a/app/src/modules/simulation/armbot/IkInstances.tsx +++ b/app/src/modules/simulation/armbot/IkInstances.tsx @@ -8,23 +8,55 @@ import { CCDIKSolver, CCDIKHelper, } from "three/examples/jsm/animation/CCDIKSol import IKAnimationController from "./IKAnimationController"; import { TransformControls } from "@react-three/drei"; +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} + +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + actions: { + uuid: string; + name: string; + speed: number; + processes: { + triggerId: string; + startPoint: string; + endPoint: string; + }[]; + }; +} + const IkInstances = ({ uuid, + selectedTrigger, modelUrl, processes, position, rotation, + armBot, + setStaticMachines, updateArmBotStatus }: { uuid: string; + selectedTrigger: string; modelUrl: string; processes: any; position: [number, number, number]; rotation: [number, number, number]; + armBot: ArmBotState; + setStaticMachines: React.Dispatch>; updateArmBotStatus: (status: string) => void; }) => { const [ikSolver, setIkSolver] = useState(null); - const [selectedTrigger, setSelectedTrigger] = useState(""); const gltf = useLoader(GLTFLoader, modelUrl, (loader) => { const draco = new DRACOLoader(); draco.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/"); @@ -82,17 +114,6 @@ const IkInstances = ({ }, [gltf]); - useEffect(() => { - const triggers = ["9f4a9b8b-e60d-4754-8c99-d71979da0e71", "b77b4f0a-ce55-4fe0-a181-a43ab3d01c83"]; - let index = 0; - - const cycleTriggers = setInterval(() => { - setSelectedTrigger(triggers[index]); - index = (index + 1) % triggers.length; - }, 10000); - - return () => clearInterval(cycleTriggers); - }, []); const logStatus = (status: string) => { // console.log(status); @@ -119,6 +140,8 @@ const IkInstances = ({ uuid={uuid} logStatus={logStatus} groupRef={groupRef} + armBot={armBot} + setStaticMachines={setStaticMachines} updateArmBotStatus={updateArmBotStatus} /> diff --git a/app/src/modules/simulation/process/processAnimator.tsx b/app/src/modules/simulation/process/processAnimator.tsx index c1063c3..460de49 100644 --- a/app/src/modules/simulation/process/processAnimator.tsx +++ b/app/src/modules/simulation/process/processAnimator.tsx @@ -11,11 +11,23 @@ import { ProcessData } from "./types"; import { useSimulationStates } from "../../../store/store"; import { retrieveGLTF } from "../../../utils/indexDB/idbUtils"; +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; +} + interface ProcessContainerProps { processes: ProcessData[]; setProcesses: React.Dispatch>; agvRef: any; MaterialRef: any; + armBots: ArmBotState[]; + setArmBots: React.Dispatch>; } const ProcessAnimator: React.FC = ({ @@ -23,6 +35,8 @@ const ProcessAnimator: React.FC = ({ setProcesses, agvRef, MaterialRef, + armBots, + setArmBots }) => { const gltf = useLoader(GLTFLoader, crate) as GLTF; const groupRef = useRef(null); @@ -42,7 +56,7 @@ const ProcessAnimator: React.FC = ({ getPointDataForAnimationIndex, processes: processedProcesses, checkAndCountTriggers, - } = useProcessAnimation(processes, setProcesses, agvRef); + } = useProcessAnimation(processes, setProcesses, agvRef, armBots, setArmBots); const baseMaterials = useMemo(() => ({ Box: new THREE.MeshStandardMaterial({ color: 0x8b4513 }), diff --git a/app/src/modules/simulation/process/processContainer.tsx b/app/src/modules/simulation/process/processContainer.tsx index 1cbc75b..4cc7edf 100644 --- a/app/src/modules/simulation/process/processContainer.tsx +++ b/app/src/modules/simulation/process/processContainer.tsx @@ -2,11 +2,23 @@ import React, { useState } from "react"; import ProcessCreator from "./processCreator"; import ProcessAnimator from "./processAnimator"; +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; +} + interface ProcessContainerProps { processes: any[]; setProcesses: React.Dispatch>; agvRef: any; MaterialRef: any; + armBots: ArmBotState[]; + setArmBots: React.Dispatch>; } const ProcessContainer: React.FC = ({ @@ -14,6 +26,8 @@ const ProcessContainer: React.FC = ({ setProcesses, agvRef, MaterialRef, + armBots, + setArmBots }) => { return ( <> @@ -23,6 +37,8 @@ const ProcessContainer: React.FC = ({ setProcesses={setProcesses} agvRef={agvRef} MaterialRef={MaterialRef} + armBots={armBots} + setArmBots={setArmBots} /> ); diff --git a/app/src/modules/simulation/process/types.ts b/app/src/modules/simulation/process/types.ts index 6f935fc..9c9a1bc 100644 --- a/app/src/modules/simulation/process/types.ts +++ b/app/src/modules/simulation/process/types.ts @@ -21,15 +21,15 @@ export interface PointAction { } export interface ProcessPoint { - uuid: string; + uuid: string; position: number[]; - rotation: number[]; - actions: PointAction[]; + rotation: number[]; + actions: PointAction[]; connections: { - source: { modelUUID: string; pointUUID: string }; - targets: { modelUUID: string; pointUUID: string }[]; + source: { modelUUID: string; pointUUID: string }; + targets: { modelUUID: string; pointUUID: string }[]; }; - triggers?: Trigger[]; + triggers?: Trigger[]; } export interface ProcessPath { modeluuid: string; @@ -38,7 +38,7 @@ export interface ProcessPath { pathPosition: number[]; pathRotation: number[]; speed: number; - type: "Conveyor" | "Vehicle"; + type: "Conveyor" | "Vehicle" | "ArmBot"; isActive: boolean } diff --git a/app/src/modules/simulation/process/useProcessAnimations.tsx b/app/src/modules/simulation/process/useProcessAnimations.tsx index 032d013..8c0f20d 100644 --- a/app/src/modules/simulation/process/useProcessAnimations.tsx +++ b/app/src/modules/simulation/process/useProcessAnimations.tsx @@ -39,10 +39,22 @@ interface PlayAgvState { setPlayAgv: (data: any) => void; } +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; +} + export const useProcessAnimation = ( processes: ProcessData[], setProcesses: React.Dispatch>, - agvRef: any + agvRef: any, + armBots: ArmBotState[], + setArmBots: React.Dispatch> ) => { // State and refs initialization const { isPlaying, setIsPlaying } = usePlayButtonStore(); @@ -438,6 +450,8 @@ export const useProcessAnimation = ( [handleMaterialSwap] ); + const deferredArmBotUpdates = useRef<{ uuid: string; triggerId: string }[]>([]); + // Trigger counting system const checkAndCountTriggers = useCallback( ( @@ -457,46 +471,54 @@ export const useProcessAnimation = ( const point = getPointDataForAnimationIndex(process, currentPointIndex); if (!point?.triggers) return prev; - const onHitTriggers = point.triggers.filter( - (t: Trigger) => t.type === "On-Hit" && t.isUsed - ); + const onHitTriggers = point.triggers.filter((t: Trigger) => t.type === "On-Hit" && t.isUsed); + if (onHitTriggers.length === 0) return prev; let newTriggerCounts = { ...processState.triggerCounts }; const newTriggerLogs = [...processState.triggerLogs]; let shouldLog = false; - // Find all vehicle paths for this process - const vehiclePaths = process.paths.filter( - (path) => path.type === "Vehicle" - ); + const vehiclePaths = process.paths.filter((path) => path.type === "Vehicle"); + const armBotPaths = process.paths.filter((path) => path.type === "ArmBot"); - // Check if any vehicle is active for this process - const activeVehicles = vehiclePaths.filter(path => { + const activeVehicles = vehiclePaths.filter((path) => { const vehicleId = path.modeluuid; - const vehicleEntry = agvRef.current.find( - (v: any) => v.vehicleId === vehicleId && v.processId === processId - ); + const vehicleEntry = agvRef.current.find((v: any) => v.vehicleId === vehicleId && v.processId === processId); return vehicleEntry?.isActive; }); - // Only count triggers if no vehicles are active for this process + // Check if any ArmBot is active for this process + // const activeArmBots = armBotPaths.filter((path) => { + // const armBotId = path.modeluuid; + // const armBotEntry = armBots.find((a: any) => a.uuid === armBotId); + // return armBotEntry; + // }); + + // Only count triggers if no vehicles and no ArmBots are active for this process + if (activeVehicles.length === 0) { onHitTriggers.forEach((trigger: Trigger) => { - const triggerKey = `${point.uuid}-${trigger.uuid}`; - newTriggerCounts[triggerKey] = (newTriggerCounts[triggerKey] || 0) + 1; - shouldLog = true; - newTriggerLogs.push({ - timestamp: currentTime, - pointId: point.uuid, - objectId, - triggerId: trigger.uuid, + const connections = point.connections?.targets || []; + + connections.forEach((connection) => { + const connectedModelUUID = connection.modelUUID; + + const matchingArmPath = armBotPaths.find((path) => path.modeluuid === connectedModelUUID); + + if (matchingArmPath) { + deferredArmBotUpdates.current.push({ + uuid: connectedModelUUID, + triggerId: trigger.uuid, + }); + } }); }); } let processTotalHits = Object.values(newTriggerCounts).reduce((a, b) => a + b, 0); + // Handle logic for vehicles and ArmBots when a trigger is hit if (shouldLog) { vehiclePaths.forEach((vehiclePath) => { if (vehiclePath.points?.length > 0) { @@ -506,7 +528,10 @@ export const useProcessAnimation = ( if (maxHitCount !== undefined) { const vehicleId = vehiclePath.modeluuid; - let vehicleEntry = agvRef.current.find((v: any) => v.vehicleId === vehicleId && v.processId === processId); + let vehicleEntry = agvRef.current.find( + (v: any) => + v.vehicleId === vehicleId && v.processId === processId + ); if (!vehicleEntry) { vehicleEntry = { @@ -515,14 +540,13 @@ export const useProcessAnimation = ( maxHitCount: maxHitCount, isActive: false, hitCount: 0, - status: 'stationed' + status: "stationed", }; agvRef.current.push(vehicleEntry); } - // if (!vehicleEntry.isActive && vehicleEntry.status === 'picking') { if (!vehicleEntry.isActive) { - vehicleEntry.hitCount = processTotalHits; + vehicleEntry.hitCount++; vehicleEntry.lastUpdated = currentTime; if (vehicleEntry.hitCount >= vehicleEntry.maxHitCount) { @@ -546,9 +570,21 @@ export const useProcessAnimation = ( }, }; }); - }, - [] - ); + }, []); + + useEffect(() => { + if (deferredArmBotUpdates.current.length > 0) { + const updates = [...deferredArmBotUpdates.current]; + deferredArmBotUpdates.current = []; + + setArmBots((prev) => + prev.map((bot) => { + const update = updates.find((u) => u.uuid === bot.uuid); + return update ? { ...bot, triggerId: update.triggerId } : bot; + }) + ); + } + }, [animationStates]); // Utility functions const hasNonInheritActions = useCallback( diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index f13fdf4..2f66ab8 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -6,6 +6,7 @@ import useModuleStore from "../../store/useModuleStore"; import ProcessContainer from "./process/processContainer"; import Agv from "../builder/agv/agv"; import ArmBot from "./armbot/ArmBot"; +import StaticMachine from "./staticMachine/staticMachine"; interface ArmBotState { uuid: string; @@ -17,10 +18,19 @@ interface ArmBotState { actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; } +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} + function Simulation() { const { activeModule } = useModuleStore(); const pathsGroupRef = useRef() as React.MutableRefObject; const [armBots, setArmBots] = useState([]); + const [staticMachines, setStaticMachines] = useState([]); const [processes, setProcesses] = useState([]); const agvRef = useRef([]); const MaterialRef = useRef([]); @@ -38,6 +48,8 @@ function Simulation() { setProcesses={setProcesses} agvRef={agvRef} MaterialRef={MaterialRef} + armBots={armBots} + setArmBots={setArmBots} /> )} - + + ); } diff --git a/app/src/modules/simulation/staticMachine/staticMachine.tsx b/app/src/modules/simulation/staticMachine/staticMachine.tsx new file mode 100644 index 0000000..ba9b4f0 --- /dev/null +++ b/app/src/modules/simulation/staticMachine/staticMachine.tsx @@ -0,0 +1,77 @@ +import React, { useEffect } from 'react' +import * as SimulationTypes from '../../../types/simulationTypes'; +import { useSimulationStates } from '../../../store/store'; +import StaticMachineInstances from './staticMachineInstances'; + +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; +} + +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} + +type StaticMachineProps = { + setArmBots: React.Dispatch>; + staticMachines: StaticMachineState[]; + setStaticMachines: React.Dispatch>; +} + +function StaticMachine({ setArmBots, staticMachines, setStaticMachines }: StaticMachineProps) { + + const { simulationStates } = useSimulationStates(); + + useEffect(() => { + const filtered = simulationStates.filter((s): s is SimulationTypes.StaticMachineEventsSchema => s.type === "StaticMachine"); + const initialStates: StaticMachineState[] = filtered + .filter(machine => machine.points.connections.targets.length > 0) + .map(machine => ({ + uuid: machine.modeluuid, + status: "idle", + actions: machine.points.actions, + machineTriggerId: machine.points.triggers.uuid, + connectedArmBot: machine.points.connections.targets[0].modelUUID + })); + setStaticMachines(initialStates); + }, [simulationStates]); + + const updateArmBotTriggerAndMachineStatus = (armBotUuid: string, triggerId: string, machineId: string) => { + setArmBots((prevArmBots) => { + return prevArmBots.map(bot => { + if (bot.uuid === armBotUuid) { + return { ...bot, triggerId: triggerId }; + } + return bot; + }); + }); + setStaticMachines((prevStaticMachines) => { + return prevStaticMachines.map(machine => { + if (machine.uuid === machineId) { + return { ...machine, status: "idle" }; + } else { + return machine; + } + }); + }); + } + + return ( + <> + {staticMachines.map((machine, index) => ( + + ))} + + ) +} + +export default StaticMachine; \ No newline at end of file diff --git a/app/src/modules/simulation/staticMachine/staticMachineInstances.tsx b/app/src/modules/simulation/staticMachine/staticMachineInstances.tsx new file mode 100644 index 0000000..94a2faa --- /dev/null +++ b/app/src/modules/simulation/staticMachine/staticMachineInstances.tsx @@ -0,0 +1,33 @@ +import React, { useEffect } from 'react' +import { useAnimationPlaySpeed } from '../../../store/usePlayButtonStore'; + +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} + +type StaticMachineInstancesProps = { + machine: StaticMachineState, + updateArmBotTriggerAndMachineStatus: (armBotUuid: string, triggerId: string, machineId: string) => void; +} + +function StaticMachineInstances({ machine, updateArmBotTriggerAndMachineStatus }: StaticMachineInstancesProps) { + const { speed } = useAnimationPlaySpeed(); + + useEffect(() => { + if (machine.status === 'running') { + setTimeout(() => { + updateArmBotTriggerAndMachineStatus(machine.connectedArmBot, machine.machineTriggerId, machine.uuid); + }, machine.actions.buffer * 1000 * speed); + } + }, [machine]) + + return ( + <> + ) +} + +export default StaticMachineInstances \ No newline at end of file diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index bea52fa..75efcd3 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -64,7 +64,7 @@ interface StaticMachineEventsSchema { uuid: string; position: [number, number, number]; rotation: [number, number, number]; - actions: { uuid: string; name: string; buffer: number | string; material: string; isUsed: boolean; }; + actions: { uuid: string; name: string; buffer: number; material: string; }; triggers: { uuid: string; name: string; type: string }; connections: { source: { modelUUID: string; pointUUID: string }; @@ -103,7 +103,7 @@ export type EventData = { isLocked: boolean; isVisible: boolean; eventData?: - | { + { type: "Conveyor"; points: { uuid: string;