- {/* Progress Bar */}
+ {/* Dynamic Progress Bar */}
{[...Array(totalBars)].map((_, i) => (
@@ -47,11 +53,11 @@ const ProductionCapacity = () => {
Avg. Process Time
- 28.4 Secs/unit
+ {avgProcessTime}
Machine Utilization
- 78%
+ {machineUtilization}
diff --git a/app/src/components/ui/analysis/ROISummary.tsx b/app/src/components/ui/analysis/ROISummary.tsx
index cacd2c7..11101fd 100644
--- a/app/src/components/ui/analysis/ROISummary.tsx
+++ b/app/src/components/ui/analysis/ROISummary.tsx
@@ -1,54 +1,68 @@
-import React from "react";
+import React, { useState } from "react";
import { ROISummaryIcon } from "../../icons/analysis";
import SemiCircleProgress from "./SemiCircleProgress";
-const ROISummary = () => {
- // Data for the cost breakdown as an array of objects
- const costBreakdownData = [
- {
- item: "Raw Material A",
- unitCost: "₹ 10/unit",
- laborCost: "₹ 0",
- totalCost: "₹ 1000",
- sellingPrice: "₹ 1500",
- },
- {
- item: "Labor",
- unitCost: "₹ 10/unit",
- laborCost: "₹ 500",
- totalCost: "₹ 500",
- sellingPrice: "N/A",
- },
- {
- item: "Product 1",
- unitCost: "₹ 10/unit",
- laborCost: "₹ 200",
- totalCost: "₹ 200",
- sellingPrice: "₹ 2000",
- },
- {
- item: "Machine",
- unitCost: "-",
- laborCost: "-",
- totalCost: "₹ 20,000",
- sellingPrice: "N/A",
- },
- {
- item: "Total",
- unitCost: "-",
- laborCost: "-",
- totalCost: "₹ 1,20,000",
- sellingPrice: "-",
- },
- {
- item: "Net Profit",
- unitCost: "-",
- laborCost: "-",
- totalCost: "₹ 1,60,000",
- sellingPrice: "-",
- },
- ];
- const progressValue = 50;
+const ROISummary = ({
+ roiSummaryData = {
+ productName: "Product name",
+ roiPercentage: 133,
+ paybackPeriod: 50.3,
+ totalCost: "₹ 1,20,000",
+ revenueGenerated: "₹ 2,80,000",
+ netProfit: "₹ 1,60,000",
+ costBreakdown: [
+ {
+ item: "Raw Material A",
+ unitCost: "₹ 10/unit",
+ laborCost: "₹ 0",
+ totalCost: "₹ 1000",
+ sellingPrice: "₹ 1500",
+ },
+ {
+ item: "Labor",
+ unitCost: "₹ 10/unit",
+ laborCost: "₹ 500",
+ totalCost: "₹ 500",
+ sellingPrice: "N/A",
+ },
+ {
+ item: "Product 1",
+ unitCost: "₹ 10/unit",
+ laborCost: "₹ 200",
+ totalCost: "₹ 200",
+ sellingPrice: "₹ 2000",
+ },
+ {
+ item: "Machine",
+ unitCost: "-",
+ laborCost: "-",
+ totalCost: "₹ 20,000",
+ sellingPrice: "N/A",
+ },
+ {
+ item: "Total",
+ unitCost: "-",
+ laborCost: "-",
+ totalCost: "₹ 1,20,000",
+ sellingPrice: "-",
+ },
+ {
+ item: "Net Profit",
+ unitCost: "-",
+ laborCost: "-",
+ totalCost: "₹ 1,60,000",
+ sellingPrice: "-",
+ },
+ ],
+ },
+}) => {
+ const [isTableOpen, setIsTableOpen] = useState(false); // State to handle the table open/close
+
+ // Function to toggle the breakdown table visibility
+ const toggleTable = () => {
+ setIsTableOpen(!isTableOpen);
+ };
+
return (
@@ -63,71 +77,94 @@ const ROISummary = () => {
Product :
-
Product name
+
{roiSummaryData.productName}
- +133% ROI with payback in just 50.3 months
+ +{roiSummaryData.roiPercentage}% ROI with payback in
+ just {roiSummaryData.paybackPeriod} months
-
-
+
+
+
+ you're on track to hit it by
+
July 2029
+
+
Total Cost Incurred
- ₹ 1,20,000
+ {roiSummaryData.totalCost}
Revenue Generated
- ₹ 2,80,000
+
+ {roiSummaryData.revenueGenerated}
+
Net Profit
- ₹ 1,60,000
+ {roiSummaryData.netProfit}
-
-
①
-
Cost Breakdown
-
⌵
+
+
+ ①
+ Cost Breakdown
+
+
+
+ {isTableOpen ? "⌵" : "⌵"}
+
-
-
-
- Item
- Unit Cost
- Labor Cost
- Total Cost
- Selling Price
-
-
-
- {costBreakdownData.map((row, index) => (
-
- {row.item}
- {row.unitCost}
- {row.laborCost}
- {row.totalCost}
- {row.sellingPrice}
+
+
+
+
+ Item
+ Unit Cost
+ Labor Cost
+ Total Cost
+ Selling Price
- ))}
-
-
+
+
+ {roiSummaryData.costBreakdown.map((row, index) => (
+
+ {row.item}
+ {row.unitCost}
+ {row.laborCost}
+ {row.totalCost}
+ {row.sellingPrice}
+
+ ))}
+
+
+
diff --git a/app/src/components/ui/analysis/SemiCircleProgress.tsx b/app/src/components/ui/analysis/SemiCircleProgress.tsx
index bd11d28..084636b 100644
--- a/app/src/components/ui/analysis/SemiCircleProgress.tsx
+++ b/app/src/components/ui/analysis/SemiCircleProgress.tsx
@@ -1,23 +1,25 @@
import React from "react";
const SemiCircleProgress = () => {
- const progress = 10;
- const clampedProgress = Math.min(Math.max(progress, 0), 100); // clamp 0-100
+ const progress = 50;
+ const clampedProgress = Math.min(Math.max(progress, 0), 100);
+ const gradientProgress = clampedProgress * 0.5;
+
return (
-
{clampedProgress}%
+
+
{clampedProgress}%
+
Years
+
+
you're on track to hit it by July 2029
);
};
diff --git a/app/src/components/ui/analysis/ThroughputSummary.tsx b/app/src/components/ui/analysis/ThroughputSummary.tsx
index cb4fac5..b92a82d 100644
--- a/app/src/components/ui/analysis/ThroughputSummary.tsx
+++ b/app/src/components/ui/analysis/ThroughputSummary.tsx
@@ -11,21 +11,57 @@ import { PowerIcon, ThroughputSummaryIcon } from "../../icons/analysis";
ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement);
+// Helper function to generate random colors
+const getRandomColor = () => {
+ const letters = "0123456789ABCDEF";
+ let color = "#";
+ for (let i = 0; i < 6; i++) {
+ color += letters[Math.floor(Math.random() * 16)];
+ }
+ return color;
+};
+
const ThroughputSummary = () => {
- const data = {
+ // Define all data internally within the component
+ const timeRange = {
+ startTime: "08:00 AM",
+ endTime: "09:00 AM",
+ };
+
+ const throughputData = {
labels: ["08:00", "08:10", "08:20", "08:30", "08:40", "08:50", "09:00"],
+ data: [100, 120, 110, 130, 125, 128, 132],
+ totalThroughput: 1240,
+ assetUsage: 85,
+ };
+
+ const energyConsumption = {
+ energyConsumed: 456,
+ unit: "KWH",
+ };
+
+ // Dynamic shift data
+ const shiftUtilization = [
+ { shift: 1, percentage: 30 },
+ { shift: 2, percentage: 40 },
+ { shift: 3, percentage: 30 },
+ ];
+
+ // Chart data configuration
+ const chartData = {
+ labels: throughputData.labels,
datasets: [
{
label: "Units/hour",
- data: [100, 120, 110, 130, 125, 128, 132],
+ data: throughputData.data,
borderColor: "#B392F0",
tension: 0.4,
- pointRadius: 0, // hide points
+ pointRadius: 0, // Hide points
},
],
};
- const options = {
+ const chartOptions = {
responsive: true,
scales: {
x: {
@@ -57,19 +93,15 @@ const ThroughputSummary = () => {
},
};
- const shiftUtilization = {
- "shift 1": 25,
- "shift 2": 45,
- "shift 3": 15,
- };
-
return (
Throughput Summary
-
08:00 - 09:00 AM
+
+ {timeRange.startTime} - {timeRange.endTime}
+
@@ -78,14 +110,15 @@ const ThroughputSummary = () => {
- 1240 Units/hour
+ {throughputData.totalThroughput} {" "}
+ Units/hour
Asset usage
-
85%
+
{throughputData.assetUsage}%
-
+
@@ -97,43 +130,41 @@ const ThroughputSummary = () => {
-
456
-
KWH
+
{energyConsumption.energyConsumed}
+
{energyConsumption.unit}
Shift Utilization
-
85%
+
{throughputData.assetUsage}%
-
-
-
+ {/* Dynamically create progress bars based on shiftUtilization array */}
+ {shiftUtilization.map((shift) => (
+
+ ))}
+
-
-
- Shift 1
-
-
-
- Shift 2
-
-
-
- Shift 3
-
+ {/* Dynamically create shift indicators with random colors */}
+ {shiftUtilization.map((shift) => (
+
+
+ Shift {shift.shift}
+
+ ))}
diff --git a/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx b/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx
index e2c2936..53e03ce 100644
--- a/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx
+++ b/app/src/components/ui/inputs/PreviewSelectionWithUpload.tsx
@@ -1,47 +1,72 @@
import React, { useState } from "react";
-import LabledDropdown from "./LabledDropdown";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
+import LabledDropdown from "./LabledDropdown";
-const PreviewSelectionWithUpload: React.FC = () => {
- const [showPreview, setSetshowPreview] = useState(false);
+interface PreviewSelectionWithUploadProps {
+ preview?: boolean;
+ upload?: boolean;
+ label?: string;
+ onSelect: (option: string) => void;
+ defaultOption: string;
+ options: string[];
+}
+
+const PreviewSelectionWithUpload: React.FC
= ({
+ preview = false,
+ upload = false,
+ onSelect,
+ label,
+ defaultOption,
+ options,
+}) => {
+ const [showPreview, setShowPreview] = useState(false);
return (
-
setSetshowPreview(!showPreview)}
- >
-
Preview
-
-
- {showPreview && (
-
-
+ {preview && (
+ <>
+
setShowPreview(!showPreview)}
+ >
+ Preview
+
+
+ {showPreview && (
+
+ )}
+ >
+ )}
+ {upload && (
+
+
+
Upload Product
+
+
+ Upload here
+
+
)}
-
-
-
Upload Product
-
-
- Upload here
-
-
-
console.log(option)}
- />
-
+
);
};
diff --git a/app/src/components/ui/list/List.tsx b/app/src/components/ui/list/List.tsx
index cac0b43..06419af 100644
--- a/app/src/components/ui/list/List.tsx
+++ b/app/src/components/ui/list/List.tsx
@@ -10,7 +10,7 @@ import {
ArrowIcon,
EyeIcon,
LockIcon,
- RmoveIcon,
+ RemoveIcon,
} from "../../icons/ExportCommonIcons";
import { useThree } from "@react-three/fiber";
import { useFloorItems, useZoneAssetId, useZones } from "../../../store/store";
@@ -142,9 +142,6 @@ const List: React.FC
= ({ items = [], remove }) => {
)
);
}
-
- console.log('newName: ', newName);
-
}
const checkZoneNameDuplicate = (name: string) => {
return zones.some(
@@ -184,7 +181,7 @@ const List: React.FC = ({ items = [], remove }) => {
{remove && (
-
+
)}
{item.assets && item.assets.length > 0 && (
@@ -221,7 +218,7 @@ const List: React.FC = ({ items = [], remove }) => {
{remove && (
-
+
)}
diff --git a/app/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx
index 6fd1e94..eb937c8 100644
--- a/app/src/components/ui/menu/EditWidgetOption.tsx
+++ b/app/src/components/ui/menu/EditWidgetOption.tsx
@@ -1,23 +1,20 @@
import React, { useEffect } from "react";
import {
- useEditWidgetOptionsStore,
useLeftData,
- useRightClickSelected,
- useRightSelected,
useTopData,
} from "../../../store/visualization/useZone3DWidgetStore";
interface EditWidgetOptionProps {
options: string[];
+ onClick: (option: string) => void;
}
const EditWidgetOption: React.FC
= ({
options,
+ onClick
}) => {
const { top } = useTopData();
const { left } = useLeftData();
- const { setRightSelect } = useRightSelected();
- const { setEditWidgetOptions } = useEditWidgetOptionsStore();
useEffect(() => {
@@ -38,10 +35,7 @@ const EditWidgetOption: React.FC = ({
{
- setRightSelect(option);
- setEditWidgetOptions(false);
- }}
+ onClick={() => onClick(option)}
>
{option}
diff --git a/app/src/components/ui/simulation/simulationPlayer.tsx b/app/src/components/ui/simulation/simulationPlayer.tsx
index 528fbc2..5313710 100644
--- a/app/src/components/ui/simulation/simulationPlayer.tsx
+++ b/app/src/components/ui/simulation/simulationPlayer.tsx
@@ -7,6 +7,15 @@ import {
usePlayButtonStore,
useResetButtonStore,
} from "../../../store/usePlayButtonStore";
+import {
+ DailyProductionIcon,
+ EndIcon,
+ ExpandIcon,
+ HourlySimulationIcon,
+ MonthlyROI,
+ SpeedIcon,
+ StartIcon,
+} from "../../icons/ExportCommonIcons";
const SimulationPlayer: React.FC = () => {
const { speed, setSpeed } = useAnimationPlaySpeed();
@@ -30,7 +39,7 @@ const SimulationPlayer: React.FC = () => {
const handleExit = () => {
setPlaySimulation(false);
setIsPlaying(false);
- setActiveTool("cursor")
+ setActiveTool("cursor");
};
// Slider functions starts
@@ -72,70 +81,277 @@ const SimulationPlayer: React.FC = () => {
}, []);
// Slider function ends
+ // UI-Part
+ const hourlySimulation = 25;
+ const dailyProduction = 75;
+ const monthlyROI = 50;
+
+ const process = [
+ { name: "process 1", completed: 0 }, // 0% completed
+ { name: "process 2", completed: 20 }, // 20% completed
+ { name: "process 3", completed: 40 }, // 40% completed
+ { name: "process 4", completed: 60 }, // 60% completed
+ { name: "process 5", completed: 80 }, // 80% completed
+ { name: "process 6", completed: 100 }, // 100% completed
+ { name: "process 7", completed: 0 }, // 0% completed
+ { name: "process 8", completed: 50 }, // 50% completed
+ { name: "process 9", completed: 90 }, // 90% completed
+ { name: "process 10", completed: 30 }, // 30% completed
+ ];
+ const [expand, setExpand] = useState(false);
+ // Move getRandomColor out of render
+ const getRandomColor = () => {
+ const letters = "0123456789ABCDEF";
+ let color = "#";
+ for (let i = 0; i < 6; i++) {
+ color += letters[Math.floor(Math.random() * 16)];
+ }
+ return color;
+ };
+
+ // Store colors for each process item
+ const [processColors, setProcessColors] = useState([]);
+
+ // Generate colors on mount or when process changes
+ useEffect(() => {
+ const generatedColors = process.map(() => getRandomColor());
+ setProcessColors(generatedColors);
+ }, []);
+
+ const intervals = [10, 20, 30, 40, 50, 60]; // in minutes
+ const totalSegments = intervals.length;
+ const progress = 80; // percent (example)
+
+ const processPlayerRef = useRef(null);
+ const processWrapperRef = useRef(null);
+ const [playerPosition, setPlayerPosition] = useState(0);
+
+ const handleProcessMouseDown = (e: React.MouseEvent) => {
+ if (!processWrapperRef.current) return;
+
+ const rect = processWrapperRef.current.getBoundingClientRect();
+ let x = e.clientX - rect.left;
+ x = Math.max(0, Math.min(x, rect.width));
+ setPlayerPosition(x);
+
+ const onMouseMove = (e: MouseEvent) => {
+ if (!processWrapperRef.current) return;
+ const newRect = processWrapperRef.current.getBoundingClientRect();
+ let newX = e.clientX - newRect.left;
+ newX = Math.max(0, Math.min(newX, newRect.width));
+ setPlayerPosition(newX);
+
+ const progressPercent = (newX / newRect.width) * 100;
+ console.log(`Dragging at progress: ${progressPercent.toFixed(1)}%`);
+ };
+
+ const onMouseUp = () => {
+ document.removeEventListener("mousemove", onMouseMove);
+ document.removeEventListener("mouseup", onMouseUp);
+ };
+
+ document.addEventListener("mousemove", onMouseMove);
+ document.addEventListener("mouseup", onMouseUp);
+ };
+
return (
-
+
-
{
- handleReset();
- }}
- >
-
- Reset
-
-
{
- handlePlayStop();
- }}
- >
-
- {playSimulation ? "Play" : "Stop"}
-
-
{
- handleExit();
- }}
- >
-
- Exit
-
-
-
-
0.5x
-
-
-
-
-
-
-
-
-
-
-
-
- {speed.toFixed(1)}x
+
+ {/* hourlySimulation */}
+
+
+
+
+
+
Hourly Simulation
-
+
+
+ {/* dailyProduction */}
+
+
+
+
+
+
Daily Production
+
+
+
+ {/* monthlyROI */}
+
-
8x
+
+
{
+ handleReset();
+ }}
+ >
+
+ Reset
+
+
{
+ handlePlayStop();
+ }}
+ >
+
+ {playSimulation ? "Play" : "Stop"}
+
+
{
+ handleExit();
+ }}
+ >
+
+ Exit
+
+
setExpand(!expand)}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
23 April ,25
+
04:41 PM
+
+
+
+
+ {intervals.map((label, index) => {
+ const segmentProgress = (index / totalSegments) * 100;
+ const isFilled = progress >= segmentProgress;
+ return (
+
+
+ {index < intervals.length - 1 && (
+ = ((index + 1) / totalSegments) * 100
+ ? "filled"
+ : ""
+ }`}
+ >
+ )}
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
0X
+
+
+
+
+
+
+
+
+
+
+
+ {speed.toFixed(1)}x
+
+
+
+
8x
+
+
+
+
+
+
+ {process.map((item, index) => (
+
+ ))}
+
+
diff --git a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts
index e88dc3c..c46c0e7 100644
--- a/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts
+++ b/app/src/modules/builder/IntialLoad/loadInitialFloorItems.ts
@@ -8,10 +8,13 @@ import * as Types from "../../../types/world/worldTypes";
import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi';
import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
+import PointsCalculator from '../../simulation/events/points/functions/pointsCalculator';
async function loadInitialFloorItems(
itemsGroup: Types.RefGroup,
setFloorItems: Types.setFloorItemSetState,
+ addEvent: (event: EventsSchema) => void,
+ renderDistance: number
): Promise
{
if (!itemsGroup.current) return;
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
@@ -63,14 +66,14 @@ async function loadInitialFloorItems(
const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z);
- if (cameraPosition.distanceTo(itemPosition) < 50) {
+ if (cameraPosition.distanceTo(itemPosition) < renderDistance) {
await new Promise(async (resolve) => {
// Check Three.js Cache
const cachedModel = THREE.Cache.get(item.modelfileID!);
if (cachedModel) {
// console.log(`[Cache] Fetching ${item.modelname}`);
- processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems);
+ processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
modelsLoaded++;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
return;
@@ -85,7 +88,7 @@ async function loadInitialFloorItems(
URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl);
THREE.Cache.add(item.modelfileID!, gltf);
- processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems);
+ processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
modelsLoaded++;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
},
@@ -106,7 +109,7 @@ async function loadInitialFloorItems(
const modelBlob = await fetch(modelUrl).then((res) => res.blob());
await storeGLTF(item.modelfileID!, modelBlob);
THREE.Cache.add(item.modelfileID!, gltf);
- processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems);
+ processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, addEvent);
modelsLoaded++;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
},
@@ -148,8 +151,9 @@ function processLoadedModel(
item: Types.FloorItemType,
itemsGroup: Types.RefGroup,
setFloorItems: Types.setFloorItemSetState,
+ addEvent: (event: EventsSchema) => void,
) {
- const model = gltf;
+ const model = gltf.clone();
model.uuid = item.modeluuid;
model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap);
model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid };
@@ -182,6 +186,242 @@ function processLoadedModel(
},
]);
+ if (item.modelfileID === "a1ee92554935007b10b3eb05") {
+ const data = PointsCalculator(
+ 'Vehicle',
+ gltf.clone(),
+ new THREE.Vector3(...model.rotation)
+ );
+
+ if (!data || !data.points) return;
+
+ const vehicleEvent: VehicleEventSchema = {
+ modelUuid: item.modeluuid,
+ modelName: item.modelname,
+ position: item.position,
+ rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
+ state: "idle",
+ type: "vehicle",
+ speed: 1,
+ point: {
+ uuid: THREE.MathUtils.generateUUID(),
+ position: [data.points[0].x, data.points[0].y, data.points[0].z],
+ rotation: [0, 0, 0],
+ action: {
+ actionUuid: THREE.MathUtils.generateUUID(),
+ actionName: "Vehicle Action",
+ actionType: "travel",
+ unLoadDuration: 5,
+ loadCapacity: 10,
+ pickUpPoint: null,
+ unLoadPoint: null,
+ triggers: []
+ }
+ }
+ };
+ addEvent(vehicleEvent);
+ } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") {
+ const data = PointsCalculator(
+ 'Conveyor',
+ gltf.clone(),
+ new THREE.Vector3(...model.rotation)
+ );
+
+ if (!data || !data.points) return;
+
+ const ConveyorEvent: ConveyorEventSchema = {
+ modelUuid: item.modeluuid,
+ modelName: item.modelname,
+ position: item.position,
+ rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
+ state: "idle",
+ type: "transfer",
+ speed: 1,
+ points: data.points.map((point: THREE.Vector3, index: number) => ({
+ uuid: THREE.MathUtils.generateUUID(),
+ position: [point.x, point.y, point.z],
+ rotation: [0, 0, 0],
+ action: {
+ actionUuid: THREE.MathUtils.generateUUID(),
+ actionName: `Action ${index + 1}`,
+ actionType: 'default',
+ material: 'Default material',
+ delay: 0,
+ spawnInterval: 5,
+ spawnCount: 1,
+ triggers: []
+ }
+ }))
+ };
+ addEvent(ConveyorEvent);
+ } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") {
+ // const data = PointsCalculator(
+ // 'Conveyor',
+ // gltf.clone(),
+ // new THREE.Vector3(...model.rotation)
+ // );
+
+ // if (!data || !data.points) return;
+
+ // const points: ConveyorPointSchema[] = data.points.map((point: THREE.Vector3, index: number) => {
+ // const actionUuid = THREE.MathUtils.generateUUID();
+ // return {
+ // uuid: THREE.MathUtils.generateUUID(),
+ // position: [point.x, point.y, point.z],
+ // rotation: [0, 0, 0],
+ // action: {
+ // actionUuid,
+ // actionName: `Action ${index}`,
+ // actionType: 'default',
+ // material: 'inherit',
+ // delay: 0,
+ // spawnInterval: 5,
+ // spawnCount: 1,
+ // triggers: []
+ // }
+ // };
+ // });
+
+ // points.forEach((point, index) => {
+ // if (index < points.length - 1) {
+ // const nextPoint = points[index + 1];
+ // point.action.triggers.push({
+ // triggerUuid: THREE.MathUtils.generateUUID(),
+ // triggerName: `Trigger 1`,
+ // triggerType: "onComplete",
+ // delay: 0,
+ // triggeredAsset: {
+ // triggeredModel: {
+ // modelName: item.modelname,
+ // modelUuid: item.modeluuid
+ // },
+ // triggeredPoint: {
+ // pointName: `Point ${index + 1}`,
+ // pointUuid: nextPoint.uuid
+ // },
+ // triggeredAction: {
+ // actionName: nextPoint.action.actionName,
+ // actionUuid: nextPoint.action.actionUuid
+ // }
+ // }
+ // });
+ // }
+ // });
+
+ // const ConveyorEvent: ConveyorEventSchema = {
+ // modelUuid: item.modeluuid,
+ // modelName: item.modelname,
+ // position: item.position,
+ // rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
+ // state: "idle",
+ // type: "transfer",
+ // speed: 1,
+ // points
+ // };
+ // addEvent(ConveyorEvent);
+ } else if (item.modelfileID === "7dc04e36882e4debbc1a8e3d") {
+ const data = PointsCalculator(
+ 'Conveyor',
+ gltf.clone(),
+ new THREE.Vector3(...model.rotation)
+ );
+
+ if (!data || !data.points) return;
+
+ const ConveyorEvent: ConveyorEventSchema = {
+ modelUuid: item.modeluuid,
+ modelName: item.modelname,
+ position: item.position,
+ rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
+ state: "idle",
+ type: "transfer",
+ speed: 1,
+ points: data.points.map((point: THREE.Vector3, index: number) => ({
+ uuid: THREE.MathUtils.generateUUID(),
+ position: [point.x, point.y, point.z],
+ rotation: [0, 0, 0],
+ action: {
+ actionUuid: THREE.MathUtils.generateUUID(),
+ actionName: `Action ${index}`,
+ actionType: 'default',
+ material: 'inherit',
+ delay: 0,
+ spawnInterval: 5,
+ spawnCount: 1,
+ triggers: []
+ }
+ }))
+ };
+ addEvent(ConveyorEvent);
+ } else if (item.modelfileID === "29dee78715ad5b853f5c346d") {
+ const data = PointsCalculator(
+ 'StaticMachine',
+ gltf.clone(),
+ new THREE.Vector3(...model.rotation)
+ );
+
+ if (!data || !data.points) return;
+
+ const machineEvent: MachineEventSchema = {
+ modelUuid: item.modeluuid,
+ modelName: item.modelname,
+ position: item.position,
+ rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
+ state: "idle",
+ type: "machine",
+ point: {
+ uuid: THREE.MathUtils.generateUUID(),
+ position: [data.points[0].x, data.points[0].y, data.points[0].z],
+ rotation: [0, 0, 0],
+ action: {
+ actionUuid: THREE.MathUtils.generateUUID(),
+ actionName: "Process Action",
+ actionType: "process",
+ processTime: 10,
+ swapMaterial: "material-id",
+ triggers: []
+ }
+ }
+ };
+ addEvent(machineEvent);
+ } else if (item.modelfileID === "52e6681fbb743a890d96c914") {
+ const data = PointsCalculator(
+ 'ArmBot',
+ gltf.clone(),
+ new THREE.Vector3(...model.rotation)
+ );
+
+ if (!data || !data.points) return;
+
+ const roboticArmEvent: RoboticArmEventSchema = {
+ modelUuid: item.modeluuid,
+ modelName: item.modelname,
+ position: item.position,
+ rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
+ state: "idle",
+ type: "roboticArm",
+ speed: 1,
+ point: {
+ uuid: THREE.MathUtils.generateUUID(),
+ position: [data.points[0].x, data.points[0].y, data.points[0].z],
+ rotation: [0, 0, 0],
+ actions: [
+ {
+ actionUuid: THREE.MathUtils.generateUUID(),
+ actionName: "Pick and Place",
+ actionType: "pickAndPlace",
+ process: {
+ startPoint: [0, 0, 0],
+ endPoint: [0, 0, 0]
+ },
+ triggers: []
+ }
+ ]
+ }
+ };
+ addEvent(roboticArmEvent);
+ }
+
gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: 'power2.out' });
gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out' });
}
diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx
index e7f83e2..e5ff1c1 100644
--- a/app/src/modules/builder/builder.tsx
+++ b/app/src/modules/builder/builder.tsx
@@ -51,7 +51,7 @@ import Ground from "../scene/environment/ground";
// import ZoneGroup from "../groups/zoneGroup1";
import { findEnvironment } from "../../services/factoryBuilder/environment/findEnvironment";
import Layer2DVisibility from "./geomentries/layers/layer2DVisibility";
-import DrieHtmlTemp from "..//visualization/mqttTemp/drieHtmlTemp";
+import DrieHtmlTemp from "../visualization/mqttTemp/drieHtmlTemp";
import ZoneGroup from "./groups/zoneGroup";
import useModuleStore from "../../store/useModuleStore";
import MeasurementTool from "../scene/tools/measurementTool";
diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts
index 193dd41..d7c278c 100644
--- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts
+++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts
@@ -10,7 +10,7 @@ import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils';
import { Socket } from 'socket.io-client';
import * as CONSTANTS from '../../../../types/world/worldConstants';
import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
-import PointsCalculator from '../../../simulation/events/points/pointsCalculator';
+import PointsCalculator from '../../../simulation/events/points/functions/pointsCalculator';
async function addAssetModel(
raycaster: THREE.Raycaster,
@@ -202,7 +202,7 @@ async function handleModelLoad(
actionUuid: THREE.MathUtils.generateUUID(),
actionName: `Action ${index}`,
actionType: 'default',
- material: 'inherit',
+ material: 'Default Material',
delay: 0,
spawnInterval: 5,
spawnCount: 1,
@@ -226,9 +226,8 @@ async function handleModelLoad(
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
- actionName: "Vehicle Action",
+ actionName: "Action 1",
actionType: "travel",
- material: null,
unLoadDuration: 5,
loadCapacity: 10,
pickUpPoint: null,
@@ -254,11 +253,11 @@ async function handleModelLoad(
actions: [
{
actionUuid: THREE.MathUtils.generateUUID(),
- actionName: "Pick and Place",
+ actionName: "Action 1",
actionType: "pickAndPlace",
process: {
- startPoint: [0, 0, 0],
- endPoint: [0, 0, 0]
+ startPoint: null,
+ endPoint: null
},
triggers: []
}
@@ -266,7 +265,7 @@ async function handleModelLoad(
}
};
addEvent(roboticArmEvent);
- } else if (selectedItem.type === "Machine") {
+ } else if (selectedItem.type === "StaticMachine") {
const machineEvent: MachineEventSchema = {
modelUuid: newFloorItem.modeluuid,
modelName: newFloorItem.modelname,
@@ -280,10 +279,10 @@ async function handleModelLoad(
rotation: [0, 0, 0],
action: {
actionUuid: THREE.MathUtils.generateUUID(),
- actionName: "Process Action",
+ actionName: "Action 1",
actionType: "process",
processTime: 10,
- swapMaterial: "material-id",
+ swapMaterial: "Default Material",
triggers: []
}
}
diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx
index 2a3c2cc..f3f5050 100644
--- a/app/src/modules/builder/groups/floorItemsGroup.tsx
+++ b/app/src/modules/builder/groups/floorItemsGroup.tsx
@@ -75,7 +75,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
gltfLoaderWorker.postMessage({ floorItems: data });
} else {
gltfLoaderWorker.postMessage({ floorItems: [] });
- loadInitialFloorItems(itemsGroup, setFloorItems);
+ loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, renderDistance);
updateLoadingProgress(100);
}
});
@@ -94,7 +94,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
updateLoadingProgress(progress);
if (loadedAssets === totalAssets) {
- loadInitialFloorItems(itemsGroup, setFloorItems);
+ loadInitialFloorItems(itemsGroup, setFloorItems, addEvent, renderDistance);
updateLoadingProgress(100);
}
});
diff --git a/app/src/modules/scene/postProcessing/postProcessing.tsx b/app/src/modules/scene/postProcessing/postProcessing.tsx
index d3245fb..dd5607a 100644
--- a/app/src/modules/scene/postProcessing/postProcessing.tsx
+++ b/app/src/modules/scene/postProcessing/postProcessing.tsx
@@ -2,90 +2,107 @@ import * as THREE from "three";
import { EffectComposer, N8AO, Outline } from "@react-three/postprocessing";
import { BlendFunction } from "postprocessing";
import {
- useDeletableFloorItem,
- useSelectedWallItem,
- useSelectedFloorItem,
+ useDeletableFloorItem,
+ useSelectedWallItem,
+ useSelectedFloorItem,
} from "../../../store/store";
import * as Types from "../../../types/world/worldTypes";
import * as CONSTANTS from "../../../types/world/worldConstants";
import { useEffect } from "react";
+import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore";
export default function PostProcessing() {
- const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
- const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
- const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
+ const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
+ const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
+ const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
+ const { selectedEventSphere } = useSelectedEventSphere();
- function flattenChildren(children: any[]) {
- const allChildren: any[] = [];
- children.forEach((child) => {
- allChildren.push(child);
- if (child.children && child.children.length > 0) {
- allChildren.push(...flattenChildren(child.children));
- }
- });
- return allChildren;
- }
+ function flattenChildren(children: any[]) {
+ const allChildren: any[] = [];
+ children.forEach((child) => {
+ allChildren.push(child);
+ if (child.children && child.children.length > 0) {
+ allChildren.push(...flattenChildren(child.children));
+ }
+ });
+ return allChildren;
+ }
- return (
- <>
-
-
- {deletableFloorItem && (
-
- )}
- {selectedWallItem && (
- child.name !== "CSG_REF"
- )}
- selectionLayer={10}
- width={3000}
- blendFunction={BlendFunction.ALPHA}
- edgeStrength={5}
- resolutionScale={2}
- pulseSpeed={0}
- visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
- hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
- blur={true}
- xRay={true}
- />
- )}
- {selectedFloorItem && (
-
- )}
-
- >
- );
+ return (
+ <>
+
+
+ {deletableFloorItem && (
+
+ )}
+ {selectedWallItem && (
+ child.name !== "CSG_REF"
+ )}
+ selectionLayer={10}
+ width={3000}
+ blendFunction={BlendFunction.ALPHA}
+ edgeStrength={5}
+ resolutionScale={2}
+ pulseSpeed={0}
+ visibleEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
+ hiddenEdgeColor={CONSTANTS.outlineConfig.assetSelectColor}
+ blur={true}
+ xRay={true}
+ />
+ )}
+ {selectedFloorItem && (
+
+ )}
+ {selectedEventSphere && (
+
+ )}
+
+ >
+ );
}
diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx
index 6564032..0f2c489 100644
--- a/app/src/modules/scene/scene.tsx
+++ b/app/src/modules/scene/scene.tsx
@@ -9,28 +9,35 @@ import Simulation from "../simulation/simulation";
import Collaboration from "../collaboration/collaboration";
export default function Scene() {
- const map = useMemo(() => [
- { name: "forward", keys: ["ArrowUp", "w", "W"] },
- { name: "backward", keys: ["ArrowDown", "s", "S"] },
- { name: "left", keys: ["ArrowLeft", "a", "A"] },
- { name: "right", keys: ["ArrowRight", "d", "D"] },],
- []);
+ const map = useMemo(
+ () => [
+ { name: "forward", keys: ["ArrowUp", "w", "W"] },
+ { name: "backward", keys: ["ArrowDown", "s", "S"] },
+ { name: "left", keys: ["ArrowLeft", "a", "A"] },
+ { name: "right", keys: ["ArrowRight", "d", "D"] },
+ ],
+ []
+ );
- return (
-
- { e.preventDefault(); }}>
+ return (
+
+ {
+ e.preventDefault();
+ }}
+ >
+
-
+
-
+
-
+
-
-
-
-
-
-
- );
+
+
+
+ );
}
diff --git a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx
index 335f1f5..39fe257 100644
--- a/app/src/modules/simulation/events/points/creator/pointsCreator.tsx
+++ b/app/src/modules/simulation/events/points/creator/pointsCreator.tsx
@@ -1,148 +1,237 @@
-import React, { useEffect, useRef, useState } from 'react';
-import * as THREE from 'three';
-import { useEventsStore } from '../../../../../store/simulation/useEventsStore';
-import useModuleStore from '../../../../../store/useModuleStore';
-import { TransformControls } from '@react-three/drei';
-import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys';
+import React, { useEffect, useRef, useState } from "react";
+import * as THREE from "three";
+import { useEventsStore } from "../../../../../store/simulation/useEventsStore";
+import useModuleStore from "../../../../../store/useModuleStore";
+import { TransformControls } from "@react-three/drei";
+import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
+import {
+ useSelectedEventSphere,
+ useSelectedEventData,
+} from "../../../../../store/simulation/useSimulationStore";
function PointsCreator() {
- const { events, updatePoint, getPointByUuid } = useEventsStore();
- const { activeModule } = useModuleStore();
- const transformRef = useRef(null);
- const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
- const [selectedPoint, setSelectedPoint] = useState(null);
- const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
+ const { events, updatePoint, getPointByUuid, getEventByModelUuid } =
+ useEventsStore();
+ const { activeModule } = useModuleStore();
+ const transformRef = useRef(null);
+ const [transformMode, setTransformMode] = useState<
+ "translate" | "rotate" | null
+ >(null);
+ const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
+ const {
+ selectedEventSphere,
+ setSelectedEventSphere,
+ clearSelectedEventSphere,
+ } = useSelectedEventSphere();
+ const { setSelectedEventData, clearSelectedEventData } =
+ useSelectedEventData();
- useEffect(() => {
- setTransformMode(null);
- const handleKeyDown = (e: KeyboardEvent) => {
- const keyCombination = detectModifierKeys(e);
- if (!selectedPoint) return;
- if (keyCombination === "G") {
- setTransformMode((prev) => (prev === "translate" ? null : "translate"));
- }
- if (keyCombination === "R") {
- setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
- }
- };
-
- window.addEventListener("keydown", handleKeyDown);
- return () => window.removeEventListener("keydown", handleKeyDown);
- }, [selectedPoint]);
-
- const updatePointToState = (selectedPoint: THREE.Mesh) => {
- let point = JSON.parse(JSON.stringify(getPointByUuid(selectedPoint.userData.modelUuid, selectedPoint.userData.pointUuid)));
- if (point) {
- point.position = [selectedPoint.position.x, selectedPoint.position.y, selectedPoint.position.z];
- updatePoint(selectedPoint.userData.modelUuid, selectedPoint.userData.pointUuid, point)
- }
+ useEffect(() => {
+ if (selectedEventSphere) {
+ const eventData = getEventByModelUuid(
+ selectedEventSphere.userData.modelUuid
+ );
+ if (eventData) {
+ setSelectedEventData(eventData, selectedEventSphere.userData.pointUuid);
+ } else {
+ clearSelectedEventData();
+ }
+ } else {
+ clearSelectedEventData();
}
+ }, [selectedEventSphere]);
- return (
- <>
- {activeModule === 'simulation' &&
- <>
-
- {events.map((event, i) => {
- if (event.type === 'transfer') {
- return (
-
- {event.points.map((point, j) => (
- (sphereRefs.current[point.uuid] = el!)}
- onClick={(e) => {
- e.stopPropagation();
- setSelectedPoint(sphereRefs.current[point.uuid]);
- }}
- onPointerMissed={() => {
- setSelectedPoint(null);
- }}
- key={`${i}-${j}`}
- position={new THREE.Vector3(...point.position)}
- userData={{ modelUuid: event.modelUuid, pointUuid: point.uuid }}
- >
-
-
-
- ))}
-
- );
- } else if (event.type === 'vehicle') {
- return (
-
- (sphereRefs.current[event.point.uuid] = el!)}
- onClick={(e) => {
- e.stopPropagation();
- setSelectedPoint(sphereRefs.current[event.point.uuid]);
- }}
- onPointerMissed={() => {
- setSelectedPoint(null);
- }}
- position={new THREE.Vector3(...event.point.position)}
- userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }}
- >
-
-
-
-
- );
- } else if (event.type === 'roboticArm') {
- return (
-
- (sphereRefs.current[event.point.uuid] = el!)}
- onClick={(e) => {
- e.stopPropagation();
- setSelectedPoint(sphereRefs.current[event.point.uuid]);
- }}
- onPointerMissed={() => {
- setSelectedPoint(null);
- }}
- position={new THREE.Vector3(...event.point.position)}
- userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }}
- >
-
-
-
-
- );
- } else if (event.type === 'machine') {
- return (
-
- (sphereRefs.current[event.point.uuid] = el!)}
- onClick={(e) => {
- e.stopPropagation();
- setSelectedPoint(sphereRefs.current[event.point.uuid]);
- }}
- onPointerMissed={() => {
- setSelectedPoint(null);
- }}
- position={new THREE.Vector3(...event.point.position)}
- userData={{ modelUuid: event.modelUuid, pointUuid: event.point.uuid }}
- >
-
-
-
-
- );
- } else {
- return null;
- }
- })}
-
- {(selectedPoint && transformMode) &&
- { updatePointToState(selectedPoint) }} />
- }
- >
- }
- >
+ useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ const keyCombination = detectModifierKeys(e);
+ if (!selectedEventSphere) return;
+ if (keyCombination === "G") {
+ setTransformMode((prev) => (prev === "translate" ? null : "translate"));
+ }
+ if (keyCombination === "R") {
+ setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
+ }
+ };
+
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, [selectedEventSphere]);
+
+ const updatePointToState = (selectedEventSphere: THREE.Mesh) => {
+ let point = JSON.parse(
+ JSON.stringify(
+ getPointByUuid(
+ selectedEventSphere.userData.modelUuid,
+ selectedEventSphere.userData.pointUuid
+ )
+ )
);
+ if (point) {
+ point.position = [
+ selectedEventSphere.position.x,
+ selectedEventSphere.position.y,
+ selectedEventSphere.position.z,
+ ];
+ updatePoint(
+ selectedEventSphere.userData.modelUuid,
+ selectedEventSphere.userData.pointUuid,
+ point
+ );
+ }
+ };
+
+ return (
+ <>
+ {activeModule === "simulation" && (
+ <>
+
+ {events.map((event, i) => {
+ if (event.type === "transfer") {
+ return (
+
+ {event.points.map((point, j) => (
+ (sphereRefs.current[point.uuid] = el!)}
+ onClick={(e) => {
+ e.stopPropagation();
+ setSelectedEventSphere(
+ sphereRefs.current[point.uuid]
+ );
+ }}
+ onPointerMissed={() => {
+ clearSelectedEventSphere();
+ setTransformMode(null);
+ }}
+ key={`${i}-${j}`}
+ position={new THREE.Vector3(...point.position)}
+ userData={{
+ modelUuid: event.modelUuid,
+ pointUuid: point.uuid,
+ }}
+ >
+
+
+
+ ))}
+
+ );
+ } else if (event.type === "vehicle") {
+ return (
+
+ (sphereRefs.current[event.point.uuid] = el!)}
+ onClick={(e) => {
+ e.stopPropagation();
+ setSelectedEventSphere(
+ sphereRefs.current[event.point.uuid]
+ );
+ }}
+ onPointerMissed={() => {
+ clearSelectedEventSphere();
+ setTransformMode(null);
+ }}
+ position={new THREE.Vector3(...event.point.position)}
+ userData={{
+ modelUuid: event.modelUuid,
+ pointUuid: event.point.uuid,
+ }}
+ >
+
+
+
+
+ );
+ } else if (event.type === "machine") {
+ return (
+
+ (sphereRefs.current[event.point.uuid] = el!)}
+ onClick={(e) => {
+ e.stopPropagation();
+ setSelectedEventSphere(
+ sphereRefs.current[event.point.uuid]
+ );
+ }}
+ onPointerMissed={() => {
+ clearSelectedEventSphere();
+ setTransformMode(null);
+ }}
+ position={new THREE.Vector3(...event.point.position)}
+ userData={{
+ modelUuid: event.modelUuid,
+ pointUuid: event.point.uuid,
+ }}
+ >
+
+
+
+
+ );
+ } else if (event.type === "roboticArm") {
+ return (
+
+ (sphereRefs.current[event.point.uuid] = el!)}
+ onClick={(e) => {
+ e.stopPropagation();
+ setSelectedEventSphere(
+ sphereRefs.current[event.point.uuid]
+ );
+ }}
+ onPointerMissed={() => {
+ clearSelectedEventSphere();
+ setTransformMode(null);
+ }}
+ position={new THREE.Vector3(...event.point.position)}
+ userData={{
+ modelUuid: event.modelUuid,
+ pointUuid: event.point.uuid,
+ }}
+ >
+
+
+
+
+ );
+ } else {
+ return null;
+ }
+ })}
+
+ {selectedEventSphere && transformMode && (
+ {
+ updatePointToState(selectedEventSphere);
+ }}
+ />
+ )}
+ >
+ )}
+ >
+ );
}
export default PointsCreator;
diff --git a/app/src/modules/simulation/events/points/functions/handleAddEventToProduct.ts b/app/src/modules/simulation/events/points/functions/handleAddEventToProduct.ts
new file mode 100644
index 0000000..7943c1c
--- /dev/null
+++ b/app/src/modules/simulation/events/points/functions/handleAddEventToProduct.ts
@@ -0,0 +1,28 @@
+interface HandleAddEventToProductParams {
+ selectedAsset: any; // Replace `any` with specific type if you have it
+ addEvent: (productId: string, asset: any) => void;
+ selectedProduct: {
+ productId: string;
+ productName: string;
+ // Add other fields if needed
+ };
+ clearSelectedAsset: () => void;
+}
+
+export const handleAddEventToProduct = ({
+ selectedAsset,
+ addEvent,
+ selectedProduct,
+ clearSelectedAsset,
+}: HandleAddEventToProductParams) => {
+ console.log('selectedProduct: ', selectedProduct);
+ if (selectedAsset) {
+ addEvent(selectedProduct.productId, selectedAsset);
+ // upsertProductOrEventApi({
+ // productName: selectedProduct.productName,
+ // productId: selectedProduct.productId,
+ // eventDatas: selectedAsset
+ // });
+ clearSelectedAsset();
+ }
+};
diff --git a/app/src/modules/simulation/events/points/pointsCalculator.ts b/app/src/modules/simulation/events/points/functions/pointsCalculator.ts
similarity index 96%
rename from app/src/modules/simulation/events/points/pointsCalculator.ts
rename to app/src/modules/simulation/events/points/functions/pointsCalculator.ts
index 86d368e..ccc2dbb 100644
--- a/app/src/modules/simulation/events/points/pointsCalculator.ts
+++ b/app/src/modules/simulation/events/points/functions/pointsCalculator.ts
@@ -1,5 +1,5 @@
import * as THREE from 'three';
-import { Group } from '../../../../types/world/worldTypes';
+import { Group } from '../../../../../types/world/worldTypes';
function PointsCalculator(
type: string,
diff --git a/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx
new file mode 100644
index 0000000..edb825f
--- /dev/null
+++ b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+
+function MachineInstance() {
+ return (
+ <>
+ >
+ )
+}
+
+export default MachineInstance
\ No newline at end of file
diff --git a/app/src/modules/simulation/machine/instances/machineInstances.tsx b/app/src/modules/simulation/machine/instances/machineInstances.tsx
new file mode 100644
index 0000000..b0c2c9f
--- /dev/null
+++ b/app/src/modules/simulation/machine/instances/machineInstances.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import MachineInstance from './machineInstance/machineInstance'
+
+function MachineInstances() {
+ return (
+ <>
+
+
+
+ >
+ )
+}
+
+export default MachineInstances
\ No newline at end of file
diff --git a/app/src/modules/simulation/machine/machine.tsx b/app/src/modules/simulation/machine/machine.tsx
new file mode 100644
index 0000000..32c3b8e
--- /dev/null
+++ b/app/src/modules/simulation/machine/machine.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import MachineInstances from './instances/machineInstances'
+
+function Machine() {
+ return (
+ <>
+
+
+
+ >
+ )
+}
+
+export default Machine
\ No newline at end of file
diff --git a/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx b/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx
new file mode 100644
index 0000000..9eececc
--- /dev/null
+++ b/app/src/modules/simulation/products/events/addOrRemoveEventsInProducts.tsx
@@ -0,0 +1,124 @@
+import { useThree } from '@react-three/fiber'
+import { useEffect } from 'react'
+import { Object3D } from 'three';
+import { useSubModuleStore } from '../../../../store/useModuleStore';
+import { useLeftData, useTopData } from '../../../../store/visualization/useZone3DWidgetStore';
+import { useEventsStore } from '../../../../store/simulation/useEventsStore';
+import { useProductStore } from '../../../../store/simulation/useProductStore';
+import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore';
+import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore';
+
+function AddOrRemoveEventsInProducts() {
+ const { gl, raycaster, scene } = useThree();
+ const { subModule } = useSubModuleStore();
+ const { setTop } = useTopData();
+ const { setLeft } = useLeftData();
+ const { getIsEventInProduct } = useProductStore();
+ const { getEventByModelUuid } = useEventsStore();
+ const { selectedProduct } = useSelectedProduct();
+ const { selectedAsset, setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
+
+ useEffect(() => {
+
+ const canvasElement = gl.domElement;
+
+ let drag = false;
+ let isRightMouseDown = false;
+
+ const onMouseDown = (evt: MouseEvent) => {
+ if (selectedAsset) {
+ clearSelectedAsset();
+ }
+ if (evt.button === 2) {
+ isRightMouseDown = true;
+ drag = false;
+ }
+ };
+
+ const onMouseUp = (evt: MouseEvent) => {
+ if (evt.button === 2) {
+ isRightMouseDown = false;
+ }
+ }
+
+ const onMouseMove = () => {
+ if (isRightMouseDown) {
+ drag = true;
+ }
+ };
+
+ const handleRightClick = (evt: MouseEvent) => {
+ if (drag) return;
+ evt.preventDefault();
+ const canvasElement = gl.domElement;
+ if (!canvasElement) return;
+
+ let intersects = raycaster.intersectObjects(scene.children, true);
+ if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
+ let currentObject = intersects[0].object;
+
+ while (currentObject) {
+ if (currentObject.name === "Scene") {
+ break;
+ }
+ currentObject = currentObject.parent as Object3D;
+ }
+ if (currentObject) {
+ const isInProduct = getIsEventInProduct(selectedProduct.productId, currentObject.uuid);
+
+ if (isInProduct) {
+ const event = getEventByModelUuid(currentObject.uuid);
+ if (event) {
+ setSelectedAsset(event)
+ const canvasRect = canvasElement.getBoundingClientRect();
+ const relativeX = evt.clientX - canvasRect.left;
+ const relativeY = evt.clientY - canvasRect.top;
+
+ setTop(relativeY);
+ setLeft(relativeX);
+ } else {
+ clearSelectedAsset()
+ }
+ } else {
+ const event = getEventByModelUuid(currentObject.uuid);
+ if (event) {
+ setSelectedAsset(event)
+ const canvasRect = canvasElement.getBoundingClientRect();
+ const relativeX = evt.clientX - canvasRect.left;
+ const relativeY = evt.clientY - canvasRect.top;
+
+ setTop(relativeY);
+ setLeft(relativeX);
+ } else {
+ clearSelectedAsset()
+ }
+ }
+
+ }
+ } else {
+ clearSelectedAsset()
+ }
+
+ };
+
+ if (subModule === 'simulations') {
+ canvasElement.addEventListener("mousedown", onMouseDown);
+ canvasElement.addEventListener("mouseup", onMouseUp);
+ canvasElement.addEventListener("mousemove", onMouseMove);
+ canvasElement.addEventListener('contextmenu', handleRightClick);
+ }
+
+ return () => {
+ canvasElement.removeEventListener("mousedown", onMouseDown);
+ canvasElement.removeEventListener("mouseup", onMouseUp);
+ canvasElement.removeEventListener("mousemove", onMouseMove);
+ canvasElement.removeEventListener('contextmenu', handleRightClick);
+ };
+
+ }, [gl, subModule, selectedProduct, selectedAsset]);
+ return (
+ <>>
+ )
+}
+
+export default AddOrRemoveEventsInProducts
\ No newline at end of file
diff --git a/app/src/modules/simulation/products/products.tsx b/app/src/modules/simulation/products/products.tsx
new file mode 100644
index 0000000..38175e2
--- /dev/null
+++ b/app/src/modules/simulation/products/products.tsx
@@ -0,0 +1,43 @@
+import * as THREE from 'three';
+import { useEffect } from 'react';
+import { useProductStore } from '../../../store/simulation/useProductStore';
+import { useSelectedProduct } from '../../../store/simulation/useSimulationStore';
+import AddOrRemoveEventsInProducts from './events/addOrRemoveEventsInProducts';
+import { upsertProductOrEventApi } from '../../../services/simulation/UpsertProductOrEventApi';
+import { getAllProductsApi } from '../../../services/simulation/getallProductsApi';
+
+function Products() {
+ const { products, addProduct } = useProductStore();
+ const { setSelectedProduct } = useSelectedProduct();
+
+ useEffect(() => {
+ if (products.length === 0) {
+ const id = THREE.MathUtils.generateUUID();
+ const name = 'Product 1';
+ addProduct(name, id);
+ // upsertProductOrEventApi({ productName: name, productId: id }).then((data) => {
+ // console.log('data: ', data);
+ // });
+ setSelectedProduct(id, name);
+ }
+ }, [products])
+
+ useEffect(() => {
+ // const email = localStorage.getItem('email')
+ // const organization = (email!.split("@")[1]).split(".")[0];
+ // console.log(organization);
+ // getAllProductsApi(organization).then((data) => {
+ // console.log('data: ', data);
+ // })
+ }, [])
+
+ return (
+ <>
+
+
+
+ >
+ )
+}
+
+export default Products
\ No newline at end of file
diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx
index 0cd4fe2..fd7590e 100644
--- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx
@@ -1,6 +1,64 @@
-import React from 'react'
+import React, { useEffect, useMemo, useRef, useState } from 'react'
+import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
+import { useFrame, useThree } from '@react-three/fiber';
+import * as THREE from "three"
+import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
+
+function RoboticArmAnimator({ armUuid, HandleCallback, currentPhase, ikSolver, targetBone, robot, logStatus, groupRef, processes, armBotCurveRef, path }: any) {
+ const { armBots } = useArmBotStore();
+ const { scene } = useThree();
+ const restSpeed = 0.1;
+ const restPosition = new THREE.Vector3(0, 2, 1.6);
+ const initialCurveRef = useRef(null);
+ const initialStartPositionRef = useRef(null);
+ const [initialProgress, setInitialProgress] = useState(0);
+ const [progress, setProgress] = useState(0);
+ const [needsInitialMovement, setNeedsInitialMovement] = useState(true);
+ const [isInitializing, setIsInitializing] = useState(true);
+ const { isPlaying } = usePlayButtonStore();
+ const statusRef = useRef("idle");
+ // Create a ref for initialProgress
+ const initialProgressRef = useRef(0);
+ const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
+
+ useEffect(() => {
+ setCurrentPath(path)
+ }, [path])
+
+ useEffect(() => {
+
+ }, [currentPath])
+
+ useFrame((_, delta) => {
+ if (!ikSolver || !currentPath || currentPath.length === 0) return;
+
+ const bone = ikSolver.mesh.skeleton.bones.find(
+ (b: any) => b.name === targetBone
+ );
+ if (!bone) return;
+
+ // Ensure currentPath is a valid array of 3D points, create a CatmullRomCurve3 from it
+ const curve = new THREE.CatmullRomCurve3(
+ currentPath.map(point => new THREE.Vector3(point[0], point[1], point[2]))
+ );
+
+ const next = initialProgressRef.current + delta * 0.5;
+ if (next >= 1) {
+ bone.position.copy(restPosition);
+ HandleCallback(); // Call the callback when the path is completed
+ initialProgressRef.current = 0; // Set ref to 1 when done
+ } else {
+
+ const point = curve.getPoint(next); // Get the interpolated point from the curve
+ bone.position.copy(point); // Update the bone position along the curve
+ initialProgressRef.current = next; // Update progress
+ }
+
+ ikSolver.update();
+ });
+
+
-function RoboticArmAnimator() {
return (
<>>
)
diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
index 2817906..86eee6a 100644
--- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
@@ -1,14 +1,179 @@
-import React from 'react'
+import React, { useEffect, useRef, useState } from 'react'
import IKInstance from '../ikInstance/ikInstance';
import RoboticArmAnimator from '../animator/roboticArmAnimator';
+import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
+import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
+import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_4.glb";
+import { useThree } from "@react-three/fiber";
+import { useFloorItems } from '../../../../../store/store';
+import useModuleStore from '../../../../../store/useModuleStore';
+import { Vector3 } from "three";
+import * as THREE from "three";
+
+interface Process {
+ triggerId: string;
+ startPoint?: Vector3;
+ endPoint?: Vector3;
+ speed: number;
+}
+function RoboticArmInstance({ robot }: { robot: ArmBotStatus }) {
+
+ const { isPlaying } = usePlayButtonStore();
+ const [currentPhase, setCurrentPhase] = useState<(string)>("init");
+ const { scene } = useThree();
+ const targetBone = "Target";
+ const { activeModule } = useModuleStore();
+ const [ikSolver, setIkSolver] = useState(null);
+ const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
+ const { floorItems } = useFloorItems();
+ const groupRef = useRef(null);
+ const [processes, setProcesses] = useState([]);
+ const [armBotCurvePoints, setArmBotCurvePoints] = useState({ start: [], end: [] })
+ const restPosition = new THREE.Vector3(0, 2, 1.6);
+ let armBotCurveRef = useRef(null)
+ const [path, setPath] = useState<[number, number, number][]>([]);
+
+ useEffect(() => {
+ let armItems = floorItems?.filter((val: any) =>
+ val.modeluuid === "3abf5d46-b59e-4e6b-9c02-a4634b64b82d"
+ );
+ // Get the first matching item
+ let armItem = armItems?.[0];
+ if (armItem) {
+ const targetMesh = scene?.getObjectByProperty("uuid", armItem.modeluuid);
+ if (targetMesh) {
+ targetMesh.visible = activeModule !== "simulation"
+ }
+ }
+ const targetBones = ikSolver?.mesh.skeleton.bones.find(
+ (b: any) => b.name === targetBone
+ );
+
+ if (isPlaying) {
+ //Moving armBot from initial point to rest position.
+ if (!robot?.isActive && robot?.state == "idle" && currentPhase == "init") {
+
+ setArmBotActive(robot.modelUuid, true)
+ setArmBotState(robot.modelUuid, "running")
+ setCurrentPhase("init-to-rest");
+ if (targetBones) {
+ let curve = createCurveBetweenTwoPoints(targetBones.position, restPosition)
+ if (curve) {
+ setPath(curve.points.map(point => [point.x, point.y, point.z]));
+ }
+ }
+ logStatus(robot.modelUuid, "Starting from init to rest")
+ }
+ //Waiting for trigger.
+ else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "rest" && !robot.currentAction) {
+ console.log("trigger");
+ setTimeout(() => {
+ addCurrentAction(robot.modelUuid, 'action-003');
+ }, 3000);
+ }
+ else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "rest" && robot.currentAction) {
+ if (robot.currentAction) {
+ setArmBotActive(robot.modelUuid, true);
+ setArmBotState(robot.modelUuid, "running");
+ setCurrentPhase("rest-to-start");
+ const startPoint = robot.point.actions[0].process.startPoint;
+ if (startPoint) {
+ let curve = createCurveBetweenTwoPoints(restPosition, new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]));
+ if (curve) {
+ setPath(curve.points.map(point => [point.x, point.y, point.z]));
+ }
+ }
+ }
+ logStatus(robot.modelUuid, "Starting from rest to start")
+ }
+ else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "picking" && robot.currentAction) {
+ setArmBotActive(robot.modelUuid, true);
+ setArmBotState(robot.modelUuid, "running");
+ setCurrentPhase("start-to-end");
+ const startPoint = robot.point.actions[0].process.startPoint;
+ const endPoint = robot.point.actions[0].process.endPoint;
+ if (startPoint && endPoint) {
+ let curve = createCurveBetweenTwoPoints(
+ new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]),
+ new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2])
+ );
+ if (curve) {
+ setPath(curve.points.map(point => [point.x, point.y, point.z]));
+ }
+ }
+ logStatus(robot.modelUuid, "Starting from start to end")
+ }
+ else if (robot && !robot.isActive && robot.state === "idle" && currentPhase === "dropping" && robot.currentAction) {
+ setArmBotActive(robot.modelUuid, true);
+ setArmBotState(robot.modelUuid, "running");
+ setCurrentPhase("end-to-rest");
+ const endPoint = robot.point.actions[0].process.endPoint;
+ if (endPoint) {
+ let curve = createCurveBetweenTwoPoints(new THREE.Vector3(endPoint[0], endPoint[1], endPoint[2]), restPosition
+ );
+ if (curve) {
+ setPath(curve.points.map(point => [point.x, point.y, point.z]));
+ }
+ }
+ logStatus(robot.modelUuid, "Starting from end to rest")
+ }
+ }
+
+ }, [currentPhase, robot, isPlaying, ikSolver])
+
+
+ function createCurveBetweenTwoPoints(p1: any, p2: any) {
+ const mid = new THREE.Vector3().addVectors(p1, p2).multiplyScalar(0.5);
+ mid.y += 0.5;
+
+ const points = [p1, mid, p2];
+ return new THREE.CatmullRomCurve3(points);
+ }
+
+
+ const HandleCallback = () => {
+ if (robot.isActive && robot.state == "running" && currentPhase == "init-to-rest") {
+ console.log("Callback triggered: rest");
+ setArmBotActive(robot.modelUuid, false)
+ setArmBotState(robot.modelUuid, "idle")
+ setCurrentPhase("rest");
+ setPath([])
+ }
+ else if (robot.isActive && robot.state == "running" && currentPhase == "rest-to-start") {
+ console.log("Callback triggered: pick.");
+ setArmBotActive(robot.modelUuid, false)
+ setArmBotState(robot.modelUuid, "idle")
+ setCurrentPhase("picking");
+ setPath([])
+ }
+ else if (robot.isActive && robot.state == "running" && currentPhase == "start-to-end") {
+ console.log("Callback triggered: drop.");
+ setArmBotActive(robot.modelUuid, false)
+ setArmBotState(robot.modelUuid, "idle")
+ setCurrentPhase("dropping");
+ setPath([])
+ }
+ else if (robot.isActive && robot.state == "running" && currentPhase == "end-to-rest") {
+ console.log("Callback triggered: rest, cycle completed.");
+ setArmBotActive(robot.modelUuid, false)
+ setArmBotState(robot.modelUuid, "idle")
+ setCurrentPhase("rest");
+ setPath([])
+ removeCurrentAction(robot.modelUuid)
+ }
+ }
+ const logStatus = (id: string, status: string) => {
+ console.log(id +","+ status);
+ }
-function RoboticArmInstance() {
return (
<>
-
-
-
+
+
>
)
diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx
index 52a8610..a8d8782 100644
--- a/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/ikInstance/ikInstance.tsx
@@ -1,8 +1,87 @@
-import React from 'react'
+import React, { useEffect, useMemo, useRef, useState } from 'react'
+import * as THREE from "three";
+import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
+import { clone } from "three/examples/jsm/utils/SkeletonUtils";
+import { useFrame, useLoader, useThree } from "@react-three/fiber";
+import { CCDIKSolver, CCDIKHelper, } from "three/examples/jsm/animation/CCDIKSolver";
+type IKInstanceProps = {
+ modelUrl: string;
+ ikSolver: any;
+ setIkSolver: any
+ robot: any;
+ groupRef: React.RefObject;
+ processes: any;
+ setArmBotCurvePoints: any
+};
+function IKInstance({ modelUrl, setIkSolver, ikSolver, robot, groupRef, processes, setArmBotCurvePoints }: IKInstanceProps) {
+ const { scene } = useThree();
+ const gltf = useLoader(GLTFLoader, modelUrl, (loader) => {
+ const draco = new DRACOLoader();
+ draco.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/");
+ loader.setDRACOLoader(draco);
+ });
+ const cloned = useMemo(() => clone(gltf?.scene), [gltf]);
+ const targetBoneName = "Target";
+ const skinnedMeshName = "link_0";
+ useEffect(() => {
+ if (!gltf) return;
+ const OOI: any = {};
+ cloned.traverse((n: any) => {
+ if (n.name === targetBoneName) OOI.Target_Bone = n;
+ if (n.name === skinnedMeshName) OOI.Skinned_Mesh = n;
+ });
+ if (!OOI.Target_Bone || !OOI.Skinned_Mesh) return;
+ const iks = [
+ {
+ target: 7,
+ effector: 6,
+ links: [
+ {
+ index: 5,
+ enabled: true,
+ rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0),
+ rotationMax: new THREE.Vector3(Math.PI / 2, 0, 0),
+ },
+ {
+ index: 4,
+ enabled: true,
+ rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0),
+ rotationMax: new THREE.Vector3(0, 0, 0),
+ },
+ {
+ index: 3,
+ enabled: true,
+ rotationMin: new THREE.Vector3(0, 0, 0),
+ rotationMax: new THREE.Vector3(2, 0, 0),
+ },
+ { index: 1, enabled: true, limitation: new THREE.Vector3(0, 1, 0) },
+ { index: 0, enabled: false, limitation: new THREE.Vector3(0, 0, 0) },
+ ],
+ },
+ ];
+
+ const solver = new CCDIKSolver(OOI.Skinned_Mesh, iks);
+ setIkSolver(solver);
+
+ const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05);
+
+ // scene.add(groupRef.current)
+
+
+ }, [gltf]);
-function IKInstance() {
return (
- <>>
+ <>
+
+
+
+ >
)
}
diff --git a/app/src/modules/simulation/roboticArm/instances/ikInstances.tsx b/app/src/modules/simulation/roboticArm/instances/ikInstances.tsx
deleted file mode 100644
index d44ddd2..0000000
--- a/app/src/modules/simulation/roboticArm/instances/ikInstances.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import React from 'react'
-import IKInstance from './ikInstance/ikInstance';
-
-function IkInstances() {
- return (
- <>
-
-
-
- >
- )
-}
-
-export default IkInstances;
\ No newline at end of file
diff --git a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx
index 6e8a70a..1089fa5 100644
--- a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx
@@ -1,11 +1,15 @@
import React from 'react'
import RoboticArmInstance from './armInstance/roboticArmInstance';
+import { useArmBotStore } from '../../../../store/simulation/useArmBotStore';
function RoboticArmInstances() {
+ const { armBots } = useArmBotStore();
+
return (
<>
-
-
+ {armBots?.map((robot: ArmBotStatus) => (
+
+ ))}
>
)
diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx
index 1270d93..dcf01da 100644
--- a/app/src/modules/simulation/roboticArm/roboticArm.tsx
+++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx
@@ -1,15 +1,166 @@
-import React from 'react'
-import RoboticArmInstances from './instances/roboticArmInstances';
-import IkInstances from './instances/ikInstances';
+import { useEffect } from "react";
+import RoboticArmInstances from "./instances/roboticArmInstances";
+import { useArmBotStore } from "../../../store/simulation/useArmBotStore";
+import { useFloorItems } from "../../../store/store";
function RoboticArm() {
+ const { armBots, addArmBot, removeArmBot } = useArmBotStore();
+ const { floorItems } = useFloorItems();
+
+ const armBotStatusSample: RoboticArmEventSchema[] = [
+ {
+ state: "idle",
+ modelUuid: "armbot-xyz-001",
+ modelName: "ArmBot-X200",
+ position: [91.94347308985614, 0, 6.742905194869091],
+ rotation: [0, 0, 0],
+ type: "roboticArm",
+ speed: 1.5,
+ point: {
+ uuid: "point-123",
+ position: [0, 1.5, 0],
+ rotation: [0, 0, 0],
+ actions: [
+ {
+ actionUuid: "action-003",
+ actionName: "Pick Component",
+ actionType: "pickAndPlace",
+ process: {
+ startPoint: [5.52543010919071, 1, -8.433681161200905],
+ endPoint: [10.52543010919071, 1, -12.433681161200905],
+ },
+ triggers: [
+ {
+ triggerUuid: "trigger-001",
+ triggerName: "Start Trigger",
+ triggerType: "onStart",
+ delay: 0,
+ triggeredAsset: {
+ triggeredModel: {
+ modelName: "Conveyor A1",
+ modelUuid: "conveyor-01",
+ },
+ triggeredPoint: {
+ pointName: "Start Point",
+ pointUuid: "conveyor-01-point-001",
+ },
+ triggeredAction: {
+ actionName: "Move Forward",
+ actionUuid: "conveyor-action-01",
+ },
+ },
+ },
+ {
+ triggerUuid: "trigger-002",
+ triggerName: "Complete Trigger",
+ triggerType: "onComplete",
+ delay: 0,
+ triggeredAsset: {
+ triggeredModel: {
+ modelName: "StaticMachine B2",
+ modelUuid: "machine-02",
+ },
+ triggeredPoint: {
+ pointName: "Receive Point",
+ pointUuid: "machine-02-point-001",
+ },
+ triggeredAction: {
+ actionName: "Process Part",
+ actionUuid: "machine-action-01",
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ },
+ {
+ state: "idle",
+ modelUuid: "armbot-xyz-002",
+ modelName: "ArmBot-X200",
+ position: [95.94347308985614, 0, 6.742905194869091],
+ rotation: [0, 0, 0],
+ type: "roboticArm",
+ speed: 1.5,
+ point: {
+ uuid: "point-123",
+ position: [0, 1.5, 0],
+ rotation: [0, 0, 0],
+ actions: [
+ {
+ actionUuid: "action-001",
+ actionName: "Pick Component",
+ actionType: "pickAndPlace",
+ process: {
+ startPoint: [2.52543010919071, 0, 8.433681161200905],
+ endPoint: [95.3438373267953, 0, 9.0279187421610025],
+ },
+ triggers: [
+ {
+ triggerUuid: "trigger-001",
+ triggerName: "Start Trigger",
+ triggerType: "onStart",
+ delay: 0,
+ triggeredAsset: {
+ triggeredModel: {
+ modelName: "Conveyor A1",
+ modelUuid: "conveyor-01",
+ },
+ triggeredPoint: {
+ pointName: "Start Point",
+ pointUuid: "conveyor-01-point-001",
+ },
+ triggeredAction: {
+ actionName: "Move Forward",
+ actionUuid: "conveyor-action-01",
+ },
+ },
+ },
+ {
+ triggerUuid: "trigger-002",
+ triggerName: "Complete Trigger",
+ triggerType: "onComplete",
+ delay: 0,
+ triggeredAsset: {
+ triggeredModel: {
+ modelName: "StaticMachine B2",
+ modelUuid: "machine-02",
+ },
+ triggeredPoint: {
+ pointName: "Receive Point",
+ pointUuid: "machine-02-point-001",
+ },
+ triggeredAction: {
+ actionName: "Process Part",
+ actionUuid: "machine-action-01",
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ },
+ ];
+
+ useEffect(() => {
+
+ removeArmBot(armBotStatusSample[0].modelUuid);
+ addArmBot('123', armBotStatusSample[0]);
+ // addArmBot('123', armBotStatusSample[1]);
+ // addCurrentAction('armbot-xyz-001', 'action-001');
+ }, []);
+
+ useEffect(() => {
+ //
+ }, [armBots]);
+
return (
<>
-
-
>
- )
+ );
}
-export default RoboticArm;
\ No newline at end of file
+export default RoboticArm;
diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx
index b7bf36d..5ca0ec5 100644
--- a/app/src/modules/simulation/simulation.tsx
+++ b/app/src/modules/simulation/simulation.tsx
@@ -5,8 +5,16 @@ import Vehicles from './vehicle/vehicles';
import Points from './events/points/points';
import Conveyor from './conveyor/conveyor';
import RoboticArm from './roboticArm/roboticArm';
+import Materials from './materials/materials';
+import Machine from './machine/machine';
+import StorageUnit from './storageUnit/storageUnit';
+import Simulator from './simulator/simulator';
+import Products from './products/products';
+import Trigger from './triggers/trigger';
+import useModuleStore from '../../store/useModuleStore';
function Simulation() {
+ const { activeModule } = useModuleStore();
const { events } = useEventsStore();
const { products } = useProductStore();
@@ -21,16 +29,36 @@ function Simulation() {
return (
<>
-
+ {activeModule === 'simulation' &&
-
+ <>
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+
+ }
>
- )
+ );
}
-export default Simulation
\ No newline at end of file
+export default Simulation;
diff --git a/app/src/modules/simulation/simulator/simulator.tsx b/app/src/modules/simulation/simulator/simulator.tsx
new file mode 100644
index 0000000..c4f1c40
--- /dev/null
+++ b/app/src/modules/simulation/simulator/simulator.tsx
@@ -0,0 +1,18 @@
+import { useEffect } from 'react'
+import { useProductStore } from '../../../store/simulation/useProductStore'
+
+function Simulator() {
+ const { products } = useProductStore();
+
+ useEffect(() => {
+ // console.log('products: ', products);
+ }, [products])
+
+ return (
+ <>
+
+ >
+ )
+}
+
+export default Simulator
\ No newline at end of file
diff --git a/app/src/modules/simulation/storageUnit/instances/storageUnitInstance/storageUnitInstance.tsx b/app/src/modules/simulation/storageUnit/instances/storageUnitInstance/storageUnitInstance.tsx
new file mode 100644
index 0000000..29d404e
--- /dev/null
+++ b/app/src/modules/simulation/storageUnit/instances/storageUnitInstance/storageUnitInstance.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+
+function storageUnitInstance() {
+ return (
+ <>
+ >
+ )
+}
+
+export default storageUnitInstance
\ No newline at end of file
diff --git a/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx b/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx
new file mode 100644
index 0000000..d79b5d8
--- /dev/null
+++ b/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import StorageUnitInstance from './storageUnitInstance/storageUnitInstance'
+
+function StorageUnitInstances() {
+ return (
+ <>
+
+
+
+ >
+ )
+}
+
+export default StorageUnitInstances
\ No newline at end of file
diff --git a/app/src/modules/simulation/storageUnit/storageUnit.tsx b/app/src/modules/simulation/storageUnit/storageUnit.tsx
new file mode 100644
index 0000000..eee0875
--- /dev/null
+++ b/app/src/modules/simulation/storageUnit/storageUnit.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import StorageUnitInstances from './instances/storageUnitInstances'
+
+function StorageUnit() {
+ return (
+ <>
+
+
+
+ >
+ )
+}
+
+export default StorageUnit
\ No newline at end of file
diff --git a/app/src/modules/simulation/triggers/connector/triggerConnector.tsx b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx
new file mode 100644
index 0000000..f836ea4
--- /dev/null
+++ b/app/src/modules/simulation/triggers/connector/triggerConnector.tsx
@@ -0,0 +1,103 @@
+import { useEffect } from "react";
+import { useThree } from "@react-three/fiber";
+import { useSubModuleStore } from "../../../../store/useModuleStore";
+import { useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
+import { useProductStore } from "../../../../store/simulation/useProductStore";
+import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore";
+
+function TriggerConnector() {
+ const { gl, raycaster, scene } = useThree();
+ const { subModule } = useSubModuleStore();
+ const { getPointByUuid, getIsEventInProduct } = useProductStore();
+ const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
+ const { selectedProduct } = useSelectedProduct();
+
+ useEffect(() => {
+
+ const canvasElement = gl.domElement;
+
+ let drag = false;
+ let isRightMouseDown = false;
+
+ const onMouseDown = (evt: MouseEvent) => {
+ if (selectedAsset) {
+ clearSelectedAsset();
+ }
+ if (evt.button === 2) {
+ isRightMouseDown = true;
+ drag = false;
+ }
+ };
+
+ const onMouseUp = (evt: MouseEvent) => {
+ if (evt.button === 2) {
+ isRightMouseDown = false;
+ }
+ }
+
+ const onMouseMove = () => {
+ if (isRightMouseDown) {
+ drag = true;
+ }
+ };
+
+ const handleRightClick = (evt: MouseEvent) => {
+ if (drag) return;
+ evt.preventDefault();
+ const canvasElement = gl.domElement;
+ if (!canvasElement) return;
+
+ let intersects = raycaster.intersectObjects(scene.children, true);
+ if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
+ let currentObject = intersects[0].object;
+
+ if (currentObject && currentObject.name === 'Event-Sphere') {
+
+ const isInProduct = getIsEventInProduct(
+ selectedProduct.productId,
+ currentObject.userData.modelUuid
+ );
+
+ // You left Here
+
+ if (isInProduct) {
+
+ const event = getPointByUuid(
+ selectedProduct.productId,
+ currentObject.userData.modelUuid,
+ currentObject.userData.pointUuid
+ );
+ console.log('event: ', event);
+ } else {
+
+ }
+ }
+
+ } else {
+
+ }
+ };
+
+ if (subModule === 'simulations') {
+ canvasElement.addEventListener("mousedown", onMouseDown);
+ canvasElement.addEventListener("mouseup", onMouseUp);
+ canvasElement.addEventListener("mousemove", onMouseMove);
+ canvasElement.addEventListener('contextmenu', handleRightClick);
+ }
+
+ return () => {
+ canvasElement.removeEventListener("mousedown", onMouseDown);
+ canvasElement.removeEventListener("mouseup", onMouseUp);
+ canvasElement.removeEventListener("mousemove", onMouseMove);
+ canvasElement.removeEventListener('contextmenu', handleRightClick);
+ };
+
+ }, [gl, subModule]);
+
+ return (
+ <>
+ >
+ )
+}
+
+export default TriggerConnector
\ No newline at end of file
diff --git a/app/src/modules/simulation/triggers/temp.md b/app/src/modules/simulation/triggers/temp.md
deleted file mode 100644
index e69de29..0000000
diff --git a/app/src/modules/simulation/triggers/trigger.tsx b/app/src/modules/simulation/triggers/trigger.tsx
new file mode 100644
index 0000000..110da2e
--- /dev/null
+++ b/app/src/modules/simulation/triggers/trigger.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import TriggerConnector from './connector/triggerConnector'
+
+function Trigger() {
+ return (
+ <>
+
+
+
+ >
+ )
+}
+
+export default Trigger
\ No newline at end of file
diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx
index cf5fd81..a0eeabd 100644
--- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx
+++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx
@@ -1,62 +1,172 @@
-import { useEffect, useState } from 'react'
+import { useEffect, useRef, useState } from 'react'
import { useFrame, useThree } from '@react-three/fiber';
+import { useFloorItems } from '../../../../../store/store';
+import * as THREE from 'three';
+import { Line } from '@react-three/drei';
+import { useAnimationPlaySpeed, usePauseButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
+import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
interface VehicleAnimatorProps {
- path: [number, number, number][];
- handleCallBack: () => void;
- currentPhase: string;
- agvUuid: number
+ path: [number, number, number][];
+ handleCallBack: () => void;
+ currentPhase: string;
+ agvUuid: number;
+ agvDetail: any;
}
+function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetail }: VehicleAnimatorProps) {
+ const { decrementVehicleLoad, vehicles } = useVehicleStore();
+ const { isPaused } = usePauseButtonStore();
+ const { speed } = useAnimationPlaySpeed();
+ const { isReset } = useResetButtonStore();
+ const [restRotation, setRestingRotation] = useState(true);
+ const [progress, setProgress] = useState(0);
+ const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
+ const { scene } = useThree();
+ const progressRef = useRef(0);
+ const movingForward = useRef(true);
+ const completedRef = useRef(false);
+ let startTime: number;
+ let pausedTime: number;
+ let fixedInterval: number;
-function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid }: VehicleAnimatorProps) {
- const [progress, setProgress] = useState(0)
- const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
- const { scene } = useThree();
+ useEffect(() => {
+ if (currentPhase === 'stationed-pickup' && path.length > 0) {
+ setCurrentPath(path);
+ } else if (currentPhase === 'pickup-drop' && path.length > 0) {
+ setCurrentPath(path);
+ } else if (currentPhase === 'drop-pickup' && path.length > 0) {
+ setCurrentPath(path);
+ }
+ }, [currentPhase, path]);
- useEffect(() => {
+ useEffect(() => {
+ setProgress(0);
+ completedRef.current = false;
+ }, [currentPath]);
- if (currentPhase === 'stationed-pickup' && path.length > 0) {
- setCurrentPath(path);
+ useFrame((_, delta) => {
+ const object = scene.getObjectByProperty('uuid', agvUuid);
+ if (!object || currentPath.length < 2) return;
+ if (isPaused) return;
+
+ let totalDistance = 0;
+ const distances = [];
+
+ for (let i = 0; i < currentPath.length - 1; i++) {
+ const start = new THREE.Vector3(...currentPath[i]);
+ const end = new THREE.Vector3(...currentPath[i + 1]);
+ const segmentDistance = start.distanceTo(end);
+ distances.push(segmentDistance);
+ totalDistance += segmentDistance;
+ }
+
+ let coveredDistance = progressRef.current;
+ let accumulatedDistance = 0;
+ let index = 0;
+
+ while (
+ index < distances.length &&
+ coveredDistance > accumulatedDistance + distances[index]
+ ) {
+ accumulatedDistance += distances[index];
+ index++;
+ }
+
+ if (index < distances.length) {
+ const start = new THREE.Vector3(...currentPath[index]);
+ const end = new THREE.Vector3(...currentPath[index + 1]);
+ const segmentDistance = distances[index];
+
+ const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
+ const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);
+ const rotationSpeed = 2.0;
+ const currentAngle = object.rotation.y;
+
+ let angleDifference = targetAngle - currentAngle;
+ if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
+ if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI;
+
+ const maxRotationStep = rotationSpeed * delta;
+ object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep);
+
+ const isAligned = Math.abs(angleDifference) < 0.01;
+
+ if (isAligned) {
+ progressRef.current += delta * (speed * agvDetail.speed);
+ coveredDistance = progressRef.current;
+
+ const t = (coveredDistance - accumulatedDistance) / segmentDistance;
+ const position = start.clone().lerp(end, t);
+ object.position.copy(position);
+ }
+ }
+
+ if (progressRef.current >= totalDistance) {
+ if (restRotation) {
+ const targetQuaternion = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0));
+ object.quaternion.slerp(targetQuaternion, delta * 2);
+ const angleDiff = object.quaternion.angleTo(targetQuaternion);
+ if (angleDiff < 0.01) {
+ let objectRotation = agvDetail.point.rotation
+ object.rotation.set(objectRotation[0], objectRotation[1], objectRotation[2]);
+ setRestingRotation(false);
}
+ return;
+ }
+ }
- }, [currentPhase, path])
+ if (progressRef.current >= totalDistance) {
+ setRestingRotation(true);
+ progressRef.current = 0;
+ movingForward.current = !movingForward.current;
+ setCurrentPath([]);
+ handleCallBack();
+ if (currentPhase === 'pickup-drop') {
+ requestAnimationFrame(firstFrame);
+ }
+ }
+ });
- useFrame((_, delta) => {
- if (!path || path.length < 2) return;
+ function firstFrame() {
+ const unLoadDuration = agvDetail.point.action.unLoadDuration;
+ const droppedMaterial = agvDetail.currentLoad;
+ fixedInterval = (unLoadDuration / droppedMaterial) * 1000;
+ startTime = performance.now();
+ step(droppedMaterial);
+ }
- const object = scene.getObjectByProperty("uuid", agvUuid)
- if (!object) return;
+ function step(droppedMaterial: number) {
+ const elapsedTime = performance.now() - startTime;
- setProgress(prev => {
- const next = prev + delta * 0.1; // speed
- return next >= 1 ? 1 : next;
- });
+ if (elapsedTime >= fixedInterval) {
+ console.log('fixedInterval: ', fixedInterval);
+ console.log('elapsedTime: ', elapsedTime);
+ let droppedMat = droppedMaterial - 1;
+ decrementVehicleLoad(agvDetail.modelUuid, 1);
+ if (droppedMat === 0) return;
+ startTime = performance.now();
+ requestAnimationFrame(() => step(droppedMat));
+ } else {
+ requestAnimationFrame(() => step(droppedMaterial));
+ }
+ }
- const totalSegments = path.length - 1;
- const segmentIndex = Math.floor(progress * totalSegments);
- const t = progress * totalSegments - segmentIndex;
-
- const start = path[segmentIndex];
- const end = path[segmentIndex + 1] || start;
-
- // Directly set position without creating a new Vector3
- object.position.x = start[0] + (end[0] - start[0]) * t;
- object.position.y = start[1] + (end[1] - start[1]) * t;
- object.position.z = start[2] + (end[2] - start[2]) * t;
- });
- // useFrame(() => {
- // if (currentPath.length === 0) return;
- // const object = scene.getObjectByProperty("uuid", agvUuid);
- // if (!object) return;
-
-
-
- // })
- return (
+ return (
+ <>
+ {currentPath.length > 0 && (
<>
+
+ {currentPath.map((point, index) => (
+
+
+
+
+ ))}
>
- )
+ )}
+ >
+ );
}
-export default VehicleAnimator
\ No newline at end of file
+export default VehicleAnimator;
\ No newline at end of file
diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx
index bf767ec..d0691f9 100644
--- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx
+++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx
@@ -1,66 +1,123 @@
-import React, { useCallback, useEffect, useState } from 'react'
-import VehicleAnimator from '../animator/vehicleAnimator'
-import * as THREE from "three";
+import React, { useCallback, useEffect, useState } from 'react';
+import VehicleAnimator from '../animator/vehicleAnimator';
+import * as THREE from 'three';
import { NavMeshQuery } from '@recast-navigation/core';
import { useNavMesh } from '../../../../../store/store';
import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useVehicleStore } from '../../../../../store/simulation/useVehicleStore';
-function VehicleInstance({ agvDetails }: any) {
- const { navMesh } = useNavMesh();
- const { isPlaying } = usePlayButtonStore();
- const { setVehicleActive, setVehicleState } = useVehicleStore();
- const [currentPhase, setCurrentPhase] = useState<(string)>("stationed");
- const [path, setPath] = useState<[number, number, number][]>([]);
+function VehicleInstance({ agvDetail }: any) {
+ const { navMesh } = useNavMesh();
+ const { isPlaying } = usePlayButtonStore();
+ const { vehicles, setVehicleActive, setVehicleState, incrementVehicleLoad } = useVehicleStore();
+ const [currentPhase, setCurrentPhase] = useState('stationed');
+ const [path, setPath] = useState<[number, number, number][]>([]);
- const computePath = useCallback((start: any, end: any) => {
+ const computePath = useCallback(
+ (start: any, end: any) => {
+ try {
+ const navMeshQuery = new NavMeshQuery(navMesh);
+ const { path: segmentPath } = navMeshQuery.computePath(start, end);
+ return (
+ segmentPath?.map(({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]) || []
+ );
+ } catch {
+ return [];
+ }
+ },
+ [navMesh]
+ );
+ function vehicleStatus(modelid: string, status: string) {
+ // console.log(`AGV ${modelid}: ${status}`);
+ }
- try {
- const navMeshQuery = new NavMeshQuery(navMesh);
- const { path: segmentPath } = navMeshQuery.computePath(start, end);
- return (
- segmentPath?.map(
- ({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]
- ) || []
- );
- } catch {
- return [];
- }
- }, [navMesh]);
-
- useEffect(() => {
-
-
- if (isPlaying) {
- if (!agvDetails.isActive && agvDetails.state == "idle" && currentPhase == "stationed") {
- const toPickupPath = computePath(new THREE.Vector3(agvDetails.position[0], agvDetails.position[1], agvDetails.position[2]), agvDetails.point.action.pickUpPoint);
- setPath(toPickupPath)
- setVehicleActive(agvDetails.modelUuid, true)
- setVehicleState(agvDetails.modelUuid, "running")
- setCurrentPhase("stationed-pickup")
- //
- }
- }
- }, [agvDetails, currentPhase, path, isPlaying])
-
- function handleCallBack() {
- if (currentPhase === "stationed-pickup") {
- setVehicleActive(agvDetails.modelUuid, false)
- setVehicleState(agvDetails.modelUuid, "idle")
- setCurrentPhase("picking")
- setPath([])
+ useEffect(() => {
+ if (isPlaying) {
+ if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'stationed') {
+ const toPickupPath = computePath(
+ new THREE.Vector3(agvDetail.position[0], agvDetail.position[1], agvDetail.position[2]),
+ agvDetail.point.action.pickUpPoint
+ );
+ setPath(toPickupPath);
+ setVehicleActive(agvDetail.modelUuid, true);
+ setVehicleState(agvDetail.modelUuid, 'running');
+ setCurrentPhase('stationed-pickup');
+ vehicleStatus(agvDetail.modelUuid, 'Started from station, heading to pickup');
+ return;
+ } else if (
+ !agvDetail.isActive &&
+ agvDetail.state === 'idle' &&
+ currentPhase === 'picking'
+ ) {
+
+ setTimeout(() => {
+ incrementVehicleLoad(agvDetail.modelUuid, 2);
+ }, 5000);
+
+ if (agvDetail.currentLoad === agvDetail.point.action.loadCapacity) {
+ const toDrop = computePath(
+ agvDetail.point.action.pickUpPoint,
+ agvDetail.point.action.unLoadPoint
+ );
+ setPath(toDrop);
+ setVehicleActive(agvDetail.modelUuid, true);
+ setVehicleState(agvDetail.modelUuid, 'running');
+ setCurrentPhase('pickup-drop');
+ vehicleStatus(agvDetail.modelUuid, 'Started from pickup point, heading to drop point');
}
+ } else if (
+ !agvDetail.isActive &&
+ agvDetail.state === 'idle' &&
+ currentPhase === 'dropping' &&
+ agvDetail.currentLoad === 0
+ ) {
+ const dropToPickup = computePath(
+ agvDetail.point.action.unLoadPoint,
+ agvDetail.point.action.pickUpPoint
+ );
+ setPath(dropToPickup);
+ setVehicleActive(agvDetail.modelUuid, true);
+ setVehicleState(agvDetail.modelUuid, 'running');
+ setCurrentPhase('drop-pickup');
+ vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point');
+ }
}
+ }, [vehicles, currentPhase, path, isPlaying]);
+ function handleCallBack() {
+ if (currentPhase === 'stationed-pickup') {
+ setVehicleActive(agvDetail.modelUuid, false);
+ setVehicleState(agvDetail.modelUuid, 'idle');
+ setCurrentPhase('picking');
+ vehicleStatus(agvDetail.modelUuid, 'Reached pickup point, waiting for material');
+ setPath([]);
+ } else if (currentPhase === 'pickup-drop') {
+ setVehicleActive(agvDetail.modelUuid, false);
+ setVehicleState(agvDetail.modelUuid, 'idle');
+ setCurrentPhase('dropping');
+ vehicleStatus(agvDetail.modelUuid, 'Reached drop point');
+ setPath([]);
+ } else if (currentPhase === 'drop-pickup') {
+ setVehicleActive(agvDetail.modelUuid, false);
+ setVehicleState(agvDetail.modelUuid, 'idle');
+ setCurrentPhase('picking');
+ setPath([]);
+ vehicleStatus(agvDetail.modelUuid, 'Reached pickup point again, cycle complete');
+ }
+ }
- return (
- <>
-
-
-
- >
- )
+ return (
+ <>
+
+ >
+ );
}
-export default VehicleInstance
\ No newline at end of file
+export default VehicleInstance;
\ No newline at end of file
diff --git a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx
index 0848883..2a0070b 100644
--- a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx
+++ b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx
@@ -1,15 +1,14 @@
import React from 'react'
import VehicleInstance from './instance/vehicleInstance'
-import { useVehicleStore } from '../../../../store/simulation/useVehicleStore';
+import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'
function VehicleInstances() {
const { vehicles } = useVehicleStore();
-
return (
<>
{vehicles.map((val: any, i: any) =>
-
+
)}
>
diff --git a/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx b/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx
index fa0d9dd..370304e 100644
--- a/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx
+++ b/app/src/modules/simulation/vehicle/navMesh/polygonGenerator.tsx
@@ -17,7 +17,7 @@ export default function PolygonGenerator({
useEffect(() => {
let allLines = arrayLinesToObject(lines.current);
const wallLines = allLines?.filter((line) => line?.type === "WallLine");
- const aisleLines = allLines?.filter((line) => line?.type === "AisleLine");
+ const aisleLines = allLines?.filter((line) => line?.type === "AisleLine")
const wallPoints = wallLines
.map((pair) => pair?.line.map((vals) => vals.position))
@@ -39,9 +39,10 @@ export default function PolygonGenerator({
);
const polygons = turf.polygonize(turf.featureCollection(lineFeatures));
+
renderWallGeometry(wallPoints);
- if (polygons.features.length > 1) {
+ if (polygons.features.length > 0) {
polygons.features.forEach((feature) => {
if (feature.geometry.type === "Polygon") {
diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx
index 3364717..f0b4d17 100644
--- a/app/src/modules/simulation/vehicle/vehicles.tsx
+++ b/app/src/modules/simulation/vehicle/vehicles.tsx
@@ -1,17 +1,19 @@
import React, { useEffect } from 'react'
import VehicleInstances from './instances/vehicleInstances';
-
import { useVehicleStore } from '../../../store/simulation/useVehicleStore';
+import { useFloorItems } from '../../../store/store';
function Vehicles() {
const { vehicles, addVehicle } = useVehicleStore();
+ const { floorItems } = useFloorItems();
+
const vehicleStatusSample: VehicleEventSchema[] = [
{
- modelUuid: "veh-123",
- modelName: "Autonomous Truck A1",
- position: [10, 0, 5],
+ modelUuid: "9356f710-4727-4b50-bdb2-9c1e747ecc74",
+ modelName: "AGV",
+ position: [97.9252965204558, 0, 37.96138815638661],
rotation: [0, 0, 0],
state: "idle",
type: "vehicle",
@@ -24,11 +26,10 @@ function Vehicles() {
actionUuid: "action-456",
actionName: "Deliver to Zone A",
actionType: "travel",
- material: "crate",
- unLoadDuration: 15,
- loadCapacity: 5,
- pickUpPoint: { x: 5, y: 0, z: 3 },
- unLoadPoint: { x: 20, y: 0, z: 10 },
+ unLoadDuration: 10,
+ loadCapacity: 2,
+ pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 },
+ unLoadPoint: { x: 105.71483985219794, y: 0, z: 28.66321267938962 },
triggers: [
{
triggerUuid: "trig-001",
@@ -53,9 +54,51 @@ function Vehicles() {
}
},
{
- modelUuid: "veh-123",
- modelName: "Autonomous Truck A1",
- position: [10, 0, 5],
+ modelUuid: "b06960bb-3d2e-41f7-a646-335f389c68b4",
+ modelName: "AGV",
+ position: [89.61609306554463, 0, 33.634136622267356],
+ rotation: [0, 0, 0],
+ state: "idle",
+ type: "vehicle",
+ speed: 2.5,
+ point: {
+ uuid: "point-789",
+ position: [0, 1, 0],
+ rotation: [0, 0, 0],
+ action: {
+ actionUuid: "action-456",
+ actionName: "Deliver to Zone A",
+ actionType: "travel",
+ unLoadDuration: 10,
+ loadCapacity: 2,
+ pickUpPoint: { x: 90, y: 0, z: 28 },
+ unLoadPoint: { x: 20, y: 0, z: 10 },
+ triggers: [
+ {
+ triggerUuid: "trig-001",
+ triggerName: "Start Travel",
+ triggerType: "onStart",
+ delay: 0,
+ triggeredAsset: {
+ triggeredModel: { modelName: "ArmBot-X", modelUuid: "arm-001" },
+ triggeredPoint: { pointName: "Pickup Arm Point", pointUuid: "arm-point-01" },
+ triggeredAction: { actionName: "Grab Widget", actionUuid: "grab-001" }
+ }
+ },
+ {
+ triggerUuid: "trig-002",
+ triggerName: "Complete Travel",
+ triggerType: "onComplete",
+ delay: 2,
+ triggeredAsset: null
+ }
+ ]
+ }
+ }
+ }, {
+ modelUuid: "e729a4f1-11d2-4778-8d6a-468f1b4f6b79",
+ modelName: "forklift",
+ position: [98.85729337188162, 0, 38.36616546567653],
rotation: [0, 0, 0],
state: "idle",
type: "vehicle",
@@ -68,10 +111,9 @@ function Vehicles() {
actionUuid: "action-456",
actionName: "Deliver to Zone A",
actionType: "travel",
- material: "crate",
unLoadDuration: 15,
loadCapacity: 5,
- pickUpPoint: { x: 5, y: 0, z: 3 },
+ pickUpPoint: { x: 98.71483985219794, y: 0, z: 28.66321267938962 },
unLoadPoint: { x: 20, y: 0, z: 10 },
triggers: [
{
@@ -101,19 +143,18 @@ function Vehicles() {
useEffect(() => {
addVehicle('123', vehicleStatusSample[0]);
- addVehicle('123', vehicleStatusSample[1]);
+ // addVehicle('123', vehicleStatusSample[1]);
+ // addVehicle('123', vehicleStatusSample[2]);
}, [])
useEffect(() => {
- // console.log('vehicles: ', vehicles);
+ console.log('vehicles: ', vehicles);
}, [vehicles])
return (
<>
-
-
>
)
}
diff --git a/app/src/modules/visualization/RealTimeVisulization.tsx b/app/src/modules/visualization/RealTimeVisulization.tsx
index 50b72ba..71faefb 100644
--- a/app/src/modules/visualization/RealTimeVisulization.tsx
+++ b/app/src/modules/visualization/RealTimeVisulization.tsx
@@ -12,15 +12,12 @@ import {
useFloatingWidget,
} from "../../store/visualization/useDroppedObjectsStore";
import {
- useAsset3dWidget,
useSocketStore,
useWidgetSubOption,
- useZones,
} from "../../store/store";
import { getZone2dData } from "../../services/visulization/zone/getZoneData";
import { generateUniqueId } from "../../functions/generateUniqueId";
import { determinePosition } from "./functions/determinePosition";
-import { addingFloatingWidgets } from "../../services/visulization/zone/addFloatingWidgets";
import SocketRealTimeViz from "./socket/realTimeVizSocket.dev";
import RenderOverlay from "../../components/templates/Overlay";
import ConfirmationPopup from "../../components/layout/confirmationPopup/ConfirmationPopup";
@@ -68,20 +65,15 @@ const RealTimeVisulization: React.FC = () => {
const containerRef = useRef(null);
const { isPlaying } = usePlayButtonStore();
const { activeModule } = useModuleStore();
- const [droppedObjects, setDroppedObjects] = useState([]);
const [zonesData, setZonesData] = useState({});
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
- const { rightSelect, setRightSelect } = useRightSelected();
- const { editWidgetOptions, setEditWidgetOptions } =
- useEditWidgetOptionsStore();
+ const { setRightSelect } = useRightSelected();
+ const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore();
const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
-
- // const [floatingWidgets, setFloatingWidgets] = useState>({});
- const { floatingWidget, setFloatingWidget } = useFloatingWidget();
- const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
- const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
+ const { setFloatingWidget } = useFloatingWidget();
+ const { widgetSubOption } = useWidgetSubOption();
const { visualizationSocket } = useSocketStore();
const { setSelectedChartId } = useWidgetStore();
@@ -99,11 +91,10 @@ const RealTimeVisulization: React.FC = () => {
useEffect(() => {
async function GetZoneData() {
- const email = localStorage.getItem("email") || "";
+ const email = localStorage.getItem("email") ?? "";
const organization = email?.split("@")[1]?.split(".")[0];
try {
const response = await getZone2dData(organization);
- // console.log('response: ', response);
if (!Array.isArray(response)) {
return;
@@ -125,7 +116,9 @@ const RealTimeVisulization: React.FC = () => {
{}
);
setZonesData(formattedData);
- } catch (error) {}
+ } catch (error) {
+ console.log(error);
+ }
}
GetZoneData();
@@ -151,12 +144,10 @@ const RealTimeVisulization: React.FC = () => {
});
}, [selectedZone]);
- // useEffect(() => {}, [floatingWidgets]);
-
const handleDrop = async (event: React.DragEvent) => {
event.preventDefault();
try {
- const email = localStorage.getItem("email") || "";
+ const email = localStorage.getItem("email") ?? "";
const organization = email?.split("@")[1]?.split(".")[0];
const data = event.dataTransfer.getData("text/plain");
@@ -172,8 +163,8 @@ const RealTimeVisulization: React.FC = () => {
const relativeY = event.clientY - rect.top;
// Widget dimensions
- const widgetWidth = droppedData.width || 125;
- const widgetHeight = droppedData.height || 100;
+ const widgetWidth = droppedData.width ?? 125;
+ const widgetHeight = droppedData.height ?? 100;
// Center the widget at cursor
const centerOffsetX = widgetWidth / 2;
@@ -275,7 +266,7 @@ const RealTimeVisulization: React.FC = () => {
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
- }, [setRightClickSelected]);
+ }, [setRightClickSelected, setRightSelect]);
const [canvasDimensions, setCanvasDimensions] = useState({
width: 0,
@@ -340,6 +331,7 @@ const RealTimeVisulization: React.FC = () => {
borderRadius:
isPlaying || activeModule !== "visualization" ? "" : "6px",
}}
+ role="application"
onDrop={(event) => handleDrop(event)}
onDragOver={(event) => event.preventDefault()}
>
@@ -362,6 +354,10 @@ const RealTimeVisulization: React.FC = () => {
"RotateY",
"Delete",
]}
+ onClick={(e) => {
+ setRightSelect(e);
+ setEditWidgetOptions(false);
+ }}
/>
)}
diff --git a/app/src/modules/visualization/visualization.tsx b/app/src/modules/visualization/visualization.tsx
index e5b1692..77956f4 100644
--- a/app/src/modules/visualization/visualization.tsx
+++ b/app/src/modules/visualization/visualization.tsx
@@ -3,9 +3,8 @@ import Dropped3dWidgets from './widgets/3d/Dropped3dWidget'
import ZoneCentreTarget from './zone/zoneCameraTarget'
import ZoneAssets from './zone/zoneAssets'
import MqttEvents from '../../services/factoryBuilder/mqtt/mqttEvents'
-import DrieHtmlTemp from './mqttTemp/drieHtmlTemp'
-const Visualization = () => {
+const Visualization:React.FC = () => {
return (
<>
diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx
index 26d1d7d..0c28efa 100644
--- a/app/src/pages/Project.tsx
+++ b/app/src/pages/Project.tsx
@@ -60,11 +60,11 @@ const Project: React.FC = () => {
return (
-
*/}
{/* {loadingProgress &&
} */}
{!isPlaying && (
diff --git a/app/src/pages/UserAuth.tsx b/app/src/pages/UserAuth.tsx
index a5a7c57..19393f9 100644
--- a/app/src/pages/UserAuth.tsx
+++ b/app/src/pages/UserAuth.tsx
@@ -2,7 +2,11 @@ import React, { useState, FormEvent } from "react";
import { useNavigate } from "react-router-dom";
import { LogoIconLarge } from "../components/icons/Logo";
import { EyeIcon } from "../components/icons/ExportCommonIcons";
-import { useLoadingProgress, useOrganization, useUserName } from "../store/store";
+import {
+ useLoadingProgress,
+ useOrganization,
+ useUserName,
+} from "../store/store";
import { signInApi } from "../services/factoryBuilder/signInSignUp/signInApi";
import { signUpApi } from "../services/factoryBuilder/signInSignUp/signUpApi";
@@ -21,7 +25,7 @@ const UserAuth: React.FC = () => {
const handleLogin = async (e: FormEvent
) => {
e.preventDefault();
- const organization = (email.split("@")[1]).split(".")[0];
+ const organization = email.split("@")[1].split(".")[0];
try {
const res = await signInApi(email, password, organization);
@@ -39,7 +43,7 @@ const UserAuth: React.FC = () => {
} else if (res.message === "User Not Found!!! Kindly signup...") {
setError("Account not found");
}
- } catch (error) { }
+ } catch (error) {}
};
const handleRegister = async (e: FormEvent) => {
@@ -47,7 +51,7 @@ const UserAuth: React.FC = () => {
if (email && password && userName) {
setError("");
try {
- const organization = (email.split("@")[1]).split(".")[0];
+ const organization = email.split("@")[1].split(".")[0];
const res = await signUpApi(userName, email, password, organization);
if (res.message === "New User created") {
@@ -56,123 +60,121 @@ const UserAuth: React.FC = () => {
if (res.message === "User already exists") {
setError("User already exists");
}
- } catch (error) { }
+ } catch (error) {}
} else {
setError("Please fill all the fields!");
}
};
return (
- <>
-
-
-
-
-
Welcome to Dwinzo
-
- {isSignIn ? (
- <>
- Don’t have an account?{" "}
- setIsSignIn(false)}
- style={{ cursor: "pointer" }}
- >
- Register here!
-
- >
- ) : (
- <>
- Already have an account?{" "}
- setIsSignIn(true)}
- style={{ cursor: "pointer" }}
- >
- Login here!
-
- >
- )}
-
+
+
+
+
+
Welcome to Dwinzo
+
+ {isSignIn ? (
+ <>
+ Don’t have an account?{" "}
+ setIsSignIn(false)}
+ style={{ cursor: "pointer" }}
+ >
+ Register here!
+
+ >
+ ) : (
+ <>
+ Already have an account?{" "}
+ setIsSignIn(true)}
+ style={{ cursor: "pointer" }}
+ >
+ Login here!
+
+ >
+ )}
+
-
- G Continue with Google
-
+
+ G Continue with Google
+
- {error &&
🛈 {error}
}
+ {error &&
🛈 {error}
}
-
+ {!isSignIn && (
+
+
+
+ I have read and agree to the terms of service
+
+
+ )}
+
+ {isSignIn ? "Continue" : "Register"}
+
+
+
+ By signing up for, or logging into, an account, you agree to our{" "}
+ navigate("/privacy")}
+ style={{ cursor: "pointer" }}
+ >
+ privacy policy
+ {" "}
+ &{" "}
+ navigate("/terms")}
+ style={{ cursor: "pointer" }}
+ >
+ terms of service
+ {" "}
+ whether you read them or not. You can also find these terms on our
+ website.
+
+
);
};
diff --git a/app/src/services/simulation/UpsertProductOrEventApi.ts b/app/src/services/simulation/UpsertProductOrEventApi.ts
new file mode 100644
index 0000000..e2f45d1
--- /dev/null
+++ b/app/src/services/simulation/UpsertProductOrEventApi.ts
@@ -0,0 +1,26 @@
+let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
+
+export const upsertProductOrEventApi = async (body: any) => {
+ try {
+ const response = await fetch(`${url_Backend_dwinzo}/api/v2/UpsertProductOrEvent`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(body),
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to add product or event");
+ }
+
+ const result = await response.json();
+ return result;
+ } catch (error) {
+ if (error instanceof Error) {
+ throw new Error(error.message);
+ } else {
+ throw new Error("An unknown error occurred");
+ }
+ }
+};
diff --git a/app/src/services/simulation/deleteEventDataApi.ts b/app/src/services/simulation/deleteEventDataApi.ts
new file mode 100644
index 0000000..f263065
--- /dev/null
+++ b/app/src/services/simulation/deleteEventDataApi.ts
@@ -0,0 +1,26 @@
+let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
+
+export const deleteEventDataApi = async (body: any) => {
+ try {
+ const response = await fetch(`${url_Backend_dwinzo}/api/v2/EventDataDelete`, {
+ method: "PATCH",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(body),
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to delete event data");
+ }
+
+ const result = await response.json();
+ return result;
+ } catch (error) {
+ if (error instanceof Error) {
+ throw new Error(error.message);
+ } else {
+ throw new Error("An unknown error occurred");
+ }
+ }
+};
diff --git a/app/src/services/simulation/deleteProductDataApi.ts b/app/src/services/simulation/deleteProductDataApi.ts
new file mode 100644
index 0000000..06718f8
--- /dev/null
+++ b/app/src/services/simulation/deleteProductDataApi.ts
@@ -0,0 +1,25 @@
+let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
+
+export const deleteProductDataApi = async (productId: string, organization: string) => {
+ try {
+ const response = await fetch(`${url_Backend_dwinzo}/api/v2/productDataDelete?productId=${productId}&organization=${organization}`, {
+ method: "PATCH",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to delete product data");
+ }
+
+ const result = await response.json();
+ return result;
+ } catch (error) {
+ if (error instanceof Error) {
+ throw new Error(error.message);
+ } else {
+ throw new Error("An unknown error occurred");
+ }
+ }
+};
diff --git a/app/src/services/simulation/getProductApi.ts b/app/src/services/simulation/getProductApi.ts
new file mode 100644
index 0000000..cf80013
--- /dev/null
+++ b/app/src/services/simulation/getProductApi.ts
@@ -0,0 +1,25 @@
+let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
+
+export const getProductApi = async (productId: string, organization: string) => {
+ try {
+ const response = await fetch(`${url_Backend_dwinzo}/api/v2/productDatas?productId=${productId}&organization=${organization}`, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to fetch product data");
+ }
+
+ const result = await response.json();
+ return result;
+ } catch (error) {
+ if (error instanceof Error) {
+ throw new Error(error.message);
+ } else {
+ throw new Error("An unknown error occurred");
+ }
+ }
+};
\ No newline at end of file
diff --git a/app/src/services/simulation/getallProductsApi.ts b/app/src/services/simulation/getallProductsApi.ts
new file mode 100644
index 0000000..46627f9
--- /dev/null
+++ b/app/src/services/simulation/getallProductsApi.ts
@@ -0,0 +1,25 @@
+let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
+
+export const getAllProductsApi = async ( organization: string) => {
+ try {
+ const response = await fetch(`${url_Backend_dwinzo}/api/v2/AllProducts/${organization}`, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to fetch all products data");
+ }
+
+ const result = await response.json();
+ return result;
+ } catch (error) {
+ if (error instanceof Error) {
+ throw new Error(error.message);
+ } else {
+ throw new Error("An unknown error occurred");
+ }
+ }
+};
\ No newline at end of file
diff --git a/app/src/services/simulation/temp.md b/app/src/services/simulation/temp.md
deleted file mode 100644
index e69de29..0000000
diff --git a/app/src/services/visulization/zone/getSelect2dZoneData.ts b/app/src/services/visulization/zone/getSelect2dZoneData.ts
index b2c39e9..00d4dfe 100644
--- a/app/src/services/visulization/zone/getSelect2dZoneData.ts
+++ b/app/src/services/visulization/zone/getSelect2dZoneData.ts
@@ -7,7 +7,7 @@ export const getSelect2dZoneData = async (
) => {
try {
const response = await fetch(
- `${url_Backend_dwinzo}/api/v2/Zone/visualization/${ZoneId}?organization=${organization}`,
+ `${url_Backend_dwinzo}/api/v2/ZoneVisualization/${ZoneId}?organization=${organization}`,
{
method: "GET",
headers: {
diff --git a/app/src/store/simulation/useArmBotStore.ts b/app/src/store/simulation/useArmBotStore.ts
index 493a068..642762f 100644
--- a/app/src/store/simulation/useArmBotStore.ts
+++ b/app/src/store/simulation/useArmBotStore.ts
@@ -17,8 +17,11 @@ interface ArmBotStore {
addAction: (modelUuid: string, action: RoboticArmPointSchema['actions'][number]) => void;
removeAction: (modelUuid: string, actionUuid: string) => void;
- setArmBotActive: (modelUuid: string, isActive: boolean) => void;
+ updateStartPoint: (modelUuid: string, actionUuid: string, startPoint: [number, number, number] | null) => void;
+ updateEndPoint: (modelUuid: string, actionUuid: string, endPoint: [number, number, number] | null) => void;
+ setArmBotActive: (modelUuid: string, isActive: boolean) => void;
+ setArmBotState: (modelUuid: string, newState: ArmBotStatus['state']) => void;
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
@@ -72,7 +75,6 @@ export const useArmBotStore = create()(
actionUuid: action.actionUuid,
actionName: action.actionName,
};
- armBot.isActive = true;
}
}
});
@@ -83,7 +85,6 @@ export const useArmBotStore = create()(
const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
if (armBot) {
armBot.currentAction = undefined;
- armBot.isActive = false;
}
});
},
@@ -106,6 +107,30 @@ export const useArmBotStore = create()(
});
},
+ updateStartPoint: (modelUuid, actionUuid, startPoint) => {
+ set((state) => {
+ const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
+ if (armBot) {
+ const action = armBot.point.actions.find(a => a.actionUuid === actionUuid);
+ if (action) {
+ action.process.startPoint = startPoint;
+ }
+ }
+ });
+ },
+
+ updateEndPoint: (modelUuid, actionUuid, endPoint) => {
+ set((state) => {
+ const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
+ if (armBot) {
+ const action = armBot.point.actions.find(a => a.actionUuid === actionUuid);
+ if (action) {
+ action.process.endPoint = endPoint;
+ }
+ }
+ });
+ },
+
setArmBotActive: (modelUuid, isActive) => {
set((state) => {
const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
@@ -115,6 +140,15 @@ export const useArmBotStore = create()(
});
},
+ setArmBotState: (modelUuid, newState) => {
+ set((state) => {
+ const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
+ if (armBot) {
+ armBot.state = newState;
+ }
+ });
+ },
+
incrementActiveTime: (modelUuid, incrementBy) => {
set((state) => {
const armBot = state.armBots.find(a => a.modelUuid === modelUuid);
diff --git a/app/src/store/simulation/useMaterialStore.ts b/app/src/store/simulation/useMaterialStore.ts
new file mode 100644
index 0000000..56a35a7
--- /dev/null
+++ b/app/src/store/simulation/useMaterialStore.ts
@@ -0,0 +1,76 @@
+import { create } from 'zustand';
+import { immer } from 'zustand/middleware/immer';
+
+type MaterialsStore = {
+ materials: MaterialsSchema;
+
+ addMaterial: (material: MaterialSchema) => void;
+ removeMaterial: (materialId: string) => void;
+ updateMaterial: (materialId: string, updates: Partial) => void;
+
+ setStartTime: (materialId: string, startTime: string) => void;
+ setEndTime: (materialId: string, endTime: string) => void;
+ setCost: (materialId: string, cost: number) => void;
+ setWeight: (materialId: string, weight: number) => void;
+
+ getMaterialById: (materialId: string) => MaterialSchema | undefined;
+};
+
+export const useMaterialStore = create()(
+ immer((set, get) => ({
+ materials: [],
+
+ addMaterial: (material) => {
+ set((state) => {
+ state.materials.push(material);
+ });
+ },
+
+ removeMaterial: (materialId) => {
+ set((state) => {
+ state.materials = state.materials.filter(m => m.materialId !== materialId);
+ });
+ },
+
+ updateMaterial: (materialId, updates) => {
+ set((state) => {
+ const material = state.materials.find(m => m.materialId === materialId);
+ if (material) {
+ Object.assign(material, updates);
+ }
+ });
+ },
+
+ setStartTime: (materialId, startTime) => {
+ set((state) => {
+ const material = state.materials.find(m => m.materialId === materialId);
+ if (material) material.startTime = startTime;
+ });
+ },
+
+ setEndTime: (materialId, endTime) => {
+ set((state) => {
+ const material = state.materials.find(m => m.materialId === materialId);
+ if (material) material.endTime = endTime;
+ });
+ },
+
+ setCost: (materialId, cost) => {
+ set((state) => {
+ const material = state.materials.find(m => m.materialId === materialId);
+ if (material) material.cost = cost;
+ });
+ },
+
+ setWeight: (materialId, weight) => {
+ set((state) => {
+ const material = state.materials.find(m => m.materialId === materialId);
+ if (material) material.weight = weight;
+ });
+ },
+
+ getMaterialById: (materialId) => {
+ return get().materials.find(m => m.materialId === materialId);
+ },
+ }))
+);
diff --git a/app/src/store/simulation/useProductStore.ts b/app/src/store/simulation/useProductStore.ts
index 41a13f6..8ec74cf 100644
--- a/app/src/store/simulation/useProductStore.ts
+++ b/app/src/store/simulation/useProductStore.ts
@@ -7,7 +7,7 @@ type ProductsStore = {
// Product-level actions
addProduct: (productName: string, productId: string) => void;
removeProduct: (productId: string) => void;
- updateProduct: (productId: string, updates: Partial<{ productName: string; eventsData: EventsSchema[] }>) => void;
+ updateProduct: (productId: string, updates: Partial<{ productName: string; eventDatas: EventsSchema[] }>) => void;
// Event-level actions
addEvent: (productId: string, event: EventsSchema) => void;
@@ -48,8 +48,18 @@ type ProductsStore = {
updates: Partial
) => void;
+ // Renaming functions
+ renameProduct: (productId: string, newName: string) => void;
+ renameAction: (actionUuid: string, newName: string) => void;
+ renameTrigger: (triggerUuid: string, newName: string) => void;
+
// Helper functions
- getProductById: (productId: string) => { productName: string; productId: string; eventsData: EventsSchema[] } | undefined;
+ getProductById: (productId: string) => { productName: string; productId: string; eventDatas: EventsSchema[] } | undefined;
+ getEventByModelUuid: (productId: string, modelUuid: string) => EventsSchema | undefined;
+ getPointByUuid: (productId: string, modelUuid: string, pointUuid: string) => ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema | undefined;
+ getActionByUuid: (productId: string, actionUuid: string) => (ConveyorPointSchema['action'] | VehiclePointSchema['action'] | RoboticArmPointSchema['actions'][0] | MachinePointSchema['action'] | StoragePointSchema['action']) | undefined;
+ getTriggerByUuid: (productId: string, triggerUuid: string) => TriggerSchema | undefined;
+ getIsEventInProduct: (productId: string, modelUuid: string) => boolean;
};
export const useProductStore = create()(
@@ -62,7 +72,7 @@ export const useProductStore = create()(
const newProduct = {
productName,
productId: productId,
- eventsData: []
+ eventDatas: []
};
state.products.push(newProduct);
});
@@ -88,7 +98,7 @@ export const useProductStore = create()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
- product.eventsData.push(event);
+ product.eventDatas.push(event);
}
});
},
@@ -97,7 +107,7 @@ export const useProductStore = create()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
- product.eventsData = product.eventsData.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid);
+ product.eventDatas = product.eventDatas.filter(e => 'modelUuid' in e && e.modelUuid !== modelUuid);
}
});
},
@@ -106,7 +116,7 @@ export const useProductStore = create()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
- const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
+ const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event) {
Object.assign(event, updates);
}
@@ -119,7 +129,7 @@ export const useProductStore = create()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
- const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
+ const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event && 'points' in event) {
(event as ConveyorEventSchema).points.push(point as ConveyorPointSchema);
} else if (event && 'point' in event) {
@@ -133,7 +143,7 @@ export const useProductStore = create()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
- const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
+ const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event && 'points' in event) {
(event as ConveyorEventSchema).points = (event as ConveyorEventSchema).points.filter(p => p.uuid !== pointUuid);
} else if (event && 'point' in event && (event as any).point.uuid === pointUuid) {
@@ -147,7 +157,7 @@ export const useProductStore = create()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
- const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
+ const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event && 'points' in event) {
const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
if (point) {
@@ -165,7 +175,7 @@ export const useProductStore = create()(
set((state) => {
const product = state.products.find(p => p.productId === productId);
if (product) {
- const event = product.eventsData.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
+ const event = product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
if (event && 'points' in event) {
const point = (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
if (point) {
@@ -185,7 +195,7 @@ export const useProductStore = create()(
removeAction: (actionUuid: string) => {
set((state) => {
for (const product of state.products) {
- for (const event of product.eventsData) {
+ for (const event of product.eventDatas) {
if ('points' in event) {
// Handle ConveyorEventSchema
for (const point of (event as ConveyorEventSchema).points) {
@@ -209,7 +219,7 @@ export const useProductStore = create()(
updateAction: (actionUuid, updates) => {
set((state) => {
for (const product of state.products) {
- for (const event of product.eventsData) {
+ for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && point.action.actionUuid === actionUuid) {
@@ -239,7 +249,7 @@ export const useProductStore = create()(
addTrigger: (actionUuid, trigger) => {
set((state) => {
for (const product of state.products) {
- for (const event of product.eventsData) {
+ for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && point.action.actionUuid === actionUuid) {
@@ -268,7 +278,7 @@ export const useProductStore = create()(
removeTrigger: (triggerUuid) => {
set((state) => {
for (const product of state.products) {
- for (const event of product.eventsData) {
+ for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && 'triggers' in point.action) {
@@ -295,7 +305,7 @@ export const useProductStore = create()(
updateTrigger: (triggerUuid, updates) => {
set((state) => {
for (const product of state.products) {
- for (const event of product.eventsData) {
+ for (const event of product.eventDatas) {
if ('points' in event) {
for (const point of (event as ConveyorEventSchema).points) {
if (point.action && 'triggers' in point.action) {
@@ -331,9 +341,170 @@ export const useProductStore = create()(
});
},
+ // Renaming functions
+ renameProduct: (productId, newName) => {
+ set((state) => {
+ const product = state.products.find(p => p.productId === productId);
+ if (product) {
+ product.productName = newName;
+ }
+ });
+ },
+
+ renameAction: (actionUuid, newName) => {
+ set((state) => {
+ for (const product of state.products) {
+ for (const event of product.eventDatas) {
+ if ('points' in event) {
+ for (const point of (event as ConveyorEventSchema).points) {
+ if (point.action && point.action.actionUuid === actionUuid) {
+ point.action.actionName = newName;
+ return;
+ }
+ }
+ } else if ('point' in event) {
+ const point = (event as any).point;
+ if ('action' in point && point.action.actionUuid === actionUuid) {
+ point.action.actionName = newName;
+ return;
+ } else if ('actions' in point) {
+ const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
+ if (action) {
+ action.actionName = newName;
+ return;
+ }
+ }
+ }
+ }
+ }
+ });
+ },
+
+ renameTrigger: (triggerUuid, newName) => {
+ set((state) => {
+ for (const product of state.products) {
+ for (const event of product.eventDatas) {
+ if ('points' in event) {
+ for (const point of (event as ConveyorEventSchema).points) {
+ if (point.action && 'triggers' in point.action) {
+ const trigger = point.action.triggers.find(t => t.triggerUuid === triggerUuid);
+ if (trigger) {
+ trigger.triggerName = newName;
+ return;
+ }
+ }
+ }
+ } else if ('point' in event) {
+ const point = (event as any).point;
+ if ('action' in point && 'triggers' in point.action) {
+ const trigger = point.action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
+ if (trigger) {
+ trigger.triggerName = newName;
+ return;
+ }
+ } else if ('actions' in point) {
+ for (const action of point.actions) {
+ if ('triggers' in action) {
+ const trigger = action.triggers.find((t: any) => t.triggerUuid === triggerUuid);
+ if (trigger) {
+ trigger.triggerName = newName;
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+ },
+
// Helper functions
getProductById: (productId) => {
return get().products.find(p => p.productId === productId);
+ },
+
+ getEventByModelUuid: (productId, modelUuid) => {
+ const product = get().getProductById(productId);
+ if (!product) return undefined;
+ return product.eventDatas.find(e => 'modelUuid' in e && e.modelUuid === modelUuid);
+ },
+
+ getPointByUuid: (productId, modelUuid, pointUuid) => {
+ const event = get().getEventByModelUuid(productId, modelUuid);
+ if (!event) return undefined;
+
+ if ('points' in event) {
+ return (event as ConveyorEventSchema).points.find(p => p.uuid === pointUuid);
+ } else if ('point' in event && (event as any).point.uuid === pointUuid) {
+ return (event as VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema).point;
+ }
+ return undefined;
+ },
+
+ getActionByUuid: (productId, actionUuid) => {
+ const product = get().products.find(p => p.productId === productId);
+ if (!product) return undefined;
+
+ for (const event of product.eventDatas) {
+ if ('points' in event) {
+ for (const point of (event as ConveyorEventSchema).points) {
+ if (point.action?.actionUuid === actionUuid) {
+ return point.action;
+ }
+ }
+ } else if ('point' in event) {
+ const point = (event as any).point;
+ if ('action' in point && point.action?.actionUuid === actionUuid) {
+ return point.action;
+ } else if ('actions' in point) {
+ const action = point.actions.find((a: any) => a.actionUuid === actionUuid);
+ if (action) return action;
+ }
+ }
+ }
+ return undefined;
+ },
+
+ getTriggerByUuid: (productId, triggerUuid) => {
+ const product = get().products.find(p => p.productId === productId);
+ if (!product) return undefined;
+
+ for (const event of product.eventDatas) {
+ if ('points' in event) {
+ for (const point of (event as ConveyorEventSchema).points) {
+ for (const trigger of point.action?.triggers || []) {
+ if (trigger.triggerUuid === triggerUuid) {
+ return trigger;
+ }
+ }
+ }
+ } else if ('point' in event) {
+ const point = (event as any).point;
+ if ('action' in point) {
+ for (const trigger of point.action?.triggers || []) {
+ if (trigger.triggerUuid === triggerUuid) {
+ return trigger;
+ }
+ }
+ } else if ('actions' in point) {
+ for (const action of point.actions) {
+ for (const trigger of action.triggers || []) {
+ if (trigger.triggerUuid === triggerUuid) {
+ return trigger;
+ }
+ }
+ }
+ }
+ }
+ }
+ return undefined;
+ },
+
+ getIsEventInProduct: (productId, modelUuid) => {
+ const product = get().getProductById(productId);
+ if (!product) return false;
+ return product.eventDatas.some(e => 'modelUuid' in e && e.modelUuid === modelUuid);
}
}))
);
diff --git a/app/src/store/simulation/useSimulationStore.ts b/app/src/store/simulation/useSimulationStore.ts
new file mode 100644
index 0000000..5085688
--- /dev/null
+++ b/app/src/store/simulation/useSimulationStore.ts
@@ -0,0 +1,117 @@
+import { create } from 'zustand';
+import { immer } from 'zustand/middleware/immer';
+import * as THREE from 'three';
+
+interface SelectedEventSphereState {
+ selectedEventSphere: THREE.Mesh | null;
+ setSelectedEventSphere: (mesh: THREE.Mesh | null) => void;
+ clearSelectedEventSphere: () => void;
+}
+
+export const useSelectedEventSphere = create()(
+ immer((set) => ({
+ selectedEventSphere: null,
+ setSelectedEventSphere: (mesh) => {
+ set((state) => {
+ state.selectedEventSphere = mesh;
+ });
+ },
+ clearSelectedEventSphere: () => {
+ set((state) => {
+ state.selectedEventSphere = null;
+ });
+ },
+ }))
+);
+
+interface SelectedEventDataState {
+ selectedEventData: { data: EventsSchema; selectedPoint: string } | undefined;
+ setSelectedEventData: (data: EventsSchema, selectedPoint: string) => void;
+ clearSelectedEventData: () => void;
+}
+
+export const useSelectedEventData = create()(
+ immer((set) => ({
+ selectedEventData: undefined,
+ setSelectedEventData: (data, selectedPoint) => {
+ set((state) => {
+ state.selectedEventData = { data, selectedPoint };
+ });
+ },
+ clearSelectedEventData: () => {
+ set((state) => {
+ state.selectedEventData = undefined;
+ });
+ },
+ }))
+);
+
+interface SelectedAssetState {
+ selectedAsset: EventsSchema | undefined;
+ setSelectedAsset: (EventData: EventsSchema) => void;
+ clearSelectedAsset: () => void;
+}
+
+export const useSelectedAsset = create()(
+ immer((set) => ({
+ selectedAsset: undefined,
+ setSelectedAsset: (EventData) => {
+ set((state) => {
+ state.selectedAsset = EventData;
+ });
+ },
+ clearSelectedAsset: () => {
+ set((state) => {
+ state.selectedAsset = undefined;
+ });
+ },
+ }))
+);
+
+interface SelectedProductState {
+ selectedProduct: { productId: string; productName: string };
+ setSelectedProduct: (productId: string, productName: string) => void;
+ clearSelectedProduct: () => void;
+}
+
+export const useSelectedProduct = create()(
+ immer((set) => ({
+ selectedProduct: { productId: '', productName: '' },
+ setSelectedProduct: (productId, productName) => {
+ set((state) => {
+ state.selectedProduct.productId = productId;
+ state.selectedProduct.productName = productName;
+ });
+ },
+ clearSelectedProduct: () => {
+ set((state) => {
+ state.selectedProduct.productId = '';
+ state.selectedProduct.productName = '';
+ });
+ },
+ }))
+);
+
+interface SelectedActionState {
+ selectedAction: { actionId: string; actionName: string };
+ setSelectedAction: (actionId: string, actionName: string) => void;
+ clearSelectedAction: () => void;
+}
+
+export const useSelectedAction = create()(
+ immer((set) => ({
+ selectedAction: { actionId: '', actionName: '' },
+ setSelectedAction: (actionId, actionName) => {
+ set((state) => {
+ state.selectedAction.actionId = actionId;
+ state.selectedAction.actionName = actionName;
+ });
+ },
+ clearSelectedAction: () => {
+ set((state) => {
+ state.selectedAction.actionId = '';
+ state.selectedAction.actionName = '';
+ });
+ },
+ }))
+);
\ No newline at end of file
diff --git a/app/src/store/simulation/useVehicleStore.ts b/app/src/store/simulation/useVehicleStore.ts
index ce28916..449ceb7 100644
--- a/app/src/store/simulation/useVehicleStore.ts
+++ b/app/src/store/simulation/useVehicleStore.ts
@@ -1,3 +1,4 @@
+
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
@@ -88,7 +89,7 @@ export const useVehicleStore = create()(
set((state) => {
const vehicle = state.vehicles.find(v => v.modelUuid === modelUuid);
if (vehicle) {
- vehicle.currentLoad = decrementBy;
+ vehicle.currentLoad -= decrementBy;
}
});
},
diff --git a/app/src/store/useModuleStore.ts b/app/src/store/useModuleStore.ts
index 1012792..3cc1d00 100644
--- a/app/src/store/useModuleStore.ts
+++ b/app/src/store/useModuleStore.ts
@@ -13,14 +13,17 @@ const useModuleStore = create((set) => ({
export default useModuleStore;
// New store for subModule
+
+type SubModule = 'properties' | 'simulations' | 'mechanics' | 'analysis' | 'zoneProperties';
+
interface SubModuleStore {
- subModule: string;
- setSubModule: (subModule: string) => void;
+ subModule: SubModule;
+ setSubModule: (subModule: SubModule) => void;
}
const useSubModuleStore = create((set) => ({
subModule: "properties", // Initial subModule state
- setSubModule: (subModule) => set({ subModule }), // Update subModule state
+ setSubModule: (value) => set({ subModule: value }), // Update subModule state
}));
export { useSubModuleStore };
diff --git a/app/src/styles/abstracts/placeholders.scss b/app/src/styles/abstracts/placeholders.scss
deleted file mode 100644
index 18f28f9..0000000
--- a/app/src/styles/abstracts/placeholders.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-// center a element
-%centered {
- display: flex;
- justify-content: center;
- align-items: center;
-}
diff --git a/app/src/styles/abstracts/variables.scss b/app/src/styles/abstracts/variables.scss
index 44e5627..6bb3d57 100644
--- a/app/src/styles/abstracts/variables.scss
+++ b/app/src/styles/abstracts/variables.scss
@@ -1,123 +1,132 @@
-/* ========================================================================
- Global SCSS Variables
- ========================================================================
- This file contains the global variables used across the project for
- colors, typography, spacing, shadows, and other design tokens.
- ======================================================================== */
-
@use "functions";
-// ========================================================================
-// Font Imports
-// ========================================================================
@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=Josefin+Sans:ital,wght@0,100..700;1,100..700&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap");
-// ========================================================================
-// Colors
-// ========================================================================
+// new variables
-// Text colors
-$text-color: #2b3344; // Primary text color
-$text-disabled: #b7b7c6; // Disabled text color
-$input-text-color: #595965; // Input field text color
+// text colors
+// ---------- light mode ----------
+$text-color: #2b3344;
+$text-disabled: #b7b7c6;
+$input-text-color: #595965;
+$highlight-text-color: #6f42c1;
-$text-color-dark: #f3f3fd; // Primary text color for dark mode
-$text-disabled-dark: #6f6f7a; // Disabled text color for dark mode
-$input-text-color-dark: #b5b5c8; // Input field text color for dark mode
+// ---------- dark mode ----------
+$text-color-dark: #f3f3fd;
+$text-disabled-dark: #6f6f7a;
+$input-text-color-dark: #b5b5c8;
+$highlight-text-color-dark: #B392F0;
-// Accent colors
-$accent-color: #6f42c1; // Primary accent color
-$accent-color-dark: #c4abf1; // Primary accent color for dark mode
-$highlight-accent-color: #e0dfff; // Highlighted accent for light mode
-$highlight-accent-color-dark: #403e6a; // Highlighted accent for dark mode
+// background colors
+// ---------- light mode ----------
+$background-color: linear-gradient(-45deg, #FCFDFDCC 0%, #FCFDFD99 100%);
+$background-color-secondary: #FCFDFD4D;
+$background-color-accent: #6f42c1;
+$background-color-button: #6f42c1;
+$background-color-drop-down: #6F42C14D;
+$background-color-input: #FFFFFF4D;
+$background-color-input-focus: #F2F2F7;
+$background-color-drop-down-gradient: linear-gradient(-45deg, #75649366 0%, #40257266 100%);
+$background-color-selected: #E0DFFF;
+$background-radial-gray-gradient: radial-gradient(circle, #bfe0f8 0%, #e9ebff 46%, #e2acff 100%);
-// Background colors
-$background-color: #fcfdfd; // Main background color
-$background-color-dark: #19191d; // Main background color for dark mode
-$background-color-secondary: #e1e0ff80; // Secondary background color
-$background-color-secondary-dark: #39394f99; // Secondary background color for dark mode
-$background-color-gray: #f3f3f3; // Main background color
-$background-color-gray-dark: #232323; // Main background color for dark mode
+// ---------- dark mode ----------
+$background-color-dark: linear-gradient(-45deg, #333333B3 0%, #2D2437B3 100%);
+$background-color-secondary-dark: #19191D99;
+$background-color-accent-dark: #6f42c1;
+$background-color-button-dark: #6f42c1;
+$background-color-drop-down-dark: #50505080;
+$background-color-input-dark: #FFFFFF33;
+$background-color-input-focus-dark: #333333;
+$background-color-drop-down-gradient-dark: linear-gradient(-45deg, #8973B166 0%, #53427366 100%);
+$background-color-selected-dark: #403E66;
+$background-radial-gray-gradient-dark: radial-gradient(circle, #31373b 0%, #48494b 46%, #52415c 100%);
-// Border colors
-$border-color: #e0dfff; // Default border color
-$border-color-dark: #403e6a; // Border color for dark mode
+// border colors
+// ---------- light mode ----------
+$border-color: #E0DFFF;
+$border-color-accent: #6F42C1;
-// Shadow color
-$shadow-color: #3c3c431a; // Shadow base color for light and dark mode
-$shadow-color-dark: #8f8f8f1a; // Shadow base color for light and dark mode
+// ---------- dark mode ----------
+$border-color-dark: #564B69;
+$border-color-accent-dark: #6F42C1;
-// Gradients
-$acent-gradient-dark: linear-gradient(
- 90deg,
- #b392f0 0%,
- #a676ff 100%
-); // Dark mode accent gradient
-$acent-gradient: linear-gradient(
- 90deg,
- #6f42c1 0%,
- #925df3 100%
-); // Light mode accent gradient
+// highlight colors
+// ---------- light mode ----------
+$highlight-accent-color: #E0DFFF;
+$highlight-secondary-color: #6F42C1;
+
+// ---------- dark mode ----------
+$highlight-accent-color-dark: #403E6A;
+$highlight-secondary-color-dark: #C4ABF1;
+
+// colors
+$color1: #A392CD;
+$color2: #7b4cd3;
+$color3: #B186FF;
+$color4: #8752E8;
+$color5: #C7A8FF;
+
+
+// old variables
+$accent-color: #6f42c1;
+$accent-color-dark: #c4abf1;
+$highlight-accent-color: #e0dfff;
+$highlight-accent-color-dark: #403e6a;
+
+$background-color: #fcfdfd;
+$background-color-dark: #19191d;
+$background-color-secondary: #e1e0ff80;
+$background-color-secondary-dark: #39394f99;
+$background-color-gray: #f3f3f3;
+$background-color-gray-dark: #232323;
+
+$border-color: #e0dfff;
+$border-color-dark: #403e6a;
+
+$shadow-color: #3c3c431a;
+$shadow-color-dark: #8f8f8f1a;
+
+$acent-gradient-dark: linear-gradient(90deg, #b392f0 0%, #a676ff 100%);
+$acent-gradient: linear-gradient(90deg, #6f42c1 0%, #925df3 100%);
$faint-gradient: radial-gradient(circle, #bfe0f8 0%, #e9ebff 46%, #e2acff 100%);
$faint-gradient-dark: radial-gradient(circle, #31373b 0%, #48494b 46%, #52415c 100%);
-// ========================================================================
-// Typography
-// ========================================================================
+$font-inter: "Inter", sans-serif;
+$font-josefin-sans: "Josefin Sans", sans-serif;
+$font-poppins: "Poppins", sans-serif;
+$font-roboto: "Roboto", sans-serif;
-// Font Family Variables
-$font-inter: "Inter", sans-serif; // Inter font
-$font-josefin-sans: "Josefin Sans", sans-serif; // Josefin Sans font
-$font-poppins: "Poppins", sans-serif; // Poppins font
-$font-roboto: "Roboto", sans-serif; // Roboto font
+$tiny: 0.625rem;
+$small: 0.75rem;
+$regular: 0.8rem;
+$large: 1rem;
+$xlarge: 1.125rem;
+$xxlarge: 1.5rem;
+$xxxlarge: 2rem;
-// Font sizes (converted to rem using a utility function)
-$tiny: 0.625rem; // Extra small text (10px)
-$small: 0.75rem; // Small text (12px)
-$regular: 0.8rem; // Default text size (14px)
-$large: 1rem; // Large text size (16px)
-$xlarge: 1.125rem; // Extra large text size (18px)
-$xxlarge: 1.5rem; // Double extra large text size (24px)
-$xxxlarge: 2rem; // Triple extra large text size (32px)
+$thin-weight: 300;
+$regular-weight: 400;
+$medium-weight: 500;
+$bold-weight: 600;
-// Font weights
-$thin-weight: 300; // Regular font weight
-$regular-weight: 400; // Regular font weight
-$medium-weight: 500; // Medium font weight
-$bold-weight: 600; // Bold font weight
+$z-index-drei-html: 1;
+$z-index-default: 1;
+$z-index-marketplace: 2;
+$z-index-tools: 3;
+$z-index-negative: -1;
+$z-index-ui-base: 10;
+$z-index-ui-overlay: 20;
+$z-index-ui-popup: 30;
+$z-index-ui-highest: 50;
-// ========================================================================
-// Z-Index Levels
-// ========================================================================
+$box-shadow-light: 0px 2px 4px $shadow-color;
+$box-shadow-medium: 0px 4px 8px $shadow-color;
+$box-shadow-heavy: 0px 8px 16px $shadow-color;
-// Z-index variables for layering
-$z-index-drei-html: 1; // For drei's Html components
-$z-index-default: 1; // For drei's Html components
-$z-index-marketplace: 2; // For drei's Html components
-$z-index-tools: 3; // For drei's Html components
-$z-index-negative: -1; // For drei's Html components
-$z-index-ui-base: 10; // Base UI elements
-$z-index-ui-overlay: 20; // Overlay UI elements (e.g., modals, tooltips)
-$z-index-ui-popup: 30; // Popups, dialogs, or higher-priority UI elements
-$z-index-ui-highest: 50; // Highest priority elements (e.g., notifications, loading screens)
-
-// ========================================================================
-// Shadows
-// ========================================================================
-
-// Box shadow variables
-$box-shadow-light: 0px 2px 4px $shadow-color; // Light shadow
-$box-shadow-medium: 0px 4px 8px $shadow-color; // Medium shadow
-$box-shadow-heavy: 0px 8px 16px $shadow-color; // Heavy shadow
-
-// ========================================================================
-// Border Radius
-// ========================================================================
-
-// Border radius variables
-$border-radius-small: 4px; // Small rounded corners
-$border-radius-medium: 6px; // Medium rounded corners
-$border-radius-large: 12px; // Large rounded corners
-$border-radius-circle: 50%; // Fully circular
-$border-radius-extra-large: 20px; // Extra-large rounded corners
+$border-radius-small: 4px;
+$border-radius-medium: 6px;
+$border-radius-large: 12px;
+$border-radius-circle: 50%;
+$border-radius-extra-large: 20px;
diff --git a/app/src/styles/base/global.scss b/app/src/styles/base/global.scss
new file mode 100644
index 0000000..3ba017e
--- /dev/null
+++ b/app/src/styles/base/global.scss
@@ -0,0 +1,5 @@
+section, .section{
+ padding: 12px;
+ outline: 1px solid var(--border-color);
+ border-radius: 16px;
+}
diff --git a/app/src/styles/base/reset.scss b/app/src/styles/base/reset.scss
index 82d286e..ab77f9a 100644
--- a/app/src/styles/base/reset.scss
+++ b/app/src/styles/base/reset.scss
@@ -12,3 +12,10 @@ input[type="password"]::-webkit-clear-button, /* For Chrome/Safari clear button
input[type="password"]::-webkit-inner-spin-button { /* Just in case */
display: none;
}
+
+button{
+ border: none;
+ outline: none;
+ background: none;
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/app/src/styles/components/analysis/ROISummary.scss b/app/src/styles/components/analysis/ROISummary.scss
index f4ee414..c1ba7d5 100644
--- a/app/src/styles/components/analysis/ROISummary.scss
+++ b/app/src/styles/components/analysis/ROISummary.scss
@@ -1,7 +1,6 @@
.roiSummary-container {
-
.roiSummary-wrapper {
- background-color: #F2F2F7;
+ background-color: var(--background-color);
.product-info {
display: flex;
@@ -22,7 +21,7 @@
}
&:last-child {
- color: #2B3344;
+ color: var(--text-color);
}
}
}
@@ -30,9 +29,30 @@
.roi-details {
display: flex;
+ align-items: center;
+ gap: 12px;
+
+ .progress-wrapper {
+ width: 250px;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+
+ .content {
+ display: flex;
+ flex-direction: column;
+ gap: 3px;
+ align-items: center;
+
+ .key {
+ font-size: var(--font-size-xlarge);
+ color: var(--accent-color);
+ }
+ }
+ }
.roi-progress {
- width: 60%;
+ width: 100%;
}
.metrics {
@@ -43,8 +63,8 @@
.metric-item {
width: 100%;
border-radius: 6px;
- border: 1px solid var(--axis-colors-green, #43C06D);
- background: var(--axis-colors-green-lite, #BEEECF);
+ border: 1px solid #00FF56;
+ background: #436D51;
display: flex;
flex-direction: column;
padding: 4px 6px;
@@ -58,7 +78,6 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
-
}
.metric-value {
@@ -74,13 +93,11 @@
.metric-item {
background-color: var(--background-color);
border: 1px solid var(--Grays-Gray-6, #F2F2F7);
-
}
}
}
}
-
.cost-breakdown {
background-color: var(--background-color);
border: 1px solid var(--text-disabled);
@@ -90,9 +107,16 @@
.breakdown-header {
display: flex;
align-items: center;
+ justify-content: space-between;
gap: 8px;
margin-bottom: 16px;
+ .section-wrapper {
+ display: flex;
+ gap: 4px;
+ align-items: center;
+ }
+
.section-number {
font-size: 20px;
color: #00aaff;
@@ -100,18 +124,23 @@
.section-title {
font-size: var(--font-size-regular);
- color: #333;
+ color: var(--text-color);
}
.expand-icon {
font-size: 16px;
- color: #666;
+ color: var(--text-color);
cursor: pointer;
+ transform: rotate(90deg);
+ transition: transform 0.2s linear;
+ }
+
+ .expand-icon.open {
+ transform: rotate(0deg);
+
}
}
-
-
.breakdown-table {
width: 100%;
border-collapse: collapse;
@@ -125,28 +154,18 @@
border-bottom: 1px solid var(--text-disabled);
}
- /* Apply left border to first child */
- th:first-child {
- border-left: 1px solid var(--text-disabled);
- }
-
- /* Apply right border to last child */
- th:last-child {
- border-right: 1px solid var(--text-disabled);
- }
-
+ th:first-child,
td:first-child {
border-left: 1px solid var(--text-disabled);
}
- /* Apply right border to last child */
+ th:last-child,
td:last-child {
border-right: 1px solid var(--text-disabled);
}
th {
background-color: var(--background-color);
-
color: #333;
}
@@ -156,12 +175,8 @@
color: #333;
}
}
-
-
-
}
-
.tips-section {
background-color: var(--background-color);
border-radius: 8px;
@@ -182,17 +197,15 @@
.tip-description {
span {
+ font-size: var(--font-size-xlarge);
color: #34C759;
- /* Default color for the first span */
&:first-child {
color: #34C759;
- /* Color for the first span */
}
&:nth-child(2) {
color: #488EF6;
- /* Color for the second span */
}
}
}
@@ -200,13 +213,10 @@
.get-tips-button {
border: none;
-
border-radius: 5px;
cursor: pointer;
font-size: 14px;
margin-top: 8px;
-
- /* Make the button content-width dependent */
display: inline-block;
display: flex;
justify-content: flex-end;
@@ -216,54 +226,93 @@
background-color: var(--accent-color);
color: var(--background-color);
padding: 4px 6px;
- /* Add padding to ensure it has space around the text */
border-radius: 5px;
display: inline-block;
- /* Ensure button width adjusts to its content */
font-size: 14px;
text-align: center;
- /* Ensure text is centered */
}
}
+ }
+ .semi-circle-wrapper {
+ width: 100%;
+ height: 125px;
+ overflow-y: hidden;
+ position: relative;
+ }
+ .semi-circle {
+ width: 100%;
+ height: 250px;
+ border-radius: 50%;
+ position: relative;
+ transition: background 0.5s ease;
+ }
+ .progress-cover {
+ position: absolute;
+ width: 75%;
+ height: 75%;
+ top: 12.5%;
+ left: 12.5%;
+ background-color: var(--background-color);
+ border-radius: 50%;
+ }
+
+ .label-wrapper {
+ .label {
+ font-size: var(--font-size-xxxlarge);
+ }
+
+ position: absolute;
+ bottom: 0%;
+ left: 50%;
+ transform: translate(-50%, 0%);
+ font-weight: bold;
+ font-size: 1.2rem;
+ color: #333;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
}
}
+// Breakdown Table Open/Close Logic
-.semi-circle-wrapper {
- width: 250px;
- height: 125px;
- overflow: hidden;
- position: relative;
- }
-
- .semi-circle {
- width: 250px;
- height: 250px;
- border-radius: 50%;
- position: relative;
- transition: background 0.5s ease;
- transform: rotate(180deg); /* rotate so 0% is at left */
- }
-
- .progress-cover {
- position: absolute;
- width: 75%;
- height: 75%;
- top: 12.5%;
- left: 12.5%;
- background-color: white;
- border-radius: 50%;
- }
-
- .label {
- position: absolute;
- top: 40%;
- left: 50%;
- transform: translate(-50%, -50%);
- font-weight: bold;
- font-size: 1.2rem;
- }
-
\ No newline at end of file
+.breakdown-table-wrapper {
+ &.closed {
+ max-height: 0;
+ padding: 0;
+ }
+
+ &.open {
+ max-height: 500px;
+ }
+
+
+
+
+
+ .breakdown-table {
+ width: 100%;
+ border-collapse: collapse;
+
+ th,
+ td {
+ padding: 10px;
+ border: 1px solid #ddd;
+ text-align: left;
+ }
+
+ .total-row {
+ background-color: #f4f4f4;
+ font-weight: bold;
+ }
+
+ .net-profit-row {
+ background-color: #dff0d8;
+ font-weight: bold;
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/styles/components/analysis/analysis.scss b/app/src/styles/components/analysis/analysis.scss
index bc33556..db4ac67 100644
--- a/app/src/styles/components/analysis/analysis.scss
+++ b/app/src/styles/components/analysis/analysis.scss
@@ -1,18 +1,25 @@
.analysis {
- position: absolute;
+ position: fixed;
top: 0;
left: 0;
display: flex;
- justify-content: center;
- align-items: center;
+ justify-content: space-between;
+ align-items: start;
width: 100%;
height: 100vh;
- z-index: 100000000000000000000000000000;
+ // pointer-events: none;k
+ z-index: 10000;
+
+ .analysis-wrapper {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ }
}
.analysis-card {
min-width: 333px;
- // background: var(--primary-color);
+ background: var(--background-color);
border-radius: 20px;
padding: 8px;
@@ -109,6 +116,7 @@
.throughoutSummary {
+
.throughoutSummary-wrapper {
.process-container {
display: flex;
diff --git a/app/src/styles/components/simulation/simulation.scss b/app/src/styles/components/simulation/simulation.scss
index eb109c2..815ec20 100644
--- a/app/src/styles/components/simulation/simulation.scss
+++ b/app/src/styles/components/simulation/simulation.scss
@@ -3,15 +3,75 @@
.simulation-player-wrapper {
position: fixed;
- bottom: 32px;
+ bottom: 50px;
left: 50%;
z-index: 2;
transform: translate(-50%, 0);
+ width: 70%;
+
.simulation-player-container {
+ background-color: var(--background-color);
+ padding: 7px;
+ border-radius: 15px;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+
+ .progresser-wrapper {
+ background-color: var(--highlight-accent-color);
+ padding: 4px 5px;
+ border-radius: 12px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding-top: 30px;
+ transition: height 0.2s linear;
+ }
+
+
+
+
.controls-container {
@include flex-center;
gap: 12px;
- margin-bottom: 4px;
+ justify-content: space-between;
+
+ .production-details,
+ .controls-wrapper {
+ display: flex;
+ gap: 6px;
+ }
+
+ .production-details {
+ .production-wrapper {
+
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ gap: 6px;
+
+ .header {
+ display: flex;
+ flex-direction: row;
+ gap: 6px
+ }
+
+ .progress-wrapper {
+ width: 164px;
+ height: 8px;
+ border-radius: 5px;
+ // overflow: hidden;
+ background-color: var(--highlight-accent-color);
+
+ .progress {
+ border-radius: 5px;
+ height: 100%;
+ background-color: var(--accent-color);
+ }
+ }
+ }
+ }
+
.simulation-button-container {
@include flex-center;
gap: 2px;
@@ -20,38 +80,74 @@
background-color: var(--background-color);
border-radius: #{$border-radius-small};
cursor: pointer;
+
&:hover {
background-color: var(--highlight-accent-color);
color: var(--accent-color);
+
path {
stroke: var(--accent-color);
}
}
}
}
+
.speed-control-container {
@include flex-center;
gap: 18px;
padding: 5px 16px;
- background: var(--background-color);
+ // background: var(--background-color);
border-radius: #{$border-radius-medium};
box-sizing: #{$box-shadow-medium};
+ border-radius: 20px;
+ position: relative;
+
.min-value,
.max-value {
+ display: flex;
+ align-items: center;
font-weight: var(--font-weight-bold);
}
+
.slider-container {
- width: 580px;
+ width: 100%;
max-width: 80vw;
height: 28px;
- background: var(--background-color-gray);
+ // background: var(--background-color-gray);
border-radius: #{$border-radius-small};
position: relative;
- padding: 4px 26px;
+ // padding: 4px 26px;
+
+
+ .speed-label {
+ font-size: var(--font-size-tiny);
+ position: absolute;
+ bottom: -4px;
+
+ &:first-child {
+ left: 0;
+ }
+
+ &:last-child {
+ right: 0;
+ }
+ }
+
+ &::after {
+ content: "";
+ background-color: #E5E5EA;
+ position: absolute;
+ top: 50%;
+ transform: translate(0, -50%);
+ width: 100%;
+ height: 3px;
+ }
+
.custom-slider {
height: 100%;
width: 100%;
position: relative;
+
.slider-input {
position: absolute;
width: 100%;
@@ -60,55 +156,209 @@
z-index: 3;
cursor: pointer;
}
+
.slider-handle {
position: absolute;
+ top: 50%;
width: 42px;
line-height: 20px;
text-align: center;
background: var(--accent-color);
color: var(--primary-color);
border-radius: #{$border-radius-small};
- transform: translateX(-50%);
+ transform: translate(-50%, -50%);
cursor: pointer;
z-index: 2;
}
}
- .marker{
- position: absolute;
- background-color: var(--text-disabled);
- width: 2px;
- height: 12px;
- border-radius: 1px;
- top: 8px;
+
+ .marker {
+ position: absolute;
+ background-color: var(--text-disabled);
+ width: 2px;
+ height: 12px;
+ border-radius: 1px;
+ top: 8px;
+ z-index: 1;
}
- .marker.marker-10{
- left: 10%;
+
+ .marker.marker-10 {
+ left: 10%;
}
- .marker.marker-20{
- left: 20%;
+
+ .marker.marker-20 {
+ left: 20%;
}
- .marker.marker-30{
- left: 30%;
+
+ .marker.marker-30 {
+ left: 30%;
}
- .marker.marker-40{
- left: 40%;
+
+ .marker.marker-40 {
+ left: 40%;
}
- .marker.marker-50{
- left: 50%;
+
+ .marker.marker-50 {
+ left: 50%;
}
- .marker.marker-60{
- left: 60%;
+
+ .marker.marker-60 {
+ left: 60%;
}
- .marker.marker-70{
- left: 70%;
+
+ .marker.marker-70 {
+ left: 70%;
}
- .marker.marker-80{
- left: 80%;
+
+ .marker.marker-80 {
+ left: 80%;
}
- .marker.marker-90{
- left: 90%;
+
+ .marker.marker-90 {
+ left: 90%;
}
}
}
+
+ .time-displayer {
+ display: flex;
+ justify-content: space-between;
+ height: auto;
+ opacity: 1;
+ // overflow: hidden;
+
+ transition: all 0.5s ease;
+
+ .start-time-wrappper,
+ .end-time-wrappper {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ }
+
+ .time-progresser {
+ width: 70%;
+
+ .timeline {
+ padding: 16px;
+ // background: #f5f3fa;
+ background: linear-gradient(90.17deg, rgba(255, 255, 255, 0.64) 1.53%, rgba(255, 255, 255, 0.48) 98.13%);
+
+ border-radius: 30px;
+ display: flex;
+ align-items: center;
+ width: 100%;
+ height: 33px;
+
+ .label-dot-wrapper {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 6px;
+ position: relative;
+
+ .label {
+ position: absolute;
+ top: -200%;
+ transform: translate(0, -0);
+ font-size: 12px;
+ color: #666;
+ white-space: nowrap;
+ }
+
+ .dot {
+ width: 14px;
+ height: 14px;
+ border-radius: 50%;
+ background-color: #d3d3e2;
+
+ &.filled {
+ background-color: #8f5cf2;
+
+ border: 4px solid var(--accent-color);
+ }
+ }
+ }
+
+ .line {
+ flex-grow: 1;
+ height: 4px;
+ background-color: #d3d3e2;
+ margin: 0 4px;
+
+ &.filled {
+ background-color: #8f5cf2;
+ }
+ }
+ }
+ }
+ }
+
+
+
}
+
+
}
+
+.processDisplayer {
+ border-radius: 5px;
+ // overflow: hidden;
+ background-color: var(--highlight-accent-color);
+ padding: 14px 6px;
+ position: relative;
+
+ .process-player {
+ position: absolute;
+ top: 50%;
+ transform: translate(0, -50%);
+ width: 3.946108102798462px;
+ height: 26px;
+ left: 86.81px;
+ border-radius: 14px;
+ border-width: 1px;
+ background: var(--accent-color, #6F42C1);
+
+ }
+
+ .process-wrapper {
+ display: flex;
+ // padding: 0px 16px;
+
+ .process {
+ height: 5px;
+ background-color: #4caf50;
+ color: white;
+ text-align: center;
+ line-height: 30px;
+ transition: width 0.3s ease;
+ }
+ }
+
+}
+
+
+.simulation-player-container.open {
+
+ .progresser-wrapper {
+ padding-top: 4px;
+ }
+
+ .time-displayer {
+ height: 0;
+ opacity: 0;
+ pointer-events: none;
+ display: none;
+ }
+
+ .processDisplayer {
+ padding: 0;
+ background: transparent;
+
+ .process-player {
+ width: 0;
+ display: none !important;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/styles/layout/sidebar.scss b/app/src/styles/layout/sidebar.scss
index cab078e..5ac39ed 100644
--- a/app/src/styles/layout/sidebar.scss
+++ b/app/src/styles/layout/sidebar.scss
@@ -366,15 +366,66 @@
min-height: 50vh;
padding-bottom: 12px;
position: relative;
- display: flex;
- flex-direction: column;
+ overflow: auto;
.sidebar-right-content-container {
border-bottom: 1px solid var(--border-color);
- // flex: 1;
height: calc(100% - 36px);
position: relative;
- overflow: auto;
+ width: 320px;
+ .no-event-selected {
+ color: #666;
+ padding: 1.8rem 1rem;
+ grid-column: 1 / -1;
+ .products-list {
+ padding-top: 1rem;
+ .products-list-title {
+ text-align: start;
+ color: var(--accent-color);
+ font-size: var(--font-size-regular);
+ }
+ ul {
+ li {
+ text-align: start;
+ margin: 8px 0;
+ padding: 2px 0;
+ text-decoration: none;
+ &::marker {
+ content: "";
+ }
+ button {
+ width: fit-content;
+ position: relative;
+ transition: all 0.2s ease;
+ @include flex-center;
+ gap: 4px;
+ &:before {
+ content: "";
+ position: absolute;
+ left: 0;
+ bottom: -4px;
+ background: var(--accent-color);
+ height: 1px;
+ width: 0%;
+ transition: all 0.3s ease;
+ }
+ }
+ &:hover {
+ button {
+ path {
+ stroke: var(--accent-color);
+ strokeWidth: 1.5px;
+ }
+ color: var(--accent-color);
+ &:before {
+ width: 100%;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
}
@@ -642,7 +693,7 @@
path {
stroke: var(--accent-color);
- stroke-width: 1.5px;
+ strokeWidth: 1.5px;
}
&:hover {
@@ -675,10 +726,14 @@
color: var(--primary-color);
border-radius: #{$border-radius-small};
cursor: pointer;
-
+ outline: none;
+ border: none;
path {
stroke: var(--primary-color);
}
+ &:disabled {
+ background-color: var(--text-disabled);
+ }
}
}
@@ -706,7 +761,7 @@
}
.selected-actions-list {
margin-bottom: 8px;
- .eye-dropper-input-container{
+ .eye-dropper-input-container {
padding: 6px 12px;
.regularDropdown-container {
padding: 5px 8px;
@@ -747,19 +802,21 @@
width: 100%;
margin: 2px 0;
border-radius: #{$border-radius-small};
- }
+ .value {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ min-width: 80%;
+ gap: 6px;
+ .input-value {
+ text-align: start;
+ }
- .value {
- display: flex;
- justify-content: flex-start;
- align-items: center;
- min-width: 80%;
- gap: 6px;
-
- input {
- width: fit-content;
- outline: none;
- accent-color: var(--accent-color);
+ input {
+ width: fit-content;
+ outline: none;
+ accent-color: var(--accent-color);
+ }
}
}
@@ -797,6 +854,7 @@
@include flex-center;
padding: 4px;
cursor: grab;
+ width: 100%;
&:active {
cursor: grabbing;
diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss
index f0f7704..66a60e7 100644
--- a/app/src/styles/main.scss
+++ b/app/src/styles/main.scss
@@ -1,12 +1,12 @@
// abstracts
@use 'abstracts/variables';
@use 'abstracts/mixins';
-@use 'abstracts/placeholders';
@use 'abstracts/functions';
// base
@use 'base/reset';
@use 'base/typography';
+@use 'base/global';
@use 'base/base';
// components
diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss
index 0d4f98d..1354859 100644
--- a/app/src/styles/pages/realTimeViz.scss
+++ b/app/src/styles/pages/realTimeViz.scss
@@ -662,6 +662,7 @@
}
.distance-line {
+
position: absolute;
border-style: dashed;
border-color: var(--accent-color);
@@ -776,13 +777,13 @@
border-radius: 6px;
overflow: hidden;
padding: 4px;
-
min-width: 150px;
.option {
padding: 4px 10px;
border-radius: #{$border-radius-small};
color: var(--text-color);
+ text-wrap: nowrap;
cursor: pointer;
&:hover {
@@ -794,8 +795,8 @@
color: #f65648;
&:hover {
- background-color: #f65648;
- color: white;
+ background-color: #f657484d;
+ color: #f65648;
}
}
}
diff --git a/app/src/types/simulationTypes.d.ts b/app/src/types/simulationTypes.d.ts
index f6b0a0c..3293699 100644
--- a/app/src/types/simulationTypes.d.ts
+++ b/app/src/types/simulationTypes.d.ts
@@ -25,7 +25,7 @@ interface ConveyorPointSchema {
action: {
actionUuid: string;
actionName: string;
- actionType: "default" | "spawn" | "swap" | "despawn";
+ actionType: "default" | "spawn" | "swap" | "delay" | "despawn";
material: string;
delay: number | "inherit";
spawnInterval: number | "inherit";
@@ -42,7 +42,6 @@ interface VehiclePointSchema {
actionUuid: string;
actionName: string;
actionType: "travel";
- material: string | null;
unLoadDuration: number;
loadCapacity: number;
pickUpPoint: { x: number; y: number, z: number } | null;
@@ -119,12 +118,14 @@ interface StorageEventSchema extends AssetEventSchema {
point: StoragePointSchema;
}
+type PointsScheme = ConveyorPointSchema | VehiclePointSchema | RoboticArmPointSchema | MachinePointSchema | StoragePointSchema;
+
type EventsSchema = ConveyorEventSchema | VehicleEventSchema | RoboticArmEventSchema | MachineEventSchema | StorageEventSchema;
type productsSchema = {
productName: string;
productId: string;
- eventsData: EventsSchema[];
+ eventDatas: EventsSchema[];
}[]
@@ -133,6 +134,7 @@ interface ConveyorStatus extends ConveyorEventSchema {
isActive: boolean;
idleTime: number;
activeTime: number;
+
}
interface MachineStatus extends MachineEventSchema {
@@ -168,4 +170,17 @@ interface StorageUnitStatus extends StorageEventSchema {
idleTime: number;
activeTime: number;
currentLoad: number;
-}
\ No newline at end of file
+}
+
+interface MaterialSchema {
+ materialId: string;
+ materialName: string;
+ materialType: string;
+ isActive: boolean;
+ startTime?: string;
+ endTime?: string;
+ cost?: number;
+ weight?: number;
+}
+
+type MaterialsSchema = MaterialSchema[];
\ No newline at end of file