using GalaSoft.MvvmLight.Ioc; using GalaSoft.MvvmLight.Messaging; 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.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.Views; using Tango.MachineStudio.UI.Windows; using Tango.PMR.Stubs; using Tango.Settings; using Tango.SharedUI; 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; /// /// 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; } 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 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( IMainView view, IAuthenticationProvider authenticationProvider, IStudioModuleLoader studioModuleLoader, INotificationProvider notificationProvider, IStudioApplicationManager applicationManager, INavigationManager navigationManager, IEventLogger eventLogger, IDiagnosticsFrameProvider frameProvider, ISpeechProvider speechProvider, IHtmlPresenter htmlPresenter) : base(view) { _eventLogger = eventLogger; _navigation = navigationManager; AuthenticationProvider = authenticationProvider; StudioModuleLoader = studioModuleLoader; NotificationProvider = notificationProvider; ApplicationManager = applicationManager; DiagnosticsFrameProvider = frameProvider; SpeechProvider = speechProvider; _htmlPresenter = htmlPresenter; 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); } 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(1)); 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)); SettingsManager.Default.MachineStudio.LastVirtualMachineSerialNumber = vm.SelectedMachine.SerialNumber; SettingsManager.SaveDefaultSettings(); } 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; foreach (var m in StudioModuleLoader.AllModules.Where(x => x != module)) { m.IsLoaded = false; } if (module != null) { CurrentModule = module; CurrentModule.IsLoaded = true; IsModuleLoaded = true; View.NavigateToModule(module); } else { IsModuleLoaded = false; View.NavigateToModule(null); } } /// /// Called when the is loaded and attached. /// protected override void OnViewAttached() { base.OnViewAttached(); } /// /// Opens the module in a new window. /// /// The module. private void OpenModuleInWindow(IStudioModule module) { if (module == null) return; try { StartModule(null); module.InNewWindow = true; var parent = (MainView.Self as MainView).TransitionControl.Controls.SingleOrDefault(x => x.Tag.ToString() == module.Name).Content as Grid; var view = parent.Children[0] as FrameworkElement; parent.Children.Remove(view); ModuleWindowVM vm = new ModuleWindowVM(module, parent); ModuleWindow window = new ModuleWindow(this, vm, view); window.Closing += (x, y) => { window.grid.Children.Remove(view); parent.Children.Add(view); module.InNewWindow = false; }; window.Owner = MainWindow.Instance; window.Show(); (_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."); } } } }