diff --git a/app/src/components/layout/sidebarRight/analysis/Analysis.tsx b/app/src/components/layout/sidebarRight/analysis/Analysis.tsx index 9b16186..cc0c597 100644 --- a/app/src/components/layout/sidebarRight/analysis/Analysis.tsx +++ b/app/src/components/layout/sidebarRight/analysis/Analysis.tsx @@ -3,6 +3,7 @@ import { AIIcon } from "../../../icons/ExportCommonIcons"; import RegularDropDown from "../../../ui/inputs/RegularDropDown"; import { AnalysisPresetsType } from "../../../../types/analysis"; import RenderAnalysisInputs from "./RenderAnalysisInputs"; +import { useInputValues } from "../../../../store/store"; const Analysis: React.FC = () => { const [selectedOption, setSelectedOption] = useState("Throughput time"); @@ -48,6 +49,10 @@ const Analysis: React.FC = () => { type: "default", inputs: { label: "Fixed costs", activeOption: "INR" }, }, + { + type: "default", + inputs: { label: "Initial Investment", activeOption: "INR" }, + }, { type: "default", inputs: { label: "Salvage value", activeOption: "Hrs" }, @@ -63,6 +68,8 @@ const Analysis: React.FC = () => { ], }; + const { inputValues, setInputValues, updateInputValue } = useInputValues(); + return (
@@ -88,10 +95,14 @@ const Analysis: React.FC = () => { presets={ AnalysisPresets[selectedOption as keyof AnalysisPresetsType] } + inputValues={inputValues} + onInputChange={(label, value) => { + updateInputValue(label, value); + }} />
- - + setInputValues({})} /> + setInputValues(inputValues)} />
Create Custom Analysis
diff --git a/app/src/components/layout/sidebarRight/analysis/RenderAnalysisInputs.tsx b/app/src/components/layout/sidebarRight/analysis/RenderAnalysisInputs.tsx index e14b542..0204582 100644 --- a/app/src/components/layout/sidebarRight/analysis/RenderAnalysisInputs.tsx +++ b/app/src/components/layout/sidebarRight/analysis/RenderAnalysisInputs.tsx @@ -6,12 +6,11 @@ import { AnalysisPresetsType } from "../../../../types/analysis"; interface InputRendererProps { keyName: string; presets: AnalysisPresetsType[keyof AnalysisPresetsType]; + inputValues: Record; // <-- Add this line + onInputChange: (label: string, value: string) => void; } -const RenderAnalysisInputs: React.FC = ({ - keyName, - presets, -}) => { +const RenderAnalysisInputs: React.FC = ({ keyName, presets,inputValues, onInputChange }) => { return (
{presets.map((preset, index) => { @@ -20,9 +19,9 @@ const RenderAnalysisInputs: React.FC = ({ {}} + onChange={(newValue) => onInputChange(preset.inputs.label, newValue)} /> ); } diff --git a/app/src/components/ui/analysis/ProductionCapacity.tsx b/app/src/components/ui/analysis/ProductionCapacity.tsx index 94ab1eb..e9242f6 100644 --- a/app/src/components/ui/analysis/ProductionCapacity.tsx +++ b/app/src/components/ui/analysis/ProductionCapacity.tsx @@ -21,9 +21,6 @@ const ThroughputSummary:React.FC = () => { }; const { machineActiveTime } = useMachineUptime(); - - - const energyConsumption = { energyConsumed: 456, unit: "KWH", diff --git a/app/src/components/ui/analysis/ThroughputSummary.tsx b/app/src/components/ui/analysis/ThroughputSummary.tsx index 1dfe184..a287a11 100644 --- a/app/src/components/ui/analysis/ThroughputSummary.tsx +++ b/app/src/components/ui/analysis/ThroughputSummary.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { useMachineCount, useMachineUptime, useMaterialCycle } from "../../../store/store"; +import { useMachineCount, useMachineUptime, useMaterialCycle, useThroughPutData } from "../../../store/store"; import { ThroughputSummaryIcon, } from "../../icons/analysis"; @@ -13,26 +13,32 @@ const ProductionCapacity = ({ }) => { - const { machineCount } = useMachineCount(); + const { machineActiveTime } = useMachineUptime(); const { materialCycleTime } = useMaterialCycle(); - + const { throughputData } = useThroughPutData() + + const progressPercent = machineActiveTime; - - + + const totalBars = 6; const barsToFill = Math.floor((progressPercent / 100) * totalBars); const partialFillPercent = - ((progressPercent / 100) * totalBars - barsToFill) * 100; - + ((progressPercent / 100) * totalBars - barsToFill) * 100; + const [isLoading, setIsLoading] = useState(false); - // const { machineCount, setMachineCount } = useMachineCount() - // const { machineActiveTime, setMachineActiveTime } = useMachineUptime() + useEffect(() => { - setIsLoading(true); - machineUtilization = machineActiveTime - console.log('machineActiveTime: ', machineActiveTime); - }, [machineActiveTime]) + if (throughputData > 0) { + console.log('machineActiveTime: ', machineActiveTime); + console.log('materialCycleTime: ', materialCycleTime); + console.log('throughputData: ', throughputData); + + setIsLoading(true); + } + + }, [throughputData]) return (
@@ -52,7 +58,7 @@ const ProductionCapacity = ({ <>
- {machineActiveTime} Units/hour + {throughputData} Units/hour
{/* Dynamic Progress Bar */} @@ -79,7 +85,7 @@ const ProductionCapacity = ({
Machine Utilization - 1 + {machineActiveTime} {/* {machineActiveTime} */}
diff --git a/app/src/components/ui/inputs/InputWithDropDown.tsx b/app/src/components/ui/inputs/InputWithDropDown.tsx index 3d42917..c6316d6 100644 --- a/app/src/components/ui/inputs/InputWithDropDown.tsx +++ b/app/src/components/ui/inputs/InputWithDropDown.tsx @@ -52,7 +52,8 @@ const InputWithDropDown: React.FC = ({ max={max} step={step} type="number" - defaultValue={value} + // defaultValue={value} + value={value} onChange={(e) => { onChange(e.target.value); }} diff --git a/app/src/modules/simulation/analysis/ROI/roiData.tsx b/app/src/modules/simulation/analysis/ROI/roiData.tsx new file mode 100644 index 0000000..22b524d --- /dev/null +++ b/app/src/modules/simulation/analysis/ROI/roiData.tsx @@ -0,0 +1,84 @@ +import React, { useEffect } from 'react' +import { useInputValues, useProductionCapacityData } from '../../../../store/store'; + +export default function ROIData() { + const { inputValues } = useInputValues(); + const { productionCapacityData } = useProductionCapacityData() + + useEffect(() => { + if (inputValues === undefined) 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 materialCost = parseFloat(inputValues["Material cost"]); + const productionPeriod = parseFloat(inputValues["Production period"]); + const salvageValue = parseFloat(inputValues["Salvage value"]); + const sellingPrice = parseFloat(inputValues["Selling price"]); + const initialInvestment = parseFloat(inputValues["Initial Investment"]); + const shiftLength = parseFloat(inputValues["Shift length"]); + 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) { + + 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 + 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); + + //Total Anuual Cost + let TotalAnnualCost = MaterialCost + LaborCost + EnergyCost + MaintenceCost; + console.log('TotalAnnualCost: ', TotalAnnualCost); + + //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); + + + // Payback Period + const paybackPeriod = initialInvestment / ProfitforYear; + console.log('paybackPeriod: ', paybackPeriod); + + + } + + }, [inputValues, productionCapacityData]); + + return ( + <> + ) +} + + diff --git a/app/src/modules/simulation/analysis/productionCapacity/productionCapacityData.tsx b/app/src/modules/simulation/analysis/productionCapacity/productionCapacityData.tsx new file mode 100644 index 0000000..b9739d3 --- /dev/null +++ b/app/src/modules/simulation/analysis/productionCapacity/productionCapacityData.tsx @@ -0,0 +1,48 @@ +import React, { useEffect } from 'react' +import { useInputValues, useProductionCapacityData, useThroughPutData } from '../../../../store/store' + +export default function ProductionCapacityData() { + const { throughputData } = useThroughPutData() + const { productionCapacityData, setProductionCapacityData } = useProductionCapacityData() + const { inputValues } = useInputValues(); + + useEffect(() => { + if (inputValues === undefined || 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 + 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: ', annualProduction.toFixed(2)); + setProductionCapacityData(annualProduction); + } + }, [throughputData, inputValues]); + + + + useEffect(() => { + + + }, []) + + return ( + <> + ) +} + + diff --git a/app/src/modules/simulation/analysis/simulationAnalysis.tsx b/app/src/modules/simulation/analysis/simulationAnalysis.tsx index 531c103..db5a88b 100644 --- a/app/src/modules/simulation/analysis/simulationAnalysis.tsx +++ b/app/src/modules/simulation/analysis/simulationAnalysis.tsx @@ -1,10 +1,23 @@ -import React from 'react' -import ThroughPut from './throughPut/throughPut' +import React, { useEffect } from 'react' +import { usePlayButtonStore } from '../../../store/usePlayButtonStore' +import ProductionCapacityData from './productionCapacity/productionCapacityData' +import ThroughPutData from './throughPut/throughPutData' +import ROIData from './ROI/roiData' function SimulationAnalysis() { + const { isPlaying } = usePlayButtonStore() + // useEffect(()=>{ + // if (isPlaying) { + // + // } else { + // + // } + // },[isPlaying]) return ( <> - + + + ) } diff --git a/app/src/modules/simulation/analysis/throughPut/throughPut.tsx b/app/src/modules/simulation/analysis/throughPut/throughPut.tsx deleted file mode 100644 index 8a9e331..0000000 --- a/app/src/modules/simulation/analysis/throughPut/throughPut.tsx +++ /dev/null @@ -1,239 +0,0 @@ -// import React, { useEffect, useState } from 'react'; -// import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; -// import { useProductStore } from '../../../../store/simulation/useProductStore'; -// import { determineExecutionMachineSequences } from '../../simulator/functions/determineExecutionMachineSequences'; -// import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; -// import { useMachineCount, useMachineUptime, useMaterialCycle } from '../../../../store/store'; -// import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; -// import { useMachineStore } from '../../../../store/simulation/useMachineStore'; -// import { useConveyorStore } from '../../../../store/simulation/useConveyorStore'; -// import { useStorageUnitStore } from '../../../../store/simulation/useStorageUnitStore'; -// import { useMaterialStore } from '../../../../store/simulation/useMaterialStore'; - -// export default function ThroughPut() { -// const { selectedProduct } = useSelectedProduct(); -// const { products, getProductById } = useProductStore(); -// const { armBots } = useArmBotStore(); -// const { vehicles } = useVehicleStore(); -// const { machines } = useMachineStore(); -// const { conveyors } = useConveyorStore(); -// const { storageUnits } = useStorageUnitStore(); -// const { materials } = useMaterialStore(); - -// const { machineCount, setMachineCount } = useMachineCount(); -// const { setMachineActiveTime } = useMachineUptime(); -// const { materialCycleTime, setMaterialCycleTime } = useMaterialCycle(); - -// const [totalActiveTime, setTotalActiveTime] = useState(0); - -// // 1. Setting static active times and counting machines -// useEffect(() => { -// const productData = getProductById(selectedProduct.productId); -// if (productData) { -// const productSequenceData = determineExecutionMachineSequences([productData]); -// if (productSequenceData?.length > 0) { -// let totalItems = 0; - -// productSequenceData.forEach((sequence) => { -// totalItems += sequence.length; -// sequence.forEach((item) => { -// if (item.type === "roboticArm") { -// armBots.filter(arm => arm.modelUuid === item.modelUuid) -// .forEach(arm => { -// if (arm.activeTime >= 0) { -// -// } -// }); -// } else if (item.type === "vehicle") { -// vehicles.filter(vehicle => vehicle.modelUuid === item.modelUuid) -// .forEach(vehicle => { -// if (vehicle.activeTime >= 0) { -// vehicle.activeTime = 10; // static -// } -// }); -// } else if (item.type === "machine") { -// machines.filter(machine => machine.modelUuid === item.modelUuid) -// .forEach(machine => { -// if (machine.activeTime >= 0) { -// machine.activeTime = 12; // static -// } -// }); -// } else if (item.type === "transfer") { -// conveyors.filter(conveyor => conveyor.modelUuid === item.modelUuid) -// .forEach(conveyor => { -// if (conveyor.activeTime >= 0) { -// conveyor.activeTime = 5; // static -// } -// }); -// } else if (item.type === "storageUnit") { -// storageUnits.filter(storage => storage.modelUuid === item.modelUuid) -// .forEach(storage => { -// if (storage.activeTime >= 0) { -// storage.activeTime = 8; // static -// } -// }); -// } -// }); -// }); - -// setMachineCount(totalItems); -// } -// } -// }, [products, selectedProduct, armBots, vehicles, machines, conveyors, storageUnits, getProductById, setMachineCount]); - -// // 2. Set material cycle time (static also) -// useEffect(() => { -// materials.forEach((material) => { -// material.startTime = 50; -// material.endTime = 100; -// const totalCycleTime = material.endTime - material.startTime; -// setMaterialCycleTime(totalCycleTime); -// }); -// }, [materials, setMaterialCycleTime]); - -// // 3. Sum of all activeTimes after static values are set -// useEffect(() => { -// let sum = 0; -// armBots.forEach(arm => { if (arm.activeTime > 0) sum += arm.activeTime; }); -// vehicles.forEach(vehicle => { if (vehicle.activeTime > 0) sum += vehicle.activeTime; }); -// machines.forEach(machine => { if (machine.activeTime > 0) sum += machine.activeTime; }); -// conveyors.forEach(conveyor => { if (conveyor.activeTime > 0) sum += conveyor.activeTime; }); -// storageUnits.forEach(storage => { if (storage.activeTime > 0) sum += storage.activeTime; }); - -// setTotalActiveTime(sum); -// setMachineActiveTime(sum); - -// }, [armBots, vehicles, machines, conveyors, storageUnits, setMachineActiveTime]); - -// // 4. Calculate throughput when activeTime and materialCycleTime are ready -// useEffect(() => { -// if (totalActiveTime > 0 && materialCycleTime > 0) { -// const avgProcessTime = (totalActiveTime / materialCycleTime) * 100; -// -// } -// }, [totalActiveTime, materialCycleTime]); - -// return ( -// <> -// -// ); -// } - - -import React, { useEffect, useState } from 'react'; -import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; -import { useProductStore } from '../../../../store/simulation/useProductStore'; -import { determineExecutionMachineSequences } from '../../simulator/functions/determineExecutionMachineSequences'; -import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; -import { useMachineCount, useMachineUptime, useMaterialCycle } from '../../../../store/store'; -import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; -import { useMachineStore } from '../../../../store/simulation/useMachineStore'; -import { useConveyorStore } from '../../../../store/simulation/useConveyorStore'; -import { useStorageUnitStore } from '../../../../store/simulation/useStorageUnitStore'; -import { useMaterialStore } from '../../../../store/simulation/useMaterialStore'; - -export default function ThroughPut() { - const { selectedProduct } = useSelectedProduct(); - const { products, getProductById } = useProductStore(); - const { armBots } = useArmBotStore(); - const { vehicles } = useVehicleStore(); - const { machines } = useMachineStore(); - const { conveyors } = useConveyorStore(); - const { storageUnits } = useStorageUnitStore(); - const { materials } = useMaterialStore(); - - const { setMachineCount } = useMachineCount(); - const { machineActiveTime, setMachineActiveTime } = useMachineUptime(); - console.log('machineActiveTime: ', machineActiveTime); - const { setMaterialCycleTime } = useMaterialCycle(); - - const [totalActiveTime, setTotalActiveTime] = useState(0); - const [throughputData, setThroughputData] = useState(0); // <=== ADD THIS - - // Setting machine count - useEffect(() => { - const productData = getProductById(selectedProduct.productId); - if (productData) { - const productSequenceData = determineExecutionMachineSequences([productData]); - if (productSequenceData?.length > 0) { - let totalItems = 0; - productSequenceData.forEach((sequence) => { - totalItems += sequence.length; - }); - setMachineCount(totalItems); - } - } - }, [products, selectedProduct, getProductById, setMachineCount]); - - // Setting material cycle time - useEffect(() => { - materials.forEach(() => { - const staticStartTime = 50; - const staticEndTime = 100; - const totalCycleTime = staticEndTime - staticStartTime; - setMaterialCycleTime(totalCycleTime); - }); - }, [materials, setMaterialCycleTime]); - - // Calculate Sum, Machine Uptime and Throughput - useEffect(() => { - let sum = 0; - - - armBots.forEach(arm => { - - if (arm.activeTime > 0) sum += arm.activeTime; - }); - - vehicles.forEach(vehicle => { - if (vehicle.activeTime > 0) sum += 10; // static - }); - - machines.forEach(machine => { - if (machine.activeTime > 0) sum += 12; // static - }); - - conveyors.forEach(conveyor => { - if (conveyor.activeTime > 0) sum += 5; // static - }); - - storageUnits.forEach(storage => { - if (storage.activeTime > 0) sum += 8; // static - }); - - - - const avgProcessTime = 100 - 50; // static 50 - - const machineUptime = (sum / avgProcessTime) * 100; - console.log('machineUptime: ', machineUptime); - - - - const machineCount = 3; // static - const throughput = (3600 / avgProcessTime) * machineCount * (machineUptime / 100); // **IMPORTANT divide by 100 for %** - - - - setTotalActiveTime(sum); - setMachineActiveTime(machineUptime) - setMaterialCycleTime(throughput) - // setMachineActiveTime(sum); - setThroughputData(throughput); // Save it properly here - - }, [armBots, vehicles, machines, conveyors, storageUnits, setMachineActiveTime]); - - // Just display throughput when ready - useEffect(() => { - - if (throughputData > 0) { - - } - }, [throughputData]); - - return ( - <> - - ); -} - diff --git a/app/src/modules/simulation/analysis/throughPut/throughPutData.tsx b/app/src/modules/simulation/analysis/throughPut/throughPutData.tsx new file mode 100644 index 0000000..b7d6130 --- /dev/null +++ b/app/src/modules/simulation/analysis/throughPut/throughPutData.tsx @@ -0,0 +1,208 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; +import { useProductStore } from '../../../../store/simulation/useProductStore'; +import { determineExecutionMachineSequences } from '../../simulator/functions/determineExecutionMachineSequences'; +import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; +import { useMachineCount, useMachineUptime, useMaterialCycle, useThroughPutData } from '../../../../store/store'; +import { useVehicleStore } from '../../../../store/simulation/useVehicleStore'; +import { useMachineStore } from '../../../../store/simulation/useMachineStore'; +import { useConveyorStore } from '../../../../store/simulation/useConveyorStore'; +import { useStorageUnitStore } from '../../../../store/simulation/useStorageUnitStore'; +import { useMaterialStore } from '../../../../store/simulation/useMaterialStore'; +import { usePauseButtonStore, usePlayButtonStore } from '../../../../store/usePlayButtonStore'; + +export default function ThroughPutData() { + const { selectedProduct } = useSelectedProduct(); + const { products, getProductById } = useProductStore(); + const { armBots, incrementActiveTime, incrementIdleTime } = useArmBotStore(); + const { vehicles } = useVehicleStore(); + const { machines } = useMachineStore(); + const { conveyors } = useConveyorStore(); + const { storageUnits } = useStorageUnitStore(); + const { materials } = useMaterialStore(); + + const { machineCount, setMachineCount } = useMachineCount(); + const { machineActiveTime, setMachineActiveTime } = useMachineUptime(); + + const { materialCycleTime, setMaterialCycleTime } = useMaterialCycle(); + + // const [totalActiveTime, setTotalActiveTime] = useState(0); + const { setThroughputData } = useThroughPutData() // <=== ADD THIS + const { isPlaying } = usePlayButtonStore(); + + // Setting machine count + useEffect(() => { + if (materialCycleTime < 0) return + // console.log('materialCycleTime: ', materialCycleTime); + const fetchProductSequenceData = async () => { + const productData = getProductById(selectedProduct.productId); + if (productData) { + const productSequenceData = await determineExecutionMachineSequences([productData]); + // console.log('productSequenceData: ', productSequenceData); + + if (productSequenceData?.length > 0) { + let totalItems = 0; + let totalActiveTime = 0; + + productSequenceData.forEach((sequence) => { + // console.log('sequence: ', sequence); + + sequence.forEach((item) => { + if (item.type === "roboticArm") { + armBots.filter(arm => arm.modelUuid === item.modelUuid) + .forEach(arm => { + + if (arm.activeTime >= 0) { + totalActiveTime += arm.activeTime; + } + }); + } else if (item.type === "vehicle") { + vehicles.filter(vehicle => vehicle.modelUuid === item.modelUuid) + .forEach(vehicle => { + + if (vehicle.activeTime >= 0) { + // totalActiveTime += vehicle.activeTime; + // totalActiveTime += 10; + } + }); + } else if (item.type === "machine") { + machines.filter(machine => machine.modelUuid === item.modelUuid) + .forEach(machine => { + if (machine.activeTime >= 0) { + // totalActiveTime += machine.activeTime; + // totalActiveTime += 12; + } + }); + } else if (item.type === "transfer") { + conveyors.filter(conveyor => conveyor.modelUuid === item.modelUuid) + .forEach(conveyor => { + if (conveyor.activeTime >= 0) { + // totalActiveTime += conveyor.activeTime; + // totalActiveTime += 7; + } + }); + } else if (item.type === "storageUnit") { + storageUnits.filter(storage => storage.modelUuid === item.modelUuid) + .forEach(storage => { + if (storage.activeTime >= 0) { + // totalActiveTime += storage.activeTime; + // totalActiveTime += 9; + } + }); + } + }); + + totalItems += sequence.length; + }); + + setMachineCount(totalItems); + setMachineActiveTime(totalActiveTime); + + } + } + }; + + fetchProductSequenceData(); + }, [products, selectedProduct, getProductById, setMachineCount, isPlaying, armBots, materialCycleTime]); + + // Setting material cycle time + useEffect(() => { + materials.map((material) => { + // console.log('material: ', material); + // const totalCycleTime = material.endTime - material.startTime;//dynamic + const staticStartTime = 50; + const staticEndTime = 100; + const totalCycleTime = staticEndTime - staticStartTime; + setMaterialCycleTime(totalCycleTime) + + }) + }, [materials]); + + + useEffect(() => { + if (machineActiveTime > 0 && materialCycleTime > 0 && machineCount > 0) { + const avgProcessTime = (machineActiveTime / materialCycleTime) * 100; // % value + const throughput = (3600 / materialCycleTime) * machineCount * (avgProcessTime / 100); // βœ… division by 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); + } + }, [machineActiveTime, materialCycleTime, machineCount]); + + + + return ( + <> + + ); +} + + + + // useEffect(() => { + // if (!isPlaying) return; + + // const intervalMs = 1000 + + // if (!armBot.isActive && armBot.state == "idle" && (currentPhase == "rest" || currentPhase == "init") && !isIdleRef.current) { + // isIdleRef.current = true + + // // Stop the timer + // // 🚨 1. Clear Active Timer + // if (activeTimerId.current) { + // clearInterval(activeTimerId.current); + // activeTimerId.current = null; + // } + // incrementActiveTime(armBot.modelUuid, activeSecondsElapsed.current) + // console.log(`βœ… Active Cycle completed in ${activeSecondsElapsed.current} seconds`); + + // // 🚨 2. Reset active timer seconds after logging + // // activeSecondsElapsed.current = 0; + + // // 🚨 3. Start Idle Timer (clean old idle timer first) + // if (idleTimerId.current) { + // clearInterval(idleTimerId.current); + // idleTimerId.current = null; + // } + + // idleSecondsElapsed.current = 0; + // idleTimerId.current = setInterval(() => { + // if (!isPausedRef.current) { + // idleSecondsElapsed.current += 1; + // console.log(`πŸ•’ Idle Timer: ${idleSecondsElapsed.current} seconds`); + // } + // }, intervalMs); + // } + // if (armBot.isActive && armBot.state != "idle" && currentPhase !== "rest" && armBot.currentAction && isIdleRef.current) { + // isIdleRef.current = false + + // if (armBot.currentAction) { + // // 🚨 Clear Idle Timer + // if (idleTimerId.current) { + // clearInterval(idleTimerId.current); + // idleTimerId.current = null; + // } + // incrementIdleTime(armBot.modelUuid, idleSecondsElapsed.current) + // console.log(`πŸ•’ Idle Cycle completed in: ${idleSecondsElapsed.current} seconds`); + // idleSecondsElapsed.current = 0; + + // // 🚨 Start Active Timer + // if (activeTimerId.current) { + // clearInterval(activeTimerId.current); + // } + // // activeSecondsElapsed.current = 0; + // activeTimerId.current = setInterval(() => { + // if (!isPausedRef.current) { + // activeSecondsElapsed.current += 1 + // console.log(`πŸ•’ Active Timer: ${activeSecondsElapsed.current} seconds`); + // } + // }, intervalMs); + // } + // } + + // }, [armBot, currentPhase, isPlaying]) \ No newline at end of file diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx index d498783..6de5f1d 100644 --- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx +++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx @@ -29,7 +29,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { const isIdleRef = useRef(false); let startTime: number; - const { setArmBotActive, setArmBotState, removeCurrentAction,incrementActiveTime,incrementIdleTime } = useArmBotStore(); + const { armBots, setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = useArmBotStore(); const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore(); const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore(); const { setIsVisible, getMaterialById } = useMaterialStore(); @@ -42,10 +42,10 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { const { speed } = useAnimationPlaySpeed(); const activeSecondsElapsed = useRef(0); - const activeTimerId = useRef | null>(null); - const idleSecondsElapsed = useRef(0); - const idleTimerId = useRef | null>(null); + + const animationFrameIdRef = useRef(null); + const previousTimeRef = useRef(null); function firstFrame() { startTime = performance.now(); @@ -168,12 +168,10 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } useEffect(() => { - isPausedRef.current = isPaused; }, [isPaused]); useEffect(() => { - isSpeedRef.current = speed; }, [speed]); @@ -189,12 +187,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { isPausedRef.current = false pauseTimeRef.current = null startTime = 0 - clearInterval(activeTimerId.current!); - clearInterval(idleTimerId.current!); - activeTimerId.current = null; activeSecondsElapsed.current = 0; idleSecondsElapsed.current = 0; - idleTimerId.current = null; + previousTimeRef.current = null; + if (animationFrameIdRef.current !== null) { + cancelAnimationFrame(animationFrameIdRef.current); + animationFrameIdRef.current = null; + } const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone ); if (targetBones && isPlaying) { @@ -207,186 +206,65 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } }, [isReset, isPlaying]) - useEffect(() => { - if (!isPlaying) return; - const intervalMs = 1000 - - if (!armBot.isActive && armBot.state == "idle" && (currentPhase == "rest" || currentPhase == "init") && !isIdleRef.current) { - isIdleRef.current = true - - // Stop the timer - // 🚨 1. Clear Active Timer - if (activeTimerId.current) { - clearInterval(activeTimerId.current); - activeTimerId.current = null; + function animate(currentTime: number) { + if (previousTimeRef.current === null) { + previousTimeRef.current = currentTime; + } + const deltaTime = (currentTime - previousTimeRef.current) / 1000; + previousTimeRef.current = currentTime; + if (armBot.isActive) { + if (!isPausedRef.current) { + activeSecondsElapsed.current += deltaTime * isSpeedRef.current; + // console.log(' activeSecondsElapsed.current: ', activeSecondsElapsed.current); } - incrementActiveTime(armBot.modelUuid, activeSecondsElapsed.current) - // console.log(`βœ… Active Cycle completed in ${activeSecondsElapsed.current} seconds`); - - // 🚨 2. Reset active timer seconds after logging - activeSecondsElapsed.current = 0; - - // 🚨 3. Start Idle Timer (clean old idle timer first) - if (idleTimerId.current) { - clearInterval(idleTimerId.current); - idleTimerId.current = null; - } - - idleSecondsElapsed.current = 0; - idleTimerId.current = setInterval(() => { - if (!isPausedRef.current) { - idleSecondsElapsed.current += 1; - // console.log(`πŸ•’ Idle Timer: ${idleSecondsElapsed.current} seconds`); - } - }, intervalMs); - } else if (armBot.isActive && armBot.state != "idle" && currentPhase !== "rest" && armBot.currentAction && isIdleRef.current) { - isIdleRef.current = false - - if (armBot.currentAction) { - // 🚨 Clear Idle Timer - if (idleTimerId.current) { - clearInterval(idleTimerId.current); - idleTimerId.current = null; - } - incrementIdleTime(armBot.modelUuid, idleSecondsElapsed.current) - // console.log(`πŸ•’ Idle Cycle completed in: ${idleSecondsElapsed.current} seconds`); - idleSecondsElapsed.current = 0; - - // 🚨 Start Active Timer - if (activeTimerId.current) { - clearInterval(activeTimerId.current); - } - activeSecondsElapsed.current = 0; - activeTimerId.current = setInterval(() => { - if (!isPausedRef.current) { - activeSecondsElapsed.current += 1 - // console.log(`πŸ•’ Active Timer: ${activeSecondsElapsed.current} seconds`); - } - }, intervalMs); + } else { + if (!isPausedRef.current) { + idleSecondsElapsed.current += deltaTime * isSpeedRef.current; + // console.log('idleSecondsElapsed.current: ', idleSecondsElapsed.current); } } + animationFrameIdRef.current = requestAnimationFrame(animate); + } - }, [armBot, currentPhase, isPlaying]) + useEffect(() => { + if (!isPlaying) return + if (!armBot.isActive && armBot.state === "idle" && (currentPhase === "rest" || currentPhase === "init")) { + cancelAnimationFrame(animationFrameIdRef.current!); + animationFrameIdRef.current = null; + const roundedActiveTime = Math.round(activeSecondsElapsed.current); // Get the final rounded active time + console.log('Final Active Time:', roundedActiveTime, 'seconds'); + incrementActiveTime(armBot.modelUuid, roundedActiveTime); + activeSecondsElapsed.current = 0; - // useEffect(() => { - // if (!isPlaying) return; - // const now = () => performance.now(); + } else if (armBot.isActive && armBot.state !== "idle" && currentPhase !== "rest" && armBot.currentAction) { + cancelAnimationFrame(animationFrameIdRef.current!); + animationFrameIdRef.current = null; + const roundedIdleTime = Math.round(idleSecondsElapsed.current); // Get the final rounded idle time + console.log('Final Idle Time:', roundedIdleTime, 'seconds'); + incrementIdleTime(armBot.modelUuid, roundedIdleTime); + idleSecondsElapsed.current = 0; + } + if (animationFrameIdRef.current === null) { + animationFrameIdRef.current = requestAnimationFrame(animate); + } - // const startActiveTimer = () => { - // let lastTime = now(); + return () => { + if (animationFrameIdRef.current !== null) { + cancelAnimationFrame(animationFrameIdRef.current); + animationFrameIdRef.current = null; // Reset the animation frame ID + } + }; - // const update = () => { - // if (!isPausedRef.current) { - // const currentTime = now(); - // const delta = currentTime - lastTime; - // activeSecondsElapsed.current += delta / 1000; - // console.log(`πŸ•’ Active Timer: ${activeSecondsElapsed.current.toFixed(2)} seconds`); - // lastTime = currentTime; - // } else { + }, [armBot.isActive, armBot.state, currentPhase]) - // lastTime = now(); - // } + useEffect(() => { - // if (!isIdleRef.current) { - // requestAnimationFrame(update); - // } - // }; + console.log('armBots: ', armBots); + }, [armBots]) - // activeSecondsElapsed.current = 0; - // update(); - // }; - - // const startIdleTimer = () => { - // let lastTime = now(); - - // const update = () => { - // if (!isPausedRef.current) { - // const currentTime = now(); - // const delta = currentTime - lastTime; - // idleSecondsElapsed.current += delta / 1000; - // console.log(`πŸ•’ Idle Timer: ${idleSecondsElapsed.current.toFixed(2)} seconds`); - // lastTime = currentTime; - // } else { - // lastTime = now(); - // } - - // if (isIdleRef.current) { - // requestAnimationFrame(update); - // } - // }; - - // idleSecondsElapsed.current = 0; - // update(); - // }; - - // // State transition logic - // if (!armBot.isActive && armBot.state === "idle" && (currentPhase === "rest" || currentPhase === "init") && !isIdleRef.current) { - // isIdleRef.current = true; - // console.log(`βœ… Active Cycle completed in ${activeSecondsElapsed.current.toFixed(2)} seconds`); - // startIdleTimer(); - // } - // else if (armBot.isActive && armBot.state !== "idle" && currentPhase !== "rest" && armBot.currentAction && isIdleRef.current) { - // isIdleRef.current = false; - // console.log(`πŸ•’ Idle Cycle completed in: ${idleSecondsElapsed.current.toFixed(2)} seconds`); - // startActiveTimer(); - // } - - // }, [armBot, currentPhase, isPlaying, isIdleRef.current, isPausedRef.current]); - - - - // useEffect(() => { - // if (!isPlaying) return; - - // let frameId: number | null = null; - // let lastTime = performance.now(); - - // const tick = (currentTime: number) => { - // const delta = currentTime - lastTime; - // lastTime = currentTime; - - // const secondsToAdd = delta / 1000; // ⚑️REAL time passed (NO speed multiplication) - - // if (!isPausedRef.current) { - // // Update Timer - // if (isIdleRef.current) { - // idleSecondsElapsed.current += secondsToAdd; - // console.log(`πŸ•’ Idle Timer: ${idleSecondsElapsed.current.toFixed(2)} seconds (Speed: ${isSpeedRef.current}x)`); - // } else { - // activeSecondsElapsed.current += secondsToAdd; - // console.log(`πŸ•’ Active Timer: ${activeSecondsElapsed.current.toFixed(2)} seconds (Speed: ${isSpeedRef.current}x)`); - // } - // } - // frameId = requestAnimationFrame(tick); - // }; - // // Detect mode switch - // if (!armBot.isActive && armBot.state === "idle" && (currentPhase === "rest" || currentPhase === "init") && !isIdleRef.current) { - // isIdleRef.current = true; - - // console.log(`βœ… Active Cycle completed in ${activeSecondsElapsed.current.toFixed(2)} seconds`); - // activeSecondsElapsed.current = 0; - // idleSecondsElapsed.current = 0; - // } else if (armBot.isActive && armBot.state !== "idle" && currentPhase !== "rest" && armBot.currentAction && isIdleRef.current) { - // isIdleRef.current = false; - // console.log(`πŸ•’ Idle Cycle completed in: ${idleSecondsElapsed.current.toFixed(2)} seconds`); - // idleSecondsElapsed.current = 0; - // activeSecondsElapsed.current = 0; - // } - - // frameId = requestAnimationFrame(tick); - - // return () => { - // if (frameId !== null) { - // cancelAnimationFrame(frameId); - // } - // }; - // }, [armBot, currentPhase, isPlaying]); - - - useEffect(() => { const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid); if (targetMesh) { @@ -409,7 +287,6 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } //Waiting for trigger. else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) { - logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction") } //Moving to pickup point @@ -449,12 +326,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { pauseTimeRef.current = null isPausedRef.current = false startTime = 0 - clearInterval(activeTimerId.current!); - clearInterval(idleTimerId.current!); - activeTimerId.current = null; activeSecondsElapsed.current = 0; idleSecondsElapsed.current = 0; - idleTimerId.current = null; + previousTimeRef.current = null; + if (animationFrameIdRef.current !== null) { + cancelAnimationFrame(animationFrameIdRef.current); + animationFrameIdRef.current = null; + } removeCurrentAction(armBot.modelUuid) } @@ -492,38 +370,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) { } else if (armBot.isActive && armBot.state == "running" && currentPhase == "end-to-rest") { logStatus(armBot.modelUuid, "Callback triggered: rest, cycle completed."); - // Stop the timer - // 🚨 1. Clear Active Timer - // if (activeTimerId.current) { - // clearInterval(activeTimerId.current); - // activeTimerId.current = null; - // } - // console.log(`βœ… Active Cycle completed in ${activeSecondsElapsed.current} seconds`); - - // // 🚨 2. Reset active timer seconds after logging - // activeSecondsElapsed.current = 0; - - // // 🚨 3. Start Idle Timer (clean old idle timer first) - // if (idleTimerId.current) { - // clearInterval(idleTimerId.current); - // idleTimerId.current = null; - // } - - // idleSecondsElapsed.current = 0; - // idleTimerId.current = setInterval(() => { - // if (!isPausedRef.current) { - // idleSecondsElapsed.current += 1; - // console.log(`πŸ•’ Idle Timer: ${idleSecondsElapsed.current} seconds`); - // } - // }, 1000); - - setArmBotActive(armBot.modelUuid, false) setArmBotState(armBot.modelUuid, "idle") setCurrentPhase("rest"); setPath([]) } } + const logStatus = (id: string, status: string) => { // console.log('status: ', status); } diff --git a/app/src/modules/simulation/simulator/simulator.tsx b/app/src/modules/simulation/simulator/simulator.tsx index d6ff2fa..aa0b9f6 100644 --- a/app/src/modules/simulation/simulator/simulator.tsx +++ b/app/src/modules/simulation/simulator/simulator.tsx @@ -3,22 +3,27 @@ import { useProductStore } from '../../../store/simulation/useProductStore'; import { useActionHandler } from '../actions/useActionHandler'; import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore'; import { determineExecutionOrder } from './functions/determineExecutionOrder'; +import { useSelectedProduct } from '../../../store/simulation/useSimulationStore'; function Simulator() { - const { products } = useProductStore(); + const { products, getProductById } = useProductStore(); const { handleAction } = useActionHandler(); + const { selectedProduct } = useSelectedProduct(); const { isPlaying } = usePlayButtonStore(); const { isReset } = useResetButtonStore(); useEffect(() => { - if (!isPlaying || isReset) return; + if (!isPlaying || isReset || !selectedProduct.productId) return; - const executionOrder = determineExecutionOrder(products); + const product = getProductById(selectedProduct.productId); + if (!product) return; + + const executionOrder = determineExecutionOrder([product]); executionOrder.forEach(point => { const action = 'actions' in point ? point.actions[0] : point.action; handleAction(action); }); - }, [products, isPlaying, isReset]); + }, [products, isPlaying, isReset, selectedProduct]); return ( diff --git a/app/src/store/store.ts b/app/src/store/store.ts index 226e4e7..6192bdf 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -67,8 +67,8 @@ export const useWalls = create((set: any) => ({ })); export const useRoomsState = create((set: any) => ({ - roomsState: [], - setRoomsState: (x: any) => set(() => ({ walls: x })), + roomsState: [], + setRoomsState: (x: any) => set(() => ({ walls: x })), })); export const useZones = create((set: any) => ({ @@ -448,3 +448,31 @@ export const useMaterialCycle = create((set: any) => ({ materialCycleTime: 0, setMaterialCycleTime: (x: any) => set({ materialCycleTime: x }), })); + +export const useThroughPutData = create((set: any) => ({ + throughputData: 0, + setThroughputData: (x: any) => set({ throughputData: x }), +})); +export const useProductionCapacityData = create((set: any) => ({ + productionCapacityData: 0, + setProductionCapacityData: (x: any) => set({ productionCapacityData: x }), +})); + + +type InputValuesStore = { + inputValues: Record; + setInputValues: (values: Record) => void; + updateInputValue: (label: string, value: string) => void; // <- New +}; + +export const useInputValues = create((set) => ({ + inputValues: {}, + setInputValues: (values) => set({ inputValues: values }), + updateInputValue: (label, value) => + set((state) => ({ + inputValues: { + ...state.inputValues, + [label]: value, + }, + })), +})); \ No newline at end of file