diff --git a/app/src/modules/builder/agv/agv.tsx b/app/src/modules/builder/agv/agv.tsx index ad8fcd5..48f4306 100644 --- a/app/src/modules/builder/agv/agv.tsx +++ b/app/src/modules/builder/agv/agv.tsx @@ -1,14 +1,8 @@ -import { useEffect, useRef, useState } from "react"; -import { useSimulationStates } from "../../../store/store"; -import PolygonGenerator from "./polygonGenerator"; +import { useEffect, useState } from "react"; +import { Line } from "@react-three/drei"; +import { useNavMesh, useSimulationStates } from "../../../store/store"; import PathNavigator from "./pathNavigator"; -import NavMeshDetails from "./navMeshDetails"; -import * as CONSTANTS from "../../../types/world/worldConstants"; -import * as Types from "../../../types/world/worldTypes"; - -type AgvProps = { - lines: Types.RefLines -}; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; type PathPoints = { modelUuid: string; @@ -18,12 +12,11 @@ type PathPoints = { hitCount: number; }; -const Agv = ({ lines }: AgvProps) => { - - let groupRef = useRef() as Types.RefGroup; +const Agv = () => { const [pathPoints, setPathPoints] = useState([]); const { simulationStates } = useSimulationStates(); - const [navMesh, setNavMesh] = useState(); + const { navMesh } = useNavMesh(); + const { isPlaying } = usePlayButtonStore(); useEffect(() => { if (simulationStates.length > 0) { @@ -37,9 +30,9 @@ const Agv = ({ lines }: AgvProps) => { bufferTime: model.points.actions.buffer, hitCount: model.points.actions.hitCount, points: [ - { x: model.position[0], y: model.position[1], z: model.position[2], }, - { x: model.points.actions.start.x, y: 0, z: model.points.actions.start.y, }, - { x: model.points.actions.end.x, y: 0, z: model.points.actions.end.y, }, + { x: model.position[0], y: model.position[1], z: model.position[2] }, + { x: model.points.actions.start.x, y: 0, z: model.points.actions.start.y }, + { x: model.points.actions.end.x, y: 0, z: model.points.actions.end.y }, ], })); @@ -49,41 +42,25 @@ const Agv = ({ lines }: AgvProps) => { return ( <> - - - - {pathPoints.map((pair, i) => ( - - ))} + + - {pathPoints.map((pair, i) => ( - - - - - - - - - + {pair.points.slice(1).map((point, idx) => ( + + + + + ))} ))} - - - - - - - ); }; diff --git a/app/src/modules/builder/agv/navMeshCreator.tsx b/app/src/modules/builder/agv/navMeshCreator.tsx new file mode 100644 index 0000000..cdbca45 --- /dev/null +++ b/app/src/modules/builder/agv/navMeshCreator.tsx @@ -0,0 +1,31 @@ +import { useRef } from "react"; +import { useNavMesh } from "../../../store/store"; +import PolygonGenerator from "./polygonGenerator"; +import NavMeshDetails from "./navMeshDetails"; +import * as CONSTANTS from "../../../types/world/worldConstants"; +import * as Types from "../../../types/world/worldTypes"; + +type NavMeshCreatorProps = { + lines: Types.RefLines +}; + +function NavMeshCreator({ lines }: NavMeshCreatorProps) { + let groupRef = useRef() as Types.RefGroup; + const { setNavMesh } = useNavMesh(); + + return ( + <> + + + + + + + + + + + ) +} + +export default NavMeshCreator \ No newline at end of file diff --git a/app/src/modules/builder/agv/pathNavigator.tsx b/app/src/modules/builder/agv/pathNavigator.tsx index 65a299b..7d7984c 100644 --- a/app/src/modules/builder/agv/pathNavigator.tsx +++ b/app/src/modules/builder/agv/pathNavigator.tsx @@ -3,179 +3,207 @@ import * as THREE from "three"; import { useFrame, useThree } from "@react-three/fiber"; import { NavMeshQuery } from "@recast-navigation/core"; import { Line } from "@react-three/drei"; -import { useActiveTool } from "../../../store/store"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; interface PathNavigatorProps { - navMesh: any; - selectedPoints: any; - id: string; - speed: number; - bufferTime: number; - hitCount: number; + navMesh: any; + pathPoints: any; + id: string; + speed: number; + bufferTime: number; + hitCount: number; } export default function PathNavigator({ - navMesh, - selectedPoints, - id, - speed, - bufferTime, - hitCount, + navMesh, + pathPoints, + id, + speed, + bufferTime, + hitCount }: PathNavigatorProps) { - const [path, setPath] = useState<[number, number, number][]>([]); - const progressRef = useRef(0); - const distancesRef = useRef([]); - const totalDistanceRef = useRef(0); - const currentSegmentIndex = useRef(0); - const [stop, setStop] = useState(true); - const { scene } = useThree(); - const { isPlaying, setIsPlaying } = usePlayButtonStore(); - const [startPoint, setStartPoint] = useState(new THREE.Vector3()); - const isWaiting = useRef(false); // Flag to track waiting state - const delayTime = bufferTime; + const [path, setPath] = useState<[number, number, number][]>([]); + const [currentPhase, setCurrentPhase] = useState<'initial' | 'loop'>('initial'); + const [toPickupPath, setToPickupPath] = useState<[number, number, number][]>([]); + const [pickupDropPath, setPickupDropPath] = useState<[number, number, number][]>([]); + const [dropPickupPath, setDropPickupPath] = useState<[number, number, number][]>([]); + const [initialPosition, setInitialPosition] = useState(null); + const [initialRotation, setInitialRotation] = useState(null); - const movingForward = useRef(true); // Tracks whether the object is moving forward - // Compute distances and total distance when the path changes - useEffect(() => { - if (!scene || !id || path.length < 2) return; + const distancesRef = useRef([]); + const totalDistanceRef = useRef(0); + const progressRef = useRef(0); + const isWaiting = useRef(false); + const timeoutRef = useRef(null); - let totalDistance = 0; - const distances: number[] = []; - for (let i = 0; i < path.length - 1; i++) { - const start = new THREE.Vector3(...path[i]); - const end = new THREE.Vector3(...path[i + 1]); - const segmentDistance = start.distanceTo(end); - distances.push(segmentDistance); - totalDistance += segmentDistance; - } - distancesRef.current = distances; - totalDistanceRef.current = totalDistance; - progressRef.current = 0; - }, [path]); + const { scene } = useThree(); + const { isPlaying } = usePlayButtonStore(); - // Compute the path using NavMeshQuery - useEffect(() => { - if (!navMesh || selectedPoints.length === 0) return; + useEffect(() => { + const object = scene.getObjectByProperty("uuid", id); + if (object) { + setInitialPosition(object.position.clone()); + setInitialRotation(object.rotation.clone()); + } + }, [scene, id]); - const allPoints = selectedPoints.flat(); - const computedPath: [number, number, number][] = []; + const computePath = (start: any, end: any) => { + try { + const navMeshQuery = new NavMeshQuery(navMesh); + const { path: segmentPath } = navMeshQuery.computePath(start, end); + return segmentPath?.map(({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]) || []; + } catch { + return []; + } + }; - for (let i = 0; i < allPoints.length - 1; i++) { - const start = allPoints[i]; - setStartPoint( - new THREE.Vector3(allPoints[0].x, allPoints[0].y, allPoints[0].z) - ); + const resetState = () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + timeoutRef.current = null; + } - const end = allPoints[i + 1]; + setPath([]); + setCurrentPhase('initial'); + setPickupDropPath([]); + setDropPickupPath([]); + distancesRef.current = []; + totalDistanceRef.current = 0; + progressRef.current = 0; + isWaiting.current = false; - try { - const navMeshQuery = new NavMeshQuery(navMesh); - const { path: segmentPath } = navMeshQuery.computePath(start, end); + if (initialPosition && initialRotation) { + const object = scene.getObjectByProperty("uuid", id); + if (object) { + object.position.copy(initialPosition); + object.rotation.copy(initialRotation); + } + } + }; - if (!segmentPath || segmentPath.length === 0) { - continue; - } + useEffect(() => { + if (!isPlaying) { + resetState(); + } - computedPath.push( - ...segmentPath.map(({ x, y, z }): [number, number, number] => [ - x, - y + 0.1, - z, - ]) - ); - } catch (error) {} - } + if (!navMesh || pathPoints.length < 2) return; - if (computedPath.length > 0) { - setPath(computedPath); - currentSegmentIndex.current = 0; - } - }, [selectedPoints, navMesh]); - useFrame((_, delta) => { - if (!scene || !id || path.length < 2) return; + const [pickup, drop] = pathPoints.slice(-2); + const object = scene.getObjectByProperty("uuid", id); + if (!object) return; - // Find the object in the scene by its UUID - const findObject = scene.getObjectByProperty("uuid", id); - if (!findObject) return; + const currentPosition = { x: object.position.x, y: object.position.y, z: object.position.z }; - if (isPlaying) { - const fast = speed; - progressRef.current += delta * fast; + const toPickupPath = computePath(currentPosition, pickup); + const pickupToDropPath = computePath(pickup, drop); + const dropToPickupPath = computePath(drop, pickup); - let coveredDistance = progressRef.current; - let accumulatedDistance = 0; - let index = 0; + if (toPickupPath.length && pickupToDropPath.length && dropToPickupPath.length) { + setPickupDropPath(pickupToDropPath); + setDropPickupPath(dropToPickupPath); + setToPickupPath(toPickupPath); + setPath(toPickupPath); + setCurrentPhase('initial'); + } + }, [navMesh, pathPoints, hitCount, isPlaying]); - // Determine the current segment of the path - while ( - index < distancesRef.current.length && - coveredDistance > accumulatedDistance + distancesRef.current[index] - ) { - accumulatedDistance += distancesRef.current[index]; - index++; - } + useEffect(() => { + if (path.length < 2) return; - if (index >= distancesRef.current.length) { - progressRef.current = totalDistanceRef.current; + let total = 0; + const segmentDistances = path.slice(0, -1).map((point, i) => { + const dist = new THREE.Vector3(...point).distanceTo(new THREE.Vector3(...path[i + 1])); + total += dist; + return dist; + }); - if (!isWaiting.current) { - isWaiting.current = true; // Set waiting flag + distancesRef.current = segmentDistances; + totalDistanceRef.current = total; + progressRef.current = 0; + isWaiting.current = false; + }, [path]); - if (movingForward.current) { - // Moving forward: reached the end, wait for `delay` - // console.log( - // "Reached end position. Waiting for delay:", - // delayTime, - // "seconds" - // ); - setTimeout(() => { - // After delay, reverse direction - movingForward.current = false; - progressRef.current = 0; // Reset progress - path.reverse(); // Reverse the path - distancesRef.current.reverse(); - isWaiting.current = false; // Reset waiting flag - }, delayTime * 1000); // Wait for `delay` seconds - } - } - return; - } + useFrame((_, delta) => { + if (!isPlaying || path.length < 2 || !scene || !id) return; - // Interpolate position within the current segment - const start = new THREE.Vector3(...path[index]); - const end = new THREE.Vector3(...path[index + 1]); - const segmentDistance = distancesRef.current[index]; + const object = scene.getObjectByProperty("uuid", id); + if (!object) return; - const t = Math.min( - (coveredDistance - accumulatedDistance) / segmentDistance, - 1 - ); // Clamp t to avoid overshooting - const position = start.clone().lerp(end, t); - findObject.position.copy(position); + const speedFactor = speed; + progressRef.current += delta * speedFactor; - // Rotate the object to face the direction of movement - const direction = new THREE.Vector3().subVectors(end, start).normalize(); - const targetYRotation = Math.atan2(direction.x, direction.z); - findObject.rotation.y += (targetYRotation - findObject.rotation.y) * 0.1; - } else { - findObject.position.copy(startPoint); - } - }); + let covered = progressRef.current; + let accumulated = 0; + let index = 0; - return ( - <> - {path.length > 0 && ( - <> - - {selectedPoints.map((val: any, i: any) => ( - - - - - ))} - - )} - - ); -} + while ( + index < distancesRef.current.length && + covered > accumulated + distancesRef.current[index] + ) { + accumulated += distancesRef.current[index]; + index++; + } + + if (index >= distancesRef.current.length) { + progressRef.current = totalDistanceRef.current; + + if (!isWaiting.current) { + isWaiting.current = true; + + timeoutRef.current = setTimeout(() => { + if (currentPhase === 'initial') { + setPath(pickupDropPath); + setCurrentPhase('loop'); + } else { + setPath(prevPath => + prevPath === pickupDropPath ? dropPickupPath : pickupDropPath + ); + } + + progressRef.current = 0; + isWaiting.current = false; + }, bufferTime * 1000); + } + return; + } + + const start = new THREE.Vector3(...path[index]); + const end = new THREE.Vector3(...path[index + 1]); + const dist = distancesRef.current[index]; + const t = THREE.MathUtils.clamp((covered - accumulated) / dist, 0, 1); + const position = start.clone().lerp(end, t); + + object.position.copy(position); + + const direction = new THREE.Vector3().subVectors(end, start).normalize(); + const targetRotationY = Math.atan2(direction.x, direction.z); + + let angleDifference = targetRotationY - object.rotation.y; + angleDifference = ((angleDifference + Math.PI) % (Math.PI * 2)) - Math.PI; + object.rotation.y += angleDifference * 0.1; + }); + + useEffect(() => { + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, []); + + return ( + + {toPickupPath.length > 0 && ( + + )} + + {pickupDropPath.length > 0 && ( + + )} + + {dropPickupPath.length > 0 && ( + + )} + + ); +} \ No newline at end of file diff --git a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts index 3f68568..b89cf40 100644 --- a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts @@ -215,7 +215,7 @@ function processEventData(item: Types.EventData, setSimulationStates: any) { data as Types.ConveyorEventsSchema ]); - } else { + } else if (item.eventData?.type === 'Vehicle') { const data: any = item.eventData; data.modeluuid = item.modeluuid; diff --git a/app/src/modules/scene/world/world.tsx b/app/src/modules/scene/world/world.tsx index 1b4f71f..678d1a4 100644 --- a/app/src/modules/scene/world/world.tsx +++ b/app/src/modules/scene/world/world.tsx @@ -53,8 +53,8 @@ import { findEnvironment } from "../../../services/factoryBuilder/environment/fi import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibility"; import DrieHtmlTemp from "../mqttTemp/drieHtmlTemp"; import ZoneGroup from "../../builder/groups/zoneGroup"; -import Agv from "../../builder/agv/agv"; import useModuleStore from "../../../store/useModuleStore"; +import NavMeshCreator from "../../builder/agv/navMeshCreator"; export default function World() { const state = useThree(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. @@ -368,7 +368,7 @@ export default function World() { {/* */} - {activeModule === "simulation" && } + ); diff --git a/app/src/modules/simulation/path/pathConnector.tsx b/app/src/modules/simulation/path/pathConnector.tsx index 2769a98..589f7d0 100644 --- a/app/src/modules/simulation/path/pathConnector.tsx +++ b/app/src/modules/simulation/path/pathConnector.tsx @@ -3,9 +3,10 @@ import React, { useEffect, useState } from 'react'; import * as THREE from 'three'; import * as Types from '../../../types/world/worldTypes'; import { QuadraticBezierLine } from '@react-three/drei'; -import { useIsConnecting, useSimulationStates } from '../../../store/store'; +import { useIsConnecting, useSimulationStates, useSocketStore } from '../../../store/store'; import useModuleStore from '../../../store/useModuleStore'; import { usePlayButtonStore } from '../../../store/usePlayButtonStore'; +import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/setEventsApt'; function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject }) { const { activeModule } = useModuleStore(); @@ -13,6 +14,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const { setIsConnecting } = useIsConnecting(); const { simulationStates, setSimulationStates } = useSimulationStates(); const { isPlaying } = usePlayButtonStore(); + const { socket } = useSocketStore(); const [firstSelected, setFirstSelected] = useState<{ pathUUID: string; @@ -170,8 +172,57 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec }); setSimulationStates(updatedPaths); + + const updatedPathDetails = updatedPaths.filter(path => + path.modeluuid === fromPathUUID || path.modeluuid === toPathUUID + ); + + updateBackend(updatedPathDetails); }; + const updateBackend = async (updatedPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => { + if (updatedPaths.length === 0) return; + const email = localStorage.getItem("email"); + const organization = email ? email.split("@")[1].split(".")[0] : ""; + + updatedPaths.forEach(async (updatedPath) => { + if (updatedPath.type === 'Conveyor') { + + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } + } + + socket.emit('v2:model-asset:updateEventData', data); + + } else if (updatedPath.type === 'Vehicle') { + + // await setEventApi( + // organization, + // updatedPath.modeluuid, + // { type: "Vehicle", points: updatedPath.points } + // ); + + const data = { + organization: organization, + modeluuid: updatedPath.modeluuid, + eventData: { type: "Vehicle", points: updatedPath.points } + } + + socket.emit('v2:model-asset:updateEventData', data); + + } + }) + + } + const handleAddConnection = (fromPathUUID: string, fromUUID: string, toPathUUID: string, toUUID: string) => { updatePathConnections(fromPathUUID, fromUUID, toPathUUID, toUUID); setFirstSelected(null); diff --git a/app/src/modules/simulation/path/pathCreation.tsx b/app/src/modules/simulation/path/pathCreation.tsx index bcb167b..9c5c2e6 100644 --- a/app/src/modules/simulation/path/pathCreation.tsx +++ b/app/src/modules/simulation/path/pathCreation.tsx @@ -93,10 +93,21 @@ function PathCreation({ : point ), }; + } else { + return path; } - return path; }) as Types.ConveyorEventsSchema[]; + const updatedPath = updatedPaths.find( + (path) => + path.type === "Conveyor" && + path.points.some( + (point) => point.uuid === selectedActionSphere.points.uuid + ) + ); + + // console.log("Updated Path:", updatedPath); + setSimulationStates(updatedPaths); }; diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 55dda4b..98fd3dd 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -42,7 +42,7 @@ function Simulation() { - {/* */} + )} diff --git a/app/src/store/store.ts b/app/src/store/store.ts index eb89771..74f396b 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -359,6 +359,11 @@ export const useSimulationStates = create((set) => ({ })), })) +export const useNavMesh = create((set: any) => ({ + navMesh: null, + setNavMesh: (x: any) => set({ navMesh: x }), +})); + export const useIsConnecting = create((set: any) => ({ isConnecting: false, setIsConnecting: (x: any) => set({ isConnecting: x }),