diff --git a/app/src/components/ui/analysis/ProductionCapacity.tsx b/app/src/components/ui/analysis/ProductionCapacity.tsx
index fd6150d..0bc8b91 100644
--- a/app/src/components/ui/analysis/ProductionCapacity.tsx
+++ b/app/src/components/ui/analysis/ProductionCapacity.tsx
@@ -9,7 +9,7 @@ import {
} from "chart.js";
import { PowerIcon, ProductionCapacityIcon } from "../../icons/analysis";
import SkeletonUI from "../../templates/SkeletonUI";
-import { useMachineUptime, useProductionCapacityData } from "../../../store/builder/store";
+import { useInputValues, useMachineUptime, useProductionCapacityData } from "../../../store/builder/store";
ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement);
@@ -73,6 +73,7 @@ const ThroughputSummary: React.FC = () => {
assetUsage: assetUsage,
};
+ const { inputValues } = useInputValues();
// Chart data configuration
const chartData = {
labels: throughputData.labels,
@@ -91,10 +92,10 @@ const ThroughputSummary: React.FC = () => {
useEffect(() => {
if (productionCapacityData >= 0) {
- setIsLoading(false);
+ setIsLoading(false);
console.log("productionCapacityData: ", productionCapacityData);
} else {
- setIsLoading(true);
+ setIsLoading(true);
}
}, [productionCapacityData]);
@@ -123,7 +124,7 @@ const ThroughputSummary: React.FC = () => {
0 ? "profit" : "loss"}`}
+ className={`metric-item net-profit ${roiSummary.netProfit > 0 ? "profit" : "loss"}`}
>
↑
diff --git a/app/src/components/ui/analysis/ThroughputSummary.tsx b/app/src/components/ui/analysis/ThroughputSummary.tsx
index e87027b..bf570f9 100644
--- a/app/src/components/ui/analysis/ThroughputSummary.tsx
+++ b/app/src/components/ui/analysis/ThroughputSummary.tsx
@@ -17,26 +17,24 @@ const ProductionCapacity = ({
const { machineActiveTime } = useMachineUptime();
const { materialCycleTime } = useMaterialCycle();
const { throughputData } = useThroughPutData()
- const { productionCapacityData } = useProductionCapacityData()
-
- const progressPercent = machineActiveTime;
-
-
- const totalBars = 6;
- const barsToFill = Math.floor((progressPercent / 100) * totalBars);
- const partialFillPercent =
+ const { productionCapacityData } = useProductionCapacityData()
+
+ const progressPercent = machineActiveTime;
+
+
+ const totalBars = 6;
+ const barsToFill = Math.floor((progressPercent / 100) * totalBars);
+ const partialFillPercent =
((progressPercent / 100) * totalBars - barsToFill) * 100;
-
- const [isLoading, setIsLoading] = useState(false);
-
- useEffect(() => {
- if (throughputData >= 0) {
- console.log('machineActiveTime: ', machineActiveTime);
- console.log('materialCycleTime: ', materialCycleTime);
- console.log('throughputData: ', throughputData);
- console.log('productionCapacityData: ', productionCapacityData);
+ const [isLoading, setIsLoading] = useState(false);
+ useEffect(() => {
+ if (throughputData >= 0) {
+ // console.log('machineActiveTime: ', machineActiveTime);
+ // console.log('materialCycleTime: ', materialCycleTime);
+ // console.log('throughputData: ', throughputData);
+ // console.log('productionCapacityData: ', productionCapacityData);
setIsLoading(true);
}
diff --git a/app/src/modules/simulation/analysis/ROI/roiData.tsx b/app/src/modules/simulation/analysis/ROI/roiData.tsx
index fd77098..f1ddda5 100644
--- a/app/src/modules/simulation/analysis/ROI/roiData.tsx
+++ b/app/src/modules/simulation/analysis/ROI/roiData.tsx
@@ -1,15 +1,28 @@
import React, { useEffect, useState } from 'react'
import { useInputValues, useProductionCapacityData, useROISummaryData } from '../../../../store/builder/store';
import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore';
+import { usePlayButtonStore } from '../../../../store/usePlayButtonStore';
export default function ROIData() {
const { inputValues } = useInputValues();
const { productionCapacityData } = useProductionCapacityData()
const { selectedProduct } = useSelectedProduct();
-
-
+ const { isPlaying } = usePlayButtonStore();
const { setRoiSummaryData } = useROISummaryData();
useEffect(() => {
+ if (!isPlaying) {
+ setRoiSummaryData({
+ productName: "",
+ roiPercentage: 0,
+ paybackPeriod: 0,
+ totalCost: 0,
+ revenueGenerated: 0,
+ netProfit: 0,
+ netLoss: 0,
+ })
+ return;
+ }
+
if (inputValues === undefined) return;
const electricityCost = parseFloat(inputValues["Electricity cost"]);
@@ -29,69 +42,77 @@ export default function ROIData() {
!isNaN(materialCost) && !isNaN(productionPeriod) && !isNaN(salvageValue) && !isNaN(sellingPrice) &&
!isNaN(shiftLength) && !isNaN(shiftsPerDay) && !isNaN(workingDaysPerYear) && productionCapacityData > 0) {
- console.log('sellingPrice: ', sellingPrice);
- console.log('salvageValue: ', salvageValue);
- console.log('productionPeriod: ', productionPeriod);
- console.log('materialCost: ', materialCost);
- console.log('maintenanceCost: ', maintenanceCost);
- console.log('laborCost: ', laborCost);
- console.log('fixedCost: ', fixedCost);
- console.log('electricityCost: ', electricityCost);
- // Revenue
- const RevenueForYear = productionCapacityData * sellingPrice;
- console.log('RevenueForYear: ', RevenueForYear);
- //Costs
+ const totalHoursPerYear = shiftLength * shiftsPerDay * workingDaysPerYear;
- let materialCount = 1200;
+ // Total good units produced per year
+ const annualProductionUnits = productionCapacityData * totalHoursPerYear;
- //Material Cost
+ // Revenue for a year
+ const annualRevenue = annualProductionUnits * sellingPrice;
- let MaterialCost = productionCapacityData * materialCost
- console.log('MaterialCost: ', MaterialCost);
- let LaborCost = laborCost * shiftLength * shiftsPerDay * workingDaysPerYear;
- console.log('LaborCost: ', LaborCost);
- let EnergyCost = electricityCost * shiftLength * shiftsPerDay * workingDaysPerYear;
- console.log('EnergyCost: ', EnergyCost);
- let MaintenceCost = maintenanceCost + fixedCost;
- console.log('MaintenceCost: ', MaintenceCost);
+ // Costs
+ const totalMaterialCost = annualProductionUnits * materialCost;
+ const totalLaborCost = laborCost * totalHoursPerYear;
+ const totalEnergyCost = electricityCost * totalHoursPerYear;
+ const totalMaintenanceCost = maintenanceCost + fixedCost;
- //Total Anuual Cost
- let TotalAnnualCost = (MaterialCost * materialCount) + LaborCost + EnergyCost + MaintenceCost;
- console.log('TotalAnnualCost: ', TotalAnnualCost);
+ const totalAnnualCost = totalMaterialCost + totalLaborCost + totalEnergyCost + totalMaintenanceCost;
+ // Annual Profit
+ const annualProfit = annualRevenue - totalAnnualCost;
+ console.log('annualProfit: ', annualProfit);
- //Profit for Year
- let ProfitforYear = RevenueForYear - TotalAnnualCost;
- console.log('ProfitforYear: ', ProfitforYear);
-
- //Net Profit
- let NetProfit = ProfitforYear * productionPeriod;
- console.log('NetProfit: ', NetProfit);
-
-
- //Final ROI
- const ROIData = ((NetProfit + salvageValue - initialInvestment) / TotalAnnualCost) * 100;
- console.log('ROIData: ', ROIData);
+ // Net Profit over production period
+ const netProfit = annualProfit * productionPeriod;
+ // ROI
+ const roiPercentage = ((netProfit + salvageValue - initialInvestment) / initialInvestment) * 100;
// Payback Period
- const paybackPeriod = initialInvestment / ProfitforYear;
+ const paybackPeriod = initialInvestment / (annualProfit || 1); // Avoid division by 0
console.log('paybackPeriod: ', paybackPeriod);
+ // console.log("--- ROI Breakdown ---");
+ // console.log("Annual Production Units:", annualProductionUnits.toFixed(2));
+ // console.log("Annual Revenue:", annualRevenue.toFixed(2));
+ // console.log("Total Annual Cost:", totalAnnualCost.toFixed(2));
+ // console.log("Annual Profit:", annualProfit.toFixed(2));
+ // console.log("Net Profit:", netProfit.toFixed(2));
+ // console.log("ROI %:", roiPercentage.toFixed(2));
+ // console.log("Payback Period (years):", paybackPeriod.toFixed(2));
setRoiSummaryData({
productName: selectedProduct.productName,
- roiPercentage: parseFloat((ROIData / 100).toFixed(2)),
+ roiPercentage: parseFloat((roiPercentage / 100).toFixed(2)), // normalized to 0.x format
paybackPeriod: parseFloat(paybackPeriod.toFixed(2)),
- totalCost: TotalAnnualCost,
- revenueGenerated: RevenueForYear,
- netProfit: NetProfit > 0 ? NetProfit : 0,
- netLoss: NetProfit < 0 ? -NetProfit : 0
+ 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;
+
+ // 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;
+
+ // console.log("--- Fixed Product Count (" + productCount + ") ---");
+ // console.log("Cost per Unit:", costPerUnit.toFixed(2));
+ // console.log("Total Cost for " + productCount + " Units:", costForTargetUnits.toFixed(2));
+ // console.log("Revenue for " + productCount + " Units:", revenueForTargetUnits.toFixed(2));
+ // console.log("Profit:", netProfitForTarget.toFixed(2));
+ // console.log("Loss:", netLossForTarget.toFixed(2));
+
}
}, [inputValues, productionCapacityData]);
diff --git a/app/src/modules/simulation/analysis/productionCapacity/productionCapacityData.tsx b/app/src/modules/simulation/analysis/productionCapacity/productionCapacityData.tsx
index a77e221..5c376a7 100644
--- a/app/src/modules/simulation/analysis/productionCapacity/productionCapacityData.tsx
+++ b/app/src/modules/simulation/analysis/productionCapacity/productionCapacityData.tsx
@@ -1,37 +1,44 @@
import React, { useEffect } from 'react'
import { useInputValues, useProductionCapacityData, useThroughPutData } from '../../../../store/builder/store'
+import { usePlayButtonStore } from '../../../../store/usePlayButtonStore';
export default function ProductionCapacityData() {
const { throughputData } = useThroughPutData()
const { productionCapacityData, setProductionCapacityData } = useProductionCapacityData()
const { inputValues } = useInputValues();
+ const { isPlaying } = usePlayButtonStore();
useEffect(() => {
- if (inputValues === undefined || throughputData === undefined) return;
+ if (!isPlaying) {
+ setProductionCapacityData(0);
+ return;
+ }
+ if (!inputValues || throughputData === undefined) return;
const shiftLength = parseFloat(inputValues["Shift length"]);
- // console.log('shiftLength: ', shiftLength);
const shiftsPerDay = parseFloat(inputValues["Shifts / day"]);
- // console.log('shiftsPerDay: ', shiftsPerDay);
const workingDaysPerYear = parseFloat(inputValues["Working days / year"]);
- // console.log('workingDaysPerYear: ', workingDaysPerYear);
const yieldRate = parseFloat(inputValues["Yield rate"]);
- // console.log('yieldRate: ', yieldRate);
-
- if (!isNaN(shiftLength) && !isNaN(shiftsPerDay) && !isNaN(workingDaysPerYear) && !isNaN(yieldRate) && throughputData >= 0) {
- //Daily Output
+ if (!isNaN(shiftLength) && !isNaN(shiftsPerDay) && !isNaN(workingDaysPerYear) &&
+ !isNaN(yieldRate) && throughputData >= 0) {
+ // Total units produced per day before yield
const dailyProduction = throughputData * shiftLength * shiftsPerDay;
- console.log("DailyProduction: ", dailyProduction.toFixed(2));
- // Good units (after Yield)
- const afterYield = dailyProduction * (yieldRate / 100);
- console.log('afterYield: ', afterYield.toFixed(2));
- //Annual Output
- const annualProduction = afterYield * workingDaysPerYear;
- console.log('annualProduction: ', Number(annualProduction.toFixed(2)));
- //Production per Hour
+
+
+ // 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);
- console.log('productionPerHour: ', productionPerHour);
+
+
+ // Set the final capacity (units/hour)
setProductionCapacityData(Number(productionPerHour.toFixed(2)));
}
}, [throughputData, inputValues]);
diff --git a/app/src/modules/simulation/analysis/throughPut/throughPutData.tsx b/app/src/modules/simulation/analysis/throughPut/throughPutData.tsx
index 401d730..340e4e0 100644
--- a/app/src/modules/simulation/analysis/throughPut/throughPutData.tsx
+++ b/app/src/modules/simulation/analysis/throughPut/throughPutData.tsx
@@ -10,101 +10,107 @@ import { useConveyorStore } from '../../../../store/simulation/useConveyorStore'
import { useStorageUnitStore } from '../../../../store/simulation/useStorageUnitStore';
import { useMaterialStore } from '../../../../store/simulation/useMaterialStore';
import { usePauseButtonStore, usePlayButtonStore } from '../../../../store/usePlayButtonStore';
+import { is, set } from 'immer/dist/internal';
export default function ThroughPutData() {
const { selectedProduct } = useSelectedProduct();
const { products, getProductById } = useProductStore();
- const { armBots, incrementActiveTime, incrementIdleTime } = useArmBotStore();
+ const { armBots } = useArmBotStore();
const { vehicles } = useVehicleStore();
const { machines } = useMachineStore();
const { conveyors } = useConveyorStore();
const { storageUnits } = useStorageUnitStore();
const { materialHistory } = useMaterialStore();
-
const { machineCount, setMachineCount } = useMachineCount();
const { machineActiveTime, setMachineActiveTime } = useMachineUptime();
-
const { materialCycleTime, setMaterialCycleTime } = useMaterialCycle();
- const { processBar, setProcessBar } = useProcessBar();
-
- // const [totalActiveTime, setTotalActiveTime] = useState(0);
- const { setThroughputData } = useThroughPutData() // <=== ADD THIS
+ const { setProcessBar } = useProcessBar();
+ const { setThroughputData } = useThroughPutData()
const { isPlaying } = usePlayButtonStore();
// Setting machine count
+ let totalItems = 0;
+ let totalActiveTime = 0;
useEffect(() => {
- if (materialCycleTime <= 0) return
- let process: any = [];
- const fetchProductSequenceData = async () => {
- const productData = getProductById(selectedProduct.productId);
- if (productData) {
- const productSequenceData = await determineExecutionMachineSequences([productData])
- if (productSequenceData?.length > 0) {
- let totalItems = 0;
- let totalActiveTime = 0;
- productSequenceData.forEach((sequence) => {
- sequence.forEach((item) => {
+ if (!isPlaying) {
+ totalActiveTime = 0;
+ totalItems = 0;
+ setMachineCount(0);
+ setMachineActiveTime(0);
+ setMaterialCycleTime(0);
+ setProcessBar([]);
+ setThroughputData(0);
+ return;
+ } else {
+ let process: any = [];
+ const fetchProductSequenceData = async () => {
+ const productData = getProductById(selectedProduct.productId);
+ if (productData) {
+ const productSequenceData = await determineExecutionMachineSequences([productData])
+ if (productSequenceData?.length > 0) {
+ productSequenceData.forEach((sequence) => {
+ sequence.forEach((item) => {
+ if (item.type === "roboticArm") {
+ armBots.filter(arm => arm.modelUuid === item.modelUuid)
+ .forEach(arm => {
+ if (arm.activeTime >= 0) {
+ process.push({ modelid: arm.modelUuid, modelName: arm.modelName, activeTime: arm?.activeTime })
+ totalActiveTime += arm.activeTime;
- if (item.type === "roboticArm") {
- armBots.filter(arm => arm.modelUuid === item.modelUuid)
- .forEach(arm => {
- if (arm.activeTime >= 0) {
- process.push({ modelid: arm.modelUuid, modelName: arm.modelName, activeTime: arm?.activeTime })
- totalActiveTime += arm.activeTime;
- }
- });
- } else if (item.type === "vehicle") {
- vehicles.filter(vehicle => vehicle.modelUuid === item.modelUuid)
- .forEach(vehicle => {
- if (vehicle.activeTime >= 0) {
- process.push({ modelid: vehicle.modelUuid, modelName: vehicle.modelName, activeTime: vehicle?.activeTime })
+ }
+ });
+ } else if (item.type === "vehicle") {
+ vehicles.filter(vehicle => vehicle.modelUuid === item.modelUuid)
+ .forEach(vehicle => {
+ if (vehicle.activeTime >= 0) {
+ process.push({ modelid: vehicle.modelUuid, modelName: vehicle.modelName, activeTime: vehicle?.activeTime })
- totalActiveTime += vehicle.activeTime;
- }
- });
- } else if (item.type === "machine") {
- machines.filter(machine => machine.modelUuid === item.modelUuid)
- .forEach(machine => {
- if (machine.activeTime >= 0) {
- process.push({ modelid: machine.modelUuid, modelName: machine.modelName, activeTime: machine?.activeTime })
- totalActiveTime += machine.activeTime;
- }
- });
- } else if (item.type === "transfer") {
- conveyors.filter(conveyor => conveyor.modelUuid === item.modelUuid)
- .forEach(conveyor => {
- if (conveyor.activeTime >= 0) {
- totalActiveTime += conveyor.activeTime;
- }
- });
- } else if (item.type === "storageUnit") {
- storageUnits.filter(storage => storage.modelUuid === item.modelUuid)
- .forEach(storage => {
- if (storage.activeTime >= 0) {
- totalActiveTime += storage.activeTime;
+ totalActiveTime += vehicle.activeTime;
+ }
+ });
+ } else if (item.type === "machine") {
+ machines.filter(machine => machine.modelUuid === item.modelUuid)
+ .forEach(machine => {
+ if (machine.activeTime >= 0) {
+ process.push({ modelid: machine.modelUuid, modelName: machine.modelName, activeTime: machine?.activeTime })
+ totalActiveTime += machine.activeTime;
+ }
+ });
+ } else if (item.type === "transfer") {
+ conveyors.filter(conveyor => conveyor.modelUuid === item.modelUuid)
+ .forEach(conveyor => {
+ if (conveyor.activeTime >= 0) {
+ totalActiveTime += conveyor.activeTime;
+ }
+ });
+ } else if (item.type === "storageUnit") {
+ storageUnits.filter(storage => storage.modelUuid === item.modelUuid)
+ .forEach(storage => {
+ if (storage.activeTime >= 0) {
+ totalActiveTime += storage.activeTime;
- }
- });
- }
+ }
+ });
+ }
+ });
+
+ totalItems += sequence.length;
});
- totalItems += sequence.length;
- });
-
- setMachineCount(totalItems);
- setMachineActiveTime(totalActiveTime);
- let arr = process.map((item: any) => ({
- name: item.modelName,
- completed: Math.round((item.activeTime / totalActiveTime) * 100)
- }));
- setProcessBar(arr);
-
-
+ setMachineCount(totalItems);
+ setMachineActiveTime(totalActiveTime);
+ let arr = process.map((item: any) => ({
+ name: item.modelName,
+ completed: Math.round((item.activeTime / totalActiveTime) * 100)
+ }));
+ setProcessBar(arr);
+ }
}
- }
- };
+ };
- fetchProductSequenceData();
+ fetchProductSequenceData();
+ }
+ // if (materialCycleTime <= 0) return
}, [products, selectedProduct, getProductById, setMachineCount, materialCycleTime, armBots, vehicles, machines]);
// Setting material cycle time
@@ -112,9 +118,7 @@ export default function ThroughPutData() {
materialHistory.forEach((material) => {
const start = material.material.startTime ?? 0;
const end = material.material.endTime ?? 0;
-
if (start === 0 || end === 0) return;
-
const totalCycleTime = (end - start) / 1000; // Convert milliseconds to seconds
setMaterialCycleTime(Number(totalCycleTime.toFixed(2))); // Set the material cycle time in the store
});
@@ -124,26 +128,24 @@ export default function ThroughPutData() {
useEffect(() => {
if (machineActiveTime > 0 && materialCycleTime > 0 && machineCount > 0) {
- const avgProcessTime = (machineActiveTime / materialCycleTime) * 100;
- const throughput = (3600 / materialCycleTime) * machineCount * (avgProcessTime / 100);
- setThroughputData(throughput.toFixed(2)); // Set the throughput data in the store
-
-
- console.log('---Throughput Results---');
- console.log('Total Active Time:', machineActiveTime);
- console.log('Material Cycle Time:', materialCycleTime);
- console.log('Machine Count:', machineCount);
- console.log('Average Process Time (%):', avgProcessTime);
- console.log('Calculated Throughput:', throughput);
-
-
+ const utilization = machineActiveTime / 3600; // Active time per hour
+ const unitsPerMachinePerHour = 3600 / materialCycleTime;
+ const throughput = unitsPerMachinePerHour * machineCount * utilization;
+ setThroughputData(throughput.toFixed(2)); // Set throughput to state/store
+ // console.log('---Throughput Results---');
+ // console.log('Machine Active Time (s):', machineActiveTime);
+ // console.log('Material Cycle Time (s):', materialCycleTime);
+ // console.log('Machine Count:', machineCount);
+ // console.log('Utilization:', utilization);
+ // console.log('Throughput (units/hr):', throughput);
}
}, [machineActiveTime, materialCycleTime, machineCount]);
+
return (
<>
>
diff --git a/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx
index a985248..ae8c97f 100644
--- a/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx
+++ b/app/src/modules/simulation/machine/instances/machineInstance/machineInstance.tsx
@@ -32,7 +32,6 @@ function MachineInstance({ machineDetail }: { machineDetail: MachineStatus }) {
const reset = () => {
setCurrentPhase("idle");
- console.log("exit");
setMachineState(machineDetail.modelUuid, 'idle');
setMachineActive(machineDetail.modelUuid, false);
isIncrememtable.current = true;
diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
index 4b0829e..7be9002 100644
--- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
@@ -190,6 +190,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
activeSecondsElapsed.current = 0;
idleSecondsElapsed.current = 0;
previousTimeRef.current = null;
+
if (animationFrameIdRef.current !== null) {
cancelAnimationFrame(animationFrameIdRef.current);
animationFrameIdRef.current = null;