elksharp stabilization
This commit is contained in:
208
src/__Libraries/StellaOps.ElkSharp/ElkLayoutDiagnostics.cs
Normal file
208
src/__Libraries/StellaOps.ElkSharp/ElkLayoutDiagnostics.cs
Normal file
@@ -0,0 +1,208 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace StellaOps.ElkSharp;
|
||||
|
||||
internal sealed class ElkLayoutDiagnosticsCapture : IDisposable
|
||||
{
|
||||
private readonly ElkLayoutRunDiagnostics? previous;
|
||||
|
||||
internal ElkLayoutDiagnosticsCapture(ElkLayoutRunDiagnostics diagnostics, ElkLayoutRunDiagnostics? previous)
|
||||
{
|
||||
Diagnostics = diagnostics;
|
||||
this.previous = previous;
|
||||
}
|
||||
|
||||
internal ElkLayoutRunDiagnostics Diagnostics { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ElkLayoutDiagnostics.Restore(previous);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ElkLayoutRunDiagnostics
|
||||
{
|
||||
internal object SyncRoot { get; } = new();
|
||||
|
||||
public EdgeRoutingScore? InitialScore { get; set; }
|
||||
public EdgeRoutingScore? FinalScore { get; set; }
|
||||
public int BaselineBrokenShortHighwayCount { get; set; }
|
||||
public int FinalBrokenShortHighwayCount { get; set; }
|
||||
public int CompletedPasses { get; set; }
|
||||
public List<ElkEdgeRefinementAttemptDiagnostics> Attempts { get; } = [];
|
||||
public List<ElkIterativeStrategyDiagnostics> IterativeStrategies { get; } = [];
|
||||
public int SelectedStrategyIndex { get; set; } = -1;
|
||||
public EdgeRoutingScore? IterativeBaselineScore { get; set; }
|
||||
public List<ElkHighwayDiagnostics> DetectedHighways { get; } = [];
|
||||
public List<string> ProgressLog { get; } = [];
|
||||
public string? ProgressLogPath { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class ElkHighwayDiagnostics
|
||||
{
|
||||
public required string TargetNodeId { get; init; }
|
||||
public required string SharedAxis { get; init; }
|
||||
public required double SharedCoord { get; init; }
|
||||
public required string[] EdgeIds { get; init; }
|
||||
public required double MinRatio { get; init; }
|
||||
public required bool WasBroken { get; init; }
|
||||
public required string Reason { get; init; }
|
||||
}
|
||||
|
||||
internal sealed class ElkIterativeStrategyDiagnostics
|
||||
{
|
||||
public required int StrategyIndex { get; init; }
|
||||
public required string OrderingName { get; init; }
|
||||
public int Attempts { get; set; }
|
||||
public double TotalDurationMs { get; set; }
|
||||
public EdgeRoutingScore? BestScore { get; set; }
|
||||
public required string Outcome { get; init; }
|
||||
public double BendPenalty { get; init; }
|
||||
public double DiagonalPenalty { get; init; }
|
||||
public double SoftObstacleWeight { get; init; }
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
public ElkRoutedEdge[]? BestEdges { get; set; }
|
||||
|
||||
public List<ElkIterativeAttemptDiagnostics> AttemptDetails { get; } = [];
|
||||
}
|
||||
|
||||
internal sealed class ElkIterativeAttemptDiagnostics
|
||||
{
|
||||
public required int Attempt { get; init; }
|
||||
public double TotalDurationMs { get; init; }
|
||||
public required EdgeRoutingScore Score { get; init; }
|
||||
public required string Outcome { get; init; }
|
||||
public ElkIterativeRouteDiagnostics? RouteDiagnostics { get; init; }
|
||||
public List<ElkIterativePhaseDiagnostics> PhaseTimings { get; } = [];
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
public ElkRoutedEdge[]? Edges { get; init; }
|
||||
}
|
||||
|
||||
internal sealed class ElkIterativeRouteDiagnostics
|
||||
{
|
||||
public required string Mode { get; init; }
|
||||
public int TotalEdges { get; init; }
|
||||
public int RoutedEdges { get; init; }
|
||||
public int SkippedEdges { get; init; }
|
||||
public int RoutedSections { get; init; }
|
||||
public int FallbackSections { get; init; }
|
||||
public int SoftObstacleSegments { get; init; }
|
||||
public IReadOnlyCollection<string> RepairedEdgeIds { get; init; } = [];
|
||||
public IReadOnlyCollection<string> RepairReasons { get; init; } = [];
|
||||
}
|
||||
|
||||
internal sealed class ElkIterativePhaseDiagnostics
|
||||
{
|
||||
public required string Phase { get; init; }
|
||||
public double DurationMs { get; init; }
|
||||
}
|
||||
|
||||
internal sealed class ElkEdgeRefinementAttemptDiagnostics
|
||||
{
|
||||
public required int PassIndex { get; init; }
|
||||
public required string EdgeId { get; init; }
|
||||
public required int Severity { get; init; }
|
||||
public required EdgeRoutingScore BaselineScore { get; init; }
|
||||
public int AttemptCount { get; set; }
|
||||
public int? AcceptedTrialIndex { get; set; }
|
||||
public EdgeRoutingScore? AcceptedScore { get; set; }
|
||||
public List<ElkEdgeRefinementTrialDiagnostics> Trials { get; } = [];
|
||||
}
|
||||
|
||||
internal sealed class ElkEdgeRefinementTrialDiagnostics
|
||||
{
|
||||
public required int TrialIndex { get; init; }
|
||||
public required double Margin { get; init; }
|
||||
public required double BendPenalty { get; init; }
|
||||
public required double SoftObstacleWeight { get; init; }
|
||||
public required string Outcome { get; init; }
|
||||
public EdgeRoutingScore? CandidateScore { get; init; }
|
||||
public bool Accepted { get; set; }
|
||||
public IReadOnlyCollection<ElkDiagnosticSectionPath> Sections { get; init; } = [];
|
||||
}
|
||||
|
||||
internal sealed class ElkDiagnosticSectionPath
|
||||
{
|
||||
public required ElkPoint StartPoint { get; init; }
|
||||
public required IReadOnlyCollection<ElkPoint> BendPoints { get; init; }
|
||||
public required ElkPoint EndPoint { get; init; }
|
||||
}
|
||||
|
||||
internal static class ElkLayoutDiagnostics
|
||||
{
|
||||
private static readonly AsyncLocal<ElkLayoutRunDiagnostics?> CurrentDiagnostics = new();
|
||||
|
||||
internal static ElkLayoutRunDiagnostics? Current => CurrentDiagnostics.Value;
|
||||
|
||||
internal static ElkLayoutDiagnosticsCapture BeginCapture()
|
||||
{
|
||||
var previous = CurrentDiagnostics.Value;
|
||||
var diagnostics = new ElkLayoutRunDiagnostics();
|
||||
CurrentDiagnostics.Value = diagnostics;
|
||||
return new ElkLayoutDiagnosticsCapture(diagnostics, previous);
|
||||
}
|
||||
|
||||
internal static ElkLayoutDiagnosticsCapture Attach(ElkLayoutRunDiagnostics diagnostics)
|
||||
{
|
||||
var previous = CurrentDiagnostics.Value;
|
||||
CurrentDiagnostics.Value = diagnostics;
|
||||
return new ElkLayoutDiagnosticsCapture(diagnostics, previous);
|
||||
}
|
||||
|
||||
internal static void Restore(ElkLayoutRunDiagnostics? previous)
|
||||
{
|
||||
CurrentDiagnostics.Value = previous;
|
||||
}
|
||||
|
||||
internal static void LogProgress(string message)
|
||||
{
|
||||
var diagnostics = CurrentDiagnostics.Value;
|
||||
if (diagnostics is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var line = $"[{DateTime.UtcNow:O}] {message}";
|
||||
lock (diagnostics.SyncRoot)
|
||||
{
|
||||
diagnostics.ProgressLog.Add(line);
|
||||
Console.WriteLine(line);
|
||||
if (!string.IsNullOrWhiteSpace(diagnostics.ProgressLogPath))
|
||||
{
|
||||
File.AppendAllText(diagnostics.ProgressLogPath, line + Environment.NewLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void AddDetectedHighway(ElkHighwayDiagnostics diagnostic)
|
||||
{
|
||||
var diagnostics = CurrentDiagnostics.Value;
|
||||
if (diagnostics is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (diagnostics.SyncRoot)
|
||||
{
|
||||
diagnostics.DetectedHighways.Add(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void AddRefinementAttempt(ElkEdgeRefinementAttemptDiagnostics attempt)
|
||||
{
|
||||
var diagnostics = CurrentDiagnostics.Value;
|
||||
if (diagnostics is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (diagnostics.SyncRoot)
|
||||
{
|
||||
diagnostics.Attempts.Add(attempt);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user