Merge pull request 'v2-ui' (#69) from v2-ui into main

Reviewed-on: http://185.100.212.76:7776/Dwinzo-Beta/Dwinzo_dev/pulls/69
This commit was merged in pull request #69.
This commit is contained in:
2025-04-28 13:22:10 +00:00
89 changed files with 4416 additions and 1284 deletions

View File

@@ -42,6 +42,7 @@ export function FlipXAxisIcon() {
} }
export function FlipYAxisIcon() { export function FlipYAxisIcon() {
return (
<svg <svg
width="12" width="12"
height="12" height="12"
@@ -79,7 +80,8 @@ export function FlipYAxisIcon() {
strokeWidth="0.75" strokeWidth="0.75"
strokeLinecap="round" strokeLinecap="round"
/> />
</svg>; </svg>
);
} }
export function FlipZAxisIcon() { export function FlipZAxisIcon() {
return ( return (

View File

@@ -168,20 +168,6 @@ export function AddIcon() {
); );
} }
export function RmoveIcon() {
return (
<svg
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M3 6.5H9" stroke="var(--text-color)" strokeLinecap="round" />
</svg>
);
}
export function CloseIcon() { export function CloseIcon() {
return ( return (
<svg <svg

View File

@@ -124,7 +124,6 @@ export function LogoIconLarge() {
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<circle cx="45" cy="45" r="45" fill="#FCFDFD" />
<circle <circle
cx="45.1957" cx="45.1957"
cy="45.1957" cy="45.1957"

View File

@@ -23,16 +23,16 @@ const MarketPlaceBanner = () => {
<path <path
d="M167.189 2C154.638 36.335 104.466 106.204 4.18872 111" d="M167.189 2C154.638 36.335 104.466 106.204 4.18872 111"
stroke="white" stroke="white"
stroke-width="3" strokeWidth="3"
stroke-linecap="round" strokeLinecap="round"
stroke-linejoin="round" strokeLinejoin="round"
/> />
<path <path
d="M10.662 118.326L1.59439 111.524L9.47334 103.374" d="M10.662 118.326L1.59439 111.524L9.47334 103.374"
stroke="white" stroke="white"
stroke-width="3" strokeWidth="3"
stroke-linecap="round" strokeLinecap="round"
stroke-linejoin="round" strokeLinejoin="round"
/> />
</svg> </svg>
</div> </div>

View File

@@ -11,7 +11,7 @@ import {
import { SettingsIcon, TrashIcon } from "../../icons/ExportCommonIcons"; import { SettingsIcon, TrashIcon } from "../../icons/ExportCommonIcons";
const SidePannel: React.FC = () => { const SidePannel: React.FC = () => {
const userName = localStorage.getItem("userName") || "Anonymous"; const userName = localStorage.getItem("userName") ?? "Anonymous";
return ( return (
<div className="side-pannel-container"> <div className="side-pannel-container">
<div className="side-pannel-header"> <div className="side-pannel-header">

View File

@@ -17,12 +17,12 @@ const ConfirmationPopup: React.FC<ConfirmationPopupProps> = ({
<div className="confirmation-modal"> <div className="confirmation-modal">
<p className="message">{message}</p> <p className="message">{message}</p>
<div className="buttton-wrapper"> <div className="buttton-wrapper">
<div className="confirmation-button" onClick={onConfirm}> <button className="confirmation-button" onClick={onConfirm}>
OK OK
</div> </button>
<div className="confirmation-button" onClick={onCancel}> <button className="confirmation-button" onClick={onCancel}>
Cancel Cancel
</div> </button>
</div> </div>
</div> </div>
</div> </div>

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

@@ -1,6 +1,5 @@
import React, { useEffect, useRef, useMemo } from "react"; import React, { useEffect, useRef, useMemo } from "react";
import { Chart } from "chart.js/auto"; import { Chart } from "chart.js/auto";
// import { useThemeStore } from "../../../../../store/useThemeStore";
// Define Props Interface // Define Props Interface
interface ChartComponentProps { interface ChartComponentProps {
@@ -29,7 +28,6 @@ const ChartComponent = ({
data: propsData, data: propsData,
}: ChartComponentProps) => { }: ChartComponentProps) => {
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
// const { themeColor } = useThemeStore();
// Memoize Theme Colors to Prevent Unnecessary Recalculations // Memoize Theme Colors to Prevent Unnecessary Recalculations
// const buttonActionColor = useMemo( // const buttonActionColor = useMemo(
@@ -66,7 +64,7 @@ const ChartComponent = ({
// Memoize Chart Font Style // Memoize Chart Font Style
const chartFontStyle = useMemo( const chartFontStyle = useMemo(
() => ({ () => ({
family: fontFamily || "Arial", family: fontFamily ?? "Arial",
size: fontSizeValue, size: fontSizeValue,
weight: fontWeightValue, weight: fontWeightValue,
color: "#2B3344", color: "#2B3344",

View File

@@ -1,4 +1,3 @@
import { useState } from "react";
import ToggleHeader from "../../../../ui/inputs/ToggleHeader"; import ToggleHeader from "../../../../ui/inputs/ToggleHeader";
import Widgets2D from "./Widgets2D"; import Widgets2D from "./Widgets2D";
import Widgets3D from "./Widgets3D"; import Widgets3D from "./Widgets3D";
@@ -6,7 +5,6 @@ import WidgetsFloating from "./WidgetsFloating";
import { useWidgetSubOption } from "../../../../../store/store"; import { useWidgetSubOption } from "../../../../../store/store";
const Widgets = () => { const Widgets = () => {
const [activeOption, setActiveOption] = useState("2D");
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption(); const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
const handleToggleClick = (option: string) => { const handleToggleClick = (option: string) => {

View File

@@ -5,45 +5,14 @@ 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 FleetEfficiency from "../../../../../modules//visualization/widgets/floating/cards/FleetEfficiency"; import FleetEfficiency from "../../../../../modules//visualization/widgets/floating/cards/FleetEfficiency";
interface Widget {
id: string;
name: string;
}
const WidgetsFloating = () => { const WidgetsFloating = () => {
// const [widgets, setWidgets] = useState<Widget[]>([
// { id: "1", name: "Working State Widget" },
// { id: "2", name: "Floating Widget 2" },
// { id: "3", name: "Floating Widget 3" },
// { id: "4", name: "Floating Widget 4" },
// ]);
// Function to handle drag start
const handleDragStart = (
e: React.DragEvent<HTMLDivElement>,
widget: Widget
) => {
e.dataTransfer.setData("application/json", JSON.stringify(widget));
};
return ( return (
<div className="floatingWidgets-wrapper widgets-wrapper"> <div className="floatingWidgets-wrapper widgets-wrapper">
{/* {widgets.map((widget) => (
<div
key={widget.id}
className="floating"
draggable
onDragStart={(e) => handleDragStart(e, widget)}
>
{widget.name}
</div>
))} */}
{/* Floating 1 */} {/* Floating 1 */}
<SimpleCard <SimpleCard
header={"Todays Earnings"} header={"Todays Earnings"}

View File

@@ -1,4 +1,4 @@
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,
@@ -14,6 +14,10 @@ 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";
@@ -24,88 +28,70 @@ const SideBarRight: React.FC = () => {
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, setSubModule]);
// romove late useEffect(() => {
const dummyData = { if (
assetType: "store", activeModule !== "mechanics" &&
selectedPoint: { selectedEventData &&
name: "Point A", selectedEventSphere
uuid: "123e4567-e89b-12d3-a456-426614174000", ) {
actions: [ setSubModule("mechanics");
{ } else if (!selectedEventData && !selectedEventSphere) {
uuid: "action-1", if (activeModule === "simulation") {
name: "Action One", setSubModule("simulations");
}, }
{ }
uuid: "action-2", }, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
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" && ( */} {activeModule !== "simulation" && (
<div <button
className={`sidebar-action-list ${ className={`sidebar-action-list ${
subModule === "properties" ? "active" : "" subModule === "properties" ? "active" : ""
}`} }`}
onClick={() => setSubModule("properties")} onClick={() => setSubModule("properties")}
> >
<PropertiesIcon isActive={subModule === "properties"} /> <PropertiesIcon isActive={subModule === "properties"} />
</div> </button>
{/* )} */} )}
{activeModule === "simulation" && ( {activeModule === "simulation" && (
<> <>
<div <button
className={`sidebar-action-list ${
subModule === "mechanics" ? "active" : ""
}`}
onClick={() => setSubModule("mechanics")}
>
<MechanicsIcon isActive={subModule === "mechanics"} />
</div>
<div
className={`sidebar-action-list ${ className={`sidebar-action-list ${
subModule === "simulations" ? "active" : "" subModule === "simulations" ? "active" : ""
}`} }`}
onClick={() => setSubModule("simulations")} onClick={() => setSubModule("simulations")}
> >
<SimulationIcon isActive={subModule === "simulations"} /> <SimulationIcon isActive={subModule === "simulations"} />
</div> </button>
<div <button
className={`sidebar-action-list ${
subModule === "mechanics" ? "active" : ""
}`}
onClick={() => setSubModule("mechanics")}
>
<MechanicsIcon isActive={subModule === "mechanics"} />
</button>
<button
className={`sidebar-action-list ${ className={`sidebar-action-list ${
subModule === "analysis" ? "active" : "" subModule === "analysis" ? "active" : ""
}`} }`}
onClick={() => setSubModule("analysis")} onClick={() => setSubModule("analysis")}
> >
<AnalysisIcon isActive={subModule === "analysis"} /> <AnalysisIcon isActive={subModule === "analysis"} />
</div> </button>
</> </>
)} )}
</div> </div>
@@ -141,13 +127,19 @@ const SideBarRight: React.FC = () => {
</div> </div>
)} )}
{/* simulation */} {/* simulation */}
{toggleUI && activeModule === "simulation" && ( {toggleUI && activeModule === "simulation" && (
<> <>
{subModule === "simulations" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Simulations />
</div>
</div>
)}
{subModule === "mechanics" && ( {subModule === "mechanics" && (
<div className="sidebar-right-container"> <div className="sidebar-right-container">
<div className="sidebar-right-content-container"> <div className="sidebar-right-content-container">
<EventProperties {...dummyData} /> <EventProperties />
</div> </div>
</div> </div>
)} )}
@@ -158,16 +150,8 @@ const SideBarRight: React.FC = () => {
</div> </div>
</div> </div>
)} )}
{subModule === "simulations" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Simulations />
</div>
</div>
)}
</> </>
)} )}
{/* realtime visualization */} {/* realtime visualization */}
{toggleUI && activeModule === "visualization" && <Visualization />} {toggleUI && activeModule === "visualization" && <Visualization />}
</div> </div>

View File

@@ -1,208 +1,114 @@
import React, { useRef, useState } from "react"; import React, { useEffect, useState } from "react";
import InputWithDropDown from "../../../../ui/inputs/InputWithDropDown";
import LabledDropdown from "../../../../ui/inputs/LabledDropdown";
import { import {
AddIcon, useSelectedEventData,
RemoveIcon, useSelectedEventSphere,
ResizeHeightIcon, useSelectedProduct,
} from "../../../../icons/ExportCommonIcons"; } from "../../../../../store/simulation/useSimulationStore";
import RenameInput from "../../../../ui/inputs/RenameInput"; import { useProductStore } from "../../../../../store/simulation/useProductStore";
import { handleResize } from "../../../../../functions/handleResizePannel"; import ConveyorMechanics from "./mechanics/conveyorMechanics";
import { handleActionToggle } from "./functions/handleActionToggle"; import VehicleMechanics from "./mechanics/vehicleMechanics";
import { handleDeleteAction } from "./functions/handleDeleteAction"; import RoboticArmMechanics from "./mechanics/roboticArmMechanics";
import DefaultAction from "./actions/DefaultAction"; import MachineMechanics from "./mechanics/machineMechanics";
import SpawnAction from "./actions/SpawnAction"; import StorageMechanics from "./mechanics/storageMechanics";
import SwapAction from "./actions/SwapAction"; import { AddIcon } from "../../../../icons/ExportCommonIcons";
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";
interface EventPropertiesProps { const EventProperties: React.FC = () => {
assetType: string; const { selectedEventData } = useSelectedEventData();
selectedPoint: { const { getEventByModelUuid } = useProductStore();
name: string; const { selectedProduct } = useSelectedProduct();
uuid: string; const [currentEventData, setCurrentEventData] = useState<EventsSchema | null>(
actions: { null
uuid: string; );
name: string; const [assetType, setAssetType] = useState<string | null>(null);
}[]; const { products } = useProductStore();
}; const { selectedEventSphere } = useSelectedEventSphere();
selectedItem: { useEffect(() => {
item: { const event = getCurrentEventData();
uuid: string; setCurrentEventData(event);
name: string;
} | null;
};
setSelectedPoint: (value: string) => void;
selectedActionSphere: string;
}
const EventProperties: React.FC<EventPropertiesProps> = ({ const type = determineAssetType(event);
assetType, setAssetType(type);
selectedPoint, // eslint-disable-next-line react-hooks/exhaustive-deps
selectedItem, }, [selectedEventData, selectedProduct]);
setSelectedPoint,
selectedActionSphere,
}) => {
const actionsContainerRef = useRef<HTMLDivElement>(null);
const [activeOption, setActiveOption] = useState("default"); const getCurrentEventData = () => {
const [dummyactiveOption, setTypeOption] = useState("default"); if (!selectedEventData?.data || !selectedProduct) return null;
return (
getEventByModelUuid(
selectedProduct.productId,
selectedEventData.data.modelUuid
) ?? null
);
};
const getAvailableActions = () => { const determineAssetType = (event: EventsSchema | null) => {
if (assetType === "conveyor") { if (!event) return null;
return {
defaultOption: "default", switch (event.type) {
options: ["default", "spawn", "swap", "despawn"], case "transfer":
}; return "conveyor";
} case "vehicle":
if (assetType === "vehicle") { return "vehicle";
return { case "roboticArm":
defaultOption: "travel", return "roboticArm";
options: ["travel"], case "machine":
}; return "machine";
} case "storageUnit":
if (assetType === "roboticArm") { return "storageUnit";
return { default:
defaultOption: "pickAndPlace", return null;
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 ( return (
<div className="event-proprties-wrapper"> <div className="event-proprties-wrapper">
{currentEventData && (
<>
<div className="header"> <div className="header">
<div className="header-value">{selectedPoint.name}</div> <div className="header-value">
</div> {selectedEventData?.data.modelName}
<div className="global-props">
<div className="property-list-container">
{/* <div className="property-item">
<LabledDropdown
defaultOption={assetType}
options={[]}
onSelect={(option) => setTypeOption(option)}
/>
</div> */}
<div className="property-item">
<InputWithDropDown
label="Speed"
value="0.5"
min={0}
step={0.1}
defaultValue="0.5"
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> </div>
{assetType === "conveyor" && <ConveyorMechanics />}
{assetType === "vehicle" && <VehicleMechanics />}
{assetType === "roboticArm" && <RoboticArmMechanics />}
{assetType === "machine" && <MachineMechanics />}
{assetType === "storageUnit" && <StorageMechanics />}
</>
)}
{!currentEventData && selectedEventSphere && (
<div className="no-event-selected">
<p>
<strong>Oops!</strong> It looks like this object doesn't have an
event assigned yet. To continue, please link it to one of the
products below.
</p>
<div className="products-list">
<p>
<strong>Here are some products you can add it to:</strong>
</p>
<ul>
{products.map((product) => (
<li key={product.productId}>
<button>
<AddIcon />
{product.productName}
</button>
</li>
))}
</ul>
</div> </div>
<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">
{selectedPoint?.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>
{selectedPoint?.actions.length > 1 && (
<div
className="remove-button"
onClick={() => handleDeleteAction(action.uuid)}
>
<RemoveIcon />
</div> </div>
)} )}
{!selectedEventSphere && (
<div className="no-event-selected">
<p>
<strong>Oops!</strong> It looks like you haven't selected an event
point yet. Please select an event to view its properties.
</p>
</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> </div>
); );
}; };

View File

@@ -0,0 +1,34 @@
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<DelayActionProps> = ({
value,
defaultValue,
min,
max,
onChange,
}) => {
return (
<InputWithDropDown
label="Delay"
value={value}
min={min}
step={0.1}
defaultValue={defaultValue}
max={max}
activeOption="s"
onClick={() => {}}
onChange={onChange}
/>
);
};
export default DelayAction;

View File

@@ -1,20 +1,8 @@
import React from "react"; import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
const DespawnAction: React.FC = () => { const DespawnAction: React.FC = () => {
return ( return (
<> <>
<InputWithDropDown
label="Delay"
value=""
min={0}
step={0.1}
max={10}
defaultValue="0"
activeOption="s"
onClick={() => {}}
onChange={(value) => console.log(value)}
/>
</> </>
); );
}; };

View File

@@ -1,11 +1,23 @@
import React from "react"; import React from "react";
import EyeDropInput from "../../../../../ui/inputs/EyeDropInput"; import EyeDropInput from "../../../../../ui/inputs/EyeDropInput";
const PickAndPlaceAction: React.FC = () => { interface PickAndPlaceActionProps {
pickPointValue: string;
pickPointOnChange: (value: string) => void;
placePointValue: string;
placePointOnChange: (value: string) => void;
}
const PickAndPlaceAction: React.FC<PickAndPlaceActionProps> = ({
pickPointValue,
pickPointOnChange,
placePointValue,
placePointOnChange,
}) => {
return ( return (
<> <>
<EyeDropInput label="Pick Point" value="na" onChange={() => {}} /> <EyeDropInput label="Pick Point" value={pickPointValue} onChange={pickPointOnChange} />
<EyeDropInput label="Unload Point" value="na" onChange={() => {}} /> <EyeDropInput label="Unload Point" value={placePointValue} onChange={placePointOnChange} />
</> </>
); );
}; };

View File

@@ -2,21 +2,45 @@ import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import SwapAction from "./SwapAction"; import SwapAction from "./SwapAction";
const ProcessAction: React.FC = () => { 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<ProcessActionProps> = ({
value,
min,
max,
defaultValue,
onChange,
swapOptions,
swapDefaultOption,
onSwapSelect,
}) => {
return ( return (
<> <>
<InputWithDropDown <InputWithDropDown
label="Process Time" label="Process Time"
value="6" value={value}
min={0} min={min}
step={1} step={1}
max={10} max={max}
defaultValue="0" defaultValue={defaultValue}
activeOption="s" activeOption="s"
onClick={() => { }} onClick={() => { }}
onChange={(value) => console.log(value)} onChange={onChange}
/>
<SwapAction
options={swapOptions}
defaultOption={swapDefaultOption}
onSelect={onSwapSelect}
/> />
<SwapAction />
</> </>
); );
}; };

View File

@@ -1,33 +1,70 @@
import React from "react"; import React from "react";
import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload"; import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
const SpawnAction: React.FC = () => { 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<SpawnActionProps> = ({
onChangeInterval,
onChangeCount,
defaultOption,
options,
onSelect,
intervalValue,
countValue,
intervalMin,
intervalMax,
intervalDefaultValue,
countMin,
countMax,
countDefaultValue,
}) => {
return ( return (
<> <>
<InputWithDropDown <InputWithDropDown
label="Spawn interval" label="Spawn interval"
value="0" value={intervalValue}
min={0} min={intervalMin}
step={1} step={1}
defaultValue="0" defaultValue={intervalDefaultValue}
max={10} max={intervalMax}
activeOption="s" activeOption="s"
onClick={() => { }} onClick={() => { }}
onChange={(value) => console.log(value)} onChange={onChangeInterval}
/> />
<InputWithDropDown <InputWithDropDown
label="Spawn count" label="Spawn count"
value="0" value={countValue}
min={0} min={countMin}
step={1} step={1}
defaultValue="0" defaultValue={countDefaultValue}
max={10} max={countMax}
activeOption="s" activeOption="s"
onClick={() => { }} onClick={() => { }}
onChange={(value) => console.log(value)} onChange={onChangeCount}
/>
{/* <PreviewSelectionWithUpload /> */}
<LabledDropdown
label="Presets"
defaultOption={defaultOption}
options={options}
onSelect={onSelect}
/> />
<PreviewSelectionWithUpload />
</> </>
); );
}; };

View File

@@ -1,18 +1,26 @@
import React from "react"; import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
const StorageAction: React.FC = () => { interface StorageActionProps {
value: string;
min: number;
max: number;
defaultValue: string;
onChange: (value: string) => void;
}
const StorageAction: React.FC<StorageActionProps> = ({ value, min, max, defaultValue, onChange }) => {
return ( return (
<InputWithDropDown <InputWithDropDown
label="Storage Capacity" label="Storage Capacity"
value="" value={value}
min={0} min={min}
step={0.1} step={1}
max={10} max={max}
defaultValue="0" defaultValue={defaultValue}
activeOption="s" activeOption="s"
onClick={() => { }} onClick={() => { }}
onChange={(value) => console.log(value)} onChange={onChange}
/> />
); );
}; };

View File

@@ -1,11 +1,24 @@
import React from "react"; import React from "react";
import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload"; import PreviewSelectionWithUpload from "../../../../../ui/inputs/PreviewSelectionWithUpload";
const SwapAction: React.FC = () => { interface SwapActionProps {
onSelect: (option: string) => void;
defaultOption: string;
options: string[];
}
const SwapAction: React.FC<SwapActionProps> = ({
onSelect,
defaultOption,
options,
}) => {
return ( return (
<> <PreviewSelectionWithUpload
<PreviewSelectionWithUpload /> label="Presets"
</> defaultOption={defaultOption}
options={options}
onSelect={onSelect}
/>
); );
}; };

View File

@@ -2,33 +2,75 @@ import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import EyeDropInput from "../../../../../ui/inputs/EyeDropInput"; import EyeDropInput from "../../../../../ui/inputs/EyeDropInput";
const TravelAction: React.FC = () => { 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<TravelActionProps> = ({
loadCapacity,
unloadDuration,
pickPoint,
unloadPoint,
}) => {
return ( return (
<> <>
<InputWithDropDown <InputWithDropDown
label="Load Capacity" label="Load Capacity"
value="" value={loadCapacity.value}
min={0} min={loadCapacity.min}
max={loadCapacity.max}
defaultValue={loadCapacity.defaultValue}
step={0.1} step={0.1}
max={10}
defaultValue="0"
activeOption="s" activeOption="s"
onClick={() => { }} onClick={() => { }}
onChange={(value) => console.log(value)} onChange={loadCapacity.onChange}
/> />
<InputWithDropDown <InputWithDropDown
label="Unload Duration" label="Unload Duration"
value="" value={unloadDuration.value}
min={0} min={unloadDuration.min}
max={unloadDuration.max}
defaultValue={unloadDuration.defaultValue}
step={0.1} step={0.1}
max={10}
defaultValue="0"
activeOption="s" activeOption="s"
onClick={() => { }} onClick={() => { }}
onChange={(value) => console.log(value)} onChange={unloadDuration.onChange}
/> />
<EyeDropInput label="Pick Point" value="na" onChange={() => {}} /> {pickPoint && (
<EyeDropInput label="Unload Point" value="na" onChange={() => {}} /> <EyeDropInput
label="Pick Point"
value={pickPoint.value}
onChange={pickPoint.onChange}
/>
)}
{unloadPoint && (
<EyeDropInput
label="Unload Point"
value={unloadPoint.value}
onChange={unloadPoint.onChange}
/>
)}
</> </>
); );
}; };

View File

@@ -0,0 +1,202 @@
import React, { useRef } from "react";
import {
AddIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../../../icons/ExportCommonIcons";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../../../functions/handleResizePannel";
import {
useSelectedAction,
useSelectedEventData,
useSelectedProduct,
} from "../../../../../../store/simulation/useSimulationStore";
import { MathUtils } from "three";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
interface ActionsListProps {
setSelectedPointData: (data: any) => void; // You can replace `any` with a more specific type if you have one
selectedPointData: any; // You can replace `any` with a more specific type if you have one
// ui control props
multipleAction?: boolean;
}
const ActionsList: React.FC<ActionsListProps> = ({
setSelectedPointData,
selectedPointData,
multipleAction = false,
}) => {
const actionsContainerRef = useRef<HTMLDivElement>(null);
// store
const { selectedEventData } = useSelectedEventData();
const { updateAction, addAction, removeAction } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const { selectedAction, setSelectedAction, clearSelectedAction } =
useSelectedAction();
const handleAddAction = () => {
if (!selectedEventData || !selectedPointData) return;
const newAction = {
actionUuid: MathUtils.generateUUID(),
actionName: `Action ${selectedPointData.actions.length + 1}`,
actionType: "pickAndPlace" as const,
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: any) => 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?.actions) {
const updatedActions = selectedPointData.actions.map((action: any) =>
action.actionUuid === selectedAction.actionId
? { ...action, actionName: newName }
: action
);
setSelectedPointData({
...selectedPointData,
actions: updatedActions,
});
} else {
// write logic for single action
return;
}
};
const handleActionSelect = (actionUuid: string, actionName: string) => {
setSelectedAction(actionUuid, actionName);
};
return (
<div className="actions-list-container">
<div className="actions">
<div className="header">
<div className="header-value">Actions</div>
<button
className="add-button"
onClick={() => handleAddAction()}
disabled={!multipleAction}
>
<AddIcon /> Add
</button>
</div>
<div
className="lists-main-container"
ref={actionsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{multipleAction &&
selectedPointData.actions.map((action: any) => (
<div
key={action.actionUuid}
className={`list-item ${
selectedAction.actionId === action.actionUuid
? "active"
: ""
}`}
>
<button
className="value"
onClick={() =>
handleActionSelect(action.actionUuid, action.actionName)
}
>
<RenameInput
value={action.actionName}
onRename={handleRenameAction}
/>
</button>
{selectedPointData.actions.length > 1 && (
<button
className="remove-button"
onClick={() => handleDeleteAction(action.actionUuid)}
>
<RemoveIcon />
</button>
)}
</div>
))}
{!multipleAction && selectedPointData && (
<div
key={selectedPointData.action.actionUuid}
className={`list-item active`}
>
<button
className="value"
onClick={() =>
handleActionSelect(
selectedPointData.action.actionUuid,
selectedPointData.action.actionName
)
}
>
<RenameInput
value={selectedPointData.action.actionName}
onRename={handleRenameAction}
/>
</button>
</div>
)}
</div>
{multipleAction && (
<button
className="resize-icon"
id="action-resize"
onMouseDown={(e: any) => handleResize(e, actionsContainerRef)}
>
<ResizeHeightIcon />
</button>
)}
</div>
</div>
</div>
);
};
export default ActionsList;

View File

@@ -0,0 +1,224 @@
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";
import ActionsList from "../components/ActionsList";
function ConveyorMechanics() {
const [activeOption, setActiveOption] = useState<
"default" | "spawn" | "swap" | "delay" | "despawn"
>("default");
const [selectedPointData, setSelectedPointData] = useState<
ConveyorPointSchema | undefined
>();
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, getPointByUuid]);
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 && (
<>
<div key={selectedPointData?.uuid} className="global-props">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
min={0}
step={0.1}
defaultValue={"0.5"}
max={10}
activeOption="m/s"
onClick={() => {}}
onChange={handleSpeedChange}
/>
</div>
</div>
</div>
<ActionsList
setSelectedPointData={setSelectedPointData}
selectedPointData={selectedPointData}
/>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={
selectedPointData
? selectedPointData.action.actionType
: "default"
}
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "default" && <DefaultAction />}
{activeOption === "spawn" && (
<SpawnAction
onChangeCount={handleSpawnCountChange}
options={["Default material", "Material 1", "Material 2"]}
defaultOption={currentMaterial}
onSelect={handleMaterialSelect}
onChangeInterval={handleSpawnIntervalChange}
intervalValue={currentSpawnInterval}
countValue={currentSpawnCount}
intervalMin={1}
intervalMax={60}
intervalDefaultValue="1"
countMin={1}
countMax={100}
countDefaultValue="1"
/>
)}
{activeOption === "swap" && (
<SwapAction
options={["Default material", "Material 1", "Material 2"]}
defaultOption={currentMaterial}
onSelect={handleMaterialSelect}
/>
)}
{activeOption === "despawn" && <DespawnAction />}
{activeOption === "delay" && (
<DelayAction
value={currentDelay}
defaultValue="0"
min={0}
max={60}
onChange={handleDelayChange}
/>
)}
</div>
</div>
<div className="tirgger">
<Trigger />
</div>
</>
)}
</>
);
}
export default ConveyorMechanics;

View File

@@ -0,0 +1,129 @@
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";
import ActionsList from "../components/ActionsList";
function MachineMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "process">(
"default"
);
const [selectedPointData, setSelectedPointData] = useState<
MachinePointSchema | undefined
>();
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, getPointByUuid]);
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 && (
<>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<ActionsList
setSelectedPointData={setSelectedPointData}
selectedPointData={selectedPointData}
/>
<div className="selected-actions-list">
<LabledDropdown
defaultOption="process"
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "process" && (
<ProcessAction
value={currentProcessTime}
min={0.1}
max={60}
defaultValue="1"
onChange={handleProcessTimeChange}
swapOptions={["Default material", "Material 1", "Material 2"]}
swapDefaultOption={currentMaterial}
onSwapSelect={handleMaterialSelect}
/>
)}
</div>
</div>
<div className="tirgger">
<Trigger />
</div>
</>
)}
</>
);
}
export default MachineMechanics;

View File

@@ -0,0 +1,194 @@
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,
useSelectedAction,
} from "../../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../../store/simulation/useProductStore";
import PickAndPlaceAction from "../actions/PickAndPlaceAction";
import ActionsList from "../components/ActionsList";
function RoboticArmMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "pickAndPlace">(
"default"
);
const [selectedPointData, setSelectedPointData] = useState<
RoboticArmPointSchema | undefined
>();
const { selectedEventData } = useSelectedEventData();
const { getPointByUuid, updateEvent, updateAction } = 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?.actions) {
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();
}
}, [
clearSelectedAction,
getPointByUuid,
selectedAction.actionId,
selectedEventData,
selectedProduct,
setSelectedAction,
]);
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 && (
<>
<div className="global-props">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
min={0}
step={0.1}
defaultValue={"0.5"}
max={10}
activeOption="m/s"
onClick={() => {}}
onChange={handleSpeedChange}
/>
</div>
</div>
</div>
<ActionsList
setSelectedPointData={setSelectedPointData}
selectedPointData={selectedPointData}
multipleAction
/>
{selectedAction.actionId && currentAction && (
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={selectedAction.actionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={activeOption}
options={availableActions.options}
onSelect={() => {}}
disabled={true}
/>
<PickAndPlaceAction
pickPointValue={currentPickPoint}
pickPointOnChange={handlePickPointChange}
placePointValue={currentPlacePoint}
placePointOnChange={handlePlacePointChange}
/>
</div>
<div className="tirgger">
<Trigger />
</div>
</div>
)}
</>
)}
</>
);
}
export default RoboticArmMechanics;

View File

@@ -0,0 +1,120 @@
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";
import ActionsList from "../components/ActionsList";
function StorageMechanics() {
const [activeOption, setActiveOption] = useState<
"default" | "store" | "spawn"
>("default");
const [selectedPointData, setSelectedPointData] = useState<
StoragePointSchema | undefined
>();
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, getPointByUuid]);
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 && (
<>
<ActionsList
setSelectedPointData={setSelectedPointData}
selectedPointData={selectedPointData}
/>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption={activeOption}
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "store" && (
<StorageAction
value={currentCapacity}
defaultValue="0"
min={0}
max={20}
onChange={handleCapacityChange}
/>
)}
{activeOption === "spawn" && (
<div className="spawn-options">
<p>Spawn configuration options would go here</p>
</div>
)}
</div>
</div>
<div className="tirgger">
<Trigger />
</div>
</>
)}
</>
);
}
export default StorageMechanics;

View File

@@ -0,0 +1,199 @@
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";
import ActionsList from "../components/ActionsList";
function VehicleMechanics() {
const [activeOption, setActiveOption] = useState<"default" | "travel">(
"default"
);
const [selectedPointData, setSelectedPointData] = useState<
VehiclePointSchema | undefined
>();
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, getPointByUuid]);
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 && (
<>
<div className="global-props">
<div className="property-list-container">
<div className="property-item">
<InputWithDropDown
label="Speed"
value={currentSpeed}
min={0}
step={0.1}
defaultValue={"0.5"}
max={10}
activeOption="m/s"
onClick={() => {}}
onChange={handleSpeedChange}
/>
</div>
</div>
</div>
<ActionsList
setSelectedPointData={setSelectedPointData}
selectedPointData={selectedPointData}
/>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput
value={currentActionName}
onRename={handleRenameAction}
/>
</div>
<div className="selected-actions-list">
<LabledDropdown
defaultOption="travel"
options={availableActions.options}
onSelect={handleActionTypeChange}
/>
{activeOption === "travel" && (
<TravelAction
loadCapacity={{
value: currentLoadCapacity,
min: 1,
max: 100,
defaultValue: "1",
onChange: handleLoadCapacityChange,
}}
unloadDuration={{
value: currentUnloadDuration,
min: 1,
max: 60,
defaultValue: "1",
onChange: handleUnloadDurationChange,
}}
// pickPoint={{
// value: currentPickPoint,
// onChange: handlePickPointChange,
// }}
// unloadPoint={{
// value: currentUnloadPoint,
// onChange: handleUnloadPointChange,
// }}
/>
)}
</div>
</div>
<div className="tirgger">
<Trigger />
</div>
</>
)}
</>
);
}
export default VehicleMechanics;

View File

@@ -1,11 +1,19 @@
import React, { useState } from "react"; import React, { useRef, useState } from "react";
import { AddIcon, RemoveIcon } from "../../../../../icons/ExportCommonIcons"; import {
AddIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../../../icons/ExportCommonIcons";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown"; import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../../../functions/handleResizePannel";
const Trigger: React.FC = () => { const Trigger: React.FC = () => {
// State to hold the list of triggers // State to hold the list of triggers
const [triggers, setTriggers] = useState<string[]>([]); const [triggers, setTriggers] = useState<string[]>(["Trigger 1"]);
const [selectedTrigger, setSelectedTrigger] = useState<string>("Trigger 1");
const [activeOption, setActiveOption] = useState("onComplete"); const [activeOption, setActiveOption] = useState("onComplete");
const triggersContainerRef = useRef<HTMLDivElement>(null);
// States for dropdowns // States for dropdowns
const [triggeredModel, setTriggeredModel] = useState<string[]>([]); const [triggeredModel, setTriggeredModel] = useState<string[]>([]);
@@ -35,28 +43,53 @@ const Trigger: React.FC = () => {
<div className="trigger-wrapper"> <div className="trigger-wrapper">
<div className="header"> <div className="header">
<div className="title">Trigger</div> <div className="title">Trigger</div>
<div <button
className="add-button" className="add-button"
onClick={addTrigger} onClick={addTrigger}
style={{ cursor: "pointer" }} style={{ cursor: "pointer" }}
> >
<AddIcon /> Add <AddIcon /> Add
</div> </button>
</div> </div>
<div className="trigger-list"> <div className="trigger-list">
{/* Map over triggers and render them */}
{triggers.map((trigger, index) => (
<div key={index} className="trigger-item">
<div className="trigger-name">
{trigger}
<div <div
className="lists-main-container"
ref={triggersContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{triggers.map((trigger: any, index: number) => (
<div
key={index}
className={`list-item ${
selectedTrigger === trigger ? "active" : ""
}`}
onClick={() => setSelectedTrigger(trigger)}
>
<button className="value" onClick={() => {}}>
<RenameInput value={trigger} onRename={() => {}} />
</button>
{triggers.length > 1 && (
<button
className="remove-button" className="remove-button"
onClick={() => removeTrigger(index)} onClick={() => removeTrigger(index)}
style={{ cursor: "pointer" }}
> >
<RemoveIcon /> <RemoveIcon />
</button>
)}
</div> </div>
))}
</div> </div>
<button
className="resize-icon"
id="action-resize"
onMouseDown={(e: any) => handleResize(e, triggersContainerRef)}
>
<ResizeHeightIcon />
</button>
</div>
<div className="trigger-item">
<div className="trigger-name">{selectedTrigger}</div>
<LabledDropdown <LabledDropdown
defaultOption={activeOption} defaultOption={activeOption}
options={["onComplete", "onStart", "onStop", "delay"]} options={["onComplete", "onStart", "onStop", "delay"]}
@@ -65,40 +98,39 @@ const Trigger: React.FC = () => {
<div className="trigger-options"> <div className="trigger-options">
<div> <div>
<LabledDropdown <LabledDropdown
defaultOption={triggeredModel[index] || "Select Model"} defaultOption={triggeredModel[0] || "Select Model"}
options={["Model 1", "Model 2", "Model 3"]} options={["Model 1", "Model 2", "Model 3"]}
onSelect={(option) => { onSelect={(option) => {
const newModel = [...triggeredModel]; const newModel = [...triggeredModel];
newModel[index] = option; newModel[0] = option;
setTriggeredModel(newModel); setTriggeredModel(newModel);
}} }}
/> />
</div> </div>
<div> <div>
<LabledDropdown <LabledDropdown
defaultOption={triggeredPoint[index] || "Select Point"} defaultOption={triggeredPoint[0] || "Select Point"}
options={["Point 1", "Point 2", "Point 3"]} options={["Point 1", "Point 2", "Point 3"]}
onSelect={(option) => { onSelect={(option) => {
const newPoint = [...triggeredPoint]; const newPoint = [...triggeredPoint];
newPoint[index] = option; newPoint[0] = option;
setTriggeredPoint(newPoint); setTriggeredPoint(newPoint);
}} }}
/> />
</div> </div>
<div> <div>
<LabledDropdown <LabledDropdown
defaultOption={triggeredAction[index] || "Select Action"} defaultOption={triggeredAction[0] || "Select Action"}
options={["Action 1", "Action 2", "Action 3"]} options={["Action 1", "Action 2", "Action 3"]}
onSelect={(option) => { onSelect={(option) => {
const newAction = [...triggeredAction]; const newAction = [...triggeredAction];
newAction[index] = option; newAction[0] = option;
setTriggeredAction(newAction); setTriggeredAction(newAction);
}} }}
/> />
</div> </div>
</div> </div>
</div> </div>
))}
</div> </div>
</div> </div>
); );

View File

@@ -1,4 +1,4 @@
import React, { useRef, useState } from "react"; import React, { useEffect, useRef } from "react";
import { import {
AddIcon, AddIcon,
ArrowIcon, ArrowIcon,
@@ -7,65 +7,98 @@ 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 { 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 Path { interface Event {
pathName: string; // Represents the name of the path pathName: string;
Children: string[]; // Represents the list of child points
} }
interface DropListProps { interface ListProps {
val: Path; // Use the Path interface for the val prop val: Event;
} }
const DropList: React.FC<DropListProps> = ({ val }) => { const List: React.FC<ListProps> = ({ val }) => {
const [openDrop, setOpenDrop] = useState(false);
return ( return (
<div className="process-container"> <div className="process-container">
<div <div className="value">
className="value"
onClick={() => {
setOpenDrop(!openDrop);
}}
>
{val.pathName} {val.pathName}
<div className={`arrow-container${openDrop ? " active" : ""}`}>
<ArrowIcon />
</div> </div>
</div> </div>
{val.Children && openDrop && (
<div className="children-drop">
{val.Children.map((child, index) => (
<div key={index} className="value">
{child}
</div>
))}
</div>
)}
</div>
); );
}; };
const Simulations: React.FC = () => { const Simulations: React.FC = () => {
const productsContainerRef = useRef<HTMLDivElement>(null); const productsContainerRef = useRef<HTMLDivElement>(null);
const [productsList, setProductsList] = useState<string[]>([]); const { products, addProduct, removeProduct, renameProduct, addEvent, removeEvent } = useProductStore();
const [selectedItem, setSelectedItem] = useState<string>(); const { selectedProduct, setSelectedProduct } = useSelectedProduct();
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
const handleAddAction = () => { const handleAddProduct = () => {
setProductsList([...productsList, `Product ${productsList.length + 1}`]); addProduct(`Product ${products.length + 1}`, generateUUID());
}; };
const handleRemoveAction = (index: number) => { const handleRemoveProduct = (productId: string) => {
setProductsList(productsList.filter((_, i) => i !== index)); const currentIndex = products.findIndex(p => p.productId === productId);
if (selectedItem === productsList[index]) { const isSelected = selectedProduct.productId === productId;
setSelectedItem("");
const updatedProducts = products.filter(p => p.productId !== productId);
if (isSelected) {
if (updatedProducts.length > 0) {
let newSelectedIndex = currentIndex;
if (currentIndex >= updatedProducts.length) {
newSelectedIndex = updatedProducts.length - 1;
}
setSelectedProduct(
updatedProducts[newSelectedIndex].productId,
updatedProducts[newSelectedIndex].productName
);
} else {
setSelectedProduct('', '');
}
}
removeProduct(productId);
};
const handleRenameProduct = (productId: string, newName: string) => {
renameProduct(productId, newName);
if (selectedProduct.productId === productId) {
setSelectedProduct(productId, newName);
} }
}; };
const Value = [ const handleAddEventToProduct = () => {
{ pathName: "Path 1", Children: ["Point 1", "Point 2"] }, if (selectedAsset) {
{ pathName: "Path 2", Children: ["Point 1", "Point 2"] }, addEvent(selectedProduct.productId, selectedAsset);
{ pathName: "Path 3", Children: ["Point 1", "Point 2"] }, // 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?.eventDatas.map((event) => ({
pathName: event.modelName,
})) || [];
return ( return (
<div className="simulations-container"> <div className="simulations-container">
@@ -74,7 +107,7 @@ const Simulations: React.FC = () => {
<div className="actions"> <div className="actions">
<div className="header"> <div className="header">
<div className="header-value">Products</div> <div className="header-value">Products</div>
<div className="add-button" onClick={handleAddAction}> <div className="add-button" onClick={handleAddProduct}>
<AddIcon /> Add <AddIcon /> Add
</div> </div>
</div> </div>
@@ -84,26 +117,34 @@ const Simulations: React.FC = () => {
style={{ height: "120px" }} style={{ height: "120px" }}
> >
<div className="list-container"> <div className="list-container">
{productsList.map((action, index) => ( {products.map((product, index) => (
<div <div
key={index} key={product.productId}
className={`list-item ${ className={`list-item ${selectedProduct.productId === product.productId ? "active" : ""}`}
selectedItem === action ? "active" : ""
}`}
> >
<div <div
className="value" className="value"
onClick={() => setSelectedItem(action)} onClick={() => setSelectedProduct(product.productId, product.productName)}
> >
<input type="radio" name="products" id="products" /> <input
<RenameInput value={action} /> type="radio"
name="products"
checked={selectedProduct.productId === product.productId}
readOnly
/>
<RenameInput
value={product.productName}
onRename={(newName) => handleRenameProduct(product.productId, newName)}
/>
</div> </div>
{products.length > 1 && (
<div <div
className="remove-button" className="remove-button"
onClick={() => handleRemoveAction(index)} onClick={() => handleRemoveProduct(product.productId)}
> >
<RemoveIcon /> <RemoveIcon />
</div> </div>
)}
</div> </div>
))} ))}
</div> </div>
@@ -116,30 +157,46 @@ const Simulations: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
<div className="simulation-process"> <div className="simulation-process">
<div className="collapse-header-container"> <div className="collapse-header-container">
<div className="header">Operations</div> <div className="header">Events</div>
<div className="arrow-container"> <div className="arrow-container">
<ArrowIcon /> <ArrowIcon />
</div> </div>
</div> </div>
{Value.map((val, index) => ( {events.map((event, index) => (
<DropList key={index} val={val} /> <List key={index} val={event} />
))} ))}
</div> </div>
<div className="compare-simulations-container"> <div className="compare-simulations-container">
<div className="compare-simulations-header"> <div className="compare-simulations-header">
Need to Compare Layout? Need to Compare Layout?
</div> </div>
<div className="content"> <div className="content">
Click <span>'Compare'</span> to review and analyze the layout Click <span>'Compare'</span> to review and analyze the layout differences between them.
differences between them.
</div> </div>
<div className="input"> <div className="input">
<input type="button" value={"Compare"} className="submit" /> <input type="button" value={"Compare"} className="submit" />
</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,14 +1,32 @@
import React, { useState } from "react"; import React, { useState } from "react";
import LabledDropdown from "./LabledDropdown";
import { ArrowIcon } from "../../icons/ExportCommonIcons"; import { ArrowIcon } from "../../icons/ExportCommonIcons";
import LabledDropdown from "./LabledDropdown";
const PreviewSelectionWithUpload: React.FC = () => { interface PreviewSelectionWithUploadProps {
const [showPreview, setSetshowPreview] = useState(false); preview?: boolean;
upload?: boolean;
label?: string;
onSelect: (option: string) => void;
defaultOption: string;
options: string[];
}
const PreviewSelectionWithUpload: React.FC<PreviewSelectionWithUploadProps> = ({
preview = false,
upload = false,
onSelect,
label,
defaultOption,
options,
}) => {
const [showPreview, setShowPreview] = useState(false);
return ( return (
<div className="preview-selection-with-upload-wrapper"> <div className="preview-selection-with-upload-wrapper">
<div {preview && (
<>
<button
className="input-header-container" className="input-header-container"
onClick={() => setSetshowPreview(!showPreview)} onClick={() => setShowPreview(!showPreview)}
> >
<div className="input-header">Preview</div> <div className="input-header">Preview</div>
<div <div
@@ -17,12 +35,15 @@ const PreviewSelectionWithUpload: React.FC = () => {
> >
<ArrowIcon /> <ArrowIcon />
</div> </div>
</div> </button>
{showPreview && ( {showPreview && (
<div className="canvas-wrapper"> <div className="canvas-wrapper">
<div className="canvas-container"></div> <div className="canvas-container"></div>
</div> </div>
)} )}
</>
)}
{upload && (
<div className="asset-selection-container"> <div className="asset-selection-container">
<div className="upload-custom-asset-button"> <div className="upload-custom-asset-button">
<div className="title">Upload Product</div> <div className="title">Upload Product</div>
@@ -31,17 +52,21 @@ const PreviewSelectionWithUpload: React.FC = () => {
accept=".glb, .gltf" accept=".glb, .gltf"
id="simulation-product-upload" id="simulation-product-upload"
/> />
<label className="upload-button" htmlFor="simulation-product-upload"> <label
className="upload-button"
htmlFor="simulation-product-upload"
>
Upload here Upload here
</label> </label>
</div> </div>
<LabledDropdown
label="Presets"
defaultOption={"Default material"}
options={["Default material", "Product 1", "Product 2"]}
onSelect={(option) => console.log(option)}
/>
</div> </div>
)}
<LabledDropdown
label={label}
defaultOption={defaultOption}
options={options}
onSelect={onSelect}
/>
</div> </div>
); );
}; };

View File

@@ -10,7 +10,7 @@ import {
ArrowIcon, ArrowIcon,
EyeIcon, EyeIcon,
LockIcon, LockIcon,
RmoveIcon, RemoveIcon,
} from "../../icons/ExportCommonIcons"; } from "../../icons/ExportCommonIcons";
import { useThree } from "@react-three/fiber"; import { useThree } from "@react-three/fiber";
import { useFloorItems, useZoneAssetId, useZones } from "../../../store/store"; import { useFloorItems, useZoneAssetId, useZones } from "../../../store/store";
@@ -142,9 +142,6 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
) )
); );
} }
console.log('newName: ', newName);
} }
const checkZoneNameDuplicate = (name: string) => { const checkZoneNameDuplicate = (name: string) => {
return zones.some( return zones.some(
@@ -184,7 +181,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
</div> </div>
{remove && ( {remove && (
<div className="remove option"> <div className="remove option">
<RmoveIcon /> <RemoveIcon />
</div> </div>
)} )}
{item.assets && item.assets.length > 0 && ( {item.assets && item.assets.length > 0 && (
@@ -221,7 +218,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
</div> </div>
{remove && ( {remove && (
<div className="remove option"> <div className="remove option">
<RmoveIcon /> <RemoveIcon />
</div> </div>
)} )}
</div> </div>

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

@@ -8,10 +8,13 @@ import * as Types from "../../../types/world/worldTypes";
import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi'; import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi';
import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
import PointsCalculator from '../../simulation/events/points/functions/pointsCalculator';
async function loadInitialFloorItems( async function loadInitialFloorItems(
itemsGroup: Types.RefGroup, itemsGroup: Types.RefGroup,
setFloorItems: Types.setFloorItemSetState, setFloorItems: Types.setFloorItemSetState,
addEvent: (event: EventsSchema) => void,
renderDistance: number
): Promise<void> { ): Promise<void> {
if (!itemsGroup.current) return; if (!itemsGroup.current) return;
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
@@ -63,14 +66,14 @@ async function loadInitialFloorItems(
const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z); const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z);
if (cameraPosition.distanceTo(itemPosition) < 50) { if (cameraPosition.distanceTo(itemPosition) < renderDistance) {
await new Promise<void>(async (resolve) => { await new Promise<void>(async (resolve) => {
// Check Three.js Cache // Check Three.js Cache
const cachedModel = THREE.Cache.get(item.modelfileID!); const cachedModel = THREE.Cache.get(item.modelfileID!);
if (cachedModel) { if (cachedModel) {
// console.log(`[Cache] Fetching ${item.modelname}`); // console.log(`[Cache] Fetching ${item.modelname}`);
processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems); processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
modelsLoaded++; modelsLoaded++;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
return; return;
@@ -85,7 +88,7 @@ async function loadInitialFloorItems(
URL.revokeObjectURL(blobUrl); URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl); THREE.Cache.remove(blobUrl);
THREE.Cache.add(item.modelfileID!, gltf); THREE.Cache.add(item.modelfileID!, gltf);
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems); processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
modelsLoaded++; modelsLoaded++;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
}, },
@@ -106,7 +109,7 @@ async function loadInitialFloorItems(
const modelBlob = await fetch(modelUrl).then((res) => res.blob()); const modelBlob = await fetch(modelUrl).then((res) => res.blob());
await storeGLTF(item.modelfileID!, modelBlob); await storeGLTF(item.modelfileID!, modelBlob);
THREE.Cache.add(item.modelfileID!, gltf); THREE.Cache.add(item.modelfileID!, gltf);
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems); processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
modelsLoaded++; modelsLoaded++;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
}, },
@@ -148,8 +151,9 @@ function processLoadedModel(
item: Types.FloorItemType, item: Types.FloorItemType,
itemsGroup: Types.RefGroup, itemsGroup: Types.RefGroup,
setFloorItems: Types.setFloorItemSetState, setFloorItems: Types.setFloorItemSetState,
addEvent: (event: EventsSchema) => void,
) { ) {
const model = gltf; const model = gltf.clone();
model.uuid = item.modeluuid; model.uuid = item.modeluuid;
model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap);
model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid }; model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid };
@@ -182,6 +186,242 @@ function processLoadedModel(
}, },
]); ]);
if (item.modelfileID === "a1ee92554935007b10b3eb05") {
const data = PointsCalculator(
'Vehicle',
gltf.clone(),
new THREE.Vector3(...model.rotation)
);
if (!data || !data.points) return;
const vehicleEvent: VehicleEventSchema = {
modelUuid: item.modeluuid,
modelName: item.modelname,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "vehicle",
speed: 1,
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Vehicle Action",
actionType: "travel",
unLoadDuration: 5,
loadCapacity: 10,
pickUpPoint: null,
unLoadPoint: null,
triggers: []
}
}
};
addEvent(vehicleEvent);
} else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") {
const data = PointsCalculator(
'Conveyor',
gltf.clone(),
new THREE.Vector3(...model.rotation)
);
if (!data || !data.points) return;
const ConveyorEvent: ConveyorEventSchema = {
modelUuid: item.modeluuid,
modelName: item.modelname,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "transfer",
speed: 1,
points: data.points.map((point: THREE.Vector3, index: number) => ({
uuid: THREE.MathUtils.generateUUID(),
position: [point.x, point.y, point.z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action ${index + 1}`,
actionType: 'default',
material: 'Default material',
delay: 0,
spawnInterval: 5,
spawnCount: 1,
triggers: []
}
}))
};
addEvent(ConveyorEvent);
} else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") {
// const data = PointsCalculator(
// 'Conveyor',
// gltf.clone(),
// new THREE.Vector3(...model.rotation)
// );
// if (!data || !data.points) return;
// const points: ConveyorPointSchema[] = data.points.map((point: THREE.Vector3, index: number) => {
// const actionUuid = THREE.MathUtils.generateUUID();
// return {
// uuid: THREE.MathUtils.generateUUID(),
// position: [point.x, point.y, point.z],
// rotation: [0, 0, 0],
// action: {
// actionUuid,
// actionName: `Action ${index}`,
// actionType: 'default',
// material: 'inherit',
// delay: 0,
// spawnInterval: 5,
// spawnCount: 1,
// triggers: []
// }
// };
// });
// points.forEach((point, index) => {
// if (index < points.length - 1) {
// const nextPoint = points[index + 1];
// point.action.triggers.push({
// triggerUuid: THREE.MathUtils.generateUUID(),
// triggerName: `Trigger 1`,
// triggerType: "onComplete",
// delay: 0,
// triggeredAsset: {
// triggeredModel: {
// modelName: item.modelname,
// modelUuid: item.modeluuid
// },
// triggeredPoint: {
// pointName: `Point ${index + 1}`,
// pointUuid: nextPoint.uuid
// },
// triggeredAction: {
// actionName: nextPoint.action.actionName,
// actionUuid: nextPoint.action.actionUuid
// }
// }
// });
// }
// });
// const ConveyorEvent: ConveyorEventSchema = {
// modelUuid: item.modeluuid,
// modelName: item.modelname,
// position: item.position,
// rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
// state: "idle",
// type: "transfer",
// speed: 1,
// points
// };
// addEvent(ConveyorEvent);
} else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") {
const data = PointsCalculator(
'Conveyor',
gltf.clone(),
new THREE.Vector3(...model.rotation)
);
if (!data || !data.points) return;
const ConveyorEvent: ConveyorEventSchema = {
modelUuid: item.modeluuid,
modelName: item.modelname,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "transfer",
speed: 1,
points: data.points.map((point: THREE.Vector3, index: number) => ({
uuid: THREE.MathUtils.generateUUID(),
position: [point.x, point.y, point.z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action ${index}`,
actionType: 'default',
material: 'inherit',
delay: 0,
spawnInterval: 5,
spawnCount: 1,
triggers: []
}
}))
};
addEvent(ConveyorEvent);
} else if (item.modelfileID === "29dee78715ad5b853f5c346d") {
const data = PointsCalculator(
'StaticMachine',
gltf.clone(),
new THREE.Vector3(...model.rotation)
);
if (!data || !data.points) return;
const machineEvent: MachineEventSchema = {
modelUuid: item.modeluuid,
modelName: item.modelname,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "machine",
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Process Action",
actionType: "process",
processTime: 10,
swapMaterial: "material-id",
triggers: []
}
}
};
addEvent(machineEvent);
} else if (item.modelfileID === "52e6681fbb743a890d96c914") {
const data = PointsCalculator(
'ArmBot',
gltf.clone(),
new THREE.Vector3(...model.rotation)
);
if (!data || !data.points) return;
const roboticArmEvent: RoboticArmEventSchema = {
modelUuid: item.modeluuid,
modelName: item.modelname,
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
state: "idle",
type: "roboticArm",
speed: 1,
point: {
uuid: THREE.MathUtils.generateUUID(),
position: [data.points[0].x, data.points[0].y, data.points[0].z],
rotation: [0, 0, 0],
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Pick and Place",
actionType: "pickAndPlace",
process: {
startPoint: [0, 0, 0],
endPoint: [0, 0, 0]
},
triggers: []
}
]
}
};
addEvent(roboticArmEvent);
}
gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: 'power2.out' }); gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: 'power2.out' });
gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out' }); gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out' });
} }

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

@@ -10,7 +10,7 @@ import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils';
import { Socket } from 'socket.io-client'; import { Socket } from 'socket.io-client';
import * as CONSTANTS from '../../../../types/world/worldConstants'; import * as CONSTANTS from '../../../../types/world/worldConstants';
import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import PointsCalculator from '../../../simulation/events/points/pointsCalculator'; import PointsCalculator from '../../../simulation/events/points/functions/pointsCalculator';
async function addAssetModel( async function addAssetModel(
raycaster: THREE.Raycaster, raycaster: THREE.Raycaster,
@@ -202,7 +202,7 @@ async function handleModelLoad(
actionUuid: THREE.MathUtils.generateUUID(), actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action ${index}`, actionName: `Action ${index}`,
actionType: 'default', actionType: 'default',
material: 'inherit', material: 'Default Material',
delay: 0, delay: 0,
spawnInterval: 5, spawnInterval: 5,
spawnCount: 1, spawnCount: 1,
@@ -226,9 +226,8 @@ async function handleModelLoad(
rotation: [0, 0, 0], rotation: [0, 0, 0],
action: { action: {
actionUuid: THREE.MathUtils.generateUUID(), actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Vehicle Action", actionName: "Action 1",
actionType: "travel", actionType: "travel",
material: null,
unLoadDuration: 5, unLoadDuration: 5,
loadCapacity: 10, loadCapacity: 10,
pickUpPoint: null, pickUpPoint: null,
@@ -254,11 +253,11 @@ async function handleModelLoad(
actions: [ actions: [
{ {
actionUuid: THREE.MathUtils.generateUUID(), actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Pick and Place", actionName: "Action 1",
actionType: "pickAndPlace", actionType: "pickAndPlace",
process: { process: {
startPoint: [0, 0, 0], startPoint: null,
endPoint: [0, 0, 0] endPoint: null
}, },
triggers: [] triggers: []
} }
@@ -266,7 +265,7 @@ async function handleModelLoad(
} }
}; };
addEvent(roboticArmEvent); addEvent(roboticArmEvent);
} else if (selectedItem.type === "Machine") { } else if (selectedItem.type === "StaticMachine") {
const machineEvent: MachineEventSchema = { const machineEvent: MachineEventSchema = {
modelUuid: newFloorItem.modeluuid, modelUuid: newFloorItem.modeluuid,
modelName: newFloorItem.modelname, modelName: newFloorItem.modelname,
@@ -280,10 +279,10 @@ async function handleModelLoad(
rotation: [0, 0, 0], rotation: [0, 0, 0],
action: { action: {
actionUuid: THREE.MathUtils.generateUUID(), actionUuid: THREE.MathUtils.generateUUID(),
actionName: "Process Action", actionName: "Action 1",
actionType: "process", actionType: "process",
processTime: 10, processTime: 10,
swapMaterial: "material-id", swapMaterial: "Default Material",
triggers: [] triggers: []
} }
} }

View File

@@ -75,7 +75,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
gltfLoaderWorker.postMessage({ floorItems: data }); gltfLoaderWorker.postMessage({ floorItems: data });
} else { } else {
gltfLoaderWorker.postMessage({ floorItems: [] }); gltfLoaderWorker.postMessage({ floorItems: [] });
loadInitialFloorItems(itemsGroup, setFloorItems); loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, renderDistance);
updateLoadingProgress(100); updateLoadingProgress(100);
} }
}); });
@@ -94,7 +94,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
updateLoadingProgress(progress); updateLoadingProgress(progress);
if (loadedAssets === totalAssets) { if (loadedAssets === totalAssets) {
loadInitialFloorItems(itemsGroup, setFloorItems); loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, renderDistance);
updateLoadingProgress(100); updateLoadingProgress(100);
} }
}); });

View File

@@ -9,11 +9,13 @@ import {
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
import * as CONSTANTS from "../../../types/world/worldConstants"; import * as CONSTANTS from "../../../types/world/worldConstants";
import { useEffect } from "react"; import { useEffect } from "react";
import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
export default function PostProcessing() { export default function PostProcessing() {
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem(); const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { selectedEventSphere } = useSelectedEventSphere();
function flattenChildren(children: any[]) { function flattenChildren(children: any[]) {
const allChildren: any[] = []; const allChildren: any[] = [];
@@ -85,6 +87,21 @@ export default function PostProcessing() {
xRay={true} xRay={true}
/> />
)} )}
{selectedEventSphere && (
<Outline
selection={[selectedEventSphere]}
selectionLayer={10}
width={1000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={10}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={0x6f42c1}
hiddenEdgeColor={0x6f42c1}
blur={true}
xRay={true}
/>
)}
</EffectComposer> </EffectComposer>
</> </>
); );

View File

@@ -9,17 +9,25 @@ import Simulation from "../simulation/simulation";
import Collaboration from "../collaboration/collaboration"; import Collaboration from "../collaboration/collaboration";
export default function Scene() { export default function Scene() {
const map = useMemo(() => [ const map = useMemo(
() => [
{ name: "forward", keys: ["ArrowUp", "w", "W"] }, { name: "forward", keys: ["ArrowUp", "w", "W"] },
{ name: "backward", keys: ["ArrowDown", "s", "S"] }, { name: "backward", keys: ["ArrowDown", "s", "S"] },
{ name: "left", keys: ["ArrowLeft", "a", "A"] }, { name: "left", keys: ["ArrowLeft", "a", "A"] },
{ name: "right", keys: ["ArrowRight", "d", "D"] },], { name: "right", keys: ["ArrowRight", "d", "D"] },
[]); ],
[]
);
return ( return (
<KeyboardControls map={map}> <KeyboardControls map={map}>
<Canvas eventPrefix="client" gl={{ powerPreference: "high-performance", antialias: true }} onContextMenu={(e) => { e.preventDefault(); }}> <Canvas
eventPrefix="client"
gl={{ powerPreference: "high-performance", antialias: true }}
onContextMenu={(e) => {
e.preventDefault();
}}
>
<Setup /> <Setup />
<Collaboration /> <Collaboration />
@@ -29,7 +37,6 @@ export default function Scene() {
<Simulation /> <Simulation />
<Visualization /> <Visualization />
</Canvas> </Canvas>
</KeyboardControls> </KeyboardControls>
); );

View File

@@ -4,20 +4,37 @@ 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, 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 [selectedPoint, setSelectedPoint] = useState<THREE.Mesh | null>(null);
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({}); 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(() => { useEffect(() => {
setTransformMode(null);
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
const keyCombination = detectModifierKeys(e); const keyCombination = detectModifierKeys(e);
if (!selectedPoint) return; if (!selectedEventSphere) return;
if (keyCombination === "G") { if (keyCombination === "G") {
setTransformMode((prev) => (prev === "translate" ? null : "translate")); setTransformMode((prev) => (prev === "translate" ? null : "translate"));
} }
@@ -28,13 +45,13 @@ function PointsCreator() {
window.addEventListener("keydown", handleKeyDown); window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown);
}, [selectedPoint]); }, [selectedEventSphere]);
const updatePointToState = (selectedPoint: THREE.Mesh) => { const updatePointToState = (selectedEventSphere: THREE.Mesh) => {
let point = JSON.parse(JSON.stringify(getPointByUuid(selectedPoint.userData.modelUuid, selectedPoint.userData.pointUuid))); let point = JSON.parse(JSON.stringify(getPointByUuid(selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid)));
if (point) { if (point) {
point.position = [selectedPoint.position.x, selectedPoint.position.y, selectedPoint.position.z]; point.position = [selectedEventSphere.position.x, selectedEventSphere.position.y, selectedEventSphere.position.z];
updatePoint(selectedPoint.userData.modelUuid, selectedPoint.userData.pointUuid, point) updatePoint(selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid, point)
} }
} }
@@ -49,14 +66,16 @@ function PointsCreator() {
<group key={i} position={new THREE.Vector3(...event.position)}> <group key={i} position={new THREE.Vector3(...event.position)}>
{event.points.map((point, j) => ( {event.points.map((point, j) => (
<mesh <mesh
name='Event-Sphere'
uuid={point.uuid} uuid={point.uuid}
ref={(el) => (sphereRefs.current[point.uuid] = el!)} ref={(el) => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setSelectedPoint(sphereRefs.current[point.uuid]); setSelectedEventSphere(sphereRefs.current[point.uuid]);
}} }}
onPointerMissed={() => { onPointerMissed={() => {
setSelectedPoint(null); clearSelectedEventSphere();
setTransformMode(null);
}} }}
key={`${i}-${j}`} key={`${i}-${j}`}
position={new THREE.Vector3(...point.position)} position={new THREE.Vector3(...point.position)}
@@ -72,14 +91,16 @@ function PointsCreator() {
return ( return (
<group key={i} position={new THREE.Vector3(...event.position)}> <group key={i} position={new THREE.Vector3(...event.position)}>
<mesh <mesh
name='Event-Sphere'
uuid={event.point.uuid} uuid={event.point.uuid}
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)} ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setSelectedPoint(sphereRefs.current[event.point.uuid]); setSelectedEventSphere(sphereRefs.current[event.point.uuid]);
}} }}
onPointerMissed={() => { onPointerMissed={() => {
setSelectedPoint(null); clearSelectedEventSphere();
setTransformMode(null);
}} }}
position={new THREE.Vector3(...event.point.position)} position={new THREE.Vector3(...event.point.position)}
userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }}
@@ -93,14 +114,16 @@ function PointsCreator() {
return ( return (
<group key={i} position={new THREE.Vector3(...event.position)}> <group key={i} position={new THREE.Vector3(...event.position)}>
<mesh <mesh
name='Event-Sphere'
uuid={event.point.uuid} uuid={event.point.uuid}
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)} ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setSelectedPoint(sphereRefs.current[event.point.uuid]); setSelectedEventSphere(sphereRefs.current[event.point.uuid]);
}} }}
onPointerMissed={() => { onPointerMissed={() => {
setSelectedPoint(null); clearSelectedEventSphere();
setTransformMode(null);
}} }}
position={new THREE.Vector3(...event.point.position)} position={new THREE.Vector3(...event.point.position)}
userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }}
@@ -114,14 +137,16 @@ function PointsCreator() {
return ( return (
<group key={i} position={new THREE.Vector3(...event.position)}> <group key={i} position={new THREE.Vector3(...event.position)}>
<mesh <mesh
name='Event-Sphere'
uuid={event.point.uuid} uuid={event.point.uuid}
ref={(el) => (sphereRefs.current[event.point.uuid] = el!)} ref={(el) => (sphereRefs.current[event.point.uuid] = el!)}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setSelectedPoint(sphereRefs.current[event.point.uuid]); setSelectedEventSphere(sphereRefs.current[event.point.uuid]);
}} }}
onPointerMissed={() => { onPointerMissed={() => {
setSelectedPoint(null); clearSelectedEventSphere();
setTransformMode(null);
}} }}
position={new THREE.Vector3(...event.point.position)} position={new THREE.Vector3(...event.point.position)}
userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }} userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }}
@@ -136,8 +161,8 @@ function PointsCreator() {
} }
})} })}
</group> </group>
{(selectedPoint && transformMode) && {(selectedEventSphere && transformMode) &&
<TransformControls ref={transformRef} object={selectedPoint} mode={transformMode} onMouseUp={(e) => { updatePointToState(selectedPoint) }} /> <TransformControls ref={transformRef} object={selectedEventSphere} mode={transformMode} onMouseUp={(e) => { updatePointToState(selectedEventSphere) }} />
} }
</> </>
} }

View File

@@ -1,5 +1,5 @@
import * as THREE from 'three'; import * as THREE from 'three';
import { Group } from '../../../../types/world/worldTypes'; import { Group } from '../../../../../types/world/worldTypes';
function PointsCalculator( function PointsCalculator(
type: string, type: string,

View File

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

View File

@@ -0,0 +1,14 @@
import React from 'react'
import MachineInstance from './machineInstance/machineInstance'
function MachineInstances() {
return (
<>
<MachineInstance />
</>
)
}
export default MachineInstances

View File

@@ -0,0 +1,14 @@
import React from 'react'
import MachineInstances from './instances/machineInstances'
function Machine() {
return (
<>
<MachineInstances />
</>
)
}
export default Machine

View File

@@ -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

View File

@@ -0,0 +1,43 @@
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) {
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 (
<>
<AddOrRemoveEventsInProducts />
</>
)
}
export default Products

View File

@@ -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<THREE.CatmullRomCurve3 | null>(null);
const initialStartPositionRef = useRef<THREE.Vector3 | null>(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 ( return (
<></> <></>
) )

View File

@@ -1,14 +1,179 @@
import React from 'react' import React, { useEffect, useRef, useState } from 'react'
import IKInstance from '../ikInstance/ikInstance'; import IKInstance from '../ikInstance/ikInstance';
import RoboticArmAnimator from '../animator/roboticArmAnimator'; 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<any>(null);
const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
const { floorItems } = useFloorItems();
const groupRef = useRef<any>(null);
const [processes, setProcesses] = useState<Process[]>([]);
const [armBotCurvePoints, setArmBotCurvePoints] = useState({ start: [], end: [] })
const restPosition = new THREE.Vector3(0, 2, 1.6);
let armBotCurveRef = useRef<THREE.CatmullRomCurve3 | null>(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 ( return (
<> <>
<IKInstance /> <IKInstance modelUrl={armModel} setIkSolver={setIkSolver} ikSolver={ikSolver} robot={robot} groupRef={groupRef} processes={processes}
setArmBotCurvePoints={setArmBotCurvePoints} />
<RoboticArmAnimator /> <RoboticArmAnimator armUuid={robot?.modelUuid} HandleCallback={HandleCallback}
currentPhase={currentPhase} targetBone={targetBone} ikSolver={ikSolver} robot={robot}
logStatus={logStatus} groupRef={groupRef} processes={processes} armBotCurveRef={armBotCurveRef} path={path} />
</> </>
) )

View File

@@ -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<THREE.Group>;
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 ( return (
<></> <>
<group ref={groupRef} position={robot.position}>
<primitive
uuid={"ArmBot-X200"}
object={cloned}
scale={[1, 1, 1]}
name={`arm-bot11`}
/>
</group>
</>
) )
} }

View File

@@ -1,14 +0,0 @@
import React from 'react'
import IKInstance from './ikInstance/ikInstance';
function IkInstances() {
return (
<>
<IKInstance />
</>
)
}
export default IkInstances;

View File

@@ -1,11 +1,15 @@
import React from 'react' import React from 'react'
import RoboticArmInstance from './armInstance/roboticArmInstance'; import RoboticArmInstance from './armInstance/roboticArmInstance';
import { useArmBotStore } from '../../../../store/simulation/useArmBotStore';
function RoboticArmInstances() { function RoboticArmInstances() {
const { armBots } = useArmBotStore();
return ( return (
<> <>
{armBots?.map((robot: ArmBotStatus) => (
<RoboticArmInstance /> <RoboticArmInstance key={robot.modelUuid} robot={robot} />
))}
</> </>
) )

View File

@@ -1,15 +1,166 @@
import React from 'react' import { useEffect } from "react";
import RoboticArmInstances from './instances/roboticArmInstances'; import RoboticArmInstances from "./instances/roboticArmInstances";
import IkInstances from './instances/ikInstances'; import { useArmBotStore } from "../../../store/simulation/useArmBotStore";
import { useFloorItems } from "../../../store/store";
function RoboticArm() { 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 ( return (
<> <>
<RoboticArmInstances /> <RoboticArmInstances />
</> </>
) );
} }
export default RoboticArm; export default RoboticArm;

View File

@@ -5,8 +5,16 @@ import Vehicles from './vehicle/vehicles';
import Points from './events/points/points'; import Points from './events/points/points';
import Conveyor from './conveyor/conveyor'; import Conveyor from './conveyor/conveyor';
import RoboticArm from './roboticArm/roboticArm'; import RoboticArm from './roboticArm/roboticArm';
import Materials from './materials/materials';
import Machine from './machine/machine';
import StorageUnit from './storageUnit/storageUnit';
import Simulator from './simulator/simulator';
import Products from './products/products';
import Trigger from './triggers/trigger';
import useModuleStore from '../../store/useModuleStore';
function Simulation() { function Simulation() {
const { activeModule } = useModuleStore();
const { events } = useEventsStore(); const { events } = useEventsStore();
const { products } = useProductStore(); const { products } = useProductStore();
@@ -19,18 +27,38 @@ function Simulation() {
}, [products]) }, [products])
return ( return (
<>
{activeModule === 'simulation' &&
<> <>
<Points /> <Points />
<Products />
<Materials />
<Trigger />
<Conveyor />
<Vehicles /> <Vehicles />
<RoboticArm /> <RoboticArm />
<Conveyor /> <Machine />
<StorageUnit />
<Simulator />
</> </>
)
} }
export default Simulation </>
);
}
export default Simulation;

View File

@@ -0,0 +1,18 @@
import { useEffect } from 'react'
import { useProductStore } from '../../../store/simulation/useProductStore'
function Simulator() {
const { products } = useProductStore();
useEffect(() => {
// console.log('products: ', products);
}, [products])
return (
<>
</>
)
}
export default Simulator

View File

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

View File

@@ -0,0 +1,14 @@
import React from 'react'
import StorageUnitInstance from './storageUnitInstance/storageUnitInstance'
function StorageUnitInstances() {
return (
<>
<StorageUnitInstance />
</>
)
}
export default StorageUnitInstances

View File

@@ -0,0 +1,14 @@
import React from 'react'
import StorageUnitInstances from './instances/storageUnitInstances'
function StorageUnit() {
return (
<>
<StorageUnitInstances />
</>
)
}
export default StorageUnit

View File

@@ -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

View File

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

View File

@@ -1,62 +1,172 @@
import { useEffect, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useFrame, useThree } from '@react-three/fiber'; 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 { interface VehicleAnimatorProps {
path: [number, number, number][]; path: [number, number, number][];
handleCallBack: () => void; handleCallBack: () => void;
currentPhase: string; currentPhase: string;
agvUuid: number agvUuid: number;
agvDetail: any;
} }
function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail }: VehicleAnimatorProps) {
function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid }: VehicleAnimatorProps) { const { decrementVehicleLoad, vehicles } = useVehicleStore();
const [progress, setProgress] = useState<number>(0) const { isPaused } = usePauseButtonStore();
const { speed } = useAnimationPlaySpeed();
const { isReset } = useResetButtonStore();
const [restRotation, setRestingRotation] = useState<boolean>(true);
const [progress, setProgress] = useState<number>(0);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
const { scene } = useThree(); const { scene } = useThree();
const progressRef = useRef<number>(0);
const movingForward = useRef<boolean>(true);
const completedRef = useRef<boolean>(false);
let startTime: number;
let pausedTime: number;
let fixedInterval: number;
useEffect(() => { useEffect(() => {
if (currentPhase === 'stationed-pickup' && path.length > 0) { if (currentPhase === 'stationed-pickup' && path.length > 0) {
setCurrentPath(path); setCurrentPath(path);
} else if (currentPhase === 'pickup-drop' && path.length > 0) {
setCurrentPath(path);
} else if (currentPhase === 'drop-pickup' && path.length > 0) {
setCurrentPath(path);
} }
}, [currentPhase, path]);
}, [currentPhase, path]) useEffect(() => {
setProgress(0);
completedRef.current = false;
}, [currentPath]);
useFrame((_, delta) => { useFrame((_, delta) => {
if (!path || path.length < 2) return; const object = scene.getObjectByProperty('uuid', agvUuid);
if (!object || currentPath.length < 2) return;
if (isPaused) return;
const object = scene.getObjectByProperty("uuid", agvUuid) let totalDistance = 0;
if (!object) return; const distances = [];
setProgress(prev => { for (let i = 0; i < currentPath.length - 1; i++) {
const next = prev + delta * 0.1; // speed const start = new THREE.Vector3(...currentPath[i]);
return next >= 1 ? 1 : next; const end = new THREE.Vector3(...currentPath[i + 1]);
}); const segmentDistance = start.distanceTo(end);
distances.push(segmentDistance);
const totalSegments = path.length - 1; totalDistance += segmentDistance;
const segmentIndex = Math.floor(progress * totalSegments);
const t = progress * totalSegments - segmentIndex;
const start = path[segmentIndex];
const end = path[segmentIndex + 1] || start;
// Directly set position without creating a new Vector3
object.position.x = start[0] + (end[0] - start[0]) * t;
object.position.y = start[1] + (end[1] - start[1]) * t;
object.position.z = start[2] + (end[2] - start[2]) * t;
});
// useFrame(() => {
// if (currentPath.length === 0) return;
// const object = scene.getObjectByProperty("uuid", agvUuid);
// if (!object) return;
// })
return (
<>
</>
)
} }
export default VehicleAnimator 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;
}
}
if (progressRef.current >= totalDistance) {
setRestingRotation(true);
progressRef.current = 0;
movingForward.current = !movingForward.current;
setCurrentPath([]);
handleCallBack();
if (currentPhase === 'pickup-drop') {
requestAnimationFrame(firstFrame);
}
}
});
function firstFrame() {
const unLoadDuration = agvDetail.point.action.unLoadDuration;
const droppedMaterial = agvDetail.currentLoad;
fixedInterval = (unLoadDuration / droppedMaterial) * 1000;
startTime = performance.now();
step(droppedMaterial);
}
function step(droppedMaterial: number) {
const elapsedTime = performance.now() - startTime;
if (elapsedTime >= fixedInterval) {
console.log('fixedInterval: ', fixedInterval);
console.log('elapsedTime: ', elapsedTime);
let droppedMat = droppedMaterial - 1;
decrementVehicleLoad(agvDetail.modelUuid, 1);
if (droppedMat === 0) return;
startTime = performance.now();
requestAnimationFrame(() => step(droppedMat));
} else {
requestAnimationFrame(() => step(droppedMaterial));
}
}
return (
<>
{currentPath.length > 0 && (
<>
<Line points={currentPath} color="blue" lineWidth={3} />
{currentPath.map((point, index) => (
<mesh key={index} position={point}>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="red" />
</mesh>
))}
</>
)}
</>
);
}
export default VehicleAnimator;

View File

@@ -1,66 +1,123 @@
import React, { useCallback, useEffect, useState } from 'react' import React, { useCallback, useEffect, useState } from 'react';
import VehicleAnimator from '../animator/vehicleAnimator' import VehicleAnimator from '../animator/vehicleAnimator';
import * as THREE from "three"; import * as THREE from 'three';
import { NavMeshQuery } from '@recast-navigation/core'; import { NavMeshQuery } from '@recast-navigation/core';
import { useNavMesh } from '../../../../../store/store'; import { useNavMesh } from '../../../../../store/store';
import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore'; import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
function VehicleInstance({ agvDetails }: any) { function VehicleInstance({ agvDetail }: any) {
const { navMesh } = useNavMesh(); const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { setVehicleActive, setVehicleState } = useVehicleStore(); const { vehicles, setVehicleActive, setVehicleState, incrementVehicleLoad } = useVehicleStore();
const [currentPhase, setCurrentPhase] = useState<(string)>("stationed"); const [currentPhase, setCurrentPhase] = useState<string>('stationed');
const [path, setPath] = useState<[number, number, number][]>([]); const [path, setPath] = useState<[number, number, number][]>([]);
const computePath = useCallback((start: any, end: any) => { const computePath = useCallback(
(start: any, end: any) => {
try { try {
const navMeshQuery = new NavMeshQuery(navMesh); const navMeshQuery = new NavMeshQuery(navMesh);
const { path: segmentPath } = navMeshQuery.computePath(start, end); const { path: segmentPath } = navMeshQuery.computePath(start, end);
return ( return (
segmentPath?.map( segmentPath?.map(({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]) || []
({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]
) || []
); );
} catch { } catch {
return []; return [];
} }
}, [navMesh]); },
[navMesh]
);
function vehicleStatus(modelid: string, status: string) {
// console.log(`AGV ${modelid}: ${status}`);
}
useEffect(() => { useEffect(() => {
if (isPlaying) { if (isPlaying) {
if (!agvDetails.isActive && agvDetails.state == "idle" && currentPhase == "stationed") { if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'stationed') {
const toPickupPath = computePath(new THREE.Vector3(agvDetails.position[0], agvDetails.position[1], agvDetails.position[2]), agvDetails.point.action.pickUpPoint); const toPickupPath = computePath(
setPath(toPickupPath) new THREE.Vector3(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]),
setVehicleActive(agvDetails.modelUuid, true) agvDetail.point.action.pickUpPoint
setVehicleState(agvDetails.modelUuid, "running") );
setCurrentPhase("stationed-pickup") 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');
} }
} }
}, [agvDetails, currentPhase, path, isPlaying]) }, [vehicles, currentPhase, path, isPlaying]);
function handleCallBack() { function handleCallBack() {
if (currentPhase === "stationed-pickup") { if (currentPhase === 'stationed-pickup') {
setVehicleActive(agvDetails.modelUuid, false) setVehicleActive(agvDetail.modelUuid, false);
setVehicleState(agvDetails.modelUuid, "idle") setVehicleState(agvDetail.modelUuid, 'idle');
setCurrentPhase("picking") setCurrentPhase('picking');
setPath([]) 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 (
<> <>
<VehicleAnimator
<VehicleAnimator path={path} handleCallBack={handleCallBack} currentPhase={currentPhase} agvUuid={agvDetails?.modelUuid} /> path={path}
handleCallBack={handleCallBack}
currentPhase={currentPhase}
agvUuid={agvDetail?.modelUuid}
agvDetail={agvDetail}
/>
</> </>
) );
} }
export default VehicleInstance export default VehicleInstance;

View File

@@ -1,15 +1,14 @@
import React from 'react' import React from 'react'
import VehicleInstance from './instance/vehicleInstance' import VehicleInstance from './instance/vehicleInstance'
import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'
function VehicleInstances() { function VehicleInstances() {
const { vehicles } = useVehicleStore(); const { vehicles } = useVehicleStore();
return ( return (
<> <>
{vehicles.map((val: any, i: any) => {vehicles.map((val: any, i: any) =>
<VehicleInstance agvDetails={val} key={i} /> <VehicleInstance agvDetail={val} key={i} />
)} )}
</> </>

View File

@@ -17,7 +17,7 @@ export default function PolygonGenerator({
useEffect(() => { useEffect(() => {
let allLines = arrayLinesToObject(lines.current); let allLines = arrayLinesToObject(lines.current);
const wallLines = allLines?.filter((line) => line?.type === "WallLine"); 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 const wallPoints = wallLines
.map((pair) => pair?.line.map((vals) => vals.position)) .map((pair) => pair?.line.map((vals) => vals.position))
@@ -39,9 +39,10 @@ export default function PolygonGenerator({
); );
const polygons = turf.polygonize(turf.featureCollection(lineFeatures)); const polygons = turf.polygonize(turf.featureCollection(lineFeatures));
renderWallGeometry(wallPoints); renderWallGeometry(wallPoints);
if (polygons.features.length > 1) { if (polygons.features.length > 0) {
polygons.features.forEach((feature) => { polygons.features.forEach((feature) => {
if (feature.geometry.type === "Polygon") { if (feature.geometry.type === "Polygon") {

View File

@@ -1,17 +1,19 @@
import React, { useEffect } from 'react' import React, { useEffect } from 'react'
import VehicleInstances from './instances/vehicleInstances'; import VehicleInstances from './instances/vehicleInstances';
import { useVehicleStore } from '../../../store/simulation/useVehicleStore'; import { useVehicleStore } from '../../../store/simulation/useVehicleStore';
import { useFloorItems } from '../../../store/store';
function Vehicles() { function Vehicles() {
const { vehicles, addVehicle } = useVehicleStore(); const { vehicles, addVehicle } = useVehicleStore();
const { floorItems } = useFloorItems();
const vehicleStatusSample: VehicleEventSchema[] = [ const vehicleStatusSample: VehicleEventSchema[] = [
{ {
modelUuid: "veh-123", modelUuid: "9356f710-4727-4b50-bdb2-9c1e747ecc74",
modelName: "Autonomous Truck A1", modelName: "AGV",
position: [10, 0, 5], position: [97.9252965204558, 0, 37.96138815638661],
rotation: [0, 0, 0], rotation: [0, 0, 0],
state: "idle", state: "idle",
type: "vehicle", type: "vehicle",
@@ -24,11 +26,10 @@ function Vehicles() {
actionUuid: "action-456", actionUuid: "action-456",
actionName: "Deliver to Zone A", actionName: "Deliver to Zone A",
actionType: "travel", actionType: "travel",
material: "crate", unLoadDuration: 10,
unLoadDuration: 15, loadCapacity: 2,
loadCapacity: 5, pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 },
pickUpPoint: { x: 5, y: 0, z: 3 }, unLoadPoint: { x: 105.71483985219794, y: 0, z: 28.66321267938962 },
unLoadPoint: { x: 20, y: 0, z: 10 },
triggers: [ triggers: [
{ {
triggerUuid: "trig-001", triggerUuid: "trig-001",
@@ -53,9 +54,51 @@ function Vehicles() {
} }
}, },
{ {
modelUuid: "veh-123", modelUuid: "b06960bb-3d2e-41f7-a646-335f389c68b4",
modelName: "Autonomous Truck A1", modelName: "AGV",
position: [10, 0, 5], 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], rotation: [0, 0, 0],
state: "idle", state: "idle",
type: "vehicle", type: "vehicle",
@@ -68,10 +111,9 @@ function Vehicles() {
actionUuid: "action-456", actionUuid: "action-456",
actionName: "Deliver to Zone A", actionName: "Deliver to Zone A",
actionType: "travel", actionType: "travel",
material: "crate",
unLoadDuration: 15, unLoadDuration: 15,
loadCapacity: 5, 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 }, unLoadPoint: { x: 20, y: 0, z: 10 },
triggers: [ triggers: [
{ {
@@ -101,19 +143,18 @@ function Vehicles() {
useEffect(() => { useEffect(() => {
addVehicle('123', vehicleStatusSample[0]); addVehicle('123', vehicleStatusSample[0]);
addVehicle('123', vehicleStatusSample[1]); // addVehicle('123', vehicleStatusSample[1]);
// addVehicle('123', vehicleStatusSample[2]);
}, []) }, [])
useEffect(() => { useEffect(() => {
// console.log('vehicles: ', vehicles); console.log('vehicles: ', vehicles);
}, [vehicles]) }, [vehicles])
return ( return (
<> <>
<VehicleInstances /> <VehicleInstances />
</> </>
) )
} }

View File

@@ -12,15 +12,12 @@ import {
useFloatingWidget, useFloatingWidget,
} from "../../store/visualization/useDroppedObjectsStore"; } from "../../store/visualization/useDroppedObjectsStore";
import { import {
useAsset3dWidget,
useSocketStore, useSocketStore,
useWidgetSubOption, useWidgetSubOption,
useZones,
} from "../../store/store"; } from "../../store/store";
import { getZone2dData } from "../../services/visulization/zone/getZoneData"; import { getZone2dData } from "../../services/visulization/zone/getZoneData";
import { generateUniqueId } from "../../functions/generateUniqueId"; import { generateUniqueId } from "../../functions/generateUniqueId";
import { determinePosition } from "./functions/determinePosition"; import { determinePosition } from "./functions/determinePosition";
import { addingFloatingWidgets } from "../../services/visulization/zone/addFloatingWidgets";
import SocketRealTimeViz from "./socket/realTimeVizSocket.dev"; import SocketRealTimeViz from "./socket/realTimeVizSocket.dev";
import RenderOverlay from "../../components/templates/Overlay"; import RenderOverlay from "../../components/templates/Overlay";
import ConfirmationPopup from "../../components/layout/confirmationPopup/ConfirmationPopup"; import ConfirmationPopup from "../../components/layout/confirmationPopup/ConfirmationPopup";
@@ -68,20 +65,15 @@ const RealTimeVisulization: React.FC = () => {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const [droppedObjects, setDroppedObjects] = useState<any[]>([]);
const [zonesData, setZonesData] = useState<FormattedZoneData>({}); const [zonesData, setZonesData] = useState<FormattedZoneData>({});
const { selectedZone, setSelectedZone } = useSelectedZoneStore(); const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { rightSelect, setRightSelect } = useRightSelected(); const { setRightSelect } = useRightSelected();
const { editWidgetOptions, setEditWidgetOptions } = const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore();
useEditWidgetOptionsStore();
const { rightClickSelected, setRightClickSelected } = useRightClickSelected(); const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false); const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
const { setFloatingWidget } = useFloatingWidget();
// const [floatingWidgets, setFloatingWidgets] = useState<Record<string, { zoneName: string; zoneId: string; objects: any[] }>>({}); const { widgetSubOption } = useWidgetSubOption();
const { floatingWidget, setFloatingWidget } = useFloatingWidget();
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
const { visualizationSocket } = useSocketStore(); const { visualizationSocket } = useSocketStore();
const { setSelectedChartId } = useWidgetStore(); const { setSelectedChartId } = useWidgetStore();
@@ -99,11 +91,10 @@ const RealTimeVisulization: React.FC = () => {
useEffect(() => { useEffect(() => {
async function GetZoneData() { async function GetZoneData() {
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") ?? "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
try { try {
const response = await getZone2dData(organization); const response = await getZone2dData(organization);
// console.log('response: ', response);
if (!Array.isArray(response)) { if (!Array.isArray(response)) {
return; return;
@@ -125,7 +116,9 @@ const RealTimeVisulization: React.FC = () => {
{} {}
); );
setZonesData(formattedData); setZonesData(formattedData);
} catch (error) {} } catch (error) {
console.log(error);
}
} }
GetZoneData(); GetZoneData();
@@ -151,12 +144,10 @@ const RealTimeVisulization: React.FC = () => {
}); });
}, [selectedZone]); }, [selectedZone]);
// useEffect(() => {}, [floatingWidgets]);
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => { const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); event.preventDefault();
try { try {
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") ?? "";
const organization = email?.split("@")[1]?.split(".")[0]; const organization = email?.split("@")[1]?.split(".")[0];
const data = event.dataTransfer.getData("text/plain"); const data = event.dataTransfer.getData("text/plain");
@@ -172,8 +163,8 @@ const RealTimeVisulization: React.FC = () => {
const relativeY = event.clientY - rect.top; const relativeY = event.clientY - rect.top;
// Widget dimensions // Widget dimensions
const widgetWidth = droppedData.width || 125; const widgetWidth = droppedData.width ?? 125;
const widgetHeight = droppedData.height || 100; const widgetHeight = droppedData.height ?? 100;
// Center the widget at cursor // Center the widget at cursor
const centerOffsetX = widgetWidth / 2; const centerOffsetX = widgetWidth / 2;
@@ -275,7 +266,7 @@ const RealTimeVisulization: React.FC = () => {
return () => { return () => {
document.removeEventListener("mousedown", handleClickOutside); document.removeEventListener("mousedown", handleClickOutside);
}; };
}, [setRightClickSelected]); }, [setRightClickSelected, setRightSelect]);
const [canvasDimensions, setCanvasDimensions] = useState({ const [canvasDimensions, setCanvasDimensions] = useState({
width: 0, width: 0,
@@ -340,6 +331,7 @@ const RealTimeVisulization: React.FC = () => {
borderRadius: borderRadius:
isPlaying || activeModule !== "visualization" ? "" : "6px", isPlaying || activeModule !== "visualization" ? "" : "6px",
}} }}
role="application"
onDrop={(event) => handleDrop(event)} onDrop={(event) => handleDrop(event)}
onDragOver={(event) => event.preventDefault()} onDragOver={(event) => event.preventDefault()}
> >
@@ -362,6 +354,10 @@ const RealTimeVisulization: React.FC = () => {
"RotateY", "RotateY",
"Delete", "Delete",
]} ]}
onClick={(e) => {
setRightSelect(e);
setEditWidgetOptions(false);
}}
/> />
)} )}

View File

@@ -3,9 +3,8 @@ import Dropped3dWidgets from './widgets/3d/Dropped3dWidget'
import ZoneCentreTarget from './zone/zoneCameraTarget' import ZoneCentreTarget from './zone/zoneCameraTarget'
import ZoneAssets from './zone/zoneAssets' import ZoneAssets from './zone/zoneAssets'
import MqttEvents from '../../services/factoryBuilder/mqtt/mqttEvents' import MqttEvents from '../../services/factoryBuilder/mqtt/mqttEvents'
import DrieHtmlTemp from './mqttTemp/drieHtmlTemp'
const Visualization = () => { const Visualization:React.FC = () => {
return ( return (
<> <>

View File

@@ -2,7 +2,11 @@ import React, { useState, FormEvent } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { LogoIconLarge } from "../components/icons/Logo"; import { LogoIconLarge } from "../components/icons/Logo";
import { EyeIcon } from "../components/icons/ExportCommonIcons"; import { EyeIcon } from "../components/icons/ExportCommonIcons";
import { useLoadingProgress, useOrganization, useUserName } from "../store/store"; import {
useLoadingProgress,
useOrganization,
useUserName,
} from "../store/store";
import { signInApi } from "../services/factoryBuilder/signInSignUp/signInApi"; import { signInApi } from "../services/factoryBuilder/signInSignUp/signInApi";
import { signUpApi } from "../services/factoryBuilder/signInSignUp/signUpApi"; import { signUpApi } from "../services/factoryBuilder/signInSignUp/signUpApi";
@@ -21,7 +25,7 @@ const UserAuth: React.FC = () => {
const handleLogin = async (e: FormEvent<HTMLFormElement>) => { const handleLogin = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault(); e.preventDefault();
const organization = (email.split("@")[1]).split(".")[0]; const organization = email.split("@")[1].split(".")[0];
try { try {
const res = await signInApi(email, password, organization); const res = await signInApi(email, password, organization);
@@ -47,7 +51,7 @@ const UserAuth: React.FC = () => {
if (email && password && userName) { if (email && password && userName) {
setError(""); setError("");
try { try {
const organization = (email.split("@")[1]).split(".")[0]; const organization = email.split("@")[1].split(".")[0];
const res = await signUpApi(userName, email, password, organization); const res = await signUpApi(userName, email, password, organization);
if (res.message === "New User created") { if (res.message === "New User created") {
@@ -63,7 +67,6 @@ const UserAuth: React.FC = () => {
}; };
return ( return (
<>
<div className="auth-container"> <div className="auth-container">
<div className="logo-icon"> <div className="logo-icon">
<LogoIconLarge /> <LogoIconLarge />
@@ -172,7 +175,6 @@ const UserAuth: React.FC = () => {
website. website.
</p> </p>
</div> </div>
</>
); );
}; };

View File

@@ -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");
}
}
};

View File

@@ -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");
}
}
};

View File

@@ -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");
}
}
};

View File

@@ -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");
}
}
};

View File

@@ -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");
}
}
};

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

@@ -17,8 +17,11 @@ interface ArmBotStore {
addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void; addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void;
removeAction: (modelUuid: string, actionUuid: string) => void; removeAction: (modelUuid: string, actionUuid: string) => void;
setArmBotActive: (modelUuid: string, isActive: boolean) => void; updateStartPoint: (modelUuid: string, actionUuid: string, startPoint: [number, number, number] | null) => void;
updateEndPoint: (modelUuid: string, actionUuid: string, endPoint: [number, number, number] | null) => void;
setArmBotActive: (modelUuid: string, isActive: boolean) => void;
setArmBotState: (modelUuid: string, newState: ArmBotStatus['state']) => void;
incrementActiveTime: (modelUuid: string, incrementBy: number) => void; incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
incrementIdleTime: (modelUuid: string, incrementBy: number) => void; incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
@@ -72,7 +75,6 @@ export const useArmBotStore = create<ArmBotStore>()(
actionUuid: action.actionUuid, actionUuid: action.actionUuid,
actionName: action.actionName, actionName: action.actionName,
}; };
armBot.isActive = true;
} }
} }
}); });
@@ -83,7 +85,6 @@ export const useArmBotStore = create<ArmBotStore>()(
const armBot = state.armBots.find(a => a.modelUuid === modelUuid); const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
if (armBot) { if (armBot) {
armBot.currentAction = undefined; armBot.currentAction = undefined;
armBot.isActive = false;
} }
}); });
}, },
@@ -106,6 +107,30 @@ export const useArmBotStore = create<ArmBotStore>()(
}); });
}, },
updateStartPoint: (modelUuid, actionUuid, startPoint) => {
set((state) => {
const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
if (armBot) {
const action = armBot.point.actions.find(a => a.actionUuid === actionUuid);
if (action) {
action.process.startPoint = startPoint;
}
}
});
},
updateEndPoint: (modelUuid, actionUuid, endPoint) => {
set((state) => {
const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
if (armBot) {
const action = armBot.point.actions.find(a => a.actionUuid === actionUuid);
if (action) {
action.process.endPoint = endPoint;
}
}
});
},
setArmBotActive: (modelUuid, isActive) => { setArmBotActive: (modelUuid, isActive) => {
set((state) => { set((state) => {
const armBot = state.armBots.find(a => a.modelUuid === modelUuid); const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
@@ -115,6 +140,15 @@ export const useArmBotStore = create<ArmBotStore>()(
}); });
}, },
setArmBotState: (modelUuid, newState) => {
set((state) => {
const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
if (armBot) {
armBot.state = newState;
}
});
},
incrementActiveTime: (modelUuid, incrementBy) => { incrementActiveTime: (modelUuid, incrementBy) => {
set((state) => { set((state) => {
const armBot = state.armBots.find(a => a.modelUuid === modelUuid); const armBot = state.armBots.find(a => a.modelUuid === modelUuid);

View File

@@ -0,0 +1,76 @@
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
type MaterialsStore = {
materials: MaterialsSchema;
addMaterial: (material: MaterialSchema) => void;
removeMaterial: (materialId: string) => void;
updateMaterial: (materialId: string, updates: Partial<MaterialSchema>) => void;
setStartTime: (materialId: string, startTime: string) => void;
setEndTime: (materialId: string, endTime: string) => void;
setCost: (materialId: string, cost: number) => void;
setWeight: (materialId: string, weight: number) => void;
getMaterialById: (materialId: string) => MaterialSchema | undefined;
};
export const useMaterialStore = create<MaterialsStore>()(
immer((set, get) => ({
materials: [],
addMaterial: (material) => {
set((state) => {
state.materials.push(material);
});
},
removeMaterial: (materialId) => {
set((state) => {
state.materials = state.materials.filter(m => m.materialId !== materialId);
});
},
updateMaterial: (materialId, updates) => {
set((state) => {
const material = state.materials.find(m => m.materialId === materialId);
if (material) {
Object.assign(material, updates);
}
});
},
setStartTime: (materialId, startTime) => {
set((state) => {
const material = state.materials.find(m => m.materialId === materialId);
if (material) material.startTime = startTime;
});
},
setEndTime: (materialId, endTime) => {
set((state) => {
const material = state.materials.find(m => m.materialId === materialId);
if (material) material.endTime = endTime;
});
},
setCost: (materialId, cost) => {
set((state) => {
const material = state.materials.find(m => m.materialId === materialId);
if (material) material.cost = cost;
});
},
setWeight: (materialId, weight) => {
set((state) => {
const material = state.materials.find(m => m.materialId === materialId);
if (material) material.weight = weight;
});
},
getMaterialById: (materialId) => {
return get().materials.find(m => m.materialId === materialId);
},
}))
);

View File

@@ -7,7 +7,7 @@ type ProductsStore = {
// Product-level actions // Product-level actions
addProduct: (productName: string, productId: string) => void; addProduct: (productName: string, productId: string) => void;
removeProduct: (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 // Event-level actions
addEvent: (productId: string, event: EventsSchema) => void; addEvent: (productId: string, event: EventsSchema) => void;
@@ -48,8 +48,18 @@ type ProductsStore = {
updates: Partial<TriggerSchema> updates: Partial<TriggerSchema>
) => void; ) => void;
// Renaming functions
renameProduct: (productId: string, newName: string) => void;
renameAction: (actionUuid: string, newName: string) => void;
renameTrigger: (triggerUuid: string, newName: string) => void;
// Helper functions // 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<ProductsStore>()( export const useProductStore = create<ProductsStore>()(
@@ -62,7 +72,7 @@ export const useProductStore = create<ProductsStore>()(
const newProduct = { const newProduct = {
productName, productName,
productId: productId, productId: productId,
eventsData: [] eventDatas: []
}; };
state.products.push(newProduct); state.products.push(newProduct);
}); });
@@ -88,7 +98,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => { set((state) => {
const product = state.products.find(p => p.productId === productId); const product = state.products.find(p => p.productId === productId);
if (product) { if (product) {
product.eventsData.push(event); product.eventDatas.push(event);
} }
}); });
}, },
@@ -97,7 +107,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => { set((state) => {
const product = state.products.find(p => p.productId === productId); const product = state.products.find(p => p.productId === productId);
if (product) { if (product) {
product.eventsData = product.eventsData.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid); product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid);
} }
}); });
}, },
@@ -106,7 +116,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => { set((state) => {
const product = state.products.find(p => p.productId === productId); const product = state.products.find(p => p.productId === productId);
if (product) { 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) { if (event) {
Object.assign(event, updates); Object.assign(event, updates);
} }
@@ -119,7 +129,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => { set((state) => {
const product = state.products.find(p => p.productId === productId); const product = state.products.find(p => p.productId === productId);
if (product) { 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) { if (event && 'points' in event) {
(event as ConveyorEventSchema).points.push(point as ConveyorPointSchema); (event as ConveyorEventSchema).points.push(point as ConveyorPointSchema);
} else if (event && 'point' in event) { } else if (event && 'point' in event) {
@@ -133,7 +143,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => { set((state) => {
const product = state.products.find(p => p.productId === productId); const product = state.products.find(p => p.productId === productId);
if (product) { 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) { if (event && 'points' in event) {
(event as ConveyorEventSchema).points = (event as ConveyorEventSchema).points.filter(p => p.uuid !== pointUuid); (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) { } else if (event && 'point' in event && (event as any).point.uuid === pointUuid) {
@@ -147,7 +157,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => { set((state) => {
const product = state.products.find(p => p.productId === productId); const product = state.products.find(p => p.productId === productId);
if (product) { 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) { if (event && 'points' in event) {
const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
if (point) { if (point) {
@@ -165,7 +175,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => { set((state) => {
const product = state.products.find(p => p.productId === productId); const product = state.products.find(p => p.productId === productId);
if (product) { 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) { if (event && 'points' in event) {
const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid); const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
if (point) { if (point) {
@@ -185,7 +195,7 @@ export const useProductStore = create<ProductsStore>()(
removeAction: (actionUuid: string) => { removeAction: (actionUuid: string) => {
set((state) => { set((state) => {
for (const product of state.products) { for (const product of state.products) {
for (const event of product.eventsData) { for (const event of product.eventDatas) {
if ('points' in event) { if ('points' in event) {
// Handle ConveyorEventSchema // Handle ConveyorEventSchema
for (const point of (event as ConveyorEventSchema).points) { for (const point of (event as ConveyorEventSchema).points) {
@@ -209,7 +219,7 @@ export const useProductStore = create<ProductsStore>()(
updateAction: (actionUuid, updates) => { updateAction: (actionUuid, updates) => {
set((state) => { set((state) => {
for (const product of state.products) { for (const product of state.products) {
for (const event of product.eventsData) { for (const event of product.eventDatas) {
if ('points' in event) { if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) { for (const point of (event as ConveyorEventSchema).points) {
if (point.action && point.action.actionUuid === actionUuid) { if (point.action && point.action.actionUuid === actionUuid) {
@@ -239,7 +249,7 @@ export const useProductStore = create<ProductsStore>()(
addTrigger: (actionUuid, trigger) => { addTrigger: (actionUuid, trigger) => {
set((state) => { set((state) => {
for (const product of state.products) { for (const product of state.products) {
for (const event of product.eventsData) { for (const event of product.eventDatas) {
if ('points' in event) { if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) { for (const point of (event as ConveyorEventSchema).points) {
if (point.action && point.action.actionUuid === actionUuid) { if (point.action && point.action.actionUuid === actionUuid) {
@@ -268,7 +278,7 @@ export const useProductStore = create<ProductsStore>()(
removeTrigger: (triggerUuid) => { removeTrigger: (triggerUuid) => {
set((state) => { set((state) => {
for (const product of state.products) { for (const product of state.products) {
for (const event of product.eventsData) { for (const event of product.eventDatas) {
if ('points' in event) { if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) { for (const point of (event as ConveyorEventSchema).points) {
if (point.action && 'triggers' in point.action) { if (point.action && 'triggers' in point.action) {
@@ -295,7 +305,7 @@ export const useProductStore = create<ProductsStore>()(
updateTrigger: (triggerUuid, updates) => { updateTrigger: (triggerUuid, updates) => {
set((state) => { set((state) => {
for (const product of state.products) { for (const product of state.products) {
for (const event of product.eventsData) { for (const event of product.eventDatas) {
if ('points' in event) { if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) { for (const point of (event as ConveyorEventSchema).points) {
if (point.action && 'triggers' in point.action) { if (point.action && 'triggers' in point.action) {
@@ -331,9 +341,170 @@ export const useProductStore = create<ProductsStore>()(
}); });
}, },
// Renaming functions
renameProduct: (productId, newName) => {
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
product.productName = newName;
}
});
},
renameAction: (actionUuid, newName) => {
set((state) => {
for (const product of state.products) {
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && point.action.actionUuid === actionUuid) {
point.action.actionName = newName;
return;
}
}
} else if ('point' in event) {
const point = (event as any).point;
if ('action' in point && point.action.actionUuid === actionUuid) {
point.action.actionName = newName;
return;
} else if ('actions' in point) {
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
if (action) {
action.actionName = newName;
return;
}
}
}
}
}
});
},
renameTrigger: (triggerUuid, newName) => {
set((state) => {
for (const product of state.products) {
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && 'triggers' in point.action) {
const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid);
if (trigger) {
trigger.triggerName = newName;
return;
}
}
}
} else if ('point' in event) {
const point = (event as any).point;
if ('action' in point && 'triggers' in point.action) {
const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
if (trigger) {
trigger.triggerName = newName;
return;
}
} else if ('actions' in point) {
for (const action of point.actions) {
if ('triggers' in action) {
const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
if (trigger) {
trigger.triggerName = newName;
return;
}
}
}
}
}
}
}
});
},
// Helper functions // 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.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);
} }
})) }))
); );

View File

@@ -0,0 +1,117 @@
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
import * as THREE from 'three';
interface SelectedEventSphereState {
selectedEventSphere: THREE.Mesh | null;
setSelectedEventSphere: (mesh: THREE.Mesh | null) => void;
clearSelectedEventSphere: () => void;
}
export const useSelectedEventSphere = create<SelectedEventSphereState>()(
immer((set) => ({
selectedEventSphere: null,
setSelectedEventSphere: (mesh) => {
set((state) => {
state.selectedEventSphere = mesh;
});
},
clearSelectedEventSphere: () => {
set((state) => {
state.selectedEventSphere = null;
});
},
}))
);
interface SelectedEventDataState {
selectedEventData: { data: EventsSchema; selectedPoint: string } | undefined;
setSelectedEventData: (data: EventsSchema, selectedPoint: string) => void;
clearSelectedEventData: () => void;
}
export const useSelectedEventData = create<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 {
selectedProduct: { productId: string; productName: string };
setSelectedProduct: (productId: string, productName: string) => void;
clearSelectedProduct: () => void;
}
export const useSelectedProduct = create<SelectedProductState>()(
immer((set) => ({
selectedProduct: { productId: '', productName: '' },
setSelectedProduct: (productId, productName) => {
set((state) => {
state.selectedProduct.productId = productId;
state.selectedProduct.productName = productName;
});
},
clearSelectedProduct: () => {
set((state) => {
state.selectedProduct.productId = '';
state.selectedProduct.productName = '';
});
},
}))
);
interface SelectedActionState {
selectedAction: { actionId: string; actionName: string };
setSelectedAction: (actionId: string, actionName: string) => void;
clearSelectedAction: () => void;
}
export const useSelectedAction = create<SelectedActionState>()(
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 = '';
});
},
}))
);

View File

@@ -1,3 +1,4 @@
import { create } from 'zustand'; import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer'; import { immer } from 'zustand/middleware/immer';
@@ -88,7 +89,7 @@ export const useVehicleStore = create<VehiclesStore>()(
set((state) => { set((state) => {
const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid); const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid);
if (vehicle) { if (vehicle) {
vehicle.currentLoad = decrementBy; vehicle.currentLoad -= decrementBy;
} }
}); });
}, },

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

@@ -1,6 +0,0 @@
// center a element
%centered {
display: flex;
justify-content: center;
align-items: center;
}

View File

@@ -1,123 +1,132 @@
/* ========================================================================
Global SCSS Variables
========================================================================
This file contains the global variables used across the project for
colors, typography, spacing, shadows, and other design tokens.
======================================================================== */
@use "functions"; @use "functions";
// ========================================================================
// Font Imports
// ========================================================================
@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Josefin+Sans:ital,wght@0,100..700;1,100..700&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Josefin+Sans:ital,wght@0,100..700;1,100..700&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap");
// ======================================================================== // new variables
// Colors
// ========================================================================
// Text colors // text colors
$text-color: #2b3344; // Primary text color // ---------- light mode ----------
$text-disabled: #b7b7c6; // Disabled text color $text-color: #2b3344;
$input-text-color: #595965; // Input field text color $text-disabled: #b7b7c6;
$input-text-color: #595965;
$highlight-text-color: #6f42c1;
$text-color-dark: #f3f3fd; // Primary text color for dark mode // ---------- dark mode ----------
$text-disabled-dark: #6f6f7a; // Disabled text color for dark mode $text-color-dark: #f3f3fd;
$input-text-color-dark: #b5b5c8; // Input field text color for dark mode $text-disabled-dark: #6f6f7a;
$input-text-color-dark: #b5b5c8;
$highlight-text-color-dark: #B392F0;
// Accent colors // background colors
$accent-color: #6f42c1; // Primary accent color // ---------- light mode ----------
$accent-color-dark: #c4abf1; // Primary accent color for dark mode $background-color: linear-gradient(-45deg, #FCFDFDCC 0%, #FCFDFD99 100%);
$highlight-accent-color: #e0dfff; // Highlighted accent for light mode $background-color-secondary: #FCFDFD4D;
$highlight-accent-color-dark: #403e6a; // Highlighted accent for dark mode $background-color-accent: #6f42c1;
$background-color-button: #6f42c1;
$background-color-drop-down: #6F42C14D;
$background-color-input: #FFFFFF4D;
$background-color-input-focus: #F2F2F7;
$background-color-drop-down-gradient: linear-gradient(-45deg, #75649366 0%, #40257266 100%);
$background-color-selected: #E0DFFF;
$background-radial-gray-gradient: radial-gradient(circle, #bfe0f8 0%, #e9ebff 46%, #e2acff 100%);
// Background colors // ---------- dark mode ----------
$background-color: #fcfdfd; // Main background color $background-color-dark: linear-gradient(-45deg, #333333B3 0%, #2D2437B3 100%);
$background-color-dark: #19191d; // Main background color for dark mode $background-color-secondary-dark: #19191D99;
$background-color-secondary: #e1e0ff80; // Secondary background color $background-color-accent-dark: #6f42c1;
$background-color-secondary-dark: #39394f99; // Secondary background color for dark mode $background-color-button-dark: #6f42c1;
$background-color-gray: #f3f3f3; // Main background color $background-color-drop-down-dark: #50505080;
$background-color-gray-dark: #232323; // Main background color for dark mode $background-color-input-dark: #FFFFFF33;
$background-color-input-focus-dark: #333333;
$background-color-drop-down-gradient-dark: linear-gradient(-45deg, #8973B166 0%, #53427366 100%);
$background-color-selected-dark: #403E66;
$background-radial-gray-gradient-dark: radial-gradient(circle, #31373b 0%, #48494b 46%, #52415c 100%);
// Border colors // border colors
$border-color: #e0dfff; // Default border color // ---------- light mode ----------
$border-color-dark: #403e6a; // Border color for dark mode $border-color: #E0DFFF;
$border-color-accent: #6F42C1;
// Shadow color // ---------- dark mode ----------
$shadow-color: #3c3c431a; // Shadow base color for light and dark mode $border-color-dark: #564B69;
$shadow-color-dark: #8f8f8f1a; // Shadow base color for light and dark mode $border-color-accent-dark: #6F42C1;
// Gradients // highlight colors
$acent-gradient-dark: linear-gradient( // ---------- light mode ----------
90deg, $highlight-accent-color: #E0DFFF;
#b392f0 0%, $highlight-secondary-color: #6F42C1;
#a676ff 100%
); // Dark mode accent gradient // ---------- dark mode ----------
$acent-gradient: linear-gradient( $highlight-accent-color-dark: #403E6A;
90deg, $highlight-secondary-color-dark: #C4ABF1;
#6f42c1 0%,
#925df3 100% // colors
); // Light mode accent gradient $color1: #A392CD;
$color2: #7b4cd3;
$color3: #B186FF;
$color4: #8752E8;
$color5: #C7A8FF;
// old variables
$accent-color: #6f42c1;
$accent-color-dark: #c4abf1;
$highlight-accent-color: #e0dfff;
$highlight-accent-color-dark: #403e6a;
$background-color: #fcfdfd;
$background-color-dark: #19191d;
$background-color-secondary: #e1e0ff80;
$background-color-secondary-dark: #39394f99;
$background-color-gray: #f3f3f3;
$background-color-gray-dark: #232323;
$border-color: #e0dfff;
$border-color-dark: #403e6a;
$shadow-color: #3c3c431a;
$shadow-color-dark: #8f8f8f1a;
$acent-gradient-dark: linear-gradient(90deg, #b392f0 0%, #a676ff 100%);
$acent-gradient: linear-gradient(90deg, #6f42c1 0%, #925df3 100%);
$faint-gradient: radial-gradient(circle, #bfe0f8 0%, #e9ebff 46%, #e2acff 100%); $faint-gradient: radial-gradient(circle, #bfe0f8 0%, #e9ebff 46%, #e2acff 100%);
$faint-gradient-dark: radial-gradient(circle, #31373b 0%, #48494b 46%, #52415c 100%); $faint-gradient-dark: radial-gradient(circle, #31373b 0%, #48494b 46%, #52415c 100%);
// ======================================================================== $font-inter: "Inter", sans-serif;
// Typography $font-josefin-sans: "Josefin Sans", sans-serif;
// ======================================================================== $font-poppins: "Poppins", sans-serif;
$font-roboto: "Roboto", sans-serif;
// Font Family Variables $tiny: 0.625rem;
$font-inter: "Inter", sans-serif; // Inter font $small: 0.75rem;
$font-josefin-sans: "Josefin Sans", sans-serif; // Josefin Sans font $regular: 0.8rem;
$font-poppins: "Poppins", sans-serif; // Poppins font $large: 1rem;
$font-roboto: "Roboto", sans-serif; // Roboto font $xlarge: 1.125rem;
$xxlarge: 1.5rem;
$xxxlarge: 2rem;
// Font sizes (converted to rem using a utility function) $thin-weight: 300;
$tiny: 0.625rem; // Extra small text (10px) $regular-weight: 400;
$small: 0.75rem; // Small text (12px) $medium-weight: 500;
$regular: 0.8rem; // Default text size (14px) $bold-weight: 600;
$large: 1rem; // Large text size (16px)
$xlarge: 1.125rem; // Extra large text size (18px)
$xxlarge: 1.5rem; // Double extra large text size (24px)
$xxxlarge: 2rem; // Triple extra large text size (32px)
// Font weights $z-index-drei-html: 1;
$thin-weight: 300; // Regular font weight $z-index-default: 1;
$regular-weight: 400; // Regular font weight $z-index-marketplace: 2;
$medium-weight: 500; // Medium font weight $z-index-tools: 3;
$bold-weight: 600; // Bold font weight $z-index-negative: -1;
$z-index-ui-base: 10;
$z-index-ui-overlay: 20;
$z-index-ui-popup: 30;
$z-index-ui-highest: 50;
// ======================================================================== $box-shadow-light: 0px 2px 4px $shadow-color;
// Z-Index Levels $box-shadow-medium: 0px 4px 8px $shadow-color;
// ======================================================================== $box-shadow-heavy: 0px 8px 16px $shadow-color;
// Z-index variables for layering $border-radius-small: 4px;
$z-index-drei-html: 1; // For drei's Html components $border-radius-medium: 6px;
$z-index-default: 1; // For drei's Html components $border-radius-large: 12px;
$z-index-marketplace: 2; // For drei's Html components $border-radius-circle: 50%;
$z-index-tools: 3; // For drei's Html components $border-radius-extra-large: 20px;
$z-index-negative: -1; // For drei's Html components
$z-index-ui-base: 10; // Base UI elements
$z-index-ui-overlay: 20; // Overlay UI elements (e.g., modals, tooltips)
$z-index-ui-popup: 30; // Popups, dialogs, or higher-priority UI elements
$z-index-ui-highest: 50; // Highest priority elements (e.g., notifications, loading screens)
// ========================================================================
// Shadows
// ========================================================================
// Box shadow variables
$box-shadow-light: 0px 2px 4px $shadow-color; // Light shadow
$box-shadow-medium: 0px 4px 8px $shadow-color; // Medium shadow
$box-shadow-heavy: 0px 8px 16px $shadow-color; // Heavy shadow
// ========================================================================
// Border Radius
// ========================================================================
// Border radius variables
$border-radius-small: 4px; // Small rounded corners
$border-radius-medium: 6px; // Medium rounded corners
$border-radius-large: 12px; // Large rounded corners
$border-radius-circle: 50%; // Fully circular
$border-radius-extra-large: 20px; // Extra-large rounded corners

View File

@@ -0,0 +1,5 @@
section, .section{
padding: 12px;
outline: 1px solid var(--border-color);
border-radius: 16px;
}

View File

@@ -12,3 +12,10 @@ input[type="password"]::-webkit-clear-button, /* For Chrome/Safari clear button
input[type="password"]::-webkit-inner-spin-button { /* Just in case */ input[type="password"]::-webkit-inner-spin-button { /* Just in case */
display: none; display: none;
} }
button{
border: none;
outline: none;
background: none;
cursor: pointer;
}

View File

@@ -366,15 +366,66 @@
min-height: 50vh; min-height: 50vh;
padding-bottom: 12px; padding-bottom: 12px;
position: relative; position: relative;
display: flex; overflow: auto;
flex-direction: column;
.sidebar-right-content-container { .sidebar-right-content-container {
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
// flex: 1;
height: calc(100% - 36px); height: calc(100% - 36px);
position: relative; position: relative;
overflow: auto; width: 320px;
.no-event-selected {
color: #666;
padding: 1.8rem 1rem;
grid-column: 1 / -1;
.products-list {
padding-top: 1rem;
.products-list-title {
text-align: start;
color: var(--accent-color);
font-size: var(--font-size-regular);
}
ul {
li {
text-align: start;
margin: 8px 0;
padding: 2px 0;
text-decoration: none;
&::marker {
content: "";
}
button {
width: fit-content;
position: relative;
transition: all 0.2s ease;
@include flex-center;
gap: 4px;
&:before {
content: "";
position: absolute;
left: 0;
bottom: -4px;
background: var(--accent-color);
height: 1px;
width: 0%;
transition: all 0.3s ease;
}
}
&:hover {
button {
path {
stroke: var(--accent-color);
stroke-width: 1.5px;
}
color: var(--accent-color);
&:before {
width: 100%;
}
}
}
}
}
}
}
} }
} }
@@ -675,10 +726,14 @@
color: var(--primary-color); color: var(--primary-color);
border-radius: #{$border-radius-small}; border-radius: #{$border-radius-small};
cursor: pointer; cursor: pointer;
outline: none;
border: none;
path { path {
stroke: var(--primary-color); stroke: var(--primary-color);
} }
&:disabled {
background-color: var(--text-disabled);
}
} }
} }
@@ -747,14 +802,15 @@
width: 100%; width: 100%;
margin: 2px 0; margin: 2px 0;
border-radius: #{$border-radius-small}; border-radius: #{$border-radius-small};
}
.value { .value {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
min-width: 80%; min-width: 80%;
gap: 6px; gap: 6px;
.input-value {
text-align: start;
}
input { input {
width: fit-content; width: fit-content;
@@ -762,6 +818,7 @@
accent-color: var(--accent-color); accent-color: var(--accent-color);
} }
} }
}
.active { .active {
background: var(--highlight-accent-color); background: var(--highlight-accent-color);
@@ -797,6 +854,7 @@
@include flex-center; @include flex-center;
padding: 4px; padding: 4px;
cursor: grab; cursor: grab;
width: 100%;
&:active { &:active {
cursor: grabbing; cursor: grabbing;

View File

@@ -1,12 +1,12 @@
// abstracts // abstracts
@use 'abstracts/variables'; @use 'abstracts/variables';
@use 'abstracts/mixins'; @use 'abstracts/mixins';
@use 'abstracts/placeholders';
@use 'abstracts/functions'; @use 'abstracts/functions';
// base // base
@use 'base/reset'; @use 'base/reset';
@use 'base/typography'; @use 'base/typography';
@use 'base/global';
@use 'base/base'; @use 'base/base';
// components // components

View File

@@ -776,13 +776,13 @@
border-radius: 6px; border-radius: 6px;
overflow: hidden; overflow: hidden;
padding: 4px; padding: 4px;
min-width: 150px; min-width: 150px;
.option { .option {
padding: 4px 10px; padding: 4px 10px;
border-radius: #{$border-radius-small}; border-radius: #{$border-radius-small};
color: var(--text-color); color: var(--text-color);
text-wrap: nowrap;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
@@ -794,8 +794,8 @@
color: #f65648; color: #f65648;
&:hover { &:hover {
background-color: #f65648; background-color: #f657484d;
color: white; color: #f65648;
} }
} }
} }

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";
@@ -42,7 +42,6 @@ interface VehiclePointSchema {
actionUuid: string; actionUuid: string;
actionName: string; actionName: string;
actionType: "travel"; actionType: "travel";
material: string | null;
unLoadDuration: number; unLoadDuration: number;
loadCapacity: number; loadCapacity: number;
pickUpPoint: { x: number; y: number, z: number } | null; pickUpPoint: { x: number; y: number, z: number } | null;
@@ -119,12 +118,14 @@ 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 = {
productName: string; productName: string;
productId: string; productId: string;
eventsData: EventsSchema[]; eventDatas: EventsSchema[];
}[] }[]
@@ -133,6 +134,7 @@ interface ConveyorStatus extends ConveyorEventSchema {
isActive: boolean; isActive: boolean;
idleTime: number; idleTime: number;
activeTime: number; activeTime: number;
} }
interface MachineStatus extends MachineEventSchema { interface MachineStatus extends MachineEventSchema {
@@ -169,3 +171,16 @@ interface StorageUnitStatus extends StorageEventSchema {
activeTime: number; activeTime: number;
currentLoad: number; currentLoad: number;
} }
interface MaterialSchema {
materialId: string;
materialName: string;
materialType: string;
isActive: boolean;
startTime?: string;
endTime?: string;
cost?: number;
weight?: number;
}
type MaterialsSchema = MaterialSchema[];