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(); } }