Merge remote-tracking branch 'origin/main' into ui

This commit is contained in:
Vishnu 2025-03-28 19:37:02 +05:30
commit 893d01ee4e
62 changed files with 3692 additions and 1839 deletions

View File

@ -9,3 +9,9 @@ REACT_APP_SERVER_REST_API_BASE_URL=185.100.212.76:5000
# Base URL for the server marketplace API. # Base URL for the server marketplace API.
REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011 REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011
# base url for IoT socket server
REACT_APP_IOT_SOCKET_SERVER_URL =185.100.212.76:5010
# Base URL for the server mqtt.
REACT_APP_SERVER_MQTT_URL=185.100.212.76:23457

205
app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,7 @@
"@react-three/drei": "^9.113.0", "@react-three/drei": "^9.113.0",
"@react-three/fiber": "^8.17.7", "@react-three/fiber": "^8.17.7",
"@react-three/postprocessing": "^2.16.3", "@react-three/postprocessing": "^2.16.3",
"@recast-navigation/core": "^0.39.0",
"@recast-navigation/three": "^0.39.0", "@recast-navigation/three": "^0.39.0",
"@testing-library/jest-dom": "^5.17.0", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",

View File

@ -11,10 +11,10 @@ const CardsScene = () => {
<Canvas> <Canvas>
{/* 3d-cards */} {/* 3d-cards */}
<Throughput />
{/* <ReturnOfInvestment /> */}
{/* <ProductionCapacity /> */} {/* <ProductionCapacity /> */}
{/* <ReturnOfInvestment /> */}
{/* <StateWorking /> */} {/* <StateWorking /> */}
{/* <Throughput /> */}
<OrbitControls /> <OrbitControls />
</Canvas> </Canvas>

View File

@ -21,8 +21,11 @@ ChartJS.register(
Tooltip, Tooltip,
Legend Legend
); );
interface ProductionCapacityProps {
position: [number, number, number];
}
const ProductionCapacity = () => { const ProductionCapacity : React.FC<ProductionCapacityProps> = ({ position }) => {
// Chart data for a week // Chart data for a week
const chartData = { const chartData = {
labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], // Days of the week labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], // Days of the week
@ -76,7 +79,10 @@ const ProductionCapacity = () => {
}; };
return ( return (
<Html position={[0, 0, 0]} transform occlude> <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
sprite>
<div className="productionCapacity-wrapper card"> <div className="productionCapacity-wrapper card">
<div className="headeproductionCapacityr-wrapper"> <div className="headeproductionCapacityr-wrapper">
<div className="header">Production Capacity</div> <div className="header">Production Capacity</div>

View File

@ -35,8 +35,10 @@ const SmoothLineGraphComponent: React.FC<SmoothLineGraphProps> = ({
}) => { }) => {
return <Line data={data} options={options} />; return <Line data={data} options={options} />;
}; };
interface ReturnOfInvestmentProps {
const ReturnOfInvestment = () => { position: [number, number, number];
}
const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ position }) => {
// Improved sample data for the smooth curve graph (single day) // Improved sample data for the smooth curve graph (single day)
const graphData: ChartData<"line"> = { const graphData: ChartData<"line"> = {
labels: [ labels: [
@ -106,7 +108,10 @@ const ReturnOfInvestment = () => {
}; };
return ( return (
<Html position={[0, 0, 0]} transform occlude> <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
sprite>
<div className="returnOfInvestment card"> <div className="returnOfInvestment card">
<div className="header">Return of Investment</div> <div className="header">Return of Investment</div>
<div className="lineGraph charts"> <div className="lineGraph charts">

View File

@ -1,6 +1,9 @@
import { Html } from "@react-three/drei"; import { Html } from "@react-three/drei";
import image from "../../../../assets/image/temp/image.png"; // import image from "../../../../assets/image/temp/image.png";
const StateWorking = () => { interface StateWorkingProps {
position: [number, number, number];
}
const StateWorking: React.FC<StateWorkingProps> = ({ position }) => {
const datas = [ const datas = [
{ key: "Oil Tank:", value: "24/341" }, { key: "Oil Tank:", value: "24/341" },
{ key: "Oil Refin:", value: 36.023 }, { key: "Oil Refin:", value: 36.023 },
@ -10,7 +13,10 @@ const StateWorking = () => {
{ key: "Time:", value: 13 - 9 - 2023 }, { key: "Time:", value: 13 - 9 - 2023 },
]; ];
return ( return (
<Html position={[0, 0, 0]} transform occlude> <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
sprite>
<div className="stateWorking-wrapper card"> <div className="stateWorking-wrapper card">
<div className="header-wrapper"> <div className="header-wrapper">
<div className="header"> <div className="header">
@ -20,7 +26,7 @@ const StateWorking = () => {
</span> </span>
</div> </div>
<div className="img"> <div className="img">
<img src={image} alt="" /> {/* <img src={image} alt="" /> */}
</div> </div>
</div> </div>
{/* Data */} {/* Data */}

View File

@ -37,7 +37,12 @@ const LineGraphComponent: React.FC<LineGraphProps> = ({ data, options }) => {
return <Line data={data} options={options} />; return <Line data={data} options={options} />;
}; };
const Throughput = () => { interface ThroughputProps {
position: [number, number, number];
}
const Throughput: React.FC<ThroughputProps> = ({ position }) => {
// Sample data for the line graph // Sample data for the line graph
const graphData: ChartData<"line"> = { const graphData: ChartData<"line"> = {
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"], labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
@ -86,7 +91,10 @@ const Throughput = () => {
}; };
return ( return (
<Html position={[0, 0, 0]} transform occlude> <Html position={[position[0], position[1], position[2]]}
scale={[0.5, 0.5, 0.5]}
transform
sprite>
<div className="throughput-wrapper"> <div className="throughput-wrapper">
<div className="header">Throughput</div> <div className="header">Throughput</div>
<div className="display-value"> <div className="display-value">

View File

@ -3,6 +3,7 @@ import { useWidgetStore } from "../../../../../store/useWidgetStore";
import { ChartType } from "chart.js/auto"; import { ChartType } from "chart.js/auto";
import ChartComponent from "./ChartComponent"; import ChartComponent from "./ChartComponent";
import { StockIncreseIcon } from "../../../../icons/RealTimeVisulationIcons"; import { StockIncreseIcon } from "../../../../icons/RealTimeVisulationIcons";
import { generateUniqueId } from "../../../../../functions/generateUniqueId";
const chartTypes: ChartType[] = [ const chartTypes: ChartType[] = [
"bar", "bar",
@ -18,7 +19,7 @@ const sampleData = {
{ {
data: [65, 59, 80, 81, 56, 55, 40], data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1", backgroundColor: "#6f42c1",
borderColor: "#ffffff", borderColor: "#b392f0",
borderWidth: 1, borderWidth: 1,
}, },
], ],
@ -40,7 +41,8 @@ const ChartWidget: React.FC<WidgetProps> = ({ type, index, title }) => {
onDragStart={() => { onDragStart={() => {
setDraggedAsset({ setDraggedAsset({
type, type,
id: `widget-${index + 1}`, id: generateUniqueId(
),
title, title,
panel: "top", panel: "top",
data: sampleData, data: sampleData,

View File

@ -2,6 +2,7 @@ import widget1 from "../../../../../assets/image/3D/ProductionCapacity.png";
import widget2 from "../../../../../assets/image/3D/ReturnOfInvestment.png"; import widget2 from "../../../../../assets/image/3D/ReturnOfInvestment.png";
import widget3 from "../../../../../assets/image/3D/StateWorking.png"; import widget3 from "../../../../../assets/image/3D/StateWorking.png";
import widget4 from "../../../../../assets/image/3D/Throughput.png"; import widget4 from "../../../../../assets/image/3D/Throughput.png";
import { useAsset3dWidget } from "../../../../../store/store";
const Widgets3D = () => { const Widgets3D = () => {
const widgets = [ const widgets = [
{ name: "Widget 1", img: widget1 }, { name: "Widget 1", img: widget1 },
@ -9,17 +10,38 @@ const Widgets3D = () => {
{ name: "Widget 3", img: widget3 }, { name: "Widget 3", img: widget3 },
{ name: "Widget 4", img: widget4 }, { name: "Widget 4", img: widget4 },
]; ];
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
return ( return (
<div className="widgets-container widget3D"> <div className="widgets-container widget3D">
{widgets?.map((widget, index) => ( {widgets?.map((widget, index) => (
<div key={index} className="widget-item" draggable> <div
<div className="widget-name">{widget.name}</div> key={index}
className="widget-item"
draggable
onDragStart={(e) => {
let crt = e.target
if (crt instanceof HTMLElement) {
const widget = crt.cloneNode(true) as HTMLElement;
console.log('widget: ', widget);
e.dataTransfer.setDragImage(widget,0,0)
e.dataTransfer.effectAllowed="move"
}
}}
onPointerDown={() => {
setWidgetSelect("ui-" + widget.name);
}}
onPointerUp={() => {
setWidgetSelect(""); // Clearing selection correctly
}}
>
{/* <div className="widget-name">{widget.name}</div> */}
<img <img
className="widget-image" className="widget-image"
src={widget.img} src={widget.img}
alt={widget.name} alt={widget.name}
draggable={false} // draggable={false}
/> />
</div> </div>
))} ))}

View File

@ -10,7 +10,7 @@ import {
SimulationIcon, SimulationIcon,
} from "../../icons/SimulationIcons"; } from "../../icons/SimulationIcons";
import useToggleStore from "../../../store/useUIToggleStore"; import useToggleStore from "../../../store/useUIToggleStore";
import MachineMechanics from "./mechanics/MachineMechanics"; import ConveyorMechanics from "./mechanics/ConveyorMechanics";
import Visualization from "./visualization/Visualization"; import Visualization from "./visualization/Visualization";
import Analysis from "./analysis/Analysis"; import Analysis from "./analysis/Analysis";
import Simulations from "./simulation/Simulations"; import Simulations from "./simulation/Simulations";
@ -18,6 +18,7 @@ import { useSelectedActionSphere } from "../../../store/store";
import GlobalProperties from "./properties/GlobalProperties"; import GlobalProperties from "./properties/GlobalProperties";
import AsstePropertiies from "./properties/AssetProperties"; import AsstePropertiies from "./properties/AssetProperties";
import ZoneProperties from "./properties/ZoneProperties"; import ZoneProperties from "./properties/ZoneProperties";
import VehicleMechanics from "./mechanics/VehicleMechanics";
const SideBarRight: React.FC = () => { const SideBarRight: React.FC = () => {
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
@ -98,17 +99,24 @@ const SideBarRight: React.FC = () => {
{toggleUI && activeModule === "simulation" && ( {toggleUI && activeModule === "simulation" && (
<> <>
{subModule === "mechanics" && selectedActionSphere && ( {subModule === "mechanics" && selectedActionSphere && selectedActionSphere.path.type === "Conveyor" && (
<div className="sidebar-right-container"> <div className="sidebar-right-container">
<div className="sidebar-right-content-container"> <div className="sidebar-right-content-container">
<MachineMechanics /> <ConveyorMechanics />
</div>
</div>
)}
{subModule === "mechanics" && selectedActionSphere && selectedActionSphere.path.type === "Vehicle" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
{/* <VehicleMechanics /> */}
</div> </div>
</div> </div>
)} )}
{subModule === "mechanics" && !selectedActionSphere && ( {subModule === "mechanics" && !selectedActionSphere && (
<div className="sidebar-right-container"> <div className="sidebar-right-container">
<div className="sidebar-right-content-container"> <div className="sidebar-right-content-container">
{/* <MachineMechanics /> */} <ConveyorMechanics />
</div> </div>
</div> </div>
)} )}

View File

@ -0,0 +1,586 @@
import React, { useRef, useState, useMemo, useEffect } from "react";
import {
AddIcon,
InfoIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../icons/ExportCommonIcons";
import RenameInput from "../../../ui/inputs/RenameInput";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
import { handleResize } from "../../../../functions/handleResizePannel";
import EyeDropInput from "../../../ui/inputs/EyeDropInput";
import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from "../../../../store/store";
import * as THREE from 'three';
import * as Types from '../../../../types/world/worldTypes';
import InputToggle from "../../../ui/inputs/InputToggle";
const ConveyorMechanics: React.FC = () => {
const { selectedActionSphere } = useSelectedActionSphere();
const { selectedPath, setSelectedPath } = useSelectedPath();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const actionsContainerRef = useRef<HTMLDivElement>(null);
const triggersContainerRef = useRef<HTMLDivElement>(null);
const selectedPoint = useMemo(() => {
if (!selectedActionSphere) return null;
return simulationPaths
.filter((path): path is Types.ConveyorEventsSchema => path.type === "Conveyor")
.flatMap((path) => path.points)
.find((point) => point.uuid === selectedActionSphere.point.uuid);
}, [selectedActionSphere, simulationPaths]);
const handleAddAction = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => {
if (path.type === "Conveyor") {
return {
...path,
points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.point.uuid) {
const actionIndex = point.actions.length;
const newAction = {
uuid: THREE.MathUtils.generateUUID(),
name: `Action ${actionIndex + 1}`,
type: 'Inherit',
material: 'Inherit',
delay: 'Inherit',
spawnInterval: 'Inherit',
isUsed: false
};
return { ...point, actions: [...point.actions, newAction] };
}
return point;
}),
};
}
return path;
});
setSimulationPaths(updatedPaths);
};
const handleDeleteAction = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) }
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
};
const handleActionSelect = (uuid: string, actionType: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid
? {
...action,
type: actionType,
material: actionType === 'Spawn' || actionType === 'Swap' ? 'Inherit' : action.material,
delay: actionType === 'Delay' ? 'Inherit' : action.delay,
spawnInterval: actionType === 'Spawn' ? 'Inherit' : action.spawnInterval
}
: action
),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
// Update the selected item to reflect changes
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
const updatedAction = updatedPaths
.filter((path): path is Types.ConveyorEventsSchema => path.type === "Conveyor")
.flatMap(path => path.points)
.find(p => p.uuid === selectedActionSphere.point.uuid)
?.actions.find(a => a.uuid === uuid);
if (updatedAction) {
setSelectedItem({
type: "action",
item: updatedAction
});
}
}
};
// Modified handleMaterialSelect to ensure it only applies to relevant action types
const handleMaterialSelect = (uuid: string, material: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid &&
(action.type === 'Spawn' || action.type === 'Swap')
? { ...action, material }
: action
),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
// Update selected item if it's the current action
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
material
}
});
}
};
const handleDelayChange = (uuid: string, delay: number | string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, delay } : action
),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
};
const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, spawnInterval } : action
),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
};
const handleSpeedChange = (speed: number) => {
if (!selectedPath) return;
const updatedPaths = simulationPaths.map((path) =>
path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path
);
setSimulationPaths(updatedPaths);
setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } });
};
const handleAddTrigger = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.point.uuid) {
const triggerIndex = point.triggers.length;
const newTrigger = {
uuid: THREE.MathUtils.generateUUID(),
name: `Trigger ${triggerIndex + 1}`,
type: '',
isUsed: false
};
return { ...point, triggers: [...point.triggers, newTrigger] };
}
return point;
}),
}
: path
);
setSimulationPaths(updatedPaths);
};
const handleDeleteTrigger = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) }
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
};
const handleTriggerSelect = (uuid: string, triggerType: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
triggers: point.triggers.map((trigger) =>
trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger
),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
};
// Update the toggle handlers to immediately update the selected item
const handleActionToggle = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) => ({
...action,
isUsed: action.uuid === uuid ? !action.isUsed : false,
})),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
// Immediately update the selected item if it's the one being toggled
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
isUsed: !selectedItem.item.isUsed
}
});
}
};
// Do the same for trigger toggle
const handleTriggerToggle = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
triggers: point.triggers.map((trigger) => ({
...trigger,
isUsed: trigger.uuid === uuid ? !trigger.isUsed : false,
})),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
// Immediately update the selected item if it's the one being toggled
if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
isUsed: !selectedItem.item.isUsed
}
});
}
};
const [selectedItem, setSelectedItem] = useState<{ type: "action" | "trigger"; item: any; } | null>(null);
useEffect(() => {
setSelectedItem(null); // Reset selectedItem when selectedActionSphere changes
}, [selectedActionSphere]);
return (
<div className="machine-mechanics-container">
<div className="machine-mechanics-header">
{selectedActionSphere?.path?.modelName || "point name not found"}
</div>
<div className="machine-mechanics-content-container">
{!selectedPath &&
<>
<div className="actions">
<div className="header">
<div className="header-value">Actions</div>
<div className="add-button" onClick={handleAddAction}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={actionsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{selectedPoint?.actions.map((action) => (
<div
key={action.uuid}
className={`list-item ${selectedItem?.type === "action" &&
selectedItem.item?.uuid === action.uuid
? "active"
: ""
}`}
>
<div
className="value"
onClick={() => setSelectedItem({ type: "action", item: action })}
>
<RenameInput value={action.name} />
</div>
<div
className="remove-button"
onClick={() => handleDeleteAction(action.uuid)}
>
<RemoveIcon />
</div>
</div>
))}
</div>
<div
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, actionsContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
<div className="triggers">
<div className="header">
<div className="header-value">Triggers</div>
<div className="add-button" onClick={handleAddTrigger}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={triggersContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{selectedPoint?.triggers.map((trigger) => (
<div
key={trigger.uuid}
className={`list-item ${selectedItem?.type === "trigger" &&
selectedItem.item?.uuid === trigger.uuid
? "active"
: ""
}`}
>
<div
className="value"
onClick={() => setSelectedItem({ type: "trigger", item: trigger })}
>
<RenameInput value={trigger.name} />
</div>
<div
className="remove-button"
onClick={() => handleDeleteTrigger(trigger.uuid)}
>
<RemoveIcon />
</div>
</div>
))}
</div>
<div
className="resize-icon"
id="trigger-resize"
onMouseDown={(e) => handleResize(e, triggersContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
</>
}
<div className="selected-properties-container">
{selectedItem && (
<>
<div className="properties-header">{selectedItem.item.name}</div>
{selectedItem.type === "action" && (
<>
<InputToggle
inputKey="enableTrigger"
label="Enable Trigger"
value={selectedItem.item.isUsed}
onClick={() => handleActionToggle(selectedItem.item.uuid)}
/>
<LabledDropdown
defaultOption={selectedItem.item.type}
options={["Inherit", "Spawn", "Swap", "Despawn", "Delay"]}
onSelect={(option) => handleActionSelect(selectedItem.item.uuid, option)}
/>
{/* Only show material dropdown for Spawn/Swap actions */}
{(selectedItem.item.type === 'Spawn' || selectedItem.item.type === 'Swap') && (
<LabledDropdown
label={selectedItem.item.type === 'Spawn' ? 'Spawn Material' : 'Swap Material'}
defaultOption={selectedItem.item.material}
options={["Inherit", "Crate", "Box"]}
onSelect={(option) => handleMaterialSelect(selectedItem.item.uuid, option)}
/>
)}
{/* Only show delay input for Delay actions */}
{selectedItem.item.type === 'Delay' && (
<InputWithDropDown
label="Delay Time"
value={selectedItem.item.delay === 'Inherit'
? undefined
: selectedItem.item.delay}
onChange={(value) => {
const numValue = parseInt(value);
handleDelayChange(
selectedItem.item.uuid,
!value ? 'Inherit' : numValue
);
}}
/>
)}
{/* Only show spawn interval for Spawn actions */}
{selectedItem.item.type === 'Spawn' && (
<InputWithDropDown
label="Spawn Interval"
min={0}
defaultValue={selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString()}
value={selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString()}
onChange={(value) => {
handleSpawnIntervalChange(selectedItem.item.uuid, (value === "") ? "Inherit" : parseInt(value));
}}
/>
)}
</>
)}
{selectedItem.type === "trigger" && (
<>
<InputToggle
inputKey="enableTrigger"
label="Enable Trigger"
value={selectedItem.item.isUsed}
onClick={() => handleTriggerToggle(selectedItem.item.uuid)}
/>
<LabledDropdown
defaultOption={selectedItem.item.type || "Select Trigger Type"}
options={["On-Hit", "Buffer"]}
onSelect={(option) => handleTriggerSelect(selectedItem.item.uuid, option)}
/>
</>
)}
</>
)}
{selectedPath && !selectedItem && (
<div className="speed-control">
<InputWithDropDown
label="Conveyor Speed"
value={selectedPath.path.speed.toString()}
onChange={(value) => handleSpeedChange(parseFloat(value))}
/>
</div>
)}
</div>
<div className="footer">
<InfoIcon />
By selecting points, you can create events and triggers.
</div>
</div>
</div>
);
};
export default ConveyorMechanics;

View File

@ -1,533 +0,0 @@
import React, { useRef, useState, useMemo, useEffect } from "react";
import {
AddIcon,
InfoIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../icons/ExportCommonIcons";
import RenameInput from "../../../ui/inputs/RenameInput";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
import { handleResize } from "../../../../functions/handleResizePannel";
import EyeDropInput from "../../../ui/inputs/EyeDropInput";
import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from "../../../../store/store";
import * as THREE from 'three';
import InputToggle from "../../../ui/inputs/InputToggle";
const MachineMechanics: React.FC = () => {
const { selectedActionSphere } = useSelectedActionSphere();
const { selectedPath, setSelectedPath } = useSelectedPath();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const actionsContainerRef = useRef<HTMLDivElement>(null);
const triggersContainerRef = useRef<HTMLDivElement>(null);
const selectedPoint = useMemo(() => {
if (!selectedActionSphere) return null;
return simulationPaths.flatMap((path) => path.points).find((point) => point.uuid === selectedActionSphere.point.uuid);
}, [selectedActionSphere, simulationPaths]);
const handleAddAction = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.point.uuid) {
const actionIndex = point.actions.length;
const newAction = {
uuid: THREE.MathUtils.generateUUID(),
name: `Action ${actionIndex + 1}`,
type: 'Inherit',
material: 'Inherit',
delay: 'Inherit',
spawnInterval: 'Inherit',
isUsed: false
};
return { ...point, actions: [...point.actions, newAction] };
}
return point;
}),
}));
setSimulationPaths(updatedPaths);
};
const handleDeleteAction = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) }
: point
),
}));
setSimulationPaths(updatedPaths);
};
const handleActionSelect = (uuid: string, actionType: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid
? {
...action,
type: actionType,
// Reset dependent fields when type changes
material: actionType === 'Spawn' || actionType === 'Swap' ? 'Inherit' : action.material,
delay: actionType === 'Delay' ? 'Inherit' : action.delay,
spawnInterval: actionType === 'Spawn' ? 'Inherit' : action.spawnInterval
}
: action
),
}
: point
),
}));
setSimulationPaths(updatedPaths);
// Update the selected item to reflect changes
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
const updatedAction = updatedPaths
.flatMap(path => path.points)
.find(p => p.uuid === selectedActionSphere.point.uuid)
?.actions.find(a => a.uuid === uuid);
if (updatedAction) {
setSelectedItem({
type: "action",
item: updatedAction
});
}
}
};
// Modified handleMaterialSelect to ensure it only applies to relevant action types
const handleMaterialSelect = (uuid: string, material: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid &&
(action.type === 'Spawn' || action.type === 'Swap')
? { ...action, material }
: action
),
}
: point
),
}));
setSimulationPaths(updatedPaths);
// Update selected item if it's the current action
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
material
}
});
}
};
const handleDelayChange = (uuid: string, delay: number | string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, delay } : action
),
}
: point
),
}));
setSimulationPaths(updatedPaths);
};
const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, spawnInterval } : action
),
}
: point
),
}));
setSimulationPaths(updatedPaths);
};
const handleSpeedChange = (speed: number) => {
if (!selectedPath) return;
const updatedPaths = simulationPaths.map((path) =>
path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path
);
setSimulationPaths(updatedPaths);
setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } });
};
const handleAddTrigger = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.point.uuid) {
const triggerIndex = point.triggers.length;
const newTrigger = {
uuid: THREE.MathUtils.generateUUID(),
name: `Trigger ${triggerIndex + 1}`,
type: '',
isUsed: false
};
return { ...point, triggers: [...point.triggers, newTrigger] };
}
return point;
}),
}));
setSimulationPaths(updatedPaths);
};
const handleDeleteTrigger = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) }
: point
),
}));
setSimulationPaths(updatedPaths);
};
const handleTriggerSelect = (uuid: string, triggerType: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
triggers: point.triggers.map((trigger) =>
trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger
),
}
: point
),
}));
setSimulationPaths(updatedPaths);
};
// Update the toggle handlers to immediately update the selected item
const handleActionToggle = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) => ({
...action,
isUsed: action.uuid === uuid ? !action.isUsed : false,
})),
}
: point
),
}));
setSimulationPaths(updatedPaths);
// Immediately update the selected item if it's the one being toggled
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
isUsed: !selectedItem.item.isUsed
}
});
}
};
// Do the same for trigger toggle
const handleTriggerToggle = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
triggers: point.triggers.map((trigger) => ({
...trigger,
isUsed: trigger.uuid === uuid ? !trigger.isUsed : false,
})),
}
: point
),
}));
setSimulationPaths(updatedPaths);
// Immediately update the selected item if it's the one being toggled
if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
isUsed: !selectedItem.item.isUsed
}
});
}
};
const [selectedItem, setSelectedItem] = useState<{ type: "action" | "trigger"; item: any; } | null>(null);
useEffect(() => {
setSelectedItem(null); // Reset selectedItem when selectedActionSphere changes
}, [selectedActionSphere]);
return (
<div className="machine-mechanics-container">
<div className="machine-mechanics-header">
{selectedActionSphere?.path?.modelName || "point name not found"}
</div>
<div className="machine-mechanics-content-container">
<div className="actions">
<div className="header">
<div className="header-value">Actions</div>
<div className="add-button" onClick={handleAddAction}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={actionsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{selectedPoint?.actions.map((action) => (
<div
key={action.uuid}
className={`list-item ${selectedItem?.type === "action" &&
selectedItem.item?.uuid === action.uuid
? "active"
: ""
}`}
>
<div
className="value"
onClick={() => setSelectedItem({ type: "action", item: action })}
>
<RenameInput value={action.name} />
</div>
<div
className="remove-button"
onClick={() => handleDeleteAction(action.uuid)}
>
<RemoveIcon />
</div>
</div>
))}
</div>
<div
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, actionsContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
<div className="triggers">
<div className="header">
<div className="header-value">Triggers</div>
<div className="add-button" onClick={handleAddTrigger}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={triggersContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{selectedPoint?.triggers.map((trigger) => (
<div
key={trigger.uuid}
className={`list-item ${selectedItem?.type === "trigger" &&
selectedItem.item?.uuid === trigger.uuid
? "active"
: ""
}`}
>
<div
className="value"
onClick={() => setSelectedItem({ type: "trigger", item: trigger })}
>
<RenameInput value={trigger.name} />
</div>
<div
className="remove-button"
onClick={() => handleDeleteTrigger(trigger.uuid)}
>
<RemoveIcon />
</div>
</div>
))}
</div>
<div
className="resize-icon"
id="trigger-resize"
onMouseDown={(e) => handleResize(e, triggersContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
<div className="selected-properties-container">
{selectedItem && (
<>
<div className="properties-header">{selectedItem.item.name}</div>
{selectedItem.type === "action" && (
<>
<InputToggle
inputKey="enableTrigger"
label="Enable Trigger"
value={selectedItem.item.isUsed}
onClick={() => handleActionToggle(selectedItem.item.uuid)}
/>
<LabledDropdown
defaultOption={selectedItem.item.type}
options={["Inherit", "Spawn", "Swap", "Despawn", "Delay"]}
onSelect={(option) => handleActionSelect(selectedItem.item.uuid, option)}
/>
{/* Only show material dropdown for Spawn/Swap actions */}
{(selectedItem.item.type === 'Spawn' || selectedItem.item.type === 'Swap') && (
<LabledDropdown
label={selectedItem.item.type === 'Spawn' ? 'Spawn Material' : 'Swap Material'}
defaultOption={selectedItem.item.material}
options={["Inherit", "Crate", "Box"]}
onSelect={(option) => handleMaterialSelect(selectedItem.item.uuid, option)}
/>
)}
{/* Only show delay input for Delay actions */}
{selectedItem.item.type === 'Delay' && (
<InputWithDropDown
label="Delay Time"
value={selectedItem.item.delay === 'Inherit'
? undefined
: selectedItem.item.delay}
onChange={(value) => {
const numValue = parseInt(value);
handleDelayChange(
selectedItem.item.uuid,
!value ? 'Inherit' : numValue
);
}}
/>
)}
{/* Only show spawn interval for Spawn actions */}
{selectedItem.item.type === 'Spawn' && (
<InputWithDropDown
label="Spawn Interval"
min={0}
defaultValue={selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString()}
value={selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString()}
onChange={(value) => {
handleSpawnIntervalChange(selectedItem.item.uuid, (value === "") ? "Inherit" : parseInt(value));
}}
/>
)}
</>
)}
{selectedItem.type === "trigger" && (
<>
<InputToggle
inputKey="enableTrigger"
label="Enable Trigger"
value={selectedItem.item.isUsed}
onClick={() => handleTriggerToggle(selectedItem.item.uuid)}
/>
<LabledDropdown
defaultOption={selectedItem.item.type || "Select Trigger Type"}
options={["On-Hit", "Buffer"]}
onSelect={(option) => handleTriggerSelect(selectedItem.item.uuid, option)}
/>
</>
)}
</>
)}
{selectedPath && !selectedItem && (
<div className="speed-control">
<InputWithDropDown
label="Path Speed"
value={selectedPath.path.speed.toString()}
onChange={(value) => handleSpeedChange(parseFloat(value))}
/>
</div>
)}
</div>
<div className="footer">
<InfoIcon />
By selecting points, you can create events and triggers.
</div>
</div>
</div>
);
};
export default MachineMechanics;

View File

@ -0,0 +1,561 @@
import React, { useRef, useState, useMemo, useEffect } from "react";
import {
AddIcon,
InfoIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../icons/ExportCommonIcons";
import RenameInput from "../../../ui/inputs/RenameInput";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
import { handleResize } from "../../../../functions/handleResizePannel";
import EyeDropInput from "../../../ui/inputs/EyeDropInput";
import { useSelectedActionSphere, useSelectedPath, useSimulationPaths } from "../../../../store/store";
import * as THREE from 'three';
import * as Types from '../../../../types/world/worldTypes';
import InputToggle from "../../../ui/inputs/InputToggle";
const VehicleMechanics: React.FC = () => {
const { selectedActionSphere } = useSelectedActionSphere();
const { selectedPath, setSelectedPath } = useSelectedPath();
const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const actionsContainerRef = useRef<HTMLDivElement>(null);
const triggersContainerRef = useRef<HTMLDivElement>(null);
const selectedPoint = useMemo(() => {
if (!selectedActionSphere) return null;
return simulationPaths
.filter((path): path is Types.ConveyorEventsSchema => path.type === "Conveyor")
.flatMap((path) => path.points)
.find((point) => point.uuid === selectedActionSphere.point.uuid);
}, [selectedActionSphere, simulationPaths]);
const handleAddAction = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => {
if (path.type === "Conveyor") {
return {
...path,
points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.point.uuid) {
const actionIndex = point.actions.length;
const newAction = {
uuid: THREE.MathUtils.generateUUID(),
name: `Action ${actionIndex + 1}`,
type: 'Inherit',
material: 'Inherit',
delay: 'Inherit',
spawnInterval: 'Inherit',
isUsed: false
};
return { ...point, actions: [...point.actions, newAction] };
}
return point;
}),
};
}
return path;
});
setSimulationPaths(updatedPaths);
};
const handleDeleteAction = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) }
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
};
const handleActionSelect = (uuid: string, actionType: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid
? {
...action,
type: actionType,
material: actionType === 'Spawn' || actionType === 'Swap' ? 'Inherit' : action.material,
delay: actionType === 'Delay' ? 'Inherit' : action.delay,
spawnInterval: actionType === 'Spawn' ? 'Inherit' : action.spawnInterval
}
: action
),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
// Update the selected item to reflect changes
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
const updatedAction = updatedPaths
.filter((path): path is Types.ConveyorEventsSchema => path.type === "Conveyor")
.flatMap(path => path.points)
.find(p => p.uuid === selectedActionSphere.point.uuid)
?.actions.find(a => a.uuid === uuid);
if (updatedAction) {
setSelectedItem({
type: "action",
item: updatedAction
});
}
}
};
// Modified handleMaterialSelect to ensure it only applies to relevant action types
const handleMaterialSelect = (uuid: string, material: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid &&
(action.type === 'Spawn' || action.type === 'Swap')
? { ...action, material }
: action
),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
// Update selected item if it's the current action
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
material
}
});
}
};
const handleDelayChange = (uuid: string, delay: number | string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, delay } : action
),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
};
const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, spawnInterval } : action
),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
};
const handleSpeedChange = (speed: number) => {
if (!selectedPath) return;
const updatedPaths = simulationPaths.map((path) =>
path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path
);
setSimulationPaths(updatedPaths);
setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } });
};
const handleAddTrigger = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.point.uuid) {
const triggerIndex = point.triggers.length;
const newTrigger = {
uuid: THREE.MathUtils.generateUUID(),
name: `Trigger ${triggerIndex + 1}`,
type: '',
isUsed: false
};
return { ...point, triggers: [...point.triggers, newTrigger] };
}
return point;
}),
}
: path
);
setSimulationPaths(updatedPaths);
};
const handleDeleteTrigger = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) }
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
};
const handleTriggerSelect = (uuid: string, triggerType: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
triggers: point.triggers.map((trigger) =>
trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger
),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
};
// Update the toggle handlers to immediately update the selected item
const handleActionToggle = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
actions: point.actions.map((action) => ({
...action,
isUsed: action.uuid === uuid ? !action.isUsed : false,
})),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
// Immediately update the selected item if it's the one being toggled
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
isUsed: !selectedItem.item.isUsed
}
});
}
};
// Do the same for trigger toggle
const handleTriggerToggle = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid
? {
...point,
triggers: point.triggers.map((trigger) => ({
...trigger,
isUsed: trigger.uuid === uuid ? !trigger.isUsed : false,
})),
}
: point
),
}
: path
);
setSimulationPaths(updatedPaths);
// Immediately update the selected item if it's the one being toggled
if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
isUsed: !selectedItem.item.isUsed
}
});
}
};
const [selectedItem, setSelectedItem] = useState<{ type: "action" | "trigger"; item: any; } | null>(null);
useEffect(() => {
setSelectedItem(null); // Reset selectedItem when selectedActionSphere changes
}, [selectedActionSphere]);
return (
<div className="machine-mechanics-container">
<div className="machine-mechanics-header">
{selectedActionSphere?.path?.modelName || "point name not found"}
</div>
<div className="machine-mechanics-content-container">
<div className="actions">
<div className="header">
<div className="header-value">Actions</div>
<div className="add-button" onClick={handleAddAction}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={actionsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
<>
{console.log(selectedPoint)}
</>
</div>
<div
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, actionsContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
<div className="triggers">
<div className="header">
<div className="header-value">Triggers</div>
<div className="add-button" onClick={handleAddTrigger}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={triggersContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{selectedPoint?.triggers.map((trigger) => (
<div
key={trigger.uuid}
className={`list-item ${selectedItem?.type === "trigger" &&
selectedItem.item?.uuid === trigger.uuid
? "active"
: ""
}`}
>
<div
className="value"
onClick={() => setSelectedItem({ type: "trigger", item: trigger })}
>
<RenameInput value={trigger.name} />
</div>
<div
className="remove-button"
onClick={() => handleDeleteTrigger(trigger.uuid)}
>
<RemoveIcon />
</div>
</div>
))}
</div>
<div
className="resize-icon"
id="trigger-resize"
onMouseDown={(e) => handleResize(e, triggersContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
<div className="selected-properties-container">
{selectedItem && (
<>
<div className="properties-header">{selectedItem.item.name}</div>
{selectedItem.type === "action" && (
<>
<InputToggle
inputKey="enableTrigger"
label="Enable Trigger"
value={selectedItem.item.isUsed}
onClick={() => handleActionToggle(selectedItem.item.uuid)}
/>
<LabledDropdown
defaultOption={selectedItem.item.type}
options={["Inherit", "Spawn", "Swap", "Despawn", "Delay"]}
onSelect={(option) => handleActionSelect(selectedItem.item.uuid, option)}
/>
{/* Only show material dropdown for Spawn/Swap actions */}
{(selectedItem.item.type === 'Spawn' || selectedItem.item.type === 'Swap') && (
<LabledDropdown
label={selectedItem.item.type === 'Spawn' ? 'Spawn Material' : 'Swap Material'}
defaultOption={selectedItem.item.material}
options={["Inherit", "Crate", "Box"]}
onSelect={(option) => handleMaterialSelect(selectedItem.item.uuid, option)}
/>
)}
{/* Only show delay input for Delay actions */}
{selectedItem.item.type === 'Delay' && (
<InputWithDropDown
label="Delay Time"
value={selectedItem.item.delay === 'Inherit'
? undefined
: selectedItem.item.delay}
onChange={(value) => {
const numValue = parseInt(value);
handleDelayChange(
selectedItem.item.uuid,
!value ? 'Inherit' : numValue
);
}}
/>
)}
{/* Only show spawn interval for Spawn actions */}
{selectedItem.item.type === 'Spawn' && (
<InputWithDropDown
label="Spawn Interval"
min={0}
defaultValue={selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString()}
value={selectedItem.item.spawnInterval === "Inherit" ? "" : selectedItem.item.spawnInterval.toString()}
onChange={(value) => {
handleSpawnIntervalChange(selectedItem.item.uuid, (value === "") ? "Inherit" : parseInt(value));
}}
/>
)}
</>
)}
{selectedItem.type === "trigger" && (
<>
<InputToggle
inputKey="enableTrigger"
label="Enable Trigger"
value={selectedItem.item.isUsed}
onClick={() => handleTriggerToggle(selectedItem.item.uuid)}
/>
<LabledDropdown
defaultOption={selectedItem.item.type || "Select Trigger Type"}
options={["On-Hit", "Buffer"]}
onSelect={(option) => handleTriggerSelect(selectedItem.item.uuid, option)}
/>
</>
)}
</>
)}
{selectedPath && !selectedItem && (
<div className="speed-control">
<InputWithDropDown
label="ConveyorEventsSchema Speed"
value={selectedPath.path.speed.toString()}
onChange={(value) => handleSpeedChange(parseFloat(value))}
/>
</div>
)}
</div>
<div className="footer">
<InfoIcon />
By selecting points, you can create events and triggers.
</div>
</div>
</div>
);
};
export default VehicleMechanics;

View File

@ -1,77 +1,177 @@
import React, { useEffect, useState } from 'react' // import React, { useEffect, useState } from 'react'
import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown' // import MultiLevelDropdown from '../../../../ui/inputs/MultiLevelDropDown'
import { AddIcon } from '../../../../icons/ExportCommonIcons' // import { AddIcon } from '../../../../icons/ExportCommonIcons'
import RegularDropDown from '../../../../ui/inputs/RegularDropDown' // import RegularDropDown from '../../../../ui/inputs/RegularDropDown'
import useChartStore from '../../../../../store/useChartStore' // import useChartStore from '../../../../../store/useChartStore'
import axios from 'axios' // import axios from 'axios'
type Props = {} // type Props = {}
// const LineGrapInput = (props: Props) => {
// const [dropDowndata, setDropDownData] = useState({})
// const [selections, setSelections] = useState<Record<string, { name: string, fields: string }>>({})
// const [selectedOption, setSelectedOption] = useState('1h')
// const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
// const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
// const handleSelectDuration = (option: string) => {
// updateDuration(option); // Normalize for key matching
// };
// useEffect(() => {
// const fetchZoneData = async () => {
// try {
// const response = await axios.get(`http://${iotApiUrl}/getinput`);
// if (response.status === 200) {
// console.log('dropdown data:', response.data);
// setDropDownData(response.data)
// } else {
// console.log('Unexpected response:', response);
// }
// } catch (error) {
// console.error('There was an error!', error);
// }
// };
// fetchZoneData();
// }, []);
// useEffect(() => {
// console.log(selections);
// }, [selections])
// const handleSelect = (inputKey: string, selectedData: { name: string, fields: string } | null) => {
// setSelections(prev => {
// if (selectedData === null) {
// const newSelections = { ...prev };
// delete newSelections[inputKey];
// return newSelections;
// } else {
// return {
// ...prev,
// [inputKey]: selectedData
// };
// }
// });
// };
// interface Measurement {
// name: string;
// fields: string;
// }
// interface InputData {
// [key: string]: Measurement;
// }
// const extractMeasurements = (input: InputData): Measurement[] => {
// return Object.values(input);
// };
// useEffect(() => {
// const measurementsData = extractMeasurements(selections);
// setMeasurements(measurementsData);
// }, [selections]);
// return (
// <>
// <div className="inputs-wrapper">
// {[...Array(6)].map((_, index) => {
// const inputKey = `input${index + 1}`;
// return (
// <div key={index} className="datas">
// <div className="datas__label">Input {index + 1}</div>
// <div className="datas__class">
// <MultiLevelDropdown
// data={dropDowndata}
// onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
// onUnselect={() => handleSelect(inputKey, null)}
// selectedValue={selections[inputKey]}
// />
// <div className="icon">
// <AddIcon />
// </div>
// </div>
// </div>
// );
// })}
// </div>
// <div>
// <div className="datas">
// <div className="datas__label">duration</div>
// <div className="datas__class">
// <RegularDropDown
// header={duration}
// options={["1h", "2h", "12h"]}
// onSelect={handleSelectDuration}
// search={false}
// />
// </div>
// </div>
// </div>
// </>
// )
// }
// export default LineGrapInput
import React, { useEffect, useState } from "react";
import MultiLevelDropdown from "../../../../ui/inputs/MultiLevelDropDown";
import { AddIcon } from "../../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import useChartStore from "../../../../../store/useChartStore";
import axios from "axios";
type Props = {};
const LineGrapInput = (props: Props) => { const LineGrapInput = (props: Props) => {
const [dropDowndata, setDropDownData] = useState({})
const [selections, setSelections] = useState<Record<string, { name: string, fields: string }>>({})
const [selectedOption, setSelectedOption] = useState('1h')
const { measurements, setMeasurements, updateDuration, duration } = useChartStore(); const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
const handleSelectDuration = (option: string) => { const [dropDowndata, setDropDownData] = useState({});
updateDuration(option); // Normalize for key matching const [selections, setSelections] = useState<Record<string, { name: string; fields: string }>>(measurements);
}; const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
const response = await axios.get('http://192.168.0.192:5010/getinput'); const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) { if (response.status === 200) {
console.log('dropdown data:', response.data); console.log("dropdown data:", response.data);
setDropDownData(response.data) setDropDownData(response.data);
} else { } else {
console.log('Unexpected response:', response); console.log("Unexpected response:", response);
} }
} catch (error) { } catch (error) {
console.error('There was an error!', error); console.error("There was an error!", error);
} }
}; };
fetchZoneData(); fetchZoneData();
}, []); }, []);
// Sync Zustand state when component mounts
useEffect(() => { useEffect(() => {
console.log(selections); setSelections(measurements);
}, [selections]) }, [measurements]);
const handleSelect = (inputKey: string, selectedData: { name: string, fields: string } | null) => { const handleSelect = (inputKey: string, selectedData: { name: string; fields: string } | null) => {
setSelections(prev => { setSelections((prev) => {
if (selectedData === null) {
const newSelections = { ...prev }; const newSelections = { ...prev };
if (selectedData === null) {
delete newSelections[inputKey]; delete newSelections[inputKey];
return newSelections;
} else { } else {
return { newSelections[inputKey] = selectedData;
...prev,
[inputKey]: selectedData
};
} }
setMeasurements(newSelections); // Update Zustand store
return newSelections;
}); });
}; };
interface Measurement { const handleSelectDuration = (option: string) => {
name: string; updateDuration(option);
fields: string;
}
interface InputData {
[key: string]: Measurement;
}
const extractMeasurements = (input: InputData): Measurement[] => {
return Object.values(input);
}; };
useEffect(() => {
const measurementsData = extractMeasurements(selections);
setMeasurements(measurementsData);
}, [selections]);
return ( return (
<> <>
<div className="inputs-wrapper"> <div className="inputs-wrapper">
@ -85,7 +185,7 @@ const LineGrapInput = (props: Props) => {
data={dropDowndata} data={dropDowndata}
onSelect={(selectedData) => handleSelect(inputKey, selectedData)} onSelect={(selectedData) => handleSelect(inputKey, selectedData)}
onUnselect={() => handleSelect(inputKey, null)} onUnselect={() => handleSelect(inputKey, null)}
selectedValue={selections[inputKey]} selectedValue={selections[inputKey]} // Load from Zustand
/> />
<div className="icon"> <div className="icon">
<AddIcon /> <AddIcon />
@ -97,7 +197,7 @@ const LineGrapInput = (props: Props) => {
</div> </div>
<div> <div>
<div className="datas"> <div className="datas">
<div className="datas__label">duration</div> <div className="datas__label">Duration</div>
<div className="datas__class"> <div className="datas__class">
<RegularDropDown <RegularDropDown
header={duration} header={duration}
@ -109,7 +209,7 @@ const LineGrapInput = (props: Props) => {
</div> </div>
</div> </div>
</> </>
) );
} };
export default LineGrapInput export default LineGrapInput;

View File

@ -8,11 +8,12 @@ type Props = {}
const PieChartInput = (props: Props) => { const PieChartInput = (props: Props) => {
const [dropDowndata, setDropDownData] = useState({}) const [dropDowndata, setDropDownData] = useState({})
const [selections, setSelections] = useState<Record<string, {name: string, fields: string}>>({}) const [selections, setSelections] = useState<Record<string, {name: string, fields: string}>>({})
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
useEffect(() => { useEffect(() => {
const fetchZoneData = async () => { const fetchZoneData = async () => {
try { try {
const response = await axios.get('http://192.168.0.192:5010/getinput'); const response = await axios.get(`http://${iotApiUrl}/getinput`);
if (response.status === 200) { if (response.status === 200) {
console.log('dropdown data:', response.data); console.log('dropdown data:', response.data);
setDropDownData(response.data) setDropDownData(response.data)

View File

@ -16,6 +16,7 @@ interface ButtonsProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -33,6 +34,7 @@ interface ButtonsProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -120,25 +122,41 @@ const AddButtons: React.FC<ButtonsProps> = ({
console.log("updatedZone: ", updatedZone); console.log("updatedZone: ", updatedZone);
setSelectedZone(updatedZone); setSelectedZone(updatedZone);
} else { } else {
// If the panel is not active, activate it const updatePanelData = async () => {
const newActiveSides = [...selectedZone.activeSides, side]; try {
// Get email and organization safely
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0] || "defaultOrg"; // Fallback value
// Prevent duplicate side entries
const newActiveSides = selectedZone.activeSides.includes(side)
? [...selectedZone.activeSides]
: [...selectedZone.activeSides, side];
const updatedZone = { const updatedZone = {
...selectedZone, ...selectedZone,
activeSides: newActiveSides, activeSides: newActiveSides,
panelOrder: newActiveSides, panelOrder: newActiveSides,
}; };
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
// let response = panelData(organization, selectedZone.zoneId, newActiveSides)
// console.log('response: ', response);
// Update the selectedZone state // API call
const response = await panelData(organization, selectedZone.zoneId, newActiveSides);
console.log("response: ", response);
// Update state
console.log("updatedZone: ", updatedZone); console.log("updatedZone: ", updatedZone);
setSelectedZone(updatedZone); setSelectedZone(updatedZone);
} catch (error) {
console.error("Error updating panel data:", error);
} }
}; };
updatePanelData(); // Call the async function
}
};
return ( return (
<> <>
<div> <div>
@ -146,8 +164,7 @@ const AddButtons: React.FC<ButtonsProps> = ({
<div key={side} className={`side-button-container ${side}`}> <div key={side} className={`side-button-container ${side}`}>
{/* "+" Button */} {/* "+" Button */}
<button <button
className={`side-button ${side}${ className={`side-button ${side}${selectedZone.activeSides.includes(side) ? " active" : ""
selectedZone.activeSides.includes(side) ? " active" : ""
}`} }`}
onClick={() => handlePlusButtonClick(side)} onClick={() => handlePlusButtonClick(side)}
title={ title={
@ -166,8 +183,7 @@ const AddButtons: React.FC<ButtonsProps> = ({
<div className="extra-Bs"> <div className="extra-Bs">
{/* Hide Panel */} {/* Hide Panel */}
<div <div
className={`icon ${ className={`icon ${hiddenPanels.includes(side) ? "active" : ""
hiddenPanels.includes(side) ? "active" : ""
}`} }`}
title={ title={
hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel" hiddenPanels.includes(side) ? "Show Panel" : "Hide Panel"
@ -190,8 +206,7 @@ const AddButtons: React.FC<ButtonsProps> = ({
{/* Lock/Unlock Panel */} {/* Lock/Unlock Panel */}
<div <div
className={`icon ${ className={`icon ${selectedZone.lockedPanels.includes(side) ? "active" : ""
selectedZone.lockedPanels.includes(side) ? "active" : ""
}`} }`}
title={ title={
selectedZone.lockedPanels.includes(side) selectedZone.lockedPanels.includes(side)

View File

@ -3,6 +3,7 @@ import { Widget } from "../../../store/useWidgetStore";
import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons"; import { MoveArrowLeft, MoveArrowRight } from "../../icons/SimulationIcons";
import { InfoIcon } from "../../icons/ExportCommonIcons"; import { InfoIcon } from "../../icons/ExportCommonIcons";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
import { getSelect2dZoneData } from "../../../services/realTimeVisulization/zoneData/getSelect2dZoneData";
// Define the type for `Side` // Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -12,6 +13,7 @@ interface DisplayZoneProps {
[key: string]: { [key: string]: {
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
widgets: Widget[]; widgets: Widget[];
zoneId: string; zoneId: string;
@ -23,6 +25,7 @@ interface DisplayZoneProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -40,6 +43,7 @@ interface DisplayZoneProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -60,6 +64,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
selectedZone, selectedZone,
setSelectedZone, setSelectedZone,
}) => { }) => {
// Ref for the container element // Ref for the container element
const containerRef = useRef<HTMLDivElement | null>(null); const containerRef = useRef<HTMLDivElement | null>(null);
@ -141,6 +146,24 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
} }
}; };
async function handleSelect2dZoneData(zoneId: string, zoneName: string) {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
let response = await getSelect2dZoneData(zoneId, organization)
setSelectedZone({
zoneName,
activeSides: response.activeSides,
panelOrder: response.panelOrder,
lockedPanels: response.lockedPanels,
widgets: response.widgets,
zoneId: zoneId,
zoneViewPortTarget:
response.viewPortCenter,
zoneViewPortPosition:
response.viewPortposition,
});
}
return ( return (
<div <div
ref={containerRef} ref={containerRef}
@ -161,23 +184,11 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
{Object.keys(zonesData).map((zoneName, index) => ( {Object.keys(zonesData).map((zoneName, index) => (
<div <div
key={index} key={index}
className={`zone ${ className={`zone ${selectedZone.zoneName === zoneName ? "active" : ""
selectedZone.zoneName === zoneName ? "active" : ""
}`} }`}
onClick={() => { onClick={() => {
useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId); useDroppedObjectsStore.getState().setZone(zoneName, zonesData[zoneName]?.zoneId);
setSelectedZone({ handleSelect2dZoneData(zonesData[zoneName]?.zoneId, zoneName)
zoneName,
activeSides: zonesData[zoneName].activeSides || [],
panelOrder: zonesData[zoneName].panelOrder || [],
lockedPanels: zonesData[zoneName].lockedPanels || [],
widgets: zonesData[zoneName].widgets || [],
zoneId: zonesData[zoneName]?.zoneId || "",
zoneViewPortTarget:
zonesData[zoneName].zoneViewPortTarget || [],
zoneViewPortPosition:
zonesData[zoneName].zoneViewPortPosition || [],
});
}} }}
> >
{zoneName} {zoneName}

View File

@ -36,6 +36,7 @@ export const DraggableWidget = ({
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
widgets: Widget[]; widgets: Widget[];
}; };
@ -44,6 +45,7 @@ export const DraggableWidget = ({
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -78,9 +80,11 @@ export const DraggableWidget = ({
const isPanelHidden = hiddenPanels.includes(widget.panel); const isPanelHidden = hiddenPanels.includes(widget.panel);
const deleteSelectedChart = () => { const deleteSelectedChart = () => {
console.log('widget.id: ', widget.id);
const updatedWidgets = selectedZone.widgets.filter( const updatedWidgets = selectedZone.widgets.filter(
(w: Widget) => w.id !== widget.id (w: Widget) => w.id !== widget.id
); );
console.log('updatedWidgets: ', updatedWidgets);
setSelectedZone((prevZone: any) => ({ setSelectedZone((prevZone: any) => ({
...prevZone, ...prevZone,
@ -122,7 +126,6 @@ export const DraggableWidget = ({
...widget, ...widget,
id: `${widget.id}-copy-${Date.now()}`, id: `${widget.id}-copy-${Date.now()}`,
}; };
setSelectedZone((prevZone: any) => ({ setSelectedZone((prevZone: any) => ({
...prevZone, ...prevZone,
widgets: [...prevZone.widgets, duplicatedWidget], widgets: [...prevZone.widgets, duplicatedWidget],
@ -130,7 +133,7 @@ export const DraggableWidget = ({
setOpenKebabId(null); setOpenKebabId(null);
console.log("Duplicated widget with ID:", duplicatedWidget.id);
}; };
const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => { const handleKebabClick = (event: React.MouseEvent<HTMLDivElement>) => {
@ -173,6 +176,7 @@ export const DraggableWidget = ({
}; };
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => { const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); event.preventDefault();
const fromIndex = parseInt(event.dataTransfer.getData("text/plain"), 10); // Get the dragged widget's index const fromIndex = parseInt(event.dataTransfer.getData("text/plain"), 10); // Get the dragged widget's index
const toIndex = index; // The index of the widget where the drop occurred const toIndex = index; // The index of the widget where the drop occurred
@ -186,8 +190,7 @@ export const DraggableWidget = ({
<div <div
draggable draggable
key={widget.id} key={widget.id}
className={`chart-container ${ className={`chart-container ${selectedChartId?.id === widget.id && "activeChart"
selectedChartId?.id === widget.id && "activeChart"
}`} }`}
onPointerDown={handlePointerDown} onPointerDown={handlePointerDown}
onDragStart={handleDragStart} onDragStart={handleDragStart}
@ -208,8 +211,7 @@ export const DraggableWidget = ({
{openKebabId === widget.id && ( {openKebabId === widget.id && (
<div className="kebab-options" ref={widgetRef}> <div className="kebab-options" ref={widgetRef}>
<div <div
className={`edit btn ${ className={`edit btn ${isPanelFull(widget.panel) ? "btn-blur" : ""
isPanelFull(widget.panel) ? "btn-blur" : ""
}`} }`}
onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget} onClick={isPanelFull(widget.panel) ? undefined : duplicateWidget}
> >
@ -237,14 +239,14 @@ export const DraggableWidget = ({
title={widget.title} title={widget.title}
fontSize={widget.fontSize} fontSize={widget.fontSize}
fontWeight={widget.fontWeight} fontWeight={widget.fontWeight}
data={{ // data={{
measurements: [ // measurements: [
{ name: "testDevice", fields: "powerConsumption" }, // { name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" }, // { name: "furnace", fields: "powerConsumption" },
], // ],
interval: 1000, // interval: 1000,
duration: "1h", // duration: "1h",
}} // }}
/> />
)} )}
{widget.type === "bar" && ( {widget.type === "bar" && (
@ -253,14 +255,14 @@ export const DraggableWidget = ({
title={widget.title} title={widget.title}
fontSize={widget.fontSize} fontSize={widget.fontSize}
fontWeight={widget.fontWeight} fontWeight={widget.fontWeight}
data={{ // data={{
measurements: [ // measurements: [
{ name: "testDevice", fields: "powerConsumption" }, // { name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" }, // { name: "furnace", fields: "powerConsumption" },
], // ],
interval: 1000, // interval: 1000,
duration: "1h", // duration: "1h",
}} // }}
/> />
)} )}
{widget.type === "pie" && ( {widget.type === "pie" && (
@ -269,14 +271,14 @@ export const DraggableWidget = ({
title={widget.title} title={widget.title}
fontSize={widget.fontSize} fontSize={widget.fontSize}
fontWeight={widget.fontWeight} fontWeight={widget.fontWeight}
data={{ // data={{
measurements: [ // measurements: [
{ name: "testDevice", fields: "powerConsumption" }, // { name: "testDevice", fields: "powerConsumption" },
{ name: "furnace", fields: "powerConsumption" }, // { name: "furnace", fields: "powerConsumption" },
], // ],
interval: 1000, // interval: 1000,
duration: "1h", // duration: "1h",
}} // }}
/> />
)} )}
{widget.type === "doughnut" && ( {widget.type === "doughnut" && (

View File

@ -0,0 +1,79 @@
import { useThree } from "@react-three/fiber";
import React, { useState, useEffect } from "react";
import { useAsset3dWidget } from "../../../store/store";
import useModuleStore from "../../../store/useModuleStore";
import { ThreeState } from "../../../types/world/worldTypes";
import * as THREE from "three";
import Throughput from "../../layout/3D-cards/cards/Throughput";
import ProductionCapacity from "../../layout/3D-cards/cards/ProductionCapacity";
import ReturnOfInvestment from "../../layout/3D-cards/cards/ReturnOfInvestment";
import StateWorking from "../../layout/3D-cards/cards/StateWorking";
export default function Dropped3dWidgets() {
const { widgetSelect } = useAsset3dWidget();
const { activeModule } = useModuleStore();
const { raycaster, gl, scene }: ThreeState = useThree();
// 🔥 Store multiple instances per widget type
const [widgetPositions, setWidgetPositions] = useState<Record<string, [number, number, number][]>>({});
useEffect(() => {
if (activeModule !== "visualization") return;
const canvasElement = gl.domElement;
const onDrop = (event: DragEvent) => {
event.preventDefault(); // Prevent default browser behavior
if (!widgetSelect.startsWith("ui")) return;
const group1 = scene.getObjectByName("itemsGroup");
if (!group1) return;
const Assets = group1.children
.map((val) => scene.getObjectByProperty("uuid", val.uuid))
.filter(Boolean) as THREE.Object3D[];
const intersects = raycaster.intersectObjects(Assets);
if (intersects.length > 0) {
const { x, y, z } = intersects[0].point;
// ✅ Allow multiple instances by storing positions in an array
setWidgetPositions((prev) => ({
...prev,
[widgetSelect]: [...(prev[widgetSelect] || []), [x, y, z]],
}));
}
};
canvasElement.addEventListener("drop", onDrop);
return () => {
canvasElement.removeEventListener("drop", onDrop);
};
}, [widgetSelect, activeModule]);
return (
<>
{widgetPositions["ui-Widget 1"]?.map((pos, index) => (
<ProductionCapacity key={`Widget1-${index}`} position={pos} />
))}
{widgetPositions["ui-Widget 2"]?.map((pos, index) => (
<ReturnOfInvestment key={`Widget2-${index}`} position={pos} />
))}
{widgetPositions["ui-Widget 3"]?.map((pos, index) => (
<StateWorking key={`Widget3-${index}`} position={pos} />
))}
{widgetPositions["ui-Widget 4"]?.map((pos, index) => (
<Throughput key={`Widget4-${index}`} position={pos} />
))}
</>
);
}

View File

@ -8,7 +8,6 @@ import { useDroppedObjectsStore, Zones } from "../../../store/useDroppedObjectsS
const DroppedObjects: React.FC = () => { const DroppedObjects: React.FC = () => {
const zones = useDroppedObjectsStore((state) => state.zones); const zones = useDroppedObjectsStore((state) => state.zones);
const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition); const updateObjectPosition = useDroppedObjectsStore((state) => state.updateObjectPosition);
const [draggingIndex, setDraggingIndex] = useState<{ zone: string; index: number } | null>(null); const [draggingIndex, setDraggingIndex] = useState<{ zone: string; index: number } | null>(null);
const [offset, setOffset] = useState<[number, number] | null>(null); const [offset, setOffset] = useState<[number, number] | null>(null);

View File

@ -3,6 +3,7 @@ import { useWidgetStore } from "../../../store/useWidgetStore";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { DraggableWidget } from "./DraggableWidget"; import { DraggableWidget } from "./DraggableWidget";
import { arrayMove } from "@dnd-kit/sortable"; import { arrayMove } from "@dnd-kit/sortable";
import { addingWidgets } from "../../../services/realTimeVisulization/zoneData/addWidgets";
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -19,6 +20,7 @@ interface PanelProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -30,6 +32,7 @@ interface PanelProps {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -72,8 +75,7 @@ const Panel: React.FC<PanelProps> = ({
case "top": case "top":
case "bottom": case "bottom":
return { return {
width: `calc(100% - ${ width: `calc(100% - ${(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
}px)`, }px)`,
height: `${panelSize - 2}px`, height: `${panelSize - 2}px`,
left: leftActive ? `${panelSize}px` : "0", left: leftActive ? `${panelSize}px` : "0",
@ -84,8 +86,7 @@ const Panel: React.FC<PanelProps> = ({
case "right": case "right":
return { return {
width: `${panelSize - 2}px`, width: `${panelSize - 2}px`,
height: `calc(100% - ${ height: `calc(100% - ${(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
}px)`, }px)`,
top: topActive ? `${panelSize}px` : "0", top: topActive ? `${panelSize}px` : "0",
bottom: bottomActive ? `${panelSize}px` : "0", bottom: bottomActive ? `${panelSize}px` : "0",
@ -99,6 +100,7 @@ const Panel: React.FC<PanelProps> = ({
); );
const handleDrop = (e: React.DragEvent, panel: Side) => { const handleDrop = (e: React.DragEvent, panel: Side) => {
e.preventDefault(); e.preventDefault();
const { draggedAsset } = useWidgetStore.getState(); const { draggedAsset } = useWidgetStore.getState();
if (!draggedAsset) return; if (!draggedAsset) return;
@ -109,8 +111,6 @@ const Panel: React.FC<PanelProps> = ({
if (currentWidgetsCount >= maxCapacity) return; if (currentWidgetsCount >= maxCapacity) return;
console.log("draggedAsset: ", draggedAsset);
console.log("panel: ", panel);
addWidgetToPanel(draggedAsset, panel); addWidgetToPanel(draggedAsset, panel);
}; };
@ -139,17 +139,27 @@ const Panel: React.FC<PanelProps> = ({
}; };
// while dublicate check this and add // while dublicate check this and add
const addWidgetToPanel = (asset: any, panel: Side) => { const addWidgetToPanel = async (asset: any, panel: Side) => {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
const newWidget = { const newWidget = {
...asset, ...asset,
id: generateUniqueId(), id: generateUniqueId(),
panel, panel,
}; };
try {
let response = await addingWidgets(selectedZone.zoneId, organization, newWidget);
console.log("response: ", response);
if (response.message === "Widget created successfully") {
setSelectedZone((prev) => ({ setSelectedZone((prev) => ({
...prev, ...prev,
widgets: [...prev.widgets, newWidget], widgets: [...prev.widgets, newWidget],
})); }));
}
} catch (error) {
console.error("Error adding widget:", error);
}
}; };
useEffect(() => { useEffect(() => {
@ -180,7 +190,7 @@ const Panel: React.FC<PanelProps> = ({
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => { const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
if (!selectedZone) return; // Ensure selectedZone is not null if (!selectedZone) return; // Ensure selectedZone is not null
console.log("selectedZone: ", selectedZone);
setSelectedZone((prev) => { setSelectedZone((prev) => {
if (!prev) return prev; // Ensure prev is not null if (!prev) return prev; // Ensure prev is not null

View File

@ -9,7 +9,9 @@ import useModuleStore from "../../../store/useModuleStore";
import DroppedObjects from "./DroppedFloatingWidgets"; import DroppedObjects from "./DroppedFloatingWidgets";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
import { useZones } from "../../../store/store"; import { useAsset3dWidget, useZones } from "../../../store/store";
import { getZoneData } from "../../../services/realTimeVisulization/zoneData/getZones";
import { getZone2dData } from "../../../services/realTimeVisulization/zoneData/getZoneData";
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -19,6 +21,7 @@ type FormattedZoneData = Record<
{ {
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];
@ -43,11 +46,18 @@ const RealTimeVisulization: React.FC = () => {
const [zonesData, setZonesData] = useState<FormattedZoneData>({}); const [zonesData, setZonesData] = useState<FormattedZoneData>({});
const { selectedZone, setSelectedZone } = useSelectedZoneStore(); const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { zones } = useZones() const { zones } = useZones()
const [floatingWidgets, setFloatingWidgets] = useState<Record<string, { zoneName: string; zoneId: string; objects: any[] }>>({});
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
useEffect(() => { useEffect(() => {
const data = Array.isArray(zones) ? zones : []; async function GetZoneData() {
const email = localStorage.getItem("email") || "";
const formattedData = data.reduce<FormattedZoneData>((acc, zone) => { const organization = email?.split("@")[1]?.split(".")[0];
try {
const response = await getZone2dData(organization);
if (!Array.isArray(response)) {
return;
}
const formattedData = response.reduce<FormattedZoneData>((acc, zone) => {
acc[zone.zoneName] = { acc[zone.zoneName] = {
activeSides: [], activeSides: [],
panelOrder: [], panelOrder: [],
@ -59,9 +69,15 @@ const RealTimeVisulization: React.FC = () => {
}; };
return acc; return acc;
}, {}); }, {});
setZonesData(formattedData); setZonesData(formattedData);
}, [zones]); } catch (error) {
console.log('error: ', error);
}
}
GetZoneData();
}, []); // Removed `zones` from dependencies
useEffect(() => { useEffect(() => {
setZonesData((prev) => { setZonesData((prev) => {
@ -82,10 +98,15 @@ const RealTimeVisulization: React.FC = () => {
}); });
}, [selectedZone]); }, [selectedZone]);
useEffect(() => {
}, [floatingWidgets])
const handleDrop = (event: React.DragEvent<HTMLDivElement>) => { const handleDrop = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault(); event.preventDefault();
const data = event.dataTransfer.getData("text/plain"); const data = event.dataTransfer.getData("text/plain");
if (!data || !selectedZone.zoneName) return; if (widgetSelect !== "") return;
if (!data || selectedZone.zoneName === "") return;
const droppedData = JSON.parse(data); const droppedData = JSON.parse(data);
const canvasElement = document.getElementById("real-time-vis-canvas"); const canvasElement = document.getElementById("real-time-vis-canvas");
@ -99,17 +120,22 @@ const RealTimeVisulization: React.FC = () => {
...droppedData, ...droppedData,
position: [relativeY, relativeX], // Y first because of top/left style position: [relativeY, relativeX], // Y first because of top/left style
}; };
console.log("newObject: ", newObject);
// Only set zone if its not already in the store (prevents overwriting objects) // Only set zone if its not already in the store (prevents overwriting objects)
const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName]; const existingZone = useDroppedObjectsStore.getState().zones[selectedZone.zoneName];
if (!existingZone) { if (!existingZone) {
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId); useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
} }
// Add the dropped object to the zone // Add the dropped object to the zone
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject); useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, newObject);
setFloatingWidgets((prevWidgets) => ({
...prevWidgets,
[selectedZone.zoneName]: {
...prevWidgets[selectedZone.zoneName],
zoneName: selectedZone.zoneName,
zoneId: selectedZone.zoneId,
objects: [...(prevWidgets[selectedZone.zoneName]?.objects || []), newObject],
},
}));
}; };
@ -123,6 +149,7 @@ const RealTimeVisulization: React.FC = () => {
width: isPlaying || activeModule !== "visualization" ? "100vw" : "", width: isPlaying || activeModule !== "visualization" ? "100vw" : "",
left: isPlaying || activeModule !== "visualization" ? "0%" : "", left: isPlaying || activeModule !== "visualization" ? "0%" : "",
}} }}
> >
<div <div
className="scene-container" className="scene-container"

View File

@ -1,6 +1,195 @@
import { useMemo } from "react"; // import React, { useEffect, useRef, useMemo, useState } from "react";
// import { Chart } from "chart.js/auto";
// import { useThemeStore } from "../../../../store/useThemeStore";
// import io from "socket.io-client";
// import { Bar } from 'react-chartjs-2';
// import useChartStore from "../../../../store/useChartStore";
// // WebSocket Connection
// // const socket = io("http://localhost:5000"); // Adjust to your backend URL
// interface ChartComponentProps {
// type: any;
// title: string;
// fontFamily?: string;
// fontSize?: string;
// fontWeight?: "Light" | "Regular" | "Bold";
// data: any;
// }
// const LineGraphComponent = ({
// type,
// title,
// fontFamily,
// fontSize,
// fontWeight = "Regular",
// data,
// }: ChartComponentProps) => {
// const canvasRef = useRef<HTMLCanvasElement>(null);
// const { themeColor } = useThemeStore();
// const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
// labels: [],
// datasets: [],
// });
// const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
// const defaultData = {
// labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
// datasets: [
// {
// label: "Dataset",
// data: [12, 19, 3, 5, 2, 3],
// backgroundColor: ["#6f42c1"],
// borderColor: "#ffffff",
// borderWidth: 2,
// },
// ],
// };
// // Memoize Theme Colors to Prevent Unnecessary Recalculations
// const buttonActionColor = useMemo(
// () => themeColor[0] || "#5c87df",
// [themeColor]
// );
// const buttonAbortColor = useMemo(
// () => themeColor[1] || "#ffffff",
// [themeColor]
// );
// // Memoize Font Weight Mapping
// const chartFontWeightMap = useMemo(
// () => ({
// Light: "lighter" as const,
// Regular: "normal" as const,
// Bold: "bold" as const,
// }),
// []
// );
// // Parse and Memoize Font Size
// const fontSizeValue = useMemo(
// () => (fontSize ? parseInt(fontSize) : 12),
// [fontSize]
// );
// // Determine and Memoize Font Weight
// const fontWeightValue = useMemo(
// () => chartFontWeightMap[fontWeight],
// [fontWeight, chartFontWeightMap]
// );
// // Memoize Chart Font Style
// const chartFontStyle = useMemo(
// () => ({
// family: fontFamily || "Arial",
// size: fontSizeValue,
// weight: fontWeightValue,
// }),
// [fontFamily, fontSizeValue, fontWeightValue]
// );
// // Memoize Chart Data
// // const data = useMemo(() => propsData, [propsData]);
// // Memoize Chart Options
// const options = useMemo(
// () => ({
// responsive: true,
// maintainAspectRatio: false,
// plugins: {
// title: {
// display: true,
// text: title,
// font: chartFontStyle,
// },
// legend: {
// display: false,
// },
// },
// scales: {
// x: {
// ticks: {
// display: true, // This hides the x-axis labels
// },
// },
// },
// }),
// [title, chartFontStyle]
// );
// const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
// useEffect(() => {
// const socket = io(`http://${iotApiUrl}`);
// if ( measurements.length > 0 ) {
// var inputes = {
// measurements: measurements,
// duration: duration,
// interval: 1000,
// }
// // Start stream
// const startStream = () => {
// socket.emit("lineInput", inputes);
// }
// socket.on('connect', startStream);
// socket.on("lineOutput", (response) => {
// const responceData = response.data;
// console.log("Received data:", responceData);
// // Extract timestamps and values
// const labels = responceData.time;
// const datasets = measurements.map((measurement: any) => {
// const key = `${measurement.name}.${measurement.fields}`;
// return {
// label: key,
// data: responceData[key]?.values ?? [], // Ensure it exists
// backgroundColor: "#6f42c1",
// borderColor: "#ffffff",
// };
// });
// setChartData({ labels, datasets });
// });
// }
// return () => {
// socket.off("lineOutput");
// socket.emit("stop_stream"); // Stop streaming when component unmounts
// };
// }, [measurements, duration]);
// // useEffect(() => {
// // if (!canvasRef.current) return;
// // const ctx = canvasRef.current.getContext("2d");
// // if (!ctx) return;
// // const chart = new Chart(ctx, {
// // type,
// // data: chartData,
// // options: options,
// // });
// // return () => chart.destroy();
// // }, [chartData, type, title]);
// return <Bar data={measurements && measurements.length > 0 ? chartData : defaultData} options={options} />;
// };
// export default LineGraphComponent;
import React, { useEffect, useMemo, useState } from "react";
import { Bar } from "react-chartjs-2"; import { Bar } from "react-chartjs-2";
import io from "socket.io-client";
import { useThemeStore } from "../../../../store/useThemeStore";
import useChartStore from "../../../../store/useChartStore";
interface ChartComponentProps { interface ChartComponentProps {
type: any; type: any;
@ -8,16 +197,42 @@ interface ChartComponentProps {
fontFamily?: string; fontFamily?: string;
fontSize?: string; fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold"; fontWeight?: "Light" | "Regular" | "Bold";
data: any;
} }
const LineGraphComponent = ({ const BarGraphComponent = ({
type,
title, title,
fontFamily, fontFamily,
fontSize, fontSize,
fontWeight = "Regular", fontWeight = "Regular",
}: ChartComponentProps) => { }: ChartComponentProps) => {
// Memoize Font Weight Mapping const { themeColor } = useThemeStore();
const { measurements, duration } = useChartStore(); // Zustand Store
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [],
datasets: [],
});
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const defaultData = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#b392f0",
borderWidth: 1,
},
],
};
// Memoize Theme Colors
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
// Memoize Font Styling
const chartFontWeightMap = useMemo( const chartFontWeightMap = useMemo(
() => ({ () => ({
Light: "lighter" as const, Light: "lighter" as const,
@ -27,19 +242,9 @@ const LineGraphComponent = ({
[] []
); );
// Parse and Memoize Font Size const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
const fontSizeValue = useMemo( const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo( const chartFontStyle = useMemo(
() => ({ () => ({
family: fontFamily || "Arial", family: fontFamily || "Arial",
@ -49,6 +254,7 @@ const LineGraphComponent = ({
[fontFamily, fontSizeValue, fontWeightValue] [fontFamily, fontSizeValue, fontWeightValue]
); );
// Memoized Chart Options
const options = useMemo( const options = useMemo(
() => ({ () => ({
responsive: true, responsive: true,
@ -66,7 +272,7 @@ const LineGraphComponent = ({
scales: { scales: {
x: { x: {
ticks: { ticks: {
display: false, // This hides the x-axis labels display: true,
}, },
}, },
}, },
@ -74,21 +280,53 @@ const LineGraphComponent = ({
[title, chartFontStyle] [title, chartFontStyle]
); );
const chartData = { useEffect(() => {
labels: ["January", "February", "March", "April", "May", "June", "July"], if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
datasets: [
{ const socket = io(`http://${iotApiUrl}`);
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40], const inputData = {
backgroundColor: "#6f42c1", measurements,
borderColor: "#ffffff", duration,
borderWidth: 2, interval: 1000,
fill: false,
},
],
}; };
return <Bar data={chartData} options={options} />; const startStream = () => {
socket.emit("lineInput", inputData);
};
socket.on("connect", startStream);
socket.on("lineOutput", (response) => {
const responseData = response.data;
console.log("Received data:", responseData);
// Extract timestamps and values
const labels = responseData.time;
const datasets = Object.keys(measurements).map((key) => {
const measurement = measurements[key];
const datasetKey = `${measurement.name}.${measurement.fields}`;
return {
label: datasetKey,
data: responseData[datasetKey]?.values ?? [],
backgroundColor: "#6f42c1",
borderColor: "#b392f0",
borderWidth: 1,
};
});
setChartData({ labels, datasets });
});
return () => {
socket.off("lineOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
};
}, [measurements, duration, iotApiUrl]);
return <Bar data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
}; };
export default LineGraphComponent; export default BarGraphComponent;

View File

@ -1,115 +1,15 @@
// import { useMemo } from "react"; import React, { useEffect, useMemo, useState } from "react";
// import { Line } from "react-chartjs-2"; import { Line } from "react-chartjs-2";
// interface ChartComponentProps {
// type: any;
// title: string;
// fontFamily?: string;
// fontSize?: string;
// fontWeight?: "Light" | "Regular" | "Bold";
// data: any;
// }
// const LineGraphComponent = ({
// title,
// fontFamily,
// fontSize,
// fontWeight = "Regular",
// }: ChartComponentProps) => {
// // Memoize Font Weight Mapping
// const chartFontWeightMap = useMemo(
// () => ({
// Light: "lighter" as const,
// Regular: "normal" as const,
// Bold: "bold" as const,
// }),
// []
// );
// // Parse and Memoize Font Size
// const fontSizeValue = useMemo(
// () => (fontSize ? parseInt(fontSize) : 12),
// [fontSize]
// );
// // Determine and Memoize Font Weight
// const fontWeightValue = useMemo(
// () => chartFontWeightMap[fontWeight],
// [fontWeight, chartFontWeightMap]
// );
// // Memoize Chart Font Style
// const chartFontStyle = useMemo(
// () => ({
// family: fontFamily || "Arial",
// size: fontSizeValue,
// weight: fontWeightValue,
// }),
// [fontFamily, fontSizeValue, fontWeightValue]
// );
// const options = useMemo(
// () => ({
// responsive: true,
// maintainAspectRatio: false,
// plugins: {
// title: {
// display: true,
// text: title,
// font: chartFontStyle,
// },
// legend: {
// display: false,
// },
// },
// scales: {
// x: {
// ticks: {
// display: true, // This hides the x-axis labels
// },
// },
// },
// }),
// [title, chartFontStyle]
// );
// const chartData = {
// labels: ["January", "February", "March", "April", "May", "June", "July"],
// datasets: [
// {
// label: "My First Dataset",
// data: [65, 59, 80, 81, 56, 55, 40],
// backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple)
// borderColor: "#ffffff", // Keeping border color white
// borderWidth: 2,
// fill: false,
// },
// ],
// };
// return <Line data={chartData} options={options} />;
// };
// export default LineGraphComponent;
import React, { useEffect, useRef, useMemo, useState } from "react";
import { Chart } from "chart.js/auto";
import { useThemeStore } from "../../../../store/useThemeStore";
import io from "socket.io-client"; import io from "socket.io-client";
import { Line } from 'react-chartjs-2'; import { useThemeStore } from "../../../../store/useThemeStore";
import useChartStore from "../../../../store/useChartStore"; import useChartStore from "../../../../store/useChartStore";
// WebSocket Connection
// const socket = io("http://localhost:5000"); // Adjust to your backend URL
interface ChartComponentProps { interface ChartComponentProps {
type: any; type: any;
title: string; title: string;
fontFamily?: string; fontFamily?: string;
fontSize?: string; fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold"; fontWeight?: "Light" | "Regular" | "Bold";
data: any;
} }
const LineGraphComponent = ({ const LineGraphComponent = ({
@ -118,26 +18,34 @@ const LineGraphComponent = ({
fontFamily, fontFamily,
fontSize, fontSize,
fontWeight = "Regular", fontWeight = "Regular",
data,
}: ChartComponentProps) => { }: ChartComponentProps) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const { themeColor } = useThemeStore(); const { themeColor } = useThemeStore();
const { measurements, duration } = useChartStore(); // Zustand Store
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({ const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [], labels: [],
datasets: [], datasets: [],
}); });
// Memoize Theme Colors to Prevent Unnecessary Recalculations const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const buttonActionColor = useMemo(
() => themeColor[0] || "#5c87df",
[themeColor]
);
const buttonAbortColor = useMemo(
() => themeColor[1] || "#ffffff",
[themeColor]
);
// Memoize Font Weight Mapping const defaultData = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#b392f0",
borderWidth: 1,
},
],
};
// Memoize Theme Colors
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
// Memoize Font Styling
const chartFontWeightMap = useMemo( const chartFontWeightMap = useMemo(
() => ({ () => ({
Light: "lighter" as const, Light: "lighter" as const,
@ -147,19 +55,9 @@ const LineGraphComponent = ({
[] []
); );
// Parse and Memoize Font Size const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
const fontSizeValue = useMemo( const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo( const chartFontStyle = useMemo(
() => ({ () => ({
family: fontFamily || "Arial", family: fontFamily || "Arial",
@ -169,9 +67,6 @@ const LineGraphComponent = ({
[fontFamily, fontSizeValue, fontWeightValue] [fontFamily, fontSizeValue, fontWeightValue]
); );
// Memoize Chart Data
// const data = useMemo(() => propsData, [propsData]);
// Memoize Chart Options // Memoize Chart Options
const options = useMemo( const options = useMemo(
() => ({ () => ({
@ -198,67 +93,54 @@ const LineGraphComponent = ({
[title, chartFontStyle] [title, chartFontStyle]
); );
const { measurements, setMeasurements, updateDuration, duration } = useChartStore(); useEffect(() => {console.log(measurements);
},[measurements])
useEffect(() => { useEffect(() => {
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
const socket = io("http://192.168.0.192:5010"); const socket = io(`http://${iotApiUrl}`);
if ( measurements.length > 0 ) { const inputData = {
var inputes = { measurements,
measurements: measurements, duration,
duration: duration,
interval: 1000, interval: 1000,
} };
// Start stream
const startStream = () => { const startStream = () => {
socket.emit("lineInput", inputes); socket.emit("lineInput", inputData);
} };
socket.on('connect', startStream); socket.on("connect", startStream);
socket.on("lineOutput", (response) => { socket.on("lineOutput", (response) => {
const responceData = response.data; const responseData = response.data;
console.log("Received data:", responceData);
// Extract timestamps and values // Extract timestamps and values
const labels = responceData.time; const labels = responseData.time;
const datasets = measurements.map((measurement: any) => { const datasets = Object.keys(measurements).map((key) => {
const key = `${measurement.name}.${measurement.fields}`; const measurement = measurements[key];
const datasetKey = `${measurement.name}.${measurement.fields}`;
return { return {
label: key, label: datasetKey,
data: responceData[key]?.values ?? [], // Ensure it exists data: responseData[datasetKey]?.values ?? [],
backgroundColor: themeColor[0] || "#5c87df", backgroundColor: "#6f42c1",
borderColor: themeColor[1] || "#ffffff", borderColor: "#b392f0",
borderWidth: 1,
}; };
}); });
setChartData({ labels, datasets }); setChartData({ labels, datasets });
}); });
}
return () => { return () => {
socket.off("lineOutput"); socket.off("lineOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
}; };
}, [measurements, duration]); }, [measurements, duration, iotApiUrl]);
// useEffect(() => { return <Line data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
// if (!canvasRef.current) return;
// const ctx = canvasRef.current.getContext("2d");
// if (!ctx) return;
// const chart = new Chart(ctx, {
// type,
// data: chartData,
// options: options,
// });
// return () => chart.destroy();
// }, [chartData, type, title]);
return <Line data={chartData} options={options} />;
}; };
export default LineGraphComponent; export default LineGraphComponent;

View File

@ -1,5 +1,195 @@
import { useMemo } from "react"; // import React, { useEffect, useRef, useMemo, useState } from "react";
// import { Chart } from "chart.js/auto";
// import { useThemeStore } from "../../../../store/useThemeStore";
// import io from "socket.io-client";
// import { Pie } from 'react-chartjs-2';
// import useChartStore from "../../../../store/useChartStore";
// // WebSocket Connection
// // const socket = io("http://localhost:5000"); // Adjust to your backend URL
// interface ChartComponentProps {
// type: any;
// title: string;
// fontFamily?: string;
// fontSize?: string;
// fontWeight?: "Light" | "Regular" | "Bold";
// data: any;
// }
// const PieChartComponent = ({
// type,
// title,
// fontFamily,
// fontSize,
// fontWeight = "Regular",
// data,
// }: ChartComponentProps) => {
// const canvasRef = useRef<HTMLCanvasElement>(null);
// const { themeColor } = useThemeStore();
// const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
// labels: [],
// datasets: [],
// });
// const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
// const defaultData = {
// labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
// datasets: [
// {
// label: "Dataset",
// data: [12, 19, 3, 5, 2, 3],
// backgroundColor: ["#6f42c1"],
// borderColor: "#ffffff",
// borderWidth: 2,
// },
// ],
// };
// // Memoize Theme Colors to Prevent Unnecessary Recalculations
// const buttonActionColor = useMemo(
// () => themeColor[0] || "#6f42c1",
// [themeColor]
// );
// const buttonAbortColor = useMemo(
// () => themeColor[1] || "#ffffff",
// [themeColor]
// );
// // Memoize Font Weight Mapping
// const chartFontWeightMap = useMemo(
// () => ({
// Light: "lighter" as const,
// Regular: "normal" as const,
// Bold: "bold" as const,
// }),
// []
// );
// // Parse and Memoize Font Size
// const fontSizeValue = useMemo(
// () => (fontSize ? parseInt(fontSize) : 12),
// [fontSize]
// );
// // Determine and Memoize Font Weight
// const fontWeightValue = useMemo(
// () => chartFontWeightMap[fontWeight],
// [fontWeight, chartFontWeightMap]
// );
// // Memoize Chart Font Style
// const chartFontStyle = useMemo(
// () => ({
// family: fontFamily || "Arial",
// size: fontSizeValue,
// weight: fontWeightValue,
// }),
// [fontFamily, fontSizeValue, fontWeightValue]
// );
// // Memoize Chart Data
// // const data = useMemo(() => propsData, [propsData]);
// // Memoize Chart Options
// const options = useMemo(
// () => ({
// responsive: true,
// maintainAspectRatio: false,
// plugins: {
// title: {
// display: true,
// text: title,
// font: chartFontStyle,
// },
// legend: {
// display: false,
// },
// },
// scales: {
// // x: {
// // ticks: {
// // display: true, // This hides the x-axis labels
// // },
// // },
// },
// }),
// [title, chartFontStyle]
// );
// const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
// useEffect(() => {
// const socket = io(`http://${iotApiUrl}`);
// if ( measurements.length > 0 ) {
// var inputes = {
// measurements: measurements,
// duration: duration,
// interval: 1000,
// }
// // Start stream
// const startStream = () => {
// socket.emit("lineInput", inputes);
// }
// socket.on('connect', startStream);
// socket.on("lineOutput", (response) => {
// const responceData = response.data;
// console.log("Received data:", responceData);
// // Extract timestamps and values
// const labels = responceData.time;
// const datasets = measurements.map((measurement: any) => {
// const key = `${measurement.name}.${measurement.fields}`;
// return {
// label: key,
// data: responceData[key]?.values ?? [], // Ensure it exists
// backgroundColor: "#6f42c1",
// borderColor: "#ffffff",
// };
// });
// setChartData({ labels, datasets });
// });
// }
// return () => {
// socket.off("lineOutput");
// socket.emit("stop_stream"); // Stop streaming when component unmounts
// };
// }, [measurements, duration]);
// // useEffect(() => {
// // if (!canvasRef.current) return;
// // const ctx = canvasRef.current.getContext("2d");
// // if (!ctx) return;
// // const chart = new Chart(ctx, {
// // type,
// // data: chartData,
// // options: options,
// // });
// // return () => chart.destroy();
// // }, [chartData, type, title]);
// return <Pie data={measurements && measurements.length > 0 ? chartData : defaultData} options={options} />;
// };
// export default PieChartComponent;
import React, { useEffect, useMemo, useState } from "react";
import { Pie } from "react-chartjs-2"; import { Pie } from "react-chartjs-2";
import io from "socket.io-client";
import { useThemeStore } from "../../../../store/useThemeStore";
import useChartStore from "../../../../store/useChartStore";
interface ChartComponentProps { interface ChartComponentProps {
type: any; type: any;
@ -7,16 +197,42 @@ interface ChartComponentProps {
fontFamily?: string; fontFamily?: string;
fontSize?: string; fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold"; fontWeight?: "Light" | "Regular" | "Bold";
data: any;
} }
const PieChartComponent = ({ const PieChartComponent = ({
type,
title, title,
fontFamily, fontFamily,
fontSize, fontSize,
fontWeight = "Regular", fontWeight = "Regular",
}: ChartComponentProps) => { }: ChartComponentProps) => {
// Memoize Font Weight Mapping const { themeColor } = useThemeStore();
const { measurements, duration } = useChartStore(); // Zustand Store
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
labels: [],
datasets: [],
});
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const defaultData = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#b392f0",
borderWidth: 1,
},
],
};
// Memoize Theme Colors
const buttonActionColor = useMemo(() => themeColor[0] || "#5c87df", [themeColor]);
const buttonAbortColor = useMemo(() => themeColor[1] || "#ffffff", [themeColor]);
// Memoize Font Styling
const chartFontWeightMap = useMemo( const chartFontWeightMap = useMemo(
() => ({ () => ({
Light: "lighter" as const, Light: "lighter" as const,
@ -26,19 +242,9 @@ const PieChartComponent = ({
[] []
); );
// Parse and Memoize Font Size const fontSizeValue = useMemo(() => (fontSize ? parseInt(fontSize) : 12), [fontSize]);
const fontSizeValue = useMemo( const fontWeightValue = useMemo(() => chartFontWeightMap[fontWeight], [fontWeight, chartFontWeightMap]);
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo( const chartFontStyle = useMemo(
() => ({ () => ({
family: fontFamily || "Arial", family: fontFamily || "Arial",
@ -48,12 +254,7 @@ const PieChartComponent = ({
[fontFamily, fontSizeValue, fontWeightValue] [fontFamily, fontSizeValue, fontWeightValue]
); );
// Access the CSS variable for the primary accent color // Memoized Chart Options
const accentColor = getComputedStyle(document.documentElement)
.getPropertyValue("--accent-color")
.trim();
console.log("accentColor: ", accentColor);
const options = useMemo( const options = useMemo(
() => ({ () => ({
responsive: true, responsive: true,
@ -68,24 +269,64 @@ const PieChartComponent = ({
display: false, display: false,
}, },
}, },
scales: {
// x: {
// ticks: {
// display: true,
// },
// },
},
}), }),
[title, chartFontStyle] [title, chartFontStyle]
); );
const chartData = { useEffect(() => {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"], if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
datasets: [
{ const socket = io(`http://${iotApiUrl}`);
label: "Dataset",
data: [12, 19, 3, 5, 2, 3], const inputData = {
backgroundColor: ["#6f42c1"], measurements,
borderColor: "#ffffff", duration,
borderWidth: 2, interval: 1000,
},
],
}; };
return <Pie data={chartData} options={options} />; const startStream = () => {
socket.emit("lineInput", inputData);
};
socket.on("connect", startStream);
socket.on("lineOutput", (response) => {
const responseData = response.data;
console.log("Received data:", responseData);
// Extract timestamps and values
const labels = responseData.time;
const datasets = Object.keys(measurements).map((key) => {
const measurement = measurements[key];
const datasetKey = `${measurement.name}.${measurement.fields}`;
return {
label: datasetKey,
data: responseData[datasetKey]?.values ?? [],
backgroundColor: "#6f42c1",
borderColor: "#b392f0",
borderWidth: 1,
};
});
setChartData({ labels, datasets });
});
return () => {
socket.off("lineOutput");
socket.emit("stop_stream"); // Stop streaming when component unmounts
socket.disconnect();
};
}, [measurements, duration, iotApiUrl]);
return <Pie data={Object.keys(measurements).length > 0 ? chartData : defaultData} options={options} />;
}; };
export default PieChartComponent; export default PieChartComponent;

View File

@ -9,7 +9,7 @@ const ProgressCard = ({
}) => ( }) => (
<div className="chart progressBar"> <div className="chart progressBar">
<div className="header">{title}</div> <div className="header">{title}</div>
{data?.stocks.map((stock, index) => ( {data?.stocks?.map((stock, index) => (
<div key={index} className="stock"> <div key={index} className="stock">
<span className="stock-item"> <span className="stock-item">
<span className="stockValues"> <span className="stockValues">

View File

@ -34,15 +34,11 @@ export default function NavMeshDetails({
const [positions, indices] = getPositionsAndIndices(meshes); const [positions, indices] = getPositionsAndIndices(meshes);
const cs = 0.5; const cs = 0.25;
const ch = 0.5; const ch = 0.5;
const walkableRadius = 0.89; const walkableRadius = 0.5;
const { success, navMesh } = generateSoloNavMesh(positions, indices, { const { success, navMesh } = generateSoloNavMesh(positions, indices, { cs, ch, walkableRadius: Math.round(walkableRadius / ch), });
cs,
ch,
walkableRadius: Math.round(walkableRadius / ch),
});
if (!success || !navMesh) { if (!success || !navMesh) {
return; return;
@ -53,7 +49,7 @@ export default function NavMeshDetails({
const debugDrawer = new DebugDrawer(); const debugDrawer = new DebugDrawer();
debugDrawer.drawNavMesh(navMesh); debugDrawer.drawNavMesh(navMesh);
// scene.add(debugDrawer); // scene.add(debugDrawer);
} catch (error) {} } catch (error) { }
}; };
initializeNavigation(); initializeNavigation();

View File

@ -86,7 +86,7 @@ export default function PathNavigator({
return ( return (
<> <>
{path.length > 0 && <Line points={path} color="blue" lineWidth={3} />} {/* {path.length > 0 && <Line points={path} color="blue" lineWidth={3} />} */}
{path.length > 0 && ( {path.length > 0 && (
<mesh ref={meshRef} position={path.length > 0 ? path[0] : [0, 0.1, 0]}> <mesh ref={meshRef} position={path.length > 0 ? path[0] : [0, 0.1, 0]}>
<boxGeometry args={[1, 1, 1]} /> <boxGeometry args={[1, 1, 1]} />

View File

@ -48,61 +48,41 @@ export default function PolygonGenerator({
const polygons = turf.polygonize(turf.featureCollection(lineFeatures)); const polygons = turf.polygonize(turf.featureCollection(lineFeatures));
renderWallGeometry(wallPoints); renderWallGeometry(wallPoints);
let union: any = []; if (polygons.features.length > 1) {
polygons.features.forEach((feature) => { polygons.features.forEach((feature) => {
union.push(feature); if (feature.geometry.type === "Polygon") {
const shape = new THREE.Shape();
const coords = feature.geometry.coordinates[0];
shape.moveTo(coords[0][0], coords[0][1]);
for (let i = 1; i < coords.length; i++) {
shape.lineTo(coords[i][0], coords[i][1]);
}
shape.lineTo(coords[0][0], coords[0][1]);
const extrudeSettings = {
depth: 5,
bevelEnabled: false,
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const material = new THREE.MeshBasicMaterial({ color: "blue", transparent: true, opacity: 0.5 });
const mesh = new THREE.Mesh(geometry, material);
mesh.rotateX(Math.PI / 2);
mesh.name = "agv-collider";
mesh.position.y = 5;
mesh.receiveShadow = true;
groupRef.current?.add(mesh);
}
}); });
if (union.length > 1) {
const unionResult = turf.union(turf.featureCollection(union));
if (unionResult?.geometry.type === "MultiPolygon") {
unionResult.geometry.coordinates.forEach((poly) => {
const coordinates = poly[0].map(([x, z]) => {
return new THREE.Vector3(x, 0, z);
});
renderBoxGeometry(coordinates);
});
} else if (unionResult?.geometry.type === "Polygon") {
const coordinates = unionResult.geometry.coordinates[0].map(
([x, z]) => {
return new THREE.Vector3(x, 0, z);
}
);
renderBoxGeometry(coordinates);
}
} else if (union.length === 1) {
const coordinates = union[0].geometry.coordinates[0].map(
([x, z]: [number, number]) => {
return new THREE.Vector3(x, 0, z);
}
);
// setRooms((prevRooms) => [...prevRooms, coordinates]);
} }
}, [lines.current]); }, [lines.current]);
const renderBoxGeometry = (coordinates: THREE.Vector3[]) => {
const minX = Math.min(...coordinates.map((p) => p.x));
const maxX = Math.max(...coordinates.map((p) => p.x));
const minZ = Math.min(...coordinates.map((p) => p.z));
const maxZ = Math.max(...coordinates.map((p) => p.z));
const width = maxX - minX;
const depth = maxZ - minZ;
const height = 3;
const geometry = new THREE.BoxGeometry(width, height, depth);
const material = new THREE.MeshBasicMaterial({
color: "#ff66cc",
visible: false,
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set((minX + maxX) / 2, height / 2, (minZ + maxZ) / 2);
groupRef.current?.add(mesh);
};
const renderWallGeometry = (walls: THREE.Vector3[][]) => { const renderWallGeometry = (walls: THREE.Vector3[][]) => {
walls.forEach((wall) => { walls.forEach((wall) => {
if (wall.length < 2) return; if (wall.length < 2) return;

View File

@ -1,5 +1,5 @@
import { useFrame, useThree } from "@react-three/fiber"; import { useFrame, useThree } from "@react-three/fiber";
import { useActiveTool, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useLoadingProgress, useRenderDistance, useselectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode } from "../../../store/store"; import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useLoadingProgress, useRenderDistance, useselectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode } from "../../../store/store";
import assetVisibility from "../geomentries/assets/assetVisibility"; import assetVisibility from "../geomentries/assets/assetVisibility";
import { useEffect } from "react"; import { useEffect } from "react";
import * as THREE from "three"; import * as THREE from "three";
@ -33,7 +33,6 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
const { setLoadingProgress } = useLoadingProgress(); const { setLoadingProgress } = useLoadingProgress();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const loader = new GLTFLoader(); const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader(); const dracoLoader = new DRACOLoader();
@ -306,6 +305,8 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
}; };
}, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]); }, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]);
useFrame(() => { useFrame(() => {
if (controls) if (controls)
assetVisibility(itemsGroup, state.camera.position, renderDistance); assetVisibility(itemsGroup, state.camera.position, renderDistance);

View File

@ -224,6 +224,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
const onDrop = (event: any) => { const onDrop = (event: any) => {
if (!event.dataTransfer?.files[0]) return if (!event.dataTransfer?.files[0]) return
pointer.x = (event.clientX / window.innerWidth) * 2 - 1; pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1; pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera); raycaster.setFromCamera(pointer, camera);

View File

@ -112,7 +112,7 @@ const CamModelsGroup = () => {
socket.off("userDisConnectRespones"); socket.off("userDisConnectRespones");
socket.off("cameraUpdateResponse"); socket.off("cameraUpdateResponse");
}; };
}, [socket]); }, [socket, activeUsers]);
useFrame(() => { useFrame(() => {
if (!groupRef.current) return; if (!groupRef.current) return;

View File

@ -1,6 +1,10 @@
import React from "react"; import React, { Suspense, useEffect } from "react";
import assetImage from "../../assets/image/image.png"; import assetImage from "../../assets/image/image.png";
import { FiileedStarsIconSmall } from "../../components/icons/marketPlaceIcons"; import { FiileedStarsIconSmall } from "../../components/icons/marketPlaceIcons";
import { Canvas, useThree } from "@react-three/fiber";
import { ContactShadows, OrbitControls, Text } from "@react-three/drei";
import GltfLoader from "./GltfLoader";
import * as THREE from "three";
// Define the shape of the selected card // Define the shape of the selected card
interface SelectedCard { interface SelectedCard {
@ -17,6 +21,14 @@ interface AssetPreviewProps {
setSelectedCard: React.Dispatch<React.SetStateAction<SelectedCard | null>>; // Type for setter function setSelectedCard: React.Dispatch<React.SetStateAction<SelectedCard | null>>; // Type for setter function
} }
function Ui() {
return (
<Text color="#6f42c1" anchorX="center" anchorY="middle" scale={0.3}>
Loading your model...
</Text>
);
}
const AssetPreview: React.FC<AssetPreviewProps> = ({ const AssetPreview: React.FC<AssetPreviewProps> = ({
selectedCard, selectedCard,
setSelectedCard, setSelectedCard,
@ -27,8 +39,6 @@ const AssetPreview: React.FC<AssetPreviewProps> = ({
Math.min(5, isNaN(selectedCard.rating) ? 0 : selectedCard.rating) Math.min(5, isNaN(selectedCard.rating) ? 0 : selectedCard.rating)
); );
console.log("selectedCard: ", selectedCard);
// Ensure that the rating is a valid positive integer for array length // Ensure that the rating is a valid positive integer for array length
const starsArray = Array.from({ length: rating }, (_, index) => index); const starsArray = Array.from({ length: rating }, (_, index) => index);
@ -36,8 +46,38 @@ const AssetPreview: React.FC<AssetPreviewProps> = ({
<div className="assetPreview-wrapper"> <div className="assetPreview-wrapper">
<div className="assetPreview"> <div className="assetPreview">
<div className="image-preview"> <div className="image-preview">
<img src={assetImage} alt="" /> {/* <img src={assetImage} alt="" /> */}
{/* Add canvas here */} {/* Add canvas here */}
<div className="canvas-container" style={{ height: "100%" }}>
<Canvas
flat
shadows
color="#FFFFFF"
camera={{ fov: 75 }}
gl={{
preserveDrawingBuffer: true,
}}
onCreated={({ scene }) => {
scene.background = new THREE.Color(0xffffff);
}}
>
<Suspense fallback={<Ui />}>
{selectedCard.assetName && (
<GltfLoader fromServer={selectedCard.assetName} />
)}
<OrbitControls minPolarAngle={0} maxPolarAngle={Math.PI / 2} />
<ContactShadows
renderOrder={2}
frames={1}
resolution={1024}
scale={120}
blur={2}
opacity={0.4}
far={100}
/>
</Suspense>
</Canvas>
</div>
</div> </div>
<div className="asset-details-preview"> <div className="asset-details-preview">

View File

@ -8,6 +8,7 @@ import {
} from "../../components/icons/marketPlaceIcons"; } from "../../components/icons/marketPlaceIcons";
import assetImage from "../../assets/image/image.png"; import assetImage from "../../assets/image/image.png";
import { getAssetDownload } from "../../services/marketplace/getAssetDownload";
interface CardProps { interface CardProps {
assetName: string; assetName: string;
@ -40,9 +41,9 @@ const Card: React.FC<CardProps> = ({
return ( return (
<div className="card-container"> <div className="card-container">
<div className="icon"> {/* <a href={getAssetDownload(assetName)} download className="icon">
<DownloadIcon /> <DownloadIcon />
</div> </a> */}
<div className="image-container"> <div className="image-container">
<img src={image} alt={assetName} /> <img src={image} alt={assetName} />
</div> </div>

View File

@ -16,109 +16,11 @@ interface ModelData {
uploadDate: number; uploadDate: number;
_id: string; _id: string;
} }
interface ModelsProps {
models: ModelData[];
}
const CardsContainer: React.FC = () => { const CardsContainer: React.FC<ModelsProps> = ({ models }) => {
const [models, setModels] = useState<ModelData[]>([]);
const array = [
{
id: 1,
name: "Asset 1",
uploadedOn: "12 Jan 23",
price: 36500,
rating: 4.5,
views: 500,
},
{
id: 2,
name: "Asset 2",
uploadedOn: "14 Jan 23",
price: 45000,
rating: 4.0,
views: 500,
},
{
id: 3,
name: "Asset 3",
uploadedOn: "15 Jan 23",
price: 52000,
rating: 4.8,
views: 500,
},
{
id: 4,
name: "Asset 4",
uploadedOn: "18 Jan 23",
price: 37000,
rating: 3.9,
views: 500,
},
{
id: 5,
name: "Asset 5",
uploadedOn: "20 Jan 23",
price: 60000,
rating: 5.0,
views: 500,
},
{
id: 6,
name: "Asset 6",
uploadedOn: "22 Jan 23",
price: 46000,
rating: 4.2,
views: 500,
},
{
id: 7,
name: "Asset 7",
uploadedOn: "25 Jan 23",
price: 38000,
rating: 4.3,
views: 500,
},
{
id: 8,
name: "Asset 8",
uploadedOn: "27 Jan 23",
price: 41000,
rating: 4.1,
views: 500,
},
{
id: 9,
name: "Asset 9",
uploadedOn: "30 Jan 23",
price: 55000,
rating: 4.6,
views: 500,
},
{
id: 10,
name: "Asset 10",
uploadedOn: "2 Feb 23",
price: 49000,
rating: 4.4,
views: 500,
},
{
id: 11,
name: "Asset 11",
uploadedOn: "5 Feb 23",
price: 62000,
rating: 5.0,
views: 500,
},
{
id: 12,
name: "Asset 12",
uploadedOn: "7 Feb 23",
price: 53000,
rating: 4.7,
views: 500,
},
];
const [selectedCard, setSelectedCard] = useState<{ const [selectedCard, setSelectedCard] = useState<{
assetName: string; assetName: string;
uploadedOn: string; uploadedOn: string;
@ -136,33 +38,11 @@ const CardsContainer: React.FC = () => {
}) => { }) => {
setSelectedCard(cardData); setSelectedCard(cardData);
}; };
const getAllAssets = async () => {
try {
const assetsData = await fetchAssets();
const reversedData = [...assetsData]?.reverse().slice(0, 8);
setModels(reversedData);
} catch (error) {
} finally {
}
};
useEffect(() => {
getAllAssets();
}, []);
return ( return (
<div className="cards-container-container"> <div className="cards-container-container">
<div className="header">Products You May Like</div> <div className="header">Products You May Like</div>
<div className="cards-wrapper-container"> <div className="cards-wrapper-container">
{/* {array.map((asset) => (
<Card
key={asset.id}
assetName={asset.name}
uploadedOn={asset.uploadedOn}
price={asset.price}
rating={asset.rating}
views={asset.views}
onSelectCard={handleCardSelect}
/>
))} */}
{models.length > 0 && {models.length > 0 &&
models.map((assetDetail) => ( models.map((assetDetail) => (
<Card <Card
@ -171,7 +51,7 @@ const CardsContainer: React.FC = () => {
uploadedOn={assetDetail.uploadDate.toString()} uploadedOn={assetDetail.uploadDate.toString()}
price={36500} price={36500}
rating={4.5} rating={4.5}
views={500} views={800}
onSelectCard={handleCardSelect} onSelectCard={handleCardSelect}
image={assetDetail.thumbnail} image={assetDetail.thumbnail}
/> />

View File

@ -1,19 +1,69 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
// import RegularDropDown from "./ui/inputs/RegularDropDown"; // import RegularDropDown from "./ui/inputs/RegularDropDown";
import Search from "../../components/ui/inputs/Search"; import Search from "../../components/ui/inputs/Search";
import { StarsIcon } from "../../components/icons/marketPlaceIcons"; import { StarsIcon } from "../../components/icons/marketPlaceIcons";
import RegularDropDown from "../../components/ui/inputs/RegularDropDown"; import RegularDropDown from "../../components/ui/inputs/RegularDropDown";
import { getSortedAssets } from "../../services/marketplace/getSortedAssets";
const FilterSearch: React.FC = () => { interface ModelData {
CreatedBy: string;
animated: string | null;
category: string;
description: string;
filename: string;
isArchieve: boolean;
modelfileID: string;
tags: string;
thumbnail: string;
uploadDate: number;
_id: string;
}
interface ModelsProps {
models: ModelData[];
setModels: React.Dispatch<React.SetStateAction<ModelData[]>>;
filteredModels: ModelData[];
}
const FilterSearch: React.FC<ModelsProps> = ({
models,
setModels,
filteredModels,
}) => {
const [activeOption, setActiveOption] = useState("Sort by"); // State for active option const [activeOption, setActiveOption] = useState("Sort by"); // State for active option
console.log("filteredModels: ", filteredModels);
const handleSelect = (option: string) => { const handleSelect = (option: string) => {
setActiveOption(option); setActiveOption(option);
console.log("option: ", option);
// Alphabet ascending
// Alphabet descending
// All
}; };
useEffect(() => {
if (activeOption == "Alphabet ascending") {
let ascending = models
?.slice()
.sort((a, b) => a.filename.localeCompare(b.filename))
.map((val) => val);
setModels(ascending);
} else if (activeOption == "Alphabet descending") {
let descending = models
?.slice()
.sort((a, b) => b.filename.localeCompare(a.filename))
.map((val) => val);
setModels(descending);
}
}, [activeOption]);
const handleSearch = (val: string) => {
const filteredModel = filteredModels?.filter((model) =>
model.filename.toLowerCase().includes(val.toLowerCase())
);
setModels(filteredModel);
};
return ( return (
<div className="filter-search-container"> <div className="filter-search-container">
<Search onChange={() => {}} /> <Search onChange={handleSearch} />
<RegularDropDown <RegularDropDown
header={activeOption} header={activeOption}
options={["Alphabet ascending", "Alphabet descending", ""]} options={["Alphabet ascending", "Alphabet descending", ""]}

View File

@ -0,0 +1,79 @@
import { useRef, useEffect } from "react";
import { useFrame } from "@react-three/fiber";
import { Stage, useGLTF } from "@react-three/drei";
import { AnimationMixer, AnimationAction, Object3D } from "three";
import * as THREE from "three";
import { fetchGltfUrl } from "../../services/marketplace/fetchGltfUrl";
interface GltfLoaderProps {
glbdata?: boolean;
fromServer?: string;
setAnimations?: (animations?: string[]) => void;
selectedAnimation?: string;
setSelectedAnimation?: (animation: string) => void;
}
// const getGLTFUrl = (url: string) => url; // Placeholder for your actual function
const GltfLoader: React.FC<GltfLoaderProps> = ({
glbdata,
fromServer,
setAnimations,
selectedAnimation,
}) => {
const modelUrl: any = fromServer ? fetchGltfUrl(fromServer) : glbdata;
const { scene, animations } = useGLTF(modelUrl ?? "") as {
scene: Object3D;
animations: THREE.AnimationClip[];
};
const mixer = useRef<AnimationMixer | null>(
scene ? new AnimationMixer(scene) : null
);
const actions = useRef<Record<string, AnimationAction>>({});
useEffect(() => {
if (animations.length > 0 && mixer.current && setAnimations) {
const animationNames = animations.map((animation) => animation.name);
setAnimations(animationNames);
animations.forEach((animation) => {
const action = mixer.current!.clipAction(animation);
actions.current[animation.name] = action;
});
} else {
setAnimations && setAnimations([]);
}
}, [animations, setAnimations]);
useEffect(() => {
if (actions.current && selectedAnimation) {
const action = actions.current[selectedAnimation];
if (action) {
action.reset().fadeIn(0.5).play();
return () => {
action.fadeOut(0.5);
};
}
}
}, [selectedAnimation]);
useFrame((_, delta) => {
if (mixer.current) {
mixer.current.update(delta);
}
});
return (
<Stage
adjustCamera={false}
intensity={0.5}
shadows="contact"
environment="city"
>
<primitive object={scene} scale={1} />
</Stage>
);
};
export default GltfLoader;

View File

@ -1,14 +1,46 @@
import React from "react"; import React, { useEffect, useState } from "react";
import FilterSearch from "./FilterSearch"; import FilterSearch from "./FilterSearch";
import CardsContainer from "./CardsContainer"; import CardsContainer from "./CardsContainer";
import { fetchAssets } from "../../services/marketplace/fetchAssets";
import { getAssetImages } from "../../services/factoryBuilder/assest/assets/getAssetImages";
interface ModelData {
CreatedBy: string;
animated: string | null;
category: string;
description: string;
filename: string;
isArchieve: boolean;
modelfileID: string;
tags: string;
thumbnail: string;
uploadDate: number;
_id: string;
}
const MarketPlace = () => { const MarketPlace = () => {
const [models, setModels] = useState<ModelData[]>([]);
const [filteredModels, setFilteredModels] = useState<ModelData[]>([]);
useEffect(() => {
const filteredAssets = async () => {
try {
const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6");
setModels(filt.items);
setFilteredModels(filt.items);
} catch {}
};
filteredAssets();
}, []);
return ( return (
<div className="marketplace-wrapper"> <div className="marketplace-wrapper">
<div className="marketplace-container"> <div className="marketplace-container">
<div className="marketPlace"> <div className="marketPlace">
<FilterSearch /> <FilterSearch
<CardsContainer /> models={models}
setModels={setModels}
filteredModels={filteredModels}
/>
<CardsContainer models={models} />
</div> </div>
</div> </div>
</div> </div>

View File

@ -19,6 +19,8 @@ import DroppedObjects from "../../components/ui/componets/DroppedFloatingWidgets
// import Simulation from "./simulationtemp/simulation"; // import Simulation from "./simulationtemp/simulation";
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget"; import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
import ProductionCapacity from "../../components/layout/3D-cards/cards/ProductionCapacity";
import Dropped3dWidgets from "../../components/ui/componets/Dropped3dWidget";
export default function Scene() { export default function Scene() {
@ -43,13 +45,13 @@ export default function Scene() {
}} }}
> >
<Dropped3dWidgets/>
<Controls /> <Controls />
<TransformControl /> <TransformControl />
<SelectionControls /> <SelectionControls />
<MeasurementTool /> <MeasurementTool />
<World /> <World />
<ZoneCentreTarget /> <ZoneCentreTarget />
{/* <Simulation /> */}
<Simulation /> <Simulation />
<PostProcessing /> <PostProcessing />
<Sun /> <Sun />
@ -58,9 +60,6 @@ export default function Scene() {
<MqttEvents /> <MqttEvents />
<Environment files={background} environmentIntensity={1.5} /> <Environment files={background} environmentIntensity={1.5} />
</Canvas> </Canvas>
</KeyboardControls> </KeyboardControls>
); );
} }

View File

@ -1,43 +1,29 @@
import { useFloorItems } from '../../../store/store'; import { useFloorItems, useSimulationPaths } from '../../../store/store';
import * as THREE from 'three'; import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes'; import * as Types from '../../../types/world/worldTypes';
import { useEffect } from 'react'; import { useEffect } from 'react';
interface Path { function Behaviour() {
modeluuid: string; const { setSimulationPaths } = useSimulationPaths();
modelName: string;
points: {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | [];
triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | [];
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
}[];
pathPosition: [number, number, number];
pathRotation: [number, number, number];
speed: number;
}
function Behaviour({ setSimulationPaths }: { setSimulationPaths: any }) {
const { floorItems } = useFloorItems(); const { floorItems } = useFloorItems();
useEffect(() => { useEffect(() => {
const newPaths: Path[] = []; const newPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] = [];
floorItems.forEach((item: Types.FloorItemType) => { floorItems.forEach((item: Types.FloorItemType) => {
if (item.modelfileID === "6633215057b31fe671145959") { if (item.modelfileID === "672a090f80d91ac979f4d0bd") {
const point1Position = new THREE.Vector3(0, 1.25, 3.3); const point1Position = new THREE.Vector3(0, 0.85, 2.2);
const middlePointPosition = new THREE.Vector3(0, 1.25, 0); const middlePointPosition = new THREE.Vector3(0, 0.85, 0);
const point2Position = new THREE.Vector3(0, 1.25, -3.3); const point2Position = new THREE.Vector3(0, 0.85, -2.2);
const point1UUID = THREE.MathUtils.generateUUID(); const point1UUID = THREE.MathUtils.generateUUID();
const middlePointUUID = THREE.MathUtils.generateUUID(); const middlePointUUID = THREE.MathUtils.generateUUID();
const point2UUID = THREE.MathUtils.generateUUID(); const point2UUID = THREE.MathUtils.generateUUID();
const newPath: Path = { const newPath: Types.ConveyorEventsSchema = {
modeluuid: item.modeluuid, modeluuid: item.modeluuid,
modelName: item.modelname, modelName: item.modelname,
type: 'Conveyor',
points: [ points: [
{ {
uuid: point1UUID, uuid: point1UUID,
@ -64,12 +50,32 @@ function Behaviour({ setSimulationPaths }: { setSimulationPaths: any }) {
connections: { source: { pathUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] }, connections: { source: { pathUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] },
}, },
], ],
pathPosition: [...item.position], assetPosition: [...item.position],
pathRotation: [item.rotation.x, item.rotation.y, item.rotation.z], assetRotation: [item.rotation.x, item.rotation.y, item.rotation.z],
speed: 1, speed: 1,
}; };
newPaths.push(newPath); newPaths.push(newPath);
} else if (item.modelfileID === "67e3da19c2e8f37134526e6a") {
const pointUUID = THREE.MathUtils.generateUUID();
const pointPosition = new THREE.Vector3(0, 1.3, 0);
const newVehiclePath: Types.VehicleEventsSchema = {
modeluuid: item.modeluuid,
modelName: item.modelname,
type: 'Vehicle',
point: {
uuid: pointUUID,
position: [pointPosition.x, pointPosition.y, pointPosition.z],
actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: THREE.MathUtils.generateUUID(), hitCount: 1, end: THREE.MathUtils.generateUUID(), buffer: 0, isUsed: false }],
triggers: [],
connections: { source: { pathUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] },
},
assetPosition: [...item.position],
speed: 2,
};
newPaths.push(newVehiclePath);
} }
}); });

View File

@ -1,6 +1,7 @@
import { useFrame, useThree } from '@react-three/fiber'; import { useFrame, useThree } from '@react-three/fiber';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import * as THREE from 'three'; import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes';
import { QuadraticBezierLine } from '@react-three/drei'; import { QuadraticBezierLine } from '@react-three/drei';
import { useIsConnecting, useSimulationPaths } from '../../../store/store'; import { useIsConnecting, useSimulationPaths } from '../../../store/store';
import useModuleStore from '../../../store/useModuleStore'; import useModuleStore from '../../../store/useModuleStore';
@ -27,6 +28,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
toPointUUID: string toPointUUID: string
) => { ) => {
const updatedPaths = simulationPaths.map(path => { const updatedPaths = simulationPaths.map(path => {
if (path.type === 'Conveyor') {
if (path.modeluuid === fromPathUUID) { if (path.modeluuid === fromPathUUID) {
return { return {
...path, ...path,
@ -83,6 +85,57 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}) })
}; };
} }
}
else if (path.type === 'Vehicle') {
// Handle outgoing connections from Vehicle
if (path.modeluuid === fromPathUUID && path.point.uuid === fromPointUUID) {
const newTarget = {
pathUUID: toPathUUID,
pointUUID: toPointUUID
};
const existingTargets = path.point.connections.targets || [];
if (!existingTargets.some(target =>
target.pathUUID === newTarget.pathUUID &&
target.pointUUID === newTarget.pointUUID
)) {
return {
...path,
point: {
...path.point,
connections: {
...path.point.connections,
targets: [...existingTargets, newTarget]
}
}
};
}
}
// Handle incoming connections to Vehicle
else if (path.modeluuid === toPathUUID && path.point.uuid === toPointUUID) {
const reverseTarget = {
pathUUID: fromPathUUID,
pointUUID: fromPointUUID
};
const existingTargets = path.point.connections.targets || [];
if (!existingTargets.some(target =>
target.pathUUID === reverseTarget.pathUUID &&
target.pointUUID === reverseTarget.pointUUID
)) {
return {
...path,
point: {
...path.point,
connections: {
...path.point.connections,
targets: [...existingTargets, reverseTarget]
}
}
};
}
}
}
return path; return path;
}); });
@ -126,25 +179,43 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
if (intersects.length > 0) { if (intersects.length > 0) {
const intersected = intersects[0].object; const intersected = intersects[0].object;
if (intersected.name.includes("action-sphere")) { if (intersected.name.includes("events-sphere")) {
const pathUUID = intersected.userData.path.modeluuid; const pathUUID = intersected.userData.path.modeluuid;
const sphereUUID = intersected.uuid; const sphereUUID = intersected.uuid;
const worldPosition = new THREE.Vector3(); const worldPosition = new THREE.Vector3();
intersected.getWorldPosition(worldPosition); intersected.getWorldPosition(worldPosition);
const isStartOrEnd = intersected.userData.path.points.length > 0 && ( let isStartOrEnd = false;
if (intersected.userData.path.points) {
isStartOrEnd = intersected.userData.path.points.length > 0 && (
sphereUUID === intersected.userData.path.points[0].uuid || sphereUUID === intersected.userData.path.points[0].uuid ||
sphereUUID === intersected.userData.path.points[intersected.userData.path.points.length - 1].uuid sphereUUID === intersected.userData.path.points[intersected.userData.path.points.length - 1].uuid
); );
} else if (intersected.userData.path.point) {
isStartOrEnd = sphereUUID === intersected.userData.path.point.uuid;
}
if (pathUUID) { if (pathUUID) {
// Check if sphere is already connected const firstPath = simulationPaths.find(p => p.modeluuid === firstSelected?.pathUUID);
const isAlreadyConnected = simulationPaths.some(path => const secondPath = simulationPaths.find(p => p.modeluuid === pathUUID);
path.points.some(point =>
if (firstPath && secondPath && firstPath.type === 'Vehicle' && secondPath.type === 'Vehicle') {
console.log("Cannot connect two vehicle paths together");
return;
}
const isAlreadyConnected = simulationPaths.some(path => {
if (path.type === 'Conveyor') {
return path.points.some(point =>
point.uuid === sphereUUID && point.uuid === sphereUUID &&
point.connections.targets.length > 0 point.connections.targets.length > 0
)
); );
} else if (path.type === 'Vehicle') {
return path.point.uuid === sphereUUID &&
path.point.connections.targets.length > 0;
}
return false;
});
if (isAlreadyConnected) { if (isAlreadyConnected) {
console.log("Sphere is already connected. Ignoring."); console.log("Sphere is already connected. Ignoring.");
@ -211,6 +282,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
raycaster.setFromCamera(pointer, camera); raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects(scene.children, true).filter((intersect) => const intersects = raycaster.intersectObjects(scene.children, true).filter((intersect) =>
!intersect.object.name.includes("Roof") && !intersect.object.name.includes("Roof") &&
!intersect.object.name.includes("agv-collider") &&
!intersect.object.name.includes("MeasurementReference") && !intersect.object.name.includes("MeasurementReference") &&
!intersect.object.userData.isPathObject && !intersect.object.userData.isPathObject &&
!(intersect.object.type === "GridHelper") !(intersect.object.type === "GridHelper")
@ -229,7 +301,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
} }
const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) => const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) =>
obj.object.name.includes("action-sphere") obj.object.name.includes("events-sphere")
); );
if (sphereIntersects.length > 0) { if (sphereIntersects.length > 0) {
@ -237,27 +309,45 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
const sphereUUID = sphere.uuid; const sphereUUID = sphere.uuid;
const spherePosition = new THREE.Vector3(); const spherePosition = new THREE.Vector3();
sphere.getWorldPosition(spherePosition); sphere.getWorldPosition(spherePosition);
const pathUUID = sphere.userData.path.modeluuid; const pathData = sphere.userData.path;
const pathUUID = pathData.modeluuid;
const isStartOrEnd = sphere.userData.path.points.length > 0 && ( const firstPath = simulationPaths.find(p => p.modeluuid === firstSelected.pathUUID);
sphereUUID === sphere.userData.path.points[0].uuid || const secondPath = simulationPaths.find(p => p.modeluuid === pathUUID);
sphereUUID === sphere.userData.path.points[sphere.userData.path.points.length - 1].uuid const isVehicleToVehicle = firstPath?.type === 'Vehicle' && secondPath?.type === 'Vehicle';
);
const isAlreadyConnected = simulationPaths.some(path => const isConnectable = (pathData.type === 'Vehicle' ||
path.points.some(point => (pathData.points.length > 0 && (
sphereUUID === pathData.points[0].uuid ||
sphereUUID === pathData.points[pathData.points.length - 1].uuid
))) && !isVehicleToVehicle;
const isAlreadyConnected = simulationPaths.some(path => {
if (path.type === 'Conveyor') {
return path.points.some(point =>
point.uuid === sphereUUID && point.uuid === sphereUUID &&
point.connections.targets.length > 0 point.connections.targets.length > 0
)
); );
} else if (path.type === 'Vehicle') {
return path.point.uuid === sphereUUID &&
path.point.connections.targets.length > 0;
}
return false;
});
if ( if (
!isAlreadyConnected && !isAlreadyConnected &&
!isVehicleToVehicle &&
firstSelected.sphereUUID !== sphereUUID && firstSelected.sphereUUID !== sphereUUID &&
firstSelected.pathUUID !== pathUUID && firstSelected.pathUUID !== pathUUID &&
(firstSelected.isCorner || isStartOrEnd) (firstSelected.isCorner || isConnectable)
) { ) {
snappedSphere = { sphereUUID, position: spherePosition, pathUUID, isCorner: isStartOrEnd }; snappedSphere = {
sphereUUID,
position: spherePosition,
pathUUID,
isCorner: isConnectable
};
} else { } else {
isInvalidConnection = true; isInvalidConnection = true;
} }
@ -281,8 +371,13 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
end: point, end: point,
mid: midPoint, mid: midPoint,
}); });
console.log({
start: firstSelected.position,
end: point,
mid: midPoint,
});
setIsConnecting(true); // setIsConnecting(true);
if (sphereIntersects.length > 0) { if (sphereIntersects.length > 0) {
setHelperLineColor(isInvalidConnection ? 'red' : '#6cf542'); setHelperLineColor(isInvalidConnection ? 'red' : '#6cf542');
@ -299,12 +394,15 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
} }
}); });
// Render connections from simulationPaths
return ( return (
<> <>
{simulationPaths.flatMap(path => {simulationPaths.flatMap(path => {
path.points.flatMap(point => if (path.type === 'Conveyor') {
return path.points.flatMap(point =>
point.connections.targets.map((target, index) => { point.connections.targets.map((target, index) => {
const targetPath = simulationPaths.find(p => p.modeluuid === target.pathUUID);
if (targetPath?.type === 'Vehicle') return null;
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', point.uuid); const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', point.uuid);
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID); const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
@ -332,15 +430,53 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
color="white" color="white"
lineWidth={4} lineWidth={4}
dashed dashed
dashSize={1} dashSize={0.75}
dashScale={20} dashScale={20}
/> />
); );
} }
return null; return null;
}) })
) );
)} } else if (path.type === 'Vehicle') {
return path.point.connections.targets.map((target, index) => {
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.point.uuid);
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
if (fromSphere && toSphere) {
const fromWorldPosition = new THREE.Vector3();
const toWorldPosition = new THREE.Vector3();
fromSphere.getWorldPosition(fromWorldPosition);
toSphere.getWorldPosition(toWorldPosition);
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
(fromWorldPosition.z + toWorldPosition.z) / 2
);
return (
<QuadraticBezierLine
key={`${path.point.uuid}-${target.pointUUID}-${index}`}
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
color="orange"
lineWidth={4}
dashed
dashSize={0.75}
dashScale={20}
/>
);
}
return null;
});
}
return [];
})}
{currentLine && ( {currentLine && (
<QuadraticBezierLine <QuadraticBezierLine

View File

@ -4,10 +4,12 @@ import { Sphere, TransformControls } from '@react-three/drei';
import { useIsConnecting, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store'; import { useIsConnecting, useRenderDistance, useSelectedActionSphere, useSelectedPath, useSimulationPaths } from '../../../store/store';
import { useFrame, useThree } from '@react-three/fiber'; import { useFrame, useThree } from '@react-three/fiber';
import { useSubModuleStore } from '../../../store/useModuleStore'; import { useSubModuleStore } from '../../../store/useModuleStore';
import { point } from '@turf/helpers';
interface Path { interface ConveyorEventsSchema {
modeluuid: string; modeluuid: string;
modelName: string; modelName: string;
type: 'Conveyor';
points: { points: {
uuid: string; uuid: string;
position: [number, number, number]; position: [number, number, number];
@ -16,8 +18,8 @@ interface Path {
triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | []; triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | [];
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] }; connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
}[]; }[];
pathPosition: [number, number, number]; assetPosition: [number, number, number];
pathRotation: [number, number, number]; assetRotation: [number, number, number];
speed: number; speed: number;
} }
@ -63,7 +65,9 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
const updateSimulationPaths = () => { const updateSimulationPaths = () => {
if (!selectedActionSphere) return; if (!selectedActionSphere) return;
const updatedPaths: Path[] = simulationPaths.map((path) => ({ const updatedPaths = simulationPaths.map((path) => {
if (path.type === "Conveyor") {
return {
...path, ...path,
points: path.points.map((point) => points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid point.uuid === selectedActionSphere.point.uuid
@ -82,15 +86,19 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
} }
: point : point
), ),
})); };
}
return path;
}) as ConveyorEventsSchema[];
setSimulationPaths(updatedPaths); setSimulationPaths(updatedPaths);
}; };
return ( return (
<group name='simulation-simulationPaths-group' ref={pathsGroupRef} > <group name='simulation-simulationPaths-group' ref={pathsGroupRef}>
{simulationPaths.map((path) => { {simulationPaths.map((path) => {
if (path.type === 'Conveyor') {
const points = path.points.map(point => new THREE.Vector3(...point.position)); const points = path.points.map(point => new THREE.Vector3(...point.position));
return ( return (
@ -98,8 +106,8 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
name={`${path.modeluuid}-event-path`} name={`${path.modeluuid}-event-path`}
key={path.modeluuid} key={path.modeluuid}
ref={el => (groupRefs.current[path.modeluuid] = el!)} ref={el => (groupRefs.current[path.modeluuid] = el!)}
position={path.pathPosition} position={path.assetPosition}
rotation={path.pathRotation} rotation={path.assetRotation}
onClick={(e) => { onClick={(e) => {
if (isConnecting) return; if (isConnecting) return;
e.stopPropagation(); e.stopPropagation();
@ -119,7 +127,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
uuid={point.uuid} uuid={point.uuid}
position={point.position} position={point.position}
args={[0.15, 32, 32]} args={[0.15, 32, 32]}
name='action-sphere' name='events-sphere'
ref={el => (sphereRefs.current[point.uuid] = el!)} ref={el => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => { onClick={(e) => {
if (isConnecting) return; if (isConnecting) return;
@ -134,7 +142,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
userData={{ point, path }} userData={{ point, path }}
onPointerMissed={() => { onPointerMissed={() => {
setSubModule('properties'); setSubModule('properties');
setSelectedActionSphere(null) setSelectedActionSphere(null);
}} }}
> >
<meshStandardMaterial <meshStandardMaterial
@ -156,6 +164,55 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
})} })}
</group> </group>
); );
} else if (path.type === 'Vehicle') {
return (
<group
name={`${path.modeluuid}-vehicle-path`}
key={path.modeluuid}
ref={el => (groupRefs.current[path.modeluuid] = el!)}
position={path.assetPosition}
onClick={(e) => {
if (isConnecting) return;
e.stopPropagation();
setSelectedPath({ path, group: groupRefs.current[path.modeluuid] });
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule('mechanics');
}}
onPointerMissed={() => {
setSelectedPath(null);
setSubModule('properties');
}}
>
<Sphere
key={path.point.uuid}
uuid={path.point.uuid}
position={path.point.position}
args={[0.15, 32, 32]}
name='events-sphere'
ref={el => (sphereRefs.current[path.point.uuid] = el!)}
onClick={(e) => {
if (isConnecting) return;
e.stopPropagation();
setSelectedActionSphere({
path,
point: sphereRefs.current[path.point.uuid]
});
setSubModule('mechanics');
setSelectedPath(null);
}}
userData={{ point: path.point, path }}
onPointerMissed={() => {
setSubModule('properties');
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial color="purple" />
</Sphere>
</group>
);
}
return null;
})} })}
{selectedActionSphere && transformMode && ( {selectedActionSphere && transformMode && (
@ -163,7 +220,7 @@ function PathCreation({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject
ref={transformRef} ref={transformRef}
object={selectedActionSphere.point} object={selectedActionSphere.point}
mode={transformMode} mode={transformMode}
onObjectChange={updateSimulationPaths} onMouseUp={updateSimulationPaths}
/> />
)} )}
</group> </group>

View File

@ -14,7 +14,7 @@ function Simulation() {
const [processes, setProcesses] = useState([]); const [processes, setProcesses] = useState([]);
useEffect(() => { useEffect(() => {
console.log('simulationPaths: ', simulationPaths);
}, [simulationPaths]); }, [simulationPaths]);
// useEffect(() => { // useEffect(() => {
@ -31,7 +31,7 @@ function Simulation() {
return ( return (
<> <>
<Behaviour setSimulationPaths={setSimulationPaths} /> <Behaviour/>
{activeModule === 'simulation' && ( {activeModule === 'simulation' && (
<> <>
<PathCreation pathsGroupRef={pathsGroupRef} /> <PathCreation pathsGroupRef={pathsGroupRef} />

View File

@ -1,409 +1,409 @@
import { useMemo, useState } from 'react'; // import { useMemo, useState } from 'react';
import { useSelectedActionSphere, useToggleView, useSimulationPaths, useSelectedPath, useStartSimulation, useDrawMaterialPath } from '../../store/store'; // import { useSelectedActionSphere, useToggleView, useSimulationPaths, useSelectedPath, useStartSimulation, useDrawMaterialPath } from '../../store/store';
import * as THREE from 'three'; // import * as THREE from 'three';
import useModuleStore from '../../store/useModuleStore'; // import useModuleStore from '../../store/useModuleStore';
function SimulationUI() { // function SimulationUI() {
const { ToggleView } = useToggleView(); // const { ToggleView } = useToggleView();
const { activeModule } = useModuleStore(); // const { activeModule } = useModuleStore();
const { startSimulation, setStartSimulation } = useStartSimulation(); // const { startSimulation, setStartSimulation } = useStartSimulation();
const { selectedActionSphere } = useSelectedActionSphere(); // const { selectedActionSphere } = useSelectedActionSphere();
const { selectedPath, setSelectedPath } = useSelectedPath(); // const { selectedPath, setSelectedPath } = useSelectedPath();
const { simulationPaths, setSimulationPaths } = useSimulationPaths(); // const { simulationPaths, setSimulationPaths } = useSimulationPaths();
const { drawMaterialPath, setDrawMaterialPath } = useDrawMaterialPath(); // const { drawMaterialPath, setDrawMaterialPath } = useDrawMaterialPath();
const [activeButton, setActiveButton] = useState<string | null>(null); // const [activeButton, setActiveButton] = useState<string | null>(null);
const handleAddAction = () => { // const handleAddAction = () => {
if (!selectedActionSphere) return; // if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({ // const updatedPaths = simulationPaths.map((path) => ({
...path, // ...path,
points: path.points.map((point) => { // points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.point.uuid) { // if (point.uuid === selectedActionSphere.point.uuid) {
const actionIndex = point.actions.length; // const actionIndex = point.actions.length;
const newAction = { // const newAction = {
uuid: THREE.MathUtils.generateUUID(), // uuid: THREE.MathUtils.generateUUID(),
name: `Action ${actionIndex + 1}`, // Assign action name based on index // name: `Action ${actionIndex + 1}`, // Assign action name based on index
type: 'Inherit', // type: 'Inherit',
material: 'Inherit', // material: 'Inherit',
delay: 'Inherit', // delay: 'Inherit',
spawnInterval: 'Inherit', // spawnInterval: 'Inherit',
isUsed: false // isUsed: false
}; // };
return { ...point, actions: [...point.actions, newAction] }; // return { ...point, actions: [...point.actions, newAction] };
} // }
return point; // return point;
}), // }),
})); // }));
setSimulationPaths(updatedPaths); // setSimulationPaths(updatedPaths);
}; // };
const handleDeleteAction = (uuid: string) => { // const handleDeleteAction = (uuid: string) => {
if (!selectedActionSphere) return; // if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({ // const updatedPaths = simulationPaths.map((path) => ({
...path, // ...path,
points: path.points.map((point) => // points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid // point.uuid === selectedActionSphere.point.uuid
? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) } // ? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) }
: point // : point
), // ),
})); // }));
setSimulationPaths(updatedPaths); // setSimulationPaths(updatedPaths);
}; // };
const handleActionSelect = (uuid: string, actionType: string) => { // const handleActionSelect = (uuid: string, actionType: string) => {
if (!selectedActionSphere) return; // if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({ // const updatedPaths = simulationPaths.map((path) => ({
...path, // ...path,
points: path.points.map((point) => // points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid // point.uuid === selectedActionSphere.point.uuid
? { // ? {
...point, // ...point,
actions: point.actions.map((action) => // actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, type: actionType } : action // action.uuid === uuid ? { ...action, type: actionType } : action
), // ),
} // }
: point // : point
), // ),
})); // }));
setSimulationPaths(updatedPaths); // setSimulationPaths(updatedPaths);
}; // };
const handleMaterialSelect = (uuid: string, material: string) => { // const handleMaterialSelect = (uuid: string, material: string) => {
if (!selectedActionSphere) return; // if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({ // const updatedPaths = simulationPaths.map((path) => ({
...path, // ...path,
points: path.points.map((point) => // points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid // point.uuid === selectedActionSphere.point.uuid
? { // ? {
...point, // ...point,
actions: point.actions.map((action) => // actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, material } : action // action.uuid === uuid ? { ...action, material } : action
), // ),
} // }
: point // : point
), // ),
})); // }));
setSimulationPaths(updatedPaths); // setSimulationPaths(updatedPaths);
}; // };
const handleDelayChange = (uuid: string, delay: number | string) => { // const handleDelayChange = (uuid: string, delay: number | string) => {
if (!selectedActionSphere) return; // if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({ // const updatedPaths = simulationPaths.map((path) => ({
...path, // ...path,
points: path.points.map((point) => // points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid // point.uuid === selectedActionSphere.point.uuid
? { // ? {
...point, // ...point,
actions: point.actions.map((action) => // actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, delay } : action // action.uuid === uuid ? { ...action, delay } : action
), // ),
} // }
: point // : point
), // ),
})); // }));
setSimulationPaths(updatedPaths); // setSimulationPaths(updatedPaths);
}; // };
const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => { // const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => {
if (!selectedActionSphere) return; // if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({ // const updatedPaths = simulationPaths.map((path) => ({
...path, // ...path,
points: path.points.map((point) => // points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid // point.uuid === selectedActionSphere.point.uuid
? { // ? {
...point, // ...point,
actions: point.actions.map((action) => // actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, spawnInterval } : action // action.uuid === uuid ? { ...action, spawnInterval } : action
), // ),
} // }
: point // : point
), // ),
})); // }));
setSimulationPaths(updatedPaths); // setSimulationPaths(updatedPaths);
}; // };
const handleSpeedChange = (speed: number) => { // const handleSpeedChange = (speed: number) => {
if (!selectedPath) return; // if (!selectedPath) return;
const updatedPaths = simulationPaths.map((path) => // const updatedPaths = simulationPaths.map((path) =>
path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path // path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path
); // );
setSimulationPaths(updatedPaths); // setSimulationPaths(updatedPaths);
setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } }); // setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } });
}; // };
const handleAddTrigger = () => { // const handleAddTrigger = () => {
if (!selectedActionSphere) return; // if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({ // const updatedPaths = simulationPaths.map((path) => ({
...path, // ...path,
points: path.points.map((point) => { // points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.point.uuid) { // if (point.uuid === selectedActionSphere.point.uuid) {
const triggerIndex = point.triggers.length; // const triggerIndex = point.triggers.length;
const newTrigger = { // const newTrigger = {
uuid: THREE.MathUtils.generateUUID(), // uuid: THREE.MathUtils.generateUUID(),
name: `Trigger ${triggerIndex + 1}`, // Assign name based on index // name: `Trigger ${triggerIndex + 1}`, // Assign name based on index
type: '', // type: '',
isUsed: false // isUsed: false
}; // };
return { ...point, triggers: [...point.triggers, newTrigger] }; // return { ...point, triggers: [...point.triggers, newTrigger] };
} // }
return point; // return point;
}), // }),
})); // }));
setSimulationPaths(updatedPaths); // setSimulationPaths(updatedPaths);
}; // };
const handleDeleteTrigger = (uuid: string) => { // const handleDeleteTrigger = (uuid: string) => {
if (!selectedActionSphere) return; // if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({ // const updatedPaths = simulationPaths.map((path) => ({
...path, // ...path,
points: path.points.map((point) => // points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid // point.uuid === selectedActionSphere.point.uuid
? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) } // ? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) }
: point // : point
), // ),
})); // }));
setSimulationPaths(updatedPaths); // setSimulationPaths(updatedPaths);
}; // };
const handleTriggerSelect = (uuid: string, triggerType: string) => { // const handleTriggerSelect = (uuid: string, triggerType: string) => {
if (!selectedActionSphere) return; // if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({ // const updatedPaths = simulationPaths.map((path) => ({
...path, // ...path,
points: path.points.map((point) => // points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid // point.uuid === selectedActionSphere.point.uuid
? { // ? {
...point, // ...point,
triggers: point.triggers.map((trigger) => // triggers: point.triggers.map((trigger) =>
trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger // trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger
), // ),
} // }
: point // : point
), // ),
})); // }));
setSimulationPaths(updatedPaths); // setSimulationPaths(updatedPaths);
}; // };
const handleResetPath = () => { // const handleResetPath = () => {
if (!selectedPath) return; // if (!selectedPath) return;
}; // };
const handleActionToggle = (uuid: string) => { // const handleActionToggle = (uuid: string) => {
if (!selectedActionSphere) return; // if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({ // const updatedPaths = simulationPaths.map((path) => ({
...path, // ...path,
points: path.points.map((point) => // points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid // point.uuid === selectedActionSphere.point.uuid
? { // ? {
...point, // ...point,
actions: point.actions.map((action) => ({ // actions: point.actions.map((action) => ({
...action, // ...action,
isUsed: action.uuid === uuid ? !action.isUsed : false, // isUsed: action.uuid === uuid ? !action.isUsed : false,
})), // })),
} // }
: point // : point
), // ),
})); // }));
setSimulationPaths(updatedPaths); // setSimulationPaths(updatedPaths);
}; // };
const handleTriggerToggle = (uuid: string) => { // const handleTriggerToggle = (uuid: string) => {
if (!selectedActionSphere) return; // if (!selectedActionSphere) return;
const updatedPaths = simulationPaths.map((path) => ({ // const updatedPaths = simulationPaths.map((path) => ({
...path, // ...path,
points: path.points.map((point) => // points: path.points.map((point) =>
point.uuid === selectedActionSphere.point.uuid // point.uuid === selectedActionSphere.point.uuid
? { // ? {
...point, // ...point,
triggers: point.triggers.map((trigger) => ({ // triggers: point.triggers.map((trigger) => ({
...trigger, // ...trigger,
isUsed: trigger.uuid === uuid ? !trigger.isUsed : false, // isUsed: trigger.uuid === uuid ? !trigger.isUsed : false,
})), // })),
} // }
: point // : point
), // ),
})); // }));
setSimulationPaths(updatedPaths); // setSimulationPaths(updatedPaths);
}; // };
const selectedPoint = useMemo(() => { // const selectedPoint = useMemo(() => {
if (!selectedActionSphere) return null; // if (!selectedActionSphere) return null;
return simulationPaths.flatMap((path) => path.points).find((point) => point.uuid === selectedActionSphere.point.uuid); // return simulationPaths.flatMap((path) => path.points).find((point) => point.uuid === selectedActionSphere.point.uuid);
}, [selectedActionSphere, simulationPaths]); // }, [selectedActionSphere, simulationPaths]);
const createPath = () => { // const createPath = () => {
setActiveButton(activeButton !== 'addMaterialPath' ? 'addMaterialPath' : null); // setActiveButton(activeButton !== 'addMaterialPath' ? 'addMaterialPath' : null);
setDrawMaterialPath(!drawMaterialPath); // setDrawMaterialPath(!drawMaterialPath);
} // }
return ( // return (
<> // <>
{activeModule === "simulation" && ( // {activeModule === "simulation" && (
<div style={{ zIndex: 10, position: "fixed", width: '260px' }}> // <div style={{ zIndex: 10, position: "fixed", width: '260px' }}>
{!ToggleView && ( // {!ToggleView && (
<> // <>
<button // <button
onClick={() => setStartSimulation(!startSimulation)} // onClick={() => setStartSimulation(!startSimulation)}
style={{ // style={{
marginTop: "10px", // marginTop: "10px",
background: startSimulation ? '#ff320e' : '', // background: startSimulation ? '#ff320e' : '',
padding: "10px", // padding: "10px",
borderRadius: "5px" // borderRadius: "5px"
}} // }}
> // >
{startSimulation ? 'Stop Simulation' : 'Start Simulation'} // {startSimulation ? 'Stop Simulation' : 'Start Simulation'}
</button> // </button>
<div style={{ zIndex: "10", position: "relative" }}> // <div style={{ zIndex: "10", position: "relative" }}>
{!ToggleView && <button onClick={createPath} style={{ marginTop: "10px", background: activeButton === 'addMaterialPath' ? '#ff320e' : '' }}> Add Material Path</button>} // {!ToggleView && <button onClick={createPath} style={{ marginTop: "10px", background: activeButton === 'addMaterialPath' ? '#ff320e' : '' }}> Add Material Path</button>}
</div> // </div>
{selectedPath && ( // {selectedPath && (
<div style={{ marginTop: "10px" }}> // <div style={{ marginTop: "10px" }}>
<label>Path Speed:</label> // <label>Path Speed:</label>
<input // <input
style={{ width: '50px' }} // style={{ width: '50px' }}
type="number" // type="number"
value={selectedPath.path.speed} // value={selectedPath.path.speed}
min="0.1" // min="0.1"
step="0.1" // step="0.1"
onChange={(e) => handleSpeedChange(parseFloat(e.target.value))} // onChange={(e) => handleSpeedChange(parseFloat(e.target.value))}
/> // />
</div> // </div>
)} // )}
{selectedActionSphere && ( // {selectedActionSphere && (
<div style={{ marginTop: "10px" }}> // <div style={{ marginTop: "10px" }}>
<button onClick={handleAddAction}>Add Action</button> // <button onClick={handleAddAction}>Add Action</button>
<button onClick={handleAddTrigger}>Add Trigger</button> // <button onClick={handleAddTrigger}>Add Trigger</button>
{selectedPoint?.actions.map((action) => ( // {selectedPoint?.actions.map((action) => (
<div key={action.uuid} style={{ marginTop: "10px" }}> // <div key={action.uuid} style={{ marginTop: "10px" }}>
<select value={action.type} onChange={(e) => handleActionSelect(action.uuid, e.target.value)}> // <select value={action.type} onChange={(e) => handleActionSelect(action.uuid, e.target.value)}>
<option value="Inherit">Inherit</option> // <option value="Inherit">Inherit</option>
<option value="Spawn">Spawn Point</option> // <option value="Spawn">Spawn Point</option>
<option value="Swap">Swap Material</option> // <option value="Swap">Swap Material</option>
<option value="Despawn">Despawn Point</option> // <option value="Despawn">Despawn Point</option>
<option value="Delay">Delay</option> // <option value="Delay">Delay</option>
</select> // </select>
<button onClick={() => handleDeleteAction(action.uuid)}>Delete Action</button> // <button onClick={() => handleDeleteAction(action.uuid)}>Delete Action</button>
<label> // <label>
<input // <input
type="checkbox" // type="checkbox"
checked={action.isUsed} // checked={action.isUsed}
onChange={() => handleActionToggle(action.uuid)} // onChange={() => handleActionToggle(action.uuid)}
/> // />
</label> // </label>
{(action.type === 'Spawn' || action.type === 'Swap') && ( // {(action.type === 'Spawn' || action.type === 'Swap') && (
<div style={{ marginTop: "10px" }}> // <div style={{ marginTop: "10px" }}>
<select value={action.material} onChange={(e) => handleMaterialSelect(action.uuid, e.target.value)}> // <select value={action.material} onChange={(e) => handleMaterialSelect(action.uuid, e.target.value)}>
<option value="Inherit">Inherit</option> // <option value="Inherit">Inherit</option>
<option value="Crate">Crate</option> // <option value="Crate">Crate</option>
<option value="Box">Box</option> // <option value="Box">Box</option>
</select> // </select>
</div> // </div>
)} // )}
{action.type === 'Delay' && ( // {action.type === 'Delay' && (
<div style={{ marginTop: "10px" }}> // <div style={{ marginTop: "10px" }}>
<label>Delay Time:</label> // <label>Delay Time:</label>
<input // <input
style={{ width: '50px' }} // style={{ width: '50px' }}
type="text" // type="text"
value={isNaN(Number(action.delay)) || action.delay === "Inherit" ? "Inherit" : action.delay} // value={isNaN(Number(action.delay)) || action.delay === "Inherit" ? "Inherit" : action.delay}
min="1" // min="1"
onChange={(e) => handleDelayChange(action.uuid, parseInt(e.target.value) || 'Inherit')} // onChange={(e) => handleDelayChange(action.uuid, parseInt(e.target.value) || 'Inherit')}
/> // />
</div> // </div>
)} // )}
{action.type === 'Spawn' && ( // {action.type === 'Spawn' && (
<div style={{ marginTop: "10px" }}> // <div style={{ marginTop: "10px" }}>
<label>Spawn Interval:</label> // <label>Spawn Interval:</label>
<input // <input
style={{ width: '50px' }} // style={{ width: '50px' }}
type="text" // type="text"
value={isNaN(Number(action.spawnInterval)) || action.spawnInterval === "Inherit" ? "Inherit" : action.spawnInterval} // value={isNaN(Number(action.spawnInterval)) || action.spawnInterval === "Inherit" ? "Inherit" : action.spawnInterval}
min="1" // min="1"
onChange={(e) => handleSpawnIntervalChange(action.uuid, parseInt(e.target.value) || 'Inherit')} // onChange={(e) => handleSpawnIntervalChange(action.uuid, parseInt(e.target.value) || 'Inherit')}
/> // />
</div> // </div>
)} // )}
<hr style={{ margin: "10px 0", borderColor: "#ccc" }} /> // <hr style={{ margin: "10px 0", borderColor: "#ccc" }} />
</div> // </div>
))} // ))}
<hr style={{ margin: "10px 0", border: "1px solid black" }} /> // <hr style={{ margin: "10px 0", border: "1px solid black" }} />
{selectedPoint?.triggers.map((trigger) => ( // {selectedPoint?.triggers.map((trigger) => (
<div key={trigger.uuid} style={{ marginTop: "10px" }}> // <div key={trigger.uuid} style={{ marginTop: "10px" }}>
<select value={trigger.type} onChange={(e) => handleTriggerSelect(trigger.uuid, e.target.value)}> // <select value={trigger.type} onChange={(e) => handleTriggerSelect(trigger.uuid, e.target.value)}>
<option value="">Select Trigger Type</option> // <option value="">Select Trigger Type</option>
<option value="On-Hit">On Hit</option> // <option value="On-Hit">On Hit</option>
<option value="Buffer">Buffer</option> // <option value="Buffer">Buffer</option>
</select> // </select>
<button onClick={() => handleDeleteTrigger(trigger.uuid)}>Delete Trigger</button> // <button onClick={() => handleDeleteTrigger(trigger.uuid)}>Delete Trigger</button>
<label> // <label>
<input // <input
type="checkbox" // type="checkbox"
checked={trigger.isUsed} // checked={trigger.isUsed}
onChange={() => handleTriggerToggle(trigger.uuid)} // onChange={() => handleTriggerToggle(trigger.uuid)}
/> // />
</label> // </label>
<hr style={{ margin: "10px 0", borderColor: "#ccc" }} /> // <hr style={{ margin: "10px 0", borderColor: "#ccc" }} />
</div> // </div>
))} // ))}
</div> // </div>
)} // )}
{selectedPath && ( // {selectedPath && (
<div style={{ marginTop: "10px" }}> // <div style={{ marginTop: "10px" }}>
<button // <button
onClick={handleResetPath} // onClick={handleResetPath}
style={{ padding: "10px", borderRadius: "5px", background: "#ff0000", color: "#fff" }} // style={{ padding: "10px", borderRadius: "5px", background: "#ff0000", color: "#fff" }}
> // >
Reset Path // Reset Path
</button> // </button>
</div> // </div>
)} // )}
</> // </>
)} // )}
</div> // </div>
)} // )}
</> // </>
); // );
} // }
export default SimulationUI; // export default SimulationUI;

View File

@ -64,7 +64,6 @@ const Project: React.FC = () => {
{activeModule === "market" && <MarketPlace />} {activeModule === "market" && <MarketPlace />}
<RealTimeVisulization /> <RealTimeVisulization />
{activeModule !== "market" && <Tools />} {activeModule !== "market" && <Tools />}
{/* <SimulationUI /> */}
{isPlaying && activeModule === "simulation" && <SimulationPlayer />} {isPlaying && activeModule === "simulation" && <SimulationPlayer />}
</div> </div>
); );

View File

@ -6,10 +6,7 @@ const MqttEvents = () => {
const { setTouch, setTemperature, setHumidity } = useDrieUIValue(); const { setTouch, setTemperature, setHumidity } = useDrieUIValue();
useEffect(() => { useEffect(() => {
const client = mqtt.connect("ws://192.168.0.192:1884", { const client = mqtt.connect(`ws://${process.env.REACT_APP_SERVER_MQTT_URL}`);
username: "gabby",
password: "gabby"
});
client.subscribe("touch"); client.subscribe("touch");
client.subscribe("temperature"); client.subscribe("temperature");

View File

@ -0,0 +1,22 @@
let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
export const getAssetDetails = async (filename: string) => {
try {
const response = await fetch(`${BackEnd_url}/api/v1/assetDetails`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ filename }),
});
if (!response.ok) {
throw new Error("Failed to fetch asset details");
}
const result = await response.json();
return result;
} catch (error: any) {
// console.error("Error fetching category:", error.message);
throw new Error(error.message);
}
};

View File

@ -6,6 +6,8 @@ export const fetchAssets = async () => {
throw new Error("Network response was not ok"); throw new Error("Network response was not ok");
} }
const result = await response.json(); const result = await response.json();
const last10Assets = result.slice(-10);
console.log('last10Assets: ', last10Assets);
return result; return result;
} catch (error) { } catch (error) {
console.log("error: ", error); console.log("error: ", error);

View File

@ -0,0 +1,7 @@
let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
export const fetchGltfUrl = (filename: string) => {
if (filename) {
return `${BackEnd_url}/api/v1/getAssetFile/${filename}`;
}
return null; // or handle the case when filename is not provided
};

View File

@ -0,0 +1,4 @@
let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
export const getAssetDownload = (filename: any) => {
return `${BackEnd_url}/api/v1/getAssetFile/${filename}.gltf`;
};

View File

@ -0,0 +1,25 @@
let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
export const getSortedAssets = async (category: any, orders: any) => {
try {
const response = await fetch(
`${BackEnd_url}/api/v1/categoryWise/${category}?sortBy=${orders}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error(`Error: ${response.statusText}`);
}
const result = await response.json();
return result; // Return the result to be used later
} catch (error: any) {
console.error("Error fetching category:", error.message);
throw new Error(error.message);
}
};

View File

@ -0,0 +1,30 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const addingWidgets = async (
zoneId: string,
organization: string,
widget: {}
) => {
try {
const response = await fetch(`${url_Backend_dwinzo}/api/v2/widget/save`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ organization, zoneId, widget }),
});
if (!response.ok) {
throw new Error("Failed to add widget in the zone");
}
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,27 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
console.log("url_Backend_dwinzo: ", url_Backend_dwinzo);
export const getSelect2dZoneData = async (
ZoneId?: string,
organization?: string
) => {
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/ZoneVisualization/${ZoneId}?organization=${organization}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error("Failed to fetch zoneDatas");
}
return await response.json();
} catch (error: any) {
throw new Error(error.message);
}
};

View File

@ -0,0 +1,23 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const getZone2dData = async (organization?: string) => {
try {
const response = await fetch(
`${url_Backend_dwinzo}/api/v2/pageZodeData?organization=${organization}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error("Failed to fetch zoneDatas");
}
return await response.json();
} catch (error: any) {
throw new Error(error.message);
}
};

View File

@ -1,22 +1,19 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_BACKEND_URL}`; let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
export const panelData = async ( export const panelData = async (
organization: string, organization: string,
zoneID: string, zoneId: string,
panelOrder: Side[] panelOrder: Side[]
) => { ) => {
console.log("panelOrder: ", panelOrder);
console.log("zoneID: ", zoneID);
console.log("organization: ", organization);
try { try {
const response = await fetch(`${url_Backend_dwinzo}/api/v1/panel/save`, { const response = await fetch(`${url_Backend_dwinzo}/api/v2/panel/save`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ organization, zoneID, panelOrder }), body: JSON.stringify({ organization, zoneId, panelOrder }),
}); });
if (!response.ok) { if (!response.ok) {
@ -33,16 +30,3 @@ export const panelData = async (
} }
} }
}; };
// {objects.map((obj, index) => (
// <group key={index} position={[Math.random() * 5, Math.random() * 5, 0]}>
// <Html wrapperClass={obj.className}>
// <div style={{ padding: "10px", background: "#fff", borderRadius: "6px" }}>
// <div className="header">{obj.header}</div>
// <div className="data-values">
// <div className="value">{obj.value}</div>
// <div className="per">{obj.per}</div>
// </div>
// </div>
// </Html>
// </group>
// ))}

View File

@ -2,7 +2,6 @@ import * as THREE from "three";
import * as Types from "../types/world/worldTypes"; import * as Types from "../types/world/worldTypes";
import { create } from "zustand"; import { create } from "zustand";
import { io } from "socket.io-client"; import { io } from "socket.io-client";
import { ComponentType, SVGProps } from "react";
export const useSocketStore = create<any>((set: any, get: any) => ({ export const useSocketStore = create<any>((set: any, get: any) => ({
socket: null, socket: null,
@ -311,30 +310,14 @@ export const useSelectedPath = create<any>((set: any) => ({
setSelectedPath: (x: any) => set({ selectedPath: x }), setSelectedPath: (x: any) => set({ selectedPath: x }),
})); }));
interface Path {
modeluuid: string;
modelName: string;
points: {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | [];
triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | [];
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
}[];
pathPosition: [number, number, number];
pathRotation: [number, number, number];
speed: number;
}
interface SimulationPathsStore { interface SimulationPathsStore {
simulationPaths: Path[]; simulationPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[];
setSimulationPaths: (paths: Path[]) => void; setSimulationPaths: (paths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[]) => void;
} }
export const useSimulationPaths = create<SimulationPathsStore>((set) => ({ export const useSimulationPaths = create<SimulationPathsStore>((set) => ({
simulationPaths: [], simulationPaths: [],
setSimulationPaths: (paths: Path[]) => set({ simulationPaths: paths }), setSimulationPaths: (paths) => set({ simulationPaths: paths }),
})); }));
export const useIsConnecting = create<any>((set: any) => ({ export const useIsConnecting = create<any>((set: any) => ({
@ -365,4 +348,7 @@ export const useEditPosition = create<EditPositionState>((set) => ({
setEdit: (value) => set({ Edit: value }), // Properly updating the state setEdit: (value) => set({ Edit: value }), // Properly updating the state
})); }));
export const useAsset3dWidget = create<any>((set: any) => ({
widgetSelect: "",
setWidgetSelect: (x: any) => set({ widgetSelect: x }),
}));

View File

@ -6,15 +6,15 @@ interface Measurement {
} }
interface MeasurementStore { interface MeasurementStore {
measurements: Measurement[]; measurements: Record<string, Measurement>; // Change array to Record<string, Measurement>
interval: number; interval: number;
duration: string; duration: string;
setMeasurements: (newMeasurements: Measurement[]) => void; setMeasurements: (newMeasurements: Record<string, Measurement>) => void;
updateDuration: (newDuration: string) => void; updateDuration: (newDuration: string) => void;
} }
const useChartStore = create<MeasurementStore>((set) => ({ const useChartStore = create<MeasurementStore>((set) => ({
measurements: [], measurements: {}, // Initialize as an empty object
interval: 1000, interval: 1000,
duration: "1h", duration: "1h",
@ -26,3 +26,4 @@ const useChartStore = create<MeasurementStore>((set) => ({
})); }));
export default useChartStore; export default useChartStore;

View File

@ -14,6 +14,7 @@ interface SelectedZoneState {
zoneName: string; zoneName: string;
activeSides: Side[]; activeSides: Side[];
panelOrder: Side[]; panelOrder: Side[];
lockedPanels: Side[]; lockedPanels: Side[];
zoneId: string; zoneId: string;
zoneViewPortTarget: number[]; zoneViewPortTarget: number[];

View File

@ -126,6 +126,8 @@
display: flex; display: flex;
max-height: 180px; max-height: 180px;
justify-content: center; justify-content: center;
border-radius: #{$border-radius-medium};
overflow: hidden;
img{ img{
height: inherit; height: inherit;
width: 100%; width: 100%;
@ -211,7 +213,7 @@
background-color: var(--background-color); background-color: var(--background-color);
display: flex; display: flex;
gap: 12px; gap: 12px;
z-index: 100; overflow: hidden;
border-radius: 20px; border-radius: 20px;
} }

View File

@ -109,7 +109,7 @@
.productionCapacity-wrapper { .productionCapacity-wrapper {
background: var(--background-color); background-color: var(--background-color);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 6px; gap: 6px;
@ -170,6 +170,9 @@
} }
} }
} }
.bar-chart{
padding:14px 0;
}
} }

View File

@ -285,3 +285,35 @@ interface ConnectionStore {
addConnection: (newConnection: PathConnection) => void; addConnection: (newConnection: PathConnection) => void;
removeConnection: (fromUUID: string, toUUID: string) => void; removeConnection: (fromUUID: string, toUUID: string) => void;
} }
interface ConveyorEventsSchema {
modeluuid: string;
modelName: string;
type: 'Conveyor';
points: {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
actions: { uuid: string; name: string; type: string; material: string; delay: number | string; spawnInterval: number | string; isUsed: boolean }[] | [];
triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | [];
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
}[];
assetPosition: [number, number, number];
assetRotation: [number, number, number];
speed: number;
}
interface VehicleEventsSchema {
modeluuid: string;
modelName: string;
type: 'Vehicle';
point: {
uuid: string;
position: [number, number, number];
actions: { uuid: string; name: string; type: string; start: string, hitCount: number, end: string, buffer: number; isUsed: boolean }[] | [];
triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] | [];
connections: { source: { pathUUID: string; pointUUID: string }; targets: { pathUUID: string; pointUUID: string }[] };
};
assetPosition: [number, number, number];
speed: number;
}