diff --git a/app/src/functions/users/functions/getAvatarColor.ts b/app/src/functions/users/functions/getAvatarColor.ts index f2bd816..d9a5d37 100644 --- a/app/src/functions/users/functions/getAvatarColor.ts +++ b/app/src/functions/users/functions/getAvatarColor.ts @@ -25,19 +25,8 @@ const avatarColors: string[] = [ export function getAvatarColor(index: number, name?: string): string { // Check if the color is already stored in localStorage const localStorageKey = "userAvatarColors"; - // Helper function to check if local storage is available - function isLocalStorageAvailable(): boolean { - try { - const testKey = "__test__"; - localStorage.setItem(testKey, "test"); - localStorage.removeItem(testKey); - return true; - } catch (e) { - return false; - } - } // Check if local storage is available - if (isLocalStorageAvailable() && name) { + if (name) { let userColors = JSON.parse(localStorage.getItem(localStorageKey) || "{}"); // Check if the user already has an assigned color diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 5837958..193dd41 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -184,24 +184,9 @@ async function handleModelLoad( ); if (!data || !data.points) return; - console.log('data: ', data); - - const createMarker = (point: THREE.Vector3) => { - const sphere = new THREE.SphereGeometry(0.1, 15); - const material = new THREE.MeshStandardMaterial(); - const mesh = new THREE.Mesh(sphere, material); - mesh.position.copy(point); - return mesh; - }; - - if (data.points && data.points.length > 0) { - data.points.forEach((Point) => { - model.add(createMarker(Point)); - }); - } if (selectedItem.type === "Conveyor") { - const event: ConveyorEventSchema = { + const ConveyorEvent: ConveyorEventSchema = { modelUuid: newFloorItem.modeluuid, modelName: newFloorItem.modelname, position: newFloorItem.position, @@ -225,7 +210,85 @@ async function handleModelLoad( } })) } - addEvent(event); + addEvent(ConveyorEvent); + } else if (selectedItem.type === "Vehicle") { + const vehicleEvent: VehicleEventSchema = { + modelUuid: newFloorItem.modeluuid, + modelName: newFloorItem.modelname, + position: newFloorItem.position, + rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + state: "idle", + type: "vehicle", + speed: 1, + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Vehicle Action", + actionType: "travel", + material: null, + unLoadDuration: 5, + loadCapacity: 10, + pickUpPoint: null, + unLoadPoint: null, + triggers: [] + } + } + }; + addEvent(vehicleEvent); + } else if (selectedItem.type === "ArmBot") { + const roboticArmEvent: RoboticArmEventSchema = { + modelUuid: newFloorItem.modeluuid, + modelName: newFloorItem.modelname, + position: newFloorItem.position, + rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + state: "idle", + type: "roboticArm", + speed: 1, + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + actions: [ + { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Pick and Place", + actionType: "pickAndPlace", + process: { + startPoint: [0, 0, 0], + endPoint: [0, 0, 0] + }, + triggers: [] + } + ] + } + }; + addEvent(roboticArmEvent); + } else if (selectedItem.type === "Machine") { + const machineEvent: MachineEventSchema = { + modelUuid: newFloorItem.modeluuid, + modelName: newFloorItem.modelname, + position: newFloorItem.position, + rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + state: "idle", + type: "machine", + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Process Action", + actionType: "process", + processTime: 10, + swapMaterial: "material-id", + triggers: [] + } + } + }; + addEvent(machineEvent); } } diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index 5ec3636..2a3c2cc 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -18,11 +18,9 @@ import useModuleStore from "../../../store/useModuleStore"; // import { retrieveGLTF } from "../../../utils/indexDB/idbUtils"; import { useEventsStore } from "../../../store/simulation/useEventsStore"; - const assetManagerWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/assetManagerWorker.js", import.meta.url)); const gltfLoaderWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", import.meta.url)); - const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject, floorGroup, tempLoader, isTempLoader, plane, }: any) => { const state: Types.ThreeState = useThree(); const { raycaster, controls }: any = state; diff --git a/app/src/modules/scene/tools/measurementTool.tsx b/app/src/modules/scene/tools/measurementTool.tsx index 9f9fa03..9a30da3 100644 --- a/app/src/modules/scene/tools/measurementTool.tsx +++ b/app/src/modules/scene/tools/measurementTool.tsx @@ -5,240 +5,225 @@ import { useToolMode } from "../../../store/store"; import { Html } from "@react-three/drei"; const MeasurementTool = () => { - const { gl, raycaster, pointer, camera, scene } = useThree(); - const { toolMode } = useToolMode(); + const { gl, raycaster, pointer, camera, scene } = useThree(); + const { toolMode } = useToolMode(); - const [points, setPoints] = useState([]); - const [tubeGeometry, setTubeGeometry] = useState( - null - ); - const groupRef = useRef(null); - const [startConePosition, setStartConePosition] = - useState(null); - const [endConePosition, setEndConePosition] = useState( - null - ); - const [startConeQuaternion, setStartConeQuaternion] = useState( - new THREE.Quaternion() - ); - const [endConeQuaternion, setEndConeQuaternion] = useState( - new THREE.Quaternion() - ); - const [coneSize, setConeSize] = useState({ radius: 0.2, height: 0.5 }); + const [points, setPoints] = useState([]); + const [tubeGeometry, setTubeGeometry] = useState( + null + ); + const groupRef = useRef(null); + const [startConePosition, setStartConePosition] = + useState(null); + const [endConePosition, setEndConePosition] = useState( + null + ); + const [startConeQuaternion, setStartConeQuaternion] = useState( + new THREE.Quaternion() + ); + const [endConeQuaternion, setEndConeQuaternion] = useState( + new THREE.Quaternion() + ); + const [coneSize, setConeSize] = useState({ radius: 0.2, height: 0.5 }); - const MIN_RADIUS = 0.001, - MAX_RADIUS = 0.1; - const MIN_CONE_RADIUS = 0.01, - MAX_CONE_RADIUS = 0.4; - const MIN_CONE_HEIGHT = 0.035, - MAX_CONE_HEIGHT = 2.0; + const MIN_RADIUS = 0.001, MAX_RADIUS = 0.1; + const MIN_CONE_RADIUS = 0.01, MAX_CONE_RADIUS = 0.4; + const MIN_CONE_HEIGHT = 0.035, MAX_CONE_HEIGHT = 2.0; - useEffect(() => { - const canvasElement = gl.domElement; - let drag = false; - let isLeftMouseDown = false; + useEffect(() => { + const canvasElement = gl.domElement; + let drag = false; + let isLeftMouseDown = false; - const onMouseDown = () => { - isLeftMouseDown = true; - drag = false; - }; + const onMouseDown = () => { + isLeftMouseDown = true; + drag = false; + }; - const onMouseUp = (evt: any) => { - isLeftMouseDown = false; - if (evt.button === 0 && !drag) { - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster - .intersectObjects(scene.children, true) - .filter( - (intersect) => - !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("MeasurementReference") && - !intersect.object.name.includes("agv-collider") && - !(intersect.object.type === "GridHelper") - ); + const onMouseUp = (evt: any) => { + isLeftMouseDown = false; + if (evt.button === 0 && !drag) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.name.includes("agv-collider") && + !(intersect.object.type === "GridHelper") + ); - if (intersects.length > 0) { - const intersectionPoint = intersects[0].point.clone(); - if (points.length < 2) { - setPoints([...points, intersectionPoint]); - } else { - setPoints([intersectionPoint]); - } + if (intersects.length > 0) { + const intersectionPoint = intersects[0].point.clone(); + if (points.length < 2) { + setPoints([...points, intersectionPoint]); + } else { + setPoints([intersectionPoint]); + } + } + } + }; + + const onMouseMove = () => { + if (isLeftMouseDown) drag = true; + }; + + const onContextMenu = (evt: any) => { + evt.preventDefault(); + if (!drag) { + evt.preventDefault(); + setPoints([]); + setTubeGeometry(null); + } + }; + + if (toolMode === "MeasurementScale") { + canvasElement.addEventListener("pointerdown", onMouseDown); + canvasElement.addEventListener("pointermove", onMouseMove); + canvasElement.addEventListener("pointerup", onMouseUp); + canvasElement.addEventListener("contextmenu", onContextMenu); + } else { + resetMeasurement(); + setPoints([]); } - } + + return () => { + canvasElement.removeEventListener("pointerdown", onMouseDown); + canvasElement.removeEventListener("pointermove", onMouseMove); + canvasElement.removeEventListener("pointerup", onMouseUp); + canvasElement.removeEventListener("contextmenu", onContextMenu); + }; + }, [toolMode, camera, raycaster, pointer, scene, points]); + + useFrame(() => { + if (points.length === 1) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.name.includes("agv-collider") && + !(intersect.object.type === "GridHelper") + ); + + if (intersects.length > 0) { + updateMeasurement(points[0], intersects[0].point); + } + } else if (points.length === 2) { + updateMeasurement(points[0], points[1]); + } else { + resetMeasurement(); + } + }); + + const updateMeasurement = (start: THREE.Vector3, end: THREE.Vector3) => { + const distance = start.distanceTo(end); + + const radius = THREE.MathUtils.clamp(distance * 0.02, MIN_RADIUS, MAX_RADIUS); + const coneRadius = THREE.MathUtils.clamp(distance * 0.05, MIN_CONE_RADIUS, MAX_CONE_RADIUS); + const coneHeight = THREE.MathUtils.clamp(distance * 0.2, MIN_CONE_HEIGHT, MAX_CONE_HEIGHT); + + setConeSize({ radius: coneRadius, height: coneHeight }); + + const direction = new THREE.Vector3().subVectors(end, start).normalize(); + + const offset = direction.clone().multiplyScalar(coneHeight * 0.5); + + let tubeStart = start.clone().add(offset); + let tubeEnd = end.clone().sub(offset); + + tubeStart.y = Math.max(tubeStart.y, 0); + tubeEnd.y = Math.max(tubeEnd.y, 0); + + const curve = new THREE.CatmullRomCurve3([tubeStart, tubeEnd]); + setTubeGeometry(new THREE.TubeGeometry(curve, 20, radius, 8, false)); + + setStartConePosition(tubeStart); + setEndConePosition(tubeEnd); + setStartConeQuaternion(getArrowOrientation(start, end)); + setEndConeQuaternion(getArrowOrientation(end, start)); }; - const onMouseMove = () => { - if (isLeftMouseDown) drag = true; - }; - - const onContextMenu = (evt: any) => { - evt.preventDefault(); - if (!drag) { - evt.preventDefault(); - setPoints([]); + const resetMeasurement = () => { setTubeGeometry(null); - } + setStartConePosition(null); + setEndConePosition(null); }; - if (toolMode === "MeasurementScale") { - canvasElement.addEventListener("pointerdown", onMouseDown); - canvasElement.addEventListener("pointermove", onMouseMove); - canvasElement.addEventListener("pointerup", onMouseUp); - canvasElement.addEventListener("contextmenu", onContextMenu); - } else { - resetMeasurement(); - setPoints([]); - } - - return () => { - canvasElement.removeEventListener("pointerdown", onMouseDown); - canvasElement.removeEventListener("pointermove", onMouseMove); - canvasElement.removeEventListener("pointerup", onMouseUp); - canvasElement.removeEventListener("contextmenu", onContextMenu); + const getArrowOrientation = (start: THREE.Vector3, end: THREE.Vector3) => { + const direction = new THREE.Vector3() + .subVectors(end, start) + .normalize() + .negate(); + const quaternion = new THREE.Quaternion(); + quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction); + return quaternion; }; - }, [toolMode, camera, raycaster, pointer, scene, points]); - useFrame(() => { - if (points.length === 1) { - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster - .intersectObjects(scene.children, true) - .filter( - (intersect) => - !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("MeasurementReference") && - !intersect.object.name.includes("agv-collider") && - !(intersect.object.type === "GridHelper") - ); + useEffect(() => { + if (points.length === 2) { + // console.log(points[0].distanceTo(points[1])); + } + }, [points]); - if (intersects.length > 0) { - updateMeasurement(points[0], intersects[0].point); - } - } else if (points.length === 2) { - updateMeasurement(points[0], points[1]); - } else { - resetMeasurement(); - } - }); + return ( + + {startConePosition && ( + + + + + )} + {endConePosition && ( + + + + + )} + {tubeGeometry && ( + + + + )} - const updateMeasurement = (start: THREE.Vector3, end: THREE.Vector3) => { - const distance = start.distanceTo(end); - - const radius = THREE.MathUtils.clamp( - distance * 0.02, - MIN_RADIUS, - MAX_RADIUS + {startConePosition && endConePosition && ( + +
+ {startConePosition.distanceTo(endConePosition).toFixed(2)} m +
+ + )} +
); - const coneRadius = THREE.MathUtils.clamp( - distance * 0.05, - MIN_CONE_RADIUS, - MAX_CONE_RADIUS - ); - const coneHeight = THREE.MathUtils.clamp( - distance * 0.2, - MIN_CONE_HEIGHT, - MAX_CONE_HEIGHT - ); - - setConeSize({ radius: coneRadius, height: coneHeight }); - - const direction = new THREE.Vector3().subVectors(end, start).normalize(); - - const offset = direction.clone().multiplyScalar(coneHeight * 0.5); - - let tubeStart = start.clone().add(offset); - let tubeEnd = end.clone().sub(offset); - - tubeStart.y = Math.max(tubeStart.y, 0); - tubeEnd.y = Math.max(tubeEnd.y, 0); - - const curve = new THREE.CatmullRomCurve3([tubeStart, tubeEnd]); - setTubeGeometry(new THREE.TubeGeometry(curve, 20, radius, 8, false)); - - setStartConePosition(tubeStart); - setEndConePosition(tubeEnd); - setStartConeQuaternion(getArrowOrientation(start, end)); - setEndConeQuaternion(getArrowOrientation(end, start)); - }; - - const resetMeasurement = () => { - setTubeGeometry(null); - setStartConePosition(null); - setEndConePosition(null); - }; - - const getArrowOrientation = (start: THREE.Vector3, end: THREE.Vector3) => { - const direction = new THREE.Vector3() - .subVectors(end, start) - .normalize() - .negate(); - const quaternion = new THREE.Quaternion(); - quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction); - return quaternion; - }; - - useEffect(() => { - if (points.length === 2) { - console.log(points[0].distanceTo(points[1])); - } - }, [points]); - - return ( - - {startConePosition && ( - - - - - )} - {endConePosition && ( - - - - - )} - {tubeGeometry && ( - - - - )} - - {startConePosition && endConePosition && ( - -
- {startConePosition.distanceTo(endConePosition).toFixed(2)} m -
- - )} -
- ); }; export default MeasurementTool; diff --git a/app/src/modules/simulation/conveyor/conveyor.tsx b/app/src/modules/simulation/conveyor/conveyor.tsx new file mode 100644 index 0000000..bd21523 --- /dev/null +++ b/app/src/modules/simulation/conveyor/conveyor.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import ConveyorInstances from './instances/conveyorInstances' + +function Conveyor() { + return ( + <> + + + + + ) +} + +export default Conveyor \ No newline at end of file diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx new file mode 100644 index 0000000..9c9d612 --- /dev/null +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx @@ -0,0 +1,10 @@ +import React from 'react' + +function ConveyorInstance() { + return ( + <> + + ) +} + +export default ConveyorInstance \ No newline at end of file diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx new file mode 100644 index 0000000..3f53784 --- /dev/null +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import ConveyorInstance from './conveyorInstance/conveyorInstance' + +function ConveyorInstances() { + return ( + <> + + + + + ) +} + +export default ConveyorInstances \ No newline at end of file diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx new file mode 100644 index 0000000..335f1f5 --- /dev/null +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -0,0 +1,148 @@ +import React, { useEffect, useRef, useState } from 'react'; +import * as THREE from 'three'; +import { useEventsStore } from '../../../../../store/simulation/useEventsStore'; +import useModuleStore from '../../../../../store/useModuleStore'; +import { TransformControls } from '@react-three/drei'; +import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys'; + +function PointsCreator() { + const { events, updatePoint, getPointByUuid } = useEventsStore(); + const { activeModule } = useModuleStore(); + const transformRef = useRef(null); + const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); + const [selectedPoint, setSelectedPoint] = useState(null); + const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); + + useEffect(() => { + setTransformMode(null); + const handleKeyDown = (e: KeyboardEvent) => { + const keyCombination = detectModifierKeys(e); + if (!selectedPoint) return; + if (keyCombination === "G") { + setTransformMode((prev) => (prev === "translate" ? null : "translate")); + } + if (keyCombination === "R") { + setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [selectedPoint]); + + const updatePointToState = (selectedPoint: THREE.Mesh) => { + let point = JSON.parse(JSON.stringify(getPointByUuid(selectedPoint.userData.modelUuid, selectedPoint.userData.pointUuid))); + if (point) { + point.position = [selectedPoint.position.x, selectedPoint.position.y, selectedPoint.position.z]; + updatePoint(selectedPoint.userData.modelUuid, selectedPoint.userData.pointUuid, point) + } + } + + return ( + <> + {activeModule === 'simulation' && + <> + + {events.map((event, i) => { + if (event.type === 'transfer') { + return ( + + {event.points.map((point, j) => ( + (sphereRefs.current[point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedPoint(sphereRefs.current[point.uuid]); + }} + onPointerMissed={() => { + setSelectedPoint(null); + }} + key={`${i}-${j}`} + position={new THREE.Vector3(...point.position)} + userData={{ modelUuid: event.modelUuid, pointUuid: point.uuid }} + > + + + + ))} + + ); + } else if (event.type === 'vehicle') { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedPoint(sphereRefs.current[event.point.uuid]); + }} + onPointerMissed={() => { + setSelectedPoint(null); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + > + + + + + ); + } else if (event.type === 'roboticArm') { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedPoint(sphereRefs.current[event.point.uuid]); + }} + onPointerMissed={() => { + setSelectedPoint(null); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + > + + + + + ); + } else if (event.type === 'machine') { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedPoint(sphereRefs.current[event.point.uuid]); + }} + onPointerMissed={() => { + setSelectedPoint(null); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + > + + + + + ); + } else { + return null; + } + })} + + {(selectedPoint && transformMode) && + { updatePointToState(selectedPoint) }} /> + } + + } + + ); +} + +export default PointsCreator; diff --git a/app/src/modules/simulation/events/points/points.tsx b/app/src/modules/simulation/events/points/points.tsx new file mode 100644 index 0000000..2a50f2d --- /dev/null +++ b/app/src/modules/simulation/events/points/points.tsx @@ -0,0 +1,12 @@ +import React from 'react' +import PointsCreator from './creator/pointsCreator' + +function Points() { + return ( + <> + + + ) +} + +export default Points \ No newline at end of file diff --git a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx new file mode 100644 index 0000000..1445e70 --- /dev/null +++ b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +function MaterialAnimator() { + return ( + <> + ) +} + +export default MaterialAnimator \ No newline at end of file diff --git a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx new file mode 100644 index 0000000..466e235 --- /dev/null +++ b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +function MaterialInstance() { + return ( + <> + ) +} + +export default MaterialInstance \ No newline at end of file diff --git a/app/src/modules/simulation/materials/instances/materialInstances.tsx b/app/src/modules/simulation/materials/instances/materialInstances.tsx new file mode 100644 index 0000000..519cba9 --- /dev/null +++ b/app/src/modules/simulation/materials/instances/materialInstances.tsx @@ -0,0 +1,17 @@ +import React from 'react' +import MaterialInstance from './instance/materialInstance' +import MaterialAnimator from './animator/materialAnimator' + +function MaterialInstances() { + return ( + <> + + + + + + + ) +} + +export default MaterialInstances \ No newline at end of file diff --git a/app/src/modules/simulation/materials/materials.tsx b/app/src/modules/simulation/materials/materials.tsx new file mode 100644 index 0000000..432d815 --- /dev/null +++ b/app/src/modules/simulation/materials/materials.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import MaterialInstances from './instances/materialInstances' + +function Materials() { + return ( + <> + + + + + ) +} + +export default Materials \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx new file mode 100644 index 0000000..0cd4fe2 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +function RoboticArmAnimator() { + return ( + <> + ) +} + +export default RoboticArmAnimator; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx new file mode 100644 index 0000000..2817906 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -0,0 +1,17 @@ +import React from 'react' +import IKInstance from '../ikInstance/ikInstance'; +import RoboticArmAnimator from '../animator/roboticArmAnimator'; + +function RoboticArmInstance() { + return ( + <> + + + + + + + ) +} + +export default RoboticArmInstance; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx new file mode 100644 index 0000000..52a8610 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +function IKInstance() { + return ( + <> + ) +} + +export default IKInstance; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstances.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstances.tsx new file mode 100644 index 0000000..d44ddd2 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/ikInstances.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import IKInstance from './ikInstance/ikInstance'; + +function IkInstances() { + return ( + <> + + + + + ) +} + +export default IkInstances; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx new file mode 100644 index 0000000..6e8a70a --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import RoboticArmInstance from './armInstance/roboticArmInstance'; + +function RoboticArmInstances() { + return ( + <> + + + + + ) +} + +export default RoboticArmInstances; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx new file mode 100644 index 0000000..1270d93 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import RoboticArmInstances from './instances/roboticArmInstances'; +import IkInstances from './instances/ikInstances'; + +function RoboticArm() { + return ( + <> + + + + + ) +} + +export default RoboticArm; \ No newline at end of file diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 3599803..b7bf36d 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -1,21 +1,34 @@ import React, { useEffect } from 'react'; import { useEventsStore } from '../../store/simulation/useEventsStore'; import { useProductStore } from '../../store/simulation/useProductStore'; +import Vehicles from './vehicle/vehicles'; +import Points from './events/points/points'; +import Conveyor from './conveyor/conveyor'; +import RoboticArm from './roboticArm/roboticArm'; function Simulation() { const { events } = useEventsStore(); const { products } = useProductStore(); useEffect(() => { - console.log('events: ', events); + // console.log('events: ', events); }, [events]) useEffect(() => { - console.log('products: ', products); + // console.log('products: ', products); }, [products]) return ( <> + + + + + + + + + ) } diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 92c1d44..cf5fd81 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -1,9 +1,62 @@ -import React from 'react' +import { useEffect, useState } from 'react' +import { useFrame, useThree } from '@react-three/fiber'; -function VehicleAnimator() { - return ( -
VehicleAnimator
- ) +interface VehicleAnimatorProps { + path: [number, number, number][]; + handleCallBack: () => void; + currentPhase: string; + agvUuid: number +} + + +function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid }: VehicleAnimatorProps) { + const [progress, setProgress] = useState(0) + const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); + const { scene } = useThree(); + + useEffect(() => { + + if (currentPhase === 'stationed-pickup' && path.length > 0) { + setCurrentPath(path); + } + + }, [currentPhase, path]) + + useFrame((_, delta) => { + if (!path || path.length < 2) return; + + const object = scene.getObjectByProperty("uuid", agvUuid) + if (!object) return; + + setProgress(prev => { + const next = prev + delta * 0.1; // speed + return next >= 1 ? 1 : next; + }); + + 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 ( + <> + + ) } export default VehicleAnimator \ No newline at end of file diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index 0996b3c..bf767ec 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -1,11 +1,63 @@ -import React from 'react' +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][]>([]); + + 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]); + + 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([]) + } + } + -function VehicleInstance() { return ( <> - + ) diff --git a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx index 13e15b7..0848883 100644 --- a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx +++ b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx @@ -1,11 +1,16 @@ import React from 'react' import VehicleInstance from './instance/vehicleInstance' +import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; function VehicleInstances() { + const { vehicles } = useVehicleStore(); + return ( <> - + {vehicles.map((val: any, i: any) => + + )} ) diff --git a/app/src/modules/simulation/vehicle/vehicle.tsx b/app/src/modules/simulation/vehicle/vehicle.tsx deleted file mode 100644 index e51effc..0000000 --- a/app/src/modules/simulation/vehicle/vehicle.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' -import VehicleInstances from './instances/vehicleInstances'; - -function Vehicle() { - return ( - <> - - - - - ) -} - -export default Vehicle \ No newline at end of file diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx new file mode 100644 index 0000000..3364717 --- /dev/null +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -0,0 +1,121 @@ +import React, { useEffect } from 'react' +import VehicleInstances from './instances/vehicleInstances'; + +import { useVehicleStore } from '../../../store/simulation/useVehicleStore'; + +function Vehicles() { + + const { vehicles, addVehicle } = useVehicleStore(); + + const vehicleStatusSample: VehicleEventSchema[] = [ + { + modelUuid: "veh-123", + modelName: "Autonomous Truck A1", + position: [10, 0, 5], + 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", + material: "crate", + unLoadDuration: 15, + loadCapacity: 5, + pickUpPoint: { x: 5, y: 0, z: 3 }, + unLoadPoint: { x: 20, y: 0, z: 10 }, + triggers: [ + { + triggerUuid: "trig-001", + triggerName: "Start Travel", + triggerType: "onComplete", + 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: "veh-123", + modelName: "Autonomous Truck A1", + position: [10, 0, 5], + 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", + material: "crate", + unLoadDuration: 15, + loadCapacity: 5, + pickUpPoint: { x: 5, y: 0, z: 3 }, + 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 + } + ] + } + } + } + ]; + + + useEffect(() => { + addVehicle('123', vehicleStatusSample[0]); + addVehicle('123', vehicleStatusSample[1]); + }, []) + + useEffect(() => { + // console.log('vehicles: ', vehicles); + }, [vehicles]) + + + return ( + <> + + + + + ) +} + +export default Vehicles; \ No newline at end of file diff --git a/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx b/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx index d09a88f..4ca3c31 100644 --- a/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx +++ b/app/src/modules/visualization/widgets/3d/Dropped3dWidget.tsx @@ -44,7 +44,7 @@ export default function Dropped3dWidgets() { const rotationStartRef = useRef<[number, number, number]>([0, 0, 0]); const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); const { setSelectedChartId } = useWidgetStore(); - const { measurements, duration} = useChartStore(); + const { measurements, duration } = useChartStore(); let [floorPlanesVertical, setFloorPlanesVertical] = useState( new THREE.Plane(new THREE.Vector3(0, 1, 0)) ); @@ -117,7 +117,6 @@ export default function Dropped3dWidgets() { !intersect.object.name.includes("Roof") && !intersect.object.name.includes("agv-collider") && !intersect.object.name.includes("MeasurementReference") && - !intersect.object.userData.isPathObject && !(intersect.object.type === "GridHelper") ); @@ -154,7 +153,6 @@ export default function Dropped3dWidgets() { !intersect.object.name.includes("Roof") && !intersect.object.name.includes("agv-collider") && !intersect.object.name.includes("MeasurementReference") && - !intersect.object.userData.isPathObject && !(intersect.object.type === "GridHelper") ); // Update widget's position in memory @@ -169,28 +167,28 @@ export default function Dropped3dWidgets() { const onDrop = (event: any) => { event.preventDefault(); event.stopPropagation(); - + hasEntered.current = false; - + const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; - + const newWidget = createdWidgetRef.current; if (!newWidget || !widgetSelect.startsWith("ui")) return; - + // ✅ Extract 2D drop position let [x, y, z] = newWidget.position; - + // ✅ Clamp Y to at least 0 y = Math.max(y, 0); newWidget.position = [x, y, z]; - + // ✅ Prepare polygon from selectedZone.points const points3D = selectedZone.points as Array<[number, number, number]>; const zonePolygonXZ = points3D.map(([x, , z]) => [x, z] as [number, number]); - + const isInside = isPointInPolygon([x, z], zonePolygonXZ); - + // ✅ Remove temp widget const prevWidgets = useZoneWidgetStore.getState().zoneWidgetData[selectedZone.zoneId] || []; const cleanedWidgets = prevWidgets.filter(w => w.id !== newWidget.id); @@ -200,29 +198,29 @@ export default function Dropped3dWidgets() { [selectedZone.zoneId]: cleanedWidgets, }, })); - + // (Optional) Prevent adding if dropped outside zone // if (!isInside) { // createdWidgetRef.current = null; // return; // } - + // ✅ Add widget addWidget(selectedZone.zoneId, newWidget); - + const add3dWidget = { organization, widget: newWidget, zoneId: selectedZone.zoneId, }; - + if (visualizationSocket) { visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); } - + createdWidgetRef.current = null; }; - + canvasElement.addEventListener("dragenter", handleDragEnter); @@ -258,7 +256,7 @@ export default function Dropped3dWidgets() { widgetToDuplicate.position[2] + 0.5, ], rotation: widgetToDuplicate.rotation || [0, 0, 0], - Data:{ + Data: { measurements: measurements, duration: duration }, @@ -365,7 +363,7 @@ export default function Dropped3dWidgets() { // floorPlanesVertical, // planeIntersect.current // ); - + // setintersectcontextmenu(intersect1.y); if (rightSelect === "RotateX" || rightSelect === "RotateY") { @@ -385,7 +383,7 @@ export default function Dropped3dWidgets() { rotationStartRef.current = selectedWidget.rotation || [0, 0, 0]; } } - + }; const handleMouseMove = (event: MouseEvent) => { @@ -429,7 +427,7 @@ export default function Dropped3dWidgets() { intersect.z + horizontalZ, ]; - + updateWidgetPosition(selectedZoneId, rightClickSelected, newPosition); } } @@ -437,24 +435,24 @@ export default function Dropped3dWidgets() { if (rightSelect === "Vertical Move") { const intersect = raycaster.ray.intersectPlane(floorPlanesVertical, planeIntersect.current); - + if (intersect && typeof intersectcontextmenu === "number") { const diff = intersect.y - intersectcontextmenu; const unclampedY = selectedWidget.position[1] + diff; const newY = Math.max(0, unclampedY); // Prevent going below floor (y=0) - + setintersectcontextmenu(intersect.y); - + const newPosition: [number, number, number] = [ selectedWidget.position[0], newY, selectedWidget.position[2], ]; - + updateWidgetPosition(selectedZoneId, rightClickSelected, newPosition); } } - + if (rightSelect?.startsWith("Rotate")) { const axis = rightSelect.slice(-1).toLowerCase(); // "x", "y", or "z" const currentX = event.pageX; diff --git a/app/src/store/simulation/useArmBotStore.ts b/app/src/store/simulation/useArmBotStore.ts index 7dd4716..493a068 100644 --- a/app/src/store/simulation/useArmBotStore.ts +++ b/app/src/store/simulation/useArmBotStore.ts @@ -1,17 +1,6 @@ import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; -interface ArmBotStatus extends RoboticArmEventSchema { - productId: string; - isActive: boolean; - idleTime: number; - activeTime: number; - currentAction?: { - actionUuid: string; - actionName: string; - }; -} - interface ArmBotStore { armBots: ArmBotStatus[]; @@ -22,9 +11,11 @@ interface ArmBotStore { updates: Partial> ) => void; - startAction: (modelUuid: string, actionUuid: string) => void; - completeAction: (modelUuid: string) => void; - cancelAction: (modelUuid: string) => void; + addCurrentAction: (modelUuid: string, actionUuid: string) => void; + removeCurrentAction: (modelUuid: string) => void; + + addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void; + removeAction: (modelUuid: string, actionUuid: string) => void; setArmBotActive: (modelUuid: string, isActive: boolean) => void; @@ -71,7 +62,7 @@ export const useArmBotStore = create()( }); }, - startAction: (modelUuid, actionUuid) => { + addCurrentAction: (modelUuid, actionUuid) => { set((state) => { const armBot = state.armBots.find(a => a.modelUuid === modelUuid); if (armBot) { @@ -87,22 +78,30 @@ export const useArmBotStore = create()( }); }, - completeAction: (modelUuid) => { + removeCurrentAction: (modelUuid) => { set((state) => { const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot && armBot.currentAction) { + if (armBot) { armBot.currentAction = undefined; armBot.isActive = false; } }); }, - cancelAction: (modelUuid) => { + addAction: (modelUuid, action) => { set((state) => { const armBot = state.armBots.find(a => a.modelUuid === modelUuid); if (armBot) { - armBot.currentAction = undefined; - armBot.isActive = false; + armBot.point.actions.push(action); + } + }); + }, + + removeAction: (modelUuid, actionUuid) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.point.actions = armBot.point.actions.filter(a => a.actionUuid !== actionUuid); } }); }, diff --git a/app/src/store/simulation/useConveyorStore.ts b/app/src/store/simulation/useConveyorStore.ts index 059e76b..15dbf34 100644 --- a/app/src/store/simulation/useConveyorStore.ts +++ b/app/src/store/simulation/useConveyorStore.ts @@ -1,13 +1,6 @@ import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; -interface ConveyorStatus extends ConveyorEventSchema { - productId: string; - isActive: boolean; - idleTime: number; - activeTime: number; -} - interface ConveyorStore { conveyors: ConveyorStatus[]; diff --git a/app/src/store/simulation/useMachineStore.ts b/app/src/store/simulation/useMachineStore.ts index 15997b9..cc927f7 100644 --- a/app/src/store/simulation/useMachineStore.ts +++ b/app/src/store/simulation/useMachineStore.ts @@ -1,13 +1,6 @@ import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; -interface MachineStatus extends MachineEventSchema { - productId: string; - isActive: boolean; - idleTime: number; - activeTime: number; -} - interface MachineStore { machines: MachineStatus[]; diff --git a/app/src/store/simulation/useStorageUnitStore.ts b/app/src/store/simulation/useStorageUnitStore.ts index 52e4185..d729708 100644 --- a/app/src/store/simulation/useStorageUnitStore.ts +++ b/app/src/store/simulation/useStorageUnitStore.ts @@ -1,14 +1,6 @@ import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; -interface StorageUnitStatus extends StorageEventSchema { - productId: string; - isActive: boolean; - idleTime: number; - activeTime: number; - currentLoad: number; -} - interface StorageUnitStore { storageUnits: StorageUnitStatus[]; @@ -25,7 +17,7 @@ interface StorageUnitStore { setStorageUnitState: (modelUuid: string, newState: StorageUnitStatus['state']) => void; // Load updates - updateStorageUnitLoad: (modelUuid: string, load: number) => void; + updateStorageUnitLoad: (modelUuid: string, incrementBy: number) => void; // Time tracking incrementActiveTime: (modelUuid: string, incrementBy: number) => void; @@ -95,11 +87,11 @@ export const useStorageUnitStore = create()( }, // Load updates - updateStorageUnitLoad: (modelUuid, load) => { + updateStorageUnitLoad: (modelUuid, incrementBy) => { set((state) => { const unit = state.storageUnits.find(s => s.modelUuid === modelUuid); if (unit) { - unit.currentLoad = load; + unit.currentLoad += incrementBy; } }); }, diff --git a/app/src/store/simulation/useVehicleStore.ts b/app/src/store/simulation/useVehicleStore.ts index 4eba3f5..ce28916 100644 --- a/app/src/store/simulation/useVehicleStore.ts +++ b/app/src/store/simulation/useVehicleStore.ts @@ -21,7 +21,8 @@ interface VehiclesStore { ) => void; setVehicleActive: (modelUuid: string, isActive: boolean) => void; - updateVehicleLoad: (modelUuid: string, load: number) => void; + incrementVehicleLoad: (modelUuid: string, incrementBy: number) => void; + decrementVehicleLoad: (modelUuid: string, decrementBy: number) => void; setVehicleState: (modelUuid: string, newState: VehicleStatus['state']) => void; incrementActiveTime: (modelUuid: string, incrementBy: number) => void; incrementIdleTime: (modelUuid: string, incrementBy: number) => void; @@ -30,7 +31,6 @@ interface VehiclesStore { getVehiclesByProduct: (productId: string) => VehicleStatus[]; getVehiclesByState: (state: string) => VehicleStatus[]; getActiveVehicles: () => VehicleStatus[]; - getIdleVehicles: () => VehicleStatus[]; } export const useVehicleStore = create()( @@ -75,11 +75,20 @@ export const useVehicleStore = create()( }); }, - updateVehicleLoad: (modelUuid, load) => { + incrementVehicleLoad: (modelUuid, incrementBy) => { set((state) => { const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); if (vehicle) { - vehicle.currentLoad = load; + vehicle.currentLoad += incrementBy; + } + }); + }, + + decrementVehicleLoad: (modelUuid, decrementBy) => { + set((state) => { + const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.currentLoad = decrementBy; } }); }, @@ -125,10 +134,6 @@ export const useVehicleStore = create()( getActiveVehicles: () => { return get().vehicles.filter(v => v.isActive); - }, - - getIdleVehicles: () => { - return get().vehicles.filter(v => !v.isActive && v.currentLoad > 0); } })) ); diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index 8f0c180..7c2bd2c 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -42,7 +42,7 @@ interface VehiclePointSchema { actionUuid: string; actionName: string; actionType: "travel"; - material: string; + material: string | null; unLoadDuration: number; loadCapacity: number; pickUpPoint: { x: number; y: number, z: number } | null; @@ -59,7 +59,7 @@ interface RoboticArmPointSchema { actionUuid: string; actionName: string; actionType: "pickAndPlace"; - process: { startPoint: string; endPoint: string }; + process: { startPoint: [number, number, number]; endPoint: [number, number, number] }; triggers: TriggerSchema[]; }[]; } @@ -119,10 +119,53 @@ interface StorageEventSchema extends AssetEventSchema { point: StoragePointSchema; } -type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema | []; +type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema; type productsSchema = { productName: string; productId: string; eventsData: EventsSchema[]; -}[] \ No newline at end of file +}[] + + +interface ConveyorStatus extends ConveyorEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; +} + +interface MachineStatus extends MachineEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; +} + +interface ArmBotStatus extends RoboticArmEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; + currentAction?: { + actionUuid: string; + actionName: string; + }; +} + +interface VehicleStatus extends VehicleEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; + currentLoad: number; + distanceTraveled: number; +} + +interface StorageUnitStatus extends StorageEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; + currentLoad: number; +} \ No newline at end of file