BuildPhysicalTopologyUseCase.cs 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. using RackPeek.Domain.Persistence;
  2. using RackPeek.Domain.Resources;
  3. using RackPeek.Domain.Resources.Connections;
  4. using RackPeek.Domain.Resources.Hardware;
  5. using RackPeek.Domain.Resources.Servers;
  6. using RackPeek.Domain.Resources.SubResources;
  7. namespace RackPeek.Domain.Graph.UseCases;
  8. public class BuildPhysicalTopologyUseCase(IResourceCollection repo) : IUseCase {
  9. public async Task<Graph> ExecuteAsync() {
  10. IReadOnlyList<Hardware> hardware = repo.HardwareResources;
  11. IReadOnlyList<Connection> connections = await repo.GetConnectionsAsync();
  12. var nodes = hardware
  13. .OrderBy(h => h.Kind, StringComparer.OrdinalIgnoreCase)
  14. .ThenBy(h => h.Name, StringComparer.OrdinalIgnoreCase)
  15. .Select(BuildNode)
  16. .ToList();
  17. var hardwareByName = hardware.ToDictionary(
  18. h => h.Name,
  19. StringComparer.OrdinalIgnoreCase);
  20. var edges = connections
  21. .Where(c => hardwareByName.ContainsKey(c.A.Resource) && hardwareByName.ContainsKey(c.B.Resource))
  22. .Select(c => BuildEdge(c, hardwareByName))
  23. .ToList();
  24. return new Graph(nodes, edges);
  25. }
  26. private static GraphNode BuildNode(Hardware resource) {
  27. var data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  28. if (resource.Tags.Length > 0) data["tags"] = string.Join(",", resource.Tags);
  29. return new GraphNode(
  30. Id: resource.Name,
  31. Label: resource.Name,
  32. Kind: resource.Kind,
  33. Subtitle: resource.Kind.ToLowerInvariant(),
  34. Data: data);
  35. }
  36. private static GraphEdge BuildEdge(Connection c, Dictionary<string, Hardware> hardwareByName) {
  37. var label = BuildEdgeLabel(c, hardwareByName);
  38. return new GraphEdge(
  39. Source: c.A.Resource,
  40. Target: c.B.Resource,
  41. Label: label,
  42. Kind: "connection");
  43. }
  44. private static string? BuildEdgeLabel(Connection c, Dictionary<string, Hardware> hardwareByName) {
  45. if (!string.IsNullOrWhiteSpace(c.Label))
  46. return c.Label;
  47. var a = PortLabel(c.A, hardwareByName);
  48. var b = PortLabel(c.B, hardwareByName);
  49. if (a is null && b is null) return null;
  50. return $"{a ?? "?"} ↔ {b ?? "?"}";
  51. }
  52. private static string? PortLabel(PortReference reference, Dictionary<string, Hardware> hardwareByName) {
  53. if (!hardwareByName.TryGetValue(reference.Resource, out Hardware? hardware))
  54. return null;
  55. if (hardware is not IPortResource portResource || portResource.Ports is null)
  56. return null;
  57. if (reference.PortGroup < 0 || reference.PortGroup >= portResource.Ports.Count)
  58. return null;
  59. Port group = portResource.Ports[reference.PortGroup];
  60. var type = string.IsNullOrWhiteSpace(group.Type) ? "port" : group.Type;
  61. return $"{type}{reference.PortIndex}";
  62. }
  63. }