enhance simulation components with state management and event handling for armbot
This commit is contained in:
parent
73d2cc8c73
commit
60c0bef5f9
|
@ -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(() => {
|
||||
|
|
|
@ -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<THREE.Vector3[] | null>(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<number>(0);
|
||||
const targetPercentageRef = useRef<number>(0);
|
||||
const interpolationStartTimeRef = useRef<number>(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 (
|
||||
<></>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -14,12 +14,13 @@ const MqttListener: React.FC<MqttListenerProps> = ({ 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<MqttListenerProps> = ({ 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]);
|
||||
|
||||
|
|
|
@ -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<MqttData>({});
|
||||
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() {
|
|||
<ConveyorSimulation data={data} assetName="conveyor0002" assetUUID="1288e8f9-95dd-4a1a-a087-0673bbbc3b34" />
|
||||
<MachineSimulation data={data} assetName="cnc0001" assetUUID="d3598136-40f6-43a9-afd2-8a8899c0986d" />
|
||||
<AgvSimulation data={data} assetName="agv0001" assetUUID="1dd01c22-cc58-40cf-9e08-84c26f3f4eba" />
|
||||
<RoboticArmSimulation data={data} assetName="roboticArm" assetUUID="f8344b0b-0c13-4e78-be1f-b6cc210038a4" />
|
||||
<RoboticArmSimulation data={data} assetName="arm0001" assetUUID="f8344b0b-0c13-4e78-be1f-b6cc210038a4" />
|
||||
<MqttListener setData={setData} />
|
||||
|
||||
</>
|
||||
|
|
|
@ -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<number | null>(null);
|
||||
|
||||
const lastPercentageRef = useRef<number>(0);
|
||||
const targetPercentageRef = useRef<number>(0);
|
||||
const interpolationStartTimeRef = useRef<number>(performance.now());
|
||||
const materialRef = useRef<any>(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<string | null>(null);
|
||||
const lastPathRef = useRef<string>(""); // 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}
|
||||
/>
|
||||
{/* <MaterialModel
|
||||
materialId={`${assetName}-mat`}
|
||||
matRef={materialRef}
|
||||
materialType={'Default material'}
|
||||
visible={isVisible}
|
||||
/> */}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
|
Loading…
Reference in New Issue