Merge remote-tracking branch 'origin/feature/layout-comparison-version' into main-dev
This commit is contained in:
@@ -9,7 +9,8 @@ import ComparisonResult from "../../ui/compareVersion/ComparisonResult";
|
||||
import RegularDropDown from "../../ui/inputs/RegularDropDown";
|
||||
import { useSimulationManager } from "../../../store/rough/useSimulationManagerStore";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { validateSimulationDataApi } from "../../../services/simulation/comparison/validateSimulationDataApi";
|
||||
import { calculateSimulationData } from "./functions/calculateSimulationData";
|
||||
type AssetData = {
|
||||
activeTime: number;
|
||||
idleTime: number;
|
||||
@@ -20,23 +21,6 @@ type AssetData = {
|
||||
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;
|
||||
// };
|
||||
simulationData: {
|
||||
roiPercentage: number;
|
||||
netProfit: number;
|
||||
@@ -48,67 +32,14 @@ export interface CompareProduct {
|
||||
simulationTime?: number;
|
||||
simulationCost?: number;
|
||||
efficiencyScore?: number;
|
||||
// totalCost: number;
|
||||
// revenueGenerated: number;
|
||||
// costPerUnit: number;
|
||||
//workingDaysPerYear: number;
|
||||
//shiftLength: number;
|
||||
//shiftsPerDay: number;
|
||||
};
|
||||
}
|
||||
|
||||
const calculateSimulationData = (assets: AssetData[]) => {
|
||||
let totalActiveTime = 0;
|
||||
let totalIdleTime = 0;
|
||||
let throughput = 0;
|
||||
let productionCapacity = 0;
|
||||
let simulationCost = 0;
|
||||
|
||||
// Cost weight per type (example values, adjust as needed)
|
||||
const costWeight: Record<string, number> = {
|
||||
roboticArm: 50,
|
||||
machine: 100,
|
||||
human: 30,
|
||||
transfer: 20,
|
||||
storageUnit: 10,
|
||||
};
|
||||
const energyUsage = assets.filter((a) => a.type === "human").reduce((sum, a) => sum + a.activeTime * 1, 0);
|
||||
|
||||
assets.forEach((asset) => {
|
||||
totalActiveTime += asset.activeTime;
|
||||
totalIdleTime += asset.idleTime;
|
||||
|
||||
if (asset.activeTime > 0) throughput += 1;
|
||||
|
||||
productionCapacity += asset.activeTime;
|
||||
simulationCost += asset.activeTime * (costWeight[asset.type] || 10);
|
||||
});
|
||||
|
||||
const machineActiveTime = assets.filter((a) => a.type === "machine").reduce((acc, a) => acc + a.activeTime, 0);
|
||||
|
||||
const machineIdleTime = assets.filter((a) => a.type === "machine").reduce((acc, a) => acc + a.idleTime, 0);
|
||||
|
||||
const simulationTime = totalActiveTime + totalIdleTime;
|
||||
|
||||
// --- Efficiency Score ---
|
||||
// Weighted formula: lower cost + lower time => higher score
|
||||
// Example formula (normalize to 0–100):
|
||||
const efficiencyScore = Math.max(
|
||||
0,
|
||||
100 -
|
||||
(simulationTime / 1000) * 0.5 - // weight 0.5 for time
|
||||
(simulationCost / 1000) * 0.5 // weight 0.5 for cost
|
||||
);
|
||||
|
||||
return {
|
||||
throughputData: throughput,
|
||||
machineActiveTime,
|
||||
machineIdleTime,
|
||||
productionCapacity,
|
||||
netProfit: 0, // placeholder
|
||||
roiPercentage: 0, // placeholder
|
||||
paybackPeriod: 0, // placeholder
|
||||
simulationTime,
|
||||
simulationCost,
|
||||
efficiencyScore,
|
||||
energyUsage,
|
||||
};
|
||||
};
|
||||
|
||||
export const createCompareProduct = (productUuid: string, productName: string, assets: AssetData[]): CompareProduct => ({
|
||||
productUuid,
|
||||
productName,
|
||||
@@ -124,14 +55,34 @@ function ComparisonScene() {
|
||||
const { activeModule } = useModuleStore();
|
||||
const { mainScene, comparisonScene, setComparisonState } = useSimulationState();
|
||||
const { loadingProgress } = useLoadingProgress();
|
||||
const { simulationRecords } = useSimulationManager();
|
||||
const { projectId } = useParams();
|
||||
const { setCompareProductsData } = useCompareProductDataStore();
|
||||
const [shouldShowComparisonResult, setShouldShowComparisonResult] = useState(false);
|
||||
const { addSimulationRecord } = useSimulationManager();
|
||||
|
||||
useEffect(() => {});
|
||||
|
||||
const handleSelectVersion = (option: string) => {
|
||||
const version = versionHistory.find((version) => version.versionName === option);
|
||||
if (version) {
|
||||
setSelectedVersion(version);
|
||||
const singleData = {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || "",
|
||||
productUuid: selectedProduct?.productUuid || "",
|
||||
};
|
||||
validateSimulationDataApi(singleData).then((getData) => {
|
||||
echo.log(getData.message);
|
||||
const getSimulate = getData?.data?.existingSimulatedData;
|
||||
if (!getSimulate) return;
|
||||
if (!selectedVersion?.versionId || !projectId || getSimulate === undefined || !selectedProduct.productUuid) {
|
||||
echo.warn("No prebacked Data found");
|
||||
alert("Please run the simulation before comparing.");
|
||||
return;
|
||||
}
|
||||
addSimulationRecord(projectId, selectedVersion?.versionId || "", selectedProduct.productUuid || "", getSimulate.data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -144,37 +95,33 @@ function ComparisonScene() {
|
||||
versionUuid: selectedVersion.versionId,
|
||||
versionName: selectedVersion.versionName,
|
||||
};
|
||||
const singleData = {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion.versionId,
|
||||
productUuid: product.productUuid,
|
||||
};
|
||||
validateSimulationDataApi(singleData).then((getData) => {
|
||||
echo.warn(getData.message);
|
||||
const getSimulate = getData?.data?.existingSimulatedData;
|
||||
if (!getSimulate) return;
|
||||
addSimulationRecord(projectId, selectedVersion?.versionId || "", product.productUuid || "", getSimulate.data);
|
||||
});
|
||||
setComparisonState(data);
|
||||
}
|
||||
};
|
||||
|
||||
// useEffect(() => {
|
||||
// if (mainProduct && comparisonProduct && compareProductsData.length > 1) {
|
||||
// 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]);
|
||||
|
||||
useEffect(() => {
|
||||
// const selectedProductData = getProductById(selectedProduct.productUuid);
|
||||
if (mainScene && comparisonScene && selectedVersion) {
|
||||
const product1 = useSimulationManager.getState().getProductById(projectId, selectedVersion?.versionId, mainScene.product.productUuid);
|
||||
const mainVersion = useSimulationManager.getState().getProductById(projectId, mainScene.version.versionUuid, mainScene.product.productUuid);
|
||||
|
||||
const product2 = useSimulationManager.getState().getProductById(projectId, selectedVersion?.versionId, comparisonScene.product.productUuid);
|
||||
const compareVersion = useSimulationManager.getState().getProductById(projectId, comparisonScene.version.versionUuid, comparisonScene.product.productUuid);
|
||||
|
||||
const compareProduct1 = createCompareProduct(product1?.productId ?? "", mainScene.product.productName, product1?.simulateData || []);
|
||||
const compareProduct2 = createCompareProduct(product2?.productId ?? "", comparisonScene.product.productName, product2?.simulateData || []);
|
||||
const mainVompareversion = createCompareProduct(mainVersion?.productId ?? "", mainScene.product.productName, mainVersion?.simulateData || []);
|
||||
const compareProduct2 = createCompareProduct(compareVersion?.productId ?? "", comparisonScene.product.productName, compareVersion?.simulateData || []);
|
||||
|
||||
const comparedArray = [compareProduct1, compareProduct2];
|
||||
const comparedArray = [mainVompareversion, compareProduct2];
|
||||
|
||||
if (product1 === undefined || product2 === undefined) {
|
||||
if (mainVersion === undefined || compareVersion === undefined) {
|
||||
setShouldShowComparisonResult(false);
|
||||
} else if (comparedArray.length === 2) {
|
||||
setCompareProductsData(comparedArray);
|
||||
@@ -183,7 +130,7 @@ function ComparisonScene() {
|
||||
} else {
|
||||
setShouldShowComparisonResult(false);
|
||||
}
|
||||
}, [mainScene, comparisonScene, selectedVersion, projectId, setCompareProductsData]);
|
||||
}, [mainScene, comparisonScene, selectedVersion, projectId, setCompareProductsData, simulationRecords]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -207,7 +154,6 @@ function ComparisonScene() {
|
||||
</div>
|
||||
)}
|
||||
<CompareLayOut />
|
||||
|
||||
{shouldShowComparisonResult && !loadingProgress && <ComparisonResult />}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
type AssetData = {
|
||||
activeTime: number;
|
||||
idleTime: number;
|
||||
type: string;
|
||||
assetId: string;
|
||||
};
|
||||
|
||||
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;
|
||||
// };
|
||||
simulationData: {
|
||||
roiPercentage: number;
|
||||
netProfit: number;
|
||||
productionCapacity: number;
|
||||
paybackPeriod: number;
|
||||
machineIdleTime: number;
|
||||
machineActiveTime: number;
|
||||
throughputData: number;
|
||||
simulationTime?: number;
|
||||
simulationCost?: number;
|
||||
efficiencyScore?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export const calculateSimulationData = (assets: AssetData[]) => {
|
||||
let totalActiveTime = 0;
|
||||
let totalIdleTime = 0;
|
||||
let throughput = 0;
|
||||
let productionCapacity = 0;
|
||||
let simulationCost = 0;
|
||||
|
||||
// Cost weight per type (example values, adjust as needed)
|
||||
const costWeight: Record<string, number> = {
|
||||
roboticArm: 50,
|
||||
machine: 100,
|
||||
human: 30,
|
||||
transfer: 20,
|
||||
storageUnit: 10,
|
||||
};
|
||||
|
||||
const energyUsage = assets.filter((a) => a.type === "human").reduce((sum, a) => sum + a.activeTime * 1, 0);
|
||||
|
||||
assets.forEach((asset) => {
|
||||
totalActiveTime += asset.activeTime;
|
||||
totalIdleTime += asset.idleTime;
|
||||
|
||||
if (asset.activeTime > 0) throughput += 1;
|
||||
|
||||
productionCapacity += asset.activeTime;
|
||||
simulationCost += asset.activeTime * (costWeight[asset.type] || 10);
|
||||
});
|
||||
|
||||
const machineActiveTime = assets.filter((a) => a.type === "human").reduce((acc, a) => acc + a.activeTime, 0);
|
||||
|
||||
const machineIdleTime = assets.filter((a) => a.type === "machine").reduce((acc, a) => acc + a.idleTime, 0);
|
||||
|
||||
const simulationTime = totalActiveTime + totalIdleTime;
|
||||
|
||||
// --- Efficiency Score ---
|
||||
// Weighted formula: lower cost + lower time => higher score
|
||||
// Example formula (normalize to 0–100):
|
||||
const efficiencyScore = Math.max(
|
||||
0,
|
||||
100 -
|
||||
(simulationTime / 1000) * 0.5 - // weight 0.5 for time
|
||||
(simulationCost / 1000) * 0.5 // weight 0.5 for cost
|
||||
);
|
||||
|
||||
return {
|
||||
throughputData: throughput,
|
||||
machineActiveTime,
|
||||
machineIdleTime,
|
||||
productionCapacity,
|
||||
netProfit: 0, // placeholder
|
||||
roiPercentage: 0, // placeholder
|
||||
paybackPeriod: 0, // placeholder
|
||||
simulationTime,
|
||||
simulationCost,
|
||||
efficiencyScore,
|
||||
energyUsage,
|
||||
};
|
||||
};
|
||||
@@ -1,135 +0,0 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
interface SimulationData {
|
||||
key: string;
|
||||
data?: object | any;
|
||||
}
|
||||
interface SimulationUsageRecord {
|
||||
activeTime: number;
|
||||
isActive: boolean;
|
||||
idleTime: number;
|
||||
type: "roboticArm" | "vehicle" | "transfer" | "storageUnit" | "crane" | "human" | "machine";
|
||||
assetId: string;
|
||||
}
|
||||
|
||||
// Product → holds multiple usage records
|
||||
interface ProductSimulation {
|
||||
productId: string;
|
||||
simulateData: SimulationUsageRecord[];
|
||||
}
|
||||
|
||||
// Version → holds multiple products
|
||||
interface VersionSimulation {
|
||||
versionId: string;
|
||||
products: ProductSimulation[];
|
||||
}
|
||||
|
||||
// Project → holds multiple versions
|
||||
interface ProjectSimulation {
|
||||
projectId: string | undefined;
|
||||
versions: VersionSimulation[];
|
||||
}
|
||||
|
||||
export const saveSimulationData = async (data: any) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/SimulatedUpsert`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "",
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
if (!response.ok) {
|
||||
console.error("Failed to add project");
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.log(error.message);
|
||||
} else {
|
||||
console.log("An unknown error occurred");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const updateSimulateData = async (data: any) => {
|
||||
// console.log("data: update", data);
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/ValidateSimulated`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "",
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
if (!response.ok) {
|
||||
console.error("Failed to update ");
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.log(error.message);
|
||||
} else {
|
||||
console.log("An unknown error occurred");
|
||||
}
|
||||
}
|
||||
};
|
||||
export const getSimulationData = async (
|
||||
projectId: string,
|
||||
versionId: string,
|
||||
productUuid: string
|
||||
) => {
|
||||
// console.log("called");
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/V1/SimulatedDatas/${projectId}/${versionId}?productUuid=${productUuid}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "",
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
console.log("response: ", response);
|
||||
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch simulateData");
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
console.error("Failed to get simulation data");
|
||||
console.log(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
// export const clearSimulationData = ({ key, data }: SimulationData) => {};
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { AddIcon, ArrowIcon, RemoveIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import RenameInput from "../../../ui/inputs/RenameInput";
|
||||
import { handleResize } from "../../../../functions/handleResizePannel";
|
||||
import { useSimulationState, useSelectedAsset } from "../../../../store/simulation/useSimulationStore";
|
||||
@@ -13,11 +13,11 @@ import { deleteProductApi } from "../../../../services/simulation/products/delet
|
||||
import { renameProductApi } from "../../../../services/simulation/products/renameProductApi";
|
||||
import { determineExecutionMachineSequences } from "../../../../modules/simulation/simulator/functions/determineExecutionMachineSequences";
|
||||
import ComparePopUp from "../../../ui/compareVersion/Compare";
|
||||
import { useCompareStore, useIsComparing, useSimulateId } from "../../../../store/builder/store";
|
||||
import { useCompareStore, useIsComparing } from "../../../../store/builder/store";
|
||||
import { useToggleStore } from "../../../../store/ui/useUIToggleStore";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useSceneContext } from "../../../../modules/scene/sceneContext";
|
||||
import { updateSimulateData } from "../../scenes/functions/simulationStorage";
|
||||
import { validateSimulationDataApi } from "../../../../services/simulation/comparison/validateSimulationDataApi";
|
||||
|
||||
interface Event {
|
||||
modelName: string;
|
||||
@@ -38,20 +38,28 @@ const Simulations: React.FC = () => {
|
||||
const { selectedVersion } = versionStore();
|
||||
const { comparePopUp, setComparePopUp } = useCompareStore();
|
||||
const { setIsComparing } = useIsComparing();
|
||||
const { simulateId } = useSimulateId();
|
||||
|
||||
const handleSaveVersion = async () => {
|
||||
setIsComparing(true);
|
||||
const handleSaveVersion = () => {
|
||||
// setIsComparing(true);
|
||||
setComparePopUp(false);
|
||||
setToggleUI(false, false);
|
||||
const singleData = {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || "",
|
||||
productUuid: selectedProduct?.productUuid || "",
|
||||
simulatedId: simulateId,
|
||||
};
|
||||
const getData = await updateSimulateData(singleData);
|
||||
echo.log(getData.message);
|
||||
validateSimulationDataApi(singleData).then((getData) => {
|
||||
echo.log(getData.message);
|
||||
const getSimulate = getData?.data?.existingSimulatedData;
|
||||
if (!selectedVersion?.versionId || !projectId || getSimulate === undefined || !selectedProduct.productUuid) {
|
||||
echo.warn("No prebacked Data found");
|
||||
alert("Please run the simulation before comparing.");
|
||||
return;
|
||||
} else if (getSimulate) {
|
||||
setToggleUI(true, true);
|
||||
setIsComparing(true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleAddProduct = () => {
|
||||
@@ -156,25 +164,15 @@ const Simulations: React.FC = () => {
|
||||
setProcesses(processes);
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedProduct.productUuid, products]);
|
||||
|
||||
useEffect(() => {
|
||||
if (comparePopUp || selectedProduct.productUuid) {
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [comparePopUp]);
|
||||
|
||||
const getSimulate = async () => {
|
||||
// const singleData = {
|
||||
// projectId: projectId,
|
||||
// versionId: selectedVersion?.versionId || "",
|
||||
// productUuid: selectedProduct?.productUuid || "",
|
||||
// };
|
||||
// console.log("singleData: ", singleData);
|
||||
// const getData = await getSimulationData(singleData);
|
||||
// console.log("getData: ", getData);
|
||||
setComparePopUp(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="simulations-container">
|
||||
<div className="header">Simulations</div>
|
||||
@@ -247,13 +245,7 @@ const Simulations: React.FC = () => {
|
||||
<div className="content">
|
||||
Click '<span>Compare</span>' to review and analyze the layout differences between them.
|
||||
</div>
|
||||
<button
|
||||
className="input"
|
||||
onClick={() => {
|
||||
// setComparePopUp(true);
|
||||
getSimulate();
|
||||
}}
|
||||
>
|
||||
<button className="input" onClick={() => setComparePopUp(true)}>
|
||||
<input type="button" value={"Compare"} className="submit" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,7 @@ import Scene from "../../../modules/scene/scene";
|
||||
import useRestStates from "../../../hooks/useResetStates";
|
||||
|
||||
import { getVersionHistoryApi } from "../../../services/factoryBuilder/versionControl/getVersionHistoryApi";
|
||||
import { validateSimulationDataApi } from "../../../services/simulation/comparison/validateSimulationDataApi";
|
||||
|
||||
const CompareLayOut = () => {
|
||||
const { clearComparisonState, comparisonScene, setComparisonState } = useSimulationState();
|
||||
@@ -29,7 +30,6 @@ const CompareLayOut = () => {
|
||||
const { setIsPlaying } = usePlayButtonStore();
|
||||
const { projectId } = useParams();
|
||||
const { resetStates } = useRestStates();
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
resetStates();
|
||||
@@ -55,9 +55,7 @@ const CompareLayOut = () => {
|
||||
});
|
||||
setVersions(versions);
|
||||
})
|
||||
.catch(() => {
|
||||
console.error("Error fetching version history");
|
||||
});
|
||||
.catch(() => {});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [projectId]);
|
||||
|
||||
@@ -65,6 +63,7 @@ const CompareLayOut = () => {
|
||||
if (!comparisonScene) {
|
||||
clearSelectedVersion();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [comparisonScene]);
|
||||
|
||||
OuterClick({
|
||||
@@ -161,13 +160,26 @@ const CompareLayOut = () => {
|
||||
};
|
||||
setComparisonState(comparisonData);
|
||||
setLoadingProgress(1);
|
||||
const singleData = {
|
||||
projectId: projectId,
|
||||
versionId: version.versionId,
|
||||
productUuid: data[0].productUuid,
|
||||
};
|
||||
|
||||
validateSimulationDataApi(singleData).then((getData) => {
|
||||
echo.log(getData.message);
|
||||
const getSimulate = getData?.data?.existingSimulatedData;
|
||||
|
||||
if (!getSimulate) return;
|
||||
// addSimulationRecord(projectId, version?.versionId || "", data[0].productUuid || "", getSimulate.data);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`compareLayOut-wrapper ${width === "0px" ? "closed" : ""}`} ref={wrapperRef} style={{ width }}>
|
||||
{loadingProgress == 0 && selectedVersion?.versionId && (
|
||||
{loadingProgress === 0 && selectedVersion?.versionId && (
|
||||
<button title="resize-canvas" id="compare-resize-slider-btn" className="resizer" onMouseDown={handleStartResizing}>
|
||||
<ResizerIcon />
|
||||
</button>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from "react";
|
||||
import PerformanceResult from "./result-card/PerformanceResult";
|
||||
import { Bar, Pie } from "react-chartjs-2";
|
||||
import { useCompareProductDataStore } from "../../../store/builder/store";
|
||||
import { useSimulationState } from "../../../store/simulation/useSimulationStore";
|
||||
import { useSimulationState } from "../../../store/simulation/useSimulationStore";
|
||||
|
||||
export interface CompareProduct {
|
||||
productUuid: string;
|
||||
@@ -28,14 +28,7 @@ const ComparisonResult = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (compareProductsData.length > 0 && comparisonScene && mainScene) {
|
||||
const mainProductData = compareProductsData.find((product) => product.productUuid === mainScene.product.productUuid);
|
||||
const comparisonProductData = compareProductsData.find((product) => product.productUuid === comparisonScene.product.productUuid);
|
||||
|
||||
if (mainProductData && comparisonProductData) {
|
||||
setComparedProducts([mainProductData, comparisonProductData]);
|
||||
} else {
|
||||
setComparedProducts([]);
|
||||
}
|
||||
setComparedProducts([compareProductsData[0], compareProductsData[1]]);
|
||||
} else {
|
||||
setComparedProducts([]);
|
||||
}
|
||||
@@ -122,18 +115,87 @@ const ComparisonResult = () => {
|
||||
],
|
||||
};
|
||||
|
||||
const highestProductivityProduct = (comparedProducts[0]?.simulationData?.productionCapacity ?? 0) > (comparedProducts[1]?.simulationData?.productionCapacity ?? 0) ? comparedProducts[0] : comparedProducts[1];
|
||||
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 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;
|
||||
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">
|
||||
<div className="header">Performance Comparison</div>
|
||||
<div className="compare-result-wrapper">
|
||||
<div className="cycle-time-container comparisionCard">
|
||||
<div className="cycle-main">
|
||||
<h4 className="cycle-header">Human Active Time</h4>
|
||||
{/* <h4 className="cycle-header">Cycle Time</h4> */}
|
||||
<div className="layers-wrapper">
|
||||
<div className="layers">
|
||||
<div className="layer-name">{comparedProducts[0]?.productName}</div>
|
||||
<div className="layer-time">{compareProductsData[0]?.simulationData.machineActiveTime} Sec</div>
|
||||
<div className={`layer-change ${product1CyclePercentage >= 50 ? "profit" : "loss"}`}>
|
||||
<span>{product1CyclePercentage >= 50 ? "↑" : "↓"}</span>
|
||||
{(100 - product1CyclePercentage).toFixed(2)}%
|
||||
</div>
|
||||
</div>
|
||||
<div className="layers">
|
||||
<div className="layer-name">{comparedProducts[1]?.productName}</div>
|
||||
<div className="layer-time">{compareProductsData[1]?.simulationData.machineActiveTime} Sec</div>
|
||||
<div className={`layer-change ${product2CyclePercentage >= 50 ? "profit" : "loss"}`}>
|
||||
<span>{product2CyclePercentage >= 50 ? "↑" : "↓"}</span>
|
||||
{(100 - product2CyclePercentage).toFixed(2)}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="chart">
|
||||
<Pie data={cycleTimePieData} options={options} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* <EnergyUsage comparedProducts={comparedProducts} /> */}
|
||||
<div className="cycle-time-container comparisionCard">
|
||||
<div className="cycle-main">
|
||||
<div className="cycle-header">Human Idle Time</div>
|
||||
{/* <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 ${product1IdlePercentage >= 50 ? "profit" : "loss"}`}>
|
||||
<span>{product1IdlePercentage >= 50 ? "↑" : "↓"}</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 ${product2IdlePercentage >= 50 ? "profit" : "loss"}`}>
|
||||
<span>{product2IdlePercentage >= 50 ? "↑" : "↓"}</span>
|
||||
{(100 - product2IdlePercentage).toFixed(2)}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="chart">
|
||||
<Pie data={downtimeData} options={options} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="throughPutCard-container comparisionCard">
|
||||
<h4>Throughput (units/hr)</h4>
|
||||
<div className="layers-wrapper">
|
||||
@@ -151,78 +213,24 @@ const ComparisonResult = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="cycle-time-container comparisionCard">
|
||||
<div className="cycle-main">
|
||||
<h4 className="cycle-header">Cycle Time</h4>
|
||||
<div className="layers-wrapper">
|
||||
<div className="layers">
|
||||
<div className="layer-name">{comparedProducts[0]?.productName}</div>
|
||||
<div className="layer-time">{compareProductsData[0]?.simulationData.machineActiveTime} Sec</div>
|
||||
<div className="layer-profit">
|
||||
<span>↑</span>
|
||||
{(100 - product1CyclePercentage).toFixed(2)}%
|
||||
</div>
|
||||
<div className="overallDowntime-container comparisionCard">
|
||||
<h4 className="overallDowntime-header">Overall Downtime</h4>
|
||||
<div className="totalDownTime-wrapper">
|
||||
<div className="totalDownTime">
|
||||
<div className="totalDownTime-right">
|
||||
<div className="totalDownTime-label">Total down time</div>
|
||||
<div className="totalDownTime-subLabel">(Simulation 1)</div>
|
||||
</div>
|
||||
<div className="layers">
|
||||
<div className="layer-name">{comparedProducts[1]?.productName}</div>
|
||||
<div className="layer-time">{compareProductsData[1]?.simulationData.machineActiveTime} Sec</div>
|
||||
<div className="layer-profit">
|
||||
<span>↑</span>
|
||||
{(100 - product2CyclePercentage).toFixed(2)}%
|
||||
</div>
|
||||
<div className="totalDownTime-left">
|
||||
<div className="value">17</div>
|
||||
<div className="key">mins</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="chart">
|
||||
<Pie data={cycleTimePieData} options={options} />
|
||||
</div>
|
||||
</div>
|
||||
{/* <EnergyUsage comparedProducts={comparedProducts} /> */}
|
||||
<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 className="chart">
|
||||
<Bar data={downtimeData} options={options} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="chart">
|
||||
<Pie data={downtimeData} options={options} />
|
||||
</div>
|
||||
</div>
|
||||
{/*
|
||||
<div className="overallDowntime-container comparisionCard">
|
||||
<h4 className="overallDowntime-header">Overall Downtime</h4>
|
||||
<div className="totalDownTime-wrapper">
|
||||
<div className="totalDownTime">
|
||||
<div className="totalDownTime-right">
|
||||
<div className="totalDownTime-label">Total down time</div>
|
||||
<div className="totalDownTime-subLabel">(Simulation 1)</div>
|
||||
</div>
|
||||
<div className="totalDownTime-left">
|
||||
<div className="value">17</div>
|
||||
<div className="key">mins</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="chart">
|
||||
<Bar data={downtimeData} options={options} />
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div className="overallScrapRate comparisionCard">
|
||||
<h4 className="overallScrapRate-header">Production Capacity</h4>
|
||||
|
||||
@@ -4,114 +4,34 @@ import { Chart as ChartJS, LineElement, PointElement, CategoryScale, LinearScale
|
||||
|
||||
ChartJS.register(LineElement, PointElement, CategoryScale, LinearScale, Tooltip, Legend);
|
||||
|
||||
// const EnergyUsage = ({comparedProducts}:any) => {
|
||||
// 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(
|
||||
// () => ({
|
||||
// responsive: true,
|
||||
// maintainAspectRatio: false,
|
||||
// plugins: {
|
||||
// title: {
|
||||
// display: true,
|
||||
// },
|
||||
// legend: {
|
||||
// display: false,
|
||||
// },
|
||||
// },
|
||||
// scales: {
|
||||
// x: {
|
||||
// display: false, // Hide x-axis
|
||||
// grid: {
|
||||
// display: false,
|
||||
// },
|
||||
// },
|
||||
// y: {
|
||||
// display: false, // Hide y-axis
|
||||
// grid: {
|
||||
// display: false,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }),
|
||||
// []
|
||||
// );
|
||||
|
||||
// return (
|
||||
// <div className="comparisionCard energy-usage">
|
||||
// <div className="energy-usage-wrapper">
|
||||
// <h4>Energy Usage</h4>
|
||||
// <p className="value">
|
||||
// 2500 <span>kWh</span>
|
||||
// </p>
|
||||
// </div>
|
||||
|
||||
// <div className="simulation-details">
|
||||
// <div className="simulation-wrapper sim-1">
|
||||
// <div className="icon"></div>
|
||||
// <div className="simulation-details-wrapper">
|
||||
// <div className="label">{comparedProducts[0]?.productName}</div>
|
||||
// <div className="value">98%</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className="simulation-wrapper sim-2">
|
||||
// <div className="icon"></div>
|
||||
// <div className="simulation-details-wrapper">
|
||||
// <div className="label">{comparedProducts[1]?.productName}</div>
|
||||
// <div className="value">97%</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
|
||||
// <div className="chart">
|
||||
// <Line data={data} options={options} />
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default EnergyUsage;
|
||||
|
||||
const EnergyUsage = ({ comparedProducts }: any) => {
|
||||
const data = useMemo(() => {
|
||||
const randomizeData = () => Array.from({ length: 5 }, () => Math.floor(Math.random() * (2000 - 300 + 1)) + 300);
|
||||
console.log("randomizeData: ", randomizeData());
|
||||
|
||||
return {
|
||||
labels: ["Mon", "Tue", "Wed", "Thu", "Fri"],
|
||||
datasets: comparedProducts.map((product: any, idx: number) => ({
|
||||
label: product.productName,
|
||||
// use actual energyUsage instead of random data
|
||||
data: Array(5).fill(product.simulationData.energyUsage),
|
||||
borderColor: idx === 0 ? "#6a0dad" : "#b19cd9",
|
||||
fill: false,
|
||||
tension: 0.5,
|
||||
pointRadius: 0,
|
||||
})),
|
||||
datasets: [
|
||||
{
|
||||
label: "Simulation 1",
|
||||
data: randomizeData(),
|
||||
// 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,
|
||||
},
|
||||
],
|
||||
};
|
||||
}, [comparedProducts]);
|
||||
}, []);
|
||||
|
||||
const options = useMemo(
|
||||
() => ({
|
||||
@@ -127,13 +47,13 @@ const EnergyUsage = ({ comparedProducts }: any) => {
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: false,
|
||||
display: false, // Hide x-axis
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
display: false,
|
||||
display: false, // Hide y-axis
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
@@ -148,20 +68,25 @@ const EnergyUsage = ({ comparedProducts }: any) => {
|
||||
<div className="energy-usage-wrapper">
|
||||
<h4>Energy Usage</h4>
|
||||
<p className="value">
|
||||
{comparedProducts.reduce((acc: number, p: any) => acc + (p.simulationData.energyUsage || 0), 0)} <span>kWh</span>
|
||||
2500 <span>kWh</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="simulation-details">
|
||||
{comparedProducts.map((product: any, idx: number) => (
|
||||
<div key={product.productUuid} className={`simulation-wrapper sim-${idx + 1}`}>
|
||||
<div className="icon"></div>
|
||||
<div className="simulation-details-wrapper">
|
||||
<div className="label">{product.productName}</div>
|
||||
<div className="value">{product.simulationData.energyUsage} kWh</div>
|
||||
</div>
|
||||
<div className="simulation-wrapper sim-1">
|
||||
<div className="icon"></div>
|
||||
<div className="simulation-details-wrapper">
|
||||
<div className="label">{comparedProducts[0]?.productName}</div>
|
||||
<div className="value">98%</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="simulation-wrapper sim-2">
|
||||
<div className="icon"></div>
|
||||
<div className="simulation-details-wrapper">
|
||||
<div className="label">{comparedProducts[1]?.productName}</div>
|
||||
<div className="value">97%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="chart">
|
||||
@@ -172,3 +97,79 @@ const EnergyUsage = ({ comparedProducts }: any) => {
|
||||
};
|
||||
|
||||
export default EnergyUsage;
|
||||
|
||||
// const EnergyUsage = ({ comparedProducts }: any) => {
|
||||
// const data = useMemo(() => {
|
||||
// return {
|
||||
// labels: ["Mon", "Tue", "Wed", "Thu", "Fri"],
|
||||
// datasets: comparedProducts.map((product: any, idx: number) => ({
|
||||
// label: product.productName,
|
||||
// // use actual energyUsage instead of random data
|
||||
// data: Array(5).fill(product.simulationData.energyUsage),
|
||||
// borderColor: idx === 0 ? "#6a0dad" : "#b19cd9",
|
||||
// fill: false,
|
||||
// tension: 0.5,
|
||||
// pointRadius: 0,
|
||||
// })),
|
||||
// };
|
||||
// }, [comparedProducts]);
|
||||
|
||||
// const options = useMemo(
|
||||
// () => ({
|
||||
// responsive: true,
|
||||
// maintainAspectRatio: false,
|
||||
// plugins: {
|
||||
// title: {
|
||||
// display: true,
|
||||
// },
|
||||
// legend: {
|
||||
// display: false,
|
||||
// },
|
||||
// },
|
||||
// scales: {
|
||||
// x: {
|
||||
// display: false,
|
||||
// grid: {
|
||||
// display: false,
|
||||
// },
|
||||
// },
|
||||
// y: {
|
||||
// display: false,
|
||||
// grid: {
|
||||
// display: false,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }),
|
||||
// []
|
||||
// );
|
||||
|
||||
// return (
|
||||
// <div className="comparisionCard energy-usage">
|
||||
// <div className="energy-usage-wrapper">
|
||||
// <h4>Energy Usage</h4>
|
||||
// <p className="value">
|
||||
// {comparedProducts.reduce((acc: number, p: any) => acc + (p.simulationData.energyUsage || 0), 0)} <span>kWh</span>
|
||||
// </p>
|
||||
// </div>
|
||||
|
||||
// <div className="simulation-details">
|
||||
// {comparedProducts.map((product: any, idx: number) => (
|
||||
// <div key={product.productUuid} className={`simulation-wrapper sim-${idx + 1}`}>
|
||||
// <div className="icon"></div>
|
||||
// <div className="simulation-details-wrapper">
|
||||
// <div className="label">{product.productName}</div>
|
||||
// <div className="value">{product.simulationData.energyUsage} kWh</div>
|
||||
// </div>
|
||||
// </div>
|
||||
// ))}
|
||||
// </div>
|
||||
|
||||
// <div className="chart">
|
||||
// <Line data={data} options={options} />
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
// export default EnergyUsage;
|
||||
|
||||
@@ -42,8 +42,6 @@ const SimulationPlayer: React.FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (materialData.length === 0) return;
|
||||
// console.log("materialData: ", materialData);
|
||||
// saveSimulationData({ key: selectedProduct.productUuid, data: materialData });
|
||||
}, [materialData]);
|
||||
|
||||
// Button functions
|
||||
@@ -333,7 +331,12 @@ const SimulationPlayer: React.FC = () => {
|
||||
<div className="marker marker-90"></div>
|
||||
<div className="custom-slider-wrapper">
|
||||
<div className="custom-slider">
|
||||
<button id="slider-handle" className={`slider-handle ${isDragging ? "dragging" : ""}`} style={{ left: `${calculateHandlePosition()}%` }} onMouseDown={handleMouseDown}>
|
||||
<button
|
||||
id="slider-handle"
|
||||
className={`slider-handle ${isDragging ? "dragging" : ""}`}
|
||||
style={{ left: `${calculateHandlePosition()}%` }}
|
||||
onMouseDown={handleMouseDown}
|
||||
>
|
||||
{speed.toFixed(1)}x
|
||||
</button>
|
||||
<input type="range" min="0.5" max={MAX_SPEED} step="0.1" value={speed} onChange={handleSpeedChange} className="slider-input" />
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { CompareProduct, comparsionMaterialData, useCompareProductDataStore, useInputValues, useMachineDowntime, useMachineUptime, useProductionCapacityData, useROISummaryData, useThroughPutData } from '../../../../store/builder/store';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { saveSimulationData } from '../../../../components/layout/scenes/functions/simulationStorage';
|
||||
import { set } from 'immer/dist/internal';
|
||||
import { usePlayButtonStore } from '../../../../store/ui/usePlayButtonStore';
|
||||
import React, { useEffect } from "react";
|
||||
import {
|
||||
CompareProduct,
|
||||
comparsionMaterialData,
|
||||
useCompareProductDataStore,
|
||||
useInputValues,
|
||||
useMachineDowntime,
|
||||
useMachineUptime,
|
||||
useProductionCapacityData,
|
||||
useROISummaryData,
|
||||
useThroughPutData,
|
||||
} from "../../../../store/builder/store";
|
||||
import { useSceneContext } from "../../../scene/sceneContext";
|
||||
|
||||
import { usePlayButtonStore } from "../../../../store/ui/usePlayButtonStore";
|
||||
|
||||
export default function ROIData() {
|
||||
const { inputValues } = useInputValues();
|
||||
@@ -15,8 +24,8 @@ export default function ROIData() {
|
||||
const { compareProductsData, setCompareProductsData } = useCompareProductDataStore();
|
||||
const { machineActiveTime } = useMachineUptime();
|
||||
const { machineIdleTime } = useMachineDowntime();
|
||||
const { throughputData } = useThroughPutData()
|
||||
const { materialData, setMaterialData } = comparsionMaterialData()
|
||||
const { throughputData } = useThroughPutData();
|
||||
const { setMaterialData } = comparsionMaterialData();
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) return;
|
||||
@@ -29,6 +38,7 @@ export default function ROIData() {
|
||||
netProfit: 0,
|
||||
netLoss: 0,
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isPlaying]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -47,7 +57,20 @@ export default function ROIData() {
|
||||
const shiftsPerDay = parseFloat(inputValues["Shifts / day"]);
|
||||
const workingDaysPerYear = parseFloat(inputValues["Working days / year"]);
|
||||
|
||||
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) {
|
||||
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;
|
||||
// const annualProductionUnits = productionCapacityData * totalHoursPerYear;
|
||||
// const annualRevenue = annualProductionUnits * sellingPrice;
|
||||
@@ -130,7 +153,8 @@ export default function ROIData() {
|
||||
|
||||
const ROI = (Net_profit / initialInvestment) * 100;
|
||||
|
||||
const Annual_net_profit = Annual_units * (sellingPrice - materialCost - laborCost) - (maintenanceCost + electricityCost + fixedCost) * workingDaysPerYear + salvageValue * workingDaysPerYear;
|
||||
const Annual_net_profit =
|
||||
Annual_units * (sellingPrice - materialCost - laborCost) - (maintenanceCost + electricityCost + fixedCost) * workingDaysPerYear + salvageValue * workingDaysPerYear;
|
||||
const Payback_period_years = initialInvestment / Annual_net_profit;
|
||||
const data = {
|
||||
productName: selectedProduct.productName,
|
||||
@@ -139,14 +163,13 @@ export default function ROIData() {
|
||||
totalCost: Total_cost,
|
||||
revenueGenerated: Total_revenue,
|
||||
netProfit: Net_profit > 0 ? Net_profit : 0,
|
||||
netLoss: Net_profit < 0 ? -Net_profit : 0
|
||||
}
|
||||
console.log('selectedProduct.productUuid: ', selectedProduct.productUuid);
|
||||
|
||||
// saveSimulationData({ key: selectedProduct.productUuid, data: data });
|
||||
netLoss: Net_profit < 0 ? -Net_profit : 0,
|
||||
};
|
||||
console.log("selectedProduct.productUuid: ", selectedProduct.productUuid);
|
||||
|
||||
const datas = {
|
||||
roi: data
|
||||
}
|
||||
roi: data,
|
||||
};
|
||||
setMaterialData(datas);
|
||||
|
||||
setRoiSummaryData({
|
||||
@@ -193,6 +216,7 @@ export default function ROIData() {
|
||||
setCompareProductsData([...prev, newData]);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [inputValues, productionCapacityData, throughputData, isPlaying]);
|
||||
|
||||
useEffect(() => {}, [compareProductsData]);
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import { useEffect } from 'react'
|
||||
import { comparsionMaterialData, useInputValues, useProductionCapacityData, useThroughPutData } from '../../../../store/builder/store'
|
||||
import { useEffect } from "react";
|
||||
import { comparsionMaterialData, useInputValues, useProductionCapacityData, useThroughPutData } from "../../../../store/builder/store";
|
||||
|
||||
import { saveSimulationData } from '../../../../components/layout/scenes/functions/simulationStorage';
|
||||
import { usePlayButtonStore } from '../../../../store/ui/usePlayButtonStore';
|
||||
import { usePlayButtonStore } from "../../../../store/ui/usePlayButtonStore";
|
||||
|
||||
export default function ProductionCapacityData() {
|
||||
const { throughputData } = useThroughPutData();
|
||||
const { setProductionCapacityData } = useProductionCapacityData();
|
||||
const { inputValues } = useInputValues();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { materialData, setMaterialData } = comparsionMaterialData()
|
||||
|
||||
const { materialData, setMaterialData } = comparsionMaterialData();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPlaying) {
|
||||
setProductionCapacityData(0);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isPlaying]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -26,7 +25,6 @@ export default function ProductionCapacityData() {
|
||||
const Monthly_working_days = workingDaysPerYear / 12;
|
||||
const Production_capacity_per_month = throughputData * Monthly_working_days;
|
||||
const data = Number(Production_capacity_per_month.toFixed(2));
|
||||
// saveSimulationData({ key: 'productionCapacity', data: data });
|
||||
setMaterialData({ ...materialData, productionCapacity: data });
|
||||
setProductionCapacityData(Number(Production_capacity_per_month.toFixed(2)));
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useEffect } from 'react';
|
||||
import { determineExecutionMachineSequences } from '../../simulator/functions/determineExecutionMachineSequences';
|
||||
import { comparsionMaterialData, useInputValues, useMachineCount, useMachineDowntime, useMachineUptime, useMaterialCycle, useProcessBar, useThroughPutData } from '../../../../store/builder/store';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { saveSimulationData } from '../../../../components/layout/scenes/functions/simulationStorage';
|
||||
import { usePlayButtonStore } from '../../../../store/ui/usePlayButtonStore';
|
||||
import { useEffect } from "react";
|
||||
import { determineExecutionMachineSequences } from "../../simulator/functions/determineExecutionMachineSequences";
|
||||
import { comparsionMaterialData, useInputValues, useMachineCount, useMachineDowntime, useMachineUptime, useMaterialCycle, useProcessBar, useThroughPutData } from "../../../../store/builder/store";
|
||||
import { useSceneContext } from "../../../scene/sceneContext";
|
||||
import { usePlayButtonStore } from "../../../../store/ui/usePlayButtonStore";
|
||||
|
||||
export default function ThroughPutData() {
|
||||
const { materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, productStore } = useSceneContext();
|
||||
@@ -22,7 +21,7 @@ export default function ThroughPutData() {
|
||||
const { setThroughputData } = useThroughPutData();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { inputValues } = useInputValues();
|
||||
const { materialData, setMaterialData } = comparsionMaterialData()
|
||||
const { materialData, setMaterialData } = comparsionMaterialData();
|
||||
|
||||
// Setting machine count
|
||||
let totalItems = 0;
|
||||
@@ -209,6 +208,7 @@ export default function ThroughPutData() {
|
||||
return () => {
|
||||
if (timeoutId) clearTimeout(timeoutId);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [armBots, materials, materialHistory, machines, vehicles, selectedProduct?.productUuid]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -220,13 +220,11 @@ export default function ThroughPutData() {
|
||||
const Units_per_shift = (shiftLength * 60) / (materialCycleTime / 60);
|
||||
|
||||
const Throughput_per_day = Units_per_shift * shiftsPerDay * (yieldRate / 100);
|
||||
const data = Number(Throughput_per_day.toFixed(2))
|
||||
console.log('data: ', data);
|
||||
// saveSimulationData({ key: selectedProduct.productUuid, data: data });
|
||||
|
||||
const data = Number(Throughput_per_day.toFixed(2));
|
||||
setMaterialData({ ...materialData, throughput: data });
|
||||
setThroughputData(Number(Throughput_per_day.toFixed(2))); // Keep as number
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [materialCycleTime, machineCount, isPlaying, inputValues, materialData]);
|
||||
|
||||
return <></>;
|
||||
|
||||
@@ -1,297 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useSceneContext } from '../../scene/sceneContext';
|
||||
import { determineExecutionMachineSequences } from './functions/determineExecutionMachineSequences';
|
||||
import { usePlayButtonStore } from '../../../store/ui/usePlayButtonStore';
|
||||
import { useSimulationManager } from '../../../store/rough/useSimulationManagerStore';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useSimulateId } from '../../../store/builder/store';
|
||||
|
||||
interface SimulationUsageRecord {
|
||||
activeTime: number;
|
||||
isActive: boolean;
|
||||
idleTime: number;
|
||||
type: "roboticArm" | "vehicle" | "transfer" | "storageUnit" | "crane" | "human" | "machine";
|
||||
assetId: string;
|
||||
}
|
||||
|
||||
// Product → holds multiple usage records
|
||||
interface ProductSimulation {
|
||||
productId: string;
|
||||
data: SimulationUsageRecord[];
|
||||
}
|
||||
|
||||
// Version → holds multiple products
|
||||
interface VersionSimulation {
|
||||
versionId: string;
|
||||
products: ProductSimulation[];
|
||||
}
|
||||
|
||||
// Project → holds multiple versions
|
||||
interface ProjectSimulation {
|
||||
projectId: string | undefined;
|
||||
versions: VersionSimulation[];
|
||||
}
|
||||
const SimulationHandler = () => {
|
||||
const { materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, productStore, craneStore, humanStore, versionStore } = useSceneContext();
|
||||
const { armBots, getArmBotById } = armBotStore();
|
||||
const { vehicles, getVehicleById } = vehicleStore();
|
||||
const { getConveyorById } = conveyorStore();
|
||||
const { materialHistory, materials } = materialStore();
|
||||
const { getProductById, selectedProduct } = productStore();
|
||||
const { machines, getMachineById } = machineStore();
|
||||
const { getHumanById } = humanStore();
|
||||
const { getCraneById } = craneStore();
|
||||
const { getStorageUnitById } = storageUnitStore();
|
||||
const { isPlaying, setIsPlaying } = usePlayButtonStore();
|
||||
const { simulationRecords, addSimulationRecord } = useSimulationManager();
|
||||
const { projectId } = useParams();
|
||||
const [simulationEntry, setSimulationEntry] = useState<any>();
|
||||
const { setSimulateId } = useSimulateId();
|
||||
const COST_RATES: Record<SimulationUsageRecord["type"], number> = {
|
||||
roboticArm: 5,
|
||||
vehicle: 2,
|
||||
transfer: 1,
|
||||
storageUnit: 1,
|
||||
crane: 6,
|
||||
human: 4,
|
||||
machine: 3,
|
||||
};
|
||||
const { selectedVersion } = versionStore();
|
||||
|
||||
// Calculate totals for one product
|
||||
function calculateProductMetrics(product: ProductSimulation) {
|
||||
let totalActiveTime = 0;
|
||||
let totalIdleTime = 0;
|
||||
let totalCost = 0;
|
||||
|
||||
product.data.forEach((record) => {
|
||||
const resourceTime = record.activeTime + record.idleTime;
|
||||
const costRate = COST_RATES[record.type] || 0;
|
||||
|
||||
totalActiveTime += record.activeTime;
|
||||
totalIdleTime += record.idleTime;
|
||||
totalCost += resourceTime * costRate;
|
||||
});
|
||||
|
||||
return {
|
||||
totalTime: totalActiveTime + totalIdleTime,
|
||||
totalActiveTime,
|
||||
totalIdleTime,
|
||||
totalCost,
|
||||
};
|
||||
}
|
||||
|
||||
// Calculate totals for a version
|
||||
function calculateVersionMetrics(version: VersionSimulation) {
|
||||
return version.products.reduce(
|
||||
(acc, product) => {
|
||||
const metrics = calculateProductMetrics(product);
|
||||
|
||||
acc.totalTime += metrics.totalTime;
|
||||
acc.totalActiveTime += metrics.totalActiveTime;
|
||||
acc.totalIdleTime += metrics.totalIdleTime;
|
||||
acc.totalCost += metrics.totalCost;
|
||||
return acc;
|
||||
},
|
||||
{ totalTime: 0, totalActiveTime: 0, totalIdleTime: 0, totalCost: 0 }
|
||||
);
|
||||
}
|
||||
|
||||
// Efficiency score (compare across versions)
|
||||
function calculateEfficiencyScores(versions: VersionSimulation[]) {
|
||||
const versionMetrics = versions.map((v) => ({
|
||||
versionId: v.versionId,
|
||||
...calculateVersionMetrics(v),
|
||||
}));
|
||||
|
||||
const minTime = Math.min(...versionMetrics.map((m) => m.totalTime));
|
||||
const minCost = Math.min(...versionMetrics.map((m) => m.totalCost));
|
||||
|
||||
return versionMetrics.map((m) => {
|
||||
const timeFactor = minTime / m.totalTime;
|
||||
const costFactor = minCost / m.totalCost;
|
||||
|
||||
const efficiencyScore = 0.5 * timeFactor + 0.5 * costFactor;
|
||||
|
||||
return { ...m, efficiencyScore };
|
||||
});
|
||||
}
|
||||
|
||||
// useEffect(() => {
|
||||
// const runSimulation = async () => {
|
||||
// console.log("simulationRecords: ", simulationRecords);
|
||||
// if (!projectId || !selectedVersion || !selectedProduct.productUuid || simulationRecords.length === 0) return;
|
||||
|
||||
// const project = simulationRecords[0];
|
||||
|
||||
// if (project) {
|
||||
// // const scores = calculateEfficiencyScores(project.versions);
|
||||
// // console.log("Version Comparisons:", scores);
|
||||
// }
|
||||
// console.log("simulationEntry: ", simulationEntry);
|
||||
// console.log("simulationEntrysaddasd: ", useSimulationManager.getState().getProductById(projectId, selectedVersion?.versionId, selectedProduct.productUuid)?.simulateData);
|
||||
|
||||
// const data = {
|
||||
// projectId: projectId,
|
||||
// versionId: selectedVersion.versionId,
|
||||
// productUuid: selectedProduct.productUuid,
|
||||
// simulateData: useSimulationManager.getState().getProductById(projectId, selectedVersion?.versionId, selectedProduct.productUuid)?.simulateData,
|
||||
// };
|
||||
|
||||
// const simulations = await saveSimulationData(data);
|
||||
// console.log("simulations: ", simulations);
|
||||
// const simulateId = localStorage.getItem("simulateId");
|
||||
// console.log("simulateId: ", simulateId);
|
||||
// if (simulations.message === "SimulatedData created Successfully") {
|
||||
// console.log("simulations.data: ", simulations.data);
|
||||
// setSimulationEntry(simulations.data);
|
||||
// localStorage.setItem("simulateId", simulations.data);
|
||||
// setSimulateId(simulations.data);
|
||||
// }
|
||||
|
||||
// // else {
|
||||
// // console.log("djh");
|
||||
// // const data = {
|
||||
// // projectId: projectId,
|
||||
// // versionId: selectedVersion.versionId,
|
||||
// // productUuid: selectedProduct.productUuid,
|
||||
// // simulateId: simulateId,
|
||||
// // simulateData: useSimulationManager.getState().getProductById(projectId, selectedVersion?.versionId, selectedProduct.productUuid)?.simulateData,
|
||||
// // };
|
||||
// // const update = await updateSimulateData(data);
|
||||
// // console.log("update: ", update);
|
||||
// // }
|
||||
// };
|
||||
|
||||
// runSimulation();
|
||||
// }, [simulationRecords, projectId, selectedVersion, selectedProduct]);
|
||||
|
||||
// useEffect(() => {
|
||||
// console.log('simulationRecords: ', simulationRecords);
|
||||
// if (!projectId || !selectedVersion || !selectedProduct.productUuid || simulationRecords.length === 0) return;
|
||||
|
||||
// const project = simulationRecords[0];
|
||||
|
||||
// if (project) {
|
||||
// const scores = calculateEfficiencyScores(project.versions);
|
||||
// console.log("Version Comparisons:", scores);
|
||||
// }
|
||||
|
||||
// saveSimulationData({
|
||||
// key: selectedProduct.productUuid,
|
||||
// data: simulationRecords,
|
||||
// });
|
||||
// // saveSimulationData({
|
||||
// // key: selectedProduct.productUuid,
|
||||
// // data: simulationRecords,
|
||||
// // });
|
||||
// }, [simulationRecords]);
|
||||
|
||||
useEffect(() => {
|
||||
let checkTimer: ReturnType<typeof setTimeout>;
|
||||
if (!projectId) return;
|
||||
async function checkActiveMachines() {
|
||||
const currentProduct = getProductById(selectedProduct.productUuid);
|
||||
let hasActiveEntity = false;
|
||||
|
||||
if (currentProduct) {
|
||||
const executionSequences = await determineExecutionMachineSequences([currentProduct]);
|
||||
if (executionSequences?.length > 0) {
|
||||
executionSequences.forEach((sequence) => {
|
||||
sequence.forEach((entity) => {
|
||||
if (entity.type === "roboticArm") {
|
||||
const roboticArm = getArmBotById(entity.modelUuid);
|
||||
if (roboticArm?.isActive) {
|
||||
hasActiveEntity = true;
|
||||
}
|
||||
}
|
||||
if (entity.type === "vehicle") {
|
||||
const vehicle = getVehicleById(entity.modelUuid);
|
||||
if (vehicle?.isActive) {
|
||||
hasActiveEntity = true;
|
||||
}
|
||||
}
|
||||
if (entity.type === "machine") {
|
||||
const machine = getMachineById(entity.modelUuid);
|
||||
if (machine?.isActive) {
|
||||
hasActiveEntity = true;
|
||||
}
|
||||
}
|
||||
if (entity.type === "human") {
|
||||
const human = getHumanById(entity.modelUuid);
|
||||
if (human?.isActive) {
|
||||
hasActiveEntity = true;
|
||||
}
|
||||
}
|
||||
if (entity.type === "crane") {
|
||||
const crane = getCraneById(entity.modelUuid);
|
||||
if (crane?.isActive) {
|
||||
hasActiveEntity = true;
|
||||
}
|
||||
}
|
||||
if (entity.type === "storageUnit") {
|
||||
const storageUnit = getStorageUnitById(entity.modelUuid);
|
||||
if (storageUnit?.isActive) {
|
||||
hasActiveEntity = true;
|
||||
}
|
||||
}
|
||||
if (entity.type === "transfer") {
|
||||
const storageUnit = getConveyorById(entity.modelUuid);
|
||||
if (storageUnit?.isActive) {
|
||||
hasActiveEntity = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (materials.length === 0 && materialHistory.length >= 0 && !hasActiveEntity) {
|
||||
if (executionSequences?.length > 0) {
|
||||
executionSequences.forEach((sequence) => {
|
||||
sequence.forEach((entity) => {
|
||||
const typeToGetter: Record<string, (id: string) => any> = {
|
||||
roboticArm: getArmBotById,
|
||||
vehicle: getVehicleById,
|
||||
machine: getMachineById,
|
||||
human: getHumanById,
|
||||
crane: getCraneById,
|
||||
storageUnit: getStorageUnitById,
|
||||
transfer: getConveyorById,
|
||||
};
|
||||
|
||||
const getter = typeToGetter[entity.type];
|
||||
if (!getter) return; // skip unknown entity types
|
||||
|
||||
const obj = getter(entity.modelUuid);
|
||||
if (!obj) return; // skip if not found
|
||||
|
||||
addSimulationRecord(projectId, selectedVersion?.versionId || "", selectedProduct?.productUuid, {
|
||||
activeTime: obj.activeTime ?? 0,
|
||||
isActive: obj.isActive ?? false,
|
||||
idleTime: obj.idleTime ?? 0,
|
||||
type: entity.type as "roboticArm" | "vehicle" | "machine" | "human" | "crane" | "storageUnit" | "transfer",
|
||||
assetId: entity.modelUuid,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
setIsPlaying(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isPlaying) {
|
||||
checkTimer = setTimeout(() => {
|
||||
checkActiveMachines();
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (checkTimer) clearTimeout(checkTimer);
|
||||
};
|
||||
}, [materials, materialHistory, selectedVersion, selectedProduct?.productUuid, isPlaying, armBots, vehicles, machines]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default SimulationHandler;
|
||||
118
app/src/modules/simulation/simulator/simulationHandler.tsx
Normal file
118
app/src/modules/simulation/simulator/simulationHandler.tsx
Normal file
@@ -0,0 +1,118 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useSceneContext } from "../../scene/sceneContext";
|
||||
import { determineExecutionMachineSequences } from "./functions/determineExecutionMachineSequences";
|
||||
import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore";
|
||||
import { useSimulationManager } from "../../../store/rough/useSimulationManagerStore";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { saveSimulationDataApi } from "../../../services/simulation/comparison/saveSimulationDataApi";
|
||||
|
||||
const SimulationHandler = () => {
|
||||
const { materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, productStore, craneStore, humanStore, versionStore } = useSceneContext();
|
||||
const { armBots, getArmBotById } = armBotStore();
|
||||
const { vehicles, getVehicleById } = vehicleStore();
|
||||
const { getConveyorById } = conveyorStore();
|
||||
const { materialHistory, materials } = materialStore();
|
||||
const { getProductById, selectedProduct } = productStore();
|
||||
const { machines, getMachineById } = machineStore();
|
||||
const { getHumanById } = humanStore();
|
||||
const { getCraneById } = craneStore();
|
||||
const { getStorageUnitById } = storageUnitStore();
|
||||
const { isPlaying, setIsPlaying } = usePlayButtonStore();
|
||||
const { resetProductRecords, addSimulationRecord } = useSimulationManager();
|
||||
const { selectedVersion } = versionStore();
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectId) return;
|
||||
|
||||
let checkTimer: ReturnType<typeof setTimeout>;
|
||||
|
||||
const typeToGetter: Record<string, (id: string) => any> = {
|
||||
roboticArm: getArmBotById,
|
||||
vehicle: getVehicleById,
|
||||
machine: getMachineById,
|
||||
human: getHumanById,
|
||||
crane: getCraneById,
|
||||
storageUnit: getStorageUnitById,
|
||||
transfer: getConveyorById,
|
||||
};
|
||||
|
||||
const isEntityActive = (entity: any): boolean => {
|
||||
const getter = typeToGetter[entity.type];
|
||||
const obj = getter?.(entity.modelUuid);
|
||||
return obj?.isActive ?? false;
|
||||
};
|
||||
|
||||
const recordEntity = (entity: any) => {
|
||||
const getter = typeToGetter[entity.type];
|
||||
const obj = getter?.(entity.modelUuid);
|
||||
if (!obj) return;
|
||||
|
||||
addSimulationRecord(projectId, selectedVersion?.versionId || "", selectedProduct?.productUuid, {
|
||||
activeTime: obj.activeTime ?? 0,
|
||||
isActive: obj.isActive ?? false,
|
||||
idleTime: obj.idleTime ?? 0,
|
||||
type: entity.type as "roboticArm" | "vehicle" | "machine" | "human" | "crane" | "storageUnit" | "transfer",
|
||||
assetId: entity.modelUuid,
|
||||
});
|
||||
};
|
||||
|
||||
function checkActiveMachines() {
|
||||
const currentProduct = getProductById(selectedProduct.productUuid);
|
||||
if (!currentProduct) return;
|
||||
|
||||
determineExecutionMachineSequences([currentProduct]).then((executionSequences) => {
|
||||
// --- Check if any entity is active ---
|
||||
let hasActiveEntity = false;
|
||||
executionSequences?.forEach((sequence) =>
|
||||
sequence.forEach((entity) => {
|
||||
if (isEntityActive(entity)) {
|
||||
hasActiveEntity = true;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// --- Save simulation data if idle ---
|
||||
const noMaterials = materials.length === 0;
|
||||
const hasHistory = materialHistory.length >= 0;
|
||||
|
||||
if (noMaterials && hasHistory && !hasActiveEntity) {
|
||||
if (!selectedVersion) return;
|
||||
|
||||
resetProductRecords(projectId, selectedVersion.versionId, selectedProduct.productUuid);
|
||||
|
||||
executionSequences?.forEach((sequence) => sequence.forEach((entity) => recordEntity(entity)));
|
||||
|
||||
const data = {
|
||||
projectId,
|
||||
versionId: selectedVersion.versionId,
|
||||
productUuid: selectedProduct.productUuid,
|
||||
simulateData: useSimulationManager.getState().getProductById(projectId, selectedVersion.versionId, selectedProduct.productUuid)?.simulateData,
|
||||
};
|
||||
|
||||
saveSimulationDataApi(data)
|
||||
.then(() => {
|
||||
echo.log("Simulation data saved successfully");
|
||||
setIsPlaying(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Failed to save simulation data:", err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isPlaying) {
|
||||
checkTimer = setTimeout(checkActiveMachines, 1500);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (checkTimer) clearTimeout(checkTimer);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [materials, materialHistory, selectedVersion, selectedProduct?.productUuid, isPlaying, armBots, vehicles, machines]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default SimulationHandler;
|
||||
@@ -3,14 +3,12 @@ import { useActionHandler } from "../actions/useActionHandler";
|
||||
import { usePlayButtonStore, useResetButtonStore } from "../../../store/ui/usePlayButtonStore";
|
||||
import { determineExecutionOrder } from "./functions/determineExecutionOrder";
|
||||
import { useSceneContext } from "../../scene/sceneContext";
|
||||
import SimulationHandler from "./SimulationHandler";
|
||||
import SimulationHandler from "./simulationHandler";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {
|
||||
getSimulationData,
|
||||
saveSimulationData,
|
||||
} from "../../../components/layout/scenes/functions/simulationStorage";
|
||||
import { useSimulationManager } from "../../../store/rough/useSimulationManagerStore";
|
||||
import { useSimulateId } from "../../../store/builder/store";
|
||||
import { getSimulationDataApi } from "../../../services/simulation/comparison/getSimulationDataApi";
|
||||
import { getProductApi } from "../../../services/simulation/products/getProductApi";
|
||||
import { useIsComparing } from "../../../store/builder/store";
|
||||
|
||||
function Simulator() {
|
||||
const { productStore, versionStore } = useSceneContext();
|
||||
@@ -20,7 +18,7 @@ function Simulator() {
|
||||
const { isReset } = useResetButtonStore();
|
||||
const { projectId } = useParams();
|
||||
const { selectedVersion } = versionStore();
|
||||
const { setSimulateId } = useSimulateId();
|
||||
const { setIsComparing } = useIsComparing();
|
||||
const { addSimulationRecords } = useSimulationManager();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -34,57 +32,31 @@ function Simulator() {
|
||||
executionOrder.forEach((action) => {
|
||||
handleAction(action);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [products, isPlaying, isReset, selectedProduct]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!projectId || !selectedVersion || !selectedProduct?.productUuid) return;
|
||||
const fetchSimulateData = async () => {
|
||||
const getData = await getSimulationData(
|
||||
projectId,
|
||||
selectedVersion.versionId,
|
||||
selectedProduct?.productUuid
|
||||
);
|
||||
const product = getProductById(selectedProduct.productUuid);
|
||||
if (!product) return;
|
||||
const products: ProductsSchema = [product];
|
||||
const getSimulate = getData?.data;
|
||||
// console.log("getSimulate: ", getSimulate);
|
||||
if (getData && getSimulate && getSimulate.productTimestamp === products[0]?.timestamp) {
|
||||
setSimulateId(getSimulate._id);
|
||||
// console.log(" getSimulate.data: ", getSimulate.data);
|
||||
addSimulationRecords(
|
||||
projectId,
|
||||
selectedVersion?.versionId || "",
|
||||
selectedProduct?.productUuid || "",
|
||||
getSimulate.data // ✅ this is already an array
|
||||
);
|
||||
echo.log("Simulation data is up to date");
|
||||
return;
|
||||
} else {
|
||||
//call create API
|
||||
const data = {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion.versionId,
|
||||
productUuid: selectedProduct.productUuid,
|
||||
simulateData: useSimulationManager
|
||||
.getState()
|
||||
.getProductById(
|
||||
projectId,
|
||||
selectedVersion?.versionId,
|
||||
selectedProduct.productUuid
|
||||
)?.simulateData,
|
||||
};
|
||||
const simulations = await saveSimulationData(data);
|
||||
// console.log("simulations: ", simulations);
|
||||
echo.log("Simulation data saved successfully");
|
||||
}
|
||||
};
|
||||
fetchSimulateData();
|
||||
}, []);
|
||||
getSimulationDataApi(projectId, selectedVersion.versionId, selectedProduct?.productUuid).then((getData) => {
|
||||
getProductApi(selectedProduct.productUuid, projectId, selectedVersion.versionId).then((product) => {
|
||||
if (!product) return;
|
||||
const getSimulate = getData?.data;
|
||||
|
||||
if (getData.message !== "Simulated data not found" && getSimulate && getSimulate.productTimestamp === product?.timestamp) {
|
||||
addSimulationRecords(projectId, selectedVersion?.versionId || "", selectedProduct?.productUuid || "", getSimulate.data);
|
||||
echo.warn("Simulation data is up to date");
|
||||
return;
|
||||
} else {
|
||||
setIsComparing(false);
|
||||
echo.warn("Please run the simulation before comparing.");
|
||||
}
|
||||
});
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedProduct, projectId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <simulationHandler/> */}
|
||||
<SimulationHandler />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const getSimulationDataApi = async (projectId: string, versionId: string, productUuid: string) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/SimulatedDatas/${projectId}/${versionId}?productUuid=${productUuid}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "",
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
});
|
||||
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch simulateData");
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error: any) {
|
||||
console.error("Failed to get simulation data");
|
||||
console.log(error.message);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
|
||||
export const saveSimulationDataApi = async (data: any) => {
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/SimulatedUpsert`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "",
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
if (!response.ok) {
|
||||
console.error("Failed to add project");
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.log(error.message);
|
||||
} else {
|
||||
console.log("An unknown error occurred");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
|
||||
export const validateSimulationDataApi = async (data: any) => {
|
||||
console.log("data: validate", data);
|
||||
try {
|
||||
const response = await fetch(`${url_Backend_dwinzo}/api/V1/ValidateSimulated`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: "Bearer <access_token>",
|
||||
"Content-Type": "application/json",
|
||||
token: localStorage.getItem("token") || "",
|
||||
refresh_token: localStorage.getItem("refreshToken") || "",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
const newAccessToken = response.headers.get("x-access-token");
|
||||
if (newAccessToken) {
|
||||
localStorage.setItem("token", newAccessToken);
|
||||
}
|
||||
if (!response.ok) {
|
||||
console.error("Failed to update ");
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log("result:Valid ", result);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.log(error.message);
|
||||
} else {
|
||||
console.log("An unknown error occurred");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -2,12 +2,13 @@ let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_UR
|
||||
|
||||
export const getProductApi = async (
|
||||
productUuid: string,
|
||||
projectId: string
|
||||
projectId: string,
|
||||
versionId: string,
|
||||
) => {
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${url_Backend_dwinzo}/api/V1/EventsByProduct?productUuid=${productUuid}&projectId=${projectId}`,
|
||||
`${url_Backend_dwinzo}/api/V1/EventsByProduct?productUuid=${productUuid}&projectId=${projectId}&versionId=${versionId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
|
||||
@@ -500,6 +500,7 @@ export interface CompareProduct {
|
||||
netProfit: number;
|
||||
productionCapacity: number;
|
||||
paybackPeriod: number;
|
||||
// netLoss: number;
|
||||
machineIdleTime: number;
|
||||
machineActiveTime: number;
|
||||
throughputData: number;
|
||||
@@ -551,7 +552,3 @@ export const comparsionMaterialData = create<any>((set: any) => ({
|
||||
materialData: [],
|
||||
setMaterialData: (x: any) => set({ materialData: x }),
|
||||
}));
|
||||
export const useSimulateId = create<any>((set: any) => ({
|
||||
simulateId: "",
|
||||
setSimulateId: (x: any) => set({ simulateId: x }),
|
||||
}));
|
||||
|
||||
@@ -25,14 +25,13 @@ interface ProjectSimulation {
|
||||
projectId: string | undefined;
|
||||
versions: VersionSimulation[];
|
||||
}
|
||||
// or same file
|
||||
|
||||
interface SimulationManagerStore {
|
||||
simulationRecords: ProjectSimulation[];
|
||||
setSimulationRecords: (simulateData: ProjectSimulation[]) => void;
|
||||
addSimulationRecord: (projectId: string | undefined, versionId: string, productId: string, record: SimulationUsageRecord) => void;
|
||||
addSimulationRecords: (projectId: string | undefined, versionId: string, productId: string, records: SimulationUsageRecord[]) => void;
|
||||
resetProductRecords: (projectId: string, versionId: string, productId: string) => void;
|
||||
resetProductRecords: (projectId: string | undefined, versionId: string | null, productId: string) => void;
|
||||
getProjectById: (projectId: string | undefined) => ProjectSimulation | undefined;
|
||||
getVersionById: (projectId: string | undefined, versionId: string) => VersionSimulation | undefined;
|
||||
getProductById: (projectId: string | undefined, versionId: string, productId: string) => ProductSimulation | undefined;
|
||||
@@ -53,14 +52,25 @@ export const useSimulationManager = create<SimulationManagerStore>((set, get) =>
|
||||
|
||||
return {
|
||||
...version,
|
||||
products: version.products.map((product) => (product.productId === productId ? { ...product, simulateData: [...product.simulateData, record] } : product)),
|
||||
products: version.products.map((product) => {
|
||||
if (product.productId !== productId) return product;
|
||||
|
||||
const exists = product.simulateData.some((r) => r.assetId === record.assetId);
|
||||
|
||||
return {
|
||||
...product,
|
||||
simulateData: exists
|
||||
? product.simulateData.map((r) => (r.assetId === record.assetId ? record : r)) // update
|
||||
: [...product.simulateData, record], // insert
|
||||
};
|
||||
}),
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
// If project doesn't exist, create it
|
||||
if (!state.simulationRecords.find((p) => p.projectId === projectId)) {
|
||||
// If project/version/product doesn’t exist, create it
|
||||
if (!projects.find((p) => p.projectId === projectId)) {
|
||||
projects.push({
|
||||
projectId,
|
||||
versions: [
|
||||
@@ -72,14 +82,15 @@ export const useSimulationManager = create<SimulationManagerStore>((set, get) =>
|
||||
});
|
||||
} else {
|
||||
const project = projects.find((p) => p.projectId === projectId)!;
|
||||
if (!project.versions.find((v) => v.versionId === versionId)) {
|
||||
let version = project.versions.find((v) => v.versionId === versionId);
|
||||
if (!version) {
|
||||
project.versions.push({
|
||||
versionId,
|
||||
products: [{ productId, simulateData: [record] }],
|
||||
});
|
||||
} else {
|
||||
const version = project.versions.find((v) => v.versionId === versionId)!;
|
||||
if (!version.products.find((p) => p.productId === productId)) {
|
||||
let product = version.products.find((p) => p.productId === productId);
|
||||
if (!product) {
|
||||
version.products.push({ productId, simulateData: [record] });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,19 +305,14 @@
|
||||
background-color: #b7b7c6;
|
||||
|
||||
// Custom polygon shape (adjust if needed)
|
||||
clip-path: polygon(96% 52%,
|
||||
96% 54%,
|
||||
45% 53%,
|
||||
3% 100%,
|
||||
0 100%,
|
||||
42% 52%);
|
||||
clip-path: polygon(96% 52%, 96% 54%, 45% 53%, 3% 100%, 0 100%, 42% 52%);
|
||||
|
||||
z-index: 0; // Behind any inner content
|
||||
}
|
||||
}
|
||||
|
||||
// Optional: content above the shape
|
||||
>* {
|
||||
> * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
@@ -372,19 +367,14 @@
|
||||
height: 100%;
|
||||
|
||||
background: var(--background-color, wheat);
|
||||
clipPath: polygon(25% 0%,
|
||||
75% 0%,
|
||||
100% 50%,
|
||||
75% 100%,
|
||||
25% 100%,
|
||||
0% 50%);
|
||||
clippath: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
|
||||
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.25));
|
||||
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
// Content stays above the shape
|
||||
>* {
|
||||
> * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
@@ -422,8 +412,6 @@
|
||||
position: relative;
|
||||
|
||||
.energy-usage-wrapper {
|
||||
|
||||
|
||||
.value {
|
||||
padding-top: 25px;
|
||||
font-size: var(--font-size-xxxlarge);
|
||||
@@ -518,14 +506,46 @@
|
||||
.layer-time {
|
||||
font-size: var(--font-size-large);
|
||||
}
|
||||
|
||||
.layer-profit {
|
||||
color: #14ca44;
|
||||
text-align: end;
|
||||
|
||||
span {
|
||||
.layer-change {
|
||||
&.profit {
|
||||
color: #14ca44;
|
||||
span {
|
||||
color: #14ca44;
|
||||
}
|
||||
}
|
||||
|
||||
&.loss {
|
||||
color: #ff0000;
|
||||
span {
|
||||
color: #ff0000;
|
||||
}
|
||||
}
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
// .layer-profit {
|
||||
// color: #14ca44;
|
||||
// text-align: end;
|
||||
|
||||
// span {
|
||||
// color: #14ca44;
|
||||
// }
|
||||
// }
|
||||
.layer-profit {
|
||||
&.profit {
|
||||
color: #14ca44;
|
||||
span {
|
||||
color: #14ca44;
|
||||
}
|
||||
}
|
||||
|
||||
&.loss {
|
||||
color: #ff0000;
|
||||
span {
|
||||
color: #ff0000;
|
||||
}
|
||||
}
|
||||
text-align: end;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -543,19 +563,19 @@
|
||||
.overallDowntime-container {
|
||||
.totalDownTime-wrapper {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
|
||||
.totalDownTime {
|
||||
width: 70%;
|
||||
background: var(--background-color-secondary);
|
||||
backdrop-filter: blur(20px);
|
||||
border-radius: 12px;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
// gap: 20px;
|
||||
padding: 8px 10px;
|
||||
margin: 44px 0;
|
||||
margin-top: 16px;
|
||||
|
||||
.totalDownTime-right {
|
||||
display: flex;
|
||||
@@ -605,4 +625,4 @@
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user