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 is contained in:
Vishnu 2025-04-28 13:22:10 +00:00
commit ad833cc8e3
89 changed files with 4416 additions and 1284 deletions

View File

@ -42,44 +42,46 @@ export function FlipXAxisIcon() {
}
export function FlipYAxisIcon() {
<svg
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.0568 11H2.94321C2.09213 11 1.6666 11 1.52703 10.7288C1.38746 10.4576 1.6348 10.1113 2.12947 9.41877L2.6942 8.62814C2.91415 8.32021 3.02413 8.16625 3.18565 8.08313C3.34718 8 3.53639 8 3.9148 8H8.08525C8.46365 8 8.65285 8 8.8144 8.08313C8.9759 8.16625 9.0859 8.32021 9.30585 8.62814L9.87055 9.41876C10.3653 10.1113 10.6126 10.4576 10.473 10.7288C10.3335 11 9.9079 11 9.0568 11Z"
fill="var(--text-color)"
/>
<path
d="M2.99935 3.15388L2.99936 3.1539C3.11318 3.31326 3.18452 3.41252 3.2461 3.48409C3.30271 3.54989 3.33407 3.57152 3.3572 3.58344L2.99935 3.15388ZM2.99935 3.15388L2.43462 2.36329C2.43462 2.36329 2.43462 2.36328 2.43462 2.36328C2.17987 2.00663 2.01464 1.7736 1.92406 1.59704C1.88093 1.51299 1.86801 1.46687 1.86465 1.44495C1.88444 1.43495 1.92948 1.41866 2.02293 1.4049C2.21925 1.37599 2.50491 1.375 2.94321 1.375L9.0568 1.375C9.49511 1.375 9.78078 1.37599 9.9771 1.4049C10.0705 1.41866 10.1156 1.43494 10.1354 1.44495C10.132 1.46687 10.1191 1.51299 10.076 1.59705C9.98539 1.7736 9.82016 2.00663 9.5654 2.36328L9.5654 2.36329L9.0007 3.15389L9.00069 3.15389M2.99935 3.15388L9.00069 3.15389M9.00069 3.15389C8.88688 3.31325 8.81553 3.41252 8.75395 3.48409C8.69737 3.54985 8.666 3.5715 8.64287 3.58343C8.61972 3.59534 8.5839 3.60831 8.49735 3.61614C8.40331 3.62465 8.28108 3.625 8.08525 3.625L3.9148 3.625C3.71896 3.625 3.59672 3.62465 3.50269 3.61614M9.00069 3.15389L3.50269 3.61614M3.50269 3.61614C3.41623 3.60832 3.38041 3.59537 3.35729 3.58348L3.50269 3.61614ZM1.86435 1.42845C1.86468 1.42867 1.86476 1.43108 1.86363 1.43501C1.86345 1.4302 1.86402 1.42823 1.86435 1.42845ZM1.85597 1.4499C1.85343 1.45311 1.85142 1.45444 1.85105 1.4543C1.85068 1.45416 1.85195 1.45255 1.85597 1.4499ZM10.149 1.45429C10.1486 1.45443 10.1466 1.4531 10.1441 1.44989C10.1481 1.45254 10.1493 1.45415 10.149 1.45429ZM10.1364 1.435C10.1353 1.43107 10.1353 1.42867 10.1357 1.42845C10.136 1.42823 10.1366 1.4302 10.1364 1.435Z"
stroke="var(--text-color)"
strokeWidth="0.75"
/>
<path
opacity="0.5"
d="M7 6H5"
stroke="var(--text-color)"
strokeWidth="0.75"
strokeLinecap="round"
/>
<path
opacity="0.5"
d="M3 6H1"
stroke="var(--text-color)"
strokeWidth="0.75"
strokeLinecap="round"
/>
<path
opacity="0.5"
d="M11 6H9"
stroke="var(--text-color)"
strokeWidth="0.75"
strokeLinecap="round"
/>
</svg>;
return (
<svg
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.0568 11H2.94321C2.09213 11 1.6666 11 1.52703 10.7288C1.38746 10.4576 1.6348 10.1113 2.12947 9.41877L2.6942 8.62814C2.91415 8.32021 3.02413 8.16625 3.18565 8.08313C3.34718 8 3.53639 8 3.9148 8H8.08525C8.46365 8 8.65285 8 8.8144 8.08313C8.9759 8.16625 9.0859 8.32021 9.30585 8.62814L9.87055 9.41876C10.3653 10.1113 10.6126 10.4576 10.473 10.7288C10.3335 11 9.9079 11 9.0568 11Z"
fill="var(--text-color)"
/>
<path
d="M2.99935 3.15388L2.99936 3.1539C3.11318 3.31326 3.18452 3.41252 3.2461 3.48409C3.30271 3.54989 3.33407 3.57152 3.3572 3.58344L2.99935 3.15388ZM2.99935 3.15388L2.43462 2.36329C2.43462 2.36329 2.43462 2.36328 2.43462 2.36328C2.17987 2.00663 2.01464 1.7736 1.92406 1.59704C1.88093 1.51299 1.86801 1.46687 1.86465 1.44495C1.88444 1.43495 1.92948 1.41866 2.02293 1.4049C2.21925 1.37599 2.50491 1.375 2.94321 1.375L9.0568 1.375C9.49511 1.375 9.78078 1.37599 9.9771 1.4049C10.0705 1.41866 10.1156 1.43494 10.1354 1.44495C10.132 1.46687 10.1191 1.51299 10.076 1.59705C9.98539 1.7736 9.82016 2.00663 9.5654 2.36328L9.5654 2.36329L9.0007 3.15389L9.00069 3.15389M2.99935 3.15388L9.00069 3.15389M9.00069 3.15389C8.88688 3.31325 8.81553 3.41252 8.75395 3.48409C8.69737 3.54985 8.666 3.5715 8.64287 3.58343C8.61972 3.59534 8.5839 3.60831 8.49735 3.61614C8.40331 3.62465 8.28108 3.625 8.08525 3.625L3.9148 3.625C3.71896 3.625 3.59672 3.62465 3.50269 3.61614M9.00069 3.15389L3.50269 3.61614M3.50269 3.61614C3.41623 3.60832 3.38041 3.59537 3.35729 3.58348L3.50269 3.61614ZM1.86435 1.42845C1.86468 1.42867 1.86476 1.43108 1.86363 1.43501C1.86345 1.4302 1.86402 1.42823 1.86435 1.42845ZM1.85597 1.4499C1.85343 1.45311 1.85142 1.45444 1.85105 1.4543C1.85068 1.45416 1.85195 1.45255 1.85597 1.4499ZM10.149 1.45429C10.1486 1.45443 10.1466 1.4531 10.1441 1.44989C10.1481 1.45254 10.1493 1.45415 10.149 1.45429ZM10.1364 1.435C10.1353 1.43107 10.1353 1.42867 10.1357 1.42845C10.136 1.42823 10.1366 1.4302 10.1364 1.435Z"
stroke="var(--text-color)"
strokeWidth="0.75"
/>
<path
opacity="0.5"
d="M7 6H5"
stroke="var(--text-color)"
strokeWidth="0.75"
strokeLinecap="round"
/>
<path
opacity="0.5"
d="M3 6H1"
stroke="var(--text-color)"
strokeWidth="0.75"
strokeLinecap="round"
/>
<path
opacity="0.5"
d="M11 6H9"
stroke="var(--text-color)"
strokeWidth="0.75"
strokeLinecap="round"
/>
</svg>
);
}
export function FlipZAxisIcon() {
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() {
return (
<svg

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,45 +5,14 @@ import {
GlobeIcon,
WalletIcon,
} from "../../../../icons/3dChartIcons";
import SimpleCard from "../../../../../modules//visualization/widgets/floating/cards/SimpleCard";
import SimpleCard from "../../../../../modules/visualization/widgets/floating/cards/SimpleCard";
import WarehouseThroughput from "../../../../../modules//visualization/widgets/floating/cards/WarehouseThroughput";
import ProductivityDashboard from "../../../../../modules//visualization/widgets/floating/cards/ProductivityDashboard";
import FleetEfficiency from "../../../../../modules//visualization/widgets/floating/cards/FleetEfficiency";
interface Widget {
id: string;
name: string;
}
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 (
<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 */}
<SimpleCard
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 useModuleStore, {
useSubModuleStore,
@ -14,6 +14,10 @@ import Visualization from "./visualization/Visualization";
import Analysis from "./analysis/Analysis";
import Simulations from "./simulation/Simulations";
import { useSelectedFloorItem } from "../../../store/store";
import {
useSelectedEventData,
useSelectedEventSphere,
} from "../../../store/simulation/useSimulationStore";
import GlobalProperties from "./properties/GlobalProperties";
import AsstePropertiies from "./properties/AssetProperties";
import ZoneProperties from "./properties/ZoneProperties";
@ -24,88 +28,70 @@ const SideBarRight: React.FC = () => {
const { toggleUI } = useToggleStore();
const { subModule, setSubModule } = useSubModuleStore();
const { selectedFloorItem } = useSelectedFloorItem();
const { selectedEventData } = useSelectedEventData();
const { selectedEventSphere } = useSelectedEventSphere();
// Reset activeList whenever activeModule changes
useEffect(() => {
if (activeModule !== "simulation") setSubModule("properties");
if (activeModule === "simulation") setSubModule("mechanics");
}, [activeModule]);
if (activeModule === "simulation") setSubModule("simulations");
}, [activeModule, setSubModule]);
// romove late
const dummyData = {
assetType: "store",
selectedPoint: {
name: "Point A",
uuid: "123e4567-e89b-12d3-a456-426614174000",
actions: [
{
uuid: "action-1",
name: "Action One",
},
{
uuid: "action-2",
name: "Action Two",
},
{
uuid: "action-3",
name: "Action Three",
},
],
},
selectedItem: {
item: {
uuid: "item-1",
name: "Item One",
isUsed: false,
},
},
setSelectedPoint: (value: string) => {
console.log(`Selected point updated to: ${value}`);
},
selectedActionSphere: "Sphere A",
};
useEffect(() => {
if (
activeModule !== "mechanics" &&
selectedEventData &&
selectedEventSphere
) {
setSubModule("mechanics");
} else if (!selectedEventData && !selectedEventSphere) {
if (activeModule === "simulation") {
setSubModule("simulations");
}
}
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
return (
<div className="sidebar-right-wrapper">
<Header />
{toggleUI && (
<div className="sidebar-actions-container">
{/* {activeModule === "builder" && ( */}
<div
className={`sidebar-action-list ${
subModule === "properties" ? "active" : ""
}`}
onClick={() => setSubModule("properties")}
>
<PropertiesIcon isActive={subModule === "properties"} />
</div>
{/* )} */}
{activeModule !== "simulation" && (
<button
className={`sidebar-action-list ${
subModule === "properties" ? "active" : ""
}`}
onClick={() => setSubModule("properties")}
>
<PropertiesIcon isActive={subModule === "properties"} />
</button>
)}
{activeModule === "simulation" && (
<>
<div
className={`sidebar-action-list ${
subModule === "mechanics" ? "active" : ""
}`}
onClick={() => setSubModule("mechanics")}
>
<MechanicsIcon isActive={subModule === "mechanics"} />
</div>
<div
<button
className={`sidebar-action-list ${
subModule === "simulations" ? "active" : ""
}`}
onClick={() => setSubModule("simulations")}
>
<SimulationIcon isActive={subModule === "simulations"} />
</div>
<div
</button>
<button
className={`sidebar-action-list ${
subModule === "mechanics" ? "active" : ""
}`}
onClick={() => setSubModule("mechanics")}
>
<MechanicsIcon isActive={subModule === "mechanics"} />
</button>
<button
className={`sidebar-action-list ${
subModule === "analysis" ? "active" : ""
}`}
onClick={() => setSubModule("analysis")}
>
<AnalysisIcon isActive={subModule === "analysis"} />
</div>
</button>
</>
)}
</div>
@ -141,13 +127,19 @@ const SideBarRight: React.FC = () => {
</div>
)}
{/* simulation */}
{toggleUI && activeModule === "simulation" && (
<>
{subModule === "simulations" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Simulations />
</div>
</div>
)}
{subModule === "mechanics" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<EventProperties {...dummyData} />
<EventProperties />
</div>
</div>
)}
@ -158,16 +150,8 @@ const SideBarRight: React.FC = () => {
</div>
</div>
)}
{subModule === "simulations" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<Simulations />
</div>
</div>
)}
</>
)}
{/* realtime visualization */}
{toggleUI && activeModule === "visualization" && <Visualization />}
</div>

View File

@ -1,208 +1,114 @@
import React, { useRef, useState } from "react";
import InputWithDropDown from "../../../../ui/inputs/InputWithDropDown";
import LabledDropdown from "../../../../ui/inputs/LabledDropdown";
import React, { useEffect, useState } from "react";
import {
AddIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../../icons/ExportCommonIcons";
import RenameInput from "../../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../../functions/handleResizePannel";
import { handleActionToggle } from "./functions/handleActionToggle";
import { handleDeleteAction } from "./functions/handleDeleteAction";
import DefaultAction from "./actions/DefaultAction";
import SpawnAction from "./actions/SpawnAction";
import SwapAction from "./actions/SwapAction";
import DespawnAction from "./actions/DespawnAction";
import TravelAction from "./actions/TravelAction";
import PickAndPlaceAction from "./actions/PickAndPlaceAction";
import ProcessAction from "./actions/ProcessAction";
import StorageAction from "./actions/StorageAction";
import Trigger from "./trigger/Trigger";
useSelectedEventData,
useSelectedEventSphere,
useSelectedProduct,
} from "../../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../../store/simulation/useProductStore";
import ConveyorMechanics from "./mechanics/conveyorMechanics";
import VehicleMechanics from "./mechanics/vehicleMechanics";
import RoboticArmMechanics from "./mechanics/roboticArmMechanics";
import MachineMechanics from "./mechanics/machineMechanics";
import StorageMechanics from "./mechanics/storageMechanics";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
interface EventPropertiesProps {
assetType: string;
selectedPoint: {
name: string;
uuid: string;
actions: {
uuid: string;
name: string;
}[];
const EventProperties: React.FC = () => {
const { selectedEventData } = useSelectedEventData();
const { getEventByModelUuid } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const [currentEventData, setCurrentEventData] = useState<EventsSchema | null>(
null
);
const [assetType, setAssetType] = useState<string | null>(null);
const { products } = useProductStore();
const { selectedEventSphere } = useSelectedEventSphere();
useEffect(() => {
const event = getCurrentEventData();
setCurrentEventData(event);
const type = determineAssetType(event);
setAssetType(type);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedEventData, selectedProduct]);
const getCurrentEventData = () => {
if (!selectedEventData?.data || !selectedProduct) return null;
return (
getEventByModelUuid(
selectedProduct.productId,
selectedEventData.data.modelUuid
) ?? null
);
};
selectedItem: {
item: {
uuid: string;
name: string;
} | null;
};
setSelectedPoint: (value: string) => void;
selectedActionSphere: string;
}
const EventProperties: React.FC<EventPropertiesProps> = ({
assetType,
selectedPoint,
selectedItem,
setSelectedPoint,
selectedActionSphere,
}) => {
const actionsContainerRef = useRef<HTMLDivElement>(null);
const determineAssetType = (event: EventsSchema | null) => {
if (!event) return null;
const [activeOption, setActiveOption] = useState("default");
const [dummyactiveOption, setTypeOption] = useState("default");
const getAvailableActions = () => {
if (assetType === "conveyor") {
return {
defaultOption: "default",
options: ["default", "spawn", "swap", "despawn"],
};
}
if (assetType === "vehicle") {
return {
defaultOption: "travel",
options: ["travel"],
};
}
if (assetType === "roboticArm") {
return {
defaultOption: "pickAndPlace",
options: ["pickAndPlace"],
};
}
if (assetType === "machine") {
return {
defaultOption: "process",
options: ["process"],
};
}
if (assetType === "store") {
return {
defaultOption: "store",
options: ["store", "spawn"],
};
} else {
return {
defaultOption: "default",
options: ["default"],
};
switch (event.type) {
case "transfer":
return "conveyor";
case "vehicle":
return "vehicle";
case "roboticArm":
return "roboticArm";
case "machine":
return "machine";
case "storageUnit":
return "storageUnit";
default:
return null;
}
};
return (
<div className="event-proprties-wrapper">
<div className="header">
<div className="header-value">{selectedPoint.name}</div>
</div>
<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 className="actions-list-container">
<div className="actions">
{currentEventData && (
<>
<div className="header">
<div className="header-value">Actions</div>
<div className="add-button" onClick={() => {}}>
<AddIcon /> Add
<div className="header-value">
{selectedEventData?.data.modelName}
</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>
{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>
))}
</div>
<div
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, actionsContainerRef)}
>
<ResizeHeightIcon />
</div>
</ul>
</div>
</div>
</div>
<div className="selected-actions-details">
<div className="selected-actions-header">
<RenameInput value="Action Name" />
)}
{!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 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>
);
};

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 InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
const DespawnAction: React.FC = () => {
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,13 +1,25 @@
import React from "react";
import EyeDropInput from "../../../../../ui/inputs/EyeDropInput";
const PickAndPlaceAction: React.FC = () => {
return (
<>
<EyeDropInput label="Pick Point" value="na" onChange={() => {}} />
<EyeDropInput label="Unload Point" value="na" onChange={() => {}} />
</>
);
interface PickAndPlaceActionProps {
pickPointValue: string;
pickPointOnChange: (value: string) => void;
placePointValue: string;
placePointOnChange: (value: string) => void;
}
const PickAndPlaceAction: React.FC<PickAndPlaceActionProps> = ({
pickPointValue,
pickPointOnChange,
placePointValue,
placePointOnChange,
}) => {
return (
<>
<EyeDropInput label="Pick Point" value={pickPointValue} onChange={pickPointOnChange} />
<EyeDropInput label="Unload Point" value={placePointValue} onChange={placePointOnChange} />
</>
);
};
export default PickAndPlaceAction;

View File

@ -2,23 +2,47 @@ import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import SwapAction from "./SwapAction";
const ProcessAction: React.FC = () => {
return (
<>
<InputWithDropDown
label="Process Time"
value="6"
min={0}
step={1}
max={10}
defaultValue="0"
activeOption="s"
onClick={() => {}}
onChange={(value) => console.log(value)}
/>
<SwapAction />
</>
);
interface ProcessActionProps {
value: string;
min: number;
max: number;
defaultValue: string;
onChange: (value: string) => void;
swapOptions: string[];
swapDefaultOption: string;
onSwapSelect: (value: string) => void;
}
const ProcessAction: React.FC<ProcessActionProps> = ({
value,
min,
max,
defaultValue,
onChange,
swapOptions,
swapDefaultOption,
onSwapSelect,
}) => {
return (
<>
<InputWithDropDown
label="Process Time"
value={value}
min={min}
step={1}
max={max}
defaultValue={defaultValue}
activeOption="s"
onClick={() => { }}
onChange={onChange}
/>
<SwapAction
options={swapOptions}
defaultOption={swapDefaultOption}
onSelect={onSwapSelect}
/>
</>
);
};
export default ProcessAction;

View File

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

View File

@ -1,20 +1,28 @@
import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
const StorageAction: React.FC = () => {
return (
<InputWithDropDown
label="Storage Capacity"
value=""
min={0}
step={0.1}
max={10}
defaultValue="0"
activeOption="s"
onClick={() => {}}
onChange={(value) => console.log(value)}
/>
);
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 (
<InputWithDropDown
label="Storage Capacity"
value={value}
min={min}
step={1}
max={max}
defaultValue={defaultValue}
activeOption="s"
onClick={() => { }}
onChange={onChange}
/>
);
};
export default StorageAction;

View File

@ -1,11 +1,24 @@
import React from "react";
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 (
<>
<PreviewSelectionWithUpload />
</>
<PreviewSelectionWithUpload
label="Presets"
defaultOption={defaultOption}
options={options}
onSelect={onSelect}
/>
);
};

View File

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

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 { AddIcon, RemoveIcon } from "../../../../../icons/ExportCommonIcons";
import React, { useRef, useState } from "react";
import {
AddIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../../../icons/ExportCommonIcons";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import RenameInput from "../../../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../../../functions/handleResizePannel";
const Trigger: React.FC = () => {
// 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 triggersContainerRef = useRef<HTMLDivElement>(null);
// States for dropdowns
const [triggeredModel, setTriggeredModel] = useState<string[]>([]);
@ -35,70 +43,94 @@ const Trigger: React.FC = () => {
<div className="trigger-wrapper">
<div className="header">
<div className="title">Trigger</div>
<div
<button
className="add-button"
onClick={addTrigger}
style={{ cursor: "pointer" }}
>
<AddIcon /> Add
</div>
</button>
</div>
<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
className="lists-main-container"
ref={triggersContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{triggers.map((trigger: any, index: number) => (
<div
className="remove-button"
onClick={() => removeTrigger(index)}
style={{ cursor: "pointer" }}
key={index}
className={`list-item ${
selectedTrigger === trigger ? "active" : ""
}`}
onClick={() => setSelectedTrigger(trigger)}
>
<RemoveIcon />
<button className="value" onClick={() => {}}>
<RenameInput value={trigger} onRename={() => {}} />
</button>
{triggers.length > 1 && (
<button
className="remove-button"
onClick={() => removeTrigger(index)}
>
<RemoveIcon />
</button>
)}
</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
defaultOption={activeOption}
options={["onComplete", "onStart", "onStop", "delay"]}
onSelect={(option) => setActiveOption(option)}
/>
<div className="trigger-options">
<div>
<LabledDropdown
defaultOption={triggeredModel[0] || "Select Model"}
options={["Model 1", "Model 2", "Model 3"]}
onSelect={(option) => {
const newModel = [...triggeredModel];
newModel[0] = option;
setTriggeredModel(newModel);
}}
/>
</div>
<LabledDropdown
defaultOption={activeOption}
options={["onComplete", "onStart", "onStop", "delay"]}
onSelect={(option) => setActiveOption(option)}
/>
<div className="trigger-options">
<div>
<LabledDropdown
defaultOption={triggeredModel[index] || "Select Model"}
options={["Model 1", "Model 2", "Model 3"]}
onSelect={(option) => {
const newModel = [...triggeredModel];
newModel[index] = option;
setTriggeredModel(newModel);
}}
/>
</div>
<div>
<LabledDropdown
defaultOption={triggeredPoint[index] || "Select Point"}
options={["Point 1", "Point 2", "Point 3"]}
onSelect={(option) => {
const newPoint = [...triggeredPoint];
newPoint[index] = option;
setTriggeredPoint(newPoint);
}}
/>
</div>
<div>
<LabledDropdown
defaultOption={triggeredAction[index] || "Select Action"}
options={["Action 1", "Action 2", "Action 3"]}
onSelect={(option) => {
const newAction = [...triggeredAction];
newAction[index] = option;
setTriggeredAction(newAction);
}}
/>
</div>
<div>
<LabledDropdown
defaultOption={triggeredPoint[0] || "Select Point"}
options={["Point 1", "Point 2", "Point 3"]}
onSelect={(option) => {
const newPoint = [...triggeredPoint];
newPoint[0] = option;
setTriggeredPoint(newPoint);
}}
/>
</div>
<div>
<LabledDropdown
defaultOption={triggeredAction[0] || "Select Action"}
options={["Action 1", "Action 2", "Action 3"]}
onSelect={(option) => {
const newAction = [...triggeredAction];
newAction[0] = option;
setTriggeredAction(newAction);
}}
/>
</div>
</div>
))}
</div>
</div>
</div>
);

View File

@ -1,147 +1,204 @@
import React, { useRef, useState } from "react";
import React, { useEffect, useRef } from "react";
import {
AddIcon,
ArrowIcon,
RemoveIcon,
ResizeHeightIcon,
AddIcon,
ArrowIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../icons/ExportCommonIcons";
import RenameInput from "../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../functions/handleResizePannel";
import { useSelectedAsset, useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../store/simulation/useProductStore";
import { generateUUID } from "three/src/math/MathUtils";
import RenderOverlay from "../../../templates/Overlay";
import EditWidgetOption from "../../../ui/menu/EditWidgetOption";
import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi";
interface Path {
pathName: string; // Represents the name of the path
Children: string[]; // Represents the list of child points
interface Event {
pathName: string;
}
interface DropListProps {
val: Path; // Use the Path interface for the val prop
interface ListProps {
val: Event;
}
const DropList: React.FC<DropListProps> = ({ val }) => {
const [openDrop, setOpenDrop] = useState(false);
return (
<div className="process-container">
<div
className="value"
onClick={() => {
setOpenDrop(!openDrop);
}}
>
{val.pathName}
<div className={`arrow-container${openDrop ? " active" : ""}`}>
<ArrowIcon />
</div>
</div>
{val.Children && openDrop && (
<div className="children-drop">
{val.Children.map((child, index) => (
<div key={index} className="value">
{child}
const List: React.FC<ListProps> = ({ val }) => {
return (
<div className="process-container">
<div className="value">
{val.pathName}
</div>
))}
</div>
)}
</div>
);
);
};
const Simulations: React.FC = () => {
const productsContainerRef = useRef<HTMLDivElement>(null);
const [productsList, setProductsList] = useState<string[]>([]);
const [selectedItem, setSelectedItem] = useState<string>();
const productsContainerRef = useRef<HTMLDivElement>(null);
const { products, addProduct, removeProduct, renameProduct, addEvent, removeEvent } = useProductStore();
const { selectedProduct, setSelectedProduct } = useSelectedProduct();
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
const handleAddAction = () => {
setProductsList([...productsList, `Product ${productsList.length + 1}`]);
};
const handleAddProduct = () => {
addProduct(`Product ${products.length + 1}`, generateUUID());
};
const handleRemoveAction = (index: number) => {
setProductsList(productsList.filter((_, i) => i !== index));
if (selectedItem === productsList[index]) {
setSelectedItem("");
}
};
const handleRemoveProduct = (productId: string) => {
const currentIndex = products.findIndex(p => p.productId === productId);
const isSelected = selectedProduct.productId === productId;
const Value = [
{ pathName: "Path 1", Children: ["Point 1", "Point 2"] },
{ pathName: "Path 2", Children: ["Point 1", "Point 2"] },
{ pathName: "Path 3", Children: ["Point 1", "Point 2"] },
];
const updatedProducts = products.filter(p => p.productId !== productId);
return (
<div className="simulations-container">
<div className="header">Simulations</div>
<div className="add-product-container">
<div className="actions">
<div className="header">
<div className="header-value">Products</div>
<div className="add-button" onClick={handleAddAction}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={productsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{productsList.map((action, index) => (
<div
key={index}
className={`list-item ${
selectedItem === action ? "active" : ""
}`}
>
<div
className="value"
onClick={() => setSelectedItem(action)}
>
<input type="radio" name="products" id="products" />
<RenameInput value={action} />
</div>
<div
className="remove-button"
onClick={() => handleRemoveAction(index)}
>
<RemoveIcon />
</div>
if (isSelected) {
if (updatedProducts.length > 0) {
let newSelectedIndex = currentIndex;
if (currentIndex >= updatedProducts.length) {
newSelectedIndex = updatedProducts.length - 1;
}
setSelectedProduct(
updatedProducts[newSelectedIndex].productId,
updatedProducts[newSelectedIndex].productName
);
} else {
setSelectedProduct('', '');
}
}
removeProduct(productId);
};
const handleRenameProduct = (productId: string, newName: string) => {
renameProduct(productId, newName);
if (selectedProduct.productId === productId) {
setSelectedProduct(productId, newName);
}
};
const handleAddEventToProduct = () => {
if (selectedAsset) {
addEvent(selectedProduct.productId, selectedAsset);
// 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 (
<div className="simulations-container">
<div className="header">Simulations</div>
<div className="add-product-container">
<div className="actions">
<div className="header">
<div className="header-value">Products</div>
<div className="add-button" onClick={handleAddProduct}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={productsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{products.map((product, index) => (
<div
key={product.productId}
className={`list-item ${selectedProduct.productId === product.productId ? "active" : ""}`}
>
<div
className="value"
onClick={() => setSelectedProduct(product.productId, product.productName)}
>
<input
type="radio"
name="products"
checked={selectedProduct.productId === product.productId}
readOnly
/>
<RenameInput
value={product.productName}
onRename={(newName) => handleRenameProduct(product.productId, newName)}
/>
</div>
{products.length > 1 && (
<div
className="remove-button"
onClick={() => handleRemoveProduct(product.productId)}
>
<RemoveIcon />
</div>
)}
</div>
))}
</div>
<div
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, productsContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
<div className="simulation-process">
<div className="collapse-header-container">
<div className="header">Events</div>
<div className="arrow-container">
<ArrowIcon />
</div>
</div>
{events.map((event, index) => (
<List key={index} val={event} />
))}
</div>
<div className="compare-simulations-container">
<div className="compare-simulations-header">
Need to Compare Layout?
</div>
<div className="content">
Click <span>'Compare'</span> to review and analyze the layout differences between them.
</div>
<div className="input">
<input type="button" value={"Compare"} className="submit" />
</div>
</div>
))}
</div>
<div
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, productsContainerRef)}
>
<ResizeHeightIcon />
</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 className="simulation-process">
<div className="collapse-header-container">
<div className="header">Operations</div>
<div className="arrow-container">
<ArrowIcon />
</div>
</div>
{Value.map((val, index) => (
<DropList key={index} val={val} />
))}
</div>
<div className="compare-simulations-container">
<div className="compare-simulations-header">
Need to Compare Layout?
</div>
<div className="content">
Click <span>'Compare'</span> to review and analyze the layout
differences between them.
</div>
<div className="input">
<input type="button" value={"Compare"} className="submit" />
</div>
</div>
</div>
</div>
);
);
};
export default Simulations;

View File

@ -1,9 +1,9 @@
import { useState, useEffect, useRef } from "react";
import { useWidgetStore } from "../../../../../store/useWidgetStore";
import ChartComponent from "../../../sidebarLeft//visualization/widgets/ChartComponent";
import ChartComponent from "../../../sidebarLeft/visualization/widgets/ChartComponent";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import { WalletIcon } from "../../../../icons/3dChartIcons";
import SimpleCard from "../../../../../modules//visualization/widgets/floating/cards/SimpleCard";
import SimpleCard from "../../../../../modules/visualization/widgets/floating/cards/SimpleCard";
interface Widget {
id: string;

View File

@ -15,7 +15,7 @@ import {
} from "../icons/ExportToolsIcons";
import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons";
import useModuleStore, { useThreeDStore } from "../../store/useModuleStore";
import { handleSaveTemplate } from "../../modules//visualization/functions/handleSaveTemplate";
import { handleSaveTemplate } from "../../modules/visualization/functions/handleSaveTemplate";
import { usePlayButtonStore } from "../../store/usePlayButtonStore";
import useTemplateStore from "../../store/useTemplateStore";
import { useSelectedZoneStore } from "../../store/visualization/useZoneStore";

View File

@ -1,47 +1,72 @@
import React, { useState } from "react";
import LabledDropdown from "./LabledDropdown";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
import LabledDropdown from "./LabledDropdown";
const PreviewSelectionWithUpload: React.FC = () => {
const [showPreview, setSetshowPreview] = useState(false);
interface PreviewSelectionWithUploadProps {
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 (
<div className="preview-selection-with-upload-wrapper">
<div
className="input-header-container"
onClick={() => setSetshowPreview(!showPreview)}
>
<div className="input-header">Preview</div>
<div
className="arrow-container"
style={{ rotate: showPreview ? "0deg" : "90deg" }}
>
<ArrowIcon />
</div>
</div>
{showPreview && (
<div className="canvas-wrapper">
<div className="canvas-container"></div>
{preview && (
<>
<button
className="input-header-container"
onClick={() => setShowPreview(!showPreview)}
>
<div className="input-header">Preview</div>
<div
className="arrow-container"
style={{ rotate: showPreview ? "0deg" : "90deg" }}
>
<ArrowIcon />
</div>
</button>
{showPreview && (
<div className="canvas-wrapper">
<div className="canvas-container"></div>
</div>
)}
</>
)}
{upload && (
<div className="asset-selection-container">
<div className="upload-custom-asset-button">
<div className="title">Upload Product</div>
<input
type="file"
accept=".glb, .gltf"
id="simulation-product-upload"
/>
<label
className="upload-button"
htmlFor="simulation-product-upload"
>
Upload here
</label>
</div>
</div>
)}
<div className="asset-selection-container">
<div className="upload-custom-asset-button">
<div className="title">Upload Product</div>
<input
type="file"
accept=".glb, .gltf"
id="simulation-product-upload"
/>
<label className="upload-button" htmlFor="simulation-product-upload">
Upload here
</label>
</div>
<LabledDropdown
label="Presets"
defaultOption={"Default material"}
options={["Default material", "Product 1", "Product 2"]}
onSelect={(option) => console.log(option)}
/>
</div>
<LabledDropdown
label={label}
defaultOption={defaultOption}
options={options}
onSelect={onSelect}
/>
</div>
);
};

View File

@ -10,7 +10,7 @@ import {
ArrowIcon,
EyeIcon,
LockIcon,
RmoveIcon,
RemoveIcon,
} from "../../icons/ExportCommonIcons";
import { useThree } from "@react-three/fiber";
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) => {
return zones.some(
@ -184,7 +181,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
</div>
{remove && (
<div className="remove option">
<RmoveIcon />
<RemoveIcon />
</div>
)}
{item.assets && item.assets.length > 0 && (
@ -221,7 +218,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
</div>
{remove && (
<div className="remove option">
<RmoveIcon />
<RemoveIcon />
</div>
)}
</div>

View File

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

View File

@ -8,10 +8,13 @@ import * as Types from "../../../types/world/worldTypes";
import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi';
import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
import PointsCalculator from '../../simulation/events/points/functions/pointsCalculator';
async function loadInitialFloorItems(
itemsGroup: Types.RefGroup,
setFloorItems: Types.setFloorItemSetState,
addEvent: (event: EventsSchema) => void,
renderDistance: number
): Promise<void> {
if (!itemsGroup.current) return;
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);
if (cameraPosition.distanceTo(itemPosition) < 50) {
if (cameraPosition.distanceTo(itemPosition) < renderDistance) {
await new Promise<void>(async (resolve) => {
// Check Three.js Cache
const cachedModel = THREE.Cache.get(item.modelfileID!);
if (cachedModel) {
// console.log(`[Cache] Fetching ${item.modelname}`);
processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems);
processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
modelsLoaded++;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
return;
@ -85,7 +88,7 @@ async function loadInitialFloorItems(
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(item.modelfileID!, gltf);
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems);
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
modelsLoaded++;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
},
@ -106,7 +109,7 @@ async function loadInitialFloorItems(
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
await storeGLTF(item.modelfileID!, modelBlob);
THREE.Cache.add(item.modelfileID!, gltf);
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems);
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
modelsLoaded++;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
},
@ -148,8 +151,9 @@ function processLoadedModel(
item: Types.FloorItemType,
itemsGroup: Types.RefGroup,
setFloorItems: Types.setFloorItemSetState,
addEvent: (event: EventsSchema) => void,
) {
const model = gltf;
const model = gltf.clone();
model.uuid = item.modeluuid;
model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap);
model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid };
@ -182,6 +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.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 { findEnvironment } from "../../services/factoryBuilder/environment/findEnvironment";
import Layer2DVisibility from "./geomentries/layers/layer2DVisibility";
import DrieHtmlTemp from "..//visualization/mqttTemp/drieHtmlTemp";
import DrieHtmlTemp from "../visualization/mqttTemp/drieHtmlTemp";
import ZoneGroup from "./groups/zoneGroup";
import useModuleStore from "../../store/useModuleStore";
import MeasurementTool from "../scene/tools/measurementTool";

View File

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

View File

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

View File

@ -2,90 +2,107 @@ import * as THREE from "three";
import { EffectComposer, N8AO, Outline } from "@react-three/postprocessing";
import { BlendFunction } from "postprocessing";
import {
useDeletableFloorItem,
useSelectedWallItem,
useSelectedFloorItem,
useDeletableFloorItem,
useSelectedWallItem,
useSelectedFloorItem,
} from "../../../store/store";
import * as Types from "../../../types/world/worldTypes";
import * as CONSTANTS from "../../../types/world/worldConstants";
import { useEffect } from "react";
import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
export default function PostProcessing() {
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { selectedEventSphere } = useSelectedEventSphere();
function flattenChildren(children: any[]) {
const allChildren: any[] = [];
children.forEach((child) => {
allChildren.push(child);
if (child.children && child.children.length > 0) {
allChildren.push(...flattenChildren(child.children));
}
});
return allChildren;
}
function flattenChildren(children: any[]) {
const allChildren: any[] = [];
children.forEach((child) => {
allChildren.push(child);
if (child.children && child.children.length > 0) {
allChildren.push(...flattenChildren(child.children));
}
});
return allChildren;
}
return (
<>
<EffectComposer autoClear={false}>
<N8AO
color="black"
aoRadius={20}
intensity={7}
distanceFalloff={4}
aoSamples={32}
denoiseRadius={6}
denoiseSamples={16}
/>
{deletableFloorItem && (
<Outline
selection={flattenChildren(deletableFloorItem.children)}
selectionLayer={10}
width={3000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
blur={true}
xRay={true}
/>
)}
{selectedWallItem && (
<Outline
selection={selectedWallItem.children[1].children[0].children.filter(
(child: Types.Mesh) => child.name !== "CSG_REF"
)}
selectionLayer={10}
width={3000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={true}
/>
)}
{selectedFloorItem && (
<Outline
selection={flattenChildren(selectedFloorItem.children)}
selectionLayer={10}
width={3000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={true}
/>
)}
</EffectComposer>
</>
);
return (
<>
<EffectComposer autoClear={false}>
<N8AO
color="black"
aoRadius={20}
intensity={7}
distanceFalloff={4}
aoSamples={32}
denoiseRadius={6}
denoiseSamples={16}
/>
{deletableFloorItem && (
<Outline
selection={flattenChildren(deletableFloorItem.children)}
selectionLayer={10}
width={3000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
blur={true}
xRay={true}
/>
)}
{selectedWallItem && (
<Outline
selection={selectedWallItem.children[1].children[0].children.filter(
(child: Types.Mesh) => child.name !== "CSG_REF"
)}
selectionLayer={10}
width={3000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={true}
/>
)}
{selectedFloorItem && (
<Outline
selection={flattenChildren(selectedFloorItem.children)}
selectionLayer={10}
width={3000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={5}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
blur={true}
xRay={true}
/>
)}
{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>
</>
);
}

View File

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

View File

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

View File

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

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 (
<></>
)

View File

@ -1,14 +1,179 @@
import React from 'react'
import React, { useEffect, useRef, useState } from 'react'
import IKInstance from '../ikInstance/ikInstance';
import RoboticArmAnimator from '../animator/roboticArmAnimator';
import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_4.glb";
import { useThree } from "@react-three/fiber";
import { useFloorItems } from '../../../../../store/store';
import useModuleStore from '../../../../../store/useModuleStore';
import { Vector3 } from "three";
import * as THREE from "three";
interface Process {
triggerId: string;
startPoint?: Vector3;
endPoint?: Vector3;
speed: number;
}
function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
const { isPlaying } = usePlayButtonStore();
const [currentPhase, setCurrentPhase] = useState<(string)>("init");
const { scene } = useThree();
const targetBone = "Target";
const { activeModule } = useModuleStore();
const [ikSolver, setIkSolver] = useState<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 (
<>
<IKInstance />
<RoboticArmAnimator />
<IKInstance modelUrl={armModel} setIkSolver={setIkSolver} ikSolver={ikSolver} robot={robot} groupRef={groupRef} processes={processes}
setArmBotCurvePoints={setArmBotCurvePoints} />
<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 (
<></>
<>
<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 RoboticArmInstance from './armInstance/roboticArmInstance';
import { useArmBotStore } from '../../../../store/simulation/useArmBotStore';
function RoboticArmInstances() {
const { armBots } = useArmBotStore();
return (
<>
<RoboticArmInstance />
{armBots?.map((robot: ArmBotStatus) => (
<RoboticArmInstance key={robot.modelUuid} robot={robot} />
))}
</>
)

View File

@ -1,15 +1,166 @@
import React from 'react'
import RoboticArmInstances from './instances/roboticArmInstances';
import IkInstances from './instances/ikInstances';
import { useEffect } from "react";
import RoboticArmInstances from "./instances/roboticArmInstances";
import { useArmBotStore } from "../../../store/simulation/useArmBotStore";
import { useFloorItems } from "../../../store/store";
function RoboticArm() {
const { armBots, addArmBot, removeArmBot } = useArmBotStore();
const { floorItems } = useFloorItems();
const armBotStatusSample: RoboticArmEventSchema[] = [
{
state: "idle",
modelUuid: "armbot-xyz-001",
modelName: "ArmBot-X200",
position: [91.94347308985614, 0, 6.742905194869091],
rotation: [0, 0, 0],
type: "roboticArm",
speed: 1.5,
point: {
uuid: "point-123",
position: [0, 1.5, 0],
rotation: [0, 0, 0],
actions: [
{
actionUuid: "action-003",
actionName: "Pick Component",
actionType: "pickAndPlace",
process: {
startPoint: [5.52543010919071, 1, -8.433681161200905],
endPoint: [10.52543010919071, 1, -12.433681161200905],
},
triggers: [
{
triggerUuid: "trigger-001",
triggerName: "Start Trigger",
triggerType: "onStart",
delay: 0,
triggeredAsset: {
triggeredModel: {
modelName: "Conveyor A1",
modelUuid: "conveyor-01",
},
triggeredPoint: {
pointName: "Start Point",
pointUuid: "conveyor-01-point-001",
},
triggeredAction: {
actionName: "Move Forward",
actionUuid: "conveyor-action-01",
},
},
},
{
triggerUuid: "trigger-002",
triggerName: "Complete Trigger",
triggerType: "onComplete",
delay: 0,
triggeredAsset: {
triggeredModel: {
modelName: "StaticMachine B2",
modelUuid: "machine-02",
},
triggeredPoint: {
pointName: "Receive Point",
pointUuid: "machine-02-point-001",
},
triggeredAction: {
actionName: "Process Part",
actionUuid: "machine-action-01",
},
},
},
],
},
],
},
},
{
state: "idle",
modelUuid: "armbot-xyz-002",
modelName: "ArmBot-X200",
position: [95.94347308985614, 0, 6.742905194869091],
rotation: [0, 0, 0],
type: "roboticArm",
speed: 1.5,
point: {
uuid: "point-123",
position: [0, 1.5, 0],
rotation: [0, 0, 0],
actions: [
{
actionUuid: "action-001",
actionName: "Pick Component",
actionType: "pickAndPlace",
process: {
startPoint: [2.52543010919071, 0, 8.433681161200905],
endPoint: [95.3438373267953, 0, 9.0279187421610025],
},
triggers: [
{
triggerUuid: "trigger-001",
triggerName: "Start Trigger",
triggerType: "onStart",
delay: 0,
triggeredAsset: {
triggeredModel: {
modelName: "Conveyor A1",
modelUuid: "conveyor-01",
},
triggeredPoint: {
pointName: "Start Point",
pointUuid: "conveyor-01-point-001",
},
triggeredAction: {
actionName: "Move Forward",
actionUuid: "conveyor-action-01",
},
},
},
{
triggerUuid: "trigger-002",
triggerName: "Complete Trigger",
triggerType: "onComplete",
delay: 0,
triggeredAsset: {
triggeredModel: {
modelName: "StaticMachine B2",
modelUuid: "machine-02",
},
triggeredPoint: {
pointName: "Receive Point",
pointUuid: "machine-02-point-001",
},
triggeredAction: {
actionName: "Process Part",
actionUuid: "machine-action-01",
},
},
},
],
},
],
},
},
];
useEffect(() => {
removeArmBot(armBotStatusSample[0].modelUuid);
addArmBot('123', armBotStatusSample[0]);
// addArmBot('123', armBotStatusSample[1]);
// addCurrentAction('armbot-xyz-001', 'action-001');
}, []);
useEffect(() => {
//
}, [armBots]);
return (
<>
<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 Conveyor from './conveyor/conveyor';
import RoboticArm from './roboticArm/roboticArm';
import Materials from './materials/materials';
import Machine from './machine/machine';
import StorageUnit from './storageUnit/storageUnit';
import Simulator from './simulator/simulator';
import Products from './products/products';
import Trigger from './triggers/trigger';
import useModuleStore from '../../store/useModuleStore';
function Simulation() {
const { activeModule } = useModuleStore();
const { events } = useEventsStore();
const { products } = useProductStore();
@ -21,16 +29,36 @@ function Simulation() {
return (
<>
<Points />
{activeModule === 'simulation' &&
<Vehicles />
<>
<RoboticArm />
<Points />
<Conveyor />
<Products />
<Materials />
<Trigger />
<Conveyor />
<Vehicles />
<RoboticArm />
<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 { useFloorItems } from '../../../../../store/store';
import * as THREE from 'three';
import { Line } from '@react-three/drei';
import { useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
interface VehicleAnimatorProps {
path: [number, number, number][];
handleCallBack: () => void;
currentPhase: string;
agvUuid: number
path: [number, number, number][];
handleCallBack: () => void;
currentPhase: string;
agvUuid: number;
agvDetail: any;
}
function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail }: VehicleAnimatorProps) {
const { decrementVehicleLoad, vehicles } = useVehicleStore();
const { isPaused } = usePauseButtonStore();
const { speed } = useAnimationPlaySpeed();
const { isReset } = useResetButtonStore();
const [restRotation, setRestingRotation] = useState<boolean>(true);
const [progress, setProgress] = useState<number>(0);
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
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;
function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid }: VehicleAnimatorProps) {
const [progress, setProgress] = useState<number>(0)
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
const { scene } = useThree();
useEffect(() => {
if (currentPhase === 'stationed-pickup' && path.length > 0) {
setCurrentPath(path);
} else if (currentPhase === 'pickup-drop' && path.length > 0) {
setCurrentPath(path);
} else if (currentPhase === 'drop-pickup' && path.length > 0) {
setCurrentPath(path);
}
}, [currentPhase, path]);
useEffect(() => {
useEffect(() => {
setProgress(0);
completedRef.current = false;
}, [currentPath]);
if (currentPhase === 'stationed-pickup' && path.length > 0) {
setCurrentPath(path);
useFrame((_, delta) => {
const object = scene.getObjectByProperty('uuid', agvUuid);
if (!object || currentPath.length < 2) return;
if (isPaused) return;
let totalDistance = 0;
const distances = [];
for (let i = 0; i < currentPath.length - 1; i++) {
const start = new THREE.Vector3(...currentPath[i]);
const end = new THREE.Vector3(...currentPath[i + 1]);
const segmentDistance = start.distanceTo(end);
distances.push(segmentDistance);
totalDistance += segmentDistance;
}
let coveredDistance = progressRef.current;
let accumulatedDistance = 0;
let index = 0;
while (
index < distances.length &&
coveredDistance > accumulatedDistance + distances[index]
) {
accumulatedDistance += distances[index];
index++;
}
if (index < distances.length) {
const start = new THREE.Vector3(...currentPath[index]);
const end = new THREE.Vector3(...currentPath[index + 1]);
const segmentDistance = distances[index];
const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);
const rotationSpeed = 2.0;
const currentAngle = object.rotation.y;
let angleDifference = targetAngle - currentAngle;
if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI;
const maxRotationStep = rotationSpeed * delta;
object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep);
const isAligned = Math.abs(angleDifference) < 0.01;
if (isAligned) {
progressRef.current += delta * (speed * agvDetail.speed);
coveredDistance = progressRef.current;
const t = (coveredDistance - accumulatedDistance) / segmentDistance;
const position = start.clone().lerp(end, t);
object.position.copy(position);
}
}
if (progressRef.current >= totalDistance) {
if (restRotation) {
const targetQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0));
object.quaternion.slerp(targetQuaternion, delta * 2);
const angleDiff = object.quaternion.angleTo(targetQuaternion);
if (angleDiff < 0.01) {
let objectRotation = agvDetail.point.rotation
object.rotation.set(objectRotation[0], objectRotation[1], objectRotation[2]);
setRestingRotation(false);
}
return;
}
}
}, [currentPhase, path])
if (progressRef.current >= totalDistance) {
setRestingRotation(true);
progressRef.current = 0;
movingForward.current = !movingForward.current;
setCurrentPath([]);
handleCallBack();
if (currentPhase === 'pickup-drop') {
requestAnimationFrame(firstFrame);
}
}
});
useFrame((_, delta) => {
if (!path || path.length < 2) return;
function firstFrame() {
const unLoadDuration = agvDetail.point.action.unLoadDuration;
const droppedMaterial = agvDetail.currentLoad;
fixedInterval = (unLoadDuration / droppedMaterial) * 1000;
startTime = performance.now();
step(droppedMaterial);
}
const object = scene.getObjectByProperty("uuid", agvUuid)
if (!object) return;
function step(droppedMaterial: number) {
const elapsedTime = performance.now() - startTime;
setProgress(prev => {
const next = prev + delta * 0.1; // speed
return next >= 1 ? 1 : next;
});
if (elapsedTime >= fixedInterval) {
console.log('fixedInterval: ', fixedInterval);
console.log('elapsedTime: ', elapsedTime);
let droppedMat = droppedMaterial - 1;
decrementVehicleLoad(agvDetail.modelUuid, 1);
if (droppedMat === 0) return;
startTime = performance.now();
requestAnimationFrame(() => step(droppedMat));
} else {
requestAnimationFrame(() => step(droppedMaterial));
}
}
const totalSegments = path.length - 1;
const segmentIndex = Math.floor(progress * totalSegments);
const t = progress * totalSegments - segmentIndex;
const start = path[segmentIndex];
const end = path[segmentIndex + 1] || start;
// Directly set position without creating a new Vector3
object.position.x = start[0] + (end[0] - start[0]) * t;
object.position.y = start[1] + (end[1] - start[1]) * t;
object.position.z = start[2] + (end[2] - start[2]) * t;
});
// useFrame(() => {
// if (currentPath.length === 0) return;
// const object = scene.getObjectByProperty("uuid", agvUuid);
// if (!object) return;
// })
return (
return (
<>
{currentPath.length > 0 && (
<>
<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
export default VehicleAnimator;

View File

@ -1,66 +1,123 @@
import React, { useCallback, useEffect, useState } from 'react'
import VehicleAnimator from '../animator/vehicleAnimator'
import * as THREE from "three";
import React, { useCallback, useEffect, useState } from 'react';
import VehicleAnimator from '../animator/vehicleAnimator';
import * as THREE from 'three';
import { NavMeshQuery } from '@recast-navigation/core';
import { useNavMesh } from '../../../../../store/store';
import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
function VehicleInstance({ agvDetails }: any) {
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
const { setVehicleActive, setVehicleState } = useVehicleStore();
const [currentPhase, setCurrentPhase] = useState<(string)>("stationed");
const [path, setPath] = useState<[number, number, number][]>([]);
function VehicleInstance({ agvDetail }: any) {
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
const { vehicles, setVehicleActive, setVehicleState, incrementVehicleLoad } = useVehicleStore();
const [currentPhase, setCurrentPhase] = useState<string>('stationed');
const [path, setPath] = useState<[number, number, number][]>([]);
const computePath = useCallback((start: any, end: any) => {
const computePath = useCallback(
(start: any, end: any) => {
try {
const navMeshQuery = new NavMeshQuery(navMesh);
const { path: segmentPath } = navMeshQuery.computePath(start, end);
return (
segmentPath?.map(({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]) || []
);
} catch {
return [];
}
},
[navMesh]
);
function vehicleStatus(modelid: string, status: string) {
// console.log(`AGV ${modelid}: ${status}`);
}
try {
const navMeshQuery = new NavMeshQuery(navMesh);
const { path: segmentPath } = navMeshQuery.computePath(start, end);
return (
segmentPath?.map(
({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]
) || []
);
} catch {
return [];
}
}, [navMesh]);
useEffect(() => {
if (isPlaying) {
if (!agvDetails.isActive && agvDetails.state == "idle" && currentPhase == "stationed") {
const toPickupPath = computePath(new THREE.Vector3(agvDetails.position[0], agvDetails.position[1], agvDetails.position[2]), agvDetails.point.action.pickUpPoint);
setPath(toPickupPath)
setVehicleActive(agvDetails.modelUuid, true)
setVehicleState(agvDetails.modelUuid, "running")
setCurrentPhase("stationed-pickup")
//
}
}
}, [agvDetails, currentPhase, path, isPlaying])
function handleCallBack() {
if (currentPhase === "stationed-pickup") {
setVehicleActive(agvDetails.modelUuid, false)
setVehicleState(agvDetails.modelUuid, "idle")
setCurrentPhase("picking")
setPath([])
useEffect(() => {
if (isPlaying) {
if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'stationed') {
const toPickupPath = computePath(
new THREE.Vector3(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]),
agvDetail.point.action.pickUpPoint
);
setPath(toPickupPath);
setVehicleActive(agvDetail.modelUuid, true);
setVehicleState(agvDetail.modelUuid, 'running');
setCurrentPhase('stationed-pickup');
vehicleStatus(agvDetail.modelUuid, 'Started from station, heading to pickup');
return;
} else if (
!agvDetail.isActive &&
agvDetail.state === 'idle' &&
currentPhase === 'picking'
) {
setTimeout(() => {
incrementVehicleLoad(agvDetail.modelUuid, 2);
}, 5000);
if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) {
const toDrop = computePath(
agvDetail.point.action.pickUpPoint,
agvDetail.point.action.unLoadPoint
);
setPath(toDrop);
setVehicleActive(agvDetail.modelUuid, true);
setVehicleState(agvDetail.modelUuid, 'running');
setCurrentPhase('pickup-drop');
vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point');
}
} else if (
!agvDetail.isActive &&
agvDetail.state === 'idle' &&
currentPhase === 'dropping' &&
agvDetail.currentLoad === 0
) {
const dropToPickup = computePath(
agvDetail.point.action.unLoadPoint,
agvDetail.point.action.pickUpPoint
);
setPath(dropToPickup);
setVehicleActive(agvDetail.modelUuid, true);
setVehicleState(agvDetail.modelUuid, 'running');
setCurrentPhase('drop-pickup');
vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point');
}
}
}, [vehicles, currentPhase, path, isPlaying]);
function handleCallBack() {
if (currentPhase === 'stationed-pickup') {
setVehicleActive(agvDetail.modelUuid, false);
setVehicleState(agvDetail.modelUuid, 'idle');
setCurrentPhase('picking');
vehicleStatus(agvDetail.modelUuid, 'Reached pickup point, waiting for material');
setPath([]);
} else if (currentPhase === 'pickup-drop') {
setVehicleActive(agvDetail.modelUuid, false);
setVehicleState(agvDetail.modelUuid, 'idle');
setCurrentPhase('dropping');
vehicleStatus(agvDetail.modelUuid, 'Reached drop point');
setPath([]);
} else if (currentPhase === 'drop-pickup') {
setVehicleActive(agvDetail.modelUuid, false);
setVehicleState(agvDetail.modelUuid, 'idle');
setCurrentPhase('picking');
setPath([]);
vehicleStatus(agvDetail.modelUuid, 'Reached pickup point again, cycle complete');
}
}
return (
<>
<VehicleAnimator path={path} handleCallBack={handleCallBack} currentPhase={currentPhase} agvUuid={agvDetails?.modelUuid} />
</>
)
return (
<>
<VehicleAnimator
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 VehicleInstance from './instance/vehicleInstance'
import { useVehicleStore } from '../../../../store/simulation/useVehicleStore';
import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'
function VehicleInstances() {
const { vehicles } = useVehicleStore();
return (
<>
{vehicles.map((val: any, i: any) =>
<VehicleInstance agvDetails={val} key={i} />
<VehicleInstance agvDetail={val} key={i} />
)}
</>

View File

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

View File

@ -1,17 +1,19 @@
import React, { useEffect } from 'react'
import VehicleInstances from './instances/vehicleInstances';
import { useVehicleStore } from '../../../store/simulation/useVehicleStore';
import { useFloorItems } from '../../../store/store';
function Vehicles() {
const { vehicles, addVehicle } = useVehicleStore();
const { floorItems } = useFloorItems();
const vehicleStatusSample: VehicleEventSchema[] = [
{
modelUuid: "veh-123",
modelName: "Autonomous Truck A1",
position: [10, 0, 5],
modelUuid: "9356f710-4727-4b50-bdb2-9c1e747ecc74",
modelName: "AGV",
position: [97.9252965204558, 0, 37.96138815638661],
rotation: [0, 0, 0],
state: "idle",
type: "vehicle",
@ -24,11 +26,10 @@ function Vehicles() {
actionUuid: "action-456",
actionName: "Deliver to Zone A",
actionType: "travel",
material: "crate",
unLoadDuration: 15,
loadCapacity: 5,
pickUpPoint: { x: 5, y: 0, z: 3 },
unLoadPoint: { x: 20, y: 0, z: 10 },
unLoadDuration: 10,
loadCapacity: 2,
pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 },
unLoadPoint: { x: 105.71483985219794, y: 0, z: 28.66321267938962 },
triggers: [
{
triggerUuid: "trig-001",
@ -53,9 +54,51 @@ function Vehicles() {
}
},
{
modelUuid: "veh-123",
modelName: "Autonomous Truck A1",
position: [10, 0, 5],
modelUuid: "b06960bb-3d2e-41f7-a646-335f389c68b4",
modelName: "AGV",
position: [89.61609306554463, 0, 33.634136622267356],
rotation: [0, 0, 0],
state: "idle",
type: "vehicle",
speed: 2.5,
point: {
uuid: "point-789",
position: [0, 1, 0],
rotation: [0, 0, 0],
action: {
actionUuid: "action-456",
actionName: "Deliver to Zone A",
actionType: "travel",
unLoadDuration: 10,
loadCapacity: 2,
pickUpPoint: { x: 90, y: 0, z: 28 },
unLoadPoint: { x: 20, y: 0, z: 10 },
triggers: [
{
triggerUuid: "trig-001",
triggerName: "Start Travel",
triggerType: "onStart",
delay: 0,
triggeredAsset: {
triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" },
triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" }
}
},
{
triggerUuid: "trig-002",
triggerName: "Complete Travel",
triggerType: "onComplete",
delay: 2,
triggeredAsset: null
}
]
}
}
}, {
modelUuid: "e729a4f1-11d2-4778-8d6a-468f1b4f6b79",
modelName: "forklift",
position: [98.85729337188162, 0, 38.36616546567653],
rotation: [0, 0, 0],
state: "idle",
type: "vehicle",
@ -68,10 +111,9 @@ function Vehicles() {
actionUuid: "action-456",
actionName: "Deliver to Zone A",
actionType: "travel",
material: "crate",
unLoadDuration: 15,
loadCapacity: 5,
pickUpPoint: { x: 5, y: 0, z: 3 },
pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 },
unLoadPoint: { x: 20, y: 0, z: 10 },
triggers: [
{
@ -101,19 +143,18 @@ function Vehicles() {
useEffect(() => {
addVehicle('123', vehicleStatusSample[0]);
addVehicle('123', vehicleStatusSample[1]);
// addVehicle('123', vehicleStatusSample[1]);
// addVehicle('123', vehicleStatusSample[2]);
}, [])
useEffect(() => {
// console.log('vehicles: ', vehicles);
console.log('vehicles: ', vehicles);
}, [vehicles])
return (
<>
<VehicleInstances />
</>
)
}

View File

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

View File

@ -2,7 +2,11 @@ import React, { useState, FormEvent } from "react";
import { useNavigate } from "react-router-dom";
import { LogoIconLarge } from "../components/icons/Logo";
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 { signUpApi } from "../services/factoryBuilder/signInSignUp/signUpApi";
@ -21,7 +25,7 @@ const UserAuth: React.FC = () => {
const handleLogin = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const organization = (email.split("@")[1]).split(".")[0];
const organization = email.split("@")[1].split(".")[0];
try {
const res = await signInApi(email, password, organization);
@ -39,7 +43,7 @@ const UserAuth: React.FC = () => {
} else if (res.message === "User Not Found!!! Kindly signup...") {
setError("Account not found");
}
} catch (error) { }
} catch (error) {}
};
const handleRegister = async (e: FormEvent) => {
@ -47,7 +51,7 @@ const UserAuth: React.FC = () => {
if (email && password && userName) {
setError("");
try {
const organization = (email.split("@")[1]).split(".")[0];
const organization = email.split("@")[1].split(".")[0];
const res = await signUpApi(userName, email, password, organization);
if (res.message === "New User created") {
@ -56,123 +60,121 @@ const UserAuth: React.FC = () => {
if (res.message === "User already exists") {
setError("User already exists");
}
} catch (error) { }
} catch (error) {}
} else {
setError("Please fill all the fields!");
}
};
return (
<>
<div className="auth-container">
<div className="logo-icon">
<LogoIconLarge />
</div>
<h1>Welcome to Dwinzo</h1>
<p>
{isSignIn ? (
<>
Dont have an account?{" "}
<span
className="link"
onClick={() => setIsSignIn(false)}
style={{ cursor: "pointer" }}
>
Register here!
</span>
</>
) : (
<>
Already have an account?{" "}
<span
className="link"
onClick={() => setIsSignIn(true)}
style={{ cursor: "pointer" }}
>
Login here!
</span>
</>
)}
</p>
<div className="auth-container">
<div className="logo-icon">
<LogoIconLarge />
</div>
<h1>Welcome to Dwinzo</h1>
<p>
{isSignIn ? (
<>
Dont have an account?{" "}
<span
className="link"
onClick={() => setIsSignIn(false)}
style={{ cursor: "pointer" }}
>
Register here!
</span>
</>
) : (
<>
Already have an account?{" "}
<span
className="link"
onClick={() => setIsSignIn(true)}
style={{ cursor: "pointer" }}
>
Login here!
</span>
</>
)}
</p>
<button className="google-login">
<span className="google-icon">G</span> Continue with Google
</button>
<button className="google-login">
<span className="google-icon">G</span> Continue with Google
</button>
{error && <div className="error-message">🛈 {error}</div>}
{error && <div className="error-message">🛈 {error}</div>}
<form
onSubmit={isSignIn ? handleLogin : handleRegister}
className="auth-form"
>
{!isSignIn && (
<input
type="text"
value={userName}
placeholder="Username"
onChange={(e) => setUserName(e.target.value)}
required
/>
)}
<form
onSubmit={isSignIn ? handleLogin : handleRegister}
className="auth-form"
>
{!isSignIn && (
<input
type="email"
name="email"
value={email}
placeholder="Email"
onChange={(e) => setEmail(e.target.value)}
type="text"
value={userName}
placeholder="Username"
onChange={(e) => setUserName(e.target.value)}
required
/>
<div className="password-container">
<input
name="password"
type={showPassword ? "text" : "password"}
value={password}
placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
required
/>
<button
type="button"
className="toggle-password"
onClick={() => setShowPassword(!showPassword)}
>
<EyeIcon isClosed={showPassword} />
</button>
</div>
{!isSignIn && (
<div className="policy-checkbox">
<input type="checkbox" name="" id="" required />
<div className="label">
I have read and agree to the terms of service
</div>
</div>
)}
<button type="submit" className="continue-button">
{isSignIn ? "Continue" : "Register"}
)}
<input
type="email"
name="email"
value={email}
placeholder="Email"
onChange={(e) => setEmail(e.target.value)}
required
/>
<div className="password-container">
<input
name="password"
type={showPassword ? "text" : "password"}
value={password}
placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
required
/>
<button
type="button"
className="toggle-password"
onClick={() => setShowPassword(!showPassword)}
>
<EyeIcon isClosed={showPassword} />
</button>
</form>
<p className="policy">
By signing up for, or logging into, an account, you agree to our{" "}
<span
className="link"
onClick={() => navigate("/privacy")}
style={{ cursor: "pointer" }}
>
privacy policy
</span>{" "}
&{" "}
<span
className="link"
onClick={() => navigate("/terms")}
style={{ cursor: "pointer" }}
>
terms of service
</span>{" "}
whether you read them or not. You can also find these terms on our
website.
</p>
</div>
</>
</div>
{!isSignIn && (
<div className="policy-checkbox">
<input type="checkbox" name="" id="" required />
<div className="label">
I have read and agree to the terms of service
</div>
</div>
)}
<button type="submit" className="continue-button">
{isSignIn ? "Continue" : "Register"}
</button>
</form>
<p className="policy">
By signing up for, or logging into, an account, you agree to our{" "}
<span
className="link"
onClick={() => navigate("/privacy")}
style={{ cursor: "pointer" }}
>
privacy policy
</span>{" "}
&{" "}
<span
className="link"
onClick={() => navigate("/terms")}
style={{ cursor: "pointer" }}
>
terms of service
</span>{" "}
whether you read them or not. You can also find these terms on our
website.
</p>
</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 {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/Zone/visualization/${ZoneId}?organization=${organization}`,
`${url_Backend_dwinzo}/api/v2/ZoneVisualization/${ZoneId}?organization=${organization}`,
{
method: "GET",
headers: {

View File

@ -17,8 +17,11 @@ interface ArmBotStore {
addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => 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;
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
@ -72,7 +75,6 @@ export const useArmBotStore = create<ArmBotStore>()(
actionUuid: action.actionUuid,
actionName: action.actionName,
};
armBot.isActive = true;
}
}
});
@ -83,7 +85,6 @@ export const useArmBotStore = create<ArmBotStore>()(
const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
if (armBot) {
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) => {
set((state) => {
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) => {
set((state) => {
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
addProduct: (productName: string, productId: string) => void;
removeProduct: (productId: string) => void;
updateProduct: (productId: string, updates: Partial<{ productName: string; eventsData: EventsSchema[] }>) => void;
updateProduct: (productId: string, updates: Partial<{ productName: string; eventDatas: EventsSchema[] }>) => void;
// Event-level actions
addEvent: (productId: string, event: EventsSchema) => void;
@ -48,8 +48,18 @@ type ProductsStore = {
updates: Partial<TriggerSchema>
) => void;
// Renaming functions
renameProduct: (productId: string, newName: string) => void;
renameAction: (actionUuid: string, newName: string) => void;
renameTrigger: (triggerUuid: string, newName: string) => void;
// Helper functions
getProductById: (productId: string) => { productName: string; productId: string; eventsData: EventsSchema[] } | undefined;
getProductById: (productId: string) => { productName: string; productId: string; eventDatas: EventsSchema[] } | undefined;
getEventByModelUuid: (productId: string, modelUuid: string) => EventsSchema | undefined;
getPointByUuid: (productId: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined;
getActionByUuid: (productId: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined;
getTriggerByUuid: (productId: string, triggerUuid: string) => TriggerSchema | undefined;
getIsEventInProduct: (productId: string, modelUuid: string) => boolean;
};
export const useProductStore = create<ProductsStore>()(
@ -62,7 +72,7 @@ export const useProductStore = create<ProductsStore>()(
const newProduct = {
productName,
productId: productId,
eventsData: []
eventDatas: []
};
state.products.push(newProduct);
});
@ -88,7 +98,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
product.eventsData.push(event);
product.eventDatas.push(event);
}
});
},
@ -97,7 +107,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
product.eventsData = product.eventsData.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid);
product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid);
}
});
},
@ -106,7 +116,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event) {
Object.assign(event, updates);
}
@ -119,7 +129,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event && 'points' in event) {
(event as ConveyorEventSchema).points.push(point as ConveyorPointSchema);
} else if (event && 'point' in event) {
@ -133,7 +143,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event && 'points' in event) {
(event as ConveyorEventSchema).points = (event as ConveyorEventSchema).points.filter(p => p.uuid !== pointUuid);
} else if (event && 'point' in event && (event as any).point.uuid === pointUuid) {
@ -147,7 +157,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event && 'points' in event) {
const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
if (point) {
@ -165,7 +175,7 @@ export const useProductStore = create<ProductsStore>()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event && 'points' in event) {
const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
if (point) {
@ -185,7 +195,7 @@ export const useProductStore = create<ProductsStore>()(
removeAction: (actionUuid: string) => {
set((state) => {
for (const product of state.products) {
for (const event of product.eventsData) {
for (const event of product.eventDatas) {
if ('points' in event) {
// Handle ConveyorEventSchema
for (const point of (event as ConveyorEventSchema).points) {
@ -209,7 +219,7 @@ export const useProductStore = create<ProductsStore>()(
updateAction: (actionUuid, updates) => {
set((state) => {
for (const product of state.products) {
for (const event of product.eventsData) {
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && point.action.actionUuid === actionUuid) {
@ -239,7 +249,7 @@ export const useProductStore = create<ProductsStore>()(
addTrigger: (actionUuid, trigger) => {
set((state) => {
for (const product of state.products) {
for (const event of product.eventsData) {
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && point.action.actionUuid === actionUuid) {
@ -268,7 +278,7 @@ export const useProductStore = create<ProductsStore>()(
removeTrigger: (triggerUuid) => {
set((state) => {
for (const product of state.products) {
for (const event of product.eventsData) {
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && 'triggers' in point.action) {
@ -295,7 +305,7 @@ export const useProductStore = create<ProductsStore>()(
updateTrigger: (triggerUuid, updates) => {
set((state) => {
for (const product of state.products) {
for (const event of product.eventsData) {
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && 'triggers' in point.action) {
@ -331,9 +341,170 @@ export const useProductStore = create<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
getProductById: (productId) => {
return get().products.find(p => p.productId === productId);
},
getEventByModelUuid: (productId, modelUuid) => {
const product = get().getProductById(productId);
if (!product) return undefined;
return product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
},
getPointByUuid: (productId, modelUuid, pointUuid) => {
const event = get().getEventByModelUuid(productId, modelUuid);
if (!event) return undefined;
if ('points' in event) {
return (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
} else if ('point' in event && (event as any).point.uuid === pointUuid) {
return (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point;
}
return undefined;
},
getActionByUuid: (productId, actionUuid) => {
const product = get().products.find(p => p.productId === productId);
if (!product) return undefined;
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action?.actionUuid === actionUuid) {
return point.action;
}
}
} else if ('point' in event) {
const point = (event as any).point;
if ('action' in point && point.action?.actionUuid === actionUuid) {
return point.action;
} else if ('actions' in point) {
const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
if (action) return action;
}
}
}
return undefined;
},
getTriggerByUuid: (productId, triggerUuid) => {
const product = get().products.find(p => p.productId === productId);
if (!product) return undefined;
for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
for (const trigger of point.action?.triggers || []) {
if (trigger.triggerUuid === triggerUuid) {
return trigger;
}
}
}
} else if ('point' in event) {
const point = (event as any).point;
if ('action' in point) {
for (const trigger of point.action?.triggers || []) {
if (trigger.triggerUuid === triggerUuid) {
return trigger;
}
}
} else if ('actions' in point) {
for (const action of point.actions) {
for (const trigger of action.triggers || []) {
if (trigger.triggerUuid === triggerUuid) {
return trigger;
}
}
}
}
}
}
return undefined;
},
getIsEventInProduct: (productId, modelUuid) => {
const product = get().getProductById(productId);
if (!product) return false;
return product.eventDatas.some(e => 'modelUuid' in e && e.modelUuid === modelUuid);
}
}))
);

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

View File

@ -13,14 +13,17 @@ const useModuleStore = create<ModuleStore>((set) => ({
export default useModuleStore;
// New store for subModule
type SubModule = 'properties' | 'simulations' | 'mechanics' | 'analysis' | 'zoneProperties';
interface SubModuleStore {
subModule: string;
setSubModule: (subModule: string) => void;
subModule: SubModule;
setSubModule: (subModule: SubModule) => void;
}
const useSubModuleStore = create<SubModuleStore>((set) => ({
subModule: "properties", // Initial subModule state
setSubModule: (subModule) => set({ subModule }), // Update subModule state
setSubModule: (value) => set({ subModule: value }), // Update subModule state
}));
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";
// ========================================================================
// 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");
// ========================================================================
// Colors
// ========================================================================
// new variables
// Text colors
$text-color: #2b3344; // Primary text color
$text-disabled: #b7b7c6; // Disabled text color
$input-text-color: #595965; // Input field text color
// text colors
// ---------- light mode ----------
$text-color: #2b3344;
$text-disabled: #b7b7c6;
$input-text-color: #595965;
$highlight-text-color: #6f42c1;
$text-color-dark: #f3f3fd; // Primary text color for dark mode
$text-disabled-dark: #6f6f7a; // Disabled text color for dark mode
$input-text-color-dark: #b5b5c8; // Input field text color for dark mode
// ---------- dark mode ----------
$text-color-dark: #f3f3fd;
$text-disabled-dark: #6f6f7a;
$input-text-color-dark: #b5b5c8;
$highlight-text-color-dark: #B392F0;
// Accent colors
$accent-color: #6f42c1; // Primary accent color
$accent-color-dark: #c4abf1; // Primary accent color for dark mode
$highlight-accent-color: #e0dfff; // Highlighted accent for light mode
$highlight-accent-color-dark: #403e6a; // Highlighted accent for dark mode
// background colors
// ---------- light mode ----------
$background-color: linear-gradient(-45deg, #FCFDFDCC 0%, #FCFDFD99 100%);
$background-color-secondary: #FCFDFD4D;
$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
$background-color: #fcfdfd; // Main background color
$background-color-dark: #19191d; // Main background color for dark mode
$background-color-secondary: #e1e0ff80; // Secondary background color
$background-color-secondary-dark: #39394f99; // Secondary background color for dark mode
$background-color-gray: #f3f3f3; // Main background color
$background-color-gray-dark: #232323; // Main background color for dark mode
// ---------- dark mode ----------
$background-color-dark: linear-gradient(-45deg, #333333B3 0%, #2D2437B3 100%);
$background-color-secondary-dark: #19191D99;
$background-color-accent-dark: #6f42c1;
$background-color-button-dark: #6f42c1;
$background-color-drop-down-dark: #50505080;
$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-color: #e0dfff; // Default border color
$border-color-dark: #403e6a; // Border color for dark mode
// border colors
// ---------- light mode ----------
$border-color: #E0DFFF;
$border-color-accent: #6F42C1;
// Shadow color
$shadow-color: #3c3c431a; // Shadow base color for light and dark mode
$shadow-color-dark: #8f8f8f1a; // Shadow base color for light and dark mode
// ---------- dark mode ----------
$border-color-dark: #564B69;
$border-color-accent-dark: #6F42C1;
// Gradients
$acent-gradient-dark: linear-gradient(
90deg,
#b392f0 0%,
#a676ff 100%
); // Dark mode accent gradient
$acent-gradient: linear-gradient(
90deg,
#6f42c1 0%,
#925df3 100%
); // Light mode accent gradient
// highlight colors
// ---------- light mode ----------
$highlight-accent-color: #E0DFFF;
$highlight-secondary-color: #6F42C1;
// ---------- dark mode ----------
$highlight-accent-color-dark: #403E6A;
$highlight-secondary-color-dark: #C4ABF1;
// colors
$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-dark: radial-gradient(circle, #31373b 0%, #48494b 46%, #52415c 100%);
// ========================================================================
// Typography
// ========================================================================
$font-inter: "Inter", sans-serif;
$font-josefin-sans: "Josefin Sans", sans-serif;
$font-poppins: "Poppins", sans-serif;
$font-roboto: "Roboto", sans-serif;
// Font Family Variables
$font-inter: "Inter", sans-serif; // Inter font
$font-josefin-sans: "Josefin Sans", sans-serif; // Josefin Sans font
$font-poppins: "Poppins", sans-serif; // Poppins font
$font-roboto: "Roboto", sans-serif; // Roboto font
$tiny: 0.625rem;
$small: 0.75rem;
$regular: 0.8rem;
$large: 1rem;
$xlarge: 1.125rem;
$xxlarge: 1.5rem;
$xxxlarge: 2rem;
// Font sizes (converted to rem using a utility function)
$tiny: 0.625rem; // Extra small text (10px)
$small: 0.75rem; // Small text (12px)
$regular: 0.8rem; // Default text size (14px)
$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)
$thin-weight: 300;
$regular-weight: 400;
$medium-weight: 500;
$bold-weight: 600;
// Font weights
$thin-weight: 300; // Regular font weight
$regular-weight: 400; // Regular font weight
$medium-weight: 500; // Medium font weight
$bold-weight: 600; // Bold font weight
$z-index-drei-html: 1;
$z-index-default: 1;
$z-index-marketplace: 2;
$z-index-tools: 3;
$z-index-negative: -1;
$z-index-ui-base: 10;
$z-index-ui-overlay: 20;
$z-index-ui-popup: 30;
$z-index-ui-highest: 50;
// ========================================================================
// Z-Index Levels
// ========================================================================
$box-shadow-light: 0px 2px 4px $shadow-color;
$box-shadow-medium: 0px 4px 8px $shadow-color;
$box-shadow-heavy: 0px 8px 16px $shadow-color;
// Z-index variables for layering
$z-index-drei-html: 1; // For drei's Html components
$z-index-default: 1; // For drei's Html components
$z-index-marketplace: 2; // For drei's Html components
$z-index-tools: 3; // For drei's Html components
$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
$border-radius-small: 4px;
$border-radius-medium: 6px;
$border-radius-large: 12px;
$border-radius-circle: 50%;
$border-radius-extra-large: 20px;

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 */
display: none;
}
button{
border: none;
outline: none;
background: none;
cursor: pointer;
}

View File

@ -366,15 +366,66 @@
min-height: 50vh;
padding-bottom: 12px;
position: relative;
display: flex;
flex-direction: column;
overflow: auto;
.sidebar-right-content-container {
border-bottom: 1px solid var(--border-color);
// flex: 1;
height: calc(100% - 36px);
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);
border-radius: #{$border-radius-small};
cursor: pointer;
outline: none;
border: none;
path {
stroke: var(--primary-color);
}
&:disabled {
background-color: var(--text-disabled);
}
}
}
@ -706,7 +761,7 @@
}
.selected-actions-list {
margin-bottom: 8px;
.eye-dropper-input-container{
.eye-dropper-input-container {
padding: 6px 12px;
.regularDropdown-container {
padding: 5px 8px;
@ -747,19 +802,21 @@
width: 100%;
margin: 2px 0;
border-radius: #{$border-radius-small};
}
.value {
display: flex;
justify-content: flex-start;
align-items: center;
min-width: 80%;
gap: 6px;
.input-value {
text-align: start;
}
.value {
display: flex;
justify-content: flex-start;
align-items: center;
min-width: 80%;
gap: 6px;
input {
width: fit-content;
outline: none;
accent-color: var(--accent-color);
input {
width: fit-content;
outline: none;
accent-color: var(--accent-color);
}
}
}
@ -797,6 +854,7 @@
@include flex-center;
padding: 4px;
cursor: grab;
width: 100%;
&:active {
cursor: grabbing;

View File

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

View File

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

View File

@ -25,7 +25,7 @@ interface ConveyorPointSchema {
action: {
actionUuid: string;
actionName: string;
actionType: "default" | "spawn" | "swap" | "despawn";
actionType: "default" | "spawn" | "swap" | "delay" | "despawn";
material: string;
delay: number | "inherit";
spawnInterval: number | "inherit";
@ -42,7 +42,6 @@ interface VehiclePointSchema {
actionUuid: string;
actionName: string;
actionType: "travel";
material: string | null;
unLoadDuration: number;
loadCapacity: number;
pickUpPoint: { x: number; y: number, z: number } | null;
@ -119,12 +118,14 @@ interface StorageEventSchema extends AssetEventSchema {
point: StoragePointSchema;
}
type PointsScheme = ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema;
type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema;
type productsSchema = {
productName: string;
productId: string;
eventsData: EventsSchema[];
eventDatas: EventsSchema[];
}[]
@ -133,6 +134,7 @@ interface ConveyorStatus extends ConveyorEventSchema {
isActive: boolean;
idleTime: number;
activeTime: number;
}
interface MachineStatus extends MachineEventSchema {
@ -168,4 +170,17 @@ interface StorageUnitStatus extends StorageEventSchema {
idleTime: number;
activeTime: number;
currentLoad: number;
}
}
interface MaterialSchema {
materialId: string;
materialName: string;
materialType: string;
isActive: boolean;
startTime?: string;
endTime?: string;
cost?: number;
weight?: number;
}
type MaterialsSchema = MaterialSchema[];