diff --git a/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx index 8b05525..d3e5301 100644 --- a/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/ArmBotMechanics.tsx @@ -2,7 +2,7 @@ import React, { useRef, useMemo, useCallback, useState } from "react"; import { InfoIcon, AddIcon, RemoveIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store"; -import * as SimulationTypes from '../../../../types/simulation'; +import * as SimulationTypes from '../../../../types/simulationTypes'; import LabledDropdown from "../../../ui/inputs/LabledDropdown"; import { handleResize } from "../../../../functions/handleResizePannel"; @@ -225,19 +225,28 @@ const ArmBotMechanics: React.FC = () => { }, [selectedPoint, selectedProcessIndex, handleProcessChange]); const handleTriggerSelect = useCallback((displayName: string, index: number) => { - const selected = connectedTriggers.find(t => t.displayName === displayName); + const availableOptions = getFilteredTriggerOptions(index); + const selectedDisplayIndex = availableOptions.indexOf(displayName); + + const filteredTriggers = connectedTriggers.filter(trigger => + !selectedPoint?.actions.processes + ?.filter((_, i) => i !== index) + .map(p => p.triggerId) + .includes(trigger.uuid) + ); + + const selected = filteredTriggers[selectedDisplayIndex]; + if (!selected || !selectedPoint?.actions.processes) return; const oldProcess = selectedPoint.actions.processes[index]; const updatedProcesses = [...selectedPoint.actions.processes]; - - // Only reset start/end if new trigger invalidates them (your logic can expand this) updatedProcesses[index] = { ...oldProcess, triggerId: selected.uuid, - startPoint: oldProcess.startPoint || "", // preserve if exists - endPoint: oldProcess.endPoint || "" // preserve if exists + startPoint: oldProcess.startPoint || "", + endPoint: oldProcess.endPoint || "" }; handleProcessChange(updatedProcesses); @@ -299,8 +308,10 @@ const ArmBotMechanics: React.FC = () => { handleSpeedChange(parseInt(value))} + onChange={(value) => handleSpeedChange(parseFloat(value))} />
diff --git a/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx index 080aba5..5e84d10 100644 --- a/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/ConveyorMechanics.tsx @@ -17,7 +17,7 @@ import { useSocketStore, } from "../../../../store/store"; import * as THREE from "three"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import InputToggle from "../../../ui/inputs/InputToggle"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; diff --git a/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx index 812ed54..6342bac 100644 --- a/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/StaticMachineMechanics.tsx @@ -2,7 +2,7 @@ import React, { useRef, useMemo, useCallback } from "react"; import { InfoIcon } from "../../../icons/ExportCommonIcons"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store"; -import * as SimulationTypes from '../../../../types/simulation'; +import * as SimulationTypes from '../../../../types/simulationTypes'; import LabledDropdown from "../../../ui/inputs/LabledDropdown"; import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; diff --git a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx index 73199c9..147d5cb 100644 --- a/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/mechanics/VehicleMechanics.tsx @@ -2,7 +2,7 @@ import React, { useRef, useMemo } from "react"; import { InfoIcon } from "../../../icons/ExportCommonIcons"; import InputWithDropDown from "../../../ui/inputs/InputWithDropDown"; import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store"; -import * as SimulationTypes from '../../../../types/simulation'; +import * as SimulationTypes from '../../../../types/simulationTypes'; import PositionInput from "../customInput/PositionInputs"; import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; import LabeledButton from "../../../ui/inputs/LabledButton"; diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx index e5debde..8e2d4f1 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/BarChartInput.tsx @@ -21,14 +21,17 @@ const BarChartInput = (props: Props) => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] + const [isLoading, setLoading] = useState(true); useEffect(() => { const fetchZoneData = async () => { try { + setLoading(true) const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); + setLoading(false); } else { console.log("Unexpected response:", response); } @@ -148,6 +151,7 @@ const BarChartInput = (props: Props) => { onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onUnselect={() => handleSelect(inputKey, null)} selectedValue={selections[inputKey]} // Load from Zustand + isLoading={isLoading} />
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx index 55ae422..af6f735 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FleetEfficiencyInputComponent.tsx @@ -21,14 +21,17 @@ const FleetEfficiencyInputComponent = (props: Props) => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] + const [isLoading, setLoading] = useState(true); useEffect(() => { const fetchZoneData = async () => { try { const response = await axios.get(`http://${iotApiUrl}/floatinput`); + setLoading(true) if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); + setLoading(false) } else { console.log("Unexpected response:", response); } @@ -149,6 +152,7 @@ const FleetEfficiencyInputComponent = (props: Props) => { onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onUnselect={() => handleSelect(inputKey, null)} selectedValue={selections[inputKey]} // Load from Zustand + isLoading={isLoading} />
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx index 1164a84..53ddbcc 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/FlotingWidgetInput.tsx @@ -21,14 +21,17 @@ const FlotingWidgetInput = (props: Props) => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] + const [isLoading, setLoading] = useState(true); useEffect(() => { const fetchZoneData = async () => { try { + setLoading(true) const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); + setLoading(false); } else { console.log("Unexpected response:", response); } @@ -149,6 +152,7 @@ const FlotingWidgetInput = (props: Props) => { onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onUnselect={() => handleSelect(inputKey, null)} selectedValue={selections[inputKey]} // Load from Zustand + isLoading={isLoading} />
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx index 6ab5ec3..3cf647e 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/LineGrapInput.tsx @@ -140,14 +140,17 @@ const LineGrapInput = (props: Props) => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] + const [isLoading, setLoading] = useState(true); useEffect(() => { const fetchZoneData = async () => { try { + setLoading(true) const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); + setLoading(false) } else { console.log("Unexpected response:", response); } @@ -267,6 +270,7 @@ const LineGrapInput = (props: Props) => { onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onUnselect={() => handleSelect(inputKey, null)} selectedValue={selections[inputKey]} // Load from Zustand + isLoading={isLoading} />
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx index 56ef990..1d16358 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/PieChartInput.tsx @@ -21,14 +21,17 @@ const PieChartInput = (props: Props) => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] + const [isLoading, setLoading] = useState(true); useEffect(() => { const fetchZoneData = async () => { try { + setLoading(true) const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); + setLoading(false) } else { console.log("Unexpected response:", response); } @@ -148,6 +151,7 @@ const PieChartInput = (props: Props) => { onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onUnselect={() => handleSelect(inputKey, null)} selectedValue={selections[inputKey]} // Load from Zustand + isLoading={isLoading} />
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx index cd347b3..5d9dd58 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress1Input.tsx @@ -21,14 +21,17 @@ const Progress1Input = (props: Props) => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] + const [isLoading, setLoading] = useState(true); useEffect(() => { const fetchZoneData = async () => { try { + setLoading(true) const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); + setLoading(false) } else { console.log("Unexpected response:", response); } @@ -142,6 +145,7 @@ const Progress1Input = (props: Props) => { onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onUnselect={() => handleSelect(inputKey, null)} selectedValue={selections[inputKey]} // Load from Zustand + isLoading={isLoading} />
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx index ba56e27..bc6059c 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Progress2Input.tsx @@ -21,14 +21,17 @@ const Progress2Input = (props: Props) => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] + const [isLoading, setLoading] = useState(true); useEffect(() => { const fetchZoneData = async () => { try { + setLoading(true) const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); + setLoading(false) } else { console.log("Unexpected response:", response); } @@ -142,6 +145,7 @@ const Progress2Input = (props: Props) => { onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onUnselect={() => handleSelect(inputKey, null)} selectedValue={selections[inputKey]} // Load from Zustand + isLoading={isLoading} />
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx index d3ed377..8d5e717 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/WarehouseThroughputInputComponent.tsx @@ -21,14 +21,17 @@ const WarehouseThroughputInputComponent = (props: Props) => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] + const [isLoading, setLoading] = useState(true); useEffect(() => { const fetchZoneData = async () => { try { + setLoading(true) const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); + setLoading(false) } else { console.log("Unexpected response:", response); } @@ -149,6 +152,7 @@ const WarehouseThroughputInputComponent = (props: Props) => { onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onUnselect={() => handleSelect(inputKey, null)} selectedValue={selections[inputKey]} // Load from Zustand + isLoading={isLoading} />
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx index a32d227..a1b4360 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget2InputCard3D.tsx @@ -21,14 +21,17 @@ const Widget2InputCard3D = (props: Props) => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] + const [isLoading, setLoading] = useState(true); useEffect(() => { const fetchZoneData = async () => { try { + setLoading(true) const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); + setLoading(false) } else { console.log("Unexpected response:", response); } @@ -147,6 +150,7 @@ const Widget2InputCard3D = (props: Props) => { onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onUnselect={() => handleSelect(inputKey, null)} selectedValue={selections[inputKey]} // Load from Zustand + isLoading={isLoading} />
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx index 95fdc33..43f8e55 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget3InputCard3D.tsx @@ -19,14 +19,17 @@ const Widget3InputCard3D = () => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] + const [isLoading, setLoading] = useState(true); useEffect(() => { const fetchZoneData = async () => { try { + setLoading(true) const response = await axios.get(`http://${iotApiUrl}/getinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); + setLoading(false) } else { console.log("Unexpected response:", response); } @@ -140,6 +143,7 @@ const Widget3InputCard3D = () => { onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onUnselect={() => handleSelect(inputKey, null)} selectedValue={selections[inputKey]} // Load from Zustand + isLoading={isLoading} />
diff --git a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx index bfd2edd..4aa9855 100644 --- a/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx +++ b/app/src/components/layout/sidebarRight/visualization/IotInputCards/Widget4InputCard3D.tsx @@ -21,14 +21,17 @@ const Widget4InputCard3D = (props: Props) => { const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0] + const [isLoading, setLoading] = useState(true); useEffect(() => { const fetchZoneData = async () => { try { + setLoading(true) const response = await axios.get(`http://${iotApiUrl}/floatinput`); if (response.status === 200) { // console.log("dropdown data:", response.data); setDropDownData(response.data); + setLoading(false) } else { console.log("Unexpected response:", response); } @@ -147,6 +150,7 @@ const Widget4InputCard3D = (props: Props) => { onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onUnselect={() => handleSelect(inputKey, null)} selectedValue={selections[inputKey]} // Load from Zustand + isLoading={isLoading} />
diff --git a/app/src/components/ui/inputs/InputWithDropDown.tsx b/app/src/components/ui/inputs/InputWithDropDown.tsx index b0fc135..b672313 100644 --- a/app/src/components/ui/inputs/InputWithDropDown.tsx +++ b/app/src/components/ui/inputs/InputWithDropDown.tsx @@ -4,7 +4,8 @@ import RenameInput from "./RenameInput"; type InputWithDropDownProps = { label: string; value: string; - min?: number + min?: number; + step?: number; defaultValue?: string; options?: string[]; // Array of dropdown options activeOption?: string; // The currently active dropdown option @@ -18,6 +19,7 @@ const InputWithDropDown: React.FC = ({ label, value, min, + step, defaultValue, options, activeOption, @@ -45,6 +47,7 @@ const InputWithDropDown: React.FC = ({
{ diff --git a/app/src/components/ui/inputs/MultiLevelDropDown.tsx b/app/src/components/ui/inputs/MultiLevelDropDown.tsx index f2be121..1293e4a 100644 --- a/app/src/components/ui/inputs/MultiLevelDropDown.tsx +++ b/app/src/components/ui/inputs/MultiLevelDropDown.tsx @@ -203,6 +203,7 @@ interface MultiLevelDropdownProps { onSelect: (selectedData: { name: string; fields: string }) => void; onUnselect: () => void; selectedValue?: { name: string; fields: string }; + isLoading?: boolean; } // Main Multi-Level Dropdown Component @@ -211,6 +212,7 @@ const MultiLevelDropdown = ({ onSelect, onUnselect, selectedValue, + isLoading = false, }: MultiLevelDropdownProps) => { const [open, setOpen] = useState(false); const dropdownRef = useRef(null); @@ -261,19 +263,22 @@ const MultiLevelDropdown = ({
{/* loading list */} - {/*
*/} + {/* Unselect Option */} {/* Nested Dropdown Items */} - {Object.entries(data).map(([key, value]) => ( - - ))} + { + isLoading ?
: + Object.entries(data).map(([key, value]) => ( + + )) + }
)} diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 97adec3..29b0627 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -5,7 +5,7 @@ import { toast } from 'react-toastify'; import TempLoader from './tempLoader'; import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils'; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { Socket } from 'socket.io-client'; @@ -225,7 +225,6 @@ async function handleModelLoad( eventData as SimulationTypes.ConveyorEventsSchema ]); - console.log('data: ', data); socket.emit("v2:model-asset:add", data); } else if (res.type === "Vehicle") { @@ -365,7 +364,7 @@ async function handleModelLoad( uuid: pointUUID, position: res.points.position as [number, number, number], rotation: res.points.rotation as [number, number, number], - actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 1, processes: [] }, + actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 0.2, processes: [] }, triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' }, connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] }, } diff --git a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts index e904947..dbd7e9b 100644 --- a/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts +++ b/app/src/modules/builder/geomentries/assets/deleteFloorItems.ts @@ -2,7 +2,7 @@ import { toast } from 'react-toastify'; import * as THREE from 'three'; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; import { Socket } from 'socket.io-client'; import { getFloorAssets } from '../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; diff --git a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts index 08bed84..7ca7db9 100644 --- a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts @@ -5,7 +5,7 @@ import * as THREE from 'three'; import * as CONSTANTS from '../../../types/world/worldConstants'; import { toast } from 'react-toastify'; import * as Types from "../../../types/world/worldTypes"; -import * as SimulationTypes from "../../..//types/simulation"; +import * as SimulationTypes from "../../../types/simulationTypes"; import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi'; import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; @@ -228,7 +228,6 @@ function processEventData(item: SimulationTypes.EventData, setSimulationStates: } else if (item.eventData?.type === 'StaticMachine') { const data: any = item.eventData; - item.eventData.points.position = [0, 1.5, 1] data.modeluuid = item.modeluuid; data.modelName = item.modelname; data.position = item.position; diff --git a/app/src/modules/scene/controls/selection/copyPasteControls.tsx b/app/src/modules/scene/controls/selection/copyPasteControls.tsx index 87ed646..1f83513 100644 --- a/app/src/modules/scene/controls/selection/copyPasteControls.tsx +++ b/app/src/modules/scene/controls/selection/copyPasteControls.tsx @@ -5,7 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, import { toast } from "react-toastify"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, selectionGroup, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => { @@ -332,8 +332,8 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas } else if (eventData.type === 'StaticMachine' && eventData) { const createStaticMachinePoint = () => { const pointUUID = THREE.MathUtils.generateUUID(); - const vehiclePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points; - const hasActions = vehiclePoint?.actions !== undefined; + const staticMachinePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points; + const hasActions = staticMachinePoint?.actions !== undefined; const defaultAction = { uuid: THREE.MathUtils.generateUUID(), @@ -344,11 +344,11 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas return { uuid: pointUUID, - position: vehiclePoint?.position, - // rotation: vehiclePoint?.rotation, + position: staticMachinePoint?.position, + rotation: staticMachinePoint?.rotation, actions: hasActions ? { - ...vehiclePoint.actions, + ...staticMachinePoint.actions, uuid: THREE.MathUtils.generateUUID() } : defaultAction, @@ -410,8 +410,8 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas } else if (eventData.type === 'ArmBot' && eventData) { const createArmBotPoint = () => { const pointUUID = THREE.MathUtils.generateUUID(); - const vehiclePoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points; - const hasActions = vehiclePoint?.actions !== undefined; + const armBotPoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points; + const hasActions = armBotPoint?.actions !== undefined; const defaultAction = { uuid: THREE.MathUtils.generateUUID(), @@ -422,18 +422,19 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas return { uuid: pointUUID, - position: vehiclePoint?.position, - // rotation: vehiclePoint?.rotation, + position: armBotPoint?.position, + rotation: armBotPoint?.rotation, actions: hasActions ? { - ...vehiclePoint.actions, - uuid: THREE.MathUtils.generateUUID() + ...armBotPoint.actions, + uuid: THREE.MathUtils.generateUUID(), + processes: [] } : defaultAction, triggers: { uuid: THREE.MathUtils.generateUUID(), - name: vehiclePoint.triggers.name, - type: vehiclePoint.triggers.type, + name: armBotPoint.triggers.name, + type: armBotPoint.triggers.type, }, connections: { source: { modelUUID: obj.uuid, pointUUID }, diff --git a/app/src/modules/scene/controls/selection/duplicationControls.tsx b/app/src/modules/scene/controls/selection/duplicationControls.tsx index 41bbce3..915cb61 100644 --- a/app/src/modules/scene/controls/selection/duplicationControls.tsx +++ b/app/src/modules/scene/controls/selection/duplicationControls.tsx @@ -5,7 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, import { toast } from "react-toastify"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; @@ -246,7 +246,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb return { uuid: pointUUID, position: vehiclePoint?.position, - // rotation: vehiclePoint?.rotation, + rotation: vehiclePoint?.rotation, actions: hasActions ? { ...vehiclePoint.actions, @@ -311,8 +311,8 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb } else if (eventData.type === 'StaticMachine' && eventData) { const createStaticMachinePoint = () => { const pointUUID = THREE.MathUtils.generateUUID(); - const vehiclePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points; - const hasActions = vehiclePoint?.actions !== undefined; + const staticMachinePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points; + const hasActions = staticMachinePoint?.actions !== undefined; const defaultAction = { uuid: THREE.MathUtils.generateUUID(), @@ -323,11 +323,11 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb return { uuid: pointUUID, - position: vehiclePoint?.position, - // rotation: vehiclePoint?.rotation, + position: staticMachinePoint?.position, + rotation: staticMachinePoint?.rotation, actions: hasActions ? { - ...vehiclePoint.actions, + ...staticMachinePoint.actions, uuid: THREE.MathUtils.generateUUID() } : defaultAction, @@ -389,8 +389,8 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb } else if (eventData.type === 'ArmBot' && eventData) { const createArmBotPoint = () => { const pointUUID = THREE.MathUtils.generateUUID(); - const vehiclePoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points; - const hasActions = vehiclePoint?.actions !== undefined; + const armBotPoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points; + const hasActions = armBotPoint?.actions !== undefined; const defaultAction = { uuid: THREE.MathUtils.generateUUID(), @@ -401,18 +401,19 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb return { uuid: pointUUID, - position: vehiclePoint?.position, - // rotation: vehiclePoint?.rotation, + position: armBotPoint?.position, + rotation: armBotPoint?.rotation, actions: hasActions ? { - ...vehiclePoint.actions, - uuid: THREE.MathUtils.generateUUID() + ...armBotPoint.actions, + uuid: THREE.MathUtils.generateUUID(), + processes: [] } : defaultAction, triggers: { uuid: THREE.MathUtils.generateUUID(), - name: vehiclePoint.triggers.name, - type: vehiclePoint.triggers.type, + name: armBotPoint.triggers.name, + type: armBotPoint.triggers.type, }, connections: { source: { modelUUID: obj.uuid, pointUUID }, diff --git a/app/src/modules/scene/controls/selection/moveControls.tsx b/app/src/modules/scene/controls/selection/moveControls.tsx index 99b9a43..d717ecb 100644 --- a/app/src/modules/scene/controls/selection/moveControls.tsx +++ b/app/src/modules/scene/controls/selection/moveControls.tsx @@ -5,7 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys"; function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) { diff --git a/app/src/modules/scene/controls/selection/rotateControls.tsx b/app/src/modules/scene/controls/selection/rotateControls.tsx index 611d14b..f602bc4 100644 --- a/app/src/modules/scene/controls/selection/rotateControls.tsx +++ b/app/src/modules/scene/controls/selection/rotateControls.tsx @@ -5,7 +5,7 @@ import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) { diff --git a/app/src/modules/scene/controls/selection/selectionControls.tsx b/app/src/modules/scene/controls/selection/selectionControls.tsx index d1eed1e..8998bc9 100644 --- a/app/src/modules/scene/controls/selection/selectionControls.tsx +++ b/app/src/modules/scene/controls/selection/selectionControls.tsx @@ -8,7 +8,7 @@ import BoundingBox from "./boundingBoxHelper"; import { toast } from "react-toastify"; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; import * as Types from "../../../../types/world/worldTypes"; -import * as SimulationTypes from "../../../../types/simulation"; +import * as SimulationTypes from "../../../../types/simulationTypes"; import DuplicationControls from "./duplicationControls"; import CopyPasteControls from "./copyPasteControls"; @@ -205,7 +205,6 @@ const SelectionControls: React.FC = () => { const organization = email ? email.split("@")[1].split(".")[0] : ""; updatedPaths.forEach(async (updatedPath) => { - if (updatedPath.type === "Conveyor") { // await setEventApi( // organization, @@ -224,9 +223,7 @@ const SelectionControls: React.FC = () => { }; socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "Vehicle") { - // await setEventApi( // organization, // updatedPath.modeluuid, @@ -240,9 +237,7 @@ const SelectionControls: React.FC = () => { }; socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "StaticMachine") { - // await setEventApi( // organization, // updatedPath.modeluuid, @@ -256,9 +251,7 @@ const SelectionControls: React.FC = () => { }; socket.emit("v2:model-asset:updateEventData", data); - } else if (updatedPath.type === "ArmBot") { - // await setEventApi( // organization, // updatedPath.modeluuid, @@ -273,239 +266,135 @@ const SelectionControls: React.FC = () => { socket.emit("v2:model-asset:updateEventData", data); } - }); }; - // const removeConnection = (modelUUID: any) => { - // - // const removedPath = simulationStates?.flatMap((state) => { - // let shouldInclude = false; + const removeConnections = (deletedModelUUIDs: string[]) => { - // if (state.type === "Conveyor") { - // state.points.forEach((point: any) => { - // const sourceMatch = - // point.connections?.source?.modelUUID === modelUUID; - // const targetMatch = point.connections?.targets?.some( - // (target: any) => target.modelUUID === modelUUID - // ); - - // if (sourceMatch || targetMatch) shouldInclude = true; - // }); - // } - - // if (state.type === "Vehicle") { - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => target.modelUUID === modelUUID - // ); - - // if (targetMatch) shouldInclude = true; - // } - - // if (state.type === "StaticMachine") { - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => target.modelUUID === modelUUID - // ); - - // if (targetMatch) shouldInclude = true; - // } - - // if (state.type === "ArmBot") { - // const sourceMatch = - // state.points.connections?.source?.modelUUID === modelUUID; - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => target.modelUUID === modelUUID - // ); - - // const processMatch = - // state.points.actions?.processes?.some( - // (process: any) => - // process.startPoint === modelUUID || process.endPoint === modelUUID - // ) ?? false; - - // if (sourceMatch || targetMatch || processMatch) shouldInclude = true; - // } - - // return shouldInclude ? [state] : []; - // }); - // updateBackend(removedPath); - // - // return removedPath; - // // updateBackend(updatedPaths); - - // // setSimulationStates(updatedStates); - // }; - // const removeConnection = (modelUUIDs: any[]) => { - // - // const removedPath = simulationStates?.flatMap((state) => { - // let shouldInclude = false; - - // if (state.type === "Conveyor") { - // state.points.forEach((point: any) => { - // const sourceMatch = modelUUIDs.includes( - // point.connections?.source?.modelUUID - // ); - // const targetMatch = point.connections?.targets?.some((target: any) => - // modelUUIDs.includes(target.modelUUID) - // ); - - // if (sourceMatch || targetMatch) shouldInclude = true; - // }); - // } - - // if (state.type === "Vehicle") { - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => modelUUIDs.includes(target.modelUUID) - // ); - - // if (targetMatch) shouldInclude = true; - // } - - // if (state.type === "StaticMachine") { - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => modelUUIDs.includes(target.modelUUID) - // ); - - // if (targetMatch) shouldInclude = true; - // } - - // if (state.type === "ArmBot") { - // const sourceMatch = modelUUIDs.includes( - // state.points.connections?.source?.modelUUID - // ); - // const targetMatch = state.points.connections?.targets?.some( - // (target: any) => modelUUIDs.includes(target.modelUUID) - // ); - - // const processMatch = - // state.points.actions?.processes?.some( - // (process: any) => - // modelUUIDs.includes(process.startPoint) || - // modelUUIDs.includes(process.endPoint) - // ) ?? false; - - // if (sourceMatch || targetMatch || processMatch) shouldInclude = true; - // } - - // return shouldInclude ? [state] : []; - // }); - // updateBackend(removedPath); - // - // return removedPath; - // }; - - const removeConnection = (modelUUIDs: any[]) => { - const removedPath = simulationStates?.flatMap((state: any) => { - let shouldInclude = false; - - // Conveyor type - if (state.type === "Conveyor") { - state.points.forEach((point: any) => { - const sourceMatch = modelUUIDs.includes(point.connections?.source?.modelUUID); - const targetMatch = point.connections?.targets?.some((target: any) => modelUUIDs.includes(target.modelUUID)); - - if (sourceMatch) { - point.connections.source = {}; - shouldInclude = true; - } - - if (targetMatch) { - point.connections.targets = []; - shouldInclude = true; - } - }); - } - - // Vehicle & StaticMachine types - if (state.type === "Vehicle") { - const targets = state.points?.connections?.targets || []; - const targetMatch = targets.some((target: any) => modelUUIDs.includes(target.modelUUID)); - - if (targetMatch) { - state.points.connections.targets = []; - shouldInclude = true; + const deletedPointUUIDs = new Set(); + simulationStates.forEach(state => { + if (deletedModelUUIDs.includes(state.modeluuid)) { + if (state.type === "Conveyor" && state.points) { + state.points.forEach(point => { + deletedPointUUIDs.add(point.uuid); + }); + } else if (state.points && 'uuid' in state.points) { + deletedPointUUIDs.add(state.points.uuid); } } - if (state.type === "StaticMachine") { - const targets = state.points?.connections?.targets || []; - const targetMatch = targets.some((target: any) => modelUUIDs.includes(target.modelUUID)); - - if (targetMatch) { - state.points.connections.targets = []; - shouldInclude = true; - } - } - - // ArmBot type - if (state.type === "ArmBot") { - const sourceMatch = modelUUIDs.includes(state.points.connections?.source?.modelUUID); - - const targetMatch = state.points.connections?.targets?.some( - (target: any) => modelUUIDs.includes(target.modelUUID) - ); - // state.points.actions.processes = state.points.actions.processes.filter( - // (process: any) => - // console.log( - // !modelUUIDs.includes(process.startPoint), - // !modelUUIDs.includes(process.endPoint), - // modelUUIDs, - // process.startPoint, - // process.endPoint - // ) - // ); - - // shouldInclude = true; - - // const processMatches = state.points.actions?.processes?.some( - // (process: any) => - // console.log( - // "process: ", - // process, - // modelUUIDs, - // process.startPoint, - // process.endPoint - // ) - // // modelUUIDs.includes(process.startPoint) || - // // modelUUIDs.includes(process.endPoint) - // ); - // const processMatch = state.points.actions?.processes?.some( - // (process: any) => - // modelUUIDs.includes(String(process.startPoint)) || - // modelUUIDs.includes(String(process.endPoint)) - // ); - - // console.log("processMatch: ", processMatch); - if (sourceMatch) { - state.points.connections.source = {}; - shouldInclude = true; - } - - if (targetMatch) { - state.points.connections.targets = - state.points.connections.targets.filter((target: any) => !modelUUIDs.includes(target.modelUUID)); - shouldInclude = true; - } - - // console.log("processMatch: ", processMatch); - // if (processMatch) { - // state.points.actions.processes = - // state.points.actions.processes.filter((process: any) => { - // const shouldRemove = - // modelUUIDs.includes(process.startPoint) || - // modelUUIDs.includes(process.endPoint); - - // console.log("shouldRemove: ", shouldRemove); - // return !shouldRemove; - // }); - // shouldInclude = true; - // } - } - - return shouldInclude ? [state] : []; }); - updateBackend(removedPath); - return removedPath; + const updatedStates = simulationStates.map((state) => { + // Handle Conveyor + if (state.type === "Conveyor") { + const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { + ...state, + points: state.points.map((point) => { + return { + ...point, + connections: { + ...point.connections, + targets: point.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }; + }), + }; + return updatedConveyor; + } + + // Handle Vehicle + else if (state.type === "Vehicle") { + const updatedVehicle: SimulationTypes.VehicleEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }, + }; + return updatedVehicle; + } + + // Handle StaticMachine + else if (state.type === "StaticMachine") { + const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = + { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }, + }; + return updatedStaticMachine; + } + + // Handle ArmBot + else if (state.type === "ArmBot") { + const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target: any) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + actions: { + ...state.points.actions, + processes: state.points.actions.processes?.filter((process) => { + // Check if trigger is from deleted model + const matchedStates = simulationStates.filter((s) => deletedModelUUIDs.includes(s.modeluuid)); + + if (matchedStates.length > 0) { + if (matchedStates[0]?.type === "StaticMachine") { + const trigPoints = matchedStates[0]?.points; + if (process.triggerId === trigPoints?.triggers?.uuid) { + return false; + } + } else if (matchedStates[0]?.type === "Conveyor") { + const trigPoints = matchedStates[0]?.points; + if (Array.isArray(trigPoints)) { + const nonEmptyTriggers = trigPoints.filter((point) => point && point.triggers && point.triggers.length > 0); + const allTriggerUUIDs = nonEmptyTriggers.flatMap((point) => point.triggers).map((trigger) => trigger.uuid); + if (allTriggerUUIDs.includes(process.triggerId)) { + return false; + } + } + } + } + + // Check if startPoint or endPoint is from deleted model + if (deletedPointUUIDs.has(process.startPoint) || deletedPointUUIDs.has(process.endPoint)) { + return false; + } + + return true; + }), + }, + }, + }; + return updatedArmBot; + } + + return state; + }); + + const filteredStates = updatedStates.filter((state) => !deletedModelUUIDs.includes(state.modeluuid)); + + updateBackend(filteredStates); + setSimulationStates(filteredStates); }; const deleteSelection = () => { @@ -551,65 +440,17 @@ const SelectionControls: React.FC = () => { }); setSimulationStates((prevEvents: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { - const updatedEvents = (prevEvents || []).filter( - (event) => event.modeluuid !== selectedMesh.uuid - ); + const updatedEvents = (prevEvents || []).filter((event) => event.modeluuid !== selectedMesh.uuid); return updatedEvents; - } - ); + }); itemsGroupRef.current?.remove(selectedMesh); }); const allUUIDs = selectedAssets.map((val: any) => val.uuid); - removeConnection(allUUIDs); + removeConnections(allUUIDs); - // const removedPath = simulationStates?.flatMap((path: any) => { - // let shouldInclude = false; - - // if (Array.isArray(path.points)) { - // path.points.forEach((point: any) => { - // const sourceMatch = - // point.connections?.source?.modelUUID === selectedAssets[0].uuid; - // const targetMatch = point.connections?.targets?.some( - // (target: any) => target.modelUUID === selectedAssets[0].uuid - // ); - - // if (sourceMatch) { - // point.connections.source = {}; - // shouldInclude = true; - // } - - // if (targetMatch) { - // point.connections.targets = []; - // shouldInclude = true; - // } - // }); - // } else { - // const sourceMatch = - // path.connections?.source?.modelUUID === selectedAssets[0].uuid; - // const targetMatch = path.connections?.targets?.some( - // (target: any) => target.modelUUID === selectedAssets[0].uuid - // ); - - // if (sourceMatch) { - // path.connections.source = {}; - // shouldInclude = true; - // } - - // if (targetMatch) { - // path.connections.targets = []; - // shouldInclude = true; - // } - // } - - // return shouldInclude ? [path] : []; - // }); - // updateBackend(removedPath); - - const updatedItems = floorItems.filter( - (item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid) - ); + const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid)); setFloorItems(updatedItems); } toast.success("Selected models removed!"); diff --git a/app/src/modules/simulation/armbot/ArmBot.tsx b/app/src/modules/simulation/armbot/ArmBot.tsx index db0f956..1c7bb83 100644 --- a/app/src/modules/simulation/armbot/ArmBot.tsx +++ b/app/src/modules/simulation/armbot/ArmBot.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { useThree } from "@react-three/fiber"; import useModuleStore from "../../../store/useModuleStore"; import { useSimulationStates } from "../../../store/store"; -import * as SimulationTypes from '../../../types/simulation'; +import * as SimulationTypes from '../../../types/simulationTypes'; import { ArmbotInstances } from "./ArmBotInstances"; interface ArmBotState { @@ -12,26 +12,47 @@ interface ArmBotState { status: string; material: string; triggerId: string; - connections: any + connections: { + source: { modelUUID: string; pointUUID: string }; + targets: { modelUUID: string; pointUUID: string }[]; + }; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; + isActive?: boolean; } -const ArmBot: React.FC = () => { +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} + +interface ArmBotProps { + armBots: ArmBotState[]; + setArmBots: React.Dispatch>; + setStaticMachines: React.Dispatch>; +} + +const ArmBot = ({ armBots, setArmBots, setStaticMachines }: ArmBotProps) => { const { activeModule } = useModuleStore(); const { scene } = useThree(); const { simulationStates } = useSimulationStates(); - const [armBots, setArmBots] = useState([]); useEffect(() => { const filtered = simulationStates.filter((s): s is SimulationTypes.ArmBotEventsSchema => s.type === "ArmBot"); - const initialStates: ArmBotState[] = filtered.map(bot => ({ - uuid: bot.modeluuid, - position: bot.position, - rotation: bot.rotation, - status: "idle", - material: "default", - triggerId: '', - connections: bot.points.connections - })); + const initialStates: ArmBotState[] = filtered + .filter(bot => bot.points.connections.targets.length > 0) + .map(bot => ({ + uuid: bot.modeluuid, + position: bot.position, + rotation: bot.rotation, + status: "idle", + material: "default", + triggerId: '', + actions: bot.points.actions, + connections: bot.points.connections + })); setArmBots(initialStates); }, [simulationStates]); @@ -53,6 +74,7 @@ const ArmBot: React.FC = () => { index={i} armBot={bot} setArmBots={setArmBots} + setStaticMachines={setStaticMachines} /> ))} diff --git a/app/src/modules/simulation/armbot/ArmBotInstances.tsx b/app/src/modules/simulation/armbot/ArmBotInstances.tsx index 6ce4e01..8d20a87 100644 --- a/app/src/modules/simulation/armbot/ArmBotInstances.tsx +++ b/app/src/modules/simulation/armbot/ArmBotInstances.tsx @@ -1,5 +1,15 @@ import IkInstances from "./IkInstances"; import armModel from "../../../assets/gltf-glb/rigged/ik_arm_4.glb"; +import { useEffect, useState } from "react"; +import { useThree } from "@react-three/fiber"; +import { Vector3 } from "three"; + +interface Process { + triggerId: string; + startPoint?: Vector3; + endPoint?: Vector3; + speed: number; +} interface ArmBotState { uuid: string; @@ -8,18 +18,74 @@ interface ArmBotState { status: string; material: string; triggerId: string; - connections: any + connections: { + source: { modelUUID: string; pointUUID: string }; + targets: { modelUUID: string; pointUUID: string }[]; + }; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; + isActive?: boolean; +} + +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; } interface ArmbotInstancesProps { index: number; armBot: ArmBotState; - setArmBots: (armBots: ArmBotState[]) => void; + setArmBots: React.Dispatch>; + setStaticMachines: React.Dispatch>; } -export const ArmbotInstances: React.FC = ({ index, armBot, setArmBots }) => { +export const ArmbotInstances: React.FC = ({ index, armBot, setArmBots, setStaticMachines }) => { + const { scene } = useThree(); + const [processes, setProcesses] = useState([]); + + useEffect(() => { + + if (armBot.actions.processes.length > 0) { + const mappedProcesses = armBot.actions.processes.map((process) => { + return { + triggerId: process.triggerId, + startPoint: scene.getObjectByProperty('uuid', process.startPoint)?.getWorldPosition(new Vector3()), + endPoint: scene.getObjectByProperty('uuid', process.endPoint)?.getWorldPosition(new Vector3()), + speed: armBot.actions.speed + }; + }); + setProcesses(mappedProcesses); + } else { + setProcesses([]); + } + }, [armBot, scene]); + + const updateArmBotStatus = (status: string) => { + setArmBots((prevArmBots) => { + return prevArmBots.map(bot => { + if (bot.uuid === armBot.uuid) { + return { ...bot, status, triggerId: status === 'idle' ? '' : armBot.triggerId }; + } + return bot; + }); + }); + }; return ( - + ); }; \ No newline at end of file diff --git a/app/src/modules/simulation/armbot/IKAnimationController.tsx b/app/src/modules/simulation/armbot/IKAnimationController.tsx index 45d482f..d0aaec1 100644 --- a/app/src/modules/simulation/armbot/IKAnimationController.tsx +++ b/app/src/modules/simulation/armbot/IKAnimationController.tsx @@ -1,96 +1,338 @@ -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useState, useRef } from "react"; import { useFrame } from "@react-three/fiber"; import * as THREE from "three"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import { useSimulationStates } from "../../../store/store"; + + +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} + +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + connections: { + source: { modelUUID: string; pointUUID: string }; + targets: { modelUUID: string; pointUUID: string }[]; + }; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; + isActive?: boolean; +} + +type IKAnimationControllerProps = { + ikSolver: any; + process: { + triggerId: string; + startPoint: THREE.Vector3; + endPoint: THREE.Vector3; + speed: number; + }[]; + selectedTrigger: string; + targetBoneName: string; + uuid: string; + logStatus: (status: string) => void; + groupRef: React.RefObject; + armBot: ArmBotState; + setArmBots: React.Dispatch>; + setStaticMachines: React.Dispatch>; + updateArmBotStatus: (status: string) => void; +} const IKAnimationController = ({ ikSolver, process, selectedTrigger, targetBoneName, -}: { - ikSolver: any; - process: { - trigger: string; - start: THREE.Vector3; - end: THREE.Vector3; - speed: number; - }[]; - selectedTrigger: string; - targetBoneName: string; -}) => { + uuid, + logStatus, + groupRef, + armBot, + setArmBots, + setStaticMachines, + updateArmBotStatus +}: IKAnimationControllerProps) => { const [progress, setProgress] = useState(0); + const [initialProgress, setInitialProgress] = useState(0); + const [needsInitialMovement, setNeedsInitialMovement] = useState(true); + const [isInitializing, setIsInitializing] = useState(true); const restSpeed = 0.1; + const restPosition = new THREE.Vector3(0, 2, 1.6); + const { isPlaying } = usePlayButtonStore(); + const { simulationStates } = useSimulationStates(); + + // Track previous states for comparison + const prevStateRef = useRef({ + isInitializing: true, + needsInitialMovement: true, + selectedTrigger: "", + progress: 0 + }); + + // Track previous status for comparison + const prevStatusRef = useRef(""); + + const initialCurveRef = useRef(null); + const initialStartPositionRef = useRef(null); useEffect(() => { setProgress(0); }, [selectedTrigger]); - const processedCurves = useMemo(() => { - const restPosition = new THREE.Vector3(0.2, 2.3, 1.6); - return process.map((p) => { - const mid = new THREE.Vector3( - (p.start.x + p.end.x) / 1, - Math.max(p.start.y, p.end.y) + 0.8, - (p.start.z + p.end.z) / 0.9 + useEffect(() => { + if (ikSolver) { + const targetBone = ikSolver.mesh.skeleton.bones.find( + (b: any) => b.name === targetBoneName ); - const points = [ - restPosition.clone(), - p.start.clone(), - mid.clone(), - p.end.clone(), - restPosition.clone(), - ]; - const curve = new THREE.CatmullRomCurve3(points); - const restToStartDist = points[0].distanceTo(points[1]); - const startToEndDist = points[1].distanceTo(points[3]); - const endToRestDist = points[3].distanceTo(points[4]); + if (targetBone) { + initialStartPositionRef.current = targetBone.position.clone(); + calculateInitialCurve(targetBone.position); + logStatus(`[Arm ${uuid}] Initializing IK system, starting position: ${targetBone.position.toArray()}`); + } + } + }, [ikSolver]); - const totalDist = restToStartDist + startToEndDist + endToRestDist; - const restToStartRange = [0, restToStartDist / totalDist]; - const startToEndRange = [ - restToStartRange[1], - restToStartRange[1] + startToEndDist / totalDist, - ]; - const endToRestRange = [startToEndRange[1], 1]; + // Log state changes + useEffect(() => { + const prev = prevStateRef.current; - return { - trigger: p.trigger, - curve, - speed: p.speed, - restToStartRange, - startToEndRange, - endToRestRange, - }; - }); - }, [process]); + if (prev.isInitializing !== isInitializing) { + if (!isInitializing) { + logStatus(`[Arm ${uuid}] Completed initialization, now at rest position`); + } + } + + if (prev.needsInitialMovement !== needsInitialMovement && !needsInitialMovement) { + logStatus(`[Arm ${uuid}] Reached rest position, ready for animation`); + + } + + if (prev.selectedTrigger !== selectedTrigger) { + logStatus(`[Arm ${uuid}] Processing new trigger: ${selectedTrigger}`); + + const currentProcess = process.find(p => p.triggerId === prev.selectedTrigger); + if (currentProcess) { + const triggerId = currentProcess.triggerId; + + const endPoint = armBot.actions.processes.find((process) => process.triggerId === triggerId)?.endPoint; + + // Search simulationStates for a StaticMachine or Conveyor that has a point matching this endPointId + const matchedMachine = simulationStates.find((state) => { + if (state.type === "Conveyor") { + // For Conveyor, points is an array + return (state).points.some( + (point) => point.uuid === endPoint + ); + } else if (state.type === "StaticMachine") { + // For StaticMachine, points is an object + return state.points.uuid === endPoint; + } + return false; + }); + + if (matchedMachine) { + // Log if the end point is a conveyor + if (matchedMachine.type === "Conveyor") { + logStatus(`[Arm ${uuid}] Reached end point which is a conveyor (${matchedMachine.modelName})`); + } else { + logStatus(`[Arm ${uuid}] Reached end point which is a static machine (${matchedMachine.modelName})`); + } + + if (matchedMachine.type === "StaticMachine") { + setStaticMachines((machines) => { + return machines.map((machine) => { + if (machine.uuid === matchedMachine.modeluuid) { + return { ...machine, status: "running" }; + } else { + return machine; + } + }); + }); + } + + if (matchedMachine.type === "Conveyor") { + setArmBots((prev) => + prev.map((arm) => { + if (arm.uuid === uuid) { + return { + ...arm, + isActive: false + }; + } + else { + return arm; + } + }) + ); + } + } + } + } + + // Update previous state + prevStateRef.current = { + isInitializing, + needsInitialMovement, + selectedTrigger, + progress + }; + }, [isInitializing, needsInitialMovement, selectedTrigger, progress]); + + const calculateInitialCurve = (startPosition: THREE.Vector3) => { + const direction = new THREE.Vector3().subVectors(restPosition, startPosition); + const distance = direction.length(); + direction.normalize(); + + const perpendicular = new THREE.Vector3(-direction.z, 0, direction.x).normalize(); + + const midHeight = 0.5; + const tiltAmount = 1; + const mid = new THREE.Vector3() + .addVectors(startPosition, restPosition) + .multiplyScalar(0.5) + .add(perpendicular.clone().multiplyScalar(distance * 0.3 * tiltAmount)) + .add(new THREE.Vector3(0, midHeight, 0)); + + initialCurveRef.current = new THREE.CatmullRomCurve3([ + startPosition, + new THREE.Vector3().lerpVectors(startPosition, mid, 0.33), + mid, + new THREE.Vector3().lerpVectors(mid, restPosition, 0.66), + restPosition + ]); + }; + + const processedCurves = useMemo(() => { + if (isPlaying) + return process.map((p) => { + const tempLift = 0.5; + const localStart = groupRef.current?.worldToLocal(p.startPoint.clone().add(new THREE.Vector3(0, tempLift, 0))); + const localEnd = groupRef.current?.worldToLocal(p.endPoint.clone().add(new THREE.Vector3(0, tempLift, 0))); + + if (localStart && localEnd) { + + const mid = new THREE.Vector3( + (localStart.x + localEnd.x) / 1, + Math.max(localStart.y, localEnd.y) + 0.8, + (localStart.z + localEnd.z) / 0.9 + ); + + const points = [ + restPosition.clone(), + localStart.clone(), + mid.clone(), + localEnd.clone(), + restPosition.clone(), + ]; + const curve = new THREE.CatmullRomCurve3(points); + const restToStartDist = points[0].distanceTo(points[1]); + const startToEndDist = points[1].distanceTo(points[3]); + const endToRestDist = points[3].distanceTo(points[4]); + + const totalDist = restToStartDist + startToEndDist + endToRestDist; + const restToStartRange = [0, restToStartDist / totalDist]; + const startToEndRange = [ + restToStartRange[1], + restToStartRange[1] + startToEndDist / totalDist, + ]; + const endToRestRange = [startToEndRange[1], 1]; + + return { + trigger: p.triggerId, + curve, + speed: p.speed, + restToStartRange, + startToEndRange, + endToRestRange, + }; + } + }); + }, [process, groupRef, isPlaying]); const activeCurve = useMemo(() => { - return processedCurves.find((c) => c.trigger === selectedTrigger); - }, [processedCurves, selectedTrigger]); + if (isPlaying && processedCurves) + return processedCurves.find((c) => c?.trigger === selectedTrigger); + }, [processedCurves, selectedTrigger, isPlaying]); + // Initial movement to rest position useFrame((_, delta) => { - if (!ikSolver || !activeCurve) return; + if (!ikSolver || !needsInitialMovement || !isInitializing || !initialCurveRef.current) return; - const { curve, speed, startToEndRange } = activeCurve; + const targetBone = ikSolver.mesh.skeleton.bones.find( + (b: any) => b.name === targetBoneName + ); + if (!targetBone) return; + + setInitialProgress((prev) => { + const next = prev + delta * 0.5; + if (next >= 1) { + targetBone.position.copy(restPosition); + setNeedsInitialMovement(false); + setIsInitializing(false); + return 1; + } + targetBone.position.copy(initialCurveRef.current!.getPoint(next)); + return next; + }); + + ikSolver.update(); + }); + + // Main animation loop + useFrame((_, delta) => { + if (!ikSolver || !activeCurve || isInitializing || !isPlaying) return; + + const { curve, speed, restToStartRange, startToEndRange, endToRestRange } = activeCurve; const targetBone = ikSolver.mesh.skeleton.bones.find( (b: any) => b.name === targetBoneName ); if (!targetBone) return; let currentSpeed = restSpeed; - if (progress >= startToEndRange[0] && progress < startToEndRange[1]) { + let currentStatus = "idle"; // Default status + + // Determine current phase and status + if (progress < restToStartRange[1]) { + currentSpeed = restSpeed; + currentStatus = "moving"; // Moving to start point + } else if (progress >= startToEndRange[0] && progress < startToEndRange[1]) { currentSpeed = speed; + currentStatus = "moving"; // Moving between points + } else if (progress >= endToRestRange[0] && progress < 1) { + currentSpeed = restSpeed; + currentStatus = "moving"; // Returning to rest + } else if (progress >= 1) { + currentStatus = "idle"; // Completed cycle } - setProgress((prev) => { - const next = prev + delta * currentSpeed; - if (next >= 1) { - targetBone.position.copy(curve.getPoint(1)); - return 1; - } - targetBone.position.copy(curve.getPoint(next)); - return next; - }); + // Update status when it changes + if (prevStatusRef.current !== currentStatus) { + updateArmBotStatus(currentStatus); + prevStatusRef.current = currentStatus; + } + + // Only update progress if we're not already at the end + if (progress < 1) { + setProgress((prev) => { + const next = prev + delta * currentSpeed; + return Math.min(next, 1); // Cap at 1 + }); + } + + // Update bone position based on progress + if (progress < 1) { + targetBone.position.copy(curve.getPoint(progress)); + } else { + targetBone.position.copy(curve.getPoint(1)); + } ikSolver.update(); }); @@ -98,4 +340,4 @@ const IKAnimationController = ({ return null; }; -export default IKAnimationController; +export default IKAnimationController; \ No newline at end of file diff --git a/app/src/modules/simulation/armbot/IkInstances.tsx b/app/src/modules/simulation/armbot/IkInstances.tsx index b789f3a..5a0d23b 100644 --- a/app/src/modules/simulation/armbot/IkInstances.tsx +++ b/app/src/modules/simulation/armbot/IkInstances.tsx @@ -1,43 +1,70 @@ import * as THREE from "three"; import { useEffect, useMemo, useRef, useState } from "react"; -import { useLoader } from "@react-three/fiber"; +import { useFrame, useLoader } from "@react-three/fiber"; 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 { CCDIKSolver, CCDIKHelper, } from "three/examples/jsm/animation/CCDIKSolver"; import IKAnimationController from "./IKAnimationController"; +import { TransformControls } from "@react-three/drei"; -const IkInstances = ({ modelUrl, position, rotation }: { modelUrl: string; position: [number, number, number]; rotation: [number, number, number]; }) => { +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} + +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + connections: { + source: { modelUUID: string; pointUUID: string }; + targets: { modelUUID: string; pointUUID: string }[]; + }; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; + isActive?: boolean; +} + +const IkInstances = ({ + uuid, + selectedTrigger, + modelUrl, + processes, + position, + rotation, + armBot, + setArmBots, + setStaticMachines, + updateArmBotStatus +}: { + uuid: string; + selectedTrigger: string; + modelUrl: string; + processes: any; + position: [number, number, number]; + rotation: [number, number, number]; + armBot: ArmBotState; + setArmBots: React.Dispatch>; + setStaticMachines: React.Dispatch>; + updateArmBotStatus: (status: string) => void; +}) => { const [ikSolver, setIkSolver] = useState(null); - const [selectedTrigger, setSelectedTrigger] = useState("idle"); 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/" - ); + 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 groupRef = useRef(null); - const [selectedArm, setSelectedArm] = useState(); const targetBoneName = "Target"; const skinnedMeshName = "link_0"; - const process = useMemo(() => [ - { - trigger: "Trigger1", - start: new THREE.Vector3(-0.75, 1.5, -2.2), - end: new THREE.Vector3(0, 1.2, 2.2), - speed: 0.25, - }, - { - trigger: "Trigger2", - start: new THREE.Vector3(0, 1.2, 2.2), - end: new THREE.Vector3(0.75, 1.5, -2.2), - speed: 0.22, - } - ], []); - useEffect(() => { if (!gltf) return; const OOI: any = {}; @@ -85,42 +112,37 @@ const IkInstances = ({ modelUrl, position, rotation }: { modelUrl: string; posit }, [gltf]); - useEffect(() => { - const triggers = ['Trigger1', 'Trigger2']; - let index = 0; - const cycleTriggers = setInterval(() => { - setSelectedTrigger(triggers[index]); - index = (index + 1) % triggers.length; - }, 10000); - - return () => clearInterval(cycleTriggers); - }, []); + const logStatus = (status: string) => { + // console.log(status); + } return ( <> { - e.stopPropagation(); - setSelectedArm(groupRef.current?.getObjectByName(targetBoneName)) - }} + position={position} + rotation={rotation} > - {/* {selectedArm && } */} ); }; diff --git a/app/src/modules/simulation/path/pathConnector.tsx b/app/src/modules/simulation/path/pathConnector.tsx index 0071240..3e35925 100644 --- a/app/src/modules/simulation/path/pathConnector.tsx +++ b/app/src/modules/simulation/path/pathConnector.tsx @@ -1,15 +1,15 @@ -import { useFrame, useThree } from '@react-three/fiber'; -import React, { useEffect, useRef, useState } from 'react'; -import * as THREE from 'three'; -import * as Types from '../../../types/world/worldTypes'; -import * as SimulationTypes from '../../../types/simulation'; -import { QuadraticBezierLine } from '@react-three/drei'; -import { useDeleteTool, useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore } from '../../../store/store'; -import useModuleStore from '../../../store/useModuleStore'; -import { usePlayButtonStore } from '../../../store/usePlayButtonStore'; -import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/setEventsApt'; +import { useFrame, useThree } from "@react-three/fiber"; +import React, { useEffect, useRef, useState } from "react"; +import * as THREE from "three"; +import * as Types from "../../../types/world/worldTypes"; +import * as SimulationTypes from "../../../types/simulationTypes"; +import { QuadraticBezierLine } from "@react-three/drei"; +import { useDeleteTool, useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore, } from "../../../store/store"; +import useModuleStore from "../../../store/useModuleStore"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import { setEventApi } from "../../../services/factoryBuilder/assest/floorAsset/setEventsApt"; -function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject }) { +function PathConnector({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObject; }) { const { activeModule } = useModuleStore(); const { gl, raycaster, scene, pointer, camera } = useThree(); const { deleteTool } = useDeleteTool(); @@ -21,84 +21,77 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const groupRefs = useRef<{ [key: string]: any }>({}); const [firstSelected, setFirstSelected] = useState<{ modelUUID: string; sphereUUID: string; position: THREE.Vector3; isCorner: boolean; } | null>(null); - const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3, end: THREE.Vector3, mid: THREE.Vector3 } | null>(null); - const [helperlineColor, setHelperLineColor] = useState('red'); + const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3; end: THREE.Vector3; mid: THREE.Vector3; } | null>(null); + const [helperlineColor, setHelperLineColor] = useState("red"); const [hoveredLineKey, setHoveredLineKey] = useState(null); const updatePathConnections = (fromModelUUID: string, fromPointUUID: string, toModelUUID: string, toPointUUID: string) => { - const updatedPaths = simulationStates.map(path => { - if (path.type === 'Conveyor') { + const updatedPaths = simulationStates.map((path) => { + if (path.type === "Conveyor") { // Handle outgoing connections from Conveyor if (path.modeluuid === fromModelUUID) { return { ...path, - points: path.points.map(point => { + points: path.points.map((point) => { if (point.uuid === fromPointUUID) { const newTarget = { modelUUID: toModelUUID, - pointUUID: toPointUUID + pointUUID: toPointUUID, }; const existingTargets = point.connections.targets || []; - if (!existingTargets.some(target => - target.modelUUID === newTarget.modelUUID && - target.pointUUID === newTarget.pointUUID - )) { + if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) { return { ...point, connections: { ...point.connections, - targets: [...existingTargets, newTarget] - } + targets: [...existingTargets, newTarget], + }, }; } } return point; - }) + }), }; } // Handle incoming connections to Conveyor else if (path.modeluuid === toModelUUID) { return { ...path, - points: path.points.map(point => { + points: path.points.map((point) => { if (point.uuid === toPointUUID) { const reverseTarget = { modelUUID: fromModelUUID, - pointUUID: fromPointUUID + pointUUID: fromPointUUID, }; const existingTargets = point.connections.targets || []; - if (!existingTargets.some(target => - target.modelUUID === reverseTarget.modelUUID && - target.pointUUID === reverseTarget.pointUUID - )) { + if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) { return { ...point, connections: { ...point.connections, - targets: [...existingTargets, reverseTarget] - } + targets: [...existingTargets, reverseTarget], + }, }; } } return point; - }) + }), }; } - } - else if (path.type === 'Vehicle') { + } else if (path.type === "Vehicle") { // Handle outgoing connections from Vehicle if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) { const newTarget = { modelUUID: toModelUUID, - pointUUID: toPointUUID + pointUUID: toPointUUID, }; const existingTargets = path.points.connections.targets || []; // Check if target is a Conveyor - const toPath = simulationStates.find(p => p.modeluuid === toModelUUID); - if (toPath?.type !== 'Conveyor') { + const toPath = simulationStates.find((p) => p.modeluuid === toModelUUID); + if (toPath?.type !== "Conveyor") { console.log("Vehicle can only connect to Conveyors"); return path; } @@ -109,19 +102,16 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec return path; } - if (!existingTargets.some(target => - target.modelUUID === newTarget.modelUUID && - target.pointUUID === newTarget.pointUUID - )) { + if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) { return { ...path, points: { ...path.points, connections: { ...path.points.connections, - targets: [...existingTargets, newTarget] - } - } + targets: [...existingTargets, newTarget], + }, + }, }; } } @@ -129,13 +119,13 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) { const reverseTarget = { modelUUID: fromModelUUID, - pointUUID: fromPointUUID + pointUUID: fromPointUUID, }; const existingTargets = path.points.connections.targets || []; // Check if source is a Conveyor - const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID); - if (fromPath?.type !== 'Conveyor') { + const fromPath = simulationStates.find((p) => p.modeluuid === fromModelUUID); + if (fromPath?.type !== "Conveyor") { console.log("Vehicle can only connect to Conveyors"); return path; } @@ -146,35 +136,31 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec return path; } - if (!existingTargets.some(target => - target.modelUUID === reverseTarget.modelUUID && - target.pointUUID === reverseTarget.pointUUID - )) { + if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) { return { ...path, points: { ...path.points, connections: { ...path.points.connections, - targets: [...existingTargets, reverseTarget] - } - } + targets: [...existingTargets, reverseTarget], + }, + }, }; } } return path; - } - else if (path.type === 'StaticMachine') { + } else if (path.type === "StaticMachine") { // Handle outgoing connections from StaticMachine if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) { const newTarget = { modelUUID: toModelUUID, - pointUUID: toPointUUID + pointUUID: toPointUUID, }; // Ensure target is an ArmBot - const toPath = simulationStates.find(p => p.modeluuid === toModelUUID); - if (toPath?.type !== 'ArmBot') { + const toPath = simulationStates.find((p) => p.modeluuid === toModelUUID); + if (toPath?.type !== "ArmBot") { console.log("StaticMachine can only connect to ArmBot"); return path; } @@ -187,19 +173,16 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec return path; } - if (!existingTargets.some(target => - target.modelUUID === newTarget.modelUUID && - target.pointUUID === newTarget.pointUUID - )) { + if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) { return { ...path, points: { ...path.points, connections: { ...path.points.connections, - targets: [...existingTargets, newTarget] - } - } + targets: [...existingTargets, newTarget], + }, + }, }; } } @@ -208,11 +191,11 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) { const reverseTarget = { modelUUID: fromModelUUID, - pointUUID: fromPointUUID + pointUUID: fromPointUUID, }; - const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID); - if (fromPath?.type !== 'ArmBot') { + const fromPath = simulationStates.find((p) => p.modeluuid === fromModelUUID); + if (fromPath?.type !== "ArmBot") { console.log("StaticMachine can only be connected from ArmBot"); return path; } @@ -224,63 +207,56 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec return path; } - if (!existingTargets.some(target => - target.modelUUID === reverseTarget.modelUUID && - target.pointUUID === reverseTarget.pointUUID - )) { + if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) { return { ...path, points: { ...path.points, connections: { ...path.points.connections, - targets: [...existingTargets, reverseTarget] - } - } + targets: [...existingTargets, reverseTarget], + }, + }, }; } } return path; - } - else if (path.type === 'ArmBot') { + } else if (path.type === "ArmBot") { // Handle outgoing connections from ArmBot if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) { const newTarget = { modelUUID: toModelUUID, - pointUUID: toPointUUID + pointUUID: toPointUUID, }; - const toPath = simulationStates.find(p => p.modeluuid === toModelUUID); + const toPath = simulationStates.find((p) => p.modeluuid === toModelUUID); if (!toPath) return path; const existingTargets = path.points.connections.targets || []; // Check if connecting to a StaticMachine and already connected to one - const alreadyConnectedToStatic = existingTargets.some(target => { - const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - return targetPath?.type === 'StaticMachine'; + const alreadyConnectedToStatic = existingTargets.some((target) => { + const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); + return targetPath?.type === "StaticMachine"; }); - if (toPath.type === 'StaticMachine') { + if (toPath.type === "StaticMachine") { if (alreadyConnectedToStatic) { console.log("ArmBot can only connect to one StaticMachine"); return path; } } - if (!existingTargets.some(target => - target.modelUUID === newTarget.modelUUID && - target.pointUUID === newTarget.pointUUID - )) { + if (!existingTargets.some((target) => target.modelUUID === newTarget.modelUUID && target.pointUUID === newTarget.pointUUID)) { return { ...path, points: { ...path.points, connections: { ...path.points.connections, - targets: [...existingTargets, newTarget] - } - } + targets: [...existingTargets, newTarget], + }, + }, }; } } @@ -289,39 +265,38 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) { const reverseTarget = { modelUUID: fromModelUUID, - pointUUID: fromPointUUID + pointUUID: fromPointUUID, }; - const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID); + const fromPath = simulationStates.find((p) => p.modeluuid === fromModelUUID); if (!fromPath) return path; const existingTargets = path.points.connections.targets || []; - const alreadyConnectedFromStatic = existingTargets.some(target => { - const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - return targetPath?.type === 'StaticMachine'; + const alreadyConnectedFromStatic = existingTargets.some((target) => { + const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); + return targetPath?.type === "StaticMachine"; }); - if (fromPath.type === 'StaticMachine') { + if (fromPath.type === "StaticMachine") { if (alreadyConnectedFromStatic) { - console.log("ArmBot can only be connected from one StaticMachine"); + console.log( + "ArmBot can only be connected from one StaticMachine" + ); return path; } } - if (!existingTargets.some(target => - target.modelUUID === reverseTarget.modelUUID && - target.pointUUID === reverseTarget.pointUUID - )) { + if (!existingTargets.some((target) => target.modelUUID === reverseTarget.modelUUID && target.pointUUID === reverseTarget.pointUUID)) { return { ...path, points: { ...path.points, connections: { ...path.points.connections, - targets: [...existingTargets, reverseTarget] - } - } + targets: [...existingTargets, reverseTarget], + }, + }, }; } } @@ -333,21 +308,18 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec setSimulationStates(updatedPaths); - const updatedPathDetails = updatedPaths.filter(path => - path.modeluuid === fromModelUUID || path.modeluuid === toModelUUID - ); + const updatedPathDetails = updatedPaths.filter((path) => path.modeluuid === fromModelUUID || path.modeluuid === toModelUUID); updateBackend(updatedPathDetails); }; - const updateBackend = async (updatedPaths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { + const updateBackend = async (updatedPaths: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { if (updatedPaths.length === 0) return; const email = localStorage.getItem("email"); const organization = email ? email.split("@")[1].split(".")[0] : ""; updatedPaths.forEach(async (updatedPath) => { - if (updatedPath.type === 'Conveyor') { - + if (updatedPath.type === "Conveyor") { // await setEventApi( // organization, // updatedPath.modeluuid, @@ -357,13 +329,15 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const data = { organization: organization, modeluuid: updatedPath.modeluuid, - eventData: { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } - } - - socket.emit('v2:model-asset:updateEventData', data); - - } else if (updatedPath.type === 'Vehicle') { + eventData: { + type: "Conveyor", + points: updatedPath.points, + speed: updatedPath.speed, + }, + }; + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "Vehicle") { // await setEventApi( // organization, // updatedPath.modeluuid, @@ -373,13 +347,11 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const data = { organization: organization, modeluuid: updatedPath.modeluuid, - eventData: { type: "Vehicle", points: updatedPath.points } - } - - socket.emit('v2:model-asset:updateEventData', data); - - } else if (updatedPath.type === 'StaticMachine') { + eventData: { type: "Vehicle", points: updatedPath.points }, + }; + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "StaticMachine") { // await setEventApi( // organization, // updatedPath.modeluuid, @@ -389,13 +361,11 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const data = { organization: organization, modeluuid: updatedPath.modeluuid, - eventData: { type: "StaticMachine", points: updatedPath.points } - } - - socket.emit('v2:model-asset:updateEventData', data); - - } else if (updatedPath.type === 'ArmBot') { + eventData: { type: "StaticMachine", points: updatedPath.points }, + }; + socket.emit("v2:model-asset:updateEventData", data); + } else if (updatedPath.type === "ArmBot") { // await setEventApi( // organization, // updatedPath.modeluuid, @@ -405,15 +375,13 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const data = { organization: organization, modeluuid: updatedPath.modeluuid, - eventData: { type: "ArmBot", points: updatedPath.points } - } - - socket.emit('v2:model-asset:updateEventData', data); + eventData: { type: "ArmBot", points: updatedPath.points }, + }; + socket.emit("v2:model-asset:updateEventData", data); } - }) - - } + }); + }; const handleAddConnection = (fromModelUUID: string, fromUUID: string, toModelUUID: string, toUUID: string) => { updatePathConnections(fromModelUUID, fromUUID, toModelUUID, toUUID); @@ -447,7 +415,10 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec if (drag || evt.button === 0) return; raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(pathsGroupRef.current.children, true); + const intersects = raycaster.intersectObjects( + pathsGroupRef.current.children, + true + ); if (intersects.length > 0) { const intersected = intersects[0].object; @@ -461,45 +432,46 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec let isStartOrEnd = false; if (intersected.userData.path.points && intersected.userData.path.points.length > 1) { - isStartOrEnd = intersected.userData.path.points.length > 0 && ( - sphereUUID === intersected.userData.path.points[0].uuid || - sphereUUID === intersected.userData.path.points[intersected.userData.path.points.length - 1].uuid - ); + isStartOrEnd = intersected.userData.path.points.length > 0 + && (sphereUUID === intersected.userData.path.points[0].uuid || sphereUUID === intersected.userData.path.points[intersected.userData.path.points.length - 1].uuid); } else if (intersected.userData.path.points) { isStartOrEnd = sphereUUID === intersected.userData.path.points.uuid; } if (modelUUID) { - const firstPath = simulationStates.find(p => p.modeluuid === firstSelected?.modelUUID); - const secondPath = simulationStates.find(p => p.modeluuid === modelUUID); + const firstPath = simulationStates.find((p) => p.modeluuid === firstSelected?.modelUUID); + const secondPath = simulationStates.find((p) => p.modeluuid === modelUUID); // Prevent vehicle-to-vehicle connections - if (firstPath && secondPath && firstPath.type === 'Vehicle' && secondPath.type === 'Vehicle') { + if (firstPath && secondPath && firstPath.type === "Vehicle" && secondPath.type === "Vehicle") { console.log("Cannot connect two vehicle paths together"); return; } // Prevent conveyor middle point to conveyor connections - if (firstPath && secondPath && - firstPath.type === 'Conveyor' && - secondPath.type === 'Conveyor' && - (!firstSelected?.isCorner || !isStartOrEnd)) { + if (firstPath && secondPath && firstPath.type === "Conveyor" && secondPath.type === "Conveyor" && (!firstSelected?.isCorner || !isStartOrEnd)) { console.log("Conveyor connections must be between start/end points"); return; } // Check if this specific connection already exists const isDuplicateConnection = firstSelected - ? simulationStates.some(path => { + ? simulationStates.some((path) => { if (path.modeluuid === firstSelected.modelUUID) { - if (path.type === 'Conveyor') { - const point = path.points.find(p => p.uuid === firstSelected.sphereUUID); - return point?.connections.targets.some(t => - t.modelUUID === modelUUID && t.pointUUID === sphereUUID + if (path.type === "Conveyor") { + const point = path.points.find( + (p) => p.uuid === firstSelected.sphereUUID ); - } else if (path.type === 'Vehicle') { - return path.points.connections.targets.some(t => - t.modelUUID === modelUUID && t.pointUUID === sphereUUID + return point?.connections.targets.some( + (t) => + t.modelUUID === modelUUID && + t.pointUUID === sphereUUID + ); + } else if (path.type === "Vehicle") { + return path.points.connections.targets.some( + (t) => + t.modelUUID === modelUUID && + t.pointUUID === sphereUUID ); } } @@ -513,8 +485,9 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec } // For Vehicles, check if they're already connected to anything - if (intersected.userData.path.type === 'Vehicle') { - const vehicleConnections = intersected.userData.path.points.connections.targets.length; + if (intersected.userData.path.type === "Vehicle") { + const vehicleConnections = + intersected.userData.path.points.connections.targets.length; if (vehicleConnections >= 1) { console.log("Vehicle can only have one connection"); return; @@ -522,12 +495,13 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec } // For non-Vehicle paths, check if already connected - if (intersected.userData.path.type !== 'Vehicle') { - const isAlreadyConnected = simulationStates.some(path => { - if (path.type === 'Conveyor') { - return path.points.some(point => - point.uuid === sphereUUID && - point.connections.targets.length > 0 + if (intersected.userData.path.type !== "Vehicle") { + const isAlreadyConnected = simulationStates.some((path) => { + if (path.type === "Conveyor") { + return path.points.some( + (point) => + point.uuid === sphereUUID && + point.connections.targets.length > 0 ); } return false; @@ -541,8 +515,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec if (firstSelected) { // Check if trying to connect Vehicle to non-Conveyor - if ((firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') || - (secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor')) { + if ((firstPath?.type === "Vehicle" && secondPath?.type !== "Conveyor") || (secondPath?.type === "Vehicle" && firstPath?.type !== "Conveyor")) { console.log("Vehicle can only connect to Conveyors"); return; } @@ -554,21 +527,20 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec } // Check if StaticMachine is involved in the connection - if ((firstPath?.type === 'StaticMachine' && secondPath?.type !== 'ArmBot') || - (secondPath?.type === 'StaticMachine' && firstPath?.type !== 'ArmBot')) { + if ((firstPath?.type === "StaticMachine" && secondPath?.type !== "ArmBot") || (secondPath?.type === "StaticMachine" && firstPath?.type !== "ArmBot")) { console.log("StaticMachine can only connect to ArmBot"); return; } // Check if StaticMachine already has a connection - if (firstPath?.type === 'StaticMachine') { + if (firstPath?.type === "StaticMachine") { const staticConnections = firstPath.points.connections.targets.length; if (staticConnections >= 1) { console.log("StaticMachine can only have one connection"); return; } } - if (secondPath?.type === 'StaticMachine') { + if (secondPath?.type === "StaticMachine") { const staticConnections = secondPath.points.connections.targets.length; if (staticConnections >= 1) { console.log("StaticMachine can only have one connection"); @@ -577,17 +549,17 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec } // Check if ArmBot is involved - if ((firstPath?.type === 'ArmBot' && secondPath?.type === 'StaticMachine') || - (secondPath?.type === 'ArmBot' && firstPath?.type === 'StaticMachine')) { - - const armBotPath = firstPath?.type === 'ArmBot' ? firstPath : secondPath; - const staticPath = firstPath?.type === 'StaticMachine' ? firstPath : secondPath; + if ((firstPath?.type === "ArmBot" && secondPath?.type === "StaticMachine") || (secondPath?.type === "ArmBot" && firstPath?.type === "StaticMachine")) { + const armBotPath = firstPath?.type === "ArmBot" ? firstPath : secondPath; + const staticPath = firstPath?.type === "StaticMachine" ? firstPath : secondPath; const armBotConnections = armBotPath.points.connections.targets || []; - const alreadyConnectedToStatic = armBotConnections.some(target => { - const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - return targetPath?.type === 'StaticMachine'; - }); + const alreadyConnectedToStatic = armBotConnections.some( + (target) => { + const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); + return targetPath?.type === "StaticMachine"; + } + ); if (alreadyConnectedToStatic) { console.log("ArmBot can only connect to one StaticMachine"); @@ -602,15 +574,15 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec } // Prevent ArmBot ↔ ArmBot - if (firstPath?.type === 'ArmBot' && secondPath?.type === 'ArmBot') { + if (firstPath?.type === "ArmBot" && secondPath?.type === "ArmBot") { console.log("Cannot connect two ArmBots together"); return; } // If one is ArmBot, ensure the other is StaticMachine or Conveyor - if (firstPath?.type === 'ArmBot' || secondPath?.type === 'ArmBot') { - const otherType = firstPath?.type === 'ArmBot' ? secondPath?.type : firstPath?.type; - if (otherType !== 'StaticMachine' && otherType !== 'Conveyor') { + if (firstPath?.type === "ArmBot" || secondPath?.type === "ArmBot") { + const otherType = firstPath?.type === "ArmBot" ? secondPath?.type : firstPath?.type; + if (otherType !== "StaticMachine" && otherType !== "Conveyor") { console.log("ArmBot can only connect to Conveyors or one StaticMachine"); return; } @@ -639,7 +611,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec } }; - if (activeModule === 'simulation' && !deleteTool) { + if (activeModule === "simulation" && !deleteTool) { canvasElement.addEventListener("mousedown", onMouseDown); canvasElement.addEventListener("mouseup", onMouseUp); canvasElement.addEventListener("mousemove", onMouseMove); @@ -662,7 +634,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec Object.values(groupRefs.current).forEach((group) => { if (group) { const distance = new THREE.Vector3(...group.position.toArray()).distanceTo(camera.position); - group.visible = ((distance <= renderDistance) && !isPlaying); + group.visible = distance <= renderDistance && !isPlaying; } }); }); @@ -670,16 +642,17 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec useFrame(() => { if (firstSelected) { raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(scene.children, true).filter((intersect) => - !intersect.object.name.includes("Roof") && - !intersect.object.name.includes("agv-collider") && - !intersect.object.name.includes("MeasurementReference") && - !intersect.object.userData.isPathObject && - !(intersect.object.type === "GridHelper") + const intersects = raycaster.intersectObjects(scene.children, true).filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("agv-collider") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.userData.isPathObject && + !(intersect.object.type === "GridHelper") ); let point: THREE.Vector3 | null = null; - let snappedSphere: { sphereUUID: string, position: THREE.Vector3, modelUUID: string, isCorner: boolean } | null = null; + let snappedSphere: { sphereUUID: string; position: THREE.Vector3; modelUUID: string; isCorner: boolean; } | null = null; let isInvalidConnection = false; if (intersects.length > 0) { @@ -699,9 +672,9 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const pathData = sphere.userData.path; const modelUUID = pathData.modeluuid; - const firstPath = simulationStates.find(p => p.modeluuid === firstSelected.modelUUID); - const secondPath = simulationStates.find(p => p.modeluuid === modelUUID); - const isVehicleToVehicle = firstPath?.type === 'Vehicle' && secondPath?.type === 'Vehicle'; + const firstPath = simulationStates.find((p) => p.modeluuid === firstSelected.modelUUID); + const secondPath = simulationStates.find((p) => p.modeluuid === modelUUID); + const isVehicleToVehicle = firstPath?.type === "Vehicle" && secondPath?.type === "Vehicle"; // Inside the useFrame hook, where we check for snapped spheres: const isConnectable = ( @@ -816,7 +789,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec } else { isInvalidConnection = true; } - } if (snappedSphere) { @@ -839,9 +811,9 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec }); if (sphereIntersects.length > 0) { - setHelperLineColor(isInvalidConnection ? 'red' : '#6cf542'); + setHelperLineColor(isInvalidConnection ? "red" : "#6cf542"); } else { - setHelperLineColor('yellow'); + setHelperLineColor("yellow"); } } else { setCurrentLine(null); @@ -854,118 +826,124 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec }); const removeConnection = (connection1: { model: string; point: string }, connection2: { model: string; point: string }) => { - const updatedStates = simulationStates.map(state => { + const updatedStates = simulationStates.map((state) => { + // Handle Conveyor (which has multiple points) - if (state.type === 'Conveyor') { + if (state.type === "Conveyor") { const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { ...state, - points: state.points.map(point => { + points: state.points.map((point) => { // Check if this point is either connection1 or connection2 - if ((state.modeluuid === connection1.model && point.uuid === connection1.point) || - (state.modeluuid === connection2.model && point.uuid === connection2.point)) { - + if ((state.modeluuid === connection1.model && point.uuid === connection1.point) || (state.modeluuid === connection2.model && point.uuid === connection2.point)) { return { ...point, connections: { ...point.connections, - targets: point.connections.targets.filter(target => { + targets: point.connections.targets.filter((target) => { // Remove the target that matches the other connection return !( - (target.modelUUID === connection1.model && target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && target.pointUUID === connection2.point) + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) ); - }) - } + }), + }, }; } return point; - }) + }), }; return updatedConveyor; } - // Handle Vehicle - else if (state.type === 'Vehicle') { - if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || - (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { + // Handle Vehicle + else if (state.type === "Vehicle") { + if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { const updatedVehicle: SimulationTypes.VehicleEventsSchema = { ...state, points: { ...state.points, connections: { ...state.points.connections, - targets: state.points.connections.targets.filter(target => { + targets: state.points.connections.targets.filter((target) => { return !( - (target.modelUUID === connection1.model && target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && target.pointUUID === connection2.point) + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) ); - }) + }), }, // Ensure all required Vehicle point properties are included speed: state.points.speed, - actions: state.points.actions - } + actions: state.points.actions, + }, }; return updatedVehicle; } } - // Handle StaticMachine - else if (state.type === 'StaticMachine') { - if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || - (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { - const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = { + // Handle StaticMachine + else if (state.type === "StaticMachine") { + if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { + const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = + { ...state, points: { ...state.points, connections: { ...state.points.connections, - targets: state.points.connections.targets.filter(target => { + targets: state.points.connections.targets.filter((target) => { return !( - (target.modelUUID === connection1.model && target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && target.pointUUID === connection2.point) + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) ); - }) + }), }, // Ensure all required StaticMachine point properties are included actions: state.points.actions, - triggers: state.points.triggers - } + triggers: state.points.triggers, + }, }; return updatedStaticMachine; } } - // Handle ArmBot - else if (state.type === 'ArmBot') { - if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || - (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { + // Handle ArmBot + else if (state.type === "ArmBot") { + if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) || (state.modeluuid === connection2.model && state.points.uuid === connection2.point)) { const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { ...state, points: { ...state.points, connections: { ...state.points.connections, - targets: state.points.connections.targets.filter(target => { + targets: state.points.connections.targets.filter((target) => { return !( - (target.modelUUID === connection1.model && target.pointUUID === connection1.point) || - (target.modelUUID === connection2.model && target.pointUUID === connection2.point) + (target.modelUUID === connection1.model && + target.pointUUID === connection1.point) || + (target.modelUUID === connection2.model && + target.pointUUID === connection2.point) ); - }) + }), }, actions: { ...state.points.actions, - processes: state.points.actions.processes?.filter(process => { - return !( - process.startPoint === connection1.point || - process.endPoint === connection1.point || - process.startPoint === connection2.point || - process.endPoint === connection2.point - ); - }) || [] + processes: + state.points.actions.processes?.filter((process) => { + return !( + process.startPoint === connection1.point || + process.endPoint === connection1.point || + process.startPoint === connection2.point || + process.endPoint === connection2.point + ); + }) || [], }, - triggers: state.points.triggers - } + triggers: state.points.triggers, + }, }; return updatedArmBot; } @@ -973,27 +951,157 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec return state; }); - const updatedPaths = updatedStates.filter(state => - state.modeluuid === connection1.model || state.modeluuid === connection2.model + const updatedPaths = updatedStates.filter( + (state) => + state.modeluuid === connection1.model || + state.modeluuid === connection2.model ); + console.log("updatedPaths: ", updatedPaths); updateBackend(updatedPaths); setSimulationStates(updatedStates); }; + const removeConnections = (deletedModelUUIDs: string[]) => { + + const deletedPointUUIDs = new Set(); + simulationStates.forEach(state => { + if (deletedModelUUIDs.includes(state.modeluuid)) { + if (state.type === "Conveyor" && state.points) { + state.points.forEach(point => { + deletedPointUUIDs.add(point.uuid); + }); + } else if (state.points && 'uuid' in state.points) { + deletedPointUUIDs.add(state.points.uuid); + } + } + }); + + const updatedStates = simulationStates.map((state) => { + // Handle Conveyor + if (state.type === "Conveyor") { + const updatedConveyor: SimulationTypes.ConveyorEventsSchema = { + ...state, + points: state.points.map((point) => { + return { + ...point, + connections: { + ...point.connections, + targets: point.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }; + }), + }; + return updatedConveyor; + } + + // Handle Vehicle + else if (state.type === "Vehicle") { + const updatedVehicle: SimulationTypes.VehicleEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }, + }; + return updatedVehicle; + } + + // Handle StaticMachine + else if (state.type === "StaticMachine") { + const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = + { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + }, + }; + return updatedStaticMachine; + } + + // Handle ArmBot + else if (state.type === "ArmBot") { + const updatedArmBot: SimulationTypes.ArmBotEventsSchema = { + ...state, + points: { + ...state.points, + connections: { + ...state.points.connections, + targets: state.points.connections.targets.filter( + (target: any) => !deletedModelUUIDs.includes(target.modelUUID) + ), + }, + actions: { + ...state.points.actions, + processes: state.points.actions.processes?.filter((process) => { + // Check if trigger is from deleted model + const matchedStates = simulationStates.filter((s) => deletedModelUUIDs.includes(s.modeluuid)); + + if (matchedStates.length > 0) { + if (matchedStates[0]?.type === "StaticMachine") { + const trigPoints = matchedStates[0]?.points; + if (process.triggerId === trigPoints?.triggers?.uuid) { + return false; + } + } else if (matchedStates[0]?.type === "Conveyor") { + const trigPoints = matchedStates[0]?.points; + if (Array.isArray(trigPoints)) { + const nonEmptyTriggers = trigPoints.filter((point) => point && point.triggers && point.triggers.length > 0); + const allTriggerUUIDs = nonEmptyTriggers.flatMap((point) => point.triggers).map((trigger) => trigger.uuid); + if (allTriggerUUIDs.includes(process.triggerId)) { + return false; + } + } + } + } + + // Check if startPoint or endPoint is from deleted model + if (deletedPointUUIDs.has(process.startPoint) || deletedPointUUIDs.has(process.endPoint)) { + return false; + } + + return true; + }), + }, + }, + }; + return updatedArmBot; + } + + return state; + }); + + const filteredStates = updatedStates.filter((state) => !deletedModelUUIDs.includes(state.modeluuid)); + + updateBackend(filteredStates); + setSimulationStates(filteredStates); + }; return ( - - {simulationStates.flatMap(path => { - if (path.type === 'Conveyor') { - return path.points.flatMap(point => + + {simulationStates.flatMap((path) => { + if (path.type === "Conveyor") { + return path.points.flatMap((point) => point.connections.targets.map((target, index) => { - const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - if (targetPath?.type !== 'Conveyor' && targetPath?.type !== 'ArmBot') return null; + const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); + if (targetPath?.type !== "Conveyor" && targetPath?.type !== "ArmBot") return null; - const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', point.uuid); - const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID); + const fromSphere = pathsGroupRef.current?.getObjectByProperty("uuid", point.uuid); + const toSphere = pathsGroupRef.current?.getObjectByProperty("uuid", target.pointUUID); if (fromSphere && toSphere) { const fromWorldPosition = new THREE.Vector3(); @@ -1003,7 +1111,12 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const distance = fromWorldPosition.distanceTo(toWorldPosition); const heightFactor = Math.max(0.5, distance * 0.2); - const midPoint = new THREE.Vector3((fromWorldPosition.x + toWorldPosition.x) / 2, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, (fromWorldPosition.z + toWorldPosition.z) / 2); + const midPoint = new THREE.Vector3( + (fromWorldPosition.x + toWorldPosition.x) / 2, + Math.max(fromWorldPosition.y, toWorldPosition.y) + + heightFactor, + (fromWorldPosition.z + toWorldPosition.z) / 2 + ); return ( setHoveredLineKey(`${point.uuid}-${target.pointUUID}-${index}`)} onPointerOut={() => setHoveredLineKey(null)} onClick={() => { if (deleteTool) { + const connection1 = { + model: path.modeluuid, + point: point.uuid, + }; + const connection2 = { + model: target.modelUUID, + point: target.pointUUID, + }; - const connection1 = { model: path.modeluuid, point: point.uuid } - const connection2 = { model: target.modelUUID, point: target.pointUUID } - - removeConnection(connection1, connection2) - + removeConnection(connection1, connection2); } }} - depthWrite={false} userData={target} /> ); @@ -1045,10 +1155,10 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec ); } - if (path.type === 'Vehicle') { + if (path.type === "Vehicle") { return path.points.connections.targets.map((target, index) => { - const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.points.uuid); - const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID); + const fromSphere = pathsGroupRef.current?.getObjectByProperty("uuid", path.points.uuid); + const toSphere = pathsGroupRef.current?.getObjectByProperty("uuid", target.pointUUID); if (fromSphere && toSphere) { const fromWorldPosition = new THREE.Vector3(); @@ -1058,7 +1168,11 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const distance = fromWorldPosition.distanceTo(toWorldPosition); const heightFactor = Math.max(0.5, distance * 0.2); - const midPoint = new THREE.Vector3((fromWorldPosition.x + toWorldPosition.x) / 2, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, (fromWorldPosition.z + toWorldPosition.z) / 2); + const midPoint = new THREE.Vector3( + (fromWorldPosition.x + toWorldPosition.x) / 2, + Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, + (fromWorldPosition.z + toWorldPosition.z) / 2 + ); return ( setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)} onPointerOut={() => setHoveredLineKey(null)} onClick={() => { if (deleteTool) { + const connection1 = { + model: path.modeluuid, + point: path.points.uuid, + }; + const connection2 = { + model: target.modelUUID, + point: target.pointUUID, + }; - const connection1 = { model: path.modeluuid, point: path.points.uuid } - const connection2 = { model: target.modelUUID, point: target.pointUUID } - - removeConnection(connection1, connection2) + removeConnection(connection1, connection2); } }} - depthWrite={false} userData={target} /> ); @@ -1096,13 +1210,13 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec }); } - if (path.type === 'StaticMachine') { + if (path.type === "StaticMachine") { return path.points.connections.targets.map((target, index) => { - const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID); - if (targetPath?.type !== 'ArmBot') return null; + const targetPath = simulationStates.find((p) => p.modeluuid === target.modelUUID); + if (targetPath?.type !== "ArmBot") return null; - const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.points.uuid); - const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID); + const fromSphere = pathsGroupRef.current?.getObjectByProperty("uuid", path.points.uuid); + const toSphere = pathsGroupRef.current?.getObjectByProperty("uuid", target.pointUUID); if (fromSphere && toSphere) { const fromWorldPosition = new THREE.Vector3(); @@ -1112,7 +1226,11 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec const distance = fromWorldPosition.distanceTo(toWorldPosition); const heightFactor = Math.max(0.5, distance * 0.2); - const midPoint = new THREE.Vector3((fromWorldPosition.x + toWorldPosition.x) / 2, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, (fromWorldPosition.z + toWorldPosition.z) / 2); + const midPoint = new THREE.Vector3( + (fromWorldPosition.x + toWorldPosition.x) / 2, + Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, + (fromWorldPosition.z + toWorldPosition.z) / 2 + ); return ( setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)} onPointerOut={() => setHoveredLineKey(null)} onClick={() => { if (deleteTool) { + const connection1 = { + model: path.modeluuid, + point: path.points.uuid, + }; + const connection2 = { + model: target.modelUUID, + point: target.pointUUID, + }; - const connection1 = { model: path.modeluuid, point: path.points.uuid } - const connection2 = { model: target.modelUUID, point: target.pointUUID } - - removeConnection(connection1, connection2) - + removeConnection(connection1, connection2); } }} - depthWrite={false} userData={target} /> ); @@ -1164,12 +1281,10 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec dashed dashSize={1} dashScale={20} - depthWrite={false} /> )} ); - } -export default PathConnector; \ No newline at end of file +export default PathConnector; diff --git a/app/src/modules/simulation/path/pathCreation.tsx b/app/src/modules/simulation/path/pathCreation.tsx index 4a8da49..1e9601d 100644 --- a/app/src/modules/simulation/path/pathCreation.tsx +++ b/app/src/modules/simulation/path/pathCreation.tsx @@ -1,5 +1,5 @@ import * as THREE from "three"; -import * as SimulationTypes from "../../../types/simulation"; +import * as SimulationTypes from "../../../types/simulationTypes"; import { useRef, useState, useEffect, useMemo } from "react"; import { Sphere, TransformControls } from "@react-three/drei"; import { @@ -66,45 +66,6 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec }); }); - const updateSimulationPaths = () => { - if (!selectedActionSphere) return; - - const updatedPaths = simulationStates.map((path) => { - if (path.type === "Conveyor") { - return { - ...path, - points: path.points.map((point) => - point.uuid === selectedActionSphere.points.uuid - ? { - ...point, - position: [ - selectedActionSphere.points.position.x, - selectedActionSphere.points.position.y, - selectedActionSphere.points.position.z, - ], - rotation: [ - selectedActionSphere.points.rotation.x, - selectedActionSphere.points.rotation.y, - selectedActionSphere.points.rotation.z, - ], - } - : point - ), - }; - } else { - return path; - } - }) as SimulationTypes.ConveyorEventsSchema[]; - - const updatedPath = updatedPaths.find( - (path) => path.type === "Conveyor" && path.points.some((point) => point.uuid === selectedActionSphere.points.uuid) - ); - - // console.log("Updated Path:", updatedPath); - - setSimulationStates(updatedPaths); - }; - useFrame(() => { if (eyeDropMode) { raycaster.setFromCamera(pointer, camera); @@ -445,7 +406,6 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec ref={transformRef} object={selectedActionSphere.points} mode={transformMode} - onMouseUp={updateSimulationPaths} /> )} diff --git a/app/src/modules/simulation/process/processAnimator.tsx b/app/src/modules/simulation/process/processAnimator.tsx index 65bc4c5..19e55b8 100644 --- a/app/src/modules/simulation/process/processAnimator.tsx +++ b/app/src/modules/simulation/process/processAnimator.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useEffect, useMemo } from "react"; +import React, { useRef, useEffect, useMemo, useCallback } from "react"; import { useLoader, useFrame } from "@react-three/fiber"; import { GLTFLoader } from "three-stdlib"; import * as THREE from "three"; @@ -10,11 +10,27 @@ import ProcessObject from "./processObject"; import { ProcessData } from "./types"; +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + connections: { + source: { modelUUID: string; pointUUID: string }; + targets: { modelUUID: string; pointUUID: string }[]; + }; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; + isActive?: boolean; +} interface ProcessContainerProps { processes: ProcessData[]; setProcesses: React.Dispatch>; agvRef: any; MaterialRef: any; + armBots: ArmBotState[]; + setArmBots: React.Dispatch>; } const ProcessAnimator: React.FC = ({ @@ -22,6 +38,8 @@ const ProcessAnimator: React.FC = ({ setProcesses, agvRef, MaterialRef, + armBots, + setArmBots }) => { const gltf = useLoader(GLTFLoader, crate) as GLTF; const groupRef = useRef(null); @@ -41,7 +59,7 @@ const ProcessAnimator: React.FC = ({ getPointDataForAnimationIndex, processes: processedProcesses, checkAndCountTriggers, - } = useProcessAnimation(processes, setProcesses, agvRef); + } = useProcessAnimation(processes, setProcesses, agvRef, armBots, setArmBots); const baseMaterials = useMemo( () => ({ @@ -96,6 +114,29 @@ const ProcessAnimator: React.FC = ({ // In processAnimator.tsx - only the relevant spawn logic part that needs fixes + + // Add this function to ProcessAnimator component + const isConnectedToActiveArmBot = useCallback( + (processId: any) => { + // Check if any active armbot is connected to this process + return armBots.some((armbot) => { + if (!armbot.isActive) return false; + + // Check if this armbot is connected to the process + return armbot.connections?.targets?.some((connection: any) => { + // Find the process that owns this modelUUID + const connectedProcess = processes.find((p) => + p.paths?.some((path) => path.modeluuid === connection.modelUUID) + ); + return connectedProcess?.id === processId; + }); + }); + }, + [armBots, processes] + ); + + // In processAnimator.tsx - only the relevant spawn logic part that needs fixes + useFrame(() => { // Spawn logic frame const currentTime = @@ -113,6 +154,14 @@ const ProcessAnimator: React.FC = ({ return; } + if (isConnectedToActiveArmBot(process.id)) { + newStates[process.id] = { + ...processState, + nextSpawnTime: Infinity, // Prevent future spawns + }; + return; + } + const spawnPoint = findSpawnPoint(process); if (!spawnPoint || !spawnPoint.actions) return; @@ -180,6 +229,29 @@ const ProcessAnimator: React.FC = ({ const processState = newStates[process.id]; if (!processState) return; + if (isConnectedToActiveArmBot(process.id)) { + newStates[process.id] = { + ...processState, + spawnedObjects: Object.entries(processState.spawnedObjects).reduce( + (acc, [id, obj]) => ({ + ...acc, + [id]: { + ...obj, + state: { + ...obj.state, + isAnimating: false, // Stop animation + isDelaying: false, // Clear delays + delayComplete: false, // Reset delays + progress: 0, // Reset progress + }, + }, + }), + {} + ), + }; + return; + } + if (processState.isProcessDelaying) { const effectiveDelayTime = processState.processDelayDuration / speedRef.current; @@ -444,7 +516,6 @@ const ProcessAnimator: React.FC = ({ return newStates; }); }); - if (!processedProcesses || processedProcesses.length === 0) { return null; } diff --git a/app/src/modules/simulation/process/processContainer.tsx b/app/src/modules/simulation/process/processContainer.tsx index 1cbc75b..0bcdb13 100644 --- a/app/src/modules/simulation/process/processContainer.tsx +++ b/app/src/modules/simulation/process/processContainer.tsx @@ -2,11 +2,28 @@ import React, { useState } from "react"; import ProcessCreator from "./processCreator"; import ProcessAnimator from "./processAnimator"; +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + connections: { + source: { modelUUID: string; pointUUID: string }; + targets: { modelUUID: string; pointUUID: string }[]; + }; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; + isActive?: boolean; +} + interface ProcessContainerProps { processes: any[]; setProcesses: React.Dispatch>; agvRef: any; MaterialRef: any; + armBots: ArmBotState[]; + setArmBots: React.Dispatch>; } const ProcessContainer: React.FC = ({ @@ -14,6 +31,8 @@ const ProcessContainer: React.FC = ({ setProcesses, agvRef, MaterialRef, + armBots, + setArmBots }) => { return ( <> @@ -23,6 +42,8 @@ const ProcessContainer: React.FC = ({ setProcesses={setProcesses} agvRef={agvRef} MaterialRef={MaterialRef} + armBots={armBots} + setArmBots={setArmBots} /> ); diff --git a/app/src/modules/simulation/process/processCreator.tsx b/app/src/modules/simulation/process/processCreator.tsx index c46791f..0a486c9 100644 --- a/app/src/modules/simulation/process/processCreator.tsx +++ b/app/src/modules/simulation/process/processCreator.tsx @@ -12,7 +12,7 @@ import { ArmBotEventsSchema, ConveyorEventsSchema, VehicleEventsSchema, -} from "../../../types/simulation"; +} from "../../../types/simulationTypes"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; // Type definitions diff --git a/app/src/modules/simulation/process/types.ts b/app/src/modules/simulation/process/types.ts index 246e780..9c9a1bc 100644 --- a/app/src/modules/simulation/process/types.ts +++ b/app/src/modules/simulation/process/types.ts @@ -39,7 +39,7 @@ export interface ProcessPath { pathRotation: number[]; speed: number; type: "Conveyor" | "Vehicle" | "ArmBot"; - isActive: boolean; + isActive: boolean } export interface ProcessData { diff --git a/app/src/modules/simulation/process/useProcessAnimations.tsx b/app/src/modules/simulation/process/useProcessAnimations.tsx index 232f17c..9f08cf3 100644 --- a/app/src/modules/simulation/process/useProcessAnimations.tsx +++ b/app/src/modules/simulation/process/useProcessAnimations.tsx @@ -45,10 +45,27 @@ interface PlayAgvState { setPlayAgv: (data: any) => void; } +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + connections: { + source: { modelUUID: string; pointUUID: string }; + targets: { modelUUID: string; pointUUID: string }[]; + }; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; + isActive?: boolean; +} + export const useProcessAnimation = ( processes: ProcessData[], setProcesses: React.Dispatch>, - agvRef: any + agvRef: any, + armBots: ArmBotState[], + setArmBots: React.Dispatch> ) => { // State and refs initialization const { isPlaying, setIsPlaying } = usePlayButtonStore(); @@ -58,18 +75,13 @@ export const useProcessAnimation = ( const clockRef = useRef(new THREE.Clock()); const pauseTimeRef = useRef(0); const elapsedBeforePauseRef = useRef(0); - const animationStatesRef = useRef< - Record - >({}); + const animationStatesRef = useRef>({}); const { speed } = useAnimationPlaySpeed(); const prevIsPlaying = useRef(null); const [internalResetFlag, setInternalResetFlag] = useState(false); - const [animationStates, setAnimationStates] = useState< - Record - >({}); + const [animationStates, setAnimationStates] = useState>({}); const speedRef = useRef(speed); - const armBotRef = useRef([]); - + const { PlayAgv, setPlayAgv } = usePlayAgv(); // Effect hooks useEffect(() => { @@ -111,8 +123,7 @@ export const useProcessAnimation = ( if (isPaused) { pauseTimeRef.current = clockRef.current.getElapsedTime(); } else if (pauseTimeRef.current > 0) { - const pausedDuration = - clockRef.current.getElapsedTime() - pauseTimeRef.current; + const pausedDuration = clockRef.current.getElapsedTime() - pauseTimeRef.current; elapsedBeforePauseRef.current += pausedDuration; } }, [isPaused]); @@ -161,8 +172,9 @@ export const useProcessAnimation = ( // Initialize AGVs for each process first processes.forEach((process) => { // Find all vehicle paths for this process - const vehiclePaths = - process.paths?.filter((path) => path.type === "Vehicle") || []; + const vehiclePaths = process.paths?.filter( + (path) => path.type === "Vehicle" + ) || []; // Initialize AGVs for each vehicle path vehiclePaths.forEach((vehiclePath) => { @@ -187,8 +199,8 @@ export const useProcessAnimation = ( maxHitCount: maxHitCount || 0, isActive: false, hitCount: 0, - status: "stationed", - lastUpdated: 0, + status: 'stationed', + lastUpdated: 0 }); } } @@ -449,6 +461,8 @@ export const useProcessAnimation = ( [handleMaterialSwap] ); + const deferredArmBotUpdates = useRef<{ uuid: string; triggerId: string }[]>([]); + // Trigger counting system const checkAndCountTriggers = useCallback( ( @@ -468,9 +482,7 @@ export const useProcessAnimation = ( const point = getPointDataForAnimationIndex(process, currentPointIndex); if (!point?.triggers) return prev; - const onHitTriggers = point.triggers.filter( - (t: Trigger) => t.type === "On-Hit" && t.isUsed - ); + const onHitTriggers = point.triggers.filter((t: Trigger) => t.type === "On-Hit" && t.isUsed); if (onHitTriggers.length === 0) return prev; @@ -478,58 +490,47 @@ export const useProcessAnimation = ( const newTriggerLogs = [...processState.triggerLogs]; let shouldLog = false; - // Find all vehicle paths for this process - const vehiclePaths = process.paths.filter( - (path) => path.type === "Vehicle" - ); + const vehiclePaths = process.paths.filter((path) => path.type === "Vehicle"); + const armBotPaths = process.paths.filter((path) => path.type === "ArmBot"); - // Find all ArmBot paths for this process - const armBotPaths = process.paths.filter( - (path) => path.type === "ArmBot" - ); - - // Check if any vehicle is active for this process const activeVehicles = vehiclePaths.filter((path) => { const vehicleId = path.modeluuid; - const vehicleEntry = agvRef.current.find( - (v: any) => v.vehicleId === vehicleId && v.processId === processId - ); + const vehicleEntry = agvRef.current.find((v: any) => v.vehicleId === vehicleId && v.processId === processId); return vehicleEntry?.isActive; }); // Check if any ArmBot is active for this process - const activeArmBots = armBotPaths.filter((path) => { - const armBotId = path.modeluuid; - const armBotEntry = armBotRef.current.find( - (a: any) => a.armBotId === armBotId && a.processId === processId - ); - return armBotEntry?.isActive; - }); + // const activeArmBots = armBotPaths.filter((path) => { + // const armBotId = path.modeluuid; + // const armBotEntry = armBots.find((a: any) => a.uuid === armBotId); + // return armBotEntry; + // }); // Only count triggers if no vehicles and no ArmBots are active for this process - if (activeVehicles.length === 0 && activeArmBots.length === 0) { + + if (activeVehicles.length === 0) { onHitTriggers.forEach((trigger: Trigger) => { - const triggerKey = `${point.uuid}-${trigger.uuid}`; - newTriggerCounts[triggerKey] = - (newTriggerCounts[triggerKey] || 0) + 1; - shouldLog = true; - newTriggerLogs.push({ - timestamp: currentTime, - pointId: point.uuid, - objectId, - triggerId: trigger.uuid, + const connections = point.connections?.targets || []; + + connections.forEach((connection) => { + const connectedModelUUID = connection.modelUUID; + + const matchingArmPath = armBotPaths.find((path) => path.modeluuid === connectedModelUUID); + + if (matchingArmPath) { + deferredArmBotUpdates.current.push({ + uuid: connectedModelUUID, + triggerId: trigger.uuid, + }); + } }); }); } - let processTotalHits = Object.values(newTriggerCounts).reduce( - (a, b) => a + b, - 0 - ); + let processTotalHits = Object.values(newTriggerCounts).reduce((a, b) => a + b, 0); // Handle logic for vehicles and ArmBots when a trigger is hit if (shouldLog) { - // Handle vehicle logic (existing code) vehiclePaths.forEach((vehiclePath) => { if (vehiclePath.points?.length > 0) { const vehiclePoint = vehiclePath.points[0]; @@ -568,48 +569,6 @@ export const useProcessAnimation = ( } } }); - - // Handle ArmBot logic (new code) - armBotPaths.forEach((armBotPath) => { - if (armBotPath.points?.length > 0) { - const armBotPoint = armBotPath.points[0]; - const action = armBotPoint.actions?.[0]; - const maxHitCount = action?.hitCount; - - if (maxHitCount !== undefined) { - const armBotId = armBotPath.modeluuid; - let armBotEntry = armBotRef.current.find( - (a: any) => - a.armBotId === armBotId && a.processId === processId - ); - - if (!armBotEntry) { - armBotEntry = { - processId, - armBotId, - maxHitCount: maxHitCount, - isActive: false, - hitCount: 0, - status: "stationed", - }; - armBotRef.current.push(armBotEntry); - console.log('armBotEntry: ', armBotEntry); - } - - if (!armBotEntry.isActive) { - armBotEntry.hitCount++; - armBotEntry.lastUpdated = currentTime; - - if (armBotEntry.hitCount >= armBotEntry.maxHitCount) { - armBotEntry.isActive = true; - // Reset trigger counts when ArmBot activates, similar to vehicle - newTriggerCounts = {}; - processTotalHits = 0; - } - } - } - } - }); } return { @@ -622,9 +581,24 @@ export const useProcessAnimation = ( }, }; }); - }, - [] - ); + }, []); + + useEffect(() => { + if (deferredArmBotUpdates.current.length > 0) { + const updates = [...deferredArmBotUpdates.current]; + deferredArmBotUpdates.current = []; + + setArmBots((prev) => + prev.map((bot) => { + const update = updates.find((u) => u.uuid === bot.uuid); + + return update + ? { ...bot, triggerId: update.triggerId, isActive: true } + : bot; + }) + ); + } + }, [animationStates]); // Utility functions const hasNonInheritActions = useCallback( @@ -632,9 +606,7 @@ export const useProcessAnimation = ( return actions.some( (action) => action.isUsed && action.type !== "Inherit" ); - }, - [] - ); + }, []); const getPointDataForAnimationIndex = useCallback( (process: ProcessData, index: number): ProcessPoint | null => { diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index fb789ec..84d3651 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -6,42 +6,69 @@ import useModuleStore from "../../store/useModuleStore"; import ProcessContainer from "./process/processContainer"; import Agv from "../builder/agv/agv"; import ArmBot from "./armbot/ArmBot"; +import StaticMachine from "./staticMachine/staticMachine"; + +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + connections: { + source: { modelUUID: string; pointUUID: string }; + targets: { modelUUID: string; pointUUID: string }[]; + }; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; + isActive?: boolean; +} + +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} function Simulation() { - const { activeModule } = useModuleStore(); - const pathsGroupRef = useRef() as React.MutableRefObject; - const [processes, setProcesses] = useState([]); + const { activeModule } = useModuleStore(); + const pathsGroupRef = useRef() as React.MutableRefObject; + const [armBots, setArmBots] = useState([]); + const [staticMachines, setStaticMachines] = useState([]); + const [processes, setProcesses] = useState([]); + const agvRef = useRef([]); + const MaterialRef = useRef([]); - const agvRef = useRef([]); - const MaterialRef = useRef([]); - const { simulationStates } = useSimulationStates(); - - return ( - <> - {activeModule === "simulation" && ( + return ( <> - + {activeModule === "simulation" && ( + <> + - + - + - + + + )} + + - )} - - - ); + ); } export default Simulation; diff --git a/app/src/modules/simulation/staticMachine/staticMachine.tsx b/app/src/modules/simulation/staticMachine/staticMachine.tsx new file mode 100644 index 0000000..26760eb --- /dev/null +++ b/app/src/modules/simulation/staticMachine/staticMachine.tsx @@ -0,0 +1,81 @@ +import React, { useEffect } from 'react' +import * as SimulationTypes from '../../../types/simulationTypes'; +import { useSimulationStates } from '../../../store/store'; +import StaticMachineInstances from './staticMachineInstances'; + +interface ArmBotState { + uuid: string; + position: [number, number, number]; + rotation: [number, number, number]; + status: string; + material: string; + triggerId: string; + connections: { + source: { modelUUID: string; pointUUID: string }; + targets: { modelUUID: string; pointUUID: string }[]; + }; + actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; }; + isActive?: boolean; +} +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} + +type StaticMachineProps = { + setArmBots: React.Dispatch>; + staticMachines: StaticMachineState[]; + setStaticMachines: React.Dispatch>; +} + +function StaticMachine({ setArmBots, staticMachines, setStaticMachines }: StaticMachineProps) { + + const { simulationStates } = useSimulationStates(); + + useEffect(() => { + const filtered = simulationStates.filter((s): s is SimulationTypes.StaticMachineEventsSchema => s.type === "StaticMachine"); + const initialStates: StaticMachineState[] = filtered + .filter(machine => machine.points.connections.targets.length > 0) + .map(machine => ({ + uuid: machine.modeluuid, + status: "idle", + actions: machine.points.actions, + machineTriggerId: machine.points.triggers.uuid, + connectedArmBot: machine.points.connections.targets[0].modelUUID + })); + setStaticMachines(initialStates); + }, [simulationStates]); + + const updateArmBotTriggerAndMachineStatus = (armBotUuid: string, triggerId: string, machineId: string) => { + setArmBots((prevArmBots) => { + return prevArmBots.map(bot => { + if (bot.uuid === armBotUuid) { + return { ...bot, triggerId: triggerId }; + } + return bot; + }); + }); + setStaticMachines((prevStaticMachines) => { + return prevStaticMachines.map(machine => { + if (machine.uuid === machineId) { + return { ...machine, status: "idle" }; + } else { + return machine; + } + }); + }); + } + + return ( + <> + {staticMachines.map((machine, index) => ( + + ))} + + ) +} + +export default StaticMachine; \ No newline at end of file diff --git a/app/src/modules/simulation/staticMachine/staticMachineInstances.tsx b/app/src/modules/simulation/staticMachine/staticMachineInstances.tsx new file mode 100644 index 0000000..94a2faa --- /dev/null +++ b/app/src/modules/simulation/staticMachine/staticMachineInstances.tsx @@ -0,0 +1,33 @@ +import React, { useEffect } from 'react' +import { useAnimationPlaySpeed } from '../../../store/usePlayButtonStore'; + +interface StaticMachineState { + uuid: string; + status: string; + actions: { uuid: string; name: string; buffer: number; material: string; }; + machineTriggerId: string; + connectedArmBot: string; +} + +type StaticMachineInstancesProps = { + machine: StaticMachineState, + updateArmBotTriggerAndMachineStatus: (armBotUuid: string, triggerId: string, machineId: string) => void; +} + +function StaticMachineInstances({ machine, updateArmBotTriggerAndMachineStatus }: StaticMachineInstancesProps) { + const { speed } = useAnimationPlaySpeed(); + + useEffect(() => { + if (machine.status === 'running') { + setTimeout(() => { + updateArmBotTriggerAndMachineStatus(machine.connectedArmBot, machine.machineTriggerId, machine.uuid); + }, machine.actions.buffer * 1000 * speed); + } + }, [machine]) + + return ( + <> + ) +} + +export default StaticMachineInstances \ No newline at end of file diff --git a/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx b/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx index 7e6ad6d..f865719 100644 --- a/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx +++ b/app/src/modules/visualization/widgets/2d/DraggableWidget.tsx @@ -100,6 +100,8 @@ export const DraggableWidget = ({ const deleteSelectedChart = async () => { try { + console.log("delete"); + const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; let deleteWidget = { @@ -109,7 +111,9 @@ export const DraggableWidget = ({ }; if (visualizationSocket) { + setSelectedChartId(null) visualizationSocket.emit("v2:viz-widget:delete", deleteWidget); + console.log("delete widget",selectedChartId); } const updatedWidgets = selectedZone.widgets.filter( (w: Widget) => w.id !== widget.id @@ -120,7 +124,6 @@ export const DraggableWidget = ({ widgets: updatedWidgets, })); setOpenKebabId(null); - // const response = await deleteWidgetApi(widget.id, organization); // if (response?.message === "Widget deleted successfully") { // const updatedWidgets = selectedZone.widgets.filter( @@ -175,6 +178,7 @@ export const DraggableWidget = ({ const duplicatedWidget: Widget = { ...widget, + title: name === '' ? widget.title : name, Data: { duration: duration, measurements: { ...measurements }, @@ -187,6 +191,7 @@ export const DraggableWidget = ({ zoneId: selectedZone.zoneId, widget: duplicatedWidget, }; + if (visualizationSocket) { visualizationSocket.emit("v2:viz-widget:add", duplicateWidget); } @@ -306,7 +311,10 @@ export const DraggableWidget = ({ : undefined, }} ref={chartWidget} - onClick={() => setSelectedChartId(widget)} + onClick={() => {setSelectedChartId(widget) + console.log('click'); + + }} > {/* Kebab Icon */}
@@ -327,7 +335,10 @@ export const DraggableWidget = ({
Duplicate
-
+
{ + e.stopPropagation() + deleteSelectedChart(); + }}>
diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 0b15b3e..765712b 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -1,6 +1,6 @@ import * as THREE from "three"; import * as Types from "../types/world/worldTypes"; -import * as SimulationTypes from "../types/simulation"; +import * as SimulationTypes from "../types/simulationTypes"; import { create } from "zustand"; import { io } from "socket.io-client"; diff --git a/app/src/store/useDroppedObjectsStore.ts b/app/src/store/useDroppedObjectsStore.ts index ca39d9f..0c03eec 100644 --- a/app/src/store/useDroppedObjectsStore.ts +++ b/app/src/store/useDroppedObjectsStore.ts @@ -101,6 +101,7 @@ export const useDroppedObjectsStore = create((set) => ({ let visualizationSocket = socketState.visualizationSocket; let iotMeasurements = iotData.flotingMeasurements; let iotDuration = iotData.flotingDuration; + let iotHeader = iotData.header if (!zone) return; @@ -117,6 +118,7 @@ export const useDroppedObjectsStore = create((set) => ({ measurements: iotMeasurements, duration: iotDuration, }, + header: iotHeader, id: `${originalObject.id}-copy-${Date.now()}`, // Unique ID position: { ...originalObject.position, diff --git a/app/src/types/simulation.d.ts b/app/src/types/simulationTypes.d.ts similarity index 97% rename from app/src/types/simulation.d.ts rename to app/src/types/simulationTypes.d.ts index bea52fa..75efcd3 100644 --- a/app/src/types/simulation.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -64,7 +64,7 @@ interface StaticMachineEventsSchema { uuid: string; position: [number, number, number]; rotation: [number, number, number]; - actions: { uuid: string; name: string; buffer: number | string; material: string; isUsed: boolean; }; + actions: { uuid: string; name: string; buffer: number; material: string; }; triggers: { uuid: string; name: string; type: string }; connections: { source: { modelUUID: string; pointUUID: string }; @@ -103,7 +103,7 @@ export type EventData = { isLocked: boolean; isVisible: boolean; eventData?: - | { + { type: "Conveyor"; points: { uuid: string;