|
|
@ -8,15 +8,13 @@ using LogicAPI.Data;
|
|
|
|
using LogicAPI.Server.Components;
|
|
|
|
using LogicAPI.Server.Components;
|
|
|
|
using LogicAPI.Services;
|
|
|
|
using LogicAPI.Services;
|
|
|
|
using LogicLog;
|
|
|
|
using LogicLog;
|
|
|
|
|
|
|
|
using LogicWorld.LogicCode;
|
|
|
|
using LogicWorld.Server.Circuitry;
|
|
|
|
using LogicWorld.Server.Circuitry;
|
|
|
|
|
|
|
|
|
|
|
|
namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
public class ServerPathTracer {
|
|
|
|
public class ServerPathTracer {
|
|
|
|
// Reflection/Delegate access helpers:
|
|
|
|
// Reflection/Delegate access helpers:
|
|
|
|
private static readonly Func<InputPeg, Cluster> GetCluster;
|
|
|
|
private static readonly Func<InputPeg, Cluster> GetCluster;
|
|
|
|
private static readonly Func<Cluster, ClusterLinker> GetLinker;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static readonly Func<ClusterLinker, List<ClusterLinker>> GetFollowers;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Services needed to lookup wires/pegs:
|
|
|
|
// Services needed to lookup wires/pegs:
|
|
|
|
private static readonly ICircuitryManager Circuits;
|
|
|
|
private static readonly ICircuitryManager Circuits;
|
|
|
@ -26,12 +24,6 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
GetCluster = Delegator.createPropertyGetter<InputPeg, Cluster>(
|
|
|
|
GetCluster = Delegator.createPropertyGetter<InputPeg, Cluster>(
|
|
|
|
Properties.getPrivate(typeof(InputPeg), "Cluster")
|
|
|
|
Properties.getPrivate(typeof(InputPeg), "Cluster")
|
|
|
|
);
|
|
|
|
);
|
|
|
|
GetLinker = Delegator.createFieldGetter<Cluster, ClusterLinker>(
|
|
|
|
|
|
|
|
Fields.getPrivate(typeof(Cluster), "Linker")
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
GetFollowers = Delegator.createFieldGetter<ClusterLinker, List<ClusterLinker>>(
|
|
|
|
|
|
|
|
Fields.getPrivate(typeof(ClusterLinker), "LinkedFollowers")
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Circuits = ServiceGetter.getService<ICircuitryManager>();
|
|
|
|
Circuits = ServiceGetter.getService<ICircuitryManager>();
|
|
|
|
World = ServiceGetter.getService<IWorldData>();
|
|
|
|
World = ServiceGetter.getService<IWorldData>();
|
|
|
@ -57,18 +49,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);
|
|
|
|
CollectPegClusters(start, startingClusters);
|
|
|
|
|
|
|
|
|
|
|
|
var endingClusters = new HashSet<Cluster>();
|
|
|
|
var endingClusters = new HashSet<Cluster>();
|
|
|
|
CollectMainClusters(end, endingClusters);
|
|
|
|
CollectPegClusters(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;
|
|
|
@ -80,7 +71,7 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
int index = clusterNodes.Count;
|
|
|
|
int index = clusterNodes.Count;
|
|
|
|
var node = new ClusterNode() {
|
|
|
|
var node = new ClusterNode() {
|
|
|
|
Index = index,
|
|
|
|
Index = index,
|
|
|
|
Clusters = new HashSet<Cluster>() {startingCluster},
|
|
|
|
Clusters = GetClustersConnectedThroughRelays(startingCluster),
|
|
|
|
Time = 0,
|
|
|
|
Time = 0,
|
|
|
|
PrevNodeIndexes = new HashSet<int>(),
|
|
|
|
PrevNodeIndexes = new HashSet<int>(),
|
|
|
|
NextNodes = new Dictionary<int, int>(),
|
|
|
|
NextNodes = new Dictionary<int, int>(),
|
|
|
@ -93,102 +84,235 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
queue.Enqueue(node);
|
|
|
|
queue.Enqueue(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logger.Info($"collected {collectedClusters.Count} main clusters");
|
|
|
|
logger.Info($"collected {startingClusters.Count} main clusters");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int iterations = 0;
|
|
|
|
|
|
|
|
while (queue.TryDequeue(out int index)) {
|
|
|
|
|
|
|
|
iterations++;
|
|
|
|
|
|
|
|
if (iterations % 1000 == 0) {
|
|
|
|
|
|
|
|
CriticalPathAnalyzerServer.LoggerInstance.Info($"{iterations} propagation iterations");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int iters = 0;
|
|
|
|
if (node.NextNodes.Count == 0) {
|
|
|
|
|
|
|
|
// source
|
|
|
|
|
|
|
|
queue.Enqueue(node);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int iterations = 0;
|
|
|
|
while (queue.TryDequeue(out ClusterNode node)) {
|
|
|
|
while (queue.TryDequeue(out ClusterNode node)) {
|
|
|
|
iters++;
|
|
|
|
iterations++;
|
|
|
|
if (iters > 10000) {
|
|
|
|
if (iterations % 1000 == 0) {
|
|
|
|
|
|
|
|
CriticalPathAnalyzerServer.LoggerInstance.Info($"{iterations} loop iterations");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
|
|
|
|
iterations++;
|
|
|
|
|
|
|
|
if (iterations % 1000 == 0) {
|
|
|
|
|
|
|
|
logger.Info($"{iterations} iterations");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (iterations > 100000) {
|
|
|
|
logger.Error("Infinite iterations");
|
|
|
|
logger.Error("Infinite iterations");
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// var perimeterComponents = new HashSet<ComponentAddress>();
|
|
|
|
// var perimeterComponents = new HashSet<ComponentAddress>();
|
|
|
|
var collectedNextClusters = new HashSet<Cluster>();
|
|
|
|
var collectedNextClusters = new HashSet<Cluster>();
|
|
|
|
|
|
|
|
var collectedNextInstantClusters = new HashSet<Cluster>();
|
|
|
|
foreach (Cluster cluster in node.Clusters) {
|
|
|
|
foreach (Cluster cluster in node.Clusters) {
|
|
|
|
foreach (InputPeg inputPeg in cluster.ConnectedInputs) {
|
|
|
|
foreach (InputPeg inputPeg in cluster.ConnectedInputs) {
|
|
|
|
if (inputPeg.LogicComponent == null) {
|
|
|
|
LogicComponent component = inputPeg.LogicComponent;
|
|
|
|
|
|
|
|
if (component == null) {
|
|
|
|
// These are regular pegs, they are not attached to any logic component, skip them
|
|
|
|
// These are regular pegs, they are not attached to any logic component, skip them
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// perimeterComponents.Add(inputPeg.LogicComponent.Address);
|
|
|
|
if (component.GetType() == typeof(Delayer)) {
|
|
|
|
foreach (IOutputPeg outputPeg in inputPeg.LogicComponent.Outputs) {
|
|
|
|
// Special handling of the delayer
|
|
|
|
CollectMainClusters(outputPeg.Address, collectedNextClusters);
|
|
|
|
var delayer = (Delayer) component;
|
|
|
|
|
|
|
|
//TODO: delayer.Data.DelayLengthInTicks;
|
|
|
|
|
|
|
|
CollectPegClusters(delayer.Outputs[0].Address, collectedNextClusters);
|
|
|
|
|
|
|
|
} else if (component.GetType() == typeof(Relay)) {
|
|
|
|
|
|
|
|
// Collect all next clusters connected via a relay (from the control input side)
|
|
|
|
|
|
|
|
if (inputPeg.Address == component.Inputs[0].Address) {
|
|
|
|
|
|
|
|
// the top control input
|
|
|
|
|
|
|
|
CollectPegClusters(component.Inputs[1].Address, collectedNextClusters);
|
|
|
|
|
|
|
|
CollectPegClusters(component.Inputs[2].Address, collectedNextClusters);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// Collect all next cluster connected via normal components
|
|
|
|
|
|
|
|
foreach (IOutputPeg outputPeg in component.Outputs) {
|
|
|
|
|
|
|
|
CollectPegClusters(outputPeg.Address, collectedNextClusters);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Collect all next clusters connected via fast buffers
|
|
|
|
|
|
|
|
if (inputPeg.OneWayPhasicLinksFollowers != null) {
|
|
|
|
|
|
|
|
foreach (InputPeg followerPeg in inputPeg.OneWayPhasicLinksFollowers) {
|
|
|
|
|
|
|
|
CollectPegClusters(followerPeg.Address, collectedNextInstantClusters);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (Cluster nextCluster in collectedNextClusters) {
|
|
|
|
LinkNextClusters(queue, clusterToNodeMapping, clusterNodes, collectedNextInstantClusters, node, 0);
|
|
|
|
// ignore already mapped clusters
|
|
|
|
LinkNextClusters(queue, clusterToNodeMapping, clusterNodes, collectedNextClusters, node, 1);
|
|
|
|
if (clusterToNodeMapping.TryGetValue(nextCluster, out int nextNodeIndex)) {
|
|
|
|
}
|
|
|
|
ClusterNode nextNode = clusterNodes[nextNodeIndex];
|
|
|
|
|
|
|
|
// only add the link, don't propagate
|
|
|
|
|
|
|
|
if (!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
|
|
|
|
logger.Info($"Finished after {iterations} iterations");
|
|
|
|
int timeDelay = 1;
|
|
|
|
}
|
|
|
|
node.NextNodes.TryAdd(nextNodeIndex, 1);
|
|
|
|
|
|
|
|
nextNode.Time = node.Time + timeDelay;
|
|
|
|
|
|
|
|
queue.Enqueue(nextNode);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// var nextClusters = new HashSet<Cluster>();
|
|
|
|
|
|
|
|
// var twoWayConnectedClusters = new HashSet<Cluster>();
|
|
|
|
|
|
|
|
// GetLinkedClusters(cluster, nextClusters, twoWayConnectedClusters, GetFollowers);
|
|
|
|
|
|
|
|
int timeDelay = 1;
|
|
|
|
|
|
|
|
nextNodeIndex = clusterNodes.Count;
|
|
|
|
|
|
|
|
var nextNode = new ClusterNode() {
|
|
|
|
|
|
|
|
Index = nextNodeIndex,
|
|
|
|
|
|
|
|
Clusters = new HashSet<Cluster>() {nextCluster},
|
|
|
|
|
|
|
|
Time = node.Time + timeDelay,
|
|
|
|
|
|
|
|
NextNodes = new Dictionary<int, int>(),
|
|
|
|
|
|
|
|
PrevNodeIndexes = new HashSet<int>() {node.Index},
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clusterNodes.Add(nextNode);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
node.NextNodes.TryAdd(nextNodeIndex, timeDelay);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (Cluster nextNodeCluster in nextNode.Clusters) {
|
|
|
|
|
|
|
|
clusterToNodeMapping.Add(nextNodeCluster, nextNodeIndex);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
queue.Enqueue(nextNode);
|
|
|
|
private static HashSet<Cluster> GetClustersConnectedThroughRelays(Cluster cluster) {
|
|
|
|
|
|
|
|
var clusters = new HashSet<Cluster>() {cluster};
|
|
|
|
|
|
|
|
foreach (InputPeg inputPeg in cluster.ConnectedInputs) {
|
|
|
|
|
|
|
|
LogicComponent component = inputPeg.LogicComponent;
|
|
|
|
|
|
|
|
if (component == null) {
|
|
|
|
|
|
|
|
// These are regular pegs, they are not attached to any logic component, skip them
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (component.GetType() == typeof(Relay)) {
|
|
|
|
|
|
|
|
// Collect all next clusters connected via a relay (from the control input side)
|
|
|
|
|
|
|
|
if (inputPeg.Address == component.Inputs[1].Address) {
|
|
|
|
|
|
|
|
// one of the sides
|
|
|
|
|
|
|
|
// TODO: need to recursively check for further relays
|
|
|
|
|
|
|
|
CollectPegClusters(component.Inputs[2].Address, clusters);
|
|
|
|
|
|
|
|
} else if (inputPeg.Address == component.Inputs[2].Address) {
|
|
|
|
|
|
|
|
// the other side
|
|
|
|
|
|
|
|
CollectPegClusters(component.Inputs[1].Address, clusters);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Attempt to get the critical path length
|
|
|
|
return clusters;
|
|
|
|
// 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:
|
|
|
|
private static void LinkNextClusters(Queue<ClusterNode> queue, Dictionary<Cluster, int> clusterToNodeMapping,
|
|
|
|
response = new AnalyzePathResponse() {
|
|
|
|
List<ClusterNode> clusterNodes, HashSet<Cluster> collectedNextClusters, ClusterNode node, int delay) {
|
|
|
|
RequestGuid = requestGuid,
|
|
|
|
foreach (Cluster nextCluster in collectedNextClusters) {
|
|
|
|
Clusters = clusterNodes.SelectMany(node =>
|
|
|
|
// ignore already mapped clusters
|
|
|
|
node.Clusters.Select(cluster => CollectClusterInformation(cluster, node.Time))).ToList(),
|
|
|
|
if (clusterToNodeMapping.TryGetValue(nextCluster, out int nextNodeIndex)) {
|
|
|
|
CriticalPathLength = criticalPathLength,
|
|
|
|
ClusterNode nextNode = clusterNodes[nextNodeIndex];
|
|
|
|
LoopingClusters = loopingNodes.SelectMany(loopingNodeIndex => clusterNodes[loopingNodeIndex].Clusters
|
|
|
|
// only add the link, don't propagate
|
|
|
|
.Select(cluster => CollectClusterInformation(cluster, 0))).ToList(),
|
|
|
|
nextNode.PrevNodeIndexes.Add(node.Index);
|
|
|
|
// PerimeterComponents = perimeterComponents.ToList(),
|
|
|
|
node.NextNodes.TryAdd(nextNodeIndex, delay);
|
|
|
|
// NextClusters = collectedNextClusters.Select(CollectClusterInformation).ToList(),
|
|
|
|
} else {
|
|
|
|
};
|
|
|
|
HashSet<Cluster> nextClusters = GetClustersConnectedThroughRelays(nextCluster);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (nextClusters.Any(clusterToNodeMapping.ContainsKey)) {
|
|
|
|
|
|
|
|
// already discovered cluster node
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logger.Info("Trace end");
|
|
|
|
nextNodeIndex = clusterNodes.Count;
|
|
|
|
|
|
|
|
var nextNode = new ClusterNode() {
|
|
|
|
|
|
|
|
Index = nextNodeIndex,
|
|
|
|
|
|
|
|
Clusters = nextClusters,
|
|
|
|
|
|
|
|
Time = 0,
|
|
|
|
|
|
|
|
NextNodes = new Dictionary<int, int>(),
|
|
|
|
|
|
|
|
PrevNodeIndexes = new HashSet<int>() {node.Index},
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
clusterNodes.Add(nextNode);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
node.NextNodes.TryAdd(nextNodeIndex, delay);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (Cluster nextNodeCluster in nextNode.Clusters) {
|
|
|
|
|
|
|
|
clusterToNodeMapping.TryAdd(nextNodeCluster, nextNodeIndex);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// propagate
|
|
|
|
|
|
|
|
queue.Enqueue(nextNode);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -224,18 +348,13 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
return cluster;
|
|
|
|
return cluster;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static bool GetLinkerAt(Cluster cluster, out ClusterLinker linker) {
|
|
|
|
|
|
|
|
linker = GetLinker(cluster);
|
|
|
|
|
|
|
|
return linker != null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// An output peg can be connected to multiple clusters. Collect all of them.
|
|
|
|
/// An output peg can be connected to multiple clusters. Collect all of them.
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="pegAddress">Address of the peg</param>
|
|
|
|
/// <param name="pegAddress">Address of the peg</param>
|
|
|
|
/// <param name="primaryClusters">Found clusters get added into this hash set</param>
|
|
|
|
/// <param name="primaryClusters">Found clusters get added into this hash set</param>
|
|
|
|
/// <exception cref="Exception">A wire of the peg is not in the world</exception>
|
|
|
|
/// <exception cref="Exception">A wire of the peg is not in the world</exception>
|
|
|
|
private static void CollectMainClusters(PegAddress pegAddress, HashSet<Cluster> primaryClusters) {
|
|
|
|
private static void CollectPegClusters(PegAddress pegAddress, HashSet<Cluster> primaryClusters) {
|
|
|
|
if (pegAddress.IsInputAddress(out InputAddress inputAddress)) {
|
|
|
|
if (pegAddress.IsInputAddress(out InputAddress inputAddress)) {
|
|
|
|
primaryClusters.Add(GetClusterAt(inputAddress));
|
|
|
|
primaryClusters.Add(GetClusterAt(inputAddress));
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@ -261,48 +380,11 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void GetLinkedClusters(
|
|
|
|
|
|
|
|
Cluster startingPoint,
|
|
|
|
|
|
|
|
HashSet<Cluster> oneWayConnectedClusters,
|
|
|
|
|
|
|
|
HashSet<Cluster> twoWayConnectedClusters,
|
|
|
|
|
|
|
|
Func<ClusterLinker, List<ClusterLinker>> linkedLinkerGetter
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
var clustersToProcess = new Queue<ClusterLinker>();
|
|
|
|
|
|
|
|
if (!GetLinkerAt(startingPoint, out ClusterLinker startingLinker)) {
|
|
|
|
|
|
|
|
return; // No linker on this cluster => no link => nothing to collect
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clustersToProcess.Enqueue(startingLinker);
|
|
|
|
|
|
|
|
twoWayConnectedClusters.Add(startingPoint);
|
|
|
|
|
|
|
|
// While the starting cluster is no source, the algorithm needs to skip it when encountered.
|
|
|
|
|
|
|
|
while (clustersToProcess.TryDequeue(out ClusterLinker linkerToCheck)) {
|
|
|
|
|
|
|
|
List<ClusterLinker> listOfLinkedLinkers = linkedLinkerGetter(linkerToCheck); // Is never null.
|
|
|
|
|
|
|
|
foreach (ClusterLinker linkedLinker in listOfLinkedLinkers) {
|
|
|
|
|
|
|
|
Cluster clusterOfLinkedLinker = linkedLinker.ClusterBeingLinked; // Should never be null.
|
|
|
|
|
|
|
|
List<ClusterLinker> listOfBackLinkers = linkedLinkerGetter(linkedLinker); // Is never null.
|
|
|
|
|
|
|
|
if (listOfBackLinkers.Contains(linkerToCheck)) {
|
|
|
|
|
|
|
|
// bidirectional - a relay
|
|
|
|
|
|
|
|
// TODO: will not find a relay connection if the relay is turned off
|
|
|
|
|
|
|
|
if (twoWayConnectedClusters.Add(clusterOfLinkedLinker)) {
|
|
|
|
|
|
|
|
// Element was not yet present in the array, so keep looking into it!
|
|
|
|
|
|
|
|
clustersToProcess.Enqueue(linkedLinker);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// unidirectional - a fast buffer
|
|
|
|
|
|
|
|
oneWayConnectedClusters.Add(clusterOfLinkedLinker);
|
|
|
|
|
|
|
|
// stop propagation
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
twoWayConnectedClusters.Remove(startingPoint);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static ClusterDetails CollectClusterInformation(Cluster cluster, int time) {
|
|
|
|
private static ClusterDetails CollectClusterInformation(Cluster cluster, int time) {
|
|
|
|
var details = new ClusterDetails {
|
|
|
|
var details = new ClusterDetails {
|
|
|
|
Pegs = new List<PegAddress>(),
|
|
|
|
Pegs = new List<PegAddress>(),
|
|
|
|
ConnectingComponents = new List<ComponentAddress>(),
|
|
|
|
ConnectingComponents = new List<ComponentAddress>(),
|
|
|
|
LinkingComponents = new List<ComponentAddress>(),
|
|
|
|
|
|
|
|
Color = HsvToRgb(time * 20, 1, 1),
|
|
|
|
Color = HsvToRgb(time * 20, 1, 1),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
@ -316,14 +398,6 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
// Socket
|
|
|
|
// Socket
|
|
|
|
details.ConnectingComponents.Add(peg.Address.ComponentAddress);
|
|
|
|
details.ConnectingComponents.Add(peg.Address.ComponentAddress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((peg.PhasicLinks != null && peg.PhasicLinks.Any())
|
|
|
|
|
|
|
|
|| (peg.OneWayPhasicLinksFollowers != null && peg.OneWayPhasicLinksFollowers.Any())
|
|
|
|
|
|
|
|
|| (peg.OneWayPhasicLinksLeaders != null && peg.OneWayPhasicLinksLeaders.Any())
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Relay / fast buffer
|
|
|
|
|
|
|
|
details.LinkingComponents.Add(peg.Address.ComponentAddress);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (OutputPeg peg in outputPegs) {
|
|
|
|
foreach (OutputPeg peg in outputPegs) {
|
|
|
@ -333,6 +407,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);
|
|
|
|