using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Tango.BL; using Tango.BL.Entities; using Tango.Integration.Operation; using Tango.Settings; using Tango.PPC.Common.Application; using Tango.Core.DI; using Tango.PPC.Common; using System.Windows; using Tango.Core; using System.IO; using Tango.Core.Helpers; using Tango.PPC.Common.Modules; using System.Windows.Threading; using Tango.Transport.Adapters; using Tango.PMR.Connection; using Tango.PPC.Common.Connection; using Tango.SQLExaminer; using System.Data.SqlClient; using Tango.BL.Builders; using Tango.PPC.Common.Threading; using System.Diagnostics; using Tango.PPC.Common.EventLogging; using Tango.BL.Enumerations; using Tango.PPC.Common.Notifications; using Tango.PPC.Common.WatchDog; using Tango.PPC.UI.Dialogs; using Tango.Core.Threading; using Tango.PPC.Common.Messages; using Tango.Core.ExtensionMethods; using Tango.PPC.Common.Navigation; using Tango.PPC.Common.Synchronization; using Tango.Insights; using System.Threading; using System.Reflection; using Tango.PPC.UI.ViewsContracts; namespace Tango.PPC.UI.PPCApplication { /// /// Represents the default PPC application manager. /// /// /// public class DefaultPPCApplicationManager : ExtendedObject, IPPCApplicationManager { private List _notifiedViewModels; private IMachineProvider _machineProvider; private Machine _machine; private IDispatcherProvider _dispatcher; private IEventLogger _eventLogger; private IPPCModuleLoader _moduleLoader; private INotificationProvider _notificationProvider; private IMachineDataSynchronizer _machineDataSynchronizer; private WatchDogServer _watchdogServer; private ObservablesContext _machineContext; private ActionTimer _screenLockTimer; /// /// Occurs when a system restart is required. /// public event EventHandler SystemRestartRequired; /// /// Occurs when the updater utility has failed to perform the last update. /// public event EventHandler UpdaterFailed; /// /// Occurs when the application has started. /// public event EventHandler ApplicationStarted; /// /// Occurs when all PPC modules are ready and initialized. /// public event EventHandler ModulesInitialized; /// /// Occurs when the application is ready and all modules are views are loaded. /// public event EventHandler ApplicationReady; /// /// Occurs when machine setup is required. /// public event EventHandler SetupRequired; /// /// Occurs when the main window content has been rendered. /// public event EventHandler ContentRendered; /// /// Occurs when the application has encountered an error when initializing. /// public event EventHandler ApplicationInitializationError; /// /// Gets a value indicating whether the application is shutting down. /// public bool IsShuttingDown { get; private set; } private bool _isInTechnicianMode; /// /// Gets a value indicating whether the application is in technician mode. /// public bool IsInTechnicianMode { get { return _isInTechnicianMode; } set { _isInTechnicianMode = value; RaisePropertyChangedAuto(); } } /// /// Gets the application version. /// public Version Version { get { return AssemblyHelper.GetCurrentAssemblyVersion(); } } /// /// Gets the application build date. /// public String BuildDate { get { return AssemblyHelper.GetCurrentAssemblyBuildDate().ToShortDateString(); } } /// /// Gets the version tag. /// public String VersionTag { get { var str = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).Comments; return str.IsNotNullOrEmpty() ? str : null; } } /// /// Gets the version and tag display. /// public String VersionAndTag { get { return Version.ToString(3) + $"{(VersionTag.IsNotNullOrEmpty() ? $" [{VersionTag}]" : String.Empty)}"; } } /// /// Gets the application startup date. /// public DateTime StartUpDate { get; private set; } /// /// Gets a value indicating whether an update has occurred before the application started. /// public bool IsAfterUpdate { get; private set; } /// /// Gets a value indicating whether the updater utility has failed to perform the last update. /// public bool IsUpdateFailed { get; private set; } private bool _isScreenLocked; /// /// Gets or sets a value indicating whether the screen is currently locked. /// public bool IsScreenLocked { get { return _isScreenLocked; } set { _isScreenLocked = value; RaisePropertyChangedAuto(); } } /// /// Gets the firmware version. /// public Version FirmwareVersion { get { return Version.Parse(SettingsManager.Default.GetOrCreate().FirmwareVersion); } } /// /// Gets or sets the application folder. /// public String StartPath { get; private set; } /// /// Initializes a new instance of the class. /// public DefaultPPCApplicationManager(IMachineProvider machineProvider, IDispatcherProvider dispatcherProvider, IEventLogger eventLogger, IPPCModuleLoader moduleLoader, INotificationProvider notificationProvider, IMachineDataSynchronizer machineDataSynchronizer) { StartPath = AssemblyHelper.GetCurrentAssemblyFolder(); _notificationProvider = notificationProvider; _machineProvider = machineProvider; _dispatcher = dispatcherProvider; _eventLogger = eventLogger; _moduleLoader = moduleLoader; _machineDataSynchronizer = machineDataSynchronizer; if (!DesignMode) { _notifiedViewModels = new List(); MainWindow.Instance.ContentRendered += (_, __) => { OnMainWindowContentRendered(); }; } } /// /// Called when the main window content has been rendered /// private void OnMainWindowContentRendered() { LogManager.Log("Main window content rendered."); ContentRendered?.Invoke(this, new EventArgs()); StartApplication(); } private async void StartApplication() { PPCSettings settings = null; bool initialized = false; bool isAfterSetup = false; StartUpDate = DateTime.Now; await Task.Factory.StartNew(() => { try { LogManager.Log("Reading PPC settings..."); settings = SettingsManager.Default.GetOrCreate(); LogManager.Log(settings.ToJsonString()); //Start watchdog _watchdogServer = new WatchDogServer(Application.Current.Dispatcher); #if (!DEBUG && !Eureka) if (settings.EnableWatchDog) { _watchdogServer.Start(); } #endif LogManager.Log("Reading Core settings..."); var coreSettings = SettingsManager.Default.GetOrCreate(); if (!SettingsManager.Default.IsFileExists()) { LogManager.Log("Settings file does not exists. creating..."); settings.Save(); } if (App.StartupArgs.Contains("-update_ok")) { LogManager.Log("Application started with '-update_ok' startup arguments. The application has been successfully updated."); if (settings.ApplicationState == ApplicationStates.PreSetup || settings.ApplicationState == ApplicationStates.FactoryRestore) { isAfterSetup = true; LogManager.Log("System restart is required."); } else { IsAfterUpdate = true; } settings.ApplicationState = ApplicationStates.Ready; settings.Save(); if (isAfterSetup) { SystemRestartRequired?.Invoke(this, new EventArgs()); return; } } if (App.StartupArgs.Contains("-update_failed")) { LogManager.Log("Application started with '-update_failed' startup arguments. The updater utility has failed."); IsUpdateFailed = true; settings.ApplicationState = ApplicationStates.Ready; settings.Save(); UpdaterFailed?.Invoke(this, new EventArgs()); return; } if (settings.ApplicationState == ApplicationStates.Ready) { LogManager.Log("Initializing ObservablesStaticCollections..."); ObservablesStaticCollections.Instance.Initialize(); LogManager.Log("Loading machine from database..."); _machineContext = ObservablesContext.CreateDefault(); _machine = new MachineBuilder(_machineContext).SetFirst().WithVersion().WithOrganization().WithConfiguration().WithSpools().WithCats().Build(); } initialized = true; } catch (Exception ex) { LogManager.Log(ex, "Application Initialization Error!"); ApplicationInitializationError?.Invoke(this, ex); return; } }); if (initialized) { try { if (settings.ApplicationState == ApplicationStates.PreSetup || settings.ApplicationState == ApplicationStates.FactoryRestore) { LogManager.Log($"The application is in {settings.ApplicationState} state. database initialization skipped. Invoking setup required event!"); SetupRequired?.Invoke(this, new EventArgs()); } else { PostDbInitialize(); } } catch (Exception ex) { LogManager.Log(ex, "Application Post Initialization Error!"); ApplicationInitializationError?.Invoke(this, ex); return; } } } /// /// Called when the database has been initialized /// private void PostDbInitialize() { LogManager.Log($"Raising {nameof(ApplicationStarted)} event..."); ApplicationStarted?.Invoke(this, new EventArgs()); LogManager.Log("Invoking PPC view models OnApplicationStarted methods..."); foreach (var vm in TangoIOC.Default.GetAllInstancesByBase()) { if (!_notifiedViewModels.Contains(vm)) { LogManager.Log($"Invoking {vm.GetType().Name}.OnApplicationStarted..."); vm.OnApplicationStarted(); _notifiedViewModels.Add(vm); } } var internalModules = this.GetType().Assembly.GetTypes().Where(xx => typeof(PPCModuleBase).IsAssignableFrom(xx)).ToList(); LogManager.Log("Waiting for IPPCModuleLoader instance injection..."); TangoIOC.Default.GetInstanceWhenAvailable((loader) => { LogManager.Log("Module loader instance has been registered. Registering for the ModulesLoaded event..."); ILayoutView layoutView = TangoIOC.Default.GetInstance(); loader.ModulesLoaded += (x, y) => { LogManager.Log("Loading modules views"); _dispatcher.InvokeBlock(() => { foreach (var module in TangoIOC.Default.GetInstance().UserModules) { if (!layoutView.GetNavigationControl().Elements.ToList().Exists(m => m.GetType() == module.MainViewType)) { try { LogManager.Log("Loading module view " + module.Name + "..."); FrameworkElement view = Activator.CreateInstance(module.MainViewType) as FrameworkElement; SharedUI.Controls.NavigationControl.SetNavigationName(view, module.Name); layoutView.GetNavigationControl().Elements.Add(view); } catch (Exception ex) { LogManager.Log(ex, $"Error loading module view for module {module.Name}."); } } } //Adding internal modules. LogManager.Log("Loading internal modules..."); foreach (var type in internalModules) { var module = Activator.CreateInstance(type) as IPPCModule; LogManager.Log("Loading module view " + module.Name + "..."); FrameworkElement view = Activator.CreateInstance(module.MainViewType) as FrameworkElement; SharedUI.Controls.NavigationControl.SetNavigationName(view, module.Name); layoutView.GetNavigationControl().Elements.Add(view); _moduleLoader.AllModules.Add(module); _moduleLoader.UserModules.Add(module); } }); LogManager.Log($"{loader.UserModules.Count} modules loaded."); LogManager.Log($"Invoking {nameof(ModulesInitialized)} event."); ModulesInitialized?.Invoke(this, new EventArgs()); FinalizeModuleInitialization(); }; }); } /// /// Finalizes the module initialization. /// private void FinalizeModuleInitialization() { var settings = SettingsManager.Default.GetOrCreate(); LogManager.Log("Finalizing application initialization..."); LogManager.Log("Initializing Machine Provider..."); _machineProvider.Init(_machine, _machineContext); LogManager.Log("Starting Machine Data Synchronizer..."); _machineDataSynchronizer.IsEnabled = true; LogManager.Log("Applications initialization completed!"); LogManager.Log("Checking for un-notified PPC view models..."); foreach (var vm in TangoIOC.Default.GetAllInstancesByBase()) { if (!_notifiedViewModels.Contains(vm)) { LogManager.Log($"Invoking {vm.GetType().Name}.OnApplicationStarted..."); vm.OnApplicationStarted(); _notifiedViewModels.Add(vm); } } _dispatcher.Invoke(() => { LogManager.Log($"Invoking {nameof(ApplicationReady)} event."); _eventLogger.Log(EventTypes.APPLICATION_STARTED, "Application Started!"); ApplicationReady?.Invoke(this, new EventArgs()); LogManager.Log("Notifying view models about application ready..."); foreach (var vm in TangoIOC.Default.GetAllInstancesByBase()) { LogManager.Log($"Invoking {vm.GetType().Name}.OnApplicationReady..."); vm.OnApplicationReady(); } if (settings.EnableTechnicianModeByDefault) { EnterTechnicianMode(false); } if (settings.EnableLockScreen) { _screenLockTimer = new ActionTimer(settings.LockScreenTimeout); _screenLockTimer.ResetReplace(ScreenLockTimerAction); } TangoMessenger.Default.Register((msg) => { if (_screenLockTimer != null) { _screenLockTimer.Dispose(); _screenLockTimer = null; } if (settings.EnableLockScreen) { _screenLockTimer = new ActionTimer(settings.LockScreenTimeout); _screenLockTimer.ResetReplace(ScreenLockTimerAction); } }); }); } /// /// Shutdown the application. /// public async void ShutDown() { if (IsShuttingDown) return; IsShuttingDown = true; try { LogManager.Log("Shutting down application..."); _watchdogServer.Dispose(); foreach (var vm in TangoIOC.Default.GetAllInstancesByBase()) { vm.OnApplicationShuttingDown(); } } catch { } try { await FinalizeApplication(); } catch (Exception ex) { LogManager.Log(ex, "Error occurred on application shutdown finalization."); } Environment.Exit(0); } /// /// Restarts the application. /// public async void Restart() { if (IsShuttingDown) return; IsShuttingDown = true; try { _dispatcher.Invoke(() => { var nav = TangoIOC.Default.GetInstance(); if (nav != null) { nav.NavigateTo(NavigationView.RestartingView); } }); LogManager.Log("Restarting the application..."); await Task.Delay(8000); _watchdogServer.Dispose(); foreach (var vm in TangoIOC.Default.GetAllInstancesByBase()) { try { vm.OnApplicationShuttingDown(); } catch { } } } catch { } try { if (_machineProvider.MachineOperator.State == Transport.TransportComponentState.Connected) { _machineProvider.MachineOperator.Adapter.Disconnect().Wait(); } } catch { } try { await FinalizeApplication(); } catch (Exception ex) { LogManager.Log(ex, "Error occurred on application shutdown finalization."); } Process.Start(Application.ResourceAssembly.Location); Environment.Exit(0); } /// /// Runs the updater utility and exits the application. /// public async void UpdateApplication(String updaterPath, String arguments) { if (IsShuttingDown) return; IsShuttingDown = true; LogManager.Log("Restarting application for update..."); try { LogManager.Log("Navigating to restart view..."); _dispatcher.Invoke(() => { var nav = TangoIOC.Default.GetInstance(); if (nav != null) { nav.NavigateTo(NavigationView.RestartingView); } }); LogManager.Log("Waiting 2 seconds..."); await Task.Delay(2000); try { LogManager.Log("Disposing watch dog..."); _watchdogServer.Dispose(); } catch (Exception ex) { LogManager.Log(ex, "Error disposing watch dog."); } LogManager.Log("Raising OnApplicationShutDown for all view models..."); foreach (var vm in TangoIOC.Default.GetAllInstancesByBase()) { try { vm.OnApplicationShuttingDown(); } catch (Exception ex) { LogManager.Log(ex, $"Error on {vm.GetType().Name}.OnApplicationShutDown()."); } } } catch { } try { LogManager.Log("Saving application settings..."); SettingsManager.Default.GetOrCreate().PreviousApplicationVersion = Version.ToString(); SettingsManager.Default.Save(); } catch (Exception ex) { LogManager.Log(ex, "Error saving application settings."); } try { await FinalizeApplication(); } catch (Exception ex) { LogManager.Log(ex, "Error occurred on application shutdown finalization."); } LogManager.Log($"Executing '{updaterPath}' with arguments '{arguments}'..."); Process p = new Process(); p.StartInfo.FileName = updaterPath; p.StartInfo.Arguments = arguments; p.StartInfo.LoadUserProfile = true; p.StartInfo.UseShellExecute = true; p.Start(); LogManager.Log("Terminating application..."); Environment.Exit(0); } /// /// Enteres the application technician mode. /// public async void EnterTechnicianMode(bool displayNotification = true) { if (displayNotification) { var vm = await _notificationProvider.ShowDialog(); if (vm.DialogResult) { if (vm.Password == "Aa123456") { IsInTechnicianMode = true; _moduleLoader.AllModules.ToList().ForEach(x => x.OnTechnicianEntered()); await _notificationProvider.ShowInfo("Technician mode is now enabled."); } else { await _notificationProvider.ShowError("Invalid technician mode password."); EnterTechnicianMode(); } } } else { IsInTechnicianMode = true; _moduleLoader.AllModules.ToList().ForEach(x => x.OnTechnicianEntered()); } } /// /// Exits the application technician mode. /// public void ExitTechnicianMode() { IsInTechnicianMode = false; _moduleLoader.AllModules.ToList().ForEach(x => x.OnTechnicianExited()); _notificationProvider.ShowInfo("Technician mode is now disabled."); } /// /// Invokes a dialog for entering a password and releasing the screen lock. /// public async void ReleaseScreenLock() { if (IsScreenLocked) { var vm = await _notificationProvider.ShowDialog(); if (vm.DialogResult) { if (vm.Password == SettingsManager.Default.GetOrCreate().LockScreenPassword) { IsScreenLocked = false; ResetScreenLockTimer(); } } } } public void ResetScreenLockTimer() { if (_screenLockTimer != null) { _screenLockTimer.ResetReplace(ScreenLockTimerAction); } } private void ScreenLockTimerAction() { IsScreenLocked = true; } public void SetWindowState(WindowState state) { InvokeUI(() => { MainWindow.Instance.WindowState = state; }); } private Task FinalizeApplication() { LogManager.Log("Finalizing application..."); return LimitedTimeTask.StartNew(() => { try { LogManager.Log("Flushing machine events..."); _eventLogger.Log(EventTypes.APPLICATION_TERMINATED, "User Interface Terminated."); _eventLogger.FlushAll(); } catch (Exception ex) { LogManager.Log(ex, "Error flushing machine events."); } try { LogManager.Log("Disposing insights manager (max 40 seconds to complete)..."); Stopwatch watch = new Stopwatch(); watch.Start(); var frame = InsightsFrame.CreateEmpty(DateTime.UtcNow); InsightsManager.Default.InsertFrame(frame); InsightsManager.Default.Dispose(); watch.Stop(); LogManager.Log($"Insights manager disposed after {(int)watch.Elapsed.TotalSeconds} seconds."); } catch (Exception ex) { LogManager.Log(ex, "Error disposing insights manager."); } }, TimeSpan.FromSeconds(40)); } } }