1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Added plugin unloading

This commit is contained in:
SpoinkyNL 2019-04-14 12:43:39 +02:00
parent dba48a2b6a
commit cdda228713
23 changed files with 309 additions and 158 deletions

View File

@ -1,5 +1,4 @@
using Artemis.Core.Plugins.Interfaces; using Artemis.Core.Plugins.Interfaces;
using Stylet;
namespace Artemis.Core.Plugins.Abstract namespace Artemis.Core.Plugins.Abstract
{ {

View File

@ -1,5 +1,4 @@
using Artemis.Core.Plugins.Interfaces; using Artemis.Core.Plugins.Interfaces;
using Artemis.Core.Plugins.Models;
using Stylet; using Stylet;
namespace Artemis.Core.Plugins.Abstract namespace Artemis.Core.Plugins.Abstract

View File

@ -17,11 +17,6 @@ namespace Artemis.Core.Plugins.Abstract
/// <inheritdoc /> /// <inheritdoc />
public abstract bool ExpandsMainDataModel { get; } public abstract bool ExpandsMainDataModel { get; }
/// <inheritdoc />
public void EnablePlugin()
{
// Load and activate the last active profile
}
/// <inheritdoc /> /// <inheritdoc />
public virtual void Update(double deltaTime) public virtual void Update(double deltaTime)
@ -47,9 +42,13 @@ namespace Artemis.Core.Plugins.Abstract
public abstract IScreen GetMainViewModel(); public abstract IScreen GetMainViewModel();
/// <inheritdoc /> /// <inheritdoc />
public void Dispose() public abstract void EnablePlugin();
{
} /// <inheritdoc />
public abstract void DisablePlugin();
/// <inheritdoc />
public abstract void Dispose();
public void ChangeActiveProfile(Profile profile) public void ChangeActiveProfile(Profile profile)
{ {

View File

@ -2,14 +2,21 @@
namespace Artemis.Core.Plugins.Interfaces namespace Artemis.Core.Plugins.Interfaces
{ {
/// <inheritdoc />
/// <summary> /// <summary>
/// This is the base plugin type, use the other interfaces such as IModule to create plugins /// This is the base plugin type, use the other interfaces such as IModule to create plugins
/// </summary> /// </summary>
public interface IPlugin : IDisposable public interface IPlugin : IDisposable
{ {
/// <summary> /// <summary>
/// Called when the plugin is loaded /// Called when the plugin is activated
/// </summary> /// </summary>
void EnablePlugin(); void EnablePlugin();
/// <summary>
/// Called when the plugin is deactivated
/// </summary>
void DisablePlugin();
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.IO;
using AppDomainToolkit; using AppDomainToolkit;
using Artemis.Core.Plugins.Interfaces; using Artemis.Core.Plugins.Interfaces;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -7,6 +8,10 @@ namespace Artemis.Core.Plugins.Models
{ {
public class PluginInfo public class PluginInfo
{ {
internal PluginInfo()
{
}
/// <summary> /// <summary>
/// The plugins GUID /// The plugins GUID
/// </summary> /// </summary>
@ -28,10 +33,10 @@ namespace Artemis.Core.Plugins.Models
public string Main { get; set; } public string Main { get; set; }
/// <summary> /// <summary>
/// Full path to the plugin's current folder /// The plugins root directory
/// </summary> /// </summary>
[JsonIgnore] [JsonIgnore]
public string Folder { get; set; } public DirectoryInfo Directory { get; set; }
/// <summary> /// <summary>
/// A reference to the type implementing IPlugin, available after successful load /// A reference to the type implementing IPlugin, available after successful load
@ -48,6 +53,7 @@ namespace Artemis.Core.Plugins.Models
/// <summary> /// <summary>
/// The AppDomain context of this plugin /// The AppDomain context of this plugin
/// </summary> /// </summary>
[JsonIgnore]
internal AppDomainContext<AssemblyTargetLoader, PathBasedAssemblyResolver> Context { get; set; } internal AppDomainContext<AssemblyTargetLoader, PathBasedAssemblyResolver> Context { get; set; }
public override string ToString() public override string ToString()

View File

@ -61,12 +61,10 @@ namespace Artemis.Core.ProfileElements
public void UpdateLayerType(ILayerType layerType) public void UpdateLayerType(ILayerType layerType)
{ {
if (LayerType != null) if (LayerType != null)
{
lock (LayerType) lock (LayerType)
{ {
LayerType.Dispose(); LayerType.Dispose();
} }
}
LayerType = layerType; LayerType = layerType;
} }

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Artemis.Core.Exceptions; using Artemis.Core.Exceptions;
using Artemis.Core.Plugins.Interfaces; 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."); throw new ArtemisCoreException("Cannot initialize the core as it is already initialized.");
// Initialize the services // Initialize the services
await _pluginService.LoadPlugins(); await Task.Run(() => _pluginService.LoadPlugins());
await _rgbService.LoadDevices(); await _rgbService.LoadDevices();
OnInitialized(); OnInitialized();
@ -48,7 +47,7 @@ namespace Artemis.Core.Services
{ {
try try
{ {
var modules = _pluginService.GetModules(); var modules = _pluginService.GetPluginsOfType<IModule>();
// Update all active modules // Update all active modules
foreach (var module in modules) foreach (var module in modules)

View File

@ -1,7 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Artemis.Core.Events; using Artemis.Core.Events;
using Artemis.Core.Plugins.Interfaces; using Artemis.Core.Plugins.Interfaces;
using Artemis.Core.Plugins.Models; using Artemis.Core.Plugins.Models;
@ -11,46 +9,81 @@ namespace Artemis.Core.Services.Interfaces
public interface IPluginService : IArtemisService, IDisposable public interface IPluginService : IArtemisService, IDisposable
{ {
/// <summary> /// <summary>
/// Indicates wether or not plugins are currently being loaded /// Indicates whether or not plugins are currently being loaded
/// </summary> /// </summary>
bool LoadingPlugins { get; } bool LoadingPlugins { get; }
/// <summary>
/// All loaded plugins
/// </summary>
ReadOnlyCollection<PluginInfo> Plugins { get; }
/// <summary> /// <summary>
/// Loads all installed plugins. If plugins already loaded this will reload them all /// Loads all installed plugins. If plugins already loaded this will reload them all
/// </summary> /// </summary>
Task LoadPlugins(); void LoadPlugins();
/// <summary> /// <summary>
/// Occurs when a single plugin has loaded /// Unloads all installed plugins.
/// </summary> /// </summary>
event EventHandler<PluginEventArgs> PluginLoaded; void UnloadPlugins();
/// <summary> /// <summary>
/// Occurs when loading all plugins has started /// Loads the plugin defined in the provided <see cref="PluginInfo" />
/// </summary> /// </summary>
event EventHandler StartedLoadingPlugins; /// <param name="pluginInfo">The plugin info defining the plugin to load</param>
void LoadPlugin(PluginInfo pluginInfo);
/// <summary> /// <summary>
/// Occurs when loading all plugins has finished /// Unloads the plugin defined in the provided <see cref="PluginInfo" />
/// </summary> /// </summary>
event EventHandler FinishedLoadedPlugins; /// <param name="pluginInfo">The plugin info defining the plugin to unload</param>
void UnloadPlugin(PluginInfo pluginInfo);
/// <summary> /// <summary>
/// If found, returns an instance of the layer type matching the given GUID /// Finds the plugin info related to the plugin
/// </summary>
/// <param name="plugin">The plugin you want to find the plugin info for</param>
/// <returns>The plugins PluginInfo</returns>
PluginInfo GetPluginInfo(IPlugin plugin);
/// <summary>
/// Gets the plugin info of all loaded plugins
/// </summary>
/// <returns>A list containing all the plugin info</returns>
List<PluginInfo> GetAllPluginInfo();
/// <summary>
/// Finds an instance of the layer type matching the given GUID
/// </summary> /// </summary>
/// <param name="layerTypeGuid">The GUID of the layer type to find</param> /// <param name="layerTypeGuid">The GUID of the layer type to find</param>
/// <returns>An instance of the layer type</returns> /// <returns>An instance of the layer type</returns>
ILayerType GetLayerTypeByGuid(Guid layerTypeGuid); ILayerType GetLayerTypeByGuid(Guid layerTypeGuid);
/// <summary> /// <summary>
/// Returns all the plugins implementing <see cref="IModule" /> /// Finds all enabled <see cref="IPlugin" /> instances of type <see cref="T" />
/// </summary> /// </summary>
/// <returns></returns> /// <typeparam name="T">Either <see cref="IPlugin" /> or a plugin type implementing <see cref="IPlugin" /></typeparam>
IReadOnlyList<IModule> GetModules(); /// <returns>Returns a list of plug instances of type <see cref="T" /></returns>
List<T> GetPluginsOfType<T>() where T : IPlugin;
#region Events
/// <summary>
/// Occurs when a plugin has loaded
/// </summary>
event EventHandler<PluginEventArgs> PluginLoaded;
/// <summary>
/// Occurs when a plugin has been unloaded
/// </summary>
event EventHandler<PluginEventArgs> PluginUnloaded;
/// <summary>
/// Occurs when a plugin has been enabled
/// </summary>
event EventHandler<PluginEventArgs> PluginEnabled;
/// <summary>
/// Occurs when a plugin has been disabled
/// </summary>
event EventHandler<PluginEventArgs> PluginDisabled;
#endregion
} }
} }

View File

@ -1,9 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using AppDomainToolkit; using AppDomainToolkit;
using Artemis.Core.Events; using Artemis.Core.Events;
using Artemis.Core.Exceptions; using Artemis.Core.Exceptions;
@ -23,7 +21,7 @@ namespace Artemis.Core.Services
private readonly List<PluginInfo> _plugins; private readonly List<PluginInfo> _plugins;
private IKernel _childKernel; private IKernel _childKernel;
public PluginService(IKernel kernel) internal PluginService(IKernel kernel)
{ {
_kernel = kernel; _kernel = kernel;
_plugins = new List<PluginInfo>(); _plugins = new List<PluginInfo>();
@ -32,19 +30,20 @@ namespace Artemis.Core.Services
Directory.CreateDirectory(Constants.DataFolder + "plugins"); Directory.CreateDirectory(Constants.DataFolder + "plugins");
} }
/// <inheritdoc />
public bool LoadingPlugins { get; private set; } public bool LoadingPlugins { get; private set; }
public ReadOnlyCollection<PluginInfo> Plugins => _plugins.AsReadOnly();
/// <inheritdoc /> /// <inheritdoc />
public async Task LoadPlugins() public void LoadPlugins()
{ {
if (LoadingPlugins) if (LoadingPlugins)
throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet."); throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet.");
OnStartedLoadingPlugins(); lock (_plugins)
await Task.Run(() =>
{ {
LoadingPlugins = true;
// Unload all currently loaded plugins first
UnloadPlugins(); UnloadPlugins();
// Create a child kernel and app domain that will only contain the plugins // 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 // Load the plugin assemblies into the plugin context
var directory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins")); var directory = new DirectoryInfo(Path.Combine(Constants.DataFolder, "plugins"));
foreach (var subDirectory in directory.EnumerateDirectories()) foreach (var subDirectory in directory.EnumerateDirectories())
{
try try
{ {
// Load the metadata // Load the metadata
@ -62,61 +62,145 @@ namespace Artemis.Core.Services
// Locate the main entry // Locate the main entry
var pluginInfo = JsonConvert.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile)); var pluginInfo = JsonConvert.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile));
// TODO Just temporarily until settings are in place pluginInfo.Directory = subDirectory;
pluginInfo.Enabled = true; LoadPlugin(pluginInfo);
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<IPlugin>().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);
} }
catch (Exception e) catch (Exception e)
{ {
throw new ArtemisPluginException("Failed to load plugin", e); throw new ArtemisPluginException("Failed to load plugin", e);
} }
}
// Activate plugins after they are all loaded // Activate plugins after they are all loaded
foreach (var pluginInfo in _plugins.Where(p => p.Enabled)) foreach (var pluginInfo in _plugins.Where(p => p.Enabled))
{
pluginInfo.Instance.EnablePlugin(); pluginInfo.Instance.EnablePlugin();
}); OnPluginEnabled(new PluginEventArgs(pluginInfo));
}
OnFinishedLoadedPlugins(); LoadingPlugins = false;
}
}
/// <inheritdoc />
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();
}
}
/// <inheritdoc />
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));
}
}
/// <inheritdoc />
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));
}
}
/// <inheritdoc />
public PluginInfo GetPluginInfo(IPlugin plugin)
{
lock (_plugins)
{
return _plugins.FirstOrDefault(p => p.Instance == plugin);
}
}
/// <inheritdoc />
public List<PluginInfo> GetAllPluginInfo()
{
return new List<PluginInfo>(_plugins);
} }
/// <inheritdoc /> /// <inheritdoc />
@ -132,9 +216,13 @@ namespace Artemis.Core.Services
return layerType; return layerType;
} }
public IReadOnlyList<IModule> GetModules() /// <inheritdoc />
public List<T> GetPluginsOfType<T>() 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() public void Dispose()
@ -142,38 +230,32 @@ namespace Artemis.Core.Services
UnloadPlugins(); UnloadPlugins();
} }
private void UnloadPlugins()
{
_plugins.Clear();
if (_childKernel != null)
{
_childKernel.Dispose();
_childKernel = null;
}
}
#region Events #region Events
public event EventHandler<PluginEventArgs> PluginLoaded; public event EventHandler<PluginEventArgs> PluginLoaded;
public event EventHandler StartedLoadingPlugins; public event EventHandler<PluginEventArgs> PluginUnloaded;
public event EventHandler FinishedLoadedPlugins; public event EventHandler<PluginEventArgs> PluginEnabled;
public event EventHandler<PluginEventArgs> PluginDisabled;
private void OnPluginLoaded(PluginEventArgs e) protected virtual void OnPluginLoaded(PluginEventArgs e)
{ {
PluginLoaded?.Invoke(this, e); PluginLoaded?.Invoke(this, e);
} }
private void OnStartedLoadingPlugins() protected virtual void OnPluginUnloaded(PluginEventArgs e)
{ {
LoadingPlugins = true; PluginUnloaded?.Invoke(this, e);
StartedLoadingPlugins?.Invoke(this, EventArgs.Empty);
} }
private void OnFinishedLoadedPlugins() protected virtual void OnPluginEnabled(PluginEventArgs e)
{ {
LoadingPlugins = false; PluginEnabled?.Invoke(this, e);
FinishedLoadedPlugins?.Invoke(this, EventArgs.Empty); }
protected virtual void OnPluginDisabled(PluginEventArgs e)
{
PluginDisabled?.Invoke(this, e);
} }
#endregion #endregion

