From fe57e6c8739d35b702378a2bd5d6803c2e7f9dae Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Tue, 29 Apr 2025 19:01:06 +0530 Subject: [PATCH 01/32] playSpeed,reset,exit added and armbot movemets added --- .../instances/animator/roboticArmAnimator.tsx | 147 +++-- .../armInstance/roboticArmInstance.tsx | 567 +++++++++++++++--- .../instances/ikInstance/ikInstance.tsx | 17 +- .../instances/roboticArmInstances.tsx | 2 +- .../simulation/roboticArm/roboticArm.tsx | 4 +- .../simulation/ui/arm/useDraggableGLTF.ts | 2 +- 6 files changed, 576 insertions(+), 163 deletions(-) diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index c82b73d..611ded5 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -1,68 +1,117 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react' -import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; -import { useFrame, useThree } from '@react-three/fiber'; -import * as THREE from "three" -import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; +import React, { useEffect, useRef, useState } from 'react'; +import { useFrame } from '@react-three/fiber'; +import * as THREE from 'three'; +import { + useAnimationPlaySpeed, + usePauseButtonStore, + usePlayButtonStore, + useResetButtonStore +} from '../../../../../store/usePlayButtonStore'; +import { Line } from '@react-three/drei'; -function RoboticArmAnimator({ armUuid, HandleCallback, currentPhase, ikSolver, targetBone, robot, logStatus, groupRef, processes, armBotCurveRef, path }: any) { - const { armBots } = useArmBotStore(); - const { scene } = useThree(); - const restSpeed = 0.1; - const restPosition = new THREE.Vector3(0, 1, -1.6); - const initialCurveRef = useRef(null); - const initialStartPositionRef = useRef(null); - const [initialProgress, setInitialProgress] = useState(0); - const [progress, setProgress] = useState(0); - const [needsInitialMovement, setNeedsInitialMovement] = useState(true); - const [isInitializing, setIsInitializing] = useState(true); - const { isPlaying } = usePlayButtonStore(); - const statusRef = useRef("idle"); - // Create a ref for initialProgress - const initialProgressRef = useRef(0); +function RoboticArmAnimator({ + HandleCallback, + restPosition, + ikSolver, + targetBone, + armBot, + logStatus, + path +}: any) { + const progressRef = useRef(0); + const curveRef = useRef(null); const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); + const [curvePoints, setCurvePoints] = useState(null); + + // Zustand stores + const { isPlaying } = usePlayButtonStore(); + const { isPaused } = usePauseButtonStore(); + const { isReset } = useResetButtonStore(); // optional use depending on your logic + const { speed } = useAnimationPlaySpeed(); useEffect(() => { - setCurrentPath(path) }, [path]) useEffect(() => { + if (!isPlaying) { + setCurrentPath([]) + curveRef.current = null + } + }, [isPlaying]) - }, [currentPath]) + useEffect(() => { + if (currentPath && currentPath.length === 3) { + const [start, mid, end] = currentPath; + const points = [ + new THREE.Vector3(start[0], start[1], start[2]), + new THREE.Vector3(start[0], mid[1] + 0.5, start[2]), + new THREE.Vector3(mid[0], mid[1] + 0.5, mid[2]), + new THREE.Vector3(mid[0], end[1] + 0.5, mid[2]), + new THREE.Vector3(end[0], end[1], end[2]) + ]; + const generatedCurve = new THREE.CatmullRomCurve3(points, false, 'centripetal', 1).getPoints(100); + // console.log('generatedCurve: ', generatedCurve); + curveRef.current = generatedCurve; + setCurvePoints(generatedCurve); + + } + }, [currentPath]); useFrame((_, delta) => { - if (!ikSolver || !currentPath || currentPath.length === 0) return; + if (ikSolver && curveRef.current && currentPath.length >= 0) { + const bone = ikSolver.mesh.skeleton.bones.find( + (b: any) => b.name === targetBone + ); + if (!bone) return; - const bone = ikSolver.mesh.skeleton.bones.find( - (b: any) => b.name === targetBone - ); - if (!bone) return; + if (isPlaying) { + if (!isPaused) { + const curvePoints = curveRef.current; + const speedAdjustedProgress = progressRef.current + (speed * armBot.speed); + const index = Math.floor(speedAdjustedProgress); + if (index >= curvePoints.length) { + HandleCallback(); + setCurrentPath([]); + curveRef.current = null; + progressRef.current = 0; + } else { + const point = curvePoints[index]; + bone.position.copy(point); + progressRef.current = speedAdjustedProgress; + } + } + } else { + logStatus(armBot.modelUuid, 'Simulation Play Exited'); + // bone.position.copy(restPosition); + } - // Ensure currentPath is a valid array of 3D points, create a CatmullRomCurve3 from it - const curve = new THREE.CatmullRomCurve3( - currentPath.map(point => new THREE.Vector3(point[0], point[1], point[2])) - ); + ikSolver.update(); + } else if (ikSolver && !isPlaying && currentPath.length === 0) { + const bone = ikSolver.mesh.skeleton.bones.find( + (b: any) => b.name === targetBone + ); + bone.position.copy(restPosition); + ikSolver.update(); + }; - - const next = initialProgressRef.current + delta * 0.5; - if (next >= 1) { - // bone.position.copy(restPosition); - HandleCallback(); // Call the callback when the path is completed - initialProgressRef.current = 0; // Set ref to 1 when done - } else { - const point = curve.getPoint(next); // Get the interpolated point from the curve - bone.position.copy(point); // Update the bone position along the curve - initialProgressRef.current = next; // Update progress - } - - ikSolver.update(); }); - - return ( - <> - ) + <> + {/* {currentPath.length > 0 && ( + <> + + {currentPath.map((point, index) => ( + + + + + ))} + + )} */} + + ); } -export default RoboticArmAnimator; \ No newline at end of file +export default RoboticArmAnimator; diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 3fe8af1..5f00a45 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -1,42 +1,100 @@ import React, { useEffect, useRef, useState } from 'react' import IKInstance from '../ikInstance/ikInstance'; import RoboticArmAnimator from '../animator/roboticArmAnimator'; -import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; +import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_4.glb"; import { useThree } from "@react-three/fiber"; import { useFloorItems } from '../../../../../store/store'; import useModuleStore from '../../../../../store/useModuleStore'; -import { Vector3 } from "three"; import * as THREE from "three"; -interface Process { - triggerId: string; - startPoint?: Vector3; - endPoint?: Vector3; - speed: number; -} +function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { -function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) { - - const { isPlaying } = usePlayButtonStore(); const [currentPhase, setCurrentPhase] = useState<(string)>("init"); - const { scene } = useThree(); - const targetBone = "Target"; - const { activeModule } = useModuleStore(); + const [path, setPath] = useState<[number, number, number][]>([]); const [ikSolver, setIkSolver] = useState(null); + const { scene } = useThree(); + const restPosition = new THREE.Vector3(0, 1.75, -1.6); + const targetBone = "Target"; + const groupRef = useRef(null); + const pauseTimeRef = useRef(null); + const isPausedRef = useRef(false); + let startTime: number; + //zustand const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore(); const { floorItems } = useFloorItems(); - const groupRef = useRef(null); - const [processes, setProcesses] = useState([]); - const [armBotCurvePoints, setArmBotCurvePoints] = useState({ start: [], end: [] }) - const restPosition = new THREE.Vector3(0, 1, -1.6); - let armBotCurveRef = useRef(null) - const [path, setPath] = useState<[number, number, number][]>([]); + const { activeModule } = useModuleStore(); + const { isPlaying } = usePlayButtonStore(); + const { isReset, setReset } = useResetButtonStore(); + const { isPaused } = usePauseButtonStore(); + + function firstFrame() { + startTime = performance.now(); + step(); + } + function step() { + if (isPausedRef.current) { + if (!pauseTimeRef.current) { + pauseTimeRef.current = performance.now(); + } + requestAnimationFrame(() => step()); + return; + } + if (pauseTimeRef.current) { + const pauseDuration = performance.now() - pauseTimeRef.current; + startTime += pauseDuration; + pauseTimeRef.current = null; + } + const elapsedTime = performance.now() - startTime; + if (elapsedTime < 1500) { + // Wait until 1500ms has passed + requestAnimationFrame(step); + return; + } + if(currentPhase==="picking"){ + + setArmBotActive(armBot.modelUuid, true); + setArmBotState(armBot.modelUuid, "running"); + setCurrentPhase("start-to-end"); + startTime=0 + const startPoint = armBot.point.actions[0].process.startPoint; + const endPoint = armBot.point.actions[0].process.endPoint; + if (startPoint && endPoint) { + let curve = createCurveBetweenTwoPoints( + new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]), + new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2])); + if (curve) { + logStatus(armBot.modelUuid, "picking the object"); + setPath(curve.points.map(point => [point.x, point.y, point.z])) + } + } + logStatus(armBot.modelUuid, "Moving armBot from start point to end position.") + }else if(currentPhase==="dropping"){ + + setArmBotActive(armBot.modelUuid, true); + setArmBotState(armBot.modelUuid, "running"); + setCurrentPhase("end-to-rest"); + startTime=0; + const endPoint = armBot.point.actions[0].process.endPoint; + if (endPoint) { + let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition); + if (curve) { + logStatus(armBot.modelUuid, "dropping the object"); + setPath(curve.points.map(point => [point.x, point.y, point.z])); + } + } + logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.") + } + + } + useEffect(() => { + isPausedRef.current = isPaused; + }, [isPaused]); useEffect(() => { let armItems = floorItems?.filter((val: any) => - val.modeluuid === "3abf5d46-b59e-4e6b-9c02-a4634b64b82d" + val.modeluuid === armBot.modelUuid ); // Get the first matching item let armItem = armItems?.[0]; @@ -49,13 +107,30 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) { const targetBones = ikSolver?.mesh.skeleton.bones.find( (b: any) => b.name === targetBone ); - + if (isReset) { + logStatus(armBot.modelUuid, "Simulation Play Reset Successfully") + removeCurrentAction(armBot.modelUuid) + setArmBotActive(armBot.modelUuid, true) + setArmBotState(armBot.modelUuid, "running") + setCurrentPhase("init-to-rest"); + isPausedRef.current=false + pauseTimeRef.current=null + isPausedRef.current=false + startTime=0 + if (targetBones) { + let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition) + if (curve) { + setPath(curve.points.map(point => [point.x, point.y, point.z])); + } + } + setReset(false); + logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.") + } if (isPlaying) { //Moving armBot from initial point to rest position. - if (!robot?.isActive && robot?.state == "idle" && currentPhase == "init") { - - setArmBotActive(robot.modelUuid, true) - setArmBotState(robot.modelUuid, "running") + if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") { + setArmBotActive(armBot.modelUuid, true) + setArmBotState(armBot.modelUuid, "running") setCurrentPhase("init-to-rest"); if (targetBones) { let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition) @@ -63,21 +138,22 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) { setPath(curve.points.map(point => [point.x, point.y, point.z])); } } - logStatus(robot.modelUuid, "Moving armBot from initial point to rest position.") + logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.") } //Waiting for trigger. - else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "rest" && !robot.currentAction) { - logStatus(robot.modelUuid, "Waiting to trigger CurrentAction") - setTimeout(() => { - addCurrentAction(robot.modelUuid, 'action-003'); + else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { + logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction") + const timeoutId = setTimeout(() => { + addCurrentAction(armBot.modelUuid, 'action-003'); }, 3000); + return () => clearTimeout(timeoutId); } - else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "rest" && robot.currentAction) { - if (robot.currentAction) { - setArmBotActive(robot.modelUuid, true); - setArmBotState(robot.modelUuid, "running"); + else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) { + if (armBot.currentAction) { + setArmBotActive(armBot.modelUuid, true); + setArmBotState(armBot.modelUuid, "running"); setCurrentPhase("rest-to-start"); - const startPoint = robot.point.actions[0].process.startPoint; + const startPoint = armBot.point.actions[0].process.startPoint; if (startPoint) { let curve = createCurveBetweenTwoPoints(restPosition, new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2])); if (curve) { @@ -85,105 +161,396 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) { } } } - logStatus(robot.modelUuid, "Moving armBot from rest point to start position.") + logStatus(armBot.modelUuid, "Moving armBot from rest point to start position.") } - else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "picking" && robot.currentAction) { - setArmBotActive(robot.modelUuid, true); - setArmBotState(robot.modelUuid, "running"); - setCurrentPhase("start-to-end"); - const startPoint = robot.point.actions[0].process.startPoint; - const endPoint = robot.point.actions[0].process.endPoint; - if (startPoint && endPoint) { - let curve = createCurveBetweenTwoPoints( - new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]), - new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]) - ); - if (curve) { - setTimeout(() => { - logStatus(robot.modelUuid, "picking the object"); - setPath(curve.points.map(point => [point.x, point.y, point.z])); - }, 1500) - } - } - logStatus(robot.modelUuid, "Moving armBot from start point to end position.") + else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "picking" && armBot.currentAction) { + requestAnimationFrame(firstFrame); + // setArmBotActive(armBot.modelUuid, true); + // setArmBotState(armBot.modelUuid, "running"); + // setCurrentPhase("start-to-end"); + // const startPoint = armBot.point.actions[0].process.startPoint; + // const endPoint = armBot.point.actions[0].process.endPoint; + // if (startPoint && endPoint) { + // let curve = createCurveBetweenTwoPoints( + // new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]), + // new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2])); + // if (curve) { + // setTimeout(() => { + // logStatus(armBot.modelUuid, "picking the object"); + // setPath(curve.points.map(point => [point.x, point.y, point.z])); + // }, 1500) + // } + // } + // logStatus(armBot.modelUuid, "Moving armBot from start point to end position.") } - else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "dropping" && robot.currentAction) { - setArmBotActive(robot.modelUuid, true); - setArmBotState(robot.modelUuid, "running"); - setCurrentPhase("end-to-rest"); - const endPoint = robot.point.actions[0].process.endPoint; - if (endPoint) { - let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition - ); - if (curve) { - setTimeout(() => { - logStatus(robot.modelUuid, "dropping the object"); - setPath(curve.points.map(point => [point.x, point.y, point.z])); - }, 1500) - } - } - logStatus(robot.modelUuid, "Moving armBot from end point to rest position.") + else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "dropping" && armBot.currentAction) { + requestAnimationFrame(firstFrame); + // setArmBotActive(armBot.modelUuid, true); + // setArmBotState(armBot.modelUuid, "running"); + // setCurrentPhase("end-to-rest"); + // const endPoint = armBot.point.actions[0].process.endPoint; + // if (endPoint) { + // let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition); + // if (curve) { + // setTimeout(() => { + // logStatus(armBot.modelUuid, "dropping the object"); + // setPath(curve.points.map(point => [point.x, point.y, point.z])); + // }, 1500) + // } + // } + // logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.") } + } else { + logStatus(armBot.modelUuid, "Simulation Play Stopped") + setArmBotActive(armBot.modelUuid, false) + setArmBotState(armBot.modelUuid, "idle") + setCurrentPhase("init"); + setPath([]) + removeCurrentAction(armBot.modelUuid) + } - }, [currentPhase, robot, isPlaying, ikSolver]) + }, [currentPhase, armBot, isPlaying, ikSolver, isReset]) function createCurveBetweenTwoPoints(p1: any, p2: any) { const mid = new THREE.Vector3().addVectors(p1, p2).multiplyScalar(0.5); - mid.y += 0.5; - + // mid.y += 0.5; const points = [p1, mid, p2]; return new THREE.CatmullRomCurve3(points); } const HandleCallback = () => { - if (robot.isActive && robot.state == "running" && currentPhase == "init-to-rest") { - logStatus(robot.modelUuid, "Callback triggered: rest"); - setArmBotActive(robot.modelUuid, false) - setArmBotState(robot.modelUuid, "idle") + if (armBot.isActive && armBot.state == "running" && currentPhase == "init-to-rest") { + logStatus(armBot.modelUuid, "Callback triggered: rest"); + setArmBotActive(armBot.modelUuid, false) + setArmBotState(armBot.modelUuid, "idle") setCurrentPhase("rest"); setPath([]) } - else if (robot.isActive && robot.state == "running" && currentPhase == "rest-to-start") { - logStatus(robot.modelUuid, "Callback triggered: pick."); - setArmBotActive(robot.modelUuid, false) - setArmBotState(robot.modelUuid, "idle") + else if (armBot.isActive && armBot.state == "running" && currentPhase == "rest-to-start") { + logStatus(armBot.modelUuid, "Callback triggered: pick."); + setArmBotActive(armBot.modelUuid, false) + setArmBotState(armBot.modelUuid, "idle") setCurrentPhase("picking"); setPath([]) } - else if (robot.isActive && robot.state == "running" && currentPhase == "start-to-end") { - logStatus(robot.modelUuid, "Callback triggered: drop."); - setArmBotActive(robot.modelUuid, false) - setArmBotState(robot.modelUuid, "idle") + else if (armBot.isActive && armBot.state == "running" && currentPhase == "start-to-end") { + logStatus(armBot.modelUuid, "Callback triggered: drop."); + setArmBotActive(armBot.modelUuid, false) + setArmBotState(armBot.modelUuid, "idle") setCurrentPhase("dropping"); setPath([]) } - else if (robot.isActive && robot.state == "running" && currentPhase == "end-to-rest") { - logStatus(robot.modelUuid, "Callback triggered: rest, cycle completed."); - setArmBotActive(robot.modelUuid, false) - setArmBotState(robot.modelUuid, "idle") + else if (armBot.isActive && armBot.state == "running" && currentPhase == "end-to-rest") { + logStatus(armBot.modelUuid, "Callback triggered: rest, cycle completed."); + setArmBotActive(armBot.modelUuid, false) + setArmBotState(armBot.modelUuid, "idle") setCurrentPhase("rest"); setPath([]) - removeCurrentAction(robot.modelUuid) + removeCurrentAction(armBot.modelUuid) } } const logStatus = (id: string, status: string) => { - // console.log(id + "," + status); - console.log( status); + // console.log('status: ', status); } return ( <> - - + + ) } -export default RoboticArmInstance; \ No newline at end of file +export default RoboticArmInstance; + + + + + + + + + + + + +// import React, { useEffect, useRef, useState } from 'react'; +// import IKInstance from '../ikInstance/ikInstance'; +// import RoboticArmAnimator from '../animator/roboticArmAnimator'; +// import { usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; +// import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; +// import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_4.glb"; +// import { useThree } from "@react-three/fiber"; +// import { useFloorItems } from '../../../../../store/store'; +// import useModuleStore from '../../../../../store/useModuleStore'; +// import * as THREE from "three"; + +// function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { + +// const [currentPhase, setCurrentPhase] = useState("init"); +// const [path, setPath] = useState<[number, number, number][]>([]); +// const [ikSolver, setIkSolver] = useState(null); +// const { scene } = useThree(); +// const restPosition = new THREE.Vector3(0, 1, -1.6); +// const targetBone = "Target"; +// const groupRef = useRef(null); + +// // Zustand Stores +// const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore(); +// const { floorItems } = useFloorItems(); +// const { activeModule } = useModuleStore(); +// const { isPlaying } = usePlayButtonStore(); +// const { isReset, setReset } = useResetButtonStore(); + +// // Generate path according to constraints +// const generateArmPath = ( +// startPoint: [number, number, number], +// endPoint: [number, number, number], +// restPos: THREE.Vector3 = new THREE.Vector3(0, 1, -1.6) +// ): [number, number, number][] => { +// const path: [number, number, number][] = []; + +// const startVec = new THREE.Vector3(...startPoint); +// const endVec = new THREE.Vector3(...endPoint); + +// // Helper functions +// const addLinePoints = (from: THREE.Vector3, to: THREE.Vector3, segments: number) => { +// for (let i = 0; i <= segments; i++) { +// const t = i / segments; +// const point = new THREE.Vector3().lerpVectors(from, to, t); +// path.push([point.x, point.y, point.z]); +// } +// }; + +// const addCurveFromTo = (from: THREE.Vector3, to: THREE.Vector3, segments: number = 15) => { +// const mid = new THREE.Vector3() +// .addVectors(from, to) +// .multiplyScalar(0.5) +// .setY(Math.max(from.y, to.y) + 0.5); +// const curve = new THREE.CatmullRomCurve3([from, mid, to]); +// const points = curve.getPoints(segments); +// for (const p of points) { +// path.push([p.x, p.y, p.z]); +// } +// }; + +// // Step 1: Move vertically to align Y with start point +// const step1Start = startVec.clone().setY(restPos.y); +// const step1End = startVec.clone(); +// addLinePoints(step1Start, step1End, 10); + +// // Step 2: Move horizontally XZ to reach start point +// const step2Start = step1End.clone(); +// const step2End = startVec.clone(); // ✅ Fixed here +// addLinePoints(step2Start, step2End, 10); +// addLinePoints(step1End, step2End, 10); + +// // Optional Step 3: Move to rest/midpoint if needed +// const distanceToRest = startVec.distanceTo(restPos); +// if (distanceToRest > 1) { +// addCurveFromTo(step2End, restPos, 15); +// } + +// // Step 4: Move up/down to align Y with end point +// const step4Start = endVec.clone().setY(restPos.y); +// const step4End = endVec.clone(); +// addLinePoints(step4Start, step4End, 10); + +// // Step 5: Move horizontally XZ to end point +// addLinePoints(step4End, endVec.clone(), 10); + +// // Step 6: Return to rest or mid-position after dropping +// const finalRestHeight = restPos.clone().setY(endVec.y + 0.5); +// addCurveFromTo(endVec, finalRestHeight, 15); + +// return path; +// }; + +// useEffect(() => { +// let armItems = floorItems?.filter((val: any) => +// val.modeluuid === armBot.modelUuid +// ); +// let armItem = armItems?.[0]; +// if (armItem) { +// const targetMesh = scene?.getObjectByProperty("uuid", armItem.modeluuid); +// if (targetMesh) { +// targetMesh.visible = activeModule !== "simulation"; +// } +// } + +// const targetBones = ikSolver?.mesh.skeleton.bones.find( +// (b: any) => b.name === targetBone +// ); + +// if (isReset) { +// logStatus(armBot.modelUuid, "Simulation Play Reset Successfully"); +// removeCurrentAction(armBot.modelUuid); +// setArmBotActive(armBot.modelUuid, true); +// setArmBotState(armBot.modelUuid, "running"); +// setCurrentPhase("init-to-rest"); + +// if (targetBones) { +// const startPoint = new THREE.Vector3(targetBones.position.x, targetBones.position.y, targetBones.position.z); +// const curve = new THREE.CatmullRomCurve3([ +// startPoint, +// startPoint.clone().setY(restPosition.y + 0.5), +// restPosition.clone(), +// ]); +// setPath(curve.getPoints(30).map(p => [p.x, p.y, p.z])); +// } + +// setReset(false); +// logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position."); +// } + +// if (isPlaying) { +// // Phase 1: Initial -> Rest +// if (!armBot.isActive && armBot.state === "idle" && currentPhase === "init") { +// setArmBotActive(armBot.modelUuid, true); +// setArmBotState(armBot.modelUuid, "running"); +// setCurrentPhase("init-to-rest"); + +// if (targetBones) { +// const startPoint = new THREE.Vector3(targetBones.position.x, targetBones.position.y, targetBones.position.z); +// const curve = new THREE.CatmullRomCurve3([ +// startPoint, +// startPoint.clone().setY(restPosition.y + 0.5), +// restPosition.clone(), +// ]); +// setPath(curve.getPoints(30).map(p => [p.x, p.y, p.z])); +// } + +// logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position."); +// } + +// // Phase 2: Wait for trigger +// else if (!armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { +// logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction"); +// const timeoutId = setTimeout(() => { +// addCurrentAction(armBot.modelUuid, 'action-003'); +// }, 3000); +// return () => clearTimeout(timeoutId); +// } + +// // Phase 3: Rest -> Start Point +// else if (!armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) { +// setArmBotActive(armBot.modelUuid, true); +// setArmBotState(armBot.modelUuid, "running"); +// setCurrentPhase("rest-to-start"); + +// const startPoint = armBot.point.actions[0].process.startPoint; +// if (startPoint) { +// const start = new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]); +// const curve = new THREE.CatmullRomCurve3([restPosition, restPosition.clone().setY(start.y), start]); +// setPath(curve.getPoints(30).map(p => [p.x, p.y, p.z])); +// } + +// logStatus(armBot.modelUuid, "Moving armBot from rest point to start position."); +// } + +// // Phase 4: Start -> End (Pick & Move Object) +// else if (!armBot.isActive && armBot.state === "idle" && currentPhase === "picking" && armBot.currentAction) { +// setArmBotActive(armBot.modelUuid, true); +// setArmBotState(armBot.modelUuid, "running"); +// setCurrentPhase("start-to-end"); + +// const startPoint = armBot.point.actions[0].process.startPoint; +// const endPoint = armBot.point.actions[0].process.endPoint; + +// if (startPoint && endPoint) { +// const path = generateArmPath(startPoint, endPoint, restPosition); +// logStatus(armBot.modelUuid, "Waiting to pick."); +// setTimeout(()=>{ +// setPath(path); +// },3000) +// } + +// logStatus(armBot.modelUuid, "Moving armBot from start point to end position."); +// } + +// // Phase 5: End -> Rest (Drop & Return) +// else if (!armBot.isActive && armBot.state === "idle" && currentPhase === "dropping" && armBot.currentAction) { +// setArmBotActive(armBot.modelUuid, true); +// setArmBotState(armBot.modelUuid, "running"); +// setCurrentPhase("end-to-rest"); + +// const endPoint = armBot.point.actions[0].process.endPoint; +// if (endPoint) { +// const start = new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]); +// const curve = new THREE.CatmullRomCurve3([start, start.clone().setY(restPosition.y), restPosition]); +// logStatus(armBot.modelUuid, "Waiting to drop."); +// setTimeout(()=>{ +// setPath(curve.getPoints(30).map(p => [p.x, p.y, p.z])); +// },3000) + +// } + +// logStatus(armBot.modelUuid, "Moving armBot from end point to rest position."); +// } +// } else { +// logStatus(armBot.modelUuid, "Simulation Play Stopped"); +// setArmBotActive(armBot.modelUuid, false); +// setArmBotState(armBot.modelUuid, "idle"); +// setCurrentPhase("init"); +// setPath([]); +// removeCurrentAction(armBot.modelUuid); +// } + +// }, [currentPhase, armBot, isPlaying, ikSolver, isReset]); + +// const HandleCallback = () => { +// if (armBot.isActive && armBot.state === "running" && currentPhase === "init-to-rest") { +// logStatus(armBot.modelUuid, "Callback triggered: rest"); +// setArmBotActive(armBot.modelUuid, false); +// setArmBotState(armBot.modelUuid, "idle"); +// setCurrentPhase("rest"); +// setPath([]); +// } else if (armBot.isActive && armBot.state === "running" && currentPhase === "rest-to-start") { +// logStatus(armBot.modelUuid, "Callback triggered: pick."); +// setArmBotActive(armBot.modelUuid, false); +// setArmBotState(armBot.modelUuid, "idle"); +// setCurrentPhase("picking"); +// setPath([]); +// } else if (armBot.isActive && armBot.state === "running" && currentPhase === "start-to-end") { +// logStatus(armBot.modelUuid, "Callback triggered: drop."); +// setArmBotActive(armBot.modelUuid, false); +// setArmBotState(armBot.modelUuid, "idle"); +// setCurrentPhase("dropping"); +// setPath([]); +// } else if (armBot.isActive && armBot.state === "running" && currentPhase === "end-to-rest") { +// logStatus(armBot.modelUuid, "Callback triggered: rest, cycle completed."); +// setArmBotActive(armBot.modelUuid, false); +// setArmBotState(armBot.modelUuid, "idle"); +// setCurrentPhase("rest"); +// setPath([]); +// removeCurrentAction(armBot.modelUuid); +// } +// }; + +// const logStatus = (id: string, status: string) => { +// console.log( status); +// // console.log(`status [${id}]: ${status}`); +// }; + +// return ( +// <> +// +// +// +// ); +// } + +// export default RoboticArmInstance; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx index 645cbb5..e49f5bf 100644 --- a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx @@ -3,20 +3,18 @@ import * as THREE from "three"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { clone } from "three/examples/jsm/utils/SkeletonUtils"; -import { useFrame, useLoader, useThree } from "@react-three/fiber"; +import { useLoader, useThree } from "@react-three/fiber"; import { CCDIKSolver, CCDIKHelper, } from "three/examples/jsm/animation/CCDIKSolver"; + type IKInstanceProps = { modelUrl: string; ikSolver: any; setIkSolver: any - robot: any; + armBot: any; groupRef: React.RefObject; - processes: any; - setArmBotCurvePoints: any }; -function IKInstance({ modelUrl, setIkSolver, ikSolver, robot, groupRef, processes, setArmBotCurvePoints }: IKInstanceProps) { - - const { scene } = useThree(); +function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef}: IKInstanceProps) { + const {scene}=useThree() 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/"); @@ -25,6 +23,7 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, robot, groupRef, processe const cloned = useMemo(() => clone(gltf?.scene), [gltf]); const targetBoneName = "Target"; const skinnedMeshName = "link_0"; + useEffect(() => { if (!gltf) return; const OOI: any = {}; @@ -67,14 +66,14 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, robot, groupRef, processe const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05) - // scene.add(helper) + scene.add(helper) }, [gltf]); return ( <> - + {armBots?.map((robot: ArmBotStatus) => ( - + ))} diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx index 69e6da0..41d10f9 100644 --- a/app/src/modules/simulation/roboticArm/roboticArm.tsx +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -6,8 +6,6 @@ import { useFloorItems } from "../../../store/store"; function RoboticArm() { const { armBots, addArmBot, removeArmBot } = useArmBotStore(); const { floorItems } = useFloorItems(); - - const armBotStatusSample: RoboticArmEventSchema[] = [ { state: "idle", @@ -95,7 +93,7 @@ function RoboticArm() { position: [95.94347308985614, 0, 6.742905194869091], rotation: [0, 0, 0], type: "roboticArm", - speed: 1.5, + speed: 0.1, point: { uuid: "point-123", position: [0, 1.5, 0], diff --git a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts index b7e9272..d73b4a9 100644 --- a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts +++ b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts @@ -80,7 +80,7 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { // Add offset for dragging intersection.add(offsetRef.current); - console.log('intersection: ', intersection); + // Get the parent's world matrix if exists const parent = activeObjRef.current.parent; -- 2.40.1 From c95f140d30f7874a1c503478af3c8e71b06dd808 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Wed, 30 Apr 2025 10:30:30 +0530 Subject: [PATCH 02/32] path helper added --- .../instances/animator/roboticArmAnimator.tsx | 31 +++++++++++++++++-- .../instances/ikInstance/ikInstance.tsx | 19 +++++++++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index 611ded5..6db275f 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -91,7 +91,7 @@ function RoboticArmAnimator({ const bone = ikSolver.mesh.skeleton.bones.find( (b: any) => b.name === targetBone ); - bone.position.copy(restPosition); + // bone.position.copy(restPosition); ikSolver.update(); }; @@ -100,7 +100,7 @@ function RoboticArmAnimator({ return ( <> {/* {currentPath.length > 0 && ( - <> + {currentPath.map((point, index) => ( @@ -108,8 +108,33 @@ function RoboticArmAnimator({ ))} - + )} */} + {curvePoints && ( + + [p.x, p.y, p.z])} + color="green" + lineWidth={5} + dashed={false} + /> + {currentPath.length >= 1 && ( + <> + {/* First point */} + + + + + + {/* Last point */} + + + + + + )} + + )} ); } diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx index e49f5bf..d818777 100644 --- a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx @@ -3,18 +3,19 @@ import * as THREE from "three"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; import { clone } from "three/examples/jsm/utils/SkeletonUtils"; -import { useLoader, useThree } from "@react-three/fiber"; +import { useLoader, useThree } from "@react-three/fiber"; import { CCDIKSolver, CCDIKHelper, } from "three/examples/jsm/animation/CCDIKSolver"; +import { TransformControls } from '@react-three/drei'; type IKInstanceProps = { modelUrl: string; ikSolver: any; setIkSolver: any armBot: any; - groupRef: React.RefObject; + groupRef: any; }; -function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef}: IKInstanceProps) { - const {scene}=useThree() +function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKInstanceProps) { + const { scene } = useThree() 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/"); @@ -23,6 +24,7 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef}: IKInst const cloned = useMemo(() => clone(gltf?.scene), [gltf]); const targetBoneName = "Target"; const skinnedMeshName = "link_0"; + const [selectedArm, setSelectedArm] = useState(); useEffect(() => { if (!gltf) return; @@ -65,6 +67,9 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef}: IKInst setIkSolver(solver); const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05) + groupRef.current.add(helper); + console.log('OOI.Target_Bone: ', OOI.Target_Bone); + setSelectedArm(OOI.Target_Bone); scene.add(helper) @@ -73,7 +78,10 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef}: IKInst return ( <> - + { + setSelectedArm(groupRef.current?.getObjectByName(targetBoneName)) + } + }> + {/* {selectedArm && } */} ) } -- 2.40.1 From ff9bb8f5664001d1ea78b26ce654e2e8c350c5a6 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Wed, 30 Apr 2025 15:27:02 +0530 Subject: [PATCH 03/32] circular curve for arm movements added --- .../instances/animator/roboticArmAnimator.tsx | 244 ++++++++++++++---- .../instances/ikInstance/ikInstance.tsx | 11 +- .../simulation/roboticArm/roboticArm.tsx | 20 +- 3 files changed, 201 insertions(+), 74 deletions(-) diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index 6db275f..d1bf523 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -1,13 +1,13 @@ import React, { useEffect, useRef, useState } from 'react'; import { useFrame } from '@react-three/fiber'; import * as THREE from 'three'; +import { Line } from '@react-three/drei'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; -import { Line } from '@react-three/drei'; function RoboticArmAnimator({ HandleCallback, @@ -22,24 +22,35 @@ function RoboticArmAnimator({ const curveRef = useRef(null); const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); const [curvePoints, setCurvePoints] = useState(null); + const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]); + const [nearestStartPoint, setNearestStartPoint] = useState<[number, number, number] | null>(null); + const [nearestEndPoint, setNearestEndPoint] = useState<[number, number, number] | null>(null); + const [customCurvePoints, setCustomCurvePoints] = useState(null); + const stepSegmentsRef = useRef([]); + const [currentStep, setCurrentStep] = useState(0); + const elapsedTimeRef = useRef(0); // Zustand stores const { isPlaying } = usePlayButtonStore(); const { isPaused } = usePauseButtonStore(); - const { isReset } = useResetButtonStore(); // optional use depending on your logic + const { isReset } = useResetButtonStore(); const { speed } = useAnimationPlaySpeed(); + // Update path state whenever `path` prop changes useEffect(() => { - setCurrentPath(path) - }, [path]) + setCurrentPath(path); + }, [path]); + + // Reset logic when `isPlaying` changes useEffect(() => { if (!isPlaying) { - setCurrentPath([]) - curveRef.current = null + setCurrentPath([]); + curveRef.current = null; } - }, [isPlaying]) + }, [isPlaying]); + // Handle path generation (including CatmullRomCurve3) useEffect(() => { if (currentPath && currentPath.length === 3) { const [start, mid, end] = currentPath; @@ -51,18 +62,152 @@ function RoboticArmAnimator({ new THREE.Vector3(end[0], end[1], end[2]) ]; const generatedCurve = new THREE.CatmullRomCurve3(points, false, 'centripetal', 1).getPoints(100); - // console.log('generatedCurve: ', generatedCurve); curveRef.current = generatedCurve; setCurvePoints(generatedCurve); - } }, [currentPath]); + // Handle circle points based on armBot position + useEffect(() => { + const radius = 1.6; // Circle radius + const segments = 64; + const points: [number, number, number][] = []; + + for (let i = 0; i <= segments; i++) { + const theta = (i / segments) * Math.PI * 2; + const x = radius * Math.cos(theta) + armBot.position[0]; + const z = radius * Math.sin(theta) + armBot.position[2]; + const y = armBot.position[1]; + points.push([x, y, z]); + } + + setCirclePoints(points); + }, [armBot.position]); + + // Handle nearest points and final path (including arc points) + useEffect(() => { + if (circlePoints.length > 0 && currentPath.length > 0) { + const startPoint = [currentPath[0][0], currentPath[0][1] + 0.5, currentPath[0][2]] as [number, number, number]; + const endPoint = [currentPath[currentPath.length - 1][0], currentPath[currentPath.length - 1][1] + 0.5, currentPath[currentPath.length - 1][2]] as [number, number, number]; + + const findNearest = (target: [number, number, number]) => { + let nearestPoint = circlePoints[0]; + let minDistance = Infinity; + + for (const point of circlePoints) { + const dx = target[0] - point[0]; + const dy = target[1] - point[1]; + const dz = target[2] - point[2]; + const distance = Math.sqrt(dx * dx + dy * dy + dz * dz); + if (distance < minDistance) { + minDistance = distance; + nearestPoint = point; + } + } + return nearestPoint; + }; + + const nearestToStart = findNearest(startPoint); + const nearestToEnd = findNearest(endPoint); + + setNearestStartPoint(nearestToStart); + setNearestEndPoint(nearestToEnd); + + const radius = 1.6; + const segments = 64; + const center = armBot.position; + + const startAngle = Math.atan2(nearestToStart[2] - center[2], nearestToStart[0] - center[0]); + const endAngle = Math.atan2(nearestToEnd[2] - center[2], nearestToEnd[0] - center[0]); + let deltaAngle = endAngle - startAngle; + + if (deltaAngle > Math.PI) { + deltaAngle -= 2 * Math.PI; + } else if (deltaAngle < -Math.PI) { + deltaAngle += 2 * Math.PI; + } + + const arcPoints: [number, number, number][] = []; + + for (let i = 0; i <= segments; i++) { + const t = i / segments; + const angle = startAngle + deltaAngle * t; + const x = center[0] + radius * Math.cos(angle); + const z = center[2] + radius * Math.sin(angle); + const y = startPoint[1]; + arcPoints.push([x, y, z]); + } + const start = currentPath[0]; + const end = currentPath[currentPath.length - 1]; + + let step1 = [ + [start[0], start[1], start[2]], + [start[0], start[1] + 0.5, start[2]] + ]; + let step2 = [ + [start[0], start[1] + 0.5, start[2]], + [nearestToStart[0], start[1] + 0.5, nearestToStart[2]] + ]; + let step3 = [ + [nearestToStart[0], start[1] + 0.5, nearestToStart[2]], + [end[0], arcPoints[arcPoints.length - 1][1], end[2]] + ] + let step4 = [ + [end[0], arcPoints[arcPoints.length - 1][1], end[2]], + [end[0], end[1] + 0.5, end[2]] + ] + let step5 = [ + [end[0], end[1] + 0.5, end[2]], + [end[0], end[1], end[2]], + ] + + const points = [ + new THREE.Vector3(start[0], start[1], start[2]), + new THREE.Vector3(start[0], start[1] + 0.5, start[2]), + + new THREE.Vector3(start[0], start[1] + 0.5, start[2]), + new THREE.Vector3(nearestToStart[0], start[1] + 0.5, nearestToStart[2]), + + new THREE.Vector3(nearestToStart[0], start[1] + 0.5, nearestToStart[2]), + new THREE.Vector3(end[0], arcPoints[arcPoints.length - 1][1], end[2]), + + new THREE.Vector3(end[0], arcPoints[arcPoints.length - 1][1], end[2]), + new THREE.Vector3(end[0], end[1] + 0.5, end[2]), + + new THREE.Vector3(end[0], end[1] + 0.5, end[2]), + new THREE.Vector3(end[0], end[1], end[2]) + ]; + const steps = [ + [points[0], points[1]], // step1 + [points[2], points[3]], // step2 + [points[4], points[5]], // step3 + [points[6], points[7]], // step4 + [points[8], points[9]] // step5 + ]; + stepSegmentsRef.current = steps; + setCurrentStep(0); + elapsedTimeRef.current = 0; + + const finalPath = [ + new THREE.Vector3(start[0], start[1], start[2]), + new THREE.Vector3(start[0], start[1] + 0.5, start[2]), + new THREE.Vector3(nearestToStart[0], start[1] + 0.5, nearestToStart[2]), + ...arcPoints.map(([x, y, z]) => new THREE.Vector3(x, y, z)), + new THREE.Vector3(end[0], arcPoints[arcPoints.length - 1][1], end[2]), + new THREE.Vector3(end[0], end[1], end[2]) + ]; + + const customCurve = new THREE.CatmullRomCurve3(finalPath, false, 'centripetal', 1); + const customCurveGeneratedPoints = customCurve.getPoints(100); + + setCustomCurvePoints(customCurveGeneratedPoints); + } + }, [circlePoints, currentPath]); + + // Frame update for animation useFrame((_, delta) => { if (ikSolver && curveRef.current && currentPath.length >= 0) { - const bone = ikSolver.mesh.skeleton.bones.find( - (b: any) => b.name === targetBone - ); + const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); if (!bone) return; if (isPlaying) { @@ -70,71 +215,62 @@ function RoboticArmAnimator({ const curvePoints = curveRef.current; const speedAdjustedProgress = progressRef.current + (speed * armBot.speed); const index = Math.floor(speedAdjustedProgress); - if (index >= curvePoints.length) { - HandleCallback(); - setCurrentPath([]); - curveRef.current = null; - progressRef.current = 0; - } else { - const point = curvePoints[index]; - bone.position.copy(point); - progressRef.current = speedAdjustedProgress; + + if (curvePoints) { + + if (index >= curvePoints.length) { + HandleCallback(); + setCurrentPath([]); + curveRef.current = null; + progressRef.current = 0; + } else { + const point = curvePoints[index]; + bone.position.copy(point); + progressRef.current = speedAdjustedProgress; + } } + + } else { + logStatus(armBot.modelUuid, 'Simulation Play Exited'); } - } else { - logStatus(armBot.modelUuid, 'Simulation Play Exited'); - // bone.position.copy(restPosition); + + ikSolver.update(); } - - ikSolver.update(); } else if (ikSolver && !isPlaying && currentPath.length === 0) { - const bone = ikSolver.mesh.skeleton.bones.find( - (b: any) => b.name === targetBone - ); - // bone.position.copy(restPosition); + const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); + bone.position.copy(restPosition); ikSolver.update(); - }; - + } }); return ( + <> - {/* {currentPath.length > 0 && ( + {customCurvePoints && currentPath && ( - - {currentPath.map((point, index) => ( - - - - - ))} - - )} */} - {curvePoints && ( - - [p.x, p.y, p.z])} - color="green" - lineWidth={5} - dashed={false} - /> + [p.x, p.y, p.z])} color="green" lineWidth={5} dashed={false} /> + {/* */} {currentPath.length >= 1 && ( <> {/* First point */} - + {/* - + */} {/* Last point */} - + {/* - + */} )} )} + + + + ); } diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx index d818777..c40bc92 100644 --- a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx @@ -67,21 +67,20 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns setIkSolver(solver); const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05) - groupRef.current.add(helper); - console.log('OOI.Target_Bone: ', OOI.Target_Bone); + // groupRef.current.add(helper); + setSelectedArm(OOI.Target_Bone); - scene.add(helper) + // scene.add(helper) }, [gltf]); return ( <> - { + { setSelectedArm(groupRef.current?.getObjectByName(targetBoneName)) - } - }> + }}> Date: Wed, 30 Apr 2025 16:50:31 +0530 Subject: [PATCH 04/32] arm ui added --- .../simulation/roboticArm/roboticArm.tsx | 18 +- .../modules/simulation/ui/arm/armBotUI.tsx | 155 ++++++++++++++++++ 2 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 app/src/modules/simulation/ui/arm/armBotUI.tsx diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx index 37e350e..5a5bd38 100644 --- a/app/src/modules/simulation/roboticArm/roboticArm.tsx +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -2,9 +2,17 @@ import { useEffect } from "react"; import RoboticArmInstances from "./instances/roboticArmInstances"; import { useArmBotStore } from "../../../store/simulation/useArmBotStore"; import { useFloorItems } from "../../../store/store"; +import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; +import { useEventsStore } from "../../../store/simulation/useEventsStore"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import ArmBotUI from "../ui/arm/armBotUI"; function RoboticArm() { const { armBots, addArmBot, removeArmBot } = useArmBotStore(); + const { addEvent } = useEventsStore(); + const { selectedEventSphere } = useSelectedEventSphere(); + const { selectedEventData } = useSelectedEventData(); + const { isPlaying } = usePlayButtonStore(); const { floorItems } = useFloorItems(); const armBotStatusSample: RoboticArmEventSchema[] = [ { @@ -156,12 +164,18 @@ function RoboticArm() { }, []); useEffect(() => { - - }, [armBots]); + + console.log('isPlaying: ', isPlaying); + console.log('selectedEventData: ', selectedEventData); + console.log('selectedEventSphere: ', selectedEventSphere); + }, [selectedEventData, selectedEventSphere, isPlaying]); return ( <> + {selectedEventSphere && selectedEventData?.data.type === "roboticArm" && !isPlaying && + < ArmBotUI /> + } ); } diff --git a/app/src/modules/simulation/ui/arm/armBotUI.tsx b/app/src/modules/simulation/ui/arm/armBotUI.tsx new file mode 100644 index 0000000..7e111e3 --- /dev/null +++ b/app/src/modules/simulation/ui/arm/armBotUI.tsx @@ -0,0 +1,155 @@ +import React from 'react' +import PickDropPoints from './PickDropPoints'; +import { useSelectedEventData, useSelectedEventSphere } from '../../../../store/simulation/useSimulationStore'; +import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; +import useModuleStore from '../../../../store/useModuleStore'; +import { useGLTF } from '@react-three/drei'; +import armPick from "../../../../assets/gltf-glb/arm_ui_pick.glb"; +import armDrop from "../../../../assets/gltf-glb/arm_ui_drop.glb"; +import useDraggableGLTF from './useDraggableGLTF'; +import * as THREE from "three"; +import { useThree } from '@react-three/fiber'; +const ArmBotUI = () => { + console.log("called"); + const { selectedEventSphere } = useSelectedEventSphere(); + const { selectedEventData } = useSelectedEventData(); + const { armBots, updateArmBot } = useArmBotStore(); + const armUiPick = useGLTF(armPick) as any; + const { scene } = useThree(); + const armUiDrop = useGLTF(armDrop) as any; + + const getDefaultPositions = (modelUuid: string) => { + const modelData = getModelByUuid(modelUuid); + if (modelData) { + + const baseX = modelData.position?.[0] || 0; + const baseY = 2.6; + const baseZ = modelData.position?.[2] || 0; + return { + pick: [baseX, baseY, baseZ + 0.4], + drop: [baseX, baseY, baseZ - 1.2], + default: [baseX, baseY, baseZ - 1.5], + }; + } + return { + pick: [0.5, 1.5, 0], + drop: [-0.5, 1.5, 0], + default: [0, 1.5, 0], + }; + }; + const getModelByUuid = (modelUuid: string) => { + try { + const modelsJson = localStorage.getItem("FloorItems"); + if (modelsJson) { + const models = JSON.parse(modelsJson); + return models.find((m: any) => m.modeluuid === modelUuid); + } + const storeModels = (useModuleStore.getState() as any).models || []; + return storeModels.find((m: any) => m.modelUuid === modelUuid); + } catch (error) { } + return null; + }; + const updatePointToState = (obj: THREE.Object3D) => { + const { modelUuid, pointUuid, actionType, actionUuid } = obj.userData; + + const newPosition = new THREE.Vector3(); + obj.getWorldPosition(newPosition); + const worldPositionArray = newPosition.toArray(); + + const armBot = armBots.find((a) => a.modelUuid === modelUuid); + if (!armBot) return; + + const updatedActions = armBot.point.actions.map((action: any) => { + if (action.actionUuid === actionUuid) { + const updatedProcess = { ...action.process }; + + if (actionType === "pick") { + updatedProcess.startPoint = getLocalPosition(modelUuid, worldPositionArray); + } + if (actionType === "drop") { + updatedProcess.endPoint = getLocalPosition(modelUuid, worldPositionArray); + } + return { + ...action, + process: updatedProcess, + }; + } + return action; + }); + + updateArmBot(modelUuid, { + point: { + ...armBot.point, + actions: updatedActions, + }, + }); + }; + function getLocalPosition(parentUuid: string, worldPosArray: [number, number, number] | null): [number, number, number] | null { + if (worldPosArray) { + const worldPos = new THREE.Vector3(...worldPosArray); + const localPos = worldPos.clone(); + + const parentObject = scene.getObjectByProperty('uuid', parentUuid); + if (parentObject) { + parentObject.worldToLocal(localPos); + + return [localPos.x, localPos.y, localPos.z]; + } else { + + } + } + return null; + } + const { handlePointerDown } = useDraggableGLTF(updatePointToState); + + return ( + <> + {armBots.map((event: any) => { + const defaultPositions = getDefaultPositions(event.modelUuid); + const isSelected = + selectedEventSphere?.userData?.modelUuid === event.modelUuid; + + return event.point.actions.map((action: any) => { + if (action.actionType === "pickAndPlace") { + const pickPosition = action.process.startPoint || defaultPositions.pick; + const dropPosition = action.process.endPoint || defaultPositions.drop; + + return ( + + {/* Pick Point */} + + + {/* Drop Point */} + + + ); + } + return null; + }); + })} + + ); + + +} +export default ArmBotUI \ No newline at end of file -- 2.40.1 From 29efeab38738b5b2bfb05abac388420470465d2a Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Wed, 30 Apr 2025 20:16:26 +0530 Subject: [PATCH 05/32] Enhance event handling and backend updates across mechanics components; refactor trigger management in TriggerConnector --- .../mechanics/machineMechanics.tsx | 35 +++- .../mechanics/storageMechanics.tsx | 51 +++++- .../mechanics/vehicleMechanics.tsx | 73 +++++++- .../geomentries/assets/addAssetModel.ts | 67 +++++-- .../selectionControls/moveControls.tsx | 33 +++- .../selectionControls/rotateControls.tsx | 33 +++- .../events/addOrRemoveEventsInProducts.tsx | 13 +- app/src/modules/simulation/simulation.tsx | 2 +- .../triggers/connector/triggerConnector.tsx | 168 +++++++++++++----- .../instances/animator/vehicleAnimator.tsx | 1 - app/src/store/simulation/useEventsStore.ts | 5 +- app/src/store/simulation/useProductStore.ts | 7 +- 12 files changed, 399 insertions(+), 89 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx index 993c46b..1a03986 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx @@ -6,6 +6,7 @@ import { useSelectedEventData, useSelectedProduct } from "../../../../../../stor import { useProductStore } from "../../../../../../store/simulation/useProductStore"; import ProcessAction from "../actions/ProcessAction"; import ActionsList from "../components/ActionsList"; +import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi"; function MachineMechanics() { const [activeOption, setActiveOption] = useState<"default" | "process">("default"); @@ -14,6 +15,9 @@ function MachineMechanics() { const { getPointByUuid, updateAction } = useProductStore(); const { selectedProduct } = useSelectedProduct(); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + useEffect(() => { if (selectedEventData) { const point = getPointByUuid( @@ -28,31 +32,54 @@ function MachineMechanics() { } }, [selectedProduct, selectedEventData, getPointByUuid]); + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData + }) + } + const handleActionTypeChange = (option: string) => { if (!selectedEventData || !selectedPointData) return; const validOption = option as "process"; setActiveOption(validOption); - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedPointData.action.actionUuid, { actionType: validOption, }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleRenameAction = (newName: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + const event = updateAction(selectedPointData.action.actionUuid, { actionName: newName }); }; const handleProcessTimeChange = (value: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedPointData.action.actionUuid, { processTime: parseFloat(value), }); }; const handleMaterialSelect = (material: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedPointData.action.actionUuid, { swapMaterial: material, }); }; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx index c85da9f..f782019 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx @@ -6,6 +6,7 @@ import { useSelectedEventData, useSelectedProduct } from "../../../../../../stor import { useProductStore } from "../../../../../../store/simulation/useProductStore"; import StorageAction from "../actions/StorageAction"; import ActionsList from "../components/ActionsList"; +import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi"; function StorageMechanics() { const [activeOption, setActiveOption] = useState<"default" | "store" | "spawn">("default"); @@ -14,6 +15,9 @@ function StorageMechanics() { const { getPointByUuid, updateAction } = useProductStore(); const { selectedProduct } = useSelectedProduct(); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + useEffect(() => { if (selectedEventData) { const point = getPointByUuid( @@ -28,26 +32,67 @@ function StorageMechanics() { } }, [selectedProduct, selectedEventData, getPointByUuid]); + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData + }) + } + const handleActionTypeChange = (option: string) => { if (!selectedEventData || !selectedPointData) return; const validOption = option as "store" | "spawn"; setActiveOption(validOption); - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedPointData.action.actionUuid, { actionType: validOption, }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleRenameAction = (newName: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + const event = updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleCapacityChange = (value: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedPointData.action.actionUuid, { storageCapacity: parseInt(value), }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; // Get current values from store diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx index 28eb61f..98eb63e 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx @@ -10,6 +10,7 @@ import { import { useProductStore } from "../../../../../../store/simulation/useProductStore"; import TravelAction from "../actions/TravelAction"; import ActionsList from "../components/ActionsList"; +import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi"; function VehicleMechanics() { const [activeOption, setActiveOption] = useState<"default" | "travel">("default"); @@ -18,6 +19,9 @@ function VehicleMechanics() { const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction } = useProductStore(); const { selectedProduct } = useSelectedProduct(); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + useEffect(() => { if (selectedEventData) { const point = getPointByUuid( @@ -33,11 +37,34 @@ function VehicleMechanics() { } }, [selectedProduct, selectedEventData, getPointByUuid]); + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData + }) + } + const handleSpeedChange = (value: string) => { if (!selectedEventData) return; - updateEvent(selectedProduct.productId, selectedEventData.data.modelUuid, { + const event = updateEvent(selectedProduct.productId, selectedEventData.data.modelUuid, { speed: parseFloat(value), }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleActionTypeChange = (option: string) => { @@ -45,28 +72,64 @@ function VehicleMechanics() { const validOption = option as "travel"; setActiveOption(validOption); - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedPointData.action.actionUuid, { actionType: validOption, }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleRenameAction = (newName: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + const event = updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleLoadCapacityChange = (value: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedPointData.action.actionUuid, { loadCapacity: parseFloat(value), }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handleUnloadDurationChange = (value: string) => { if (!selectedPointData) return; - updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedPointData.action.actionUuid, { unLoadDuration: parseFloat(value), }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } }; const handlePickPointChange = (value: string) => { diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index c718ec3..64656a0 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -186,22 +186,59 @@ async function handleModelLoad( state: "idle", type: 'transfer', speed: 1, - points: data.points.map((point: THREE.Vector3, index: number) => ({ - uuid: THREE.MathUtils.generateUUID(), - position: [point.x, point.y, point.z], - rotation: [0, 0, 0], - action: { - actionUuid: THREE.MathUtils.generateUUID(), - actionName: `Action ${index}`, - actionType: 'default', - material: 'Default Material', - delay: 0, - spawnInterval: 5, - spawnCount: 1, - triggers: [] + points: data.points.map((point: THREE.Vector3, index: number) => { + const triggers: TriggerSchema[] = []; + + if (data.points && index < data.points.length - 1) { + triggers.push({ + triggerUuid: THREE.MathUtils.generateUUID(), + triggerName: `Trigger 1`, + triggerType: "onComplete", + delay: 0, + triggeredAsset: { + triggeredModel: { + modelName: newFloorItem.modelName, + modelUuid: newFloorItem.modelUuid + }, + triggeredPoint: { + pointName: `Point`, + pointUuid: "" + }, + triggeredAction: { + actionName: `Action 1`, + actionUuid: "" + } + } + }); } - })) + + return { + uuid: THREE.MathUtils.generateUUID(), + position: [point.x, point.y, point.z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: `Action 1`, + actionType: 'default', + material: 'Default Material', + delay: 0, + spawnInterval: 5, + spawnCount: 1, + triggers: triggers + } + }; + }) }; + + for (let i = 0; i < ConveyorEvent.points.length - 1; i++) { + const currentPoint = ConveyorEvent.points[i]; + const nextPoint = ConveyorEvent.points[i + 1]; + + if (currentPoint.action.triggers.length > 0) { + currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint.pointUuid = nextPoint.uuid; + currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid = nextPoint.action.actionUuid; + } + } addEvent(ConveyorEvent); eventData.points = ConveyorEvent.points.map(point => ({ uuid: point.uuid, @@ -228,7 +265,7 @@ async function handleModelLoad( actionType: "travel", unLoadDuration: 5, loadCapacity: 10, - steeringAngle:0, + steeringAngle: 0, pickUpPoint: null, unLoadPoint: null, triggers: [] diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index d371806..cc9ce50 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -9,6 +9,7 @@ import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifie import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; +import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); @@ -16,10 +17,28 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const { selectedProduct } = useSelectedProduct(); const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); const itemsData = useRef([]); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData + }) + } + useEffect(() => { if (!camera || !scene || toggleView || !itemsGroupRef.current) return; @@ -190,10 +209,19 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje }) } if (productData) { - useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, { + const event = useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, { position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], }) + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } } } @@ -203,9 +231,6 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje return updatedItems; }); - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : "default"; - //REST // await setFloorItemApi( diff --git a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx index 08667b4..7ea7045 100644 --- a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx @@ -8,6 +8,7 @@ import * as Types from "../../../../types/world/worldTypes"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; +import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); @@ -15,10 +16,28 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets(); + const { selectedProduct } = useSelectedProduct(); const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); const itemsData = useRef([]); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData + }) + } + const prevPointerPosition = useRef(null); useEffect(() => { @@ -190,10 +209,19 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo }) } if (productData) { - useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, { + const event = useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, { position: [worldPosition.x, worldPosition.y, worldPosition.z], rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], }) + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } } } @@ -203,9 +231,6 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo return updatedItems; }); - const email = localStorage.getItem("email"); - const organization = email ? email.split("@")[1].split(".")[0] : "default"; - //REST // await setFloorItemApi( diff --git a/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx b/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx index 9eececc..715aa37 100644 --- a/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx +++ b/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx @@ -53,7 +53,17 @@ function AddOrRemoveEventsInProducts() { const canvasElement = gl.domElement; if (!canvasElement) return; - let intersects = raycaster.intersectObjects(scene.children, true); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.name.includes("agv-collider") && + !(intersect.object.type === "GridHelper") && + !(intersect.object?.parent?.name.includes('zones')) && + !(intersect.object?.parent?.name.includes('Zone')) + ); if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) { let currentObject = intersects[0].object; @@ -116,6 +126,7 @@ function AddOrRemoveEventsInProducts() { }; }, [gl, subModule, selectedProduct, selectedAsset]); + return ( <> ) diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 5ca0ec5..757a9ef 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -23,7 +23,7 @@ function Simulation() { }, [events]) useEffect(() => { - // console.log('products: ', products); + console.log('products: ', products); }, [products]) return ( diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx index efefa0c..b554979 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -7,21 +7,24 @@ import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; import { handleAddEventToProduct } from "../../events/points/functions/handleAddEventToProduct"; +import { QuadraticBezierLine } from "@react-three/drei"; +import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; interface ConnectionLine { id: string; - start: THREE.Vector3; - end: THREE.Vector3; - mid: THREE.Vector3; + startPointUuid: string; + endPointUuid: string; trigger: TriggerSchema; } function TriggerConnector() { const { gl, raycaster, scene } = useThree(); const { subModule } = useSubModuleStore(); - const { products, getPointByUuid, getIsEventInProduct, getActionByUuid, addTrigger, addEvent, getEventByModelUuid } = useProductStore(); + const { products, getPointByUuid, getIsEventInProduct, getActionByUuid, addTrigger, addEvent, getEventByModelUuid, getProductById } = useProductStore(); const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); const { selectedProduct } = useSelectedProduct(); + const [hoveredLineKey, setHoveredLineKey] = useState(null); + const groupRefs = useRef>({}); const [firstSelectedPoint, setFirstSelectedPoint] = useState<{ productId: string; @@ -32,52 +35,59 @@ function TriggerConnector() { const [connections, setConnections] = useState([]); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData + }) + } + useEffect(() => { const newConnections: ConnectionLine[] = []; - products.forEach(product => { - product.eventDatas.forEach(event => { - if ('points' in event) { - event.points.forEach(point => { - if ('action' in point && point.action?.triggers) { - point.action.triggers.forEach(trigger => { - if (trigger.triggeredAsset) { - const targetPoint = getPointByUuid( - product.productId, - trigger.triggeredAsset.triggeredModel.modelUuid, - trigger.triggeredAsset.triggeredPoint.pointUuid - ); + const product = getProductById(selectedProduct.productId); + if (!product || products.length === 0) return; - if (targetPoint) { - const startPos = new THREE.Vector3(...point.position); - const endPos = new THREE.Vector3(...targetPoint.position); - const midPos = new THREE.Vector3() - .addVectors(startPos, endPos) - .multiplyScalar(0.5) - .add(new THREE.Vector3(0, 2, 0)); + product.eventDatas.forEach(event => { + if ('points' in event) { + event.points.forEach(point => { + if ('action' in point && point.action?.triggers) { + point.action.triggers.forEach(trigger => { + if (trigger.triggeredAsset) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + } + }); + } else if ('point' in event) { + if ('actions' in event.point) { + console.log(event); - newConnections.push({ - id: `${point.uuid}-${targetPoint.uuid}-${trigger.triggerUuid}`, - start: startPos, - end: endPos, - mid: midPos, - trigger - }); - } - } - }); - } - }); + // you left here + + } else if ('action' in event.point) { + console.log(event); } - }); + } }); setConnections(newConnections); - }, [products]); - - useEffect(() => { - console.log('connections: ', connections); - }, connections) + }, [products, selectedProduct.productId]); useEffect(() => { const canvasElement = gl.domElement; @@ -111,7 +121,12 @@ function TriggerConnector() { if (drag) return; evt.preventDefault(); - const intersects = raycaster.intersectObjects(scene.children, true); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + intersect.object.name === ('Event-Sphere') + ); if (intersects.length === 0) return; const currentObject = intersects[0].object; @@ -128,7 +143,9 @@ function TriggerConnector() { pointUuid ); - if (!point) return; + const event = getEventByModelUuid(selectedProduct.productId, modelUuid); + + if (!point || !event) return; let actionUuid: string | undefined; if ('action' in point && point.action) { @@ -152,12 +169,12 @@ function TriggerConnector() { delay: 0, triggeredAsset: { triggeredModel: { - modelName: currentObject.parent?.parent?.name || 'Unknown', + modelName: event.modelName || 'Unknown', modelUuid: modelUuid }, triggeredPoint: { - pointName: currentObject.name, - pointUuid: pointUuid + pointName: 'Point', + pointUuid: point.uuid }, triggeredAction: actionUuid ? { actionName: getActionByUuid(selectedProduct.productId, actionUuid)?.actionName || 'Action', @@ -167,7 +184,16 @@ function TriggerConnector() { }; if (firstSelectedPoint.actionUuid) { - addTrigger(firstSelectedPoint.actionUuid, trigger); + const event = addTrigger(firstSelectedPoint.actionUuid, trigger); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } } setFirstSelectedPoint(null); } @@ -237,9 +263,53 @@ function TriggerConnector() { }, [gl, subModule, selectedProduct, firstSelectedPoint]); + const getWorldPositionFromScene = (pointUuid: string): THREE.Vector3 | null => { + const pointObj = scene.getObjectByProperty("uuid", pointUuid); + if (!pointObj) return null; + + const worldPosition = new THREE.Vector3(); + pointObj.getWorldPosition(worldPosition); + return worldPosition; + }; + return ( - <> - + + {connections.map((connection) => { + const startPoint = getWorldPositionFromScene(connection.startPointUuid); + const endPoint = getWorldPositionFromScene(connection.endPointUuid); + + if (!startPoint || !endPoint) return null; + + const distance = startPoint.distanceTo(endPoint); + const heightFactor = Math.max(0.5, distance * 0.2); + const midPoint = new THREE.Vector3( + (startPoint.x + endPoint.x) / 2, + Math.max(startPoint.y, endPoint.y) + heightFactor, + (startPoint.z + endPoint.z) / 2 + ); + + return ( + (groupRefs.current[connection.id] = el!)} + start={startPoint.toArray()} + end={endPoint.toArray()} + mid={midPoint.toArray()} + color={hoveredLineKey === connection.id ? "red" : "#42a5f5"} + lineWidth={4} + dashed={hoveredLineKey !== connection.id} + dashSize={0.75} + dashScale={20} + onPointerOver={() => setHoveredLineKey(connection.id)} + onPointerOut={() => setHoveredLineKey(null)} + onClick={() => { + console.log("Connection clicked:", connection); + }} + userData={connection.trigger} + /> + ); + })} + ); } diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 2f0b235..421dea8 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -67,7 +67,6 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai setRestingRotation(true); decrementVehicleLoad(agvDetail.modelUuid, 0); const object = scene.getObjectByProperty('uuid', agvUuid); - console.log('currentPhase: ', currentPhase); if (object) { object.position.set(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]); object.rotation.set(objectRotation.x, objectRotation.y, objectRotation.z); diff --git a/app/src/store/simulation/useEventsStore.ts b/app/src/store/simulation/useEventsStore.ts index 2d92fc2..8fa6838 100644 --- a/app/src/store/simulation/useEventsStore.ts +++ b/app/src/store/simulation/useEventsStore.ts @@ -7,7 +7,7 @@ type EventsStore = { // Event-level actions addEvent: (event: EventsSchema) => void; removeEvent: (modelUuid: string) => void; - updateEvent: (modelUuid: string, updates: Partial) => void; + updateEvent: (modelUuid: string, updates: Partial) => EventsSchema | undefined; // Point-level actions addPoint: (modelUuid: string, point: ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema) => void; @@ -60,12 +60,15 @@ export const useEventsStore = create()( }, updateEvent: (modelUuid, updates) => { + let updatedEvent: EventsSchema | undefined; set((state) => { const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event) { Object.assign(event, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); } }); + return updatedEvent; }, // Point-level actions diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index 4f2b546..0918128 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -43,7 +43,7 @@ type ProductsStore = { addTrigger: ( actionUuid: string, trigger: TriggerSchema - ) => void; + ) => EventsSchema | undefined; removeTrigger: (triggerUuid: string) => void; updateTrigger: ( triggerUuid: string, @@ -284,6 +284,7 @@ export const useProductStore = create()( // Trigger-level actions addTrigger: (actionUuid, trigger) => { + let updatedEvent: EventsSchema | undefined; set((state) => { for (const product of state.products) { for (const event of product.eventDatas) { @@ -291,6 +292,7 @@ export const useProductStore = create()( for (const point of (event as ConveyorEventSchema).points) { if (point.action && point.action.actionUuid === actionUuid) { point.action.triggers.push(trigger); + updatedEvent = JSON.parse(JSON.stringify(event)); return; } } @@ -298,11 +300,13 @@ export const useProductStore = create()( const point = (event as any).point; if ('action' in point && point.action.actionUuid === actionUuid) { point.action.triggers.push(trigger); + updatedEvent = JSON.parse(JSON.stringify(event)); return; } else if ('actions' in point) { const action = point.actions.find((a: any) => a.actionUuid === actionUuid); if (action) { action.triggers.push(trigger); + updatedEvent = JSON.parse(JSON.stringify(event)); return; } } @@ -310,6 +314,7 @@ export const useProductStore = create()( } } }); + return updatedEvent; }, removeTrigger: (triggerUuid) => { -- 2.40.1 From 571da0a78a431e4ac37f224a12910a444474a0e5 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 2 May 2025 10:50:52 +0530 Subject: [PATCH 06/32] Refactor Machine and TriggerConnector components: enhance event handling for machine points and streamline vehicle status sample initialization --- .../modules/simulation/machine/machine.tsx | 23 +++++++ .../triggers/connector/triggerConnector.tsx | 60 +++++++++++++++---- .../modules/simulation/vehicle/vehicles.tsx | 8 +-- 3 files changed, 76 insertions(+), 15 deletions(-) diff --git a/app/src/modules/simulation/machine/machine.tsx b/app/src/modules/simulation/machine/machine.tsx index 32c3b8e..96a5b39 100644 --- a/app/src/modules/simulation/machine/machine.tsx +++ b/app/src/modules/simulation/machine/machine.tsx @@ -2,6 +2,29 @@ import React from 'react' import MachineInstances from './instances/machineInstances' function Machine() { + + const machineSample: MachineEventSchema = { + modelUuid: "machine-1234-5678-9012", + modelName: "CNC Milling Machine", + position: [10, 0, 5], + rotation: [0, 0, 0], + state: "idle", + type: "machine", + point: { + uuid: "machine-point-9876-5432-1098", + position: [10, 0.5, 5.2], + rotation: [0, 0, 0], + action: { + actionUuid: "machine-action-2468-1357-8024", + actionName: "Metal Processing", + actionType: "process", + processTime: 10, + swapMaterial: "steel", + triggers: [] + } + } + }; + return ( <> diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx index b554979..5c4c602 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -59,9 +59,10 @@ function TriggerConnector() { if (!product || products.length === 0) return; product.eventDatas.forEach(event => { - if ('points' in event) { + // Handle Conveyor points + if (event.type === "transfer" && 'points' in event) { event.points.forEach(point => { - if ('action' in point && point.action?.triggers) { + if (point.action?.triggers) { point.action.triggers.forEach(trigger => { if (trigger.triggeredAsset) { newConnections.push({ @@ -74,14 +75,53 @@ function TriggerConnector() { }); } }); - } else if ('point' in event) { - if ('actions' in event.point) { - console.log(event); - - // you left here - - } else if ('action' in event.point) { - console.log(event); + } + // Handle Vehicle point + else if (event.type === "vehicle" && 'point' in event) { + const point = event.point; + if (point.action?.triggers) { + point.action.triggers.forEach(trigger => { + if (trigger.triggeredAsset) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + } + } + // Handle Robotic Arm points + else if (event.type === "roboticArm" && 'point' in event) { + const point = event.point; + point.actions?.forEach(action => { + action.triggers?.forEach(trigger => { + if (trigger.triggeredAsset) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); + }); + } + // Handle Machine point + else if (event.type === "machine" && 'point' in event) { + const point = event.point; + if (point.action?.triggers) { + point.action.triggers.forEach(trigger => { + if (trigger.triggeredAsset) { + newConnections.push({ + id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, + startPointUuid: point.uuid, + endPointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid, + trigger + }); + } + }); } } }); diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx index 7badec5..61dc1a0 100644 --- a/app/src/modules/simulation/vehicle/vehicles.tsx +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -13,9 +13,7 @@ function Vehicles() { const { floorItems } = useFloorItems(); const { isPlaying } = usePlayButtonStore(); - const [vehicleStatusSample, setVehicleStatusSample] = useState< - VehicleEventSchema[] - >([ + const [vehicleStatusSample, setVehicleStatusSample] = useState([ { modelUuid: "9356f710-4727-4b50-bdb2-9c1e747ecc74", modelName: "AGV", @@ -34,7 +32,7 @@ function Vehicles() { actionType: "travel", unLoadDuration: 10, loadCapacity: 2, - steeringAngle:0, + steeringAngle: 0, pickUpPoint: { position: { x: 98.71483985219794, y: 0, z: 28.66321267938962 }, rotation: { x: 0, y: 0, z: 0 } }, unLoadPoint: { position: { x: 105.71483985219794, y: 0, z: 28.66321267938962 }, rotation: { x: 0, y: 0, z: 0 } }, triggers: [ @@ -78,7 +76,7 @@ function Vehicles() { actionType: "travel", unLoadDuration: 10, loadCapacity: 2, - steeringAngle:0, + steeringAngle: 0, pickUpPoint: null, unLoadPoint: null, triggers: [ -- 2.40.1 From 2a669f63372efe153c294b7f75e676081a3b8abc Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Fri, 2 May 2025 11:15:11 +0530 Subject: [PATCH 07/32] removed sample data and worked with schema for agv --- .../mechanics/vehicleMechanics.tsx | 2 +- .../IntialLoad/loadInitialFloorItems.ts | 2 +- .../events/points/creator/pointsCreator.tsx | 10 +- .../armInstance/roboticArmInstance.tsx | 4 +- .../instances/animator/vehicleAnimator.tsx | 2 +- .../instances/instance/vehicleInstance.tsx | 6 +- .../modules/simulation/vehicle/vehicles.tsx | 215 ++---------------- 7 files changed, 34 insertions(+), 207 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx index 28eb61f..68464c9 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx @@ -19,7 +19,7 @@ function VehicleMechanics() { const { selectedProduct } = useSelectedProduct(); useEffect(() => { - if (selectedEventData) { + if (selectedEventData && selectedEventData.data.type === 'vehicle') { const point = getPointByUuid( selectedProduct.productId, selectedEventData.data.modelUuid, diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index a351d73..27175ee 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -187,7 +187,7 @@ function processLoadedModel( }, ]); - if (item.eventData.type === "vehicle") { + if (item.eventData.type === "Vehicle") { const vehicleEvent: VehicleEventSchema = { modelUuid: item.modelUuid, modelName: item.modelName, diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index c6ec316..36ec89a 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -93,9 +93,7 @@ function PointsCreator() { ); }} onPointerMissed={() => { - if (selectedEventData?.data.type !== 'vehicle') { - clearSelectedEventSphere(); - } + // clearSelectedEventSphere(); setTransformMode(null); }} key={`${i}-${j}`} @@ -123,7 +121,7 @@ function PointsCreator() { ); }} onPointerMissed={() => { - clearSelectedEventSphere(); + // clearSelectedEventSphere(); setTransformMode(null); }} position={new THREE.Vector3(...event.point.position)} @@ -149,7 +147,7 @@ function PointsCreator() { ); }} onPointerMissed={() => { - clearSelectedEventSphere(); + // clearSelectedEventSphere(); setTransformMode(null); }} position={new THREE.Vector3(...event.point.position)} @@ -175,7 +173,7 @@ function PointsCreator() { ); }} onPointerMissed={() => { - clearSelectedEventSphere(); + // clearSelectedEventSphere(); setTransformMode(null); }} position={new THREE.Vector3(...event.point.position)} diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 6bde587..af3ffe3 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -170,8 +170,8 @@ function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) { } } const logStatus = (id: string, status: string) => { - // console.log(id + "," + status); - console.log( status); + // + } return ( diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 2f0b235..f17efcb 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -67,7 +67,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai setRestingRotation(true); decrementVehicleLoad(agvDetail.modelUuid, 0); const object = scene.getObjectByProperty('uuid', agvUuid); - console.log('currentPhase: ', currentPhase); + if (object) { object.position.set(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]); object.rotation.set(objectRotation.x, objectRotation.y, objectRotation.z); diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index 6a81d3a..412a4ba 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -30,7 +30,8 @@ function VehicleInstance({ agvDetail }: any) { ); function vehicleStatus(modelId: string, status: string) { - // console.log(`${modelId} , ${status}); + // console.log(`${modelId} , ${status}`); + } // Function to reset everything @@ -44,7 +45,7 @@ function VehicleInstance({ agvDetail }: any) { const increment = () => { if (isIncrememtable.current) { - incrementVehicleLoad(agvDetail.modelUuid, 2); + incrementVehicleLoad(agvDetail.modelUuid, 10); isIncrememtable.current = false; } } @@ -69,6 +70,7 @@ function VehicleInstance({ agvDetail }: any) { increment(); }, 5000); + if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) { const toDrop = computePath( agvDetail.point.action.pickUpPoint.position, diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx index 7badec5..9691372 100644 --- a/app/src/modules/simulation/vehicle/vehicles.tsx +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -2,208 +2,38 @@ import React, { useEffect, useState } from "react"; import VehicleInstances from "./instances/vehicleInstances"; import { useVehicleStore } from "../../../store/simulation/useVehicleStore"; import { useFloorItems } from "../../../store/store"; -import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; +import { useSelectedEventData, useSelectedEventSphere, useSelectedProduct } from "../../../store/simulation/useSimulationStore"; import VehicleUI from "../ui/vehicle/vehicleUI"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; -function Vehicles() { +import { useProductStore } from "../../../store/simulation/useProductStore"; - const { vehicles, addVehicle } = useVehicleStore(); +function Vehicles() { + const { getProductById } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + const { vehicles, addVehicle, removeVehicle } = useVehicleStore(); const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventData } = useSelectedEventData(); const { floorItems } = useFloorItems(); const { isPlaying } = usePlayButtonStore(); - const [vehicleStatusSample, setVehicleStatusSample] = useState< - VehicleEventSchema[] - >([ - { - modelUuid: "9356f710-4727-4b50-bdb2-9c1e747ecc74", - modelName: "AGV", - position: [97.9252965204558, 0, 37.96138815638661], - rotation: [0, 0, 0], - state: "idle", - type: "vehicle", - speed: 2.5, - point: { - uuid: "point-789", - position: [0, 1, 0], - rotation: [0, 0, 0], - action: { - actionUuid: "action-456", - actionName: "Deliver to Zone A", - actionType: "travel", - unLoadDuration: 10, - loadCapacity: 2, - steeringAngle:0, - pickUpPoint: { position: { x: 98.71483985219794, y: 0, z: 28.66321267938962 }, rotation: { x: 0, y: 0, z: 0 } }, - unLoadPoint: { position: { x: 105.71483985219794, y: 0, z: 28.66321267938962 }, rotation: { x: 0, y: 0, z: 0 } }, - triggers: [ - { - triggerUuid: "trig-001", - triggerName: "Start Travel", - triggerType: "onComplete", - delay: 0, - triggeredAsset: { - triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" }, - triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" }, - triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" } - } - }, - { - triggerUuid: "trig-002", - triggerName: "Complete Travel", - triggerType: "onComplete", - delay: 2, - triggeredAsset: null - } - ] - } - } - }, - { - modelUuid: "b06960bb-3d2e-41f7-a646-335f389c68b4", - modelName: "AGV", - position: [89.61609306554463, 0, 33.634136622267356], - rotation: [0, 0, 0], - state: "idle", - type: "vehicle", - speed: 2.5, - point: { - uuid: "point-789", - position: [0, 1, 0], - rotation: [0, 0, 0], - action: { - actionUuid: "action-456", - actionName: "Deliver to Zone A", - actionType: "travel", - unLoadDuration: 10, - loadCapacity: 2, - steeringAngle:0, - pickUpPoint: null, - unLoadPoint: null, - triggers: [ - { - triggerUuid: "trig-001", - triggerName: "Start Travel", - triggerType: "onStart", - delay: 0, - triggeredAsset: { - triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" }, - triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" }, - triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" } - } - }, - { - triggerUuid: "trig-002", - triggerName: "Complete Travel", - triggerType: "onComplete", - delay: 2, - triggeredAsset: null - } - ] - } - } - }, - // { - // modelUuid: "cd7d0584-0684-42b4-b051-9e882c1914aa", - // modelName: "AGV", - // position: [105.90938758014703, 0, 31.584209911095215], - // rotation: [0, 0, 0], - // state: "idle", - // type: "vehicle", - // speed: 2.5, - // point: { - // uuid: "point-789", - // position: [0, 1, 0], - // rotation: [0, 0, 0], - // action: { - // actionUuid: "action-456", - // actionName: "Deliver to Zone A", - // actionType: "travel", - // unLoadDuration: 10, - // loadCapacity: 2, - // steeringAngle:0, - // pickUpPoint: null, - // unLoadPoint: null, - // triggers: [ - // { - // triggerUuid: "trig-001", - // triggerName: "Start Travel", - // triggerType: "onStart", - // delay: 0, - // triggeredAsset: { - // triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" }, - // triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" }, - // triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" } - // } - // }, - // { - // triggerUuid: "trig-002", - // triggerName: "Complete Travel", - // triggerType: "onComplete", - // delay: 2, - // triggeredAsset: null - // } - // ] - // } - // } - // }, - // { - // modelUuid: "e729a4f1-11d2-4778-8d6a-468f1b4f6b79", - // modelName: "forklift", - // position: [98.85729337188162, 0, 38.36616546567653], - // rotation: [0, 0, 0], - // state: "idle", - // type: "vehicle", - // speed: 2.5, - // point: { - // uuid: "point-789", - // position: [0, 1, 0], - // rotation: [0, 0, 0], - // action: { - // actionUuid: "action-456", - // actionName: "Deliver to Zone A", - // actionType: "travel", - // unLoadDuration: 15, - // loadCapacity: 5, - // steeringAngle:0, - // pickUpPoint: null, - // unLoadPoint: null, - // triggers: [ - // { - // triggerUuid: "trig-001", - // triggerName: "Start Travel", - // triggerType: "onStart", - // delay: 0, - // triggeredAsset: { - // triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" }, - // triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" }, - // triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" } - // } - // }, - // { - // triggerUuid: "trig-002", - // triggerName: "Complete Travel", - // triggerType: "onComplete", - // delay: 2, - // triggeredAsset: null - // } - // ] - // } - // } - // } - ]); useEffect(() => { + if (selectedProduct.productId) { + const product = getProductById(selectedProduct.productId); + if (product) { + product.eventDatas.forEach(events => { + if (events.type === 'vehicle') { + removeVehicle(events.modelUuid); + addVehicle(selectedProduct.productId, events); + } + }); + } + } + }, [selectedProduct]); + useEffect(() => { + // console.log('vehicles: ', vehicles); }, [vehicles]) - useEffect(() => { - addVehicle("123", vehicleStatusSample[0]); - addVehicle('123', vehicleStatusSample[1]); - // addVehicle('123', vehicleStatusSample[2]); - }, []); - - return ( <> @@ -214,7 +44,4 @@ function Vehicles() { ); } -export default Vehicles; - - - +export default Vehicles; \ No newline at end of file -- 2.40.1 From d3f5c5e506f74fbb494f5bc93ad03fa7ef8f92f7 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 2 May 2025 11:35:03 +0530 Subject: [PATCH 08/32] Enhance TriggerConnector and useProductStore: add removeTrigger return value and improve event handling in TriggerConnector --- .../triggers/connector/triggerConnector.tsx | 105 ++++++++++++++++-- app/src/store/simulation/useProductStore.ts | 7 +- 2 files changed, 104 insertions(+), 8 deletions(-) diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx index 5c4c602..7eb0e81 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from "react"; -import { useThree } from "@react-three/fiber"; +import { useFrame, useThree } from "@react-three/fiber"; import * as THREE from "three"; import { useSubModuleStore } from "../../../../store/useModuleStore"; import { useSelectedAsset } from "../../../../store/simulation/useSimulationStore"; @@ -9,6 +9,7 @@ import { useSelectedProduct } from "../../../../store/simulation/useSimulationSt import { handleAddEventToProduct } from "../../events/points/functions/handleAddEventToProduct"; import { QuadraticBezierLine } from "@react-three/drei"; import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; +import { useDeleteTool } from "../../../../store/store"; interface ConnectionLine { id: string; @@ -18,13 +19,16 @@ interface ConnectionLine { } function TriggerConnector() { - const { gl, raycaster, scene } = useThree(); + const { gl, raycaster, scene, pointer, camera } = useThree(); const { subModule } = useSubModuleStore(); - const { products, getPointByUuid, getIsEventInProduct, getActionByUuid, addTrigger, addEvent, getEventByModelUuid, getProductById } = useProductStore(); + const { products, getPointByUuid, getIsEventInProduct, getActionByUuid, addTrigger, removeTrigger, addEvent, getEventByModelUuid, getProductById } = useProductStore(); const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); const { selectedProduct } = useSelectedProduct(); const [hoveredLineKey, setHoveredLineKey] = useState(null); const groupRefs = useRef>({}); + const [helperlineColor, setHelperLineColor] = useState("red"); + const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3; end: THREE.Vector3; mid: THREE.Vector3; } | null>(null); + const { deleteTool } = useDeleteTool(); const [firstSelectedPoint, setFirstSelectedPoint] = useState<{ productId: string; @@ -303,6 +307,61 @@ function TriggerConnector() { }, [gl, subModule, selectedProduct, firstSelectedPoint]); + + useFrame(() => { + if (firstSelectedPoint) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObjects(scene.children, true).filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("agv-collider") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.userData.isPathObject && + !(intersect.object.type === "GridHelper") + ); + + let point: THREE.Vector3 | null = null; + + if (intersects.length > 0) { + point = intersects[0].point; + if (point.y < 0.05) { + point = new THREE.Vector3(point.x, 0.05, point.z); + } + } + + const sphereIntersects = raycaster.intersectObjects(scene.children, true).filter((intersect) => intersect.object.name === ('Event-Sphere')); + const startPoint = getWorldPositionFromScene(firstSelectedPoint.pointUuid); + + if (point && startPoint) { + + const distance = startPoint.distanceTo(point); + const heightFactor = Math.max(0.5, distance * 0.2); + const midPoint = new THREE.Vector3( + (startPoint.x + point.x) / 2, + Math.max(startPoint.y, point.y) + heightFactor, + (startPoint.z + point.z) / 2 + ); + + setCurrentLine({ + start: startPoint, + mid: midPoint, + end: point, + }); + + if (sphereIntersects.length > 0) { + setHelperLineColor("#6cf542"); + } else { + setHelperLineColor("red"); + } + } else { + setCurrentLine(null); + } + } else { + setCurrentLine(null); + } + + }) + const getWorldPositionFromScene = (pointUuid: string): THREE.Vector3 | null => { const pointObj = scene.getObjectByProperty("uuid", pointUuid); if (!pointObj) return null; @@ -312,8 +371,23 @@ function TriggerConnector() { return worldPosition; }; + const removeConnection = (connection: ConnectionLine) => { + if (connection.trigger.triggerUuid) { + const event = removeTrigger(connection.trigger.triggerUuid); + if (event) { + console.log('event: ', event); + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + } + }; + return ( - + {connections.map((connection) => { const startPoint = getWorldPositionFromScene(connection.startPointUuid); const endPoint = getWorldPositionFromScene(connection.endPointUuid); @@ -335,20 +409,37 @@ function TriggerConnector() { start={startPoint.toArray()} end={endPoint.toArray()} mid={midPoint.toArray()} - color={hoveredLineKey === connection.id ? "red" : "#42a5f5"} + color={deleteTool && hoveredLineKey === connection.id ? "red" : "#42a5f5"} lineWidth={4} - dashed={hoveredLineKey !== connection.id} + dashed={deleteTool && hoveredLineKey === connection.id ? false : true} dashSize={0.75} dashScale={20} onPointerOver={() => setHoveredLineKey(connection.id)} onPointerOut={() => setHoveredLineKey(null)} onClick={() => { - console.log("Connection clicked:", connection); + if (deleteTool) { + setHoveredLineKey(null); + setCurrentLine(null); + removeConnection(connection); + } }} userData={connection.trigger} /> ); })} + + {currentLine && ( + + )} ); } diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index 0918128..a47bcdb 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -44,7 +44,7 @@ type ProductsStore = { actionUuid: string, trigger: TriggerSchema ) => EventsSchema | undefined; - removeTrigger: (triggerUuid: string) => void; + removeTrigger: (triggerUuid: string) => EventsSchema | undefined; updateTrigger: ( triggerUuid: string, updates: Partial @@ -318,6 +318,7 @@ export const useProductStore = create()( }, removeTrigger: (triggerUuid) => { + let updatedEvent: EventsSchema | undefined; set((state) => { for (const product of state.products) { for (const event of product.eventDatas) { @@ -325,16 +326,19 @@ export const useProductStore = create()( for (const point of (event as ConveyorEventSchema).points) { if (point.action && 'triggers' in point.action) { point.action.triggers = point.action.triggers.filter(t => t.triggerUuid !== triggerUuid); + updatedEvent = JSON.parse(JSON.stringify(event)); } } } else if ('point' in event) { const point = (event as any).point; if ('action' in point && 'triggers' in point.action) { point.action.triggers = point.action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + updatedEvent = JSON.parse(JSON.stringify(event)); } else if ('actions' in point) { for (const action of point.actions) { if ('triggers' in action) { action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + updatedEvent = JSON.parse(JSON.stringify(event)); } } } @@ -342,6 +346,7 @@ export const useProductStore = create()( } } }); + return updatedEvent; }, updateTrigger: (triggerUuid, updates) => { -- 2.40.1 From 96530b981f378e3002f01f872dc1202af99957fe Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 2 May 2025 11:54:40 +0530 Subject: [PATCH 09/32] Add dragging and rotating state management to simulation store; enhance PointsCreator and VehicleUI components --- .../events/points/creator/pointsCreator.tsx | 74 ++++++++++++++----- .../simulation/ui/vehicle/vehicleUI.tsx | 10 +-- .../store/simulation/useSimulationStore.ts | 32 ++++++++ 3 files changed, 93 insertions(+), 23 deletions(-) diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index 36ec89a..e178a81 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -1,22 +1,29 @@ import React, { useEffect, useRef, useState } from "react"; import * as THREE from "three"; import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; -import useModuleStore from "../../../../../store/useModuleStore"; +import useModuleStore, { useSubModuleStore } from "../../../../../store/useModuleStore"; import { TransformControls } from "@react-three/drei"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; import { useSelectedEventSphere, useSelectedEventData, + useIsDragging, + useIsRotating, } from "../../../../../store/simulation/useSimulationStore"; +import { useThree } from "@react-three/fiber"; function PointsCreator() { + const { gl, raycaster, scene, pointer, camera } = useThree(); + const { subModule } = useSubModuleStore(); const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore(); const { activeModule } = useModuleStore(); const transformRef = useRef(null); const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere(); - const { selectedEventData, setSelectedEventData, clearSelectedEventData } = useSelectedEventData(); + const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData(); + const { isDragging } = useIsDragging(); + const { isRotating } = useIsRotating(); useEffect(() => { if (selectedEventSphere) { @@ -72,6 +79,53 @@ function PointsCreator() { } }; + useEffect(() => { + const canvasElement = gl.domElement; + + let drag = false; + let isMouseDown = false; + + const onMouseDown = () => { + isMouseDown = true; + drag = false; + }; + + const onMouseUp = () => { + if (selectedEventSphere && !drag) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + intersect.object.name === ('Event-Sphere') + ); + if (intersects.length === 0) { + clearSelectedEventSphere(); + setTransformMode(null); + } + } + } + + const onMouseMove = () => { + if (isMouseDown) { + drag = true; + } + }; + + if (subModule === 'mechanics') { + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + } + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + }; + + }, [gl, subModule, selectedEventSphere]); + return ( <> {activeModule === "simulation" && ( @@ -92,10 +146,6 @@ function PointsCreator() { sphereRefs.current[point.uuid] ); }} - onPointerMissed={() => { - // clearSelectedEventSphere(); - setTransformMode(null); - }} key={`${i}-${j}`} position={new THREE.Vector3(...point.position)} // rotation={new THREE.Euler(...point.rotation)} @@ -120,10 +170,6 @@ function PointsCreator() { sphereRefs.current[event.point.uuid] ); }} - onPointerMissed={() => { - // clearSelectedEventSphere(); - setTransformMode(null); - }} position={new THREE.Vector3(...event.point.position)} // rotation={new THREE.Euler(...event.point.rotation)} userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} @@ -146,10 +192,6 @@ function PointsCreator() { sphereRefs.current[event.point.uuid] ); }} - onPointerMissed={() => { - // clearSelectedEventSphere(); - setTransformMode(null); - }} position={new THREE.Vector3(...event.point.position)} // rotation={new THREE.Euler(...event.point.rotation)} userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} @@ -172,10 +214,6 @@ function PointsCreator() { sphereRefs.current[event.point.uuid] ); }} - onPointerMissed={() => { - // clearSelectedEventSphere(); - setTransformMode(null); - }} position={new THREE.Vector3(...event.point.position)} // rotation={new THREE.Euler(...event.point.rotation)} userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} diff --git a/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx b/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx index 5dec724..43ba833 100644 --- a/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx +++ b/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx @@ -4,7 +4,7 @@ import startEnd from "../../../../assets/gltf-glb/arrow_red.glb"; import * as THREE from "three"; import { useGLTF } from '@react-three/drei'; import { useFrame, useThree } from '@react-three/fiber'; -import { useSelectedEventSphere } from '../../../../store/simulation/useSimulationStore'; +import { useSelectedEventSphere, useIsDragging, useIsRotating } from '../../../../store/simulation/useSimulationStore'; import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; import * as Types from "../../../../types/world/worldTypes"; const VehicleUI = () => { @@ -19,8 +19,8 @@ const VehicleUI = () => { const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 0, 0]); const [startRotation, setStartRotation] = useState<[number, number, number]>([0, 0, 0]); const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0]); - const [isDragging, setIsDragging] = useState<"start" | "end" | null>(null); - const [isRotating, setIsRotating] = useState<"start" | "end" | null>(null); + const { isDragging, setIsDragging } = useIsDragging(); + const { isRotating, setIsRotating } = useIsRotating(); const { raycaster } = useThree(); const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); const state: Types.ThreeState = useThree(); @@ -46,13 +46,13 @@ const VehicleUI = () => { pickUpPoint.rotation.y, pickUpPoint.rotation.z ); - pickupPosition.y = 0; + pickupPosition.y = 0; setStartPosition([pickupPosition.x, 0, pickupPosition.z]); setStartRotation([pickupRotation.x, pickupRotation.y, pickupRotation.z]); } else { const defaultLocal = new THREE.Vector3(0, 0, 1.5); const defaultWorld = selectedEventSphere.localToWorld(defaultLocal); - defaultWorld.y = 0; + defaultWorld.y = 0; setStartPosition([defaultWorld.x, 0, defaultWorld.z]); setStartRotation([0, 0, 0]); } diff --git a/app/src/store/simulation/useSimulationStore.ts b/app/src/store/simulation/useSimulationStore.ts index 5085688..6e4321f 100644 --- a/app/src/store/simulation/useSimulationStore.ts +++ b/app/src/store/simulation/useSimulationStore.ts @@ -114,4 +114,36 @@ export const useSelectedAction = create()( }); }, })) +); + +interface IsDraggingState { + isDragging: "start" | "end" | null; + setIsDragging: (state: "start" | "end" | null) => void; +} + +export const useIsDragging = create()( + immer((set) => ({ + isDragging: null, + setIsDragging: (state) => { + set((s) => { + s.isDragging = state; + }); + }, + })) +); + +interface IsRotatingState { + isRotating: "start" | "end" | null; + setIsRotating: (state: "start" | "end" | null) => void; +} + +export const useIsRotating = create()( + immer((set) => ({ + isRotating: null, + setIsRotating: (state) => { + set((s) => { + s.isRotating = state; + }); + }, + })) ); \ No newline at end of file -- 2.40.1 From 2ff2f850f3cd2f0bab4713f7fd96f28255750c38 Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Fri, 2 May 2025 11:56:04 +0530 Subject: [PATCH 10/32] feat: handle snap function added --- app/src/utils/handleSnap.ts | 65 +++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 app/src/utils/handleSnap.ts diff --git a/app/src/utils/handleSnap.ts b/app/src/utils/handleSnap.ts new file mode 100644 index 0000000..35a0f96 --- /dev/null +++ b/app/src/utils/handleSnap.ts @@ -0,0 +1,65 @@ +import { useEffect } from "react"; +import { detectModifierKeys } from "./shortcutkeys/detectModifierKeys"; + +// Define the props expected by the component +type SnapControlsProps = { + x_point: number; // X-coordinate value to snap + z_point: number; // Z-coordinate value to snap +}; + +const SnapControls: React.FC = ({ x_point, z_point }) => { + useEffect(() => { + // Handler function for keypress events + const handleKeyPress = (event: KeyboardEvent) => { + // Detect which modifier keys (Ctrl, Shift, etc.) are currently pressed + const keyCombination = detectModifierKeys(event); + + // Define the snapping distances + const CTRL_DISTANCE = 1; // Coarse snapping when Ctrl is pressed + const SHIFT_DISTANCE = 0.01; // Fine snapping when Shift is pressed + + // Prevent default behavior to avoid unintended side effects + event.preventDefault(); + + // Create new coordinates to apply snapping to + let newX = x_point; + let newZ = z_point; + + // Check for modifier key combinations and apply appropriate snapping logic + if (keyCombination) { + if (keyCombination === "Ctrl") { + // Snap to nearest integer unit + newX = Math.round(x_point / CTRL_DISTANCE) * CTRL_DISTANCE; + newZ = Math.round(z_point / CTRL_DISTANCE) * CTRL_DISTANCE; + return { newX, newZ }; + } + + if (keyCombination === "Ctrl+Shift") { + // Snap to nearest small unit (0.01) + newX = Math.round(x_point / SHIFT_DISTANCE) * SHIFT_DISTANCE; + newZ = Math.round(z_point / SHIFT_DISTANCE) * SHIFT_DISTANCE; + return { newX, newZ }; + } + + if (keyCombination === "Shift") { + // Incorrect snapping logic — rounding the point and multiplying by small value + newX = Math.round(x_point) * SHIFT_DISTANCE; + newZ = Math.round(z_point) * SHIFT_DISTANCE; + return { newX, newZ }; + } + } + }; + + // Attach keydown event listener when the component mounts + window.addEventListener("keydown", handleKeyPress); + + // Clean up the event listener when the component unmounts + return () => { + window.removeEventListener("keydown", handleKeyPress); + }; + }, []); // Empty dependency array means this effect runs once on mount + + return null; // This component doesn’t render anything +}; + +export default SnapControls; -- 2.40.1 From 34c30bb5a270d5d9d7677a44339de3e8e5ca2b1b Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 2 May 2025 12:11:09 +0530 Subject: [PATCH 11/32] Enhance Machine and Vehicle components: add current action management and update machine sample structure --- .../modules/simulation/machine/machine.tsx | 51 ++++++++++++------- .../modules/simulation/vehicle/vehicles.tsx | 15 +++++- app/src/store/simulation/useMachineStore.ts | 28 ++++++++++ app/src/types/simulationTypes.d.ts | 4 ++ 4 files changed, 78 insertions(+), 20 deletions(-) diff --git a/app/src/modules/simulation/machine/machine.tsx b/app/src/modules/simulation/machine/machine.tsx index 96a5b39..e9d2dea 100644 --- a/app/src/modules/simulation/machine/machine.tsx +++ b/app/src/modules/simulation/machine/machine.tsx @@ -1,29 +1,42 @@ -import React from 'react' +import React, { useEffect } from 'react' import MachineInstances from './instances/machineInstances' +import { useMachineStore } from '../../../store/simulation/useMachineStore' +import { useSelectedProduct } from '../../../store/simulation/useSimulationStore'; function Machine() { + const { addMachine, addCurrentAction, removeMachine } = useMachineStore(); + const { selectedProduct } = useSelectedProduct(); - const machineSample: MachineEventSchema = { - modelUuid: "machine-1234-5678-9012", - modelName: "CNC Milling Machine", - position: [10, 0, 5], - rotation: [0, 0, 0], - state: "idle", - type: "machine", - point: { - uuid: "machine-point-9876-5432-1098", - position: [10, 0.5, 5.2], + const machineSample: MachineEventSchema[] = [ + { + modelUuid: "machine-1234-5678-9012", + modelName: "CNC Milling Machine", + position: [10, 0, 5], rotation: [0, 0, 0], - action: { - actionUuid: "machine-action-2468-1357-8024", - actionName: "Metal Processing", - actionType: "process", - processTime: 10, - swapMaterial: "steel", - triggers: [] + state: "idle", + type: "machine", + point: { + uuid: "machine-point-9876-5432-1098", + position: [10, 0.5, 5.2], + rotation: [0, 0, 0], + action: { + actionUuid: "machine-action-2468-1357-8024", + actionName: "Metal Processing", + actionType: "process", + processTime: 10, + swapMaterial: "steel", + triggers: [] + } } } - }; + ]; + + useEffect(() => { + removeMachine(machineSample[0].modelUuid); + addMachine(selectedProduct.productId, machineSample[0]); + + // addCurrentAction(machineSample[0].modelUuid, machineSample[0].point.action.actionUuid); + }, []) return ( <> diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx index 9691372..a4d72a1 100644 --- a/app/src/modules/simulation/vehicle/vehicles.tsx +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -8,7 +8,7 @@ import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import { useProductStore } from "../../../store/simulation/useProductStore"; function Vehicles() { - const { getProductById } = useProductStore(); + const { products, getProductById } = useProductStore(); const { selectedProduct } = useSelectedProduct(); const { vehicles, addVehicle, removeVehicle } = useVehicleStore(); const { selectedEventSphere } = useSelectedEventSphere(); @@ -29,6 +29,19 @@ function Vehicles() { } } }, [selectedProduct]); + + // useEffect(() => { + // vehicles.forEach(vehicle => { + // const product = getProductById(vehicle.productId); + // if (product) { + // const eventData = product.eventDatas.find(event => event.modelUuid === vehicle.modelUuid); + // if (eventData) { + // vehicle.eventData = eventData; + // } + // } + // }); + // }, [vehicles, products]); + useEffect(() => { // console.log('vehicles: ', vehicles); diff --git a/app/src/store/simulation/useMachineStore.ts b/app/src/store/simulation/useMachineStore.ts index cc927f7..af7119c 100644 --- a/app/src/store/simulation/useMachineStore.ts +++ b/app/src/store/simulation/useMachineStore.ts @@ -12,6 +12,9 @@ interface MachineStore { updates: Partial> ) => void; + addCurrentAction: (modelUuid: string, actionUuid: string) => void; + removeCurrentAction: (modelUuid: string) => void; + // Status updates setMachineActive: (modelUuid: string, isActive: boolean) => void; setMachineState: (modelUuid: string, newState: MachineStatus['state']) => void; @@ -61,6 +64,31 @@ export const useMachineStore = create()( }); }, + + addCurrentAction: (modelUuid) => { + set((state) => { + const armBot = state.machines.find(a => a.modelUuid === modelUuid); + if (armBot) { + const action = armBot.point.action; + if (action) { + armBot.currentAction = { + actionUuid: action.actionUuid, + actionName: action.actionName, + }; + } + } + }); + }, + + removeCurrentAction: (modelUuid) => { + set((state) => { + const armBot = state.machines.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.currentAction = undefined; + } + }); + }, + // Status updates setMachineActive: (modelUuid, isActive) => { set((state) => { diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index cb71864..346e408 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -143,6 +143,10 @@ interface MachineStatus extends MachineEventSchema { isActive: boolean; idleTime: number; activeTime: number; + currentAction?: { + actionUuid: string; + actionName: string; + }; } interface ArmBotStatus extends RoboticArmEventSchema { -- 2.40.1 From 01a03f5166dac43c7ca7343b8e2b57587ed6b964 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 2 May 2025 13:13:41 +0530 Subject: [PATCH 12/32] Refactor action updates to include productId in updateAction calls across mechanics components; enhance event handling in product store and trigger management. Add clear functions for various stores to reset state. Update action and trigger management to prevent duplicates and ensure integrity. Adjust initial load actions to use consistent naming conventions. --- .../components/ActionsList.tsx | 2 +- .../mechanics/conveyorMechanics.tsx | 12 +- .../mechanics/machineMechanics.tsx | 8 +- .../mechanics/roboticArmMechanics.tsx | 8 +- .../mechanics/storageMechanics.tsx | 6 +- .../mechanics/vehicleMechanics.tsx | 8 +- .../IntialLoad/loadInitialFloorItems.ts | 8 +- .../modules/simulation/products/products.tsx | 5 +- .../triggers/connector/triggerConnector.tsx | 7 +- .../modules/simulation/vehicle/vehicles.tsx | 26 +--- .../visualization/RealTimeVisulization.tsx | 5 +- app/src/store/simulation/useArmBotStore.ts | 26 ++-- app/src/store/simulation/useConveyorStore.ts | 26 ++-- app/src/store/simulation/useEventsStore.ts | 38 +++-- app/src/store/simulation/useMachineStore.ts | 33 ++--- app/src/store/simulation/useProductStore.ts | 135 ++++++++++++------ .../store/simulation/useStorageUnitStore.ts | 38 ++--- app/src/store/simulation/useVehicleStore.ts | 28 ++-- app/src/types/simulationTypes.d.ts | 1 + 19 files changed, 245 insertions(+), 175 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx index 09017de..8aae3b2 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/components/ActionsList.tsx @@ -35,7 +35,7 @@ const ActionsList: React.FC = ({ const handleRenameAction = (newName: string) => { if (!selectedAction.actionId) return; - const event = renameAction(selectedAction.actionId, newName); + const event = renameAction(selectedProduct.productId, selectedAction.actionId, newName); if (event) { upsertProductOrEventApi({ diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx index f68b6b8..ded65af 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx @@ -72,7 +72,7 @@ function ConveyorMechanics() { const validOption = option as | "default" | "spawn" | "swap" | "delay" | "despawn"; setActiveOption(validOption); - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionType: validOption, }); @@ -88,7 +88,7 @@ function ConveyorMechanics() { const handleRenameAction = (newName: string) => { if (!selectedEventData || !selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionName: newName }); if (event) { updateBackend( @@ -102,7 +102,7 @@ function ConveyorMechanics() { const handleSpawnCountChange = (value: string) => { if (!selectedEventData || !selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { spawnCount: value === "inherit" ? "inherit" : parseFloat(value), }); @@ -118,7 +118,7 @@ function ConveyorMechanics() { const handleSpawnIntervalChange = (value: string) => { if (!selectedEventData || !selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { spawnInterval: value === "inherit" ? "inherit" : parseFloat(value), }); @@ -134,7 +134,7 @@ function ConveyorMechanics() { const handleMaterialSelect = (material: string) => { if (!selectedEventData || !selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { material }); + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { material }); if (event) { updateBackend( @@ -148,7 +148,7 @@ function ConveyorMechanics() { const handleDelayChange = (value: string) => { if (!selectedEventData || !selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { delay: value === "inherit" ? "inherit" : parseFloat(value), }); diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx index 1a03986..3e11e54 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx @@ -51,7 +51,7 @@ function MachineMechanics() { const validOption = option as "process"; setActiveOption(validOption); - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionType: validOption, }); @@ -67,19 +67,19 @@ function MachineMechanics() { const handleRenameAction = (newName: string) => { if (!selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionName: newName }); }; const handleProcessTimeChange = (value: string) => { if (!selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { processTime: parseFloat(value), }); }; const handleMaterialSelect = (material: string) => { if (!selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { swapMaterial: material, }); }; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx index e56741f..df5c358 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx @@ -59,7 +59,7 @@ function RoboticArmMechanics() { const handleRenameAction = (newName: string) => { if (!selectedAction.actionId) return; - const event = updateAction(selectedAction.actionId, { actionName: newName }); + const event = updateAction(selectedProduct.productId, selectedAction.actionId, { actionName: newName }); if (selectedPointData) { const updatedActions = selectedPointData.actions.map((action) => @@ -101,7 +101,7 @@ function RoboticArmMechanics() { if (!selectedAction.actionId || !selectedPointData) return; const [x, y, z] = value.split(",").map(Number); - const event = updateAction(selectedAction.actionId, { + const event = updateAction(selectedProduct.productId, selectedAction.actionId, { process: { startPoint: [x, y, z] as [number, number, number], endPoint: selectedPointData.actions.find((a) => a.actionUuid === selectedAction.actionId)?.process.endPoint || null, @@ -122,7 +122,7 @@ function RoboticArmMechanics() { if (!selectedAction.actionId || !selectedPointData) return; const [x, y, z] = value.split(",").map(Number); - const event = updateAction(selectedAction.actionId, { + const event = updateAction(selectedProduct.productId, selectedAction.actionId, { process: { startPoint: selectedPointData.actions.find((a) => a.actionUuid === selectedAction.actionId)?.process.startPoint || null, endPoint: [x, y, z] as [number, number, number], @@ -181,7 +181,7 @@ function RoboticArmMechanics() { const handleDeleteAction = (actionUuid: string) => { if (!selectedPointData) return; - const event = removeAction(actionUuid); + const event = removeAction(selectedProduct.productId, actionUuid); if (event) { updateBackend( diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx index f782019..3273ce4 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx @@ -51,7 +51,7 @@ function StorageMechanics() { const validOption = option as "store" | "spawn"; setActiveOption(validOption); - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionType: validOption, }); @@ -67,7 +67,7 @@ function StorageMechanics() { const handleRenameAction = (newName: string) => { if (!selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionName: newName }); if (event) { updateBackend( @@ -81,7 +81,7 @@ function StorageMechanics() { const handleCapacityChange = (value: string) => { if (!selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { storageCapacity: parseInt(value), }); diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx index 35c2821..7839168 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx @@ -72,7 +72,7 @@ function VehicleMechanics() { const validOption = option as "travel"; setActiveOption(validOption); - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionType: validOption, }); @@ -88,7 +88,7 @@ function VehicleMechanics() { const handleRenameAction = (newName: string) => { if (!selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { actionName: newName }); + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { actionName: newName }); if (event) { updateBackend( @@ -102,7 +102,7 @@ function VehicleMechanics() { const handleLoadCapacityChange = (value: string) => { if (!selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { loadCapacity: parseFloat(value), }); @@ -118,7 +118,7 @@ function VehicleMechanics() { const handleUnloadDurationChange = (value: string) => { if (!selectedPointData) return; - const event = updateAction(selectedPointData.action.actionUuid, { + const event = updateAction(selectedProduct.productId, selectedPointData.action.actionUuid, { unLoadDuration: parseFloat(value), }); diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index 27175ee..ee5c283 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -202,11 +202,11 @@ function processLoadedModel( rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], action: { actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Vehicle Action", + actionName: "Action 1", actionType: "travel", unLoadDuration: 5, loadCapacity: 10, - steeringAngle:0, + steeringAngle: 0, pickUpPoint: null, unLoadPoint: null, triggers: [] @@ -254,7 +254,7 @@ function processLoadedModel( rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0], action: { actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Process Action", + actionName: "Action 1", actionType: "process", processTime: 10, swapMaterial: "material-id", @@ -279,7 +279,7 @@ function processLoadedModel( actions: [ { actionUuid: THREE.MathUtils.generateUUID(), - actionName: "Pick and Place", + actionName: "Action 1", actionType: "pickAndPlace", process: { startPoint: [0, 0, 0], diff --git a/app/src/modules/simulation/products/products.tsx b/app/src/modules/simulation/products/products.tsx index ee1ac42..921dd96 100644 --- a/app/src/modules/simulation/products/products.tsx +++ b/app/src/modules/simulation/products/products.tsx @@ -7,7 +7,7 @@ import { upsertProductOrEventApi } from '../../../services/simulation/UpsertProd import { getAllProductsApi } from '../../../services/simulation/getallProductsApi'; function Products() { - const { products, addProduct, setProducts } = useProductStore(); + const { addProduct, setProducts } = useProductStore(); const { setSelectedProduct } = useSelectedProduct(); useEffect(() => { @@ -27,9 +27,6 @@ function Products() { }) }, []) - useEffect(() => { - }, []) - return ( <> diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx index 7eb0e81..5f83528 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -228,7 +228,7 @@ function TriggerConnector() { }; if (firstSelectedPoint.actionUuid) { - const event = addTrigger(firstSelectedPoint.actionUuid, trigger); + const event = addTrigger(selectedProduct.productId, firstSelectedPoint.actionUuid, trigger); if (event) { updateBackend( @@ -285,7 +285,7 @@ function TriggerConnector() { }; if (firstSelectedPoint.actionUuid) { - addTrigger(firstSelectedPoint.actionUuid, trigger); + addTrigger(selectedProduct.productId, firstSelectedPoint.actionUuid, trigger); } setFirstSelectedPoint(null); } @@ -373,9 +373,8 @@ function TriggerConnector() { const removeConnection = (connection: ConnectionLine) => { if (connection.trigger.triggerUuid) { - const event = removeTrigger(connection.trigger.triggerUuid); + const event = removeTrigger(selectedProduct.productId, connection.trigger.triggerUuid); if (event) { - console.log('event: ', event); updateBackend( selectedProduct.productName, selectedProduct.productId, diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx index a4d72a1..19f049c 100644 --- a/app/src/modules/simulation/vehicle/vehicles.tsx +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -1,7 +1,6 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import VehicleInstances from "./instances/vehicleInstances"; import { useVehicleStore } from "../../../store/simulation/useVehicleStore"; -import { useFloorItems } from "../../../store/store"; import { useSelectedEventData, useSelectedEventSphere, useSelectedProduct } from "../../../store/simulation/useSimulationStore"; import VehicleUI from "../ui/vehicle/vehicleUI"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; @@ -10,49 +9,38 @@ import { useProductStore } from "../../../store/simulation/useProductStore"; function Vehicles() { const { products, getProductById } = useProductStore(); const { selectedProduct } = useSelectedProduct(); - const { vehicles, addVehicle, removeVehicle } = useVehicleStore(); + const { vehicles, addVehicle, clearvehicles } = useVehicleStore(); const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventData } = useSelectedEventData(); - const { floorItems } = useFloorItems(); const { isPlaying } = usePlayButtonStore(); useEffect(() => { if (selectedProduct.productId) { const product = getProductById(selectedProduct.productId); if (product) { + clearvehicles(); product.eventDatas.forEach(events => { if (events.type === 'vehicle') { - removeVehicle(events.modelUuid); addVehicle(selectedProduct.productId, events); } }); } } - }, [selectedProduct]); - - // useEffect(() => { - // vehicles.forEach(vehicle => { - // const product = getProductById(vehicle.productId); - // if (product) { - // const eventData = product.eventDatas.find(event => event.modelUuid === vehicle.modelUuid); - // if (eventData) { - // vehicle.eventData = eventData; - // } - // } - // }); - // }, [vehicles, products]); + }, [selectedProduct, products]); useEffect(() => { - // console.log('vehicles: ', vehicles); }, [vehicles]) return ( <> + + {selectedEventSphere && selectedEventData?.data.type === "vehicle" && !isPlaying && < VehicleUI /> } + ); } diff --git a/app/src/modules/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx index caf37a1..56e3054 100644 --- a/app/src/modules/visualization/RealTimeVisulization.tsx +++ b/app/src/modules/visualization/RealTimeVisulization.tsx @@ -66,8 +66,7 @@ const RealTimeVisulization: React.FC = () => { const { selectedZone, setSelectedZone } = useSelectedZoneStore(); const { setRightSelect } = useRightSelected(); - const { editWidgetOptions, setEditWidgetOptions } = - useEditWidgetOptionsStore(); + const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore(); const { rightClickSelected, setRightClickSelected } = useRightClickSelected(); const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false); const { setFloatingWidget } = useFloatingWidget(); @@ -76,8 +75,6 @@ const RealTimeVisulization: React.FC = () => { const { setSelectedChartId } = useWidgetStore(); const [waitingPanels, setWaitingPanels] = useState(null); - console.log("waitingPanels: ", waitingPanels); - OuterClick({ contextClassName: [ "chart-container", diff --git a/app/src/store/simulation/useArmBotStore.ts b/app/src/store/simulation/useArmBotStore.ts index 642762f..3c54596 100644 --- a/app/src/store/simulation/useArmBotStore.ts +++ b/app/src/store/simulation/useArmBotStore.ts @@ -10,6 +10,7 @@ interface ArmBotStore { modelUuid: string, updates: Partial> ) => void; + clearArmBots: () => void; addCurrentAction: (modelUuid: string, actionUuid: string) => void; removeCurrentAction: (modelUuid: string) => void; @@ -39,14 +40,17 @@ export const useArmBotStore = create()( addArmBot: (productId, event) => { set((state) => { - state.armBots.push({ - ...event, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - state: 'idle', - }); + const exists = state.armBots.some(a => a.modelUuid === event.modelUuid); + if (!exists) { + state.armBots.push({ + ...event, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + state: 'idle', + }); + } }); }, @@ -65,6 +69,12 @@ export const useArmBotStore = create()( }); }, + clearArmBots: () => { + set((state) => { + state.armBots = []; + }); + }, + addCurrentAction: (modelUuid, actionUuid) => { set((state) => { const armBot = state.armBots.find(a => a.modelUuid === modelUuid); diff --git a/app/src/store/simulation/useConveyorStore.ts b/app/src/store/simulation/useConveyorStore.ts index 15dbf34..862ce79 100644 --- a/app/src/store/simulation/useConveyorStore.ts +++ b/app/src/store/simulation/useConveyorStore.ts @@ -10,6 +10,7 @@ interface ConveyorStore { modelUuid: string, updates: Partial> ) => void; + clearConveyors: () => void; setConveyorActive: (modelUuid: string, isActive: boolean) => void; setConveyorState: (modelUuid: string, newState: ConveyorStatus['state']) => void; @@ -30,14 +31,17 @@ export const useConveyorStore = create()( addConveyor: (productId, event) => { set((state) => { - state.conveyors.push({ - ...event, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - state: 'idle', - }); + const exists = state.conveyors.some(c => c.modelUuid === event.modelUuid); + if (!exists) { + state.conveyors.push({ + ...event, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + state: 'idle', + }); + } }); }, @@ -56,6 +60,12 @@ export const useConveyorStore = create()( }); }, + clearConveyors: () => { + set((state) => { + state.conveyors = []; + }); + }, + setConveyorActive: (modelUuid, isActive) => { set((state) => { const conveyor = state.conveyors.find(c => c.modelUuid === modelUuid); diff --git a/app/src/store/simulation/useEventsStore.ts b/app/src/store/simulation/useEventsStore.ts index 8fa6838..5580eb1 100644 --- a/app/src/store/simulation/useEventsStore.ts +++ b/app/src/store/simulation/useEventsStore.ts @@ -49,7 +49,9 @@ export const useEventsStore = create()( // Event-level actions addEvent: (event) => { set((state) => { - state.events.push(event); + if (!state.events.some(e => 'modelUuid' in e && e.modelUuid === event.modelUuid)) { + state.events.push(event); + } }); }, @@ -76,9 +78,14 @@ export const useEventsStore = create()( set((state) => { const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event && 'points' in event) { - (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); + const existingPoint = (event as ConveyorEventSchema).points.find(p => p.uuid === point.uuid); + if (!existingPoint) { + (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); + } } else if (event && 'point' in event) { - (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any; + if (!(event as any).point || (event as any).point.uuid !== point.uuid) { + (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any; + } } }); }, @@ -113,14 +120,15 @@ export const useEventsStore = create()( const event = state.events.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event && 'points' in event) { const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); - if (point) { + if (point && (!point.action || point.action.actionUuid !== action.actionUuid)) { point.action = action as any; } } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { - if ('action' in (event as any).point) { - (event as any).point.action = action; - } else if ('actions' in (event as any).point) { - (event as any).point.actions.push(action); + const point = (event as any).point; + if ('action' in point && (!point.action || point.action.actionUuid !== action.actionUuid)) { + point.action = action; + } else if ('actions' in point && !point.actions.some((a: any) => a.actionUuid === action.actionUuid)) { + point.actions.push(action); } } }); @@ -183,18 +191,22 @@ export const useEventsStore = create()( if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { if (point.action && point.action.actionUuid === actionUuid) { - point.action.triggers.push(trigger); + if (!point.action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) { + point.action.triggers.push(trigger); + } return; } } } else if ('point' in event) { - const point = (event as any).point; + const point: MachinePointSchema | VehiclePointSchema = (event as any).point; if ('action' in point && point.action.actionUuid === actionUuid) { - point.action.triggers.push(trigger); + if (!point.action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) { + point.action.triggers.push(trigger); + } return; } else if ('actions' in point) { - const action = point.actions.find((a: any) => a.actionUuid === actionUuid); - if (action) { + const action = (point as RoboticArmPointSchema).actions.find((a) => a.actionUuid === actionUuid); + if (action && !action.triggers.some(t => t.triggerUuid === trigger.triggerUuid)) { action.triggers.push(trigger); return; } diff --git a/app/src/store/simulation/useMachineStore.ts b/app/src/store/simulation/useMachineStore.ts index af7119c..9a564ac 100644 --- a/app/src/store/simulation/useMachineStore.ts +++ b/app/src/store/simulation/useMachineStore.ts @@ -4,26 +4,23 @@ import { immer } from 'zustand/middleware/immer'; interface MachineStore { machines: MachineStatus[]; - // Actions addMachine: (productId: string, machine: MachineEventSchema) => void; removeMachine: (modelUuid: string) => void; updateMachine: ( modelUuid: string, updates: Partial> ) => void; + clearMachines: () => void; addCurrentAction: (modelUuid: string, actionUuid: string) => void; removeCurrentAction: (modelUuid: string) => void; - // Status updates setMachineActive: (modelUuid: string, isActive: boolean) => void; setMachineState: (modelUuid: string, newState: MachineStatus['state']) => void; - // Time tracking incrementActiveTime: (modelUuid: string, incrementBy: number) => void; incrementIdleTime: (modelUuid: string, incrementBy: number) => void; - // Helpers getMachineById: (modelUuid: string) => MachineStatus | undefined; getMachinesByProduct: (productId: string) => MachineStatus[]; getMachinesBystate: (state: string) => MachineStatus[]; @@ -35,17 +32,19 @@ export const useMachineStore = create()( immer((set, get) => ({ machines: [], - // Actions addMachine: (productId, machine) => { set((state) => { - state.machines.push({ - ...machine, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - state: 'idle', - }); + const exists = state.machines.some(m => m.modelUuid === machine.modelUuid); + if (!exists) { + state.machines.push({ + ...machine, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + state: 'idle', + }); + } }); }, @@ -64,6 +63,11 @@ export const useMachineStore = create()( }); }, + clearMachines: () => { + set((state) => { + state.machines = []; + }); + }, addCurrentAction: (modelUuid) => { set((state) => { @@ -89,7 +93,6 @@ export const useMachineStore = create()( }); }, - // Status updates setMachineActive: (modelUuid, isActive) => { set((state) => { const machine = state.machines.find(m => m.modelUuid === modelUuid); @@ -108,7 +111,6 @@ export const useMachineStore = create()( }); }, - // Time tracking incrementActiveTime: (modelUuid, incrementBy) => { set((state) => { const machine = state.machines.find(m => m.modelUuid === modelUuid); @@ -127,7 +129,6 @@ export const useMachineStore = create()( }); }, - // Helpers getMachineById: (modelUuid) => { return get().machines.find(m => m.modelUuid === modelUuid); }, diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index a47bcdb..b7448f2 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -33,27 +33,30 @@ type ProductsStore = { pointUuid: string, action: ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action'] ) => EventsSchema | undefined; - removeAction: (actionUuid: string) => EventsSchema | undefined; + removeAction: (productId: string, actionUuid: string) => EventsSchema | undefined; updateAction: ( + productId: string, actionUuid: string, updates: Partial ) => EventsSchema | undefined; // Trigger-level actions addTrigger: ( + productId: string, actionUuid: string, trigger: TriggerSchema ) => EventsSchema | undefined; - removeTrigger: (triggerUuid: string) => EventsSchema | undefined; + removeTrigger: (productId: string, triggerUuid: string) => EventsSchema | undefined; updateTrigger: ( + productId: string, triggerUuid: string, updates: Partial ) => void; // Renaming functions renameProduct: (productId: string, newName: string) => void; - renameAction: (actionUuid: string, newName: string) => EventsSchema | undefined; - renameTrigger: (triggerUuid: string, newName: string) => void; + renameAction: (productId: string, actionUuid: string, newName: string) => EventsSchema | undefined; + renameTrigger: (productId: string, triggerUuid: string, newName: string) => void; // Helper functions getProductById: (productId: string) => { productName: string; productId: string; eventDatas: EventsSchema[] } | undefined; @@ -71,12 +74,15 @@ export const useProductStore = create()( // Product-level actions addProduct: (productName, productId) => { set((state) => { - const newProduct = { - productName, - productId: productId, - eventDatas: [] - }; - state.products.push(newProduct); + const existingProduct = state.products.find(p => p.productId === productId); + if (!existingProduct) { + const newProduct = { + productName, + productId: productId, + eventDatas: [] + }; + state.products.push(newProduct); + } }); }, @@ -106,7 +112,10 @@ export const useProductStore = create()( set((state) => { const product = state.products.find(p => p.productId === productId); if (product) { - product.eventDatas.push(event); + const existingEvent = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === event.modelUuid); + if (!existingEvent) { + product.eventDatas.push(event); + } } }); }, @@ -120,7 +129,7 @@ export const useProductStore = create()( }); }, - deleteEvent: (modelUuid: string) => { + deleteEvent: (modelUuid) => { set((state) => { for (const product of state.products) { product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid); @@ -150,9 +159,15 @@ export const useProductStore = create()( if (product) { const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event && 'points' in event) { - (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); + const existingPoint = (event as ConveyorEventSchema).points.find(p => p.uuid === point.uuid); + if (!existingPoint) { + (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); + } } else if (event && 'point' in event) { - (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any; + const existingPoint = (event as any).point?.uuid === point.uuid; + if (!existingPoint) { + (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point = point as any; + } } } }); @@ -198,17 +213,22 @@ export const useProductStore = create()( const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event && 'points' in event) { const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); - if (point) { + if (point && (!point.action || point.action.actionUuid !== action.actionUuid)) { point.action = action as any; updatedEvent = JSON.parse(JSON.stringify(event)); } } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { if ('action' in (event as any).point) { - (event as any).point.action = action; - updatedEvent = JSON.parse(JSON.stringify(event)); + if (!(event as any).point.action || (event as any).point.action.actionUuid !== action.actionUuid) { + (event as any).point.action = action; + updatedEvent = JSON.parse(JSON.stringify(event)); + } } else if ('actions' in (event as any).point) { - (event as any).point.actions.push(action); - updatedEvent = JSON.parse(JSON.stringify(event)); + const existingAction = (event as any).point.actions.find((a: any) => a.actionUuid === action.actionUuid); + if (!existingAction) { + (event as any).point.actions.push(action); + updatedEvent = JSON.parse(JSON.stringify(event)); + } } } } @@ -216,10 +236,11 @@ export const useProductStore = create()( return updatedEvent; }, - removeAction: (actionUuid: string) => { + removeAction: (productId, actionUuid) => { let updatedEvent: EventsSchema | undefined; set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { // Handle ConveyorEventSchema @@ -248,10 +269,11 @@ export const useProductStore = create()( return updatedEvent; }, - updateAction: (actionUuid, updates) => { + updateAction: (productId, actionUuid, updates) => { let updatedEvent: EventsSchema | undefined; set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { @@ -283,30 +305,40 @@ export const useProductStore = create()( }, // Trigger-level actions - addTrigger: (actionUuid, trigger) => { + addTrigger: (productId, actionUuid, trigger) => { let updatedEvent: EventsSchema | undefined; set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { if (point.action && point.action.actionUuid === actionUuid) { - point.action.triggers.push(trigger); - updatedEvent = JSON.parse(JSON.stringify(event)); + const existingTrigger = point.action.triggers.find(t => t.triggerUuid === trigger.triggerUuid); + if (!existingTrigger) { + point.action.triggers.push(trigger); + updatedEvent = JSON.parse(JSON.stringify(event)); + } return; } } } else if ('point' in event) { const point = (event as any).point; if ('action' in point && point.action.actionUuid === actionUuid) { - point.action.triggers.push(trigger); - updatedEvent = JSON.parse(JSON.stringify(event)); + const existingTrigger = point.action.triggers.find((t: any) => t.triggerUuid === trigger.triggerUuid); + if (!existingTrigger) { + point.action.triggers.push(trigger); + updatedEvent = JSON.parse(JSON.stringify(event)); + } return; } else if ('actions' in point) { const action = point.actions.find((a: any) => a.actionUuid === actionUuid); if (action) { - action.triggers.push(trigger); - updatedEvent = JSON.parse(JSON.stringify(event)); + const existingTrigger = action.triggers.find((t: any) => t.triggerUuid === trigger.triggerUuid); + if (!existingTrigger) { + action.triggers.push(trigger); + updatedEvent = JSON.parse(JSON.stringify(event)); + } return; } } @@ -317,28 +349,38 @@ export const useProductStore = create()( return updatedEvent; }, - removeTrigger: (triggerUuid) => { + removeTrigger: (productId, triggerUuid) => { let updatedEvent: EventsSchema | undefined; set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { if (point.action && 'triggers' in point.action) { - point.action.triggers = point.action.triggers.filter(t => t.triggerUuid !== triggerUuid); - updatedEvent = JSON.parse(JSON.stringify(event)); + const Trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); + if (Trigger) { + point.action.triggers = point.action.triggers.filter(t => t.triggerUuid !== triggerUuid); + updatedEvent = JSON.parse(JSON.stringify(event)); + } } } } else if ('point' in event) { const point = (event as any).point; if ('action' in point && 'triggers' in point.action) { - point.action.triggers = point.action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); - updatedEvent = JSON.parse(JSON.stringify(event)); + const Trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (Trigger) { + point.action.triggers = point.action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + updatedEvent = JSON.parse(JSON.stringify(event)); + } } else if ('actions' in point) { for (const action of point.actions) { if ('triggers' in action) { - action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); - updatedEvent = JSON.parse(JSON.stringify(event)); + const Trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (Trigger) { + action.triggers = action.triggers.filter((t: any) => t.triggerUuid !== triggerUuid); + updatedEvent = JSON.parse(JSON.stringify(event)); + } } } } @@ -349,9 +391,10 @@ export const useProductStore = create()( return updatedEvent; }, - updateTrigger: (triggerUuid, updates) => { + updateTrigger: (productId, triggerUuid, updates) => { set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { @@ -398,10 +441,11 @@ export const useProductStore = create()( }); }, - renameAction: (actionUuid, newName) => { + renameAction: (productId, actionUuid, newName) => { let updatedEvent: EventsSchema | undefined; set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { @@ -432,9 +476,10 @@ export const useProductStore = create()( return updatedEvent; }, - renameTrigger: (triggerUuid, newName) => { + renameTrigger: (productId, triggerUuid, newName) => { set((state) => { - for (const product of state.products) { + const product = state.products.find(p => p.productId === productId); + if (product) { for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { diff --git a/app/src/store/simulation/useStorageUnitStore.ts b/app/src/store/simulation/useStorageUnitStore.ts index d729708..aec2f12 100644 --- a/app/src/store/simulation/useStorageUnitStore.ts +++ b/app/src/store/simulation/useStorageUnitStore.ts @@ -4,26 +4,22 @@ import { immer } from 'zustand/middleware/immer'; interface StorageUnitStore { storageUnits: StorageUnitStatus[]; - // Actions addStorageUnit: (productId: string, storageUnit: StorageEventSchema) => void; removeStorageUnit: (modelUuid: string) => void; updateStorageUnit: ( modelUuid: string, updates: Partial> ) => void; + clearStorageUnits: () => void; - // Status updates setStorageUnitActive: (modelUuid: string, isActive: boolean) => void; setStorageUnitState: (modelUuid: string, newState: StorageUnitStatus['state']) => void; - // Load updates updateStorageUnitLoad: (modelUuid: string, incrementBy: number) => void; - // Time tracking incrementActiveTime: (modelUuid: string, incrementBy: number) => void; incrementIdleTime: (modelUuid: string, incrementBy: number) => void; - // Helpers getStorageUnitById: (modelUuid: string) => StorageUnitStatus | undefined; getStorageUnitsByProduct: (productId: string) => StorageUnitStatus[]; getStorageUnitsBystate: (state: string) => StorageUnitStatus[]; @@ -37,18 +33,20 @@ export const useStorageUnitStore = create()( immer((set, get) => ({ storageUnits: [], - // Actions addStorageUnit: (productId, storageUnit) => { set((state) => { - state.storageUnits.push({ - ...storageUnit, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - currentLoad: 0, - state: 'idle', - }); + const exists = state.storageUnits.some(s => s.modelUuid === storageUnit.modelUuid); + if (!exists) { + state.storageUnits.push({ + ...storageUnit, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + currentLoad: 0, + state: 'idle', + }); + } }); }, @@ -67,7 +65,12 @@ export const useStorageUnitStore = create()( }); }, - // Status updates + clearStorageUnits: () => { + set(() => ({ + storageUnits: [], + })); + }, + setStorageUnitActive: (modelUuid, isActive) => { set((state) => { const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); @@ -86,7 +89,6 @@ export const useStorageUnitStore = create()( }); }, - // Load updates updateStorageUnitLoad: (modelUuid, incrementBy) => { set((state) => { const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); @@ -96,7 +98,6 @@ export const useStorageUnitStore = create()( }); }, - // Time tracking incrementActiveTime: (modelUuid, incrementBy) => { set((state) => { const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); @@ -115,7 +116,6 @@ export const useStorageUnitStore = create()( }); }, - // Helpers getStorageUnitById: (modelUuid) => { return get().storageUnits.find(s => s.modelUuid === modelUuid); }, diff --git a/app/src/store/simulation/useVehicleStore.ts b/app/src/store/simulation/useVehicleStore.ts index a5ea3be..ecef809 100644 --- a/app/src/store/simulation/useVehicleStore.ts +++ b/app/src/store/simulation/useVehicleStore.ts @@ -20,6 +20,7 @@ interface VehiclesStore { modelUuid: string, updates: Partial> ) => void; + clearvehicles: () => void; setVehicleActive: (modelUuid: string, isActive: boolean) => void; updateSteeringAngle: (modelUuid: string, steeringAngle: number) => void; @@ -41,15 +42,18 @@ export const useVehicleStore = create()( addVehicle: (productId, event) => { set((state) => { - state.vehicles.push({ - ...event, - productId, - isActive: false, - idleTime: 0, - activeTime: 0, - currentLoad: 0, - distanceTraveled: 0, - }); + const exists = state.vehicles.some(v => v.modelUuid === event.modelUuid); + if (!exists) { + state.vehicles.push({ + ...event, + productId, + isActive: false, + idleTime: 0, + activeTime: 0, + currentLoad: 0, + distanceTraveled: 0, + }); + } }); }, @@ -68,6 +72,12 @@ export const useVehicleStore = create()( }); }, + clearvehicles: () => { + set((state) => { + state.vehicles = []; + }); + }, + setVehicleActive: (modelUuid, isActive) => { set((state) => { const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index 346e408..c7ccd8b 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -88,6 +88,7 @@ interface StoragePointSchema { actionType: "store"; materials: { materialName: string; materialId: string; }[]; storageCapacity: number; + triggers: TriggerSchema[]; }; } -- 2.40.1 From a3b48d12c14fea7da380726aaafdc3c6eb3086dc Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 2 May 2025 13:40:00 +0530 Subject: [PATCH 13/32] Refactor TriggerConnector to improve event handling: reset firstSelectedPoint on invalid intersections, update event model name, and ensure proper cleanup of state. Adjust event listener conditions for better performance. --- .../triggers/connector/triggerConnector.tsx | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx index 5f83528..a10580f 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -171,10 +171,16 @@ function TriggerConnector() { (intersect) => intersect.object.name === ('Event-Sphere') ); - if (intersects.length === 0) return; + if (intersects.length === 0) { + setFirstSelectedPoint(null); + return; + }; const currentObject = intersects[0].object; - if (!currentObject || currentObject.name !== 'Event-Sphere') return; + if (!currentObject || currentObject.name !== 'Event-Sphere') { + setFirstSelectedPoint(null); + return; + }; const modelUuid = currentObject.userData.modelUuid; const pointUuid = currentObject.userData.pointUuid; @@ -189,7 +195,10 @@ function TriggerConnector() { const event = getEventByModelUuid(selectedProduct.productId, modelUuid); - if (!point || !event) return; + if (!point || !event) { + setFirstSelectedPoint(null); + return; + }; let actionUuid: string | undefined; if ('action' in point && point.action) { @@ -254,7 +263,12 @@ function TriggerConnector() { pointUuid ); - if (!point) return; + const event = getEventByModelUuid(selectedProduct.productId, modelUuid); + + if (!point || !event) { + setFirstSelectedPoint(null); + return; + }; let actionUuid: string | undefined; if ('action' in point && point.action) { @@ -270,12 +284,12 @@ function TriggerConnector() { delay: 0, triggeredAsset: { triggeredModel: { - modelName: currentObject.parent?.parent?.name || 'Unknown', + modelName: event.modelName || 'Unknown', modelUuid: modelUuid }, triggeredPoint: { - pointName: currentObject.name, - pointUuid: pointUuid + pointName: 'Point', + pointUuid: point.uuid }, triggeredAction: actionUuid ? { actionName: getActionByUuid(selectedProduct.productId, actionUuid)?.actionName || 'Action', @@ -285,13 +299,24 @@ function TriggerConnector() { }; if (firstSelectedPoint.actionUuid) { - addTrigger(selectedProduct.productId, firstSelectedPoint.actionUuid, trigger); + const event = addTrigger(selectedProduct.productId, firstSelectedPoint.actionUuid, trigger); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } } setFirstSelectedPoint(null); + } else if (firstSelectedPoint) { + setFirstSelectedPoint(null); } }; - if (subModule === 'simulations') { + if (subModule === 'simulations' && !deleteTool) { canvasElement.addEventListener("mousedown", onMouseDown); canvasElement.addEventListener("mouseup", onMouseUp); canvasElement.addEventListener("mousemove", onMouseMove); @@ -305,7 +330,7 @@ function TriggerConnector() { canvasElement.removeEventListener('contextmenu', handleRightClick); }; - }, [gl, subModule, selectedProduct, firstSelectedPoint]); + }, [gl, subModule, selectedProduct, firstSelectedPoint, deleteTool]); useFrame(() => { -- 2.40.1 From ef2baacf558cd822e6a53dc3fe88b5fcdc3bd06e Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Fri, 2 May 2025 14:04:52 +0530 Subject: [PATCH 14/32] added snap function for ctrl and ctrl+shift key --- .../selectionControls/moveControls.tsx | 47 ++++++++--- app/src/utils/handleSnap.ts | 79 +++++-------------- 2 files changed, 56 insertions(+), 70 deletions(-) diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index cc9ce50..f29396d 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -1,7 +1,7 @@ import * as THREE from "three"; -import { useEffect, useMemo, useRef } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store"; +import { useFloorItems, useSelectedAssets, useSocketStore, useStartSimulation, useToggleView } from "../../../../store/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; @@ -10,6 +10,7 @@ import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; +import { snapControls } from "../../../../utils/handleSnap"; function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); @@ -21,6 +22,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); const itemsData = useRef([]); + const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("") const email = localStorage.getItem('email') const organization = (email!.split("@")[1]).split(".")[0]; @@ -54,6 +56,15 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje const onPointerMove = () => { isMoving = true; }; + const onKeyUp = (event: KeyboardEvent) => { + // When any modifier is released, reset snap + const isModifierKey = + event.key === "Control" || event.key === "Shift"; + + if (isModifierKey) { + setKeyEvent(""); + } + }; const onPointerUp = (event: PointerEvent) => { if (!isMoving && movedObjects.length > 0 && event.button === 0) { @@ -75,18 +86,28 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje setMovedObjects([]); itemsData.current = []; } + setKeyEvent("") }; const onKeyDown = (event: KeyboardEvent) => { const keyCombination = detectModifierKeys(event); if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0) return; + + if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") { + // update state here + setKeyEvent(keyCombination) + } else { + setKeyEvent("") + } + if (keyCombination === "G") { if (selectedAssets.length > 0) { moveAssets(); itemsData.current = floorItems.filter((item: { modelUuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)); } } + if (keyCombination === "ESCAPE") { event.preventDefault(); @@ -109,6 +130,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje canvasElement.addEventListener("pointermove", onPointerMove); canvasElement.addEventListener("pointerup", onPointerUp); canvasElement.addEventListener("keydown", onKeyDown); + canvasElement?.addEventListener("keyup", onKeyUp); } return () => { @@ -116,12 +138,11 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje canvasElement.removeEventListener("pointermove", onPointerMove); canvasElement.removeEventListener("pointerup", onPointerUp); canvasElement.removeEventListener("keydown", onKeyDown); + canvasElement?.removeEventListener("keyup", onKeyUp); }; - }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects]); + }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent]); - const gridSize = 0.25; - const moveSpeed = 0.25; - const isGridSnap = false; + let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25; useFrame(() => { if (movedObjects.length > 0) { @@ -132,10 +153,17 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje if (point) { let targetX = point.x; let targetZ = point.z; + if (keyEvent === "Ctrl") { + targetX = snapControls(targetX, "Ctrl"); + targetZ = snapControls(targetZ, "Ctrl"); + } else if (keyEvent === "Ctrl+Shift") { + targetX = snapControls(targetX, "Ctrl+Shift"); + targetZ = snapControls(targetZ, "Ctrl+Shift"); + } else if (keyEvent === "Shift") { + targetX = snapControls(targetX, "Shift"); + targetZ = snapControls(targetZ, "Shift"); + } else { - if (isGridSnap) { - targetX = Math.round(point.x / gridSize) * gridSize; - targetZ = Math.round(point.z / gridSize) * gridSize; } const position = new THREE.Vector3(); @@ -278,6 +306,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje setMovedObjects([]); setRotatedObjects([]); setSelectedAssets([]); + setKeyEvent("") } return null; diff --git a/app/src/utils/handleSnap.ts b/app/src/utils/handleSnap.ts index 35a0f96..bd6a74d 100644 --- a/app/src/utils/handleSnap.ts +++ b/app/src/utils/handleSnap.ts @@ -1,65 +1,22 @@ -import { useEffect } from "react"; -import { detectModifierKeys } from "./shortcutkeys/detectModifierKeys"; +export function snapControls(value: number, event: string): number { + const CTRL_DISTANCE = 1; // Snap to whole numbers when Ctrl is pressed + const SHIFT_DISTANCE = 0.01; // Snap to half-step increments when Shift is pressed + const CTRL_SHIFT_DISTANCE = 0.1; // Snap to fine increments when both Ctrl and Shift are pressed -// Define the props expected by the component -type SnapControlsProps = { - x_point: number; // X-coordinate value to snap - z_point: number; // Z-coordinate value to snap -}; + switch (event) { + case "Ctrl": + return Math.round(value / CTRL_DISTANCE) * CTRL_DISTANCE; -const SnapControls: React.FC = ({ x_point, z_point }) => { - useEffect(() => { - // Handler function for keypress events - const handleKeyPress = (event: KeyboardEvent) => { - // Detect which modifier keys (Ctrl, Shift, etc.) are currently pressed - const keyCombination = detectModifierKeys(event); + case "Shift": + return Math.round(value / SHIFT_DISTANCE) * SHIFT_DISTANCE; - // Define the snapping distances - const CTRL_DISTANCE = 1; // Coarse snapping when Ctrl is pressed - const SHIFT_DISTANCE = 0.01; // Fine snapping when Shift is pressed + case "Ctrl+Shift": + const base = Math.floor(value / CTRL_DISTANCE) * CTRL_DISTANCE; + const offset = + Math.round((value - base) / CTRL_SHIFT_DISTANCE) * CTRL_SHIFT_DISTANCE; + return base + offset; - // Prevent default behavior to avoid unintended side effects - event.preventDefault(); - - // Create new coordinates to apply snapping to - let newX = x_point; - let newZ = z_point; - - // Check for modifier key combinations and apply appropriate snapping logic - if (keyCombination) { - if (keyCombination === "Ctrl") { - // Snap to nearest integer unit - newX = Math.round(x_point / CTRL_DISTANCE) * CTRL_DISTANCE; - newZ = Math.round(z_point / CTRL_DISTANCE) * CTRL_DISTANCE; - return { newX, newZ }; - } - - if (keyCombination === "Ctrl+Shift") { - // Snap to nearest small unit (0.01) - newX = Math.round(x_point / SHIFT_DISTANCE) * SHIFT_DISTANCE; - newZ = Math.round(z_point / SHIFT_DISTANCE) * SHIFT_DISTANCE; - return { newX, newZ }; - } - - if (keyCombination === "Shift") { - // Incorrect snapping logic — rounding the point and multiplying by small value - newX = Math.round(x_point) * SHIFT_DISTANCE; - newZ = Math.round(z_point) * SHIFT_DISTANCE; - return { newX, newZ }; - } - } - }; - - // Attach keydown event listener when the component mounts - window.addEventListener("keydown", handleKeyPress); - - // Clean up the event listener when the component unmounts - return () => { - window.removeEventListener("keydown", handleKeyPress); - }; - }, []); // Empty dependency array means this effect runs once on mount - - return null; // This component doesn’t render anything -}; - -export default SnapControls; + default: + return value; // No snapping if no modifier key is pressed + } +} -- 2.40.1 From fb9e507f4fe28f0426d2cc24d7b62b23ee1885ed Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 2 May 2025 14:44:29 +0530 Subject: [PATCH 15/32] Enhance TriggerConnector and VehicleUI components: improve event handling by resetting firstSelectedPoint on invalid intersections, refine intersection checks, and streamline state updates in VehicleUI. Integrate backend updates for vehicle actions and ensure consistent rotation handling. --- .../triggers/connector/triggerConnector.tsx | 31 +++-- .../simulation/ui/vehicle/vehicleUI.tsx | 117 ++++++++---------- 2 files changed, 75 insertions(+), 73 deletions(-) diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx index a10580f..dcb46f4 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -185,6 +185,11 @@ function TriggerConnector() { const modelUuid = currentObject.userData.modelUuid; const pointUuid = currentObject.userData.pointUuid; + if (firstSelectedPoint && firstSelectedPoint.pointUuid === pointUuid) { + setFirstSelectedPoint(null); + return; + } + if (selectedProduct && getIsEventInProduct(selectedProduct.productId, modelUuid)) { const point = getPointByUuid( @@ -341,8 +346,9 @@ function TriggerConnector() { !intersect.object.name.includes("Roof") && !intersect.object.name.includes("agv-collider") && !intersect.object.name.includes("MeasurementReference") && - !intersect.object.userData.isPathObject && - !(intersect.object.type === "GridHelper") + !intersect.object.parent?.name.includes("Zone") && + !(intersect.object.type === "GridHelper") && + !(intersect.object.type === "Line2") ); let point: THREE.Vector3 | null = null; @@ -355,10 +361,19 @@ function TriggerConnector() { } const sphereIntersects = raycaster.intersectObjects(scene.children, true).filter((intersect) => intersect.object.name === ('Event-Sphere')); + + if (sphereIntersects.length > 0 && sphereIntersects[0].object.uuid === firstSelectedPoint.pointUuid) { + setHelperLineColor('red'); + setCurrentLine(null); + return; + } + const startPoint = getWorldPositionFromScene(firstSelectedPoint.pointUuid); if (point && startPoint) { - + if (sphereIntersects.length > 0) { + point = sphereIntersects[0].object.getWorldPosition(new THREE.Vector3()); + } const distance = startPoint.distanceTo(point); const heightFactor = Math.max(0.5, distance * 0.2); const midPoint = new THREE.Vector3( @@ -367,17 +382,15 @@ function TriggerConnector() { (startPoint.z + point.z) / 2 ); + const endPoint = point; + setCurrentLine({ start: startPoint, mid: midPoint, - end: point, + end: endPoint, }); - if (sphereIntersects.length > 0) { - setHelperLineColor("#6cf542"); - } else { - setHelperLineColor("red"); - } + setHelperLineColor(sphereIntersects.length > 0 ? "#6cf542" : "red"); } else { setCurrentLine(null); } diff --git a/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx b/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx index 43ba833..2a124cd 100644 --- a/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx +++ b/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx @@ -1,12 +1,16 @@ import React, { useEffect, useRef, useState } from 'react'; +import * as Types from "../../../../types/world/worldTypes"; import startPoint from "../../../../assets/gltf-glb/arrow_green.glb"; -import startEnd from "../../../../assets/gltf-glb/arrow_red.glb"; import * as THREE from "three"; +import startEnd from "../../../../assets/gltf-glb/arrow_red.glb"; import { useGLTF } from '@react-three/drei'; import { useFrame, useThree } from '@react-three/fiber'; import { useSelectedEventSphere, useIsDragging, useIsRotating } from '../../../../store/simulation/useSimulationStore'; import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; -import * as Types from "../../../../types/world/worldTypes"; +import { useProductStore } from '../../../../store/simulation/useProductStore'; +import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; +import { upsertProductOrEventApi } from '../../../../services/simulation/UpsertProductOrEventApi'; + const VehicleUI = () => { const { scene: startScene } = useGLTF(startPoint) as any; const { scene: endScene } = useGLTF(startEnd) as any; @@ -14,7 +18,9 @@ const VehicleUI = () => { const endMarker = useRef(null); const prevMousePos = useRef({ x: 0, y: 0 }); const { selectedEventSphere } = useSelectedEventSphere(); - const { vehicles, updateVehicle } = useVehicleStore(); + const { selectedProduct } = useSelectedProduct(); + const { getVehicleById } = useVehicleStore(); + const { updateEvent } = useProductStore(); const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 0, 0]); const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 0, 0]); const [startRotation, setStartRotation] = useState<[number, number, number]>([0, 0, 0]); @@ -26,55 +32,46 @@ const VehicleUI = () => { const state: Types.ThreeState = useThree(); const controls: any = state.controls; + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData + }) + } + useEffect(() => { if (!selectedEventSphere) return; - const selectedVehicle = vehicles.find( - (vehicle: any) => vehicle.modelUuid === selectedEventSphere.userData.modelUuid - ); + const selectedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid); if (selectedVehicle?.point?.action) { const { pickUpPoint, unLoadPoint } = selectedVehicle.point.action; if (pickUpPoint) { - const pickupPosition = new THREE.Vector3( - pickUpPoint.position.x, - pickUpPoint.position.y, - pickUpPoint.position.z - ); - const pickupRotation = new THREE.Vector3( - pickUpPoint.rotation.x, - pickUpPoint.rotation.y, - pickUpPoint.rotation.z - ); - pickupPosition.y = 0; - setStartPosition([pickupPosition.x, 0, pickupPosition.z]); - setStartRotation([pickupRotation.x, pickupRotation.y, pickupRotation.z]); + setStartPosition([pickUpPoint.position.x, 0, pickUpPoint.position.z]); + setStartRotation([pickUpPoint.rotation.x, pickUpPoint.rotation.y, pickUpPoint.rotation.z]); } else { const defaultLocal = new THREE.Vector3(0, 0, 1.5); const defaultWorld = selectedEventSphere.localToWorld(defaultLocal); - defaultWorld.y = 0; setStartPosition([defaultWorld.x, 0, defaultWorld.z]); setStartRotation([0, 0, 0]); } if (unLoadPoint) { - const unLoadPosition = new THREE.Vector3( - unLoadPoint.position.x, - unLoadPoint.position.y, - unLoadPoint.position.z - ); - const unLoadRotation = new THREE.Vector3( - unLoadPoint.rotation.x, - unLoadPoint.rotation.y, - unLoadPoint.position.z - ); - unLoadPosition.y = 0; // Force y to 0 - setEndPosition([unLoadPosition.x, 0, unLoadPosition.z]); - setEndRotation([unLoadRotation.x, unLoadRotation.y, unLoadRotation.z]); + setEndPosition([unLoadPoint.position.x, 0, unLoadPoint.position.z]); + setEndRotation([unLoadPoint.rotation.x, unLoadPoint.rotation.y, unLoadPoint.rotation.z]); } else { const defaultLocal = new THREE.Vector3(0, 0, -1.5); const defaultWorld = selectedEventSphere.localToWorld(defaultLocal); - defaultWorld.y = 0; // Force y to 0 setEndPosition([defaultWorld.x, 0, defaultWorld.z]); setEndRotation([0, 0, 0]); } @@ -87,7 +84,6 @@ const VehicleUI = () => { const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint); if (intersects) { - intersectPoint.y = 0; // Force y to 0 if (isDragging === "start") { setStartPosition([intersectPoint.x, 0, intersectPoint.z]); } @@ -108,12 +104,12 @@ const VehicleUI = () => { if (marker) { const rotationSpeed = 10; - marker.rotation.y += deltaX * rotationSpeed; if (isRotating === 'start') { - setStartRotation([marker.rotation.x, marker.rotation.y, marker.rotation.z]) + const y = startRotation[1] + deltaX * rotationSpeed; + setStartRotation([0, y, 0]); } else { - - setEndRotation([marker.rotation.x, marker.rotation.y, marker.rotation.z]) + const y = endRotation[1] + deltaX * rotationSpeed; + setEndRotation([0, y, 0]); } } }); @@ -142,43 +138,34 @@ const VehicleUI = () => { setIsRotating(null); if (selectedEventSphere?.userData.modelUuid) { - const updatedVehicle = vehicles.find( - (vehicle) => vehicle.modelUuid === selectedEventSphere.userData.modelUuid - ); + const updatedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid); if (updatedVehicle) { - updateVehicle(selectedEventSphere.userData.modelUuid, { + const event = updateEvent(selectedProduct.productId, selectedEventSphere.userData.modelUuid, { point: { ...updatedVehicle.point, action: { ...updatedVehicle.point?.action, pickUpPoint: { - position: { - x: startPosition[0], - y: startPosition[1], - z: startPosition[2], - }, - rotation: { - x: startRotation[0], - y: startRotation[1], - z: startRotation[2], - }, + position: { x: startPosition[0], y: startPosition[1], z: startPosition[2], }, + rotation: { x: 0, y: startRotation[1], z: 0, }, }, unLoadPoint: { - position: { - x: endPosition[0], - y: endPosition[1], - z: endPosition[2], - }, - rotation: { - x: endRotation[0], - y: endRotation[1], - z: endRotation[2], - }, + position: { x: endPosition[0], y: endPosition[1], z: endPosition[2], }, + rotation: { x: 0, y: endRotation[1], z: 0, }, }, }, }, - }); + }) + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } } } }; @@ -208,6 +195,7 @@ const VehicleUI = () => { object={startScene} ref={startMarker} position={startPosition} + rotation={startRotation} onPointerDown={(e: any) => { e.stopPropagation(); handlePointerDown(e, "start", "start"); @@ -224,6 +212,7 @@ const VehicleUI = () => { object={endScene} ref={endMarker} position={endPosition} + rotation={endRotation} onPointerDown={(e: any) => { e.stopPropagation(); handlePointerDown(e, "end", "end"); -- 2.40.1 From 66b3d755006fc0c1e0d571b23a19854527021d81 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 2 May 2025 14:59:19 +0530 Subject: [PATCH 16/32] Refactor VehicleAnimator component: update agvDetail type to VehicleStatus, ensure objectRotation is defined before use, and adjust object rotation handling to utilize agvDetail's rotation values. --- .../vehicle/instances/animator/vehicleAnimator.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 421dea8..9ca7355 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -11,7 +11,7 @@ interface VehicleAnimatorProps { reset: () => void; currentPhase: string; agvUuid: number; - agvDetail: any; + agvDetail: VehicleStatus; } function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail, reset }: VehicleAnimatorProps) { @@ -32,7 +32,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai let startTime: number; let fixedInterval: number; let coveredDistance = progressRef.current; - let objectRotation = (agvDetail.point?.action?.pickUpPoint?.rotation || { x: 0, y: 0, z: 0 }) as { x: number; y: number; z: number }; + let objectRotation = (agvDetail.point?.action?.pickUpPoint?.rotation || { x: 0, y: 0, z: 0 }) as { x: number; y: number; z: number } | undefined; useEffect(() => { @@ -69,7 +69,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai const object = scene.getObjectByProperty('uuid', agvUuid); if (object) { object.position.set(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]); - object.rotation.set(objectRotation.x, objectRotation.y, objectRotation.z); + object.rotation.set(agvDetail.rotation[0], agvDetail.rotation[1], agvDetail.rotation[2]); } } }, [isReset, isPlaying]) @@ -131,7 +131,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai } if (progressRef.current >= totalDistance) { - if (restRotation) { + if (restRotation && objectRotation) { const targetQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(objectRotation.x, objectRotation.y, objectRotation.z)); object.quaternion.slerp(targetQuaternion, delta * 2); const angleDiff = object.quaternion.angleTo(targetQuaternion); -- 2.40.1 From db162c9ffaebe7a955b94a187596467fca5d1316 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 2 May 2025 16:43:43 +0530 Subject: [PATCH 17/32] refactor: Update icon imports and improve UI components; enhance styles for better layout and responsiveness; modify event properties and simulation player functionality --- .../components/icons/ExportCommonIcons.tsx | 4 +- .../components/layout/sidebarLeft/Header.tsx | 4 +- .../layout/sidebarRight/analysis/Analysis.tsx | 64 ++---- .../properties/GlobalProperties.tsx | 4 +- .../eventProperties/EventProperties.tsx | 216 +++++++++--------- .../ui/simulation/simulationPlayer.tsx | 167 ++++++++------ .../visualization/RealTimeVisulization.tsx | 2 - .../components/simulation/simulation.scss | 13 +- app/src/styles/components/tools.scss | 2 + app/src/styles/layout/sidebar.scss | 28 +-- 10 files changed, 252 insertions(+), 252 deletions(-) diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx index eaa7701..6925853 100644 --- a/app/src/components/icons/ExportCommonIcons.tsx +++ b/app/src/components/icons/ExportCommonIcons.tsx @@ -458,7 +458,7 @@ export function InfoIcon() { > { -
{ if (activeModule !== "market") { @@ -29,7 +29,7 @@ const Header: React.FC = () => { }} > -
+ ); }; diff --git a/app/src/components/layout/sidebarRight/analysis/Analysis.tsx b/app/src/components/layout/sidebarRight/analysis/Analysis.tsx index 9d2889f..9b16186 100644 --- a/app/src/components/layout/sidebarRight/analysis/Analysis.tsx +++ b/app/src/components/layout/sidebarRight/analysis/Analysis.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { AI_Icon } from "../../../icons/ExportCommonIcons"; +import { AIIcon } from "../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../ui/inputs/RegularDropDown"; import { AnalysisPresetsType } from "../../../../types/analysis"; import RenderAnalysisInputs from "./RenderAnalysisInputs"; @@ -13,53 +13,24 @@ const Analysis: React.FC = () => { const AnalysisPresets: AnalysisPresetsType = { "Throughput time": [ - { type: "default", inputs: { label: "Height", activeOption: "mm" } }, - { type: "default", inputs: { label: "Width", activeOption: "mm" } }, - { type: "default", inputs: { label: "Length", activeOption: "mtr" } }, - { type: "default", inputs: { label: "Thickness", activeOption: "mm" } }, - { - type: "default", - inputs: { label: "Raw Material", activeOption: "mm" }, - }, - { - type: "range", - inputs: { label: "Material flow", activeOption: "m/min" }, - }, + { type: "default", inputs: { label: "Cycle time", activeOption: "s" } }, + { type: "default", inputs: { label: "machines / lines", activeOption: "item" } }, + { type: "default", inputs: { label: "Machine uptime", activeOption: "%" } }, ], "Production capacity": [ - { type: "default", inputs: { label: "Height", activeOption: "mm" } }, - { type: "default", inputs: { label: "Width", activeOption: "mm" } }, - { type: "default", inputs: { label: "Length", activeOption: "mtr" } }, - { type: "default", inputs: { label: "Thickness", activeOption: "mm" } }, - { - type: "default", - inputs: { label: "Raw Material", activeOption: "mm" }, - }, - { - type: "range", - inputs: { label: "Material flow", activeOption: "m/min" }, - }, - { - type: "range", - inputs: { label: "Shift 1", activeOption: "hr(s)" }, - }, - { - type: "range", - inputs: { label: "Shift 2", activeOption: "hr(s)" }, - }, - { - type: "range", - inputs: { label: "Over time", activeOption: "hr(s)" }, - }, + { type: "default", inputs: { label: "Shift length", activeOption: "hr" } }, + { type: "default", inputs: { label: "Shifts / day", activeOption: "unit" } }, + { type: "default", inputs: { label: "Working days / year", activeOption: "days" } }, + { type: "default", inputs: { label: "Yield rate", activeOption: "%" } }, ], ROI: [ { type: "default", - inputs: { label: "Equipment Cost", activeOption: "INR" }, + inputs: { label: "Selling price", activeOption: "INR" }, }, { type: "default", - inputs: { label: "Employee Salary", activeOption: "INR" }, + inputs: { label: "Material cost", activeOption: "INR" }, }, { type: "default", @@ -67,7 +38,7 @@ const Analysis: React.FC = () => { }, { type: "default", - inputs: { label: "Cost per unit", activeOption: "INR" }, + inputs: { label: "Maintenance cost", activeOption: "INR" }, }, { type: "default", @@ -75,20 +46,19 @@ const Analysis: React.FC = () => { }, { type: "default", - inputs: { label: "Upkeep Cost", activeOption: "INR" }, + inputs: { label: "Fixed costs", activeOption: "INR" }, }, { type: "default", - inputs: { label: "Working Hours", activeOption: "Hrs" }, + inputs: { label: "Salvage value", activeOption: "Hrs" }, }, { type: "default", - inputs: { label: "Power Usage", activeOption: "KWH" }, + inputs: { label: "Production period", activeOption: "yrs" }, }, - { type: "default", inputs: { label: "KWH", activeOption: "Mos" } }, { type: "default", - inputs: { label: "Man Power", activeOption: "Person" }, + inputs: { label: "Tax rate", activeOption: "%" }, }, ], }; @@ -98,7 +68,7 @@ const Analysis: React.FC = () => {
Object
- Generate Report + Generate Report
@@ -121,7 +91,7 @@ const Analysis: React.FC = () => { />
- +
Create Custom Analysis
diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index 8b91afc..514808a 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import InputRange from "../../../ui/inputs/InputRange"; import InputToggle from "../../../ui/inputs/InputToggle"; -import { AI_Icon } from "../../../icons/ExportCommonIcons"; +import { AIIcon } from "../../../icons/ExportCommonIcons"; import LabeledButton from "../../../ui/inputs/LabledButton"; import { useAzimuth, @@ -225,7 +225,7 @@ const GlobalProperties: React.FC = () => {
Environment
- + Optimize
diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx index 4fa9105..053fc14 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useState } from "react"; import { - useSelectedEventData, - useSelectedEventSphere, - useSelectedProduct, + useSelectedEventData, + useSelectedEventSphere, + useSelectedProduct, } from "../../../../../store/simulation/useSimulationStore"; import { useProductStore } from "../../../../../store/simulation/useProductStore"; import ConveyorMechanics from "./mechanics/conveyorMechanics"; @@ -15,114 +15,118 @@ import { handleAddEventToProduct } from "../../../../../modules/simulation/event import { useEventsStore } from "../../../../../store/simulation/useEventsStore"; const EventProperties: React.FC = () => { - const { selectedEventData } = useSelectedEventData(); - const { getEventByModelUuid } = useProductStore(); - const { selectedProduct } = useSelectedProduct(); - const [currentEventData, setCurrentEventData] = useState(null); - const [assetType, setAssetType] = useState(null); - const { products, addEvent } = useProductStore(); - const { selectedEventSphere } = useSelectedEventSphere(); + const { selectedEventData } = useSelectedEventData(); + const { getEventByModelUuid } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + const [currentEventData, setCurrentEventData] = useState( + null + ); + const [assetType, setAssetType] = useState(null); + const { products, addEvent } = useProductStore(); + const { selectedEventSphere } = useSelectedEventSphere(); - useEffect(() => { - const event = getCurrentEventData(); - setCurrentEventData(event); + useEffect(() => { + const event = getCurrentEventData(); + setCurrentEventData(event); - const type = determineAssetType(event); - setAssetType(type); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedEventData, selectedProduct]); - - const getCurrentEventData = () => { - if (!selectedEventData?.data || !selectedProduct) return null; - return ( - getEventByModelUuid( - selectedProduct.productId, - selectedEventData.data.modelUuid - ) ?? null - ); - }; - - const determineAssetType = (event: EventsSchema | null) => { - if (!event) return null; - - switch (event.type) { - case "transfer": - return "conveyor"; - case "vehicle": - return "vehicle"; - case "roboticArm": - return "roboticArm"; - case "machine": - return "machine"; - case "storageUnit": - return "storageUnit"; - default: - return null; - } - }; + const type = determineAssetType(event); + setAssetType(type); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedEventData, selectedProduct]); + const getCurrentEventData = () => { + if (!selectedEventData?.data || !selectedProduct) return null; return ( -
- {currentEventData && ( - <> -
-
- {selectedEventData?.data.modelName} -
-
- {assetType === "conveyor" && } - {assetType === "vehicle" && } - {assetType === "roboticArm" && } - {assetType === "machine" && } - {assetType === "storageUnit" && } - - )} - {!currentEventData && selectedEventSphere && ( -
-

- Oops! It looks like this object doesn't have an - event assigned yet. To continue, please link it to one of the - products below. -

- -
-

- Here are some products you can add it to: -

-
    - {products.map((product) => ( -
  • - -
  • - ))} -
-
-
- ) - } - {!selectedEventSphere && ( -
-

- Oops! It looks like you haven't selected an event - point yet. Please select an event to view its properties. -

-
- )} -
+ getEventByModelUuid( + selectedProduct.productId, + selectedEventData.data.modelUuid + ) ?? null ); + }; + + const determineAssetType = (event: EventsSchema | null) => { + if (!event) return null; + + switch (event.type) { + case "transfer": + return "conveyor"; + case "vehicle": + return "vehicle"; + case "roboticArm": + return "roboticArm"; + case "machine": + return "machine"; + case "storageUnit": + return "storageUnit"; + default: + return null; + } + }; + + return ( +
+ {currentEventData && ( + <> +
+
+ {selectedEventData?.data.modelName} +
+
+ {assetType === "conveyor" && } + {assetType === "vehicle" && } + {assetType === "roboticArm" && } + {assetType === "machine" && } + {assetType === "storageUnit" && } + + )} + {!currentEventData && selectedEventSphere && ( +
+

+ Oops! It looks like this object doesn't have an + event assigned yet. To continue, please link it to one of the + products below. +

+ +
+

+ Here are some products you can add it to: +

+
+ {products.map((product) => ( + + ))} +
+
+
+ )} + {!selectedEventSphere && ( +
+

+ Oops! It looks like you haven't selected an event + point yet. Please select an event to view its properties. +

+
+ )} +
+ ); }; export default EventProperties; diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx index 4c7c76d..901c537 100644 --- a/app/src/components/ui/simulation/simulationPlayer.tsx +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -12,25 +12,28 @@ import { EndIcon, ExpandIcon, HourlySimulationIcon, + InfoIcon, MonthlyROI, SpeedIcon, StartIcon, } from "../../icons/ExportCommonIcons"; import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor"; +import { useSubModuleStore } from "../../../store/useModuleStore"; const SimulationPlayer: React.FC = () => { const MAX_SPEED = 8; // Maximum speed + const isDragging = useRef(false); + const sliderRef = useRef(null); const [expand, setExpand] = useState(true); + const [playSimulation, setPlaySimulation] = useState(false); const { speed, setSpeed } = useAnimationPlaySpeed(); - const [playSimulation, setPlaySimulation] = useState(false); const { setIsPlaying } = usePlayButtonStore(); - const sliderRef = useRef(null); - const isDragging = useRef(false); const { setActiveTool } = useActiveTool(); const { isPaused, setIsPaused } = usePauseButtonStore(); const { isReset, setReset } = useResetButtonStore(); + const { subModule } = useSubModuleStore(); // Button functions const handleReset = () => { @@ -114,7 +117,7 @@ const SimulationPlayer: React.FC = () => { }; // Store colors for each process item - const [processColors, setProcessColors] = useState([]); + const [_, setProcessColors] = useState([]); // Generate colors on mount or when process changes useEffect(() => { @@ -162,50 +165,60 @@ const SimulationPlayer: React.FC = () => {
-
- {/* hourlySimulation */} -
-
-
- + {subModule === "analysis" && ( +
+ {/* hourlySimulation */} +
+
+
+ +
+
Hourly Simulation
+
+
+
-
Hourly Simulation
-
-
+ {/* dailyProduction */} +
+
+
+ +
+
Daily Production
+
+
+
+
+
+ {/* monthlyROI */} +
+
+
+ +
+
Monthly ROI
+
+
+
+
{" "}
- {/* dailyProduction */} -
-
-
- -
-
Daily Production
-
-
-
-
+ )} + {subModule === "simulations" && ( +
+ + {playSimulation + ? "Paused - system idle." + : "Running simulation..."}
- {/* monthlyROI */} -
-
-
- -
-
Monthly ROI
-
-
-
-
{" "} -
-
+ )}
- + {subModule === "analysis" && ( + + )}
@@ -330,37 +345,39 @@ const SimulationPlayer: React.FC = () => {
-
-
00:00
-
24:00
-
+ {subModule === "analysis" && ( +
+
00:00
+
24:00
- {process.map((item, index) => ( -
+
+ {process.map((item, index) => (
-
- ))} + key={index} + className="process" + style={{ + width: `${item.completed}%`, + backgroundColor: getAvatarColor(index), + }} + > +
+
+ ))} +
-
+ )}
); diff --git a/app/src/modules/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx index caf37a1..ad24e58 100644 --- a/app/src/modules/visualization/RealTimeVisulization.tsx +++ b/app/src/modules/visualization/RealTimeVisulization.tsx @@ -76,8 +76,6 @@ const RealTimeVisulization: React.FC = () => { const { setSelectedChartId } = useWidgetStore(); const [waitingPanels, setWaitingPanels] = useState(null); - console.log("waitingPanels: ", waitingPanels); - OuterClick({ contextClassName: [ "chart-container", diff --git a/app/src/styles/components/simulation/simulation.scss b/app/src/styles/components/simulation/simulation.scss index 0fc3df8..f40ead1 100644 --- a/app/src/styles/components/simulation/simulation.scss +++ b/app/src/styles/components/simulation/simulation.scss @@ -32,10 +32,17 @@ } .controls-container { - @include flex-center; + @include flex-space-between; gap: 12px; justify-content: space-between; - + .header{ + @include flex-center; + gap: 6px; + padding: 0 8px; + svg{ + scale: 1.3; + } + } .production-details, .controls-wrapper { @include flex-center; @@ -72,7 +79,7 @@ .expand-icon-container { @include flex-center; - padding: 6px 8px; + padding: 0 8px; cursor: pointer; } diff --git a/app/src/styles/components/tools.scss b/app/src/styles/components/tools.scss index e476c3c..0a23871 100644 --- a/app/src/styles/components/tools.scss +++ b/app/src/styles/components/tools.scss @@ -124,6 +124,8 @@ padding: 4px; border-radius: #{$border-radius-medium}; background: var(--background-color); + outline: 1px solid var(--border-color); + outline-offset: -1px; gap: 5px; position: relative; diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index c7f1694..f23d08e 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -58,12 +58,8 @@ fill: var(--icon-default-color-active); } &:hover { - rect { - stroke: var(--icon-default-color); - } - circle { - fill: var(--icon-default-color); - } + filter: saturate(0.8); + background: var(--background-color-accent); } } } @@ -714,10 +710,10 @@ .add-button { @include flex-center; - padding: 2px 4px; + padding: 4px 8px; background: var(--background-color-button); color: var(--text-button-color); - border-radius: #{$border-radius-small}; + border-radius: #{$border-radius-large}; cursor: pointer; outline: none; border: none; @@ -832,10 +828,10 @@ transform: translateX(4px); &:hover { - background: var(--accent-color); + background: var(--background-color-accent); path { - stroke: var(--primary-color); + stroke: var(--text-button-color); } } } @@ -1003,10 +999,10 @@ border-radius: 8px 0 0 8px; &:hover { - background: var(--accent-color); + background: var(--background-color-accent); path { - stroke: var(--primary-color); + stroke: var(--text-button-color); } } } @@ -1067,7 +1063,13 @@ .dropdown-content-container { padding: 6px 12px; } - + .value-field-container { + padding: 6px; + .dropdown { + min-width: 44px; + text-align: center; + } + } .input-range-container { .input-container { width: 75%; -- 2.40.1 From 4b7a868d1afbc499f671c6459781fda85375fa36 Mon Sep 17 00:00:00 2001 From: Nalvazhuthi Date: Fri, 2 May 2025 17:14:36 +0530 Subject: [PATCH 18/32] added logs list --- app/src/app.tsx | 28 ++-- .../components/icons/ExportCommonIcons.tsx | 150 +++++++++++++++--- .../ui/analysis/ThroughputSummary.tsx | 9 +- app/src/components/ui/footer/Footer.tsx | 44 +++++ app/src/components/ui/log/LogList.tsx | 87 ++++++++++ app/src/components/ui/log/LoggerContext.tsx | 77 +++++++++ app/src/components/ui/log/logger.ts | 71 +++++++++ .../ui/simulation/simulationPlayer.tsx | 14 +- app/src/pages/Project.tsx | 13 +- app/src/styles/components/footer/footer.scss | 56 +++++++ app/src/styles/components/logs/logs.scss | 75 +++++++++ .../components/simulation/simulation.scss | 21 +++ app/src/styles/layout/sidebar.scss | 4 +- app/src/styles/main.scss | 2 + app/src/styles/pages/realTimeViz.scss | 4 +- 15 files changed, 604 insertions(+), 51 deletions(-) create mode 100644 app/src/components/ui/footer/Footer.tsx create mode 100644 app/src/components/ui/log/LogList.tsx create mode 100644 app/src/components/ui/log/LoggerContext.tsx create mode 100644 app/src/components/ui/log/logger.ts create mode 100644 app/src/styles/components/footer/footer.scss create mode 100644 app/src/styles/components/logs/logs.scss diff --git a/app/src/app.tsx b/app/src/app.tsx index f5e19b0..af3284b 100644 --- a/app/src/app.tsx +++ b/app/src/app.tsx @@ -4,23 +4,23 @@ import Dashboard from "./pages/Dashboard"; import Project from "./pages/Project"; import UserAuth from "./pages/UserAuth"; import ToastProvider from "./components/templates/ToastProvider"; -import "./styles/main.scss" +import "./styles/main.scss"; +import { LoggerProvider } from "./components/ui/log/LoggerContext"; const App: React.FC = () => { return ( - - - - } - /> - } /> - } /> - - - + + + + + } /> + } /> + } /> + + + + ); }; -export default App; +export default App; \ No newline at end of file diff --git a/app/src/components/icons/ExportCommonIcons.tsx b/app/src/components/icons/ExportCommonIcons.tsx index 8e03fd0..67d7211 100644 --- a/app/src/components/icons/ExportCommonIcons.tsx +++ b/app/src/components/icons/ExportCommonIcons.tsx @@ -785,21 +785,135 @@ export const SpeedIcon = () => { ); }; -// export const DublicateIcon = () => { -// return ( -// -// -// -// ); -// }; +export const LogListIcon = () => { + return ( + + + + ); +}; + +export const LogTickIcon = () => { + return ( + + + + + + + + + + + ); +}; + +export const LogInfoIcon = () => { + return ( + + + + + + + ); +}; + +export const WarningIcon = () => { + return ( + + + + + + + ); +}; + +export const ErrorIcon = () => { + return ( + + + + ); +}; diff --git a/app/src/components/ui/analysis/ThroughputSummary.tsx b/app/src/components/ui/analysis/ThroughputSummary.tsx index b92a82d..e9946ae 100644 --- a/app/src/components/ui/analysis/ThroughputSummary.tsx +++ b/app/src/components/ui/analysis/ThroughputSummary.tsx @@ -8,6 +8,7 @@ import { PointElement, } from "chart.js"; import { PowerIcon, ThroughputSummaryIcon } from "../../icons/analysis"; +import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor"; ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement); @@ -142,13 +143,13 @@ const ThroughputSummary = () => {
{/* Dynamically create progress bars based on shiftUtilization array */} - {shiftUtilization.map((shift) => ( + {shiftUtilization.map((shift, index) => (
))} @@ -156,11 +157,11 @@ const ThroughputSummary = () => {
{/* Dynamically create shift indicators with random colors */} - {shiftUtilization.map((shift) => ( + {shiftUtilization.map((shift, index) => (
diff --git a/app/src/components/ui/footer/Footer.tsx b/app/src/components/ui/footer/Footer.tsx new file mode 100644 index 0000000..93da68c --- /dev/null +++ b/app/src/components/ui/footer/Footer.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import { HelpIcon } from "../../icons/DashboardIcon"; +import { useLogger } from "../log/LoggerContext"; + +const Footer = () => { + const { logs, setIsLogListVisible } = useLogger(); + + const lastLog = logs.length > 0 ? logs[logs.length - 1] : null; + + return ( +
+
+
+
+
Selection
+
+
+
+
Rotate/Zoom
+
+
+
+
Pan/Context Menu
+
+
+ +
+
setIsLogListVisible(true)}> + {lastLog + ? `[${lastLog.type.toUpperCase()}] ${lastLog.message}` + : "No logs yet."} +
+
+ V 0.01 +
+ +
+
+
+
+ ); +}; + +export default Footer; diff --git a/app/src/components/ui/log/LogList.tsx b/app/src/components/ui/log/LogList.tsx new file mode 100644 index 0000000..4a43933 --- /dev/null +++ b/app/src/components/ui/log/LogList.tsx @@ -0,0 +1,87 @@ +// LogList.tsx +import React, { useState } from "react"; +import { + LogListIcon, + TickIcon, + LogInfoIcon, + WarningIcon, + ErrorIcon, +} from "../../icons/ExportCommonIcons"; // Adjust path as needed +import { useLogger } from "./LoggerContext"; + +const LogList: React.FC = () => { + const { logs, clear, setIsLogListVisible } = useLogger(); + const [selectedTab, setSelectedTab] = useState< + "all" | "info" | "warning" | "error" | "log" + >("all"); + + const getLogIcon = (type: string) => { + switch (type) { + case "info": + return ; + case "error": + return ; + case "warning": + return ; + case "log": + default: + return ; + } + }; + + const formatTimestamp = (date: Date) => new Date(date).toLocaleTimeString(); + + const filteredLogs = + selectedTab === "all" + ? [...logs].reverse() + : [...logs].filter((log) => log.type === selectedTab).reverse(); + + return ( +
+
+
+
+
+ +
+
Log List
+
+
setIsLogListVisible(false)}> + X +
+
+ + {/* Tabs */} +
+ {["all", "info", "warning", "error"].map((type) => ( +
setSelectedTab(type as any)} + > + {`${type.charAt(0).toUpperCase() + type.slice(1)} Logs`} +
+ ))} +
+ + {/* Log Entries */} +
+ {filteredLogs.map((log) => ( +
+
+ +
+
{getLogIcon(log.type)}
+
+ [{formatTimestamp(log.timestamp)}] [{log.type.toUpperCase()}]{" "} + {log.message} +
+
+ ))} +
+
+
+ ); +}; + +export default LogList; diff --git a/app/src/components/ui/log/LoggerContext.tsx b/app/src/components/ui/log/LoggerContext.tsx new file mode 100644 index 0000000..f323b93 --- /dev/null +++ b/app/src/components/ui/log/LoggerContext.tsx @@ -0,0 +1,77 @@ +import React, { createContext, useContext, useState, useCallback } from "react"; + +export type LogType = "log" | "info" | "warning" | "error"; + +export interface LogEntry { + id: string; + type: LogType; + message: string; + timestamp: Date; +} + +interface LoggerContextValue { + logs: LogEntry[]; + setLogs: React.Dispatch>; + isLogListVisible: boolean; + setIsLogListVisible: React.Dispatch>; + log: (message: string) => void; + info: (message: string) => void; + warn: (message: string) => void; + error: (message: string) => void; + clear: () => void; +} + +const LoggerContext = createContext(undefined); + +export const LoggerProvider: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + const [logs, setLogs] = useState([]); + const [isLogListVisible, setIsLogListVisible] = useState(false); + + const generateId = useCallback( + () => Math.random().toString(36).substring(2, 9), + [] + ); + + const addLog = useCallback( + (type: LogType, message: string) => { + const newLog: LogEntry = { + id: generateId(), + type, + message, + timestamp: new Date(), + }; + setLogs((prevLogs) => [...prevLogs, newLog]); + }, + [generateId] + ); + + const loggerMethods: LoggerContextValue = { + logs, + setLogs, + isLogListVisible, + setIsLogListVisible, + log: (message: string) => addLog("log", message), + info: (message: string) => addLog("info", message), + warn: (message: string) => addLog("warning", message), + error: (message: string) => addLog("error", message), + clear: () => { + setLogs([]); + }, + }; + + return ( + + {children} + + ); +}; + +export const useLogger = () => { + const context = useContext(LoggerContext); + if (!context) { + throw new Error("useLogger must be used within a LoggerProvider"); + } + return context; +}; diff --git a/app/src/components/ui/log/logger.ts b/app/src/components/ui/log/logger.ts new file mode 100644 index 0000000..1781e26 --- /dev/null +++ b/app/src/components/ui/log/logger.ts @@ -0,0 +1,71 @@ +type LogType = 'log' | 'info' | 'warning' | 'error'; + +interface LogEntry { + type: LogType; + message: string; + timestamp: Date; + context?: string; +} + +class Logger { + private static instance: Logger; + private logs: LogEntry[] = []; + private subscribers: Array<(log: LogEntry) => void> = []; + + private constructor() {} + + public static getInstance(): Logger { + if (!Logger.instance) { + Logger.instance = new Logger(); + } + return Logger.instance; + } + + private notifySubscribers(log: LogEntry) { + this.subscribers.forEach(callback => callback(log)); + } + + private addLog(type: LogType, message: string, context?: string) { + const log: LogEntry = { type, message, timestamp: new Date(), context }; + this.logs.push(log); + this.notifySubscribers(log); + + if (process.env.NODE_ENV === 'development') { + const logMessage = context ? `[${context}] ${message}` : message; + console[type === 'warning' ? 'warn' : type](logMessage); + } + } + + public log(message: string, context?: string) { + this.addLog('log', message, context); + } + + public info(message: string, context?: string) { + this.addLog('info', message, context); + } + + public warning(message: string, context?: string) { + this.addLog('warning', message, context); + } + + public error(message: string, context?: string) { + this.addLog('error', message, context); + } + + public getLogs(): LogEntry[] { + return [...this.logs]; + } + + public clear() { + this.logs = []; + } + + public subscribe(callback: (log: LogEntry) => void) { + this.subscribers.push(callback); + return () => { + this.subscribers = this.subscribers.filter(sub => sub !== callback); + }; + } +} + +export const logger = Logger.getInstance(); diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx index 86c5372..6ab6738 100644 --- a/app/src/components/ui/simulation/simulationPlayer.tsx +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -16,6 +16,7 @@ import { SpeedIcon, StartIcon, } from "../../icons/ExportCommonIcons"; +import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor"; const SimulationPlayer: React.FC = () => { const { speed, setSpeed } = useAnimationPlaySpeed(); @@ -110,15 +111,6 @@ const SimulationPlayer: React.FC = () => { return color; }; - // Store colors for each process item - const [processColors, setProcessColors] = useState([]); - - // Generate colors on mount or when process changes - useEffect(() => { - const generatedColors = process.map(() => getRandomColor()); - setProcessColors(generatedColors); - }, []); - const intervals = [10, 20, 30, 40, 50, 60]; // in minutes const totalSegments = intervals.length; const progress = 20; // percent (example) @@ -328,6 +320,8 @@ const SimulationPlayer: React.FC = () => {
+
00:00
+
24:00
{ className="process" style={{ width: `${item.completed}%`, - backgroundColor: processColors[index], + backgroundColor: getAvatarColor(index), }} >
))} diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index bdefb8b..c3b70e7 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -25,6 +25,10 @@ import FollowPerson from "../components/templates/FollowPerson"; import ProductionCapacity from "../components/ui/analysis/ProductionCapacity"; import ThroughputSummary from "../components/ui/analysis/ThroughputSummary"; import ROISummary from "../components/ui/analysis/ROISummary"; +import Footer from "../components/ui/footer/Footer"; +import RenderOverlay from "../components/templates/Overlay"; +import LogList from "../components/ui/log/LogList"; +import { useLogger } from "../components/ui/log/LoggerContext"; const Project: React.FC = () => { let navigate = useNavigate(); @@ -58,6 +62,7 @@ const Project: React.FC = () => { const { isPlaying } = usePlayButtonStore(); const { toggleThreeD } = useThreeDStore(); const { selectedUser } = useSelectedUserStore(); + const { isLogListVisible } = useLogger(); return (
@@ -69,7 +74,7 @@ const Project: React.FC = () => {
*/} - {loadingProgress > 0 && } + {/* {loadingProgress > 0 && } */} {!isPlaying && ( <> {toggleThreeD && } @@ -85,7 +90,13 @@ const Project: React.FC = () => { {activeModule !== "market" && } {isPlaying && activeModule === "simulation" && } {/* {} */} + {isLogListVisible && ( + + + + )} {selectedUser && } +
); }; diff --git a/app/src/styles/components/footer/footer.scss b/app/src/styles/components/footer/footer.scss new file mode 100644 index 0000000..a595db0 --- /dev/null +++ b/app/src/styles/components/footer/footer.scss @@ -0,0 +1,56 @@ +.footer-wrapper { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + z-index: 1; + display: flex; + justify-content: space-between; + + .selection-wrapper { + display: flex; + gap: 6px; + + .selector-wrapper { + display: flex; + gap: 6px; + align-items: center; + background: var(--background-color); + padding: 4px 6px; + border-radius: 12px; + color: var(--text-color); + + .selector { + color: var(--text-button-color); + font-weight: 200; + } + } + } + + .logs-wrapper { + display: flex; + gap: 6px; + + .logs-detail, + .version { + border-radius: 12px; + background: var(--background-color); + padding: 4px 6px; + color: var(--text-button-color); + font-weight: 200; + display: flex; + align-items: center; + } + + .logs-detail { + background-color: #fff; + cursor: pointer; + } + + .version { + display: flex; + gap: 6px; + + } + } +} \ No newline at end of file diff --git a/app/src/styles/components/logs/logs.scss b/app/src/styles/components/logs/logs.scss new file mode 100644 index 0000000..99e7548 --- /dev/null +++ b/app/src/styles/components/logs/logs.scss @@ -0,0 +1,75 @@ +.log-list-container { + width: 100vw; + height: 100vh; + background: var(--background-color-secondary); + backdrop-filter: blur(2px); + + .log-list-wrapper { + height: 50%; + min-width: 50%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 5; + background: var(--background-color); + padding: 14px 12px; + border-radius: 15px; + display: flex; + flex-direction: column; + gap: 12px; + + .log-header { + display: flex; + justify-content: space-between; + + .log-header-wrapper { + display: flex; + align-items: center; + gap: 6px; + } + + .close { + cursor: pointer; + } + } + + .log-nav-wrapper { + display: flex; + gap: 6px; + + .log-nav { + padding: 8px 16px; + border-radius: 19px; + } + + .log-nav.active { + background-color: var(--accent-color); + color: var(--text-button-color); + } + } + + .log-entry-wrapper { + height: 100%; + display: flex; + flex-direction: column; + gap: 4px; + background: var(--background-color); + padding: 18px 10px; + border-radius: 16px; + + .log-entry { + padding: 4px; + border-radius: 4px; + font-size: var(--font-size-small); + display: flex; + align-items: center; + gap: 6px; + + &:nth-child(odd) { + background: var(--background-color); + } + } + } + } +} \ No newline at end of file diff --git a/app/src/styles/components/simulation/simulation.scss b/app/src/styles/components/simulation/simulation.scss index ec53e6f..61655e4 100644 --- a/app/src/styles/components/simulation/simulation.scss +++ b/app/src/styles/components/simulation/simulation.scss @@ -312,6 +312,22 @@ padding: 14px 6px; position: relative; + .timmer { + width: auto; + position: absolute; + bottom: 0; + font-size: var(--font-size-tiny); + } + + .start-displayer { + left: 8px; + } + + .end-displayer { + width: auto; + right: 8px; + } + .process-player { position: absolute; top: 50%; @@ -344,6 +360,11 @@ .simulation-player-container.open { + .start-displayer, + .end-displayer { + display: none; + } + .progresser-wrapper { padding-top: 4px; } diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index c7f1694..f4ccaa8 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -420,7 +420,7 @@ outline: none; path { stroke: var(--text-button-color); - stroke-width: 1.3; + strokeWidth: 1.3; } } } @@ -686,7 +686,7 @@ path { stroke: var(--accent-color); - stroke-width: 1.5px; + strokeWidth: 1.5px; } &:hover { diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index 66a60e7..9fe7222 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -27,6 +27,8 @@ @use 'components/confirmationPopUp'; @use 'components/analysis/analysis'; @use 'components/analysis/ROISummary.scss'; +@use 'components/logs/logs'; +@use 'components/footer/footer.scss'; // layout @use 'layout/loading'; diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index 7348eff..b5fff7e 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -416,7 +416,7 @@ path { stroke: var(--text-color); - stroke-width: 2; + strokeWidth: 2; } } @@ -428,7 +428,7 @@ path { stroke: #f65648; - stroke-width: 1.3; + strokeWidth: 1.3; } -- 2.40.1 From 439f91788470c26032912d37738a60eb3969c242 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Fri, 2 May 2025 17:34:55 +0530 Subject: [PATCH 19/32] Refactor Trigger component: pass selectedPointData and type props, enhance state management for triggers, and streamline rendering logic for better performance. --- .../mechanics/conveyorMechanics.tsx | 2 +- .../eventProperties/trigger/Trigger.tsx | 237 +++++++++--------- 2 files changed, 117 insertions(+), 122 deletions(-) diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx index ded65af..a6a1fa9 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx @@ -271,7 +271,7 @@ function ConveyorMechanics() {
- +
diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx index 1434446..86e4e7b 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx @@ -1,137 +1,132 @@ -import React, { useRef, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { - AddIcon, - RemoveIcon, - ResizeHeightIcon, + AddIcon, + RemoveIcon, + ResizeHeightIcon, } from "../../../../../icons/ExportCommonIcons"; import LabledDropdown from "../../../../../ui/inputs/LabledDropdown"; import RenameInput from "../../../../../ui/inputs/RenameInput"; import { handleResize } from "../../../../../../functions/handleResizePannel"; +import { useProductStore } from "../../../../../../store/simulation/useProductStore"; +import { useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore"; -const Trigger: React.FC = () => { - // State to hold the list of triggers - const [triggers, setTriggers] = useState(["Trigger 1"]); - const [selectedTrigger, setSelectedTrigger] = useState("Trigger 1"); - const [activeOption, setActiveOption] = useState("onComplete"); - const triggersContainerRef = useRef(null); +type TriggerProps = { + selectedPointData?: PointsScheme | undefined; + type?: 'Conveyor' | 'Vehicle' | 'RoboticArm' | 'Machine' | 'StorageUnit'; +}; - // States for dropdowns - const [triggeredModel, setTriggeredModel] = useState([]); - const [triggeredPoint, setTriggeredPoint] = useState([]); - const [triggeredAction, setTriggeredAction] = useState([]); +const Trigger = ({ selectedPointData, type }: TriggerProps) => { + const [currentAction, setCurrentAction] = useState(); + const { selectedProduct } = useSelectedProduct(); + const { getActionByUuid } = useProductStore(); + const [triggers, setTriggers] = useState([]); + const [selectedTrigger, setSelectedTrigger] = useState(); + const [activeOption, setActiveOption] = useState("onComplete"); + const triggersContainerRef = useRef(null); - // Function to handle adding a new trigger - const addTrigger = (): void => { - const newTrigger = `Trigger ${triggers.length + 1}`; - setTriggers([...triggers, newTrigger]); // Add new trigger to the state + useEffect(() => { + if (!selectedPointData) return; + if (type === 'Conveyor' || type === 'Vehicle' || type === 'Machine' || type === 'StorageUnit') { + setCurrentAction((selectedPointData as ConveyorPointSchema).action.actionUuid); + } + }, [selectedPointData]); - // Initialize the states for the new trigger - setTriggeredModel([...triggeredModel, ""]); - setTriggeredPoint([...triggeredPoint, ""]); - setTriggeredAction([...triggeredAction, ""]); - }; + useEffect(() => { + if (!currentAction || !selectedProduct) return; + const action = getActionByUuid(selectedProduct.productId, currentAction); + setTriggers(action?.triggers || []); + setSelectedTrigger(action?.triggers[0] || undefined); + }, [currentAction, selectedProduct]); - // Function to handle removing a trigger - const removeTrigger = (index: number): void => { - setTriggers(triggers.filter((_, i) => i !== index)); // Remove trigger by index - setTriggeredModel(triggeredModel.filter((_, i) => i !== index)); - setTriggeredPoint(triggeredPoint.filter((_, i) => i !== index)); - setTriggeredAction(triggeredAction.filter((_, i) => i !== index)); - }; + const addTrigger = (): void => { + }; - return ( -
-
-
Trigger
- -
-
-
-
- {triggers.map((trigger: any, index: number) => ( -
setSelectedTrigger(trigger)} - > - - {triggers.length > 1 && ( - - )} -
- ))} -
- +
+
+
+
+ {triggers.map((trigger) => ( +
setSelectedTrigger(trigger)} + > + + {triggers.length > 1 && ( + + )} +
+ ))} +
+ +
+
+
{selectedTrigger?.triggerName}
+ setActiveOption(option)} + /> +
+ { }} + /> + { }} + /> + { }} + /> +
+
+
-
-
{selectedTrigger}
- setActiveOption(option)} - /> -
- { - const newModel = [...triggeredModel]; - newModel[0] = option; - setTriggeredModel(newModel); - }} - /> - { - const newPoint = [...triggeredPoint]; - newPoint[0] = option; - setTriggeredPoint(newPoint); - }} - /> - { - const newAction = [...triggeredAction]; - newAction[0] = option; - setTriggeredAction(newAction); - }} - /> -
-
-
-
- ); + ); }; export default Trigger; -- 2.40.1 From d83b934e61554c0dd17c76b060a7d477da1a9476 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Fri, 2 May 2025 17:35:52 +0530 Subject: [PATCH 20/32] armbot circular path updated and ui dynamic updation --- .../IntialLoad/loadInitialFloorItems.ts | 2 +- .../events/points/creator/pointsCreator.tsx | 55 ++- .../instances/animator/roboticArmAnimator.tsx | 316 +++++++---------- .../armInstance/roboticArmInstance.tsx | 312 +---------------- .../instances/ikInstance/ikInstance.tsx | 3 +- .../instances/roboticArmInstances.tsx | 2 - .../simulation/roboticArm/roboticArm.tsx | 42 ++- .../simulation/ui/arm/PickDropPoints.tsx | 20 +- .../modules/simulation/ui/arm/armBotUI.tsx | 328 ++++++++++-------- .../simulation/ui/arm/useDraggableGLTF.ts | 56 +-- 10 files changed, 438 insertions(+), 698 deletions(-) diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index a351d73..9515188 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -206,7 +206,7 @@ function processLoadedModel( actionType: "travel", unLoadDuration: 5, loadCapacity: 10, - steeringAngle:0, + steeringAngle: 0, pickUpPoint: null, unLoadPoint: null, triggers: [] diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index c6ec316..e4eaafb 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -23,6 +23,9 @@ function PointsCreator() { const eventData = getEventByModelUuid( selectedEventSphere.userData.modelUuid ); + + + if (eventData) { setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid); } else { @@ -80,7 +83,10 @@ function PointsCreator() { {events.map((event, i) => { if (event.type === "transfer") { return ( - + {event.points.map((point, j) => ( { if (selectedEventData?.data.type !== 'vehicle') { - clearSelectedEventSphere(); + // clearSelectedEventSphere(); } setTransformMode(null); }} key={`${i}-${j}`} position={new THREE.Vector3(...point.position)} - // rotation={new THREE.Euler(...point.rotation)} - userData={{ modelUuid: event.modelUuid, pointUuid: point.uuid }} + userData={{ + modelUuid: event.modelUuid, + pointUuid: point.uuid, + }} > @@ -111,7 +119,10 @@ function PointsCreator() { ); } else if (event.type === "vehicle") { return ( - + @@ -137,7 +150,10 @@ function PointsCreator() { ); } else if (event.type === "roboticArm") { return ( - + { - clearSelectedEventSphere(); + // clearSelectedEventSphere(); setTransformMode(null); }} position={new THREE.Vector3(...event.point.position)} - // rotation={new THREE.Euler(...event.point.rotation)} - userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} > @@ -163,7 +181,10 @@ function PointsCreator() { ); } else if (event.type === "machine") { return ( - + { - clearSelectedEventSphere(); + // clearSelectedEventSphere(); setTransformMode(null); }} position={new THREE.Vector3(...event.point.position)} - // rotation={new THREE.Euler(...event.point.rotation)} - userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + userData={{ + modelUuid: event.modelUuid, + pointUuid: event.point.uuid, + }} > @@ -208,4 +231,4 @@ function PointsCreator() { ); } -export default PointsCreator; +export default PointsCreator; \ 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 d1bf523..d32a6c3 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useFrame } from '@react-three/fiber'; import * as THREE from 'three'; import { Line } from '@react-three/drei'; @@ -21,14 +21,8 @@ function RoboticArmAnimator({ const progressRef = useRef(0); const curveRef = useRef(null); const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); - const [curvePoints, setCurvePoints] = useState(null); const [circlePoints, setCirclePoints] = useState<[number, number, number][]>([]); - const [nearestStartPoint, setNearestStartPoint] = useState<[number, number, number] | null>(null); - const [nearestEndPoint, setNearestEndPoint] = useState<[number, number, number] | null>(null); const [customCurvePoints, setCustomCurvePoints] = useState(null); - const stepSegmentsRef = useRef([]); - const [currentStep, setCurrentStep] = useState(0); - const elapsedTimeRef = useRef(0); // Zustand stores const { isPlaying } = usePlayButtonStore(); @@ -41,7 +35,6 @@ function RoboticArmAnimator({ setCurrentPath(path); }, [path]); - // Reset logic when `isPlaying` changes useEffect(() => { if (!isPlaying) { @@ -50,221 +43,172 @@ function RoboticArmAnimator({ } }, [isPlaying]); - // Handle path generation (including CatmullRomCurve3) - useEffect(() => { - if (currentPath && currentPath.length === 3) { - const [start, mid, end] = currentPath; - const points = [ - new THREE.Vector3(start[0], start[1], start[2]), - new THREE.Vector3(start[0], mid[1] + 0.5, start[2]), - new THREE.Vector3(mid[0], mid[1] + 0.5, mid[2]), - new THREE.Vector3(mid[0], end[1] + 0.5, mid[2]), - new THREE.Vector3(end[0], end[1], end[2]) - ]; - const generatedCurve = new THREE.CatmullRomCurve3(points, false, 'centripetal', 1).getPoints(100); - curveRef.current = generatedCurve; - setCurvePoints(generatedCurve); - } - }, [currentPath]); - // Handle circle points based on armBot position useEffect(() => { - const radius = 1.6; // Circle radius - const segments = 64; - const points: [number, number, number][] = []; - - for (let i = 0; i <= segments; i++) { - const theta = (i / segments) * Math.PI * 2; - const x = radius * Math.cos(theta) + armBot.position[0]; - const z = radius * Math.sin(theta) + armBot.position[2]; - const y = armBot.position[1]; - points.push([x, y, z]); - } - + const points = generateRingPoints(1.6, 64) setCirclePoints(points); }, [armBot.position]); + + 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 startPoint = [currentPath[0][0], currentPath[0][1] + 0.5, currentPath[0][2]] as [number, number, number]; - const endPoint = [currentPath[currentPath.length - 1][0], currentPath[currentPath.length - 1][1] + 0.5, currentPath[currentPath.length - 1][2]] as [number, number, number]; - - const findNearest = (target: [number, number, number]) => { - let nearestPoint = circlePoints[0]; - let minDistance = Infinity; - - for (const point of circlePoints) { - const dx = target[0] - point[0]; - const dy = target[1] - point[1]; - const dz = target[2] - point[2]; - const distance = Math.sqrt(dx * dx + dy * dy + dz * dz); - if (distance < minDistance) { - minDistance = distance; - nearestPoint = point; - } - } - return nearestPoint; - }; - - const nearestToStart = findNearest(startPoint); - const nearestToEnd = findNearest(endPoint); - - setNearestStartPoint(nearestToStart); - setNearestEndPoint(nearestToEnd); - - const radius = 1.6; - const segments = 64; - const center = armBot.position; - - const startAngle = Math.atan2(nearestToStart[2] - center[2], nearestToStart[0] - center[0]); - const endAngle = Math.atan2(nearestToEnd[2] - center[2], nearestToEnd[0] - center[0]); - let deltaAngle = endAngle - startAngle; - - if (deltaAngle > Math.PI) { - deltaAngle -= 2 * Math.PI; - } else if (deltaAngle < -Math.PI) { - deltaAngle += 2 * Math.PI; - } - - const arcPoints: [number, number, number][] = []; - - for (let i = 0; i <= segments; i++) { - const t = i / segments; - const angle = startAngle + deltaAngle * t; - const x = center[0] + radius * Math.cos(angle); - const z = center[2] + radius * Math.sin(angle); - const y = startPoint[1]; - arcPoints.push([x, y, z]); - } const start = currentPath[0]; const end = currentPath[currentPath.length - 1]; - let step1 = [ - [start[0], start[1], start[2]], - [start[0], start[1] + 0.5, start[2]] - ]; - let step2 = [ - [start[0], start[1] + 0.5, start[2]], - [nearestToStart[0], start[1] + 0.5, nearestToStart[2]] - ]; - let step3 = [ - [nearestToStart[0], start[1] + 0.5, nearestToStart[2]], - [end[0], arcPoints[arcPoints.length - 1][1], end[2]] - ] - let step4 = [ - [end[0], arcPoints[arcPoints.length - 1][1], end[2]], - [end[0], end[1] + 0.5, end[2]] - ] - let step5 = [ - [end[0], end[1] + 0.5, end[2]], - [end[0], end[1], end[2]], - ] + 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 points = [ - new THREE.Vector3(start[0], start[1], start[2]), - new THREE.Vector3(start[0], start[1] + 0.5, start[2]), + 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]); + }; - new THREE.Vector3(start[0], start[1] + 0.5, start[2]), - new THREE.Vector3(nearestToStart[0], start[1] + 0.5, nearestToStart[2]), + const nearestToStart = findNearest(raisedStart); - new THREE.Vector3(nearestToStart[0], start[1] + 0.5, nearestToStart[2]), - new THREE.Vector3(end[0], arcPoints[arcPoints.length - 1][1], end[2]), + const nearestToEnd = findNearest(raisedEnd); - new THREE.Vector3(end[0], arcPoints[arcPoints.length - 1][1], end[2]), - new THREE.Vector3(end[0], end[1] + 0.5, end[2]), + const indexOfNearestStart = findNearestIndex(nearestToStart, circlePoints); - new THREE.Vector3(end[0], end[1] + 0.5, end[2]), - new THREE.Vector3(end[0], end[1], end[2]) - ]; - const steps = [ - [points[0], points[1]], // step1 - [points[2], points[3]], // step2 - [points[4], points[5]], // step3 - [points[6], points[7]], // step4 - [points[8], points[9]] // step5 - ]; - stepSegmentsRef.current = steps; - setCurrentStep(0); - elapsedTimeRef.current = 0; + const indexOfNearestEnd = findNearestIndex(nearestToEnd, circlePoints); - const finalPath = [ - new THREE.Vector3(start[0], start[1], start[2]), - new THREE.Vector3(start[0], start[1] + 0.5, start[2]), - new THREE.Vector3(nearestToStart[0], start[1] + 0.5, nearestToStart[2]), - ...arcPoints.map(([x, y, z]) => new THREE.Vector3(x, y, z)), - new THREE.Vector3(end[0], arcPoints[arcPoints.length - 1][1], end[2]), - new THREE.Vector3(end[0], end[1], end[2]) + // Find clockwise and counter-clockwise distances + const clockwiseDistance = (indexOfNearestEnd - indexOfNearestStart + 64) % 64; + + const counterClockwiseDistance = (indexOfNearestStart - indexOfNearestEnd + 64) % 64; + + const clockwiseIsShorter = clockwiseDistance <= counterClockwiseDistance; + + // Collect arc points between start and end + let arcPoints: [number, number, number][] = []; + + if (clockwiseIsShorter) { + if (indexOfNearestStart <= indexOfNearestEnd) { + arcPoints = circlePoints.slice(indexOfNearestStart, indexOfNearestEnd + 1); + } else { + // Wrap around + 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]); + } + } + } + + // Continue your custom path logic + const pathVectors = [ + new THREE.Vector3(start[0], start[1], start[2]), // start + new THREE.Vector3(raisedStart[0], raisedStart[1], raisedStart[2]), // lift up + new THREE.Vector3(nearestToStart[0], raisedStart[1], nearestToStart[2]), // move to arc start + ...arcPoints.map(point => new THREE.Vector3(point[0], raisedStart[1], point[2])), + new THREE.Vector3(nearestToEnd[0], raisedEnd[1], nearestToEnd[2]), // move from arc end + new THREE.Vector3(raisedEnd[0], raisedEnd[1], raisedEnd[2]), // lowered end + new THREE.Vector3(end[0], end[1], end[2]) // end ]; - const customCurve = new THREE.CatmullRomCurve3(finalPath, false, 'centripetal', 1); - const customCurveGeneratedPoints = customCurve.getPoints(100); - - setCustomCurvePoints(customCurveGeneratedPoints); + const customCurve = new THREE.CatmullRomCurve3(pathVectors, false, 'centripetal', 1); + const generatedPoints = customCurve.getPoints(100); + setCustomCurvePoints(generatedPoints); } }, [circlePoints, currentPath]); // Frame update for animation useFrame((_, delta) => { - if (ikSolver && curveRef.current && currentPath.length >= 0) { - const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); - if (!bone) return; + if (!ikSolver) return; - if (isPlaying) { - if (!isPaused) { - const curvePoints = curveRef.current; - const speedAdjustedProgress = progressRef.current + (speed * armBot.speed); - const index = Math.floor(speedAdjustedProgress); + const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); + if (!bone) return; - if (curvePoints) { - - if (index >= curvePoints.length) { - HandleCallback(); - setCurrentPath([]); - curveRef.current = null; - progressRef.current = 0; - } else { - const point = curvePoints[index]; - bone.position.copy(point); - progressRef.current = speedAdjustedProgress; - } - } + if (isPlaying) { + if (!isPaused && customCurvePoints && currentPath.length > 0) { + const curvePoints = customCurvePoints; + const speedAdjustedProgress = progressRef.current + (speed * armBot.speed); + const index = Math.floor(speedAdjustedProgress); + if (index >= curvePoints.length) { + // Reached the end of the curve + HandleCallback(); + setCurrentPath([]); + curveRef.current = null; + progressRef.current = 0; } else { - logStatus(armBot.modelUuid, 'Simulation Play Exited'); + const point = curvePoints[index]; + bone.position.copy(point); + progressRef.current = speedAdjustedProgress; } - - ikSolver.update(); + } else if (isPaused) { + logStatus(armBot.modelUuid, 'Simulation Paused'); } - } else if (ikSolver && !isPlaying && currentPath.length === 0) { - const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); + + ikSolver.update(); + } else if (!isPlaying && currentPath.length === 0) { + // Not playing anymore, reset to rest bone.position.copy(restPosition); ikSolver.update(); } }); + return ( - <> - {customCurvePoints && currentPath && ( - - [p.x, p.y, p.z])} color="green" lineWidth={5} dashed={false} /> - {/* */} - {currentPath.length >= 1 && ( - <> - {/* First point */} - {/* - - - */} - - {/* Last point */} - {/* - - - */} - - )} + {customCurvePoints && currentPath && isPlaying && ( + + [p.x, p.y, p.z] as [number, number, number])} + color="green" + lineWidth={5} + dashed={false} + /> )} diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 7660ad1..ff5e7e3 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -8,6 +8,7 @@ import { useThree } from "@react-three/fiber"; import { useFloorItems } from '../../../../../store/store'; import useModuleStore from '../../../../../store/useModuleStore'; import * as THREE from "three"; +import { useSelectedAction } from '../../../../../store/simulation/useSimulationStore'; function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { @@ -28,6 +29,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { const { isPlaying } = usePlayButtonStore(); const { isReset, setReset } = useResetButtonStore(); const { isPaused } = usePauseButtonStore(); + const { selectedAction } = useSelectedAction(); function firstFrame() { startTime = performance.now(); @@ -78,6 +80,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { startTime=0; const endPoint = armBot.point.actions[0].process.endPoint; if (endPoint) { + let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition); if (curve) { logStatus(armBot.modelUuid, "dropping the object"); @@ -93,21 +96,17 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { }, [isPaused]); useEffect(() => { - let armItems = floorItems?.filter((val: any) => - val.modelUuid === "3abf5d46-b59e-4e6b-9c02-a4634b64b82d" - ); - // Get the first matching item - let armItem = armItems?.[0]; - if (armItem) { - const targetMesh = scene?.getObjectByProperty("uuid", armItem.modelUuid); + const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid); + if (targetMesh) { targetMesh.visible = activeModule !== "simulation" } - } + const targetBones = ikSolver?.mesh.skeleton.bones.find( (b: any) => b.name === targetBone ); if (isReset) { + logStatus(armBot.modelUuid, "Simulation Play Reset Successfully") removeCurrentAction(armBot.modelUuid) setArmBotActive(armBot.modelUuid, true) @@ -127,8 +126,10 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.") } if (isPlaying) { + //Moving armBot from initial point to rest position. if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") { + setArmBotActive(armBot.modelUuid, true) setArmBotState(armBot.modelUuid, "running") setCurrentPhase("init-to-rest"); @@ -144,12 +145,15 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction") const timeoutId = setTimeout(() => { - addCurrentAction(armBot.modelUuid, 'action-003'); + let actionId=armBot.point.actions[0].actionUuid + // addCurrentAction(armBot.modelUuid,actionId); + addCurrentAction(armBot.modelUuid, selectedAction?.actionId); }, 3000); return () => clearTimeout(timeoutId); } else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) { if (armBot.currentAction) { + setArmBotActive(armBot.modelUuid, true); setArmBotState(armBot.modelUuid, "running"); setCurrentPhase("rest-to-start"); @@ -253,7 +257,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } } const logStatus = (id: string, status: string) => { - // console.log('status: ', status); + // } return ( @@ -261,296 +265,8 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { - ) } export default RoboticArmInstance; - - - - - - - - - - - - -// import React, { useEffect, useRef, useState } from 'react'; -// import IKInstance from '../ikInstance/ikInstance'; -// import RoboticArmAnimator from '../animator/roboticArmAnimator'; -// import { usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; -// import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; -// import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_4.glb"; -// import { useThree } from "@react-three/fiber"; -// import { useFloorItems } from '../../../../../store/store'; -// import useModuleStore from '../../../../../store/useModuleStore'; -// import * as THREE from "three"; - -// function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { - -// const [currentPhase, setCurrentPhase] = useState("init"); -// const [path, setPath] = useState<[number, number, number][]>([]); -// const [ikSolver, setIkSolver] = useState(null); -// const { scene } = useThree(); -// const restPosition = new THREE.Vector3(0, 1, -1.6); -// const targetBone = "Target"; -// const groupRef = useRef(null); - -// // Zustand Stores -// const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore(); -// const { floorItems } = useFloorItems(); -// const { activeModule } = useModuleStore(); -// const { isPlaying } = usePlayButtonStore(); -// const { isReset, setReset } = useResetButtonStore(); - -// // Generate path according to constraints -// const generateArmPath = ( -// startPoint: [number, number, number], -// endPoint: [number, number, number], -// restPos: THREE.Vector3 = new THREE.Vector3(0, 1, -1.6) -// ): [number, number, number][] => { -// const path: [number, number, number][] = []; - -// const startVec = new THREE.Vector3(...startPoint); -// const endVec = new THREE.Vector3(...endPoint); - -// // Helper functions -// const addLinePoints = (from: THREE.Vector3, to: THREE.Vector3, segments: number) => { -// for (let i = 0; i <= segments; i++) { -// const t = i / segments; -// const point = new THREE.Vector3().lerpVectors(from, to, t); -// path.push([point.x, point.y, point.z]); -// } -// }; - -// const addCurveFromTo = (from: THREE.Vector3, to: THREE.Vector3, segments: number = 15) => { -// const mid = new THREE.Vector3() -// .addVectors(from, to) -// .multiplyScalar(0.5) -// .setY(Math.max(from.y, to.y) + 0.5); -// const curve = new THREE.CatmullRomCurve3([from, mid, to]); -// const points = curve.getPoints(segments); -// for (const p of points) { -// path.push([p.x, p.y, p.z]); -// } -// }; - -// // Step 1: Move vertically to align Y with start point -// const step1Start = startVec.clone().setY(restPos.y); -// const step1End = startVec.clone(); -// addLinePoints(step1Start, step1End, 10); - -// // Step 2: Move horizontally XZ to reach start point -// const step2Start = step1End.clone(); -// const step2End = startVec.clone(); // ✅ Fixed here -// addLinePoints(step2Start, step2End, 10); -// addLinePoints(step1End, step2End, 10); - -// // Optional Step 3: Move to rest/midpoint if needed -// const distanceToRest = startVec.distanceTo(restPos); -// if (distanceToRest > 1) { -// addCurveFromTo(step2End, restPos, 15); -// } - -// // Step 4: Move up/down to align Y with end point -// const step4Start = endVec.clone().setY(restPos.y); -// const step4End = endVec.clone(); -// addLinePoints(step4Start, step4End, 10); - -// // Step 5: Move horizontally XZ to end point -// addLinePoints(step4End, endVec.clone(), 10); - -// // Step 6: Return to rest or mid-position after dropping -// const finalRestHeight = restPos.clone().setY(endVec.y + 0.5); -// addCurveFromTo(endVec, finalRestHeight, 15); - -// return path; -// }; - -// useEffect(() => { -// let armItems = floorItems?.filter((val: any) => -// val.modeluuid === armBot.modelUuid -// ); -// let armItem = armItems?.[0]; -// if (armItem) { -// const targetMesh = scene?.getObjectByProperty("uuid", armItem.modeluuid); -// if (targetMesh) { -// targetMesh.visible = activeModule !== "simulation"; -// } -// } - -// const targetBones = ikSolver?.mesh.skeleton.bones.find( -// (b: any) => b.name === targetBone -// ); - -// if (isReset) { -// logStatus(armBot.modelUuid, "Simulation Play Reset Successfully"); -// removeCurrentAction(armBot.modelUuid); -// setArmBotActive(armBot.modelUuid, true); -// setArmBotState(armBot.modelUuid, "running"); -// setCurrentPhase("init-to-rest"); - -// if (targetBones) { -// const startPoint = new THREE.Vector3(targetBones.position.x, targetBones.position.y, targetBones.position.z); -// const curve = new THREE.CatmullRomCurve3([ -// startPoint, -// startPoint.clone().setY(restPosition.y + 0.5), -// restPosition.clone(), -// ]); -// setPath(curve.getPoints(30).map(p => [p.x, p.y, p.z])); -// } - -// setReset(false); -// logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position."); -// } - -// if (isPlaying) { -// // Phase 1: Initial -> Rest -// if (!armBot.isActive && armBot.state === "idle" && currentPhase === "init") { -// setArmBotActive(armBot.modelUuid, true); -// setArmBotState(armBot.modelUuid, "running"); -// setCurrentPhase("init-to-rest"); - -// if (targetBones) { -// const startPoint = new THREE.Vector3(targetBones.position.x, targetBones.position.y, targetBones.position.z); -// const curve = new THREE.CatmullRomCurve3([ -// startPoint, -// startPoint.clone().setY(restPosition.y + 0.5), -// restPosition.clone(), -// ]); -// setPath(curve.getPoints(30).map(p => [p.x, p.y, p.z])); -// } - -// logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position."); -// } - -// // Phase 2: Wait for trigger -// else if (!armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { -// logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction"); -// const timeoutId = setTimeout(() => { -// addCurrentAction(armBot.modelUuid, 'action-003'); -// }, 3000); -// return () => clearTimeout(timeoutId); -// } - -// // Phase 3: Rest -> Start Point -// else if (!armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) { -// setArmBotActive(armBot.modelUuid, true); -// setArmBotState(armBot.modelUuid, "running"); -// setCurrentPhase("rest-to-start"); - -// const startPoint = armBot.point.actions[0].process.startPoint; -// if (startPoint) { -// const start = new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]); -// const curve = new THREE.CatmullRomCurve3([restPosition, restPosition.clone().setY(start.y), start]); -// setPath(curve.getPoints(30).map(p => [p.x, p.y, p.z])); -// } - -// logStatus(armBot.modelUuid, "Moving armBot from rest point to start position."); -// } - -// // Phase 4: Start -> End (Pick & Move Object) -// else if (!armBot.isActive && armBot.state === "idle" && currentPhase === "picking" && armBot.currentAction) { -// setArmBotActive(armBot.modelUuid, true); -// setArmBotState(armBot.modelUuid, "running"); -// setCurrentPhase("start-to-end"); - -// const startPoint = armBot.point.actions[0].process.startPoint; -// const endPoint = armBot.point.actions[0].process.endPoint; - -// if (startPoint && endPoint) { -// const path = generateArmPath(startPoint, endPoint, restPosition); -// logStatus(armBot.modelUuid, "Waiting to pick."); -// setTimeout(()=>{ -// setPath(path); -// },3000) -// } - -// logStatus(armBot.modelUuid, "Moving armBot from start point to end position."); -// } - -// // Phase 5: End -> Rest (Drop & Return) -// else if (!armBot.isActive && armBot.state === "idle" && currentPhase === "dropping" && armBot.currentAction) { -// setArmBotActive(armBot.modelUuid, true); -// setArmBotState(armBot.modelUuid, "running"); -// setCurrentPhase("end-to-rest"); - -// const endPoint = armBot.point.actions[0].process.endPoint; -// if (endPoint) { -// const start = new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]); -// const curve = new THREE.CatmullRomCurve3([start, start.clone().setY(restPosition.y), restPosition]); -// logStatus(armBot.modelUuid, "Waiting to drop."); -// setTimeout(()=>{ -// setPath(curve.getPoints(30).map(p => [p.x, p.y, p.z])); -// },3000) - -// } - -// logStatus(armBot.modelUuid, "Moving armBot from end point to rest position."); -// } -// } else { -// logStatus(armBot.modelUuid, "Simulation Play Stopped"); -// setArmBotActive(armBot.modelUuid, false); -// setArmBotState(armBot.modelUuid, "idle"); -// setCurrentPhase("init"); -// setPath([]); -// removeCurrentAction(armBot.modelUuid); -// } - -// }, [currentPhase, armBot, isPlaying, ikSolver, isReset]); - -// const HandleCallback = () => { -// if (armBot.isActive && armBot.state === "running" && currentPhase === "init-to-rest") { -// logStatus(armBot.modelUuid, "Callback triggered: rest"); -// setArmBotActive(armBot.modelUuid, false); -// setArmBotState(armBot.modelUuid, "idle"); -// setCurrentPhase("rest"); -// setPath([]); -// } else if (armBot.isActive && armBot.state === "running" && currentPhase === "rest-to-start") { -// logStatus(armBot.modelUuid, "Callback triggered: pick."); -// setArmBotActive(armBot.modelUuid, false); -// setArmBotState(armBot.modelUuid, "idle"); -// setCurrentPhase("picking"); -// setPath([]); -// } else if (armBot.isActive && armBot.state === "running" && currentPhase === "start-to-end") { -// logStatus(armBot.modelUuid, "Callback triggered: drop."); -// setArmBotActive(armBot.modelUuid, false); -// setArmBotState(armBot.modelUuid, "idle"); -// setCurrentPhase("dropping"); -// setPath([]); -// } else if (armBot.isActive && armBot.state === "running" && currentPhase === "end-to-rest") { -// logStatus(armBot.modelUuid, "Callback triggered: rest, cycle completed."); -// setArmBotActive(armBot.modelUuid, false); -// setArmBotState(armBot.modelUuid, "idle"); -// setCurrentPhase("rest"); -// setPath([]); -// removeCurrentAction(armBot.modelUuid); -// } -// }; - -// const logStatus = (id: string, status: string) => { -// console.log( status); -// // console.log(`status [${id}]: ${status}`); -// }; - -// return ( -// <> -// -// -// -// ); -// } - -// export default RoboticArmInstance; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx index c40bc92..e692ca5 100644 --- a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx @@ -71,8 +71,7 @@ function IKInstance({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKIns setSelectedArm(OOI.Target_Bone); - // scene.add(helper) - + scene.add(helper) }, [gltf]); diff --git a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx index a313278..0acc1d9 100644 --- a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx +++ b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx @@ -1,4 +1,3 @@ -import React from 'react' import RoboticArmInstance from './armInstance/roboticArmInstance'; import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; @@ -10,7 +9,6 @@ function RoboticArmInstances() { {armBots?.map((robot: ArmBotStatus) => ( ))} - ) } diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx index 5a5bd38..e4f2355 100644 --- a/app/src/modules/simulation/roboticArm/roboticArm.tsx +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -2,24 +2,26 @@ import { useEffect } from "react"; import RoboticArmInstances from "./instances/roboticArmInstances"; import { useArmBotStore } from "../../../store/simulation/useArmBotStore"; import { useFloorItems } from "../../../store/store"; -import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; -import { useEventsStore } from "../../../store/simulation/useEventsStore"; +import { useSelectedEventData, useSelectedEventSphere, useSelectedProduct } from "../../../store/simulation/useSimulationStore"; +import { useProductStore } from "../../../store/simulation/useProductStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import ArmBotUI from "../ui/arm/armBotUI"; function RoboticArm() { const { armBots, addArmBot, removeArmBot } = useArmBotStore(); - const { addEvent } = useEventsStore(); + const { getProductById } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventData } = useSelectedEventData(); const { isPlaying } = usePlayButtonStore(); const { floorItems } = useFloorItems(); + const armBotStatusSample: RoboticArmEventSchema[] = [ { state: "idle", - modelUuid: "3abf5d46-b59e-4e6b-9c02-a4634b64b82d", + modelUuid: "8790b4d5-fb6e-49e0-8161-04945fe3fdc4", modelName: "ArmBot-X200", - position: [0.20849215906958463, 0, 0.32079278127773675], + position: [4.317833205016363, 0, -3.2829924989068715], rotation: [-1.3768690876192207e-15, 1.4883085074751308, 1.5407776675834467e-15], type: "roboticArm", speed: 1.5, @@ -156,24 +158,36 @@ function RoboticArm() { ]; useEffect(() => { + if (selectedProduct.productId) { + const product = getProductById(selectedProduct.productId); + if (product) { + product.eventDatas.forEach(events => { + if (events.type === 'roboticArm') { + removeArmBot(events.modelUuid); + addArmBot(selectedProduct.productId, events); + } + }); + } + } + }, [selectedProduct]); - removeArmBot(armBotStatusSample[0].modelUuid); - addArmBot('123', armBotStatusSample[0]); - // addArmBot('123', armBotStatusSample[1]); - // addCurrentAction('armbot-xyz-001', 'action-001'); - }, []); + // useEffect(()=>{ + // // removeArmBot("123", armBotStatusSample[0]); + // addArmBot("123", armBotStatusSample[0]); + // },[]) + + useEffect(() => { + + }, [armBots]) useEffect(() => { - console.log('isPlaying: ', isPlaying); - console.log('selectedEventData: ', selectedEventData); - console.log('selectedEventSphere: ', selectedEventSphere); }, [selectedEventData, selectedEventSphere, isPlaying]); return ( <> - {selectedEventSphere && selectedEventData?.data.type === "roboticArm" && !isPlaying && + {selectedEventSphere && selectedEventData?.data.type === "roboticArm" && < ArmBotUI /> } diff --git a/app/src/modules/simulation/ui/arm/PickDropPoints.tsx b/app/src/modules/simulation/ui/arm/PickDropPoints.tsx index 4544a46..831da7f 100644 --- a/app/src/modules/simulation/ui/arm/PickDropPoints.tsx +++ b/app/src/modules/simulation/ui/arm/PickDropPoints.tsx @@ -9,7 +9,7 @@ interface PickDropProps { actionType: "pick" | "drop"; actionUuid: string; gltfScene: THREE.Group; - selectedPoint: THREE.Mesh | null; + handlePointerDown: (e: ThreeEvent) => void; isSelected: boolean; } @@ -21,10 +21,11 @@ const PickDropPoints: React.FC = ({ actionType, actionUuid, gltfScene, - selectedPoint, handlePointerDown, isSelected, }) => { + + const groupRef = useRef(null); return ( @@ -36,15 +37,24 @@ const PickDropPoints: React.FC = ({ : new THREE.Vector3(0, 0, 0) } onPointerDown={(e) => { - e.stopPropagation(); // Important to prevent event bubbling + + e.stopPropagation(); // Prevent event bubbling if (!isSelected) return; handlePointerDown(e); }} userData={{ modelUuid, pointUuid, actionType, actionUuid }} > { + const cloned = gltfScene.clone(); + cloned.traverse((child: any) => { + if (child.isMesh) { + child.userData = { modelUuid, pointUuid, actionType, actionUuid }; + } + }); + return cloned; + })()} + position={[0, 0, 0]} scale={[0.5, 0.5, 0.5]} /> diff --git a/app/src/modules/simulation/ui/arm/armBotUI.tsx b/app/src/modules/simulation/ui/arm/armBotUI.tsx index 7e111e3..d616206 100644 --- a/app/src/modules/simulation/ui/arm/armBotUI.tsx +++ b/app/src/modules/simulation/ui/arm/armBotUI.tsx @@ -1,155 +1,189 @@ -import React from 'react' -import PickDropPoints from './PickDropPoints'; -import { useSelectedEventData, useSelectedEventSphere } from '../../../../store/simulation/useSimulationStore'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useSelectedEventData, useSelectedEventSphere, useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; -import useModuleStore from '../../../../store/useModuleStore'; import { useGLTF } from '@react-three/drei'; +import { useThree } from '@react-three/fiber'; +import { useProductStore } from '../../../../store/simulation/useProductStore'; +import PickDropPoints from './PickDropPoints'; +import useDraggableGLTF from './useDraggableGLTF'; +import * as THREE from 'three'; + import armPick from "../../../../assets/gltf-glb/arm_ui_pick.glb"; import armDrop from "../../../../assets/gltf-glb/arm_ui_drop.glb"; -import useDraggableGLTF from './useDraggableGLTF'; -import * as THREE from "three"; -import { useThree } from '@react-three/fiber'; +import useModuleStore from '../../../../store/useModuleStore'; + +type Positions = { + pick: [number, number, number]; + drop: [number, number, number]; + default: [number, number, number]; +}; + const ArmBotUI = () => { - console.log("called"); - const { selectedEventSphere } = useSelectedEventSphere(); - const { selectedEventData } = useSelectedEventData(); - const { armBots, updateArmBot } = useArmBotStore(); - const armUiPick = useGLTF(armPick) as any; - const { scene } = useThree(); - const armUiDrop = useGLTF(armDrop) as any; + const { getEventByModelUuid } = useProductStore(); + const { selectedEventData } = useSelectedEventData(); + const { selectedProduct } = useSelectedProduct(); + const { armBots, updateStartPoint, updateEndPoint } = useArmBotStore(); + const { scene } = useThree(); - const getDefaultPositions = (modelUuid: string) => { - const modelData = getModelByUuid(modelUuid); - if (modelData) { - - const baseX = modelData.position?.[0] || 0; - const baseY = 2.6; - const baseZ = modelData.position?.[2] || 0; - return { - pick: [baseX, baseY, baseZ + 0.4], - drop: [baseX, baseY, baseZ - 1.2], - default: [baseX, baseY, baseZ - 1.5], - }; + const armUiPick = useGLTF(armPick) as any; + const armUiDrop = useGLTF(armDrop) as any; + + const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 0, 0]); + const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 0, 0]); + const [selectedArmBotData, setSelectedArmBotData] = useState(null); + + // Fetch and setup selected ArmBot data + useEffect(() => { + if (selectedEventData?.data.type === "roboticArm") { + const selectedArmBot = getEventByModelUuid(selectedProduct.productId, selectedEventData.data.modelUuid); + + if (selectedArmBot) { + if (selectedArmBot.type === "roboticArm") { + + setSelectedArmBotData(selectedArmBot); + + const defaultPositions = getDefaultPositions(selectedArmBot.modelUuid); + + selectedArmBot?.point?.actions?.forEach((action: any) => { + if (action.actionType === "pickAndPlace") { + const startPoint = action.process.startPoint; + const pickPosition = (!startPoint || (Array.isArray(startPoint) && startPoint.every(v => v === 0))) + ? defaultPositions.pick + : startPoint; + const endPoint = action.process.endPoint; + const dropPosition = (!endPoint || (Array.isArray(endPoint) && endPoint.every(v => v === 0))) + ? defaultPositions.drop + : endPoint; + setStartPosition(pickPosition); + setEndPosition(dropPosition) + + } + }); } - return { - pick: [0.5, 1.5, 0], - drop: [-0.5, 1.5, 0], - default: [0, 1.5, 0], - }; - }; - const getModelByUuid = (modelUuid: string) => { - try { - const modelsJson = localStorage.getItem("FloorItems"); - if (modelsJson) { - const models = JSON.parse(modelsJson); - return models.find((m: any) => m.modeluuid === modelUuid); - } - const storeModels = (useModuleStore.getState() as any).models || []; - return storeModels.find((m: any) => m.modelUuid === modelUuid); - } catch (error) { } - return null; - }; - const updatePointToState = (obj: THREE.Object3D) => { - const { modelUuid, pointUuid, actionType, actionUuid } = obj.userData; - - const newPosition = new THREE.Vector3(); - obj.getWorldPosition(newPosition); - const worldPositionArray = newPosition.toArray(); - - const armBot = armBots.find((a) => a.modelUuid === modelUuid); - if (!armBot) return; - - const updatedActions = armBot.point.actions.map((action: any) => { - if (action.actionUuid === actionUuid) { - const updatedProcess = { ...action.process }; - - if (actionType === "pick") { - updatedProcess.startPoint = getLocalPosition(modelUuid, worldPositionArray); - } - if (actionType === "drop") { - updatedProcess.endPoint = getLocalPosition(modelUuid, worldPositionArray); - } - return { - ...action, - process: updatedProcess, - }; - } - return action; - }); - - updateArmBot(modelUuid, { - point: { - ...armBot.point, - actions: updatedActions, - }, - }); - }; - function getLocalPosition(parentUuid: string, worldPosArray: [number, number, number] | null): [number, number, number] | null { - if (worldPosArray) { - const worldPos = new THREE.Vector3(...worldPosArray); - const localPos = worldPos.clone(); - - const parentObject = scene.getObjectByProperty('uuid', parentUuid); - if (parentObject) { - parentObject.worldToLocal(localPos); - - return [localPos.x, localPos.y, localPos.z]; - } else { - - } - } - return null; - } - const { handlePointerDown } = useDraggableGLTF(updatePointToState); - - return ( - <> - {armBots.map((event: any) => { - const defaultPositions = getDefaultPositions(event.modelUuid); - const isSelected = - selectedEventSphere?.userData?.modelUuid === event.modelUuid; - - return event.point.actions.map((action: any) => { - if (action.actionType === "pickAndPlace") { - const pickPosition = action.process.startPoint || defaultPositions.pick; - const dropPosition = action.process.endPoint || defaultPositions.drop; - - return ( - - {/* Pick Point */} - - - {/* Drop Point */} - - - ); - } - return null; - }); - })} - - ); - + } + } + }, [selectedEventData, selectedProduct, getEventByModelUuid]); -} -export default ArmBotUI \ No newline at end of file + function getDefaultPositions(modelUuid: string): Positions { + const modelData = getEventByModelUuid(selectedProduct.productId, modelUuid); + + if (modelData?.type === "roboticArm") { + const baseX = modelData.point.position?.[0] || 0; + const baseY = modelData.point.position?.[1] || 0;; + const baseZ = modelData.point.position?.[2] || 0; + return { + pick: [baseX, baseY , baseZ + 0.5], + drop: [baseX, baseY , baseZ - 0.5], + default: [baseX, baseY, baseZ], + }; + } + + return { + pick: [0.5, 1.5, 0], + drop: [-0.5, 1.5, 0], + default: [0, 1.5, 0], + }; + } + + function getLocalPosition(parentUuid: string, worldPosArray: [number, number, number] | null): [number, number, number] | null { + if (worldPosArray) { + const worldPos = new THREE.Vector3(...worldPosArray); + const parentObject = scene.getObjectByProperty('uuid', parentUuid); + + if (parentObject) { + const localPos = worldPos.clone(); + parentObject.worldToLocal(localPos); + return [localPos.x, localPos.y, localPos.z]; + } + } + return null; + } + + const updatePointToState = (obj: THREE.Object3D) => { + const { modelUuid, actionType, actionUuid } = obj.userData; + const newPosition = new THREE.Vector3(); + obj.getWorldPosition(newPosition); + const worldPositionArray = newPosition.toArray() as [number, number, number]; + + if (selectedEventData?.data.type === "roboticArm") { + const selectedArmBot = getEventByModelUuid(selectedProduct.productId, selectedEventData.data.modelUuid); + + const armBot = selectedArmBot?.modelUuid === modelUuid ? selectedArmBot : null; + if (!armBot) return; + + if (armBot.type === "roboticArm") { + armBot?.point?.actions?.map((action: any) => { + if (action.actionUuid === actionUuid) { + const updatedProcess = { ...action.process }; + + if (actionType === "pick") { + updatedProcess.startPoint = getLocalPosition(modelUuid, worldPositionArray); + // updatedProcess.startPoint = worldPositionArray + setStartPosition(updatedProcess.startPoint) + + updateStartPoint(modelUuid, actionUuid, updatedProcess.startPoint); + } else if (actionType === "drop") { + updatedProcess.endPoint = getLocalPosition(modelUuid, worldPositionArray); + // updatedProcess.endPoint = worldPositionArray + setEndPosition(updatedProcess.endPoint) + updateEndPoint(modelUuid, actionUuid, updatedProcess.endPoint); + } + return { + ...action, + process: updatedProcess, + }; + } + return action; + }); + + } + } + } + + useEffect(() => { + + +}, [armBots]) + + const { handlePointerDown } = useDraggableGLTF(updatePointToState); + + if (!selectedArmBotData || !Array.isArray(selectedArmBotData.point?.actions)) { + return null; // avoid rendering if no data yet + } + + return ( + <> + {selectedArmBotData.point.actions.map((action: any) => ( + + + + + + + ))} + + ); +}; + +export default ArmBotUI; diff --git a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts index d73b4a9..01b7932 100644 --- a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts +++ b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts @@ -1,11 +1,11 @@ -import { useRef } from "react"; +import { useRef, useState } from "react"; import * as THREE from "three"; import { ThreeEvent, useThree } from "@react-three/fiber"; type OnUpdateCallback = (object: THREE.Object3D) => void; export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { - const { camera, gl, controls, scene } = useThree(); + const { camera, gl, controls } = useThree(); const activeObjRef = useRef(null); const planeRef = useRef( new THREE.Plane(new THREE.Vector3(0, 1, 0), 0) @@ -15,6 +15,7 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { const raycaster = new THREE.Raycaster(); const pointer = new THREE.Vector2(); + const [objectWorldPos, setObjectWorldPos] = useState(new THREE.Vector3()); const handlePointerDown = (e: ThreeEvent) => { e.stopPropagation(); @@ -33,10 +34,10 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { activeObjRef.current = obj; initialPositionRef.current.copy(obj.position); + console.log("obj.position: ", obj.position); // Get world position - const objectWorldPos = new THREE.Vector3(); - obj.getWorldPosition(objectWorldPos); + setObjectWorldPos(obj.getWorldPosition(objectWorldPos)); // Set plane at the object's Y level planeRef.current.set(new THREE.Vector3(0, 1, 0), -objectWorldPos.y); @@ -61,52 +62,56 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { const handlePointerMove = (e: PointerEvent) => { if (!activeObjRef.current) return; - + // Check if Shift key is pressed const isShiftKeyPressed = e.shiftKey; - + // Get the mouse position relative to the canvas const rect = gl.domElement.getBoundingClientRect(); pointer.x = ((e.clientX - rect.left) / rect.width) * 2 - 1; pointer.y = -((e.clientY - rect.top) / rect.height) * 2 + 1; - + // Update raycaster to point to the mouse position raycaster.setFromCamera(pointer, camera); - + // Create a vector to store intersection point const intersection = new THREE.Vector3(); - const intersects = raycaster.ray.intersectPlane(planeRef.current, intersection); + const intersects = raycaster.ray.intersectPlane( + planeRef.current, + intersection + ); if (!intersects) return; - + // Add offset for dragging intersection.add(offsetRef.current); - - + // Get the parent's world matrix if exists const parent = activeObjRef.current.parent; const targetPosition = new THREE.Vector3(); - + + // OnPointerDown + initialPositionRef.current.copy(objectWorldPos); + + // OnPointerMove if (isShiftKeyPressed) { - console.log('isShiftKeyPressed: ', isShiftKeyPressed); - // For Y-axis only movement, maintain original X and Z - console.log('initialPositionRef: ', initialPositionRef); - console.log('intersection.y: ', intersection); - targetPosition.set( - initialPositionRef.current.x, - intersection.y, - initialPositionRef.current.z - ); + const { x: initialX, y: initialY } = initialPositionRef.current; + const { x: objectX, z: objectZ } = objectWorldPos; + + const deltaX = intersection.x - initialX; + + targetPosition.set(objectX, initialY + deltaX, objectZ); } else { // For free movement targetPosition.copy(intersection); } - + // Convert world position to local if object is nested inside a parent if (parent) { parent.worldToLocal(targetPosition); } - + // Update object position + activeObjRef.current.position.copy(targetPosition); }; @@ -126,6 +131,3 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { return { handlePointerDown }; } - - - -- 2.40.1 From 44e3f5c207cf5090186bbb85575046dc606e3b90 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 2 May 2025 17:39:11 +0530 Subject: [PATCH 21/32] Refactor RealTimeVisualization component and handle widget drop functionality - Commented out the handleDrop function in RealTimeVisualization.tsx and moved its logic to a new utility function createHandleDrop for better separation of concerns. - Updated Project.tsx to utilize the new createHandleDrop function, improving readability and maintainability. - Enhanced styling for the scene container and real-time visualization components to improve layout and responsiveness. - Removed unnecessary styles and consolidated button and input styles for consistency. - Cleaned up unused imports and variables in various files to streamline the codebase. --- app/src/components/icons/SimulationIcons.tsx | 12 +- app/src/components/ui/Tools.tsx | 497 +++++++++--------- .../ui/simulation/simulationPlayer.tsx | 435 +++++++-------- .../visualization/RealTimeVisulization.tsx | 193 +++---- .../visualization/functions/handleUiDrop.ts | 122 +++++ app/src/pages/Project.tsx | 81 ++- .../visualization/useDroppedObjectsStore.ts | 1 - app/src/styles/base/global.scss | 32 +- app/src/styles/base/reset.scss | 7 - .../components/analysis/ROISummary.scss | 16 +- .../styles/components/analysis/analysis.scss | 466 ++++++++-------- app/src/styles/components/button.scss | 24 + app/src/styles/components/input.scss | 15 - app/src/styles/components/tools.scss | 2 +- app/src/styles/pages/realTimeViz.scss | 26 +- 15 files changed, 1031 insertions(+), 898 deletions(-) create mode 100644 app/src/modules/visualization/functions/handleUiDrop.ts diff --git a/app/src/components/icons/SimulationIcons.tsx b/app/src/components/icons/SimulationIcons.tsx index ef23c8f..1e89a51 100644 --- a/app/src/components/icons/SimulationIcons.tsx +++ b/app/src/components/icons/SimulationIcons.tsx @@ -125,21 +125,21 @@ export function ResetIcon() { > @@ -158,7 +158,7 @@ export function PlayStopIcon() { > @@ -177,7 +177,7 @@ export function ExitIcon() { > diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index 21ea6a7..14bc517 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -36,7 +36,6 @@ import { import useToggleStore from "../../store/useUIToggleStore"; import { use3DWidget, - useDroppedObjectsStore, useFloatingWidget, } from "../../store/visualization/useDroppedObjectsStore"; @@ -57,20 +56,18 @@ const Tools: React.FC = () => { const { widgets3D } = use3DWidget(); - const zones = useDroppedObjectsStore((state) => state.zones); - // wall options const { toggleView, setToggleView } = useToggleView(); const { setDeleteTool } = useDeleteTool(); const { setAddAction } = useAddAction(); const { setSelectedWallItem } = useSelectedWallItem(); - const { transformMode, setTransformMode } = useTransformMode(); - const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine(); - const { movePoint, setMovePoint } = useMovePoint(); - const { toolMode, setToolMode } = useToolMode(); + const { setTransformMode } = useTransformMode(); + const { setDeletePointOrLine } = useDeletePointOrLine(); + const { setMovePoint } = useMovePoint(); + const { setToolMode } = useToolMode(); const { activeTool, setActiveTool } = useActiveTool(); - const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); + const { setRefTextUpdate } = useRefTextUpdate(); // Reset activeTool whenever activeModule changes useEffect(() => { @@ -209,268 +206,266 @@ const Tools: React.FC = () => { return ( <> {!isPlaying ? ( - <> -
-
-
- {activeSubTool == "cursor" && ( -
{ - setActiveTool("cursor"); - }} - > - -
- )} - {activeSubTool == "free-hand" && ( -
{ - setActiveTool("free-hand"); - }} - > - -
- )} - {activeSubTool == "delete" && ( -
{ - setActiveTool("delete"); - }} - > - -
- )} - {activeModule !== "visualization" && ( -
{ - setOpenDrop(!openDrop); - }} - > - - {openDrop && ( -
-
{ - setOpenDrop(false); - setActiveTool("cursor"); - setActiveSubTool("cursor"); - }} - > -
- {activeSubTool === "cursor" && } -
- -
Cursor
-
-
{ - setOpenDrop(false); - setActiveTool("free-hand"); - setActiveSubTool("free-hand"); - }} - > -
- {activeSubTool === "free-hand" && } -
- -
Free Hand
-
-
{ - setOpenDrop(false); - setActiveTool("delete"); - setActiveSubTool("delete"); - }} - > -
- {activeSubTool === "delete" && } -
- -
Delete
-
-
- )} -
- )} -
-
- {!toggleThreeD && activeModule === "builder" && ( - <> -
-
-
{ - setActiveTool("draw-wall"); - }} - title="Wall" - > - -
-
{ - setActiveTool("draw-zone"); - }} - title="Zone" - > - -
-
{ - setActiveTool("draw-aisle"); - }} - title="Aisle" - > - -
-
{ - setActiveTool("draw-floor"); - }} - title="Floor" - > - -
-
- - )} - {activeModule === "builder" && ( - <> -
-
-
{ - setActiveTool("measure"); - }} - title="Measure" - > - -
-
- - )} - {activeModule === "simulation" && ( - <> -
-
-
{ - setActiveTool("pen"); - }} - > - -
-
- - )} - {activeModule === "visualization" && ( - <> -
-
-
{ - handleSaveTemplate({ - addTemplate, - floatingWidget, - widgets3D, - selectedZone, - templates, - visualizationSocket, - }); - }} - > - -
-
- - )} -
-
-
{ - setActiveTool("comment"); - }} - > - -
- {toggleThreeD && ( +
+
+
+ {activeSubTool == "cursor" && (
{ - setIsPlaying(!isPlaying); + setActiveTool("cursor"); }} > - + +
+ )} + {activeSubTool == "free-hand" && ( +
{ + setActiveTool("free-hand"); + }} + > + +
+ )} + {activeSubTool == "delete" && ( +
{ + setActiveTool("delete"); + }} + > + +
+ )} + {activeModule !== "visualization" && ( +
{ + setOpenDrop(!openDrop); + }} + > + + {openDrop && ( +
+
{ + setOpenDrop(false); + setActiveTool("cursor"); + setActiveSubTool("cursor"); + }} + > +
+ {activeSubTool === "cursor" && } +
+ +
Cursor
+
+
{ + setOpenDrop(false); + setActiveTool("free-hand"); + setActiveSubTool("free-hand"); + }} + > +
+ {activeSubTool === "free-hand" && } +
+ +
Free Hand
+
+
{ + setOpenDrop(false); + setActiveTool("delete"); + setActiveSubTool("delete"); + }} + > +
+ {activeSubTool === "delete" && } +
+ +
Delete
+
+
+ )}
)}
- {activeModule === "builder" && ( - <> -
+
+ {!toggleThreeD && activeModule === "builder" && ( + <> +
+
{ + setActiveTool("draw-wall"); + }} + title="Wall" > -
- 2d -
-
- 3d -
+
- +
{ + setActiveTool("draw-zone"); + }} + title="Zone" + > + +
+
{ + setActiveTool("draw-aisle"); + }} + title="Aisle" + > + +
+
{ + setActiveTool("draw-floor"); + }} + title="Floor" + > + +
+
+ + )} + {activeModule === "builder" && ( + <> +
+
+
{ + setActiveTool("measure"); + }} + title="Measure" + > + +
+
+ + )} + {activeModule === "simulation" && ( + <> +
+
+
{ + setActiveTool("pen"); + }} + > + +
+
+ + )} + {activeModule === "visualization" && ( + <> +
+
+
{ + handleSaveTemplate({ + addTemplate, + floatingWidget, + widgets3D, + selectedZone, + templates, + visualizationSocket, + }); + }} + > + +
+
+ + )} +
+
+
{ + setActiveTool("comment"); + }} + > + +
+ {toggleThreeD && ( +
{ + setIsPlaying(!isPlaying); + }} + > + +
)}
- + {activeModule === "builder" && ( + <> +
+
+
+ 2d +
+
+ 3d +
+
+ + )} +
) : ( <> {activeModule !== "simulation" && ( -
setIsPlaying(false)}> +
+ )} )} diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx index 901c537..e569250 100644 --- a/app/src/components/ui/simulation/simulationPlayer.tsx +++ b/app/src/components/ui/simulation/simulationPlayer.tsx @@ -19,6 +19,9 @@ import { } from "../../icons/ExportCommonIcons"; import { getAvatarColor } from "../../../modules/collaboration/functions/getAvatarColor"; import { useSubModuleStore } from "../../../store/useModuleStore"; +import ProductionCapacity from "../analysis/ProductionCapacity"; +import ThroughputSummary from "../analysis/ThroughputSummary"; +import ROISummary from "../analysis/ROISummary"; const SimulationPlayer: React.FC = () => { const MAX_SPEED = 8; // Maximum speed @@ -162,224 +165,236 @@ const SimulationPlayer: React.FC = () => { }; return ( -
-
-
- {subModule === "analysis" && ( -
- {/* hourlySimulation */} -
-
-
- -
-
Hourly Simulation
-
-
-
-
-
- {/* dailyProduction */} -
-
-
- -
-
Daily Production
-
-
-
-
-
- {/* monthlyROI */} -
-
-
- -
-
Monthly ROI
-
-
-
-
{" "} -
-
- )} - {subModule === "simulations" && ( -
- - {playSimulation - ? "Paused - system idle." - : "Running simulation..."} -
- )} -
- - - + <> +
+
+
{subModule === "analysis" && ( - - )} -
-
-
-
-
-
- -
-
-
23 April ,25
-
04:41 PM
-
-
-
-
- {intervals.map((label, index) => { - const segmentProgress = (index / totalSegments) * 100; - const isFilled = progress >= segmentProgress; - return ( - -
-
{label} mins
-
-
- {index < intervals.length - 1 && ( -
= ((index + 1) / totalSegments) * 100 - ? "filled" - : "" - }`} - >
- )} -
- ); - })} -
-
- -
-
-
00:10:20
-
-
- -
-
-
-
-
-
- -
- Speed -
-
-
0.5X
-
-
-
-
-
-
-
-
-
-
- - -
-
4x
-
-
-
- {subModule === "analysis" && ( -
-
00:00
-
24:00
-
-
- {process.map((item, index) => ( -
+
+ {/* hourlySimulation */} +
+
+
+ +
+
Hourly Simulation
+
+
- ))} +
+ {/* dailyProduction */} +
+
+
+ +
+
Daily Production
+
+
+
+
+
+ {/* monthlyROI */} +
+
+
+ +
+
Monthly ROI
+
+
+
+
{" "} +
+
+ )} + {subModule === "simulations" && ( +
+ + {playSimulation + ? "Paused - system idle." + : "Running simulation..."} +
+ )} +
+ + + + {subModule === "analysis" && ( + + )} +
+
+
+
+
+
+ +
+
+
23 April ,25
+
04:41 PM
+
+
+
+
+ {intervals.map((label, index) => { + const segmentProgress = (index / totalSegments) * 100; + const isFilled = progress >= segmentProgress; + return ( + +
+
{label} mins
+
+
+ {index < intervals.length - 1 && ( +
= ((index + 1) / totalSegments) * 100 + ? "filled" + : "" + }`} + >
+ )} +
+ ); + })} +
+
+ +
+
+
00:10:20
+
+
+ +
+
+
+
+
+
+ +
+ Speed +
+
+
0.5X
+
+
+
+
+
+
+
+
+
+
+ + +
+
4x
- )} + {subModule === "analysis" && ( +
+
00:00
+
24:00
+
+
+ {process.map((item, index) => ( +
+
+
+ ))} +
+
+
+ )} +
-
+
+
+ + +
+ +
+ ); }; diff --git a/app/src/modules/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx index ad24e58..dadaf9c 100644 --- a/app/src/modules/visualization/RealTimeVisulization.tsx +++ b/app/src/modules/visualization/RealTimeVisulization.tsx @@ -143,109 +143,109 @@ const RealTimeVisulization: React.FC = () => { }); }, [selectedZone]); - const handleDrop = async (event: React.DragEvent) => { - event.preventDefault(); - try { - const email = localStorage.getItem("email") ?? ""; - const organization = email?.split("@")[1]?.split(".")[0]; + // const handleDrop = async (event: React.DragEvent) => { + // event.preventDefault(); + // try { + // const email = localStorage.getItem("email") ?? ""; + // const organization = email?.split("@")[1]?.split(".")[0]; - const data = event.dataTransfer.getData("text/plain"); - if (widgetSubOption === "3D") return; - if (!data || selectedZone.zoneName === "") return; + // const data = event.dataTransfer.getData("text/plain"); + // if (widgetSubOption === "3D") return; + // if (!data || selectedZone.zoneName === "") return; - const droppedData = JSON.parse(data); - const canvasElement = document.getElementById("real-time-vis-canvas"); - if (!canvasElement) throw new Error("Canvas element not found"); + // const droppedData = JSON.parse(data); + // const canvasElement = document.getElementById("real-time-vis-canvas"); + // if (!canvasElement) throw new Error("Canvas element not found"); - const rect = canvasElement.getBoundingClientRect(); - const relativeX = event.clientX - rect.left; - const relativeY = event.clientY - rect.top; + // const rect = canvasElement.getBoundingClientRect(); + // const relativeX = event.clientX - rect.left; + // const relativeY = event.clientY - rect.top; - // Widget dimensions - const widgetWidth = droppedData.width ?? 125; - const widgetHeight = droppedData.height ?? 100; + // // Widget dimensions + // const widgetWidth = droppedData.width ?? 125; + // const widgetHeight = droppedData.height ?? 100; - // Center the widget at cursor - const centerOffsetX = widgetWidth / 2; - const centerOffsetY = widgetHeight / 2; + // // Center the widget at cursor + // const centerOffsetX = widgetWidth / 2; + // const centerOffsetY = widgetHeight / 2; - const adjustedX = relativeX - centerOffsetX; - const adjustedY = relativeY - centerOffsetY; + // const adjustedX = relativeX - centerOffsetX; + // const adjustedY = relativeY - centerOffsetY; - const finalPosition = determinePosition(rect, adjustedX, adjustedY); - const [activeProp1, activeProp2] = getActiveProperties(finalPosition); + // const finalPosition = determinePosition(rect, adjustedX, adjustedY); + // const [activeProp1, activeProp2] = getActiveProperties(finalPosition); - let finalY = 0; - let finalX = 0; + // let finalY = 0; + // let finalX = 0; - if (activeProp1 === "top") { - finalY = adjustedY; - } else { - finalY = rect.height - (adjustedY + widgetHeight); - } + // if (activeProp1 === "top") { + // finalY = adjustedY; + // } else { + // finalY = rect.height - (adjustedY + widgetHeight); + // } - if (activeProp2 === "left") { - finalX = adjustedX; - } else { - finalX = rect.width - (adjustedX + widgetWidth); - } + // if (activeProp2 === "left") { + // finalX = adjustedX; + // } else { + // finalX = rect.width - (adjustedX + widgetWidth); + // } - // Clamp to boundaries - finalX = Math.max(0, Math.min(rect.width - widgetWidth, finalX)); - finalY = Math.max(0, Math.min(rect.height - widgetHeight, finalY)); + // // Clamp to boundaries + // finalX = Math.max(0, Math.min(rect.width - widgetWidth, finalX)); + // finalY = Math.max(0, Math.min(rect.height - widgetHeight, finalY)); - const boundedPosition = { - ...finalPosition, - [activeProp1]: finalY, - [activeProp2]: finalX, - [activeProp1 === "top" ? "bottom" : "top"]: "auto", - [activeProp2 === "left" ? "right" : "left"]: "auto", - }; + // const boundedPosition = { + // ...finalPosition, + // [activeProp1]: finalY, + // [activeProp2]: finalX, + // [activeProp1 === "top" ? "bottom" : "top"]: "auto", + // [activeProp2 === "left" ? "right" : "left"]: "auto", + // }; - const newObject = { - ...droppedData, - id: generateUniqueId(), - position: boundedPosition, - }; + // const newObject = { + // ...droppedData, + // id: generateUniqueId(), + // position: boundedPosition, + // }; - const existingZone = - useDroppedObjectsStore.getState().zones[selectedZone.zoneName]; - if (!existingZone) { - useDroppedObjectsStore - .getState() - .setZone(selectedZone.zoneName, selectedZone.zoneId); - } + // const existingZone = + // useDroppedObjectsStore.getState().zones[selectedZone.zoneName]; + // if (!existingZone) { + // useDroppedObjectsStore + // .getState() + // .setZone(selectedZone.zoneName, selectedZone.zoneId); + // } - const addFloatingWidget = { - organization, - widget: newObject, - zoneId: selectedZone.zoneId, - }; + // const addFloatingWidget = { + // organization, + // widget: newObject, + // zoneId: selectedZone.zoneId, + // }; - if (visualizationSocket) { - visualizationSocket.emit("v2:viz-float:add", addFloatingWidget); - } + // if (visualizationSocket) { + // visualizationSocket.emit("v2:viz-float:add", addFloatingWidget); + // } - useDroppedObjectsStore - .getState() - .addObject(selectedZone.zoneName, newObject); + // useDroppedObjectsStore + // .getState() + // .addObject(selectedZone.zoneName, newObject); - const droppedObjectsStore = useDroppedObjectsStore.getState(); - const currentZone = droppedObjectsStore.zones[selectedZone.zoneName]; + // const droppedObjectsStore = useDroppedObjectsStore.getState(); + // const currentZone = droppedObjectsStore.zones[selectedZone.zoneName]; - if (currentZone && currentZone.zoneId === selectedZone.zoneId) { - console.log( - `Objects for Zone ${selectedZone.zoneId}:`, - currentZone.objects - ); - setFloatingWidget(currentZone.objects); - } else { - console.warn("Zone not found or zoneId mismatch"); - } - } catch (error) { - console.error("Error in handleDrop:", error); - } - }; + // if (currentZone && currentZone.zoneId === selectedZone.zoneId) { + // console.log( + // `Objects for Zone ${selectedZone.zoneId}:`, + // currentZone.objects + // ); + // setFloatingWidget(currentZone.objects); + // } else { + // console.warn("Zone not found or zoneId mismatch"); + // } + // } catch (error) { + // console.error("Error in handleDrop:", error); + // } + // }; useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -302,16 +302,7 @@ const RealTimeVisulization: React.FC = () => { } `} -
+
{openConfirmationPopup && ( @@ -322,20 +313,6 @@ const RealTimeVisulization: React.FC = () => { /> )} -
handleDrop(event)} - onDragOver={(event) => event.preventDefault()} - > - -
{activeModule === "visualization" && selectedZone.zoneName !== "" && ( )} diff --git a/app/src/modules/visualization/functions/handleUiDrop.ts b/app/src/modules/visualization/functions/handleUiDrop.ts new file mode 100644 index 0000000..a70742e --- /dev/null +++ b/app/src/modules/visualization/functions/handleUiDrop.ts @@ -0,0 +1,122 @@ +import { generateUniqueId } from "../../../functions/generateUniqueId"; +import { useDroppedObjectsStore } from "../../../store/visualization/useDroppedObjectsStore"; +import { determinePosition } from "./determinePosition"; +import { getActiveProperties } from "./getActiveProperties"; + +interface HandleDropProps { + widgetSubOption: any; + visualizationSocket: any; + selectedZone: any; + setFloatingWidget: (value: any) => void; + event: React.DragEvent +} + +export const createHandleDrop = ({ + widgetSubOption, + visualizationSocket, + selectedZone, + setFloatingWidget, + event, +}: HandleDropProps) => { + event.preventDefault(); + try { + const email = localStorage.getItem("email") ?? ""; + const organization = email?.split("@")[1]?.split(".")[0]; + + const data = event.dataTransfer.getData("text/plain"); + if (widgetSubOption === "3D") return; + if (!data || selectedZone.zoneName === "") return; + + const droppedData = JSON.parse(data); + const canvasElement = document.getElementById("real-time-vis-canvas"); + if (!canvasElement) throw new Error("Canvas element not found"); + + const rect = canvasElement.getBoundingClientRect(); + const relativeX = event.clientX - rect.left; + const relativeY = event.clientY - rect.top; + + // Widget dimensions + const widgetWidth = droppedData.width ?? 125; + const widgetHeight = droppedData.height ?? 100; + + // Center the widget at cursor + const centerOffsetX = widgetWidth / 2; + const centerOffsetY = widgetHeight / 2; + + const adjustedX = relativeX - centerOffsetX; + const adjustedY = relativeY - centerOffsetY; + + const finalPosition = determinePosition(rect, adjustedX, adjustedY); + const [activeProp1, activeProp2] = getActiveProperties(finalPosition); + + let finalY = 0; + let finalX = 0; + + if (activeProp1 === "top") { + finalY = adjustedY; + } else { + finalY = rect.height - (adjustedY + widgetHeight); + } + + if (activeProp2 === "left") { + finalX = adjustedX; + } else { + finalX = rect.width - (adjustedX + widgetWidth); + } + + // Clamp to boundaries + finalX = Math.max(0, Math.min(rect.width - widgetWidth, finalX)); + finalY = Math.max(0, Math.min(rect.height - widgetHeight, finalY)); + + const boundedPosition = { + ...finalPosition, + [activeProp1]: finalY, + [activeProp2]: finalX, + [activeProp1 === "top" ? "bottom" : "top"]: "auto", + [activeProp2 === "left" ? "right" : "left"]: "auto", + }; + + const newObject = { + ...droppedData, + id: generateUniqueId(), + position: boundedPosition, + }; + + const existingZone = + useDroppedObjectsStore.getState().zones[selectedZone.zoneName]; + if (!existingZone) { + useDroppedObjectsStore + .getState() + .setZone(selectedZone.zoneName, selectedZone.zoneId); + } + + const addFloatingWidget = { + organization, + widget: newObject, + zoneId: selectedZone.zoneId, + }; + + if (visualizationSocket) { + visualizationSocket.emit("v2:viz-float:add", addFloatingWidget); + } + + useDroppedObjectsStore + .getState() + .addObject(selectedZone.zoneName, newObject); + + const droppedObjectsStore = useDroppedObjectsStore.getState(); + const currentZone = droppedObjectsStore.zones[selectedZone.zoneName]; + + if (currentZone && currentZone.zoneId === selectedZone.zoneId) { + console.log( + `Objects for Zone ${selectedZone.zoneId}:`, + currentZone.objects + ); + setFloatingWidget(currentZone.objects); + } else { + console.warn("Zone not found or zoneId mismatch"); + } + } catch (error) { + console.error("Error in handleDrop:", error); + } +}; diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index bdefb8b..9bb7d0b 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -13,6 +13,7 @@ import { useWallItems, useZones, useLoadingProgress, + useWidgetSubOption, } from "../store/store"; import { useNavigate } from "react-router-dom"; import { usePlayButtonStore } from "../store/usePlayButtonStore"; @@ -22,9 +23,10 @@ import SimulationPlayer from "../components/ui/simulation/simulationPlayer"; import KeyPressListener from "../utils/shortcutkeys/handleShortcutKeys"; import { useSelectedUserStore } from "../store/useCollabStore"; import FollowPerson from "../components/templates/FollowPerson"; -import ProductionCapacity from "../components/ui/analysis/ProductionCapacity"; -import ThroughputSummary from "../components/ui/analysis/ThroughputSummary"; -import ROISummary from "../components/ui/analysis/ROISummary"; +import Scene from "../modules/scene/scene"; +import { createHandleDrop } from "../modules/visualization/functions/handleUiDrop"; +import { useSelectedZoneStore } from "../store/visualization/useZoneStore"; +import { useFloatingWidget } from "../store/visualization/useDroppedObjectsStore"; const Project: React.FC = () => { let navigate = useNavigate(); @@ -53,38 +55,67 @@ const Project: React.FC = () => { } else { navigate("/"); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const { isPlaying } = usePlayButtonStore(); + // global store const { toggleThreeD } = useThreeDStore(); + + // simulation store + const { isPlaying } = usePlayButtonStore(); + + // collaboration store const { selectedUser } = useSelectedUserStore(); + // real-time visualization store + const { widgetSubOption } = useWidgetSubOption(); + const { visualizationSocket } = useSocketStore(); + const { selectedZone } = useSelectedZoneStore(); + const { setFloatingWidget } = useFloatingWidget(); + return (
- {/*
-
- - -
- -
*/} - - {loadingProgress > 0 && } - {!isPlaying && ( + {!selectedUser && ( <> - {toggleThreeD && } - - + + {loadingProgress > 0 && } + {!isPlaying && ( + <> + {toggleThreeD && } + + + + )} + + {activeModule === "market" && } + {activeModule !== "market" && } + {isPlaying && activeModule === "simulation" && } )} - {/* - - */} - {activeModule === "market" && } - - {activeModule !== "market" && } - {isPlaying && activeModule === "simulation" && } - {/* {} */} +
+ createHandleDrop({ + widgetSubOption, + visualizationSocket, + selectedZone, + setFloatingWidget, + event, + }) + } + onDragOver={(event) => event.preventDefault()} + > + +
{selectedUser && }
); diff --git a/app/src/store/visualization/useDroppedObjectsStore.ts b/app/src/store/visualization/useDroppedObjectsStore.ts index 5c4527b..bbe4cde 100644 --- a/app/src/store/visualization/useDroppedObjectsStore.ts +++ b/app/src/store/visualization/useDroppedObjectsStore.ts @@ -1,5 +1,4 @@ import { create } from "zustand"; -import { addingFloatingWidgets } from "../../services/visulization/zone/addFloatingWidgets"; import { useSocketStore } from "../store"; import useChartStore from "./useChartStore"; diff --git a/app/src/styles/base/global.scss b/app/src/styles/base/global.scss index d90e6fb..f86ba0f 100644 --- a/app/src/styles/base/global.scss +++ b/app/src/styles/base/global.scss @@ -1,11 +1,29 @@ @use "../abstracts/variables" as *; @use "../abstracts/mixins" as *; -section, .section{ - padding: 4px; - outline: 1px solid var(--border-color); - outline-offset: -1px; - border-radius: #{$border-radius-large}; - background: var(--background-color); - margin: 4px 0; +section, +.section { + padding: 4px; + outline: 1px solid var(--border-color); + outline-offset: -1px; + border-radius: #{$border-radius-large}; + background: var(--background-color); + margin: 4px 0; +} + +.scene-container { + width: calc(100% - (320px + 270px + 90px)); + height: calc(100% - (250px)); + position: absolute; + top: 50%; + left: calc(270px + 45px); + overflow: hidden; + z-index: 1; + transform: translate(0, -50%); + transition: all 0.2s; + box-shadow: $box-shadow-medium; + canvas { + outline: none; + border: none; + } } diff --git a/app/src/styles/base/reset.scss b/app/src/styles/base/reset.scss index ab77f9a..82d286e 100644 --- a/app/src/styles/base/reset.scss +++ b/app/src/styles/base/reset.scss @@ -12,10 +12,3 @@ input[type="password"]::-webkit-clear-button, /* For Chrome/Safari clear button input[type="password"]::-webkit-inner-spin-button { /* Just in case */ display: none; } - -button{ - border: none; - outline: none; - background: none; - cursor: pointer; -} \ No newline at end of file diff --git a/app/src/styles/components/analysis/ROISummary.scss b/app/src/styles/components/analysis/ROISummary.scss index 96b4a5d..5e2f78a 100644 --- a/app/src/styles/components/analysis/ROISummary.scss +++ b/app/src/styles/components/analysis/ROISummary.scss @@ -1,3 +1,6 @@ +@use "../../abstracts/variables" as *; +@use "../../abstracts/mixins" as *; + .roiSummary-container { .roiSummary-wrapper { background-color: var(--background-color); @@ -64,7 +67,7 @@ width: 100%; border-radius: 6px; border: 1px solid #00FF56; - background: #436D51; + background: #17eb5d65; display: flex; flex-direction: column; padding: 4px 6px; @@ -223,12 +226,11 @@ background: none; .btn { - background-color: var(--accent-color); - color: var(--background-color); - padding: 4px 6px; - border-radius: 5px; + color: var(--text-button-color); + background: var(--background-color-button); + padding: 4px 12px; + border-radius: #{$border-radius-large}; display: inline-block; - font-size: 14px; text-align: center; } } @@ -244,7 +246,6 @@ height: 250px; border-radius: 50%; position: relative; - transition: background 0.5s ease; } .progress-cover { position: absolute; @@ -252,7 +253,6 @@ height: 75%; top: 12.5%; left: 12.5%; - background: #000000cc; border-radius: 50%; } } diff --git a/app/src/styles/components/analysis/analysis.scss b/app/src/styles/components/analysis/analysis.scss index 030a79f..00a9c32 100644 --- a/app/src/styles/components/analysis/analysis.scss +++ b/app/src/styles/components/analysis/analysis.scss @@ -1,279 +1,269 @@ .analysis { - position: fixed; - top: 0; - left: 0; + position: fixed; + top: 0; + left: 0; + display: flex; + justify-content: space-between; + align-items: start; + width: 100%; + height: 100vh; + pointer-events: none; + padding: 10px; + z-index: 2; + + .analysis-wrapper { display: flex; - justify-content: space-between; - align-items: start; - width: 100%; - height: 100vh; - // pointer-events: none; - z-index: 10000; - - .analysis-wrapper { - display: flex; - flex-direction: column; - gap: 12px; - } -} - -.analysis-card { + flex-direction: column; + gap: 12px; + } + .analysis-card { min-width: 333px; background: var(--background-color); border-radius: 20px; - padding: 8px; + backdrop-filter: blur(10px); + outline: 1px solid var(--border-color); + outline-offset: -1px; + pointer-events: all; .analysis-card-wrapper { - width: 100%; - background: var(--background-color); - border-radius: 14px; - padding: 16px; + width: 100%; + background: var(--background-color); + border-radius: 14px; + padding: 16px; + display: flex; + flex-direction: column; + gap: 14px; + .card-header { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + + .main-header { + line-height: 20px; + font-size: var(--font-size-regular); + } + } + + .process-container { display: flex; flex-direction: column; - gap: 14px; - .card-header { + .throughput-value { + font-size: 1rem; + + .value { + font-weight: bold; + font-size: 1.5rem; + } + } + + .progress-bar-wrapper { + display: flex; + gap: 8px; + margin-top: 6px; + } + + .progress-bar { + position: relative; + width: 100%; + height: 4px; + border-radius: 13px; + overflow: hidden; + background: #fbebd7; + + .bar-fill { + position: absolute; + height: 100%; + top: 0; + left: 0; + background: #fc9d2f; + border-radius: 13px; + } + + .bar-fill.full { width: 100%; - display: flex; - justify-content: space-between; - align-items: center; + } - .main-header { - line-height: 20px; - font-size: var(--font-size-regular); - } + .bar-fill.partial { + width: 0; // inline style will override this + } } + } - .process-container { - display: flex; - flex-direction: column; + .metrics-section { + padding-top: 16px; + border-top: 1px solid var(--background-color-gray); - .throughput-value { - font-size: 1rem; + .metric { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 14px; + margin-bottom: 8px; - .value { - font-weight: bold; - font-size: 1.5rem; - } - } + .label { + color: var(--text-color); + } - .progress-bar-wrapper { - display: flex; - gap: 8px; - margin-top: 6px; - } - - .progress-bar { - position: relative; - // width: 36px; - width: 100%; - height: 4px; - border-radius: 13px; - overflow: hidden; - background: #FBEBD7; - - .bar-fill { - position: absolute; - height: 100%; - top: 0; - left: 0; - background: #FC9D2F; - border-radius: 13px; - } - - .bar-fill.full { - width: 100%; - } - - .bar-fill.partial { - width: 0; // inline style will override this - } - } - } - - .metrics-section { - padding-top: 16px; - border-top: 1px solid var(--background-color-gray); - - .metric { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 14px; - margin-bottom: 8px; - - .label { - color: var(--text-color); - } - - .value { - font-weight: bold; - } - } + .value { + font-weight: bold; + } } + } } -} - - -.throughoutSummary { .throughoutSummary-wrapper { - .process-container { + .process-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + gap: 16px; + width: 100%; + + .throughput-value { + font-size: var(--font-size-small); + flex: 1; + display: flex; + flex-direction: column; + + .value { + color: var(--accent-color); + } + + /* Let the text take available space */ + } + + .lineChart { + max-width: 200px; + height: 100px; + position: relative; + + .assetUsage { + text-align: right; + position: absolute; + right: 0; + top: 0; + } + + canvas { + background: transparent; + } + } + } + + .footer { + display: flex; + gap: 16px; // Space between cards + margin-top: 24px; + + .footer-card { + width: 100%; + background: var(--background-color-gray); + border-radius: 6px; + padding: 8px; + display: flex; + flex-direction: column; + gap: 6px; + + &:first-child { + width: 85%; + } + + .header { + font-size: var(--font-size-regular); + } + + .value-container { display: flex; flex-direction: row; align-items: center; + justify-content: end; + gap: 6px; + } + } + + .shiftUtilization { + .value-container { + flex-direction: column; + align-items: flex-start; justify-content: flex-start; - gap: 16px; - width: 100%; - .throughput-value { - font-size: var(--font-size-small); - flex: 1; - display: flex; - flex-direction: column; - - .value { - color: var(--accent-color); - } - - /* Let the text take available space */ + .value { + font-size: var(--font-size-xlarge); } - .lineChart { - max-width: 200px; - height: 100px; - position: relative; + .progress-wrapper { + width: 100%; + display: flex; + gap: 6px; - .assetUsage { - text-align: right; - position: absolute; - right: 0; - top: 0; - } - - canvas { - background: transparent; - } - } - } - - .footer { - display: flex; - gap: 16px; // Space between cards - margin-top: 24px; - - .footer-card { - width: 100%; - background: var(--background-color-gray); + .progress { border-radius: 6px; - padding: 8px; + height: 5px; + + &:nth-child(1) { + background: #f3c64d; + } + + &:nth-child(2) { + background: #67b3f4; + } + + &:nth-child(3) { + background: #7981f5; + } + } + } + + .progress-indicator { + display: flex; + justify-content: space-between; + width: 100%; + gap: 6px; + + .shift-wrapper { display: flex; - flex-direction: column; - gap: 6px; + align-items: center; + gap: 5px; - &:first-child { - width: 85%; + /* Align items vertically */ + &:nth-child(1) { + .indicator { + background: #f3c64d; + } } - .header { - font-size: var(--font-size-regular); + &:nth-child(2) { + .indicator { + background: #67b3f4; + } } - .value-container { - display: flex; - flex-direction: row; - align-items: center; - justify-content: end; - gap: 6px; + &:nth-child(3) { + .indicator { + background: #7981f5; + } } + + label { + font-size: var(--font-size-small); + position: relative; + } + + .indicator { + display: inline-block; + width: 5px; + height: 5px; + border-radius: 50%; + } + } } - - .shiftUtilization { - .value-container { - flex-direction: column; - align-items: flex-start; - justify-content: flex-start; - - .value { - font-size: var(--font-size-xlarge); - } - - .progress-wrapper { - width: 100%; - display: flex; - gap: 6px; - - .progress { - border-radius: 6px; - height: 5px; - - &:nth-child(1) { - background: #F3C64D; - } - - &:nth-child(2) { - background: #67B3F4; - } - - &:nth-child(3) { - background: #7981F5; - } - } - } - - .progress-indicator { - display: flex; - justify-content: space-between; - width: 100%; - gap: 6px; - - .shift-wrapper { - display: flex; - align-items: center; - gap: 5px; - - /* Align items vertically */ - &:nth-child(1) { - .indicator { - - background: #F3C64D; - } - } - - &:nth-child(2) { - .indicator { - - background: #67B3F4; - } - } - - &:nth-child(3) { - .indicator { - - background: #7981F5; - } - } - - label { - font-size: var(--font-size-small); - position: relative; - } - - .indicator { - display: inline-block; - width: 5px; - height: 5px; - border-radius: 50%; - - } - } - } - } - } - + } } - - + } } -} \ No newline at end of file + } +} diff --git a/app/src/styles/components/button.scss b/app/src/styles/components/button.scss index e69de29..dad9120 100644 --- a/app/src/styles/components/button.scss +++ b/app/src/styles/components/button.scss @@ -0,0 +1,24 @@ +@use "../abstracts/variables" as *; +@use "../abstracts/mixins" as *; + +.labeled-button-container { + @include flex-space-between; + padding: 6px 12px; + + button { + padding: 2px 32px; + border: none; + border-radius: #{$border-radius-large}; + color: var(--text-button-color); + background: var(--background-color-button); + transition: all 0.2s; + cursor: pointer; + } +} + +button { + border: none; + outline: none; + background: none; + cursor: pointer; +} diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index e3b9585..d4c6544 100644 --- a/app/src/styles/components/input.scss +++ b/app/src/styles/components/input.scss @@ -639,21 +639,6 @@ input[type="number"] { } } -.labeled-button-container { - @include flex-space-between; - padding: 6px 12px; - - button { - padding: 2px 32px; - border: none; - border-radius: #{$border-radius-large}; - color: var(--text-button-color); - background: var(--background-color-button); - transition: all 0.2s; - cursor: pointer; - } -} - .value-field-container { margin-bottom: 6px; padding: 6px 12px; diff --git a/app/src/styles/components/tools.scss b/app/src/styles/components/tools.scss index 0a23871..29d37b4 100644 --- a/app/src/styles/components/tools.scss +++ b/app/src/styles/components/tools.scss @@ -15,7 +15,7 @@ transition: width 0.2s; background: var(--background-color); backdrop-filter: blur(8px); - z-index: #{$z-index-default}; + z-index: 2; outline: 1px solid var(--border-color); outline-offset: -1px; diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index fa1b86c..319a75c 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -3,8 +3,6 @@ // Main Container .realTime-viz { - background: #131313; - box-shadow: $box-shadow-medium; width: calc(100% - (320px + 270px + 90px)); height: calc(100% - (250px)); position: absolute; @@ -12,8 +10,8 @@ left: calc(270px + 45px); transform: translate(0, -50%); border-radius: #{$border-radius-medium}; - transition: all 0.2s; - z-index: #{$z-index-default}; + z-index: 2; + pointer-events: none; .realTime-viz-wrapper { width: 100%; @@ -39,10 +37,6 @@ z-index: 1; } - .scene-container { - overflow: hidden; - } - .icon { display: flex; align-items: center; @@ -74,6 +68,8 @@ z-index: 3; transform: translate(-50%, -10%); transition: transform 0.5s linear; + pointer-events: all; + &::-webkit-scrollbar { display: none; } @@ -367,6 +363,7 @@ border-radius: 2px; transition: transform 0.3s ease; box-shadow: #{$box-shadow-medium}; + pointer-events: all; svg { stroke: var(--icon-default-color) !important; @@ -428,8 +425,6 @@ stroke: #f65648; stroke-width: 1.3; } - - } } @@ -778,17 +773,10 @@ } } - - - .panel-content { background: var(--background-color); - } - - - /* RIGHT */ .panel-content.right-opening { animation: rightExpand 0.5s ease-in-out forwards; @@ -913,9 +901,6 @@ } } - - - // Add button .extra-Bs-addopening { @@ -926,7 +911,6 @@ animation: slideUp 0.3s ease forwards; } - @keyframes slideDown { from { opacity: 0; -- 2.40.1 From 52c6ab8a658006b796b73aa1ffaa7d4953360059 Mon Sep 17 00:00:00 2001 From: Nalvazhuthi Date: Fri, 2 May 2025 18:06:29 +0530 Subject: [PATCH 22/32] added echo logs list --- app/src/pages/Project.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index b4422cd..83e4497 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -34,7 +34,7 @@ import LogList from "../components/ui/log/LogList"; const Project: React.FC = () => { let navigate = useNavigate(); - const logger = useLogger(); + const echo = useLogger(); const { activeModule, setActiveModule } = useModuleStore(); const { loadingProgress } = useLoadingProgress(); @@ -58,7 +58,7 @@ const Project: React.FC = () => { setOrganization(Organization); setUserName(name); } - logger.info("Log in success full"); + echo.info("Log in success full"); } else { navigate("/"); } -- 2.40.1 From 6a1bf7f769cc22a1c38873699d36ca9b6c7339a5 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 2 May 2025 18:16:22 +0530 Subject: [PATCH 23/32] refactor: Update ROISummary component to use ArrowIcon for expand functionality; enhance styles in ROISummary, global, and analysis components for improved layout and responsiveness --- app/src/components/ui/analysis/ROISummary.tsx | 3 +- app/src/styles/base/base.scss | 6 +- app/src/styles/base/global.scss | 1 + .../components/analysis/ROISummary.scss | 561 +++++++++--------- .../styles/components/analysis/analysis.scss | 7 +- 5 files changed, 277 insertions(+), 301 deletions(-) diff --git a/app/src/components/ui/analysis/ROISummary.tsx b/app/src/components/ui/analysis/ROISummary.tsx index 11101fd..63ae707 100644 --- a/app/src/components/ui/analysis/ROISummary.tsx +++ b/app/src/components/ui/analysis/ROISummary.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import { ROISummaryIcon } from "../../icons/analysis"; import SemiCircleProgress from "./SemiCircleProgress"; +import { ArrowIcon } from "../../icons/ExportCommonIcons"; const ROISummary = ({ roiSummaryData = { @@ -121,7 +122,7 @@ const ROISummary = ({
- {isTableOpen ? "⌵" : "⌵"} +
Date: Fri, 2 May 2025 18:31:00 +0530 Subject: [PATCH 24/32] refactor: Clean up LogList component by removing unused TickIcon; enhance footer and logs styles for better layout and readability --- app/src/components/ui/log/LogList.tsx | 3 - app/src/styles/abstracts/variables.scss | 27 +++- app/src/styles/components/footer/footer.scss | 108 +++++++------- app/src/styles/components/logs/logs.scss | 143 +++++++++---------- 4 files changed, 155 insertions(+), 126 deletions(-) diff --git a/app/src/components/ui/log/LogList.tsx b/app/src/components/ui/log/LogList.tsx index 1b39e2f..fe25aee 100644 --- a/app/src/components/ui/log/LogList.tsx +++ b/app/src/components/ui/log/LogList.tsx @@ -69,9 +69,6 @@ const LogList: React.FC = () => {
{filteredLogs.map((log) => (
-
- -
{getLogIcon(log.type)}
[{formatTimestamp(log.timestamp)}] [{log.type.toUpperCase()}]{" "} diff --git a/app/src/styles/abstracts/variables.scss b/app/src/styles/abstracts/variables.scss index f4a6495..29976dc 100644 --- a/app/src/styles/abstracts/variables.scss +++ b/app/src/styles/abstracts/variables.scss @@ -22,7 +22,11 @@ $text-button-color-dark: #f3f3fd; // background colors // ---------- light mode ---------- $background-color: linear-gradient(-45deg, #fcfdfd71 0%, #fcfdfd79 100%); -$background-color-solid-gradient: linear-gradient(-45deg, #fcfdfd 0%, #fcfdfd 100%); +$background-color-solid-gradient: linear-gradient( + -45deg, + #fcfdfd 0%, + #fcfdfd 100% +); $background-color-solid: #fcfdfd; $background-color-secondary: #fcfdfd4d; $background-color-accent: #6f42c1; @@ -45,7 +49,11 @@ $background-radial-gray-gradient: radial-gradient( // ---------- dark mode ---------- $background-color-dark: linear-gradient(-45deg, #333333b3 0%, #2d2437b3 100%); -$background-color-solid-gradient-dark: linear-gradient(-45deg, #333333 0%, #2d2437 100%); +$background-color-solid-gradient-dark: linear-gradient( + -45deg, + #333333 0%, + #2d2437 100% +); $background-color-solid-dark: #19191d; $background-color-secondary-dark: #19191d99; $background-color-accent-dark: #6f42c1; @@ -104,6 +112,21 @@ $color3: #b186ff; $color4: #8752e8; $color5: #c7a8ff; +// log indication colors +// ------------ text ------------- +$log-default-text-color: #6f42c1; +$log-info-text-color: #488ef6; +$log-warn-text-color: #f3a50c; +$log-error-text-color: #f65648; +$log-success-text-color: #43c06d; + +// ------------ background ------------- +$log-default-backgroung-color: #6e42c133; +$log-info-background-color: #488ef633; +$log-warn-background-color: #f3a50c33; +$log-error-background-color: #f6564833; +$log-success-background-color: #43c06d33; + // old variables $accent-color: #6f42c1; $accent-color-dark: #c4abf1; diff --git a/app/src/styles/components/footer/footer.scss b/app/src/styles/components/footer/footer.scss index 0fed13b..d3a3d92 100644 --- a/app/src/styles/components/footer/footer.scss +++ b/app/src/styles/components/footer/footer.scss @@ -1,57 +1,67 @@ +@use "../../abstracts/variables" as *; +@use "../../abstracts/mixins" as *; + .footer-wrapper { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - z-index: 1; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + z-index: 1; + display: flex; + justify-content: space-between; + padding: 2px 12px; + + .selection-wrapper { display: flex; - justify-content: space-between; - padding: 12px 24px; + gap: 6px; - .selection-wrapper { - display: flex; - gap: 6px; + .selector-wrapper { + display: flex; + gap: 6px; + align-items: center; + background: var(--background-color); + padding: 3px 6px; + border-radius: 12px; + color: var(--text-color); - .selector-wrapper { - display: flex; - gap: 6px; - align-items: center; - background: var(--background-color); - padding: 3px 6px; - border-radius: 12px; - color: var(--text-color); + .selector { + color: var(--text-color); + } + } + } - .selector { - color: var(--text-color); - } - } + .logs-wrapper { + display: flex; + gap: 6px; + + .logs-detail, + .version { + border-radius: 12px; + background: var(--background-color); + padding: 3px 6px; + color: var(--text-color); + display: flex; + align-items: center; + gap: 6px; } - .logs-wrapper { - display: flex; - gap: 6px; - - .logs-detail, - .version { - - border-radius: 12px; - background: var(--background-color); - padding: 3px 6px; - color: var(--text-color); - display: flex; - align-items: center; - gap: 6px; - } - - .logs-detail { - // background-color: #fff; - cursor: pointer; - } - - .version { - display: flex; - gap: 6px; - - } + .logs-detail { + padding: 2px 12px; + cursor: pointer; + .log-icon { + @include flex-center; + } + .log-message { + max-width: 40vw; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } } -} \ No newline at end of file + + .version { + display: flex; + gap: 6px; + } + } +} diff --git a/app/src/styles/components/logs/logs.scss b/app/src/styles/components/logs/logs.scss index ba0f43d..d2535aa 100644 --- a/app/src/styles/components/logs/logs.scss +++ b/app/src/styles/components/logs/logs.scss @@ -1,77 +1,76 @@ .log-list-container { - width: 100vw; - height: 100vh; - // background: var(--background-color-secondary); - // backdrop-filter: blur(2px); + width: 100vw; + height: 100vh; + // background: var(--background-color-secondary); + // backdrop-filter: blur(2px); - .log-list-wrapper { - height: 50%; - min-width: 50%; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 5; - background: var(--background-color); - padding: 14px 12px; - border-radius: 15px; + .log-list-wrapper { + height: 50%; + min-width: 50%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 5; + background: var(--background-color); + padding: 14px 12px; + border-radius: 15px; + display: flex; + flex-direction: column; + gap: 12px; + backdrop-filter: blur(50px); + + .log-header { + display: flex; + justify-content: space-between; + + .log-header-wrapper { display: flex; - flex-direction: column; - gap: 12px; - backdrop-filter: blur(50px); + align-items: center; + gap: 6px; + } - .log-header { - display: flex; - justify-content: space-between; - - .log-header-wrapper { - display: flex; - align-items: center; - gap: 6px; - } - - .close { - // transform: scale(1.5); - cursor: pointer; - } - } - - .log-nav-wrapper { - display: flex; - gap: 6px; - - .log-nav { - padding: 8px 16px; - border-radius: 19px; - } - - .log-nav.active { - background-color: var(--accent-color); - color: var(--text-button-color); - } - } - - .log-entry-wrapper { - height: 100%; - display: flex; - flex-direction: column; - gap: 4px; - background: var(--background-color); - padding: 18px 10px; - border-radius: 16px; - - .log-entry { - padding: 4px; - border-radius: 4px; - font-size: var(--font-size-small); - display: flex; - align-items: center; - gap: 6px; - - &:nth-child(odd) { - background: var(--background-color); - } - } - } + .close { + cursor: pointer; + } } -} \ No newline at end of file + + .log-nav-wrapper { + display: flex; + gap: 6px; + + .log-nav { + padding: 8px 16px; + border-radius: 19px; + } + + .log-nav.active { + background-color: var(--background-color-accent); + color: var(--text-button-color); + } + } + + .log-entry-wrapper { + height: 100%; + display: flex; + flex-direction: column; + gap: 4px; + background: var(--background-color); + padding: 18px 10px; + border-radius: 16px; + + .log-entry { + padding: 4px; + border-radius: 4px; + font-size: var(--font-size-small); + display: flex; + align-items: center; + gap: 6px; + + &:nth-child(odd) { + background: var(--background-color); + } + } + } + } +} -- 2.40.1 From 80e7bf4bf9c701ec83693f9de127b60674b16cfb Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Fri, 2 May 2025 18:33:27 +0530 Subject: [PATCH 25/32] armbot position updated --- .../builder/IntialLoad/loadInitialFloorItems.ts | 4 ++-- .../builder/geomentries/assets/assetManager.ts | 3 ++- .../controls/selectionControls/moveControls.tsx | 2 ++ .../controls/selectionControls/rotateControls.tsx | 2 ++ .../instances/armInstance/roboticArmInstance.tsx | 4 ++-- .../modules/simulation/roboticArm/roboticArm.tsx | 13 ++++--------- app/src/modules/simulation/simulation.tsx | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index ee5c283..9f71945 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -282,8 +282,8 @@ function processLoadedModel( actionName: "Action 1", actionType: "pickAndPlace", process: { - startPoint: [0, 0, 0], - endPoint: [0, 0, 0] + startPoint: null, + endPoint: null }, triggers: [] } diff --git a/app/src/modules/builder/geomentries/assets/assetManager.ts b/app/src/modules/builder/geomentries/assets/assetManager.ts index 5f7798e..b632626 100644 --- a/app/src/modules/builder/geomentries/assets/assetManager.ts +++ b/app/src/modules/builder/geomentries/assets/assetManager.ts @@ -121,7 +121,8 @@ export default async function assetManager( const model = gltf; model.uuid = item.modelUuid; - model.userData = { name: item.modelName, modelId: item.modelfileID, modelUuid: item.modelUuid }; + console.log('item: ', item); + model.userData = { name: item.modelName, modelId: item.modelfileID, modelUuid: item.modelUuid, eventData: item.eventData }; model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); model.position.set(...item.position); model.rotation.set(item.rotation.x, item.rotation.y, item.rotation.z); diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index cc9ce50..3084952 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -222,6 +222,8 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje event ); } + + newFloorItem.eventData = eventData; } } diff --git a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx index 7ea7045..58dab0c 100644 --- a/app/src/modules/scene/controls/selectionControls/rotateControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/rotateControls.tsx @@ -222,6 +222,8 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo event ); } + + newFloorItem.eventData = eventData; } } diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index d4b7eaa..4fd5860 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -146,8 +146,8 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction") const timeoutId = setTimeout(() => { let actionId=armBot.point.actions[0].actionUuid - // addCurrentAction(armBot.modelUuid,actionId); - addCurrentAction(armBot.modelUuid, selectedAction?.actionId); + addCurrentAction(armBot.modelUuid,actionId); + // addCurrentAction(armBot.modelUuid, selectedAction?.actionId); }, 3000); return () => clearTimeout(timeoutId); } diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx index e4f2355..528b0bd 100644 --- a/app/src/modules/simulation/roboticArm/roboticArm.tsx +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -8,8 +8,8 @@ import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import ArmBotUI from "../ui/arm/armBotUI"; function RoboticArm() { - const { armBots, addArmBot, removeArmBot } = useArmBotStore(); - const { getProductById } = useProductStore(); + const { armBots, addArmBot, clearArmBots } = useArmBotStore(); + const { products, getProductById } = useProductStore(); const { selectedProduct } = useSelectedProduct(); const { selectedEventSphere } = useSelectedEventSphere(); const { selectedEventData } = useSelectedEventData(); @@ -161,20 +161,15 @@ function RoboticArm() { if (selectedProduct.productId) { const product = getProductById(selectedProduct.productId); if (product) { + clearArmBots(); product.eventDatas.forEach(events => { if (events.type === 'roboticArm') { - removeArmBot(events.modelUuid); addArmBot(selectedProduct.productId, events); } }); } } - }, [selectedProduct]); - - // useEffect(()=>{ - // // removeArmBot("123", armBotStatusSample[0]); - // addArmBot("123", armBotStatusSample[0]); - // },[]) + }, [selectedProduct, products]); useEffect(() => { diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 757a9ef..efacd53 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -29,14 +29,14 @@ function Simulation() { return ( <> + + {activeModule === 'simulation' && <> - - -- 2.40.1 From 39c86b6fc149d98c5aa1e9a1b26f688323ccac4f Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Fri, 2 May 2025 18:34:44 +0530 Subject: [PATCH 26/32] consoles cleared --- .../builder/IntialLoad/loadInitialFloorItems.ts | 8 ++++---- .../builder/geomentries/assets/assetManager.ts | 11 +++++------ app/src/modules/simulation/ui/arm/useDraggableGLTF.ts | 2 +- app/src/modules/simulation/vehicle/vehicles.tsx | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index 9f71945..0ce28ce 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -72,7 +72,7 @@ async function loadInitialFloorItems( // Check Three.js Cache const cachedModel = THREE.Cache.get(item.modelfileID!); if (cachedModel) { - // console.log(`[Cache] Fetching ${item.modelName}`); + // processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent); modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); @@ -82,7 +82,7 @@ async function loadInitialFloorItems( // Check IndexedDB const indexedDBModel = await retrieveGLTF(item.modelfileID!); if (indexedDBModel) { - // console.log(`[IndexedDB] Fetching ${item.modelName}`); + // const blobUrl = URL.createObjectURL(indexedDBModel); loader.load(blobUrl, (gltf) => { URL.revokeObjectURL(blobUrl); @@ -103,7 +103,7 @@ async function loadInitialFloorItems( } // Fetch from Backend - // console.log(`[Backend] Fetching ${item.modelName}`); + // const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`; loader.load(modelUrl, async (gltf) => { const modelBlob = await fetch(modelUrl).then((res) => res.blob()); @@ -121,7 +121,7 @@ async function loadInitialFloorItems( ); }); } else { - // console.log(`Item ${item.modelName} is not near`); + // setFloorItems((prevItems) => [ ...(prevItems || []), { diff --git a/app/src/modules/builder/geomentries/assets/assetManager.ts b/app/src/modules/builder/geomentries/assets/assetManager.ts index b632626..889d905 100644 --- a/app/src/modules/builder/geomentries/assets/assetManager.ts +++ b/app/src/modules/builder/geomentries/assets/assetManager.ts @@ -18,7 +18,7 @@ export default async function assetManager( const taskId = ++currentTaskId; // Increment taskId for each call activePromises.set(taskId, true); // Mark task as active - // console.log("Received message from worker:", data); + // if (data.toRemove.length > 0) { data.toRemove.forEach((uuid: string) => { @@ -58,7 +58,7 @@ export default async function assetManager( // Check Three.js Cache const cachedModel = THREE.Cache.get(item.modelfileID!); if (cachedModel) { - // console.log(`[Cache] Fetching ${item.modelName}`); + // processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, resolve); return; } @@ -66,7 +66,7 @@ export default async function assetManager( // Check IndexedDB const indexedDBModel = await retrieveGLTF(item.modelfileID!); if (indexedDBModel) { - // console.log(`[IndexedDB] Fetching ${item.modelName}`); + // const blobUrl = URL.createObjectURL(indexedDBModel); loader.load( blobUrl, @@ -86,7 +86,7 @@ export default async function assetManager( } // Fetch from Backend - // console.log(`[Backend] Fetching ${item.modelName}`); + // loader.load( modelUrl, async (gltf) => { @@ -114,14 +114,13 @@ export default async function assetManager( const existingModel = itemsGroup?.current?.getObjectByProperty("uuid", item.modelUuid); if (existingModel) { - // console.log(`Model ${item.modelName} already exists in the scene.`); + // resolve(); return; } const model = gltf; model.uuid = item.modelUuid; - console.log('item: ', item); model.userData = { name: item.modelName, modelId: item.modelfileID, modelUuid: item.modelUuid, eventData: item.eventData }; model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); model.position.set(...item.position); diff --git a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts index 01b7932..e450bfd 100644 --- a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts +++ b/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts @@ -34,7 +34,7 @@ export default function useDraggableGLTF(onUpdate: OnUpdateCallback) { activeObjRef.current = obj; initialPositionRef.current.copy(obj.position); - console.log("obj.position: ", obj.position); + // Get world position setObjectWorldPos(obj.getWorldPosition(objectWorldPos)); diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx index 19f049c..3fbc8ef 100644 --- a/app/src/modules/simulation/vehicle/vehicles.tsx +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -29,7 +29,7 @@ function Vehicles() { }, [selectedProduct, products]); useEffect(() => { - // console.log('vehicles: ', vehicles); + // }, [vehicles]) return ( -- 2.40.1 From ad63a8d72b67f51ff7ffca73fccbeb6ea4a2c1a3 Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Fri, 2 May 2025 18:40:22 +0530 Subject: [PATCH 27/32] added actions for machines --- .../instances/animator/machineAnimator.tsx | 102 ++++++++++++++++++ .../machineInstance/machineInstance.tsx | 61 ++++++++++- .../machine/instances/machineInstances.tsx | 7 +- .../modules/simulation/machine/machine.tsx | 8 +- .../instances/roboticArmInstances.tsx | 2 +- .../instances/animator/vehicleAnimator.tsx | 4 +- .../vehicle/instances/vehicleInstances.tsx | 6 +- 7 files changed, 179 insertions(+), 11 deletions(-) create mode 100644 app/src/modules/simulation/machine/instances/animator/machineAnimator.tsx diff --git a/app/src/modules/simulation/machine/instances/animator/machineAnimator.tsx b/app/src/modules/simulation/machine/instances/animator/machineAnimator.tsx new file mode 100644 index 0000000..6ef2b0e --- /dev/null +++ b/app/src/modules/simulation/machine/instances/animator/machineAnimator.tsx @@ -0,0 +1,102 @@ +import { useFrame } from '@react-three/fiber'; +import React, { useEffect, useRef } from 'react'; +import { useMachineStore } from '../../../../../store/simulation/useMachineStore'; +import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; + + +interface MachineAnimatorProps { + currentPhase: string; + handleCallBack: () => void; + reset: () => void; + machineStatus: (modelId: string, status: string) => void; + processingTime: number; + machineUuid: string +} + +const MachineAnimator = ({ currentPhase, handleCallBack, processingTime, machineUuid, machineStatus, reset }: MachineAnimatorProps) => { + const animationStarted = useRef(false); + const isPausedRef = useRef(false); + const startTimeRef = useRef(0); + const animationFrameId = useRef(null); + const pauseTimeRef = useRef(null); + const { isPaused } = usePauseButtonStore(); + const { removeCurrentAction } = useMachineStore(); + const { isReset, setReset } = useResetButtonStore(); + const { isPlaying } = usePlayButtonStore(); + const { speed } = useAnimationPlaySpeed(); + const isPlayingRef = useRef(false); + const isResetRef = useRef(false) + + + useEffect(() => { + isPausedRef.current = isPaused; + }, [isPaused]); + useEffect(() => { + isPlayingRef.current = isPlaying; + }, [isPlaying]); + useEffect(() => { + isResetRef.current = isReset; + }, [isReset]); + + + useEffect(() => { + + if (isReset || !isPlaying) { + reset(); + setReset(false); + startTimeRef.current = 0; + isPausedRef.current = false; + pauseTimeRef.current = 0; + animationFrameId.current = null; + animationStarted.current = false; + removeCurrentAction(machineUuid) + } + }, [isReset, isPlaying]) + + useEffect(() => { + if (currentPhase === 'processing' && !animationStarted.current && machineUuid) { + animationStarted.current = true; + startTimeRef.current = performance.now(); + animationFrameId.current = requestAnimationFrame(step); + } + }, [currentPhase]); + + function step(time: number) { + if (!isPausedRef.current || !isResetRef.current) { + if (animationFrameId.current) { + cancelAnimationFrame(animationFrameId.current); + animationFrameId.current = null; + } + if (isPausedRef.current) { + if (!pauseTimeRef.current) { + pauseTimeRef.current = performance.now(); + } + animationFrameId.current = requestAnimationFrame(step); + return; + } + + if (pauseTimeRef.current) { + const pauseDuration = performance.now() - pauseTimeRef.current; + startTimeRef.current += pauseDuration; + pauseTimeRef.current = null; + } + + const elapsed = time - startTimeRef.current; + const processedTime = processingTime * 1000; + if (elapsed < processedTime) { + machineStatus(machineUuid, "Machine is currently processing the task"); + animationFrameId.current = requestAnimationFrame(step); + } else { + removeCurrentAction(machineUuid); + animationStarted.current = false; + handleCallBack(); + + } + } + } + + + return null; +} + +export default MachineAnimator; diff --git a/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx index edb825f..714bcdb 100644 --- a/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx +++ b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx @@ -1,8 +1,65 @@ -import React from 'react' +import React, { useEffect, useRef, useState } from 'react' +import { useMachineStore } from '../../../../../store/simulation/useMachineStore'; +import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; +import MachineAnimator from '../animator/machineAnimator'; + +function MachineInstance({ machineDetail }: any) { + const [currentPhase, setCurrentPhase] = useState('idle'); + let isIncrememtable = useRef(true); + const { isPlaying } = usePlayButtonStore(); + const { machines, addCurrentAction, setMachineState, setMachineActive } = useMachineStore(); + + const reset = () => { + setMachineState(machineDetail.modelUuid, 'idle'); + setMachineActive(machineDetail.modelUuid, false); + isIncrememtable.current = true; + setCurrentPhase("idle"); + } + const increment = () => { + if (isIncrememtable.current) { + addCurrentAction(machineDetail.modelUuid, "machine-action-2468-1357-8024") + isIncrememtable.current = false; + } + } + function machineStatus(modelId: string, status: string) { + // console.log(`${modelId} , ${status}`); + + } + + useEffect(() => { + if (isPlaying) { + if (!machineDetail.isActive && machineDetail.state === "idle" && currentPhase == "idle" && !machineDetail.currentAction) { + setTimeout(() => { + increment(); + }, 2000); + machineStatus(machineDetail.modelUuid, 'Machine is idle and waiting for next instruction.') + } else if (!machineDetail.isActive && machineDetail.state === "idle" && currentPhase == "idle" && machineDetail.currentAction) { + setCurrentPhase("processing"); + setMachineState(machineDetail.modelUuid, 'running'); + setMachineActive(machineDetail.modelUuid, true); + machineStatus(machineDetail.modelUuid, "Machine started processing") + } + } else { + reset(); + } + }, [currentPhase, isPlaying, machines]) + + function handleCallBack() { + if (currentPhase == "processing") { + setMachineState(machineDetail.modelUuid, 'idle'); + setMachineActive(machineDetail.modelUuid, false); + setCurrentPhase("idle") + isIncrememtable.current = true; + machineStatus(machineDetail.modelUuid, "Machine has completed the processing") + } + } + // console.log('currentPhase: ', currentPhase); + + -function MachineInstance() { return ( <> + ) } diff --git a/app/src/modules/simulation/machine/instances/machineInstances.tsx b/app/src/modules/simulation/machine/instances/machineInstances.tsx index b0c2c9f..8536cac 100644 --- a/app/src/modules/simulation/machine/instances/machineInstances.tsx +++ b/app/src/modules/simulation/machine/instances/machineInstances.tsx @@ -1,11 +1,14 @@ import React from 'react' import MachineInstance from './machineInstance/machineInstance' +import { useMachineStore } from '../../../../store/simulation/useMachineStore'; function MachineInstances() { + const { machines } = useMachineStore(); return ( <> - - + {machines.map((val: MachineStatus) => ( + + ))} ) diff --git a/app/src/modules/simulation/machine/machine.tsx b/app/src/modules/simulation/machine/machine.tsx index e9d2dea..3d24f61 100644 --- a/app/src/modules/simulation/machine/machine.tsx +++ b/app/src/modules/simulation/machine/machine.tsx @@ -4,7 +4,7 @@ import { useMachineStore } from '../../../store/simulation/useMachineStore' import { useSelectedProduct } from '../../../store/simulation/useSimulationStore'; function Machine() { - const { addMachine, addCurrentAction, removeMachine } = useMachineStore(); + const { addMachine, addCurrentAction, removeMachine, machines } = useMachineStore(); const { selectedProduct } = useSelectedProduct(); const machineSample: MachineEventSchema[] = [ @@ -38,6 +38,12 @@ function Machine() { // addCurrentAction(machineSample[0].modelUuid, machineSample[0].point.action.actionUuid); }, []) + + useEffect(() => { + + // console.log('machines: ', machines); + }, [machines]) + return ( <> diff --git a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx index 1089fa5..d247b35 100644 --- a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx +++ b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx @@ -7,7 +7,7 @@ function RoboticArmInstances() { return ( <> - {armBots?.map((robot: ArmBotStatus) => ( + {armBots.map((robot: ArmBotStatus) => ( ))} diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 421dea8..8e522ff 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -25,9 +25,9 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai const completedRef = useRef(false); const isPausedRef = useRef(false); const pauseTimeRef = useRef(null); + const [progress, setProgress] = useState(0); const [restRotation, setRestingRotation] = useState(true); const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); - const [progress, setProgress] = useState(0); const { scene } = useThree(); let startTime: number; let fixedInterval: number; @@ -66,6 +66,8 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai setReset(false); setRestingRotation(true); decrementVehicleLoad(agvDetail.modelUuid, 0); + isPausedRef.current = false; + pauseTimeRef.current = 0; const object = scene.getObjectByProperty('uuid', agvUuid); if (object) { object.position.set(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]); diff --git a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx index 91111cf..fcc840d 100644 --- a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx +++ b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx @@ -9,10 +9,8 @@ function VehicleInstances() { return ( <> - {vehicles.map((val: any, i: any) => - - - + {vehicles.map((val: VehicleStatus) => + )} -- 2.40.1 From 70b48b39cfa59d1efb1def4dbcd3c83b0e1c447e Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 2 May 2025 19:09:05 +0530 Subject: [PATCH 28/32] refactor: Enhance LogList component layout and styles; improve footer and logs styles for better readability and responsiveness --- app/src/components/ui/log/LogList.tsx | 28 +++++++++++------ app/src/styles/components/footer/footer.scss | 4 +++ app/src/styles/components/logs/logs.scss | 33 ++++++++++++++++++-- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/app/src/components/ui/log/LogList.tsx b/app/src/components/ui/log/LogList.tsx index fe25aee..fad277c 100644 --- a/app/src/components/ui/log/LogList.tsx +++ b/app/src/components/ui/log/LogList.tsx @@ -2,7 +2,6 @@ import React, { useState } from "react"; import { LogListIcon, - TickIcon, LogInfoIcon, WarningIcon, ErrorIcon, @@ -38,8 +37,17 @@ const LogList: React.FC = () => { : [...logs].filter((log) => log.type === selectedTab).reverse(); return ( -
-
+ // eslint-disable-next-line +
setIsLogListVisible(false)} + > +
{ + e.stopPropagation(); + }} + >
@@ -47,9 +55,9 @@ const LogList: React.FC = () => {
Log List
-
setIsLogListVisible(false)}> - {/* */}X -
+
{/* Tabs */} @@ -70,9 +78,11 @@ const LogList: React.FC = () => { {filteredLogs.map((log) => (
{getLogIcon(log.type)}
-
- [{formatTimestamp(log.timestamp)}] [{log.type.toUpperCase()}]{" "} - {log.message} +
+
{log.message}
+
+ {formatTimestamp(log.timestamp)} +
))} diff --git a/app/src/styles/components/footer/footer.scss b/app/src/styles/components/footer/footer.scss index d3a3d92..b2d85d0 100644 --- a/app/src/styles/components/footer/footer.scss +++ b/app/src/styles/components/footer/footer.scss @@ -60,8 +60,12 @@ } .version { + font-size: var(--font-size-tiny); display: flex; gap: 6px; + .icon{ + @include flex-center; + } } } } diff --git a/app/src/styles/components/logs/logs.scss b/app/src/styles/components/logs/logs.scss index d2535aa..f07708b 100644 --- a/app/src/styles/components/logs/logs.scss +++ b/app/src/styles/components/logs/logs.scss @@ -1,8 +1,10 @@ +@use "../../abstracts/variables" as *; +@use "../../abstracts/mixins" as *; + .log-list-container { width: 100vw; height: 100vh; - // background: var(--background-color-secondary); - // backdrop-filter: blur(2px); + background: var(--background-color-secondary); .log-list-wrapper { height: 50%; @@ -19,6 +21,7 @@ flex-direction: column; gap: 12px; backdrop-filter: blur(50px); + outline: 1px solid var(--border-color); .log-header { display: flex; @@ -31,7 +34,13 @@ } .close { + @include flex-center; + height: 28px; + width: 28px; cursor: pointer; + svg { + scale: 1.6; + } } } @@ -58,6 +67,8 @@ background: var(--background-color); padding: 18px 10px; border-radius: 16px; + outline: 1px solid var(--border-color); + outline-offset: -1px; .log-entry { padding: 4px; @@ -67,6 +78,24 @@ align-items: center; gap: 6px; + .log-icon { + @include flex-center; + } + .log-entry-message-container { + @include flex-space-between; + gap: 12px; + width: 100%; + .message-time { + font-size: var(--font-size-tiny); + font-weight: 300; + opacity: 0.8; + text-wrap: nowrap; + } + .log-entry-message{ + width: 100%; + } + } + &:nth-child(odd) { background: var(--background-color); } -- 2.40.1 From ac49f62bb03c373030c0b245ddf712603be7e22f Mon Sep 17 00:00:00 2001 From: Vishnu Date: Sat, 3 May 2025 09:45:29 +0530 Subject: [PATCH 29/32] refactor: Remove unused analysis and ROISummary styles; consolidate simulation styles for improved organization --- .../styles/components/analysis/analysis.scss | 268 ----------------- .../analysis.scss} | 273 +++++++++++++++++- app/src/styles/main.scss | 5 +- 3 files changed, 272 insertions(+), 274 deletions(-) delete mode 100644 app/src/styles/components/analysis/analysis.scss rename app/src/styles/components/{analysis/ROISummary.scss => simulation/analysis.scss} (50%) diff --git a/app/src/styles/components/analysis/analysis.scss b/app/src/styles/components/analysis/analysis.scss deleted file mode 100644 index 3185ed6..0000000 --- a/app/src/styles/components/analysis/analysis.scss +++ /dev/null @@ -1,268 +0,0 @@ -.analysis { - position: fixed; - top: 0; - left: 0; - display: flex; - justify-content: space-between; - align-items: start; - width: 100%; - height: 100vh; - pointer-events: none; - padding: 10px; - z-index: 2; - - .analysis-wrapper { - display: flex; - flex-direction: column; - gap: 12px; - } - .analysis-card { - min-width: 333px; - border-radius: 20px; - padding: 8px; - pointer-events: all; - - .analysis-card-wrapper { - width: 100%; - background: var(--background-color); - border-radius: 14px; - padding: 16px; - display: flex; - flex-direction: column; - gap: 14px; - backdrop-filter: blur(10px); - outline: 1px solid var(--border-color); - outline-offset: -1px; - - .card-header { - width: 100%; - display: flex; - justify-content: space-between; - align-items: center; - - .main-header { - line-height: 20px; - font-size: var(--font-size-regular); - } - } - - .process-container { - display: flex; - flex-direction: column; - - .throughput-value { - font-size: 1rem; - - .value { - font-weight: bold; - font-size: 1.5rem; - } - } - - .progress-bar-wrapper { - display: flex; - gap: 8px; - margin-top: 6px; - } - - .progress-bar { - position: relative; - width: 100%; - height: 4px; - border-radius: 13px; - overflow: hidden; - background: #fbebd7; - - .bar-fill { - position: absolute; - height: 100%; - top: 0; - left: 0; - background: #fc9d2f; - border-radius: 13px; - } - - .bar-fill.full { - width: 100%; - } - - .bar-fill.partial { - width: 0; // inline style will override this - } - } - } - - .metrics-section { - padding-top: 16px; - border-top: 1px solid var(--background-color-gray); - - .metric { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 14px; - margin-bottom: 8px; - - .label { - color: var(--text-color); - } - - .value { - font-weight: bold; - } - } - } - } - .throughoutSummary-wrapper { - .process-container { - display: flex; - flex-direction: row; - align-items: center; - justify-content: flex-start; - gap: 16px; - width: 100%; - - .throughput-value { - font-size: var(--font-size-small); - flex: 1; - display: flex; - flex-direction: column; - - .value { - color: var(--accent-color); - } - - /* Let the text take available space */ - } - - .lineChart { - max-width: 200px; - height: 100px; - position: relative; - - .assetUsage { - text-align: right; - position: absolute; - right: 0; - top: 0; - } - - canvas { - background: transparent; - } - } - } - - .footer { - display: flex; - gap: 16px; // Space between cards - margin-top: 24px; - - .footer-card { - width: 100%; - background: var(--background-color-gray); - border-radius: 6px; - padding: 8px; - display: flex; - flex-direction: column; - gap: 6px; - - &:first-child { - width: 85%; - } - - .header { - font-size: var(--font-size-regular); - } - - .value-container { - display: flex; - flex-direction: row; - align-items: center; - justify-content: end; - gap: 6px; - } - } - - .shiftUtilization { - .value-container { - flex-direction: column; - align-items: flex-start; - justify-content: flex-start; - - .value { - font-size: var(--font-size-xlarge); - } - - .progress-wrapper { - width: 100%; - display: flex; - gap: 6px; - - .progress { - border-radius: 6px; - height: 5px; - - &:nth-child(1) { - background: #f3c64d; - } - - &:nth-child(2) { - background: #67b3f4; - } - - &:nth-child(3) { - background: #7981f5; - } - } - } - - .progress-indicator { - display: flex; - justify-content: space-between; - width: 100%; - gap: 6px; - - .shift-wrapper { - display: flex; - align-items: center; - gap: 5px; - - /* Align items vertically */ - &:nth-child(1) { - .indicator { - background: #f3c64d; - } - } - - &:nth-child(2) { - .indicator { - background: #67b3f4; - } - } - - &:nth-child(3) { - .indicator { - background: #7981f5; - } - } - - label { - font-size: var(--font-size-small); - position: relative; - } - - .indicator { - display: inline-block; - width: 5px; - height: 5px; - border-radius: 50%; - } - } - } - } - } - } - } - } -} diff --git a/app/src/styles/components/analysis/ROISummary.scss b/app/src/styles/components/simulation/analysis.scss similarity index 50% rename from app/src/styles/components/analysis/ROISummary.scss rename to app/src/styles/components/simulation/analysis.scss index 1f6f824..9aed5c8 100644 --- a/app/src/styles/components/analysis/ROISummary.scss +++ b/app/src/styles/components/simulation/analysis.scss @@ -1,7 +1,273 @@ @use "../../abstracts/variables" as *; @use "../../abstracts/mixins" as *; -.roiSummary-container { +.analysis { + position: fixed; + top: 0; + left: 0; + display: flex; + justify-content: space-between; + align-items: start; + width: 100%; + height: 100vh; + pointer-events: none; + padding: 10px; + z-index: 2; + + .analysis-wrapper { + display: flex; + flex-direction: column; + gap: 12px; + } + .analysis-card { + min-width: 333px; + border-radius: 20px; + padding: 8px; + pointer-events: all; + + .analysis-card-wrapper { + width: 100%; + background: var(--background-color); + border-radius: 14px; + padding: 16px; + display: flex; + flex-direction: column; + gap: 14px; + backdrop-filter: blur(10px); + outline: 1px solid var(--border-color); + outline-offset: -1px; + + .card-header { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + + .main-header { + line-height: 20px; + font-size: var(--font-size-regular); + } + } + + .process-container { + display: flex; + flex-direction: column; + + .throughput-value { + font-size: 1rem; + + .value { + font-weight: bold; + font-size: 1.5rem; + } + } + + .progress-bar-wrapper { + display: flex; + gap: 8px; + margin-top: 6px; + } + + .progress-bar { + position: relative; + width: 100%; + height: 4px; + border-radius: 13px; + overflow: hidden; + background: #fbebd7; + + .bar-fill { + position: absolute; + height: 100%; + top: 0; + left: 0; + background: #fc9d2f; + border-radius: 13px; + } + + .bar-fill.full { + width: 100%; + } + + .bar-fill.partial { + width: 0; // inline style will override this + } + } + } + + .metrics-section { + padding-top: 16px; + border-top: 1px solid var(--background-color-gray); + + .metric { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 14px; + margin-bottom: 8px; + + .label { + color: var(--text-color); + } + + .value { + font-weight: bold; + } + } + } + } + .throughoutSummary-wrapper { + .process-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + gap: 16px; + width: 100%; + + .throughput-value { + font-size: var(--font-size-small); + flex: 1; + display: flex; + flex-direction: column; + + .value { + color: var(--accent-color); + } + + /* Let the text take available space */ + } + + .lineChart { + max-width: 200px; + height: 100px; + position: relative; + + .assetUsage { + text-align: right; + position: absolute; + right: 0; + top: 0; + } + + canvas { + background: transparent; + } + } + } + + .footer { + display: flex; + gap: 16px; // Space between cards + margin-top: 24px; + + .footer-card { + width: 100%; + background: var(--background-color-gray); + border-radius: 6px; + padding: 8px; + display: flex; + flex-direction: column; + gap: 6px; + + &:first-child { + width: 85%; + } + + .header { + font-size: var(--font-size-regular); + } + + .value-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: end; + gap: 6px; + } + } + + .shiftUtilization { + .value-container { + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + + .value { + font-size: var(--font-size-xlarge); + } + + .progress-wrapper { + width: 100%; + display: flex; + gap: 6px; + + .progress { + border-radius: 6px; + height: 5px; + + &:nth-child(1) { + background: #f3c64d; + } + + &:nth-child(2) { + background: #67b3f4; + } + + &:nth-child(3) { + background: #7981f5; + } + } + } + + .progress-indicator { + display: flex; + justify-content: space-between; + width: 100%; + gap: 6px; + + .shift-wrapper { + display: flex; + align-items: center; + gap: 5px; + + /* Align items vertically */ + &:nth-child(1) { + .indicator { + background: #f3c64d; + } + } + + &:nth-child(2) { + .indicator { + background: #67b3f4; + } + } + + &:nth-child(3) { + .indicator { + background: #7981f5; + } + } + + label { + font-size: var(--font-size-small); + position: relative; + } + + .indicator { + display: inline-block; + width: 5px; + height: 5px; + border-radius: 50%; + } + } + } + } + } + } + } + } .roiSummary-wrapper { max-width: 470px; background-color: var(--background-color); @@ -264,8 +530,6 @@ } } -// Breakdown Table Open/Close Logic - .breakdown-table-wrapper { &.closed { max-height: 0; @@ -288,3 +552,6 @@ } } } + +// Breakdown Table Open/Close Logic + diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index 9fe7222..6bea32f 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -22,11 +22,10 @@ @use 'components/visualization/ui/styledWidgets'; @use 'components/visualization/floating/common'; @use 'components/marketPlace/marketPlace'; -@use 'components/simulation/simulation'; @use 'components/menu/menu'; @use 'components/confirmationPopUp'; -@use 'components/analysis/analysis'; -@use 'components/analysis/ROISummary.scss'; +@use 'components/simulation/simulation'; +@use 'components/simulation/analysis'; @use 'components/logs/logs'; @use 'components/footer/footer.scss'; -- 2.40.1 From 39c0017a5bc16f1e593dc875e6e1f7a3280b4847 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Sat, 3 May 2025 10:02:40 +0530 Subject: [PATCH 30/32] refactor: Reintroduce Footer component with updated log handling; remove unused layouts styles --- app/src/components/{ui => }/footer/Footer.tsx | 8 ++++---- app/src/pages/Project.tsx | 2 +- app/src/styles/components/layouts.scss | 0 app/src/styles/main.scss | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) rename app/src/components/{ui => }/footer/Footer.tsx (88%) delete mode 100644 app/src/styles/components/layouts.scss diff --git a/app/src/components/ui/footer/Footer.tsx b/app/src/components/footer/Footer.tsx similarity index 88% rename from app/src/components/ui/footer/Footer.tsx rename to app/src/components/footer/Footer.tsx index 9f73284..19fc6c5 100644 --- a/app/src/components/ui/footer/Footer.tsx +++ b/app/src/components/footer/Footer.tsx @@ -1,11 +1,11 @@ import React from "react"; -import { HelpIcon } from "../../icons/DashboardIcon"; -import { useLogger } from "../log/LoggerContext"; +import { HelpIcon } from "../icons/DashboardIcon"; import { LogInfoIcon, ErrorIcon, WarningIcon, -} from "../../icons/ExportCommonIcons"; // Adjust path as needed +} from "../icons/ExportCommonIcons"; // Adjust path as needed +import { useLogger } from "../ui/log/LoggerContext"; const getLogIcon = (type: string) => { switch (type) { @@ -21,7 +21,7 @@ const getLogIcon = (type: string) => { } }; -const Footer = () => { +const Footer: React.FC = () => { const { logs, setIsLogListVisible } = useLogger(); const lastLog = logs.length > 0 ? logs[logs.length - 1] : null; diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 83e4497..17a6315 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -28,9 +28,9 @@ import { createHandleDrop } from "../modules/visualization/functions/handleUiDro import { useSelectedZoneStore } from "../store/visualization/useZoneStore"; import { useFloatingWidget } from "../store/visualization/useDroppedObjectsStore"; import { useLogger } from "../components/ui/log/LoggerContext"; -import Footer from "../components/ui/footer/Footer"; import RenderOverlay from "../components/templates/Overlay"; import LogList from "../components/ui/log/LogList"; +import Footer from "../components/footer/Footer"; const Project: React.FC = () => { let navigate = useNavigate(); diff --git a/app/src/styles/components/layouts.scss b/app/src/styles/components/layouts.scss deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index 6bea32f..3adfc0f 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -13,7 +13,6 @@ @use 'components/button'; @use 'components/form'; @use 'components/input'; -@use 'components/layouts'; @use 'components/lists'; @use 'components/moduleToggle'; @use 'components/templates'; -- 2.40.1 From 0e4005f31e79101d693cc263c521c514880980c2 Mon Sep 17 00:00:00 2001 From: Gomathi9520 Date: Sat, 3 May 2025 10:05:25 +0530 Subject: [PATCH 31/32] multiple action state updated --- .../events/points/creator/pointsCreator.tsx | 3 - .../instances/animator/roboticArmAnimator.tsx | 2 - .../armInstance/roboticArmInstance.tsx | 125 +++++++++--------- .../simulation/roboticArm/roboticArm.tsx | 1 - .../simulation/ui/arm/PickDropPoints.tsx | 3 - .../modules/simulation/ui/arm/armBotUI.tsx | 119 ++++++++--------- 6 files changed, 125 insertions(+), 128 deletions(-) diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx index 5037f75..0ed7e13 100644 --- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -31,8 +31,6 @@ function PointsCreator() { selectedEventSphere.userData.modelUuid ); - - if (eventData) { setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid); } else { @@ -215,7 +213,6 @@ function PointsCreator() { ); }} onPointerMissed={() => { - // clearSelectedEventSphere(); setTransformMode(null); }} position={new THREE.Vector3(...event.point.position)} diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index d32a6c3..10f90bf 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -55,11 +55,9 @@ function RoboticArmAnimator({ 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; diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index 4fd5860..9199ede 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -8,7 +8,9 @@ import { useThree } from "@react-three/fiber"; import { useFloorItems } from '../../../../../store/store'; import useModuleStore from '../../../../../store/useModuleStore'; import * as THREE from "three"; -import { useSelectedAction } from '../../../../../store/simulation/useSimulationStore'; +import { useSelectedAction, useSelectedProduct } from '../../../../../store/simulation/useSimulationStore'; +import { useProductStore } from '../../../../../store/simulation/useProductStore'; + function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { @@ -24,17 +26,19 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { let startTime: number; //zustand const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore(); + const { products, getActionByUuid } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); const { floorItems } = useFloorItems(); const { activeModule } = useModuleStore(); const { isPlaying } = usePlayButtonStore(); const { isReset, setReset } = useResetButtonStore(); const { isPaused } = usePauseButtonStore(); - const { selectedAction } = useSelectedAction(); - + const { selectedAction } = useSelectedAction(); + function firstFrame() { startTime = performance.now(); step(); - } + } function step() { if (isPausedRef.current) { if (!pauseTimeRef.current) { @@ -54,70 +58,70 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { requestAnimationFrame(step); return; } - if(currentPhase==="picking"){ - - setArmBotActive(armBot.modelUuid, true); - setArmBotState(armBot.modelUuid, "running"); - setCurrentPhase("start-to-end"); - startTime=0 - const startPoint = armBot.point.actions[0].process.startPoint; - const endPoint = armBot.point.actions[0].process.endPoint; - if (startPoint && endPoint) { - let curve = createCurveBetweenTwoPoints( - new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]), - new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2])); - if (curve) { - logStatus(armBot.modelUuid, "picking the object"); - setPath(curve.points.map(point => [point.x, point.y, point.z])) - } + if (currentPhase === "picking") { + + setArmBotActive(armBot.modelUuid, true); + setArmBotState(armBot.modelUuid, "running"); + setCurrentPhase("start-to-end"); + startTime = 0 + const startPoint = armBot.point.actions[0].process.startPoint; + const endPoint = armBot.point.actions[0].process.endPoint; + if (startPoint && endPoint) { + let curve = createCurveBetweenTwoPoints( + new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]), + new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2])); + if (curve) { + logStatus(armBot.modelUuid, "picking the object"); + setPath(curve.points.map(point => [point.x, point.y, point.z])) } - logStatus(armBot.modelUuid, "Moving armBot from start point to end position.") - }else if(currentPhase==="dropping"){ - - setArmBotActive(armBot.modelUuid, true); - setArmBotState(armBot.modelUuid, "running"); - setCurrentPhase("end-to-rest"); - startTime=0; - const endPoint = armBot.point.actions[0].process.endPoint; - if (endPoint) { - - let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition); - if (curve) { - logStatus(armBot.modelUuid, "dropping the object"); - setPath(curve.points.map(point => [point.x, point.y, point.z])); - } - } - logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.") } - - } - useEffect(() => { + logStatus(armBot.modelUuid, "Moving armBot from start point to end position.") + } else if (currentPhase === "dropping") { + + setArmBotActive(armBot.modelUuid, true); + setArmBotState(armBot.modelUuid, "running"); + setCurrentPhase("end-to-rest"); + startTime = 0; + const endPoint = armBot.point.actions[0].process.endPoint; + if (endPoint) { + + let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition); + if (curve) { + logStatus(armBot.modelUuid, "dropping the object"); + setPath(curve.points.map(point => [point.x, point.y, point.z])); + } + } + logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.") + } + + } + useEffect(() => { isPausedRef.current = isPaused; }, [isPaused]); useEffect(() => { - const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid); - - if (targetMesh) { - targetMesh.visible = activeModule !== "simulation" - } + const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid); + + if (targetMesh) { + targetMesh.visible = activeModule !== "simulation" + } const targetBones = ikSolver?.mesh.skeleton.bones.find( (b: any) => b.name === targetBone ); if (isReset) { - + logStatus(armBot.modelUuid, "Simulation Play Reset Successfully") removeCurrentAction(armBot.modelUuid) setArmBotActive(armBot.modelUuid, true) setArmBotState(armBot.modelUuid, "running") setCurrentPhase("init-to-rest"); - isPausedRef.current=false - pauseTimeRef.current=null - isPausedRef.current=false - startTime=0 + isPausedRef.current = false + pauseTimeRef.current = null + isPausedRef.current = false + startTime = 0 if (targetBones) { - let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition) + let curve = createCurveBetweenTwoPoints(targetBones.position, targetBones.position) if (curve) { setPath(curve.points.map(point => [point.x, point.y, point.z])); } @@ -126,10 +130,10 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.") } if (isPlaying) { - + //Moving armBot from initial point to rest position. if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") { - + setArmBotActive(armBot.modelUuid, true) setArmBotState(armBot.modelUuid, "running") setCurrentPhase("init-to-rest"); @@ -145,18 +149,19 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction") const timeoutId = setTimeout(() => { - let actionId=armBot.point.actions[0].actionUuid - addCurrentAction(armBot.modelUuid,actionId); - // addCurrentAction(armBot.modelUuid, selectedAction?.actionId); + addCurrentAction(armBot.modelUuid, selectedAction?.actionId); + console.log('selectedAction?.actionId: ', selectedAction?.actionId); }, 3000); return () => clearTimeout(timeoutId); } else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) { if (armBot.currentAction) { - + setArmBotActive(armBot.modelUuid, true); setArmBotState(armBot.modelUuid, "running"); setCurrentPhase("rest-to-start"); + let actiondata = getActionByUuid(selectedProduct.productId, selectedAction.actionId) + console.log('actiondata: ', actiondata); const startPoint = armBot.point.actions[0].process.startPoint; if (startPoint) { let curve = createCurveBetweenTwoPoints(restPosition, new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2])); @@ -211,7 +216,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { setCurrentPhase("init"); setPath([]) removeCurrentAction(armBot.modelUuid) - + } }, [currentPhase, armBot, isPlaying, ikSolver, isReset]) @@ -258,14 +263,14 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } const logStatus = (id: string, status: string) => { // - + } return ( <> + logStatus={logStatus} path={path} currentPhase={currentPhase} /> ) } diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx index 528b0bd..2cb9ea6 100644 --- a/app/src/modules/simulation/roboticArm/roboticArm.tsx +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -172,7 +172,6 @@ function RoboticArm() { }, [selectedProduct, products]); useEffect(() => { - }, [armBots]) useEffect(() => { diff --git a/app/src/modules/simulation/ui/arm/PickDropPoints.tsx b/app/src/modules/simulation/ui/arm/PickDropPoints.tsx index 831da7f..c44eee8 100644 --- a/app/src/modules/simulation/ui/arm/PickDropPoints.tsx +++ b/app/src/modules/simulation/ui/arm/PickDropPoints.tsx @@ -24,10 +24,7 @@ const PickDropPoints: React.FC = ({ handlePointerDown, isSelected, }) => { - - const groupRef = useRef(null); - return ( { const { selectedProduct } = useSelectedProduct(); const { armBots, updateStartPoint, updateEndPoint } = useArmBotStore(); const { scene } = useThree(); + const { selectedAction } = useSelectedAction(); const armUiPick = useGLTF(armPick) as any; const armUiDrop = useGLTF(armDrop) as any; @@ -37,43 +38,40 @@ const ArmBotUI = () => { if (selectedEventData?.data.type === "roboticArm") { const selectedArmBot = getEventByModelUuid(selectedProduct.productId, selectedEventData.data.modelUuid); - if (selectedArmBot) { - if (selectedArmBot.type === "roboticArm") { + if (selectedArmBot?.type === "roboticArm") { + setSelectedArmBotData(selectedArmBot); + const defaultPositions = getDefaultPositions(selectedArmBot.modelUuid); + const matchingAction = armBots?.flatMap((robot: ArmBotStatus) => robot.point.actions) + .find((action) => action.actionUuid === selectedAction.actionId); + if (matchingAction) { + const startPoint = matchingAction.process.startPoint; + const pickPosition = (!startPoint || (Array.isArray(startPoint) && startPoint.every(v => v === 0))) + ? defaultPositions.pick + : startPoint; - setSelectedArmBotData(selectedArmBot); + const endPoint = matchingAction.process.endPoint; + const dropPosition = (!endPoint || (Array.isArray(endPoint) && endPoint.every(v => v === 0))) + ? defaultPositions.drop + : endPoint; - const defaultPositions = getDefaultPositions(selectedArmBot.modelUuid); - - selectedArmBot?.point?.actions?.forEach((action: any) => { - if (action.actionType === "pickAndPlace") { - const startPoint = action.process.startPoint; - const pickPosition = (!startPoint || (Array.isArray(startPoint) && startPoint.every(v => v === 0))) - ? defaultPositions.pick - : startPoint; - const endPoint = action.process.endPoint; - const dropPosition = (!endPoint || (Array.isArray(endPoint) && endPoint.every(v => v === 0))) - ? defaultPositions.drop - : endPoint; - setStartPosition(pickPosition); - setEndPosition(dropPosition) - - } - }); + setStartPosition(pickPosition); + setEndPosition(dropPosition); } } } - }, [selectedEventData, selectedProduct, getEventByModelUuid]); + }, [selectedEventData, selectedProduct, getEventByModelUuid, selectedAction]); + function getDefaultPositions(modelUuid: string): Positions { const modelData = getEventByModelUuid(selectedProduct.productId, modelUuid); - + if (modelData?.type === "roboticArm") { const baseX = modelData.point.position?.[0] || 0; const baseY = modelData.point.position?.[1] || 0;; const baseZ = modelData.point.position?.[2] || 0; return { - pick: [baseX, baseY , baseZ + 0.5], - drop: [baseX, baseY , baseZ - 0.5], + pick: [baseX, baseY, baseZ + 0.5], + drop: [baseX, baseY, baseZ - 0.5], default: [baseX, baseY, baseZ], }; } @@ -118,13 +116,10 @@ const ArmBotUI = () => { if (actionType === "pick") { updatedProcess.startPoint = getLocalPosition(modelUuid, worldPositionArray); - // updatedProcess.startPoint = worldPositionArray setStartPosition(updatedProcess.startPoint) - updateStartPoint(modelUuid, actionUuid, updatedProcess.startPoint); } else if (actionType === "drop") { updatedProcess.endPoint = getLocalPosition(modelUuid, worldPositionArray); - // updatedProcess.endPoint = worldPositionArray setEndPosition(updatedProcess.endPoint) updateEndPoint(modelUuid, actionUuid, updatedProcess.endPoint); } @@ -143,47 +138,53 @@ const ArmBotUI = () => { useEffect(() => { -}, [armBots]) + }, [armBots]) const { handlePointerDown } = useDraggableGLTF(updatePointToState); if (!selectedArmBotData || !Array.isArray(selectedArmBotData.point?.actions)) { return null; // avoid rendering if no data yet } - return ( <> - {selectedArmBotData.point.actions.map((action: any) => ( - - - - - - - ))} + {selectedArmBotData.point.actions.map((action: any) => { + if (action.actionUuid === selectedAction.actionId) { + return ( + + + + + + + ); + } else { + return null; // important! must return something + } + })} ); + }; export default ArmBotUI; -- 2.40.1 From 94c306c81365a31b63d9ebdb533d028a386e7c98 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Sat, 3 May 2025 10:09:39 +0530 Subject: [PATCH 32/32] Refactor Trigger component usage in mechanics files: pass selectedPointData and type props for Machine, RoboticArm, StorageUnit, and Vehicle to enhance functionality and maintain consistency. --- .../mechanics/machineMechanics.tsx | 2 +- .../mechanics/roboticArmMechanics.tsx | 2 +- .../mechanics/storageMechanics.tsx | 2 +- .../mechanics/vehicleMechanics.tsx | 2 +- .../eventProperties/trigger/Trigger.tsx | 273 +++++++++++++++--- .../IntialLoad/loadInitialFloorItems.ts | 1 - .../geomentries/assets/addAssetModel.ts | 2 +- .../triggers/connector/triggerConnector.tsx | 8 +- .../services/simulation/renameProductApi.ts | 26 ++ app/src/store/simulation/useProductStore.ts | 7 +- app/src/types/simulationTypes.d.ts | 2 +- 11 files changed, 275 insertions(+), 52 deletions(-) create mode 100644 app/src/services/simulation/renameProductApi.ts diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx index 3e11e54..be50990 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx @@ -137,7 +137,7 @@ function MachineMechanics() {
- +
)} diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx index df5c358..3e8aa67 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx @@ -280,7 +280,7 @@ function RoboticArmMechanics() { />
- +
)} diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx index 3273ce4..34e35ae 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx @@ -146,7 +146,7 @@ function StorageMechanics() {
- +
)} diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx index 7839168..0f52437 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx @@ -235,7 +235,7 @@ function VehicleMechanics() {
- +
diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx index 86e4e7b..f48f875 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useRef, useState } from "react"; +import React, { useEffect, useMemo, useRef, useState } from "react"; +import * as THREE from "three"; import { AddIcon, RemoveIcon, @@ -9,6 +10,7 @@ import RenameInput from "../../../../../ui/inputs/RenameInput"; import { handleResize } from "../../../../../../functions/handleResizePannel"; import { useProductStore } from "../../../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore"; +import { upsertProductOrEventApi } from "../../../../../../services/simulation/UpsertProductOrEventApi"; type TriggerProps = { selectedPointData?: PointsScheme | undefined; @@ -18,35 +20,215 @@ type TriggerProps = { const Trigger = ({ selectedPointData, type }: TriggerProps) => { const [currentAction, setCurrentAction] = useState(); const { selectedProduct } = useSelectedProduct(); - const { getActionByUuid } = useProductStore(); + const { getActionByUuid, addTrigger, removeTrigger, updateTrigger, renameTrigger, getProductById } = useProductStore(); const [triggers, setTriggers] = useState([]); const [selectedTrigger, setSelectedTrigger] = useState(); - const [activeOption, setActiveOption] = useState("onComplete"); + const [activeOption, setActiveOption] = useState<"onComplete" | "onStart" | "onStop" | "delay" | "onError">("onComplete"); const triggersContainerRef = useRef(null); + const email = localStorage.getItem('email') + const organization = (email!.split("@")[1]).split(".")[0]; + useEffect(() => { - if (!selectedPointData) return; + if (!selectedPointData || !selectedProduct) return; + + let actionUuid: string | undefined; + if (type === 'Conveyor' || type === 'Vehicle' || type === 'Machine' || type === 'StorageUnit') { - setCurrentAction((selectedPointData as ConveyorPointSchema).action.actionUuid); + actionUuid = (selectedPointData as ConveyorPointSchema | VehiclePointSchema | MachinePointSchema | StoragePointSchema).action?.actionUuid; + } else if (type === 'RoboticArm') { + actionUuid = (selectedPointData as RoboticArmPointSchema).actions[0]?.actionUuid; } - }, [selectedPointData]); + + setCurrentAction(actionUuid); + }, [selectedPointData, selectedProduct, type]); + + + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData + }) + } useEffect(() => { if (!currentAction || !selectedProduct) return; const action = getActionByUuid(selectedProduct.productId, currentAction); - setTriggers(action?.triggers || []); - setSelectedTrigger(action?.triggers[0] || undefined); + const actionTriggers = action?.triggers || []; + setTriggers(actionTriggers); + setSelectedTrigger(actionTriggers[0]); }, [currentAction, selectedProduct]); - const addTrigger = (): void => { + const handleAddTrigger = () => { + if (!selectedProduct || !currentAction) return; + + const newTrigger: TriggerSchema = { + triggerUuid: THREE.MathUtils.generateUUID(), + triggerName: `New Trigger ${triggers.length + 1}`, + triggerType: activeOption, + delay: 0, + triggeredAsset: null + }; + + addTrigger(selectedProduct.productId, currentAction, newTrigger); + setSelectedTrigger(newTrigger); }; - const removeTrigger = (triggerUuid: string): void => { + const handleRemoveTrigger = (triggerUuid: string) => { + if (!selectedProduct) return; + removeTrigger(selectedProduct.productId, triggerUuid); + if (selectedTrigger?.triggerUuid === triggerUuid) { + const remainingTriggers = triggers.filter(t => t.triggerUuid !== triggerUuid); + setSelectedTrigger(remainingTriggers[0]); + } + }; + + const handleTriggerRename = (triggerUuid: string, newName: string) => { + if (!selectedProduct) return; + renameTrigger(selectedProduct.productId, triggerUuid, newName); + }; + + const handleTriggerTypeChange = (option: string) => { + if (!selectedTrigger || !selectedProduct) return; + + const validTypes: Array = ["onComplete", "onStart", "onStop", "delay", "onError"]; + if (!validTypes.includes(option as TriggerSchema['triggerType'])) return; + + setActiveOption(option as TriggerSchema['triggerType']); + updateTrigger(selectedProduct.productId, selectedTrigger.triggerUuid, { + triggerType: option as TriggerSchema['triggerType'] + }); }; const triggeredModel = selectedTrigger?.triggeredAsset?.triggeredModel || { modelName: "Select Model", modelUuid: "" }; const triggeredPoint = selectedTrigger?.triggeredAsset?.triggeredPoint || { pointName: "Select Point", pointUuid: "" }; const triggeredAction = selectedTrigger?.triggeredAsset?.triggeredAction || { actionName: "Select Action", actionUuid: "" }; + console.log('selectedTrigger: ', selectedTrigger); + console.log('triggeredAction: ', triggeredAction); + + const modelOptions = getProductById(selectedProduct.productId)?.eventDatas || []; + + const pointOptions: PointsScheme[] = useMemo(() => { + if (!triggeredModel.modelUuid) return []; + + const model = modelOptions.find(m => m.modelUuid === triggeredModel.modelUuid); + if (!model) return []; + + if ('points' in model) { + return (model as ConveyorEventSchema).points; + } else if ('point' in model) { + return [(model as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point]; + } + return []; + }, [triggeredModel.modelUuid, modelOptions]); + + const actionOptions: any = useMemo(() => { + if (!triggeredPoint.pointUuid) return []; + const point = pointOptions.find((p) => p.uuid === triggeredPoint.pointUuid); + if (!point) return []; + + if ('action' in point) { + const typedPoint = point as ConveyorPointSchema | VehiclePointSchema | MachinePointSchema | StoragePointSchema; + return typedPoint.action ? [typedPoint.action] : []; + } else if ('actions' in point) { + const typedPoint = point as RoboticArmPointSchema; + return typedPoint.actions; + } + return []; + }, [triggeredPoint.pointUuid, pointOptions]); + + const handleModelSelect = (option: string, triggerUuid: string) => { + if (!selectedProduct) return; + + const selectedModel = modelOptions.find(m => m.modelName === option); + if (!selectedModel) return; + + const event = updateTrigger(selectedProduct.productId, triggerUuid, { + triggeredAsset: { + triggeredModel: { + modelName: selectedModel.modelName, + modelUuid: selectedModel.modelUuid + }, + triggeredPoint: null, + triggeredAction: null + } + }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + }; + + const handlePointSelect = (option: string, triggerUuid: string) => { + if (!selectedProduct || !selectedTrigger) return; + + const pointUuid = pointOptions.find(p => `Point ${p.uuid.slice(0, 5)}` === option)?.uuid; + + if (!pointUuid) return; + + if (selectedTrigger.triggeredAsset?.triggeredModel) { + const event = updateTrigger(selectedProduct.productId, triggerUuid, { + triggeredAsset: { + ...selectedTrigger.triggeredAsset, + triggeredPoint: { + pointName: option, + pointUuid: pointUuid + }, + triggeredAction: null + } + }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + } + }; + + const handleActionSelect = (option: string, triggerUuid: string) => { + if (!selectedProduct || !selectedTrigger) return; + + const selectedAction = actionOptions.find((a: any) => a.actionName === option); + + if (!selectedAction) return; + + if (selectedTrigger.triggeredAsset?.triggeredPoint) { + const event = updateTrigger(selectedProduct.productId, triggerUuid, { + triggeredAsset: { + ...selectedTrigger.triggeredAsset, + triggeredAction: { + actionName: option, + actionUuid: selectedAction.actionUuid + } + } + }); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + } + }; return (
@@ -54,8 +236,9 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
Trigger
@@ -73,13 +256,19 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => { className={`list-item ${selectedTrigger?.triggerUuid === trigger.triggerUuid ? "active" : ""}`} onClick={() => setSelectedTrigger(trigger)} > - {triggers.length > 1 && ( @@ -95,35 +284,39 @@ const Trigger = ({ selectedPointData, type }: TriggerProps) => {
-
-
{selectedTrigger?.triggerName}
- setActiveOption(option)} - /> -
+ + {selectedTrigger && ( +
+
{selectedTrigger.triggerName}
{ }} - /> - { }} - /> - { }} + label="Trigger Type" + defaultOption={selectedTrigger.triggerType} + options={["onComplete", "onStart", "onStop", "delay", "onError"]} + onSelect={handleTriggerTypeChange} /> + +
+ (option.modelName))]} + onSelect={(option) => { handleModelSelect(option, selectedTrigger.triggerUuid) }} + /> + (`Point ${option.uuid.slice(0, 5)}`))]} + onSelect={(option) => { handlePointSelect(option, selectedTrigger.triggerUuid) }} + /> + (option.actionName))]} + onSelect={(option) => { handleActionSelect(option, selectedTrigger.triggerUuid) }} + /> +
-
+ )}
); diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index ee5c283..6ab7230 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -8,7 +8,6 @@ import * as Types from "../../../types/world/worldTypes"; import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi'; import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; -import PointsCalculator from '../../simulation/events/points/functions/pointsCalculator'; async function loadInitialFloorItems( itemsGroup: Types.RefGroup, diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 64656a0..6e978dc 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -235,7 +235,7 @@ async function handleModelLoad( const nextPoint = ConveyorEvent.points[i + 1]; if (currentPoint.action.triggers.length > 0) { - currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint.pointUuid = nextPoint.uuid; + currentPoint.action.triggers[0].triggeredAsset!.triggeredPoint!.pointUuid = nextPoint.uuid; currentPoint.action.triggers[0].triggeredAsset!.triggeredAction!.actionUuid = nextPoint.action.actionUuid; } } diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx index dcb46f4..f9fc30f 100644 --- a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -68,7 +68,7 @@ function TriggerConnector() { event.points.forEach(point => { if (point.action?.triggers) { point.action.triggers.forEach(trigger => { - if (trigger.triggeredAsset) { + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) { newConnections.push({ id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, startPointUuid: point.uuid, @@ -85,7 +85,7 @@ function TriggerConnector() { const point = event.point; if (point.action?.triggers) { point.action.triggers.forEach(trigger => { - if (trigger.triggeredAsset) { + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) { newConnections.push({ id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, startPointUuid: point.uuid, @@ -101,7 +101,7 @@ function TriggerConnector() { const point = event.point; point.actions?.forEach(action => { action.triggers?.forEach(trigger => { - if (trigger.triggeredAsset) { + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) { newConnections.push({ id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, startPointUuid: point.uuid, @@ -117,7 +117,7 @@ function TriggerConnector() { const point = event.point; if (point.action?.triggers) { point.action.triggers.forEach(trigger => { - if (trigger.triggeredAsset) { + if (trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint) { newConnections.push({ id: `${point.uuid}-${trigger.triggeredAsset.triggeredPoint.pointUuid}-${trigger.triggerUuid}`, startPointUuid: point.uuid, diff --git a/app/src/services/simulation/renameProductApi.ts b/app/src/services/simulation/renameProductApi.ts new file mode 100644 index 0000000..afa493c --- /dev/null +++ b/app/src/services/simulation/renameProductApi.ts @@ -0,0 +1,26 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const renameProductApi = async (body: { productName: string, productId: string, organization: string }) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/v2/productRename`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }); + + if (!response.ok) { + throw new Error("Failed to rename product"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index b7448f2..8b83cdd 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -51,7 +51,7 @@ type ProductsStore = { productId: string, triggerUuid: string, updates: Partial - ) => void; + ) => EventsSchema | undefined; // Renaming functions renameProduct: (productId: string, newName: string) => void; @@ -392,6 +392,7 @@ export const useProductStore = create()( }, updateTrigger: (productId, triggerUuid, updates) => { + let updatedEvent: EventsSchema | undefined; set((state) => { const product = state.products.find(p => p.productId === productId); if (product) { @@ -402,6 +403,7 @@ export const useProductStore = create()( const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); if (trigger) { Object.assign(trigger, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); return; } } @@ -412,6 +414,7 @@ export const useProductStore = create()( const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); if (trigger) { Object.assign(trigger, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); return; } } else if ('actions' in point) { @@ -420,6 +423,7 @@ export const useProductStore = create()( const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); if (trigger) { Object.assign(trigger, updates); + updatedEvent = JSON.parse(JSON.stringify(event)); return; } } @@ -429,6 +433,7 @@ export const useProductStore = create()( } } }); + return updatedEvent; }, // Renaming functions diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index c7ccd8b..11d5156 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -13,7 +13,7 @@ interface TriggerSchema { delay: number; triggeredAsset: { triggeredModel: { modelName: string, modelUuid: string }; - triggeredPoint: { pointName: string, pointUuid: string }; + triggeredPoint: { pointName: string, pointUuid: string } | null; triggeredAction: { actionName: string, actionUuid: string } | null; } | null; } -- 2.40.1