import { useProductStore } from '../../../store/simulation/useProductStore'; import { useActionHandler } from '../actions/useActionHandler'; function Simulator() { const { products } = useProductStore(); const executionOrder = determineExecutionOrder(products); executionOrder.forEach(point => { useActionHandler(point); }); function determineExecutionSequences(products: productsSchema): PointsScheme[][] { // Create maps for all points const pointMap = new Map(); const allPoints: PointsScheme[] = []; // First pass: collect all points products.forEach(product => { product.eventDatas.forEach(event => { if (event.type === 'transfer') { event.points.forEach(point => { pointMap.set(point.uuid, point); allPoints.push(point); }); } else if (event.type === 'vehicle' || event.type === 'machine' || event.type === 'storageUnit' || event.type === 'roboticArm') { pointMap.set(event.point.uuid, event.point); allPoints.push(event.point); } }); }); // Build complete 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 && pointMap.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); }); // Identify independent root points (points that trigger others but aren't triggered themselves) const rootPoints = allPoints.filter(point => { const hasOutgoingTriggers = extractTriggersFromPoint(point).some( t => t.triggeredAsset?.triggeredPoint?.pointUuid ); return hasOutgoingTriggers && !triggeredPoints.has(point.uuid); }); // For each root point, build its complete trigger chain const executionSequences: PointsScheme[][] = []; function buildSequence(startUuid: string): PointsScheme[] { const sequence: PointsScheme[] = []; const visited = new Set(); function traverse(uuid: string) { if (visited.has(uuid)) return; visited.add(uuid); const point = pointMap.get(uuid); if (point) { sequence.push(point); } // Follow forward dependencies const nextPoints = dependencyGraph.get(uuid) || []; nextPoints.forEach(nextUuid => traverse(nextUuid)); } traverse(startUuid); return sequence; } // Build sequences for all root points rootPoints.forEach(root => { executionSequences.push(buildSequence(root.uuid)); }); // Handle any triggered points not reachable from roots (isolated chains) const processedPoints = new Set( executionSequences.flat().map(p => p.uuid) ); allPoints.forEach(point => { if (triggeredPoints.has(point.uuid) && !processedPoints.has(point.uuid)) { executionSequences.push(buildSequence(point.uuid)); } }); return executionSequences; } function determineExecutionOrder(products: productsSchema): PointsScheme[] { // Create maps for all events and points const eventMap = new Map(); const pointMap = new Map(); const allPoints: PointsScheme[] = []; // First pass: collect all points products.forEach(product => { product.eventDatas.forEach(event => { eventMap.set(event.modelUuid, event); if (event.type === 'transfer') { event.points.forEach(point => { pointMap.set(point.uuid, point); allPoints.push(point); }); } else if (event.type === 'vehicle' || event.type === 'machine' || event.type === 'storageUnit') { pointMap.set(event.point.uuid, event.point); allPoints.push(event.point); } else if (event.type === 'roboticArm') { pointMap.set(event.point.uuid, event.point); allPoints.push(event.point); } }); }); // Build dependency graphs const graph = new Map(); const reverseGraph = new Map(); const allTriggeredPoints = new Set(); allPoints.forEach(point => { const triggers = extractTriggersFromPoint(point); const dependencies: string[] = []; triggers.forEach(trigger => { const targetUuid = trigger.triggeredAsset?.triggeredPoint?.pointUuid; if (targetUuid && pointMap.has(targetUuid)) { dependencies.push(targetUuid); allTriggeredPoints.add(targetUuid); if (!reverseGraph.has(targetUuid)) { reverseGraph.set(targetUuid, []); } reverseGraph.get(targetUuid)!.push(point.uuid); } }); graph.set(point.uuid, dependencies); }); // Identify root points (points that trigger others but aren't triggered themselves) const rootPoints = allPoints .filter(point => !allTriggeredPoints.has(point.uuid)) .filter(point => { // Only include roots that actually have triggers pointing FROM them const triggers = extractTriggersFromPoint(point); return triggers.some(t => t.triggeredAsset?.triggeredPoint?.pointUuid); }); // If no root points found but we have triggered points, find the earliest triggers if (rootPoints.length === 0 && allTriggeredPoints.size > 0) { // This handles cases where we have circular dependencies // but still want to include the triggered points const minTriggerCount = Math.min( ...Array.from(allTriggeredPoints) .map(uuid => (graph.get(uuid) || []).length) ); const potentialRoots = Array.from(allTriggeredPoints) .filter(uuid => (graph.get(uuid) || []).length === minTriggerCount); rootPoints.push(...potentialRoots.map(uuid => pointMap.get(uuid)!)); } // Topological sort only for triggered points const visited = new Set(); const temp = new Set(); const order: string[] = []; let hasCycle = false; function visit(node: string) { if (temp.has(node)) { hasCycle = true; return; } if (visited.has(node)) return; temp.add(node); const dependencies = reverseGraph.get(node) || []; for (const dep of dependencies) { visit(dep); } temp.delete(node); visited.add(node); order.push(node); } // Start processing from root points rootPoints.forEach(root => visit(root.uuid)); // Convert UUIDs back to points and filter out untriggered points const triggeredPoints = order .map(uuid => pointMap.get(uuid)!) .filter(point => allTriggeredPoints.has(point.uuid) || rootPoints.some(root => root.uuid === point.uuid)); return triggeredPoints; } function extractTriggersFromPoint(point: PointsScheme): TriggerSchema[] { if ('actions' in point) { return point.actions.flatMap(action => action.triggers); } else if ('action' in point) { return point.action.triggers; } return []; } return <>; } export default Simulator;