|
|
|
@ -8,15 +8,13 @@ using LogicAPI.Data;
|
|
|
|
|
using LogicAPI.Server.Components;
|
|
|
|
|
using LogicAPI.Services;
|
|
|
|
|
using LogicLog;
|
|
|
|
|
using LogicWorld.LogicCode;
|
|
|
|
|
using LogicWorld.Server.Circuitry;
|
|
|
|
|
|
|
|
|
|
namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
|
public class ServerPathTracer {
|
|
|
|
|
// Reflection/Delegate access helpers:
|
|
|
|
|
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:
|
|
|
|
|
private static readonly ICircuitryManager Circuits;
|
|
|
|
@ -26,12 +24,6 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
|
GetCluster = Delegator.createPropertyGetter<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>();
|
|
|
|
|
World = ServiceGetter.getService<IWorldData>();
|
|
|
|
@ -62,10 +54,10 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
|
// An output peg however can be connected to multiple clusters.
|
|
|
|
|
// It only makes sense to then select all these clusters as primary cluster.
|
|
|
|
|
var startingClusters = new HashSet<Cluster>();
|
|
|
|
|
CollectMainClusters(start, startingClusters);
|
|
|
|
|
CollectPegClusters(start, startingClusters);
|
|
|
|
|
|
|
|
|
|
var endingClusters = new HashSet<Cluster>();
|
|
|
|
|
CollectMainClusters(end, endingClusters);
|
|
|
|
|
CollectPegClusters(end, endingClusters);
|
|
|
|
|
|
|
|
|
|
foreach (Cluster startingCluster in startingClusters) {
|
|
|
|
|
// ignore already mapped clusters
|
|
|
|
@ -204,35 +196,95 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
|
|
|
|
|
|
// var perimeterComponents = new HashSet<ComponentAddress>();
|
|
|
|
|
var collectedNextClusters = new HashSet<Cluster>();
|
|
|
|
|
var collectedNextInstantClusters = new HashSet<Cluster>();
|
|
|
|
|
foreach (Cluster cluster in node.Clusters) {
|
|
|
|
|
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
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (component.GetType() == typeof(Delayer)) {
|
|
|
|
|
// Special handling of the delayer
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LinkNextClusters(queue, clusterToNodeMapping, clusterNodes, collectedNextInstantClusters, node, 0);
|
|
|
|
|
LinkNextClusters(queue, clusterToNodeMapping, clusterNodes, collectedNextClusters, node, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.Info($"Finished after {iterations} iterations");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// perimeterComponents.Add(inputPeg.LogicComponent.Address);
|
|
|
|
|
foreach (IOutputPeg outputPeg in inputPeg.LogicComponent.Outputs) {
|
|
|
|
|
CollectMainClusters(outputPeg.Address, collectedNextClusters);
|
|
|
|
|
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
|
|
|
|
|
CollectPegClusters(component.Inputs[2].Address, clusters);
|
|
|
|
|
} else if (inputPeg.Address == component.Inputs[2].Address) {
|
|
|
|
|
// the other side
|
|
|
|
|
CollectPegClusters(component.Inputs[1].Address, clusters);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return clusters;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void LinkNextClusters(Queue<ClusterNode> queue, Dictionary<Cluster, int> clusterToNodeMapping,
|
|
|
|
|
List<ClusterNode> clusterNodes, HashSet<Cluster> collectedNextClusters, ClusterNode node, int delay) {
|
|
|
|
|
foreach (Cluster nextCluster in collectedNextClusters) {
|
|
|
|
|
// ignore already mapped clusters
|
|
|
|
|
if (clusterToNodeMapping.TryGetValue(nextCluster, out int nextNodeIndex)) {
|
|
|
|
|
ClusterNode nextNode = clusterNodes[nextNodeIndex];
|
|
|
|
|
// only add the link, don't propagate
|
|
|
|
|
nextNode.PrevNodeIndexes.Add(node.Index);
|
|
|
|
|
node.NextNodes.TryAdd(nextNodeIndex, 1);
|
|
|
|
|
node.NextNodes.TryAdd(nextNodeIndex, delay);
|
|
|
|
|
} else {
|
|
|
|
|
// var nextClusters = new HashSet<Cluster>();
|
|
|
|
|
// var twoWayConnectedClusters = new HashSet<Cluster>();
|
|
|
|
|
// GetLinkedClusters(cluster, nextClusters, twoWayConnectedClusters, GetFollowers);
|
|
|
|
|
HashSet<Cluster> nextClusters = GetClustersConnectedThroughRelays(nextCluster);
|
|
|
|
|
|
|
|
|
|
if (nextClusters.Any(clusterToNodeMapping.ContainsKey)) {
|
|
|
|
|
// already discovered cluster node
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nextNodeIndex = clusterNodes.Count;
|
|
|
|
|
var nextNode = new ClusterNode() {
|
|
|
|
|
Index = nextNodeIndex,
|
|
|
|
|
Clusters = new HashSet<Cluster>() {nextCluster},
|
|
|
|
|
Clusters = nextClusters,
|
|
|
|
|
Time = 0,
|
|
|
|
|
NextNodes = new Dictionary<int, int>(),
|
|
|
|
|
PrevNodeIndexes = new HashSet<int>() {node.Index},
|
|
|
|
@ -240,10 +292,10 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
|
|
|
|
|
|
clusterNodes.Add(nextNode);
|
|
|
|
|
|
|
|
|
|
node.NextNodes.TryAdd(nextNodeIndex, 1);
|
|
|
|
|
node.NextNodes.TryAdd(nextNodeIndex, delay);
|
|
|
|
|
|
|
|
|
|
foreach (Cluster nextNodeCluster in nextNode.Clusters) {
|
|
|
|
|
clusterToNodeMapping.Add(nextNodeCluster, nextNodeIndex);
|
|
|
|
|
clusterToNodeMapping.TryAdd(nextNodeCluster, nextNodeIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// propagate
|
|
|
|
@ -252,9 +304,6 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logger.Info($"Finished after {iterations} iterations");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Check if the peg exists in the world
|
|
|
|
@ -288,18 +337,13 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
|
return cluster;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool GetLinkerAt(Cluster cluster, out ClusterLinker linker) {
|
|
|
|
|
linker = GetLinker(cluster);
|
|
|
|
|
return linker != null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// An output peg can be connected to multiple clusters. Collect all of them.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="pegAddress">Address of the peg</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>
|
|
|
|
|
private static void CollectMainClusters(PegAddress pegAddress, HashSet<Cluster> primaryClusters) {
|
|
|
|
|
private static void CollectPegClusters(PegAddress pegAddress, HashSet<Cluster> primaryClusters) {
|
|
|
|
|
if (pegAddress.IsInputAddress(out InputAddress inputAddress)) {
|
|
|
|
|
primaryClusters.Add(GetClusterAt(inputAddress));
|
|
|
|
|
} else {
|
|
|
|
@ -325,48 +369,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) {
|
|
|
|
|
var details = new ClusterDetails {
|
|
|
|
|
Pegs = new List<PegAddress>(),
|
|
|
|
|
ConnectingComponents = new List<ComponentAddress>(),
|
|
|
|
|
LinkingComponents = new List<ComponentAddress>(),
|
|
|
|
|
Color = HsvToRgb(time * 20, 1, 1),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -380,14 +387,6 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
|
// Socket
|
|
|
|
|
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) {
|
|
|
|
|