Compare commits

...

3 Commits

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Phasic/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

@ -10,4 +10,3 @@ CriticalPathAnalyzer.CriticalPathAnalyzerTool:
Triggers: Triggers:
- CriticalPathAnalyzer.AnalyzePathStart - CriticalPathAnalyzer.AnalyzePathStart
- CriticalPathAnalyzer.AnalyzePathEnd - CriticalPathAnalyzer.AnalyzePathEnd
- CriticalPathAnalyzer.DisplayLoop

@ -16,9 +16,3 @@ CriticalPathAnalyzer.AnalyzePathEnd:
DefaultBinding: DefaultBinding:
Options: Options:
- O - O
CriticalPathAnalyzer.DisplayLoop:
Heading: "CriticalPathAnalyzer"
DefaultBinding:
Options:
- P

@ -14,8 +14,3 @@ FancyInput.Trigger.CriticalPathAnalyzer.AnalyzePathEnd: "Select end of path"
FancyInput.Trigger.CriticalPathAnalyzer.AnalyzePathEnd.Description: """ FancyInput.Trigger.CriticalPathAnalyzer.AnalyzePathEnd.Description: """
Press to select the end of the path to be analyzed. Press to select the end of the path to be analyzed.
""" """
FancyInput.Trigger.CriticalPathAnalyzer.DisplayLoop: "Display loop"
FancyInput.Trigger.CriticalPathAnalyzer.DisplayLoop.Description: """
Highlights only clusters that are causing a loop.
"""

@ -1,7 +1,7 @@
ID: CriticalPathAnalyzer ID: CriticalPathAnalyzer
Name: CriticalPathAnalyzer Name: CriticalPathAnalyzer
Author: D4VID Author: D4VID
Version: 0.0.3 Version: 0.0.4
Priority: 0 Priority: 0
Dependencies: Dependencies:
- HarmonyForLogicWorld - HarmonyForLogicWorld

@ -24,7 +24,6 @@ namespace CriticalPathAnalyzer.Client {
UITrigger.Back, UITrigger.Back,
CriticalPathAnalyzerTrigger.AnalyzePathStart, CriticalPathAnalyzerTrigger.AnalyzePathStart,
CriticalPathAnalyzerTrigger.AnalyzePathEnd, CriticalPathAnalyzerTrigger.AnalyzePathEnd,
CriticalPathAnalyzerTrigger.DisplayLoop,
}; };
/// <summary> /// <summary>
@ -44,8 +43,6 @@ namespace CriticalPathAnalyzer.Client {
CriticalPathAnalyzerTool.SelectPathStart(); CriticalPathAnalyzerTool.SelectPathStart();
} else if (CustomInput.DownThisFrame(CriticalPathAnalyzerTrigger.AnalyzePathEnd)) { } else if (CustomInput.DownThisFrame(CriticalPathAnalyzerTrigger.AnalyzePathEnd)) {
CriticalPathAnalyzerTool.SelectPathEnd(); CriticalPathAnalyzerTool.SelectPathEnd();
} else if (CustomInput.DownThisFrame(CriticalPathAnalyzerTrigger.DisplayLoop)) {
CriticalPathAnalyzerTool.DisplayLoop();
} }
} }

@ -5,6 +5,5 @@ namespace CriticalPathAnalyzer.Client.Keybindings {
OpenPathAnalyzer, OpenPathAnalyzer,
AnalyzePathStart, AnalyzePathStart,
AnalyzePathEnd, AnalyzePathEnd,
DisplayLoop,
} }
} }

@ -20,7 +20,7 @@ namespace CriticalPathAnalyzer.Client.Tool {
private static AnalyzePathResponse _response; private static AnalyzePathResponse _response;
private static readonly OutlineData StartOutline = new OutlineData(new Color24(0x00ff00)); private static readonly OutlineData StartOutline = new OutlineData(new Color24(0x00ff00));
private static readonly OutlineData EndOutline = new OutlineData(new Color24(0xff0000)); private static readonly OutlineData EndOutline = new OutlineData(new Color24(0x00aaff));
public static void Init(ILogicLogger logger) { public static void Init(ILogicLogger logger) {
_logger = logger; _logger = logger;
@ -97,19 +97,6 @@ namespace CriticalPathAnalyzer.Client.Tool {
} }
} }
/// <summary>
/// Remove standard highlighting and only highlight clusters that form a loop.
/// </summary>
public static void DisplayLoop() {
if (_response == null) {
_logger.Error("Got no response - cannot display loop");
return;
}
PathHighlighter.RemoveHighlighting();
PathHighlighter.HighlightWires(_response.LoopingClusters);
}
/// <summary> /// <summary>
/// Send a request to the server to analyze the path between the selected start and end pegs. /// Send a request to the server to analyze the path between the selected start and end pegs.
/// </summary> /// </summary>
@ -141,10 +128,14 @@ namespace CriticalPathAnalyzer.Client.Tool {
_response = response; _response = response;
_logger.Info($"Critical path length is: {_response.CriticalPathLength}");
PathHighlighter.RemoveHighlighting(); PathHighlighter.RemoveHighlighting();
PathHighlighter.HighlightWires(_response.Clusters);
if (_response.LoopingClusters.Count > 0) {
PathHighlighter.HighlightWires(_response.LoopingClusters);
} else {
_logger.Info($"Critical path length is: {_response.CriticalPathLength}");
PathHighlighter.HighlightWires(_response.Clusters);
}
} }
} }
} }

