using C4IT.FASD.Cockpit.Communication; using F4SD.Gamification.Services; using FasdCockpitBase.Models.RemoteDesktopConnection; using FasdCockpitCommunication.RemoteDesktopCommunication; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using static C4IT.Logging.cLogManager; namespace FasdDesktopUi.Basics.Services.RemoteDesktop { internal static class AgentRemoteDesktopService { private static readonly Dictionary _viewerProcesses = new Dictionary(); internal static async Task StartRemoteDesktopConnectionAsync(AgentRemoteClientInfo clientInfo, bool isElevated, CancellationToken token = default) { const int maxRetryCount = 20; TimeSpan baseDelay = TimeSpan.FromSeconds(0.5); AgentRemoteDesktopConnecitonDetails connectionDetails = await cFasdCockpitCommunicationBase.Instance.RemoteDesktopManager.InitiateConnection(clientInfo, isElevated, token); RemoteConnetionStatusChanged?.Invoke(null, RemoteDesktopConnectionStatus.Initiated); if (token.IsCancellationRequested) { RemoteConnetionStatusChanged?.Invoke(null, RemoteDesktopConnectionStatus.Canceled); return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Canceled); } if (connectionDetails is null) { LogEntry("Could not initiate remote connection.", C4IT.Logging.LogLevels.Warning); return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Unknown); } if (connectionDetails.Errors != null && connectionDetails.Errors.Count > 0) { RemoteConnetionStatusChanged?.Invoke(null, RemoteDesktopConnectionStatus.Error); return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Error) { Errors = connectionDetails.Errors.Select(e => e.Message).ToArray() }; } RemoteDesktopConnectionStatusResult connectionStatus = null; for (int i = 0; i < maxRetryCount; i++) { try { connectionStatus = await cFasdCockpitCommunicationBase.Instance.RemoteDesktopManager.GetConnectionStatus(connectionDetails.ConnectionId, token); LogEntry($"Update {i} RemoteDesktop connection status: {connectionStatus}", C4IT.Logging.LogLevels.Debug); if (token.IsCancellationRequested || IsRemoteConnectionEstablished(connectionStatus.Status)) break; await Task.Delay(TimeSpan.FromTicks(baseDelay.Ticks * i)); } catch (Exception ex) { LogException(ex); } } if (token.IsCancellationRequested) { RemoteConnetionStatusChanged?.Invoke(null, RemoteDesktopConnectionStatus.Canceled); return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Canceled); } RemoteConnetionStatusChanged?.Invoke(null, connectionStatus.Status); if (connectionStatus.Status != RemoteDesktopConnectionStatus.Accepted) { LogEntry($"Could not connect to remote desktop. Status: {connectionStatus}", C4IT.Logging.LogLevels.Warning); return new RemoteDesktopConnectionStatusResult(connectionStatus.Status); } StartViewer(connectionDetails); GamificationService.TrackAction(F4SD.Gamification.CockpitAction.StartRemoteConnection); return new RemoteDesktopConnectionStatusResult(RemoteDesktopConnectionStatus.Connected); } private static bool IsRemoteConnectionEstablished(RemoteDesktopConnectionStatus status) { switch (status) { case RemoteDesktopConnectionStatus.Accepted: case RemoteDesktopConnectionStatus.Connected: case RemoteDesktopConnectionStatus.Finished: case RemoteDesktopConnectionStatus.Canceled: return true; case RemoteDesktopConnectionStatus.Unknown: case RemoteDesktopConnectionStatus.Initiated: default: return false; } } private static void StartViewer(AgentRemoteDesktopConnecitonDetails connectionDetails) { try { ProcessStartInfo info = new ProcessStartInfo(GetViewerPath()) { Arguments = $"-connectionId {connectionDetails.ConnectionId} -phoenixServiceUrl {connectionDetails.PhoenixServiceUrl} -secret {connectionDetails.Secret}" }; Process process = Process.Start(info); _viewerProcesses.Add(connectionDetails.ConnectionId, process); RemoteConnetionStatusChanged?.Invoke(null, RemoteDesktopConnectionStatus.Connected); } catch (Exception ex) { LogException(ex); } } internal static async Task StopRemoteDesktopConnection(Guid connectionId, CancellationToken token = default) { await cFasdCockpitCommunicationBase.Instance.RemoteDesktopManager.StopConnection(connectionId, token); if (_viewerProcesses.TryGetValue(connectionId, out var process)) process.Close(); } internal static string GetViewerPath() { return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Phoenix\Phoenix.Viewer.exe").ToString(); } internal static EventHandler RemoteConnetionStatusChanged; } }