Merge remote-tracking branch 'origin/simulation-agv-v2' into v2
This commit is contained in:
commit
9233bb97c8
|
@ -1,7 +1,7 @@
|
|||
import * as THREE from "three";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store";
|
||||
import { useFloorItems, useSelectedAssets, useSocketStore, useStartSimulation, useToggleView } from "../../../../store/store";
|
||||
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
|
||||
import { toast } from "react-toastify";
|
||||
import * as Types from "../../../../types/world/worldTypes";
|
||||
|
@ -10,6 +10,7 @@ import { useEventsStore } from "../../../../store/simulation/useEventsStore";
|
|||
import { useProductStore } from "../../../../store/simulation/useProductStore";
|
||||
import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
|
||||
import { snapControls } from "../../../../utils/handleSnap";
|
||||
|
||||
function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) {
|
||||
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
|
||||
|
@ -21,6 +22,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
|
|||
const { floorItems, setFloorItems } = useFloorItems();
|
||||
const { socket } = useSocketStore();
|
||||
const itemsData = useRef<Types.FloorItems>([]);
|
||||
const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("")
|
||||
|
||||
const email = localStorage.getItem('email')
|
||||
const organization = (email!.split("@")[1]).split(".")[0];
|
||||
|
@ -54,6 +56,15 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
|
|||
const onPointerMove = () => {
|
||||
isMoving = true;
|
||||
};
|
||||
const onKeyUp = (event: KeyboardEvent) => {
|
||||
// When any modifier is released, reset snap
|
||||
const isModifierKey =
|
||||
event.key === "Control" || event.key === "Shift";
|
||||
|
||||
if (isModifierKey) {
|
||||
setKeyEvent("");
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerUp = (event: PointerEvent) => {
|
||||
if (!isMoving && movedObjects.length > 0 && event.button === 0) {
|
||||
|
@ -75,18 +86,28 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
|
|||
setMovedObjects([]);
|
||||
itemsData.current = [];
|
||||
}
|
||||
setKeyEvent("")
|
||||
};
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
const keyCombination = detectModifierKeys(event);
|
||||
|
||||
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0) return;
|
||||
|
||||
if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
|
||||
// update state here
|
||||
setKeyEvent(keyCombination)
|
||||
} else {
|
||||
setKeyEvent("")
|
||||
}
|
||||
|
||||
if (keyCombination === "G") {
|
||||
if (selectedAssets.length > 0) {
|
||||
moveAssets();
|
||||
itemsData.current = floorItems.filter((item: { modelUuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modelUuid));
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCombination === "ESCAPE") {
|
||||
event.preventDefault();
|
||||
|
||||
|
@ -109,6 +130,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
|
|||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
canvasElement.addEventListener("keydown", onKeyDown);
|
||||
canvasElement?.addEventListener("keyup", onKeyUp);
|
||||
}
|
||||
|
||||
return () => {
|
||||
|
@ -116,12 +138,11 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
|
|||
canvasElement.removeEventListener("pointermove", onPointerMove);
|
||||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
canvasElement?.removeEventListener("keyup", onKeyUp);
|
||||
};
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects]);
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent]);
|
||||
|
||||
const gridSize = 0.25;
|
||||
const moveSpeed = 0.25;
|
||||
const isGridSnap = false;
|
||||
let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25;
|
||||
|
||||
useFrame(() => {
|
||||
if (movedObjects.length > 0) {
|
||||
|
@ -132,10 +153,17 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
|
|||
if (point) {
|
||||
let targetX = point.x;
|
||||
let targetZ = point.z;
|
||||
if (keyEvent === "Ctrl") {
|
||||
targetX = snapControls(targetX, "Ctrl");
|
||||
targetZ = snapControls(targetZ, "Ctrl");
|
||||
} else if (keyEvent === "Ctrl+Shift") {
|
||||
targetX = snapControls(targetX, "Ctrl+Shift");
|
||||
targetZ = snapControls(targetZ, "Ctrl+Shift");
|
||||
} else if (keyEvent === "Shift") {
|
||||
targetX = snapControls(targetX, "Shift");
|
||||
targetZ = snapControls(targetZ, "Shift");
|
||||
} else {
|
||||
|
||||
if (isGridSnap) {
|
||||
targetX = Math.round(point.x / gridSize) * gridSize;
|
||||
targetZ = Math.round(point.z / gridSize) * gridSize;
|
||||
}
|
||||
|
||||
const position = new THREE.Vector3();
|
||||
|
@ -280,6 +308,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
|
|||
setMovedObjects([]);
|
||||
setRotatedObjects([]);
|
||||
setSelectedAssets([]);
|
||||
setKeyEvent("")
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
import { useFrame } from '@react-three/fiber';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { useMachineStore } from '../../../../../store/simulation/useMachineStore';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
|
||||
|
||||
interface MachineAnimatorProps {
|
||||
currentPhase: string;
|
||||
handleCallBack: () => void;
|
||||
reset: () => void;
|
||||
machineStatus: (modelId: string, status: string) => void;
|
||||
processingTime: number;
|
||||
machineUuid: string
|
||||
}
|
||||
|
||||
const MachineAnimator = ({ currentPhase, handleCallBack, processingTime, machineUuid, machineStatus, reset }: MachineAnimatorProps) => {
|
||||
const animationStarted = useRef<boolean>(false);
|
||||
const isPausedRef = useRef<boolean>(false);
|
||||
const startTimeRef = useRef<number>(0);
|
||||
const animationFrameId = useRef<number | null>(null);
|
||||
const pauseTimeRef = useRef<number | null>(null);
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const { removeCurrentAction } = useMachineStore();
|
||||
const { isReset, setReset } = useResetButtonStore();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const isPlayingRef = useRef<boolean>(false);
|
||||
const isResetRef = useRef<boolean>(false)
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
isPausedRef.current = isPaused;
|
||||
}, [isPaused]);
|
||||
useEffect(() => {
|
||||
isPlayingRef.current = isPlaying;
|
||||
}, [isPlaying]);
|
||||
useEffect(() => {
|
||||
isResetRef.current = isReset;
|
||||
}, [isReset]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (isReset || !isPlaying) {
|
||||
reset();
|
||||
setReset(false);
|
||||
startTimeRef.current = 0;
|
||||
isPausedRef.current = false;
|
||||
pauseTimeRef.current = 0;
|
||||
animationFrameId.current = null;
|
||||
animationStarted.current = false;
|
||||
removeCurrentAction(machineUuid)
|
||||
}
|
||||
}, [isReset, isPlaying])
|
||||
|
||||
useEffect(() => {
|
||||
if (currentPhase === 'processing' && !animationStarted.current && machineUuid) {
|
||||
animationStarted.current = true;
|
||||
startTimeRef.current = performance.now();
|
||||
animationFrameId.current = requestAnimationFrame(step);
|
||||
}
|
||||
}, [currentPhase]);
|
||||
|
||||
function step(time: number) {
|
||||
if (!isPausedRef.current || !isResetRef.current) {
|
||||
if (animationFrameId.current) {
|
||||
cancelAnimationFrame(animationFrameId.current);
|
||||
animationFrameId.current = null;
|
||||
}
|
||||
if (isPausedRef.current) {
|
||||
if (!pauseTimeRef.current) {
|
||||
pauseTimeRef.current = performance.now();
|
||||
}
|
||||
animationFrameId.current = requestAnimationFrame(step);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pauseTimeRef.current) {
|
||||
const pauseDuration = performance.now() - pauseTimeRef.current;
|
||||
startTimeRef.current += pauseDuration;
|
||||
pauseTimeRef.current = null;
|
||||
}
|
||||
|
||||
const elapsed = time - startTimeRef.current;
|
||||
const processedTime = processingTime * 1000;
|
||||
if (elapsed < processedTime) {
|
||||
machineStatus(machineUuid, "Machine is currently processing the task");
|
||||
animationFrameId.current = requestAnimationFrame(step);
|
||||
} else {
|
||||
removeCurrentAction(machineUuid);
|
||||
animationStarted.current = false;
|
||||
handleCallBack();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default MachineAnimator;
|
|
@ -1,8 +1,65 @@
|
|||
import React from 'react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useMachineStore } from '../../../../../store/simulation/useMachineStore';
|
||||
import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import MachineAnimator from '../animator/machineAnimator';
|
||||
|
||||
function MachineInstance({ machineDetail }: any) {
|
||||
const [currentPhase, setCurrentPhase] = useState<string>('idle');
|
||||
let isIncrememtable = useRef<boolean>(true);
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { machines, addCurrentAction, setMachineState, setMachineActive } = useMachineStore();
|
||||
|
||||
const reset = () => {
|
||||
setMachineState(machineDetail.modelUuid, 'idle');
|
||||
setMachineActive(machineDetail.modelUuid, false);
|
||||
isIncrememtable.current = true;
|
||||
setCurrentPhase("idle");
|
||||
}
|
||||
const increment = () => {
|
||||
if (isIncrememtable.current) {
|
||||
addCurrentAction(machineDetail.modelUuid, "machine-action-2468-1357-8024")
|
||||
isIncrememtable.current = false;
|
||||
}
|
||||
}
|
||||
function machineStatus(modelId: string, status: string) {
|
||||
// console.log(`${modelId} , ${status}`);
|
||||
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
if (!machineDetail.isActive && machineDetail.state === "idle" && currentPhase == "idle" && !machineDetail.currentAction) {
|
||||
setTimeout(() => {
|
||||
increment();
|
||||
}, 2000);
|
||||
machineStatus(machineDetail.modelUuid, 'Machine is idle and waiting for next instruction.')
|
||||
} else if (!machineDetail.isActive && machineDetail.state === "idle" && currentPhase == "idle" && machineDetail.currentAction) {
|
||||
setCurrentPhase("processing");
|
||||
setMachineState(machineDetail.modelUuid, 'running');
|
||||
setMachineActive(machineDetail.modelUuid, true);
|
||||
machineStatus(machineDetail.modelUuid, "Machine started processing")
|
||||
}
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}, [currentPhase, isPlaying, machines])
|
||||
|
||||
function handleCallBack() {
|
||||
if (currentPhase == "processing") {
|
||||
setMachineState(machineDetail.modelUuid, 'idle');
|
||||
setMachineActive(machineDetail.modelUuid, false);
|
||||
setCurrentPhase("idle")
|
||||
isIncrememtable.current = true;
|
||||
machineStatus(machineDetail.modelUuid, "Machine has completed the processing")
|
||||
}
|
||||
}
|
||||
// console.log('currentPhase: ', currentPhase);
|
||||
|
||||
|
||||
|
||||
function MachineInstance() {
|
||||
return (
|
||||
<>
|
||||
<MachineAnimator processingTime={machineDetail.point.action.processTime} handleCallBack={handleCallBack} currentPhase={currentPhase} machineUuid={machineDetail.modelUuid} machineStatus={machineStatus} reset={reset} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import React from 'react'
|
||||
import MachineInstance from './machineInstance/machineInstance'
|
||||
import { useMachineStore } from '../../../../store/simulation/useMachineStore';
|
||||
|
||||
function MachineInstances() {
|
||||
const { machines } = useMachineStore();
|
||||
return (
|
||||
<>
|
||||
|
||||
<MachineInstance />
|
||||
{machines.map((val: MachineStatus) => (
|
||||
<MachineInstance key={val.modelUuid} machineDetail={val} />
|
||||
))}
|
||||
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useMachineStore } from '../../../store/simulation/useMachineStore'
|
|||
import { useSelectedProduct } from '../../../store/simulation/useSimulationStore';
|
||||
|
||||
function Machine() {
|
||||
const { addMachine, addCurrentAction, removeMachine } = useMachineStore();
|
||||
const { addMachine, addCurrentAction, removeMachine, machines } = useMachineStore();
|
||||
const { selectedProduct } = useSelectedProduct();
|
||||
|
||||
const machineSample: MachineEventSchema[] = [
|
||||
|
@ -38,6 +38,12 @@ function Machine() {
|
|||
// addCurrentAction(machineSample[0].modelUuid, machineSample[0].point.action.actionUuid);
|
||||
}, [])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
// console.log('machines: ', machines);
|
||||
}, [machines])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
|||
const completedRef = useRef<boolean>(false);
|
||||
const isPausedRef = useRef<boolean>(false);
|
||||
const pauseTimeRef = useRef<number | null>(null);
|
||||
const [progress, setProgress] = useState<number>(0);
|
||||
const [restRotation, setRestingRotation] = useState<boolean>(true);
|
||||
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
|
||||
const [progress, setProgress] = useState<number>(0);
|
||||
const { scene } = useThree();
|
||||
let startTime: number;
|
||||
let fixedInterval: number;
|
||||
|
@ -66,6 +66,8 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
|||
setReset(false);
|
||||
setRestingRotation(true);
|
||||
decrementVehicleLoad(agvDetail.modelUuid, 0);
|
||||
isPausedRef.current = false;
|
||||
pauseTimeRef.current = 0;
|
||||
const object = scene.getObjectByProperty('uuid', agvUuid);
|
||||
if (object) {
|
||||
object.position.set(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]);
|
||||
|
|
|
@ -9,10 +9,8 @@ function VehicleInstances() {
|
|||
return (
|
||||
<>
|
||||
|
||||
{vehicles.map((val: any, i: any) =>
|
||||
|
||||
<VehicleInstance agvDetail={val} key={i} />
|
||||
|
||||
{vehicles.map((val: VehicleStatus) =>
|
||||
<VehicleInstance agvDetail={val} key={val.modelUuid} />
|
||||
)}
|
||||
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
export function snapControls(value: number, event: string): number {
|
||||
const CTRL_DISTANCE = 1; // Snap to whole numbers when Ctrl is pressed
|
||||
const SHIFT_DISTANCE = 0.01; // Snap to half-step increments when Shift is pressed
|
||||
const CTRL_SHIFT_DISTANCE = 0.1; // Snap to fine increments when both Ctrl and Shift are pressed
|
||||
|
||||
switch (event) {
|
||||
case "Ctrl":
|
||||
return Math.round(value / CTRL_DISTANCE) * CTRL_DISTANCE;
|
||||
|
||||
case "Shift":
|
||||
return Math.round(value / SHIFT_DISTANCE) * SHIFT_DISTANCE;
|
||||
|
||||
case "Ctrl+Shift":
|
||||
const base = Math.floor(value / CTRL_DISTANCE) * CTRL_DISTANCE;
|
||||
const offset =
|
||||
Math.round((value - base) / CTRL_SHIFT_DISTANCE) * CTRL_SHIFT_DISTANCE;
|
||||
return base + offset;
|
||||
|
||||
default:
|
||||
return value; // No snapping if no modifier key is pressed
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue