import { extractTriggersFromPoint } from "./extractTriggersFromPoint"; export 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' || event.type === 'roboticArm' || event.type === 'human' || event.type === 'crane' ) { 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; }