Repository URL to install this package:
Version:
1.3.1 ▾
|
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Fluctio.FluctioSim.EditorCore.Training.PythonWrappers;
using Fluctio.FluctioSim.EditorUtils.EditorGeneral;
using Fluctio.FluctioSim.EditorUtils.OperatingSystem;
using Fluctio.FluctioSim.Utils.Extensions;
using Fluctio.FluctioSim.Utils.General;
using JetBrains.Annotations;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace Fluctio.FluctioSim.EditorCore.Training.TensorboardApi
{
public static class Tensorboard
{
private static string GetSessionKey(string property)
{
return $"{typeof(Tensorboard).FullName}.{property}";
}
#region Running/stopping
private static readonly SessionProcess SessionProcess = new(typeof(Tensorboard));
[CanBeNull]
private static Process Process
{
get => SessionProcess.Process;
set => SessionProcess.Process = value;
}
public static bool IsStarting
{
get => SessionState.GetBool(GetSessionKey(nameof(IsStarting)), false);
set => SessionState.SetBool(GetSessionKey(nameof(IsStarting)), value);
}
public static bool IsRunning
{
get => SessionState.GetBool(GetSessionKey(nameof(IsRunning)), false);
set => SessionState.SetBool(GetSessionKey(nameof(IsRunning)), value);
}
private static void CheckRunning()
{
if (!IsRunning)
{
throw new InvalidOperationException("Tensorboard is not running");
}
}
[InitializeOnLoadMethod]
private static void Initialize()
{
EditorApplication.quitting += async () =>
{
await Stop();
};
if (EditorUtil.DidEditorJustOpen)
{
Start();
}
}
public static void Start()
{
if (IsStarting || IsRunning)
{
return;
}
IsStarting = true;
var windowTitle = $"{Application.productName} TensorBoard";
Process = PythonVenv.RunInBackground($"tensorboard --logdir {ProcessUtil.InQuotes(MlAgentsLearn.ResultsFolder)} --window_title {ProcessUtil.InQuotes(windowTitle)}", OnOutput);
Process!.Exited += delegate
{
EditorUpdateEvents.MainThread += delegate
{
Process = null;
Url = null;
IsStarting = false;
IsRunning = false;
};
};
}
public static async Task Stop()
{
if (!IsRunning && !IsStarting)
{
return;
}
Debug.Log("Stopping TensorBoard...");
await Process!.KillTree();
}
#endregion
#region Opening Url
public static readonly Regex LaunchLineRegex = new(@"^TensorBoard [\d\.]+ at (?<url>http.*?) \(Press CTRL\+C to quit\)$");
[CanBeNull]
private static string Url
{
get
{
var url = SessionState.GetString(GetSessionKey(nameof(Url)), "");
return url != "" ? url: null;
}
set
{
HttpClient.BaseAddress = value != null ? new Uri(value) : null;
SessionState.SetString(GetSessionKey(nameof(Url)), value ?? "");
}
}
private static void OnOutput(string line, ProcessStreamType streamType)
{
if (line.Contains("error"))
{
Debug.LogError(line);
}
var urlGroup = LaunchLineRegex.Match(line).Groups["url"];
if (!urlGroup.Success)
{
return;
}
EditorUpdateEvents.MainThread += () =>
{
Url = urlGroup.Value;
IsRunning = true;
IsStarting = false;
};
}
public static void OpenURL()
{
if (IsStarting)
{
Debug.Log("TensorBoard is still initializing, please wait...");
return;
}
CheckRunning();
Application.OpenURL(Url);
}
#endregion
#region HTTP API
private static readonly HttpClient HttpClient = new()
{
BaseAddress = Url != null ? new Uri(Url) : null
};
//TODO: add caching?
public static async Task<List<ScalarEvent>> GetScalarData(string runId, string behaviorName, string tag)
{
CheckRunning();
using var profilerScope = new ProfilerScope("Tensorboard.GetScalarData");
var queryParams = new Dictionary<string, string>
{
["run"] = $@"{runId}\{behaviorName}",
["tag"] = tag,
};
var endpoint = new UriBuilder
{
Scheme = "",
Host = "",
Path = "data/plugin/scalars/scalars",
Query = queryParams.ToQueryString(),
};
return await HttpClient.MakeApiCallJson<List<ScalarEvent>>(HttpMethod.Get, endpoint.ToString());
}
public static async Task<List<ScalarEvent>> GetCumulativeReward(string runId, string behaviorName)
{
return await GetScalarData(runId, behaviorName, "Environment/Cumulative Reward");
}
#endregion
}
}