Refactor EventProperties component to utilize new state management for selected event data and asset selection; implement action handling based on asset type and improve action rendering logic.

Enhance Simulations component to support adding and removing events from products; integrate new asset selection store for better state management.

Fix import paths in Design component and related files to ensure correct module resolution.

Update Tools component to correct import paths for template saving functionality.

Refactor EditWidgetOption component to simplify option handling and remove unnecessary state management.

Add new mechanics components for various asset types (Conveyor, Machine, Robotic Arm, Storage, Vehicle) as placeholders for future implementation.

Implement Trigger and TriggerConnector components to manage right-click interactions and asset selection in the simulation environment.

Enhance product store with new helper functions for event and action retrieval based on UUIDs.

Introduce new selected event data and asset state management in the simulation store for improved event handling.

Update simulation types to include new action types and improve type definitions for better type safety.

Remove obsolete temp markdown file from triggers directory.
This commit is contained in:
Jerald-Golden-B 2025-04-24 16:38:42 +05:30
parent 85515c6cd3
commit a305c3c006
26 changed files with 813 additions and 383 deletions

View File

@ -5,8 +5,8 @@ import Header from "./Header";
import useToggleStore from "../../../store/useUIToggleStore"; import useToggleStore from "../../../store/useUIToggleStore";
import Assets from "./Assets"; import Assets from "./Assets";
import useModuleStore from "../../../store/useModuleStore"; import useModuleStore from "../../../store/useModuleStore";
import Widgets from ".//visualization/widgets/Widgets"; import Widgets from "./visualization/widgets/Widgets";
import Templates from "../../../modules//visualization/template/Templates"; import Templates from "../../../modules/visualization/template/Templates";
import Search from "../../ui/inputs/Search"; import Search from "../../ui/inputs/Search";
const SideBarLeft: React.FC = () => { const SideBarLeft: React.FC = () => {

View File

@ -5,11 +5,11 @@ import {
GlobeIcon, GlobeIcon,
WalletIcon, WalletIcon,
} from "../../../../icons/3dChartIcons"; } 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 WarehouseThroughput from "../../../../../modules/visualization/widgets/floating/cards/WarehouseThroughput";
import ProductivityDashboard from "../../../../../modules//visualization/widgets/floating/cards/ProductivityDashboard"; import ProductivityDashboard from "../../../../../modules/visualization/widgets/floating/cards/ProductivityDashboard";
import FleetEfficiency from "../../../../../modules//visualization/widgets/floating/cards/FleetEfficiency"; import FleetEfficiency from "../../../../../modules/visualization/widgets/floating/cards/FleetEfficiency";
interface Widget { interface Widget {
id: string; id: string;

View File

@ -1,173 +1,148 @@
import React, { useEffect, useState } from "react"; import React, { useEffect } from "react";
import Header from "./Header"; import Header from "./Header";
import useModuleStore, { import useModuleStore, {
useSubModuleStore, useSubModuleStore,
} from "../../../store/useModuleStore"; } from "../../../store/useModuleStore";
import { import {
AnalysisIcon, AnalysisIcon,
MechanicsIcon, MechanicsIcon,
PropertiesIcon, PropertiesIcon,
SimulationIcon, SimulationIcon,
} from "../../icons/SimulationIcons"; } from "../../icons/SimulationIcons";
import useToggleStore from "../../../store/useUIToggleStore"; import useToggleStore from "../../../store/useUIToggleStore";
import Visualization from "./visualization/Visualization"; import Visualization from "./visualization/Visualization";
import Analysis from "./analysis/Analysis"; import Analysis from "./analysis/Analysis";
import Simulations from "./simulation/Simulations"; import Simulations from "./simulation/Simulations";
import { useSelectedFloorItem } from "../../../store/store"; import { useSelectedFloorItem } from "../../../store/store";
import { useSelectedEventData, useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
import GlobalProperties from "./properties/GlobalProperties"; import GlobalProperties from "./properties/GlobalProperties";
import AsstePropertiies from "./properties/AssetProperties"; import AsstePropertiies from "./properties/AssetProperties";
import ZoneProperties from "./properties/ZoneProperties"; import ZoneProperties from "./properties/ZoneProperties";
import EventProperties from "./properties/eventProperties/EventProperties"; import EventProperties from "./properties/eventProperties/EventProperties";
const SideBarRight: React.FC = () => { const SideBarRight: React.FC = () => {
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { toggleUI } = useToggleStore(); const { toggleUI } = useToggleStore();
const { subModule, setSubModule } = useSubModuleStore(); const { subModule, setSubModule } = useSubModuleStore();
const { selectedFloorItem } = useSelectedFloorItem(); const { selectedFloorItem } = useSelectedFloorItem();
const { selectedEventData } = useSelectedEventData();
const { selectedEventSphere } = useSelectedEventSphere();
// Reset activeList whenever activeModule changes // Reset activeList whenever activeModule changes
useEffect(() => { useEffect(() => {
if (activeModule !== "simulation") setSubModule("properties"); if (activeModule !== "simulation") setSubModule("properties");
if (activeModule === "simulation") setSubModule("mechanics"); if (activeModule === "simulation") setSubModule("simulations");
}, [activeModule]); }, [activeModule]);
// romove late useEffect(() => {
const dummyData = { if (activeModule !== "mechanics" && selectedEventData && selectedEventSphere) {
assetType: "store", setSubModule("mechanics");
selectedPoint: { } else {
name: "Point A", if (activeModule === 'simulation') {
uuid: "123e4567-e89b-12d3-a456-426614174000", setSubModule("simulations");
actions: [ }
{ };
uuid: "action-1", }, [activeModule, selectedEventData, selectedEventSphere])
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",
};
return ( return (
<div className="sidebar-right-wrapper"> <div className="sidebar-right-wrapper">
<Header /> <Header />
{toggleUI && ( {toggleUI && (
<div className="sidebar-actions-container"> <div className="sidebar-actions-container">
{/* {activeModule === "builder" && ( */} <div
<div className={`sidebar-action-list ${subModule === "properties" ? "active" : ""
className={`sidebar-action-list ${subModule === "properties" ? "active" : "" }`}
}`} onClick={() => setSubModule("properties")}
onClick={() => setSubModule("properties")} >
> <PropertiesIcon isActive={subModule === "properties"} />
<PropertiesIcon isActive={subModule === "properties"} /> </div>
</div> {activeModule === "simulation" && (
{/* )} */} <>
{activeModule === "simulation" && ( <div
<> className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""
<div }`}
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : "" onClick={() => setSubModule("mechanics")}
}`} >
onClick={() => setSubModule("mechanics")} <MechanicsIcon isActive={subModule === "mechanics"} />
> </div>
<MechanicsIcon isActive={subModule === "mechanics"} /> <div
</div> className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""
<div }`}
className={`sidebar-action-list ${subModule === "simulations" ? "active" : "" onClick={() => setSubModule("simulations")}
}`} >
onClick={() => setSubModule("simulations")} <SimulationIcon isActive={subModule === "simulations"} />
> </div>
<SimulationIcon isActive={subModule === "simulations"} /> <div
</div> className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""
<div }`}
className={`sidebar-action-list ${subModule === "analysis" ? "active" : "" onClick={() => setSubModule("analysis")}
}`} >
onClick={() => setSubModule("analysis")} <AnalysisIcon isActive={subModule === "analysis"} />
> </div>
<AnalysisIcon isActive={subModule === "analysis"} /> </>
</div> )}
</> </div>
)} )}
{/* process builder */}
{toggleUI &&
subModule === "properties" &&
activeModule !== "visualization" &&
!selectedFloorItem && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<GlobalProperties />
</div>
</div>
)}
{toggleUI &&
subModule === "properties" &&
activeModule !== "visualization" &&
selectedFloorItem && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<AsstePropertiies />
</div>
</div>
)}
{toggleUI &&
subModule === "zoneProperties" &&
(activeModule === "builder" || activeModule === "simulation") && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<ZoneProperties />
</div>
</div>
)}
{/* simulation */}
{toggleUI && activeModule === "simulation" && (
<>
{subModule === "simulations" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Simulations />
</div>
</div>
)}
{subModule === "mechanics" && selectedEventData && selectedEventSphere && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<EventProperties />
</div>
</div>
)}
{subModule === "analysis" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Analysis />
</div>
</div>
)}
</>
)}
{/* realtime visualization */}
{toggleUI && activeModule === "visualization" && <Visualization />}
</div> </div>
)} );
{/* process builder */}
{toggleUI &&
subModule === "properties" &&
activeModule !== "visualization" &&
!selectedFloorItem && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<GlobalProperties />
</div>
</div>
)}
{toggleUI &&
subModule === "properties" &&
activeModule !== "visualization" &&
selectedFloorItem && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<AsstePropertiies />
</div>
</div>
)}
{toggleUI &&
subModule === "zoneProperties" &&
(activeModule === "builder" || activeModule === "simulation") && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<ZoneProperties />
</div>
</div>
)}
{/* simulation */}
{toggleUI && activeModule === "simulation" && (
<>
{subModule === "mechanics" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<EventProperties {...dummyData} />
</div>
</div>
)}
{subModule === "analysis" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Analysis />
</div>
</div>
)}
{subModule === "simulations" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Simulations />
</div>
</div>
)}
</>
)}
{/* realtime visualization */}
{toggleUI && activeModule === "visualization" && <Visualization />}
</div>
);
}; };
export default SideBarRight; export default SideBarRight;

