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 dfe70d7..4c085aa 100644
--- a/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx
+++ b/app/src/components/layout/sidebarLeft/visualization/widgets/WidgetsFloating.tsx
@@ -5,7 +5,7 @@ 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 FleetEfficiency from "../../../../../modules//visualization/widgets/floating/cards/FleetEfficiency";
diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx
index b13944c..38f0b18 100644
--- a/app/src/components/layout/sidebarRight/SideBarRight.tsx
+++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx
@@ -1,173 +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 { 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();
+ const { activeModule } = useModuleStore();
+ const { toggleUI } = useToggleStore();
+ const { subModule, setSubModule } = useSubModuleStore();
+ const { selectedFloorItem } = useSelectedFloorItem();
+ const { selectedEventData } = useSelectedEventData();
+ const { selectedEventSphere } = useSelectedEventSphere();
- // Reset activeList whenever activeModule changes
- useEffect(() => {
- if (activeModule !== "simulation") setSubModule("properties");
- if (activeModule === "simulation") setSubModule("mechanics");
- }, [activeModule]);
+ // Reset activeList whenever activeModule changes
+ useEffect(() => {
+ if (activeModule !== "simulation") setSubModule("properties");
+ if (activeModule === "simulation") setSubModule("simulations");
+ }, [activeModule]);
- // romove late
- const dummyData = {
- assetType: "store",
- selectedPoint: {
- name: "Point A",
- uuid: "123e4567-e89b-12d3-a456-426614174000",
- actions: [
- {
- uuid: "action-1",
- name: "Action One",
- },
- {
- uuid: "action-2",
- name: "Action Two",
- },
- {
- uuid: "action-3",
- name: "Action Three",
- },
- ],
- },
- selectedItem: {
- item: {
- uuid: "item-1",
- name: "Item One",
- isUsed: false,
- },
- },
- setSelectedPoint: (value: string) => {
- console.log(`Selected point updated to: ${value}`);
- },
- selectedActionSphere: "Sphere A",
- };
+ useEffect(() => {
+ if (activeModule !== "mechanics" && selectedEventData && selectedEventSphere) {
+ setSubModule("mechanics");
+ } else if (!selectedEventData && !selectedEventSphere) {
+ if (activeModule === 'simulation') {
+ setSubModule("simulations");
+ }
+ };
+ }, [activeModule, selectedEventData, selectedEventSphere])
- return (
-
-
- {toggleUI && (
-
- {/* {activeModule === "builder" && ( */}
-
setSubModule("properties")}
- >
-
-
- {/* )} */}
- {activeModule === "simulation" && (
- <>
-
setSubModule("mechanics")}
- >
-
-
-
setSubModule("simulations")}
- >
-
-
-
setSubModule("analysis")}
- >
-
-
- >
- )}
+ 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
index 05f5b2d..f8a8eeb 100644
--- a/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx
+++ b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx
@@ -1,210 +1,62 @@
-import React, { useRef, useState } from "react";
-import InputWithDropDown from "../../../../ui/inputs/InputWithDropDown";
-import LabledDropdown from "../../../../ui/inputs/LabledDropdown";
-import {
- AddIcon,
- RemoveIcon,
- ResizeHeightIcon,
-} from "../../../../icons/ExportCommonIcons";
-import RenameInput from "../../../../ui/inputs/RenameInput";
-import { handleResize } from "../../../../../functions/handleResizePannel";
-import { handleActionToggle } from "./functions/handleActionToggle";
-import { handleDeleteAction } from "./functions/handleDeleteAction";
-import DefaultAction from "./actions/DefaultAction";
-import SpawnAction from "./actions/SpawnAction";
-import SwapAction from "./actions/SwapAction";
-import DespawnAction from "./actions/DespawnAction";
-import TravelAction from "./actions/TravelAction";
-import PickAndPlaceAction from "./actions/PickAndPlaceAction";
-import ProcessAction from "./actions/ProcessAction";
-import StorageAction from "./actions/StorageAction";
-import Trigger from "./trigger/Trigger";
+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";
-interface EventPropertiesProps {
- assetType: string;
- selectedPoint: {
- name: string;
- uuid: string;
- actions: {
- uuid: string;
- name: string;
- }[];
- };
- selectedItem: {
- item: {
- uuid: string;
- name: string;
- } | null;
- };
- setSelectedPoint: (value: string) => void;
- selectedActionSphere: string;
-}
+const EventProperties: React.FC = () => {
+ const { selectedEventData } = useSelectedEventData();
+ const { getEventByModelUuid } = useProductStore();
+ const { selectedProduct } = useSelectedProduct();
+ const [currentEventData, setCurrentEventData] = useState
(null);
+ const [assetType, setAssetType] = useState(null);
-const EventProperties: React.FC = ({
- assetType,
- selectedPoint,
- selectedItem,
- setSelectedPoint,
- selectedActionSphere,
-}) => {
- const actionsContainerRef = useRef(null);
+ useEffect(() => {
+ const event = getCurrentEventData();
+ setCurrentEventData(event);
- const [activeOption, setActiveOption] = useState("default");
+ const type = determineAssetType(event);
+ setAssetType(type);
- const getAvailableActions = () => {
- if (assetType === "conveyor") {
- return {
- defaultOption: "default",
- options: ["default", "spawn", "swap", "despawn"],
- };
- }
- if (assetType === "vehicle") {
- return {
- defaultOption: "travel",
- options: ["travel"],
- };
- }
- if (assetType === "roboticArm") {
- return {
- defaultOption: "pickAndPlace",
- options: ["pickAndPlace"],
- };
- }
- if (assetType === "machine") {
- return {
- defaultOption: "process",
- options: ["process"],
- };
- }
- if (assetType === "store") {
- return {
- defaultOption: "store",
- options: ["store", "spawn"],
- };
- } else {
- return {
- defaultOption: "default",
- options: ["default"],
- };
- }
- };
+ }, [selectedEventData, selectedProduct]);
- return (
-
-
-
-
- {/*
- setTypeOption(option)}
- />
-
*/}
-
- {}}
- onChange={(value) => console.log(value)}
- />
-
-
- {}}
- onChange={(value) => console.log(value)}
- />
-
-
-
-
-
-
-
Actions
-
-
-
-
- {selectedPoint?.actions.map((action) => (
-
-
handleActionToggle(action.uuid)}
- >
-
-
- {selectedPoint?.actions.length > 1 && (
-
handleDeleteAction(action.uuid)}
- >
-
-
- )}
-
- ))}
+ 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' &&
}
+ >
+ }
-
handleResize(e, actionsContainerRef)}
- >
-
-
-
-
-
-
-
-
-
-
-
setActiveOption(option)}
- />
- {activeOption === "default" && } {/* done */}
- {activeOption === "spawn" && } {/* done */}
- {activeOption === "swap" && } {/* done */}
- {activeOption === "despawn" && } {/* done */}
- {activeOption === "travel" && } {/* done */}
- {activeOption === "pickAndPlace" && }{" "}
- {/* done */}
- {activeOption === "process" && } {/* done */}
- {activeOption === "store" && } {/* done */}
-
-
-
-
-
-
- );
+ );
};
-export default EventProperties;
+export default EventProperties;
\ No newline at end of file
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
index 4116add..88cbd2e 100644
--- a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DespawnAction.tsx
+++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/DespawnAction.tsx
@@ -1,19 +1,9 @@
import React from "react";
-import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
const DespawnAction: React.FC = () => {
return (
- {}}
- onChange={(value) => console.log(value)}
- />
+ <>
+ >
);
};
diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/PickAndPlaceAction.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/PickAndPlaceAction.tsx
index 9574669..175a824 100644
--- a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/PickAndPlaceAction.tsx
+++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/PickAndPlaceAction.tsx
@@ -1,13 +1,25 @@
import React from "react";
import EyeDropInput from "../../../../../ui/inputs/EyeDropInput";
-const PickAndPlaceAction: React.FC = () => {
- return (
- <>
- {}} />
- {}} />
- >
- );
+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
index a27894e..331cf1b 100644
--- a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/ProcessAction.tsx
+++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/ProcessAction.tsx
@@ -2,23 +2,47 @@ import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import SwapAction from "./SwapAction";
-const ProcessAction: React.FC = () => {
- return (
- <>
- {}}
- onChange={(value) => console.log(value)}
- />
-
- >
- );
+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
index 0c37cdb..7d8002e 100644
--- a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SpawnAction.tsx
+++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SpawnAction.tsx
@@ -1,35 +1,72 @@
import React from "react";
import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
+import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
-const SpawnAction: React.FC = () => {
- return (
- <>
- {}}
- onChange={(value) => console.log(value)}
- />
- {}}
- onChange={(value) => console.log(value)}
- />
-
- >
- );
+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
index ab2109b..5c9bf86 100644
--- a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/StorageAction.tsx
+++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/StorageAction.tsx
@@ -1,20 +1,28 @@
import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
-const StorageAction: React.FC = () => {
- return (
- {}}
- onChange={(value) => console.log(value)}
- />
- );
+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
index eb8e483..23b203f 100644
--- a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SwapAction.tsx
+++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/SwapAction.tsx
@@ -1,8 +1,27 @@
import React from "react";
import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload";
+import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
-const SwapAction: React.FC = () => {
- return ;
+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
index ee4bda0..49eb683 100644
--- a/app/src/components/layout/sidebarRight/properties/eventProperties/actions/TravelAction.tsx
+++ b/app/src/components/layout/sidebarRight/properties/eventProperties/actions/TravelAction.tsx
@@ -2,35 +2,77 @@ import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import EyeDropInput from "../../../../../ui/inputs/EyeDropInput";
-const TravelAction: React.FC = () => {
- return (
- <>
- {}}
- onChange={(value) => console.log(value)}
- />
- {}}
- onChange={(value) => console.log(value)}
- />
- {}} />
- {}} />
- >
- );
+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/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}
+ />
+
+
+
+
+
+
+
+
+
+ {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/simulation/Simulations.tsx b/app/src/components/layout/sidebarRight/simulation/Simulations.tsx
index cbb19fa..926ce44 100644
--- a/app/src/components/layout/sidebarRight/simulation/Simulations.tsx
+++ b/app/src/components/layout/sidebarRight/simulation/Simulations.tsx
@@ -7,9 +7,12 @@ import {
} from "../../../icons/ExportCommonIcons";
import RenameInput from "../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../functions/handleResizePannel";
-import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
+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";
+import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
interface Event {
pathName: string;
@@ -31,14 +34,9 @@ const List: React.FC = ({ val }) => {
const Simulations: React.FC = () => {
const productsContainerRef = useRef(null);
- const { products, addProduct, removeProduct, renameProduct } = useProductStore();
+ const { products, addProduct, removeProduct, renameProduct, addEvent, removeEvent } = useProductStore();
const { selectedProduct, setSelectedProduct } = useSelectedProduct();
-
- useEffect(() => {
- if (products.length > 0 && selectedProduct.productId === '' && selectedProduct.productName === '') {
- setSelectedProduct(products[0].productId, products[0].productName);
- }
- }, [products, selectedProduct]);
+ const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
const handleAddProduct = () => {
addProduct(`Product ${products.length + 1}`, generateUUID());
@@ -75,12 +73,31 @@ const Simulations: React.FC = () => {
}
};
+ const handleAddEventToProduct = () => {
+ if (selectedAsset) {
+ addEvent(selectedProduct.productId, selectedAsset);
+ // upsertProductOrEventApi({
+ // productName: selectedProduct.productName,
+ // productId: selectedProduct.productId,
+ // eventDatas: 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?.eventsData.map((event, index) => ({
- pathName: `${event.modelName} - ${event.type} #${index + 1}`,
+ const events: Event[] = selectedProductData?.eventDatas.map((event) => ({
+ pathName: event.modelName,
})) || [];
return (
@@ -165,6 +182,21 @@ const Simulations: React.FC = () => {
+
+ {selectedAsset &&
+
+ {
+ if (option === 'Add to Product') {
+ handleAddEventToProduct();
+ } else {
+ handleRemoveEventFromProduct();
+ }
+ }}
+ />
+
+ }
);
};
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/PreviewSelectionWithUpload.tsx b/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx
index e2c2936..3e14517 100644
--- a/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx
+++ b/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx
@@ -35,12 +35,6 @@ const PreviewSelectionWithUpload: React.FC = () => {
Upload here
- console.log(option)}
- />
);
diff --git a/app/src/components/ui/list/List.tsx b/app/src/components/ui/list/List.tsx
index ade61cc..06419af 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 2378bf3..c46c0e7 100644
--- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts
+++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts
@@ -14,6 +14,7 @@ async function loadInitialFloorItems(
itemsGroup: Types.RefGroup,
setFloorItems: Types.setFloorItemSetState,
addEvent: (event: EventsSchema) => void,
+ renderDistance: number
): Promise {
if (!itemsGroup.current) return;
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
@@ -65,7 +66,7 @@ async function loadInitialFloorItems(
const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z);
- if (cameraPosition.distanceTo(itemPosition) < 50) {
+ if (cameraPosition.distanceTo(itemPosition) < renderDistance) {
await new Promise(async (resolve) => {
// Check Three.js Cache
@@ -210,7 +211,6 @@ function processLoadedModel(
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Vehicle Action",
actionType: "travel",
- material: null,
unLoadDuration: 5,
loadCapacity: 10,
pickUpPoint: null,
@@ -243,9 +243,9 @@ function processLoadedModel(
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
- actionName: `Action ${index}`,
+ actionName: `Action ${index + 1}`,
actionType: 'default',
- material: 'inherit',
+ material: 'Default material',
delay: 0,
spawnInterval: 5,
spawnCount: 1,
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 670c7b5..d7c278c 100644
--- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts
+++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts
@@ -202,7 +202,7 @@ async function handleModelLoad(
actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action ${index}`,
actionType: 'default',
- material: 'inherit',
+ material: 'Default Material',
delay: 0,
spawnInterval: 5,
spawnCount: 1,
@@ -226,9 +226,8 @@ async function handleModelLoad(
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
- actionName: "Vehicle Action",
+ actionName: "Action 1",
actionType: "travel",
- material: null,
unLoadDuration: 5,
loadCapacity: 10,
pickUpPoint: null,
@@ -254,11 +253,11 @@ async function handleModelLoad(
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
- actionName: "Pick and Place",
+ actionName: "Action 1",
actionType: "pickAndPlace",
process: {
- startPoint: [0, 0, 0],
- endPoint: [0, 0, 0]
+ startPoint: null,
+ endPoint: null
},
triggers: []
}
@@ -280,10 +279,10 @@ async function handleModelLoad(
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
- actionName: "Process Action",
+ actionName: "Action 1",
actionType: "process",
processTime: 10,
- swapMaterial: "material-id",
+ swapMaterial: "Default Material",
triggers: []
}
}
diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx
index 241f628..f3f5050 100644
--- a/app/src/modules/builder/groups/floorItemsGroup.tsx
+++ b/app/src/modules/builder/groups/floorItemsGroup.tsx
@@ -75,7 +75,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
gltfLoaderWorker.postMessage({ floorItems: data });
} else {
gltfLoaderWorker.postMessage({ floorItems: [] });
- loadInitialFloorItems(itemsGroup, setFloorItems, addEvent);
+ loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, renderDistance);
updateLoadingProgress(100);
}
});
@@ -94,7 +94,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
updateLoadingProgress(progress);
if (loadedAssets === totalAssets) {
- loadInitialFloorItems(itemsGroup, setFloorItems, addEvent);
+ loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, renderDistance);
updateLoadingProgress(100);
}
});
diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx
index 8baacd9..bc2f4db 100644
--- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx
+++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx
@@ -4,15 +4,32 @@ import { useEventsStore } from '../../../../../store/simulation/useEventsStore';
import useModuleStore from '../../../../../store/useModuleStore';
import { TransformControls } from '@react-three/drei';
import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys';
-import { useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore';
+import { useSelectedEventSphere, useSelectedEventData } from '../../../../../store/simulation/useSimulationStore';
function PointsCreator() {
- const { events, updatePoint, getPointByUuid } = useEventsStore();
+ const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore();
const { activeModule } = useModuleStore();
const transformRef = useRef(null);
const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere } = useSelectedEventSphere();
+ const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData();
+
+ useEffect(() => {
+ if (selectedEventSphere) {
+ const eventData = getEventByModelUuid(selectedEventSphere.userData.modelUuid);
+ if (eventData) {
+ setSelectedEventData(
+ eventData,
+ selectedEventSphere.userData.pointUuid
+ );
+ } else {
+ clearSelectedEventData();
+ }
+ } else {
+ clearSelectedEventData();
+ }
+ }, [selectedEventSphere]);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
@@ -49,6 +66,7 @@ function PointsCreator() {
{event.points.map((point, j) => (
(sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
@@ -73,6 +91,7 @@ function PointsCreator() {
return (
(sphereRefs.current[event.point.uuid] = el!)}
onClick={(e) => {
@@ -95,6 +114,7 @@ function PointsCreator() {
return (
(sphereRefs.current[event.point.uuid] = el!)}
onClick={(e) => {
@@ -117,6 +137,7 @@ function PointsCreator() {
return (
(sphereRefs.current[event.point.uuid] = el!)}
onClick={(e) => {
diff --git a/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx b/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx
new file mode 100644
index 0000000..9eececc
--- /dev/null
+++ b/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx
@@ -0,0 +1,124 @@
+import { useThree } from '@react-three/fiber'
+import { useEffect } from 'react'
+import { Object3D } from 'three';
+import { useSubModuleStore } from '../../../../store/useModuleStore';
+import { useLeftData, useTopData } from '../../../../store/visualization/useZone3DWidgetStore';
+import { useEventsStore } from '../../../../store/simulation/useEventsStore';
+import { useProductStore } from '../../../../store/simulation/useProductStore';
+import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore';
+import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore';
+
+function AddOrRemoveEventsInProducts() {
+ const { gl, raycaster, scene } = useThree();
+ const { subModule } = useSubModuleStore();
+ const { setTop } = useTopData();
+ const { setLeft } = useLeftData();
+ const { getIsEventInProduct } = useProductStore();
+ const { getEventByModelUuid } = useEventsStore();
+ const { selectedProduct } = useSelectedProduct();
+ const { selectedAsset, setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
+
+ useEffect(() => {
+
+ const canvasElement = gl.domElement;
+
+ let drag = false;
+ let isRightMouseDown = false;
+
+ const onMouseDown = (evt: MouseEvent) => {
+ if (selectedAsset) {
+ clearSelectedAsset();
+ }
+ if (evt.button === 2) {
+ isRightMouseDown = true;
+ drag = false;
+ }
+ };
+
+ const onMouseUp = (evt: MouseEvent) => {
+ if (evt.button === 2) {
+ isRightMouseDown = false;
+ }
+ }
+
+ const onMouseMove = () => {
+ if (isRightMouseDown) {
+ drag = true;
+ }
+ };
+
+ const handleRightClick = (evt: MouseEvent) => {
+ if (drag) return;
+ evt.preventDefault();
+ const canvasElement = gl.domElement;
+ if (!canvasElement) return;
+
+ let intersects = raycaster.intersectObjects(scene.children, true);
+ if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
+ let currentObject = intersects[0].object;
+
+ while (currentObject) {
+ if (currentObject.name === "Scene") {
+ break;
+ }
+ currentObject = currentObject.parent as Object3D;
+ }
+ if (currentObject) {
+ const isInProduct = getIsEventInProduct(selectedProduct.productId, currentObject.uuid);
+
+ if (isInProduct) {
+ const event = getEventByModelUuid(currentObject.uuid);
+ if (event) {
+ setSelectedAsset(event)
+ const canvasRect = canvasElement.getBoundingClientRect();
+ const relativeX = evt.clientX - canvasRect.left;
+ const relativeY = evt.clientY - canvasRect.top;
+
+ setTop(relativeY);
+ setLeft(relativeX);
+ } else {
+ clearSelectedAsset()
+ }
+ } else {
+ const event = getEventByModelUuid(currentObject.uuid);
+ if (event) {
+ setSelectedAsset(event)
+ const canvasRect = canvasElement.getBoundingClientRect();
+ const relativeX = evt.clientX - canvasRect.left;
+ const relativeY = evt.clientY - canvasRect.top;
+
+ setTop(relativeY);
+ setLeft(relativeX);
+ } else {
+ clearSelectedAsset()
+ }
+ }
+
+ }
+ } else {
+ clearSelectedAsset()
+ }
+
+ };
+
+ if (subModule === 'simulations') {
+ canvasElement.addEventListener("mousedown", onMouseDown);
+ canvasElement.addEventListener("mouseup", onMouseUp);
+ canvasElement.addEventListener("mousemove", onMouseMove);
+ canvasElement.addEventListener('contextmenu', handleRightClick);
+ }
+
+ return () => {
+ canvasElement.removeEventListener("mousedown", onMouseDown);
+ canvasElement.removeEventListener("mouseup", onMouseUp);
+ canvasElement.removeEventListener("mousemove", onMouseMove);
+ canvasElement.removeEventListener('contextmenu', handleRightClick);
+ };
+
+ }, [gl, subModule, selectedProduct, selectedAsset]);
+ return (
+ <>>
+ )
+}
+
+export default AddOrRemoveEventsInProducts
\ No newline at end of file
diff --git a/app/src/modules/simulation/products/products.tsx b/app/src/modules/simulation/products/products.tsx
index 2e9a16e..38175e2 100644
--- a/app/src/modules/simulation/products/products.tsx
+++ b/app/src/modules/simulation/products/products.tsx
@@ -1,18 +1,41 @@
-import React, { useEffect } from 'react'
-import { useProductStore } from '../../../store/simulation/useProductStore'
import * as THREE from 'three';
+import { useEffect } from 'react';
+import { useProductStore } from '../../../store/simulation/useProductStore';
+import { useSelectedProduct } from '../../../store/simulation/useSimulationStore';
+import AddOrRemoveEventsInProducts from './events/addOrRemoveEventsInProducts';
+import { upsertProductOrEventApi } from '../../../services/simulation/UpsertProductOrEventApi';
+import { getAllProductsApi } from '../../../services/simulation/getallProductsApi';
function Products() {
const { products, addProduct } = useProductStore();
+ const { setSelectedProduct } = useSelectedProduct();
useEffect(() => {
if (products.length === 0) {
- addProduct('Product 1', THREE.MathUtils.generateUUID());
+ const id = THREE.MathUtils.generateUUID();
+ const name = 'Product 1';
+ addProduct(name, id);
+ // upsertProductOrEventApi({ productName: name, productId: id }).then((data) => {
+ // console.log('data: ', data);
+ // });
+ setSelectedProduct(id, name);
}
}, [products])
+ useEffect(() => {
+ // const email = localStorage.getItem('email')
+ // const organization = (email!.split("@")[1]).split(".")[0];
+ // console.log(organization);
+ // getAllProductsApi(organization).then((data) => {
+ // console.log('data: ', data);
+ // })
+ }, [])
+
return (
<>
+
+
+
>
)
}
diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx
index 0cd4fe2..fd7590e 100644
--- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx
@@ -1,6 +1,64 @@
-import React from 'react'
+import React, { useEffect, useMemo, useRef, useState } from 'react'
+import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
+import { useFrame, useThree } from '@react-three/fiber';
+import * as THREE from "three"
+import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
+
+function RoboticArmAnimator({ armUuid, HandleCallback, currentPhase, ikSolver, targetBone, robot, logStatus, groupRef, processes, armBotCurveRef, path }: any) {
+ const { armBots } = useArmBotStore();
+ const { scene } = useThree();
+ const restSpeed = 0.1;
+ const restPosition = new THREE.Vector3(0, 2, 1.6);
+ const initialCurveRef = useRef(null);
+ const initialStartPositionRef = useRef(null);
+ const [initialProgress, setInitialProgress] = useState(0);
+ const [progress, setProgress] = useState(0);
+ const [needsInitialMovement, setNeedsInitialMovement] = useState(true);
+ const [isInitializing, setIsInitializing] = useState(true);
+ const { isPlaying } = usePlayButtonStore();
+ const statusRef = useRef("idle");
+ // Create a ref for initialProgress
+ const initialProgressRef = useRef(0);
+ const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
+
+ useEffect(() => {
+ setCurrentPath(path)
+ }, [path])
+
+ useEffect(() => {
+
+ }, [currentPath])
+
+ useFrame((_, delta) => {
+ if (!ikSolver || !currentPath || currentPath.length === 0) return;
+
+ const bone = ikSolver.mesh.skeleton.bones.find(
+ (b: any) => b.name === targetBone
+ );
+ if (!bone) return;
+
+ // Ensure currentPath is a valid array of 3D points, create a CatmullRomCurve3 from it
+ const curve = new THREE.CatmullRomCurve3(
+ currentPath.map(point => new THREE.Vector3(point[0], point[1], point[2]))
+ );
+
+ const next = initialProgressRef.current + delta * 0.5;
+ if (next >= 1) {
+ bone.position.copy(restPosition);
+ HandleCallback(); // Call the callback when the path is completed
+ initialProgressRef.current = 0; // Set ref to 1 when done
+ } else {
+
+ const point = curve.getPoint(next); // Get the interpolated point from the curve
+ bone.position.copy(point); // Update the bone position along the curve
+ initialProgressRef.current = next; // Update progress
+ }
+
+ ikSolver.update();
+ });
+
+
-function RoboticArmAnimator() {
return (
<>>
)
diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
index 2817906..86eee6a 100644
--- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
@@ -1,14 +1,179 @@
-import React from 'react'
+import React, { useEffect, useRef, useState } from 'react'
import IKInstance from '../ikInstance/ikInstance';
import RoboticArmAnimator from '../animator/roboticArmAnimator';
+import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
+import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
+import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_4.glb";
+import { useThree } from "@react-three/fiber";
+import { useFloorItems } from '../../../../../store/store';
+import useModuleStore from '../../../../../store/useModuleStore';
+import { Vector3 } from "three";
+import * as THREE from "three";
+
+interface Process {
+ triggerId: string;
+ startPoint?: Vector3;
+ endPoint?: Vector3;
+ speed: number;
+}
+function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
+
+ const { isPlaying } = usePlayButtonStore();
+ const [currentPhase, setCurrentPhase] = useState<(string)>("init");
+ const { scene } = useThree();
+ const targetBone = "Target";
+ const { activeModule } = useModuleStore();
+ const [ikSolver, setIkSolver] = useState(null);
+ const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
+ const { floorItems } = useFloorItems();
+ const groupRef = useRef(null);
+ const [processes, setProcesses] = useState([]);
+ const [armBotCurvePoints, setArmBotCurvePoints] = useState({ start: [], end: [] })
+ const restPosition = new THREE.Vector3(0, 2, 1.6);
+ let armBotCurveRef = useRef(null)
+ const [path, setPath] = useState<[number, number, number][]>([]);
+
+ useEffect(() => {
+ let armItems = floorItems?.filter((val: any) =>
+ val.modeluuid === "3abf5d46-b59e-4e6b-9c02-a4634b64b82d"
+ );
+ // Get the first matching item
+ let armItem = armItems?.[0];
+ if (armItem) {
+ const targetMesh = scene?.getObjectByProperty("uuid", armItem.modeluuid);
+ if (targetMesh) {
+ targetMesh.visible = activeModule !== "simulation"
+ }
+ }
+ const targetBones = ikSolver?.mesh.skeleton.bones.find(
+ (b: any) => b.name === targetBone
+ );
+
+ if (isPlaying) {
+ //Moving armBot from initial point to rest position.
+ if (!robot?.isActive && robot?.state == "idle" && currentPhase == "init") {
+
+ setArmBotActive(robot.modelUuid, true)
+ setArmBotState(robot.modelUuid, "running")
+ setCurrentPhase("init-to-rest");
+ if (targetBones) {
+ let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition)
+ if (curve) {
+ setPath(curve.points.map(point => [point.x, point.y, point.z]));
+ }
+ }
+ logStatus(robot.modelUuid, "Starting from init to rest")
+ }
+ //Waiting for trigger.
+ else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "rest" && !robot.currentAction) {
+ console.log("trigger");
+ setTimeout(() => {
+ addCurrentAction(robot.modelUuid, 'action-003');
+ }, 3000);
+ }
+ else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "rest" && robot.currentAction) {
+ if (robot.currentAction) {
+ setArmBotActive(robot.modelUuid, true);
+ setArmBotState(robot.modelUuid, "running");
+ setCurrentPhase("rest-to-start");
+ const startPoint = robot.point.actions[0].process.startPoint;
+ if (startPoint) {
+ let curve = createCurveBetweenTwoPoints(restPosition, new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]));
+ if (curve) {
+ setPath(curve.points.map(point => [point.x, point.y, point.z]));
+ }
+ }
+ }
+ logStatus(robot.modelUuid, "Starting from rest to start")
+ }
+ else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "picking" && robot.currentAction) {
+ setArmBotActive(robot.modelUuid, true);
+ setArmBotState(robot.modelUuid, "running");
+ setCurrentPhase("start-to-end");
+ const startPoint = robot.point.actions[0].process.startPoint;
+ const endPoint = robot.point.actions[0].process.endPoint;
+ if (startPoint && endPoint) {
+ let curve = createCurveBetweenTwoPoints(
+ new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]),
+ new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2])
+ );
+ if (curve) {
+ setPath(curve.points.map(point => [point.x, point.y, point.z]));
+ }
+ }
+ logStatus(robot.modelUuid, "Starting from start to end")
+ }
+ else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "dropping" && robot.currentAction) {
+ setArmBotActive(robot.modelUuid, true);
+ setArmBotState(robot.modelUuid, "running");
+ setCurrentPhase("end-to-rest");
+ const endPoint = robot.point.actions[0].process.endPoint;
+ if (endPoint) {
+ let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition
+ );
+ if (curve) {
+ setPath(curve.points.map(point => [point.x, point.y, point.z]));
+ }
+ }
+ logStatus(robot.modelUuid, "Starting from end to rest")
+ }
+ }
+
+ }, [currentPhase, robot, isPlaying, ikSolver])
+
+
+ function createCurveBetweenTwoPoints(p1: any, p2: any) {
+ const mid = new THREE.Vector3().addVectors(p1, p2).multiplyScalar(0.5);
+ mid.y += 0.5;
+
+ const points = [p1, mid, p2];
+ return new THREE.CatmullRomCurve3(points);
+ }
+
+
+ const HandleCallback = () => {
+ if (robot.isActive && robot.state == "running" && currentPhase == "init-to-rest") {
+ console.log("Callback triggered: rest");
+ setArmBotActive(robot.modelUuid, false)
+ setArmBotState(robot.modelUuid, "idle")
+ setCurrentPhase("rest");
+ setPath([])
+ }
+ else if (robot.isActive && robot.state == "running" && currentPhase == "rest-to-start") {
+ console.log("Callback triggered: pick.");
+ setArmBotActive(robot.modelUuid, false)
+ setArmBotState(robot.modelUuid, "idle")
+ setCurrentPhase("picking");
+ setPath([])
+ }
+ else if (robot.isActive && robot.state == "running" && currentPhase == "start-to-end") {
+ console.log("Callback triggered: drop.");
+ setArmBotActive(robot.modelUuid, false)
+ setArmBotState(robot.modelUuid, "idle")
+ setCurrentPhase("dropping");
+ setPath([])
+ }
+ else if (robot.isActive && robot.state == "running" && currentPhase == "end-to-rest") {
+ console.log("Callback triggered: rest, cycle completed.");
+ setArmBotActive(robot.modelUuid, false)
+ setArmBotState(robot.modelUuid, "idle")
+ setCurrentPhase("rest");
+ setPath([])
+ removeCurrentAction(robot.modelUuid)
+ }
+ }
+ const logStatus = (id: string, status: string) => {
+ console.log(id +","+ status);
+ }
-function RoboticArmInstance() {
return (
<>
-
-
-
+
+
>
)
diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx
index 52a8610..a8d8782 100644
--- a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx
@@ -1,8 +1,87 @@
-import React from 'react'
+import React, { useEffect, useMemo, useRef, useState } from 'react'
+import * as THREE from "three";
+import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
+import { clone } from "three/examples/jsm/utils/SkeletonUtils";
+import { useFrame, useLoader, useThree } from "@react-three/fiber";
+import { CCDIKSolver, CCDIKHelper, } from "three/examples/jsm/animation/CCDIKSolver";
+type IKInstanceProps = {
+ modelUrl: string;
+ ikSolver: any;
+ setIkSolver: any
+ robot: any;
+ groupRef: React.RefObject;
+ processes: any;
+ setArmBotCurvePoints: any
+};
+function IKInstance({ modelUrl, setIkSolver, ikSolver, robot, groupRef, processes, setArmBotCurvePoints }: IKInstanceProps) {
+ const { scene } = useThree();
+ const gltf = useLoader(GLTFLoader, modelUrl, (loader) => {
+ const draco = new DRACOLoader();
+ draco.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/");
+ loader.setDRACOLoader(draco);
+ });
+ const cloned = useMemo(() => clone(gltf?.scene), [gltf]);
+ const targetBoneName = "Target";
+ const skinnedMeshName = "link_0";
+ useEffect(() => {
+ if (!gltf) return;
+ const OOI: any = {};
+ cloned.traverse((n: any) => {
+ if (n.name === targetBoneName) OOI.Target_Bone = n;
+ if (n.name === skinnedMeshName) OOI.Skinned_Mesh = n;
+ });
+ if (!OOI.Target_Bone || !OOI.Skinned_Mesh) return;
+ const iks = [
+ {
+ target: 7,
+ effector: 6,
+ links: [
+ {
+ index: 5,
+ enabled: true,
+ rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0),
+ rotationMax: new THREE.Vector3(Math.PI / 2, 0, 0),
+ },
+ {
+ index: 4,
+ enabled: true,
+ rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0),
+ rotationMax: new THREE.Vector3(0, 0, 0),
+ },
+ {
+ index: 3,
+ enabled: true,
+ rotationMin: new THREE.Vector3(0, 0, 0),
+ rotationMax: new THREE.Vector3(2, 0, 0),
+ },
+ { index: 1, enabled: true, limitation: new THREE.Vector3(0, 1, 0) },
+ { index: 0, enabled: false, limitation: new THREE.Vector3(0, 0, 0) },
+ ],
+ },
+ ];
+
+ const solver = new CCDIKSolver(OOI.Skinned_Mesh, iks);
+ setIkSolver(solver);
+
+ const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05);
+
+ // scene.add(groupRef.current)
+
+
+ }, [gltf]);
-function IKInstance() {
return (
- <>>
+ <>
+
+
+
+ >
)
}
diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstances.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstances.tsx
deleted file mode 100644
index d44ddd2..0000000
--- a/app/src/modules/simulation/roboticArm/instances/ikInstances.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react'
-import IKInstance from './ikInstance/ikInstance';
-
-function IkInstances() {
- return (
- <>
-
-
-
- >
- )
-}
-
-export default IkInstances;
\ No newline at end of file
diff --git a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx
index 6e8a70a..1089fa5 100644
--- a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx
@@ -1,11 +1,15 @@
import React from 'react'
import RoboticArmInstance from './armInstance/roboticArmInstance';
+import { useArmBotStore } from '../../../../store/simulation/useArmBotStore';
function RoboticArmInstances() {
+ const { armBots } = useArmBotStore();
+
return (
<>
-
-
+ {armBots?.map((robot: ArmBotStatus) => (
+
+ ))}
>
)
diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx
index 02a1690..dcf01da 100644
--- a/app/src/modules/simulation/roboticArm/roboticArm.tsx
+++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx
@@ -1,14 +1,166 @@
-import React from 'react'
-import RoboticArmInstances from './instances/roboticArmInstances';
+import { useEffect } from "react";
+import RoboticArmInstances from "./instances/roboticArmInstances";
+import { useArmBotStore } from "../../../store/simulation/useArmBotStore";
+import { useFloorItems } from "../../../store/store";
function RoboticArm() {
+ const { armBots, addArmBot, removeArmBot } = useArmBotStore();
+ const { floorItems } = useFloorItems();
+
+ const armBotStatusSample: RoboticArmEventSchema[] = [
+ {
+ state: "idle",
+ modelUuid: "armbot-xyz-001",
+ modelName: "ArmBot-X200",
+ position: [91.94347308985614, 0, 6.742905194869091],
+ rotation: [0, 0, 0],
+ type: "roboticArm",
+ speed: 1.5,
+ point: {
+ uuid: "point-123",
+ position: [0, 1.5, 0],
+ rotation: [0, 0, 0],
+ actions: [
+ {
+ actionUuid: "action-003",
+ actionName: "Pick Component",
+ actionType: "pickAndPlace",
+ process: {
+ startPoint: [5.52543010919071, 1, -8.433681161200905],
+ endPoint: [10.52543010919071, 1, -12.433681161200905],
+ },
+ triggers: [
+ {
+ triggerUuid: "trigger-001",
+ triggerName: "Start Trigger",
+ triggerType: "onStart",
+ delay: 0,
+ triggeredAsset: {
+ triggeredModel: {
+ modelName: "Conveyor A1",
+ modelUuid: "conveyor-01",
+ },
+ triggeredPoint: {
+ pointName: "Start Point",
+ pointUuid: "conveyor-01-point-001",
+ },
+ triggeredAction: {
+ actionName: "Move Forward",
+ actionUuid: "conveyor-action-01",
+ },
+ },
+ },
+ {
+ triggerUuid: "trigger-002",
+ triggerName: "Complete Trigger",
+ triggerType: "onComplete",
+ delay: 0,
+ triggeredAsset: {
+ triggeredModel: {
+ modelName: "StaticMachine B2",
+ modelUuid: "machine-02",
+ },
+ triggeredPoint: {
+ pointName: "Receive Point",
+ pointUuid: "machine-02-point-001",
+ },
+ triggeredAction: {
+ actionName: "Process Part",
+ actionUuid: "machine-action-01",
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ },
+ {
+ state: "idle",
+ modelUuid: "armbot-xyz-002",
+ modelName: "ArmBot-X200",
+ position: [95.94347308985614, 0, 6.742905194869091],
+ rotation: [0, 0, 0],
+ type: "roboticArm",
+ speed: 1.5,
+ point: {
+ uuid: "point-123",
+ position: [0, 1.5, 0],
+ rotation: [0, 0, 0],
+ actions: [
+ {
+ actionUuid: "action-001",
+ actionName: "Pick Component",
+ actionType: "pickAndPlace",
+ process: {
+ startPoint: [2.52543010919071, 0, 8.433681161200905],
+ endPoint: [95.3438373267953, 0, 9.0279187421610025],
+ },
+ triggers: [
+ {
+ triggerUuid: "trigger-001",
+ triggerName: "Start Trigger",
+ triggerType: "onStart",
+ delay: 0,
+ triggeredAsset: {
+ triggeredModel: {
+ modelName: "Conveyor A1",
+ modelUuid: "conveyor-01",
+ },
+ triggeredPoint: {
+ pointName: "Start Point",
+ pointUuid: "conveyor-01-point-001",
+ },
+ triggeredAction: {
+ actionName: "Move Forward",
+ actionUuid: "conveyor-action-01",
+ },
+ },
+ },
+ {
+ triggerUuid: "trigger-002",
+ triggerName: "Complete Trigger",
+ triggerType: "onComplete",
+ delay: 0,
+ triggeredAsset: {
+ triggeredModel: {
+ modelName: "StaticMachine B2",
+ modelUuid: "machine-02",
+ },
+ triggeredPoint: {
+ pointName: "Receive Point",
+ pointUuid: "machine-02-point-001",
+ },
+ triggeredAction: {
+ actionName: "Process Part",
+ actionUuid: "machine-action-01",
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ },
+ ];
+
+ useEffect(() => {
+
+ removeArmBot(armBotStatusSample[0].modelUuid);
+ addArmBot('123', armBotStatusSample[0]);
+ // addArmBot('123', armBotStatusSample[1]);
+ // addCurrentAction('armbot-xyz-001', 'action-001');
+ }, []);
+
+ useEffect(() => {
+ //
+ }, [armBots]);
+
return (
<>
-
-
>
- )
+ );
}
-export default RoboticArm;
\ No newline at end of file
+export default RoboticArm;
diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx
index f02b066..5ca0ec5 100644
--- a/app/src/modules/simulation/simulation.tsx
+++ b/app/src/modules/simulation/simulation.tsx
@@ -10,13 +10,16 @@ import Machine from './machine/machine';
import StorageUnit from './storageUnit/storageUnit';
import Simulator from './simulator/simulator';
import Products from './products/products';
+import Trigger from './triggers/trigger';
+import useModuleStore from '../../store/useModuleStore';
function Simulation() {
+ const { activeModule } = useModuleStore();
const { events } = useEventsStore();
const { products } = useProductStore();
useEffect(() => {
- console.log('events: ', events);
+ // console.log('events: ', events);
}, [events])
useEffect(() => {
@@ -26,26 +29,36 @@ function Simulation() {
return (
<>
-
+ {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
index 8cc22d6..c4f1c40 100644
--- a/app/src/modules/simulation/simulator/simulator.tsx
+++ b/app/src/modules/simulation/simulator/simulator.tsx
@@ -1,9 +1,16 @@
-import React from 'react'
+import { useEffect } from 'react'
+import { useProductStore } from '../../../store/simulation/useProductStore'
function Simulator() {
+ const { products } = useProductStore();
+
+ useEffect(() => {
+ // console.log('products: ', products);
+ }, [products])
+
return (
<>
-
+
>
)
}
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..f836ea4
--- /dev/null
+++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx
@@ -0,0 +1,103 @@
+import { useEffect } from "react";
+import { useThree } from "@react-three/fiber";
+import { useSubModuleStore } from "../../../../store/useModuleStore";
+import { useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
+import { useProductStore } from "../../../../store/simulation/useProductStore";
+import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
+
+function TriggerConnector() {
+ const { gl, raycaster, scene } = useThree();
+ const { subModule } = useSubModuleStore();
+ const { getPointByUuid, getIsEventInProduct } = useProductStore();
+ const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
+ const { selectedProduct } = useSelectedProduct();
+
+ useEffect(() => {
+
+ const canvasElement = gl.domElement;
+
+ let drag = false;
+ let isRightMouseDown = false;
+
+ const onMouseDown = (evt: MouseEvent) => {
+ if (selectedAsset) {
+ clearSelectedAsset();
+ }
+ if (evt.button === 2) {
+ isRightMouseDown = true;
+ drag = false;
+ }
+ };
+
+ const onMouseUp = (evt: MouseEvent) => {
+ if (evt.button === 2) {
+ isRightMouseDown = false;
+ }
+ }
+
+ const onMouseMove = () => {
+ if (isRightMouseDown) {
+ drag = true;
+ }
+ };
+
+ const handleRightClick = (evt: MouseEvent) => {
+ if (drag) return;
+ evt.preventDefault();
+ const canvasElement = gl.domElement;
+ if (!canvasElement) return;
+
+ let intersects = raycaster.intersectObjects(scene.children, true);
+ if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
+ let currentObject = intersects[0].object;
+
+ if (currentObject && currentObject.name === 'Event-Sphere') {
+
+ const isInProduct = getIsEventInProduct(
+ selectedProduct.productId,
+ currentObject.userData.modelUuid
+ );
+
+ // You left Here
+
+ if (isInProduct) {
+
+ const event = getPointByUuid(
+ selectedProduct.productId,
+ currentObject.userData.modelUuid,
+ currentObject.userData.pointUuid
+ );
+ console.log('event: ', event);
+ } else {
+
+ }
+ }
+
+ } else {
+
+ }
+ };
+
+ if (subModule === 'simulations') {
+ canvasElement.addEventListener("mousedown", onMouseDown);
+ canvasElement.addEventListener("mouseup", onMouseUp);
+ canvasElement.addEventListener("mousemove", onMouseMove);
+ canvasElement.addEventListener('contextmenu', handleRightClick);
+ }
+
+ return () => {
+ canvasElement.removeEventListener("mousedown", onMouseDown);
+ canvasElement.removeEventListener("mouseup", onMouseUp);
+ canvasElement.removeEventListener("mousemove", onMouseMove);
+ canvasElement.removeEventListener('contextmenu', handleRightClick);
+ };
+
+ }, [gl, subModule]);
+
+ return (
+ <>
+ >
+ )
+}
+
+export default TriggerConnector
\ 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 cf5fd81..a0eeabd 100644
--- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx
+++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx
@@ -1,62 +1,172 @@
-import { useEffect, useState } from 'react'
+import { useEffect, useRef, useState } from 'react'
import { useFrame, useThree } from '@react-three/fiber';
+import { useFloorItems } from '../../../../../store/store';
+import * as THREE from 'three';
+import { Line } from '@react-three/drei';
+import { useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
+import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
interface VehicleAnimatorProps {
- path: [number, number, number][];
- handleCallBack: () => void;
- currentPhase: string;
- agvUuid: number
+ path: [number, number, number][];
+ handleCallBack: () => void;
+ currentPhase: string;
+ agvUuid: number;
+ agvDetail: any;
}
+function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail }: VehicleAnimatorProps) {
+ const { decrementVehicleLoad, vehicles } = useVehicleStore();
+ const { isPaused } = usePauseButtonStore();
+ const { speed } = useAnimationPlaySpeed();
+ const { isReset } = useResetButtonStore();
+ const [restRotation, setRestingRotation] = useState(true);
+ const [progress, setProgress] = useState(0);
+ const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
+ const { scene } = useThree();
+ const progressRef = useRef(0);
+ const movingForward = useRef(true);
+ const completedRef = useRef(false);
+ let startTime: number;
+ let pausedTime: number;
+ let fixedInterval: number;
-function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid }: VehicleAnimatorProps) {
- const [progress, setProgress] = useState(0)
- const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
- const { scene } = useThree();
+ useEffect(() => {
+ if (currentPhase === 'stationed-pickup' && path.length > 0) {
+ setCurrentPath(path);
+ } else if (currentPhase === 'pickup-drop' && path.length > 0) {
+ setCurrentPath(path);
+ } else if (currentPhase === 'drop-pickup' && path.length > 0) {
+ setCurrentPath(path);
+ }
+ }, [currentPhase, path]);
- useEffect(() => {
+ useEffect(() => {
+ setProgress(0);
+ completedRef.current = false;
+ }, [currentPath]);
- if (currentPhase === 'stationed-pickup' && path.length > 0) {
- setCurrentPath(path);
+ useFrame((_, delta) => {
+ const object = scene.getObjectByProperty('uuid', agvUuid);
+ if (!object || currentPath.length < 2) return;
+ if (isPaused) return;
+
+ let totalDistance = 0;
+ const distances = [];
+
+ for (let i = 0; i < currentPath.length - 1; i++) {
+ const start = new THREE.Vector3(...currentPath[i]);
+ const end = new THREE.Vector3(...currentPath[i + 1]);
+ const segmentDistance = start.distanceTo(end);
+ distances.push(segmentDistance);
+ totalDistance += segmentDistance;
+ }
+
+ let coveredDistance = progressRef.current;
+ let accumulatedDistance = 0;
+ let index = 0;
+
+ while (
+ index < distances.length &&
+ coveredDistance > accumulatedDistance + distances[index]
+ ) {
+ accumulatedDistance += distances[index];
+ index++;
+ }
+
+ if (index < distances.length) {
+ const start = new THREE.Vector3(...currentPath[index]);
+ const end = new THREE.Vector3(...currentPath[index + 1]);
+ const segmentDistance = distances[index];
+
+ const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
+ const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);
+ const rotationSpeed = 2.0;
+ const currentAngle = object.rotation.y;
+
+ let angleDifference = targetAngle - currentAngle;
+ if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
+ if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI;
+
+ const maxRotationStep = rotationSpeed * delta;
+ object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep);
+
+ const isAligned = Math.abs(angleDifference) < 0.01;
+
+ if (isAligned) {
+ progressRef.current += delta * (speed * agvDetail.speed);
+ coveredDistance = progressRef.current;
+
+ const t = (coveredDistance - accumulatedDistance) / segmentDistance;
+ const position = start.clone().lerp(end, t);
+ object.position.copy(position);
+ }
+ }
+
+ if (progressRef.current >= totalDistance) {
+ if (restRotation) {
+ const targetQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0));
+ object.quaternion.slerp(targetQuaternion, delta * 2);
+ const angleDiff = object.quaternion.angleTo(targetQuaternion);
+ if (angleDiff < 0.01) {
+ let objectRotation = agvDetail.point.rotation
+ object.rotation.set(objectRotation[0], objectRotation[1], objectRotation[2]);
+ setRestingRotation(false);
}
+ return;
+ }
+ }
- }, [currentPhase, path])
+ if (progressRef.current >= totalDistance) {
+ setRestingRotation(true);
+ progressRef.current = 0;
+ movingForward.current = !movingForward.current;
+ setCurrentPath([]);
+ handleCallBack();
+ if (currentPhase === 'pickup-drop') {
+ requestAnimationFrame(firstFrame);
+ }
+ }
+ });
- useFrame((_, delta) => {
- if (!path || path.length < 2) return;
+ function firstFrame() {
+ const unLoadDuration = agvDetail.point.action.unLoadDuration;
+ const droppedMaterial = agvDetail.currentLoad;
+ fixedInterval = (unLoadDuration / droppedMaterial) * 1000;
+ startTime = performance.now();
+ step(droppedMaterial);
+ }
- const object = scene.getObjectByProperty("uuid", agvUuid)
- if (!object) return;
+ function step(droppedMaterial: number) {
+ const elapsedTime = performance.now() - startTime;
- setProgress(prev => {
- const next = prev + delta * 0.1; // speed
- return next >= 1 ? 1 : next;
- });
+ if (elapsedTime >= fixedInterval) {
+ console.log('fixedInterval: ', fixedInterval);
+ console.log('elapsedTime: ', elapsedTime);
+ let droppedMat = droppedMaterial - 1;
+ decrementVehicleLoad(agvDetail.modelUuid, 1);
+ if (droppedMat === 0) return;
+ startTime = performance.now();
+ requestAnimationFrame(() => step(droppedMat));
+ } else {
+ requestAnimationFrame(() => step(droppedMaterial));
+ }
+ }
- const totalSegments = path.length - 1;
- const segmentIndex = Math.floor(progress * totalSegments);
- const t = progress * totalSegments - segmentIndex;
-
- const start = path[segmentIndex];
- const end = path[segmentIndex + 1] || start;
-
- // Directly set position without creating a new Vector3
- object.position.x = start[0] + (end[0] - start[0]) * t;
- object.position.y = start[1] + (end[1] - start[1]) * t;
- object.position.z = start[2] + (end[2] - start[2]) * t;
- });
- // useFrame(() => {
- // if (currentPath.length === 0) return;
- // const object = scene.getObjectByProperty("uuid", agvUuid);
- // if (!object) return;
-
-
-
- // })
- return (
+ return (
+ <>
+ {currentPath.length > 0 && (
<>
+
+ {currentPath.map((point, index) => (
+
+
+
+
+ ))}
>
- )
+ )}
+ >
+ );
}
-export default VehicleAnimator
\ No newline at end of file
+export default VehicleAnimator;
\ No newline at end of file
diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx
index bf767ec..d0691f9 100644
--- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx
+++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx
@@ -1,66 +1,123 @@
-import React, { useCallback, useEffect, useState } from 'react'
-import VehicleAnimator from '../animator/vehicleAnimator'
-import * as THREE from "three";
+import React, { useCallback, useEffect, useState } from 'react';
+import VehicleAnimator from '../animator/vehicleAnimator';
+import * as THREE from 'three';
import { NavMeshQuery } from '@recast-navigation/core';
import { useNavMesh } from '../../../../../store/store';
import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
-function VehicleInstance({ agvDetails }: any) {
- const { navMesh } = useNavMesh();
- const { isPlaying } = usePlayButtonStore();
- const { setVehicleActive, setVehicleState } = useVehicleStore();
- const [currentPhase, setCurrentPhase] = useState<(string)>("stationed");
- const [path, setPath] = useState<[number, number, number][]>([]);
+function VehicleInstance({ agvDetail }: any) {
+ const { navMesh } = useNavMesh();
+ const { isPlaying } = usePlayButtonStore();
+ const { vehicles, setVehicleActive, setVehicleState, incrementVehicleLoad } = useVehicleStore();
+ const [currentPhase, setCurrentPhase] = useState('stationed');
+ const [path, setPath] = useState<[number, number, number][]>([]);
- const computePath = useCallback((start: any, end: any) => {
+ const computePath = useCallback(
+ (start: any, end: any) => {
+ try {
+ const navMeshQuery = new NavMeshQuery(navMesh);
+ const { path: segmentPath } = navMeshQuery.computePath(start, end);
+ return (
+ segmentPath?.map(({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]) || []
+ );
+ } catch {
+ return [];
+ }
+ },
+ [navMesh]
+ );
+ function vehicleStatus(modelid: string, status: string) {
+ // console.log(`AGV ${modelid}: ${status}`);
+ }
- try {
- const navMeshQuery = new NavMeshQuery(navMesh);
- const { path: segmentPath } = navMeshQuery.computePath(start, end);
- return (
- segmentPath?.map(
- ({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]
- ) || []
- );
- } catch {
- return [];
- }
- }, [navMesh]);
-
- useEffect(() => {
-
-
- if (isPlaying) {
- if (!agvDetails.isActive && agvDetails.state == "idle" && currentPhase == "stationed") {
- const toPickupPath = computePath(new THREE.Vector3(agvDetails.position[0], agvDetails.position[1], agvDetails.position[2]), agvDetails.point.action.pickUpPoint);
- setPath(toPickupPath)
- setVehicleActive(agvDetails.modelUuid, true)
- setVehicleState(agvDetails.modelUuid, "running")
- setCurrentPhase("stationed-pickup")
- //
- }
- }
- }, [agvDetails, currentPhase, path, isPlaying])
-
- function handleCallBack() {
- if (currentPhase === "stationed-pickup") {
- setVehicleActive(agvDetails.modelUuid, false)
- setVehicleState(agvDetails.modelUuid, "idle")
- setCurrentPhase("picking")
- setPath([])
+ useEffect(() => {
+ if (isPlaying) {
+ if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'stationed') {
+ const toPickupPath = computePath(
+ new THREE.Vector3(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]),
+ agvDetail.point.action.pickUpPoint
+ );
+ setPath(toPickupPath);
+ setVehicleActive(agvDetail.modelUuid, true);
+ setVehicleState(agvDetail.modelUuid, 'running');
+ setCurrentPhase('stationed-pickup');
+ vehicleStatus(agvDetail.modelUuid, 'Started from station, heading to pickup');
+ return;
+ } else if (
+ !agvDetail.isActive &&
+ agvDetail.state === 'idle' &&
+ currentPhase === 'picking'
+ ) {
+
+ setTimeout(() => {
+ incrementVehicleLoad(agvDetail.modelUuid, 2);
+ }, 5000);
+
+ if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) {
+ const toDrop = computePath(
+ agvDetail.point.action.pickUpPoint,
+ agvDetail.point.action.unLoadPoint
+ );
+ setPath(toDrop);
+ setVehicleActive(agvDetail.modelUuid, true);
+ setVehicleState(agvDetail.modelUuid, 'running');
+ setCurrentPhase('pickup-drop');
+ vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point');
}
+ } else if (
+ !agvDetail.isActive &&
+ agvDetail.state === 'idle' &&
+ currentPhase === 'dropping' &&
+ agvDetail.currentLoad === 0
+ ) {
+ const dropToPickup = computePath(
+ agvDetail.point.action.unLoadPoint,
+ agvDetail.point.action.pickUpPoint
+ );
+ setPath(dropToPickup);
+ setVehicleActive(agvDetail.modelUuid, true);
+ setVehicleState(agvDetail.modelUuid, 'running');
+ setCurrentPhase('drop-pickup');
+ vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point');
+ }
}
+ }, [vehicles, currentPhase, path, isPlaying]);
+ function handleCallBack() {
+ if (currentPhase === 'stationed-pickup') {
+ setVehicleActive(agvDetail.modelUuid, false);
+ setVehicleState(agvDetail.modelUuid, 'idle');
+ setCurrentPhase('picking');
+ vehicleStatus(agvDetail.modelUuid, 'Reached pickup point, waiting for material');
+ setPath([]);
+ } else if (currentPhase === 'pickup-drop') {
+ setVehicleActive(agvDetail.modelUuid, false);
+ setVehicleState(agvDetail.modelUuid, 'idle');
+ setCurrentPhase('dropping');
+ vehicleStatus(agvDetail.modelUuid, 'Reached drop point');
+ setPath([]);
+ } else if (currentPhase === 'drop-pickup') {
+ setVehicleActive(agvDetail.modelUuid, false);
+ setVehicleState(agvDetail.modelUuid, 'idle');
+ setCurrentPhase('picking');
+ setPath([]);
+ vehicleStatus(agvDetail.modelUuid, 'Reached pickup point again, cycle complete');
+ }
+ }
- return (
- <>
-
-
-
- >
- )
+ return (
+ <>
+
+ >
+ );
}
-export default VehicleInstance
\ No newline at end of file
+export default VehicleInstance;
\ No newline at end of file
diff --git a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx
index 0848883..2a0070b 100644
--- a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx
+++ b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx
@@ -1,15 +1,14 @@
import React from 'react'
import VehicleInstance from './instance/vehicleInstance'
-import { useVehicleStore } from '../../../../store/simulation/useVehicleStore';
+import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'
function VehicleInstances() {
const { vehicles } = useVehicleStore();
-
return (
<>
{vehicles.map((val: any, i: any) =>
-
+
)}
>
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 3364717..f0b4d17 100644
--- a/app/src/modules/simulation/vehicle/vehicles.tsx
+++ b/app/src/modules/simulation/vehicle/vehicles.tsx
@@ -1,17 +1,19 @@
import React, { useEffect } from 'react'
import VehicleInstances from './instances/vehicleInstances';
-
import { useVehicleStore } from '../../../store/simulation/useVehicleStore';
+import { useFloorItems } from '../../../store/store';
function Vehicles() {
const { vehicles, addVehicle } = useVehicleStore();
+ const { floorItems } = useFloorItems();
+
const vehicleStatusSample: VehicleEventSchema[] = [
{
- modelUuid: "veh-123",
- modelName: "Autonomous Truck A1",
- position: [10, 0, 5],
+ modelUuid: "9356f710-4727-4b50-bdb2-9c1e747ecc74",
+ modelName: "AGV",
+ position: [97.9252965204558, 0, 37.96138815638661],
rotation: [0, 0, 0],
state: "idle",
type: "vehicle",
@@ -24,11 +26,10 @@ function Vehicles() {
actionUuid: "action-456",
actionName: "Deliver to Zone A",
actionType: "travel",
- material: "crate",
- unLoadDuration: 15,
- loadCapacity: 5,
- pickUpPoint: { x: 5, y: 0, z: 3 },
- unLoadPoint: { x: 20, y: 0, z: 10 },
+ unLoadDuration: 10,
+ loadCapacity: 2,
+ pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 },
+ unLoadPoint: { x: 105.71483985219794, y: 0, z: 28.66321267938962 },
triggers: [
{
triggerUuid: "trig-001",
@@ -53,9 +54,51 @@ function Vehicles() {
}
},
{
- modelUuid: "veh-123",
- modelName: "Autonomous Truck A1",
- position: [10, 0, 5],
+ modelUuid: "b06960bb-3d2e-41f7-a646-335f389c68b4",
+ modelName: "AGV",
+ position: [89.61609306554463, 0, 33.634136622267356],
+ rotation: [0, 0, 0],
+ state: "idle",
+ type: "vehicle",
+ speed: 2.5,
+ point: {
+ uuid: "point-789",
+ position: [0, 1, 0],
+ rotation: [0, 0, 0],
+ action: {
+ actionUuid: "action-456",
+ actionName: "Deliver to Zone A",
+ actionType: "travel",
+ unLoadDuration: 10,
+ loadCapacity: 2,
+ pickUpPoint: { x: 90, y: 0, z: 28 },
+ unLoadPoint: { x: 20, y: 0, z: 10 },
+ triggers: [
+ {
+ triggerUuid: "trig-001",
+ triggerName: "Start Travel",
+ triggerType: "onStart",
+ delay: 0,
+ triggeredAsset: {
+ triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
+ triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" },
+ triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" }
+ }
+ },
+ {
+ triggerUuid: "trig-002",
+ triggerName: "Complete Travel",
+ triggerType: "onComplete",
+ delay: 2,
+ triggeredAsset: null
+ }
+ ]
+ }
+ }
+ }, {
+ modelUuid: "e729a4f1-11d2-4778-8d6a-468f1b4f6b79",
+ modelName: "forklift",
+ position: [98.85729337188162, 0, 38.36616546567653],
rotation: [0, 0, 0],
state: "idle",
type: "vehicle",
@@ -68,10 +111,9 @@ function Vehicles() {
actionUuid: "action-456",
actionName: "Deliver to Zone A",
actionType: "travel",
- material: "crate",
unLoadDuration: 15,
loadCapacity: 5,
- pickUpPoint: { x: 5, y: 0, z: 3 },
+ pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 },
unLoadPoint: { x: 20, y: 0, z: 10 },
triggers: [
{
@@ -101,19 +143,18 @@ function Vehicles() {
useEffect(() => {
addVehicle('123', vehicleStatusSample[0]);
- addVehicle('123', vehicleStatusSample[1]);
+ // addVehicle('123', vehicleStatusSample[1]);
+ // addVehicle('123', vehicleStatusSample[2]);
}, [])
useEffect(() => {
- // console.log('vehicles: ', vehicles);
+ console.log('vehicles: ', vehicles);
}, [vehicles])
return (
<>
-
-
>
)
}
diff --git a/app/src/modules/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx
index 44c6717..05f7371 100644
--- a/app/src/modules/visualization/RealTimeVisulization.tsx
+++ b/app/src/modules/visualization/RealTimeVisulization.tsx
@@ -69,7 +69,7 @@ const RealTimeVisulization: React.FC = () => {
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { setRightSelect } = useRightSelected();
- const { editWidgetOptions } = useEditWidgetOptionsStore();
+ const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore();
const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
const { setFloatingWidget } = useFloatingWidget();
@@ -354,6 +354,10 @@ const RealTimeVisulization: React.FC = () => {
"RotateY",
"Delete",
]}
+ onClick={(e) => {
+ setRightSelect(e);
+ setEditWidgetOptions(false);
+ }}
/>
)}
diff --git a/app/src/services/simulation/UpsertProductOrEventApi.ts b/app/src/services/simulation/UpsertProductOrEventApi.ts
new file mode 100644
index 0000000..e2f45d1
--- /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 upsertProductOrEventApi = 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 d907f21..642762f 100644
--- a/app/src/store/simulation/useArmBotStore.ts
+++ b/app/src/store/simulation/useArmBotStore.ts
@@ -21,7 +21,7 @@ interface ArmBotStore {
updateEndPoint: (modelUuid: string, actionUuid: string, endPoint: [number, number, number] | null) => void;
setArmBotActive: (modelUuid: string, isActive: boolean) => void;
-
+ setArmBotState: (modelUuid: string, newState: ArmBotStatus['state']) => void;
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
@@ -75,7 +75,6 @@ export const useArmBotStore = create()(
actionUuid: action.actionUuid,
actionName: action.actionName,
};
- armBot.isActive = true;
}
}
});
@@ -86,7 +85,6 @@ export const useArmBotStore = create()(
const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
if (armBot) {
armBot.currentAction = undefined;
- armBot.isActive = false;
}
});
},
@@ -142,6 +140,15 @@ export const useArmBotStore = create()(
});
},
+ setArmBotState: (modelUuid, newState) => {
+ set((state) => {
+ const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
+ if (armBot) {
+ armBot.state = newState;
+ }
+ });
+ },
+
incrementActiveTime: (modelUuid, incrementBy) => {
set((state) => {
const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts
index 81feb7f..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;
@@ -54,7 +54,12 @@ type ProductsStore = {
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()(
@@ -67,7 +72,7 @@ export const useProductStore = create()(
const newProduct = {
productName,
productId: productId,
- eventsData: []
+ eventDatas: []
};
state.products.push(newProduct);
});
@@ -93,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);
}
});
},
@@ -102,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);
}
});
},
@@ -111,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);
}
@@ -124,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) {
@@ -138,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) {
@@ -152,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) {
@@ -170,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) {
@@ -190,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) {
@@ -214,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) {
@@ -244,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) {
@@ -273,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) {
@@ -300,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) {
@@ -349,7 +354,7 @@ export const useProductStore = create()(
renameAction: (actionUuid, newName) => {
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) {
@@ -378,7 +383,7 @@ export const useProductStore = create()(
renameTrigger: (triggerUuid, newName) => {
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) {
@@ -417,6 +422,89 @@ export const useProductStore = create()(
// 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
index 9c0fc00..5085688 100644
--- a/app/src/store/simulation/useSimulationStore.ts
+++ b/app/src/store/simulation/useSimulationStore.ts
@@ -24,6 +24,50 @@ export const useSelectedEventSphere = create()(
}))
);
+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;
@@ -46,4 +90,28 @@ export const useSelectedProduct = create()(
});
},
}))
+);
+
+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/useVehicleStore.ts b/app/src/store/simulation/useVehicleStore.ts
index ce28916..449ceb7 100644
--- a/app/src/store/simulation/useVehicleStore.ts
+++ b/app/src/store/simulation/useVehicleStore.ts
@@ -1,3 +1,4 @@
+
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
@@ -88,7 +89,7 @@ export const useVehicleStore = create()(
set((state) => {
const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid);
if (vehicle) {
- vehicle.currentLoad = decrementBy;
+ 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/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts
index 1b99456..3293699 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";
@@ -42,7 +42,6 @@ interface VehiclePointSchema {
actionUuid: string;
actionName: string;
actionType: "travel";
- material: string | null;
unLoadDuration: number;
loadCapacity: number;
pickUpPoint: { x: number; y: number, z: number } | null;
@@ -119,12 +118,14 @@ interface StorageEventSchema extends AssetEventSchema {
point: StoragePointSchema;
}
+type PointsScheme = ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema;
+
type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema;
type productsSchema = {
productName: string;
productId: string;
- eventsData: EventsSchema[];
+ eventDatas: EventsSchema[];
}[]
@@ -133,6 +134,7 @@ interface ConveyorStatus extends ConveyorEventSchema {
isActive: boolean;
idleTime: number;
activeTime: number;
+
}
interface MachineStatus extends MachineEventSchema {