|
|
@ -57,18 +57,17 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
var clusterNodes = new List<ClusterNode>();
|
|
|
|
var clusterNodes = new List<ClusterNode>();
|
|
|
|
var clusterToNodeMapping = new Dictionary<Cluster, int>(); // Cluster / index of node
|
|
|
|
var clusterToNodeMapping = new Dictionary<Cluster, int>(); // Cluster / index of node
|
|
|
|
var queue = new Queue<ClusterNode>();
|
|
|
|
var queue = new Queue<ClusterNode>();
|
|
|
|
var loopingNodes = new HashSet<int>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// An input peg, only has a single cluster.
|
|
|
|
// An input peg, only has a single cluster.
|
|
|
|
// An output peg however can be connected to multiple clusters.
|
|
|
|
// An output peg however can be connected to multiple clusters.
|
|
|
|
// It only makes sense to then select all these clusters as primary cluster.
|
|
|
|
// It only makes sense to then select all these clusters as primary cluster.
|
|
|
|
var collectedClusters = new HashSet<Cluster>();
|
|
|
|
var startingClusters = new HashSet<Cluster>();
|
|
|
|
CollectMainClusters(start, collectedClusters);
|
|
|
|
CollectMainClusters(start, startingClusters);
|
|
|
|
|
|
|
|
|
|
|
|
var endingClusters = new HashSet<Cluster>();
|
|
|
|
var endingClusters = new HashSet<Cluster>();
|
|
|
|
CollectMainClusters(end, endingClusters);
|
|
|
|
CollectMainClusters(end, endingClusters);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (Cluster startingCluster in collectedClusters) {
|
|
|
|
foreach (Cluster startingCluster in startingClusters) {
|
|
|
|
// ignore already mapped clusters
|
|
|
|
// ignore already mapped clusters
|
|
|
|
if (clusterToNodeMapping.ContainsKey(startingCluster)) {
|
|
|
|
if (clusterToNodeMapping.ContainsKey(startingCluster)) {
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
@ -93,12 +92,112 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
queue.Enqueue(node);
|
|
|
|
queue.Enqueue(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logger.Info($"collected {collectedClusters.Count} main clusters");
|
|
|
|
logger.Info($"collected {startingClusters.Count} main clusters");
|
|
|
|
|
|
|
|
|
|
|
|
int iters = 0;
|
|
|
|
// Backup starting clusters for later
|
|
|
|
|
|
|
|
List<int> startingNodeIndexes = clusterNodes.Select(node => node.Index).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BuildClusterTree(queue, clusterToNodeMapping, clusterNodes);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<ClusterNode> loopingNodes = FindLoops(clusterNodes);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.Info($"Found {loopingNodes.Count} looping nodes");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (loopingNodes.Count == 0) {
|
|
|
|
|
|
|
|
PropagateTimeDelay(clusterNodes, startingNodeIndexes);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int criticalPathLength = -1;
|
|
|
|
|
|
|
|
if (endingClusters.Count > 0) {
|
|
|
|
|
|
|
|
// Attempt to get the critical path length
|
|
|
|
|
|
|
|
// If the ending cluster has not been found, return -1
|
|
|
|
|
|
|
|
Cluster endingCluster = endingClusters.First();
|
|
|
|
|
|
|
|
if (clusterToNodeMapping.TryGetValue(endingCluster, out int nodeIndex)) {
|
|
|
|
|
|
|
|
criticalPathLength = clusterNodes[nodeIndex].Time;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Collect information about each cluster:
|
|
|
|
|
|
|
|
response = new AnalyzePathResponse() {
|
|
|
|
|
|
|
|
RequestGuid = requestGuid,
|
|
|
|
|
|
|
|
Clusters = clusterNodes.SelectMany(node =>
|
|
|
|
|
|
|
|
node.Clusters.Select(cluster => CollectClusterInformation(cluster, node.Time))).ToList(),
|
|
|
|
|
|
|
|
CriticalPathLength = criticalPathLength,
|
|
|
|
|
|
|
|
LoopingClusters = loopingNodes.SelectMany(loopingNode => loopingNode.Clusters
|
|
|
|
|
|
|
|
.Select(cluster => CollectClusterInformation(cluster, 0))).ToList(),
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.Info("Trace end");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void PropagateTimeDelay(List<ClusterNode> clusterNodes, List<int> startingNodeIndexes) {
|
|
|
|
|
|
|
|
var queue = new Queue<int>(startingNodeIndexes);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (queue.TryDequeue(out int index)) {
|
|
|
|
|
|
|
|
ClusterNode node = clusterNodes[index];
|
|
|
|
|
|
|
|
foreach ((int nextNodeIndex, int delay) in node.NextNodes) {
|
|
|
|
|
|
|
|
clusterNodes[nextNodeIndex].Time = node.Time + delay;
|
|
|
|
|
|
|
|
queue.Enqueue(nextNodeIndex);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static List<ClusterNode> FindLoops(List<ClusterNode> clusterNodesOrig) {
|
|
|
|
|
|
|
|
List<ClusterNode> clusterNodes = clusterNodesOrig.Select(node => new ClusterNode() {
|
|
|
|
|
|
|
|
Index = node.Index,
|
|
|
|
|
|
|
|
Clusters = node.Clusters,
|
|
|
|
|
|
|
|
NextNodes = node.NextNodes.ToDictionary(),
|
|
|
|
|
|
|
|
PrevNodeIndexes = node.PrevNodeIndexes.ToHashSet(),
|
|
|
|
|
|
|
|
Time = node.Time,
|
|
|
|
|
|
|
|
}).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var queue = new Queue<ClusterNode>();
|
|
|
|
|
|
|
|
foreach (ClusterNode node in clusterNodes) {
|
|
|
|
|
|
|
|
if (node.PrevNodeIndexes.Count == 0) {
|
|
|
|
|
|
|
|
// source
|
|
|
|
|
|
|
|
queue.Enqueue(node);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (node.NextNodes.Count == 0) {
|
|
|
|
|
|
|
|
// source
|
|
|
|
|
|
|
|
queue.Enqueue(node);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (queue.TryDequeue(out ClusterNode node)) {
|
|
|
|
|
|
|
|
foreach ((int nextNodeIndex, int _) in node.NextNodes) {
|
|
|
|
|
|
|
|
ClusterNode nextNode = clusterNodes[nextNodeIndex];
|
|
|
|
|
|
|
|
nextNode.PrevNodeIndexes.Remove(node.Index);
|
|
|
|
|
|
|
|
if (nextNode.PrevNodeIndexes.Count == 0) {
|
|
|
|
|
|
|
|
queue.Enqueue(nextNode);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (int prevNodeIndex in node.PrevNodeIndexes) {
|
|
|
|
|
|
|
|
ClusterNode prevNode = clusterNodes[prevNodeIndex];
|
|
|
|
|
|
|
|
prevNode.NextNodes.Remove(node.Index);
|
|
|
|
|
|
|
|
if (prevNode.NextNodes.Count == 0) {
|
|
|
|
|
|
|
|
queue.Enqueue(prevNode);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return clusterNodes.Where(node => node.PrevNodeIndexes.Count > 0 && node.NextNodes.Count > 0).ToList();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void BuildClusterTree(Queue<ClusterNode> queue, Dictionary<Cluster, int> clusterToNodeMapping,
|
|
|
|
|
|
|
|
List<ClusterNode> clusterNodes) {
|
|
|
|
|
|
|
|
ILogicLogger logger = CriticalPathAnalyzerServer.LoggerInstance;
|
|
|
|
|
|
|
|
int iterations = 0;
|
|
|
|
while (queue.TryDequeue(out ClusterNode node)) {
|
|
|
|
while (queue.TryDequeue(out ClusterNode node)) {
|
|
|
|
iters++;
|
|
|
|
iterations++;
|
|
|
|
if (iters > 10000) {
|
|
|
|
if (iterations % 1000 == 0) {
|
|
|
|
|
|
|
|
logger.Info($"{iterations} iterations");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (iterations > 100000) {
|
|
|
|
logger.Error("Infinite iterations");
|
|
|
|
logger.Error("Infinite iterations");
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -124,71 +223,36 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
if (clusterToNodeMapping.TryGetValue(nextCluster, out int nextNodeIndex)) {
|
|
|
|
if (clusterToNodeMapping.TryGetValue(nextCluster, out int nextNodeIndex)) {
|
|
|
|
ClusterNode nextNode = clusterNodes[nextNodeIndex];
|
|
|
|
ClusterNode nextNode = clusterNodes[nextNodeIndex];
|
|
|
|
// only add the link, don't propagate
|
|
|
|
// only add the link, don't propagate
|
|
|
|
if (!nextNode.PrevNodeIndexes.Add(node.Index)) {
|
|
|
|
nextNode.PrevNodeIndexes.Add(node.Index);
|
|
|
|
logger.Warn("Loop detected");
|
|
|
|
|
|
|
|
loopingNodes.Add(node.Index);
|
|
|
|
|
|
|
|
loopingNodes.Add(nextNode.Index);
|
|
|
|
|
|
|
|
// already been here - there is a cycle
|
|
|
|
|
|
|
|
// TODO: this doesn't work - if cluster A is updated by cluster B,
|
|
|
|
|
|
|
|
// TODO: and cluster B is updated by clusters C and D, it detects a cycle where isn't one
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Replace the time with a later one and continue
|
|
|
|
|
|
|
|
int timeDelay = 1;
|
|
|
|
|
|
|
|
node.NextNodes.TryAdd(nextNodeIndex, 1);
|
|
|
|
node.NextNodes.TryAdd(nextNodeIndex, 1);
|
|
|
|
nextNode.Time = node.Time + timeDelay;
|
|
|
|
|
|
|
|
queue.Enqueue(nextNode);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// var nextClusters = new HashSet<Cluster>();
|
|
|
|
// var nextClusters = new HashSet<Cluster>();
|
|
|
|
// var twoWayConnectedClusters = new HashSet<Cluster>();
|
|
|
|
// var twoWayConnectedClusters = new HashSet<Cluster>();
|
|
|
|
// GetLinkedClusters(cluster, nextClusters, twoWayConnectedClusters, GetFollowers);
|
|
|
|
// GetLinkedClusters(cluster, nextClusters, twoWayConnectedClusters, GetFollowers);
|
|
|
|
int timeDelay = 1;
|
|
|
|
|
|
|
|
nextNodeIndex = clusterNodes.Count;
|
|
|
|
nextNodeIndex = clusterNodes.Count;
|
|
|
|
var nextNode = new ClusterNode() {
|
|
|
|
var nextNode = new ClusterNode() {
|
|
|
|
Index = nextNodeIndex,
|
|
|
|
Index = nextNodeIndex,
|
|
|
|
Clusters = new HashSet<Cluster>() {nextCluster},
|
|
|
|
Clusters = new HashSet<Cluster>() {nextCluster},
|
|
|
|
Time = node.Time + timeDelay,
|
|
|
|
Time = 0,
|
|
|
|
NextNodes = new Dictionary<int, int>(),
|
|
|
|
NextNodes = new Dictionary<int, int>(),
|
|
|
|
PrevNodeIndexes = new HashSet<int>() {node.Index},
|
|
|
|
PrevNodeIndexes = new HashSet<int>() {node.Index},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
clusterNodes.Add(nextNode);
|
|
|
|
clusterNodes.Add(nextNode);
|
|
|
|
|
|
|
|
|
|
|
|
node.NextNodes.TryAdd(nextNodeIndex, timeDelay);
|
|
|
|
node.NextNodes.TryAdd(nextNodeIndex, 1);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (Cluster nextNodeCluster in nextNode.Clusters) {
|
|
|
|
foreach (Cluster nextNodeCluster in nextNode.Clusters) {
|
|
|
|
clusterToNodeMapping.Add(nextNodeCluster, nextNodeIndex);
|
|
|
|
clusterToNodeMapping.Add(nextNodeCluster, nextNodeIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// propagate
|
|
|
|
queue.Enqueue(nextNode);
|
|
|
|
queue.Enqueue(nextNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Attempt to get the critical path length
|
|
|
|
logger.Info($"Finished after {iterations} iterations");
|
|
|
|
// If the ending cluster has not been found, return -1
|
|
|
|
|
|
|
|
Cluster endingCluster = endingClusters.First();
|
|
|
|
|
|
|
|
int criticalPathLength = -1;
|
|
|
|
|
|
|
|
if (clusterToNodeMapping.TryGetValue(endingCluster, out int nodeIndex)) {
|
|
|
|
|
|
|
|
criticalPathLength = clusterNodes[nodeIndex].Time;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Collect information about each cluster:
|
|
|
|
|
|
|
|
response = new AnalyzePathResponse() {
|
|
|
|
|
|
|
|
RequestGuid = requestGuid,
|
|
|
|
|
|
|
|
Clusters = clusterNodes.SelectMany(node =>
|
|
|
|
|
|
|
|
node.Clusters.Select(cluster => CollectClusterInformation(cluster, node.Time))).ToList(),
|
|
|
|
|
|
|
|
CriticalPathLength = criticalPathLength,
|
|
|
|
|
|
|
|
LoopingClusters = loopingNodes.SelectMany(loopingNodeIndex => clusterNodes[loopingNodeIndex].Clusters
|
|
|
|
|
|
|
|
.Select(cluster => CollectClusterInformation(cluster, 0))).ToList(),
|
|
|
|
|
|
|
|
// PerimeterComponents = perimeterComponents.ToList(),
|
|
|
|
|
|
|
|
// NextClusters = collectedNextClusters.Select(CollectClusterInformation).ToList(),
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.Info("Trace end");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -333,6 +397,10 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
return details;
|
|
|
|
return details;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <param name="h">0 - 360</param>
|
|
|
|
|
|
|
|
/// <param name="s">0.0 - 1.0</param>
|
|
|
|
|
|
|
|
/// <param name="v">0.0 - 1.0</param>
|
|
|
|
|
|
|
|
/// <returns>0xrrggbb</returns>
|
|
|
|
private static int HsvToRgb(int h, float s, float v) {
|
|
|
|
private static int HsvToRgb(int h, float s, float v) {
|
|
|
|
s = Math.Clamp(s, 0, 1);
|
|
|
|
s = Math.Clamp(s, 0, 1);
|
|
|
|
v = Math.Clamp(v, 0, 1);
|
|
|
|
v = Math.Clamp(v, 0, 1);
|
|
|
|