2025-05-03 18:36:30 +05:30
|
|
|
import { useProductStore } from '../../../store/simulation/useProductStore';
|
|
|
|
|
import { useActionHandler } from '../actions/useActionHandler';
|
2025-04-23 18:25:49 +05:30
|
|
|
|
|
|
|
|
function Simulator() {
|
2025-04-25 19:31:56 +05:30
|
|
|
const { products } = useProductStore();
|
|
|
|
|
|
2025-05-03 18:36:30 +05:30
|
|
|
const executionOrder = determineExecutionOrder(products);
|
2025-04-25 19:31:56 +05:30
|
|
|
|
2025-05-03 18:36:30 +05:30
|
|
|
executionOrder.forEach(point => {
|
|
|
|
|
useActionHandler(point);
|
|
|
|
|
});
|
2025-04-25 19:31:56 +05:30
|
|
|
|
2025-05-03 18:36:30 +05:30
|
|
|
function determineExecutionSequences(products: productsSchema): PointsScheme[][] {
|
|
|
|
|
// Create maps for all points
|
|
|
|
|
const pointMap = new Map<string, PointsScheme>();
|
|
|
|
|
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<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 && 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<string>();
|
|
|
|
|
|
|
|
|
|
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<string, EventsSchema>();
|
|
|
|
|
const pointMap = new Map<string, PointsScheme>();
|
|
|
|
|
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<string, string[]>();
|
|
|
|
|
const reverseGraph = new Map<string, string[]>();
|
|
|
|
|
const allTriggeredPoints = new Set<string>();
|
|
|
|
|
|
|
|
|
|
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<string>();
|
|
|
|
|
const temp = new Set<string>();
|
|
|
|
|
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 <></>;
|
2025-04-23 18:25:49 +05:30
|
|
|
}
|
|
|
|
|
|
2025-05-03 18:36:30 +05:30
|
|
|
export default Simulator;
|