Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
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
		
	}
	
}