View File

@ -1,10 +1,10 @@
import React, { useRef, useState } from "react"; import React, { useEffect, useRef, useState } from "react";
import InputWithDropDown from "../../../../ui/inputs/InputWithDropDown"; import InputWithDropDown from "../../../../ui/inputs/InputWithDropDown";
import LabledDropdown from "../../../../ui/inputs/LabledDropdown"; import LabledDropdown from "../../../../ui/inputs/LabledDropdown";
import { import {
AddIcon, AddIcon,
RemoveIcon, RemoveIcon,
ResizeHeightIcon, ResizeHeightIcon,
} from "../../../../icons/ExportCommonIcons"; } from "../../../../icons/ExportCommonIcons";
import RenameInput from "../../../../ui/inputs/RenameInput"; import RenameInput from "../../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../../functions/handleResizePannel"; import { handleResize } from "../../../../../functions/handleResizePannel";
@ -20,191 +20,260 @@ import ProcessAction from "./actions/ProcessAction";
import StorageAction from "./actions/StorageAction"; import StorageAction from "./actions/StorageAction";
import Trigger from "./trigger/Trigger"; import Trigger from "./trigger/Trigger";
interface EventPropertiesProps { import { useSelectedEventData, useSelectedProduct } from "../../../../../store/simulation/useSimulationStore";
assetType: string; import { useProductStore } from "../../../../../store/simulation/useProductStore";
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<EventPropertiesProps> = ({ const EventProperties: React.FC = () => {
assetType, const actionsContainerRef = useRef<HTMLDivElement>(null);
selectedPoint,
selectedItem,
setSelectedPoint,
selectedActionSphere,
}) => {
const actionsContainerRef = useRef<HTMLDivElement>(null);
const [activeOption, setActiveOption] = useState("default"); const [activeOption, setActiveOption] = useState("default");
const [dummyactiveOption, setTypeOption] = useState("default"); const [selectedItem, setSelectedItem] = useState<{ item: { uuid: string; name: string } | null; }>({ item: null });
const { selectedEventData } = useSelectedEventData();
const { getEventByModelUuid } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const getAvailableActions = () => { useEffect(() => {
if (assetType === "conveyor") { const actions = getActions();
return { if (actions.length > 0 && !selectedItem.item) {
defaultOption: "default", setSelectedItem({ item: actions[0] });
options: ["default", "spawn", "swap", "despawn"], }
}; }, [selectedEventData]);
}
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"],
};
}
};
return ( const getCurrentEventData = () => {
<div className="event-proprties-wrapper"> if (!selectedEventData?.data || !selectedProduct) return null;
<div className="header"> const event = getEventByModelUuid(selectedProduct.productId, selectedEventData.data.modelUuid);
<div className="header-value">{selectedPoint.name}</div> return event || null;
</div> };
<div className="global-props">
<div className="property-list-container"> const getAssetType = () => {
{/* <div className="property-item"> const event = getCurrentEventData();
<LabledDropdown if (!event) return null;
defaultOption={assetType}
options={[]} switch (event.type) {
onSelect={(option) => setTypeOption(option)} case 'transfer': return 'conveyor';
/> case 'vehicle': return 'vehicle';
</div> */} case 'roboticArm': return 'roboticArm';
<div className="property-item"> case 'machine': return 'machine';
<InputWithDropDown case 'storageUnit': return 'storageUnit';
label="Speed" default: return null;
value="0.5" }
min={0} };
step={0.1}
defaultValue="0.5" const getAvailableActions = () => {
max={10} switch (getAssetType()) {
activeOption="s" case "conveyor":
onClick={() => {}} return {
onChange={(value) => console.log(value)} defaultOption: "default",
/> options: ["default", "spawn", "swap", "despawn"],
</div> };
<div className="property-item"> case "vehicle":
<InputWithDropDown return {
label="Delay" defaultOption: "travel",
value="0.5" options: ["travel"],
min={0} };
step={0.1} case "roboticArm":
defaultValue="0.5" return {
max={10} defaultOption: "pickAndPlace",
activeOption="s" options: ["pickAndPlace"],
onClick={() => {}} };
onChange={(value) => console.log(value)} case "machine":
/> return {
</div> defaultOption: "process",
</div> options: ["process"],
</div> };
<div className="actions-list-container"> case "storageUnit":
<div className="actions"> return {
<div className="header"> defaultOption: "store",
<div className="header-value">Actions</div> options: ["store", "spawn"],
<div className="add-button" onClick={() => {}}> };
<AddIcon /> Add default:
</div> return {
</div> defaultOption: "default",
<div options: ["default"],
className="lists-main-container" };
ref={actionsContainerRef} }
style={{ height: "120px" }} };
>
<div className="list-container"> // Get actions based on asset type
{selectedPoint?.actions.map((action) => ( const getActions = () => {
<div if (!selectedEventData?.data) return [];
key={action.uuid}
className={`list-item ${ const event = selectedEventData.data;
selectedItem.item?.uuid === action.uuid ? "active" : "" switch (getAssetType()) {
}`} case "conveyor":
> return (event as ConveyorEventSchema).points
<div .find((point) => point.uuid === selectedEventData?.selectedPoint)
className="value" ?.action?.triggers.map((trigger) => ({
onClick={() => handleActionToggle(action.uuid)} uuid: trigger.triggerUuid,
> name: trigger.triggerName,
<RenameInput value={action.name} /> })) || [];
</div> case "vehicle":
{selectedPoint?.actions.length > 1 && ( return (event as VehicleEventSchema).point.action.triggers.map(
<div (trigger) => ({
className="remove-button" uuid: trigger.triggerUuid,
onClick={() => handleDeleteAction(action.uuid)} name: trigger.triggerName,
> })
<RemoveIcon /> ) || [];
case "roboticArm":
return (event as RoboticArmEventSchema).point.actions.flatMap(
(action) =>
action.triggers.map((trigger) => ({
uuid: trigger.triggerUuid,
name: trigger.triggerName,
}))
) || [];
case "machine":
return (event as MachineEventSchema).point.action.triggers.map(
(trigger) => ({
uuid: trigger.triggerUuid,
name: trigger.triggerName,
})
) || [];
case "storageUnit":
return [
{
uuid: (event as StorageEventSchema).point.action.actionUuid,
name: (event as StorageEventSchema).point.action.actionName,
},
];
default:
return [];
}
};
const handleActionToggle = (actionUuid: string) => {
const actions = getActions();
const selected = actions.find(action => action.uuid === actionUuid);
if (selected) {
setSelectedItem({ item: selected });
}
};
const handleDeleteAction = (actionUuid: string) => {
};
const actions = getActions();
const getSpeed = () => {
if (!selectedEventData) return "0.5";
if ("speed" in selectedEventData.data) return selectedEventData.data.speed.toString();
return "0.5";
};
return (
<>
{getCurrentEventData() &&
<div className="event-proprties-wrapper">
<div className="header">
<div className="header-value">{selectedEventData?.data.modelName}</div>
</div>
<div className="global-props">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value="0.5"
min={0}
step={0.1}
defaultValue={getSpeed()}
max={10}
activeOption="s"
onClick={() => { }}
onChange={(value) => console.log(value)}
/>
</div>
<div className="property-item">
<InputWithDropDown
label="Delay"
value="0.5"
min={0}
step={0.1}
defaultValue="0.5"
max={10}
activeOption="s"
onClick={() => { }}
onChange={(value) => console.log(value)}
/>
</div>
</div>
</div>
{getAssetType() === 'roboticArm' &&
<div className="actions-list-container">
<div className="actions">
<div className="header">
<div className="header-value">Actions</div>
<div className="add-button" onClick={() => { }}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={actionsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{actions.map((action) => (
<div
key={action.uuid}
className={`list-item ${selectedItem.item?.uuid === action.uuid ? "active" : ""}`}
>
<div
className="value"
onClick={() => handleActionToggle(action.uuid)}
>
<RenameInput value={action.name} />
</div>
{actions.length > 1 && (
<div
className="remove-button"
onClick={() => handleDeleteAction(action.uuid)}
>
<RemoveIcon />
</div>
)}
</div>
))}
</div>
<div
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, actionsContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
</div>
}
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput value="Action Name" />
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={getAvailableActions().defaultOption}
options={getAvailableActions().options}
onSelect={(option) => setActiveOption(option)}
/>
{activeOption === "default" && <DefaultAction />}
{activeOption === "spawn" && <SpawnAction />}
{activeOption === "swap" && <SwapAction />}
{activeOption === "despawn" && <DespawnAction />}
{activeOption === "travel" && <TravelAction />}
{activeOption === "pickAndPlace" && <PickAndPlaceAction />}
{activeOption === "process" && <ProcessAction />}
{activeOption === "store" && <StorageAction />}
</div>
</div>
<div className="tirgger">
<Trigger />
</div> </div>
)}
</div> </div>
))} }
</div> </>
<div );
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, actionsContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
</div>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput value="Action Name" />
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={getAvailableActions().defaultOption}
options={getAvailableActions().options}
onSelect={(option) => setActiveOption(option)}
/>
{activeOption === "default" && <DefaultAction />} {/* done */}
{activeOption === "spawn" && <SpawnAction />} {/* done */}
{activeOption === "swap" && <SwapAction />} {/* done */}
{activeOption === "despawn" && <DespawnAction />} {/* done */}
{activeOption === "travel" && <TravelAction />} {/* done */}
{activeOption === "pickAndPlace" && <PickAndPlaceAction />} {/* done */}
{activeOption === "process" && <ProcessAction />} {/* done */}
{activeOption === "store" && <StorageAction />} {/* done */}
</div>
</div>
<div className="tirgger">
<Trigger />
</div>
</div>
);
}; };
export default EventProperties; export default EventProperties;