@ -9,7 +9,6 @@ using LogicWorld.Outlines;
namespace CriticalPathAnalyzer.Client.Tool { namespace CriticalPathAnalyzer.Client.Tool {
public class PathHighlighter { public class PathHighlighter {
private static List<ClusterDetails> _clusters = new List<ClusterDetails>(); private static List<ClusterDetails> _clusters = new List<ClusterDetails>();
private static List<ComponentAddress> _highlightedComponents = new List<ComponentAddress>();
private static List<WireAddress> _highlightedWires = new List<WireAddress>(); private static List<WireAddress> _highlightedWires = new List<WireAddress>();
public static void HighlightWires(List<ClusterDetails> clusters) { public static void HighlightWires(List<ClusterDetails> clusters) {
@ -18,22 +17,6 @@ namespace CriticalPathAnalyzer.Client.Tool {
foreach (ClusterDetails clusterDetails in _clusters) { foreach (ClusterDetails clusterDetails in _clusters) {
HighlightCluster(clusterDetails); HighlightCluster(clusterDetails);
} }
// IWorldData world = Instances.MainWorld.Data;
// foreach (ComponentAddress componentAddress in response.PerimeterComponents) {
// if (!world.Contains(componentAddress)) {
// continue;
// }
//
// Outliner.Outline(componentAddress, PerimeterColor);
// _highlightedComponents.Add(componentAddress);
// }
// _clusters.AddRange(response.NextClusters); // add to the collection to unhighlight
// foreach (ClusterDetails clusterDetails in response.NextClusters) {
// HighlightCluster(clusterDetails, NextColor);
// }
} }
/// <summary> /// <summary>
@ -52,14 +35,6 @@ namespace CriticalPathAnalyzer.Client.Tool {
Outliner.Outline(address, outline); Outliner.Outline(address, outline);
} }
foreach (ComponentAddress address in cluster.LinkingComponents) {
if (!world.Contains(address)) {
continue;
}
Outliner.Outline(address, outline);
}
foreach (PegAddress pegAddress in cluster.Pegs) { foreach (PegAddress pegAddress in cluster.Pegs) {
if (!world.Contains(pegAddress.ComponentAddress)) { if (!world.Contains(pegAddress.ComponentAddress)) {
continue; continue;
@ -99,14 +74,9 @@ namespace CriticalPathAnalyzer.Client.Tool {
foreach (WireAddress wireAddress in _highlightedWires) { foreach (WireAddress wireAddress in _highlightedWires) {
Outliner.RemoveOutline(wireAddress); Outliner.RemoveOutline(wireAddress);
} }
foreach (ComponentAddress componentAddress in _highlightedComponents) {
Outliner.RemoveOutline(componentAddress);
}
_clusters = null; _clusters = null;
_highlightedWires.Clear(); _highlightedWires.Clear();
_highlightedComponents.Clear();
} }
private static void UnhighlightCluster(ClusterDetails cluster) { private static void UnhighlightCluster(ClusterDetails cluster) {
@ -117,10 +87,6 @@ namespace CriticalPathAnalyzer.Client.Tool {
foreach (ComponentAddress address in cluster.ConnectingComponents) { foreach (ComponentAddress address in cluster.ConnectingComponents) {
Outliner.RemoveOutline(address); Outliner.RemoveOutline(address);
} }
foreach (ComponentAddress address in cluster.LinkingComponents) {
Outliner.RemoveOutline(address);
}
} }
} }
} }

@ -7,6 +7,6 @@ namespace CriticalPathAnalyzer.Server.Tool {
public HashSet<Cluster> Clusters; // bidirectionally connected clusters without delay public HashSet<Cluster> Clusters; // bidirectionally connected clusters without delay
public int Time; // what point in time has been this cluster last updated public int Time; // what point in time has been this cluster last updated
public Dictionary<int, int> NextNodes; // next node index / delay between them public Dictionary<int, int> NextNodes; // next node index / delay between them
public HashSet<int> PrevNodeIndexes; // the last cluster that updated this one public HashSet<int> PrevNodeIndexes; // node indexes that update this one
} }
} }

@ -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);

@ -13,8 +13,6 @@ namespace CriticalPathAnalyzer.Shared.Packets.S2C {
[Key(2)] public int CriticalPathLength; [Key(2)] public int CriticalPathLength;
[Key(3)] public List<ClusterDetails> LoopingClusters; [Key(3)] public List<ClusterDetails> LoopingClusters;
// [Key(4)] public List<ComponentAddress> PerimeterComponents;
// [Key(5)] public List<ClusterDetails> NextClusters;
} }
[MessagePackObject] [MessagePackObject]
@ -29,11 +27,6 @@ namespace CriticalPathAnalyzer.Shared.Packets.S2C {
/// </summary> /// </summary>
[Key(1)] public List<ComponentAddress> ConnectingComponents; [Key(1)] public List<ComponentAddress> ConnectingComponents;
/// <summary>
/// Relays and fast buffers
/// </summary>
[Key(2)] public List<ComponentAddress> LinkingComponents;
[Key(3)] public int Color; [Key(3)] public int Color;
} }
} }
Loading…
Cancel
Save