View File

@ -13,8 +13,8 @@ namespace Artemis.Core.Services
{ {
public class RgbService : IRgbService, IDisposable public class RgbService : IRgbService, IDisposable
{ {
private readonly TimerUpdateTrigger _updateTrigger;
private readonly List<IRGBDevice> _loadedDevices; private readonly List<IRGBDevice> _loadedDevices;
private readonly TimerUpdateTrigger _updateTrigger;
public RgbService() public RgbService()
{ {
@ -68,7 +68,9 @@ namespace Artemis.Core.Services
OnDeviceLoaded(new DeviceEventArgs(surfaceDevice)); OnDeviceLoaded(new DeviceEventArgs(surfaceDevice));
} }
else else
{
OnDeviceReloaded(new DeviceEventArgs(surfaceDevice)); OnDeviceReloaded(new DeviceEventArgs(surfaceDevice));
}
} }
} }
}); });

View File

@ -28,7 +28,8 @@
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
@ -52,7 +53,8 @@
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis.CSharp.Scripting" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="Microsoft.CodeAnalysis.CSharp.Scripting" publicKeyToken="31bf3856ad364e35"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
@ -68,7 +70,8 @@
<bindingRedirect oldVersion="0.0.0.0-2.6.0.0" newVersion="2.6.0.0" /> <bindingRedirect oldVersion="0.0.0.0-2.6.0.0" newVersion="2.6.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
@ -84,7 +87,8 @@
<bindingRedirect oldVersion="0.0.0.0-2.2.0.0" newVersion="2.2.0.0" /> <bindingRedirect oldVersion="0.0.0.0-2.2.0.0" newVersion="2.2.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" /> <bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
@ -92,7 +96,8 @@
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" /> <bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.1" newVersion="4.0.3.1" /> <bindingRedirect oldVersion="0.0.0.0-4.0.3.1" newVersion="4.0.3.1" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="AppDomainToolkit" version="1.0.4.3" targetFramework="net461" /> <package id="AppDomainToolkit" version="1.0.4.3" targetFramework="net461" />
<package id="Castle.Core" version="4.4.0" targetFramework="net461" /> <package id="Castle.Core" version="4.4.0" targetFramework="net461" />

