Merge pull request 'v3' (#100) from v3 into main

Reviewed-on: http://185.100.212.76:7776/Dwinzo-Beta/Dwinzo_dev/pulls/100
This commit is contained in:
Vishnu 2025-06-12 11:49:01 +00:00
commit 0d83783d9c
33 changed files with 670 additions and 316 deletions

View File

@ -1,6 +1,6 @@
import React, { useEffect } from "react";
import { Cache } from "three";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { BrowserRouter as Router, Routes, Route, useParams } from "react-router-dom";
import Dashboard from "./pages/Dashboard";
import Project from "./pages/Project";
import UserAuth from "./pages/UserAuth";
@ -14,6 +14,7 @@ const App: React.FC = () => {
Cache.enabled = true;
}, []);
return (
<LoggerProvider>
<Router>

View File

@ -116,7 +116,6 @@ const DashboardHome: React.FC = () => {
const renderProjects = () => {
const projectList = recentProjects[Object.keys(recentProjects)[0]];
console.log('projectList: ', projectList);
if (!projectList?.length) {
return <div className="empty-state">No recent projects found</div>;
}

View File

@ -1292,8 +1292,8 @@ export const PerformanceIcon = () => {
<path
d="M11.1484 1.16797C16.2895 1.16797 20.4921 5.3714 20.4922 10.5117C20.4922 12.7821 19.6669 14.8349 18.3467 16.4316L18.1807 16.2656C19.3378 14.8012 20.1179 12.9631 20.2236 10.9746L20.2354 10.5107C20.2364 9.46665 20.0573 8.43143 19.707 7.4502L19.5469 7.03223C19.1476 6.06682 18.5848 5.17876 17.8848 4.40625L17.5771 4.08301C16.733 3.23884 15.7302 2.56951 14.627 2.11328C13.524 1.65716 12.342 1.42257 11.1484 1.42383C10.1041 1.42277 9.06837 1.60175 8.08691 1.95215L7.66992 2.11328C6.70451 2.51257 5.81644 3.07536 5.04395 3.77539L4.71973 4.08398C3.98113 4.8228 3.37584 5.68237 2.93066 6.625L2.75 7.03418C2.3509 7.99977 2.12214 9.02598 2.07227 10.0674L2.06152 10.5127C2.06152 12.7338 2.80096 14.7019 4.10059 16.2803L3.93652 16.4443C2.58335 14.8587 1.80469 12.8058 1.80469 10.5117C1.80479 5.37146 6.00742 1.16807 11.1484 1.16797ZM10.8008 11.2383L10.3887 10.8076L13.8027 7.81543L10.8008 11.2383Z"
stroke="url(#paint0_linear_1736_988)"
stroke-width="1.80917"
stroke-linecap="round"
strokeWidth="1.80917"
strokeLinecap="round"
/>
<defs>
<linearGradient
@ -1304,8 +1304,8 @@ export const PerformanceIcon = () => {
y2="17.742"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#6F42C1" />
<stop offset="1" stop-color="#B392F0" />
<stop stopColor="#6F42C1" />
<stop offset="1" stopColor="#B392F0" />
</linearGradient>
</defs>
</svg>
@ -1328,9 +1328,9 @@ export const GreenTickIcon = () => {
<path
d="M4.85742 7.20505L6.91318 9.25578L10.3394 5.83789"
stroke="white"
stroke-width="2.45534"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="2.45534"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);

View File

@ -1,12 +1,13 @@
import { useProductContext } from '../../../modules/simulation/products/productContext'
import RegularDropDown from '../../ui/inputs/RegularDropDown';
import { useProductStore } from '../../../store/simulation/useProductStore';
import { useLoadingProgress, useSaveVersion } from '../../../store/builder/store';
import { useCompareProductDataStore, useLoadingProgress, useSaveVersion } from '../../../store/builder/store';
import useModuleStore from '../../../store/useModuleStore';
import CompareLayOut from '../../ui/compareVersion/CompareLayOut';
import ComparisonResult from '../../ui/compareVersion/ComparisonResult';
import { useComparisonProduct, useMainProduct } from '../../../store/simulation/useSimulationStore';
import { usePauseButtonStore, usePlayButtonStore } from '../../../store/usePlayButtonStore';
import { usePlayButtonStore } from '../../../store/usePlayButtonStore';
import { useEffect, useState } from 'react';
function ComparisonScene() {
const { isPlaying, setIsPlaying } = usePlayButtonStore();
@ -17,16 +18,63 @@ function ComparisonScene() {
const { selectedProduct } = selectedProductStore();
const { comparisonProduct, setComparisonProduct } = useComparisonProduct();
const { mainProduct } = useMainProduct();
const { setIsPaused } = usePauseButtonStore();
const { loadingProgress } = useLoadingProgress();
const { compareProductsData, setCompareProductsData } = useCompareProductDataStore();
const [shouldShowComparisonResult, setShouldShowComparisonResult] = useState(false);
const handleSelectLayout = (option: string) => {
const product = products.find((product) => product.productName === option);
if (product) {
setComparisonProduct(product.productUuid, product.productName);
setIsPaused(true);
}
};
// useEffect(() => {
// setCompareProductsData([
// {
// "productUuid": "15193386-ec58-4ec6-8a92-e665a39eebf1",
// "productName": "Product 1",
// "simulationData": {
// "roiPercentage": 273.9428571428571,
// "paybackPeriod": 1.8251981643721318,
// "netProfit": 9588000,
// "productionCapacity": 4508.5,
// "machineIdleTime": 1450,
// "machineActiveTime": 430,
// "throughputData": 180.34
// }
// },
// {
// "productUuid": "f614bf50-f61d-41c5-acc0-3783fb4da6b8",
// "productName": "Product 2",
// "simulationData": {
// "roiPercentage": 281.7214285714286,
// "paybackPeriod": 1.7748028701097842,
// "netProfit": 9860250,
// "productionCapacity": 4599.25,
// "machineIdleTime": 1885,
// "machineActiveTime": 646,
// "throughputData": 183.97
// }
// }
// ])
// }, []); // ✅ Runs only once on mount
useEffect(() => {
if (mainProduct && comparisonProduct && compareProductsData.length > 1) {
// console.log('compareProductsData: ', compareProductsData);
const hasMain = compareProductsData.some(val => val.productUuid === mainProduct.productUuid);
const hasComparison = compareProductsData.some(val => val.productUuid === comparisonProduct.productUuid);
if (hasMain && hasComparison && mainProduct.productUuid !== comparisonProduct.productUuid) {
setShouldShowComparisonResult(true);
} else {
setShouldShowComparisonResult(false);
}
} else {
setShouldShowComparisonResult(false);
}
}, [compareProductsData, mainProduct, comparisonProduct]);
return (
<>
{isVersionSaved && activeModule === "simulation" && selectedProduct && (
@ -42,7 +90,8 @@ function ComparisonScene() {
</div>
}
<CompareLayOut />
{(comparisonProduct && mainProduct && !loadingProgress) && <ComparisonResult />}
{(shouldShowComparisonResult && !loadingProgress) && <ComparisonResult />}
</>
)}
</>

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect } from "react";
import {
useLoadingProgress,
useRenameModeStore,
@ -42,7 +42,7 @@ function MainScene() {
const { setMainProduct } = useMainProduct();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { isVersionSaved } = useSaveVersion();
const { isVersionSaved, setIsVersionSaved } = useSaveVersion();
const { activeModule } = useModuleStore();
const { selectedUser } = useSelectedUserStore();
const { loadingProgress } = useLoadingProgress();
@ -52,11 +52,19 @@ function MainScene() {
const { visualizationSocket } = useSocketStore();
const { selectedZone } = useSelectedZoneStore();
const { setFloatingWidget } = useFloatingWidget();
const { comparisonProduct } = useComparisonProduct();
const { clearComparisonProduct } = useComparisonProduct();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { setName } = useAssetsStore();
const { projectId } = useParams()
const { isRenameMode, setIsRenameMode } = useRenameModeStore();
useEffect(() => {
if (activeModule !== 'simulation') {
clearComparisonProduct();
setIsVersionSaved(false);
}
}, [activeModule])
const handleSelectLayout = (option: string) => {
const product = products.find((product) => product.productName === option);
if (product) {
@ -64,6 +72,7 @@ function MainScene() {
}
};
const handleObjectRename = async (newName: string) => {
if (!projectId) return
const email = localStorage.getItem("email") ?? "";
const organization = email?.split("@")[1]?.split(".")[0];
let response = await setFloorItemApi(
@ -99,10 +108,10 @@ function MainScene() {
{activeModule !== "market" && !isPlaying && !isVersionSaved && (
<Tools />
)}
{(isPlaying || comparisonProduct !== null) &&
{(isPlaying) &&
activeModule === "simulation" &&
loadingProgress == 0 && <SimulationPlayer />}
{(isPlaying || comparisonProduct !== null) &&
{(isPlaying) &&
activeModule !== "simulation" && <ControlsPlayer />}
{isRenameMode && selectedFloorItem?.userData.modelName && <RenameTooltip name={selectedFloorItem?.userData.modelName} onSubmit={handleObjectRename} />}

View File

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import { AIIcon } from "../../../icons/ExportCommonIcons";
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
import { AnalysisPresetsType } from "../../../../types/analysis";
@ -19,55 +19,70 @@ const Analysis: React.FC = () => {
// { type: "default", inputs: { label: "Machine uptime", activeOption: "%" } },
],
"Production capacity": [
{ type: "range", inputs: { label: "Shift length", activeOption: "hr" } },
{ type: "default", inputs: { label: "Shifts / day", activeOption: "unit" } },
{ type: "default", inputs: { label: "Working days / year", activeOption: "days" } },
{ type: "default", inputs: { label: "Yield rate", activeOption: "%" } },
{ type: "range", inputs: { label: "Shift length", activeOption: "hr", defaultValue: 1 } },
{ type: "default", inputs: { label: "Shifts / day", activeOption: "unit", defaultValue: 3 } },
{ type: "default", inputs: { label: "Working days / year", activeOption: "days", defaultValue: 300 } },
{ type: "default", inputs: { label: "Yield rate", activeOption: "%", defaultValue: 98 } },
],
ROI: [
{
type: "default",
inputs: { label: "Selling price", activeOption: "INR" },
inputs: { label: "Selling price", activeOption: "INR", defaultValue: 500 },
},
{
type: "default",
inputs: { label: "Material cost", activeOption: "INR" },
inputs: { label: "Material cost", activeOption: "INR", defaultValue: 300 },
},
{
type: "default",
inputs: { label: "Labor Cost", activeOption: "INR" },
inputs: { label: "Labor Cost", activeOption: "INR", defaultValue: 150 },
},
{
type: "default",
inputs: { label: "Maintenance cost", activeOption: "INR" },
inputs: { label: "Labor Count", activeOption: "", defaultValue: 1 },
},
{
type: "default",
inputs: { label: "Electricity cost", activeOption: "INR" },
inputs: { label: "Maintenance cost", activeOption: "INR", defaultValue: 1200 },
},
{
type: "default",
inputs: { label: "Fixed costs", activeOption: "INR" },
inputs: { label: "Electricity cost", activeOption: "INR", defaultValue: 840 },
},
{
type: "default",
inputs: { label: "Initial Investment", activeOption: "INR" },
inputs: { label: "Fixed costs", activeOption: "INR", defaultValue: 1150 },
},
{
type: "default",
inputs: { label: "Salvage value", activeOption: "Hrs" },
inputs: { label: "Initial Investment", activeOption: "INR", defaultValue: 3500000 },
},
{
type: "default",
inputs: { label: "Production period", activeOption: "yrs" },
inputs: { label: "Salvage value", activeOption: "Day", defaultValue: 565 },
},
{
type: "default",
inputs: { label: "Tax rate", activeOption: "%" },
inputs: { label: "Production period", activeOption: "yrs", defaultValue: 5 },
},
{
type: "default",
inputs: { label: "Tax rate", activeOption: "%", defaultValue: 30 },
},
],
};
useEffect(() => {
Object.values(AnalysisPresets).forEach((category) => {
category.forEach((item) => {
const { label, defaultValue } = item.inputs;
if (defaultValue !== undefined) {
updateInputValue(label, defaultValue.toString());
}
});
});
}, []);
const { inputValues, setInputValues, updateInputValue } = useInputValues();
return (

View File

@ -10,7 +10,8 @@ interface InputRendererProps {
onInputChange: (label: string, value: string) => void;
}
const RenderAnalysisInputs: React.FC<InputRendererProps> = ({ keyName, presets,inputValues, onInputChange }) => {
const RenderAnalysisInputs: React.FC<InputRendererProps> = ({ keyName, presets, inputValues, onInputChange }) => {
return (
<div key={`main-${keyName}`} className="analysis-inputs">
{presets.map((preset, index) => {
@ -19,7 +20,7 @@ const RenderAnalysisInputs: React.FC<InputRendererProps> = ({ keyName, presets,i
<InputWithDropDown
key={index}
label={preset.inputs.label}
value={inputValues[preset.inputs.label] || ""}
value={preset.inputs.defaultValue?.toString() || inputValues[preset.inputs.label] || ""}
activeOption={preset.inputs.activeOption}
onChange={(newValue) => onInputChange(preset.inputs.label, newValue)}
/>
@ -32,7 +33,7 @@ const RenderAnalysisInputs: React.FC<InputRendererProps> = ({ keyName, presets,i
label={preset.inputs.label}
min={0}
max={8}
value={5}
value={Number(preset.inputs.defaultValue) || Number(inputValues[preset.inputs.label]) || 5}
/>
);
}

View File

@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from "react";
import { AddIcon, ArrowIcon, RemoveIcon, ResizeHeightIcon, } from "../../../icons/ExportCommonIcons";
import RenameInput from "../../../ui/inputs/RenameInput";
import { handleResize } from "../../../../functions/handleResizePannel";
import { useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
import { useMainProduct, useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
import { useProductStore } from "../../../../store/simulation/useProductStore";
import { generateUUID } from "three/src/math/MathUtils";
import RenderOverlay from "../../../templates/Overlay";
@ -48,6 +48,7 @@ const Simulations: React.FC = () => {
const [processes, setProcesses] = useState<Event[][]>();
const { setToggleUI } = useToggleStore();
const { projectId } = useParams();
const { setMainProduct } = useMainProduct();
const { comparePopUp, setComparePopUp } = useCompareStore();
const { setIsVersionSaved } = useSaveVersion();
@ -85,8 +86,13 @@ const Simulations: React.FC = () => {
updatedProducts[newSelectedIndex].productUuid,
updatedProducts[newSelectedIndex].productName
);
setMainProduct(
updatedProducts[newSelectedIndex].productUuid,
updatedProducts[newSelectedIndex].productName
);
} else {
setSelectedProduct("", "");
setMainProduct("", "");
}
}
@ -102,6 +108,7 @@ const Simulations: React.FC = () => {
renameProductApi({ productName: newName, productUuid, projectId: projectId || '' });
if (selectedProduct.productUuid === productUuid) {
setSelectedProduct(productUuid, newName);
setMainProduct(productUuid, newName);
}
};
@ -172,9 +179,10 @@ const Simulations: React.FC = () => {
{/* eslint-disable-next-line */}
<div
className="value"
onClick={() =>
onClick={() => {
setSelectedProduct(product.productUuid, product.productName)
}
setMainProduct(product.productUuid, product.productName)
}}
>
<input
type="radio"

View File

@ -26,6 +26,8 @@ const ThroughputSummary: React.FC = () => {
unit: "KWH",
};
// Dynamic shift data
const shiftUtilization = [
{ shift: 1, percentage: 30, color: "#F3C64D" },
@ -126,7 +128,7 @@ const ThroughputSummary: React.FC = () => {
<div className="process-container">
<div className="throughput-value">
<span className="value">{productionCapacityData}</span>{" "}
Units/hour
Units/Month
</div>
<div className="lineChart">
<div className="assetUsage">

View File

@ -9,7 +9,7 @@ import {
import SemiCircleProgress from "./SemiCircleProgress";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
import SkeletonUI from "../../templates/SkeletonUI";
import { useROISummaryData } from "../../../store/builder/store";
import { useInputValues, useROISummaryData } from "../../../store/builder/store";
const ROISummary = ({
roiSummaryData = {
@ -67,6 +67,8 @@ const ROISummary = ({
},
}) => {
const [isTableOpen, setIsTableOpen] = useState(false); // State to handle the table open/close
const { inputValues } = useInputValues();
const productionPeriod = parseFloat(inputValues["Production period"]);
// Function to toggle the breakdown table visibility
const toggleTable = () => {
@ -95,6 +97,17 @@ const ROISummary = ({
}
}, [roiSummary]);
function getPaybackDateFromYears(yearsToAdd: number) {
const now = new Date();
const totalMonths = Math.round(yearsToAdd * 12);
const paybackDate = new Date(now.getFullYear(), now.getMonth() + totalMonths, now.getDate());
const month = paybackDate.toLocaleString("en-GB", { month: "long" });
const year = paybackDate.getFullYear();
return `${month} ${year}`;
}
return (
<>
@ -121,25 +134,30 @@ const ROISummary = ({
<SonarCrownIcon />
<div className="icon"></div>
<div className="info">
<span> {roiSummary.roiPercentage}%</span> ROI with payback
in just <span>{roiSummary.paybackPeriod}</span> months
<span>{roiSummary.roiPercentage.toFixed(2)}% </span>
ROI in the period of {productionPeriod} years
<span></span>
</div>
</div>
<div className="roi-details">
<div className="progress-wrapper">
<SemiCircleProgress />
<SemiCircleProgress
progress={(parseFloat(roiSummary.paybackPeriod.toFixed(2)) / (productionPeriod)) * 100}
years={parseFloat(roiSummary.paybackPeriod.toFixed(2))}
/>
<div className="content">
you're on track to hit it by
<div className="key">July 2029</div>
<div className="key">{getPaybackDateFromYears(roiSummary.paybackPeriod)}</div>
</div>
</div>
<div className="metrics">
<div className="metric-wrapper">
<div className="metric-item">
<span className="metric-label">Total Cost Incurred</span>
<span className="metric-value">
<span></span>
{roiSummary.totalCost}
{roiSummary.totalCost.toFixed(0)}
</span>
</div>
<div className="metric-item">
@ -147,7 +165,7 @@ const ROISummary = ({
<span className="metric-value">
<span></span>
{roiSummary.revenueGenerated}
{roiSummary.revenueGenerated.toFixed(0)}
</span>
</div>
</div>
@ -161,8 +179,8 @@ const ROISummary = ({
<div className="metric-value">
<span></span>
{roiSummary.netProfit > 0
? roiSummary.netProfit
: roiSummary.netLoss}
? roiSummary.netProfit.toFixed(0)
: roiSummary.netLoss.toFixed(0)}
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
import { useMachineCount, useMachineUptime, useMaterialCycle, useProductionCapacityData, useThroughPutData } from "../../../store/builder/store";
import { useInputValues, useMachineCount, useMachineUptime, useMaterialCycle, useProductionCapacityData, useThroughPutData } from "../../../store/builder/store";
import {
ThroughputSummaryIcon,
} from "../../icons/analysis";
@ -14,14 +14,15 @@ const ProductionCapacity = ({
const { machineActiveTime } = useMachineUptime();
const { materialCycleTime } = useMaterialCycle();
const { throughputData } = useThroughPutData()
const { inputValues } = useInputValues();
const progressPercent = machineActiveTime;
const shiftLength = parseFloat(inputValues["Shift length"]);
const totalBars = 6;
const barsToFill = Math.floor((progressPercent / 100) * totalBars);
const partialFillPercent =
((progressPercent / 100) * totalBars - barsToFill) * 100;
const partialFillPercent = ((progressPercent / 1000) * totalBars - barsToFill) * 100;
const [isLoading, setIsLoading] = useState(true);
@ -29,13 +30,14 @@ const ProductionCapacity = ({
if (throughputData > 0) {
setIsLoading(false);
} else {
setIsLoading(true);
setIsLoading(false);
}
}, [throughputData])
const Units_per_hour = ((shiftLength * 60) / (materialCycleTime / 60) / shiftLength)
return (
<>
{!isLoading && <div className="throughtputSummary-container analysis-card">
<div className="throughtputSummary-wrapper analysis-card-wrapper">
<div className="card-header">
@ -53,8 +55,7 @@ const ProductionCapacity = ({
<>
<div className="process-container">
<div className="throughput-value">
<span className="value">{throughputData}</span> Units/hour
<span className="value">{(Units_per_hour).toFixed(2) === "Infinity"? 0 : (Units_per_hour).toFixed(2) }</span> Units/hour
</div>
{/* Dynamic Progress Bar */}

View File

@ -118,7 +118,6 @@ const CompareLayOut = () => {
if (product) {
setComparisonProduct(product.productUuid, product.productName);
setLoadingProgress(1);
setIsPaused(true);
}
};

View File

@ -1,9 +1,41 @@
import React, { useMemo } from "react";
import React, { useEffect, useMemo, useState } from "react";
import PerformanceResult from "./result-card/PerformanceResult";
import EnergyUsage from "./result-card/EnergyUsage";
import { Bar, Line, Pie } from "react-chartjs-2";
import { CompareProduct, useCompareProductDataStore } from "../../../store/builder/store";
import { useComparisonProduct, useMainProduct } from "../../../store/simulation/useSimulationStore";
const ComparisonResult = () => {
const { compareProductsData, setCompareProductsData } = useCompareProductDataStore();
const { comparisonProduct, setComparisonProduct } = useComparisonProduct();
const { mainProduct } = useMainProduct();
const [comparedProducts, setComparedProducts] = useState<[CompareProduct, CompareProduct] | []>([]);
useEffect(() => {
if (compareProductsData.length > 0 && mainProduct && comparisonProduct) {
const mainProductData = compareProductsData.find(
(product) => product.productUuid === mainProduct.productUuid
);
const comparisonProductData = compareProductsData.find(
(product) => product.productUuid === comparisonProduct.productUuid
);
if (mainProductData && comparisonProductData) {
setComparedProducts([mainProductData, comparisonProductData]);
} else {
setComparedProducts([]);
}
} else {
setComparedProducts([]);
}
}, [compareProductsData, mainProduct, comparisonProduct]);
useEffect(() => {
if (comparedProducts.length === 2) {
}
}, [comparedProducts]);
const options = useMemo(
() => ({
responsive: true,
@ -25,11 +57,11 @@ const ComparisonResult = () => {
const purpleLight = "#b19cd9";
const throughputData = {
labels: ["Layout 1", "Layout 2"],
labels: [comparedProducts[0]?.productName, comparedProducts[1]?.productName],
datasets: [
{
label: "Throughput (units/hr)",
data: [500, 550],
data: [comparedProducts[0]?.simulationData.throughputData, comparedProducts[1]?.simulationData.throughputData],
backgroundColor: [purpleDark, purpleLight],
borderColor: [purpleDark, purpleLight],
borderWidth: 1,
@ -41,11 +73,11 @@ const ComparisonResult = () => {
const cycleTimePieData = {
labels: ["Layout 1", "Layout 2"],
labels: [comparedProducts[0]?.productName, comparedProducts[1]?.productName],
datasets: [
{
label: "Cycle Time (sec)",
data: [120, 110],
data: [comparedProducts[0]?.simulationData.machineActiveTime, comparedProducts[1]?.simulationData.machineActiveTime],
backgroundColor: [purpleDark, purpleLight],
borderColor: "#fff",
borderWidth: 2,
@ -54,11 +86,24 @@ const ComparisonResult = () => {
};
const downtimeData = {
labels: ["Layout 1", "Layout 2"],
labels: [comparedProducts[0]?.productName, comparedProducts[1]?.productName],
datasets: [
{
label: "Downtime (mins)",
data: [17, 12],
data: [comparedProducts[0]?.simulationData.machineIdleTime, comparedProducts[1]?.simulationData.machineIdleTime],
backgroundColor: [purpleDark, purpleLight],
borderColor: "#fff",
borderWidth: 2,
},
],
};
const productionCapacityData = {
labels: [comparedProducts[0]?.productName, comparedProducts[1]?.productName],
datasets: [
{
label: "Production Capacity (units)",
data: [comparedProducts[0]?.simulationData.productionCapacity, comparedProducts[1]?.simulationData.productionCapacity],
backgroundColor: [purpleDark, purpleLight],
borderColor: [purpleDark, purpleLight],
borderWidth: 1,
@ -68,20 +113,21 @@ const ComparisonResult = () => {
],
};
const scrapRateData = {
labels: ["Layout 1", "Layout 2"],
datasets: [
{
label: "Scrap Rate (tons)",
data: [2.7, 1.9],
backgroundColor: [purpleDark, purpleLight],
borderColor: [purpleDark, purpleLight],
borderWidth: 1,
borderRadius: 10,
borderSkipped: false,
},
],
};
const highestProductivityProduct = (comparedProducts[0]?.simulationData?.productionCapacity ?? 0) > (comparedProducts[1]?.simulationData?.productionCapacity ?? 0) ? comparedProducts[0] : comparedProducts[1];
const product1CyclePercentage = (comparedProducts[0]?.simulationData?.machineActiveTime ?? 0) /
((compareProductsData[0]?.simulationData?.machineActiveTime ?? 0) +
(compareProductsData[0]?.simulationData?.machineIdleTime ?? 0)) * 100;
const product2CyclePercentage = ((comparedProducts[1]?.simulationData?.machineActiveTime ?? 0) /
((compareProductsData[1]?.simulationData?.machineActiveTime ?? 0) +
(compareProductsData[1]?.simulationData?.machineIdleTime ?? 0))) * 100;
const product1IdlePercentage = (comparedProducts[0]?.simulationData?.machineIdleTime ?? 0) /
((compareProductsData[0]?.simulationData?.machineActiveTime ?? 0) +
(compareProductsData[0]?.simulationData?.machineIdleTime ?? 0)) * 100;
const product2IdlePercentage = ((comparedProducts[1]?.simulationData?.machineIdleTime ?? 0) /
((compareProductsData[1]?.simulationData?.machineActiveTime ?? 0) +
(compareProductsData[1]?.simulationData?.machineIdleTime ?? 0))) * 100;
return (
<div className="compare-result-container">
@ -92,12 +138,12 @@ const ComparisonResult = () => {
<h4>Throughput (units/hr)</h4>
<div className="layers-wrapper">
<div className="layer-wrapper">
<div className="key">Layout 1</div>
<div className="value">500/ hr</div>
<div className="key">{comparedProducts[0]?.productName}</div>
<div className="value">{comparedProducts[0]?.simulationData.throughputData}/ hr</div>
</div>
<div className="layer-wrapper">
<div className="key">Layout 2</div>
<div className="value">550/ hr</div>
<div className="key">{comparedProducts[1]?.productName}</div>
<div className="value">{comparedProducts[1]?.simulationData.throughputData}/ hr</div>
</div>
<div className="chart">
<Bar data={throughputData} options={options} />
@ -110,17 +156,17 @@ const ComparisonResult = () => {
<div className="cycle-header">Cycle Time</div>
<div className="layers-wrapper">
<div className="layers">
<div className="layer-name">Layout 1</div>
<div className="layer-time">120 Sec</div>
<div className="layer-name">{comparedProducts[0]?.productName}</div>
<div className="layer-time">{compareProductsData[0]?.simulationData.machineActiveTime} Sec</div>
<div className="layer-profit">
<span></span>19.6%
<span></span>{(100 - product1CyclePercentage).toFixed(2)}%
</div>
</div>
<div className="layers">
<div className="layer-name">Layout 2</div>
<div className="layer-time">110 Sec</div>
<div className="layer-name">{comparedProducts[1]?.productName}</div>
<div className="layer-time">{compareProductsData[1]?.simulationData.machineActiveTime} Sec</div>
<div className="layer-profit">
<span></span>1.6%
<span></span>{(100 - product2CyclePercentage).toFixed(2)}%
</div>
</div>
</div>
@ -130,6 +176,31 @@ const ComparisonResult = () => {
</div>
</div>
<div className="cycle-time-container comparisionCard">
<div className="cycle-main">
<div className="cycle-header">Overall Downtime</div>
<div className="layers-wrapper">
<div className="layers">
<div className="layer-name">{comparedProducts[0]?.productName}</div>
<div className="layer-time">{compareProductsData[0]?.simulationData.machineIdleTime} Sec</div>
<div className="layer-profit">
<span></span>{(100 - product1IdlePercentage).toFixed(2)}%
</div>
</div>
<div className="layers">
<div className="layer-name">{comparedProducts[1]?.productName}</div>
<div className="layer-time">{compareProductsData[1]?.simulationData.machineIdleTime} Sec</div>
<div className="layer-profit">
<span></span>{(100 - product2IdlePercentage).toFixed(2)}%
</div>
</div>
</div>
</div>
<div className="chart">
<Pie data={downtimeData} options={options} />
</div>
</div>
{/*
<div className="overallDowntime-container comparisionCard">
<div className="overallDowntime-header">Overall Downtime</div>
<div className="totalDownTime-wrapper">
@ -147,23 +218,23 @@ const ComparisonResult = () => {
<Bar data={downtimeData} options={options} />
</div>
</div>
</div>
</div> */}
<div className="overallScrapRate comparisionCard">
<div className="overallScrapRate-header">Overall Scrap Rate</div>
<div className="overallScrapRate-header">Production Capacity</div>
<div className="overallScrapRate-wrapper">
<div className="overallScrapRate-value">
<div className="overallScrapRate-label">Layout 1</div>
<div className="overallScrapRate-key">Total scrap produced by</div>
<div className="overallScrapRateKey-value">2.7 ton</div>
<div className="overallScrapRate-label">{highestProductivityProduct?.productName}</div>
<div className="overallScrapRate-key">Total product produced</div>
<div className="overallScrapRateKey-value">{highestProductivityProduct?.simulationData.productionCapacity}</div>
</div>
<div className="chart">
<Bar data={scrapRateData} options={options} />
<Bar data={productionCapacityData} options={options} />
</div>
</div>
</div>
<PerformanceResult />
{ comparedProducts.length === 2 &&<PerformanceResult comparedProducts={comparedProducts}/>}
</div>
</div>
);

View File

@ -20,27 +20,32 @@ ChartJS.register(
);
const EnergyUsage = () => {
const data = {
labels: ["Mon", "Tue", "Wed", "Thu", "Fri"],
datasets: [
{
label: "Simulation 1",
data: [400, 600, 450, 1000, 1000],
borderColor: "#6a0dad",
fill: false,
tension: 0.5, // More curved line
pointRadius: 0, // Remove point indicators
},
{
label: "Simulation 2",
data: [300, 500, 700, 950, 1100],
borderColor: "#b19cd9",
fill: false,
tension: 0.5,
pointRadius: 0,
},
],
};
const data = useMemo(() => {
const randomizeData = () =>
Array.from({ length: 5 }, () => Math.floor(Math.random() * (2000 - 300 + 1)) + 300);
return {
labels: ["Mon", "Tue", "Wed", "Thu", "Fri"],
datasets: [
{
label: "Simulation 1",
data: randomizeData(),
borderColor: "#6a0dad",
fill: false,
tension: 0.5, // More curved line
pointRadius: 0, // Remove point indicators
},
{
label: "Simulation 2",
data: randomizeData(),
borderColor: "#b19cd9",
fill: false,
tension: 0.5,
pointRadius: 0,
},
],
};
}, []);
const options = useMemo(
() => ({

View File

@ -5,7 +5,8 @@ import {
TickIcon,
} from "../../../icons/ExportCommonIcons";
const PerformanceResult = () => {
const PerformanceResult = ({ comparedProducts }: any) => {
const ProfitProduct = comparedProducts[0].simulationData.netProfit > comparedProducts[1].simulationData.netProfit ? comparedProducts[0] : comparedProducts[1];
return (
<div className="performanceResult-wrapper comparisionCard">
<div className="header">
@ -26,30 +27,30 @@ const PerformanceResult = () => {
</div>
<div className="metric-value">98%</div>
</div>
<div className="label">Environmental impact</div>
<div className="label">Net Profit</div>
</div>
<div className="metrics-right">
<div className="metric-wrapper">
<div className="metric-label">Waste generation</div>
<div className="metric-label">ROI Percentage</div>
<div className="metric">
<div className="metric-icon">I</div>
<div className="metric-value">0.5%</div>
<div className="metric-icon"></div>
<div className="metric-value">{ProfitProduct.simulationData.roiPercentage.toFixed(2)}</div>
</div>
</div>
<div className="metric-wrapper">
<div className="metric-label">Risk management</div>
<div className="metric-label">Payback Period</div>
<div className="metric">
<div className="metric-icon">I</div>
<div className="metric-value">0.1%</div>
<div className="metric-icon"></div>
<div className="metric-value">{ProfitProduct.simulationData.netProfit}</div>
</div>
</div>
<div className="metric-wrapper">
<div className="metric">
<div className="metric-icon">I</div>
<div className="metric-value">0.5%</div>
<div className="metric-icon"></div>
<div className="metric-value">{parseFloat(ProfitProduct.simulationData.paybackPeriod.toFixed(2))}years</div>
</div>
</div>
</div>

View File

@ -147,7 +147,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
projectId
);
// console.log("response: ", response);
console.log(' zoneAssetId.id,: ', zoneAssetId.id,);
console.log(' zoneAssetId.id,: ', zoneAssetId.id,);
setName(zoneAssetId.id, response.modelName);
}

View File

@ -187,7 +187,7 @@ const SimulationPlayer: React.FC = () => {
<div className="icon">
<HourlySimulationIcon />
</div>
<div className="label">ThroughPut Data</div>
<div className="label">ThroughPut</div>
</div>
<div className="progress-wrapper">
<div
@ -202,7 +202,7 @@ const SimulationPlayer: React.FC = () => {
<div className="icon">
<DailyProductionIcon />
</div>
<div className="label">Daily Production</div>
<div className="label">Production Capacity</div>
</div>
<div className="progress-wrapper">
<div
@ -217,7 +217,7 @@ const SimulationPlayer: React.FC = () => {
<div className="icon">
<MonthlyROI />
</div>
<div className="label">Monthly ROI</div>
<div className="label">ROI</div>
</div>
<div className="progress-wrapper">
<div

View File

@ -1,5 +1,4 @@
import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes";
function hideWalls(
@ -7,21 +6,24 @@ function hideWalls(
scene: THREE.Scene,
camera: THREE.Camera
): void {
////////// Altering the visibility of the Walls when the world direction of the wall is facing the camera //////////
const v = new THREE.Vector3();
const u = new THREE.Vector3();
const wallNormal = new THREE.Vector3();
const cameraToWall = new THREE.Vector3();
const cameraDirection = new THREE.Vector3();
if (visibility === true) {
for (const children of scene.children) {
if (children.name === "Walls" && children.children[0]?.children.length > 0) {
children.children[0].children.forEach((child: any) => {
if (child.children[0]?.userData.WallType === "RoomWall") {
child.children[0].getWorldDirection(v);
camera.getWorldDirection(u);
if (child.children[0].material) {
child.children[0].material.visible = (2 * v.dot(u)) >= -0.5;
const wallMesh = child.children[0];
wallMesh.getWorldDirection(wallNormal);
cameraToWall.copy(wallMesh.position).sub(camera.position).normalize();
camera.getWorldDirection(cameraDirection);
const isFacingCamera = wallNormal.dot(cameraToWall) > 0;
const isInFrontOfCamera = cameraDirection.dot(cameraToWall) > -0.3;
if (wallMesh.material) {
wallMesh.material.visible = isFacingCamera && isInFrontOfCamera;
}
}
});
@ -31,10 +33,8 @@ function hideWalls(
for (const children of scene.children) {
if (children.name === "Walls" && children.children[0]?.children.length > 0) {
children.children[0].children.forEach((child: any) => {
if (child.children[0]?.userData.WallType === "RoomWall") {
if (child.children[0].material) {
child.children[0].material.visible = true;
}
if (child.children[0]?.userData.WallType === "RoomWall" && child.children[0].material) {
child.children[0].material.visible = true;
}
});
}
@ -42,4 +42,4 @@ function hideWalls(
}
}
export default hideWalls;
export default hideWalls;

View File

@ -49,6 +49,7 @@ function CommentsGroup() {
!intersect.object.name.includes("zonePlane") &&
!intersect.object.name.includes("SelectionGroup") &&
!intersect.object.name.includes("selectionAssetGroup") &&
!intersect.object.name.includes("commentHolder") &&
!intersect.object.name.includes("SelectionGroupBoundingBoxLine") &&
!intersect.object.name.includes("SelectionGroupBoundingBox") &&
!intersect.object.name.includes("SelectionGroupBoundingLine") &&
@ -76,11 +77,13 @@ function CommentsGroup() {
!intersect.object.name.includes("zonePlane") &&
!intersect.object.name.includes("SelectionGroup") &&
!intersect.object.name.includes("selectionAssetGroup") &&
!intersect.object.name.includes("commentHolder") &&
!intersect.object.name.includes("SelectionGroupBoundingBoxLine") &&
!intersect.object.name.includes("SelectionGroupBoundingBox") &&
!intersect.object.name.includes("SelectionGroupBoundingLine") &&
intersect.object.type !== "GridHelper"
);
console.log('intersects: ', intersects);
if (intersects.length > 0) {
const position = new Vector3(intersects[0].point.x, Math.max(intersects[0].point.y, 0), intersects[0].point.z);

View File

@ -1,6 +1,5 @@
import { useMemo } from "react";
import { useEffect, useMemo } from "react";
import { Canvas } from "@react-three/fiber";
import { Color } from "three";
import { KeyboardControls } from "@react-three/drei";
import { SceneProvider } from "./sceneContext";
@ -9,6 +8,12 @@ import Visualization from "../visualization/visualization";
import Setup from "./setup/setup";
import Simulation from "../simulation/simulation";
import Collaboration from "../collaboration/collaboration";
import useModuleStore from "../../store/useModuleStore";
import { useParams } from "react-router-dom";
import { getAllProjects } from "../../services/dashboard/getAllProjects";
import { getUserData } from "../../components/Dashboard/functions/getUserData";
import { useLoadingProgress, useSocketStore } from "../../store/builder/store";
import { useAssetsStore } from "../../store/builder/useAssetStore";
export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Comparison Layout' }) {
const map = useMemo(() => [
@ -17,19 +22,76 @@ export default function Scene({ layout }: { readonly layout: 'Main Layout' | 'Co
{ name: "left", keys: ["ArrowLeft", "a", "A"] },
{ name: "right", keys: ["ArrowRight", "d", "D"] },
], []);
const { assets } = useAssetsStore();
const { userId, organization } = getUserData();
const { activeModule } = useModuleStore();
const { projectId } = useParams();
const { projectSocket } = useSocketStore();
const { loadingProgress } = useLoadingProgress();
const handleUpdatingProject = async () => {
if (!projectId) return;
try {
const projects = await getAllProjects(userId, organization);
let projectUuid = projects.Projects.find(
(val: any) => val.projectUuid === projectId || val._id === projectId
);
if (activeModule === "builder" && loadingProgress !== 1) {
const canvas =
document.getElementById("sceneCanvas")?.children[0]?.children[0];
const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL("image/png");
setTimeout(() => {
const updateProjects = {
projectId: projectUuid,
organization,
userId,
projectName: projectUuid.projectName,
thumbnail: screenshotDataUrl,
};
if (projectSocket) {
projectSocket.emit("v1:project:update", updateProjects);
}
}, 8000);
} else {
const canvas =
document.getElementById("sceneCanvas")?.children[0]?.children[0];
const screenshotDataUrl = (canvas as HTMLCanvasElement)?.toDataURL("image/png");
const updateProjects = {
projectId: projectUuid,
organization,
userId,
projectName: projectUuid.projectName,
thumbnail: screenshotDataUrl,
};
// console.log('screenshotDataUrl: ', screenshotDataUrl);
// console.log('updateProjects: ', updateProjects);
if (projectSocket) {
projectSocket.emit("v1:project:update", updateProjects);
}
}
} catch (error) { }
};
useEffect(() => {
handleUpdatingProject()
}, [activeModule, assets, loadingProgress])
return (
<SceneProvider layout={layout}>
<KeyboardControls map={map}>
<Canvas
id="sceneCanvas"
shadows
color="#aaaa"
eventPrefix="client"
gl={{ powerPreference: "high-performance", antialias: true }}
onContextMenu={(e) => {
e.preventDefault();
}}
onCreated={(e) => {
e.scene.background = new Color(0x19191d);
e.scene.background = null;
}}
gl={{ powerPreference: "high-performance", antialias: true, preserveDrawingBuffer: true }}
>
<Setup />
<Collaboration />

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'
import { useInputValues, useProductionCapacityData, useROISummaryData } from '../../../../store/builder/store';
import React, { useEffect } from 'react'
import { CompareProduct, useCompareProductDataStore, useInputValues, useMachineDowntime, useMachineUptime, useProductionCapacityData, useROISummaryData, useThroughPutData } from '../../../../store/builder/store';
import { usePlayButtonStore } from '../../../../store/usePlayButtonStore';
import { useProductContext } from '../../products/productContext';
import { useProductStore } from '../../../../store/simulation/useProductStore';
@ -12,28 +12,31 @@ export default function ROIData() {
const { isPlaying } = usePlayButtonStore();
const { setRoiSummaryData } = useROISummaryData();
const { products, getProductById } = useProductStore();
const [compareProducts, setCompareProducts] = useState<any[]>([]);
const { compareProductsData, setCompareProductsData } = useCompareProductDataStore();
const { machineActiveTime, setMachineActiveTime } = useMachineUptime();
const { machineIdleTime, setMachineIdleTime } = useMachineDowntime();
const { throughputData } = useThroughPutData()
useEffect(() => {
if (!isPlaying) {
setRoiSummaryData({
productName: "",
roiPercentage: 0,
paybackPeriod: 0,
totalCost: 0,
revenueGenerated: 0,
netProfit: 0,
netLoss: 0,
})
return;
}
if (inputValues === undefined) return;
if (isPlaying) return;
setRoiSummaryData({
productName: "",
roiPercentage: 0,
paybackPeriod: 0,
totalCost: 0,
revenueGenerated: 0,
netProfit: 0,
netLoss: 0,
});
}, [isPlaying]);
useEffect(() => {
if (inputValues === undefined || !isPlaying) return;
const electricityCost = parseFloat(inputValues["Electricity cost"]);
const fixedCost = parseFloat(inputValues["Fixed costs"]);
const laborCost = parseFloat(inputValues["Labor Cost"]);
const maintenanceCost = parseFloat(inputValues["Maintenance cost"]); // Remove space typ
const laborCount = parseFloat(inputValues["Labor Count"]);
const maintenanceCost = parseFloat(inputValues["Maintenance cost"]);
const materialCost = parseFloat(inputValues["Material cost"]);
const productionPeriod = parseFloat(inputValues["Production period"]);
const salvageValue = parseFloat(inputValues["Salvage value"]);
@ -46,109 +49,146 @@ export default function ROIData() {
if (!isNaN(electricityCost) && !isNaN(fixedCost) && !isNaN(laborCost) && !isNaN(maintenanceCost) &&
!isNaN(materialCost) && !isNaN(productionPeriod) && !isNaN(salvageValue) && !isNaN(sellingPrice) &&
!isNaN(shiftLength) && !isNaN(shiftsPerDay) && !isNaN(workingDaysPerYear) && productionCapacityData > 0) {
const totalHoursPerYear = shiftLength * shiftsPerDay * workingDaysPerYear;
// Total good units produced per year
const annualProductionUnits = productionCapacityData * totalHoursPerYear;
// Revenue for a year
const annualRevenue = annualProductionUnits * sellingPrice;
// Costs
const totalMaterialCost = annualProductionUnits * materialCost;
const totalLaborCost = laborCost * totalHoursPerYear;
const totalEnergyCost = electricityCost * totalHoursPerYear;
const totalMaintenanceCost = maintenanceCost + fixedCost;
const totalAnnualCost = totalMaterialCost + totalLaborCost + totalEnergyCost + totalMaintenanceCost;
// Annual Profit
const annualProfit = annualRevenue - totalAnnualCost;
// Net Profit over production period
const netProfit = annualProfit * productionPeriod;
// ROI
const roiPercentage = ((netProfit + salvageValue - initialInvestment) / initialInvestment) * 100;
// Payback Period
const paybackPeriod = initialInvestment / (annualProfit || 1); // Avoid division by 0
//
//
//
//
//
//
//
//
// const totalHoursPerYear = shiftLength * shiftsPerDay * workingDaysPerYear;
// const annualProductionUnits = productionCapacityData * totalHoursPerYear;
// const annualRevenue = annualProductionUnits * sellingPrice;
// const totalMaterialCost = annualProductionUnits * materialCost;
// const totalLaborCost = laborCost * totalHoursPerYear;
// const totalEnergyCost = electricityCost * totalHoursPerYear;
// const totalMaintenanceCost = maintenanceCost + fixedCost;
// const totalAnnualCost = totalMaterialCost + totalLaborCost + totalEnergyCost + totalMaintenanceCost;
// const annualProfit = annualRevenue - totalAnnualCost;
// const netProfit = annualProfit * productionPeriod;
// const roiPercentage = ((annualProfit + salvageValue - initialInvestment) / initialInvestment) * 100;
// const paybackPeriod = initialInvestment / (annualProfit || 1); // Avoid division by 0
// setRoiSummaryData({
// productName: selectedProduct.productName,
// roiPercentage: parseFloat((roiPercentage / 100).toFixed(2)),
// paybackPeriod: parseFloat(paybackPeriod.toFixed(2)),
// totalCost: parseFloat(totalAnnualCost.toFixed(2)),
// revenueGenerated: parseFloat(annualRevenue.toFixed(2)),
// netProfit: netProfit > 0 ? parseFloat(netProfit.toFixed(2)) : 0,
// netLoss: netProfit < 0 ? -netProfit : 0
// });
// const productCount = 1000;
// const costPerUnit = totalAnnualCost / annualProductionUnits;
// const costForTargetUnits = productCount * costPerUnit;
// const revenueForTargetUnits = productCount * sellingPrice;
// const profitForTargetUnits = revenueForTargetUnits - costForTargetUnits;
// const netProfitForTarget = profitForTargetUnits > 0 ? profitForTargetUnits : 0;
// const netLossForTarget = profitForTargetUnits < 0 ? -profitForTargetUnits : 0;
// const productData = getProductById(selectedProduct.productUuid);
// const prev = useCompareProductDataStore.getState().compareProductsData;
// const newData: CompareProduct = {
// productUuid: productData?.productUuid ?? '',
// productName: productData?.productName ?? '',
// simulationData: {
// // costPerUnit: parseFloat(costPerUnit.toFixed(2)),
// // workingDaysPerYear: parseFloat(workingDaysPerYear.toFixed(2)),
// // shiftLength: parseFloat(shiftLength.toFixed(2)),
// // shiftsPerDay: parseFloat(shiftsPerDay.toFixed(2)),
// roiPercentage: parseFloat((roiPercentage / 100).toFixed(2)),
// // paybackPeriod: parseFloat(paybackPeriod.toFixed(2)),
// // totalCost: parseFloat(totalAnnualCost.toFixed(2)),
// // revenueGenerated: parseFloat(annualRevenue.toFixed(2)),
// netProfit: netProfit > 0 ? parseFloat(netProfit.toFixed(2)) : 0,
// productionCapacity: parseFloat(productionCapacityData.toFixed(2)),
// // netLoss: netProfit < 0 ? parseFloat((-netProfit).toFixed(2)) : 0,
// machineIdleTime: parseFloat(machineIdleTime.toFixed(2)),
// machineActiveTime: parseFloat(machineActiveTime.toFixed(2)),
// throughputData: throughputData,
// }
// };
// const existingIndex = prev.findIndex((item: CompareProduct) =>
// item.productUuid === productData?.productUuid
// );
// if (existingIndex !== -1) {
// const updated = [...prev];
// updated[existingIndex] = newData;
// setCompareProductsData(updated);
// } else {
// setCompareProductsData([...prev, newData]);
// }
const Annual_units = throughputData * workingDaysPerYear
const Total_units = Annual_units * productionPeriod
const Total_revenue = Total_units * sellingPrice
const Total_variable_cost = Total_units * (materialCost + (laborCost))
const Total_fixed_cost = (maintenanceCost + electricityCost + fixedCost) * workingDaysPerYear * productionPeriod
const Total_cost = Total_variable_cost + Total_fixed_cost
const Net_profit = Total_revenue - Total_cost + (salvageValue * workingDaysPerYear * productionPeriod)
const ROI = (Net_profit / initialInvestment) * 100
const Annual_net_profit = (Annual_units * (sellingPrice - materialCost - laborCost)) - (maintenanceCost + electricityCost + fixedCost) * workingDaysPerYear + (salvageValue * workingDaysPerYear)
const Payback_period_years = initialInvestment / Annual_net_profit;
setRoiSummaryData({
productName: selectedProduct.productName,
roiPercentage: parseFloat((roiPercentage / 100).toFixed(2)), // normalized to 0.x format
paybackPeriod: parseFloat(paybackPeriod.toFixed(2)),
totalCost: parseFloat(totalAnnualCost.toFixed(2)),
revenueGenerated: parseFloat(annualRevenue.toFixed(2)),
netProfit: netProfit > 0 ? parseFloat(netProfit.toFixed(2)) : 0,
netLoss: netProfit < 0 ? -netProfit : 0
roiPercentage: ROI,
paybackPeriod: Payback_period_years,
totalCost: Total_cost,
revenueGenerated: Total_revenue,
netProfit: Net_profit > 0 ? Net_profit : 0,
netLoss: Net_profit < 0 ? -Net_profit : 0
});
const productCount = 1000;
// Cost per unit (based on full annual cost)
const costPerUnit = totalAnnualCost / annualProductionUnits;
const costForTargetUnits = productCount * costPerUnit;
const revenueForTargetUnits = productCount * sellingPrice;
const profitForTargetUnits = revenueForTargetUnits - costForTargetUnits;
const netProfitForTarget = profitForTargetUnits > 0 ? profitForTargetUnits : 0;
const netLossForTarget = profitForTargetUnits < 0 ? -profitForTargetUnits : 0;
//
//
//
//
//
//
const productData = getProductById(selectedProduct.productUuid);
setCompareProducts(prev => {
const newData = {
productUuid: productData?.productUuid,
productName: productData?.productName,
costPerUnit: parseFloat(costPerUnit.toFixed(2)),
workingDaysPerYear: parseFloat(workingDaysPerYear.toFixed(2)),
shiftLength: parseFloat(shiftLength.toFixed(2)),
shiftsPerDay: parseFloat(shiftsPerDay.toFixed(2)),
roiPercentage: parseFloat((roiPercentage / 100).toFixed(2)),
paybackPeriod: parseFloat(paybackPeriod.toFixed(2)),
totalCost: parseFloat(totalAnnualCost.toFixed(2)),
revenueGenerated: parseFloat(annualRevenue.toFixed(2)),
netProfit: netProfit > 0 ? parseFloat(netProfit.toFixed(2)) : 0,
netLoss: netProfit < 0 ? parseFloat((-netProfit).toFixed(2)) : 0
};
const existingIndex = prev.findIndex(
item => item.productUuid === productData?.productUuid
);
if (existingIndex !== -1) {
// Replace the existing item
const updated = [...prev];
updated[existingIndex] = newData;
return updated;
} else {
// Add as new item
return [...prev, newData];
const prev = useCompareProductDataStore.getState().compareProductsData;
const newData: CompareProduct = {
productUuid: productData?.productUuid ?? '',
productName: productData?.productName ?? '',
simulationData: {
// costPerUnit: costPerUnit,
// workingDaysPerYear: workingDaysPerYear,
// shiftLength: shiftLength,
// shiftsPerDay: shiftsPerDay,
roiPercentage: ROI,
paybackPeriod: Payback_period_years,
// paybackPeriod: paybackPeriod,
// totalCost: totalAnnualCost,
// revenueGenerated: annualRevenue,
netProfit: Net_profit > 0 ? Net_profit : 0,
productionCapacity: productionCapacityData,
// netLoss: netProfit < 0 ? (-netProfit) : 0,
machineIdleTime: machineIdleTime,
machineActiveTime: machineActiveTime,
throughputData: throughputData,
}
});
// console.log('compareProducts: ', compareProducts);
};
const existingIndex = prev.findIndex((item: CompareProduct) =>
item.productUuid === productData?.productUuid
);
if (existingIndex !== -1) {
const updated = [...prev];
updated[existingIndex] = newData;
setCompareProductsData(updated);
} else {
setCompareProductsData([...prev, newData]);
}
}
}, [inputValues, productionCapacityData]);
}, [inputValues, productionCapacityData, throughputData, isPlaying]);
return (
<></>
)
useEffect(() => {
}, [compareProductsData])
return null;
}

View File

@ -1,47 +1,36 @@
import React, { useEffect } from 'react'
import { useInputValues, useProductionCapacityData, useThroughPutData } from '../../../../store/builder/store'
import { usePlayButtonStore } from '../../../../store/usePlayButtonStore';
import { useProductContext } from '../../products/productContext';
export default function ProductionCapacityData() {
const { throughputData } = useThroughPutData()
const { productionCapacityData, setProductionCapacityData } = useProductionCapacityData()
const { inputValues } = useInputValues();
const { isPlaying } = usePlayButtonStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
useEffect(() => {
if (!isPlaying) {
setProductionCapacityData(0);
return;
}
if (!inputValues || throughputData === undefined) return;
}, [isPlaying]);
const shiftLength = parseFloat(inputValues["Shift length"]);
const shiftsPerDay = parseFloat(inputValues["Shifts / day"]);
useEffect(() => {
if (!inputValues || throughputData === undefined || !isPlaying) return;
const workingDaysPerYear = parseFloat(inputValues["Working days / year"]);
const yieldRate = parseFloat(inputValues["Yield rate"]);
if (!isNaN(shiftLength) && !isNaN(shiftsPerDay) && !isNaN(workingDaysPerYear) &&
!isNaN(yieldRate) && throughputData >= 0) {
// Total units produced per day before yield
const dailyProduction = throughputData * shiftLength * shiftsPerDay;
if (!isNaN(workingDaysPerYear) && throughputData > 0) {
const Monthly_working_days = workingDaysPerYear / 12;
const Production_capacity_per_month = throughputData * Monthly_working_days;
// Units after applying yield rate
const goodUnitsPerDay = dailyProduction * (yieldRate / 100);
// Annual output
const annualProduction = goodUnitsPerDay * workingDaysPerYear;
// Final production capacity per hour (after yield)
const productionPerHour = throughputData * (yieldRate / 100);
// Set the final capacity (units/hour)
setProductionCapacityData(Number(productionPerHour.toFixed(2)));
setProductionCapacityData(Number(Production_capacity_per_month.toFixed(2)));
}
}, [throughputData, inputValues]);
}, [throughputData, inputValues, isPlaying]);
return (
<></>

View File

@ -1,10 +1,11 @@
import { useEffect } from 'react';
import { useProductStore } from '../../../../store/simulation/useProductStore';
import { determineExecutionMachineSequences } from '../../simulator/functions/determineExecutionMachineSequences';
import { useMachineCount, useMachineUptime, useMaterialCycle, useProcessBar, useThroughPutData } from '../../../../store/builder/store';
import { useInputValues, useMachineCount, useMachineDowntime, useMachineUptime, useMaterialCycle, useProcessBar, useThroughPutData } from '../../../../store/builder/store';
import { usePlayButtonStore } from '../../../../store/usePlayButtonStore';
import { useSceneContext } from '../../../scene/sceneContext';
import { useProductContext } from '../../products/productContext';
import { set } from 'immer/dist/internal';
export default function ThroughPutData() {
const { materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore } = useSceneContext();
@ -19,25 +20,34 @@ export default function ThroughPutData() {
const { materialHistory, materials } = materialStore();
const { machineCount, setMachineCount } = useMachineCount();
const { machineActiveTime, setMachineActiveTime } = useMachineUptime();
const { machineIdleTime, setMachineIdleTime } = useMachineDowntime();
const { materialCycleTime, setMaterialCycleTime } = useMaterialCycle();
const { setProcessBar } = useProcessBar();
const { setThroughputData } = useThroughPutData()
const { isPlaying } = usePlayButtonStore();
const { inputValues } = useInputValues();
// Setting machine count
let totalItems = 0;
let totalActiveTime = 0;
let totalIdleTime = 0
useEffect(() => {
if (!isPlaying) {
totalActiveTime = 0;
totalItems = 0;
totalIdleTime = 0;
setMachineCount(0);
setMachineActiveTime(0);
setMachineIdleTime(0);
setMaterialCycleTime(0);
setProcessBar([]);
setThroughputData(0);
return;
} else {
}
}, [isPlaying])
useEffect(() => {
if (isPlaying) {
let process: any = [];
const fetchProductSequenceData = async () => {
const productData = getProductById(selectedProduct.productUuid);
@ -53,6 +63,9 @@ export default function ThroughPutData() {
process.push({ modelid: arm.modelUuid, modelName: arm.modelName, activeTime: arm?.activeTime })
totalActiveTime += arm.activeTime;
}
if (arm.idleTime > 0) {
totalIdleTime += arm.idleTime;
}
});
} else if (item.type === "vehicle") {
vehicles.filter(vehicle => vehicle.modelUuid === item.modelUuid)
@ -62,6 +75,9 @@ export default function ThroughPutData() {
totalActiveTime += vehicle.activeTime;
}
if (vehicle.idleTime > 0) {
totalIdleTime += vehicle.idleTime;
}
});
} else if (item.type === "machine") {
machines.filter(machine => machine.modelUuid === item.modelUuid)
@ -70,6 +86,9 @@ export default function ThroughPutData() {
process.push({ modelid: machine.modelUuid, modelName: machine.modelName, activeTime: machine?.activeTime })
totalActiveTime += machine.activeTime;
}
if (machine.idleTime > 0) {
totalIdleTime += machine.idleTime;
}
});
} else if (item.type === "transfer") {
conveyors.filter(conveyor => conveyor.modelUuid === item.modelUuid)
@ -83,7 +102,7 @@ export default function ThroughPutData() {
.forEach(storage => {
if (storage.activeTime > 0) {
// totalActiveTime += storage.activeTime;
//
//
}
});
}
@ -92,9 +111,10 @@ export default function ThroughPutData() {
totalItems += sequence.length;
});
setMachineCount(totalItems);
setMachineActiveTime(totalActiveTime);
setMachineIdleTime(totalIdleTime);
let arr = process.map((item: any) => ({
name: item.modelName,
completed: Math.round((item.activeTime / totalActiveTime) * 100)
@ -107,15 +127,15 @@ export default function ThroughPutData() {
fetchProductSequenceData();
}
// if (materialCycleTime <= 0) return
}, [products, selectedProduct, getProductById, setMachineCount, materialCycleTime, armBots, vehicles, machines]);
}, [products, selectedProduct?.productUuid, getProductById, setMachineCount, materialCycleTime, armBots, vehicles, machines]);
useEffect(() => {
let timeoutId: ReturnType<typeof setTimeout>;
async function getMachineActive() {
const productData = getProductById(selectedProduct.productUuid);
let anyArmActive;
let anyVehicleActive;
let anyMachineActive;
if (productData) {
const productSequenceData = await determineExecutionMachineSequences([productData]);
if (productSequenceData?.length > 0) {
@ -160,8 +180,8 @@ export default function ThroughPutData() {
}
const allInactive = !anyArmActive && !anyVehicleActive && !anyMachineActive;
if (allInactive && materials.length === 0 && materialHistory.length > 0) {
if (materials.length >= 0 && materialHistory.length > 0) {
let totalCycleTimeSum = 0;
let cycleCount = 0;
@ -182,21 +202,28 @@ export default function ThroughPutData() {
}
}
if (isPlaying) {
setTimeout(() => {
timeoutId = setTimeout(() => {
getMachineActive();
}, 500)
}, 1500);
}
}, [armBots, materials, materialHistory, machines, vehicles, selectedProduct])
return () => {
if (timeoutId) clearTimeout(timeoutId);
};
}, [armBots, materials, materialHistory, machines, vehicles, selectedProduct?.productUuid])
useEffect(() => {
if (machineActiveTime > 0 && materialCycleTime > 0 && machineCount > 0) {
const utilization = machineActiveTime / 3600; // Active time per hour
const unitsPerMachinePerHour = 3600 / materialCycleTime;
const throughput = unitsPerMachinePerHour * machineCount * utilization;
setThroughputData(Number(throughput.toFixed(2))); // Keep as number
//
const shiftLength = parseFloat(inputValues["Shift length"]);
const shiftsPerDay = parseFloat(inputValues["Shifts / day"]);
const yieldRate = parseFloat(inputValues["Yield rate"]);
if (shiftLength > 0 && materialCycleTime > 0 && machineCount > 0 && isPlaying) {
const Units_per_shift = (shiftLength * 60) / (materialCycleTime / 60);
const Throughput_per_day = Units_per_shift * shiftsPerDay * (yieldRate / 100);
setThroughputData(Number(Throughput_per_day.toFixed(2))); // Keep as number
}
}, [machineActiveTime, materialCycleTime, machineCount]);
}, [materialCycleTime, machineCount, isPlaying, inputValues]);
return (
<>

View File

@ -13,6 +13,7 @@ function Products() {
const { armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, layout } = useSceneContext();
const { products, getProductById, addProduct, setProducts } = useProductStore();
const { selectedProductStore } = useProductContext();
const { setMainProduct } = useMainProduct();
const { selectedProduct, setSelectedProduct } = selectedProductStore();
const { addVehicle, clearvehicles } = vehicleStore();
const { addArmBot, clearArmBots } = armBotStore();
@ -51,11 +52,13 @@ function Products() {
})
if (layout === 'Main Layout') {
setSelectedProduct(id, name);
setMainProduct(id, name);
}
} else {
setProducts(data);
if (layout === 'Main Layout') {
setSelectedProduct(data[0].productUuid, data[0].productName);
setMainProduct(data[0].productUuid, data[0].productName);
}
}
})

View File

@ -48,7 +48,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
const computePath = useCallback(
(start: any, end: any) => {
console.log('end: ', end);
try {
const navMeshQuery = new NavMeshQuery(navMesh);
const { path: segmentPath } = navMeshQuery.computePath(start, end);
@ -57,7 +56,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(end.x) &&
Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(end.z)
) {
console.log('if ', segmentPath);
return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
} else {
console.log("There is no path here...Choose valid path")

View File

@ -43,7 +43,7 @@ const UserAuth: React.FC = () => {
const organization = email.split("@")[1].split(".")[0];
try {
const res = await signInApi(email, password, organization, fingerprint);
console.log('res: ', res);
// console.log('res: ', res);
if (res.message.message === "login successfull") {
setError("");
setOrganization(organization);
@ -56,10 +56,10 @@ const UserAuth: React.FC = () => {
localStorage.setItem("refreshToken", res.message.refreshToken);
try {
console.log('res.message.userId: ', res.message.userId);
console.log('organization: ', organization);
// console.log('res.message.userId: ', res.message.userId);
// console.log('organization: ', organization);
const projects = await recentlyViewed(organization, res.message.userId);
console.log('projects: ', projects);
// console.log('projects: ', projects);
if (res.message.isShare) {
if (Object.values(projects.RecentlyViewed).length > 0) {

View File

@ -3,26 +3,25 @@ export const setFloorItemApi = async (
organization: string,
modelUuid?: string,
modelName?: string,
assetId?: string,
projectId?: string,
assetId?: string,
position?: Object,
rotation?: Object,
isLocked?: boolean,
isVisible?: boolean
isVisible?: boolean,
) => {
try {
const body: any = {
organization,
modelUuid,
modelName,
projectId,
position,
rotation,
assetId,
isLocked,
isVisible,
projectId,
};
const response = await fetch(`${url_Backend_dwinzo}/api/V1/setAsset`, {
method: "POST",
headers: {

View File

@ -26,7 +26,7 @@ export const useSocketStore = create<any>((set: any, get: any) => ({
auth: { token },
}
);
const dashBoardSocket = io(
`http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/dashboard`,
@ -546,6 +546,10 @@ export const useMachineUptime = create<any>((set: any) => ({
machineActiveTime: 0,
setMachineActiveTime: (x: any) => set({ machineActiveTime: x }),
}));
export const useMachineDowntime = create<any>((set: any) => ({
machineIdleTime: 0,
setMachineIdleTime: (x: any) => set({ machineIdleTime: x }),
}));
export const useMaterialCycle = create<any>((set: any) => ({
materialCycleTime: 0,
setMaterialCycleTime: (x: any) => set({ materialCycleTime: x }),
@ -709,3 +713,32 @@ function getInitialViewSceneLabels(): boolean {
const saved = localStorage.getItem('viewSceneLabels');
return saved ? JSON.parse(saved) : false;
}
export interface CompareProduct {
productUuid: string;
productName: string;
simulationData: {
// costPerUnit: number;
// workingDaysPerYear: number;
// shiftLength: number;
// shiftsPerDay: number;
roiPercentage: number;
// paybackPeriod: number;
// totalCost: number;
// revenueGenerated: number;
netProfit: number;
productionCapacity: number;
paybackPeriod: number;
// netLoss: number;
machineIdleTime: number;
machineActiveTime: number;
throughputData: number;
}
}
export const useCompareProductDataStore = create<{
compareProductsData: CompareProduct[];
setCompareProductsData: (x: CompareProduct[]) => void;
}>((set) => ({
compareProductsData: [],
setCompareProductsData: (x) => set({ compareProductsData: x }),
}));

View File

@ -88,6 +88,7 @@ export const createMaterialStore = () => {
clearMaterials: () => {
set((state) => {
state.materials = [];
state.materialHistory = [];
});
},

View File

@ -0,0 +1,18 @@
// store/simulation/useCompareProductDataStore.ts
import { create } from 'zustand';
interface CompareProduct {
productUuid: string;
productName: string;
costPerUnit: number;
workingDaysPerYear: number;
shiftLength: number;
shiftsPerDay: number;
roiPercentage: number;
paybackPeriod: number;
totalCost: number;
revenueGenerated: number;
netProfit: number;
netLoss: number;
}

View File

@ -64,13 +64,14 @@
.production-details {
.production-wrapper {
display: flex;
align-items: center;
// align-items: center;
flex-direction: column;
gap: 6px;
.header {
display: flex;
flex-direction: row;
justify-content: start;
gap: 6px;
}

View File

@ -657,7 +657,7 @@
path {
stroke: var(--text-button-color);
stroke-width: 1.3;
strokeWidth: 1.3;
}
}
}
@ -1021,7 +1021,7 @@
path {
stroke: var(--accent-color);
stroke-width: 1.5px;
strokeWidth: 1.5px;
}
&:hover {

View File

@ -5,6 +5,7 @@ type Preset = {
activeOption: string;
min?: number;
max?: number;
defaultValue?: string | number;
};
};