233 lines
10 KiB
TypeScript
233 lines
10 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
|
import { useSceneContext } from "../../scene/sceneContext";
|
|
import { determineExecutionMachineSequences } from "./functions/determineExecutionMachineSequences";
|
|
import { usePlayButtonStore } from "../../../store/ui/usePlayButtonStore";
|
|
import { useSimulationManager } from "../../../store/rough/useSimulationManagerStore";
|
|
import { useParams } from "react-router-dom";
|
|
import { saveSimulationDataApi } from "../../../services/simulation/comparison/saveSimulationDataApi";
|
|
|
|
interface SimulationUsageRecord {
|
|
activeTime: number;
|
|
isActive: boolean;
|
|
idleTime: number;
|
|
type: "roboticArm" | "vehicle" | "transfer" | "storageUnit" | "crane" | "human" | "machine";
|
|
assetId: string;
|
|
}
|
|
|
|
// Product → holds multiple usage records
|
|
interface ProductSimulation {
|
|
productId: string;
|
|
data: SimulationUsageRecord[];
|
|
}
|
|
|
|
// Version → holds multiple products
|
|
interface VersionSimulation {
|
|
versionId: string;
|
|
products: ProductSimulation[];
|
|
}
|
|
|
|
const SimulationHandler = () => {
|
|
const { materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, productStore, craneStore, humanStore, versionStore } = useSceneContext();
|
|
const { armBots, getArmBotById } = armBotStore();
|
|
const { vehicles, getVehicleById } = vehicleStore();
|
|
const { getConveyorById } = conveyorStore();
|
|
const { materialHistory, materials } = materialStore();
|
|
const { getProductById, selectedProduct } = productStore();
|
|
const { machines, getMachineById } = machineStore();
|
|
const { getHumanById } = humanStore();
|
|
const { getCraneById } = craneStore();
|
|
const { getStorageUnitById } = storageUnitStore();
|
|
const { isPlaying, setIsPlaying } = usePlayButtonStore();
|
|
const { resetProductRecords, addSimulationRecord } = useSimulationManager();
|
|
const { projectId } = useParams();
|
|
const COST_RATES: Record<SimulationUsageRecord["type"], number> = {
|
|
roboticArm: 5,
|
|
vehicle: 2,
|
|
transfer: 1,
|
|
storageUnit: 1,
|
|
crane: 6,
|
|
human: 4,
|
|
machine: 3,
|
|
};
|
|
const { selectedVersion } = versionStore();
|
|
|
|
// Calculate totals for one product
|
|
function calculateProductMetrics(product: ProductSimulation) {
|
|
let totalActiveTime = 0;
|
|
let totalIdleTime = 0;
|
|
let totalCost = 0;
|
|
|
|
product.data.forEach((record) => {
|
|
const resourceTime = record.activeTime + record.idleTime;
|
|
const costRate = COST_RATES[record.type] || 0;
|
|
|
|
totalActiveTime += record.activeTime;
|
|
totalIdleTime += record.idleTime;
|
|
totalCost += resourceTime * costRate;
|
|
});
|
|
|
|
return {
|
|
totalTime: totalActiveTime + totalIdleTime,
|
|
totalActiveTime,
|
|
totalIdleTime,
|
|
totalCost,
|
|
};
|
|
}
|
|
|
|
// Calculate totals for a version
|
|
function calculateVersionMetrics(version: VersionSimulation) {
|
|
return version.products.reduce(
|
|
(acc, product) => {
|
|
const metrics = calculateProductMetrics(product);
|
|
|
|
acc.totalTime += metrics.totalTime;
|
|
acc.totalActiveTime += metrics.totalActiveTime;
|
|
acc.totalIdleTime += metrics.totalIdleTime;
|
|
acc.totalCost += metrics.totalCost;
|
|
return acc;
|
|
},
|
|
{ totalTime: 0, totalActiveTime: 0, totalIdleTime: 0, totalCost: 0 }
|
|
);
|
|
}
|
|
|
|
// Efficiency score (compare across versions)
|
|
function calculateEfficiencyScores(versions: VersionSimulation[]) {
|
|
const versionMetrics = versions.map((v) => ({
|
|
versionId: v.versionId,
|
|
...calculateVersionMetrics(v),
|
|
}));
|
|
|
|
const minTime = Math.min(...versionMetrics.map((m) => m.totalTime));
|
|
const minCost = Math.min(...versionMetrics.map((m) => m.totalCost));
|
|
|
|
return versionMetrics.map((m) => {
|
|
const timeFactor = minTime / m.totalTime;
|
|
const costFactor = minCost / m.totalCost;
|
|
|
|
const efficiencyScore = 0.5 * timeFactor + 0.5 * costFactor;
|
|
|
|
return { ...m, efficiencyScore };
|
|
});
|
|
}
|
|
|
|
useEffect(() => {
|
|
let checkTimer: ReturnType<typeof setTimeout>;
|
|
if (!projectId) return;
|
|
async function checkActiveMachines() {
|
|
const currentProduct = getProductById(selectedProduct.productUuid);
|
|
let hasActiveEntity = false;
|
|
|
|
if (currentProduct) {
|
|
const executionSequences = await determineExecutionMachineSequences([currentProduct]);
|
|
if (executionSequences?.length > 0) {
|
|
executionSequences.forEach((sequence) => {
|
|
sequence.forEach((entity) => {
|
|
if (entity.type === "roboticArm") {
|
|
const roboticArm = getArmBotById(entity.modelUuid);
|
|
if (roboticArm?.isActive) {
|
|
hasActiveEntity = true;
|
|
}
|
|
}
|
|
if (entity.type === "vehicle") {
|
|
const vehicle = getVehicleById(entity.modelUuid);
|
|
if (vehicle?.isActive) {
|
|
hasActiveEntity = true;
|
|
}
|
|
}
|
|
if (entity.type === "machine") {
|
|
const machine = getMachineById(entity.modelUuid);
|
|
if (machine?.isActive) {
|
|
hasActiveEntity = true;
|
|
}
|
|
}
|
|
if (entity.type === "human") {
|
|
const human = getHumanById(entity.modelUuid);
|
|
if (human?.isActive) {
|
|
hasActiveEntity = true;
|
|
}
|
|
}
|
|
if (entity.type === "crane") {
|
|
const crane = getCraneById(entity.modelUuid);
|
|
if (crane?.isActive) {
|
|
hasActiveEntity = true;
|
|
}
|
|
}
|
|
if (entity.type === "storageUnit") {
|
|
const storageUnit = getStorageUnitById(entity.modelUuid);
|
|
if (storageUnit?.isActive) {
|
|
hasActiveEntity = true;
|
|
}
|
|
}
|
|
if (entity.type === "transfer") {
|
|
const storageUnit = getConveyorById(entity.modelUuid);
|
|
if (storageUnit?.isActive) {
|
|
hasActiveEntity = true;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
if (materials.length === 0 && materialHistory.length >= 0 && !hasActiveEntity) {
|
|
if (!selectedVersion) return;
|
|
resetProductRecords(projectId, selectedVersion?.versionId, selectedProduct?.productUuid);
|
|
if (executionSequences?.length > 0) {
|
|
executionSequences.forEach((sequence) => {
|
|
sequence.forEach((entity) => {
|
|
const typeToGetter: Record<string, (id: string) => any> = {
|
|
roboticArm: getArmBotById,
|
|
vehicle: getVehicleById,
|
|
machine: getMachineById,
|
|
human: getHumanById,
|
|
crane: getCraneById,
|
|
storageUnit: getStorageUnitById,
|
|
transfer: getConveyorById,
|
|
};
|
|
|
|
const getter = typeToGetter[entity.type];
|
|
if (!getter) return; // skip unknown entity types
|
|
|
|
const obj = getter(entity.modelUuid);
|
|
if (!obj) return; // skip if not found
|
|
|
|
addSimulationRecord(projectId, selectedVersion?.versionId || "", selectedProduct?.productUuid, {
|
|
activeTime: obj.activeTime ?? 0,
|
|
isActive: obj.isActive ?? false,
|
|
idleTime: obj.idleTime ?? 0,
|
|
type: entity.type as "roboticArm" | "vehicle" | "machine" | "human" | "crane" | "storageUnit" | "transfer",
|
|
assetId: entity.modelUuid,
|
|
});
|
|
});
|
|
});
|
|
}
|
|
const data = {
|
|
projectId: projectId,
|
|
versionId: selectedVersion?.versionId,
|
|
productUuid: selectedProduct.productUuid,
|
|
simulateData: useSimulationManager.getState().getProductById(projectId || "", selectedVersion?.versionId || "", selectedProduct?.productUuid || "")?.simulateData,
|
|
};
|
|
const simulations = await saveSimulationDataApi(data);
|
|
|
|
echo.log("Simulation data saved successfully");
|
|
setIsPlaying(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isPlaying) {
|
|
checkTimer = setTimeout(() => {
|
|
checkActiveMachines();
|
|
}, 1500);
|
|
}
|
|
|
|
return () => {
|
|
if (checkTimer) clearTimeout(checkTimer);
|
|
};
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [materials, materialHistory, selectedVersion, selectedProduct?.productUuid, isPlaying, armBots, vehicles, machines]);
|
|
|
|
return null;
|
|
};
|
|
|
|
export default SimulationHandler;
|