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));
}
}
}