diff --git a/app/src/components/layout/sidebarLeft/Assets.tsx b/app/src/components/layout/sidebarLeft/Assets.tsx index 17a4df3..66e185a 100644 --- a/app/src/components/layout/sidebarLeft/Assets.tsx +++ b/app/src/components/layout/sidebarLeft/Assets.tsx @@ -73,7 +73,7 @@ const Assets: React.FC = () => { try { const filt = await fetchAssets(); setFiltereredAssets(filt); - } catch { } + } catch {} }; filteredAssets(); }, [categoryAssets]); @@ -135,7 +135,7 @@ const Assets: React.FC = () => { const res = await getCategoryAsset(asset); setCategoryAssets(res); setFiltereredAssets(res); - } catch (error) { } + } catch (error) {} } }; return ( @@ -151,7 +151,12 @@ const Assets: React.FC = () => {
{categoryAssets && categoryAssets?.map((asset: any, index: number) => ( -
+
{asset.filename} {
{categoryAssets && categoryAssets?.map((asset: any, index: number) => ( -
+
{asset.filename} { setSelectedItem({ name: asset.filename, id: asset.AssetID, - type: asset.type === "undefined" ? undefined : asset.type - }) + type: + asset.type === "undefined" ? undefined : asset.type, + }); }} /> diff --git a/app/src/components/layout/sidebarLeft/SideBarLeft.tsx b/app/src/components/layout/sidebarLeft/SideBarLeft.tsx index dc412f7..e0b56d4 100644 --- a/app/src/components/layout/sidebarLeft/SideBarLeft.tsx +++ b/app/src/components/layout/sidebarLeft/SideBarLeft.tsx @@ -5,8 +5,8 @@ import Header from "./Header"; import useToggleStore from "../../../store/useUIToggleStore"; import Assets from "./Assets"; import useModuleStore from "../../../store/useModuleStore"; -import Widgets from ".//visualization/widgets/Widgets"; -import Templates from "../../../modules//visualization/template/Templates"; +import Widgets from "./visualization/widgets/Widgets"; +import Templates from "../../../modules/visualization/template/Templates"; import Search from "../../ui/inputs/Search"; const SideBarLeft: React.FC = () => { diff --git a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx index 50d9712..b5ae0bb 100644 --- a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx +++ b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx @@ -5,11 +5,11 @@ import { GlobeIcon, WalletIcon, } from "../../../../icons/3dChartIcons"; -import SimpleCard from "../../../../../modules//visualization/widgets/floating/cards/SimpleCard"; +import SimpleCard from "../../../../../modules/visualization/widgets/floating/cards/SimpleCard"; -import WarehouseThroughput from "../../../../../modules//visualization/widgets/floating/cards/WarehouseThroughput"; -import ProductivityDashboard from "../../../../../modules//visualization/widgets/floating/cards/ProductivityDashboard"; -import FleetEfficiency from "../../../../../modules//visualization/widgets/floating/cards/FleetEfficiency"; +import WarehouseThroughput from "../../../../../modules/visualization/widgets/floating/cards/WarehouseThroughput"; +import ProductivityDashboard from "../../../../../modules/visualization/widgets/floating/cards/ProductivityDashboard"; +import FleetEfficiency from "../../../../../modules/visualization/widgets/floating/cards/FleetEfficiency"; interface Widget { id: string; diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index 125cef9..38f0b18 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -1,138 +1,148 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import Header from "./Header"; import useModuleStore, { - useSubModuleStore, + useSubModuleStore, } from "../../../store/useModuleStore"; import { - AnalysisIcon, - MechanicsIcon, - PropertiesIcon, - SimulationIcon, + AnalysisIcon, + MechanicsIcon, + PropertiesIcon, + SimulationIcon, } from "../../icons/SimulationIcons"; import useToggleStore from "../../../store/useUIToggleStore"; import Visualization from "./visualization/Visualization"; import Analysis from "./analysis/Analysis"; import Simulations from "./simulation/Simulations"; -import { - useSelectedFloorItem, -} from "../../../store/store"; +import { useSelectedFloorItem } from "../../../store/store"; +import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; import GlobalProperties from "./properties/GlobalProperties"; import AsstePropertiies from "./properties/AssetProperties"; import ZoneProperties from "./properties/ZoneProperties"; +import EventProperties from "./properties/eventProperties/EventProperties"; const SideBarRight: React.FC = () => { - const { activeModule } = useModuleStore(); - const { toggleUI } = useToggleStore(); - const { subModule, setSubModule } = useSubModuleStore(); - const { selectedFloorItem } = useSelectedFloorItem(); - // Reset activeList whenever activeModule changes - useEffect(() => { - if (activeModule !== "simulation") setSubModule("properties"); - if (activeModule === "simulation") setSubModule("mechanics"); - }, [activeModule]); + const { activeModule } = useModuleStore(); + const { toggleUI } = useToggleStore(); + const { subModule, setSubModule } = useSubModuleStore(); + const { selectedFloorItem } = useSelectedFloorItem(); + const { selectedEventData } = useSelectedEventData(); + const { selectedEventSphere } = useSelectedEventSphere(); - return ( -
-
- {toggleUI && ( -
- {/* {activeModule === "builder" && ( */} -
setSubModule("properties")} - > - -
- {/* )} */} - {activeModule === "simulation" && ( - <> -
setSubModule("mechanics")} - > - -
-
setSubModule("simulations")} - > - -
-
setSubModule("analysis")} - > - -
- - )} + // Reset activeList whenever activeModule changes + useEffect(() => { + if (activeModule !== "simulation") setSubModule("properties"); + if (activeModule === "simulation") setSubModule("simulations"); + }, [activeModule]); + + useEffect(() => { + if (activeModule !== "mechanics" && selectedEventData && selectedEventSphere) { + setSubModule("mechanics"); + } else if (!selectedEventData && !selectedEventSphere) { + if (activeModule === 'simulation') { + setSubModule("simulations"); + } + }; + }, [activeModule, selectedEventData, selectedEventSphere]) + + return ( +
+
+ {toggleUI && ( +
+
setSubModule("properties")} + > + +
+ {activeModule === "simulation" && ( + <> +
setSubModule("mechanics")} + > + +
+
setSubModule("simulations")} + > + +
+
setSubModule("analysis")} + > + +
+ + )} +
+ )} + {/* process builder */} + {toggleUI && + subModule === "properties" && + activeModule !== "visualization" && + !selectedFloorItem && ( +
+
+ +
+
+ )} + {toggleUI && + subModule === "properties" && + activeModule !== "visualization" && + selectedFloorItem && ( +
+
+ +
+
+ )} + {toggleUI && + subModule === "zoneProperties" && + (activeModule === "builder" || activeModule === "simulation") && ( +
+
+ +
+
+ )} + {/* simulation */} + {toggleUI && activeModule === "simulation" && ( + <> + {subModule === "simulations" && ( +
+
+ +
+
+ )} + {subModule === "mechanics" && ( +
+
+ +
+
+ )} + {subModule === "analysis" && ( +
+
+ +
+
+ )} + + )} + {/* realtime visualization */} + {toggleUI && activeModule === "visualization" && }
- )} - {/* process builder */} - {toggleUI && - subModule === "properties" && - activeModule !== "visualization" && - !selectedFloorItem && ( -
-
- -
-
- )} - {toggleUI && - subModule === "properties" && - activeModule !== "visualization" && - selectedFloorItem && ( -
-
- -
-
- )} - {toggleUI && - subModule === "zoneProperties" && - (activeModule === "builder" || activeModule === "simulation") && ( -
-
- -
-
- )} - {/* simulation */} - - {toggleUI && activeModule === "simulation" && ( - <> - {subModule === "mechanics" && ( -
-
-
-
- )} - {subModule === "analysis" && ( -
-
- -
-
- )} - {subModule === "simulations" && ( -
-
- -
-
- )} - - )} - - {/* realtime visualization */} - {toggleUI && activeModule === "visualization" && } -
- ); + ); }; -export default SideBarRight; +export default SideBarRight; \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx new file mode 100644 index 0000000..ca32594 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx @@ -0,0 +1,64 @@ +import React, { useEffect, useRef, useState } from "react"; +import { useSelectedEventData, useSelectedProduct } from "../../../../../store/simulation/useSimulationStore"; +import { useProductStore } from "../../../../../store/simulation/useProductStore"; +import ConveyorMechanics from "./mechanics/conveyorMechanics"; +import VehicleMechanics from "./mechanics/vehicleMechanics"; +import RoboticArmMechanics from "./mechanics/roboticArmMechanics"; +import MachineMechanics from "./mechanics/machineMechanics"; +import StorageMechanics from "./mechanics/storageMechanics"; + +const EventProperties: React.FC = () => { + const { selectedEventData } = useSelectedEventData(); + const { getEventByModelUuid } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + const [currentEventData, setCurrentEventData] = useState(null); + const [assetType, setAssetType] = useState(null); + + useEffect(() => { + const event = getCurrentEventData(); + setCurrentEventData(event); + + const type = determineAssetType(event); + setAssetType(type); + + }, [selectedEventData, selectedProduct]); + + const getCurrentEventData = () => { + if (!selectedEventData?.data || !selectedProduct) return null; + return getEventByModelUuid(selectedProduct.productId, selectedEventData.data.modelUuid) || null; + }; + + const determineAssetType = (event: EventsSchema | null) => { + if (!event) return null; + + switch (event.type) { + case 'transfer': return 'conveyor'; + case 'vehicle': return 'vehicle'; + case 'roboticArm': return 'roboticArm'; + case 'machine': return 'machine'; + case 'storageUnit': return 'storageUnit'; + default: return null; + } + }; + + return ( + <> +
+ {currentEventData && + <> +
+
{selectedEventData?.data.modelName}
+
+ {assetType === 'conveyor' && } + {assetType === 'vehicle' && } + {assetType === 'roboticArm' && } + {assetType === 'machine' && } + {assetType === 'storageUnit' && } + + } +
+ + ); +}; + +export default EventProperties; \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DefaultAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DefaultAction.tsx new file mode 100644 index 0000000..88f515e --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DefaultAction.tsx @@ -0,0 +1,7 @@ +import React from "react"; + +const DefaultAction:React.FC = () => { + return <>; +}; + +export default DefaultAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DelayAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DelayAction.tsx new file mode 100644 index 0000000..2bb63d4 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DelayAction.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; + +interface DelayActionProps { + value: string; + defaultValue: string; + min: number; + max: number; + onChange: (value: string) => void; +} + +const DelayAction: React.FC = ({ value, defaultValue, min, max, onChange }) => { + return ( + <> + { }} + onChange={onChange} + /> + + ); +}; + +export default DelayAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DespawnAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DespawnAction.tsx new file mode 100644 index 0000000..0d54476 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DespawnAction.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; + +const DespawnAction: React.FC = () => { + return ( + <> + + ); +}; + +export default DespawnAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/PickAndPlaceAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/PickAndPlaceAction.tsx new file mode 100644 index 0000000..175a824 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/PickAndPlaceAction.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import EyeDropInput from "../../../../../ui/inputs/EyeDropInput"; + +interface PickAndPlaceActionProps { + pickPointValue: string; + pickPointOnChange: (value: string) => void; + placePointValue: string; + placePointOnChange: (value: string) => void; +} + +const PickAndPlaceAction: React.FC = ({ + pickPointValue, + pickPointOnChange, + placePointValue, + placePointOnChange, +}) => { + return ( + <> + + + + ); +}; + +export default PickAndPlaceAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/ProcessAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/ProcessAction.tsx new file mode 100644 index 0000000..331cf1b --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/ProcessAction.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; +import SwapAction from "./SwapAction"; + +interface ProcessActionProps { + value: string; + min: number; + max: number; + defaultValue: string; + onChange: (value: string) => void; + swapOptions: string[]; + swapDefaultOption: string; + onSwapSelect: (value: string) => void; +} + +const ProcessAction: React.FC = ({ + value, + min, + max, + defaultValue, + onChange, + swapOptions, + swapDefaultOption, + onSwapSelect, +}) => { + return ( + <> + { }} + onChange={onChange} + /> + + + ); +}; + +export default ProcessAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SpawnAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SpawnAction.tsx new file mode 100644 index 0000000..7d8002e --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SpawnAction.tsx @@ -0,0 +1,72 @@ +import React from "react"; +import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; +import LabledDropdown from "../../../../../ui/inputs/LabledDropdown"; + +interface SpawnActionProps { + onChangeInterval: (value: string) => void; + onChangeCount: (value: string) => void; + defaultOption: string; + options: string[]; + onSelect: (option: string) => void; + intervalValue: string; + countValue: string; + intervalMin: number; + intervalMax: number; + intervalDefaultValue: string; + countMin: number; + countMax: number; + countDefaultValue: string; +} + +const SpawnAction: React.FC = ({ + onChangeInterval, + onChangeCount, + defaultOption, + options, + onSelect, + intervalValue, + countValue, + intervalMin, + intervalMax, + intervalDefaultValue, + countMin, + countMax, + countDefaultValue, +}) => { + return ( + <> + { }} + onChange={onChangeInterval} + /> + { }} + onChange={onChangeCount} + /> + {/* */} + + + ); +}; + +export default SpawnAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/StorageAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/StorageAction.tsx new file mode 100644 index 0000000..5c9bf86 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/StorageAction.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; + +interface StorageActionProps { + value: string; + min: number; + max: number; + defaultValue: string; + onChange: (value: string) => void; +} + +const StorageAction: React.FC = ({ value, min, max, defaultValue, onChange }) => { + return ( + { }} + onChange={onChange} + /> + ); +}; + +export default StorageAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SwapAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SwapAction.tsx new file mode 100644 index 0000000..23b203f --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SwapAction.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload"; +import LabledDropdown from "../../../../../ui/inputs/LabledDropdown"; + +interface SwapActionProps { + onSelect: (option: string) => void; + defaultOption: string; + options: string[]; +} + +const SwapAction: React.FC = ({ onSelect, defaultOption, options }) => { + + return ( + <> + {/* */} + + + + ); +}; + +export default SwapAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/TravelAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/TravelAction.tsx new file mode 100644 index 0000000..49eb683 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/TravelAction.tsx @@ -0,0 +1,78 @@ +import React from "react"; +import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; +import EyeDropInput from "../../../../../ui/inputs/EyeDropInput"; + +interface TravelActionProps { + loadCapacity: { + value: string; + min: number; + max: number; + defaultValue: string; + onChange: (value: string) => void; + }; + unloadDuration: { + value: string; + min: number; + max: number; + defaultValue: string; + onChange: (value: string) => void; + }; + pickPoint?: { + value: string; + onChange: (value: string) => void; + }; + unloadPoint?: { + value: string; + onChange: (value: string) => void; + }; +} + +const TravelAction: React.FC = ({ + loadCapacity, + unloadDuration, + pickPoint, + unloadPoint, +}) => { + return ( + <> + { }} + onChange={loadCapacity.onChange} + /> + { }} + onChange={unloadDuration.onChange} + /> + {pickPoint && ( + + )} + {unloadPoint && ( + + )} + + ); +}; + +export default TravelAction; diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/functions/handleActionToggle.ts b/app/src/components/layout/sidebarRight/properties/eventProperties/functions/handleActionToggle.ts new file mode 100644 index 0000000..96e986a --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/functions/handleActionToggle.ts @@ -0,0 +1,6 @@ +export function handleActionToggle(uuid: string) { + // This function handles the action toggle for the event properties. + // It updates the selected action and its properties based on the provided UUID. + // The function is currently empty and needs to be implemented. + // You can add your logic here to handle the action toggle. +} \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/functions/handleDeleteAction.ts b/app/src/components/layout/sidebarRight/properties/eventProperties/functions/handleDeleteAction.ts new file mode 100644 index 0000000..6a367d5 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/functions/handleDeleteAction.ts @@ -0,0 +1,6 @@ +export function handleDeleteAction(uuid: string) { + // This function handles the action toggle for the event properties. + // It updates the selected action and its properties based on the provided UUID. + // The function is currently empty and needs to be implemented. + // You can add your logic here to handle the action toggle. +} \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx new file mode 100644 index 0000000..c13bd75 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/conveyorMechanics.tsx @@ -0,0 +1,212 @@ +import { useEffect, useState } from 'react' +import InputWithDropDown from '../../../../../ui/inputs/InputWithDropDown' +import DelayAction from '../actions/DelayAction' +import RenameInput from '../../../../../ui/inputs/RenameInput' +import LabledDropdown from '../../../../../ui/inputs/LabledDropdown' +import DespawnAction from '../actions/DespawnAction' +import SwapAction from '../actions/SwapAction' +import SpawnAction from '../actions/SpawnAction' +import DefaultAction from '../actions/DefaultAction' +import Trigger from '../trigger/Trigger' +import { useSelectedEventData, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore"; +import { useProductStore } from "../../../../../../store/simulation/useProductStore"; + +function ConveyorMechanics() { + const [activeOption, setActiveOption] = useState<"default" | "spawn" | "swap" | "delay" | "despawn">("default"); + const [selectedPointData, setSelectedPointData] = useState(); + const { selectedEventData } = useSelectedEventData(); + const { getPointByUuid, updateEvent, updateAction } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + + useEffect(() => { + if (selectedEventData) { + const point = getPointByUuid( + selectedProduct.productId, + selectedEventData?.data.modelUuid, + selectedEventData?.selectedPoint + ) as ConveyorPointSchema | undefined; + if (point && 'action' in point) { + setSelectedPointData(point); + setActiveOption(point.action.actionType as "default" | "spawn" | "swap" | "delay" | "despawn"); + } + } + }, [selectedProduct, selectedEventData]) + + const handleSpeedChange = (value: string) => { + if (!selectedEventData) return; + updateEvent( + selectedProduct.productId, + selectedEventData.data.modelUuid, + { speed: parseFloat(value) } + ); + }; + + const handleActionTypeChange = (option: string) => { + if (!selectedEventData || !selectedPointData) return; + const validOption = option as "default" | "spawn" | "swap" | "delay" | "despawn"; + setActiveOption(validOption); + + updateAction( + selectedPointData.action.actionUuid, + { actionType: validOption } + ); + }; + + const handleRenameAction = (newName: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { actionName: newName } + ); + }; + + const handleSpawnCountChange = (value: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { spawnCount: value === "inherit" ? "inherit" : parseFloat(value) } + ); + }; + + const handleSpawnIntervalChange = (value: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { spawnInterval: value === "inherit" ? "inherit" : parseFloat(value) } + ); + }; + + const handleMaterialSelect = (material: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { material } + ); + }; + + const handleDelayChange = (value: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { delay: value === "inherit" ? "inherit" : parseFloat(value) } + ); + }; + + const availableActions = { + defaultOption: "default", + options: ["default", "spawn", "swap", "delay", "despawn"], + }; + + // Get current values from store + const currentSpeed = selectedEventData?.data.type === "transfer" + ? selectedEventData.data.speed.toString() + : "0.5"; + + const currentActionName = selectedPointData + ? selectedPointData.action.actionName + : "Action Name"; + + const currentMaterial = selectedPointData + ? selectedPointData.action.material + : "Default material"; + + const currentSpawnCount = selectedPointData + ? selectedPointData.action.spawnCount?.toString() || "1" + : "1"; + + const currentSpawnInterval = selectedPointData + ? selectedPointData.action.spawnInterval?.toString() || "1" + : "1"; + + const currentDelay = selectedPointData + ? selectedPointData.action.delay?.toString() || "0" + : "0"; + + return ( + <> + {selectedEventData && + <> +
+
+
+ { }} + onChange={handleSpeedChange} + /> +
+
+
+ +
+
+ +
+
+ + {activeOption === "default" && + + } + {activeOption === "spawn" && + + } + {activeOption === "swap" && + + } + {activeOption === "despawn" && + + } + {activeOption === "delay" && + + } +
+
+
+ +
+ + } + + ) +} + +export default ConveyorMechanics \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx new file mode 100644 index 0000000..e131864 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/machineMechanics.tsx @@ -0,0 +1,123 @@ +import { useEffect, useState } from 'react' +import RenameInput from '../../../../../ui/inputs/RenameInput' +import LabledDropdown from '../../../../../ui/inputs/LabledDropdown' +import Trigger from '../trigger/Trigger' +import { useSelectedEventData, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore"; +import { useProductStore } from "../../../../../../store/simulation/useProductStore"; +import ProcessAction from '../actions/ProcessAction' + +function MachineMechanics() { + const [activeOption, setActiveOption] = useState<"default" | "process">("default"); + const [selectedPointData, setSelectedPointData] = useState(); + const { selectedEventData } = useSelectedEventData(); + const { getPointByUuid, updateAction } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + + useEffect(() => { + if (selectedEventData) { + const point = getPointByUuid( + selectedProduct.productId, + selectedEventData?.data.modelUuid, + selectedEventData?.selectedPoint + ) as MachinePointSchema | undefined; + if (point && 'action' in point) { + setSelectedPointData(point); + setActiveOption(point.action.actionType as "process"); + } + } + }, [selectedProduct, selectedEventData]) + + const handleActionTypeChange = (option: string) => { + if (!selectedEventData || !selectedPointData) return; + const validOption = option as "process"; + setActiveOption(validOption); + + updateAction( + selectedPointData.action.actionUuid, + { actionType: validOption } + ); + }; + + const handleRenameAction = (newName: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { actionName: newName } + ); + }; + + const handleProcessTimeChange = (value: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { processTime: parseFloat(value) } + ); + }; + + const handleMaterialSelect = (material: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { swapMaterial: material } + ); + }; + + // Get current values from store + const currentActionName = selectedPointData + ? selectedPointData.action.actionName + : "Action Name"; + + const currentProcessTime = selectedPointData + ? selectedPointData.action.processTime.toString() + : "1"; + + const currentMaterial = selectedPointData + ? selectedPointData.action.swapMaterial + : "Default material"; + + const availableActions = { + defaultOption: "process", + options: ["process"], + }; + + return ( + <> + {selectedEventData && + <> +
+
+ +
+
+ + {activeOption === "process" && + + } +
+
+
+ +
+ + } + + ) +} + +export default MachineMechanics \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx new file mode 100644 index 0000000..78a32f5 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/roboticArmMechanics.tsx @@ -0,0 +1,276 @@ +import { useEffect, useRef, useState } from 'react' +import * as THREE from 'three'; +import InputWithDropDown from '../../../../../ui/inputs/InputWithDropDown' +import RenameInput from '../../../../../ui/inputs/RenameInput' +import LabledDropdown from '../../../../../ui/inputs/LabledDropdown' +import Trigger from '../trigger/Trigger' +import { useSelectedEventData, useSelectedProduct, useSelectedAction } from "../../../../../../store/simulation/useSimulationStore"; +import { useProductStore } from "../../../../../../store/simulation/useProductStore"; +import { AddIcon, RemoveIcon, ResizeHeightIcon } from '../../../../../icons/ExportCommonIcons' +import { handleResize } from '../../../../../../functions/handleResizePannel' +import PickAndPlaceAction from '../actions/PickAndPlaceAction' + +function RoboticArmMechanics() { + const actionsContainerRef = useRef(null); + const [activeOption, setActiveOption] = useState<"default" | "pickAndPlace">("default"); + const [selectedPointData, setSelectedPointData] = useState(); + const { selectedEventData } = useSelectedEventData(); + const { getPointByUuid, updateEvent, updateAction, addAction, removeAction } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction(); + + useEffect(() => { + if (selectedEventData) { + const point = getPointByUuid( + selectedProduct.productId, + selectedEventData.data.modelUuid, + selectedEventData.selectedPoint + ) as RoboticArmPointSchema | undefined; + if (point) { + setSelectedPointData(point); + setActiveOption(point.actions[0].actionType as "default" | "pickAndPlace"); + if (point.actions.length > 0 && !selectedAction.actionId) { + setSelectedAction(point.actions[0].actionUuid, point.actions[0].actionName); + } + } + } else { + clearSelectedAction(); + } + }, [selectedEventData, selectedProduct]); + + const handleActionSelect = (actionUuid: string, actionName: string) => { + setSelectedAction(actionUuid, actionName); + }; + + const handleAddAction = () => { + if (!selectedEventData || !selectedPointData) return; + + const newAction = { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: `Action ${selectedPointData.actions.length + 1}`, + actionType: "pickAndPlace" as "pickAndPlace", + process: { + startPoint: null, + endPoint: null + }, + triggers: [] as TriggerSchema[] + }; + + addAction( + selectedProduct.productId, + selectedEventData.data.modelUuid, + selectedEventData.selectedPoint, + newAction + ); + + const updatedPoint = { + ...selectedPointData, + actions: [...selectedPointData.actions, newAction] + }; + setSelectedPointData(updatedPoint); + setSelectedAction(newAction.actionUuid, newAction.actionName); + }; + + const handleDeleteAction = (actionUuid: string) => { + if (!selectedPointData) return; + + removeAction(actionUuid); + const newActions = selectedPointData.actions.filter(a => a.actionUuid !== actionUuid); + + const updatedPoint = { + ...selectedPointData, + actions: newActions + }; + setSelectedPointData(updatedPoint); + + if (selectedAction.actionId === actionUuid) { + if (newActions.length > 0) { + setSelectedAction(newActions[0].actionUuid, newActions[0].actionName); + } else { + clearSelectedAction(); + } + } + }; + + const handleRenameAction = (newName: string) => { + if (!selectedAction.actionId) return; + updateAction( + selectedAction.actionId, + { actionName: newName } + ); + + if (selectedPointData) { + const updatedActions = selectedPointData.actions.map(action => + action.actionUuid === selectedAction.actionId + ? { ...action, actionName: newName } + : action + ); + setSelectedPointData({ + ...selectedPointData, + actions: updatedActions + }); + } + }; + + const handleSpeedChange = (value: string) => { + if (!selectedEventData) return; + updateEvent( + selectedProduct.productId, + selectedEventData.data.modelUuid, + { speed: parseFloat(value) } + ); + }; + + const handlePickPointChange = (value: string) => { + if (!selectedAction.actionId || !selectedPointData) return; + const [x, y, z] = value.split(',').map(Number); + + updateAction( + selectedAction.actionId, + { + process: { + startPoint: [x, y, z] as [number, number, number], + endPoint: selectedPointData.actions.find(a => a.actionUuid === selectedAction.actionId)?.process.endPoint || null + } + } + ); + }; + + const handlePlacePointChange = (value: string) => { + if (!selectedAction.actionId || !selectedPointData) return; + const [x, y, z] = value.split(',').map(Number); + + updateAction( + selectedAction.actionId, + { + process: { + startPoint: selectedPointData.actions.find(a => a.actionUuid === selectedAction.actionId)?.process.startPoint || null, + endPoint: [x, y, z] as [number, number, number] + } + } + ); + }; + + const availableActions = { + defaultOption: "pickAndPlace", + options: ["pickAndPlace"], + }; + + const currentSpeed = selectedEventData?.data.type === "roboticArm" + ? selectedEventData.data.speed.toString() + : "0.5"; + + const currentAction = selectedPointData?.actions.find(a => a.actionUuid === selectedAction.actionId); + const currentPickPoint = currentAction?.process.startPoint + ? `${currentAction.process.startPoint[0]},${currentAction.process.startPoint[1]},${currentAction.process.startPoint[2]}` + : ""; + const currentPlacePoint = currentAction?.process.endPoint + ? `${currentAction.process.endPoint[0]},${currentAction.process.endPoint[1]},${currentAction.process.endPoint[2]}` + : ""; + + return ( + <> + {selectedEventData && selectedPointData && ( + <> +
+
+
+ { }} + onChange={handleSpeedChange} + /> +
+
+
+ +
+
+
+
Actions
+
+ Add +
+
+
+
+ {selectedPointData.actions.map((action) => ( +
+
handleActionSelect(action.actionUuid, action.actionName)} + > + +
+ {selectedPointData.actions.length > 1 && ( +
handleDeleteAction(action.actionUuid)} + > + +
+ )} +
+ ))} +
+
handleResize(e, actionsContainerRef)} + > + +
+
+
+
+ + {selectedAction.actionId && currentAction && ( +
+
+ +
+
+ { }} + disabled={true} + /> + +
+
+ +
+
+ )} + + )} + + ) +} + +export default RoboticArmMechanics \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx new file mode 100644 index 0000000..593dcc3 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/storageMechanics.tsx @@ -0,0 +1,113 @@ +import { useEffect, useState } from 'react' +import RenameInput from '../../../../../ui/inputs/RenameInput' +import LabledDropdown from '../../../../../ui/inputs/LabledDropdown' +import Trigger from '../trigger/Trigger' +import { useSelectedEventData, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore"; +import { useProductStore } from "../../../../../../store/simulation/useProductStore"; +import StorageAction from '../actions/StorageAction'; + +function StorageMechanics() { + const [activeOption, setActiveOption] = useState<"default" | "store" | "spawn">("default"); + const [selectedPointData, setSelectedPointData] = useState(); + const { selectedEventData } = useSelectedEventData(); + const { getPointByUuid, updateAction } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + + useEffect(() => { + if (selectedEventData) { + const point = getPointByUuid( + selectedProduct.productId, + selectedEventData?.data.modelUuid, + selectedEventData?.selectedPoint + ) as StoragePointSchema | undefined; + if (point && 'action' in point) { + setSelectedPointData(point); + setActiveOption(point.action.actionType as "store" | "spawn"); + } + } + }, [selectedProduct, selectedEventData]) + + const handleActionTypeChange = (option: string) => { + if (!selectedEventData || !selectedPointData) return; + const validOption = option as "store" | "spawn"; + setActiveOption(validOption); + + updateAction( + selectedPointData.action.actionUuid, + { actionType: validOption } + ); + }; + + const handleRenameAction = (newName: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { actionName: newName } + ); + }; + + const handleCapacityChange = (value: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { storageCapacity: parseInt(value) } + ); + }; + + // Get current values from store + const currentActionName = selectedPointData + ? selectedPointData.action.actionName + : "Action Name"; + + const currentCapacity = selectedPointData + ? selectedPointData.action.storageCapacity.toString() + : "0"; + + const availableActions = { + defaultOption: "store", + options: ["store", "spawn"], + }; + + return ( + <> + {selectedEventData && + <> +
+
+ +
+
+ + {activeOption === "store" && + + } + {activeOption === "spawn" && ( +
+

Spawn configuration options would go here

+
+ )} +
+
+
+ +
+ + } + + ) +} + +export default StorageMechanics \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx new file mode 100644 index 0000000..cc2bfa0 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx @@ -0,0 +1,197 @@ +import { useEffect, useState } from 'react' +import InputWithDropDown from '../../../../../ui/inputs/InputWithDropDown' +import RenameInput from '../../../../../ui/inputs/RenameInput' +import LabledDropdown from '../../../../../ui/inputs/LabledDropdown' +import Trigger from '../trigger/Trigger' +import { useSelectedEventData, useSelectedProduct } from "../../../../../../store/simulation/useSimulationStore"; +import { useProductStore } from "../../../../../../store/simulation/useProductStore"; +import TravelAction from '../actions/TravelAction' + +function VehicleMechanics() { + const [activeOption, setActiveOption] = useState<"default" | "travel">("default"); + const [selectedPointData, setSelectedPointData] = useState(); + const { selectedEventData } = useSelectedEventData(); + const { getPointByUuid, updateEvent, updateAction } = useProductStore(); + const { selectedProduct } = useSelectedProduct(); + + useEffect(() => { + if (selectedEventData) { + const point = getPointByUuid( + selectedProduct.productId, + selectedEventData.data.modelUuid, + selectedEventData.selectedPoint + ) as VehiclePointSchema | undefined; + + if (point) { + setSelectedPointData(point); + setActiveOption(point.action.actionType as "travel"); + } + } + }, [selectedProduct, selectedEventData]) + + const handleSpeedChange = (value: string) => { + if (!selectedEventData) return; + updateEvent( + selectedProduct.productId, + selectedEventData.data.modelUuid, + { speed: parseFloat(value) } + ); + }; + + const handleActionTypeChange = (option: string) => { + if (!selectedEventData || !selectedPointData) return; + const validOption = option as "travel"; + setActiveOption(validOption); + + updateAction( + selectedPointData.action.actionUuid, + { actionType: validOption } + ); + }; + + const handleRenameAction = (newName: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { actionName: newName } + ); + }; + + const handleLoadCapacityChange = (value: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { loadCapacity: parseFloat(value) } + ); + }; + + const handleUnloadDurationChange = (value: string) => { + if (!selectedPointData) return; + updateAction( + selectedPointData.action.actionUuid, + { unLoadDuration: parseFloat(value) } + ); + }; + + const handlePickPointChange = (value: string) => { + if (!selectedPointData) return; + const [x, y, z] = value.split(',').map(Number); + updateAction( + selectedPointData.action.actionUuid, + { pickUpPoint: { x, y, z } } + ); + }; + + const handleUnloadPointChange = (value: string) => { + if (!selectedPointData) return; + const [x, y, z] = value.split(',').map(Number); + updateAction( + selectedPointData.action.actionUuid, + { unLoadPoint: { x, y, z } } + ); + }; + + // Get current values from store + const currentSpeed = selectedEventData?.data.type === "vehicle" + ? selectedEventData.data.speed.toString() + : "0.5"; + + const currentActionName = selectedPointData + ? selectedPointData.action.actionName + : "Action Name"; + + const currentLoadCapacity = selectedPointData + ? selectedPointData.action.loadCapacity.toString() + : "1"; + + const currentUnloadDuration = selectedPointData + ? selectedPointData.action.unLoadDuration.toString() + : "1"; + + const currentPickPoint = selectedPointData?.action.pickUpPoint + ? `${selectedPointData.action.pickUpPoint.x},${selectedPointData.action.pickUpPoint.y},${selectedPointData.action.pickUpPoint.z}` + : ""; + + const currentUnloadPoint = selectedPointData?.action.unLoadPoint + ? `${selectedPointData.action.unLoadPoint.x},${selectedPointData.action.unLoadPoint.y},${selectedPointData.action.unLoadPoint.z}` + : ""; + + const availableActions = { + defaultOption: "travel", + options: ["travel"], + }; + + return ( + <> + {selectedEventData && + <> +
+
+
+ { }} + onChange={handleSpeedChange} + /> +
+
+
+ +
+
+ +
+
+ + + {activeOption === 'travel' && + + } +
+
+
+ +
+ + } + + ) +} + +export default VehicleMechanics \ No newline at end of file diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx new file mode 100644 index 0000000..f287b63 --- /dev/null +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/trigger/Trigger.tsx @@ -0,0 +1,107 @@ +import React, { useState } from "react"; +import { AddIcon, RemoveIcon } from "../../../../../icons/ExportCommonIcons"; +import LabledDropdown from "../../../../../ui/inputs/LabledDropdown"; + +const Trigger: React.FC = () => { + // State to hold the list of triggers + const [triggers, setTriggers] = useState([]); + const [activeOption, setActiveOption] = useState("onComplete"); + + // States for dropdowns + const [triggeredModel, setTriggeredModel] = useState([]); + const [triggeredPoint, setTriggeredPoint] = useState([]); + const [triggeredAction, setTriggeredAction] = useState([]); + + // Function to handle adding a new trigger + const addTrigger = (): void => { + const newTrigger = `Trigger ${triggers.length + 1}`; + setTriggers([...triggers, newTrigger]); // Add new trigger to the state + + // Initialize the states for the new trigger + setTriggeredModel([...triggeredModel, ""]); + setTriggeredPoint([...triggeredPoint, ""]); + setTriggeredAction([...triggeredAction, ""]); + }; + + // Function to handle removing a trigger + const removeTrigger = (index: number): void => { + setTriggers(triggers.filter((_, i) => i !== index)); // Remove trigger by index + setTriggeredModel(triggeredModel.filter((_, i) => i !== index)); + setTriggeredPoint(triggeredPoint.filter((_, i) => i !== index)); + setTriggeredAction(triggeredAction.filter((_, i) => i !== index)); + }; + + return ( +
+
+
Trigger
+
+ Add +
+
+
+ {/* Map over triggers and render them */} + {triggers.map((trigger, index) => ( +
+
+ {trigger} +
removeTrigger(index)} + style={{ cursor: "pointer" }} + > + +
+
+ setActiveOption(option)} + /> +
+
+ { + const newModel = [...triggeredModel]; + newModel[index] = option; + setTriggeredModel(newModel); + }} + /> +
+
+ { + const newPoint = [...triggeredPoint]; + newPoint[index] = option; + setTriggeredPoint(newPoint); + }} + /> +
+
+ { + const newAction = [...triggeredAction]; + newAction[index] = option; + setTriggeredAction(newAction); + }} + /> +
+
+
+ ))} +
+
+ ); +}; + +export default Trigger; diff --git a/app/src/components/layout/sidebarRight/simulation/Simulations.tsx b/app/src/components/layout/sidebarRight/simulation/Simulations.tsx index 2a12734..d6b4ac8 100644 --- a/app/src/components/layout/sidebarRight/simulation/Simulations.tsx +++ b/app/src/components/layout/sidebarRight/simulation/Simulations.tsx @@ -1,147 +1,198 @@ -import React, { useRef, useState } from "react"; +import React, { useEffect, useRef } from "react"; import { - AddIcon, - ArrowIcon, - RemoveIcon, - ResizeHeightIcon, + AddIcon, + ArrowIcon, + RemoveIcon, + ResizeHeightIcon, } from "../../../icons/ExportCommonIcons"; import RenameInput from "../../../ui/inputs/RenameInput"; import { handleResize } from "../../../../functions/handleResizePannel"; +import { useSelectedAsset, useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; +import { useProductStore } from "../../../../store/simulation/useProductStore"; +import { generateUUID } from "three/src/math/MathUtils"; +import RenderOverlay from "../../../templates/Overlay"; +import EditWidgetOption from "../../../ui/menu/EditWidgetOption"; -interface Path { - pathName: string; // Represents the name of the path - Children: string[]; // Represents the list of child points +interface Event { + pathName: string; } -interface DropListProps { - val: Path; // Use the Path interface for the val prop +interface ListProps { + val: Event; } -const DropList: React.FC = ({ val }) => { - const [openDrop, setOpenDrop] = useState(false); - return ( -
-
{ - setOpenDrop(!openDrop); - }} - > - {val.pathName} -
- -
-
- {val.Children && openDrop && ( -
- {val.Children.map((child, index) => ( -
- {child} +const List: React.FC = ({ val }) => { + return ( +
+
+ {val.pathName}
- ))}
- )} -
- ); + ); }; const Simulations: React.FC = () => { - const productsContainerRef = useRef(null); - const [productsList, setProductsList] = useState([]); - const [selectedItem, setSelectedItem] = useState(); + const productsContainerRef = useRef(null); + const { products, addProduct, removeProduct, renameProduct, addEvent, removeEvent } = useProductStore(); + const { selectedProduct, setSelectedProduct } = useSelectedProduct(); + const { selectedAsset, clearSelectedAsset } = useSelectedAsset(); - const handleAddAction = () => { - setProductsList([...productsList, `Product ${productsList.length + 1}`]); - }; + const handleAddProduct = () => { + addProduct(`Product ${products.length + 1}`, generateUUID()); + }; - const handleRemoveAction = (index: number) => { - setProductsList(productsList.filter((_, i) => i !== index)); - if (selectedItem === productsList[index]) { - setSelectedItem(""); - } - }; + const handleRemoveProduct = (productId: string) => { + const currentIndex = products.findIndex(p => p.productId === productId); + const isSelected = selectedProduct.productId === productId; - const Value = [ - { pathName: "Path 1", Children: ["Point 1", "Point 2"] }, - { pathName: "Path 2", Children: ["Point 1", "Point 2"] }, - { pathName: "Path 3", Children: ["Point 1", "Point 2"] }, - ]; + const updatedProducts = products.filter(p => p.productId !== productId); - return ( -
-
Simulations
-
-
-
-
Products
-
- Add -
-
-
-
- {productsList.map((action, index) => ( -
-
setSelectedItem(action)} - > - - -
-
handleRemoveAction(index)} - > - -
+ if (isSelected) { + if (updatedProducts.length > 0) { + let newSelectedIndex = currentIndex; + if (currentIndex >= updatedProducts.length) { + newSelectedIndex = updatedProducts.length - 1; + } + setSelectedProduct( + updatedProducts[newSelectedIndex].productId, + updatedProducts[newSelectedIndex].productName + ); + } else { + setSelectedProduct('', ''); + } + } + + removeProduct(productId); + }; + + const handleRenameProduct = (productId: string, newName: string) => { + renameProduct(productId, newName); + if (selectedProduct.productId === productId) { + setSelectedProduct(productId, newName); + } + }; + + const handleAddEventToProduct = () => { + if (selectedAsset) { + addEvent(selectedProduct.productId, selectedAsset); + clearSelectedAsset(); + } + }; + + const handleRemoveEventFromProduct = () => { + if (selectedAsset) { + removeEvent(selectedProduct.productId, selectedAsset.modelUuid); + clearSelectedAsset(); + } + }; + + const selectedProductData = products.find( + (product) => product.productId === selectedProduct.productId + ); + + const events: Event[] = selectedProductData?.eventDatas.map((event) => ({ + pathName: event.modelName, + })) || []; + + return ( +
+
Simulations
+
+
+
+
Products
+
+ Add +
+
+
+
+ {products.map((product, index) => ( +
+
setSelectedProduct(product.productId, product.productName)} + > + + handleRenameProduct(product.productId, newName)} + /> +
+ {products.length > 1 && ( +
handleRemoveProduct(product.productId)} + > + +
+ )} +
+ ))} +
+
handleResize(e, productsContainerRef)} + > + +
+
+
+ +
+
+
Events
+
+ +
+
+ {events.map((event, index) => ( + + ))} +
+ +
+
+ Need to Compare Layout? +
+
+ Click 'Compare' to review and analyze the layout differences between them. +
+
+ +
- ))}
-
handleResize(e, productsContainerRef)} - > - -
-
+ + {selectedAsset && + + { + if (option === 'Add to Product') { + handleAddEventToProduct(); + } else { + handleRemoveEventFromProduct(); + } + }} + /> + + }
-
-
-
Operations
-
- -
-
- {Value.map((val, index) => ( - - ))} -
-
-
- Need to Compare Layout? -
-
- Click 'Compare' to review and analyze the layout - differences between them. -
-
- -
-
-
-
- ); + ); }; export default Simulations; diff --git a/app/src/components/layout/sidebarRight/visualization/design/Design.tsx b/app/src/components/layout/sidebarRight/visualization/design/Design.tsx index 04a569a..234b936 100644 --- a/app/src/components/layout/sidebarRight/visualization/design/Design.tsx +++ b/app/src/components/layout/sidebarRight/visualization/design/Design.tsx @@ -1,9 +1,9 @@ import { useState, useEffect, useRef } from "react"; import { useWidgetStore } from "../../../../../store/useWidgetStore"; -import ChartComponent from "../../../sidebarLeft//visualization/widgets/ChartComponent"; +import ChartComponent from "../../../sidebarLeft/visualization/widgets/ChartComponent"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; import { WalletIcon } from "../../../../icons/3dChartIcons"; -import SimpleCard from "../../../../../modules//visualization/widgets/floating/cards/SimpleCard"; +import SimpleCard from "../../../../../modules/visualization/widgets/floating/cards/SimpleCard"; interface Widget { id: string; diff --git a/app/src/components/ui/Tools.tsx b/app/src/components/ui/Tools.tsx index 70b1461..21ea6a7 100644 --- a/app/src/components/ui/Tools.tsx +++ b/app/src/components/ui/Tools.tsx @@ -15,7 +15,7 @@ import { } from "../icons/ExportToolsIcons"; import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons"; import useModuleStore, { useThreeDStore } from "../../store/useModuleStore"; -import { handleSaveTemplate } from "../../modules//visualization/functions/handleSaveTemplate"; +import { handleSaveTemplate } from "../../modules/visualization/functions/handleSaveTemplate"; import { usePlayButtonStore } from "../../store/usePlayButtonStore"; import useTemplateStore from "../../store/useTemplateStore"; import { useSelectedZoneStore } from "../../store/visualization/useZoneStore"; diff --git a/app/src/components/ui/inputs/InputWithDropDown.tsx b/app/src/components/ui/inputs/InputWithDropDown.tsx index b672313..3d42917 100644 --- a/app/src/components/ui/inputs/InputWithDropDown.tsx +++ b/app/src/components/ui/inputs/InputWithDropDown.tsx @@ -5,6 +5,7 @@ type InputWithDropDownProps = { label: string; value: string; min?: number; + max?: number; step?: number; defaultValue?: string; options?: string[]; // Array of dropdown options @@ -19,6 +20,7 @@ const InputWithDropDown: React.FC = ({ label, value, min, + max, step, defaultValue, options, @@ -47,6 +49,7 @@ const InputWithDropDown: React.FC = ({
{ + const [showPreview, setSetshowPreview] = useState(false); + return ( +
+
setSetshowPreview(!showPreview)} + > +
Preview
+
+ +
+
+ {showPreview && ( +
+
+
+ )} +
+
+
Upload Product
+ + +
+
+
+ ); +}; + +export default PreviewSelectionWithUpload; diff --git a/app/src/components/ui/list/List.tsx b/app/src/components/ui/list/List.tsx index cac0b43..ac21c5a 100644 --- a/app/src/components/ui/list/List.tsx +++ b/app/src/components/ui/list/List.tsx @@ -142,9 +142,6 @@ const List: React.FC = ({ items = [], remove }) => { ) ); } - - console.log('newName: ', newName); - } const checkZoneNameDuplicate = (name: string) => { return zones.some( diff --git a/app/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx index 6fd1e94..eb937c8 100644 --- a/app/src/components/ui/menu/EditWidgetOption.tsx +++ b/app/src/components/ui/menu/EditWidgetOption.tsx @@ -1,23 +1,20 @@ import React, { useEffect } from "react"; import { - useEditWidgetOptionsStore, useLeftData, - useRightClickSelected, - useRightSelected, useTopData, } from "../../../store/visualization/useZone3DWidgetStore"; interface EditWidgetOptionProps { options: string[]; + onClick: (option: string) => void; } const EditWidgetOption: React.FC = ({ options, + onClick }) => { const { top } = useTopData(); const { left } = useLeftData(); - const { setRightSelect } = useRightSelected(); - const { setEditWidgetOptions } = useEditWidgetOptionsStore(); useEffect(() => { @@ -38,10 +35,7 @@ const EditWidgetOption: React.FC = ({
{ - setRightSelect(option); - setEditWidgetOptions(false); - }} + onClick={() => onClick(option)} > {option}
diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts index e88dc3c..0359f1f 100644 --- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts @@ -8,10 +8,12 @@ import * as Types from "../../../types/world/worldTypes"; import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi'; import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; +import PointsCalculator from '../../simulation/events/points/functions/pointsCalculator'; async function loadInitialFloorItems( itemsGroup: Types.RefGroup, setFloorItems: Types.setFloorItemSetState, + addEvent: (event: EventsSchema) => void, ): Promise { if (!itemsGroup.current) return; let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; @@ -70,7 +72,7 @@ async function loadInitialFloorItems( const cachedModel = THREE.Cache.get(item.modelfileID!); if (cachedModel) { // console.log(`[Cache] Fetching ${item.modelname}`); - processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems); + processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent); modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); return; @@ -85,7 +87,7 @@ async function loadInitialFloorItems( URL.revokeObjectURL(blobUrl); THREE.Cache.remove(blobUrl); THREE.Cache.add(item.modelfileID!, gltf); - processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems); + processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent); modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); }, @@ -106,7 +108,7 @@ async function loadInitialFloorItems( const modelBlob = await fetch(modelUrl).then((res) => res.blob()); await storeGLTF(item.modelfileID!, modelBlob); THREE.Cache.add(item.modelfileID!, gltf); - processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems); + processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent); modelsLoaded++; checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); }, @@ -148,8 +150,9 @@ function processLoadedModel( item: Types.FloorItemType, itemsGroup: Types.RefGroup, setFloorItems: Types.setFloorItemSetState, + addEvent: (event: EventsSchema) => void, ) { - const model = gltf; + const model = gltf.clone(); model.uuid = item.modeluuid; model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid }; @@ -182,6 +185,243 @@ function processLoadedModel( }, ]); + if (item.modelfileID === "a1ee92554935007b10b3eb05") { + const data = PointsCalculator( + 'Vehicle', + gltf.clone(), + new THREE.Vector3(...model.rotation) + ); + + if (!data || !data.points) return; + + const vehicleEvent: VehicleEventSchema = { + modelUuid: item.modeluuid, + modelName: item.modelname, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "vehicle", + speed: 1, + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Vehicle Action", + actionType: "travel", + material: null, + unLoadDuration: 5, + loadCapacity: 10, + pickUpPoint: null, + unLoadPoint: null, + triggers: [] + } + } + }; + addEvent(vehicleEvent); + } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") { + const data = PointsCalculator( + 'Conveyor', + gltf.clone(), + new THREE.Vector3(...model.rotation) + ); + + if (!data || !data.points) return; + + const ConveyorEvent: ConveyorEventSchema = { + modelUuid: item.modeluuid, + modelName: item.modelname, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "transfer", + speed: 1, + points: data.points.map((point: THREE.Vector3, index: number) => ({ + uuid: THREE.MathUtils.generateUUID(), + position: [point.x, point.y, point.z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: `Action ${index + 1}`, + actionType: 'default', + material: 'Default material', + delay: 0, + spawnInterval: 5, + spawnCount: 1, + triggers: [] + } + })) + }; + addEvent(ConveyorEvent); + } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") { + // const data = PointsCalculator( + // 'Conveyor', + // gltf.clone(), + // new THREE.Vector3(...model.rotation) + // ); + + // if (!data || !data.points) return; + + // const points: ConveyorPointSchema[] = data.points.map((point: THREE.Vector3, index: number) => { + // const actionUuid = THREE.MathUtils.generateUUID(); + // return { + // uuid: THREE.MathUtils.generateUUID(), + // position: [point.x, point.y, point.z], + // rotation: [0, 0, 0], + // action: { + // actionUuid, + // actionName: `Action ${index}`, + // actionType: 'default', + // material: 'inherit', + // delay: 0, + // spawnInterval: 5, + // spawnCount: 1, + // triggers: [] + // } + // }; + // }); + + // points.forEach((point, index) => { + // if (index < points.length - 1) { + // const nextPoint = points[index + 1]; + // point.action.triggers.push({ + // triggerUuid: THREE.MathUtils.generateUUID(), + // triggerName: `Trigger 1`, + // triggerType: "onComplete", + // delay: 0, + // triggeredAsset: { + // triggeredModel: { + // modelName: item.modelname, + // modelUuid: item.modeluuid + // }, + // triggeredPoint: { + // pointName: `Point ${index + 1}`, + // pointUuid: nextPoint.uuid + // }, + // triggeredAction: { + // actionName: nextPoint.action.actionName, + // actionUuid: nextPoint.action.actionUuid + // } + // } + // }); + // } + // }); + + // const ConveyorEvent: ConveyorEventSchema = { + // modelUuid: item.modeluuid, + // modelName: item.modelname, + // position: item.position, + // rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + // state: "idle", + // type: "transfer", + // speed: 1, + // points + // }; + // addEvent(ConveyorEvent); + } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") { + const data = PointsCalculator( + 'Conveyor', + gltf.clone(), + new THREE.Vector3(...model.rotation) + ); + + if (!data || !data.points) return; + + const ConveyorEvent: ConveyorEventSchema = { + modelUuid: item.modeluuid, + modelName: item.modelname, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "transfer", + speed: 1, + points: data.points.map((point: THREE.Vector3, index: number) => ({ + uuid: THREE.MathUtils.generateUUID(), + position: [point.x, point.y, point.z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: `Action ${index}`, + actionType: 'default', + material: 'inherit', + delay: 0, + spawnInterval: 5, + spawnCount: 1, + triggers: [] + } + })) + }; + addEvent(ConveyorEvent); + } else if (item.modelfileID === "29dee78715ad5b853f5c346d") { + const data = PointsCalculator( + 'StaticMachine', + gltf.clone(), + new THREE.Vector3(...model.rotation) + ); + + if (!data || !data.points) return; + + const machineEvent: MachineEventSchema = { + modelUuid: item.modeluuid, + modelName: item.modelname, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "machine", + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Process Action", + actionType: "process", + processTime: 10, + swapMaterial: "material-id", + triggers: [] + } + } + }; + addEvent(machineEvent); + } else if (item.modelfileID === "52e6681fbb743a890d96c914") { + const data = PointsCalculator( + 'ArmBot', + gltf.clone(), + new THREE.Vector3(...model.rotation) + ); + + if (!data || !data.points) return; + + const roboticArmEvent: RoboticArmEventSchema = { + modelUuid: item.modeluuid, + modelName: item.modelname, + position: item.position, + rotation: [item.rotation.x, item.rotation.y, item.rotation.z], + state: "idle", + type: "roboticArm", + speed: 1, + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + actions: [ + { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Pick and Place", + actionType: "pickAndPlace", + process: { + startPoint: [0, 0, 0], + endPoint: [0, 0, 0] + }, + triggers: [] + } + ] + } + }; + addEvent(roboticArmEvent); + } + gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: 'power2.out' }); gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out' }); } diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index e7f83e2..e5ff1c1 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -51,7 +51,7 @@ import Ground from "../scene/environment/ground"; // import ZoneGroup from "../groups/zoneGroup1"; import { findEnvironment } from "../../services/factoryBuilder/environment/findEnvironment"; import Layer2DVisibility from "./geomentries/layers/layer2DVisibility"; -import DrieHtmlTemp from "..//visualization/mqttTemp/drieHtmlTemp"; +import DrieHtmlTemp from "../visualization/mqttTemp/drieHtmlTemp"; import ZoneGroup from "./groups/zoneGroup"; import useModuleStore from "../../store/useModuleStore"; import MeasurementTool from "../scene/tools/measurementTool"; diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 5837958..670c7b5 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -10,7 +10,7 @@ import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils'; import { Socket } from 'socket.io-client'; import * as CONSTANTS from '../../../../types/world/worldConstants'; import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; -import PointsCalculator from '../../../simulation/events/points/pointsCalculator'; +import PointsCalculator from '../../../simulation/events/points/functions/pointsCalculator'; async function addAssetModel( raycaster: THREE.Raycaster, @@ -184,24 +184,9 @@ async function handleModelLoad( ); if (!data || !data.points) return; - console.log('data: ', data); - - const createMarker = (point: THREE.Vector3) => { - const sphere = new THREE.SphereGeometry(0.1, 15); - const material = new THREE.MeshStandardMaterial(); - const mesh = new THREE.Mesh(sphere, material); - mesh.position.copy(point); - return mesh; - }; - - if (data.points && data.points.length > 0) { - data.points.forEach((Point) => { - model.add(createMarker(Point)); - }); - } if (selectedItem.type === "Conveyor") { - const event: ConveyorEventSchema = { + const ConveyorEvent: ConveyorEventSchema = { modelUuid: newFloorItem.modeluuid, modelName: newFloorItem.modelname, position: newFloorItem.position, @@ -225,7 +210,85 @@ async function handleModelLoad( } })) } - addEvent(event); + addEvent(ConveyorEvent); + } else if (selectedItem.type === "Vehicle") { + const vehicleEvent: VehicleEventSchema = { + modelUuid: newFloorItem.modeluuid, + modelName: newFloorItem.modelname, + position: newFloorItem.position, + rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + state: "idle", + type: "vehicle", + speed: 1, + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Vehicle Action", + actionType: "travel", + material: null, + unLoadDuration: 5, + loadCapacity: 10, + pickUpPoint: null, + unLoadPoint: null, + triggers: [] + } + } + }; + addEvent(vehicleEvent); + } else if (selectedItem.type === "ArmBot") { + const roboticArmEvent: RoboticArmEventSchema = { + modelUuid: newFloorItem.modeluuid, + modelName: newFloorItem.modelname, + position: newFloorItem.position, + rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + state: "idle", + type: "roboticArm", + speed: 1, + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + actions: [ + { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Pick and Place", + actionType: "pickAndPlace", + process: { + startPoint: [0, 0, 0], + endPoint: [0, 0, 0] + }, + triggers: [] + } + ] + } + }; + addEvent(roboticArmEvent); + } else if (selectedItem.type === "StaticMachine") { + const machineEvent: MachineEventSchema = { + modelUuid: newFloorItem.modeluuid, + modelName: newFloorItem.modelname, + position: newFloorItem.position, + rotation: [newFloorItem.rotation.x, newFloorItem.rotation.y, newFloorItem.rotation.z], + state: "idle", + type: "machine", + point: { + uuid: THREE.MathUtils.generateUUID(), + position: [data.points[0].x, data.points[0].y, data.points[0].z], + rotation: [0, 0, 0], + action: { + actionUuid: THREE.MathUtils.generateUUID(), + actionName: "Process Action", + actionType: "process", + processTime: 10, + swapMaterial: "material-id", + triggers: [] + } + } + }; + addEvent(machineEvent); } } diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index 04376ce..241f628 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -16,8 +16,8 @@ import addAssetModel from "../geomentries/assets/addAssetModel"; import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; import useModuleStore from "../../../store/useModuleStore"; // import { retrieveGLTF } from "../../../utils/indexDB/idbUtils"; - import { useEventsStore } from "../../../store/simulation/useEventsStore"; + const assetManagerWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/assetManagerWorker.js", import.meta.url)); const gltfLoaderWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", import.meta.url)); @@ -75,7 +75,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject gltfLoaderWorker.postMessage({ floorItems: data }); } else { gltfLoaderWorker.postMessage({ floorItems: [] }); - loadInitialFloorItems(itemsGroup, setFloorItems); + loadInitialFloorItems(itemsGroup, setFloorItems, addEvent); updateLoadingProgress(100); } }); @@ -94,7 +94,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject updateLoadingProgress(progress); if (loadedAssets === totalAssets) { - loadInitialFloorItems(itemsGroup, setFloorItems); + loadInitialFloorItems(itemsGroup, setFloorItems, addEvent); updateLoadingProgress(100); } }); diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx index d3245fb..dd5607a 100644 --- a/app/src/modules/scene/postProcessing/postProcessing.tsx +++ b/app/src/modules/scene/postProcessing/postProcessing.tsx @@ -2,90 +2,107 @@ import * as THREE from "three"; import { EffectComposer, N8AO, Outline } from "@react-three/postprocessing"; import { BlendFunction } from "postprocessing"; import { - useDeletableFloorItem, - useSelectedWallItem, - useSelectedFloorItem, + useDeletableFloorItem, + useSelectedWallItem, + useSelectedFloorItem, } from "../../../store/store"; import * as Types from "../../../types/world/worldTypes"; import * as CONSTANTS from "../../../types/world/worldConstants"; import { useEffect } from "react"; +import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; export default function PostProcessing() { - const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem(); - const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); - const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); + const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem(); + const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); + const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); + const { selectedEventSphere } = useSelectedEventSphere(); - function flattenChildren(children: any[]) { - const allChildren: any[] = []; - children.forEach((child) => { - allChildren.push(child); - if (child.children && child.children.length > 0) { - allChildren.push(...flattenChildren(child.children)); - } - }); - return allChildren; - } + function flattenChildren(children: any[]) { + const allChildren: any[] = []; + children.forEach((child) => { + allChildren.push(child); + if (child.children && child.children.length > 0) { + allChildren.push(...flattenChildren(child.children)); + } + }); + return allChildren; + } - return ( - <> - - - {deletableFloorItem && ( - - )} - {selectedWallItem && ( - child.name !== "CSG_REF" - )} - selectionLayer={10} - width={3000} - blendFunction={BlendFunction.ALPHA} - edgeStrength={5} - resolutionScale={2} - pulseSpeed={0} - visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor} - hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor} - blur={true} - xRay={true} - /> - )} - {selectedFloorItem && ( - - )} - - - ); + return ( + <> + + + {deletableFloorItem && ( + + )} + {selectedWallItem && ( + child.name !== "CSG_REF" + )} + selectionLayer={10} + width={3000} + blendFunction={BlendFunction.ALPHA} + edgeStrength={5} + resolutionScale={2} + pulseSpeed={0} + visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor} + hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor} + blur={true} + xRay={true} + /> + )} + {selectedFloorItem && ( + + )} + {selectedEventSphere && ( + + )} + + + ); } diff --git a/app/src/modules/simulation/conveyor/conveyor.tsx b/app/src/modules/simulation/conveyor/conveyor.tsx new file mode 100644 index 0000000..bd21523 --- /dev/null +++ b/app/src/modules/simulation/conveyor/conveyor.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import ConveyorInstances from './instances/conveyorInstances' + +function Conveyor() { + return ( + <> + + + + + ) +} + +export default Conveyor \ No newline at end of file diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx new file mode 100644 index 0000000..9c9d612 --- /dev/null +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstance/conveyorInstance.tsx @@ -0,0 +1,10 @@ +import React from 'react' + +function ConveyorInstance() { + return ( + <> + + ) +} + +export default ConveyorInstance \ No newline at end of file diff --git a/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx b/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx new file mode 100644 index 0000000..3f53784 --- /dev/null +++ b/app/src/modules/simulation/conveyor/instances/conveyorInstances.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import ConveyorInstance from './conveyorInstance/conveyorInstance' + +function ConveyorInstances() { + return ( + <> + + + + + ) +} + +export default ConveyorInstances \ No newline at end of file diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx new file mode 100644 index 0000000..1b3defa --- /dev/null +++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx @@ -0,0 +1,169 @@ +import React, { useEffect, useRef, useState } from 'react'; +import * as THREE from 'three'; +import { useEventsStore } from '../../../../../store/simulation/useEventsStore'; +import useModuleStore from '../../../../../store/useModuleStore'; +import { TransformControls } from '@react-three/drei'; +import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys'; +import { useSelectedEventSphere, useSelectedEventData } from '../../../../../store/simulation/useSimulationStore'; + +function PointsCreator() { + const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore(); + const { activeModule } = useModuleStore(); + const transformRef = useRef(null); + const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); + const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); + const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere } = useSelectedEventSphere(); + const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData(); + + useEffect(() => { + if (selectedEventSphere) { + const eventData = getEventByModelUuid(selectedEventSphere.userData.modelUuid); + if (eventData) { + setSelectedEventData( + eventData, + selectedEventSphere.userData.pointUuid + ); + } else { + clearSelectedEventData(); + } + } else { + clearSelectedEventData(); + } + }, [selectedEventSphere]); + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + const keyCombination = detectModifierKeys(e); + if (!selectedEventSphere) return; + if (keyCombination === "G") { + setTransformMode((prev) => (prev === "translate" ? null : "translate")); + } + if (keyCombination === "R") { + setTransformMode((prev) => (prev === "rotate" ? null : "rotate")); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [selectedEventSphere]); + + const updatePointToState = (selectedEventSphere: THREE.Mesh) => { + let point = JSON.parse(JSON.stringify(getPointByUuid(selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid))); + if (point) { + point.position = [selectedEventSphere.position.x, selectedEventSphere.position.y, selectedEventSphere.position.z]; + updatePoint(selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid, point) + } + } + + return ( + <> + {activeModule === 'simulation' && + <> + + {events.map((event, i) => { + if (event.type === 'transfer') { + return ( + + {event.points.map((point, j) => ( + (sphereRefs.current[point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere(sphereRefs.current[point.uuid]); + }} + onPointerMissed={() => { + clearSelectedEventSphere(); + setTransformMode(null); + }} + key={`${i}-${j}`} + position={new THREE.Vector3(...point.position)} + userData={{ modelUuid: event.modelUuid, pointUuid: point.uuid }} + > + + + + ))} + + ); + } else if (event.type === 'vehicle') { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere(sphereRefs.current[event.point.uuid]); + }} + onPointerMissed={() => { + clearSelectedEventSphere(); + setTransformMode(null); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + > + + + + + ); + } else if (event.type === 'roboticArm') { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere(sphereRefs.current[event.point.uuid]); + }} + onPointerMissed={() => { + clearSelectedEventSphere(); + setTransformMode(null); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + > + + + + + ); + } else if (event.type === 'machine') { + return ( + + (sphereRefs.current[event.point.uuid] = el!)} + onClick={(e) => { + e.stopPropagation(); + setSelectedEventSphere(sphereRefs.current[event.point.uuid]); + }} + onPointerMissed={() => { + clearSelectedEventSphere(); + setTransformMode(null); + }} + position={new THREE.Vector3(...event.point.position)} + userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} + > + + + + + ); + } else { + return null; + } + })} + + {(selectedEventSphere && transformMode) && + { updatePointToState(selectedEventSphere) }} /> + } + + } + + ); +} + +export default PointsCreator; diff --git a/app/src/modules/simulation/events/points/pointsCalculator.ts b/app/src/modules/simulation/events/points/functions/pointsCalculator.ts similarity index 96% rename from app/src/modules/simulation/events/points/pointsCalculator.ts rename to app/src/modules/simulation/events/points/functions/pointsCalculator.ts index 86d368e..ccc2dbb 100644 --- a/app/src/modules/simulation/events/points/pointsCalculator.ts +++ b/app/src/modules/simulation/events/points/functions/pointsCalculator.ts @@ -1,5 +1,5 @@ import * as THREE from 'three'; -import { Group } from '../../../../types/world/worldTypes'; +import { Group } from '../../../../../types/world/worldTypes'; function PointsCalculator( type: string, diff --git a/app/src/modules/simulation/events/points/points.tsx b/app/src/modules/simulation/events/points/points.tsx new file mode 100644 index 0000000..2a50f2d --- /dev/null +++ b/app/src/modules/simulation/events/points/points.tsx @@ -0,0 +1,12 @@ +import React from 'react' +import PointsCreator from './creator/pointsCreator' + +function Points() { + return ( + <> + + + ) +} + +export default Points \ No newline at end of file diff --git a/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx new file mode 100644 index 0000000..edb825f --- /dev/null +++ b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx @@ -0,0 +1,10 @@ +import React from 'react' + +function MachineInstance() { + return ( + <> + + ) +} + +export default MachineInstance \ No newline at end of file diff --git a/app/src/modules/simulation/machine/instances/machineInstances.tsx b/app/src/modules/simulation/machine/instances/machineInstances.tsx new file mode 100644 index 0000000..b0c2c9f --- /dev/null +++ b/app/src/modules/simulation/machine/instances/machineInstances.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import MachineInstance from './machineInstance/machineInstance' + +function MachineInstances() { + return ( + <> + + + + + ) +} + +export default MachineInstances \ No newline at end of file diff --git a/app/src/modules/simulation/machine/machine.tsx b/app/src/modules/simulation/machine/machine.tsx new file mode 100644 index 0000000..32c3b8e --- /dev/null +++ b/app/src/modules/simulation/machine/machine.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import MachineInstances from './instances/machineInstances' + +function Machine() { + return ( + <> + + + + + ) +} + +export default Machine \ No newline at end of file diff --git a/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx new file mode 100644 index 0000000..1445e70 --- /dev/null +++ b/app/src/modules/simulation/materials/instances/animator/materialAnimator.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +function MaterialAnimator() { + return ( + <> + ) +} + +export default MaterialAnimator \ No newline at end of file diff --git a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx new file mode 100644 index 0000000..466e235 --- /dev/null +++ b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +function MaterialInstance() { + return ( + <> + ) +} + +export default MaterialInstance \ No newline at end of file diff --git a/app/src/modules/simulation/materials/instances/materialInstances.tsx b/app/src/modules/simulation/materials/instances/materialInstances.tsx new file mode 100644 index 0000000..519cba9 --- /dev/null +++ b/app/src/modules/simulation/materials/instances/materialInstances.tsx @@ -0,0 +1,17 @@ +import React from 'react' +import MaterialInstance from './instance/materialInstance' +import MaterialAnimator from './animator/materialAnimator' + +function MaterialInstances() { + return ( + <> + + + + + + + ) +} + +export default MaterialInstances \ No newline at end of file diff --git a/app/src/modules/simulation/materials/materials.tsx b/app/src/modules/simulation/materials/materials.tsx new file mode 100644 index 0000000..432d815 --- /dev/null +++ b/app/src/modules/simulation/materials/materials.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import MaterialInstances from './instances/materialInstances' + +function Materials() { + return ( + <> + + + + + ) +} + +export default Materials \ No newline at end of file diff --git a/app/src/modules/simulation/products/products.tsx b/app/src/modules/simulation/products/products.tsx new file mode 100644 index 0000000..2ddd7d1 --- /dev/null +++ b/app/src/modules/simulation/products/products.tsx @@ -0,0 +1,25 @@ +import React, { useEffect } from 'react' +import { useProductStore } from '../../../store/simulation/useProductStore' +import * as THREE from 'three'; +import { useSelectedProduct } from '../../../store/simulation/useSimulationStore'; + +function Products() { + const { products, addProduct } = useProductStore(); + const { setSelectedProduct } = useSelectedProduct(); + + useEffect(() => { + if (products.length === 0) { + const id = THREE.MathUtils.generateUUID(); + const name = 'Product 1'; + addProduct(name, id); + setSelectedProduct(id, name); + } + }, [products]) + + return ( + <> + + ) +} + +export default Products \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx new file mode 100644 index 0000000..6b35a43 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +function RoboticArmAnimator({ armUuid, HandleCallback, currentPhase }: any) { + return ( + <> + ) +} + +export default RoboticArmAnimator; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx new file mode 100644 index 0000000..4b6e0a0 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -0,0 +1,61 @@ +import React, { useEffect, useState } from 'react' +import IKInstance from '../ikInstance/ikInstance'; +import RoboticArmAnimator from '../animator/roboticArmAnimator'; +import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; +import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore'; + +function RoboticArmInstance({ armBot }: any) { + const { isPlaying } = usePlayButtonStore(); + const [currentPhase, setCurrentPhase] = useState<(string)>("init"); + // console.log('currentPhase: ', currentPhase); + const { armBots, addArmBot, addCurrentAction } = useArmBotStore(); + + useEffect(() => { + + // console.log('isPlaying: ', isPlaying); + if (isPlaying) { + //Moving armBot from initial point to rest position. + + if (armBot?.isActive && armBot?.state == "idle" && currentPhase == "init") { + addCurrentAction(armBot.modelUuid, 'action-001'); + setCurrentPhase("moving-to-rest"); + + } + //Waiting for trigger. + if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "moving-to-rest") { + setCurrentPhase("rest"); + } + // Moving armBot from rest position to pick up point. + if (!armBot?.isActive && armBot?.state == "idle" && currentPhase == "rest") { + + } + //Moving arm from start point to end point. + if (armBot?.isActive && armBot?.state == "running " && currentPhase == "rest-to-start ") { + + } + //Moving arm from end point to idle. + if (armBot?.isActive && armBot?.state == "running" && currentPhase == "end-to-start") { + + } + + } + + }, [currentPhase, armBot, isPlaying]) + + const HandleCallback = () => { + if (armBot.isActive && armBot.state == "idle" && currentPhase == "init") { + addCurrentAction('armbot-xyz-001', 'action-001'); + } + } + + return ( + <> + + + + + + ) +} + +export default RoboticArmInstance; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx new file mode 100644 index 0000000..52a8610 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx @@ -0,0 +1,9 @@ +import React from 'react' + +function IKInstance() { + return ( + <> + ) +} + +export default IKInstance; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx new file mode 100644 index 0000000..415c1d3 --- /dev/null +++ b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx @@ -0,0 +1,20 @@ +import React from 'react' +import RoboticArmInstance from './armInstance/roboticArmInstance'; +import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; + +function RoboticArmInstances() { + const { armBots } = useArmBotStore(); + + + return ( + <> + + {armBots?.map((robot) => ( + + ))} + + + ) +} + +export default RoboticArmInstances; \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx new file mode 100644 index 0000000..d403c6a --- /dev/null +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -0,0 +1,103 @@ +import { useEffect } from "react"; +import RoboticArmInstances from "./instances/roboticArmInstances"; +import { useArmBotStore } from "../../../store/simulation/useArmBotStore"; + +function RoboticArm() { + const { armBots, addArmBot, addCurrentAction } = useArmBotStore(); + + const armBotStatusSample: RoboticArmEventSchema[] = [ + { + state: "idle", + // currentAction: { + // actionUuid: "action-001", + // actionName: "Pick Component", + // }, + modelUuid: "armbot-xyz-001", + modelName: "ArmBot-X200", + position: [0, 0, 0], + rotation: [91.94347308985614, 0, 6.742905194869091], + type: "roboticArm", + speed: 1.5, + point: { + uuid: "point-123", + position: [0, 1.5, 0], + rotation: [0, 0, 0], + actions: [ + { + actionUuid: "action-001", + actionName: "Pick Component", + actionType: "pickAndPlace", + process: { + startPoint: [1.2, 0.3, 0.5], + endPoint: [-0.8, 1.1, 0.7], + }, + triggers: [ + { + triggerUuid: "trigger-001", + triggerName: "Start Trigger", + triggerType: "onStart", + delay: 0, + triggeredAsset: { + triggeredModel: { + modelName: "Conveyor A1", + modelUuid: "conveyor-01", + }, + triggeredPoint: { + pointName: "Start Point", + pointUuid: "conveyor-01-point-001", + }, + triggeredAction: { + actionName: "Move Forward", + actionUuid: "conveyor-action-01", + }, + }, + }, + { + triggerUuid: "trigger-002", + triggerName: "Complete Trigger", + triggerType: "onComplete", + delay: 0, + triggeredAsset: { + triggeredModel: { + modelName: "StaticMachine B2", + modelUuid: "machine-02", + }, + triggeredPoint: { + pointName: "Receive Point", + pointUuid: "machine-02-point-001", + }, + triggeredAction: { + actionName: "Process Part", + actionUuid: "machine-action-01", + }, + }, + }, + ], + }, + ], + }, + }, + ]; + + useEffect(() => { + addArmBot('123', armBotStatusSample[0]); + // addCurrentAction('armbot-xyz-001', 'action-001'); + }, []); + + + useEffect(() => { + // console.log('armBots: ', armBots); + }, [armBots]); + + return ( + <> + + + + <> + + + ); +} + +export default RoboticArm; diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 1f30e00..757a9ef 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -2,13 +2,24 @@ import React, { useEffect } from 'react'; import { useEventsStore } from '../../store/simulation/useEventsStore'; import { useProductStore } from '../../store/simulation/useProductStore'; import Vehicles from './vehicle/vehicles'; +import Points from './events/points/points'; +import Conveyor from './conveyor/conveyor'; +import RoboticArm from './roboticArm/roboticArm'; +import Materials from './materials/materials'; +import Machine from './machine/machine'; +import StorageUnit from './storageUnit/storageUnit'; +import Simulator from './simulator/simulator'; +import Products from './products/products'; +import Trigger from './triggers/trigger'; +import useModuleStore from '../../store/useModuleStore'; function Simulation() { + const { activeModule } = useModuleStore(); const { events } = useEventsStore(); const { products } = useProductStore(); useEffect(() => { - console.log('events: ', events); + // console.log('events: ', events); }, [events]) useEffect(() => { @@ -17,9 +28,37 @@ function Simulation() { return ( <> - + + {activeModule === 'simulation' && + + <> + + + + + + + + + + + + + + + + + + + + + + + + } + - ) + ); } -export default Simulation \ No newline at end of file +export default Simulation; diff --git a/app/src/modules/simulation/simulator/simulator.tsx b/app/src/modules/simulation/simulator/simulator.tsx new file mode 100644 index 0000000..8cc22d6 --- /dev/null +++ b/app/src/modules/simulation/simulator/simulator.tsx @@ -0,0 +1,11 @@ +import React from 'react' + +function Simulator() { + return ( + <> + + + ) +} + +export default Simulator \ No newline at end of file diff --git a/app/src/modules/simulation/storageUnit/instances/storageUnitInstance/storageUnitInstance.tsx b/app/src/modules/simulation/storageUnit/instances/storageUnitInstance/storageUnitInstance.tsx new file mode 100644 index 0000000..29d404e --- /dev/null +++ b/app/src/modules/simulation/storageUnit/instances/storageUnitInstance/storageUnitInstance.tsx @@ -0,0 +1,10 @@ +import React from 'react' + +function storageUnitInstance() { + return ( + <> + + ) +} + +export default storageUnitInstance \ No newline at end of file diff --git a/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx b/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx new file mode 100644 index 0000000..d79b5d8 --- /dev/null +++ b/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import StorageUnitInstance from './storageUnitInstance/storageUnitInstance' + +function StorageUnitInstances() { + return ( + <> + + + + + ) +} + +export default StorageUnitInstances \ No newline at end of file diff --git a/app/src/modules/simulation/storageUnit/storageUnit.tsx b/app/src/modules/simulation/storageUnit/storageUnit.tsx new file mode 100644 index 0000000..eee0875 --- /dev/null +++ b/app/src/modules/simulation/storageUnit/storageUnit.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import StorageUnitInstances from './instances/storageUnitInstances' + +function StorageUnit() { + return ( + <> + + + + + ) +} + +export default StorageUnit \ No newline at end of file diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx new file mode 100644 index 0000000..b38e6dd --- /dev/null +++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx @@ -0,0 +1,126 @@ +import { useThree } from '@react-three/fiber' +import React, { useEffect } from 'react' +import { Object3D } from 'three'; +import { useSubModuleStore } from '../../../../store/useModuleStore'; +import { useLeftData, useTopData } from '../../../../store/visualization/useZone3DWidgetStore'; +import { useEventsStore } from '../../../../store/simulation/useEventsStore'; +import { useProductStore } from '../../../../store/simulation/useProductStore'; +import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; +import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore'; + +function TriggerConnector() { + const { gl, raycaster, scene } = useThree(); + const { subModule } = useSubModuleStore(); + const { setTop } = useTopData(); + const { setLeft } = useLeftData(); + const { getIsEventInProduct } = useProductStore(); + const { getEventByModelUuid } = useEventsStore(); + const { selectedProduct } = useSelectedProduct(); + const { selectedAsset, setSelectedAsset, clearSelectedAsset } = useSelectedAsset(); + + useEffect(() => { + + const canvasElement = gl.domElement; + + let drag = false; + let isRightMouseDown = false; + + const onMouseDown = (evt: MouseEvent) => { + if (selectedAsset) { + clearSelectedAsset(); + } + if (evt.button === 2) { + isRightMouseDown = true; + drag = false; + } + }; + + const onMouseUp = (evt: MouseEvent) => { + if (evt.button === 2) { + isRightMouseDown = false; + } + } + + const onMouseMove = () => { + if (isRightMouseDown) { + drag = true; + } + }; + + const handleRightClick = (evt: MouseEvent) => { + if (drag) return; + evt.preventDefault(); + const canvasElement = gl.domElement; + if (!canvasElement) return; + + let intersects = raycaster.intersectObjects(scene.children, true); + if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) { + let currentObject = intersects[0].object; + + while (currentObject) { + if (currentObject.name === "Scene") { + break; + } + currentObject = currentObject.parent as Object3D; + } + if (currentObject) { + const isInProduct = getIsEventInProduct(selectedProduct.productId, currentObject.uuid); + + if (isInProduct) { + const event = getEventByModelUuid(currentObject.uuid); + if (event) { + setSelectedAsset(event) + const canvasRect = canvasElement.getBoundingClientRect(); + const relativeX = evt.clientX - canvasRect.left; + const relativeY = evt.clientY - canvasRect.top; + + setTop(relativeY); + setLeft(relativeX); + } else { + clearSelectedAsset() + } + } else { + const event = getEventByModelUuid(currentObject.uuid); + if (event) { + setSelectedAsset(event) + const canvasRect = canvasElement.getBoundingClientRect(); + const relativeX = evt.clientX - canvasRect.left; + const relativeY = evt.clientY - canvasRect.top; + + setTop(relativeY); + setLeft(relativeX); + } else { + clearSelectedAsset() + } + } + + } + } else { + clearSelectedAsset() + } + + }; + + if (subModule === 'simulations') { + canvasElement.addEventListener("mousedown", onMouseDown); + canvasElement.addEventListener("mouseup", onMouseUp); + canvasElement.addEventListener("mousemove", onMouseMove); + canvasElement.addEventListener('contextmenu', handleRightClick); + } + + return () => { + canvasElement.removeEventListener("mousedown", onMouseDown); + canvasElement.removeEventListener("mouseup", onMouseUp); + canvasElement.removeEventListener("mousemove", onMouseMove); + canvasElement.removeEventListener('contextmenu', handleRightClick); + }; + + }, [gl, subModule, selectedProduct, selectedAsset]); + + return ( + <> + + ) +} + +export default TriggerConnector \ No newline at end of file diff --git a/app/src/modules/simulation/triggers/temp.md b/app/src/modules/simulation/triggers/temp.md deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/modules/simulation/triggers/trigger.tsx b/app/src/modules/simulation/triggers/trigger.tsx new file mode 100644 index 0000000..110da2e --- /dev/null +++ b/app/src/modules/simulation/triggers/trigger.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import TriggerConnector from './connector/triggerConnector' + +function Trigger() { + return ( + <> + + + + + ) +} + +export default Trigger \ No newline at end of file diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index 0d59f2b..a0eeabd 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -1,5 +1,5 @@ +import { useEffect, useRef, useState } from 'react' import { useFrame, useThree } from '@react-three/fiber'; -import React, { useEffect, useRef, useState } from 'react'; import { useFloorItems } from '../../../../../store/store'; import * as THREE from 'three'; import { Line } from '@react-three/drei'; @@ -15,7 +15,7 @@ interface VehicleAnimatorProps { } function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail }: VehicleAnimatorProps) { - const { updateVehicleLoad, vehicles } = useVehicleStore(); + const { decrementVehicleLoad, vehicles } = useVehicleStore(); const { isPaused } = usePauseButtonStore(); const { speed } = useAnimationPlaySpeed(); const { isReset } = useResetButtonStore(); @@ -132,30 +132,23 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai const unLoadDuration = agvDetail.point.action.unLoadDuration; const droppedMaterial = agvDetail.currentLoad; fixedInterval = (unLoadDuration / droppedMaterial) * 1000; - if (!isPaused) { - step(droppedMaterial); - } else { - pausedTime = performance.now(); - step(droppedMaterial); - } + startTime = performance.now(); + step(droppedMaterial); } function step(droppedMaterial: number) { - if (!isPaused) { + const elapsedTime = performance.now() - startTime; + + if (elapsedTime >= fixedInterval) { + console.log('fixedInterval: ', fixedInterval); + console.log('elapsedTime: ', elapsedTime); + let droppedMat = droppedMaterial - 1; + decrementVehicleLoad(agvDetail.modelUuid, 1); + if (droppedMat === 0) return; startTime = performance.now(); - const elapsedTime = performance.now() - startTime - pausedTime; - if (elapsedTime >= fixedInterval) { - let droppedMat = droppedMaterial - 1; - updateVehicleLoad(agvDetail.modelUuid, droppedMat); - if (droppedMat === 0) return; - startTime = performance.now(); - pausedTime = 0; - requestAnimationFrame(() => step(droppedMat)); - } else { - requestAnimationFrame(() => step(droppedMaterial)); - } + requestAnimationFrame(() => step(droppedMat)); } else { - requestAnimationFrame(() => firstFrame); + requestAnimationFrame(() => step(droppedMaterial)); } } diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index 98d037c..d0691f9 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -9,7 +9,7 @@ import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore function VehicleInstance({ agvDetail }: any) { const { navMesh } = useNavMesh(); const { isPlaying } = usePlayButtonStore(); - const { vehicles, setVehicleActive, setVehicleState, updateVehicleLoad } = useVehicleStore(); + const { vehicles, setVehicleActive, setVehicleState, incrementVehicleLoad } = useVehicleStore(); const [currentPhase, setCurrentPhase] = useState('stationed'); const [path, setPath] = useState<[number, number, number][]>([]); @@ -50,8 +50,9 @@ function VehicleInstance({ agvDetail }: any) { agvDetail.state === 'idle' && currentPhase === 'picking' ) { + setTimeout(() => { - updateVehicleLoad(agvDetail.modelUuid, 2); + incrementVehicleLoad(agvDetail.modelUuid, 2); }, 5000); if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) { diff --git a/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx b/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx index fa0d9dd..370304e 100644 --- a/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx +++ b/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx @@ -17,7 +17,7 @@ export default function PolygonGenerator({ useEffect(() => { let allLines = arrayLinesToObject(lines.current); const wallLines = allLines?.filter((line) => line?.type === "WallLine"); - const aisleLines = allLines?.filter((line) => line?.type === "AisleLine"); + const aisleLines = allLines?.filter((line) => line?.type === "AisleLine") const wallPoints = wallLines .map((pair) => pair?.line.map((vals) => vals.position)) @@ -39,9 +39,10 @@ export default function PolygonGenerator({ ); const polygons = turf.polygonize(turf.featureCollection(lineFeatures)); + renderWallGeometry(wallPoints); - if (polygons.features.length > 1) { + if (polygons.features.length > 0) { polygons.features.forEach((feature) => { if (feature.geometry.type === "Polygon") { diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx index 6c5ea34..94e8a2c 100644 --- a/app/src/modules/simulation/vehicle/vehicles.tsx +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -146,7 +146,7 @@ function Vehicles() { useEffect(() => { addVehicle('123', vehicleStatusSample[0]); - addVehicle('123', vehicleStatusSample[1]); + // addVehicle('123', vehicleStatusSample[1]); // addVehicle('123', vehicleStatusSample[2]); }, []) diff --git a/app/src/modules/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx index cd2c57c..30f0014 100644 --- a/app/src/modules/visualization/RealTimeVisulization.tsx +++ b/app/src/modules/visualization/RealTimeVisulization.tsx @@ -125,7 +125,7 @@ const RealTimeVisulization: React.FC = () => { {} ); setZonesData(formattedData); - } catch (error) {} + } catch (error) { } } GetZoneData(); @@ -362,6 +362,10 @@ const RealTimeVisulization: React.FC = () => { "RotateY", "Delete", ]} + onClick={(e) => { + setRightSelect(e); + setEditWidgetOptions(false); + }} /> )} diff --git a/app/src/services/simulation/UpsertProductOrEventApi.ts b/app/src/services/simulation/UpsertProductOrEventApi.ts new file mode 100644 index 0000000..27fa126 --- /dev/null +++ b/app/src/services/simulation/UpsertProductOrEventApi.ts @@ -0,0 +1,26 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const addProductOrEventApi = async (body: any) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/v2/UpsertProductOrEvent`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }); + + if (!response.ok) { + throw new Error("Failed to add product or event"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/simulation/deleteEventDataApi.ts b/app/src/services/simulation/deleteEventDataApi.ts new file mode 100644 index 0000000..f263065 --- /dev/null +++ b/app/src/services/simulation/deleteEventDataApi.ts @@ -0,0 +1,26 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const deleteEventDataApi = async (body: any) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/v2/EventDataDelete`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }); + + if (!response.ok) { + throw new Error("Failed to delete event data"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/simulation/deleteProductDataApi.ts b/app/src/services/simulation/deleteProductDataApi.ts new file mode 100644 index 0000000..06718f8 --- /dev/null +++ b/app/src/services/simulation/deleteProductDataApi.ts @@ -0,0 +1,25 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const deleteProductDataApi = async (productId: string, organization: string) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/v2/productDataDelete?productId=${productId}&organization=${organization}`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + throw new Error("Failed to delete product data"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; diff --git a/app/src/services/simulation/getProductApi.ts b/app/src/services/simulation/getProductApi.ts new file mode 100644 index 0000000..cf80013 --- /dev/null +++ b/app/src/services/simulation/getProductApi.ts @@ -0,0 +1,25 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const getProductApi = async (productId: string, organization: string) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/v2/productDatas?productId=${productId}&organization=${organization}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + throw new Error("Failed to fetch product data"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; \ No newline at end of file diff --git a/app/src/services/simulation/getallProductsApi.ts b/app/src/services/simulation/getallProductsApi.ts new file mode 100644 index 0000000..46627f9 --- /dev/null +++ b/app/src/services/simulation/getallProductsApi.ts @@ -0,0 +1,25 @@ +let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`; + +export const getAllProductsApi = async ( organization: string) => { + try { + const response = await fetch(`${url_Backend_dwinzo}/api/v2/AllProducts/${organization}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + throw new Error("Failed to fetch all products data"); + } + + const result = await response.json(); + return result; + } catch (error) { + if (error instanceof Error) { + throw new Error(error.message); + } else { + throw new Error("An unknown error occurred"); + } + } +}; \ No newline at end of file diff --git a/app/src/services/simulation/temp.md b/app/src/services/simulation/temp.md deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/services/visulization/zone/getSelect2dZoneData.ts b/app/src/services/visulization/zone/getSelect2dZoneData.ts index b2c39e9..00d4dfe 100644 --- a/app/src/services/visulization/zone/getSelect2dZoneData.ts +++ b/app/src/services/visulization/zone/getSelect2dZoneData.ts @@ -7,7 +7,7 @@ export const getSelect2dZoneData = async ( ) => { try { const response = await fetch( - `${url_Backend_dwinzo}/api/v2/Zone/visualization/${ZoneId}?organization=${organization}`, + `${url_Backend_dwinzo}/api/v2/ZoneVisualization/${ZoneId}?organization=${organization}`, { method: "GET", headers: { diff --git a/app/src/store/simulation/useArmBotStore.ts b/app/src/store/simulation/useArmBotStore.ts index 7dd4716..d907f21 100644 --- a/app/src/store/simulation/useArmBotStore.ts +++ b/app/src/store/simulation/useArmBotStore.ts @@ -1,17 +1,6 @@ import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; -interface ArmBotStatus extends RoboticArmEventSchema { - productId: string; - isActive: boolean; - idleTime: number; - activeTime: number; - currentAction?: { - actionUuid: string; - actionName: string; - }; -} - interface ArmBotStore { armBots: ArmBotStatus[]; @@ -22,9 +11,14 @@ interface ArmBotStore { updates: Partial> ) => void; - startAction: (modelUuid: string, actionUuid: string) => void; - completeAction: (modelUuid: string) => void; - cancelAction: (modelUuid: string) => void; + addCurrentAction: (modelUuid: string, actionUuid: string) => void; + removeCurrentAction: (modelUuid: string) => void; + + addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void; + removeAction: (modelUuid: string, actionUuid: string) => void; + + updateStartPoint: (modelUuid: string, actionUuid: string, startPoint: [number, number, number] | null) => void; + updateEndPoint: (modelUuid: string, actionUuid: string, endPoint: [number, number, number] | null) => void; setArmBotActive: (modelUuid: string, isActive: boolean) => void; @@ -71,7 +65,7 @@ export const useArmBotStore = create()( }); }, - startAction: (modelUuid, actionUuid) => { + addCurrentAction: (modelUuid, actionUuid) => { set((state) => { const armBot = state.armBots.find(a => a.modelUuid === modelUuid); if (armBot) { @@ -87,22 +81,54 @@ export const useArmBotStore = create()( }); }, - completeAction: (modelUuid) => { + removeCurrentAction: (modelUuid) => { set((state) => { const armBot = state.armBots.find(a => a.modelUuid === modelUuid); - if (armBot && armBot.currentAction) { + if (armBot) { armBot.currentAction = undefined; armBot.isActive = false; } }); }, - cancelAction: (modelUuid) => { + addAction: (modelUuid, action) => { set((state) => { const armBot = state.armBots.find(a => a.modelUuid === modelUuid); if (armBot) { - armBot.currentAction = undefined; - armBot.isActive = false; + armBot.point.actions.push(action); + } + }); + }, + + removeAction: (modelUuid, actionUuid) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + armBot.point.actions = armBot.point.actions.filter(a => a.actionUuid !== actionUuid); + } + }); + }, + + updateStartPoint: (modelUuid, actionUuid, startPoint) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + const action = armBot.point.actions.find(a => a.actionUuid === actionUuid); + if (action) { + action.process.startPoint = startPoint; + } + } + }); + }, + + updateEndPoint: (modelUuid, actionUuid, endPoint) => { + set((state) => { + const armBot = state.armBots.find(a => a.modelUuid === modelUuid); + if (armBot) { + const action = armBot.point.actions.find(a => a.actionUuid === actionUuid); + if (action) { + action.process.endPoint = endPoint; + } } }); }, diff --git a/app/src/store/simulation/useConveyorStore.ts b/app/src/store/simulation/useConveyorStore.ts index 059e76b..15dbf34 100644 --- a/app/src/store/simulation/useConveyorStore.ts +++ b/app/src/store/simulation/useConveyorStore.ts @@ -1,13 +1,6 @@ import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; -interface ConveyorStatus extends ConveyorEventSchema { - productId: string; - isActive: boolean; - idleTime: number; - activeTime: number; -} - interface ConveyorStore { conveyors: ConveyorStatus[]; diff --git a/app/src/store/simulation/useMachineStore.ts b/app/src/store/simulation/useMachineStore.ts index 15997b9..cc927f7 100644 --- a/app/src/store/simulation/useMachineStore.ts +++ b/app/src/store/simulation/useMachineStore.ts @@ -1,13 +1,6 @@ import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; -interface MachineStatus extends MachineEventSchema { - productId: string; - isActive: boolean; - idleTime: number; - activeTime: number; -} - interface MachineStore { machines: MachineStatus[]; diff --git a/app/src/store/simulation/useMaterialStore.ts b/app/src/store/simulation/useMaterialStore.ts new file mode 100644 index 0000000..56a35a7 --- /dev/null +++ b/app/src/store/simulation/useMaterialStore.ts @@ -0,0 +1,76 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; + +type MaterialsStore = { + materials: MaterialsSchema; + + addMaterial: (material: MaterialSchema) => void; + removeMaterial: (materialId: string) => void; + updateMaterial: (materialId: string, updates: Partial) => void; + + setStartTime: (materialId: string, startTime: string) => void; + setEndTime: (materialId: string, endTime: string) => void; + setCost: (materialId: string, cost: number) => void; + setWeight: (materialId: string, weight: number) => void; + + getMaterialById: (materialId: string) => MaterialSchema | undefined; +}; + +export const useMaterialStore = create()( + immer((set, get) => ({ + materials: [], + + addMaterial: (material) => { + set((state) => { + state.materials.push(material); + }); + }, + + removeMaterial: (materialId) => { + set((state) => { + state.materials = state.materials.filter(m => m.materialId !== materialId); + }); + }, + + updateMaterial: (materialId, updates) => { + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) { + Object.assign(material, updates); + } + }); + }, + + setStartTime: (materialId, startTime) => { + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) material.startTime = startTime; + }); + }, + + setEndTime: (materialId, endTime) => { + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) material.endTime = endTime; + }); + }, + + setCost: (materialId, cost) => { + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) material.cost = cost; + }); + }, + + setWeight: (materialId, weight) => { + set((state) => { + const material = state.materials.find(m => m.materialId === materialId); + if (material) material.weight = weight; + }); + }, + + getMaterialById: (materialId) => { + return get().materials.find(m => m.materialId === materialId); + }, + })) +); diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts index 41a13f6..8ec74cf 100644 --- a/app/src/store/simulation/useProductStore.ts +++ b/app/src/store/simulation/useProductStore.ts @@ -7,7 +7,7 @@ type ProductsStore = { // Product-level actions addProduct: (productName: string, productId: string) => void; removeProduct: (productId: string) => void; - updateProduct: (productId: string, updates: Partial<{ productName: string; eventsData: EventsSchema[] }>) => void; + updateProduct: (productId: string, updates: Partial<{ productName: string; eventDatas: EventsSchema[] }>) => void; // Event-level actions addEvent: (productId: string, event: EventsSchema) => void; @@ -48,8 +48,18 @@ type ProductsStore = { updates: Partial ) => void; + // Renaming functions + renameProduct: (productId: string, newName: string) => void; + renameAction: (actionUuid: string, newName: string) => void; + renameTrigger: (triggerUuid: string, newName: string) => void; + // Helper functions - getProductById: (productId: string) => { productName: string; productId: string; eventsData: EventsSchema[] } | undefined; + getProductById: (productId: string) => { productName: string; productId: string; eventDatas: EventsSchema[] } | undefined; + getEventByModelUuid: (productId: string, modelUuid: string) => EventsSchema | undefined; + getPointByUuid: (productId: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined; + getActionByUuid: (productId: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined; + getTriggerByUuid: (productId: string, triggerUuid: string) => TriggerSchema | undefined; + getIsEventInProduct: (productId: string, modelUuid: string) => boolean; }; export const useProductStore = create()( @@ -62,7 +72,7 @@ export const useProductStore = create()( const newProduct = { productName, productId: productId, - eventsData: [] + eventDatas: [] }; state.products.push(newProduct); }); @@ -88,7 +98,7 @@ export const useProductStore = create()( set((state) => { const product = state.products.find(p => p.productId === productId); if (product) { - product.eventsData.push(event); + product.eventDatas.push(event); } }); }, @@ -97,7 +107,7 @@ export const useProductStore = create()( set((state) => { const product = state.products.find(p => p.productId === productId); if (product) { - product.eventsData = product.eventsData.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid); + product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid); } }); }, @@ -106,7 +116,7 @@ export const useProductStore = create()( set((state) => { const product = state.products.find(p => p.productId === productId); if (product) { - const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event) { Object.assign(event, updates); } @@ -119,7 +129,7 @@ export const useProductStore = create()( set((state) => { const product = state.products.find(p => p.productId === productId); if (product) { - const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event && 'points' in event) { (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); } else if (event && 'point' in event) { @@ -133,7 +143,7 @@ export const useProductStore = create()( set((state) => { const product = state.products.find(p => p.productId === productId); if (product) { - const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event && 'points' in event) { (event as ConveyorEventSchema).points = (event as ConveyorEventSchema).points.filter(p => p.uuid !== pointUuid); } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) { @@ -147,7 +157,7 @@ export const useProductStore = create()( set((state) => { const product = state.products.find(p => p.productId === productId); if (product) { - const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event && 'points' in event) { const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); if (point) { @@ -165,7 +175,7 @@ export const useProductStore = create()( set((state) => { const product = state.products.find(p => p.productId === productId); if (product) { - const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); if (event && 'points' in event) { const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); if (point) { @@ -185,7 +195,7 @@ export const useProductStore = create()( removeAction: (actionUuid: string) => { set((state) => { for (const product of state.products) { - for (const event of product.eventsData) { + for (const event of product.eventDatas) { if ('points' in event) { // Handle ConveyorEventSchema for (const point of (event as ConveyorEventSchema).points) { @@ -209,7 +219,7 @@ export const useProductStore = create()( updateAction: (actionUuid, updates) => { set((state) => { for (const product of state.products) { - for (const event of product.eventsData) { + for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { if (point.action && point.action.actionUuid === actionUuid) { @@ -239,7 +249,7 @@ export const useProductStore = create()( addTrigger: (actionUuid, trigger) => { set((state) => { for (const product of state.products) { - for (const event of product.eventsData) { + for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { if (point.action && point.action.actionUuid === actionUuid) { @@ -268,7 +278,7 @@ export const useProductStore = create()( removeTrigger: (triggerUuid) => { set((state) => { for (const product of state.products) { - for (const event of product.eventsData) { + for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { if (point.action && 'triggers' in point.action) { @@ -295,7 +305,7 @@ export const useProductStore = create()( updateTrigger: (triggerUuid, updates) => { set((state) => { for (const product of state.products) { - for (const event of product.eventsData) { + for (const event of product.eventDatas) { if ('points' in event) { for (const point of (event as ConveyorEventSchema).points) { if (point.action && 'triggers' in point.action) { @@ -331,9 +341,170 @@ export const useProductStore = create()( }); }, + // Renaming functions + renameProduct: (productId, newName) => { + set((state) => { + const product = state.products.find(p => p.productId === productId); + if (product) { + product.productName = newName; + } + }); + }, + + renameAction: (actionUuid, newName) => { + set((state) => { + for (const product of state.products) { + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && point.action.actionUuid === actionUuid) { + point.action.actionName = newName; + return; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action.actionUuid === actionUuid) { + point.action.actionName = newName; + return; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) { + action.actionName = newName; + return; + } + } + } + } + } + }); + }, + + renameTrigger: (triggerUuid, newName) => { + set((state) => { + for (const product of state.products) { + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action && 'triggers' in point.action) { + const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid); + if (trigger) { + trigger.triggerName = newName; + return; + } + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && 'triggers' in point.action) { + const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + trigger.triggerName = newName; + return; + } + } else if ('actions' in point) { + for (const action of point.actions) { + if ('triggers' in action) { + const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid); + if (trigger) { + trigger.triggerName = newName; + return; + } + } + } + } + } + } + } + }); + }, + // Helper functions getProductById: (productId) => { return get().products.find(p => p.productId === productId); + }, + + getEventByModelUuid: (productId, modelUuid) => { + const product = get().getProductById(productId); + if (!product) return undefined; + return product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid); + }, + + getPointByUuid: (productId, modelUuid, pointUuid) => { + const event = get().getEventByModelUuid(productId, modelUuid); + if (!event) return undefined; + + if ('points' in event) { + return (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); + } else if ('point' in event && (event as any).point.uuid === pointUuid) { + return (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point; + } + return undefined; + }, + + getActionByUuid: (productId, actionUuid) => { + const product = get().products.find(p => p.productId === productId); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + if (point.action?.actionUuid === actionUuid) { + return point.action; + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point && point.action?.actionUuid === actionUuid) { + return point.action; + } else if ('actions' in point) { + const action = point.actions.find((a: any) => a.actionUuid === actionUuid); + if (action) return action; + } + } + } + return undefined; + }, + + getTriggerByUuid: (productId, triggerUuid) => { + const product = get().products.find(p => p.productId === productId); + if (!product) return undefined; + + for (const event of product.eventDatas) { + if ('points' in event) { + for (const point of (event as ConveyorEventSchema).points) { + for (const trigger of point.action?.triggers || []) { + if (trigger.triggerUuid === triggerUuid) { + return trigger; + } + } + } + } else if ('point' in event) { + const point = (event as any).point; + if ('action' in point) { + for (const trigger of point.action?.triggers || []) { + if (trigger.triggerUuid === triggerUuid) { + return trigger; + } + } + } else if ('actions' in point) { + for (const action of point.actions) { + for (const trigger of action.triggers || []) { + if (trigger.triggerUuid === triggerUuid) { + return trigger; + } + } + } + } + } + } + return undefined; + }, + + getIsEventInProduct: (productId, modelUuid) => { + const product = get().getProductById(productId); + if (!product) return false; + return product.eventDatas.some(e => 'modelUuid' in e && e.modelUuid === modelUuid); } })) ); diff --git a/app/src/store/simulation/useSimulationStore.ts b/app/src/store/simulation/useSimulationStore.ts new file mode 100644 index 0000000..5085688 --- /dev/null +++ b/app/src/store/simulation/useSimulationStore.ts @@ -0,0 +1,117 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; +import * as THREE from 'three'; + +interface SelectedEventSphereState { + selectedEventSphere: THREE.Mesh | null; + setSelectedEventSphere: (mesh: THREE.Mesh | null) => void; + clearSelectedEventSphere: () => void; +} + +export const useSelectedEventSphere = create()( + immer((set) => ({ + selectedEventSphere: null, + setSelectedEventSphere: (mesh) => { + set((state) => { + state.selectedEventSphere = mesh; + }); + }, + clearSelectedEventSphere: () => { + set((state) => { + state.selectedEventSphere = null; + }); + }, + })) +); + +interface SelectedEventDataState { + selectedEventData: { data: EventsSchema; selectedPoint: string } | undefined; + setSelectedEventData: (data: EventsSchema, selectedPoint: string) => void; + clearSelectedEventData: () => void; +} + +export const useSelectedEventData = create()( + immer((set) => ({ + selectedEventData: undefined, + setSelectedEventData: (data, selectedPoint) => { + set((state) => { + state.selectedEventData = { data, selectedPoint }; + }); + }, + clearSelectedEventData: () => { + set((state) => { + state.selectedEventData = undefined; + }); + }, + })) +); + +interface SelectedAssetState { + selectedAsset: EventsSchema | undefined; + setSelectedAsset: (EventData: EventsSchema) => void; + clearSelectedAsset: () => void; +} + +export const useSelectedAsset = create()( + immer((set) => ({ + selectedAsset: undefined, + setSelectedAsset: (EventData) => { + set((state) => { + state.selectedAsset = EventData; + }); + }, + clearSelectedAsset: () => { + set((state) => { + state.selectedAsset = undefined; + }); + }, + })) +); + +interface SelectedProductState { + selectedProduct: { productId: string; productName: string }; + setSelectedProduct: (productId: string, productName: string) => void; + clearSelectedProduct: () => void; +} + +export const useSelectedProduct = create()( + immer((set) => ({ + selectedProduct: { productId: '', productName: '' }, + setSelectedProduct: (productId, productName) => { + set((state) => { + state.selectedProduct.productId = productId; + state.selectedProduct.productName = productName; + }); + }, + clearSelectedProduct: () => { + set((state) => { + state.selectedProduct.productId = ''; + state.selectedProduct.productName = ''; + }); + }, + })) +); + +interface SelectedActionState { + selectedAction: { actionId: string; actionName: string }; + setSelectedAction: (actionId: string, actionName: string) => void; + clearSelectedAction: () => void; +} + +export const useSelectedAction = create()( + immer((set) => ({ + selectedAction: { actionId: '', actionName: '' }, + setSelectedAction: (actionId, actionName) => { + set((state) => { + state.selectedAction.actionId = actionId; + state.selectedAction.actionName = actionName; + }); + }, + clearSelectedAction: () => { + set((state) => { + state.selectedAction.actionId = ''; + state.selectedAction.actionName = ''; + }); + }, + })) +); \ No newline at end of file diff --git a/app/src/store/simulation/useStorageUnitStore.ts b/app/src/store/simulation/useStorageUnitStore.ts index 04b4db0..d729708 100644 --- a/app/src/store/simulation/useStorageUnitStore.ts +++ b/app/src/store/simulation/useStorageUnitStore.ts @@ -1,14 +1,6 @@ import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; -interface StorageUnitStatus extends StorageEventSchema { - productId: string; - isActive: boolean; - idleTime: number; - activeTime: number; - currentLoad: number; -} - interface StorageUnitStore { storageUnits: StorageUnitStatus[]; diff --git a/app/src/store/simulation/useVehicleStore.ts b/app/src/store/simulation/useVehicleStore.ts index 9f7ac50..779153e 100644 --- a/app/src/store/simulation/useVehicleStore.ts +++ b/app/src/store/simulation/useVehicleStore.ts @@ -21,7 +21,8 @@ interface VehiclesStore { ) => void; setVehicleActive: (modelUuid: string, isActive: boolean) => void; - updateVehicleLoad: (modelUuid: string, load: number) => void; + incrementVehicleLoad: (modelUuid: string, incrementBy: number) => void; + decrementVehicleLoad: (modelUuid: string, decrementBy: number) => void; setVehicleState: (modelUuid: string, newState: VehicleStatus['state']) => void; incrementActiveTime: (modelUuid: string, incrementBy: number) => void; incrementIdleTime: (modelUuid: string, incrementBy: number) => void; @@ -74,11 +75,20 @@ export const useVehicleStore = create()( }); }, - updateVehicleLoad: (modelUuid, load) => { + incrementVehicleLoad: (modelUuid, incrementBy) => { set((state) => { const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); if (vehicle) { - vehicle.currentLoad = load; + vehicle.currentLoad += incrementBy; + } + }); + }, + + decrementVehicleLoad: (modelUuid, decrementBy) => { + set((state) => { + const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); + if (vehicle) { + vehicle.currentLoad -= decrementBy; } }); }, diff --git a/app/src/store/useModuleStore.ts b/app/src/store/useModuleStore.ts index 1012792..3cc1d00 100644 --- a/app/src/store/useModuleStore.ts +++ b/app/src/store/useModuleStore.ts @@ -13,14 +13,17 @@ const useModuleStore = create((set) => ({ export default useModuleStore; // New store for subModule + +type SubModule = 'properties' | 'simulations' | 'mechanics' | 'analysis' | 'zoneProperties'; + interface SubModuleStore { - subModule: string; - setSubModule: (subModule: string) => void; + subModule: SubModule; + setSubModule: (subModule: SubModule) => void; } const useSubModuleStore = create((set) => ({ subModule: "properties", // Initial subModule state - setSubModule: (subModule) => set({ subModule }), // Update subModule state + setSubModule: (value) => set({ subModule: value }), // Update subModule state })); export { useSubModuleStore }; diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index 883f6c2..21554e8 100644 --- a/app/src/styles/components/input.scss +++ b/app/src/styles/components/input.scss @@ -7,8 +7,8 @@ input { width: 100%; padding: 2px 4px; border-radius: #{$border-radius-small}; - outline: 2px solid var(--border-color); - outline-offset: -2px; + outline: 1px solid var(--border-color); + outline-offset: -1px; border: none; background: transparent; color: var(--input-text-color); @@ -30,6 +30,24 @@ input { background-color: var(--background-color) !important; -webkit-box-shadow: 0 0 0px 1000px var(--background-color) inset !important; } + + // File input specific style adjustments + &::file-selector-button { + font-size: 14px; + color: var(--accent-color); + background-color: var(--background-color-secondary); + border: none; + outline: none; + border-radius: #{$border-radius-small}; + padding: 2px; + cursor: pointer; + + // Hover effect for the file button + &:hover { + color: var(--primary-color); + background-color: var(--accent-color); + } + } } .input-value { @@ -712,3 +730,47 @@ input { border: 1px solid var(--accent-color); } } + +.preview-selection-with-upload-wrapper { + .input-header-container { + padding: 6px 12px; + @include flex-space-between; + .arrow-container { + transition: all 0.2s; + @include flex-center; + } + } + .upload-custom-asset-button{ + padding: 6px 12px; + @include flex-space-between; + .title{ + white-space: nowrap; + width: 40%; + } + input{ + display: none; + } + .upload-button{ + width: 60%; + background: var(--highlight-accent-color); + color: var(--accent-color); + padding: 3px 6px; + border-radius: #{$border-radius-small}; + text-align: center; + } + } + .canvas-wrapper { + height: 150px; + width: 100%; + padding: 8px; + padding-right: 4px; + overflow: hidden; + position: relative; + .canvas-container { + width: 100%; + height: 100%; + border-radius: #{$border-radius-small}; + background-color: var(--background-color-gray); + } + } +} diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss index 305206a..cab078e 100644 --- a/app/src/styles/layout/sidebar.scss +++ b/app/src/styles/layout/sidebar.scss @@ -7,6 +7,7 @@ top: 32px; left: 8px; background-color: var(--background-color); + backdrop-filter: blur(150px); border-radius: #{$border-radius-extra-large}; box-shadow: #{$box-shadow-medium}; z-index: #{$z-index-tools}; @@ -131,15 +132,12 @@ } .widgets-wrapper { - min-height: 50vh; max-height: 60vh; overflow: auto; } .widget-left-sideBar { - - .widget2D { overflow: auto; @@ -246,6 +244,7 @@ top: 32px; right: 8px; background-color: var(--background-color); + backdrop-filter: blur(150px); border-radius: #{$border-radius-extra-large}; box-shadow: #{$box-shadow-medium}; z-index: #{$z-index-tools}; @@ -643,7 +642,7 @@ path { stroke: var(--accent-color); - strokewidth: 1.5px; + stroke-width: 1.5px; } &:hover { @@ -658,7 +657,8 @@ } .machine-mechanics-content-container, - .simulations-container { + .simulations-container, + .event-proprties-wrapper { max-height: calc(60vh - (47px - 35px)); overflow: auto; overflow-y: scroll; @@ -682,6 +682,52 @@ } } + .global-props { + .property-list-container { + .property-item { + .value-field-container { + margin: 0; + input { + padding: 5px 4px; + } + .dropdown { + top: 4px; + right: 4px; + } + } + } + } + } + + .selected-actions-details { + .selected-actions-header .input-value { + padding: 8px 12px; + color: var(--accent-color); + } + .selected-actions-list { + margin-bottom: 8px; + .eye-dropper-input-container{ + padding: 6px 12px; + .regularDropdown-container { + padding: 5px 8px; + outline: 2px solid var(--border-color); + outline-offset: -2px; + border: none; + } + } + .value-field-container { + margin: 0; + input { + padding: 5px 4px; + } + .dropdown { + top: 4px; + right: 4px; + } + } + } + } + .lists-main-container { margin: 2px 8px; width: calc(100% - 12px); @@ -712,6 +758,7 @@ input { width: fit-content; + outline: none; accent-color: var(--accent-color); } } @@ -1183,25 +1230,21 @@ z-index: 3; padding: 8px; width: 100%; + max-height: 38px; font-size: var(--font-size-regular); - background: color-mix(in srgb, - var(--background-color) 40%, - transparent); + background: color-mix( + in srgb, + var(--background-color) 40%, + transparent + ); backdrop-filter: blur(5px); opacity: 0; transition: opacity 0.3s ease; - - /* Added properties for ellipsis */ display: -webkit-box; - /* Necessary for multiline truncation */ -webkit-line-clamp: 2; - /* Number of lines to show */ -webkit-box-orient: vertical; - /* Box orientation for the ellipsis */ overflow: hidden; - /* Hide overflowing content */ text-overflow: ellipsis; - /* Add ellipsis for truncated content */ } .asset-image { @@ -1271,4 +1314,4 @@ .assets-wrapper { margin: 0; } -} \ No newline at end of file +} diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts index 3d75662..eb49368 100644 --- a/app/src/types/simulationTypes.d.ts +++ b/app/src/types/simulationTypes.d.ts @@ -25,7 +25,7 @@ interface ConveyorPointSchema { action: { actionUuid: string; actionName: string; - actionType: "default" | "spawn" | "swap" | "despawn"; + actionType: "default" | "spawn" | "swap" | "delay" | "despawn"; material: string; delay: number | "inherit"; spawnInterval: number | "inherit"; @@ -59,7 +59,7 @@ interface RoboticArmPointSchema { actionUuid: string; actionName: string; actionType: "pickAndPlace"; - process: { startPoint: string; endPoint: string }; + process: { startPoint: [number, number, number] | null; endPoint: [number, number, number] | null; }; triggers: TriggerSchema[]; }[]; } @@ -85,7 +85,7 @@ interface StoragePointSchema { action: { actionUuid: string; actionName: string; - actionType: "storage"; + actionType: "store"; materials: { materialName: string; materialId: string; }[]; storageCapacity: number; }; @@ -119,14 +119,43 @@ interface StorageEventSchema extends AssetEventSchema { point: StoragePointSchema; } -type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema | []; +type PointsScheme = ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema; + +type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema; type productsSchema = { productName: string; productId: string; - eventsData: EventsSchema[]; + eventDatas: EventsSchema[]; }[] + +interface ConveyorStatus extends ConveyorEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; + +} + +interface MachineStatus extends MachineEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; +} + +interface ArmBotStatus extends RoboticArmEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; + currentAction?: { + actionUuid: string; + actionName: string; + }; +} + interface VehicleStatus extends VehicleEventSchema { productId: string; isActive: boolean; @@ -134,4 +163,25 @@ interface VehicleStatus extends VehicleEventSchema { activeTime: number; currentLoad: number; distanceTraveled: number; -} \ No newline at end of file +} + +interface StorageUnitStatus extends StorageEventSchema { + productId: string; + isActive: boolean; + idleTime: number; + activeTime: number; + currentLoad: number; +} + +interface MaterialSchema { + materialId: string; + materialName: string; + materialType: string; + isActive: boolean; + startTime?: string; + endTime?: string; + cost?: number; + weight?: number; +} + +type MaterialsSchema = MaterialSchema[]; \ No newline at end of file