View File

@ -0,0 +1,10 @@
import React from 'react'
function ConveyorMechanics() {
return (
<>
</>
)
}
export default ConveyorMechanics

View File

@ -0,0 +1,10 @@
import React from 'react'
function MachineMechanics() {
return (
<>
</>
)
}
export default MachineMechanics

View File

@ -0,0 +1,10 @@
import React from 'react'
function RoboticArmMechanics() {
return (
<>
</>
)
}
export default RoboticArmMechanics

View File

@ -0,0 +1,10 @@
import React from 'react'
function StorageMechanics() {
return (
<>
</>
)
}
export default StorageMechanics

View File

@ -0,0 +1,10 @@
import React from 'react'
function VehicleMechanics() {
return (
<>
</>
)
}
export default VehicleMechanics

View File

@ -7,9 +7,11 @@ import {
} from "../../../icons/ExportCommonIcons"; } from "../../../icons/ExportCommonIcons";
import RenameInput from "../../../ui/inputs/RenameInput"; import RenameInput from "../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../functions/handleResizePannel"; 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 { useProductStore } from "../../../../store/simulation/useProductStore";
import { generateUUID } from "three/src/math/MathUtils"; import { generateUUID } from "three/src/math/MathUtils";
import RenderOverlay from "../../../templates/Overlay";
import EditWidgetOption from "../../../ui/menu/EditWidgetOption";
interface Event { interface Event {
pathName: string; pathName: string;
@ -31,14 +33,9 @@ const List: React.FC<ListProps> = ({ val }) => {
const Simulations: React.FC = () => { const Simulations: React.FC = () => {
const productsContainerRef = useRef<HTMLDivElement>(null); const productsContainerRef = useRef<HTMLDivElement>(null);
const { products, addProduct, removeProduct, renameProduct } = useProductStore(); const { products, addProduct, removeProduct, renameProduct, addEvent, removeEvent } = useProductStore();
const { selectedProduct, setSelectedProduct } = useSelectedProduct(); const { selectedProduct, setSelectedProduct } = useSelectedProduct();
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
useEffect(() => {
if (products.length > 0 && selectedProduct.productId === '' && selectedProduct.productName === '') {
setSelectedProduct(products[0].productId, products[0].productName);
}
}, [products, selectedProduct]);
const handleAddProduct = () => { const handleAddProduct = () => {
addProduct(`Product ${products.length + 1}`, generateUUID()); addProduct(`Product ${products.length + 1}`, generateUUID());
@ -75,12 +72,26 @@ const Simulations: React.FC = () => {
} }
}; };
const handleAddEventToProduct = () => {
if (selectedAsset) {
addEvent(selectedProduct.productId, selectedAsset);
clearSelectedAsset();
}
};
const handleRemoveEventFromProduct = () => {
if (selectedAsset) {
removeEvent(selectedProduct.productId, selectedAsset.modelUuid);
clearSelectedAsset();
}
};
const selectedProductData = products.find( const selectedProductData = products.find(
(product) => product.productId === selectedProduct.productId (product) => product.productId === selectedProduct.productId
); );
const events: Event[] = selectedProductData?.eventsData.map((event, index) => ({ const events: Event[] = selectedProductData?.eventsData.map((event) => ({
pathName: `${event.modelName} - ${event.type} #${index + 1}`, pathName: event.modelName,
})) || []; })) || [];
return ( return (
@ -165,6 +176,21 @@ const Simulations: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
{selectedAsset &&
<RenderOverlay>
<EditWidgetOption
options={['Add to Product', 'Remove from Product']}
onClick={(option) => {
if (option === 'Add to Product') {
handleAddEventToProduct();
} else {
handleRemoveEventFromProduct();
}
}}
/>
</RenderOverlay>
}
</div> </div>
); );
}; };

View File

@ -1,9 +1,9 @@
import { useState, useEffect, useRef } from "react"; import { useState, useEffect, useRef } from "react";
import { useWidgetStore } from "../../../../../store/useWidgetStore"; 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 RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import { WalletIcon } from "../../../../icons/3dChartIcons"; 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 { interface Widget {
id: string; id: string;

View File

@ -15,7 +15,7 @@ import {
} from "../icons/ExportToolsIcons"; } from "../icons/ExportToolsIcons";
import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons"; import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons";
import useModuleStore, { useThreeDStore } from "../../store/useModuleStore"; 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 { usePlayButtonStore } from "../../store/usePlayButtonStore";
import useTemplateStore from "../../store/useTemplateStore"; import useTemplateStore from "../../store/useTemplateStore";
import { useSelectedZoneStore } from "../../store/visualization/useZoneStore"; import { useSelectedZoneStore } from "../../store/visualization/useZoneStore";

View File

@ -1,23 +1,20 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { import {
useEditWidgetOptionsStore,
useLeftData, useLeftData,
useRightClickSelected,
useRightSelected,
useTopData, useTopData,
} from "../../../store/visualization/useZone3DWidgetStore"; } from "../../../store/visualization/useZone3DWidgetStore";
interface EditWidgetOptionProps { interface EditWidgetOptionProps {
options: string[]; options: string[];
onClick: (option: string) => void;
} }
const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({ const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({
options, options,
onClick
}) => { }) => {
const { top } = useTopData(); const { top } = useTopData();
const { left } = useLeftData(); const { left } = useLeftData();
const { setRightSelect } = useRightSelected();
const { setEditWidgetOptions } = useEditWidgetOptionsStore();
useEffect(() => { useEffect(() => {
@ -38,10 +35,7 @@ const EditWidgetOption: React.FC<EditWidgetOptionProps> = ({
<div <div
className="option" className="option"
key={index} key={index}
onClick={(e) => { onClick={() => onClick(option)}
setRightSelect(option);
setEditWidgetOptions(false);
}}
> >
{option} {option}
</div> </div>

View File

@ -51,7 +51,7 @@ import Ground from "../scene/environment/ground";
// import ZoneGroup from "../groups/zoneGroup1"; // import ZoneGroup from "../groups/zoneGroup1";
import { findEnvironment } from "../../services/factoryBuilder/environment/findEnvironment"; import { findEnvironment } from "../../services/factoryBuilder/environment/findEnvironment";
import Layer2DVisibility from "./geomentries/layers/layer2DVisibility"; import Layer2DVisibility from "./geomentries/layers/layer2DVisibility";
import DrieHtmlTemp from "..//visualization/mqttTemp/drieHtmlTemp"; import DrieHtmlTemp from "../visualization/mqttTemp/drieHtmlTemp";
import ZoneGroup from "./groups/zoneGroup"; import ZoneGroup from "./groups/zoneGroup";
import useModuleStore from "../../store/useModuleStore"; import useModuleStore from "../../store/useModuleStore";
import MeasurementTool from "../scene/tools/measurementTool"; import MeasurementTool from "../scene/tools/measurementTool";

View File

@ -4,15 +4,32 @@ import { useEventsStore } from '../../../../../store/simulation/useEventsStore';
import useModuleStore from '../../../../../store/useModuleStore'; import useModuleStore from '../../../../../store/useModuleStore';
import { TransformControls } from '@react-three/drei'; import { TransformControls } from '@react-three/drei';
import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys'; import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys';
import { useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore'; import { useSelectedEventSphere, useSelectedEventData } from '../../../../../store/simulation/useSimulationStore';
function PointsCreator() { function PointsCreator() {
const { events, updatePoint, getPointByUuid } = useEventsStore(); const { events, updatePoint, getPointByUuid, getEventByModelUuid } = useEventsStore();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const transformRef = useRef<any>(null); const transformRef = useRef<any>(null);
const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null); const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere } = useSelectedEventSphere(); 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(() => { useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {

View File

@ -1,13 +1,18 @@
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import { useProductStore } from '../../../store/simulation/useProductStore' import { useProductStore } from '../../../store/simulation/useProductStore'
import * as THREE from 'three'; import * as THREE from 'three';
import { useSelectedProduct } from '../../../store/simulation/useSimulationStore';
function Products() { function Products() {
const { products, addProduct } = useProductStore(); const { products, addProduct } = useProductStore();
const { setSelectedProduct } = useSelectedProduct();
useEffect(() => { useEffect(() => {
if (products.length === 0) { if (products.length === 0) {
addProduct('Product 1', THREE.MathUtils.generateUUID()); const id = THREE.MathUtils.generateUUID();
const name = 'Product 1';
addProduct(name, id);
setSelectedProduct(id, name);
} }
}, [products]) }, [products])

View File

@ -10,39 +10,52 @@ import Machine from './machine/machine';
import StorageUnit from './storageUnit/storageUnit'; import StorageUnit from './storageUnit/storageUnit';
import Simulator from './simulator/simulator'; import Simulator from './simulator/simulator';
import Products from './products/products'; import Products from './products/products';
import Trigger from './triggers/trigger';
import useModuleStore from '../../store/useModuleStore';
function Simulation() { function Simulation() {
const { activeModule } = useModuleStore();
const { events } = useEventsStore(); const { events } = useEventsStore();
const { products } = useProductStore(); const { products } = useProductStore();
useEffect(() => { useEffect(() => {
console.log('events: ', events); // console.log('events: ', events);
}, [events]) }, [events])
useEffect(() => { useEffect(() => {
// console.log('products: ', products); console.log('products: ', products);
}, [products]) }, [products])
return ( return (
<> <>
<Points /> {activeModule === 'simulation' &&
<Products /> <>
<Materials /> <Points />
<Conveyor /> <Products />
<Vehicles /> <Materials />
<RoboticArm /> <Trigger />
<Machine /> <Conveyor />
<StorageUnit /> <Vehicles />
<Simulator /> <RoboticArm />
<Machine />
<StorageUnit />
<Simulator />
</>
}
</> </>
) )

View File

@ -0,0 +1,126 @@
import { useThree } from '@react-three/fiber'
import React, { useEffect } from 'react'
import { Object3D } from 'three';
import { useSubModuleStore } from '../../../../store/useModuleStore';
import { useLeftData, useTopData } from '../../../../store/visualization/useZone3DWidgetStore';
import { useEventsStore } from '../../../../store/simulation/useEventsStore';
import { useProductStore } from '../../../../store/simulation/useProductStore';
import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore';
import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore';
function TriggerConnector() {
const { gl, raycaster, scene } = useThree();
const { subModule } = useSubModuleStore();
const { setTop } = useTopData();
const { setLeft } = useLeftData();
const { getIsEventInProduct } = useProductStore();
const { getEventByModelUuid } = useEventsStore();
const { selectedProduct } = useSelectedProduct();
const { selectedAsset, setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
useEffect(() => {
const canvasElement = gl.domElement;
let drag = false;
let isRightMouseDown = false;
const onMouseDown = (evt: MouseEvent) => {
if (selectedAsset) {
clearSelectedAsset();
}
if (evt.button === 2) {
isRightMouseDown = true;
drag = false;
}
};
const onMouseUp = (evt: MouseEvent) => {
if (evt.button === 2) {
isRightMouseDown = false;
}
}
const onMouseMove = () => {
if (isRightMouseDown) {
drag = true;
}
};
const handleRightClick = (evt: MouseEvent) => {
if (drag) return;
evt.preventDefault();
const canvasElement = gl.domElement;
if (!canvasElement) return;
let intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
let currentObject = intersects[0].object;
while (currentObject) {
if (currentObject.name === "Scene") {
break;
}
currentObject = currentObject.parent as Object3D;
}
if (currentObject) {
const isInProduct = getIsEventInProduct(selectedProduct.productId, currentObject.uuid);
if (isInProduct) {
const event = getEventByModelUuid(currentObject.uuid);
if (event) {
setSelectedAsset(event)
const canvasRect = canvasElement.getBoundingClientRect();
const relativeX = evt.clientX - canvasRect.left;
const relativeY = evt.clientY - canvasRect.top;
setTop(relativeY);
setLeft(relativeX);
} else {
clearSelectedAsset()
}
} else {
const event = getEventByModelUuid(currentObject.uuid);
if (event) {
setSelectedAsset(event)
const canvasRect = canvasElement.getBoundingClientRect();
const relativeX = evt.clientX - canvasRect.left;
const relativeY = evt.clientY - canvasRect.top;
setTop(relativeY);
setLeft(relativeX);
} else {
clearSelectedAsset()
}
}
}
} else {
clearSelectedAsset()
}
};
if (subModule === 'simulations') {
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener('contextmenu', handleRightClick);
}
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener('contextmenu', handleRightClick);
};
}, [gl, subModule, selectedProduct, selectedAsset]);
return (
<>
</>
)
}
export default TriggerConnector

View File

@ -0,0 +1,14 @@
import React from 'react'
import TriggerConnector from './connector/triggerConnector'
function Trigger() {
return (
<>
<TriggerConnector />
</>
)
}
export default Trigger

View File

@ -125,7 +125,7 @@ const RealTimeVisulization: React.FC = () => {
{} {}
); );
setZonesData(formattedData); setZonesData(formattedData);
} catch (error) {} } catch (error) { }
} }
GetZoneData(); GetZoneData();
@ -362,6 +362,10 @@ const RealTimeVisulization: React.FC = () => {
"RotateY", "RotateY",
"Delete", "Delete",
]} ]}
onClick={(e) => {
setRightSelect(e);
setEditWidgetOptions(false);
}}
/> />
)} )}

View File

@ -7,7 +7,7 @@ export const getSelect2dZoneData = async (
) => { ) => {
try { try {
const response = await fetch( 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", method: "GET",
headers: { headers: {

View File

@ -55,6 +55,11 @@ type ProductsStore = {
// Helper functions // Helper functions
getProductById: (productId: string) => { productName: string; productId: string; eventsData: EventsSchema[] } | undefined; getProductById: (productId: string) => { productName: string; productId: string; eventsData: 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<ProductsStore>()( export const useProductStore = create<ProductsStore>()(
@ -417,6 +422,89 @@ export const useProductStore = create<ProductsStore>()(
// Helper functions // Helper functions
getProductById: (productId) => { getProductById: (productId) => {
return get().products.find(p => p.productId === productId); return get().products.find(p => p.productId === productId);
},
getEventByModelUuid: (productId, modelUuid) => {
const product = get().getProductById(productId);
if (!product) return undefined;
return product.eventsData.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.eventsData) {
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.eventsData) {
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.eventsData.some(e => 'modelUuid' in e && e.modelUuid === modelUuid);
} }
})) }))
); );

View File

@ -24,6 +24,50 @@ export const useSelectedEventSphere = create<SelectedEventSphereState>()(
})) }))
); );
interface SelectedEventDataState {
selectedEventData: { data: EventsSchema; selectedPoint: string } | undefined;
setSelectedEventData: (data: EventsSchema, selectedPoint: string) => void;
clearSelectedEventData: () => void;
}
export const useSelectedEventData = create<SelectedEventDataState>()(
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<SelectedAssetState>()(
immer((set) => ({
selectedAsset: undefined,
setSelectedAsset: (EventData) => {
set((state) => {
state.selectedAsset = EventData;
});
},
clearSelectedAsset: () => {
set((state) => {
state.selectedAsset = undefined;
});
},
}))
);
interface SelectedProductState { interface SelectedProductState {
selectedProduct: { productId: string; productName: string }; selectedProduct: { productId: string; productName: string };
setSelectedProduct: (productId: string, productName: string) => void; setSelectedProduct: (productId: string, productName: string) => void;

View File

@ -13,14 +13,17 @@ const useModuleStore = create<ModuleStore>((set) => ({
export default useModuleStore; export default useModuleStore;
// New store for subModule // New store for subModule
type SubModule = 'properties' | 'simulations' | 'mechanics' | 'analysis' | 'zoneProperties';
interface SubModuleStore { interface SubModuleStore {
subModule: string; subModule: SubModule;
setSubModule: (subModule: string) => void; setSubModule: (subModule: SubModule) => void;
} }
const useSubModuleStore = create<SubModuleStore>((set) => ({ const useSubModuleStore = create<SubModuleStore>((set) => ({
subModule: "properties", // Initial subModule state subModule: "properties", // Initial subModule state
setSubModule: (subModule) => set({ subModule }), // Update subModule state setSubModule: (value) => set({ subModule: value }), // Update subModule state
})); }));
export { useSubModuleStore }; export { useSubModuleStore };

View File

@ -25,7 +25,7 @@ interface ConveyorPointSchema {
action: { action: {
actionUuid: string; actionUuid: string;
actionName: string; actionName: string;
actionType: "default" | "spawn" | "swap" | "despawn"; actionType: "default" | "spawn" | "swap" | "delay" | "despawn";
material: string; material: string;
delay: number | "inherit"; delay: number | "inherit";
spawnInterval: number | "inherit"; spawnInterval: number | "inherit";
@ -119,6 +119,8 @@ interface StorageEventSchema extends AssetEventSchema {
point: StoragePointSchema; point: StoragePointSchema;
} }
type PointsScheme = ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema;
type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema; type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema;
type productsSchema = { type productsSchema = {