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 RegularDropDown from "../../../ui/inputs/RegularDropDown";
|
||||||
import { AnalysisPresetsType } from "../../../../types/analysis";
|
import { AnalysisPresetsType } from "../../../../types/analysis";
|
||||||
import RenderAnalysisInputs from "./RenderAnalysisInputs";
|
import RenderAnalysisInputs from "./RenderAnalysisInputs";
|
||||||
|
import { useInputValues } from "../../../../store/builder/store";
|
||||||
|
|
||||||
const Analysis: React.FC = () => {
|
const Analysis: React.FC = () => {
|
||||||
const [selectedOption, setSelectedOption] = useState("Throughput time");
|
const [selectedOption, setSelectedOption] = useState("Throughput time");
|
||||||
@@ -48,6 +49,10 @@ const Analysis: React.FC = () => {
|
|||||||
type: "default",
|
type: "default",
|
||||||
inputs: { label: "Fixed costs", activeOption: "INR" },
|
inputs: { label: "Fixed costs", activeOption: "INR" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "default",
|
||||||
|
inputs: { label: "Initial Investment", activeOption: "INR" },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: "default",
|
type: "default",
|
||||||
inputs: { label: "Salvage value", activeOption: "Hrs" },
|
inputs: { label: "Salvage value", activeOption: "Hrs" },
|
||||||
@@ -63,6 +68,8 @@ const Analysis: React.FC = () => {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { inputValues, setInputValues, updateInputValue } = useInputValues();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="analysis-main-wrapper">
|
<div className="analysis-main-wrapper">
|
||||||
<div className="analysis-main-container">
|
<div className="analysis-main-container">
|
||||||
@@ -88,10 +95,14 @@ const Analysis: React.FC = () => {
|
|||||||
presets={
|
presets={
|
||||||
AnalysisPresets[selectedOption as keyof AnalysisPresetsType]
|
AnalysisPresets[selectedOption as keyof AnalysisPresetsType]
|
||||||
}
|
}
|
||||||
|
inputValues={inputValues}
|
||||||
|
onInputChange={(label, value) => {
|
||||||
|
updateInputValue(label, value);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="buttons-container">
|
<div className="buttons-container">
|
||||||
<input type="button" value={"Clear"} className="cancel" />
|
<input type="button" value={"Clear"} className="cancel" onClick={() => setInputValues({})} />
|
||||||
<input type="button" value={"Update"} className="submit" />
|
<input type="button" value={"Update"} className="submit" onClick={() => setInputValues(inputValues)} />
|
||||||
</div>
|
</div>
|
||||||
<div className="create-custom-analysis-container">
|
<div className="create-custom-analysis-container">
|
||||||
<div className="custom-analysis-header">Create Custom Analysis</div>
|
<div className="custom-analysis-header">Create Custom Analysis</div>
|
||||||
|
|||||||
@@ -6,12 +6,11 @@ import { AnalysisPresetsType } from "../../../../types/analysis";
|
|||||||
interface InputRendererProps {
|
interface InputRendererProps {
|
||||||
keyName: string;
|
keyName: string;
|
||||||
presets: AnalysisPresetsType[keyof AnalysisPresetsType];
|
presets: AnalysisPresetsType[keyof AnalysisPresetsType];
|
||||||
|
inputValues: Record<string, string>; // <-- Add this line
|
||||||
|
onInputChange: (label: string, value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RenderAnalysisInputs: React.FC<InputRendererProps> = ({
|
const RenderAnalysisInputs: React.FC<InputRendererProps> = ({ keyName, presets,inputValues, onInputChange }) => {
|
||||||
keyName,
|
|
||||||
presets,
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<div key={`main-${keyName}`} className="analysis-inputs">
|
<div key={`main-${keyName}`} className="analysis-inputs">
|
||||||
{presets.map((preset, index) => {
|
{presets.map((preset, index) => {
|
||||||
@@ -20,9 +19,9 @@ const RenderAnalysisInputs: React.FC<InputRendererProps> = ({
|
|||||||
<InputWithDropDown
|
<InputWithDropDown
|
||||||
key={index}
|
key={index}
|
||||||
label={preset.inputs.label}
|
label={preset.inputs.label}
|
||||||
value=""
|
value={inputValues[preset.inputs.label] || ""}
|
||||||
activeOption={preset.inputs.activeOption}
|
activeOption={preset.inputs.activeOption}
|
||||||
onChange={() => {}}
|
onChange={(newValue) => onInputChange(preset.inputs.label, newValue)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Line } from "react-chartjs-2";
|
import { Line } from "react-chartjs-2";
|
||||||
import {
|
import {
|
||||||
Chart as ChartJS,
|
Chart as ChartJS,
|
||||||
@@ -9,23 +9,18 @@ import {
|
|||||||
} from "chart.js";
|
} from "chart.js";
|
||||||
import { PowerIcon, ProductionCapacityIcon } from "../../icons/analysis";
|
import { PowerIcon, ProductionCapacityIcon } from "../../icons/analysis";
|
||||||
import SkeletonUI from "../../templates/SkeletonUI";
|
import SkeletonUI from "../../templates/SkeletonUI";
|
||||||
|
import { useInputValues, useMachineUptime, useProductionCapacityData } from "../../../store/builder/store";
|
||||||
|
|
||||||
ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement);
|
ChartJS.register(LineElement, CategoryScale, LinearScale, PointElement);
|
||||||
|
|
||||||
const ThroughputSummary:React.FC = () => {
|
const ThroughputSummary: React.FC = () => {
|
||||||
// Define all data internally within the component
|
// Define all data internally within the component
|
||||||
const timeRange = {
|
const timeRange = {
|
||||||
startTime: "08:00 AM",
|
startTime: "08:00 AM",
|
||||||
endTime: "06:00 PM",
|
endTime: "06:00 PM",
|
||||||
};
|
};
|
||||||
|
|
||||||
const throughputData = {
|
const { machineActiveTime } = useMachineUptime();
|
||||||
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 energyConsumption = {
|
const energyConsumption = {
|
||||||
energyConsumed: 456,
|
energyConsumed: 456,
|
||||||
unit: "KWH",
|
unit: "KWH",
|
||||||
@@ -38,19 +33,8 @@ const ThroughputSummary:React.FC = () => {
|
|||||||
{ shift: 3, percentage: 30, color: "#7981F5" },
|
{ shift: 3, percentage: 30, color: "#7981F5" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Chart data configuration
|
const { productionCapacityData } = useProductionCapacityData()
|
||||||
const chartData = {
|
|
||||||
labels: throughputData.labels,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "Units/hour",
|
|
||||||
data: throughputData.data,
|
|
||||||
borderColor: "#B392F0",
|
|
||||||
tension: 0.4,
|
|
||||||
pointRadius: 0, // Hide points
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const chartOptions = {
|
const chartOptions = {
|
||||||
responsive: true,
|
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 (
|
return (
|
||||||
<div className="production analysis-card">
|
<div className="production analysis-card">
|
||||||
@@ -94,13 +118,13 @@ const ThroughputSummary:React.FC = () => {
|
|||||||
<>
|
<>
|
||||||
<div className="process-container">
|
<div className="process-container">
|
||||||
<div className="throughput-value">
|
<div className="throughput-value">
|
||||||
<span className="value">{throughputData.totalThroughput}</span>{" "}
|
<span className="value">{productionCapacityData}</span>{" "}
|
||||||
Units/hour
|
Units/hour
|
||||||
</div>
|
</div>
|
||||||
<div className="lineChart">
|
<div className="lineChart">
|
||||||
<div className="assetUsage">
|
<div className="assetUsage">
|
||||||
<div className="key">Asset usage</div>
|
<div className="key">Asset usage</div>
|
||||||
<div className="value">{throughputData.assetUsage}%</div>
|
<div className="value">{parseFloat(inputValues["Yield rate"])}%</div>
|
||||||
</div>
|
</div>
|
||||||
<Line data={chartData} options={chartOptions} />
|
<Line data={chartData} options={chartOptions} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
CostBreakDownIcon,
|
CostBreakDownIcon,
|
||||||
LightBulpIcon,
|
LightBulpIcon,
|
||||||
@@ -9,6 +9,8 @@ import {
|
|||||||
import SemiCircleProgress from "./SemiCircleProgress";
|
import SemiCircleProgress from "./SemiCircleProgress";
|
||||||
import { ArrowIcon } from "../../icons/ExportCommonIcons";
|
import { ArrowIcon } from "../../icons/ExportCommonIcons";
|
||||||
import SkeletonUI from "../../templates/SkeletonUI";
|
import SkeletonUI from "../../templates/SkeletonUI";
|
||||||
|
import { useROISummaryData } from "../../../store/builder/store";
|
||||||
|
import { set } from "immer/dist/internal";
|
||||||
|
|
||||||
const ROISummary = ({
|
const ROISummary = ({
|
||||||
roiSummaryData = {
|
roiSummaryData = {
|
||||||
@@ -79,8 +81,17 @@ const ROISummary = ({
|
|||||||
const year = now.getFullYear();
|
const year = now.getFullYear();
|
||||||
return `${day} ${month}, ${year}`;
|
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 (
|
return (
|
||||||
<div className="roiSummary-container analysis-card">
|
<div className="roiSummary-container analysis-card">
|
||||||
<div className="roiSummary-wrapper analysis-card-wrapper">
|
<div className="roiSummary-wrapper analysis-card-wrapper">
|
||||||
@@ -98,14 +109,14 @@ const ROISummary = ({
|
|||||||
<div className="product-info">
|
<div className="product-info">
|
||||||
<ROISummaryProductName />
|
<ROISummaryProductName />
|
||||||
<div className="product-label">Product :</div>
|
<div className="product-label">Product :</div>
|
||||||
<div className="product-name">{roiSummaryData.productName}</div>
|
<div className="product-name">{roiSummary.productName}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="playBack">
|
<div className="playBack">
|
||||||
<SonarCrownIcon />
|
<SonarCrownIcon />
|
||||||
<div className="icon"></div>
|
<div className="icon"></div>
|
||||||
<div className="info">
|
<div className="info">
|
||||||
<span>+{roiSummaryData.roiPercentage}%</span> ROI with payback
|
<span> {roiSummary.roiPercentage}%</span> ROI with payback
|
||||||
in just <span>{roiSummaryData.paybackPeriod}</span> months
|
in just <span>{roiSummary.paybackPeriod}</span> months
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="roi-details">
|
<div className="roi-details">
|
||||||
@@ -122,7 +133,7 @@ const ROISummary = ({
|
|||||||
<span className="metric-label">Total Cost Incurred</span>
|
<span className="metric-label">Total Cost Incurred</span>
|
||||||
<span className="metric-value">
|
<span className="metric-value">
|
||||||
<span>₹</span>
|
<span>₹</span>
|
||||||
{roiSummaryData.totalCost}
|
{roiSummary.totalCost}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="metric-item">
|
<div className="metric-item">
|
||||||
@@ -130,14 +141,12 @@ const ROISummary = ({
|
|||||||
<span className="metric-value">
|
<span className="metric-value">
|
||||||
<span>₹</span>
|
<span>₹</span>
|
||||||
|
|
||||||
{roiSummaryData.revenueGenerated}
|
{roiSummary.revenueGenerated}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`metric-item net-profit ${
|
className={`metric-item net-profit ${roiSummary.netProfit > 0 ? "profit" : "loss"}`}
|
||||||
roiSummaryData.netProfit ?? "loss"
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<div className="metric-label">
|
<div className="metric-label">
|
||||||
<span>↑</span>
|
<span>↑</span>
|
||||||
@@ -145,9 +154,9 @@ const ROISummary = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="metric-value">
|
<div className="metric-value">
|
||||||
<span>₹</span>
|
<span>₹</span>
|
||||||
{roiSummaryData.netProfit
|
{roiSummary.netProfit > 0
|
||||||
? roiSummaryData.netProfit
|
? roiSummary.netProfit
|
||||||
: roiSummaryData.netLoss}
|
: roiSummary.netLoss}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -164,9 +173,8 @@ const ROISummary = ({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`breakdown-table-wrapper ${
|
className={`breakdown-table-wrapper ${isTableOpen ? "open" : "closed"
|
||||||
isTableOpen ? "open" : "closed"
|
}`}
|
||||||
}`}
|
|
||||||
style={{
|
style={{
|
||||||
transition: "max-height 0.3s ease-in-out",
|
transition: "max-height 0.3s ease-in-out",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
@@ -190,8 +198,8 @@ const ROISummary = ({
|
|||||||
row.item === "Total"
|
row.item === "Total"
|
||||||
? "total-row"
|
? "total-row"
|
||||||
: row.item === "Net Profit"
|
: row.item === "Net Profit"
|
||||||
? "net-profit-row"
|
? "net-profit-row"
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<td>{row.item}</td>
|
<td>{row.item}</td>
|
||||||
@@ -228,6 +236,7 @@ const ROISummary = ({
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<SkeletonUI type={"default"} />
|
<SkeletonUI type={"default"} />
|
||||||
|
// <div> No Data</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,21 +1,45 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useMachineCount, useMachineUptime, useMaterialCycle, useProductionCapacityData, useThroughPutData } from "../../../store/builder/store";
|
||||||
import {
|
import {
|
||||||
ThroughputSummaryIcon,
|
ThroughputSummaryIcon,
|
||||||
} from "../../icons/analysis";
|
} from "../../icons/analysis";
|
||||||
import SkeletonUI from "../../templates/SkeletonUI";
|
import SkeletonUI from "../../templates/SkeletonUI";
|
||||||
|
|
||||||
const ProductionCapacity = ({
|
const ProductionCapacity = ({
|
||||||
progressPercent = 50,
|
avgProcessTime = "28.4 Secs/unit",
|
||||||
avgProcessTime = "28.4",
|
|
||||||
machineUtilization = "78%",
|
machineUtilization = "78%",
|
||||||
throughputValue = 128,
|
throughputValue = 128,
|
||||||
timeRange = { startTime: "08:00 AM", endTime: "09:00 AM" },
|
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 totalBars = 6;
|
||||||
const barsToFill = Math.floor((progressPercent / 100) * totalBars);
|
const barsToFill = Math.floor((progressPercent / 100) * totalBars);
|
||||||
const partialFillPercent =
|
const partialFillPercent =
|
||||||
((progressPercent / 100) * totalBars - barsToFill) * 100;
|
((progressPercent / 100) * totalBars - barsToFill) * 100;
|
||||||
|
|
||||||
const isLoading = 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 (
|
return (
|
||||||
<div className="throughtputSummary-container analysis-card">
|
<div className="throughtputSummary-container analysis-card">
|
||||||
<div className="throughtputSummary-wrapper analysis-card-wrapper">
|
<div className="throughtputSummary-wrapper analysis-card-wrapper">
|
||||||
@@ -34,7 +58,8 @@ const ProductionCapacity = ({
|
|||||||
<>
|
<>
|
||||||
<div className="process-container">
|
<div className="process-container">
|
||||||
<div className="throughput-value">
|
<div className="throughput-value">
|
||||||
<span className="value">{avgProcessTime}</span> secs/unit
|
<span className="value">{throughputData}</span> Units/hour
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Dynamic Progress Bar */}
|
{/* Dynamic Progress Bar */}
|
||||||
@@ -56,12 +81,13 @@ const ProductionCapacity = ({
|
|||||||
|
|
||||||
<div className="metrics-section">
|
<div className="metrics-section">
|
||||||
<div className="metric">
|
<div className="metric">
|
||||||
<span className="label">Units/hour</span>
|
<span className="label">Avg. Process Time</span>
|
||||||
<span className="value">{throughputValue} avg.</span>
|
<span className="value">{materialCycleTime} secs/unit </span>
|
||||||
</div>
|
</div>
|
||||||
<div className="metric">
|
<div className="metric">
|
||||||
<span className="label">Machine Utilization</span>
|
<span className="label">Machine Utilization</span>
|
||||||
<span className="value">{machineUtilization}</span>
|
<span className="value">{machineActiveTime}</span>
|
||||||
|
{/* <span className="value">{machineActiveTime}</span> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
|
|||||||
max={max}
|
max={max}
|
||||||
step={step}
|
step={step}
|
||||||
type="number"
|
type="number"
|
||||||
defaultValue={value}
|
// defaultValue={value}
|
||||||
|
value={value}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onChange(e.target.value);
|
onChange(e.target.value);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
import { ExitIcon, PlayStopIcon, ResetIcon } from "../../icons/SimulationIcons";
|
import { ExitIcon, PlayStopIcon, ResetIcon } from "../../icons/SimulationIcons";
|
||||||
import { useActiveTool } from "../../../store/builder/store";
|
import { useActiveTool, useProcessBar } from "../../../store/builder/store";
|
||||||
import {
|
import {
|
||||||
useAnimationPlaySpeed,
|
useAnimationPlaySpeed,
|
||||||
usePauseButtonStore,
|
usePauseButtonStore,
|
||||||
@@ -111,21 +111,25 @@ const SimulationPlayer: React.FC = () => {
|
|||||||
const hourlySimulation = 25;
|
const hourlySimulation = 25;
|
||||||
const dailyProduction = 75;
|
const dailyProduction = 75;
|
||||||
const monthlyROI = 50;
|
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 = [
|
useEffect(() => {
|
||||||
{ name: "process 1", completed: 0 }, // 0% completed
|
// console.log('processBar: ', processBar);
|
||||||
{ name: "process 2", completed: 20 }, // 20% completed
|
}, [processBar])
|
||||||
{ 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 intervals = [10, 20, 30, 40, 50, 60]; // in minutes
|
const intervals = [50, 20, 30, 40, 50, 60]; // in minutes
|
||||||
const totalSegments = intervals.length;
|
const totalSegments = intervals.length;
|
||||||
const progress = 20; // percent (example)
|
const progress = 20; // percent (example)
|
||||||
|
|
||||||
@@ -388,22 +392,26 @@ const SimulationPlayer: React.FC = () => {
|
|||||||
ref={processWrapperRef}
|
ref={processWrapperRef}
|
||||||
onMouseDown={handleProcessMouseDown}
|
onMouseDown={handleProcessMouseDown}
|
||||||
>
|
>
|
||||||
{process.map((item, index) => (
|
{processBar?.length > 0 ? (
|
||||||
<div
|
processBar.map((item: any, index: any) => (
|
||||||
key={`${index}-${item.name}`}
|
|
||||||
className="process"
|
|
||||||
style={{
|
|
||||||
width: `${item.completed}%`,
|
|
||||||
backgroundColor: getAvatarColor(index),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
className="process-player"
|
key={`${index}-${item.name}`}
|
||||||
ref={processPlayerRef}
|
className="process"
|
||||||
style={{ left: playerPosition, position: "absolute" }}
|
style={{
|
||||||
></div>
|
width: `${item.completed}%`,
|
||||||
</div>
|
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ const Card: React.FC<CardProps> = ({
|
|||||||
image,
|
image,
|
||||||
description,
|
description,
|
||||||
AssetID,
|
AssetID,
|
||||||
modelUrl,
|
|
||||||
onSelectCard,
|
onSelectCard,
|
||||||
}) => {
|
}) => {
|
||||||
const handleCardSelect = () => {
|
const handleCardSelect = () => {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const CardsContainer: React.FC<ModelsProps> = ({ models }) => {
|
|||||||
}) => {
|
}) => {
|
||||||
setSelectedCard(cardData);
|
setSelectedCard(cardData);
|
||||||
const res = await fetchGltfUrl(cardData.assetName, cardData.AssetID);
|
const res = await fetchGltfUrl(cardData.assetName, cardData.AssetID);
|
||||||
console.log("res: ", res);
|
// console.log("res: ", res);
|
||||||
setModelUrl(res.url);
|
setModelUrl(res.url);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
@@ -65,8 +65,8 @@ const CardsContainer: React.FC<ModelsProps> = ({ models }) => {
|
|||||||
onSelectCard={handleCardSelect}
|
onSelectCard={handleCardSelect}
|
||||||
AssetID={assetDetail.AssetID}
|
AssetID={assetDetail.AssetID}
|
||||||
image={assetDetail.thumbnail}
|
image={assetDetail.thumbnail}
|
||||||
|
modelUrl={assetDetail.modelfileID}
|
||||||
description={assetDetail.description}
|
description={assetDetail.description}
|
||||||
modelUrl={modelUrl}
|
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ const MarketPlace = () => {
|
|||||||
try {
|
try {
|
||||||
const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6");
|
const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6");
|
||||||
setModels(filt.items);
|
setModels(filt.items);
|
||||||
console.log('filt.items: ', filt.items);
|
|
||||||
setFilteredModels(filt.items);
|
setFilteredModels(filt.items);
|
||||||
setisLoading(false);
|
setisLoading(false);
|
||||||
} catch {
|
} 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 { useEffect, useRef, useState } from 'react'
|
||||||
import { useMachineStore } from '../../../../../store/simulation/useMachineStore';
|
import { useMachineStore } from '../../../../../store/simulation/useMachineStore';
|
||||||
import { usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||||
import MachineAnimator from '../animator/machineAnimator';
|
import MachineAnimator from '../animator/machineAnimator';
|
||||||
import { useProductStore } from '../../../../../store/simulation/useProductStore';
|
import { useProductStore } from '../../../../../store/simulation/useProductStore';
|
||||||
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
|
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
|
||||||
@@ -9,22 +9,93 @@ import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHa
|
|||||||
function MachineInstance({ machineDetail }: { machineDetail: MachineStatus }) {
|
function MachineInstance({ machineDetail }: { machineDetail: MachineStatus }) {
|
||||||
const [currentPhase, setCurrentPhase] = useState<string>('idle');
|
const [currentPhase, setCurrentPhase] = useState<string>('idle');
|
||||||
let isIncrememtable = useRef<boolean>(true);
|
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 { isPlaying } = usePlayButtonStore();
|
||||||
const { machines, setMachineState, setMachineActive } = useMachineStore();
|
const { machines, setMachineState, setMachineActive, incrementIdleTime, incrementActiveTime, resetTime } = useMachineStore();
|
||||||
const { selectedProduct } = useSelectedProduct();
|
const { selectedProduct } = useSelectedProduct();
|
||||||
const { getActionByUuid } = useProductStore();
|
const { getActionByUuid } = useProductStore();
|
||||||
const { triggerPointActions } = useTriggerHandler();
|
const { triggerPointActions } = useTriggerHandler();
|
||||||
|
const { speed } = useAnimationPlaySpeed();
|
||||||
|
const { isPaused } = usePauseButtonStore();
|
||||||
|
useEffect(() => {
|
||||||
|
isPausedRef.current = isPaused;
|
||||||
|
}, [isPaused]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
isSpeedRef.current = speed;
|
||||||
|
}, [speed]);
|
||||||
|
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
setCurrentPhase("idle");
|
setCurrentPhase("idle");
|
||||||
setMachineState(machineDetail.modelUuid, 'idle');
|
setMachineState(machineDetail.modelUuid, 'idle');
|
||||||
setMachineActive(machineDetail.modelUuid, false);
|
setMachineActive(machineDetail.modelUuid, false);
|
||||||
isIncrememtable.current = true;
|
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) {
|
function machineStatus(modelId: string, status: string) {
|
||||||
// console.log(`${modelId} , ${status}`);
|
// 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(() => {
|
useEffect(() => {
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import IKInstance from '../ikInstance/ikInstance';
|
|||||||
import RoboticArmAnimator from '../animator/roboticArmAnimator';
|
import RoboticArmAnimator from '../animator/roboticArmAnimator';
|
||||||
import MaterialAnimator from '../animator/materialAnimator';
|
import MaterialAnimator from '../animator/materialAnimator';
|
||||||
import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb";
|
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 { useMaterialStore } from '../../../../../store/simulation/useMaterialStore';
|
||||||
import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
|
import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
|
||||||
import { useProductStore } from '../../../../../store/simulation/useProductStore';
|
import { useProductStore } from '../../../../../store/simulation/useProductStore';
|
||||||
@@ -26,9 +26,11 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
const groupRef = useRef<any>(null);
|
const groupRef = useRef<any>(null);
|
||||||
const pauseTimeRef = useRef<number | null>(null);
|
const pauseTimeRef = useRef<number | null>(null);
|
||||||
const isPausedRef = useRef<boolean>(false);
|
const isPausedRef = useRef<boolean>(false);
|
||||||
|
const isSpeedRef = useRef<any>(null);
|
||||||
|
const isIdleRef = useRef<boolean>(false);
|
||||||
let startTime: number;
|
let startTime: number;
|
||||||
|
|
||||||
const { setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
|
const { armBots, setArmBotActive, setArmBotState, removeCurrentAction, incrementActiveTime, incrementIdleTime } = useArmBotStore();
|
||||||
const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore();
|
const { decrementVehicleLoad, removeLastMaterial } = useVehicleStore();
|
||||||
const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore();
|
const { removeLastMaterial: removeLastStorageMaterial, updateCurrentLoad } = useStorageUnitStore();
|
||||||
const { setIsVisible, setIsPaused, getMaterialById } = useMaterialStore();
|
const { setIsVisible, setIsPaused, getMaterialById } = useMaterialStore();
|
||||||
@@ -38,6 +40,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
const { isPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
const { isReset } = useResetButtonStore();
|
const { isReset } = useResetButtonStore();
|
||||||
const { isPaused } = usePauseButtonStore();
|
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 checkActiveRoboticArms = useCheckActiveRoboticArmsInSubsequence();
|
||||||
|
|
||||||
const lastRemoved = useRef<{ type: string, materialId: string } | null>(null);
|
const lastRemoved = useRef<{ type: string, materialId: string } | null>(null);
|
||||||
@@ -176,6 +185,10 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
isPausedRef.current = isPaused;
|
isPausedRef.current = isPaused;
|
||||||
}, [isPaused]);
|
}, [isPaused]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
isSpeedRef.current = speed;
|
||||||
|
}, [speed]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isReset || !isPlaying) {
|
if (isReset || !isPlaying) {
|
||||||
logStatus(armBot.modelUuid, "Simulation Play Reset Successfully")
|
logStatus(armBot.modelUuid, "Simulation Play Reset Successfully")
|
||||||
@@ -188,6 +201,14 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
isPausedRef.current = false
|
isPausedRef.current = false
|
||||||
pauseTimeRef.current = null
|
pauseTimeRef.current = null
|
||||||
startTime = 0
|
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
|
const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone
|
||||||
);
|
);
|
||||||
if (targetBones && isPlaying) {
|
if (targetBones && isPlaying) {
|
||||||
@@ -200,6 +221,61 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
}
|
}
|
||||||
}, [isReset, isPlaying])
|
}, [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(() => {
|
useEffect(() => {
|
||||||
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
|
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
|
||||||
if (targetMesh) {
|
if (targetMesh) {
|
||||||
@@ -227,6 +303,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
//Moving to pickup point
|
//Moving to pickup point
|
||||||
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) {
|
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) {
|
||||||
if (armBot.currentAction) {
|
if (armBot.currentAction) {
|
||||||
|
|
||||||
setArmBotActive(armBot.modelUuid, true);
|
setArmBotActive(armBot.modelUuid, true);
|
||||||
setArmBotState(armBot.modelUuid, "running");
|
setArmBotState(armBot.modelUuid, "running");
|
||||||
setCurrentPhase("rest-to-start");
|
setCurrentPhase("rest-to-start");
|
||||||
@@ -260,6 +337,13 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
pauseTimeRef.current = null
|
pauseTimeRef.current = null
|
||||||
isPausedRef.current = false
|
isPausedRef.current = false
|
||||||
startTime = 0
|
startTime = 0
|
||||||
|
activeSecondsElapsed.current = 0;
|
||||||
|
idleSecondsElapsed.current = 0;
|
||||||
|
previousTimeRef.current = null;
|
||||||
|
if (animationFrameIdRef.current !== null) {
|
||||||
|
cancelAnimationFrame(animationFrameIdRef.current);
|
||||||
|
animationFrameIdRef.current = null;
|
||||||
|
}
|
||||||
removeCurrentAction(armBot.modelUuid)
|
removeCurrentAction(armBot.modelUuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,6 +395,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const logStatus = (id: string, status: string) => {
|
const logStatus = (id: string, status: string) => {
|
||||||
// console.log('status: ', status);
|
// console.log('status: ', status);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import Simulator from './simulator/simulator';
|
|||||||
import Products from './products/products';
|
import Products from './products/products';
|
||||||
import Trigger from './triggers/trigger';
|
import Trigger from './triggers/trigger';
|
||||||
import useModuleStore from '../../store/useModuleStore';
|
import useModuleStore from '../../store/useModuleStore';
|
||||||
|
import SimulationAnalysis from './analysis/simulationAnalysis';
|
||||||
|
|
||||||
function Simulation() {
|
function Simulation() {
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
@@ -53,6 +54,8 @@ function Simulation() {
|
|||||||
|
|
||||||
<Simulator />
|
<Simulator />
|
||||||
|
|
||||||
|
<SimulationAnalysis />
|
||||||
|
|
||||||
</>
|
</>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,4 +98,4 @@ export async function determineExecutionMachineSequences(products: productsSchem
|
|||||||
});
|
});
|
||||||
|
|
||||||
return executionSequences;
|
return executionSequences;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,7 @@ import { useProductStore } from '../../../../../store/simulation/useProductStore
|
|||||||
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
|
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
|
||||||
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
|
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
|
||||||
import MaterialAnimator from '../animator/materialAnimator';
|
import MaterialAnimator from '../animator/materialAnimator';
|
||||||
type Timer = {
|
|
||||||
start: number | null;
|
|
||||||
active: boolean;
|
|
||||||
};
|
|
||||||
function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) {
|
function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) {
|
||||||
const { navMesh } = useNavMesh();
|
const { navMesh } = useNavMesh();
|
||||||
const { isPlaying } = usePlayButtonStore();
|
const { isPlaying } = usePlayButtonStore();
|
||||||
@@ -27,20 +24,30 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
|||||||
const { triggerPointActions } = useTriggerHandler();
|
const { triggerPointActions } = useTriggerHandler();
|
||||||
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = useProductStore();
|
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = useProductStore();
|
||||||
const { selectedProduct } = useSelectedProduct();
|
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 [currentPhase, setCurrentPhase] = useState<string>('stationed');
|
||||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||||
const pauseTimeRef = useRef<number | null>(null);
|
const pauseTimeRef = useRef<number | null>(null);
|
||||||
|
const idleTimeRef = useRef<number>(0);
|
||||||
|
const activeTimeRef = useRef<number>(0);
|
||||||
const isPausedRef = useRef<boolean>(false);
|
const isPausedRef = useRef<boolean>(false);
|
||||||
|
const isSpeedRef = useRef<number>(0);
|
||||||
let startTime: number;
|
let startTime: number;
|
||||||
let fixedInterval: number;
|
let fixedInterval: number;
|
||||||
const { speed } = useAnimationPlaySpeed();
|
const { speed } = useAnimationPlaySpeed();
|
||||||
const { isPaused } = usePauseButtonStore();
|
const { isPaused } = usePauseButtonStore();
|
||||||
|
const previousTimeRef = useRef<number | null>(null);
|
||||||
|
const animationFrameIdRef = useRef<number | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
isPausedRef.current = isPaused;
|
isPausedRef.current = isPaused;
|
||||||
}, [isPaused]);
|
}, [isPaused]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
isSpeedRef.current = speed;
|
||||||
|
}, [speed]);
|
||||||
|
|
||||||
const computePath = useCallback(
|
const computePath = useCallback(
|
||||||
(start: any, end: any) => {
|
(start: any, end: any) => {
|
||||||
try {
|
try {
|
||||||
@@ -58,7 +65,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
|||||||
);
|
);
|
||||||
|
|
||||||
function vehicleStatus(modelId: string, status: string) {
|
function vehicleStatus(modelId: string, status: string) {
|
||||||
//
|
// console.log(`${modelId} , ${status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to reset everything
|
// Function to reset everything
|
||||||
@@ -72,6 +79,14 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
|||||||
startTime = 0;
|
startTime = 0;
|
||||||
isPausedRef.current = false;
|
isPausedRef.current = false;
|
||||||
pauseTimeRef.current = 0;
|
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(() => {
|
useEffect(() => {
|
||||||
@@ -119,12 +134,61 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
|||||||
vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point');
|
vehicleStatus(agvDetail.modelUuid, 'Started from dropping point, heading to pickup point');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
reset()
|
reset()
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [vehicles, currentPhase, path, isPlaying]);
|
}, [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() {
|
function handleCallBack() {
|
||||||
if (currentPhase === 'stationed-pickup') {
|
if (currentPhase === 'stationed-pickup') {
|
||||||
setCurrentPhase('picking');
|
setCurrentPhase('picking');
|
||||||
@@ -211,7 +275,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
|||||||
action: VehicleAction
|
action: VehicleAction
|
||||||
) {
|
) {
|
||||||
startTime = performance.now();
|
startTime = performance.now();
|
||||||
const fixedInterval = ((unLoadDuration / vehicleCurrentLoad) * (1000 / speed));
|
const fixedInterval = ((unLoadDuration / vehicleCurrentLoad) * (1000 / isSpeedRef.current));
|
||||||
|
|
||||||
const unloadLoop = () => {
|
const unloadLoop = () => {
|
||||||
if (isPausedRef.current) {
|
if (isPausedRef.current) {
|
||||||
@@ -417,7 +481,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
|||||||
|
|
||||||
const elapsedTime = performance.now() - startTime;
|
const elapsedTime = performance.now() - startTime;
|
||||||
const unLoadDuration = agvDetail.point.action.unLoadDuration;
|
const unLoadDuration = agvDetail.point.action.unLoadDuration;
|
||||||
fixedInterval = ((unLoadDuration / agvDetail.currentLoad) * (1000 / speed));
|
fixedInterval = ((unLoadDuration / agvDetail.currentLoad) * (1000 / isSpeedRef.current));
|
||||||
|
|
||||||
if (elapsedTime >= fixedInterval) {
|
if (elapsedTime >= fixedInterval) {
|
||||||
let droppedMat = droppedMaterial - 1;
|
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 React, { useEffect, useMemo, useState } from "react";
|
||||||
import { Bar } from "react-chartjs-2";
|
import { Bar } from "react-chartjs-2";
|
||||||
@@ -243,7 +60,7 @@ const BarGraphComponent = ({
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {}, []);
|
useEffect(() => { }, []);
|
||||||
|
|
||||||
// Memoize Theme Colors
|
// Memoize Theme Colors
|
||||||
const buttonActionColor = useMemo(
|
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 React, { useEffect, useMemo, useState } from "react";
|
||||||
import { Pie } from "react-chartjs-2";
|
import { Pie } from "react-chartjs-2";
|
||||||
import io from "socket.io-client";
|
import io from "socket.io-client";
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({
|
|||||||
scale={[0.5, 0.5, 0.5]}
|
scale={[0.5, 0.5, 0.5]}
|
||||||
rotation={rotation}
|
rotation={rotation}
|
||||||
transform
|
transform
|
||||||
sprite={false}
|
sprite={true}
|
||||||
zIndexRange={[1, 0]}
|
zIndexRange={[1, 0]}
|
||||||
// style={{
|
// style={{
|
||||||
// transform: transformStyle.transform,
|
// transform: transformStyle.transform,
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({
|
|||||||
rotation={rotation}
|
rotation={rotation}
|
||||||
scale={[0.5, 0.5, 0.5]}
|
scale={[0.5, 0.5, 0.5]}
|
||||||
transform
|
transform
|
||||||
sprite={false}
|
sprite={true}
|
||||||
// style={{
|
// style={{
|
||||||
// transform: transformStyle.transform,
|
// transform: transformStyle.transform,
|
||||||
// transformStyle: "preserve-3d",
|
// transformStyle: "preserve-3d",
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ const StateWorking: React.FC<StateWorkingProps> = ({
|
|||||||
scale={[0.5, 0.5, 0.5]}
|
scale={[0.5, 0.5, 0.5]}
|
||||||
transform
|
transform
|
||||||
zIndexRange={[1, 0]}
|
zIndexRange={[1, 0]}
|
||||||
sprite={false}
|
sprite={true}
|
||||||
// style={{
|
// style={{
|
||||||
// transform: transformStyle.transform,
|
// transform: transformStyle.transform,
|
||||||
// transformStyle: "preserve-3d",
|
// transformStyle: "preserve-3d",
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ const Throughput: React.FC<ThroughputProps> = ({
|
|||||||
const email = localStorage.getItem("email") || "";
|
const email = localStorage.getItem("email") || "";
|
||||||
const organization = email?.split("@")[1]?.split(".")[0];
|
const organization = email?.split("@")[1]?.split(".")[0];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sample data for the line graph
|
// Sample data for the line graph
|
||||||
const graphData: ChartData<"line"> = {
|
const graphData: ChartData<"line"> = {
|
||||||
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
|
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
|
||||||
@@ -221,17 +223,11 @@ const Throughput: React.FC<ThroughputProps> = ({
|
|||||||
scale={[0.5, 0.5, 0.5]}
|
scale={[0.5, 0.5, 0.5]}
|
||||||
transform
|
transform
|
||||||
zIndexRange={[1, 0]}
|
zIndexRange={[1, 0]}
|
||||||
sprite={false}
|
sprite={true}
|
||||||
// style={{
|
|
||||||
// transform: transformStyle.transform,
|
|
||||||
// transformStyle: "preserve-3d",
|
|
||||||
// transition: "transform 0.1s ease-out",
|
|
||||||
// }}
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`throughput-wrapper card ${
|
className={`throughput-wrapper card ${selectedChartId?.id === id ? "activeChart" : ""
|
||||||
selectedChartId?.id === id ? "activeChart" : ""
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => setSelectedChartId({ id: id, type: type })}
|
onClick={() => setSelectedChartId({ id: id, type: type })}
|
||||||
onContextMenu={onContextMenu}
|
onContextMenu={onContextMenu}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -459,12 +459,88 @@ interface ShortcutStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useShortcutStore = create<ShortcutStore>((set) => ({
|
export const useShortcutStore = create<ShortcutStore>((set) => ({
|
||||||
showShortcuts: false,
|
showShortcuts: false,
|
||||||
setShowShortcuts: (value) => set({ showShortcuts: value }),
|
setShowShortcuts: (value) => set({ showShortcuts: value }),
|
||||||
toggleShortcuts: () =>
|
toggleShortcuts: () =>
|
||||||
set((state) => ({ showShortcuts: !state.showShortcuts })),
|
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 {
|
interface CompareStore {
|
||||||
comparePopUp: boolean;
|
comparePopUp: boolean;
|
||||||
setComparePopUp: (value: boolean) => void;
|
setComparePopUp: (value: boolean) => void;
|
||||||
|
|||||||
@@ -1,154 +1,176 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from "zustand";
|
||||||
import { immer } from 'zustand/middleware/immer';
|
import { immer } from "zustand/middleware/immer";
|
||||||
|
|
||||||
interface MachineStore {
|
interface MachineStore {
|
||||||
machines: MachineStatus[];
|
machines: MachineStatus[];
|
||||||
|
|
||||||
addMachine: (productId: string, machine: MachineEventSchema) => void;
|
addMachine: (productId: string, machine: MachineEventSchema) => void;
|
||||||
removeMachine: (modelUuid: string) => void;
|
removeMachine: (modelUuid: string) => void;
|
||||||
updateMachine: (
|
updateMachine: (
|
||||||
modelUuid: string,
|
modelUuid: string,
|
||||||
updates: Partial<Omit<MachineStatus, 'modelUuid' | 'productId'>>
|
updates: Partial<Omit<MachineStatus, "modelUuid" | "productId">>
|
||||||
) => void;
|
) => void;
|
||||||
clearMachines: () => void;
|
clearMachines: () => void;
|
||||||
|
|
||||||
addCurrentAction: (modelUuid: string, actionUuid: string, materialType: string, materialId: string) => void;
|
addCurrentAction: (
|
||||||
removeCurrentAction: (modelUuid: string) => void;
|
modelUuid: string,
|
||||||
|
actionUuid: string,
|
||||||
|
materialType: string,
|
||||||
|
materialId: string
|
||||||
|
) => void;
|
||||||
|
removeCurrentAction: (modelUuid: string) => void;
|
||||||
|
|
||||||
setMachineActive: (modelUuid: string, isActive: boolean) => void;
|
setMachineActive: (modelUuid: string, isActive: boolean) => void;
|
||||||
setMachineState: (modelUuid: string, newState: MachineStatus['state']) => void;
|
setMachineState: (
|
||||||
|
modelUuid: string,
|
||||||
|
newState: MachineStatus["state"]
|
||||||
|
) => void;
|
||||||
|
|
||||||
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
|
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
|
||||||
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
|
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
|
||||||
|
resetTime: (modelUuid: string) => void;
|
||||||
|
|
||||||
getMachineById: (modelUuid: string) => MachineStatus | undefined;
|
getMachineById: (modelUuid: string) => MachineStatus | undefined;
|
||||||
getMachinesByProduct: (productId: string) => MachineStatus[];
|
getMachinesByProduct: (productId: string) => MachineStatus[];
|
||||||
getMachinesBystate: (state: string) => MachineStatus[];
|
getMachinesBystate: (state: string) => MachineStatus[];
|
||||||
getActiveMachines: () => MachineStatus[];
|
getActiveMachines: () => MachineStatus[];
|
||||||
getIdleMachines: () => MachineStatus[];
|
getIdleMachines: () => MachineStatus[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useMachineStore = create<MachineStore>()(
|
export const useMachineStore = create<MachineStore>()(
|
||||||
immer((set, get) => ({
|
immer((set, get) => ({
|
||||||
machines: [],
|
machines: [],
|
||||||
|
|
||||||
addMachine: (productId, machine) => {
|
addMachine: (productId, machine) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const exists = state.machines.some(m => m.modelUuid === machine.modelUuid);
|
const exists = state.machines.some(
|
||||||
if (!exists) {
|
(m) => m.modelUuid === machine.modelUuid
|
||||||
state.machines.push({
|
);
|
||||||
...machine,
|
if (!exists) {
|
||||||
productId,
|
state.machines.push({
|
||||||
isActive: false,
|
...machine,
|
||||||
idleTime: 0,
|
productId,
|
||||||
activeTime: 0,
|
isActive: false,
|
||||||
state: 'idle',
|
idleTime: 0,
|
||||||
});
|
activeTime: 0,
|
||||||
}
|
state: "idle",
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
removeMachine: (modelUuid) => {
|
removeMachine: (modelUuid) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.machines = state.machines.filter(m => m.modelUuid !== modelUuid);
|
state.machines = state.machines.filter(
|
||||||
});
|
(m) => m.modelUuid !== modelUuid
|
||||||
},
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
updateMachine: (modelUuid, updates) => {
|
updateMachine: (modelUuid, updates) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const machine = state.machines.find(m => m.modelUuid === modelUuid);
|
const machine = state.machines.find((m) => m.modelUuid === modelUuid);
|
||||||
if (machine) {
|
if (machine) {
|
||||||
Object.assign(machine, updates);
|
Object.assign(machine, updates);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
clearMachines: () => {
|
clearMachines: () => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.machines = [];
|
state.machines = [];
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
addCurrentAction: (modelUuid, actionUuid, materialType, materialId) => {
|
addCurrentAction: (modelUuid, actionUuid, materialType, materialId) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const armBot = state.machines.find(a => a.modelUuid === modelUuid);
|
const armBot = state.machines.find((a) => a.modelUuid === modelUuid);
|
||||||
if (armBot) {
|
if (armBot) {
|
||||||
const action = armBot.point.action;
|
const action = armBot.point.action;
|
||||||
if (action) {
|
if (action) {
|
||||||
armBot.currentAction = {
|
armBot.currentAction = {
|
||||||
actionUuid: actionUuid,
|
actionUuid: actionUuid,
|
||||||
actionName: action.actionName,
|
actionName: action.actionName,
|
||||||
materialType: materialType,
|
materialType: materialType,
|
||||||
materialId: materialId
|
materialId: materialId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
removeCurrentAction: (modelUuid) => {
|
removeCurrentAction: (modelUuid) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const armBot = state.machines.find(a => a.modelUuid === modelUuid);
|
const armBot = state.machines.find((a) => a.modelUuid === modelUuid);
|
||||||
if (armBot) {
|
if (armBot) {
|
||||||
armBot.currentAction = undefined;
|
armBot.currentAction = undefined;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setMachineActive: (modelUuid, isActive) => {
|
setMachineActive: (modelUuid, isActive) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const machine = state.machines.find(m => m.modelUuid === modelUuid);
|
const machine = state.machines.find((m) => m.modelUuid === modelUuid);
|
||||||
if (machine) {
|
if (machine) {
|
||||||
machine.isActive = isActive;
|
machine.isActive = isActive;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setMachineState: (modelUuid, newState) => {
|
setMachineState: (modelUuid, newState) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const machine = state.machines.find(m => m.modelUuid === modelUuid);
|
const machine = state.machines.find((m) => m.modelUuid === modelUuid);
|
||||||
if (machine) {
|
if (machine) {
|
||||||
machine.state = newState;
|
machine.state = newState;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
incrementActiveTime: (modelUuid, incrementBy) => {
|
incrementActiveTime: (modelUuid, incrementBy) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const machine = state.machines.find(m => m.modelUuid === modelUuid);
|
const machine = state.machines.find((m) => m.modelUuid === modelUuid);
|
||||||
if (machine) {
|
if (machine) {
|
||||||
machine.activeTime += incrementBy;
|
machine.activeTime += incrementBy;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
incrementIdleTime: (modelUuid, incrementBy) => {
|
incrementIdleTime: (modelUuid, incrementBy) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const machine = state.machines.find(m => m.modelUuid === modelUuid);
|
const machine = state.machines.find((m) => m.modelUuid === modelUuid);
|
||||||
if (machine) {
|
if (machine) {
|
||||||
machine.idleTime += incrementBy;
|
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) => {
|
getMachineById: (modelUuid) => {
|
||||||
return get().machines.find(m => m.modelUuid === modelUuid);
|
return get().machines.find((m) => m.modelUuid === modelUuid);
|
||||||
},
|
},
|
||||||
|
|
||||||
getMachinesByProduct: (productId) => {
|
getMachinesByProduct: (productId) => {
|
||||||
return get().machines.filter(m => m.productId === productId);
|
return get().machines.filter((m) => m.productId === productId);
|
||||||
},
|
},
|
||||||
|
|
||||||
getMachinesBystate: (state) => {
|
getMachinesBystate: (state) => {
|
||||||
return get().machines.filter(m => m.state === state);
|
return get().machines.filter((m) => m.state === state);
|
||||||
},
|
},
|
||||||
|
|
||||||
getActiveMachines: () => {
|
getActiveMachines: () => {
|
||||||
return get().machines.filter(m => m.isActive);
|
return get().machines.filter((m) => m.isActive);
|
||||||
},
|
},
|
||||||
|
|
||||||
getIdleMachines: () => {
|
getIdleMachines: () => {
|
||||||
return get().machines.filter(m => !m.isActive && m.state === 'idle');
|
return get().machines.filter((m) => !m.isActive && m.state === "idle");
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ interface VehiclesStore {
|
|||||||
clearCurrentMaterials: (modelUuid: string) => void;
|
clearCurrentMaterials: (modelUuid: string) => void;
|
||||||
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
|
incrementActiveTime: (modelUuid: string, incrementBy: number) => void;
|
||||||
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
|
incrementIdleTime: (modelUuid: string, incrementBy: number) => void;
|
||||||
|
resetTime: (modelUuid: string) => void;
|
||||||
|
|
||||||
getVehicleById: (modelUuid: string) => VehicleStatus | undefined;
|
getVehicleById: (modelUuid: string) => VehicleStatus | undefined;
|
||||||
getVehiclesByProduct: (productId: string) => VehicleStatus[];
|
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) => {
|
getVehicleById: (modelUuid) => {
|
||||||
return get().vehicles.find((v) => v.modelUuid === modelUuid);
|
return get().vehicles.find((v) => v.modelUuid === modelUuid);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -768,7 +768,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border-radius: #{$border-radius-medium};
|
border-radius: #{$border-radius-medium};
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
@@ -780,6 +779,7 @@
|
|||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
text-wrap: nowrap;
|
text-wrap: nowrap;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
pointer-events: all;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--highlight-accent-color);
|
background: var(--highlight-accent-color);
|
||||||
|
|||||||
Reference in New Issue
Block a user