diff --git a/app/src/modules/simulation/analysis/simulationAnalysis.tsx b/app/src/modules/simulation/analysis/simulationAnalysis.tsx
new file mode 100644
index 0000000..531c103
--- /dev/null
+++ b/app/src/modules/simulation/analysis/simulationAnalysis.tsx
@@ -0,0 +1,12 @@
+import React from 'react'
+import ThroughPut from './throughPut/throughPut'
+
+function SimulationAnalysis() {
+ return (
+ <>
+
+ >
+ )
+}
+
+export default SimulationAnalysis
diff --git a/app/src/modules/simulation/analysis/throughPut/throughPut.tsx b/app/src/modules/simulation/analysis/throughPut/throughPut.tsx
new file mode 100644
index 0000000..71e82f8
--- /dev/null
+++ b/app/src/modules/simulation/analysis/throughPut/throughPut.tsx
@@ -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 (
+ <>
+ >
+ )
+}
\ No newline at end of file
diff --git a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
index 7cd3634..a412714 100644
--- a/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
+++ b/app/src/modules/simulation/roboticArm/instances/armInstance/roboticArmInstance.tsx
@@ -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
(null);
const pauseTimeRef = useRef(null);
const isPausedRef = useRef(false);
+ const isSpeedRef = useRef(null);
+ const isIdleRef = useRef(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 | null>(null);
+
+ const idleSecondsElapsed = useRef(0);
+ const idleTimerId = useRef | 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 && (
- <>
+ <>
- >
- )}
+ >
+ )}
>
)
diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx
index 7fe3c50..f918f56 100644
--- a/app/src/modules/simulation/simulation.tsx
+++ b/app/src/modules/simulation/simulation.tsx
@@ -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() {
+
+
>
}
diff --git a/app/src/modules/simulation/simulator/functions/determineExecutionMachineSequences.ts b/app/src/modules/simulation/simulator/functions/determineExecutionMachineSequences.ts
new file mode 100644
index 0000000..49e3cc6
--- /dev/null
+++ b/app/src/modules/simulation/simulator/functions/determineExecutionMachineSequences.ts
@@ -0,0 +1,101 @@
+import { extractTriggersFromPoint } from "./extractTriggersFromPoint";
+
+export function determineExecutionMachineSequences(products: productsSchema): EventsSchema[][] {
+ const pointToEventMap = new Map();
+ 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();
+ const reverseDependencyGraph = new Map();
+ const triggeredPoints = new Set();
+
+ 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();
+
+ 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;
+}