From cdda228713b5e07639b3de1e97a118026b0dbc05 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Sun, 14 Apr 2019 12:43:39 +0200 Subject: [PATCH] Added plugin unloading --- .../Plugins/Abstract/ModuleDataModel.cs | 1 - .../Plugins/Abstract/ModuleViewModel.cs | 1 - .../Plugins/Abstract/ProfileModule.cs | 15 +- .../Plugins/Interfaces/IPlugin.cs | 9 +- src/Artemis.Core/Plugins/Models/PluginInfo.cs | 10 +- src/Artemis.Core/ProfileElements/Layer.cs | 2 - src/Artemis.Core/Services/CoreService.cs | 5 +- .../Services/Interfaces/IPluginService.cs | 71 ++++-- src/Artemis.Core/Services/PluginService.cs | 230 ++++++++++++------ src/Artemis.Core/Services/RgbService.cs | 4 +- src/Artemis.Core/app.config | 15 +- src/Artemis.Core/packages.config | 1 + .../BrushLayerType.cs | 8 +- .../packages.config | 1 + .../GeneralModule.cs | 8 +- .../packages.config | 1 + .../Migrations/StorageContextModelSnapshot.cs | 4 +- src/Artemis.UI/App.config | 15 +- src/Artemis.UI/Bootstrapper.cs | 2 + .../Visualizers/RGBSurfaceVisualizer.cs | 2 - src/Artemis.UI/Stylet/ArtemisViewManager.cs | 1 - src/Artemis.UI/Stylet/NinjectBootstrapper.cs | 2 +- src/Artemis.UI/ViewModels/RootViewModel.cs | 59 ++--- 23 files changed, 309 insertions(+), 158 deletions(-) diff --git a/src/Artemis.Core/Plugins/Abstract/ModuleDataModel.cs b/src/Artemis.Core/Plugins/Abstract/ModuleDataModel.cs index ad95bd980..d2ce723de 100644 --- a/src/Artemis.Core/Plugins/Abstract/ModuleDataModel.cs +++ b/src/Artemis.Core/Plugins/Abstract/ModuleDataModel.cs @@ -1,5 +1,4 @@ using Artemis.Core.Plugins.Interfaces; -using Stylet; namespace Artemis.Core.Plugins.Abstract { diff --git a/src/Artemis.Core/Plugins/Abstract/ModuleViewModel.cs b/src/Artemis.Core/Plugins/Abstract/ModuleViewModel.cs index 68ec913a6..10ba6fe62 100644 --- a/src/Artemis.Core/Plugins/Abstract/ModuleViewModel.cs +++ b/src/Artemis.Core/Plugins/Abstract/ModuleViewModel.cs @@ -1,5 +1,4 @@ using Artemis.Core.Plugins.Interfaces; -using Artemis.Core.Plugins.Models; using Stylet; namespace Artemis.Core.Plugins.Abstract diff --git a/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs b/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs index 8b719727d..8cce411ae 100644 --- a/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs +++ b/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs @@ -17,11 +17,6 @@ namespace Artemis.Core.Plugins.Abstract /// public abstract bool ExpandsMainDataModel { get; } - /// - public void EnablePlugin() - { - // Load and activate the last active profile - } /// public virtual void Update(double deltaTime) @@ -47,9 +42,13 @@ namespace Artemis.Core.Plugins.Abstract public abstract IScreen GetMainViewModel(); /// - public void Dispose() - { - } + public abstract void EnablePlugin(); + + /// + public abstract void DisablePlugin(); + + /// + public abstract void Dispose(); public void ChangeActiveProfile(Profile profile) { diff --git a/src/Artemis.Core/Plugins/Interfaces/IPlugin.cs b/src/Artemis.Core/Plugins/Interfaces/IPlugin.cs index 238122061..c1a703cee 100644 --- a/src/Artemis.Core/Plugins/Interfaces/IPlugin.cs +++ b/src/Artemis.Core/Plugins/Interfaces/IPlugin.cs @@ -2,14 +2,21 @@ namespace Artemis.Core.Plugins.Interfaces { + /// /// /// This is the base plugin type, use the other interfaces such as IModule to create plugins /// public interface IPlugin : IDisposable { /// - /// Called when the plugin is loaded + /// Called when the plugin is activated /// void EnablePlugin(); + + + /// + /// Called when the plugin is deactivated + /// + void DisablePlugin(); } } \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/Models/PluginInfo.cs b/src/Artemis.Core/Plugins/Models/PluginInfo.cs index 8cba360d8..d2746ca07 100644 --- a/src/Artemis.Core/Plugins/Models/PluginInfo.cs +++ b/src/Artemis.Core/Plugins/Models/PluginInfo.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using AppDomainToolkit; using Artemis.Core.Plugins.Interfaces; using Newtonsoft.Json; @@ -7,6 +8,10 @@ namespace Artemis.Core.Plugins.Models { public class PluginInfo { + internal PluginInfo() + { + } + /// /// The plugins GUID /// @@ -28,10 +33,10 @@ namespace Artemis.Core.Plugins.Models public string Main { get; set; } /// - /// Full path to the plugin's current folder + /// The plugins root directory /// [JsonIgnore] - public string Folder { get; set; } + public DirectoryInfo Directory { get; set; } /// /// A reference to the type implementing IPlugin, available after successful load @@ -48,6 +53,7 @@ namespace Artemis.Core.Plugins.Models /// /// The AppDomain context of this plugin /// + [JsonIgnore] internal AppDomainContext Context { get; set; } public override string ToString() diff --git a/src/Artemis.Core/ProfileElements/Layer.cs b/src/Artemis.Core/ProfileElements/Layer.cs index dd519f67c..7cd3a612a 100644 --- a/src/Artemis.Core/ProfileElements/Layer.cs +++ b/src/Artemis.Core/ProfileElements/Layer.cs @@ -61,12 +61,10 @@ namespace Artemis.Core.ProfileElements public void UpdateLayerType(ILayerType layerType) { if (LayerType != null) - { lock (LayerType) { LayerType.Dispose(); } - } LayerType = layerType; } diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs index 48ec8119d..f4b1ebd6b 100644 --- a/src/Artemis.Core/Services/CoreService.cs +++ b/src/Artemis.Core/Services/CoreService.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Threading.Tasks; using Artemis.Core.Exceptions; using Artemis.Core.Plugins.Interfaces; @@ -38,7 +37,7 @@ namespace Artemis.Core.Services throw new ArtemisCoreException("Cannot initialize the core as it is already initialized."); // Initialize the services - await _pluginService.LoadPlugins(); + await Task.Run(() => _pluginService.LoadPlugins()); await _rgbService.LoadDevices(); OnInitialized(); @@ -48,7 +47,7 @@ namespace Artemis.Core.Services { try { - var modules = _pluginService.GetModules(); + var modules = _pluginService.GetPluginsOfType(); // Update all active modules foreach (var module in modules) diff --git a/src/Artemis.Core/Services/Interfaces/IPluginService.cs b/src/Artemis.Core/Services/Interfaces/IPluginService.cs index 85e6bde9d..ce31000e3 100644 --- a/src/Artemis.Core/Services/Interfaces/IPluginService.cs +++ b/src/Artemis.Core/Services/Interfaces/IPluginService.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Threading.Tasks; using Artemis.Core.Events; using Artemis.Core.Plugins.Interfaces; using Artemis.Core.Plugins.Models; @@ -11,46 +9,81 @@ namespace Artemis.Core.Services.Interfaces public interface IPluginService : IArtemisService, IDisposable { /// - /// Indicates wether or not plugins are currently being loaded + /// Indicates whether or not plugins are currently being loaded /// bool LoadingPlugins { get; } - /// - /// All loaded plugins - /// - ReadOnlyCollection Plugins { get; } - /// /// Loads all installed plugins. If plugins already loaded this will reload them all /// - Task LoadPlugins(); + void LoadPlugins(); /// - /// Occurs when a single plugin has loaded + /// Unloads all installed plugins. /// - event EventHandler PluginLoaded; + void UnloadPlugins(); /// - /// Occurs when loading all plugins has started + /// Loads the plugin defined in the provided /// - event EventHandler StartedLoadingPlugins; + /// The plugin info defining the plugin to load + void LoadPlugin(PluginInfo pluginInfo); /// - /// Occurs when loading all plugins has finished + /// Unloads the plugin defined in the provided /// - event EventHandler FinishedLoadedPlugins; + /// The plugin info defining the plugin to unload + void UnloadPlugin(PluginInfo pluginInfo); /// - /// If found, returns an instance of the layer type matching the given GUID + /// Finds the plugin info related to the plugin + /// + /// The plugin you want to find the plugin info for + /// The plugins PluginInfo + PluginInfo GetPluginInfo(IPlugin plugin); + + /// + /// Gets the plugin info of all loaded plugins + /// + /// A list containing all the plugin info + List GetAllPluginInfo(); + + /// + /// Finds an instance of the layer type matching the given GUID /// /// The GUID of the layer type to find /// An instance of the layer type ILayerType GetLayerTypeByGuid(Guid layerTypeGuid); /// - /// Returns all the plugins implementing + /// Finds all enabled instances of type /// - /// - IReadOnlyList GetModules(); + /// Either or a plugin type implementing + /// Returns a list of plug instances of type + List GetPluginsOfType() where T : IPlugin; + + #region Events + + /// + /// Occurs when a plugin has loaded + /// + event EventHandler PluginLoaded; + + /// + /// Occurs when a plugin has been unloaded + /// + event EventHandler PluginUnloaded; + + /// + /// Occurs when a plugin has been enabled + /// + event EventHandler PluginEnabled; + + /// + /// Occurs when a plugin has been disabled + /// + event EventHandler PluginDisabled; + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/PluginService.cs b/src/Artemis.Core/Services/PluginService.cs index d7ecb467d..aa7e9a4b9 100644 --- a/src/Artemis.Core/Services/PluginService.cs +++ b/src/Artemis.Core/Services/PluginService.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; using System.Linq; -using System.Threading.Tasks; using AppDomainToolkit; using Artemis.Core.Events; using Artemis.Core.Exceptions; @@ -23,7 +21,7 @@ namespace Artemis.Core.Services private readonly List _plugins; private IKernel _childKernel; - public PluginService(IKernel kernel) + internal PluginService(IKernel kernel) { _kernel = kernel; _plugins = new List(); @@ -32,19 +30,20 @@ namespace Artemis.Core.Services Directory.CreateDirectory(Constants.DataFolder + "plugins"); } + /// public bool LoadingPlugins { get; private set; } - public ReadOnlyCollection Plugins => _plugins.AsReadOnly(); /// - public async Task LoadPlugins() + public void LoadPlugins() { if (LoadingPlugins) throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet."); - OnStartedLoadingPlugins(); - - await Task.Run(() => + lock (_plugins) { + LoadingPlugins = true; + + // Unload all currently loaded plugins first UnloadPlugins(); // Create a child kernel and app domain that will only contain the plugins @@ -53,6 +52,7 @@ namespace Artemis.Core.Services // Load the plugin assemblies into the plugin context var directory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins")); foreach (var subDirectory in directory.EnumerateDirectories()) + { try { // Load the metadata @@ -62,61 +62,145 @@ namespace Artemis.Core.Services // Locate the main entry var pluginInfo = JsonConvert.DeserializeObject(File.ReadAllText(metadataFile)); - // TODO Just temporarily until settings are in place - pluginInfo.Enabled = true; - var mainFile = Path.Combine(subDirectory.FullName, pluginInfo.Main); - if (!File.Exists(mainFile)) - throw new ArtemisPluginException(pluginInfo, "Couldn't find the plugins main entry at " + mainFile); - - // Load the plugin, all types implementing IPlugin and register them with DI - var setupInfo = new AppDomainSetup - { - ApplicationName = pluginInfo.Guid.ToString(), - ApplicationBase = AppDomain.CurrentDomain.BaseDirectory, - PrivateBinPath = subDirectory.FullName - }; - pluginInfo.Context = AppDomainContext.Create(setupInfo); - - try - { - pluginInfo.Context.LoadAssemblyWithReferences(LoadMethod.LoadFrom, mainFile); - } - catch (Exception e) - { - throw new ArtemisPluginException(pluginInfo, "Failed to load the plugins assembly", e); - } - - // Get the IPlugin implementation from the main assembly and if there is only one, instantiate it - var mainAssembly = pluginInfo.Context.Domain.GetAssemblies().First(a => a.Location == mainFile); - var pluginTypes = mainAssembly.GetTypes().Where(t => typeof(IPlugin).IsAssignableFrom(t)).ToList(); - if (pluginTypes.Count > 1) - throw new ArtemisPluginException(pluginInfo, $"Plugin contains {pluginTypes.Count} implementations of IPlugin, only 1 allowed"); - if (pluginTypes.Count == 0) - throw new ArtemisPluginException(pluginInfo, "Plugin contains no implementation of IPlugin"); - - var pluginType = pluginTypes.Single(); - _childKernel.Bind().To(pluginType).InSingletonScope(); - try - { - pluginInfo.Instance = (IPlugin) _childKernel.Get(pluginType); - } - catch (Exception e) - { - throw new ArtemisPluginException(pluginInfo, "Failed to instantiate the plugin", e); - } - _plugins.Add(pluginInfo); + pluginInfo.Directory = subDirectory; + LoadPlugin(pluginInfo); } catch (Exception e) { throw new ArtemisPluginException("Failed to load plugin", e); } + } // Activate plugins after they are all loaded foreach (var pluginInfo in _plugins.Where(p => p.Enabled)) + { pluginInfo.Instance.EnablePlugin(); - }); + OnPluginEnabled(new PluginEventArgs(pluginInfo)); + } - OnFinishedLoadedPlugins(); + LoadingPlugins = false; + } + } + + /// + public void UnloadPlugins() + { + lock (_plugins) + { + // Unload all plugins + while (_plugins.Count > 0) + { + UnloadPlugin(_plugins[0]); + } + + // Dispose the child kernel and therefore any leftover plugins instantiated with it + if (_childKernel != null) + { + _childKernel.Dispose(); + _childKernel = null; + } + + _plugins.Clear(); + } + } + + /// + public void LoadPlugin(PluginInfo pluginInfo) + { + lock (_plugins) + { + // Unload the plugin first if it is already loaded + if (_plugins.Contains(pluginInfo)) + UnloadPlugin(pluginInfo); + + // TODO Just temporarily until settings are in place + pluginInfo.Enabled = true; + var mainFile = Path.Combine(pluginInfo.Directory.FullName, pluginInfo.Main); + if (!File.Exists(mainFile)) + throw new ArtemisPluginException(pluginInfo, "Couldn't find the plugins main entry at " + mainFile); + + // Load the plugin, all types implementing IPlugin and register them with DI + var setupInfo = new AppDomainSetup + { + ApplicationName = pluginInfo.Guid.ToString(), + ApplicationBase = AppDomain.CurrentDomain.BaseDirectory, + PrivateBinPath = pluginInfo.Directory.FullName + }; + pluginInfo.Context = AppDomainContext.Create(setupInfo); + + try + { + pluginInfo.Context.LoadAssemblyWithReferences(LoadMethod.LoadFrom, mainFile); + } + catch (Exception e) + { + throw new ArtemisPluginException(pluginInfo, "Failed to load the plugins assembly", e); + } + + // Get the IPlugin implementation from the main assembly and if there is only one, instantiate it + var mainAssembly = pluginInfo.Context.Domain.GetAssemblies().First(a => a.Location == mainFile); + var pluginTypes = mainAssembly.GetTypes().Where(t => typeof(IPlugin).IsAssignableFrom(t)).ToList(); + if (pluginTypes.Count > 1) + throw new ArtemisPluginException(pluginInfo, $"Plugin contains {pluginTypes.Count} implementations of IPlugin, only 1 allowed"); + if (pluginTypes.Count == 0) + throw new ArtemisPluginException(pluginInfo, "Plugin contains no implementation of IPlugin"); + + var pluginType = pluginTypes.Single(); + try + { + pluginInfo.Instance = (IPlugin) _childKernel.Get(pluginType); + } + catch (Exception e) + { + throw new ArtemisPluginException(pluginInfo, "Failed to instantiate the plugin", e); + } + + _plugins.Add(pluginInfo); + OnPluginLoaded(new PluginEventArgs(pluginInfo)); + } + } + + /// + public void UnloadPlugin(PluginInfo pluginInfo) + { + lock (_plugins) + { + try + { + pluginInfo.Instance.DisablePlugin(); + } + catch (Exception) + { + // TODO: Log these + } + finally + { + OnPluginDisabled(new PluginEventArgs(pluginInfo)); + } + + _childKernel.Unbind(pluginInfo.Instance.GetType()); + + pluginInfo.Instance.Dispose(); + pluginInfo.Context.Dispose(); + _plugins.Remove(pluginInfo); + + OnPluginUnloaded(new PluginEventArgs(pluginInfo)); + } + } + + /// + public PluginInfo GetPluginInfo(IPlugin plugin) + { + lock (_plugins) + { + return _plugins.FirstOrDefault(p => p.Instance == plugin); + } + } + + /// + public List GetAllPluginInfo() + { + return new List(_plugins); } /// @@ -132,9 +216,13 @@ namespace Artemis.Core.Services return layerType; } - public IReadOnlyList GetModules() + /// + public List GetPluginsOfType() where T : IPlugin { - return Plugins.Where(p => p.Instance is IModule).Select(p => (IModule) p.Instance).ToList(); + lock (_plugins) + { + return _plugins.Where(p => p.Enabled && p.Instance is T).Select(p => (T) p.Instance).ToList(); + } } public void Dispose() @@ -142,38 +230,32 @@ namespace Artemis.Core.Services UnloadPlugins(); } - private void UnloadPlugins() - { - _plugins.Clear(); - - if (_childKernel != null) - { - _childKernel.Dispose(); - _childKernel = null; - } - } #region Events public event EventHandler PluginLoaded; - public event EventHandler StartedLoadingPlugins; - public event EventHandler FinishedLoadedPlugins; + public event EventHandler PluginUnloaded; + public event EventHandler PluginEnabled; + public event EventHandler PluginDisabled; - private void OnPluginLoaded(PluginEventArgs e) + protected virtual void OnPluginLoaded(PluginEventArgs e) { PluginLoaded?.Invoke(this, e); } - private void OnStartedLoadingPlugins() + protected virtual void OnPluginUnloaded(PluginEventArgs e) { - LoadingPlugins = true; - StartedLoadingPlugins?.Invoke(this, EventArgs.Empty); + PluginUnloaded?.Invoke(this, e); } - private void OnFinishedLoadedPlugins() + protected virtual void OnPluginEnabled(PluginEventArgs e) { - LoadingPlugins = false; - FinishedLoadedPlugins?.Invoke(this, EventArgs.Empty); + PluginEnabled?.Invoke(this, e); + } + + protected virtual void OnPluginDisabled(PluginEventArgs e) + { + PluginDisabled?.Invoke(this, e); } #endregion diff --git a/src/Artemis.Core/Services/RgbService.cs b/src/Artemis.Core/Services/RgbService.cs index 45606879f..8dadac100 100644 --- a/src/Artemis.Core/Services/RgbService.cs +++ b/src/Artemis.Core/Services/RgbService.cs @@ -13,8 +13,8 @@ namespace Artemis.Core.Services { public class RgbService : IRgbService, IDisposable { - private readonly TimerUpdateTrigger _updateTrigger; private readonly List _loadedDevices; + private readonly TimerUpdateTrigger _updateTrigger; public RgbService() { @@ -68,7 +68,9 @@ namespace Artemis.Core.Services OnDeviceLoaded(new DeviceEventArgs(surfaceDevice)); } else + { OnDeviceReloaded(new DeviceEventArgs(surfaceDevice)); + } } } }); diff --git a/src/Artemis.Core/app.config b/src/Artemis.Core/app.config index 58347c373..ec91ee1d6 100644 --- a/src/Artemis.Core/app.config +++ b/src/Artemis.Core/app.config @@ -28,7 +28,8 @@ - + @@ -52,7 +53,8 @@ - + @@ -68,7 +70,8 @@ - + @@ -84,7 +87,8 @@ - + @@ -92,7 +96,8 @@ - + diff --git a/src/Artemis.Core/packages.config b/src/Artemis.Core/packages.config index ce487c687..6dc3ac0de 100644 --- a/src/Artemis.Core/packages.config +++ b/src/Artemis.Core/packages.config @@ -1,4 +1,5 @@  + diff --git a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerType.cs b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerType.cs index 67aa2b011..2c792b09e 100644 --- a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerType.cs +++ b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerType.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using System; +using System.Drawing; using Artemis.Core.Plugins.Interfaces; using Artemis.Core.ProfileElements; using QRCoder; @@ -17,6 +18,11 @@ namespace Artemis.Plugins.LayerTypes.Brush var qrGenerator = new QRCodeGenerator(); } + public void DisablePlugin() + { + throw new NotImplementedException(); + } + public void Update(Layer layer) { var config = layer.LayerTypeConfiguration as BrushConfiguration; diff --git a/src/Artemis.Plugins.LayerTypes.Brush/packages.config b/src/Artemis.Plugins.LayerTypes.Brush/packages.config index f559146e0..046348732 100644 --- a/src/Artemis.Plugins.LayerTypes.Brush/packages.config +++ b/src/Artemis.Plugins.LayerTypes.Brush/packages.config @@ -1,4 +1,5 @@  + diff --git a/src/Artemis.Plugins.Modules.General/GeneralModule.cs b/src/Artemis.Plugins.Modules.General/GeneralModule.cs index cafc22e97..afac2ad5d 100644 --- a/src/Artemis.Plugins.Modules.General/GeneralModule.cs +++ b/src/Artemis.Plugins.Modules.General/GeneralModule.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Drawing; using Artemis.Core; using Artemis.Core.Plugins.Interfaces; @@ -70,6 +71,11 @@ namespace Artemis.Plugins.Modules.General PopulateColors(); } + public void DisablePlugin() + { + throw new NotImplementedException(); + } + private void UpdateLedColor(Led led, double deltaTime) { if (_colors.ContainsKey(led)) diff --git a/src/Artemis.Plugins.Modules.General/packages.config b/src/Artemis.Plugins.Modules.General/packages.config index a2474caba..a7463e291 100644 --- a/src/Artemis.Plugins.Modules.General/packages.config +++ b/src/Artemis.Plugins.Modules.General/packages.config @@ -1,4 +1,5 @@  + diff --git a/src/Artemis.Storage/Migrations/StorageContextModelSnapshot.cs b/src/Artemis.Storage/Migrations/StorageContextModelSnapshot.cs index c27d9c124..d0eb9328a 100644 --- a/src/Artemis.Storage/Migrations/StorageContextModelSnapshot.cs +++ b/src/Artemis.Storage/Migrations/StorageContextModelSnapshot.cs @@ -11,7 +11,7 @@ namespace Artemis.Storage.Migrations { protected override void BuildModel(ModelBuilder modelBuilder) { -#pragma warning disable 612, 618 + #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); @@ -180,7 +180,7 @@ namespace Artemis.Storage.Migrations .WithMany() .HasForeignKey("RootFolderGuid"); }); -#pragma warning restore 612, 618 + #pragma warning restore 612, 618 } } } \ No newline at end of file diff --git a/src/Artemis.UI/App.config b/src/Artemis.UI/App.config index 39dcaca51..fcbfc6adb 100644 --- a/src/Artemis.UI/App.config +++ b/src/Artemis.UI/App.config @@ -31,7 +31,8 @@ - + @@ -55,7 +56,8 @@ - + @@ -71,7 +73,8 @@ - + @@ -87,7 +90,8 @@ - + @@ -95,7 +99,8 @@ - + diff --git a/src/Artemis.UI/Bootstrapper.cs b/src/Artemis.UI/Bootstrapper.cs index 5a4f82d85..c3c420e5b 100644 --- a/src/Artemis.UI/Bootstrapper.cs +++ b/src/Artemis.UI/Bootstrapper.cs @@ -22,6 +22,8 @@ namespace Artemis.UI protected override void ConfigureIoC(IKernel kernel) { + kernel.Settings.InjectNonPublic = true; + // Load this assembly's module kernel.Load(); // Load the core assembly's module diff --git a/src/Artemis.UI/Controls/Visualizers/RGBSurfaceVisualizer.cs b/src/Artemis.UI/Controls/Visualizers/RGBSurfaceVisualizer.cs index 32c0cb364..d05fd77c1 100644 --- a/src/Artemis.UI/Controls/Visualizers/RGBSurfaceVisualizer.cs +++ b/src/Artemis.UI/Controls/Visualizers/RGBSurfaceVisualizer.cs @@ -70,10 +70,8 @@ namespace Artemis.UI.Controls.Visualizers private void RGBSurfaceOnSurfaceLayoutChanged(SurfaceLayoutChangedEventArgs args) { if (args.DeviceAdded) - { foreach (var device in args.Devices) _newDevices.Add(device); - } UpdateSurface(); } diff --git a/src/Artemis.UI/Stylet/ArtemisViewManager.cs b/src/Artemis.UI/Stylet/ArtemisViewManager.cs index fdb164b3e..75461aa26 100644 --- a/src/Artemis.UI/Stylet/ArtemisViewManager.cs +++ b/src/Artemis.UI/Stylet/ArtemisViewManager.cs @@ -6,7 +6,6 @@ namespace Artemis.UI.Stylet { public ArtemisViewManager(ViewManagerConfig config) : base(config) { - } } } \ No newline at end of file diff --git a/src/Artemis.UI/Stylet/NinjectBootstrapper.cs b/src/Artemis.UI/Stylet/NinjectBootstrapper.cs index e04484bed..e8717f153 100644 --- a/src/Artemis.UI/Stylet/NinjectBootstrapper.cs +++ b/src/Artemis.UI/Stylet/NinjectBootstrapper.cs @@ -34,7 +34,7 @@ namespace Artemis.UI.Stylet kernel.Bind().ToConstant(new ViewManager(viewManagerConfig)); kernel.Bind().ToConstant(this).InTransientScope(); - kernel.Bind().ToMethod(c => new WindowManager(c.Kernel.Get(),() => c.Kernel.Get(), c.Kernel.Get())).InSingletonScope(); + kernel.Bind().ToMethod(c => new WindowManager(c.Kernel.Get(), () => c.Kernel.Get(), c.Kernel.Get())).InSingletonScope(); kernel.Bind().To().InSingletonScope(); kernel.Bind().To(); // Not singleton! } diff --git a/src/Artemis.UI/ViewModels/RootViewModel.cs b/src/Artemis.UI/ViewModels/RootViewModel.cs index a62f43220..392cc0933 100644 --- a/src/Artemis.UI/ViewModels/RootViewModel.cs +++ b/src/Artemis.UI/ViewModels/RootViewModel.cs @@ -1,9 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Threading.Tasks; using System.Windows.Controls; +using Artemis.Core.Events; using Artemis.Core.Plugins.Interfaces; using Artemis.Core.Services.Interfaces; using Artemis.UI.ViewModels.Interfaces; @@ -29,44 +29,22 @@ namespace Artemis.UI.ViewModels // Sync up with the plugin service Modules = new BindableCollection(); - LoadingPlugins = _pluginService.LoadingPlugins; - _pluginService.StartedLoadingPlugins += PluginServiceOnStartedLoadingPlugins; - _pluginService.FinishedLoadedPlugins += PluginServiceOnFinishedLoadedPlugins; - - if (!LoadingPlugins) - Modules.AddRange(_pluginService.GetModules()); + Modules.AddRange(_pluginService.GetPluginsOfType()); + _pluginService.PluginEnabled += PluginServiceOnPluginEnabled; + _pluginService.PluginDisabled += PluginServiceOnPluginDisabled; PropertyChanged += OnSelectedModuleChanged; PropertyChanged += OnSelectedPageChanged; } - public IObservableCollection Modules { get; set; } - public bool MenuOpen { get; set; } - public bool LoadingPlugins { get; set; } public ListBoxItem SelectedPage { get; set; } public IModule SelectedModule { get; set; } - private void PluginServiceOnStartedLoadingPlugins(object sender, EventArgs eventArgs) - { - LoadingPlugins = true; - - Modules.Clear(); - SelectedModule = null; - } - - private void PluginServiceOnFinishedLoadedPlugins(object sender, EventArgs eventArgs) - { - Modules.AddRange(_pluginService.GetModules()); - SelectedModule = null; - - LoadingPlugins = false; - } - public async Task NavigateToSelectedModule() { - if (SelectedModule == null || LoadingPlugins) + if (SelectedModule == null) return; // Create a view model for the given plugin info (which will be a module) @@ -78,6 +56,31 @@ namespace Artemis.UI.ViewModels MenuOpen = false; } + private void PluginServiceOnPluginEnabled(object sender, PluginEventArgs e) + { + var existing = Modules.FirstOrDefault(m => _pluginService.GetPluginInfo(m)?.Guid == e.PluginInfo.Guid); + if (existing != null) + { + if (SelectedModule == existing && SelectedModule != null) + SelectedModule = null; + Modules.Remove(existing); + } + + if (e.PluginInfo.Instance is IModule module) + Modules.Add(module); + } + + private void PluginServiceOnPluginDisabled(object sender, PluginEventArgs e) + { + var existing = Modules.FirstOrDefault(m => _pluginService.GetPluginInfo(m)?.Guid == e.PluginInfo.Guid); + if (existing != null) + { + if (SelectedModule == existing && SelectedModule != null) + SelectedModule = null; + Modules.Remove(existing); + } + } + private async void OnSelectedModuleChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "SelectedModule")