Add loading state and integrate simulation analysis components

This commit is contained in:
Gomathi 2025-05-08 09:18:32 +05:30
parent 67ec49fff6
commit 1ca5f00161
6 changed files with 360 additions and 5 deletions

View File

@ -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">

View File

@ -0,0 +1,12 @@
import React from 'react'
import ThroughPut from './throughPut/throughPut'
function SimulationAnalysis() {
return (
<>
<ThroughPut />
</>
)
}
export default SimulationAnalysis

View File

@ -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 (
<>
</>
)
}

View File

@ -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} />
</>
)

View File

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

View File

@ -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;
}