Enhance useRetrieveHandler to manage retrieval timing based on animation speed and arm bot state Update materials component to streamline imports and improve readability Modify roboticArmInstance to change state handling from 'idle' to 'running' during active phases Fix useTriggerHandler to ensure actions are handled correctly when material is null Refactor vehicleInstance to improve material drop handling and integrate conveyor and robotic arm interactions Add getLastMaterial method to useVehicleStore for better material management in vehicle instances
319 lines
14 KiB
TypeScript
319 lines
14 KiB
TypeScript
import React, { useEffect, useRef, useState } from 'react'
|
|
import * as THREE from "three";
|
|
import { useThree } from "@react-three/fiber";
|
|
import IKInstance from '../ikInstance/ikInstance';
|
|
import RoboticArmAnimator from '../animator/roboticArmAnimator';
|
|
import MaterialAnimator from '../animator/materialAnimator';
|
|
import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb";
|
|
import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
|
import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore';
|
|
import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
|
|
import { useProductStore } from '../../../../../store/simulation/useProductStore';
|
|
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
|
|
import { useStorageUnitStore } from '../../../../../store/simulation/useStorageUnitStore';
|
|
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
|
|
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
|
|
|
|
function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|
|
|
const [currentPhase, setCurrentPhase] = useState<(string)>("init");
|
|
const [path, setPath] = useState<[number, number, number][]>([]);
|
|
const [ikSolver, setIkSolver] = useState<any>(null);
|
|
const { scene } = useThree();
|
|
const restPosition = new THREE.Vector3(0, 1.75, -1.6);
|
|
const targetBone = "Target";
|
|
const groupRef = useRef<any>(null);
|
|
const pauseTimeRef = useRef<number | null>(null);
|
|
const isPausedRef = useRef<boolean>(false);
|
|
let startTime: number;
|
|
|
|
const { setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
|
|
const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore();
|
|
const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore();
|
|
const { setIsVisible, getMaterialById } = useMaterialStore();
|
|
const { selectedProduct } = useSelectedProduct();
|
|
const { getActionByUuid, getEventByActionUuid, getEventByModelUuid } = useProductStore();
|
|
const { triggerPointActions } = useTriggerHandler();
|
|
const { isPlaying } = usePlayButtonStore();
|
|
const { isReset } = useResetButtonStore();
|
|
const { isPaused } = usePauseButtonStore();
|
|
|
|
function firstFrame() {
|
|
startTime = performance.now();
|
|
step();
|
|
}
|
|
|
|
const action = getActionByUuid(selectedProduct.productId, armBot.currentAction?.actionUuid || '');
|
|
|
|
const handlePickUpTrigger = () => {
|
|
if (armBot.currentAction && armBot.currentAction.materialId) {
|
|
const material = getMaterialById(armBot.currentAction.materialId);
|
|
if (material && material.previous && material.previous.modelUuid) {
|
|
const previousModel = getEventByActionUuid(selectedProduct.productId, material.previous.actionUuid);
|
|
if (previousModel) {
|
|
if (previousModel.type === 'transfer') {
|
|
setIsVisible(armBot.currentAction.materialId, false);
|
|
} else if (previousModel.type === 'machine') {
|
|
// machine specific logic
|
|
} else if (previousModel.type === 'vehicle') {
|
|
decrementVehicleLoad(previousModel.modelUuid, 1);
|
|
removeLastMaterial(previousModel.modelUuid);
|
|
} else if (previousModel.type === 'storageUnit') {
|
|
// storage unit logic
|
|
removeLastStorageMaterial(previousModel.modelUuid);
|
|
updateCurrentLoad(previousModel.modelUuid, -1)
|
|
}
|
|
} else {
|
|
setIsVisible(armBot.currentAction.materialId, false);
|
|
}
|
|
} else {
|
|
setIsVisible(armBot.currentAction.materialId, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
const handleDropTrigger = () => {
|
|
|
|
if (armBot.currentAction) {
|
|
const action = getActionByUuid(selectedProduct.productId, armBot.currentAction.actionUuid);
|
|
if (action && action.triggers[0].triggeredAsset?.triggeredModel.modelUuid) {
|
|
const model = getEventByModelUuid(selectedProduct.productId, action?.triggers[0].triggeredAsset?.triggeredModel.modelUuid);
|
|
if (!model) return;
|
|
if (model.type === 'transfer') {
|
|
setIsVisible(armBot.currentAction.materialId || '', true);
|
|
} else if (model.type === 'machine') {
|
|
//
|
|
} else if (model.type === 'vehicle') {
|
|
//
|
|
} else if (model.type === 'storageUnit') {
|
|
//
|
|
}
|
|
}
|
|
if (action && armBot.currentAction.materialId) {
|
|
triggerPointActions(action, armBot.currentAction.materialId)
|
|
removeCurrentAction(armBot.modelUuid)
|
|
}
|
|
}
|
|
}
|
|
|
|
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 < 1000) {
|
|
// 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
|
|
if (!action) return;
|
|
const startPoint = (action as RoboticArmAction).process.startPoint;
|
|
const endPoint = (action as RoboticArmAction).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]))
|
|
|
|
handlePickUpTrigger();
|
|
|
|
}
|
|
}
|
|
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;
|
|
if (!action) return;
|
|
const endPoint = (action as RoboticArmAction).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]));
|
|
|
|
handleDropTrigger();
|
|
|
|
}
|
|
}
|
|
logStatus(armBot.modelUuid, "Moving armBot from end point to rest position.")
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
isPausedRef.current = isPaused;
|
|
}, [isPaused]);
|
|
|
|
useEffect(() => {
|
|
if (isReset || !isPlaying) {
|
|
logStatus(armBot.modelUuid, "Simulation Play Reset Successfully")
|
|
setArmBotActive(armBot.modelUuid, false)
|
|
setArmBotState(armBot.modelUuid, "idle")
|
|
setCurrentPhase("init");
|
|
setPath([])
|
|
setIkSolver(null);
|
|
removeCurrentAction(armBot.modelUuid)
|
|
isPausedRef.current = false
|
|
pauseTimeRef.current = null
|
|
startTime = 0
|
|
const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone
|
|
);
|
|
if (targetBones && isPlaying) {
|
|
let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition)
|
|
if (curve) {
|
|
setPath(curve.points.map(point => [point.x, point.y, point.z]));
|
|
logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.")
|
|
}
|
|
}
|
|
}
|
|
}, [isReset, isPlaying])
|
|
|
|
useEffect(() => {
|
|
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
|
|
if (targetMesh) {
|
|
targetMesh.visible = (!isPlaying)
|
|
}
|
|
const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone);
|
|
if (!isReset && isPlaying) {
|
|
//Moving armBot from initial point to rest position.
|
|
if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") {
|
|
if (targetBones) {
|
|
setArmBotActive(armBot.modelUuid, true)
|
|
setArmBotState(armBot.modelUuid, "running")
|
|
setCurrentPhase("init-to-rest");
|
|
let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition)
|
|
if (curve) {
|
|
setPath(curve.points.map(point => [point.x, point.y, point.z]));
|
|
}
|
|
}
|
|
logStatus(armBot.modelUuid, "Moving armBot from initial point to rest position.")
|
|
}
|
|
//Waiting for trigger.
|
|
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) {
|
|
logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction")
|
|
}
|
|
//Moving to pickup point
|
|
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");
|
|
if (!action) return;
|
|
const startPoint = (action as RoboticArmAction).process.startPoint;
|
|
if (startPoint) {
|
|
let curve = createCurveBetweenTwoPoints(targetBones.position, new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]));
|
|
if (curve) {
|
|
setPath(curve.points.map(point => [point.x, point.y, point.z]));
|
|
}
|
|
}
|
|
}
|
|
logStatus(armBot.modelUuid, "Moving armBot from rest point to start position.")
|
|
}
|
|
// Moving to Pick to Drop position
|
|
else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "picking" && armBot.currentAction) {
|
|
requestAnimationFrame(firstFrame);
|
|
}
|
|
//Moving to drop point to restPosition
|
|
else if (armBot && !armBot.isActive && armBot.state === "running" && currentPhase === "dropping" && armBot.currentAction) {
|
|
requestAnimationFrame(firstFrame);
|
|
}
|
|
} else {
|
|
logStatus(armBot.modelUuid, "Simulation Play Exited")
|
|
setArmBotActive(armBot.modelUuid, false)
|
|
setArmBotState(armBot.modelUuid, "idle")
|
|
setCurrentPhase("init");
|
|
setIkSolver(null);
|
|
setPath([])
|
|
isPausedRef.current = false
|
|
pauseTimeRef.current = null
|
|
isPausedRef.current = false
|
|
startTime = 0
|
|
removeCurrentAction(armBot.modelUuid)
|
|
}
|
|
|
|
}, [currentPhase, armBot, isPlaying, isReset, ikSolver])
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
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, "running")
|
|
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, "running")
|
|
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([])
|
|
}
|
|
}
|
|
const logStatus = (id: string, status: string) => {
|
|
// console.log('status: ', status);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{!isReset && isPlaying && (
|
|
<>
|
|
<IKInstance modelUrl={armModel} setIkSolver={setIkSolver} ikSolver={ikSolver} armBot={armBot} groupRef={groupRef} />
|
|
<RoboticArmAnimator
|
|
HandleCallback={HandleCallback}
|
|
restPosition={restPosition}
|
|
ikSolver={ikSolver}
|
|
targetBone={targetBone}
|
|
armBot={armBot}
|
|
logStatus={logStatus}
|
|
path={path}
|
|
currentPhase={currentPhase}
|
|
/>
|
|
</>
|
|
)}
|
|
<MaterialAnimator ikSolver={ikSolver} armBot={armBot} currentPhase={currentPhase} />
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default RoboticArmInstance;
|