in Sources/Visualization/Microsoft.Psi.Visualization.Windows/Views/Visuals2D/DiagnosticsVisualization/PipelineDiagnosticsVisualizationPresenter.cs [552:765]
private Graph BuildVisualGraph(PipelineDiagnostics diagnostics, Dictionary<int, PipelineDiagnostics> pipelineIdToPipelineDiagnostics)
{
var subpipelineIdToPipelineDiagnostics = diagnostics.SubpipelineDiagnostics.ToDictionary(p => p.Id);
var graph = new Graph($"{diagnostics.Name} (running={diagnostics.IsPipelineRunning})", $"g{diagnostics.Id}");
switch (this.model.VisualizationObject.LayoutDirection)
{
case PipelineDiagnosticsVisualizationObject.GraphLayoutDirection.LeftToRight:
graph.Attr.LayerDirection = LayerDirection.LR;
break;
case PipelineDiagnosticsVisualizationObject.GraphLayoutDirection.TopToBottom:
graph.Attr.LayerDirection = LayerDirection.TB;
break;
case PipelineDiagnosticsVisualizationObject.GraphLayoutDirection.RightToLeft:
graph.Attr.LayerDirection = LayerDirection.RL;
break;
case PipelineDiagnosticsVisualizationObject.GraphLayoutDirection.BottomToTop:
graph.Attr.LayerDirection = LayerDirection.BT;
break;
}
graph.UserData = diagnostics.Id;
graph.Attr.BackgroundColor = Color.Transparent;
var subpipelineNodes = new Dictionary<int, PipelineDiagnostics.PipelineElementDiagnostics>();
var connectorsWithinSubpipelines = new Dictionary<int, PipelineDiagnostics.PipelineElementDiagnostics>();
var statsSelector = this.StatsSelector(false);
// add nodes
foreach (var node in diagnostics.PipelineElements.Where(n => !this.IsConnectorBridge(n)))
{
var vis = this.BuildVisualNode(node);
if (node.Kind == PipelineElementKind.Subpipeline && node.RepresentsSubpipeline != null)
{
vis.UserData = node.RepresentsSubpipeline;
subpipelineNodes.Add(node.RepresentsSubpipeline.Id, node);
foreach (var n in node.RepresentsSubpipeline.PipelineElements.Where(n => n.Kind == PipelineElementKind.Connector))
{
connectorsWithinSubpipelines.Add(n.Id, n);
}
}
else if (node.Kind == PipelineElementKind.Connector)
{
this.SetConnectorVisualAttributes(vis, node.Name);
}
graph.AddNode(vis);
}
// add connectors
foreach (var node in diagnostics.PipelineElements.Where(this.IsConnectorBridge))
{
var connectsToSubpipeline = subpipelineNodes.ContainsKey(node.ConnectorBridgeToPipelineElement.PipelineId);
if (!connectsToSubpipeline)
{
if (!this.ShowExporterConnections && IsBridgeToExporter(node))
{
continue;
}
var connector = new Node($"n{node.Id}");
var bridgedPipeline = pipelineIdToPipelineDiagnostics[node.ConnectorBridgeToPipelineElement.PipelineId];
this.SetConnectorVisualAttributes(connector, $"{node.Name} ({bridgedPipeline.Name})");
graph.AddNode(connector);
}
}
// add edges
var selectedEdgeUpdated = false;
foreach (var n in diagnostics.PipelineElements)
{
foreach (var i in n.Receivers)
{
if (i.Source != null)
{
if (this.AddVisualEdge(i.Source.PipelineElement.Id, n.Id, i, graph, statsSelector))
{
selectedEdgeUpdated = true;
}
}
}
}
// add connector bridge edges
foreach (var n in diagnostics.PipelineElements.Where(this.IsConnectorBridge))
{
if (!this.ShowExporterConnections && IsBridgeToExporter(n))
{
continue;
}
// connector bridging to subpipeline?
if (subpipelineNodes.TryGetValue(n.ConnectorBridgeToPipelineElement.PipelineId, out PipelineDiagnostics.PipelineElementDiagnostics subNode))
{
// edges from connector source directly to bridge target (subpipeline)
var sub = graph.FindNode($"n{subNode.Id}");
if (sub != null)
{
foreach (var i in n.Receivers)
{
if (i.Source != null)
{
var source = graph.FindNode($"n{i.Source.PipelineElement.Id}");
if (source != null)
{
if (this.AddVisualEdge(source, sub, i, graph, statsSelector))
{
selectedEdgeUpdated = true;
}
}
}
}
// edges from connector bridge source (subpipeline) to connector targets
foreach (var o in n.Emitters)
{
foreach (var t in o.Targets)
{
var target = graph.FindNode($"n{t.PipelineElement.Id}");
if (target != null)
{
if (this.AddVisualEdge(sub, target, t, graph, statsSelector))
{
selectedEdgeUpdated = true;
}
}
}
}
}
}
else
{
// connector bridging graphs
var bridgedPipeline = pipelineIdToPipelineDiagnostics[n.ConnectorBridgeToPipelineElement.PipelineId];
var connector = graph.FindNode($"n{n.Id}");
// add dotted line edge representing connector bridged to descendant pipeline
var targetPipeline = bridgedPipeline;
while (targetPipeline != null)
{
if (subpipelineIdToPipelineDiagnostics.ContainsKey(targetPipeline.Id))
{
var targetNode = graph.FindNode($"n{subpipelineNodes[targetPipeline.Id].Id}");
graph.AddPrecalculatedEdge(this.BuildVisualEdge(connector, targetNode, n.Name, bridgedPipeline.Name, -1, string.Empty, string.Empty, Style.Dotted));
break;
}
// walk up ancestor chain until we're at a direct child subpipeline
targetPipeline = targetPipeline.ParentPipelineDiagnostics;
}
}
}
// add connector bridge edges between descendants (shown between current-level subpiplines)
int? TryFindCurrentLevelAncestorSubpipelineId(int id)
{
foreach (var ancestor in pipelineIdToPipelineDiagnostics[id].AncestorPipelines)
{
if (subpipelineNodes.TryGetValue(ancestor.Id, out PipelineDiagnostics.PipelineElementDiagnostics subpipeline))
{
return subpipeline.Id;
}
}
return null;
}
foreach (var descendantConnector in diagnostics.GetAllPipelineElementDiagnostics().Where(this.IsConnectorBridge))
{
if (descendantConnector.Emitters.Length == 0 /* source-side of connector pair */)
{
var sourceId = descendantConnector.PipelineId;
var targetId = descendantConnector.ConnectorBridgeToPipelineElement.PipelineId;
var sourceCurrentLevelId = TryFindCurrentLevelAncestorSubpipelineId(sourceId);
var targetCurrentLevelId = TryFindCurrentLevelAncestorSubpipelineId(targetId);
if (sourceCurrentLevelId != null && targetCurrentLevelId != null && sourceCurrentLevelId != targetCurrentLevelId)
{
var sourceNode = graph.FindNode($"n{sourceCurrentLevelId}");
var targetNode = graph.FindNode($"n{targetCurrentLevelId}");
graph.AddPrecalculatedEdge(this.BuildVisualEdge(sourceNode, targetNode, string.Empty, descendantConnector.Name, -1, string.Empty, string.Empty, Style.Dotted));
}
}
}
// add direct connections from one subpipeline (connector) to another
foreach (var c in connectorsWithinSubpipelines.Values)
{
if (c.ConnectorBridgeToPipelineElement != null)
{
if (c.ConnectorBridgeToPipelineElement.PipelineId == diagnostics.Id && c.ConnectorBridgeToPipelineElement.Receivers.Length == 1)
{
var i = c.ConnectorBridgeToPipelineElement.Receivers[0];
if (i.Source != null && i.Source.PipelineElement.PipelineId == diagnostics.Id && i.Source.PipelineElement.ConnectorBridgeToPipelineElement != null)
{
if (subpipelineNodes.TryGetValue(i.Source.PipelineElement.ConnectorBridgeToPipelineElement.PipelineId, out PipelineDiagnostics.PipelineElementDiagnostics source) &&
subpipelineNodes.TryGetValue(c.PipelineId, out PipelineDiagnostics.PipelineElementDiagnostics target))
{
if (this.AddVisualEdge(source.Id, target.Id, i, graph, statsSelector))
{
selectedEdgeUpdated = true;
}
}
}
}
}
}
if (!selectedEdgeUpdated && this.model.SelectedEdgeId != -1)
{
// hide while in subpipeline
this.model.SelectedEdgeDetails = string.Empty;
}
this.VisualizeEdgeColoring(graph);
return graph;
}