diff --git a/app/src/modules/IIOTTemp/AgvSimulation.tsx b/app/src/modules/IIOTTemp/AgvSimulation.tsx index b9fed17..015eac9 100644 --- a/app/src/modules/IIOTTemp/AgvSimulation.tsx +++ b/app/src/modules/IIOTTemp/AgvSimulation.tsx @@ -89,7 +89,7 @@ function AgvSimulation({ data, assetName, assetUUID }: AgvSimulationProps) { if (materialRef.current && offsetRef.current) { materialRef.current.position.copy(currentPos.clone().add(offsetRef.current)); } - console.log(` ${assetName} is running at ${data.percentage}%`); + }); useEffect(() => { diff --git a/app/src/modules/IIOTTemp/ArmAnimator.tsx b/app/src/modules/IIOTTemp/ArmAnimator.tsx index 3a1a38c..11bf86c 100644 --- a/app/src/modules/IIOTTemp/ArmAnimator.tsx +++ b/app/src/modules/IIOTTemp/ArmAnimator.tsx @@ -8,7 +8,7 @@ type PointWithDegree = { degree: number; }; -function ArmAnimator({ armBot, ikSolver, setIkSolver, targetBone, restPosition, path }: any) { +function ArmAnimator({ armBot, ikSolver, setIkSolver, targetBone, restPosition, path, assetName, data }: any) { const { scene } = useThree(); const progressRef = useRef(0); const curveRef = useRef(null); @@ -24,9 +24,13 @@ function ArmAnimator({ armBot, ikSolver, setIkSolver, targetBone, restPosition, const { speed } = useAnimationPlaySpeed(); let duration = 5000 + const [isRunning, setIsRunning] = useState(false); + const lastPercentageRef = useRef(0); + const targetPercentageRef = useRef(0); + const interpolationStartTimeRef = useRef(performance.now()); + useEffect(() => { - console.log('path: ', path); setCurrentPath(path); }, [path]); @@ -183,22 +187,32 @@ function ArmAnimator({ armBot, ikSolver, setIkSolver, targetBone, restPosition, // Frame update for animation useFrame((state, delta) => { + + if (!startTimeRef.current || !isRunning) return; + + if (!ikSolver || !customCurvePoints || customCurvePoints.length === 0) return; + const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); + if (!bone) return; + + const now = performance.now(); + const elapsed = now - interpolationStartTimeRef.current; + const duration = 2000; + const t = Math.min(elapsed / duration, 1); + const interpolatedPercentage = + lastPercentageRef.current + + (targetPercentageRef.current - lastPercentageRef.current) * t; + const progress = Math.min(interpolatedPercentage / 100, 1); + const distances = segmentDistancesRef.current; const totalDistance = totalDistanceRef.current; - // Initialize start time - if (startTimeRef.current === null) { - startTimeRef.current = state.clock.elapsedTime; - } - - const elapsed = (state.clock.elapsedTime - startTimeRef.current) * 1000; // ms - const clampedProgress = Math.min(elapsed / duration, 1); // progress [0,1] - const coveredDistance = clampedProgress * totalDistance; + const coveredDistance = progress * totalDistance; + // console.log('coveredDistance: ', coveredDistance); // Traverse segments to find current position let index = 0; @@ -215,6 +229,8 @@ function ArmAnimator({ armBot, ikSolver, setIkSolver, targetBone, restPosition, const segmentDistance = distances[index]; const t = (coveredDistance - accumulatedDistance) / segmentDistance; + + if (startPoint && endPoint) { const position = startPoint.clone().lerp(endPoint, t); bone.position.copy(position); @@ -224,7 +240,7 @@ function ArmAnimator({ armBot, ikSolver, setIkSolver, targetBone, restPosition, ikSolver.update(); // Reset at the end - if (clampedProgress >= 1) { + if (progress >= 1) { setCurrentPath([]); setCustomCurvePoints([]); curveRef.current = null; @@ -237,6 +253,25 @@ function ArmAnimator({ armBot, ikSolver, setIkSolver, targetBone, restPosition, } }); + useEffect(() => { + if (data.assetName !== assetName) return; + if (data.state === 'running' && data.percentage !== undefined) { + console.log('data.percentage: ', data.percentage); + if (data.percentage === 0) { + startTimeRef.current = performance.now(); + lastPercentageRef.current = 0; + targetPercentageRef.current = 0 + } else { + lastPercentageRef.current = targetPercentageRef.current; + } + targetPercentageRef.current = data.percentage; + interpolationStartTimeRef.current = performance.now(); + setIsRunning(true); + } else { + + setIsRunning(false); + } + }, [data, assetName]); return ( <> diff --git a/app/src/modules/IIOTTemp/ConveyorSimulation.tsx b/app/src/modules/IIOTTemp/ConveyorSimulation.tsx index cf2d7f0..4abe165 100644 --- a/app/src/modules/IIOTTemp/ConveyorSimulation.tsx +++ b/app/src/modules/IIOTTemp/ConveyorSimulation.tsx @@ -85,7 +85,7 @@ function ConveyorSimulation({ data, assetName, assetUUID }: ConveyorSimulationPr materialRef.current.position.copy(currentPos); materialRef.current.quaternion.copy(quaternionRef.current); - console.log(` ${assetName} is running at ${data.percentage}%`); + }); // Update movement state diff --git a/app/src/modules/IIOTTemp/IIotIkSolver.tsx b/app/src/modules/IIOTTemp/IIotIkSolver.tsx index 7ba6cd2..e280dbd 100644 --- a/app/src/modules/IIOTTemp/IIotIkSolver.tsx +++ b/app/src/modules/IIOTTemp/IIotIkSolver.tsx @@ -71,7 +71,7 @@ function IIotIkSolver({ modelUrl, setIkSolver, ikSolver, armBot, groupRef }: IKI setSelectedArm(OOI.Target_Bone); - scene.add(helper); + // scene.add(helper); }, [cloned, gltf, setIkSolver]); diff --git a/app/src/modules/IIOTTemp/MachineSimulation.tsx b/app/src/modules/IIOTTemp/MachineSimulation.tsx index ad5d9a9..b270227 100644 --- a/app/src/modules/IIOTTemp/MachineSimulation.tsx +++ b/app/src/modules/IIOTTemp/MachineSimulation.tsx @@ -43,7 +43,7 @@ function MachineSimulation({ data, assetName, assetUUID }: MachineSimulationProp const percentage = percentageRef.current; // 🟢 Use percentage in logic if needed - console.log(` ${assetName} is running at ${data.percentage}%`); + }); return null; diff --git a/app/src/modules/IIOTTemp/MqttListener.tsx b/app/src/modules/IIOTTemp/MqttListener.tsx index 8db00b7..fb2ebf1 100644 --- a/app/src/modules/IIOTTemp/MqttListener.tsx +++ b/app/src/modules/IIOTTemp/MqttListener.tsx @@ -14,12 +14,13 @@ const MqttListener: React.FC = ({ setData }) => { useEffect(() => { if (!client) return; - const topic = 'conveyor0001/0001/status'; + const topic1 = 'conveyor0001/0001/status'; const topic2 = 'conveyor0002/0001/status'; const topic3 = 'cnc0001/0001/status' const topic4 = 'agv0001/0001/status' + const topic5 = 'arm0001/0001/status' - client.subscribe(topic, {} as IClientSubscribeOptions, (err) => { + client.subscribe(topic1, {} as IClientSubscribeOptions, (err) => { if (err) { console.error('Subscription error:', err); } @@ -39,36 +40,58 @@ const MqttListener: React.FC = ({ setData }) => { console.error('Subscription error:', err); } }); + client.subscribe(topic5, {} as IClientSubscribeOptions, (err) => { + if (err) { + console.error('Subscription error:', err); + } + }); const handleMessage = (receivedTopic: string, payload: Buffer) => { - if (receivedTopic === topic) { + if (receivedTopic === topic1) { const msgStr = payload.toString(); - setData(JSON.parse(msgStr)) - // setMessage(msgStr); + let mqttData = JSON.parse(msgStr); + // if (mqttData.state === "running") { + setData(mqttData) + // } } if (receivedTopic === topic2) { const msgStr = payload.toString(); - setData(JSON.parse(msgStr)) - // setMessage(msgStr); + let mqttData = JSON.parse(msgStr); + // if (mqttData.state === "running") { + setData(mqttData) + // } } if (receivedTopic === topic3) { const msgStr = payload.toString(); - setData(JSON.parse(msgStr)) - - // setMessage(msgStr); + let mqttData = JSON.parse(msgStr); + // if (mqttData.state === "running") { + setData(mqttData) + // } } if (receivedTopic === topic4) { const msgStr = payload.toString(); - setData(JSON.parse(msgStr)) - - // setMessage(msgStr); + let mqttData = JSON.parse(msgStr); + // if (mqttData.state === "running") { + setData(mqttData) + // } + } + if (receivedTopic === topic5) { + const msgStr = payload.toString(); + let mqttData = JSON.parse(msgStr); + // if (mqttData.state === "running") { + setData(mqttData) + // } } }; client.on('message', handleMessage); return () => { client.off('message', handleMessage); - client.unsubscribe(topic); + client.unsubscribe(topic1); + client.unsubscribe(topic2); + client.unsubscribe(topic3); + client.unsubscribe(topic4); + client.unsubscribe(topic5); }; }, [client]); diff --git a/app/src/modules/IIOTTemp/RealTimeSimulation.tsx b/app/src/modules/IIOTTemp/RealTimeSimulation.tsx index 160bb92..b1b0f5f 100644 --- a/app/src/modules/IIOTTemp/RealTimeSimulation.tsx +++ b/app/src/modules/IIOTTemp/RealTimeSimulation.tsx @@ -6,6 +6,7 @@ import ConveyorSimulation from './ConveyorSimulation'; import MachineSimulation from './MachineSimulation'; import AgvSimulation from './AgvSimulation'; import RoboticArmSimulation from './RoboticArmSimulation'; +import useModuleStore from '../../store/useModuleStore'; type MqttData = { state?: string; @@ -17,8 +18,14 @@ type MqttData = { function RealTimeSimulation() { const [data, setData] = useState({}); + const { activeModule } = useModuleStore(); useEffect(() => { + if (activeModule !== "visualization") return; + // console.log('data: ', data); + if (data.state === "running") { + console.log('data: ', data); + } }, [data]) return ( @@ -27,7 +34,7 @@ function RealTimeSimulation() { - + diff --git a/app/src/modules/IIOTTemp/RoboticArmSimulation.tsx b/app/src/modules/IIOTTemp/RoboticArmSimulation.tsx index f7fe22a..316fbe1 100644 --- a/app/src/modules/IIOTTemp/RoboticArmSimulation.tsx +++ b/app/src/modules/IIOTTemp/RoboticArmSimulation.tsx @@ -6,6 +6,7 @@ import armModel from "../../assets/gltf-glb/rigged/ik_arm_1.glb"; import * as THREE from "three"; import ArmAnimator from "./ArmAnimator"; import { useFloorItems } from "../../store/builder/store"; +import { MaterialModel } from "../simulation/materials/instances/material/materialModel"; type ArmBotSimulationProps = { data: any; @@ -26,6 +27,13 @@ function RoboticArmSimulation({ const targetBone = "Target"; const restPosition = new THREE.Vector3(0, 1.75, -1.6); const [path, setPath] = useState<[number, number, number][]>([]); + const [isRunning, setIsRunning] = useState(false); + const startTimeRef = useRef(null); + + const lastPercentageRef = useRef(0); + const targetPercentageRef = useRef(0); + const interpolationStartTimeRef = useRef(performance.now()); + const materialRef = useRef(null); useEffect(() => { if (activeModule !== "visualization") return; @@ -43,44 +51,139 @@ function RoboticArmSimulation({ position: [RoboticArmObject.position.x, RoboticArmObject.position.y, RoboticArmObject.position.z,], rotation: [RoboticArmObject.rotation.x, RoboticArmObject.rotation.y, RoboticArmObject.rotation.z,], }; - - setArmBot(updatedArmBot); }, [scene, assetUUID, activeModule]); + + const lastEventRef = useRef(null); + const lastPathRef = useRef(""); // Stringified path to compare + useEffect(() => { + if (data.assetName !== assetName) return; if (!ikSolver) return; + if (!isRunning) return; - const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone); + const targetBones = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); if (!targetBones) return; + //action 1 + const startPoint = new THREE.Vector3(-0.5119150023588372, 1.08653750252812, 1.2082537344869024); + const endPoint = new THREE.Vector3(-0.005966507840761359, 1.101367457937616, -1.242848820163239); + //action 2 + const startPoint1 = new THREE.Vector3(-0.005966507840761359, 1.101367457937616, -1.242848820163239); + const endPoint1 = new THREE.Vector3(0.8449084618119792, 1.0275816535760263, 1.399557309225635); - const startPoint = new THREE.Vector3(-0.050142171738890684, 1.9, -1.999371341850562); - const endPoint = new THREE.Vector3(1.9999942666761656, 1.9, 0.004788868599774787); + if (lastEventRef.current === data.event) return; + if (data.event === 'pick1') { + console.log("picking"); + const curve = createCurveBetweenTwoPoints(targetBones.position.clone(), startPoint.clone()); + if (curve) { + setPath(curve.points.map(point => [point.x, point.y, point.z])); + } + lastEventRef.current = 'pick1'; + } - const curve1 = createCurveBetweenTwoPoints(targetBones.position.clone(), restPosition.clone()); - const curve2 = createCurveBetweenTwoPoints(restPosition.clone(), startPoint.clone()); - const curve3 = createCurveBetweenTwoPoints(startPoint.clone(), endPoint.clone()); - const curve4 = createCurveBetweenTwoPoints(endPoint.clone(), restPosition.clone()); + if (data.event === 'drop1') { + console.log("dropping"); + const curve = createCurveBetweenTwoPoints(startPoint.clone(), endPoint.clone()); + if (curve) { + setPath(curve.points.map(point => [point.x, point.y, point.z])); + } + lastEventRef.current = 'drop1'; - const curveList = [curve1, curve2, curve3, curve4]; + } + if (data.event === 'pick2') { + console.log("picking111"); + const curve = createCurveBetweenTwoPoints(targetBones.position.clone(), startPoint1.clone()); + if (curve) { + setPath(curve.points.map(point => [point.x, point.y, point.z])); + } + lastEventRef.current = 'pick2'; - let allPoints: [number, number, number][] = []; + } + if (data.event === 'drop2') { + console.log("dropping1111"); + const curve = createCurveBetweenTwoPoints(startPoint1.clone(), endPoint1.clone()); + if (curve) { + setPath(curve.points.map(point => [point.x, point.y, point.z])); + } + lastEventRef.current = 'drop2'; - curveList.forEach((curve, index) => { - setTimeout(() => { - if (curve) { - setPath(curve.points.map(point => [point.x, point.y, point.z])); - } - }, index * 5000); // 2 seconds delay between each curve - }); - }, [ikSolver]); + } + }, [ikSolver, data, assetName, isRunning]); + useEffect(() => { + if (data.assetName !== assetName) return; + if (data.state === 'running' && data.percentage !== undefined) { + if (data.percentage === 0) { + lastPercentageRef.current = 0; + targetPercentageRef.current = 0 + } else { + lastPercentageRef.current = targetPercentageRef.current; + } + targetPercentageRef.current = data.percentage; + interpolationStartTimeRef.current = performance.now(); + setIsRunning(true); + } else { + setIsRunning(false); + } + }, [data, assetName]); function createCurveBetweenTwoPoints(p1: any, p2: any) { const mid = new THREE.Vector3().addVectors(p1, p2).multiplyScalar(0.5); const points = [p1, mid, p2]; return new THREE.CatmullRomCurve3(points); } + useEffect(() => { + if (!ikSolver || !materialRef.current) return; + + const boneTarget = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === "Bone004"); + const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === "Effector"); + + if (!boneTarget || !bone) return; + + if (data.state === "running" && data.assetName === assetName) { + // Get world positions + const boneTargetWorldPos = new THREE.Vector3(); + const boneWorldPos = new THREE.Vector3(); + boneTarget.getWorldPosition(boneTargetWorldPos); + bone.getWorldPosition(boneWorldPos); + + // Calculate direction + const direction = new THREE.Vector3(); + direction.subVectors(boneWorldPos, boneTargetWorldPos).normalize(); + + const adjustedPosition = boneWorldPos.clone().addScaledVector(direction, 0.01); + + //set position + materialRef.current.position.copy(adjustedPosition); + + // Set rotation + const worldQuaternion = new THREE.Quaternion(); + bone.getWorldQuaternion(worldQuaternion); + materialRef.current.quaternion.copy(worldQuaternion); + } + + }, [data]) + const [isVisible, setIsVisible] = useState(false); + useEffect(() => { + if (data.assetName !== assetName) return; + + if (data.state === 'running' && typeof data.percentage === 'number') { + const shouldBeVisible = data.percentage <= 100; + // Only update visibility if it changes + setIsVisible(prev => { + if (prev !== shouldBeVisible) { + return shouldBeVisible; + } + return prev; + }); + } else { + setIsVisible(false); + } + }, [data, assetName]); + + + return ( <> {activeModule === "visualization" && armBot && ( @@ -99,7 +202,15 @@ function RoboticArmSimulation({ restPosition={restPosition} targetBone={targetBone} path={path} + assetName={assetName} + data={data} /> + {/* */} )}