Merge remote-tracking branch 'origin/v2' into v2-ui
This commit is contained in:
@@ -14,6 +14,7 @@ async function loadInitialFloorItems(
|
||||
itemsGroup: Types.RefGroup,
|
||||
setFloorItems: Types.setFloorItemSetState,
|
||||
addEvent: (event: EventsSchema) => void,
|
||||
renderDistance: number
|
||||
): Promise<void> {
|
||||
if (!itemsGroup.current) return;
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
@@ -65,7 +66,7 @@ async function loadInitialFloorItems(
|
||||
|
||||
const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z);
|
||||
|
||||
if (cameraPosition.distanceTo(itemPosition) < 50) {
|
||||
if (cameraPosition.distanceTo(itemPosition) < renderDistance) {
|
||||
await new Promise<void>(async (resolve) => {
|
||||
|
||||
// Check Three.js Cache
|
||||
@@ -210,7 +211,6 @@ function processLoadedModel(
|
||||
actionUuid: THREE.MathUtils.generateUUID(),
|
||||
actionName: "Vehicle Action",
|
||||
actionType: "travel",
|
||||
material: null,
|
||||
unLoadDuration: 5,
|
||||
loadCapacity: 10,
|
||||
pickUpPoint: null,
|
||||
@@ -243,9 +243,9 @@ function processLoadedModel(
|
||||
rotation: [0, 0, 0],
|
||||
action: {
|
||||
actionUuid: THREE.MathUtils.generateUUID(),
|
||||
actionName: `Action ${index}`,
|
||||
actionName: `Action ${index + 1}`,
|
||||
actionType: 'default',
|
||||
material: 'inherit',
|
||||
material: 'Default material',
|
||||
delay: 0,
|
||||
spawnInterval: 5,
|
||||
spawnCount: 1,
|
||||
|
||||
@@ -51,7 +51,7 @@ import Ground from "../scene/environment/ground";
|
||||
// import ZoneGroup from "../groups/zoneGroup1";
|
||||
import { findEnvironment } from "../../services/factoryBuilder/environment/findEnvironment";
|
||||
import Layer2DVisibility from "./geomentries/layers/layer2DVisibility";
|
||||
import DrieHtmlTemp from "..//visualization/mqttTemp/drieHtmlTemp";
|
||||
import DrieHtmlTemp from "../visualization/mqttTemp/drieHtmlTemp";
|
||||
import ZoneGroup from "./groups/zoneGroup";
|
||||
import useModuleStore from "../../store/useModuleStore";
|
||||
import MeasurementTool from "../scene/tools/measurementTool";
|
||||
|
||||
@@ -202,7 +202,7 @@ async function handleModelLoad(
|
||||
actionUuid: THREE.MathUtils.generateUUID(),
|
||||
actionName: `Action ${index}`,
|
||||
actionType: 'default',
|
||||
material: 'inherit',
|
||||
material: 'Default Material',
|
||||
delay: 0,
|
||||
spawnInterval: 5,
|
||||
spawnCount: 1,
|
||||
@@ -226,9 +226,8 @@ async function handleModelLoad(
|
||||
rotation: [0, 0, 0],
|
||||
action: {
|
||||
actionUuid: THREE.MathUtils.generateUUID(),
|
||||
actionName: "Vehicle Action",
|
||||
actionName: "Action 1",
|
||||
actionType: "travel",
|
||||
material: null,
|
||||
unLoadDuration: 5,
|
||||
loadCapacity: 10,
|
||||
pickUpPoint: null,
|
||||
@@ -254,11 +253,11 @@ async function handleModelLoad(
|
||||
actions: [
|
||||
{
|
||||
actionUuid: THREE.MathUtils.generateUUID(),
|
||||
actionName: "Pick and Place",
|
||||
actionName: "Action 1",
|
||||
actionType: "pickAndPlace",
|
||||
process: {
|
||||
startPoint: [0, 0, 0],
|
||||
endPoint: [0, 0, 0]
|
||||
startPoint: null,
|
||||
endPoint: null
|
||||
},
|
||||
triggers: []
|
||||
}
|
||||
@@ -280,10 +279,10 @@ async function handleModelLoad(
|
||||
rotation: [0, 0, 0],
|
||||
action: {
|
||||
actionUuid: THREE.MathUtils.generateUUID(),
|
||||
actionName: "Process Action",
|
||||
actionName: "Action 1",
|
||||
actionType: "process",
|
||||
processTime: 10,
|
||||
swapMaterial: "material-id",
|
||||
swapMaterial: "Default Material",
|
||||
triggers: []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
|
||||
gltfLoaderWorker.postMessage({ floorItems: data });
|
||||
} else {
|
||||
gltfLoaderWorker.postMessage({ floorItems: [] });
|
||||
loadInitialFloorItems(itemsGroup, setFloorItems, addEvent);
|
||||
loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, renderDistance);
|
||||
updateLoadingProgress(100);
|
||||
}
|
||||
});
|
||||
@@ -94,7 +94,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
|
||||
updateLoadingProgress(progress);
|
||||
|
||||
if (loadedAssets === totalAssets) {
|
||||
loadInitialFloorItems(itemsGroup, setFloorItems, addEvent);
|
||||
loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, renderDistance);
|
||||
updateLoadingProgress(100);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,15 +4,32 @@ import { useEventsStore } from '../../../../../store/simulation/useEventsStore';
|
||||
import useModuleStore from '../../../../../store/useModuleStore';
|
||||
import { TransformControls } from '@react-three/drei';
|
||||
import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys';
|
||||
import { useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore';
|
||||
import { useSelectedEventSphere, useSelectedEventData } from '../../../../../store/simulation/useSimulationStore';
|
||||
|
||||
function PointsCreator() {
|
||||
const { events, updatePoint, getPointByUuid } = useEventsStore();
|
||||
const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const transformRef = useRef<any>(null);
|
||||
const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
|
||||
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
|
||||
const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere } = useSelectedEventSphere();
|
||||
const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedEventSphere) {
|
||||
const eventData = getEventByModelUuid(selectedEventSphere.userData.modelUuid);
|
||||
if (eventData) {
|
||||
setSelectedEventData(
|
||||
eventData,
|
||||
selectedEventSphere.userData.pointUuid
|
||||
);
|
||||
} else {
|
||||
clearSelectedEventData();
|
||||
}
|
||||
} else {
|
||||
clearSelectedEventData();
|
||||
}
|
||||
}, [selectedEventSphere]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
@@ -49,6 +66,7 @@ function PointsCreator() {
|
||||
<group key={i} position={new THREE.Vector3(...event.position)}>
|
||||
{event.points.map((point, j) => (
|
||||
<mesh
|
||||
name='Event-Sphere'
|
||||
uuid={point.uuid}
|
||||
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
|
||||
onClick={(e) => {
|
||||
@@ -73,6 +91,7 @@ function PointsCreator() {
|
||||
return (
|
||||
<group key={i} position={new THREE.Vector3(...event.position)}>
|
||||
<mesh
|
||||
name='Event-Sphere'
|
||||
uuid={event.point.uuid}
|
||||
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
||||
onClick={(e) => {
|
||||
@@ -95,6 +114,7 @@ function PointsCreator() {
|
||||
return (
|
||||
<group key={i} position={new THREE.Vector3(...event.position)}>
|
||||
<mesh
|
||||
name='Event-Sphere'
|
||||
uuid={event.point.uuid}
|
||||
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
||||
onClick={(e) => {
|
||||
@@ -117,6 +137,7 @@ function PointsCreator() {
|
||||
return (
|
||||
<group key={i} position={new THREE.Vector3(...event.position)}>
|
||||
<mesh
|
||||
name='Event-Sphere'
|
||||
uuid={event.point.uuid}
|
||||
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
|
||||
onClick={(e) => {
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import { useThree } from '@react-three/fiber'
|
||||
import { useEffect } from 'react'
|
||||
import { Object3D } from 'three';
|
||||
import { useSubModuleStore } from '../../../../store/useModuleStore';
|
||||
import { useLeftData, useTopData } from '../../../../store/visualization/useZone3DWidgetStore';
|
||||
import { useEventsStore } from '../../../../store/simulation/useEventsStore';
|
||||
import { useProductStore } from '../../../../store/simulation/useProductStore';
|
||||
import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore';
|
||||
import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore';
|
||||
|
||||
function AddOrRemoveEventsInProducts() {
|
||||
const { gl, raycaster, scene } = useThree();
|
||||
const { subModule } = useSubModuleStore();
|
||||
const { setTop } = useTopData();
|
||||
const { setLeft } = useLeftData();
|
||||
const { getIsEventInProduct } = useProductStore();
|
||||
const { getEventByModelUuid } = useEventsStore();
|
||||
const { selectedProduct } = useSelectedProduct();
|
||||
const { selectedAsset, setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
let drag = false;
|
||||
let isRightMouseDown = false;
|
||||
|
||||
const onMouseDown = (evt: MouseEvent) => {
|
||||
if (selectedAsset) {
|
||||
clearSelectedAsset();
|
||||
}
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown = true;
|
||||
drag = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = (evt: MouseEvent) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isRightMouseDown) {
|
||||
drag = true;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRightClick = (evt: MouseEvent) => {
|
||||
if (drag) return;
|
||||
evt.preventDefault();
|
||||
const canvasElement = gl.domElement;
|
||||
if (!canvasElement) return;
|
||||
|
||||
let intersects = raycaster.intersectObjects(scene.children, true);
|
||||
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;
|
||||
|
||||
while (currentObject) {
|
||||
if (currentObject.name === "Scene") {
|
||||
break;
|
||||
}
|
||||
currentObject = currentObject.parent as Object3D;
|
||||
}
|
||||
if (currentObject) {
|
||||
const isInProduct = getIsEventInProduct(selectedProduct.productId, currentObject.uuid);
|
||||
|
||||
if (isInProduct) {
|
||||
const event = getEventByModelUuid(currentObject.uuid);
|
||||
if (event) {
|
||||
setSelectedAsset(event)
|
||||
const canvasRect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = evt.clientX - canvasRect.left;
|
||||
const relativeY = evt.clientY - canvasRect.top;
|
||||
|
||||
setTop(relativeY);
|
||||
setLeft(relativeX);
|
||||
} else {
|
||||
clearSelectedAsset()
|
||||
}
|
||||
} else {
|
||||
const event = getEventByModelUuid(currentObject.uuid);
|
||||
if (event) {
|
||||
setSelectedAsset(event)
|
||||
const canvasRect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = evt.clientX - canvasRect.left;
|
||||
const relativeY = evt.clientY - canvasRect.top;
|
||||
|
||||
setTop(relativeY);
|
||||
setLeft(relativeX);
|
||||
} else {
|
||||
clearSelectedAsset()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
clearSelectedAsset()
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if (subModule === 'simulations') {
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
canvasElement.addEventListener('contextmenu', handleRightClick);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener('contextmenu', handleRightClick);
|
||||
};
|
||||
|
||||
}, [gl, subModule, selectedProduct, selectedAsset]);
|
||||
return (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
|
||||
export default AddOrRemoveEventsInProducts
|
||||
@@ -1,18 +1,41 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { useProductStore } from '../../../store/simulation/useProductStore'
|
||||
import * as THREE from 'three';
|
||||
import { useEffect } from 'react';
|
||||
import { useProductStore } from '../../../store/simulation/useProductStore';
|
||||
import { useSelectedProduct } from '../../../store/simulation/useSimulationStore';
|
||||
import AddOrRemoveEventsInProducts from './events/addOrRemoveEventsInProducts';
|
||||
import { upsertProductOrEventApi } from '../../../services/simulation/UpsertProductOrEventApi';
|
||||
import { getAllProductsApi } from '../../../services/simulation/getallProductsApi';
|
||||
|
||||
function Products() {
|
||||
const { products, addProduct } = useProductStore();
|
||||
const { setSelectedProduct } = useSelectedProduct();
|
||||
|
||||
useEffect(() => {
|
||||
if (products.length === 0) {
|
||||
addProduct('Product 1', THREE.MathUtils.generateUUID());
|
||||
const id = THREE.MathUtils.generateUUID();
|
||||
const name = 'Product 1';
|
||||
addProduct(name, id);
|
||||
// upsertProductOrEventApi({ productName: name, productId: id }).then((data) => {
|
||||
// console.log('data: ', data);
|
||||
// });
|
||||
setSelectedProduct(id, name);
|
||||
}
|
||||
}, [products])
|
||||
|
||||
useEffect(() => {
|
||||
// const email = localStorage.getItem('email')
|
||||
// const organization = (email!.split("@")[1]).split(".")[0];
|
||||
// console.log(organization);
|
||||
// getAllProductsApi(organization).then((data) => {
|
||||
// console.log('data: ', data);
|
||||
// })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<AddOrRemoveEventsInProducts />
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,64 @@
|
||||
import React from 'react'
|
||||
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';
|
||||
|
||||
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, 2, 1.6);
|
||||
const initialCurveRef = useRef<THREE.CatmullRomCurve3 | null>(null);
|
||||
const initialStartPositionRef = useRef<THREE.Vector3 | null>(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);
|
||||
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentPath(path)
|
||||
}, [path])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
}, [currentPath])
|
||||
|
||||
useFrame((_, delta) => {
|
||||
if (!ikSolver || !currentPath || currentPath.length === 0) return;
|
||||
|
||||
const bone = ikSolver.mesh.skeleton.bones.find(
|
||||
(b: any) => b.name === targetBone
|
||||
);
|
||||
if (!bone) return;
|
||||
|
||||
// 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]))
|
||||
);
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
|
||||
|
||||
function RoboticArmAnimator() {
|
||||
return (
|
||||
<></>
|
||||
)
|
||||
|
||||
@@ -1,14 +1,179 @@
|
||||
import React from 'react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import IKInstance from '../ikInstance/ikInstance';
|
||||
import RoboticArmAnimator from '../animator/roboticArmAnimator';
|
||||
import { usePlayButtonStore } 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({ robot }: { robot: ArmBotStatus }) {
|
||||
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const [currentPhase, setCurrentPhase] = useState<(string)>("init");
|
||||
const { scene } = useThree();
|
||||
const targetBone = "Target";
|
||||
const { activeModule } = useModuleStore();
|
||||
const [ikSolver, setIkSolver] = useState<any>(null);
|
||||
const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
|
||||
const { floorItems } = useFloorItems();
|
||||
const groupRef = useRef<any>(null);
|
||||
const [processes, setProcesses] = useState<Process[]>([]);
|
||||
const [armBotCurvePoints, setArmBotCurvePoints] = useState({ start: [], end: [] })
|
||||
const restPosition = new THREE.Vector3(0, 2, 1.6);
|
||||
let armBotCurveRef = useRef<THREE.CatmullRomCurve3 | null>(null)
|
||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||
|
||||
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);
|
||||
if (targetMesh) {
|
||||
targetMesh.visible = activeModule !== "simulation"
|
||||
}
|
||||
}
|
||||
const targetBones = ikSolver?.mesh.skeleton.bones.find(
|
||||
(b: any) => b.name === targetBone
|
||||
);
|
||||
|
||||
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")
|
||||
setCurrentPhase("init-to-rest");
|
||||
if (targetBones) {
|
||||
let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition)
|
||||
if (curve) {
|
||||
setPath(curve.points.map(point => [point.x, point.y, point.z]));
|
||||
}
|
||||
}
|
||||
logStatus(robot.modelUuid, "Starting from init to rest")
|
||||
}
|
||||
//Waiting for trigger.
|
||||
else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "rest" && !robot.currentAction) {
|
||||
console.log("trigger");
|
||||
setTimeout(() => {
|
||||
addCurrentAction(robot.modelUuid, 'action-003');
|
||||
}, 3000);
|
||||
}
|
||||
else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "rest" && robot.currentAction) {
|
||||
if (robot.currentAction) {
|
||||
setArmBotActive(robot.modelUuid, true);
|
||||
setArmBotState(robot.modelUuid, "running");
|
||||
setCurrentPhase("rest-to-start");
|
||||
const startPoint = robot.point.actions[0].process.startPoint;
|
||||
if (startPoint) {
|
||||
let curve = createCurveBetweenTwoPoints(restPosition, new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]));
|
||||
if (curve) {
|
||||
setPath(curve.points.map(point => [point.x, point.y, point.z]));
|
||||
}
|
||||
}
|
||||
}
|
||||
logStatus(robot.modelUuid, "Starting from rest to start")
|
||||
}
|
||||
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) {
|
||||
setPath(curve.points.map(point => [point.x, point.y, point.z]));
|
||||
}
|
||||
}
|
||||
logStatus(robot.modelUuid, "Starting from start to end")
|
||||
}
|
||||
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) {
|
||||
setPath(curve.points.map(point => [point.x, point.y, point.z]));
|
||||
}
|
||||
}
|
||||
logStatus(robot.modelUuid, "Starting from end to rest")
|
||||
}
|
||||
}
|
||||
|
||||
}, [currentPhase, robot, isPlaying, ikSolver])
|
||||
|
||||
|
||||
function createCurveBetweenTwoPoints(p1: any, p2: any) {
|
||||
const mid = new THREE.Vector3().addVectors(p1, p2).multiplyScalar(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") {
|
||||
console.log("Callback triggered: rest");
|
||||
setArmBotActive(robot.modelUuid, false)
|
||||
setArmBotState(robot.modelUuid, "idle")
|
||||
setCurrentPhase("rest");
|
||||
setPath([])
|
||||
}
|
||||
else if (robot.isActive && robot.state == "running" && currentPhase == "rest-to-start") {
|
||||
console.log("Callback triggered: pick.");
|
||||
setArmBotActive(robot.modelUuid, false)
|
||||
setArmBotState(robot.modelUuid, "idle")
|
||||
setCurrentPhase("picking");
|
||||
setPath([])
|
||||
}
|
||||
else if (robot.isActive && robot.state == "running" && currentPhase == "start-to-end") {
|
||||
console.log("Callback triggered: drop.");
|
||||
setArmBotActive(robot.modelUuid, false)
|
||||
setArmBotState(robot.modelUuid, "idle")
|
||||
setCurrentPhase("dropping");
|
||||
setPath([])
|
||||
}
|
||||
else if (robot.isActive && robot.state == "running" && currentPhase == "end-to-rest") {
|
||||
console.log("Callback triggered: rest, cycle completed.");
|
||||
setArmBotActive(robot.modelUuid, false)
|
||||
setArmBotState(robot.modelUuid, "idle")
|
||||
setCurrentPhase("rest");
|
||||
setPath([])
|
||||
removeCurrentAction(robot.modelUuid)
|
||||
}
|
||||
}
|
||||
const logStatus = (id: string, status: string) => {
|
||||
console.log(id +","+ status);
|
||||
}
|
||||
|
||||
function RoboticArmInstance() {
|
||||
return (
|
||||
<>
|
||||
|
||||
<IKInstance />
|
||||
|
||||
<RoboticArmAnimator />
|
||||
<IKInstance modelUrl={armModel} setIkSolver={setIkSolver} ikSolver={ikSolver} robot={robot} groupRef={groupRef} processes={processes}
|
||||
setArmBotCurvePoints={setArmBotCurvePoints} />
|
||||
<RoboticArmAnimator armUuid={robot?.modelUuid} HandleCallback={HandleCallback}
|
||||
currentPhase={currentPhase} targetBone={targetBone} ikSolver={ikSolver} robot={robot}
|
||||
logStatus={logStatus} groupRef={groupRef} processes={processes} armBotCurveRef={armBotCurveRef} path={path} />
|
||||
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,8 +1,87 @@
|
||||
import React from 'react'
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
||||
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 { CCDIKSolver, CCDIKHelper, } from "three/examples/jsm/animation/CCDIKSolver";
|
||||
type IKInstanceProps = {
|
||||
modelUrl: string;
|
||||
ikSolver: any;
|
||||
setIkSolver: any
|
||||
robot: any;
|
||||
groupRef: React.RefObject<THREE.Group>;
|
||||
processes: any;
|
||||
setArmBotCurvePoints: any
|
||||
};
|
||||
function IKInstance({ modelUrl, setIkSolver, ikSolver, robot, groupRef, processes, setArmBotCurvePoints }: 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/");
|
||||
loader.setDRACOLoader(draco);
|
||||
});
|
||||
const cloned = useMemo(() => clone(gltf?.scene), [gltf]);
|
||||
const targetBoneName = "Target";
|
||||
const skinnedMeshName = "link_0";
|
||||
useEffect(() => {
|
||||
if (!gltf) return;
|
||||
const OOI: any = {};
|
||||
cloned.traverse((n: any) => {
|
||||
if (n.name === targetBoneName) OOI.Target_Bone = n;
|
||||
if (n.name === skinnedMeshName) OOI.Skinned_Mesh = n;
|
||||
});
|
||||
if (!OOI.Target_Bone || !OOI.Skinned_Mesh) return;
|
||||
const iks = [
|
||||
{
|
||||
target: 7,
|
||||
effector: 6,
|
||||
links: [
|
||||
{
|
||||
index: 5,
|
||||
enabled: true,
|
||||
rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0),
|
||||
rotationMax: new THREE.Vector3(Math.PI / 2, 0, 0),
|
||||
},
|
||||
{
|
||||
index: 4,
|
||||
enabled: true,
|
||||
rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0),
|
||||
rotationMax: new THREE.Vector3(0, 0, 0),
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
enabled: true,
|
||||
rotationMin: new THREE.Vector3(0, 0, 0),
|
||||
rotationMax: new THREE.Vector3(2, 0, 0),
|
||||
},
|
||||
{ index: 1, enabled: true, limitation: new THREE.Vector3(0, 1, 0) },
|
||||
{ index: 0, enabled: false, limitation: new THREE.Vector3(0, 0, 0) },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const solver = new CCDIKSolver(OOI.Skinned_Mesh, iks);
|
||||
setIkSolver(solver);
|
||||
|
||||
const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05);
|
||||
|
||||
// scene.add(groupRef.current)
|
||||
|
||||
|
||||
}, [gltf]);
|
||||
|
||||
function IKInstance() {
|
||||
return (
|
||||
<></>
|
||||
<>
|
||||
<group ref={groupRef} position={robot.position}>
|
||||
<primitive
|
||||
uuid={"ArmBot-X200"}
|
||||
object={cloned}
|
||||
scale={[1, 1, 1]}
|
||||
name={`arm-bot11`}
|
||||
/>
|
||||
</group>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from 'react'
|
||||
import IKInstance from './ikInstance/ikInstance';
|
||||
|
||||
function IkInstances() {
|
||||
return (
|
||||
<>
|
||||
|
||||
<IKInstance />
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default IkInstances;
|
||||
@@ -1,11 +1,15 @@
|
||||
import React from 'react'
|
||||
import RoboticArmInstance from './armInstance/roboticArmInstance';
|
||||
import { useArmBotStore } from '../../../../store/simulation/useArmBotStore';
|
||||
|
||||
function RoboticArmInstances() {
|
||||
const { armBots } = useArmBotStore();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<RoboticArmInstance />
|
||||
{armBots?.map((robot: ArmBotStatus) => (
|
||||
<RoboticArmInstance key={robot.modelUuid} robot={robot} />
|
||||
))}
|
||||
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,14 +1,166 @@
|
||||
import React from 'react'
|
||||
import RoboticArmInstances from './instances/roboticArmInstances';
|
||||
import { useEffect } from "react";
|
||||
import RoboticArmInstances from "./instances/roboticArmInstances";
|
||||
import { useArmBotStore } from "../../../store/simulation/useArmBotStore";
|
||||
import { useFloorItems } from "../../../store/store";
|
||||
|
||||
function RoboticArm() {
|
||||
const { armBots, addArmBot, removeArmBot } = useArmBotStore();
|
||||
const { floorItems } = useFloorItems();
|
||||
|
||||
const armBotStatusSample: RoboticArmEventSchema[] = [
|
||||
{
|
||||
state: "idle",
|
||||
modelUuid: "armbot-xyz-001",
|
||||
modelName: "ArmBot-X200",
|
||||
position: [91.94347308985614, 0, 6.742905194869091],
|
||||
rotation: [0, 0, 0],
|
||||
type: "roboticArm",
|
||||
speed: 1.5,
|
||||
point: {
|
||||
uuid: "point-123",
|
||||
position: [0, 1.5, 0],
|
||||
rotation: [0, 0, 0],
|
||||
actions: [
|
||||
{
|
||||
actionUuid: "action-003",
|
||||
actionName: "Pick Component",
|
||||
actionType: "pickAndPlace",
|
||||
process: {
|
||||
startPoint: [5.52543010919071, 1, -8.433681161200905],
|
||||
endPoint: [10.52543010919071, 1, -12.433681161200905],
|
||||
},
|
||||
triggers: [
|
||||
{
|
||||
triggerUuid: "trigger-001",
|
||||
triggerName: "Start Trigger",
|
||||
triggerType: "onStart",
|
||||
delay: 0,
|
||||
triggeredAsset: {
|
||||
triggeredModel: {
|
||||
modelName: "Conveyor A1",
|
||||
modelUuid: "conveyor-01",
|
||||
},
|
||||
triggeredPoint: {
|
||||
pointName: "Start Point",
|
||||
pointUuid: "conveyor-01-point-001",
|
||||
},
|
||||
triggeredAction: {
|
||||
actionName: "Move Forward",
|
||||
actionUuid: "conveyor-action-01",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
triggerUuid: "trigger-002",
|
||||
triggerName: "Complete Trigger",
|
||||
triggerType: "onComplete",
|
||||
delay: 0,
|
||||
triggeredAsset: {
|
||||
triggeredModel: {
|
||||
modelName: "StaticMachine B2",
|
||||
modelUuid: "machine-02",
|
||||
},
|
||||
triggeredPoint: {
|
||||
pointName: "Receive Point",
|
||||
pointUuid: "machine-02-point-001",
|
||||
},
|
||||
triggeredAction: {
|
||||
actionName: "Process Part",
|
||||
actionUuid: "machine-action-01",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
state: "idle",
|
||||
modelUuid: "armbot-xyz-002",
|
||||
modelName: "ArmBot-X200",
|
||||
position: [95.94347308985614, 0, 6.742905194869091],
|
||||
rotation: [0, 0, 0],
|
||||
type: "roboticArm",
|
||||
speed: 1.5,
|
||||
point: {
|
||||
uuid: "point-123",
|
||||
position: [0, 1.5, 0],
|
||||
rotation: [0, 0, 0],
|
||||
actions: [
|
||||
{
|
||||
actionUuid: "action-001",
|
||||
actionName: "Pick Component",
|
||||
actionType: "pickAndPlace",
|
||||
process: {
|
||||
startPoint: [2.52543010919071, 0, 8.433681161200905],
|
||||
endPoint: [95.3438373267953, 0, 9.0279187421610025],
|
||||
},
|
||||
triggers: [
|
||||
{
|
||||
triggerUuid: "trigger-001",
|
||||
triggerName: "Start Trigger",
|
||||
triggerType: "onStart",
|
||||
delay: 0,
|
||||
triggeredAsset: {
|
||||
triggeredModel: {
|
||||
modelName: "Conveyor A1",
|
||||
modelUuid: "conveyor-01",
|
||||
},
|
||||
triggeredPoint: {
|
||||
pointName: "Start Point",
|
||||
pointUuid: "conveyor-01-point-001",
|
||||
},
|
||||
triggeredAction: {
|
||||
actionName: "Move Forward",
|
||||
actionUuid: "conveyor-action-01",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
triggerUuid: "trigger-002",
|
||||
triggerName: "Complete Trigger",
|
||||
triggerType: "onComplete",
|
||||
delay: 0,
|
||||
triggeredAsset: {
|
||||
triggeredModel: {
|
||||
modelName: "StaticMachine B2",
|
||||
modelUuid: "machine-02",
|
||||
},
|
||||
triggeredPoint: {
|
||||
pointName: "Receive Point",
|
||||
pointUuid: "machine-02-point-001",
|
||||
},
|
||||
triggeredAction: {
|
||||
actionName: "Process Part",
|
||||
actionUuid: "machine-action-01",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
removeArmBot(armBotStatusSample[0].modelUuid);
|
||||
addArmBot('123', armBotStatusSample[0]);
|
||||
// addArmBot('123', armBotStatusSample[1]);
|
||||
// addCurrentAction('armbot-xyz-001', 'action-001');
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
//
|
||||
}, [armBots]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<RoboticArmInstances />
|
||||
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default RoboticArm;
|
||||
export default RoboticArm;
|
||||
|
||||
@@ -10,13 +10,16 @@ import Machine from './machine/machine';
|
||||
import StorageUnit from './storageUnit/storageUnit';
|
||||
import Simulator from './simulator/simulator';
|
||||
import Products from './products/products';
|
||||
import Trigger from './triggers/trigger';
|
||||
import useModuleStore from '../../store/useModuleStore';
|
||||
|
||||
function Simulation() {
|
||||
const { activeModule } = useModuleStore();
|
||||
const { events } = useEventsStore();
|
||||
const { products } = useProductStore();
|
||||
|
||||
useEffect(() => {
|
||||
console.log('events: ', events);
|
||||
// console.log('events: ', events);
|
||||
}, [events])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -26,26 +29,36 @@ function Simulation() {
|
||||
return (
|
||||
<>
|
||||
|
||||
<Points />
|
||||
{activeModule === 'simulation' &&
|
||||
|
||||
<Products />
|
||||
<>
|
||||
|
||||
<Materials />
|
||||
<Points />
|
||||
|
||||
<Conveyor />
|
||||
<Products />
|
||||
|
||||
<Vehicles />
|
||||
<Materials />
|
||||
|
||||
<RoboticArm />
|
||||
<Trigger />
|
||||
|
||||
<Machine />
|
||||
<Conveyor />
|
||||
|
||||
<StorageUnit />
|
||||
<Vehicles />
|
||||
|
||||
<Simulator />
|
||||
<RoboticArm />
|
||||
|
||||
<Machine />
|
||||
|
||||
<StorageUnit />
|
||||
|
||||
<Simulator />
|
||||
|
||||
</>
|
||||
|
||||
}
|
||||
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Simulation
|
||||
export default Simulation;
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import React from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { useProductStore } from '../../../store/simulation/useProductStore'
|
||||
|
||||
function Simulator() {
|
||||
const { products } = useProductStore();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('products: ', products);
|
||||
}, [products])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import { useEffect } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { useSubModuleStore } from "../../../../store/useModuleStore";
|
||||
import { useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
|
||||
import { useProductStore } from "../../../../store/simulation/useProductStore";
|
||||
import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
|
||||
|
||||
function TriggerConnector() {
|
||||
const { gl, raycaster, scene } = useThree();
|
||||
const { subModule } = useSubModuleStore();
|
||||
const { getPointByUuid, getIsEventInProduct } = useProductStore();
|
||||
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
|
||||
const { selectedProduct } = useSelectedProduct();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
let drag = false;
|
||||
let isRightMouseDown = false;
|
||||
|
||||
const onMouseDown = (evt: MouseEvent) => {
|
||||
if (selectedAsset) {
|
||||
clearSelectedAsset();
|
||||
}
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown = true;
|
||||
drag = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = (evt: MouseEvent) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
const onMouseMove = () => {
|
||||
if (isRightMouseDown) {
|
||||
drag = true;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRightClick = (evt: MouseEvent) => {
|
||||
if (drag) return;
|
||||
evt.preventDefault();
|
||||
const canvasElement = gl.domElement;
|
||||
if (!canvasElement) return;
|
||||
|
||||
let intersects = raycaster.intersectObjects(scene.children, true);
|
||||
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;
|
||||
|
||||
if (currentObject && currentObject.name === 'Event-Sphere') {
|
||||
|
||||
const isInProduct = getIsEventInProduct(
|
||||
selectedProduct.productId,
|
||||
currentObject.userData.modelUuid
|
||||
);
|
||||
|
||||
// You left Here
|
||||
|
||||
if (isInProduct) {
|
||||
|
||||
const event = getPointByUuid(
|
||||
selectedProduct.productId,
|
||||
currentObject.userData.modelUuid,
|
||||
currentObject.userData.pointUuid
|
||||
);
|
||||
console.log('event: ', event);
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
if (subModule === 'simulations') {
|
||||
canvasElement.addEventListener("mousedown", onMouseDown);
|
||||
canvasElement.addEventListener("mouseup", onMouseUp);
|
||||
canvasElement.addEventListener("mousemove", onMouseMove);
|
||||
canvasElement.addEventListener('contextmenu', handleRightClick);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("mousedown", onMouseDown);
|
||||
canvasElement.removeEventListener("mouseup", onMouseUp);
|
||||
canvasElement.removeEventListener("mousemove", onMouseMove);
|
||||
canvasElement.removeEventListener('contextmenu', handleRightClick);
|
||||
};
|
||||
|
||||
}, [gl, subModule]);
|
||||
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default TriggerConnector
|
||||
14
app/src/modules/simulation/triggers/trigger.tsx
Normal file
14
app/src/modules/simulation/triggers/trigger.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from 'react'
|
||||
import TriggerConnector from './connector/triggerConnector'
|
||||
|
||||
function Trigger() {
|
||||
return (
|
||||
<>
|
||||
|
||||
<TriggerConnector />
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Trigger
|
||||
@@ -1,62 +1,172 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import { useFloorItems } from '../../../../../store/store';
|
||||
import * as THREE from 'three';
|
||||
import { Line } from '@react-three/drei';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
|
||||
|
||||
interface VehicleAnimatorProps {
|
||||
path: [number, number, number][];
|
||||
handleCallBack: () => void;
|
||||
currentPhase: string;
|
||||
agvUuid: number
|
||||
path: [number, number, number][];
|
||||
handleCallBack: () => void;
|
||||
currentPhase: string;
|
||||
agvUuid: number;
|
||||
agvDetail: any;
|
||||
}
|
||||
|
||||
function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail }: VehicleAnimatorProps) {
|
||||
const { decrementVehicleLoad, vehicles } = useVehicleStore();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const { isReset } = useResetButtonStore();
|
||||
const [restRotation, setRestingRotation] = useState<boolean>(true);
|
||||
const [progress, setProgress] = useState<number>(0);
|
||||
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
|
||||
const { scene } = useThree();
|
||||
const progressRef = useRef<number>(0);
|
||||
const movingForward = useRef<boolean>(true);
|
||||
const completedRef = useRef<boolean>(false);
|
||||
let startTime: number;
|
||||
let pausedTime: number;
|
||||
let fixedInterval: number;
|
||||
|
||||
function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid }: VehicleAnimatorProps) {
|
||||
const [progress, setProgress] = useState<number>(0)
|
||||
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
|
||||
const { scene } = useThree();
|
||||
useEffect(() => {
|
||||
if (currentPhase === 'stationed-pickup' && path.length > 0) {
|
||||
setCurrentPath(path);
|
||||
} else if (currentPhase === 'pickup-drop' && path.length > 0) {
|
||||
setCurrentPath(path);
|
||||
} else if (currentPhase === 'drop-pickup' && path.length > 0) {
|
||||
setCurrentPath(path);
|
||||
}
|
||||
}, [currentPhase, path]);
|
||||
|
||||
useEffect(() => {
|
||||
useEffect(() => {
|
||||
setProgress(0);
|
||||
completedRef.current = false;
|
||||
}, [currentPath]);
|
||||
|
||||
if (currentPhase === 'stationed-pickup' && path.length > 0) {
|
||||
setCurrentPath(path);
|
||||
useFrame((_, delta) => {
|
||||
const object = scene.getObjectByProperty('uuid', agvUuid);
|
||||
if (!object || currentPath.length < 2) return;
|
||||
if (isPaused) return;
|
||||
|
||||
let totalDistance = 0;
|
||||
const distances = [];
|
||||
|
||||
for (let i = 0; i < currentPath.length - 1; i++) {
|
||||
const start = new THREE.Vector3(...currentPath[i]);
|
||||
const end = new THREE.Vector3(...currentPath[i + 1]);
|
||||
const segmentDistance = start.distanceTo(end);
|
||||
distances.push(segmentDistance);
|
||||
totalDistance += segmentDistance;
|
||||
}
|
||||
|
||||
let coveredDistance = progressRef.current;
|
||||
let accumulatedDistance = 0;
|
||||
let index = 0;
|
||||
|
||||
while (
|
||||
index < distances.length &&
|
||||
coveredDistance > accumulatedDistance + distances[index]
|
||||
) {
|
||||
accumulatedDistance += distances[index];
|
||||
index++;
|
||||
}
|
||||
|
||||
if (index < distances.length) {
|
||||
const start = new THREE.Vector3(...currentPath[index]);
|
||||
const end = new THREE.Vector3(...currentPath[index + 1]);
|
||||
const segmentDistance = distances[index];
|
||||
|
||||
const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
|
||||
const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);
|
||||
const rotationSpeed = 2.0;
|
||||
const currentAngle = object.rotation.y;
|
||||
|
||||
let angleDifference = targetAngle - currentAngle;
|
||||
if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
|
||||
if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI;
|
||||
|
||||
const maxRotationStep = rotationSpeed * delta;
|
||||
object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep);
|
||||
|
||||
const isAligned = Math.abs(angleDifference) < 0.01;
|
||||
|
||||
if (isAligned) {
|
||||
progressRef.current += delta * (speed * agvDetail.speed);
|
||||
coveredDistance = progressRef.current;
|
||||
|
||||
const t = (coveredDistance - accumulatedDistance) / segmentDistance;
|
||||
const position = start.clone().lerp(end, t);
|
||||
object.position.copy(position);
|
||||
}
|
||||
}
|
||||
|
||||
if (progressRef.current >= totalDistance) {
|
||||
if (restRotation) {
|
||||
const targetQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0));
|
||||
object.quaternion.slerp(targetQuaternion, delta * 2);
|
||||
const angleDiff = object.quaternion.angleTo(targetQuaternion);
|
||||
if (angleDiff < 0.01) {
|
||||
let objectRotation = agvDetail.point.rotation
|
||||
object.rotation.set(objectRotation[0], objectRotation[1], objectRotation[2]);
|
||||
setRestingRotation(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}, [currentPhase, path])
|
||||
if (progressRef.current >= totalDistance) {
|
||||
setRestingRotation(true);
|
||||
progressRef.current = 0;
|
||||
movingForward.current = !movingForward.current;
|
||||
setCurrentPath([]);
|
||||
handleCallBack();
|
||||
if (currentPhase === 'pickup-drop') {
|
||||
requestAnimationFrame(firstFrame);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
useFrame((_, delta) => {
|
||||
if (!path || path.length < 2) return;
|
||||
function firstFrame() {
|
||||
const unLoadDuration = agvDetail.point.action.unLoadDuration;
|
||||
const droppedMaterial = agvDetail.currentLoad;
|
||||
fixedInterval = (unLoadDuration / droppedMaterial) * 1000;
|
||||
startTime = performance.now();
|
||||
step(droppedMaterial);
|
||||
}
|
||||
|
||||
const object = scene.getObjectByProperty("uuid", agvUuid)
|
||||
if (!object) return;
|
||||
function step(droppedMaterial: number) {
|
||||
const elapsedTime = performance.now() - startTime;
|
||||
|
||||
setProgress(prev => {
|
||||
const next = prev + delta * 0.1; // speed
|
||||
return next >= 1 ? 1 : next;
|
||||
});
|
||||
if (elapsedTime >= fixedInterval) {
|
||||
console.log('fixedInterval: ', fixedInterval);
|
||||
console.log('elapsedTime: ', elapsedTime);
|
||||
let droppedMat = droppedMaterial - 1;
|
||||
decrementVehicleLoad(agvDetail.modelUuid, 1);
|
||||
if (droppedMat === 0) return;
|
||||
startTime = performance.now();
|
||||
requestAnimationFrame(() => step(droppedMat));
|
||||
} else {
|
||||
requestAnimationFrame(() => step(droppedMaterial));
|
||||
}
|
||||
}
|
||||
|
||||
const totalSegments = path.length - 1;
|
||||
const segmentIndex = Math.floor(progress * totalSegments);
|
||||
const t = progress * totalSegments - segmentIndex;
|
||||
|
||||
const start = path[segmentIndex];
|
||||
const end = path[segmentIndex + 1] || start;
|
||||
|
||||
// Directly set position without creating a new Vector3
|
||||
object.position.x = start[0] + (end[0] - start[0]) * t;
|
||||
object.position.y = start[1] + (end[1] - start[1]) * t;
|
||||
object.position.z = start[2] + (end[2] - start[2]) * t;
|
||||
});
|
||||
// useFrame(() => {
|
||||
// if (currentPath.length === 0) return;
|
||||
// const object = scene.getObjectByProperty("uuid", agvUuid);
|
||||
// if (!object) return;
|
||||
|
||||
|
||||
|
||||
// })
|
||||
return (
|
||||
return (
|
||||
<>
|
||||
{currentPath.length > 0 && (
|
||||
<>
|
||||
<Line points={currentPath} color="blue" lineWidth={3} />
|
||||
{currentPath.map((point, index) => (
|
||||
<mesh key={index} position={point}>
|
||||
<sphereGeometry args={[0.1, 16, 16]} />
|
||||
<meshStandardMaterial color="red" />
|
||||
</mesh>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default VehicleAnimator
|
||||
export default VehicleAnimator;
|
||||
@@ -1,66 +1,123 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import VehicleAnimator from '../animator/vehicleAnimator'
|
||||
import * as THREE from "three";
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import VehicleAnimator from '../animator/vehicleAnimator';
|
||||
import * as THREE from 'three';
|
||||
import { NavMeshQuery } from '@recast-navigation/core';
|
||||
import { useNavMesh } from '../../../../../store/store';
|
||||
import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
|
||||
|
||||
function VehicleInstance({ agvDetails }: any) {
|
||||
const { navMesh } = useNavMesh();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { setVehicleActive, setVehicleState } = useVehicleStore();
|
||||
const [currentPhase, setCurrentPhase] = useState<(string)>("stationed");
|
||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||
function VehicleInstance({ agvDetail }: any) {
|
||||
const { navMesh } = useNavMesh();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { vehicles, setVehicleActive, setVehicleState, incrementVehicleLoad } = useVehicleStore();
|
||||
const [currentPhase, setCurrentPhase] = useState<string>('stationed');
|
||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||
|
||||
const computePath = useCallback((start: any, end: any) => {
|
||||
const computePath = useCallback(
|
||||
(start: any, end: any) => {
|
||||
try {
|
||||
const navMeshQuery = new NavMeshQuery(navMesh);
|
||||
const { path: segmentPath } = navMeshQuery.computePath(start, end);
|
||||
return (
|
||||
segmentPath?.map(({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]) || []
|
||||
);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
[navMesh]
|
||||
);
|
||||
|
||||
function vehicleStatus(modelid: string, status: string) {
|
||||
// console.log(`AGV ${modelid}: ${status}`);
|
||||
}
|
||||
|
||||
try {
|
||||
const navMeshQuery = new NavMeshQuery(navMesh);
|
||||
const { path: segmentPath } = navMeshQuery.computePath(start, end);
|
||||
return (
|
||||
segmentPath?.map(
|
||||
({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]
|
||||
) || []
|
||||
);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}, [navMesh]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
|
||||
if (isPlaying) {
|
||||
if (!agvDetails.isActive && agvDetails.state == "idle" && currentPhase == "stationed") {
|
||||
const toPickupPath = computePath(new THREE.Vector3(agvDetails.position[0], agvDetails.position[1], agvDetails.position[2]), agvDetails.point.action.pickUpPoint);
|
||||
setPath(toPickupPath)
|
||||
setVehicleActive(agvDetails.modelUuid, true)
|
||||
setVehicleState(agvDetails.modelUuid, "running")
|
||||
setCurrentPhase("stationed-pickup")
|
||||
//
|
||||
}
|
||||
}
|
||||
}, [agvDetails, currentPhase, path, isPlaying])
|
||||
|
||||
function handleCallBack() {
|
||||
if (currentPhase === "stationed-pickup") {
|
||||
setVehicleActive(agvDetails.modelUuid, false)
|
||||
setVehicleState(agvDetails.modelUuid, "idle")
|
||||
setCurrentPhase("picking")
|
||||
setPath([])
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'stationed') {
|
||||
const toPickupPath = computePath(
|
||||
new THREE.Vector3(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]),
|
||||
agvDetail.point.action.pickUpPoint
|
||||
);
|
||||
setPath(toPickupPath);
|
||||
setVehicleActive(agvDetail.modelUuid, true);
|
||||
setVehicleState(agvDetail.modelUuid, 'running');
|
||||
setCurrentPhase('stationed-pickup');
|
||||
vehicleStatus(agvDetail.modelUuid, 'Started from station, heading to pickup');
|
||||
return;
|
||||
} else if (
|
||||
!agvDetail.isActive &&
|
||||
agvDetail.state === 'idle' &&
|
||||
currentPhase === 'picking'
|
||||
) {
|
||||
|
||||
setTimeout(() => {
|
||||
incrementVehicleLoad(agvDetail.modelUuid, 2);
|
||||
}, 5000);
|
||||
|
||||
if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) {
|
||||
const toDrop = computePath(
|
||||
agvDetail.point.action.pickUpPoint,
|
||||
agvDetail.point.action.unLoadPoint
|
||||
);
|
||||
setPath(toDrop);
|
||||
setVehicleActive(agvDetail.modelUuid, true);
|
||||
setVehicleState(agvDetail.modelUuid, 'running');
|
||||
setCurrentPhase('pickup-drop');
|
||||
vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point');
|
||||
}
|
||||
} else if (
|
||||
!agvDetail.isActive &&
|
||||
agvDetail.state === 'idle' &&
|
||||
currentPhase === 'dropping' &&
|
||||
agvDetail.currentLoad === 0
|
||||
) {
|
||||
const dropToPickup = computePath(
|
||||
agvDetail.point.action.unLoadPoint,
|
||||
agvDetail.point.action.pickUpPoint
|
||||
);
|
||||
setPath(dropToPickup);
|
||||
setVehicleActive(agvDetail.modelUuid, true);
|
||||
setVehicleState(agvDetail.modelUuid, 'running');
|
||||
setCurrentPhase('drop-pickup');
|
||||
vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point');
|
||||
}
|
||||
}
|
||||
}, [vehicles, currentPhase, path, isPlaying]);
|
||||
|
||||
function handleCallBack() {
|
||||
if (currentPhase === 'stationed-pickup') {
|
||||
setVehicleActive(agvDetail.modelUuid, false);
|
||||
setVehicleState(agvDetail.modelUuid, 'idle');
|
||||
setCurrentPhase('picking');
|
||||
vehicleStatus(agvDetail.modelUuid, 'Reached pickup point, waiting for material');
|
||||
setPath([]);
|
||||
} else if (currentPhase === 'pickup-drop') {
|
||||
setVehicleActive(agvDetail.modelUuid, false);
|
||||
setVehicleState(agvDetail.modelUuid, 'idle');
|
||||
setCurrentPhase('dropping');
|
||||
vehicleStatus(agvDetail.modelUuid, 'Reached drop point');
|
||||
setPath([]);
|
||||
} else if (currentPhase === 'drop-pickup') {
|
||||
setVehicleActive(agvDetail.modelUuid, false);
|
||||
setVehicleState(agvDetail.modelUuid, 'idle');
|
||||
setCurrentPhase('picking');
|
||||
setPath([]);
|
||||
vehicleStatus(agvDetail.modelUuid, 'Reached pickup point again, cycle complete');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<VehicleAnimator path={path} handleCallBack={handleCallBack} currentPhase={currentPhase} agvUuid={agvDetails?.modelUuid} />
|
||||
|
||||
</>
|
||||
)
|
||||
return (
|
||||
<>
|
||||
<VehicleAnimator
|
||||
path={path}
|
||||
handleCallBack={handleCallBack}
|
||||
currentPhase={currentPhase}
|
||||
agvUuid={agvDetail?.modelUuid}
|
||||
agvDetail={agvDetail}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default VehicleInstance
|
||||
export default VehicleInstance;
|
||||
@@ -1,15 +1,14 @@
|
||||
import React from 'react'
|
||||
import VehicleInstance from './instance/vehicleInstance'
|
||||
import { useVehicleStore } from '../../../../store/simulation/useVehicleStore';
|
||||
import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'
|
||||
|
||||
function VehicleInstances() {
|
||||
const { vehicles } = useVehicleStore();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
{vehicles.map((val: any, i: any) =>
|
||||
<VehicleInstance agvDetails={val} key={i} />
|
||||
<VehicleInstance agvDetail={val} key={i} />
|
||||
)}
|
||||
|
||||
</>
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function PolygonGenerator({
|
||||
useEffect(() => {
|
||||
let allLines = arrayLinesToObject(lines.current);
|
||||
const wallLines = allLines?.filter((line) => line?.type === "WallLine");
|
||||
const aisleLines = allLines?.filter((line) => line?.type === "AisleLine");
|
||||
const aisleLines = allLines?.filter((line) => line?.type === "AisleLine")
|
||||
|
||||
const wallPoints = wallLines
|
||||
.map((pair) => pair?.line.map((vals) => vals.position))
|
||||
@@ -39,9 +39,10 @@ export default function PolygonGenerator({
|
||||
);
|
||||
|
||||
const polygons = turf.polygonize(turf.featureCollection(lineFeatures));
|
||||
|
||||
renderWallGeometry(wallPoints);
|
||||
|
||||
if (polygons.features.length > 1) {
|
||||
if (polygons.features.length > 0) {
|
||||
polygons.features.forEach((feature) => {
|
||||
if (feature.geometry.type === "Polygon") {
|
||||
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import VehicleInstances from './instances/vehicleInstances';
|
||||
|
||||
import { useVehicleStore } from '../../../store/simulation/useVehicleStore';
|
||||
import { useFloorItems } from '../../../store/store';
|
||||
|
||||
function Vehicles() {
|
||||
|
||||
const { vehicles, addVehicle } = useVehicleStore();
|
||||
|
||||
const { floorItems } = useFloorItems();
|
||||
|
||||
const vehicleStatusSample: VehicleEventSchema[] = [
|
||||
{
|
||||
modelUuid: "veh-123",
|
||||
modelName: "Autonomous Truck A1",
|
||||
position: [10, 0, 5],
|
||||
modelUuid: "9356f710-4727-4b50-bdb2-9c1e747ecc74",
|
||||
modelName: "AGV",
|
||||
position: [97.9252965204558, 0, 37.96138815638661],
|
||||
rotation: [0, 0, 0],
|
||||
state: "idle",
|
||||
type: "vehicle",
|
||||
@@ -24,11 +26,10 @@ function Vehicles() {
|
||||
actionUuid: "action-456",
|
||||
actionName: "Deliver to Zone A",
|
||||
actionType: "travel",
|
||||
material: "crate",
|
||||
unLoadDuration: 15,
|
||||
loadCapacity: 5,
|
||||
pickUpPoint: { x: 5, y: 0, z: 3 },
|
||||
unLoadPoint: { x: 20, y: 0, z: 10 },
|
||||
unLoadDuration: 10,
|
||||
loadCapacity: 2,
|
||||
pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 },
|
||||
unLoadPoint: { x: 105.71483985219794, y: 0, z: 28.66321267938962 },
|
||||
triggers: [
|
||||
{
|
||||
triggerUuid: "trig-001",
|
||||
@@ -53,9 +54,51 @@ function Vehicles() {
|
||||
}
|
||||
},
|
||||
{
|
||||
modelUuid: "veh-123",
|
||||
modelName: "Autonomous Truck A1",
|
||||
position: [10, 0, 5],
|
||||
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,
|
||||
pickUpPoint: { x: 90, y: 0, z: 28 },
|
||||
unLoadPoint: { x: 20, y: 0, z: 10 },
|
||||
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",
|
||||
@@ -68,10 +111,9 @@ function Vehicles() {
|
||||
actionUuid: "action-456",
|
||||
actionName: "Deliver to Zone A",
|
||||
actionType: "travel",
|
||||
material: "crate",
|
||||
unLoadDuration: 15,
|
||||
loadCapacity: 5,
|
||||
pickUpPoint: { x: 5, y: 0, z: 3 },
|
||||
pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 },
|
||||
unLoadPoint: { x: 20, y: 0, z: 10 },
|
||||
triggers: [
|
||||
{
|
||||
@@ -101,19 +143,18 @@ function Vehicles() {
|
||||
|
||||
useEffect(() => {
|
||||
addVehicle('123', vehicleStatusSample[0]);
|
||||
addVehicle('123', vehicleStatusSample[1]);
|
||||
// addVehicle('123', vehicleStatusSample[1]);
|
||||
// addVehicle('123', vehicleStatusSample[2]);
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('vehicles: ', vehicles);
|
||||
console.log('vehicles: ', vehicles);
|
||||
}, [vehicles])
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<VehicleInstances />
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ const RealTimeVisulization: React.FC = () => {
|
||||
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
|
||||
|
||||
const { setRightSelect } = useRightSelected();
|
||||
const { editWidgetOptions } = useEditWidgetOptionsStore();
|
||||
const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore();
|
||||
const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
|
||||
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
|
||||
const { setFloatingWidget } = useFloatingWidget();
|
||||
@@ -354,6 +354,10 @@ const RealTimeVisulization: React.FC = () => {
|
||||
"RotateY",
|
||||
"Delete",
|
||||
]}
|
||||
onClick={(e) => {
|
||||
setRightSelect(e);
|
||||
setEditWidgetOptions(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user