Merge remote-tracking branch 'origin/analysis'

This commit is contained in:
2025-05-19 11:02:53 +05:30
32 changed files with 1017 additions and 898 deletions

View File

@@ -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/builder/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 (
<div className="analysis-main-wrapper">
<div className="analysis-main-container">
@@ -88,10 +95,14 @@ const Analysis: React.FC = () => {
presets={
AnalysisPresets[selectedOption as keyof AnalysisPresetsType]
}
inputValues={inputValues}
onInputChange={(label, value) => {
updateInputValue(label, value);
}}
/>
<div className="buttons-container">
<input type="button" value={"Clear"} className="cancel" />
<input type="button" value={"Update"} className="submit" />
<input type="button" value={"Clear"} className="cancel" onClick={() => setInputValues({})} />
<input type="button" value={"Update"} className="submit" onClick={() => setInputValues(inputValues)} />
</div>
<div className="create-custom-analysis-container">
<div className="custom-analysis-header">Create Custom Analysis</div>

View File

@@ -6,12 +6,11 @@ import { AnalysisPresetsType } from "../../../../types/analysis";
interface InputRendererProps {
keyName: string;
presets: AnalysisPresetsType[keyof AnalysisPresetsType];
inputValues: Record<string, string>; // <-- Add this line
onInputChange: (label: string, value: string) => void;
}
const RenderAnalysisInputs: React.FC<InputRendererProps> = ({
keyName,
presets,
}) => {
const RenderAnalysisInputs: React.FC<InputRendererProps> = ({ keyName, presets,inputValues, onInputChange }) => {
return (
<div key={`main-${keyName}`} className="analysis-inputs">
{presets.map((preset, index) => {
@@ -20,9 +19,9 @@ const RenderAnalysisInputs: React.FC<InputRendererProps> = ({
<InputWithDropDown
key={index}
label={preset.inputs.label}
value=""
value={inputValues[preset.inputs.label] || ""}
activeOption={preset.inputs.activeOption}
onChange={() => {}}
onChange={(newValue) => onInputChange(preset.inputs.label, newValue)}
/>
);
}

View File

@@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { Line } from "react-chartjs-2";
import {
Chart as ChartJS,
@@ -9,23 +9,18 @@ import {
} from "chart.js";
import { PowerIcon, ProductionCapacityIcon } from "../../icons/analysis";
import SkeletonUI from "../../templates/SkeletonUI";
import { useInputValues, useMachineUptime, useProductionCapacityData } from "../../../store/builder/store";
ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement);
const ThroughputSummary:React.FC = () => {
const ThroughputSummary: React.FC = () => {
// Define all data internally within the component
const timeRange = {
startTime: "08:00 AM",
endTime: "06:00 PM",
};
const throughputData = {
labels: ["08:00", "08:10", "08:20", "08:30", "08:40", "08:50"],
data: [5, 10, 8, 10, 12, 10],
totalThroughput: 1240,
assetUsage: 85,
};
const { machineActiveTime } = useMachineUptime();
const energyConsumption = {
energyConsumed: 456,
unit: "KWH",
@@ -38,19 +33,8 @@ const ThroughputSummary:React.FC = () => {
{ shift: 3, percentage: 30, color: "#7981F5" },
];
// Chart data configuration
const chartData = {
labels: throughputData.labels,
datasets: [
{
label: "Units/hour",
data: throughputData.data,
borderColor: "#B392F0",
tension: 0.4,
pointRadius: 0, // Hide points
},
],
};
const { productionCapacityData } = useProductionCapacityData()
const chartOptions = {
responsive: true,
@@ -73,7 +57,47 @@ const ThroughputSummary:React.FC = () => {
},
};
const isLoading = false;
const assetUsage = 85;
// machineActiveTime => Throughput
// shiftUtilization.length => Shifts per day
// 8 => Shift length
// assetUsage => Yield rate
const throughtputMachineData = machineActiveTime * shiftUtilization.length * 8
const throughputData = {
labels: ["08:00", "08:10", "08:20", "08:30", "08:40", "08:50"],
data: [5, 10, 8, 10, 12, 10],
totalThroughput: (throughtputMachineData) * assetUsage / 100,
assetUsage: assetUsage,
};
const { inputValues } = useInputValues();
// Chart data configuration
const chartData = {
labels: throughputData.labels,
datasets: [
{
label: "Units/hour",
data: throughputData.data,
borderColor: "#B392F0",
tension: 0.4,
pointRadius: 0, // Hide points
},
],
};
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
if (productionCapacityData >= 0) {
setIsLoading(false);
console.log("productionCapacityData: ", productionCapacityData);
} else {
setIsLoading(true);
}
}, [productionCapacityData]);
return (
<div className="production analysis-card">
@@ -94,13 +118,13 @@ const ThroughputSummary:React.FC = () => {
<>
<div className="process-container">
<div className="throughput-value">
<span className="value">{throughputData.totalThroughput}</span>{" "}
<span className="value">{productionCapacityData}</span>{" "}
Units/hour
</div>
<div className="lineChart">
<div className="assetUsage">
<div className="key">Asset usage</div>
<div className="value">{throughputData.assetUsage}%</div>
<div className="value">{parseFloat(inputValues["Yield rate"])}%</div>
</div>
<Line data={chartData} options={chartOptions} />
</div>

View File

@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import {
CostBreakDownIcon,
LightBulpIcon,
@@ -9,6 +9,8 @@ import {
import SemiCircleProgress from "./SemiCircleProgress";
import { ArrowIcon } from "../../icons/ExportCommonIcons";
import SkeletonUI from "../../templates/SkeletonUI";
import { useROISummaryData } from "../../../store/builder/store";
import { set } from "immer/dist/internal";
const ROISummary = ({
roiSummaryData = {
@@ -79,8 +81,17 @@ const ROISummary = ({
const year = now.getFullYear();
return `${day} ${month}, ${year}`;
}
const [isLoading, setIsLoading] = useState(false);
const { roiSummary } = useROISummaryData();
useEffect(() => {
if (roiSummary && typeof roiSummary === "object") {
setIsLoading(false); // Data loaded
} else {
setIsLoading(true); // Show skeleton while loading
}
}, [roiSummary]);
const isLoading = false;
return (
<div className="roiSummary-container analysis-card">
<div className="roiSummary-wrapper analysis-card-wrapper">
@@ -98,14 +109,14 @@ const ROISummary = ({
<div className="product-info">
<ROISummaryProductName />
<div className="product-label">Product :</div>
<div className="product-name">{roiSummaryData.productName}</div>
<div className="product-name">{roiSummary.productName}</div>
</div>
<div className="playBack">
<SonarCrownIcon />
<div className="icon"></div>
<div className="info">
<span>+{roiSummaryData.roiPercentage}%</span> ROI with payback
in just <span>{roiSummaryData.paybackPeriod}</span> months
<span> {roiSummary.roiPercentage}%</span> ROI with payback
in just <span>{roiSummary.paybackPeriod}</span> months
</div>
</div>
<div className="roi-details">
@@ -122,7 +133,7 @@ const ROISummary = ({
<span className="metric-label">Total Cost Incurred</span>
<span className="metric-value">
<span></span>
{roiSummaryData.totalCost}
{roiSummary.totalCost}
</span>
</div>
<div className="metric-item">
@@ -130,14 +141,12 @@ const ROISummary = ({
<span className="metric-value">
<span></span>
{roiSummaryData.revenueGenerated}
{roiSummary.revenueGenerated}
</span>
</div>
</div>
<div
className={`metric-item net-profit ${
roiSummaryData.netProfit ?? "loss"
}`}
className={`metric-item net-profit ${roiSummary.netProfit > 0 ? "profit" : "loss"}`}
>
<div className="metric-label">
<span></span>
@@ -145,9 +154,9 @@ const ROISummary = ({
</div>
<div className="metric-value">
<span></span>
{roiSummaryData.netProfit
? roiSummaryData.netProfit
: roiSummaryData.netLoss}
{roiSummary.netProfit > 0
? roiSummary.netProfit
: roiSummary.netLoss}
</div>
</div>
</div>
@@ -164,9 +173,8 @@ const ROISummary = ({
</span>
</div>
<div
className={`breakdown-table-wrapper ${
isTableOpen ? "open" : "closed"
}`}
className={`breakdown-table-wrapper ${isTableOpen ? "open" : "closed"
}`}
style={{
transition: "max-height 0.3s ease-in-out",
overflow: "hidden",
@@ -190,8 +198,8 @@ const ROISummary = ({
row.item === "Total"
? "total-row"
: row.item === "Net Profit"
? "net-profit-row"
: ""
? "net-profit-row"
: ""
}
>
<td>{row.item}</td>
@@ -228,6 +236,7 @@ const ROISummary = ({
</>
) : (
<SkeletonUI type={"default"} />
// <div> No Data</div>
)}
</div>
</div>

View File

@@ -1,21 +1,45 @@
import { useEffect, useState } from "react";
import { useMachineCount, useMachineUptime, useMaterialCycle, useProductionCapacityData, useThroughPutData } from "../../../store/builder/store";
import {
ThroughputSummaryIcon,
} from "../../icons/analysis";
import SkeletonUI from "../../templates/SkeletonUI";
const ProductionCapacity = ({
progressPercent = 50,
avgProcessTime = "28.4",
avgProcessTime = "28.4 Secs/unit",
machineUtilization = "78%",
throughputValue = 128,
timeRange = { startTime: "08:00 AM", endTime: "09:00 AM" },
}) => {
const { machineActiveTime } = useMachineUptime();
const { materialCycleTime } = useMaterialCycle();
const { throughputData } = useThroughPutData()
const { productionCapacityData } = useProductionCapacityData()
const progressPercent = machineActiveTime;
const totalBars = 6;
const barsToFill = Math.floor((progressPercent / 100) * totalBars);
const partialFillPercent =
((progressPercent / 100) * totalBars - barsToFill) * 100;
const isLoading = true;
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
if (throughputData >= 0) {
// console.log('machineActiveTime: ', machineActiveTime);
// console.log('materialCycleTime: ', materialCycleTime);
// console.log('throughputData: ', throughputData);
// console.log('productionCapacityData: ', productionCapacityData);
setIsLoading(true);
}
}, [throughputData])
return (
<div className="throughtputSummary-container analysis-card">
<div className="throughtputSummary-wrapper analysis-card-wrapper">
@@ -34,7 +58,8 @@ const ProductionCapacity = ({
<>
<div className="process-container">
<div className="throughput-value">
<span className="value">{avgProcessTime}</span> secs/unit
<span className="value">{throughputData}</span> Units/hour
</div>
{/* Dynamic Progress Bar */}
@@ -56,12 +81,13 @@ const ProductionCapacity = ({
<div className="metrics-section">
<div className="metric">
<span className="label">Units/hour</span>
<span className="value">{throughputValue} avg.</span>
<span className="label">Avg. Process Time</span>
<span className="value">{materialCycleTime} secs/unit </span>
</div>
<div className="metric">
<span className="label">Machine Utilization</span>
<span className="value">{machineUtilization}</span>
<span className="value">{machineActiveTime}</span>
{/* <span className="value">{machineActiveTime}</span> */}
</div>
</div>
</>

View File

@@ -52,7 +52,8 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
max={max}
step={step}
type="number"
defaultValue={value}
// defaultValue={value}
value={value}
onChange={(e) => {
onChange(e.target.value);
}}

View File

@@ -1,6 +1,6 @@
import React, { useState, useRef, useEffect } from "react";
import { ExitIcon, PlayStopIcon, ResetIcon } from "../../icons/SimulationIcons";
import { useActiveTool } from "../../../store/builder/store";
import { useActiveTool, useProcessBar } from "../../../store/builder/store";
import {
useAnimationPlaySpeed,
usePauseButtonStore,
@@ -111,21 +111,25 @@ const SimulationPlayer: React.FC = () => {
const hourlySimulation = 25;
const dailyProduction = 75;
const monthlyROI = 50;
const { processBar, setProcessBar } = useProcessBar();
// const process = [
// { name: "process 1", completed: 0 }, // 0% completed
// { name: "process 2", completed: 20 }, // 20% completed
// { name: "process 3", completed: 40 }, // 40% completed
// { name: "process 4", completed: 60 }, // 60% completed
// { name: "process 5", completed: 80 }, // 80% completed
// { name: "process 6", completed: 100 }, // 100% completed
// { name: "process 7", completed: 0 }, // 0% completed
// { name: "process 8", completed: 50 }, // 50% completed
// { name: "process 9", completed: 90 }, // 90% completed
// { name: "process 10", completed: 30 }, // 30% completed
// ];
const process = [
{ name: "process 1", completed: 0 }, // 0% completed
{ name: "process 2", completed: 20 }, // 20% completed
{ name: "process 3", completed: 40 }, // 40% completed
{ name: "process 4", completed: 60 }, // 60% completed
{ name: "process 5", completed: 80 }, // 80% completed
{ name: "process 6", completed: 100 }, // 100% completed
{ name: "process 7", completed: 0 }, // 0% completed
{ name: "process 8", completed: 50 }, // 50% completed
{ name: "process 9", completed: 90 }, // 90% completed
{ name: "process 10", completed: 30 }, // 30% completed
];
useEffect(() => {
// console.log('processBar: ', processBar);
}, [processBar])
const intervals = [10, 20, 30, 40, 50, 60]; // in minutes
const intervals = [50, 20, 30, 40, 50, 60]; // in minutes
const totalSegments = intervals.length;
const progress = 20; // percent (example)
@@ -388,22 +392,26 @@ const SimulationPlayer: React.FC = () => {
ref={processWrapperRef}
onMouseDown={handleProcessMouseDown}
>
{process.map((item, index) => (
<div
key={`${index}-${item.name}`}
className="process"
style={{
width: `${item.completed}%`,
backgroundColor: getAvatarColor(index),
}}
>
{processBar?.length > 0 ? (
processBar.map((item: any, index: any) => (
<div
className="process-player"
ref={processPlayerRef}
style={{ left: playerPosition, position: "absolute" }}
></div>
</div>
))}
key={`${index}-${item.name}`}
className="process"
style={{
width: `${item.completed}%`,
backgroundColor: getAvatarColor(index),
}}
>
<div
className="process-player"
ref={processPlayerRef}
style={{ left: playerPosition, position: "absolute" }}
></div>
</div>
))
) : (
<div>No process data available</div>
)}
</div>
</div>
</div>

View File

@@ -37,7 +37,6 @@ const Card: React.FC<CardProps> = ({
image,
description,
AssetID,
modelUrl,
onSelectCard,
}) => {
const handleCardSelect = () => {

View File

@@ -45,7 +45,7 @@ const CardsContainer: React.FC<ModelsProps> = ({ models }) => {
}) => {
setSelectedCard(cardData);
const res = await fetchGltfUrl(cardData.assetName, cardData.AssetID);
console.log("res: ", res);
// console.log("res: ", res);
setModelUrl(res.url);
};
return (
@@ -65,8 +65,8 @@ const CardsContainer: React.FC<ModelsProps> = ({ models }) => {
onSelectCard={handleCardSelect}
AssetID={assetDetail.AssetID}
image={assetDetail.thumbnail}
modelUrl={assetDetail.modelfileID}
description={assetDetail.description}
modelUrl={modelUrl}
/>
</React.Fragment>
))}

View File

@@ -33,7 +33,6 @@ const MarketPlace = () => {
try {
const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6");
setModels(filt.items);
console.log('filt.items: ', filt.items);
setFilteredModels(filt.items);
setisLoading(false);
} catch {

View File

@@ -0,0 +1,125 @@
import React, { useEffect, useState } from 'react'
import { useInputValues, useProductionCapacityData, useROISummaryData } from '../../../../store/builder/store';
import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore';
import { usePlayButtonStore } from '../../../../store/usePlayButtonStore';
export default function ROIData() {
const { inputValues } = useInputValues();
const { productionCapacityData } = useProductionCapacityData()
const { selectedProduct } = useSelectedProduct();
const { isPlaying } = usePlayButtonStore();
const { setRoiSummaryData } = useROISummaryData();
useEffect(() => {
if (!isPlaying) {
setRoiSummaryData({
productName: "",
roiPercentage: 0,
paybackPeriod: 0,
totalCost: 0,
revenueGenerated: 0,
netProfit: 0,
netLoss: 0,
})
return;
}
if (inputValues === undefined) return;
const electricityCost = parseFloat(inputValues["Electricity cost"]);
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) {
const totalHoursPerYear = shiftLength * shiftsPerDay * workingDaysPerYear;
// Total good units produced per year
const annualProductionUnits = productionCapacityData * totalHoursPerYear;
// Revenue for a year
const annualRevenue = annualProductionUnits * sellingPrice;
// Costs
const totalMaterialCost = annualProductionUnits * materialCost;
const totalLaborCost = laborCost * totalHoursPerYear;
const totalEnergyCost = electricityCost * totalHoursPerYear;
const totalMaintenanceCost = maintenanceCost + fixedCost;
const totalAnnualCost = totalMaterialCost + totalLaborCost + totalEnergyCost + totalMaintenanceCost;
// Annual Profit
const annualProfit = annualRevenue - totalAnnualCost;
console.log('annualProfit: ', annualProfit);
// Net Profit over production period
const netProfit = annualProfit * productionPeriod;
// ROI
const roiPercentage = ((netProfit + salvageValue - initialInvestment) / initialInvestment) * 100;
// Payback Period
const paybackPeriod = initialInvestment / (annualProfit || 1); // Avoid division by 0
console.log('paybackPeriod: ', paybackPeriod);
// console.log("--- ROI Breakdown ---");
// console.log("Annual Production Units:", annualProductionUnits.toFixed(2));
// console.log("Annual Revenue:", annualRevenue.toFixed(2));
// console.log("Total Annual Cost:", totalAnnualCost.toFixed(2));
// console.log("Annual Profit:", annualProfit.toFixed(2));
// console.log("Net Profit:", netProfit.toFixed(2));
// console.log("ROI %:", roiPercentage.toFixed(2));
// console.log("Payback Period (years):", paybackPeriod.toFixed(2));
setRoiSummaryData({
productName: selectedProduct.productName,
roiPercentage: parseFloat((roiPercentage / 100).toFixed(2)), // normalized to 0.x format
paybackPeriod: parseFloat(paybackPeriod.toFixed(2)),
totalCost: parseFloat(totalAnnualCost.toFixed(2)),
revenueGenerated: parseFloat(annualRevenue.toFixed(2)),
netProfit: netProfit > 0 ? parseFloat(netProfit.toFixed(2)) : 0,
netLoss: netProfit < 0 ? -netProfit : 0
});
const productCount = 1000;
// Cost per unit (based on full annual cost)
const costPerUnit = totalAnnualCost / annualProductionUnits;
const costForTargetUnits = productCount * costPerUnit;
const revenueForTargetUnits = productCount * sellingPrice;
const profitForTargetUnits = revenueForTargetUnits - costForTargetUnits;
const netProfitForTarget = profitForTargetUnits > 0 ? profitForTargetUnits : 0;
const netLossForTarget = profitForTargetUnits < 0 ? -profitForTargetUnits : 0;
// console.log("--- Fixed Product Count (" + productCount + ") ---");
// console.log("Cost per Unit:", costPerUnit.toFixed(2));
// console.log("Total Cost for " + productCount + " Units:", costForTargetUnits.toFixed(2));
// console.log("Revenue for " + productCount + " Units:", revenueForTargetUnits.toFixed(2));
// console.log("Profit:", netProfitForTarget.toFixed(2));
// console.log("Loss:", netLossForTarget.toFixed(2));
}
}, [inputValues, productionCapacityData]);
return (
<></>
)
}

View File

@@ -0,0 +1,51 @@
import React, { useEffect } from 'react'
import { useInputValues, useProductionCapacityData, useThroughPutData } from '../../../../store/builder/store'
import { usePlayButtonStore } from '../../../../store/usePlayButtonStore';
export default function ProductionCapacityData() {
const { throughputData } = useThroughPutData()
const { productionCapacityData, setProductionCapacityData } = useProductionCapacityData()
const { inputValues } = useInputValues();
const { isPlaying } = usePlayButtonStore();
useEffect(() => {
if (!isPlaying) {
setProductionCapacityData(0);
return;
}
if (!inputValues || throughputData === undefined) return;
const shiftLength = parseFloat(inputValues["Shift length"]);
const shiftsPerDay = parseFloat(inputValues["Shifts / day"]);
const workingDaysPerYear = parseFloat(inputValues["Working days / year"]);
const yieldRate = parseFloat(inputValues["Yield rate"]);
if (!isNaN(shiftLength) && !isNaN(shiftsPerDay) && !isNaN(workingDaysPerYear) &&
!isNaN(yieldRate) && throughputData >= 0) {
// Total units produced per day before yield
const dailyProduction = throughputData * shiftLength * shiftsPerDay;
// Units after applying yield rate
const goodUnitsPerDay = dailyProduction * (yieldRate / 100);
// Annual output
const annualProduction = goodUnitsPerDay * workingDaysPerYear;
// Final production capacity per hour (after yield)
const productionPerHour = throughputData * (yieldRate / 100);
// Set the final capacity (units/hour)
setProductionCapacityData(Number(productionPerHour.toFixed(2)));
}
}, [throughputData, inputValues]);
return (
<></>
)
}

View File

@@ -0,0 +1,25 @@
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 (
<>
<ThroughPutData />
<ProductionCapacityData />
<ROIData />
</>
)
}
export default SimulationAnalysis

View File

@@ -0,0 +1,153 @@
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, useProcessBar, useThroughPutData } from '../../../../store/builder/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';
import { is, set } from 'immer/dist/internal';
export default function ThroughPutData() {
const { selectedProduct } = useSelectedProduct();
const { products, getProductById } = useProductStore();
const { armBots } = useArmBotStore();
const { vehicles } = useVehicleStore();
const { machines } = useMachineStore();
const { conveyors } = useConveyorStore();
const { storageUnits } = useStorageUnitStore();
const { materialHistory } = useMaterialStore();
const { machineCount, setMachineCount } = useMachineCount();
const { machineActiveTime, setMachineActiveTime } = useMachineUptime();
const { materialCycleTime, setMaterialCycleTime } = useMaterialCycle();
const { setProcessBar } = useProcessBar();
const { setThroughputData } = useThroughPutData()
const { isPlaying } = usePlayButtonStore();
// Setting machine count
let totalItems = 0;
let totalActiveTime = 0;
useEffect(() => {
if (!isPlaying) {
totalActiveTime = 0;
totalItems = 0;
setMachineCount(0);
setMachineActiveTime(0);
setMaterialCycleTime(0);
setProcessBar([]);
setThroughputData(0);
return;
} else {
let process: any = [];
const fetchProductSequenceData = async () => {
const productData = getProductById(selectedProduct.productId);
if (productData) {
const productSequenceData = await determineExecutionMachineSequences([productData])
if (productSequenceData?.length > 0) {
productSequenceData.forEach((sequence) => {
sequence.forEach((item) => {
if (item.type === "roboticArm") {
armBots.filter(arm => arm.modelUuid === item.modelUuid)
.forEach(arm => {
if (arm.activeTime >= 0) {
process.push({ modelid: arm.modelUuid, modelName: arm.modelName, activeTime: arm?.activeTime })
totalActiveTime += arm.activeTime;
}
});
} else if (item.type === "vehicle") {
vehicles.filter(vehicle => vehicle.modelUuid === item.modelUuid)
.forEach(vehicle => {
if (vehicle.activeTime >= 0) {
process.push({ modelid: vehicle.modelUuid, modelName: vehicle.modelName, activeTime: vehicle?.activeTime })
totalActiveTime += vehicle.activeTime;
}
});
} else if (item.type === "machine") {
machines.filter(machine => machine.modelUuid === item.modelUuid)
.forEach(machine => {
if (machine.activeTime >= 0) {
process.push({ modelid: machine.modelUuid, modelName: machine.modelName, activeTime: machine?.activeTime })
totalActiveTime += machine.activeTime;
}
});
} else if (item.type === "transfer") {
conveyors.filter(conveyor => conveyor.modelUuid === item.modelUuid)
.forEach(conveyor => {
if (conveyor.activeTime >= 0) {
totalActiveTime += conveyor.activeTime;
}
});
} else if (item.type === "storageUnit") {
storageUnits.filter(storage => storage.modelUuid === item.modelUuid)
.forEach(storage => {
if (storage.activeTime >= 0) {
totalActiveTime += storage.activeTime;
}
});
}
});
totalItems += sequence.length;
});
setMachineCount(totalItems);
setMachineActiveTime(totalActiveTime);
let arr = process.map((item: any) => ({
name: item.modelName,
completed: Math.round((item.activeTime / totalActiveTime) * 100)
}));
setProcessBar(arr);
}
}
};
fetchProductSequenceData();
}
// if (materialCycleTime <= 0) return
}, [products, selectedProduct, getProductById, setMachineCount, materialCycleTime, armBots, vehicles, machines]);
// Setting material cycle time
useEffect(() => {
materialHistory.forEach((material) => {
const start = material.material.startTime ?? 0;
const end = material.material.endTime ?? 0;
if (start === 0 || end === 0) return;
const totalCycleTime = (end - start) / 1000; // Convert milliseconds to seconds
setMaterialCycleTime(Number(totalCycleTime.toFixed(2))); // Set the material cycle time in the store
});
}, [materialHistory]);
useEffect(() => {
if (machineActiveTime > 0 && materialCycleTime > 0 && machineCount > 0) {
const utilization = machineActiveTime / 3600; // Active time per hour
const unitsPerMachinePerHour = 3600 / materialCycleTime;
const throughput = unitsPerMachinePerHour * machineCount * utilization;
setThroughputData(throughput.toFixed(2)); // Set throughput to state/store
// console.log('---Throughput Results---');
// console.log('Machine Active Time (s):', machineActiveTime);
// console.log('Material Cycle Time (s):', materialCycleTime);
// console.log('Machine Count:', machineCount);
// console.log('Utilization:', utilization);
// console.log('Throughput (units/hr):', throughput);
}
}, [machineActiveTime, materialCycleTime, machineCount]);
return (
<>
</>
);
}

View File

@@ -1,6 +1,6 @@
import { useEffect, useRef, useState } from 'react'
import { useMachineStore } from '../../../../../store/simulation/useMachineStore';
import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
import MachineAnimator from '../animator/machineAnimator';
import { useProductStore } from '../../../../../store/simulation/useProductStore';
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
@@ -9,22 +9,93 @@ import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHa
function MachineInstance({ machineDetail }: { machineDetail: MachineStatus }) {
const [currentPhase, setCurrentPhase] = useState<string>('idle');
let isIncrememtable = useRef<boolean>(true);
const idleTimeRef = useRef<number>(0);
const activeTimeRef = useRef<number>(0);
const previousTimeRef = useRef<number | null>(null);
const animationFrameIdRef = useRef<number | null>(null);
const isSpeedRef = useRef<number>(0);
const isPausedRef = useRef<boolean>(false);
const { isPlaying } = usePlayButtonStore();
const { machines, setMachineState, setMachineActive } = useMachineStore();
const { machines, setMachineState, setMachineActive, incrementIdleTime, incrementActiveTime, resetTime } = useMachineStore();
const { selectedProduct } = useSelectedProduct();
const { getActionByUuid } = useProductStore();
const { triggerPointActions } = useTriggerHandler();
const { speed } = useAnimationPlaySpeed();
const { isPaused } = usePauseButtonStore();
useEffect(() => {
isPausedRef.current = isPaused;
}, [isPaused]);
useEffect(() => {
isSpeedRef.current = speed;
}, [speed]);
const reset = () => {
setCurrentPhase("idle");
setMachineState(machineDetail.modelUuid, 'idle');
setMachineActive(machineDetail.modelUuid, false);
isIncrememtable.current = true;
isPausedRef.current = false;
resetTime(machineDetail.modelUuid)
activeTimeRef.current = 0
idleTimeRef.current = 0
previousTimeRef.current = null
if (animationFrameIdRef.current !== null) {
cancelAnimationFrame(animationFrameIdRef.current)
animationFrameIdRef.current = null
}
}
function machineStatus(modelId: string, status: string) {
// console.log(`${modelId} , ${status}`);
}
function animate(currentTime: number) {
if (previousTimeRef.current === null) {
previousTimeRef.current = currentTime;
}
const deltaTime = (currentTime - previousTimeRef.current) / 1000;
previousTimeRef.current = currentTime;
if (machineDetail.isActive) {
if (!isPausedRef.current) {
activeTimeRef.current += deltaTime * isSpeedRef.current;
// console.log(' activeTimeRef.current: ', activeTimeRef.current);
}
} else {
if (!isPausedRef.current) {
idleTimeRef.current += deltaTime * isSpeedRef.current;
// console.log('idleTimeRef.curre: ', idleTimeRef.current);
}
}
animationFrameIdRef.current = requestAnimationFrame(animate);
}
useEffect(() => {
if (!isPlaying) return
if (!machineDetail.isActive) {
const roundedActiveTime = Math.round(activeTimeRef.current);
// console.log('Final Active Time:', roundedActiveTime, 'seconds');
incrementActiveTime(machineDetail.modelUuid, roundedActiveTime);
activeTimeRef.current = 0;
} else {
const roundedIdleTime = Math.round(idleTimeRef.current);
// console.log('Final Idle Time:', roundedIdleTime, 'seconds');
incrementIdleTime(machineDetail.modelUuid, roundedIdleTime);
idleTimeRef.current = 0;
}
if (animationFrameIdRef.current === null) {
animationFrameIdRef.current = requestAnimationFrame(animate);
}
return () => {
if (animationFrameIdRef.current !== null) {
cancelAnimationFrame(animationFrameIdRef.current);
animationFrameIdRef.current = null;
}
};
}, [machineDetail, isPlaying]);
useEffect(() => {
if (isPlaying) {

View File

@@ -5,7 +5,7 @@ import IKInstance from '../ikInstance/ikInstance';
import RoboticArmAnimator from '../animator/roboticArmAnimator';
import MaterialAnimator from '../animator/materialAnimator';
import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb";
import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
import { useMaterialStore } from '../../../../../store/simulation/useMaterialStore';
import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
import { useProductStore } from '../../../../../store/simulation/useProductStore';
@@ -26,9 +26,11 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
const groupRef = useRef<any>(null);
const pauseTimeRef = useRef<number | null>(null);
const isPausedRef = useRef<boolean>(false);
const isSpeedRef = useRef<any>(null);
const isIdleRef = useRef<boolean>(false);
let startTime: number;
const { setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
const { armBots, setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = useArmBotStore();
const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore();
const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore();
const { setIsVisible, setIsPaused, getMaterialById } = useMaterialStore();
@@ -38,6 +40,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
const { isPlaying } = usePlayButtonStore();
const { isReset } = useResetButtonStore();
const { isPaused } = usePauseButtonStore();
const { speed } = useAnimationPlaySpeed();
const activeSecondsElapsed = useRef(0);
const idleSecondsElapsed = useRef(0);
const animationFrameIdRef = useRef<number | null>(null);
const previousTimeRef = useRef<number | null>(null);
const checkActiveRoboticArms = useCheckActiveRoboticArmsInSubsequence();
const lastRemoved = useRef<{ type: string, materialId: string } | null>(null);
@@ -176,6 +185,10 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
isPausedRef.current = isPaused;
}, [isPaused]);
useEffect(() => {
isSpeedRef.current = speed;
}, [speed]);
useEffect(() => {
if (isReset || !isPlaying) {
logStatus(armBot.modelUuid, "Simulation Play Reset Successfully")
@@ -188,6 +201,14 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
isPausedRef.current = false
pauseTimeRef.current = null
startTime = 0
activeSecondsElapsed.current = 0;
idleSecondsElapsed.current = 0;
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) {
@@ -200,6 +221,61 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
}
}, [isReset, isPlaying])
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);
}
} else {
if (!isPausedRef.current) {
idleSecondsElapsed.current += deltaTime * isSpeedRef.current;
// console.log('idleSecondsElapsed.current: ', idleSecondsElapsed.current);
}
}
animationFrameIdRef.current = requestAnimationFrame(animate);
}
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:',armBot.modelUuid, roundedActiveTime, 'seconds');
incrementActiveTime(armBot.modelUuid, roundedActiveTime);
activeSecondsElapsed.current = 0;
} 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:', armBot.modelUuid,roundedIdleTime, 'seconds');
incrementIdleTime(armBot.modelUuid, roundedIdleTime);
idleSecondsElapsed.current = 0;
}
if (animationFrameIdRef.current === null) {
animationFrameIdRef.current = requestAnimationFrame(animate);
}
return () => {
if (animationFrameIdRef.current !== null) {
cancelAnimationFrame(animationFrameIdRef.current);
animationFrameIdRef.current = null; // Reset the animation frame ID
}
};
}, [armBot.isActive, armBot.state, currentPhase])
useEffect(() => {
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
if (targetMesh) {
@@ -227,6 +303,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
//Moving to pickup point
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) {
if (armBot.currentAction) {
setArmBotActive(armBot.modelUuid, true);
setArmBotState(armBot.modelUuid, "running");
setCurrentPhase("rest-to-start");
@@ -260,6 +337,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
pauseTimeRef.current = null
isPausedRef.current = false
startTime = 0
activeSecondsElapsed.current = 0;
idleSecondsElapsed.current = 0;
previousTimeRef.current = null;
if (animationFrameIdRef.current !== null) {
cancelAnimationFrame(animationFrameIdRef.current);
animationFrameIdRef.current = null;
}
removeCurrentAction(armBot.modelUuid)
}
@@ -311,6 +395,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
}
}
}
const logStatus = (id: string, status: string) => {
// console.log('status: ', status);
}

View File

@@ -12,6 +12,7 @@ import Simulator from './simulator/simulator';
import Products from './products/products';
import Trigger from './triggers/trigger';
import useModuleStore from '../../store/useModuleStore';
import SimulationAnalysis from './analysis/simulationAnalysis';
function Simulation() {
const { activeModule } = useModuleStore();
@@ -53,6 +54,8 @@ function Simulation() {
<Simulator />
<SimulationAnalysis />
</>
}

View File

@@ -98,4 +98,4 @@ export async function determineExecutionMachineSequences(products: productsSchem
});
return executionSequences;
}
}

View File

@@ -13,10 +13,7 @@ import { useProductStore } from '../../../../../store/simulation/useProductStore
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
import MaterialAnimator from '../animator/materialAnimator';
type Timer = {
start: number | null;
active: boolean;
};
function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) {
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
@@ -27,20 +24,30 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
const { triggerPointActions } = useTriggerHandler();
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = useProductStore();
const { selectedProduct } = useSelectedProduct();
const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial, getLastMaterial } = useVehicleStore();
const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial, getLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = useVehicleStore();
const [currentPhase, setCurrentPhase] = useState<string>('stationed');
const [path, setPath] = useState<[number, number, number][]>([]);
const pauseTimeRef = useRef<number | null>(null);
const idleTimeRef = useRef<number>(0);
const activeTimeRef = useRef<number>(0);
const isPausedRef = useRef<boolean>(false);
const isSpeedRef = useRef<number>(0);
let startTime: number;
let fixedInterval: number;
const { speed } = useAnimationPlaySpeed();
const { isPaused } = usePauseButtonStore();
const previousTimeRef = useRef<number | null>(null);
const animationFrameIdRef = useRef<number | null>(null);
useEffect(() => {
isPausedRef.current = isPaused;
}, [isPaused]);
useEffect(() => {
isSpeedRef.current = speed;
}, [speed]);
const computePath = useCallback(
(start: any, end: any) => {
try {
@@ -58,7 +65,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
);
function vehicleStatus(modelId: string, status: string) {
//
// console.log(`${modelId} , ${status}`);
}
// Function to reset everything
@@ -72,6 +79,14 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
startTime = 0;
isPausedRef.current = false;
pauseTimeRef.current = 0;
resetTime(agvDetail.modelUuid)
activeTimeRef.current = 0
idleTimeRef.current = 0
previousTimeRef.current = null
if (animationFrameIdRef.current !== null) {
cancelAnimationFrame(animationFrameIdRef.current)
animationFrameIdRef.current = null
}
}
useEffect(() => {
@@ -119,12 +134,61 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point');
}
}
} else {
}
else {
reset()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [vehicles, currentPhase, path, isPlaying]);
function animate(currentTime: number) {
if (previousTimeRef.current === null) {
previousTimeRef.current = currentTime;
}
const deltaTime = (currentTime - previousTimeRef.current) / 1000;
previousTimeRef.current = currentTime;
if (agvDetail.isActive) {
if (!isPausedRef.current) {
activeTimeRef.current += deltaTime * isSpeedRef.current;
}
} else {
if (!isPausedRef.current) {
idleTimeRef.current += deltaTime * isSpeedRef.current; // Scale idle time by speed
}
}
animationFrameIdRef.current = requestAnimationFrame(animate);
}
useEffect(() => {
if (!isPlaying) return
if (!agvDetail.isActive) {
const roundedActiveTime = Math.round(activeTimeRef.current);
// console.log('Final Active Time:', roundedActiveTime, 'seconds');
incrementActiveTime(agvDetail.modelUuid, roundedActiveTime);
activeTimeRef.current = 0;
} else {
const roundedIdleTime = Math.round(idleTimeRef.current);
// console.log('Final Idle Time:', roundedIdleTime, 'seconds');
incrementIdleTime(agvDetail.modelUuid, roundedIdleTime);
idleTimeRef.current = 0;
}
if (animationFrameIdRef.current === null) {
animationFrameIdRef.current = requestAnimationFrame(animate);
}
return () => {
if (animationFrameIdRef.current !== null) {
cancelAnimationFrame(animationFrameIdRef.current);
animationFrameIdRef.current = null;
}
};
}, [agvDetail, isPlaying]);
function handleCallBack() {
if (currentPhase === 'stationed-pickup') {
setCurrentPhase('picking');
@@ -211,7 +275,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
action: VehicleAction
) {
startTime = performance.now();
const fixedInterval = ((unLoadDuration / vehicleCurrentLoad) * (1000 / speed));
const fixedInterval = ((unLoadDuration / vehicleCurrentLoad) * (1000 / isSpeedRef.current));
const unloadLoop = () => {
if (isPausedRef.current) {
@@ -417,7 +481,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
const elapsedTime = performance.now() - startTime;
const unLoadDuration = agvDetail.point.action.unLoadDuration;
fixedInterval = ((unLoadDuration / agvDetail.currentLoad) * (1000 / speed));
fixedInterval = ((unLoadDuration / agvDetail.currentLoad) * (1000 / isSpeedRef.current));
if (elapsedTime >= fixedInterval) {
let droppedMat = droppedMaterial - 1;
@@ -454,4 +518,10 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
);
}
export default VehicleInstance;
export default VehicleInstance;

View File

@@ -1,94 +0,0 @@
import { useMemo } from "react";
import { Bar } from "react-chartjs-2";
interface ChartComponentProps {
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const LineGraphComponent = ({
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
Regular: "normal" as const,
Bold: "bold" as const,
}),
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
size: fontSizeValue,
weight: fontWeightValue,
}),
[fontFamily, fontSizeValue, fontWeightValue]
);
const options = useMemo(
() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: chartFontStyle,
},
legend: {
display: false,
},
},
scales: {
x: {
ticks: {
display: false, // This hides the x-axis labels
},
},
},
}),
[title, chartFontStyle]
);
const chartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1",
borderColor: "#ffffff",
borderWidth: 2,
fill: false,
},
],
};
return <Bar data={chartData} options={options} />;
};
export default LineGraphComponent;

View File

@@ -1,93 +0,0 @@
import { useMemo } from "react";
import { Line } from "react-chartjs-2";
interface ChartComponentProps {
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const LineGraphComponent = ({
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
Regular: "normal" as const,
Bold: "bold" as const,
}),
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
size: fontSizeValue,
weight: fontWeightValue,
}),
[fontFamily, fontSizeValue, fontWeightValue]
);
const options = useMemo(
() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: chartFontStyle,
},
legend: {
display: false,
},
},
scales: {
x: {
ticks: {
display: false, // This hides the x-axis labels
},
},
},
}),
[title, chartFontStyle]
);
const chartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [
{
label: "My First Dataset",
data: [65, 59, 80, 81, 56, 55, 40],
backgroundColor: "#6f42c1", // Updated to #6f42c1 (Purple)
borderColor: "#ffffff", // Keeping border color white
borderWidth: 2,
fill: false,
},
],
};
return <Line data={chartData} options={options} />;
};
export default LineGraphComponent;

View File

@@ -1,90 +0,0 @@
import { useMemo } from "react";
import { Pie } from "react-chartjs-2";
interface ChartComponentProps {
type: any;
title: string;
fontFamily?: string;
fontSize?: string;
fontWeight?: "Light" | "Regular" | "Bold";
data: any;
}
const PieChartComponent = ({
title,
fontFamily,
fontSize,
fontWeight = "Regular",
}: ChartComponentProps) => {
// Memoize Font Weight Mapping
const chartFontWeightMap = useMemo(
() => ({
Light: "lighter" as const,
Regular: "normal" as const,
Bold: "bold" as const,
}),
[]
);
// Parse and Memoize Font Size
const fontSizeValue = useMemo(
() => (fontSize ? parseInt(fontSize) : 12),
[fontSize]
);
// Determine and Memoize Font Weight
const fontWeightValue = useMemo(
() => chartFontWeightMap[fontWeight],
[fontWeight, chartFontWeightMap]
);
// Memoize Chart Font Style
const chartFontStyle = useMemo(
() => ({
family: fontFamily || "Arial",
size: fontSizeValue,
weight: fontWeightValue,
}),
[fontFamily, fontSizeValue, fontWeightValue]
);
// Access the CSS variable for the primary accent color
const accentColor = getComputedStyle(document.documentElement)
.getPropertyValue("--accent-color")
.trim();
const options = useMemo(
() => ({
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: chartFontStyle,
},
legend: {
display: false,
},
},
}),
[title, chartFontStyle]
);
const chartData = {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [
{
label: "Dataset",
data: [12, 19, 3, 5, 2, 3],
backgroundColor: ["#6f42c1"],
borderColor: "#ffffff",
borderWidth: 2,
},
],
};
return <Pie data={chartData} options={options} />;
};
export default PieChartComponent;

View File

@@ -1,187 +1,4 @@
// import React, { useEffect, useRef, useMemo, useState } from "react";
// import { Chart } from "chart.js/auto";
// import { useThemeStore } from "../../../../store/useThemeStore";
// import io from "socket.io-client";
// import { Bar } from 'react-chartjs-2';
// import useChartStore from "../../../../store/useChartStore";
// // WebSocket Connection
// // const socket = io("http://localhost:5000"); // Adjust to your backend URL
// interface ChartComponentProps {
// type: any;
// title: string;
// fontFamily?: string;
// fontSize?: string;
// fontWeight?: "Light" | "Regular" | "Bold";
// data: any;
// }
// const LineGraphComponent = ({
// type,
// title,
// fontFamily,
// fontSize,
// fontWeight = "Regular",
// data,
// }: ChartComponentProps) => {
// const canvasRef = useRef<HTMLCanvasElement>(null);
// const { themeColor } = useThemeStore();
// const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
// labels: [],
// datasets: [],
// });
// const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
// const defaultData = {
// labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
// datasets: [
// {
// label: "Dataset",
// data: [12, 19, 3, 5, 2, 3],
// backgroundColor: ["#6f42c1"],
// borderColor: "#ffffff",
// borderWidth: 2,
// },
// ],
// };
// // Memoize Theme Colors to Prevent Unnecessary Recalculations
// const buttonActionColor = useMemo(
// () => themeColor[0] || "#5c87df",
// [themeColor]
// );
// const buttonAbortColor = useMemo(
// () => themeColor[1] || "#ffffff",
// [themeColor]
// );
// // Memoize Font Weight Mapping
// const chartFontWeightMap = useMemo(
// () => ({
// Light: "lighter" as const,
// Regular: "normal" as const,
// Bold: "bold" as const,
// }),
// []
// );
// // Parse and Memoize Font Size
// const fontSizeValue = useMemo(
// () => (fontSize ? parseInt(fontSize) : 12),
// [fontSize]
// );
// // Determine and Memoize Font Weight
// const fontWeightValue = useMemo(
// () => chartFontWeightMap[fontWeight],
// [fontWeight, chartFontWeightMap]
// );
// // Memoize Chart Font Style
// const chartFontStyle = useMemo(
// () => ({
// family: fontFamily || "Arial",
// size: fontSizeValue,
// weight: fontWeightValue,
// }),
// [fontFamily, fontSizeValue, fontWeightValue]
// );
// // Memoize Chart Data
// // const data = useMemo(() => propsData, [propsData]);
// // Memoize Chart Options
// const options = useMemo(
// () => ({
// responsive: true,
// maintainAspectRatio: false,
// plugins: {
// title: {
// display: true,
// text: title,
// font: chartFontStyle,
// },
// legend: {
// display: false,
// },
// },
// scales: {
// x: {
// ticks: {
// display: true, // This hides the x-axis labels
// },
// },
// },
// }),
// [title, chartFontStyle]
// );
// const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
// useEffect(() => {
// const socket = io(`http://${iotApiUrl}`);
// if ( measurements.length > 0 ) {
// var inputes = {
// measurements: measurements,
// duration: duration,
// interval: 1000,
// }
// // Start stream
// const startStream = () => {
// socket.emit("lineInput", inputes);
// }
// socket.on('connect', startStream);
// socket.on("lineOutput", (response) => {
// const responceData = response.data;
// console.log("Received data:", responceData);
// // Extract timestamps and values
// const labels = responceData.time;
// const datasets = measurements.map((measurement: any) => {
// const key = `${measurement.name}.${measurement.fields}`;
// return {
// label: key,
// data: responceData[key]?.values ?? [], // Ensure it exists
// backgroundColor: "#6f42c1",
// borderColor: "#ffffff",
// };
// });
// setChartData({ labels, datasets });
// });
// }
// return () => {
// socket.off("lineOutput");
// socket.emit("stop_stream"); // Stop streaming when component unmounts
// };
// }, [measurements, duration]);
// // useEffect(() => {
// // if (!canvasRef.current) return;
// // const ctx = canvasRef.current.getContext("2d");
// // if (!ctx) return;
// // const chart = new Chart(ctx, {
// // type,
// // data: chartData,
// // options: options,
// // });
// // return () => chart.destroy();
// // }, [chartData, type, title]);
// return <Bar data={measurements && measurements.length > 0 ? chartData : defaultData} options={options} />;
// };
// export default LineGraphComponent;
import React, { useEffect, useMemo, useState } from "react";
import { Bar } from "react-chartjs-2";
@@ -243,7 +60,7 @@ const BarGraphComponent = ({
],
};
useEffect(() => {}, []);
useEffect(() => { }, []);
// Memoize Theme Colors
const buttonActionColor = useMemo(

View File

@@ -1,188 +1,3 @@
// import React, { useEffect, useRef, useMemo, useState } from "react";
// import { Chart } from "chart.js/auto";
// import { useThemeStore } from "../../../../store/useThemeStore";
// import io from "socket.io-client";
// import { Pie } from 'react-chartjs-2';
// import useChartStore from "../../../../store/useChartStore";
// // WebSocket Connection
// // const socket = io("http://localhost:5000"); // Adjust to your backend URL
// interface ChartComponentProps {
// type: any;
// title: string;
// fontFamily?: string;
// fontSize?: string;
// fontWeight?: "Light" | "Regular" | "Bold";
// data: any;
// }
// const PieChartComponent = ({
// type,
// title,
// fontFamily,
// fontSize,
// fontWeight = "Regular",
// data,
// }: ChartComponentProps) => {
// const canvasRef = useRef<HTMLCanvasElement>(null);
// const { themeColor } = useThemeStore();
// const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
// labels: [],
// datasets: [],
// });
// const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
// const defaultData = {
// labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
// datasets: [
// {
// label: "Dataset",
// data: [12, 19, 3, 5, 2, 3],
// backgroundColor: ["#6f42c1"],
// borderColor: "#ffffff",
// borderWidth: 2,
// },
// ],
// };
// // Memoize Theme Colors to Prevent Unnecessary Recalculations
// const buttonActionColor = useMemo(
// () => themeColor[0] || "#6f42c1",
// [themeColor]
// );
// const buttonAbortColor = useMemo(
// () => themeColor[1] || "#ffffff",
// [themeColor]
// );
// // Memoize Font Weight Mapping
// const chartFontWeightMap = useMemo(
// () => ({
// Light: "lighter" as const,
// Regular: "normal" as const,
// Bold: "bold" as const,
// }),
// []
// );
// // Parse and Memoize Font Size
// const fontSizeValue = useMemo(
// () => (fontSize ? parseInt(fontSize) : 12),
// [fontSize]
// );
// // Determine and Memoize Font Weight
// const fontWeightValue = useMemo(
// () => chartFontWeightMap[fontWeight],
// [fontWeight, chartFontWeightMap]
// );
// // Memoize Chart Font Style
// const chartFontStyle = useMemo(
// () => ({
// family: fontFamily || "Arial",
// size: fontSizeValue,
// weight: fontWeightValue,
// }),
// [fontFamily, fontSizeValue, fontWeightValue]
// );
// // Memoize Chart Data
// // const data = useMemo(() => propsData, [propsData]);
// // Memoize Chart Options
// const options = useMemo(
// () => ({
// responsive: true,
// maintainAspectRatio: false,
// plugins: {
// title: {
// display: true,
// text: title,
// font: chartFontStyle,
// },
// legend: {
// display: false,
// },
// },
// scales: {
// // x: {
// // ticks: {
// // display: true, // This hides the x-axis labels
// // },
// // },
// },
// }),
// [title, chartFontStyle]
// );
// const { measurements, setMeasurements, updateDuration, duration } = useChartStore();
// useEffect(() => {
// const socket = io(`http://${iotApiUrl}`);
// if ( measurements.length > 0 ) {
// var inputes = {
// measurements: measurements,
// duration: duration,
// interval: 1000,
// }
// // Start stream
// const startStream = () => {
// socket.emit("lineInput", inputes);
// }
// socket.on('connect', startStream);
// socket.on("lineOutput", (response) => {
// const responceData = response.data;
// console.log("Received data:", responceData);
// // Extract timestamps and values
// const labels = responceData.time;
// const datasets = measurements.map((measurement: any) => {
// const key = `${measurement.name}.${measurement.fields}`;
// return {
// label: key,
// data: responceData[key]?.values ?? [], // Ensure it exists
// backgroundColor: "#6f42c1",
// borderColor: "#ffffff",
// };
// });
// setChartData({ labels, datasets });
// });
// }
// return () => {
// socket.off("lineOutput");
// socket.emit("stop_stream"); // Stop streaming when component unmounts
// };
// }, [measurements, duration]);
// // useEffect(() => {
// // if (!canvasRef.current) return;
// // const ctx = canvasRef.current.getContext("2d");
// // if (!ctx) return;
// // const chart = new Chart(ctx, {
// // type,
// // data: chartData,
// // options: options,
// // });
// // return () => chart.destroy();
// // }, [chartData, type, title]);
// return <Pie data={measurements && measurements.length > 0 ? chartData : defaultData} options={options} />;
// };
// export default PieChartComponent;
import React, { useEffect, useMemo, useState } from "react";
import { Pie } from "react-chartjs-2";
import io from "socket.io-client";

View File

@@ -203,7 +203,7 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({
scale={[0.5, 0.5, 0.5]}
rotation={rotation}
transform
sprite={false}
sprite={true}
zIndexRange={[1, 0]}
// style={{
// transform: transformStyle.transform,

View File

@@ -242,7 +242,7 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({
rotation={rotation}
scale={[0.5, 0.5, 0.5]}
transform
sprite={false}
sprite={true}
// style={{
// transform: transformStyle.transform,
// transformStyle: "preserve-3d",

View File

@@ -121,7 +121,7 @@ const StateWorking: React.FC<StateWorkingProps> = ({
scale={[0.5, 0.5, 0.5]}
transform
zIndexRange={[1, 0]}
sprite={false}
sprite={true}
// style={{
// transform: transformStyle.transform,
// transformStyle: "preserve-3d",

View File

@@ -83,6 +83,8 @@ const Throughput: React.FC<ThroughputProps> = ({
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
// Sample data for the line graph
const graphData: ChartData<"line"> = {
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
@@ -221,17 +223,11 @@ const Throughput: React.FC<ThroughputProps> = ({
scale={[0.5, 0.5, 0.5]}
transform
zIndexRange={[1, 0]}
sprite={false}
// style={{
// transform: transformStyle.transform,
// transformStyle: "preserve-3d",
// transition: "transform 0.1s ease-out",
// }}
sprite={true}
>
<div
className={`throughput-wrapper card ${
selectedChartId?.id === id ? "activeChart" : ""
}`}
className={`throughput-wrapper card ${selectedChartId?.id === id ? "activeChart" : ""
}`}
onClick={() => setSelectedChartId({ id: id, type: type })}
onContextMenu={onContextMenu}
>

View File

@@ -459,12 +459,88 @@ interface ShortcutStore {
}
export const useShortcutStore = create<ShortcutStore>((set) => ({
showShortcuts: false,
setShowShortcuts: (value) => set({ showShortcuts: value }),
toggleShortcuts: () =>
set((state) => ({ showShortcuts: !state.showShortcuts })),
showShortcuts: false,
setShowShortcuts: (value) => set({ showShortcuts: value }),
toggleShortcuts: () =>
set((state) => ({ showShortcuts: !state.showShortcuts })),
}));
export const useMachineCount = create<any>((set: any) => ({
machineCount: 0,
setMachineCount: (x: any) => set({ machineCount: x }),
}));
export const useMachineUptime = create<any>((set: any) => ({
machineActiveTime: 0,
setMachineActiveTime: (x: any) => set({ machineActiveTime: x }),
}));
export const useMaterialCycle = create<any>((set: any) => ({
materialCycleTime: 0,
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 }),
}));
export const useProcessBar = create<any>((set: any) => ({
processBar: [],
setProcessBar: (x: any) => set({ processBar: 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,
},
})),
}));
export interface ROISummaryData {
productName: string;
roiPercentage: number;
paybackPeriod: number;
totalCost: number;
revenueGenerated: number;
netProfit: number;
netLoss: number;
}
interface ROISummaryStore {
roiSummary: ROISummaryData;
setRoiSummaryData: (values: ROISummaryData) => void;
}
export const useROISummaryData = create<ROISummaryStore>((set) => ({
roiSummary: {
productName: "",
roiPercentage: 0,
paybackPeriod: 0,
totalCost: 0,
revenueGenerated: 0,
netProfit: 0,
netLoss: 0,
},
setRoiSummaryData: (values) => set({ roiSummary: values }),
}));
interface CompareStore {
comparePopUp: boolean;
setComparePopUp: (value: boolean) => void;

View File

@@ -1,154 +1,176 @@
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
interface MachineStore {
machines: MachineStatus[];
machines: MachineStatus[];
addMachine: (productId: string, machine: MachineEventSchema) => void;
removeMachine: (modelUuid: string) => void;
updateMachine: (
modelUuid: string,
updates: Partial<Omit<MachineStatus, 'modelUuid' | 'productId'>>
) => void;
clearMachines: () => void;
addMachine: (productId: string, machine: MachineEventSchema) => void;
removeMachine: (modelUuid: string) => void;
updateMachine: (
modelUuid: string,
updates: Partial<Omit<MachineStatus, "modelUuid" | "productId">>
) => void;
clearMachines: () => void;
addCurrentAction: (modelUuid: string, actionUuid: string, materialType: string, materialId: string) => void;
removeCurrentAction: (modelUuid: string) => void;
addCurrentAction: (
modelUuid: string,
actionUuid: string,
materialType: string,
materialId: string
) => void;
removeCurrentAction: (modelUuid: string) => void;
setMachineActive: (modelUuid: string, isActive: boolean) => void;
setMachineState: (modelUuid: string, newState: MachineStatus['state']) => void;
setMachineActive: (modelUuid: string, isActive: boolean) => void;
setMachineState: (
modelUuid: string,
newState: MachineStatus["state"]
) => void;
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
resetTime: (modelUuid: string) => void;
getMachineById: (modelUuid: string) => MachineStatus | undefined;
getMachinesByProduct: (productId: string) => MachineStatus[];
getMachinesBystate: (state: string) => MachineStatus[];
getActiveMachines: () => MachineStatus[];
getIdleMachines: () => MachineStatus[];
getMachineById: (modelUuid: string) => MachineStatus | undefined;
getMachinesByProduct: (productId: string) => MachineStatus[];
getMachinesBystate: (state: string) => MachineStatus[];
getActiveMachines: () => MachineStatus[];
getIdleMachines: () => MachineStatus[];
}
export const useMachineStore = create<MachineStore>()(
immer((set, get) => ({
machines: [],
immer((set, get) => ({
machines: [],
addMachine: (productId, machine) => {
set((state) => {
const exists = state.machines.some(m => m.modelUuid === machine.modelUuid);
if (!exists) {
state.machines.push({
...machine,
productId,
isActive: false,
idleTime: 0,
activeTime: 0,
state: 'idle',
});
}
});
},
addMachine: (productId, machine) => {
set((state) => {
const exists = state.machines.some(
(m) => m.modelUuid === machine.modelUuid
);
if (!exists) {
state.machines.push({
...machine,
productId,
isActive: false,
idleTime: 0,
activeTime: 0,
state: "idle",
});
}
});
},
removeMachine: (modelUuid) => {
set((state) => {
state.machines = state.machines.filter(m => m.modelUuid !== modelUuid);
});
},
removeMachine: (modelUuid) => {
set((state) => {
state.machines = state.machines.filter(
(m) => m.modelUuid !== modelUuid
);
});
},
updateMachine: (modelUuid, updates) => {
set((state) => {
const machine = state.machines.find(m => m.modelUuid === modelUuid);
if (machine) {
Object.assign(machine, updates);
}
});
},
updateMachine: (modelUuid, updates) => {
set((state) => {
const machine = state.machines.find((m) => m.modelUuid === modelUuid);
if (machine) {
Object.assign(machine, updates);
}
});
},
clearMachines: () => {
set((state) => {
state.machines = [];
});
},
clearMachines: () => {
set((state) => {
state.machines = [];
});
},
addCurrentAction: (modelUuid, actionUuid, materialType, materialId) => {
set((state) => {
const armBot = state.machines.find(a => a.modelUuid === modelUuid);
if (armBot) {
const action = armBot.point.action;
if (action) {
armBot.currentAction = {
actionUuid: actionUuid,
actionName: action.actionName,
materialType: materialType,
materialId: materialId
};
}
}
});
},
addCurrentAction: (modelUuid, actionUuid, materialType, materialId) => {
set((state) => {
const armBot = state.machines.find((a) => a.modelUuid === modelUuid);
if (armBot) {
const action = armBot.point.action;
if (action) {
armBot.currentAction = {
actionUuid: actionUuid,
actionName: action.actionName,
materialType: materialType,
materialId: materialId,
};
}
}
});
},
removeCurrentAction: (modelUuid) => {
set((state) => {
const armBot = state.machines.find(a => a.modelUuid === modelUuid);
if (armBot) {
armBot.currentAction = undefined;
}
});
},
removeCurrentAction: (modelUuid) => {
set((state) => {
const armBot = state.machines.find((a) => a.modelUuid === modelUuid);
if (armBot) {
armBot.currentAction = undefined;
}
});
},
setMachineActive: (modelUuid, isActive) => {
set((state) => {
const machine = state.machines.find(m => m.modelUuid === modelUuid);
if (machine) {
machine.isActive = isActive;
}
});
},
setMachineActive: (modelUuid, isActive) => {
set((state) => {
const machine = state.machines.find((m) => m.modelUuid === modelUuid);
if (machine) {
machine.isActive = isActive;
}
});
},
setMachineState: (modelUuid, newState) => {
set((state) => {
const machine = state.machines.find(m => m.modelUuid === modelUuid);
if (machine) {
machine.state = newState;
}
});
},
setMachineState: (modelUuid, newState) => {
set((state) => {
const machine = state.machines.find((m) => m.modelUuid === modelUuid);
if (machine) {
machine.state = newState;
}
});
},
incrementActiveTime: (modelUuid, incrementBy) => {
set((state) => {
const machine = state.machines.find(m => m.modelUuid === modelUuid);
if (machine) {
machine.activeTime += incrementBy;
}
});
},
incrementActiveTime: (modelUuid, incrementBy) => {
set((state) => {
const machine = state.machines.find((m) => m.modelUuid === modelUuid);
if (machine) {
machine.activeTime += incrementBy;
}
});
},
incrementIdleTime: (modelUuid, incrementBy) => {
set((state) => {
const machine = state.machines.find(m => m.modelUuid === modelUuid);
if (machine) {
machine.idleTime += incrementBy;
}
});
},
incrementIdleTime: (modelUuid, incrementBy) => {
set((state) => {
const machine = state.machines.find((m) => m.modelUuid === modelUuid);
if (machine) {
machine.idleTime += incrementBy;
}
});
},
resetTime: (modelUuid) => {
set((state) => {
const machine = state.machines.find((m) => m.modelUuid === modelUuid);
if (machine) {
machine.activeTime = 0;
machine.idleTime = 0;
}
});
},
getMachineById: (modelUuid) => {
return get().machines.find(m => m.modelUuid === modelUuid);
},
getMachineById: (modelUuid) => {
return get().machines.find((m) => m.modelUuid === modelUuid);
},
getMachinesByProduct: (productId) => {
return get().machines.filter(m => m.productId === productId);
},
getMachinesByProduct: (productId) => {
return get().machines.filter((m) => m.productId === productId);
},
getMachinesBystate: (state) => {
return get().machines.filter(m => m.state === state);
},
getMachinesBystate: (state) => {
return get().machines.filter((m) => m.state === state);
},
getActiveMachines: () => {
return get().machines.filter(m => m.isActive);
},
getActiveMachines: () => {
return get().machines.filter((m) => m.isActive);
},
getIdleMachines: () => {
return get().machines.filter(m => !m.isActive && m.state === 'idle');
},
}))
getIdleMachines: () => {
return get().machines.filter((m) => !m.isActive && m.state === "idle");
},
}))
);

View File

@@ -29,6 +29,7 @@ interface VehiclesStore {
clearCurrentMaterials: (modelUuid: string) => void;
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
resetTime: (modelUuid: string) => void;
getVehicleById: (modelUuid: string) => VehicleStatus | undefined;
getVehiclesByProduct: (productId: string) => VehicleStatus[];
@@ -223,6 +224,16 @@ export const useVehicleStore = create<VehiclesStore>()(
});
},
resetTime: (modelUuid) => {
set((state) => {
const vehicle = state.vehicles.find((v) => v.modelUuid === modelUuid);
if (vehicle) {
vehicle.activeTime = 0;
vehicle.idleTime = 0;
}
});
},
getVehicleById: (modelUuid) => {
return get().vehicles.find((v) => v.modelUuid === modelUuid);
},

View File

@@ -768,7 +768,6 @@
display: flex;
flex-direction: column;
border-radius: #{$border-radius-medium};
overflow: hidden;
padding: 4px;
min-width: 150px;
@@ -780,6 +779,7 @@
color: var(--text-color);
text-wrap: nowrap;
cursor: pointer;
pointer-events: all;
&:hover {
background: var(--highlight-accent-color);