Add loading state and integrate simulation analysis components
This commit is contained in:
parent
67ec49fff6
commit
1ca5f00161
|
@ -16,7 +16,7 @@ const ProductionCapacity = ({
|
|||
const partialFillPercent =
|
||||
((progressPercent / 100) * totalBars - barsToFill) * 100;
|
||||
|
||||
const isLoading = false;
|
||||
const isLoading = true;
|
||||
return (
|
||||
<div className="throughtputSummary-container analysis-card">
|
||||
<div className="throughtputSummary-wrapper analysis-card-wrapper">
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import React from 'react'
|
||||
import ThroughPut from './throughPut/throughPut'
|
||||
|
||||
function SimulationAnalysis() {
|
||||
return (
|
||||
<>
|
||||
<ThroughPut />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SimulationAnalysis
|
|
@ -0,0 +1,31 @@
|
|||
import React, { useEffect } from 'react'
|
||||
import { useSelectedProduct } from '../../../../store/simulation/useSimulationStore';
|
||||
import { useProductStore } from '../../../../store/simulation/useProductStore';
|
||||
import { determineExecutionMachineSequences } from '../../simulator/functions/determineExecutionMachineSequences';
|
||||
export default function ThroughPut() {
|
||||
const { selectedProduct } = useSelectedProduct();
|
||||
const { products,getProductById } = useProductStore()
|
||||
|
||||
useEffect(() => {
|
||||
let productData = getProductById(selectedProduct.productId)
|
||||
if (productData) {
|
||||
let productSequenceData = determineExecutionMachineSequences([productData])
|
||||
console.log('productSequenceData: ', productSequenceData);
|
||||
if (productSequenceData?.length > 0) {
|
||||
let totalItems = 0; // 👈 initialize total count
|
||||
productSequenceData.map((sequence, index) => {
|
||||
totalItems += sequence.length;
|
||||
// sequence.map((item, idx) => {});
|
||||
});
|
||||
console.log(`Total items across all sequences: ${totalItems}`); // 👈 FINAL TOTAL ✅
|
||||
} else {
|
||||
console.log('No productSequenceData found!');
|
||||
}
|
||||
}
|
||||
}, [products])
|
||||
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import IKInstance from '../ikInstance/ikInstance';
|
||||
import RoboticArmAnimator from '../animator/roboticArmAnimator';
|
||||
import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
|
||||
import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
import MaterialAnimator from '../animator/materialAnimator';
|
||||
import { clear } from 'console';
|
||||
|
||||
|
||||
function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
||||
|
@ -20,12 +21,21 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
const groupRef = useRef<any>(null);
|
||||
const pauseTimeRef = useRef<number | null>(null);
|
||||
const isPausedRef = useRef<boolean>(false);
|
||||
const isSpeedRef = useRef<any>(null);
|
||||
const isIdleRef = useRef<boolean>(false);
|
||||
let startTime: number;
|
||||
//zustand
|
||||
const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { isReset } = useResetButtonStore();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
|
||||
const activeSecondsElapsed = useRef(0);
|
||||
const activeTimerId = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
|
||||
const idleSecondsElapsed = useRef(0);
|
||||
const idleTimerId = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
|
||||
function firstFrame() {
|
||||
startTime = performance.now();
|
||||
|
@ -87,9 +97,15 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
isPausedRef.current = isPaused;
|
||||
}, [isPaused]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
isSpeedRef.current = speed;
|
||||
}, [speed]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isReset || !isPlaying) {
|
||||
logStatus(armBot.modelUuid, "Simulation Play Reset Successfully")
|
||||
|
@ -102,6 +118,12 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
isPausedRef.current = false
|
||||
pauseTimeRef.current = null
|
||||
startTime = 0
|
||||
clearInterval(activeTimerId.current!);
|
||||
clearInterval(idleTimerId.current!);
|
||||
activeTimerId.current = null;
|
||||
activeSecondsElapsed.current = 0;
|
||||
idleSecondsElapsed.current = 0;
|
||||
idleTimerId.current = null;
|
||||
const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone
|
||||
);
|
||||
if (targetBones && isPlaying) {
|
||||
|
@ -114,6 +136,135 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
}
|
||||
}, [isReset, isPlaying])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPlaying) return;
|
||||
|
||||
const intervalMs = 1000 / isSpeedRef.current;
|
||||
console.log('intervalMs: ', intervalMs);
|
||||
|
||||
if (!armBot.isActive && armBot.state == "idle" && (currentPhase == "rest" || currentPhase == "init") && !isIdleRef.current) {
|
||||
isIdleRef.current = true
|
||||
|
||||
// Stop the timer
|
||||
// 🚨 1. Clear Active Timer
|
||||
if (activeTimerId.current) {
|
||||
clearInterval(activeTimerId.current);
|
||||
activeTimerId.current = null;
|
||||
}
|
||||
console.log(`✅ Active Cycle completed in ${activeSecondsElapsed.current} seconds`);
|
||||
|
||||
// 🚨 2. Reset active timer seconds after logging
|
||||
activeSecondsElapsed.current = 0;
|
||||
|
||||
// 🚨 3. Start Idle Timer (clean old idle timer first)
|
||||
if (idleTimerId.current) {
|
||||
clearInterval(idleTimerId.current);
|
||||
idleTimerId.current = null;
|
||||
}
|
||||
|
||||
idleSecondsElapsed.current = 0;
|
||||
idleTimerId.current = setInterval(() => {
|
||||
if (!isPausedRef.current) {
|
||||
idleSecondsElapsed.current += 1;
|
||||
console.log(`🕒 Idle Timer: ${idleSecondsElapsed.current} seconds`);
|
||||
}
|
||||
}, intervalMs);
|
||||
} else if (armBot.isActive && armBot.state != "idle" && currentPhase !== "rest" && armBot.currentAction && isIdleRef.current) {
|
||||
isIdleRef.current = false
|
||||
|
||||
if (armBot.currentAction) {
|
||||
// 🚨 Clear Idle Timer
|
||||
if (idleTimerId.current) {
|
||||
clearInterval(idleTimerId.current);
|
||||
idleTimerId.current = null;
|
||||
}
|
||||
console.log(`🕒 Idle Cycle completed in: ${idleSecondsElapsed.current} seconds`);
|
||||
idleSecondsElapsed.current = 0;
|
||||
|
||||
// 🚨 Start Active Timer
|
||||
if (activeTimerId.current) {
|
||||
clearInterval(activeTimerId.current);
|
||||
}
|
||||
activeSecondsElapsed.current = 0;
|
||||
activeTimerId.current = setInterval(() => {
|
||||
if (!isPausedRef.current) {
|
||||
activeSecondsElapsed.current += 1
|
||||
console.log(`🕒 Active Timer: ${activeSecondsElapsed.current} seconds`);
|
||||
}
|
||||
}, intervalMs);
|
||||
}
|
||||
}
|
||||
|
||||
}, [armBot, currentPhase, isPlaying])
|
||||
|
||||
|
||||
// useEffect(() => {
|
||||
// if (!isPlaying) return;
|
||||
// const now = () => performance.now();
|
||||
|
||||
|
||||
// const startActiveTimer = () => {
|
||||
// let lastTime = now();
|
||||
|
||||
// const update = () => {
|
||||
// if (!isPausedRef.current) {
|
||||
// const currentTime = now();
|
||||
// const delta = currentTime - lastTime;
|
||||
// activeSecondsElapsed.current += delta / 1000;
|
||||
// console.log(`🕒 Active Timer: ${activeSecondsElapsed.current.toFixed(2)} seconds`);
|
||||
// lastTime = currentTime;
|
||||
// } else {
|
||||
|
||||
// lastTime = now();
|
||||
// }
|
||||
|
||||
// if (!isIdleRef.current) {
|
||||
// requestAnimationFrame(update);
|
||||
// }
|
||||
// };
|
||||
|
||||
// activeSecondsElapsed.current = 0;
|
||||
// update();
|
||||
// };
|
||||
|
||||
// const startIdleTimer = () => {
|
||||
// let lastTime = now();
|
||||
|
||||
// const update = () => {
|
||||
// if (!isPausedRef.current) {
|
||||
// const currentTime = now();
|
||||
// const delta = currentTime - lastTime;
|
||||
// idleSecondsElapsed.current += delta / 1000;
|
||||
// console.log(`🕒 Idle Timer: ${idleSecondsElapsed.current.toFixed(2)} seconds`);
|
||||
// lastTime = currentTime;
|
||||
// } else {
|
||||
// lastTime = now();
|
||||
// }
|
||||
|
||||
// if (isIdleRef.current) {
|
||||
// requestAnimationFrame(update);
|
||||
// }
|
||||
// };
|
||||
|
||||
// idleSecondsElapsed.current = 0;
|
||||
// update();
|
||||
// };
|
||||
|
||||
// // State transition logic
|
||||
// if (!armBot.isActive && armBot.state === "idle" && (currentPhase === "rest" || currentPhase === "init") && !isIdleRef.current) {
|
||||
// isIdleRef.current = true;
|
||||
// console.log(`✅ Active Cycle completed in ${activeSecondsElapsed.current.toFixed(2)} seconds`);
|
||||
// startIdleTimer();
|
||||
// }
|
||||
// else if (armBot.isActive && armBot.state !== "idle" && currentPhase !== "rest" && armBot.currentAction && isIdleRef.current) {
|
||||
// isIdleRef.current = false;
|
||||
// console.log(`🕒 Idle Cycle completed in: ${idleSecondsElapsed.current.toFixed(2)} seconds`);
|
||||
// startActiveTimer();
|
||||
// }
|
||||
|
||||
// }, [armBot, currentPhase, isPlaying, isIdleRef.current, isPausedRef.current]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
|
||||
if (targetMesh) {
|
||||
|
@ -136,6 +287,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
}
|
||||
//Waiting for trigger.
|
||||
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) {
|
||||
|
||||
logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction")
|
||||
const timeoutId = setTimeout(() => {
|
||||
addCurrentAction(armBot.modelUuid, armBot.point.actions[0].actionUuid, 'Material 2');
|
||||
|
@ -145,9 +297,30 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
//Moving to pickup point
|
||||
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && armBot.currentAction) {
|
||||
if (armBot.currentAction) {
|
||||
|
||||
setArmBotActive(armBot.modelUuid, true);
|
||||
setArmBotState(armBot.modelUuid, "running");
|
||||
setCurrentPhase("rest-to-start");
|
||||
// // 🚨 Clear Idle Timer
|
||||
// if (idleTimerId.current) {
|
||||
// clearInterval(idleTimerId.current);
|
||||
// idleTimerId.current = null;
|
||||
// }
|
||||
// console.log(`🕒 Idle Cycle completed in: ${idleSecondsElapsed.current} seconds`);
|
||||
// idleSecondsElapsed.current = 0;
|
||||
|
||||
// // 🚨 Start Active Timer
|
||||
// if (activeTimerId.current) {
|
||||
// clearInterval(activeTimerId.current);
|
||||
// }
|
||||
// activeSecondsElapsed.current = 0;
|
||||
// activeTimerId.current = setInterval(() => {
|
||||
// if (!isPausedRef.current) {
|
||||
// activeSecondsElapsed.current += 1;
|
||||
// console.log(`🕒 Active Timer: ${activeSecondsElapsed.current} seconds`);
|
||||
// }
|
||||
// }, 1000);
|
||||
|
||||
const startPoint = armBot.point.actions[0].process.startPoint;
|
||||
if (startPoint) {
|
||||
let curve = createCurveBetweenTwoPoints(targetBones.position, new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]));
|
||||
|
@ -177,6 +350,12 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
pauseTimeRef.current = null
|
||||
isPausedRef.current = false
|
||||
startTime = 0
|
||||
clearInterval(activeTimerId.current!);
|
||||
clearInterval(idleTimerId.current!);
|
||||
activeTimerId.current = null;
|
||||
activeSecondsElapsed.current = 0;
|
||||
idleSecondsElapsed.current = 0;
|
||||
idleTimerId.current = null;
|
||||
removeCurrentAction(armBot.modelUuid)
|
||||
}
|
||||
|
||||
|
@ -215,10 +394,39 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
}
|
||||
else if (armBot.isActive && armBot.state == "running" && currentPhase == "end-to-rest") {
|
||||
logStatus(armBot.modelUuid, "Callback triggered: rest, cycle completed.");
|
||||
// Stop the timer
|
||||
// 🚨 1. Clear Active Timer
|
||||
// if (activeTimerId.current) {
|
||||
// clearInterval(activeTimerId.current);
|
||||
// activeTimerId.current = null;
|
||||
// }
|
||||
// console.log(`✅ Active Cycle completed in ${activeSecondsElapsed.current} seconds`);
|
||||
|
||||
// // 🚨 2. Reset active timer seconds after logging
|
||||
// activeSecondsElapsed.current = 0;
|
||||
|
||||
// // 🚨 3. Start Idle Timer (clean old idle timer first)
|
||||
// if (idleTimerId.current) {
|
||||
// clearInterval(idleTimerId.current);
|
||||
// idleTimerId.current = null;
|
||||
// }
|
||||
|
||||
// idleSecondsElapsed.current = 0;
|
||||
// idleTimerId.current = setInterval(() => {
|
||||
// if (!isPausedRef.current) {
|
||||
// idleSecondsElapsed.current += 1;
|
||||
// console.log(`🕒 Idle Timer: ${idleSecondsElapsed.current} seconds`);
|
||||
// }
|
||||
// }, 1000);
|
||||
|
||||
|
||||
setArmBotActive(armBot.modelUuid, false)
|
||||
setArmBotState(armBot.modelUuid, "idle")
|
||||
setCurrentPhase("rest");
|
||||
setPath([])
|
||||
|
||||
//end active calculation
|
||||
|
||||
removeCurrentAction(armBot.modelUuid)
|
||||
}
|
||||
}
|
||||
|
@ -228,12 +436,12 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||
return (
|
||||
<>
|
||||
{!isReset && isPlaying && (
|
||||
<>
|
||||
<>
|
||||
<IKInstance modelUrl={armModel} setIkSolver={setIkSolver} ikSolver={ikSolver} armBot={armBot} groupRef={groupRef} />
|
||||
<RoboticArmAnimator HandleCallback={HandleCallback} restPosition={restPosition} ikSolver={ikSolver} targetBone={targetBone} armBot={armBot}
|
||||
logStatus={logStatus} path={path} currentPhase={currentPhase} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<MaterialAnimator ikSolver={ikSolver} armBot={armBot} currentPhase={currentPhase} />
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -12,6 +12,7 @@ import Simulator from './simulator/simulator';
|
|||
import Products from './products/products';
|
||||
import Trigger from './triggers/trigger';
|
||||
import useModuleStore from '../../store/useModuleStore';
|
||||
import SimulationAnalysis from './analysis/simulationAnalysis';
|
||||
|
||||
function Simulation() {
|
||||
const { activeModule } = useModuleStore();
|
||||
|
@ -53,6 +54,8 @@ function Simulation() {
|
|||
|
||||
<Simulator />
|
||||
|
||||
<SimulationAnalysis />
|
||||
|
||||
</>
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
import { extractTriggersFromPoint } from "./extractTriggersFromPoint";
|
||||
|
||||
export function determineExecutionMachineSequences(products: productsSchema): EventsSchema[][] {
|
||||
const pointToEventMap = new Map<string, EventsSchema>();
|
||||
const allPoints: PointsScheme[] = [];
|
||||
|
||||
// First pass: map points to their corresponding events
|
||||
products.forEach(product => {
|
||||
product.eventDatas.forEach(event => {
|
||||
if (event.type === 'transfer') {
|
||||
event.points.forEach(point => {
|
||||
pointToEventMap.set(point.uuid, event);
|
||||
allPoints.push(point);
|
||||
});
|
||||
} else if (
|
||||
event.type === 'vehicle' ||
|
||||
event.type === 'machine' ||
|
||||
event.type === 'storageUnit' ||
|
||||
event.type === 'roboticArm'
|
||||
) {
|
||||
pointToEventMap.set(event.point.uuid, event);
|
||||
allPoints.push(event.point);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Build dependency graph
|
||||
const dependencyGraph = new Map<string, string[]>();
|
||||
const reverseDependencyGraph = new Map<string, string[]>();
|
||||
const triggeredPoints = new Set<string>();
|
||||
|
||||
allPoints.forEach(point => {
|
||||
const triggers = extractTriggersFromPoint(point);
|
||||
const dependencies: string[] = [];
|
||||
|
||||
triggers.forEach(trigger => {
|
||||
const targetUuid = trigger.triggeredAsset?.triggeredPoint?.pointUuid;
|
||||
if (targetUuid && pointToEventMap.has(targetUuid)) {
|
||||
dependencies.push(targetUuid);
|
||||
triggeredPoints.add(targetUuid);
|
||||
|
||||
if (!reverseDependencyGraph.has(targetUuid)) {
|
||||
reverseDependencyGraph.set(targetUuid, []);
|
||||
}
|
||||
reverseDependencyGraph.get(targetUuid)!.push(point.uuid);
|
||||
}
|
||||
});
|
||||
|
||||
dependencyGraph.set(point.uuid, dependencies);
|
||||
});
|
||||
|
||||
// Find root points (points that trigger others but are not triggered themselves)
|
||||
const rootPoints = allPoints.filter(point => {
|
||||
const hasOutgoingTriggers = extractTriggersFromPoint(point).some(
|
||||
t => t.triggeredAsset?.triggeredPoint?.pointUuid
|
||||
);
|
||||
return hasOutgoingTriggers && !triggeredPoints.has(point.uuid);
|
||||
});
|
||||
|
||||
const executionSequences: EventsSchema[][] = [];
|
||||
|
||||
function buildSequence(startUuid: string): EventsSchema[] {
|
||||
const sequence: EventsSchema[] = [];
|
||||
const visited = new Set<string>();
|
||||
|
||||
function traverse(uuid: string) {
|
||||
if (visited.has(uuid)) return;
|
||||
visited.add(uuid);
|
||||
|
||||
const event = pointToEventMap.get(uuid);
|
||||
if (event && !sequence.includes(event)) {
|
||||
sequence.push(event);
|
||||
}
|
||||
|
||||
const nextPoints = dependencyGraph.get(uuid) || [];
|
||||
nextPoints.forEach(nextUuid => traverse(nextUuid));
|
||||
}
|
||||
|
||||
traverse(startUuid);
|
||||
return sequence;
|
||||
}
|
||||
|
||||
// Build sequences from root points
|
||||
rootPoints.forEach(root => {
|
||||
executionSequences.push(buildSequence(root.uuid));
|
||||
});
|
||||
|
||||
// Handle any isolated triggered points
|
||||
const processedEvents = new Set(
|
||||
executionSequences.flat().map(event => event)
|
||||
);
|
||||
|
||||
allPoints.forEach(point => {
|
||||
const event = pointToEventMap.get(point.uuid);
|
||||
if (triggeredPoints.has(point.uuid) && event && !processedEvents.has(event)) {
|
||||
executionSequences.push(buildSequence(point.uuid));
|
||||
}
|
||||
});
|
||||
|
||||
return executionSequences;
|
||||
}
|
Loading…
Reference in New Issue