Merge remote-tracking branch 'origin/analysis'
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -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);
|
||||
}}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -37,7 +37,6 @@ const Card: React.FC<CardProps> = ({
|
||||
image,
|
||||
description,
|
||||
AssetID,
|
||||
modelUrl,
|
||||
onSelectCard,
|
||||
}) => {
|
||||
const handleCardSelect = () => {
|
||||
|
||||
@@ -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>
|
||||
))}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
125
app/src/modules/simulation/analysis/ROI/roiData.tsx
Normal file
125
app/src/modules/simulation/analysis/ROI/roiData.tsx
Normal 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 (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
25
app/src/modules/simulation/analysis/simulationAnalysis.tsx
Normal file
25
app/src/modules/simulation/analysis/simulationAnalysis.tsx
Normal 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
|
||||
@@ -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 (
|
||||
<>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 />
|
||||
|
||||
</>
|
||||
|
||||
}
|
||||
|
||||
@@ -98,4 +98,4 @@ export async function determineExecutionMachineSequences(products: productsSchem
|
||||
});
|
||||
|
||||
return executionSequences;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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(
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user