analysis data
This commit is contained in:
@@ -3,6 +3,7 @@ import { AIIcon } from "../../../icons/ExportCommonIcons";
|
|||||||
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
|
import RegularDropDown from "../../../ui/inputs/RegularDropDown";
|
||||||
import { AnalysisPresetsType } from "../../../../types/analysis";
|
import { AnalysisPresetsType } from "../../../../types/analysis";
|
||||||
import RenderAnalysisInputs from "./RenderAnalysisInputs";
|
import RenderAnalysisInputs from "./RenderAnalysisInputs";
|
||||||
|
import { useInputValues } from "../../../../store/store";
|
||||||
|
|
||||||
const Analysis: React.FC = () => {
|
const Analysis: React.FC = () => {
|
||||||
const [selectedOption, setSelectedOption] = useState("Throughput time");
|
const [selectedOption, setSelectedOption] = useState("Throughput time");
|
||||||
@@ -48,6 +49,10 @@ const Analysis: React.FC = () => {
|
|||||||
type: "default",
|
type: "default",
|
||||||
inputs: { label: "Fixed costs", activeOption: "INR" },
|
inputs: { label: "Fixed costs", activeOption: "INR" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "default",
|
||||||
|
inputs: { label: "Initial Investment", activeOption: "INR" },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: "default",
|
type: "default",
|
||||||
inputs: { label: "Salvage value", activeOption: "Hrs" },
|
inputs: { label: "Salvage value", activeOption: "Hrs" },
|
||||||
@@ -63,6 +68,8 @@ const Analysis: React.FC = () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { inputValues, setInputValues, updateInputValue } = useInputValues();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="analysis-main-wrapper">
|
<div className="analysis-main-wrapper">
|
||||||
<div className="analysis-main-container">
|
<div className="analysis-main-container">
|
||||||
@@ -88,10 +95,14 @@ const Analysis: React.FC = () => {
|
|||||||
presets={
|
presets={
|
||||||
AnalysisPresets[selectedOption as keyof AnalysisPresetsType]
|
AnalysisPresets[selectedOption as keyof AnalysisPresetsType]
|
||||||
}
|
}
|
||||||
|
inputValues={inputValues}
|
||||||
|
onInputChange={(label, value) => {
|
||||||
|
updateInputValue(label, value);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="buttons-container">
|
<div className="buttons-container">
|
||||||
<input type="button" value={"Clear"} className="cancel" />
|
<input type="button" value={"Clear"} className="cancel" onClick={() => setInputValues({})} />
|
||||||
<input type="button" value={"Update"} className="submit" />
|
<input type="button" value={"Update"} className="submit" onClick={() => setInputValues(inputValues)} />
|
||||||
</div>
|
</div>
|
||||||
<div className="create-custom-analysis-container">
|
<div className="create-custom-analysis-container">
|
||||||
<div className="custom-analysis-header">Create Custom Analysis</div>
|
<div className="custom-analysis-header">Create Custom Analysis</div>
|
||||||
|
|||||||
@@ -6,12 +6,11 @@ import { AnalysisPresetsType } from "../../../../types/analysis";
|
|||||||
interface InputRendererProps {
|
interface InputRendererProps {
|
||||||
keyName: string;
|
keyName: string;
|
||||||
presets: AnalysisPresetsType[keyof AnalysisPresetsType];
|
presets: AnalysisPresetsType[keyof AnalysisPresetsType];
|
||||||
|
inputValues: Record<string, string>; // <-- Add this line
|
||||||
|
onInputChange: (label: string, value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RenderAnalysisInputs: React.FC<InputRendererProps> = ({
|
const RenderAnalysisInputs: React.FC<InputRendererProps> = ({ keyName, presets,inputValues, onInputChange }) => {
|
||||||
keyName,
|
|
||||||
presets,
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<div key={`main-${keyName}`} className="analysis-inputs">
|
<div key={`main-${keyName}`} className="analysis-inputs">
|
||||||
{presets.map((preset, index) => {
|
{presets.map((preset, index) => {
|
||||||
@@ -20,9 +19,9 @@ const RenderAnalysisInputs: React.FC<InputRendererProps> = ({
|
|||||||
<InputWithDropDown
|
<InputWithDropDown
|
||||||
key={index}
|
key={index}
|
||||||
label={preset.inputs.label}
|
label={preset.inputs.label}
|
||||||
value=""
|
value={inputValues[preset.inputs.label] || ""}
|
||||||
activeOption={preset.inputs.activeOption}
|
activeOption={preset.inputs.activeOption}
|
||||||
onChange={() => {}}
|
onChange={(newValue) => onInputChange(preset.inputs.label, newValue)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ const ThroughputSummary:React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { machineActiveTime } = useMachineUptime();
|
const { machineActiveTime } = useMachineUptime();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const energyConsumption = {
|
const energyConsumption = {
|
||||||
energyConsumed: 456,
|
energyConsumed: 456,
|
||||||
unit: "KWH",
|
unit: "KWH",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useMachineCount, useMachineUptime, useMaterialCycle } from "../../../store/store";
|
import { useMachineCount, useMachineUptime, useMaterialCycle, useThroughPutData } from "../../../store/store";
|
||||||
import {
|
import {
|
||||||
ThroughputSummaryIcon,
|
ThroughputSummaryIcon,
|
||||||
} from "../../icons/analysis";
|
} from "../../icons/analysis";
|
||||||
@@ -13,26 +13,32 @@ const ProductionCapacity = ({
|
|||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
|
|
||||||
const { machineCount } = useMachineCount();
|
|
||||||
const { machineActiveTime } = useMachineUptime();
|
const { machineActiveTime } = useMachineUptime();
|
||||||
const { materialCycleTime } = useMaterialCycle();
|
const { materialCycleTime } = useMaterialCycle();
|
||||||
|
const { throughputData } = useThroughPutData()
|
||||||
|
|
||||||
|
|
||||||
const progressPercent = machineActiveTime;
|
const progressPercent = machineActiveTime;
|
||||||
|
|
||||||
|
|
||||||
const totalBars = 6;
|
const totalBars = 6;
|
||||||
const barsToFill = Math.floor((progressPercent / 100) * totalBars);
|
const barsToFill = Math.floor((progressPercent / 100) * totalBars);
|
||||||
const partialFillPercent =
|
const partialFillPercent =
|
||||||
((progressPercent / 100) * totalBars - barsToFill) * 100;
|
((progressPercent / 100) * totalBars - barsToFill) * 100;
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
// const { machineCount, setMachineCount } = useMachineCount()
|
|
||||||
// const { machineActiveTime, setMachineActiveTime } = useMachineUptime()
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsLoading(true);
|
if (throughputData > 0) {
|
||||||
machineUtilization = machineActiveTime
|
console.log('machineActiveTime: ', machineActiveTime);
|
||||||
console.log('machineActiveTime: ', machineActiveTime);
|
console.log('materialCycleTime: ', materialCycleTime);
|
||||||
}, [machineActiveTime])
|
console.log('throughputData: ', throughputData);
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [throughputData])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="throughtputSummary-container analysis-card">
|
<div className="throughtputSummary-container analysis-card">
|
||||||
@@ -52,7 +58,7 @@ const ProductionCapacity = ({
|
|||||||
<>
|
<>
|
||||||
<div className="process-container">
|
<div className="process-container">
|
||||||
<div className="throughput-value">
|
<div className="throughput-value">
|
||||||
<span className="value">{machineActiveTime}</span> Units/hour
|
<span className="value">{throughputData}</span> Units/hour
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Dynamic Progress Bar */}
|
{/* Dynamic Progress Bar */}
|
||||||
@@ -79,7 +85,7 @@ const ProductionCapacity = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="metric">
|
<div className="metric">
|
||||||
<span className="label">Machine Utilization</span>
|
<span className="label">Machine Utilization</span>
|
||||||
<span className="value">1</span>
|
<span className="value">{machineActiveTime}</span>
|
||||||
{/* <span className="value">{machineActiveTime}</span> */}
|
{/* <span className="value">{machineActiveTime}</span> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
|
|||||||
max={max}
|
max={max}
|
||||||
step={step}
|
step={step}
|
||||||
type="number"
|
type="number"
|
||||||
defaultValue={value}
|
// defaultValue={value}
|
||||||
|
value={value}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onChange(e.target.value);
|
onChange(e.target.value);
|
||||||
}}
|
}}
|
||||||
|
|||||||
84
app/src/modules/simulation/analysis/ROI/roiData.tsx
Normal file
84
app/src/modules/simulation/analysis/ROI/roiData.tsx
Normal file
@@ -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 (
|
||||||
|
<></>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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 (
|
||||||
|
<></>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,10 +1,23 @@
|
|||||||
import React from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import ThroughPut from './throughPut/throughPut'
|
import { usePlayButtonStore } from '../../../store/usePlayButtonStore'
|
||||||
|
import ProductionCapacityData from './productionCapacity/productionCapacityData'
|
||||||
|
import ThroughPutData from './throughPut/throughPutData'
|
||||||
|
import ROIData from './ROI/roiData'
|
||||||
|
|
||||||
function SimulationAnalysis() {
|
function SimulationAnalysis() {
|
||||||
|
const { isPlaying } = usePlayButtonStore()
|
||||||
|
// useEffect(()=>{
|
||||||
|
// if (isPlaying) {
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// },[isPlaying])
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ThroughPut />
|
<ThroughPutData />
|
||||||
|
<ProductionCapacityData />
|
||||||
|
<ROIData />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 (
|
|
||||||
<>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -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])
|
||||||
@@ -29,7 +29,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
const isIdleRef = useRef<boolean>(false);
|
const isIdleRef = useRef<boolean>(false);
|
||||||
let startTime: number;
|
let startTime: number;
|
||||||
|
|
||||||
const { setArmBotActive, setArmBotState, removeCurrentAction,incrementActiveTime,incrementIdleTime } = useArmBotStore();
|
const { armBots, setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = useArmBotStore();
|
||||||
const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore();
|
const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore();
|
||||||
const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore();
|
const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore();
|
||||||
const { setIsVisible, getMaterialById } = useMaterialStore();
|
const { setIsVisible, getMaterialById } = useMaterialStore();
|
||||||
@@ -42,10 +42,10 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
const { speed } = useAnimationPlaySpeed();
|
const { speed } = useAnimationPlaySpeed();
|
||||||
|
|
||||||
const activeSecondsElapsed = useRef(0);
|
const activeSecondsElapsed = useRef(0);
|
||||||
const activeTimerId = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
||||||
|
|
||||||
const idleSecondsElapsed = useRef(0);
|
const idleSecondsElapsed = useRef(0);
|
||||||
const idleTimerId = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
||||||
|
const animationFrameIdRef = useRef<number | null>(null);
|
||||||
|
const previousTimeRef = useRef<number | null>(null);
|
||||||
|
|
||||||
function firstFrame() {
|
function firstFrame() {
|
||||||
startTime = performance.now();
|
startTime = performance.now();
|
||||||
@@ -168,12 +168,10 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
isPausedRef.current = isPaused;
|
isPausedRef.current = isPaused;
|
||||||
}, [isPaused]);
|
}, [isPaused]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
isSpeedRef.current = speed;
|
isSpeedRef.current = speed;
|
||||||
}, [speed]);
|
}, [speed]);
|
||||||
|
|
||||||
@@ -189,12 +187,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
isPausedRef.current = false
|
isPausedRef.current = false
|
||||||
pauseTimeRef.current = null
|
pauseTimeRef.current = null
|
||||||
startTime = 0
|
startTime = 0
|
||||||
clearInterval(activeTimerId.current!);
|
|
||||||
clearInterval(idleTimerId.current!);
|
|
||||||
activeTimerId.current = null;
|
|
||||||
activeSecondsElapsed.current = 0;
|
activeSecondsElapsed.current = 0;
|
||||||
idleSecondsElapsed.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
|
const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone
|
||||||
);
|
);
|
||||||
if (targetBones && isPlaying) {
|
if (targetBones && isPlaying) {
|
||||||
@@ -207,186 +206,65 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
}
|
}
|
||||||
}, [isReset, isPlaying])
|
}, [isReset, isPlaying])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!isPlaying) return;
|
|
||||||
|
|
||||||
const intervalMs = 1000
|
function animate(currentTime: number) {
|
||||||
|
if (previousTimeRef.current === null) {
|
||||||
if (!armBot.isActive && armBot.state == "idle" && (currentPhase == "rest" || currentPhase == "init") && !isIdleRef.current) {
|
previousTimeRef.current = currentTime;
|
||||||
isIdleRef.current = true
|
}
|
||||||
|
const deltaTime = (currentTime - previousTimeRef.current) / 1000;
|
||||||
// Stop the timer
|
previousTimeRef.current = currentTime;
|
||||||
// 🚨 1. Clear Active Timer
|
if (armBot.isActive) {
|
||||||
if (activeTimerId.current) {
|
if (!isPausedRef.current) {
|
||||||
clearInterval(activeTimerId.current);
|
activeSecondsElapsed.current += deltaTime * isSpeedRef.current;
|
||||||
activeTimerId.current = null;
|
// console.log(' activeSecondsElapsed.current: ', activeSecondsElapsed.current);
|
||||||
}
|
}
|
||||||
incrementActiveTime(armBot.modelUuid, activeSecondsElapsed.current)
|
} else {
|
||||||
// console.log(`✅ Active Cycle completed in ${activeSecondsElapsed.current} seconds`);
|
if (!isPausedRef.current) {
|
||||||
|
idleSecondsElapsed.current += deltaTime * isSpeedRef.current;
|
||||||
// 🚨 2. Reset active timer seconds after logging
|
// console.log('idleSecondsElapsed.current: ', idleSecondsElapsed.current);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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(() => {
|
} else if (armBot.isActive && armBot.state !== "idle" && currentPhase !== "rest" && armBot.currentAction) {
|
||||||
// if (!isPlaying) return;
|
cancelAnimationFrame(animationFrameIdRef.current!);
|
||||||
// const now = () => performance.now();
|
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 = () => {
|
return () => {
|
||||||
// let lastTime = now();
|
if (animationFrameIdRef.current !== null) {
|
||||||
|
cancelAnimationFrame(animationFrameIdRef.current);
|
||||||
|
animationFrameIdRef.current = null; // Reset the animation frame ID
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// const update = () => {
|
}, [armBot.isActive, armBot.state, currentPhase])
|
||||||
// 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 {
|
|
||||||
|
|
||||||
// lastTime = now();
|
useEffect(() => {
|
||||||
// }
|
|
||||||
|
|
||||||
// if (!isIdleRef.current) {
|
console.log('armBots: ', armBots);
|
||||||
// requestAnimationFrame(update);
|
}, [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(() => {
|
useEffect(() => {
|
||||||
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
|
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
|
||||||
if (targetMesh) {
|
if (targetMesh) {
|
||||||
@@ -409,7 +287,6 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
}
|
}
|
||||||
//Waiting for trigger.
|
//Waiting for trigger.
|
||||||
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) {
|
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) {
|
||||||
|
|
||||||
logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction")
|
logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction")
|
||||||
}
|
}
|
||||||
//Moving to pickup point
|
//Moving to pickup point
|
||||||
@@ -449,12 +326,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
pauseTimeRef.current = null
|
pauseTimeRef.current = null
|
||||||
isPausedRef.current = false
|
isPausedRef.current = false
|
||||||
startTime = 0
|
startTime = 0
|
||||||
clearInterval(activeTimerId.current!);
|
|
||||||
clearInterval(idleTimerId.current!);
|
|
||||||
activeTimerId.current = null;
|
|
||||||
activeSecondsElapsed.current = 0;
|
activeSecondsElapsed.current = 0;
|
||||||
idleSecondsElapsed.current = 0;
|
idleSecondsElapsed.current = 0;
|
||||||
idleTimerId.current = null;
|
previousTimeRef.current = null;
|
||||||
|
if (animationFrameIdRef.current !== null) {
|
||||||
|
cancelAnimationFrame(animationFrameIdRef.current);
|
||||||
|
animationFrameIdRef.current = null;
|
||||||
|
}
|
||||||
removeCurrentAction(armBot.modelUuid)
|
removeCurrentAction(armBot.modelUuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,38 +370,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
}
|
}
|
||||||
else if (armBot.isActive && armBot.state == "running" && currentPhase == "end-to-rest") {
|
else if (armBot.isActive && armBot.state == "running" && currentPhase == "end-to-rest") {
|
||||||
logStatus(armBot.modelUuid, "Callback triggered: rest, cycle completed.");
|
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)
|
setArmBotActive(armBot.modelUuid, false)
|
||||||
setArmBotState(armBot.modelUuid, "idle")
|
setArmBotState(armBot.modelUuid, "idle")
|
||||||
setCurrentPhase("rest");
|
setCurrentPhase("rest");
|
||||||
setPath([])
|
setPath([])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const logStatus = (id: string, status: string) => {
|
const logStatus = (id: string, status: string) => {
|
||||||
// console.log('status: ', status);
|
// console.log('status: ', status);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,22 +3,27 @@ import { useProductStore } from '../../../store/simulation/useProductStore';
|
|||||||
import { useActionHandler } from '../actions/useActionHandler';
|
import { useActionHandler } from '../actions/useActionHandler';
|
||||||
import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore';
|
import { usePlayButtonStore, useResetButtonStore } from '../../../store/usePlayButtonStore';
|
||||||
import { determineExecutionOrder } from './functions/determineExecutionOrder';
|
import { determineExecutionOrder } from './functions/determineExecutionOrder';
|
||||||
|
import { useSelectedProduct } from '../../../store/simulation/useSimulationStore';
|
||||||
|
|
||||||
function Simulator() {
|
function Simulator() {
|
||||||
const { products } = useProductStore();
|
const { products, getProductById } = useProductStore();
|
||||||
const { handleAction } = useActionHandler();
|
const { handleAction } = useActionHandler();
|
||||||
|
const { selectedProduct } = useSelectedProduct();
|
||||||
const { isPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { isReset } = useResetButtonStore();
|
const { isReset } = useResetButtonStore();
|
||||||
|
|
||||||
useEffect(() => {
|
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 => {
|
executionOrder.forEach(point => {
|
||||||
const action = 'actions' in point ? point.actions[0] : point.action;
|
const action = 'actions' in point ? point.actions[0] : point.action;
|
||||||
handleAction(action);
|
handleAction(action);
|
||||||
});
|
});
|
||||||
}, [products, isPlaying, isReset]);
|
}, [products, isPlaying, isReset, selectedProduct]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ export const useWalls = create<any>((set: any) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const useRoomsState = create<any>((set: any) => ({
|
export const useRoomsState = create<any>((set: any) => ({
|
||||||
roomsState: [],
|
roomsState: [],
|
||||||
setRoomsState: (x: any) => set(() => ({ walls: x })),
|
setRoomsState: (x: any) => set(() => ({ walls: x })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const useZones = create<any>((set: any) => ({
|
export const useZones = create<any>((set: any) => ({
|
||||||
@@ -448,3 +448,31 @@ export const useMaterialCycle = create<any>((set: any) => ({
|
|||||||
materialCycleTime: 0,
|
materialCycleTime: 0,
|
||||||
setMaterialCycleTime: (x: any) => set({ materialCycleTime: x }),
|
setMaterialCycleTime: (x: any) => set({ materialCycleTime: x }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const useThroughPutData = create<any>((set: any) => ({
|
||||||
|
throughputData: 0,
|
||||||
|
setThroughputData: (x: any) => set({ throughputData: x }),
|
||||||
|
}));
|
||||||
|
export const useProductionCapacityData = create<any>((set: any) => ({
|
||||||
|
productionCapacityData: 0,
|
||||||
|
setProductionCapacityData: (x: any) => set({ productionCapacityData: x }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
type InputValuesStore = {
|
||||||
|
inputValues: Record<string, string>;
|
||||||
|
setInputValues: (values: Record<string, string>) => void;
|
||||||
|
updateInputValue: (label: string, value: string) => void; // <- New
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useInputValues = create<InputValuesStore>((set) => ({
|
||||||
|
inputValues: {},
|
||||||
|
setInputValues: (values) => set({ inputValues: values }),
|
||||||
|
updateInputValue: (label, value) =>
|
||||||
|
set((state) => ({
|
||||||
|
inputValues: {
|
||||||
|
...state.inputValues,
|
||||||
|
[label]: value,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
}));
|
||||||
Reference in New Issue
Block a user