using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Tango.BL.Entities;
using Tango.Core.Commands;
using Tango.Core.DI;
using Tango.Integration.Services;
using Tango.Logging;
using Tango.MachineStudio.Common;
using Tango.MachineStudio.Common.Authentication;
using Tango.MachineStudio.Common.Diagnostics;
using Tango.MachineStudio.Common.EventLogging;
using Tango.MachineStudio.Common.Html;
using Tango.MachineStudio.Common.Messages;
using Tango.MachineStudio.Common.Modules;
using Tango.MachineStudio.Common.Navigation;
using Tango.MachineStudio.Common.Notifications;
using Tango.MachineStudio.Common.Speech;
using Tango.MachineStudio.Common.StudioApplication;
using Tango.MachineStudio.Common.Update;
using Tango.MachineStudio.UI.StudioApplication;
using Tango.MachineStudio.UI.SupervisingController;
using Tango.MachineStudio.UI.TFS;
using Tango.MachineStudio.UI.Views;
using Tango.MachineStudio.UI.Windows;
using Tango.PMR.Stubs;
using Tango.Settings;
using Tango.SharedUI;
using Tango.SharedUI.Controls;
using Tango.SharedUI.Helpers;
using Tango.Transport.Adapters;
namespace Tango.MachineStudio.UI.ViewModels
{
///
/// Represents the Machine Studio main view, view model.
///
///
public class MainViewVM : ViewModel
{
private IStudioModule _currentModule;
private INavigationManager _navigation;
private bool _isDisconnecting;
private Thread _updateCheckThread;
private IEventLogger _eventLogger;
private IHtmlPresenter _htmlPresenter;
private MachineStudioSettings _settings;
///
/// Gets or sets the current loaded module.
///
public IStudioModule CurrentModule
{
get { return _currentModule; }
set { _currentModule = value; RaisePropertyChangedAuto(); }
}
private bool _isModuleLoaded;
///
/// Gets or sets a value indicating whether any module is loaded at the moment.
///
public bool IsModuleLoaded
{
get { return _isModuleLoaded; }
set { _isModuleLoaded = value; RaisePropertyChangedAuto(); }
}
private bool _isMenuOpened;
///
/// Gets or sets a value indicating whether the side menu is opened.
///
public bool IsMenuOpened
{
get { return _isMenuOpened; }
set { _isMenuOpened = value; RaisePropertyChangedAuto(); }
}
private bool _isMachineErrorsOpened;
///
/// Gets or sets a value indicating whether to display the machine errors.
///
public bool IsMachineErrorsOpened
{
get { return _isMachineErrorsOpened; }
set { _isMachineErrorsOpened = value; RaisePropertyChangedAuto(); }
}
///
/// Gets or sets the start module command.
///
public RelayCommand StartModuleCommand { get; set; }
///
/// Gets or sets the open module in window command.
///
public RelayCommand OpenModuleInWindowCommand { get; set; }
///
/// Gets or sets the home command.
///
public RelayCommand HomeCommand { get; set; }
///
/// Gets or sets the connect command.
///
public RelayCommand ConnectCommand { get; set; }
///
/// Gets or sets the disconnect command.
///
public RelayCommand DisconnectCommand { get; set; }
///
/// Gets or sets the sign-out command.
///
public RelayCommand SignoutCommand { get; set; }
///
/// Gets or sets the exit command.
///
public RelayCommand ExitCommand { get; set; }
///
/// Gets or sets the update center command.
///
public RelayCommand UpdateCenterCommand { get; set; }
///
/// Gets or sets the toggle speech command.
///
public RelayCommand ToggleSpeechCommand { get; set; }
///
/// Gets or sets the display HTML command.
///
public RelayCommand ResolveMachineEventCommand { get; set; }
///
/// Gets or sets the report issue command.
///
public RelayCommand ReportIssueCommand { get; set; }
///
/// Gets or sets the open resolved bugs.
///
public RelayCommand OpenResolvedBugsCommand { get; set; }
///
/// Gets or sets the open developer console command.
///
public RelayCommand OpenDeveloperConsoleCommand { get; set; }
private IAuthenticationProvider _authenticationProvider;
///
/// Gets or sets the authentication provider.
///
public IAuthenticationProvider AuthenticationProvider
{
get { return _authenticationProvider; }
set { _authenticationProvider = value; RaisePropertyChangedAuto(); }
}
private IStudioModuleLoader _studioModuleLoader;
///
/// Gets or sets the studio module loader.
///
public IStudioModuleLoader StudioModuleLoader
{
get { return _studioModuleLoader; }
set { _studioModuleLoader = value; RaisePropertyChangedAuto(); }
}
private INotificationProvider _notificationProvider;
///
/// Gets or sets the notification provider.
///
public INotificationProvider NotificationProvider
{
get { return _notificationProvider; }
set { _notificationProvider = value; RaisePropertyChangedAuto(); }
}
private IStudioApplicationManager _applicationManager;
///
/// Gets or sets the application manager.
///
public IStudioApplicationManager ApplicationManager
{
get { return _applicationManager; }
set { _applicationManager = value; RaisePropertyChangedAuto(); }
}
///
/// Gets or sets the TFS client.
///
public TeamFoundationServiceExtendedClient TFSClient { get; set; }
///
/// Gets or sets the speech provider.
///
public ISpeechProvider SpeechProvider { get; set; }
private bool _isUpdateAvailable;
///
/// Gets or sets a value indicating whether a new version update is available.
///
public bool IsUpdateAvailable
{
get { return _isUpdateAvailable; }
set { _isUpdateAvailable = value; RaisePropertyChangedAuto(); }
}
private String _latestVersion;
///
/// Gets or sets the latest version.
///
public String LatestVersion
{
get { return _latestVersion; }
set { _latestVersion = value; RaisePropertyChangedAuto(); }
}
private bool _disableCheckForUpdates;
///
/// Gets or sets a value indicating whether [disable check for updates].
///
public bool DisableCheckForUpdates
{
get { return _disableCheckForUpdates; }
set { _disableCheckForUpdates = value; RaisePropertyChangedAuto(); }
}
private IDiagnosticsFrameProvider _diagnosticsFrameProvider;
///
/// Gets or sets the diagnostics frame provider.
///
public IDiagnosticsFrameProvider DiagnosticsFrameProvider
{
get { return _diagnosticsFrameProvider; }
set { _diagnosticsFrameProvider = value; RaisePropertyChangedAuto(); }
}
///
/// Initializes a new instance of the class.
///
/// The view.
/// The authentication provider.
/// The studio module loader.
/// The notification provider.
/// The application manager.
/// The navigation manager.
public MainViewVM(
IAuthenticationProvider authenticationProvider,
IStudioModuleLoader studioModuleLoader,
INotificationProvider notificationProvider,
IStudioApplicationManager applicationManager,
INavigationManager navigationManager,
IEventLogger eventLogger,
IDiagnosticsFrameProvider frameProvider,
ISpeechProvider speechProvider,
IHtmlPresenter htmlPresenter, TeamFoundationServiceExtendedClient tfs) : base()
{
TFSClient = tfs;
_eventLogger = eventLogger;
_navigation = navigationManager;
AuthenticationProvider = authenticationProvider;
StudioModuleLoader = studioModuleLoader;
NotificationProvider = notificationProvider;
ApplicationManager = applicationManager;
DiagnosticsFrameProvider = frameProvider;
SpeechProvider = speechProvider;
_htmlPresenter = htmlPresenter;
_settings = SettingsManager.Default.GetOrCreate();
StartModuleCommand = new RelayCommand(StartModule);
HomeCommand = new RelayCommand(Home);
ConnectCommand = new RelayCommand(ConnectToMachine);
SignoutCommand = new RelayCommand(SignOut);
DisconnectCommand = new RelayCommand(DisconnectFromMachine, (x) => ApplicationManager.IsMachineConnected && !_isDisconnecting);
OpenModuleInWindowCommand = new RelayCommand(OpenModuleInWindow);
ExitCommand = new RelayCommand(ExitApplication);
UpdateCenterCommand = new RelayCommand(NavigateToUpdateCenter);
_updateCheckThread = new Thread(UpdateCheckThreadMethod);
_updateCheckThread.IsBackground = true;
_updateCheckThread.Start();
ApplicationManager.ConnectedMachineChanged += (sender, machine) =>
{
if (machine != null)
{
machine.MachineEventsStateProvider.NewEvents -= MachineEventsStateProvider_NewEvents;
machine.MachineEventsStateProvider.NewEvents += MachineEventsStateProvider_NewEvents;
machine.MachineEventsStateProvider.EventsResolved -= MachineEventsStateProvider_EventsResolved;
machine.MachineEventsStateProvider.EventsResolved += MachineEventsStateProvider_EventsResolved;
}
};
ToggleSpeechCommand = new RelayCommand(() => { SpeechProvider.Mute = !SpeechProvider.Mute; });
ResolveMachineEventCommand = new RelayCommand(ResolveMachineEvent);
ReportIssueCommand = new RelayCommand(ReportIssue);
OpenResolvedBugsCommand = new RelayCommand(OpenResolvedBugs);
OpenDeveloperConsoleCommand = new RelayCommand(OpenDeveloperConsole);
TangoMessenger.Default.Register((x) => DisableCheckForUpdates = true);
}
private void MachineEventsStateProvider_EventsResolved(object sender, IEnumerable e)
{
if (ApplicationManager.ConnectedMachine.MachineEventsStateProvider.Events.Count == 0)
{
IsMachineErrorsOpened = false;
}
}
private void MachineEventsStateProvider_NewEvents(object sender, IEnumerable e)
{
IsMachineErrorsOpened = true;
}
private void UpdateCheckThreadMethod()
{
while (!DisableCheckForUpdates)
{
Thread.Sleep(TimeSpan.FromMinutes(0.2));
try
{
if (_authenticationProvider.CurrentUser != null)
{
var service = UpdateServiceHelper.GetUpdateServiceChannel();
var client = service.CreateChannel();
CheckForUpdatesResponse response = client.CheckForUpdates(new CheckForUpdatesRequest()
{
Email = _authenticationProvider.CurrentUser.Email,
Password = _authenticationProvider.CurrentUser.Password,
Version = _applicationManager.Version,
});
IsUpdateAvailable = response.IsUpdateAvailable;
LatestVersion = response.Version;
}
}
catch (Exception ex)
{
LogManager.Log(ex, "Error in version update periodic check...");
}
Thread.Sleep(TimeSpan.FromMinutes(4));
}
}
///
/// Disconnected from the current connected machine.
///
private async void DisconnectFromMachine()
{
using (_notificationProvider.PushTaskItem("Disconnecting from machine..."))
{
try
{
_isDisconnecting = true;
InvalidateRelayCommands();
String serial = ApplicationManager.ConnectedMachine.SerialNumber;
await ApplicationManager.ConnectedMachine.Disconnect();
ApplicationManager.ConnectedMachine = null;
_eventLogger.Log("Disconnected from machine " + serial);
PostMessage(new MachineConnectionChangedMessage() { Machine = null });
}
catch (Exception ex)
{
_eventLogger.Log(ex, "Error disconnecting from machine.");
LogManager.Log(ex, "Could not disconnect from machine.");
}
finally
{
_isDisconnecting = false;
InvalidateRelayCommands();
}
}
}
///
/// Signs-out the current logged-in user.
///
private void SignOut()
{
_authenticationProvider.Logout();
_navigation.NavigateTo(NavigationView.LoginView);
CurrentModule = null;
IsMenuOpened = false;
}
///
/// Opens the machine connection dialog to allow the user to scan and connect to remote machines view USB/TCP.
///
private void ConnectToMachine()
{
if (ApplicationManager.ConnectedMachine == null)
{
_notificationProvider.ShowModalDialog(async (x) =>
{
if (x.SelectedMachine != null)
{
if (ApplicationManager.IsMachineConnected)
{
using (_notificationProvider.PushTaskItem("Disconnecting..."))
{
await ApplicationManager.ConnectedMachine.Disconnect();
await Task.Delay(1000);
}
}
if (x.SelectedMachine.RequiresAuthentication)
{
_notificationProvider.ShowModalDialog(async (login) =>
{
using (NotificationProvider.PushTaskItem("Connecting to machine " + x.SelectedMachine.ToString() + "..."))
{
try
{
await x.SelectedMachine.Connect();
var authenticated = await x.SelectedMachine.As().Authenticate(login.Password);
if (!authenticated)
{
_notificationProvider.ShowError("It seems like you are not authorized to access the selected machine.");
}
else
{
ApplicationManager.ConnectedMachine = x.SelectedMachine;
PostMessage(new MachineConnectionChangedMessage() { Machine = x.SelectedMachine });
_eventLogger.Log(String.Format("Successfully connected to machine {0} via TCP", x.SelectedMachine.SerialNumber));
}
}
catch (Exception ex)
{
LogManager.Log(ex);
_eventLogger.Log(ex, "Error connecting to machine " + x.SelectedMachine.SerialNumber);
_notificationProvider.ShowError("Could not connect to the selected machine." + Environment.NewLine + ex.Message);
}
InvalidateRelayCommands();
}
});
}
else
{
_notificationProvider.ShowModalDialog(async (vm) =>
{
if (vm.SelectedMachine != null)
{
using (NotificationProvider.PushTaskItem("Connecting to " + x.SelectedMachine.ToString() + "..."))
{
try
{
await x.SelectedMachine.Connect();
x.SelectedMachine.SerialNumber = vm.SelectedMachine.SerialNumber;
ApplicationManager.ConnectedMachine = x.SelectedMachine;
PostMessage(new MachineConnectionChangedMessage() { Machine = x.SelectedMachine });
_eventLogger.Log(String.Format("Successfully connected to machine {0} via USB", x.SelectedMachine.SerialNumber));
_settings.LastVirtualMachineSerialNumber = vm.SelectedMachine.SerialNumber;
_settings.Save();
}
catch (Exception ex)
{
LogManager.Log(ex);
_eventLogger.Log(ex, "Error connecting to machine " + x.SelectedMachine.SerialNumber);
_notificationProvider.ShowError("Could not connect to the selected machine." + Environment.NewLine + ex.Message);
}
InvalidateRelayCommands();
}
}
});
}
InvalidateRelayCommands();
}
InvalidateRelayCommands();
});
}
else
{
_notificationProvider.ShowModalDialog((x) =>
{
DisconnectFromMachine();
});
}
InvalidateRelayCommands();
}
///
/// Navigates to the home screen.
///
private void Home()
{
StartModule(null);
}
///
/// Starts the specified module.
///
/// The module.
internal void StartModule(IStudioModule module)
{
IsMenuOpened = false;
if (module != null)
{
LogManager.Log(String.Format("Starting module '{0}'...", module.Name));
if (!(MainView.Self as MainView).TransitionControl.Elements.ToList().Exists(x => x.GetType() == module.MainViewType))
{
LogManager.Log("Module was not initialized. Initializing...");
FrameworkElement view = Activator.CreateInstance(module.MainViewType) as FrameworkElement;
NavigationControl.SetNavigationName(view, module.Name);
(MainView.Self as MainView).TransitionControl.Elements.Add(view);
}
}
foreach (var m in StudioModuleLoader.AllModules.Where(x => x != module && !x.InNewWindow))
{
m.IsLoaded = false;
TangoIOC.Default.GetModuleViewModels(m).ToList().ForEach(x => x.OnNavigatedFrom());
}
if (module != null)
{
CurrentModule = module;
CurrentModule.IsLoaded = true;
IsModuleLoaded = true;
LogManager.Log(String.Format("Navigating to module '{0}'...", module.Name));
(MainView.Self as MainView).TransitionControl.NavigateTo(module.Name);
TangoIOC.Default.GetModuleViewModels(module).ToList().ForEach(x => x.OnNavigatedTo());
}
else
{
IsModuleLoaded = false;
LogManager.Log(String.Format("Navigating to Home..."));
(MainView.Self as MainView).TransitionControl.NavigateTo("Home");
}
}
///
/// Opens the module in a new window.
///
/// The module.
private void OpenModuleInWindow(IStudioModule module)
{
if (module == null) return;
try
{
module.InNewWindow = true;
StartModule(null);
LogManager.Log(String.Format("Starting module '{0}' in new window...", module.Name));
if (!(MainView.Self as MainView).TransitionControl.Elements.ToList().Exists(x => x.GetType() == module.MainViewType))
{
LogManager.Log("Module was not initialized. Initializing...");
FrameworkElement v = Activator.CreateInstance(module.MainViewType) as FrameworkElement;
NavigationControl.SetNavigationName(v, module.Name);
(MainView.Self as MainView).TransitionControl.Elements.Add(v);
}
LogManager.Log("Detaching module view...");
var view = (MainView.Self as MainView).TransitionControl.GetAndDetach(module.Name);
ModuleWindowVM vm = new ModuleWindowVM(module);
ModuleWindow window = new ModuleWindow(this, vm, view);
window.Closing += (x, y) =>
{
LogManager.Log(String.Format("Closing module '{0}' on new window...", module.Name));
window.grid.Children.Remove(view);
module.InNewWindow = false;
TangoIOC.Default.GetModuleViewModels(module).ToList().ForEach(v => v.OnNavigatedFrom());
};
window.Owner = MainWindow.Instance;
LogManager.Log("Opening new window...");
window.Show();
TangoIOC.Default.GetModuleViewModels(module).ToList().ForEach(x => x.OnNavigatedTo());
(_applicationManager as DefaultStudioApplicationManager).RegisterOpenedWindow(window);
}
catch (Exception ex)
{
LogManager.Log(ex, "Error popping out module " + module.Name);
_notificationProvider.ShowError("Error popping out module " + module.Name);
}
}
///
/// Navigates to update center.
///
private void NavigateToUpdateCenter()
{
_navigation.NavigateTo(NavigationView.UpdateView);
}
///
/// Exits the application.
///
private void ExitApplication()
{
_applicationManager.ShutDown();
}
///
/// Displays the HTML.
///
/// The HTML page.
private async void ResolveMachineEvent(MachinesEvent machineEvent)
{
if (machineEvent.EventType.HtmlPage != null)
{
if (_htmlPresenter.DisplayHtml(machineEvent.EventType.HtmlPage))
{
if (ApplicationManager.ConnectedMachine != null && machineEvent.EventType.Resolvable)
{
_eventLogger.Log(String.Format("Event '{0}' resolved by user.", machineEvent.EventType.Name));
await ApplicationManager.ConnectedMachine.ResolveEvent((PMR.Diagnostics.EventType)machineEvent.Type);
}
}
}
else
{
_notificationProvider.ShowWarning("Could not locate guidance content for the specified event.");
}
}
private void ReportIssue()
{
_notificationProvider.ShowModalDialog(new ReportIssueViewVM(TFSClient.Project, TFSClient.CreateBug()), async (vm) =>
{
using (_notificationProvider.PushTaskItem("Uploading bug report..."))
{
try
{
TFSClient.FinalizeBug(vm.WorkItem);
await TFSClient.UploadWorkItem(vm.WorkItem);
}
catch (Exception ex)
{
_notificationProvider.ShowError("An error occurred while trying to create the issue." + Environment.NewLine + ex.Message);
}
}
}, null);
}
private void OpenResolvedBugs()
{
ResolvedIssuesViewVM vm = null;
vm = new ResolvedIssuesViewVM(TFSClient, async (item) =>
{
//Approve
using (_notificationProvider.PushTaskItem("Approving issue..."))
{
vm.IsAvailable = false;
try
{
await TFSClient.CloseWorkItem(item);
}
catch (Exception ex)
{
_notificationProvider.ShowError("An error occurred while trying to update the issue." + Environment.NewLine + ex.Message);
}
vm.IsAvailable = true;
if (TFSClient.ResolvedWorkItems.Count == 0)
{
vm.Close();
}
}
}, async (item) =>
{
//Decline
using (_notificationProvider.PushTaskItem("Reactivating issue..."))
{
vm.IsAvailable = false;
try
{
await TFSClient.ReactivateWorkItem(item);
}
catch (Exception ex)
{
_notificationProvider.ShowError("An error occurred while trying to update the issue." + Environment.NewLine + ex.Message);
}
vm.IsAvailable = true;
if (TFSClient.ResolvedWorkItems.Count == 0)
{
vm.Close();
}
}
});
_notificationProvider.ShowModalDialog(vm, (_) => { }, null);
}
private void OpenDeveloperConsole()
{
Console.ConsoleWindow console = new Console.ConsoleWindow();
ApplicationManager.RegisterOpenedWindow(console);
console.Owner = MainWindow.Instance;
console.Show();
}
}
}