View File

@ -1,4 +1,5 @@
using System.Drawing; using System;
using System.Drawing;
using Artemis.Core.Plugins.Interfaces; using Artemis.Core.Plugins.Interfaces;
using Artemis.Core.ProfileElements; using Artemis.Core.ProfileElements;
using QRCoder; using QRCoder;
@ -17,6 +18,11 @@ namespace Artemis.Plugins.LayerTypes.Brush
var qrGenerator = new QRCodeGenerator(); var qrGenerator = new QRCodeGenerator();
} }
public void DisablePlugin()
{
throw new NotImplementedException();
}
public void Update(Layer layer) public void Update(Layer layer)
{ {
var config = layer.LayerTypeConfiguration as BrushConfiguration; var config = layer.LayerTypeConfiguration as BrushConfiguration;

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="QRCoder" version="1.2.5" targetFramework="net461" /> <package id="QRCoder" version="1.2.5" targetFramework="net461" />
<package id="RGB.NET.Core" version="0.1.22" targetFramework="net461" /> <package id="RGB.NET.Core" version="0.1.22" targetFramework="net461" />

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Plugins.Interfaces; using Artemis.Core.Plugins.Interfaces;
@ -70,6 +71,11 @@ namespace Artemis.Plugins.Modules.General
PopulateColors(); PopulateColors();
} }
public void DisablePlugin()
{
throw new NotImplementedException();
}
private void UpdateLedColor(Led led, double deltaTime) private void UpdateLedColor(Led led, double deltaTime)
{ {
if (_colors.ContainsKey(led)) if (_colors.ContainsKey(led))

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="QRCoder" version="1.3.5" targetFramework="net461" /> <package id="QRCoder" version="1.3.5" targetFramework="net461" />
<package id="RGB.NET.Core" version="0.1.22" targetFramework="net461" /> <package id="RGB.NET.Core" version="0.1.22" targetFramework="net461" />

View File

@ -11,7 +11,7 @@ namespace Artemis.Storage.Migrations
{ {
protected override void BuildModel(ModelBuilder modelBuilder) protected override void BuildModel(ModelBuilder modelBuilder)
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); .HasAnnotation("ProductVersion", "2.0.2-rtm-10011");
@ -180,7 +180,7 @@ namespace Artemis.Storage.Migrations
.WithMany() .WithMany()
.HasForeignKey("RootFolderGuid"); .HasForeignKey("RootFolderGuid");
}); });
#pragma warning restore 612, 618 #pragma warning restore 612, 618
} }
} }
} }

