|
|
@ -4,9 +4,11 @@ using System.Linq;
|
|
|
|
using CriticalPathAnalyzer.Shared.Packets.S2C;
|
|
|
|
using CriticalPathAnalyzer.Shared.Packets.S2C;
|
|
|
|
using EccsLogicWorldAPI.Server;
|
|
|
|
using EccsLogicWorldAPI.Server;
|
|
|
|
using EccsLogicWorldAPI.Shared.AccessHelper;
|
|
|
|
using EccsLogicWorldAPI.Shared.AccessHelper;
|
|
|
|
|
|
|
|
using JimmysUnityUtilities;
|
|
|
|
using LogicAPI.Data;
|
|
|
|
using LogicAPI.Data;
|
|
|
|
using LogicAPI.Server.Components;
|
|
|
|
using LogicAPI.Server.Components;
|
|
|
|
using LogicAPI.Services;
|
|
|
|
using LogicAPI.Services;
|
|
|
|
|
|
|
|
using LogicLog;
|
|
|
|
using LogicWorld.Server.Circuitry;
|
|
|
|
using LogicWorld.Server.Circuitry;
|
|
|
|
|
|
|
|
|
|
|
|
namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
@ -42,18 +44,20 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
PegAddress end,
|
|
|
|
PegAddress end,
|
|
|
|
out AnalyzePathResponse response
|
|
|
|
out AnalyzePathResponse response
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
|
|
|
|
ILogicLogger logger = CriticalPathAnalyzerServer.LoggerInstance;
|
|
|
|
response = null;
|
|
|
|
response = null;
|
|
|
|
|
|
|
|
|
|
|
|
CriticalPathAnalyzerServer.LoggerInstance.Info("Trace start");
|
|
|
|
logger.Info("Trace start");
|
|
|
|
|
|
|
|
|
|
|
|
// Validate, that the peg is actually existing:
|
|
|
|
// Validate, that the peg is actually existing:
|
|
|
|
if (!PegExists(start) || !PegExists(end)) {
|
|
|
|
if (!PegExists(start) || !PegExists(end)) {
|
|
|
|
CriticalPathAnalyzerServer.LoggerInstance.Error("Peg not found");
|
|
|
|
logger.Error("Peg not found");
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var clusterNodes = new List<ClusterNode>();
|
|
|
|
var clusterNodes = new List<ClusterNode>();
|
|
|
|
var nodeQueue = new Queue<ClusterNode>();
|
|
|
|
var clusterToNodeMapping = new Dictionary<Cluster, int>(); // Cluster / index of node
|
|
|
|
|
|
|
|
var queue = new Queue<(Cluster, 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.
|
|
|
@ -61,61 +65,95 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
var collectedClusters = new HashSet<Cluster>();
|
|
|
|
var collectedClusters = new HashSet<Cluster>();
|
|
|
|
CollectMainClusters(start, collectedClusters);
|
|
|
|
CollectMainClusters(start, collectedClusters);
|
|
|
|
|
|
|
|
|
|
|
|
CriticalPathAnalyzerServer.LoggerInstance.Info($"collected {collectedClusters.Count} main clusters");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Collect clusters that get powered by the original cluster using relays or fast buffers
|
|
|
|
|
|
|
|
var oneWayConnectedClusters = new HashSet<Cluster>();
|
|
|
|
|
|
|
|
var twoWayConnectedClusters = new HashSet<Cluster>();
|
|
|
|
|
|
|
|
foreach (Cluster cluster in collectedClusters) {
|
|
|
|
foreach (Cluster cluster in collectedClusters) {
|
|
|
|
GetLinkedClusters(cluster, oneWayConnectedClusters, twoWayConnectedClusters, GetFollowers);
|
|
|
|
queue.Enqueue((cluster, 0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CriticalPathAnalyzerServer.LoggerInstance.Info(
|
|
|
|
logger.Info($"collected {collectedClusters.Count} main clusters");
|
|
|
|
$"collected {oneWayConnectedClusters.Count} one way linked clusters");
|
|
|
|
|
|
|
|
CriticalPathAnalyzerServer.LoggerInstance.Info(
|
|
|
|
|
|
|
|
$"collected {twoWayConnectedClusters.Count} two way linked clusters");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// collectedClusters.UnionWith(oneWayConnectedClusters);
|
|
|
|
int iters = 0;
|
|
|
|
collectedClusters.UnionWith(twoWayConnectedClusters);
|
|
|
|
while (queue.TryDequeue(out (Cluster, int) item)) {
|
|
|
|
|
|
|
|
iters++;
|
|
|
|
|
|
|
|
if (iters > 100000) {
|
|
|
|
|
|
|
|
logger.Error("Infinite iterations");
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CriticalPathAnalyzerServer.LoggerInstance.Info($"union {collectedClusters.Count} clusters");
|
|
|
|
(Cluster cluster, int time) = item;
|
|
|
|
|
|
|
|
|
|
|
|
clusterNodes.Add(new ClusterNode() {
|
|
|
|
logger.Warn(">> cluster iter <<");
|
|
|
|
Index = clusterNodes.Count,
|
|
|
|
|
|
|
|
Clusters = collectedClusters.ToList(),
|
|
|
|
|
|
|
|
Time = 0,
|
|
|
|
|
|
|
|
PrevNodeStateId = null,
|
|
|
|
|
|
|
|
NextNodes = new Dictionary<int, int>(),
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var perimeterComponents = new HashSet<ComponentAddress>();
|
|
|
|
if (clusterToNodeMapping.ContainsKey(cluster)) {
|
|
|
|
var collectedNextClusters = new HashSet<Cluster>();
|
|
|
|
logger.Info("already mapped, continuing");
|
|
|
|
foreach (Cluster cluster in collectedClusters) {
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Collect clusters that get powered by the original cluster using relays or fast buffers
|
|
|
|
|
|
|
|
// var nextClusters = new HashSet<Cluster>();
|
|
|
|
|
|
|
|
// var twoWayConnectedClusters = new HashSet<Cluster>();
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// GetLinkedClusters(cluster, nextClusters, twoWayConnectedClusters, GetFollowers);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// logger.Info($"collected {nextClusters.Count} one way linked clusters");
|
|
|
|
|
|
|
|
// logger.Info($"collected {twoWayConnectedClusters.Count} two way linked clusters");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int index = clusterNodes.Count;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// foreach (Cluster connectedCluster in twoWayConnectedClusters) {
|
|
|
|
|
|
|
|
// if (!clusterToNodeMapping.TryAdd(connectedCluster, index)) {
|
|
|
|
|
|
|
|
// // already been here
|
|
|
|
|
|
|
|
// logger.Error("Adding already mapped cluster");
|
|
|
|
|
|
|
|
// break;
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// foreach (Cluster connectedCluster in nextClusters) {
|
|
|
|
|
|
|
|
// var nextClusters = new HashSet<Cluster>();
|
|
|
|
|
|
|
|
// var twoWayConnectedClusters = new HashSet<Cluster>();
|
|
|
|
|
|
|
|
// GetLinkedClusters(connectedCluster, nextClusters, twoWayConnectedClusters, GetFollowers);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clusterToNodeMapping.Add(cluster, index);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clusterNodes.Add(new ClusterNode() {
|
|
|
|
|
|
|
|
Index = index,
|
|
|
|
|
|
|
|
Clusters = new List<Cluster>() {cluster},
|
|
|
|
|
|
|
|
Time = index,
|
|
|
|
|
|
|
|
PrevNodeStateId = null,
|
|
|
|
|
|
|
|
NextNodes = new Dictionary<int, int>(),
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// var perimeterComponents = new HashSet<ComponentAddress>();
|
|
|
|
|
|
|
|
var collectedNextClusters = new HashSet<Cluster>();
|
|
|
|
foreach (InputPeg inputPeg in cluster.ConnectedInputs) {
|
|
|
|
foreach (InputPeg inputPeg in cluster.ConnectedInputs) {
|
|
|
|
if (inputPeg.LogicComponent == null) {
|
|
|
|
if (inputPeg.LogicComponent == 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);
|
|
|
|
// perimeterComponents.Add(inputPeg.LogicComponent.Address);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (IOutputPeg outputPeg in inputPeg.LogicComponent.Outputs) {
|
|
|
|
foreach (IOutputPeg outputPeg in inputPeg.LogicComponent.Outputs) {
|
|
|
|
CollectMainClusters(outputPeg.Address, collectedNextClusters);
|
|
|
|
CollectMainClusters(outputPeg.Address, collectedNextClusters);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CriticalPathAnalyzerServer.LoggerInstance.Info("collected perimeter components");
|
|
|
|
foreach (Cluster nextCluster in collectedNextClusters) {
|
|
|
|
|
|
|
|
queue.Enqueue((nextCluster, time + 1));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.Info("collected perimeter components");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Collect information about each cluster:
|
|
|
|
// Collect information about each cluster:
|
|
|
|
response = new AnalyzePathResponse() {
|
|
|
|
response = new AnalyzePathResponse() {
|
|
|
|
RequestGuid = requestGuid,
|
|
|
|
RequestGuid = requestGuid,
|
|
|
|
Clusters = collectedClusters.Select(CollectClusterInformation).ToList(),
|
|
|
|
Clusters = clusterNodes.SelectMany(node => node.Clusters.Select(cluster => CollectClusterInformation(cluster, node.Time))).ToList(),
|
|
|
|
PerimeterComponents = perimeterComponents.ToList(),
|
|
|
|
// PerimeterComponents = perimeterComponents.ToList(),
|
|
|
|
NextClusters = collectedNextClusters.Select(CollectClusterInformation).ToList(),
|
|
|
|
// NextClusters = collectedNextClusters.Select(CollectClusterInformation).ToList(),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
CriticalPathAnalyzerServer.LoggerInstance.Info("Trace end");
|
|
|
|
logger.Info("Trace end");
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -191,6 +229,7 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
clustersToProcess.Enqueue(startingLinker);
|
|
|
|
clustersToProcess.Enqueue(startingLinker);
|
|
|
|
|
|
|
|
twoWayConnectedClusters.Add(startingPoint);
|
|
|
|
// While the starting cluster is no source, the algorithm needs to skip it when encountered.
|
|
|
|
// While the starting cluster is no source, the algorithm needs to skip it when encountered.
|
|
|
|
while (clustersToProcess.TryDequeue(out ClusterLinker linkerToCheck)) {
|
|
|
|
while (clustersToProcess.TryDequeue(out ClusterLinker linkerToCheck)) {
|
|
|
|
List<ClusterLinker> listOfLinkedLinkers = linkedLinkerGetter(linkerToCheck); // Is never null.
|
|
|
|
List<ClusterLinker> listOfLinkedLinkers = linkedLinkerGetter(linkerToCheck); // Is never null.
|
|
|
@ -199,6 +238,7 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
List<ClusterLinker> listOfBackLinkers = linkedLinkerGetter(linkedLinker); // Is never null.
|
|
|
|
List<ClusterLinker> listOfBackLinkers = linkedLinkerGetter(linkedLinker); // Is never null.
|
|
|
|
if (listOfBackLinkers.Contains(linkerToCheck)) {
|
|
|
|
if (listOfBackLinkers.Contains(linkerToCheck)) {
|
|
|
|
// bidirectional - a relay
|
|
|
|
// bidirectional - a relay
|
|
|
|
|
|
|
|
// TODO: will not find a relay connection if the relay is turned off
|
|
|
|
if (twoWayConnectedClusters.Add(clusterOfLinkedLinker)) {
|
|
|
|
if (twoWayConnectedClusters.Add(clusterOfLinkedLinker)) {
|
|
|
|
// Element was not yet present in the array, so keep looking into it!
|
|
|
|
// Element was not yet present in the array, so keep looking into it!
|
|
|
|
clustersToProcess.Enqueue(linkedLinker);
|
|
|
|
clustersToProcess.Enqueue(linkedLinker);
|
|
|
@ -214,11 +254,12 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
twoWayConnectedClusters.Remove(startingPoint);
|
|
|
|
twoWayConnectedClusters.Remove(startingPoint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static ClusterDetails CollectClusterInformation(Cluster cluster) {
|
|
|
|
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>(),
|
|
|
|
LinkingComponents = new List<ComponentAddress>(),
|
|
|
|
|
|
|
|
Color = HsvToRgb(time * 20, 1, 1),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Two lists are never null, according to how it is created and used:
|
|
|
|
// Two lists are never null, according to how it is created and used:
|
|
|
@ -247,5 +288,49 @@ namespace CriticalPathAnalyzer.Server.Tool {
|
|
|
|
|
|
|
|
|
|
|
|
return details;
|
|
|
|
return details;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static int HsvToRgb(int h, float s, float v) {
|
|
|
|
|
|
|
|
s = Math.Clamp(s, 0, 1);
|
|
|
|
|
|
|
|
v = Math.Clamp(v, 0, 1);
|
|
|
|
|
|
|
|
h = (h % 360 + 360) % 360; // Ensure hue is within 0-359 range
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float c = v * s;
|
|
|
|
|
|
|
|
float x = c * (1 - Math.Abs((h / 60.0f) % 2 - 1));
|
|
|
|
|
|
|
|
float m = v - c;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float r = 0, g = 0, b = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (h < 60) {
|
|
|
|
|
|
|
|
r = c;
|
|
|
|
|
|
|
|
g = x;
|
|
|
|
|
|
|
|
b = 0;
|
|
|
|
|
|
|
|
} else if (h < 120) {
|
|
|
|
|
|
|
|
r = x;
|
|
|
|
|
|
|
|
g = c;
|
|
|
|
|
|
|
|
b = 0;
|
|
|
|
|
|
|
|
} else if (h < 180) {
|
|
|
|
|
|
|
|
r = 0;
|
|
|
|
|
|
|
|
g = c;
|
|
|
|
|
|
|
|
b = x;
|
|
|
|
|
|
|
|
} else if (h < 240) {
|
|
|
|
|
|
|
|
r = 0;
|
|
|
|
|
|
|
|
g = x;
|
|
|
|
|
|
|
|
b = c;
|
|
|
|
|
|
|
|
} else if (h < 300) {
|
|
|
|
|
|
|
|
r = x;
|
|
|
|
|
|
|
|
g = 0;
|
|
|
|
|
|
|
|
b = c;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
r = c;
|
|
|
|
|
|
|
|
g = 0;
|
|
|
|
|
|
|
|
b = x;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int rInt = (byte) ((r + m) * 255);
|
|
|
|
|
|
|
|
int gInt = (byte) ((g + m) * 255);
|
|
|
|
|
|
|
|
int bInt = (byte) ((b + m) * 255);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (rInt << 16) | (gInt << 8) | bInt;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|