Add loading state and integrate simulation analysis components
This commit is contained in:
@@ -16,7 +16,7 @@ const ProductionCapacity = ({
|
|||||||
const partialFillPercent =
|
const partialFillPercent =
|
||||||
((progressPercent / 100) * totalBars - barsToFill) * 100;
|
((progressPercent / 100) * totalBars - barsToFill) * 100;
|
||||||
|
|
||||||
const isLoading = false;
|
const isLoading = true;
|
||||||
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">
|
||||||
|
|||||||
12
app/src/modules/simulation/analysis/simulationAnalysis.tsx
Normal file
12
app/src/modules/simulation/analysis/simulationAnalysis.tsx
Normal file
@@ -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 React, { useEffect, useRef, useState } from 'react'
|
||||||
import IKInstance from '../ikInstance/ikInstance';
|
import IKInstance from '../ikInstance/ikInstance';
|
||||||
import RoboticArmAnimator from '../animator/roboticArmAnimator';
|
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 { useArmBotStore } from '../../../../../store/simulation/useArmBotStore';
|
||||||
import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb";
|
import armModel from "../../../../../assets/gltf-glb/rigged/ik_arm_1.glb";
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import MaterialAnimator from '../animator/materialAnimator';
|
import MaterialAnimator from '../animator/materialAnimator';
|
||||||
|
import { clear } from 'console';
|
||||||
|
|
||||||
|
|
||||||
function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
||||||
@@ -20,12 +21,21 @@ 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;
|
||||||
//zustand
|
//zustand
|
||||||
const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
|
const { addCurrentAction, setArmBotActive, setArmBotState, removeCurrentAction } = useArmBotStore();
|
||||||
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 activeTimerId = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
|
|
||||||
|
const idleSecondsElapsed = useRef(0);
|
||||||
|
const idleTimerId = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
|
|
||||||
function firstFrame() {
|
function firstFrame() {
|
||||||
startTime = performance.now();
|
startTime = performance.now();
|
||||||
@@ -87,9 +97,15 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
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")
|
||||||
@@ -102,6 +118,12 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
isPausedRef.current = false
|
isPausedRef.current = false
|
||||||
pauseTimeRef.current = null
|
pauseTimeRef.current = null
|
||||||
startTime = 0
|
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
|
const targetBones = ikSolver?.mesh.skeleton.bones.find((b: any) => b.name === targetBone
|
||||||
);
|
);
|
||||||
if (targetBones && isPlaying) {
|
if (targetBones && isPlaying) {
|
||||||
@@ -114,6 +136,135 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
}
|
}
|
||||||
}, [isReset, isPlaying])
|
}, [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(() => {
|
useEffect(() => {
|
||||||
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
|
const targetMesh = scene?.getObjectByProperty("uuid", armBot.modelUuid);
|
||||||
if (targetMesh) {
|
if (targetMesh) {
|
||||||
@@ -136,6 +287,7 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
}
|
}
|
||||||
//Waiting for trigger.
|
//Waiting for trigger.
|
||||||
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) {
|
else if (armBot && !armBot.isActive && armBot.state === "idle" && currentPhase === "rest" && !armBot.currentAction) {
|
||||||
|
|
||||||
logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction")
|
logStatus(armBot.modelUuid, "Waiting to trigger CurrentAction")
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = setTimeout(() => {
|
||||||
addCurrentAction(armBot.modelUuid, armBot.point.actions[0].actionUuid, 'Material 2');
|
addCurrentAction(armBot.modelUuid, armBot.point.actions[0].actionUuid, 'Material 2');
|
||||||
@@ -145,9 +297,30 @@ 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");
|
||||||
|
// // 🚨 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;
|
const startPoint = armBot.point.actions[0].process.startPoint;
|
||||||
if (startPoint) {
|
if (startPoint) {
|
||||||
let curve = createCurveBetweenTwoPoints(targetBones.position, new THREE.Vector3(startPoint[0], startPoint[1], startPoint[2]));
|
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
|
pauseTimeRef.current = null
|
||||||
isPausedRef.current = false
|
isPausedRef.current = false
|
||||||
startTime = 0
|
startTime = 0
|
||||||
|
clearInterval(activeTimerId.current!);
|
||||||
|
clearInterval(idleTimerId.current!);
|
||||||
|
activeTimerId.current = null;
|
||||||
|
activeSecondsElapsed.current = 0;
|
||||||
|
idleSecondsElapsed.current = 0;
|
||||||
|
idleTimerId.current = null;
|
||||||
removeCurrentAction(armBot.modelUuid)
|
removeCurrentAction(armBot.modelUuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,10 +394,39 @@ function RoboticArmInstance({ armBot }: { armBot: ArmBotStatus }) {
|
|||||||
}
|
}
|
||||||
else if (armBot.isActive && armBot.state == "running" && currentPhase == "end-to-rest") {
|
else if (armBot.isActive && armBot.state == "running" && currentPhase == "end-to-rest") {
|
||||||
logStatus(armBot.modelUuid, "Callback triggered: rest, cycle completed.");
|
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)
|
setArmBotActive(armBot.modelUuid, false)
|
||||||
setArmBotState(armBot.modelUuid, "idle")
|
setArmBotState(armBot.modelUuid, "idle")
|
||||||
setCurrentPhase("rest");
|
setCurrentPhase("rest");
|
||||||
setPath([])
|
setPath([])
|
||||||
|
|
||||||
|
//end active calculation
|
||||||
|
|
||||||
removeCurrentAction(armBot.modelUuid)
|
removeCurrentAction(armBot.modelUuid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 />
|
||||||
|
|
||||||
</>
|
</>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user