feat: serve planning snapshots on loopback

This commit is contained in:
梁薄云
2026-08-06 09:38:12 +08:00
parent 106201e87b
commit fa1a2666d3
6 changed files with 955 additions and 0 deletions
@@ -0,0 +1,119 @@
using System;
using System.Security.Cryptography;
using System.Threading;
namespace TrajectoryPlanningVisualization;
public sealed class PlanningVisualizationSessionInfo
{
public PlanningVisualizationSessionInfo(Uri uri, string token)
{
Uri = uri ?? throw new ArgumentNullException(nameof(uri));
Token = token ?? throw new ArgumentNullException(nameof(token));
}
public Uri Uri { get; }
public string Token { get; }
}
public sealed class PlanningVisualizationSession : IDisposable
{
private readonly object lifecycleLock = new object();
private readonly PlanningVisualizationOptions options;
private LoopbackVisualizationServer server;
private PlanningVisualizationSessionInfo sessionInfo;
public PlanningVisualizationSession(PlanningVisualizationOptions options)
{
this.options = options ?? throw new ArgumentNullException(nameof(options));
}
public bool IsRunning
{
get
{
lock (lifecycleLock)
{
return server != null && server.IsRunning;
}
}
}
public PlanningVisualizationSessionInfo Start(PlanningVisualizationStaticSnapshot staticSnapshot)
{
if (staticSnapshot == null)
{
throw new ArgumentNullException(nameof(staticSnapshot));
}
lock (lifecycleLock)
{
if (server != null)
{
return sessionInfo;
}
PlanningVisualizationOptionsSnapshot validated = options.CreateValidatedSnapshot();
string token = CreateToken();
var created = new LoopbackVisualizationServer(validated, staticSnapshot, token);
try
{
created.Start();
server = created;
sessionInfo = new PlanningVisualizationSessionInfo(
new Uri("http://127.0.0.1:" + created.Port + "/?token=" + token), token);
return sessionInfo;
}
catch
{
created.Dispose();
throw;
}
}
}
public void Publish(PlanningVisualizationDynamicSnapshot snapshot)
{
if (snapshot == null)
{
throw new ArgumentNullException(nameof(snapshot));
}
try
{
Volatile.Read(ref server)?.Publish(snapshot);
}
catch
{
}
}
public void Stop()
{
LoopbackVisualizationServer current;
lock (lifecycleLock)
{
current = server;
server = null;
sessionInfo = null;
}
current?.Dispose();
}
public void Dispose()
{
Stop();
}
private static string CreateToken()
{
byte[] tokenBytes = new byte[32];
using (RandomNumberGenerator random = RandomNumberGenerator.Create())
{
random.GetBytes(tokenBytes);
}
return BitConverter.ToString(tokenBytes).Replace("-", string.Empty).ToLowerInvariant();
}
}