View File

@ -31,7 +31,8 @@
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
@ -55,7 +56,8 @@
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="Microsoft.CodeAnalysis.CSharp.Scripting" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <assemblyIdentity name="Microsoft.CodeAnalysis.CSharp.Scripting" publicKeyToken="31bf3856ad364e35"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
@ -71,7 +73,8 @@
<bindingRedirect oldVersion="0.0.0.0-2.6.0.0" newVersion="2.6.0.0" /> <bindingRedirect oldVersion="0.0.0.0-2.6.0.0" newVersion="2.6.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
@ -87,7 +90,8 @@
<bindingRedirect oldVersion="0.0.0.0-2.2.0.0" newVersion="2.2.0.0" /> <bindingRedirect oldVersion="0.0.0.0-2.2.0.0" newVersion="2.2.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" /> <bindingRedirect oldVersion="0.0.0.0-4.0.4.1" newVersion="4.0.4.1" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
@ -95,7 +99,8 @@
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" /> <bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> <assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51"
culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.1" newVersion="4.0.3.1" /> <bindingRedirect oldVersion="0.0.0.0-4.0.3.1" newVersion="4.0.3.1" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>

View File

@ -22,6 +22,8 @@ namespace Artemis.UI
protected override void ConfigureIoC(IKernel kernel) protected override void ConfigureIoC(IKernel kernel)
{ {
kernel.Settings.InjectNonPublic = true;
// Load this assembly's module // Load this assembly's module
kernel.Load<UIModule>(); kernel.Load<UIModule>();
// Load the core assembly's module // Load the core assembly's module

View File

@ -70,10 +70,8 @@ namespace Artemis.UI.Controls.Visualizers
private void RGBSurfaceOnSurfaceLayoutChanged(SurfaceLayoutChangedEventArgs args) private void RGBSurfaceOnSurfaceLayoutChanged(SurfaceLayoutChangedEventArgs args)
{ {
if (args.DeviceAdded) if (args.DeviceAdded)
{
foreach (var device in args.Devices) foreach (var device in args.Devices)
_newDevices.Add(device); _newDevices.Add(device);
}
UpdateSurface(); UpdateSurface();
} }

View File

@ -6,7 +6,6 @@ namespace Artemis.UI.Stylet
{ {
public ArtemisViewManager(ViewManagerConfig config) : base(config) public ArtemisViewManager(ViewManagerConfig config) : base(config)
{ {
} }
} }
} }

