updated comparision

This commit is contained in:
2025-06-12 17:35:42 +05:30
parent c7cc5cf2ca
commit 2fbdf8ab61
33 changed files with 670 additions and 316 deletions

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")