View File

@ -34,7 +34,7 @@ namespace Artemis.UI.Stylet
kernel.Bind<IViewManager>().ToConstant(new ViewManager(viewManagerConfig)); kernel.Bind<IViewManager>().ToConstant(new ViewManager(viewManagerConfig));
kernel.Bind<IWindowManagerConfig>().ToConstant(this).InTransientScope(); kernel.Bind<IWindowManagerConfig>().ToConstant(this).InTransientScope();
kernel.Bind<IWindowManager>().ToMethod(c => new WindowManager(c.Kernel.Get<IViewManager>(),() => c.Kernel.Get<IMessageBoxViewModel>(), c.Kernel.Get<IWindowManagerConfig>())).InSingletonScope(); kernel.Bind<IWindowManager>().ToMethod(c => new WindowManager(c.Kernel.Get<IViewManager>(), () => c.Kernel.Get<IMessageBoxViewModel>(), c.Kernel.Get<IWindowManagerConfig>())).InSingletonScope();
kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope(); kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope();
kernel.Bind<IMessageBoxViewModel>().To<MessageBoxViewModel>(); // Not singleton! kernel.Bind<IMessageBoxViewModel>().To<MessageBoxViewModel>(); // Not singleton!
} }

View File

@ -1,9 +1,9 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Controls; using System.Windows.Controls;
using Artemis.Core.Events;
using Artemis.Core.Plugins.Interfaces; using Artemis.Core.Plugins.Interfaces;
using Artemis.Core.Services.Interfaces; using Artemis.Core.Services.Interfaces;
using Artemis.UI.ViewModels.Interfaces; using Artemis.UI.ViewModels.Interfaces;
@ -29,44 +29,22 @@ namespace Artemis.UI.ViewModels
// Sync up with the plugin service // Sync up with the plugin service
Modules = new BindableCollection<IModule>(); Modules = new BindableCollection<IModule>();
LoadingPlugins = _pluginService.LoadingPlugins; Modules.AddRange(_pluginService.GetPluginsOfType<IModule>());
_pluginService.StartedLoadingPlugins += PluginServiceOnStartedLoadingPlugins;
_pluginService.FinishedLoadedPlugins += PluginServiceOnFinishedLoadedPlugins;
if (!LoadingPlugins)
Modules.AddRange(_pluginService.GetModules());
_pluginService.PluginEnabled += PluginServiceOnPluginEnabled;
_pluginService.PluginDisabled += PluginServiceOnPluginDisabled;
PropertyChanged += OnSelectedModuleChanged; PropertyChanged += OnSelectedModuleChanged;
PropertyChanged += OnSelectedPageChanged; PropertyChanged += OnSelectedPageChanged;
} }
public IObservableCollection<IModule> Modules { get; set; } public IObservableCollection<IModule> Modules { get; set; }
public bool MenuOpen { get; set; } public bool MenuOpen { get; set; }
public bool LoadingPlugins { get; set; }
public ListBoxItem SelectedPage { get; set; } public ListBoxItem SelectedPage { get; set; }
public IModule SelectedModule { 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() public async Task NavigateToSelectedModule()
{ {
if (SelectedModule == null || LoadingPlugins) if (SelectedModule == null)
return; return;
// Create a view model for the given plugin info (which will be a module) // Create a view model for the given plugin info (which will be a module)
@ -78,6 +56,31 @@ namespace Artemis.UI.ViewModels
MenuOpen = false; 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) private async void OnSelectedModuleChanged(object sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName == "SelectedModule") if (e.PropertyName == "SelectedModule")