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

Plugins - Reworked most of the activation logic

This commit is contained in:
Robert 2020-11-10 20:51:29 +01:00
parent 1e0dc1894d
commit b56966c585
34 changed files with 509 additions and 362 deletions

View File

@ -8,11 +8,11 @@ namespace Artemis.Core
{
}
public PluginEventArgs(PluginInfo pluginInfo)
public PluginEventArgs(Plugin plugin)
{
PluginInfo = pluginInfo;
Plugin = plugin;
}
public PluginInfo PluginInfo { get; }
public Plugin Plugin { get; }
}
}

View File

@ -0,0 +1,18 @@
using System;
namespace Artemis.Core
{
public class PluginImplementationEventArgs : EventArgs
{
public PluginImplementationEventArgs()
{
}
public PluginImplementationEventArgs(PluginImplementation pluginImplementation)
{
PluginImplementation = pluginImplementation;
}
public PluginImplementation PluginImplementation { get; }
}
}

View File

@ -4,19 +4,19 @@ namespace Artemis.Core
{
public class ArtemisPluginException : Exception
{
public ArtemisPluginException(PluginInfo pluginInfo)
public ArtemisPluginException(Plugin plugin)
{
PluginInfo = pluginInfo;
Plugin = plugin;
}
public ArtemisPluginException(PluginInfo pluginInfo, string message) : base(message)
public ArtemisPluginException(Plugin plugin, string message) : base(message)
{
PluginInfo = pluginInfo;
Plugin = plugin;
}
public ArtemisPluginException(PluginInfo pluginInfo, string message, Exception inner) : base(message, inner)
public ArtemisPluginException(Plugin plugin, string message, Exception inner) : base(message, inner)
{
PluginInfo = pluginInfo;
Plugin = plugin;
}
public ArtemisPluginException(string message) : base(message)
@ -27,6 +27,6 @@ namespace Artemis.Core
{
}
public PluginInfo PluginInfo { get; }
public Plugin Plugin { get; }
}
}

View File

@ -10,12 +10,12 @@ namespace Artemis.Core.Ninject
{
private static readonly List<PluginSettings> PluginSettings = new List<PluginSettings>();
private readonly IPluginRepository _pluginRepository;
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
internal PluginSettingsProvider(IPluginRepository pluginRepository, IPluginService pluginService)
internal PluginSettingsProvider(IPluginRepository pluginRepository, IPluginManagementService pluginManagementService)
{
_pluginRepository = pluginRepository;
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
}
protected override PluginSettings CreateInstance(IContext context)
@ -27,7 +27,7 @@ namespace Artemis.Core.Ninject
// First try by PluginInfo parameter
PluginInfo pluginInfo = parentRequest.Parameters.FirstOrDefault(p => p.Name == "PluginInfo")?.GetValue(context, null) as PluginInfo;
if (pluginInfo == null)
pluginInfo = _pluginService.GetPluginByAssembly(parentRequest.Service.Assembly)?.PluginInfo;
pluginInfo = _pluginManagementService.GetPluginByAssembly(parentRequest.Service.Assembly)?.PluginInfo;
// Fall back to assembly based detection
if (pluginInfo == null)
throw new ArtemisCoreException("PluginSettings can only be injected with the PluginInfo parameter provided " +

View File

@ -1,18 +1,131 @@
namespace Artemis.Core
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using Artemis.Storage.Entities.Plugins;
using McMaster.NETCore.Plugins;
using Ninject;
using Stylet;
namespace Artemis.Core
{
/// <summary>
/// Represents a plugin
/// </summary>
public abstract class Plugin
public class Plugin : PropertyChangedBase, IDisposable
{
private readonly List<PluginImplementation> _implementations;
private bool _isEnabled;
internal Plugin(PluginInfo info, DirectoryInfo directory)
{
Info = info;
Directory = directory;
_implementations = new List<PluginImplementation>();
}
/// <summary>
/// Gets the plugin GUID
/// </summary>
public Guid Guid => Info.Guid;
/// <summary>
/// Gets the plugin info related to this plugin
/// </summary>
public PluginInfo Info { get; internal set; }
public PluginInfo Info { get; }
/// <summary>
/// The plugins root directory
/// </summary>
public DirectoryInfo Directory { get; }
/// <summary>
/// Gets or sets a configuration dialog for this plugin that is accessible in the UI under Settings > Plugins
/// </summary>
public PluginConfigurationDialog ConfigurationDialog { get; protected set; }
public PluginConfigurationDialog? ConfigurationDialog { get; protected set; }
/// <summary>
/// Indicates whether the user enabled the plugin or not
/// </summary>
public bool IsEnabled
{
get => _isEnabled;
private set => SetAndNotify(ref _isEnabled, value);
}
/// <summary>
/// Gets a read-only collection of all implementations this plugin provides
/// </summary>
public ReadOnlyCollection<PluginImplementation> Implementations => _implementations.AsReadOnly();
/// <summary>
/// The assembly the plugin code lives in
/// </summary>
internal Assembly? Assembly { get; set; }
/// <summary>
/// The Ninject kernel of the plugin
/// </summary>
internal IKernel? Kernel { get; set; }
/// <summary>
/// The PluginLoader backing this plugin
/// </summary>
internal PluginLoader? PluginLoader { get; set; }
/// <summary>
/// The entity representing the plugin
/// </summary>
internal PluginEntity? Entity { get; set; }
/// <summary>
/// Resolves the relative path provided in the <paramref name="path" /> parameter to an absolute path
/// </summary>
/// <param name="path">The path to resolve</param>
/// <returns>An absolute path pointing to the provided relative path</returns>
public string? ResolveRelativePath(string path)
{
return path == null ? null : Path.Combine(Directory.FullName, path);
}
internal void ApplyToEntity()
{
Entity.Id = Guid;
Entity.IsEnabled = IsEnabled;
}
internal void AddImplementation(PluginImplementation implementation)
{
implementation.Plugin = this;
_implementations.Add(implementation);
}
public void SetEnabled(bool enable)
{
if (IsEnabled == enable)
return;
if (!enable && Implementations.Any(e => e.IsEnabled))
throw new ArtemisCoreException("Cannot disable this plugin because it still has enabled implementations");
}
/// <inheritdoc />
public override string ToString()
{
return Info.ToString();
}
public void Dispose()
{
foreach (PluginImplementation pluginImplementation in Implementations)
pluginImplementation.Dispose();
Kernel?.Dispose();
PluginLoader?.Dispose();
}
}
}

View File

@ -1,27 +1,44 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Artemis.Storage.Entities.Plugins;
using Stylet;
namespace Artemis.Core
{
/// <summary>
/// Represents an implementation of a certain type provided by a plugin
/// </summary>
public abstract class PluginImplementation : IDisposable
public abstract class PluginImplementation : PropertyChangedBase, IDisposable
{
private Exception? _loadException;
private bool _isEnabled;
/// <summary>
/// Gets the plugin that provides this implementation
/// </summary>
public Plugin Plugin { get; internal set; }
/// <summary>
/// Gets the plugin info related to this plugin
/// </summary>
public PluginInfo PluginInfo { get; internal set; }
public Plugin? Plugin { get; internal set; }
/// <summary>
/// Gets whether the plugin is enabled
/// </summary>
public bool IsEnabled { get; internal set; }
public bool IsEnabled
{
get => _isEnabled;
internal set => SetAndNotify(ref _isEnabled, value);
}
/// <summary>
/// Gets the exception thrown while loading
/// </summary>
public Exception? LoadException
{
get => _loadException;
internal set => SetAndNotify(ref _loadException, value);
}
internal PluginImplementationEntity? Entity { get; set; }
/// <summary>
/// Called when the implementation is activated
@ -35,62 +52,75 @@ namespace Artemis.Core
internal void SetEnabled(bool enable, bool isAutoEnable = false)
{
if (enable && !IsEnabled)
if (enable == IsEnabled)
return;
if (Plugin == null)
throw new ArtemisCoreException("Cannot enable a plugin implementation that is not associated with a plugin");
lock (Plugin)
{
if (!Plugin.IsEnabled)
throw new ArtemisCoreException("Cannot enable a plugin implementation of a disabled plugin");
if (!enable)
{
IsEnabled = false;
// Even if disable failed, still leave it in a disabled state to avoid more issues
InternalDisable();
OnDisabled();
return;
}
try
{
if (isAutoEnable && PluginInfo.GetLockFileCreated())
if (isAutoEnable && GetLockFileCreated())
{
// Don't wrap existing lock exceptions, simply rethrow them
if (PluginInfo.LoadException is ArtemisPluginLockException)
throw PluginInfo.LoadException;
if (LoadException is ArtemisPluginLockException)
throw LoadException;
throw new ArtemisPluginLockException(PluginInfo.LoadException);
throw new ArtemisPluginLockException(LoadException);
}
IsEnabled = true;
PluginInfo.IsEnabled = true;
PluginInfo.CreateLockFile();
CreateLockFile();
// Allow up to 15 seconds for plugins to activate.
// This means plugins that need more time should do their long running tasks in a background thread, which is intentional
// Little meh: Running this from a different thread could cause deadlocks
Task enableTask = Task.Run(InternalEnable);
if (!enableTask.Wait(TimeSpan.FromSeconds(15)))
throw new ArtemisPluginException(PluginInfo, "Plugin load timeout");
ManualResetEvent wait = new ManualResetEvent(false);
Thread work = new Thread(() =>
{
InternalEnable();
wait.Set();
});
work.Start();
wait.WaitOne(TimeSpan.FromSeconds(15));
if (work.IsAlive)
{
work.Abort();
throw new ArtemisPluginException(Plugin, "Plugin load timeout");
}
PluginInfo.LoadException = null;
LoadException = null;
OnEnabled();
}
// If enable failed, put it back in a disabled state
catch (Exception e)
{
IsEnabled = false;
PluginInfo.IsEnabled = false;
PluginInfo.LoadException = e;
LoadException = e;
throw;
}
finally
{
if (!(PluginInfo.LoadException is ArtemisPluginLockException))
PluginInfo.DeleteLockFile();
// Clean up the lock file unless the failure was due to the lock file
// After all, we failed but not miserably :)
if (!(LoadException is ArtemisPluginLockException))
DeleteLockFile();
}
}
else if (!enable && IsEnabled)
{
IsEnabled = false;
PluginInfo.IsEnabled = false;
// Even if disable failed, still leave it in a disabled state to avoid more issues
InternalDisable();
OnDisabled();
}
// A failed load is still enabled in plugin info (to avoid disabling it permanently after a fail)
// update even that when manually disabling
else if (!enable && !IsEnabled)
{
PluginInfo.IsEnabled = false;
}
}
internal virtual void InternalEnable()
@ -109,6 +139,35 @@ namespace Artemis.Core
Disable();
}
#region Loading
internal void CreateLockFile()
{
if (Plugin == null)
throw new ArtemisCoreException("Cannot lock a plugin implementation that is not associated with a plugin");
File.Create(Plugin.ResolveRelativePath($"{GetType().FullName}.lock")).Close();
}
internal void DeleteLockFile()
{
if (Plugin == null)
throw new ArtemisCoreException("Cannot lock a plugin implementation that is not associated with a plugin");
if (GetLockFileCreated())
File.Delete(Plugin.ResolveRelativePath($"{GetType().FullName}.lock"));
}
internal bool GetLockFileCreated()
{
if (Plugin == null)
throw new ArtemisCoreException("Cannot lock a plugin implementation that is not associated with a plugin");
return File.Exists(Plugin.ResolveRelativePath($"{GetType().FullName}.lock"));
}
#endregion
#region Events
/// <summary>

View File

@ -1,10 +1,5 @@
using System;
using System.IO;
using System.Reflection;
using Artemis.Storage.Entities.Plugins;
using McMaster.NETCore.Plugins;
using Newtonsoft.Json;
using Ninject;
using Stylet;
namespace Artemis.Core
@ -16,14 +11,11 @@ namespace Artemis.Core
public class PluginInfo : PropertyChangedBase
{
private string _description;
private DirectoryInfo _directory;
private Guid _guid;
private string _icon;
private Plugin _instance;
private bool _isEnabled;
private Exception _loadException;
private string _main;
private string _name;
private Plugin _plugin;
private Version _version;
internal PluginInfo()
@ -92,93 +84,19 @@ namespace Artemis.Core
internal set => SetAndNotify(ref _main, value);
}
/// <summary>
/// The plugins root directory
/// </summary>
public DirectoryInfo Directory
{
get => _directory;
internal set => SetAndNotify(ref _directory, value);
}
/// <summary>
/// Gets the plugin this info is associated with
/// </summary>
public Plugin Instance
public Plugin Plugin
{
get => _instance;
internal set => SetAndNotify(ref _instance, value);
get => _plugin;
internal set => SetAndNotify(ref _plugin, value);
}
/// <summary>
/// Indicates whether the user enabled the plugin or not
/// </summary>
public bool IsEnabled
{
get => _isEnabled;
internal set => SetAndNotify(ref _isEnabled, value);
}
/// <summary>
/// Gets the exception thrown while loading
/// </summary>
public Exception LoadException
{
get => _loadException;
internal set => SetAndNotify(ref _loadException, value);
}
/// <summary>
/// The assembly the plugin code lives in
/// </summary>
public Assembly Assembly { get; internal set; }
/// <summary>
/// The Ninject kernel of the plugin
/// </summary>
public IKernel Kernel { get; internal set; }
/// <summary>
/// The PluginLoader backing this plugin
/// </summary>
internal PluginLoader PluginLoader { get; set; }
/// <summary>
/// The entity representing the plugin
/// </summary>
internal PluginEntity PluginEntity { get; set; }
/// <inheritdoc />
public override string ToString()
{
return $"{Name} v{Version} - {Guid}";
}
public string? ResolveRelativePath(string path)
{
return path == null ? null : Path.Combine(Directory.FullName, path);
}
internal void ApplyToEntity()
{
PluginEntity.Id = Guid;
PluginEntity.IsEnabled = IsEnabled;
}
internal void CreateLockFile()
{
File.Create(Path.Combine(Directory.FullName, "artemis.lock")).Close();
}
internal void DeleteLockFile()
{
if (GetLockFileCreated())
File.Delete(Path.Combine(Directory.FullName, "artemis.lock"));
}
internal bool GetLockFileCreated()
{
return File.Exists(Path.Combine(Directory.FullName, "artemis.lock"));
}
}
}

View File

@ -19,9 +19,9 @@ namespace Artemis.Core
Interval = interval;
Action = action;
PluginInfo.Instance.Enabled += InstanceOnEnabled;
PluginInfo.Instance.Disabled += InstanceOnDisabled;
if (PluginInfo.Instance.IsEnabled)
PluginInfo.Plugin.Enabled += InstanceOnEnabled;
PluginInfo.Plugin.Disabled += InstanceOnDisabled;
if (PluginInfo.Plugin.IsEnabled)
Start();
}
@ -31,9 +31,9 @@ namespace Artemis.Core
Interval = interval;
AsyncAction = asyncAction;
PluginInfo.Instance.Enabled += InstanceOnEnabled;
PluginInfo.Instance.Disabled += InstanceOnDisabled;
if (PluginInfo.Instance.IsEnabled)
PluginInfo.Plugin.Enabled += InstanceOnEnabled;
PluginInfo.Plugin.Disabled += InstanceOnDisabled;
if (PluginInfo.Plugin.IsEnabled)
Start();
}
@ -66,7 +66,7 @@ namespace Artemis.Core
{
lock (this)
{
if (!PluginInfo.Instance.IsEnabled)
if (!PluginInfo.Plugin.IsEnabled)
throw new ArtemisPluginException("Cannot start a timed update for a disabled plugin");
if (_timer != null)
@ -99,7 +99,7 @@ namespace Artemis.Core
private void TimerOnElapsed(object sender, ElapsedEventArgs e)
{
if (!PluginInfo.Instance.IsEnabled)
if (!PluginInfo.Plugin.IsEnabled)
return;
lock (this)
@ -108,7 +108,7 @@ namespace Artemis.Core
_lastEvent = DateTime.Now;
// Modules don't always want to update, honor that
if (PluginInfo.Instance is Module module && !module.IsUpdateAllowed)
if (PluginInfo.Plugin is Module module && !module.IsUpdateAllowed)
return;
if (Action != null)

View File

@ -28,7 +28,7 @@ namespace Artemis.Core.Services
private readonly Stopwatch _frameStopWatch;
private readonly ILogger _logger;
private readonly PluginSetting<LogEventLevel> _loggingLevel;
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private readonly IProfileService _profileService;
private readonly IRgbService _rgbService;
private readonly ISurfaceService _surfaceService;
@ -36,14 +36,14 @@ namespace Artemis.Core.Services
private List<Module> _modules;
// ReSharper disable once UnusedParameter.Local - Storage migration service is injected early to ensure it runs before anything else
public CoreService(IKernel kernel, ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginService pluginService,
public CoreService(IKernel kernel, ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginManagementService pluginManagementService,
IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService, IModuleService moduleService)
{
Kernel = kernel;
Constants.CorePluginInfo.Kernel = kernel;
_logger = logger;
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
_rgbService = rgbService;
_surfaceService = surfaceService;
_profileService = profileService;
@ -57,8 +57,8 @@ namespace Artemis.Core.Services
_rgbService.Surface.Updated += SurfaceOnUpdated;
_loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel();
_pluginService.PluginEnabled += (sender, args) => UpdatePluginCache();
_pluginService.PluginDisabled += (sender, args) => UpdatePluginCache();
_pluginManagementService.PluginEnabled += (sender, args) => UpdatePluginCache();
_pluginManagementService.PluginDisabled += (sender, args) => UpdatePluginCache();
}
public TimeSpan FrameTime { get; private set; }
@ -68,7 +68,7 @@ namespace Artemis.Core.Services
public void Dispose()
{
// Dispose services
_pluginService.Dispose();
_pluginManagementService.Dispose();
}
public bool IsInitialized { get; set; }
@ -85,8 +85,8 @@ namespace Artemis.Core.Services
DeserializationLogger.Initialize(Kernel);
// Initialize the services
_pluginService.CopyBuiltInPlugins();
_pluginService.LoadPlugins(StartupArguments.Contains("--ignore-plugin-lock"));
_pluginManagementService.CopyBuiltInPlugins();
_pluginManagementService.LoadPlugins(StartupArguments.Contains("--ignore-plugin-lock"));
ArtemisSurface surfaceConfig = _surfaceService.ActiveSurface;
if (surfaceConfig != null)
@ -133,8 +133,8 @@ namespace Artemis.Core.Services
private void UpdatePluginCache()
{
_modules = _pluginService.GetPluginsOfType<Module>().Where(p => p.IsEnabled).ToList();
_dataModelExpansions = _pluginService.GetPluginsOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled).ToList();
_modules = _pluginManagementService.GetPluginsOfType<Module>().Where(p => p.IsEnabled).ToList();
_dataModelExpansions = _pluginManagementService.GetPluginsOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled).ToList();
}
private void ConfigureJsonConvert()

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using RGB.NET.Core;
@ -8,7 +9,7 @@ namespace Artemis.Core.Services
/// <summary>
/// A service providing plugin management
/// </summary>
public interface IPluginService : IArtemisService, IDisposable
public interface IPluginManagementService : IArtemisService, IDisposable
{
/// <summary>
/// Indicates whether or not plugins are currently being loaded
@ -35,7 +36,7 @@ namespace Artemis.Core.Services
/// Loads the plugin defined in the provided <see cref="PluginInfo" />
/// </summary>
/// <param name="pluginInfo">The plugin info defining the plugin to load</param>
void LoadPlugin(PluginInfo pluginInfo);
void LoadPlugin(DirectoryInfo pluginInfo);
/// <summary>
/// Unloads the plugin defined in the provided <see cref="PluginInfo" />
@ -48,13 +49,13 @@ namespace Artemis.Core.Services
/// </summary>
/// <param name="pluginImplementation"></param>
/// <param name="isAutoEnable">If true, fails if there is a lock file present</param>
void EnablePlugin(PluginImplementation pluginImplementation, bool isAutoEnable = false);
void EnablePluginImplementation(PluginImplementation pluginImplementation, bool isAutoEnable = false);
/// <summary>
/// Disables the provided plugin
/// </summary>
/// <param name="pluginImplementation"></param>
void DisablePlugin(PluginImplementation pluginImplementation);
void DisablePluginImplementation(PluginImplementation pluginImplementation);
/// <summary>
/// Finds the plugin info related to the plugin

View File

@ -17,22 +17,22 @@ namespace Artemis.Core.Services
private static readonly SemaphoreSlim ActiveModuleSemaphore = new SemaphoreSlim(1, 1);
private readonly ILogger _logger;
private readonly IModuleRepository _moduleRepository;
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private readonly IProfileService _profileService;
public ModuleService(ILogger logger, IModuleRepository moduleRepository, IPluginService pluginService, IProfileService profileService)
public ModuleService(ILogger logger, IModuleRepository moduleRepository, IPluginManagementService pluginManagementService, IProfileService profileService)
{
_logger = logger;
_moduleRepository = moduleRepository;
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
_profileService = profileService;
_pluginService.PluginEnabled += PluginServiceOnPluginEnabled;
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginManagementEnabled;
Timer activationUpdateTimer = new Timer(2000);
activationUpdateTimer.Start();
activationUpdateTimer.Elapsed += ActivationUpdateTimerOnElapsed;
foreach (Module module in _pluginService.GetPluginsOfType<Module>())
foreach (Module module in _pluginManagementService.GetPluginsOfType<Module>())
InitialiseOrApplyPriority(module);
}
@ -56,7 +56,7 @@ namespace Artemis.Core.Services
_logger.Information("Clearing active module override");
// Always deactivate all other modules whenever override is called
List<Module> modules = _pluginService.GetPluginsOfType<Module>().ToList();
List<Module> modules = _pluginManagementService.GetPluginsOfType<Module>().ToList();
foreach (Module module in modules.Where(m => m != overrideModule))
OverrideDeactivate(module, overrideModule != null);
@ -97,7 +97,7 @@ namespace Artemis.Core.Services
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
List<Module> modules = _pluginService.GetPluginsOfType<Module>().ToList();
List<Module> modules = _pluginManagementService.GetPluginsOfType<Module>().ToList();
List<Task> tasks = new List<Task>();
foreach (Module module in modules)
{
@ -128,7 +128,7 @@ namespace Artemis.Core.Services
if (module.PriorityCategory == category && module.Priority == priority)
return;
List<Module> modules = _pluginService.GetPluginsOfType<Module>().Where(m => m.PriorityCategory == category).OrderBy(m => m.Priority).ToList();
List<Module> modules = _pluginManagementService.GetPluginsOfType<Module>().Where(m => m.PriorityCategory == category).OrderBy(m => m.Priority).ToList();
if (modules.Contains(module))
modules.Remove(module);
@ -234,9 +234,9 @@ namespace Artemis.Core.Services
}
}
private void PluginServiceOnPluginEnabled(object sender, PluginEventArgs e)
private void PluginManagementServiceOnPluginManagementEnabled(object sender, PluginEventArgs e)
{
if (e.PluginInfo.Instance is Module module)
if (e.PluginInfo.Plugin is Module module)
InitialiseOrApplyPriority(module);
}

View File

@ -22,19 +22,19 @@ namespace Artemis.Core.Services
/// <summary>
/// Provides access to plugin loading and unloading
/// </summary>
internal class PluginService : IPluginService
internal class PluginManagementService : IPluginManagementService
{
private readonly IKernel _kernel;
private readonly ILogger _logger;
private readonly IPluginRepository _pluginRepository;
private readonly List<PluginInfo> _plugins;
private readonly List<Plugin> _plugins;
public PluginService(IKernel kernel, ILogger logger, IPluginRepository pluginRepository)
public PluginManagementService(IKernel kernel, ILogger logger, IPluginRepository pluginRepository)
{
_kernel = kernel;
_logger = logger;
_pluginRepository = pluginRepository;
_plugins = new List<PluginInfo>();
_plugins = new List<Plugin>();
// Ensure the plugins directory exists
if (!Directory.Exists(Constants.DataFolder + "plugins"))
@ -43,6 +43,8 @@ namespace Artemis.Core.Services
public bool LoadingPlugins { get; private set; }
#region Built in plugins
public void CopyBuiltInPlugins()
{
OnCopyingBuildInPlugins();
@ -101,6 +103,10 @@ namespace Artemis.Core.Services
}
}
#endregion
#region Plugins
public void LoadPlugins(bool ignorePluginLock)
{
if (LoadingPlugins)
@ -119,15 +125,7 @@ namespace Artemis.Core.Services
{
try
{
// Load the metadata
string metadataFile = Path.Combine(subDirectory.FullName, "plugin.json");
if (!File.Exists(metadataFile)) _logger.Warning(new ArtemisPluginException("Couldn't find the plugins metadata file at " + metadataFile), "Plugin exception");
// Locate the main entry
PluginInfo pluginInfo = JsonConvert.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile));
pluginInfo.Directory = subDirectory;
LoadPlugin(pluginInfo);
LoadPlugin(subDirectory);
}
catch (Exception e)
{
@ -136,15 +134,18 @@ namespace Artemis.Core.Services
}
// Activate plugins after they are all loaded
foreach (PluginInfo pluginInfo in _plugins.Where(p => p.IsEnabled))
foreach (Plugin plugin in _plugins.Where(p => p.Entity.IsEnabled))
{
try
foreach (PluginImplementation pluginImplementation in plugin.Implementations.Where(i => i.Entity.IsEnabled))
{
EnablePlugin(pluginInfo.Instance, !ignorePluginLock);
}
catch (Exception)
{
// ignored, logged in EnablePlugin
try
{
EnablePluginImplementation(pluginImplementation, !ignorePluginLock);
}
catch (Exception)
{
// ignored, logged in EnablePlugin
}
}
}
@ -164,30 +165,37 @@ namespace Artemis.Core.Services
}
}
public void LoadPlugin(PluginInfo pluginInfo)
public void LoadPlugin(DirectoryInfo pluginDirectory)
{
lock (_plugins)
{
_logger.Debug("Loading plugin {pluginInfo}", pluginInfo);
OnPluginLoading(new PluginEventArgs(pluginInfo));
_logger.Debug("Loading plugin from {directory}", pluginDirectory.FullName);
// Unload the plugin first if it is already loaded
if (_plugins.Contains(pluginInfo))
UnloadPlugin(pluginInfo);
// Load the metadata
string metadataFile = Path.Combine(pluginDirectory.FullName, "plugin.json");
if (!File.Exists(metadataFile))
_logger.Warning(new ArtemisPluginException("Couldn't find the plugins metadata file at " + metadataFile), "Plugin exception");
PluginEntity pluginEntity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid);
if (pluginEntity == null)
pluginEntity = new PluginEntity {Id = pluginInfo.Guid, IsEnabled = true};
// PluginInfo contains the ID which we need to move on
PluginInfo pluginInfo = JsonConvert.DeserializeObject<PluginInfo>(File.ReadAllText(metadataFile));
pluginInfo.PluginEntity = pluginEntity;
pluginInfo.IsEnabled = pluginEntity.IsEnabled;
// Ensure the plugin is not already loaded
if (_plugins.Any(p => p.Guid == pluginInfo.Guid))
throw new ArtemisCoreException("Cannot load a plugin that is already loaded");
string mainFile = Path.Combine(pluginInfo.Directory.FullName, pluginInfo.Main);
Plugin plugin = new Plugin(pluginInfo, pluginDirectory);
OnPluginLoading(new PluginEventArgs(plugin));
// Load the entity and fall back on creating a new one
plugin.Entity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid) ?? new PluginEntity { Id = plugin.Guid, IsEnabled = true };
// Locate the main assembly entry
string? mainFile = plugin.ResolveRelativePath(plugin.Info.Main);
if (!File.Exists(mainFile))
throw new ArtemisPluginException(pluginInfo, "Couldn't find the plugins main entry at " + mainFile);
throw new ArtemisPluginException(plugin, "Couldn't find the plugins main entry at " + mainFile);
// Load the plugin, all types implementing Plugin and register them with DI
pluginInfo.PluginLoader = PluginLoader.CreateFromAssemblyFile(mainFile, configure =>
plugin.PluginLoader = PluginLoader.CreateFromAssemblyFile(mainFile!, configure =>
{
configure.IsUnloadable = true;
configure.PreferSharedTypes = true;
@ -195,57 +203,58 @@ namespace Artemis.Core.Services
try
{
pluginInfo.Assembly = pluginInfo.PluginLoader.LoadDefaultAssembly();
plugin.Assembly = plugin.PluginLoader.LoadDefaultAssembly();
}
catch (Exception e)
{
throw new ArtemisPluginException(pluginInfo, "Failed to load the plugins assembly", e);
throw new ArtemisPluginException(plugin, "Failed to load the plugins assembly", e);
}
// Get the Plugin implementation from the main assembly and if there is only one, instantiate it
List<Type> pluginTypes;
List<Type> implementationTypes;
try
{
pluginTypes = pluginInfo.Assembly.GetTypes().Where(t => typeof(PluginImplementation).IsAssignableFrom(t)).ToList();
implementationTypes = plugin.Assembly.GetTypes().Where(t => typeof(PluginImplementation).IsAssignableFrom(t)).ToList();
}
catch (ReflectionTypeLoadException e)
{
throw new ArtemisPluginException(pluginInfo, "Failed to initialize the plugin assembly", new AggregateException(e.LoaderExceptions));
throw new ArtemisPluginException(plugin, "Failed to initialize the plugin assembly", new AggregateException(e.LoaderExceptions));
}
if (pluginTypes.Count > 1)
throw new ArtemisPluginException(pluginInfo, $"Plugin contains {pluginTypes.Count} implementations of Plugin, only 1 allowed");
if (pluginTypes.Count == 0)
throw new ArtemisPluginException(pluginInfo, "Plugin contains no implementation of Plugin");
// Create the Ninject child kernel and load the module
plugin.Kernel = new ChildKernel(_kernel);
plugin.Kernel.Load(new PluginModule(pluginInfo));
Type pluginType = pluginTypes.Single();
try
if (!implementationTypes.Any())
_logger.Warning("Plugin {plugin} contains no implementations", plugin);
// Create instances of each implementation and add them to the plugin
// Construction should be simple and not contain any logic so failure at this point means the entire plugin fails
foreach (Type implementationType in implementationTypes)
{
IParameter[] parameters = {
new Parameter("PluginInfo", pluginInfo, false)
};
pluginInfo.Kernel = new ChildKernel(_kernel);
pluginInfo.Kernel.Load(new PluginModule(pluginInfo));
pluginInfo.Instance = (PluginImplementation) pluginInfo.Kernel.Get(pluginType, constraint: null, parameters: parameters);
pluginInfo.Instance.PluginInfo = pluginInfo;
}
catch (Exception e)
{
throw new ArtemisPluginException(pluginInfo, "Failed to instantiate the plugin", e);
try
{
PluginImplementation instance = (PluginImplementation)plugin.Kernel.Get(implementationType);
plugin.AddImplementation(instance);
}
catch (Exception e)
{
throw new ArtemisPluginException(plugin, "Failed to load instantiate implementation", e);
}
}
_plugins.Add(pluginInfo);
OnPluginLoaded(new PluginEventArgs(pluginInfo));
_plugins.Add(plugin);
OnPluginLoaded(new PluginEventArgs(plugin));
}
}
public void UnloadPlugin(PluginInfo pluginInfo)
public void UnloadPlugin(Plugin plugin)
{
lock (_plugins)
{
try
{
pluginInfo.Instance.SetEnabled(false);
plugin.SetEnabled(false);
}
catch (Exception)
{
@ -253,23 +262,24 @@ namespace Artemis.Core.Services
}
finally
{
OnPluginDisabled(new PluginEventArgs(pluginInfo));
OnPluginDisabled(new PluginEventArgs(plugin));
}
pluginInfo.Instance.Dispose();
pluginInfo.PluginLoader.Dispose();
pluginInfo.Kernel.Dispose();
plugin.Dispose();
_plugins.Remove(plugin);
_plugins.Remove(pluginInfo);
OnPluginUnloaded(new PluginEventArgs(pluginInfo));
OnPluginUnloaded(new PluginEventArgs(plugin));
}
}
public void EnablePlugin(PluginImplementation pluginImplementation, bool isAutoEnable = false)
#endregion
#region Implementations
public void EnablePluginImplementation(PluginImplementation pluginImplementation, bool isAutoEnable = false)
{
_logger.Debug("Enabling plugin {pluginInfo}", pluginImplementation.PluginInfo);
OnPluginEnabling(new PluginEventArgs(pluginImplementation.PluginInfo));
_logger.Debug("Enabling plugin implementation {implementation} - {plugin}", pluginImplementation, pluginImplementation.Plugin);
lock (_plugins)
{
@ -306,10 +316,10 @@ namespace Artemis.Core.Services
}
}
OnPluginEnabled(new PluginEventArgs(pluginImplementation.PluginInfo));
OnPluginImplementationEnabled(new PluginImplementationEventArgs(pluginImplementation));
}
public void DisablePlugin(PluginImplementation pluginImplementation)
public void DisablePluginImplementation(PluginImplementation pluginImplementation)
{
lock (_plugins)
{
@ -335,9 +345,12 @@ namespace Artemis.Core.Services
OnPluginDisabled(new PluginEventArgs(pluginImplementation.PluginInfo));
}
#endregion
public PluginInfo GetPluginInfo(PluginImplementation pluginImplementation)
{
return _plugins.FirstOrDefault(p => p.Instance == pluginImplementation);
return _plugins.FirstOrDefault(p => p.Plugin == pluginImplementation);
}
public List<PluginInfo> GetAllPluginInfo()
@ -347,12 +360,12 @@ namespace Artemis.Core.Services
public List<T> GetPluginsOfType<T>() where T : PluginImplementation
{
return _plugins.Where(p => p.IsEnabled && p.Instance is T).Select(p => (T) p.Instance).ToList();
return _plugins.Where(p => p.IsEnabled && p.Plugin is T).Select(p => (T) p.Plugin).ToList();
}
public PluginImplementation GetPluginByAssembly(Assembly assembly)
{
return _plugins.FirstOrDefault(p => p.Assembly == assembly)?.Instance;
return _plugins.FirstOrDefault(p => p.Assembly == assembly)?.Plugin;
}
public PluginImplementation GetPluginByDevice(IRGBDevice rgbDevice)
@ -362,8 +375,8 @@ namespace Artemis.Core.Services
public PluginImplementation GetCallingPlugin()
{
StackTrace stackTrace = new StackTrace(); // get call stack
StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
StackTrace stackTrace = new StackTrace(); // get call stack
StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
foreach (StackFrame stackFrame in stackFrames)
{
@ -395,7 +408,7 @@ namespace Artemis.Core.Services
if (createLockFile)
File.Create(Path.Combine(pluginDirectory.FullName, "artemis.lock")).Close();
}
#region Events
public event EventHandler CopyingBuildInPlugins;
@ -406,6 +419,9 @@ namespace Artemis.Core.Services
public event EventHandler<PluginEventArgs> PluginEnabled;
public event EventHandler<PluginEventArgs> PluginDisabled;
public event EventHandler<PluginImplementationEventArgs> PluginImplementationEnabled;
public event EventHandler<PluginImplementationEventArgs> PluginImplementationDisabled;
protected virtual void OnCopyingBuildInPlugins()
{
CopyingBuildInPlugins?.Invoke(this, EventArgs.Empty);
@ -441,6 +457,16 @@ namespace Artemis.Core.Services
PluginDisabled?.Invoke(this, e);
}
protected virtual void OnPluginImplementationDisabled(PluginImplementationEventArgs e)
{
PluginImplementationDisabled?.Invoke(this, e);
}
protected virtual void OnPluginImplementationEnabled(PluginImplementationEventArgs e)
{
PluginImplementationEnabled?.Invoke(this, e);
}
#endregion
}
}

View File

@ -8,16 +8,16 @@ namespace Artemis.Core.Services
{
internal class DataModelService : IDataModelService
{
public DataModelService(IPluginService pluginService)
public DataModelService(IPluginManagementService pluginManagementService)
{
// Add data models of already loaded plugins
foreach (Module module in pluginService.GetPluginsOfType<Module>().Where(p => p.IsEnabled))
foreach (Module module in pluginManagementService.GetPluginsOfType<Module>().Where(p => p.IsEnabled))
AddModuleDataModel(module);
foreach (BaseDataModelExpansion dataModelExpansion in pluginService.GetPluginsOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled))
foreach (BaseDataModelExpansion dataModelExpansion in pluginManagementService.GetPluginsOfType<BaseDataModelExpansion>().Where(p => p.IsEnabled))
AddDataModelExpansionDataModel(dataModelExpansion);
// Add data models of new plugins when they get enabled
pluginService.PluginEnabled += PluginServiceOnPluginEnabled;
pluginManagementService.PluginEnabled += PluginServiceOnPluginEnabled;
}
public DataModelRegistration RegisterDataModel(DataModel dataModel)
@ -56,9 +56,9 @@ namespace Artemis.Core.Services
private void PluginServiceOnPluginEnabled(object sender, PluginEventArgs e)
{
if (e.PluginInfo.Instance is Module module)
if (e.PluginInfo.Plugin is Module module)
AddModuleDataModel(module);
else if (e.PluginInfo.Instance is BaseDataModelExpansion dataModelExpansion)
else if (e.PluginInfo.Plugin is BaseDataModelExpansion dataModelExpansion)
AddDataModelExpansionDataModel(dataModelExpansion);
}

View File

@ -13,19 +13,19 @@ namespace Artemis.Core.Services
internal class ProfileService : IProfileService
{
private readonly ILogger _logger;
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private readonly IProfileRepository _profileRepository;
private readonly ISurfaceService _surfaceService;
internal ProfileService(ILogger logger,
IPluginService pluginService,
IPluginManagementService pluginManagementService,
ISurfaceService surfaceService,
IConditionOperatorService conditionOperatorService,
IDataBindingService dataBindingService,
IProfileRepository profileRepository)
{
_logger = logger;
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
_surfaceService = surfaceService;
_profileRepository = profileRepository;
@ -118,15 +118,15 @@ namespace Artemis.Core.Services
}
// This could happen during activation so subscribe to it
_pluginService.PluginEnabled += ActivatingProfilePluginToggle;
_pluginService.PluginDisabled += ActivatingProfilePluginToggle;
_pluginManagementService.PluginEnabled += ActivatingProfilePluginToggle;
_pluginManagementService.PluginDisabled += ActivatingProfilePluginToggle;
_surfaceService.SurfaceConfigurationUpdated += ActivatingProfileSurfaceUpdate;
await profileDescriptor.ProfileModule.ChangeActiveProfileAnimated(profile, _surfaceService.ActiveSurface);
SaveActiveProfile(profileDescriptor.ProfileModule);
_pluginService.PluginEnabled -= ActivatingProfilePluginToggle;
_pluginService.PluginDisabled -= ActivatingProfilePluginToggle;
_pluginManagementService.PluginEnabled -= ActivatingProfilePluginToggle;
_pluginManagementService.PluginDisabled -= ActivatingProfilePluginToggle;
_surfaceService.SurfaceConfigurationUpdated -= ActivatingProfileSurfaceUpdate;
return profile;
@ -285,7 +285,7 @@ namespace Artemis.Core.Services
/// <param name="surface"></param>
private void ActiveProfilesPopulateLeds(ArtemisSurface surface)
{
List<ProfileModule> profileModules = _pluginService.GetPluginsOfType<ProfileModule>();
List<ProfileModule> profileModules = _pluginManagementService.GetPluginsOfType<ProfileModule>();
foreach (ProfileModule profileModule in profileModules.Where(p => p.ActiveProfile != null).ToList())
profileModule.ActiveProfile.PopulateLeds(surface);
}

View File

@ -12,18 +12,18 @@ namespace Artemis.Core.Services
internal class SurfaceService : ISurfaceService
{
private readonly ILogger _logger;
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private readonly PluginSetting<double> _renderScaleSetting;
private readonly IRgbService _rgbService;
private readonly List<ArtemisSurface> _surfaceConfigurations;
private readonly ISurfaceRepository _surfaceRepository;
public SurfaceService(ILogger logger, ISurfaceRepository surfaceRepository, IRgbService rgbService, IPluginService pluginService, ISettingsService settingsService)
public SurfaceService(ILogger logger, ISurfaceRepository surfaceRepository, IRgbService rgbService, IPluginManagementService pluginManagementService, ISettingsService settingsService)
{
_logger = logger;
_surfaceRepository = surfaceRepository;
_rgbService = rgbService;
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
_surfaceConfigurations = new List<ArtemisSurface>();
_renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 0.5);
@ -44,7 +44,7 @@ namespace Artemis.Core.Services
// Add all current devices
foreach (IRGBDevice rgbDevice in _rgbService.LoadedDevices)
{
PluginImplementation pluginImplementation = _pluginService.GetPluginByDevice(rgbDevice);
PluginImplementation pluginImplementation = _pluginManagementService.GetPluginByDevice(rgbDevice);
configuration.Devices.Add(new ArtemisDevice(rgbDevice, pluginImplementation, configuration));
}
@ -136,7 +136,7 @@ namespace Artemis.Core.Services
IRGBDevice device = _rgbService.Surface.Devices.FirstOrDefault(d => d.GetDeviceIdentifier() == position.DeviceIdentifier);
if (device != null)
{
PluginImplementation pluginImplementation = _pluginService.GetPluginByDevice(device);
PluginImplementation pluginImplementation = _pluginManagementService.GetPluginByDevice(device);
surfaceConfiguration.Devices.Add(new ArtemisDevice(device, pluginImplementation, surfaceConfiguration, position));
}
}
@ -178,7 +178,7 @@ namespace Artemis.Core.Services
DeviceEntity existingDeviceConfig = surface.SurfaceEntity.DeviceEntities.FirstOrDefault(d => d.DeviceIdentifier == deviceIdentifier);
if (existingDeviceConfig != null)
{
PluginImplementation pluginImplementation = _pluginService.GetPluginByDevice(rgbDevice);
PluginImplementation pluginImplementation = _pluginManagementService.GetPluginByDevice(rgbDevice);
device = new ArtemisDevice(rgbDevice, pluginImplementation, surface, existingDeviceConfig);
}
// Fall back on creating a new device
@ -189,7 +189,7 @@ namespace Artemis.Core.Services
rgbDevice.DeviceInfo,
deviceIdentifier
);
PluginImplementation pluginImplementation = _pluginService.GetPluginByDevice(rgbDevice);
PluginImplementation pluginImplementation = _pluginManagementService.GetPluginByDevice(rgbDevice);
device = new ArtemisDevice(rgbDevice, pluginImplementation, surface);
}

View File

@ -16,7 +16,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.ConditionOperator == conditionOperator))
throw new ArtemisCoreException($"Condition operator store store already contains operator '{conditionOperator.Description}'");
registration = new ConditionOperatorRegistration(conditionOperator, conditionOperator.PluginInfo.Instance) {IsInStore = true};
registration = new ConditionOperatorRegistration(conditionOperator, conditionOperator.PluginInfo.Plugin) {IsInStore = true};
Registrations.Add(registration);
}

View File

@ -16,7 +16,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.DataBindingModifierType == modifierType))
throw new ArtemisCoreException($"Data binding modifier type store already contains modifier '{modifierType.Name}'");
typeRegistration = new DataBindingModifierTypeRegistration(modifierType, modifierType.PluginInfo.Instance) { IsInStore = true };
typeRegistration = new DataBindingModifierTypeRegistration(modifierType, modifierType.PluginInfo.Plugin) { IsInStore = true };
Registrations.Add(typeRegistration);
}

View File

@ -17,7 +17,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.LayerBrushDescriptor == descriptor))
throw new ArtemisCoreException($"Store already contains layer brush '{descriptor.DisplayName}'");
registration = new LayerBrushRegistration(descriptor, descriptor.LayerBrushProvider.PluginInfo.Instance) {IsInStore = true};
registration = new LayerBrushRegistration(descriptor, descriptor.LayerBrushProvider.PluginInfo.Plugin) {IsInStore = true};
Registrations.Add(registration);
}

View File

@ -17,7 +17,7 @@ namespace Artemis.Core
if (Registrations.Any(r => r.LayerEffectDescriptor == descriptor))
throw new ArtemisCoreException($"Store already contains layer brush '{descriptor.DisplayName}'");
registration = new LayerEffectRegistration(descriptor, descriptor.LayerEffectProvider.PluginInfo.Instance) { IsInStore = true };
registration = new LayerEffectRegistration(descriptor, descriptor.LayerEffectProvider.PluginInfo.Plugin) { IsInStore = true };
Registrations.Add(registration);
}

View File

@ -9,7 +9,7 @@ namespace Artemis.Core
{
public CorePluginImplementation()
{
Constants.CorePluginInfo.Instance = this;
Constants.CorePluginInfo.Plugin = this;
IsEnabled = true;
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
namespace Artemis.Storage.Entities.Plugins
{
@ -9,5 +10,16 @@ namespace Artemis.Storage.Entities.Plugins
{
public Guid Id { get; set; }
public bool IsEnabled { get; set; }
public List<PluginImplementationEntity> Implementations { get; set; }
}
/// <summary>
/// Represents the configuration of a plugin implementation, each implementation has one configuration
/// </summary>
public class PluginImplementationEntity
{
public string Type { get; set; }
public bool IsEnabled { get; set; }
}
}

View File

@ -22,7 +22,7 @@ namespace Artemis.UI.Shared
ViewModelType = viewModelType;
if (PluginInfo != Constants.CorePluginInfo)
PluginInfo.Instance.Disabled += InstanceOnDisabled;
PluginInfo.Plugin.Disabled += InstanceOnDisabled;
}
public RegistrationType RegistrationType { get; }
@ -35,7 +35,7 @@ namespace Artemis.UI.Shared
internal void Unsubscribe()
{
if (PluginInfo != Constants.CorePluginInfo)
PluginInfo.Instance.Disabled -= InstanceOnDisabled;
PluginInfo.Plugin.Disabled -= InstanceOnDisabled;
}
private void InstanceOnDisabled(object sender, EventArgs e)

View File

@ -16,7 +16,7 @@ namespace Artemis.UI.Shared
ViewModelType = viewModelType;
if (PluginInfo != Constants.CorePluginInfo)
PluginInfo.Instance.Disabled += InstanceOnDisabled;
PluginInfo.Plugin.Disabled += InstanceOnDisabled;
}
public PluginInfo PluginInfo { get; }
@ -26,7 +26,7 @@ namespace Artemis.UI.Shared
internal void Unsubscribe()
{
if (PluginInfo != Constants.CorePluginInfo)
PluginInfo.Instance.Disabled -= InstanceOnDisabled;
PluginInfo.Plugin.Disabled -= InstanceOnDisabled;
}
private void InstanceOnDisabled(object sender, EventArgs e)

View File

@ -22,14 +22,14 @@ namespace Artemis.UI.Shared.Services
private readonly IKernel _kernel;
private readonly IViewManager _viewManager;
private readonly IWindowManager _windowManager;
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
public DialogService(IKernel kernel, IViewManager viewManager, IWindowManager windowManager, IPluginService pluginService)
public DialogService(IKernel kernel, IViewManager viewManager, IWindowManager windowManager, IPluginManagementService pluginManagementService)
{
_kernel = kernel;
_viewManager = viewManager;
_windowManager = windowManager;
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
}
private async Task<object> ShowDialog<T>(IParameter[] parameters) where T : DialogViewModelBase
@ -109,7 +109,7 @@ namespace Artemis.UI.Shared.Services
private async Task<object> ShowDialogAt<T>(string identifier, IParameter[] parameters) where T : DialogViewModelBase
{
PluginImplementation callingPluginImplementation = _pluginService.GetCallingPlugin();
PluginImplementation callingPluginImplementation = _pluginManagementService.GetCallingPlugin();
if (parameters == null) throw new ArgumentNullException(nameof(parameters));
if (callingPluginImplementation != null)

View File

@ -11,17 +11,17 @@ namespace Artemis.UI.PropertyInput
{
public class BrushPropertyInputViewModel : PropertyInputViewModel<LayerBrushReference>
{
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private BindableCollection<LayerBrushDescriptor> _descriptors;
public BrushPropertyInputViewModel(LayerProperty<LayerBrushReference> layerProperty,
IProfileEditorService profileEditorService,
IPluginService pluginService) : base(layerProperty, profileEditorService)
IPluginManagementService pluginManagementService) : base(layerProperty, profileEditorService)
{
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
_pluginService.PluginEnabled += PluginServiceOnPluginLoaded;
_pluginService.PluginDisabled += PluginServiceOnPluginLoaded;
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginManagementLoaded;
_pluginManagementService.PluginDisabled += PluginManagementServiceOnPluginManagementLoaded;
UpdateEnumValues();
}
@ -39,7 +39,7 @@ namespace Artemis.UI.PropertyInput
public void UpdateEnumValues()
{
List<LayerBrushProvider> layerBrushProviders = _pluginService.GetPluginsOfType<LayerBrushProvider>();
List<LayerBrushProvider> layerBrushProviders = _pluginManagementService.GetPluginsOfType<LayerBrushProvider>();
Descriptors = new BindableCollection<LayerBrushDescriptor>(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors));
NotifyOfPropertyChange(nameof(SelectedDescriptor));
}
@ -47,8 +47,8 @@ namespace Artemis.UI.PropertyInput
public override void Dispose()
{
_pluginService.PluginEnabled -= PluginServiceOnPluginLoaded;
_pluginService.PluginDisabled -= PluginServiceOnPluginLoaded;
_pluginManagementService.PluginEnabled -= PluginManagementServiceOnPluginManagementLoaded;
_pluginManagementService.PluginDisabled -= PluginManagementServiceOnPluginManagementLoaded;
base.Dispose();
}
@ -63,7 +63,7 @@ namespace Artemis.UI.PropertyInput
InputValue = new LayerBrushReference(value);
}
private void PluginServiceOnPluginLoaded(object sender, PluginEventArgs e)
private void PluginManagementServiceOnPluginManagementLoaded(object sender, PluginEventArgs e)
{
UpdateEnumValues();
}

View File

@ -12,13 +12,13 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects
{
public class EffectsViewModel : Conductor<LayerEffectDescriptor>.Collection.AllActive
{
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private readonly IProfileEditorService _profileEditorService;
private LayerEffectDescriptor _selectedLayerEffectDescriptor;
public EffectsViewModel(LayerPropertiesViewModel layerPropertiesViewModel, IPluginService pluginService, IProfileEditorService profileEditorService)
public EffectsViewModel(LayerPropertiesViewModel layerPropertiesViewModel, IPluginManagementService pluginManagementService, IProfileEditorService profileEditorService)
{
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
_profileEditorService = profileEditorService;
LayerPropertiesViewModel = layerPropertiesViewModel;
PropertyChanged += HandleSelectedLayerEffectChanged;
@ -35,7 +35,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.LayerEffects
public void PopulateDescriptors()
{
List<LayerEffectProvider> layerBrushProviders = _pluginService.GetPluginsOfType<LayerEffectProvider>();
List<LayerEffectProvider> layerBrushProviders = _pluginManagementService.GetPluginsOfType<LayerEffectProvider>();
List<LayerEffectDescriptor> descriptors = layerBrushProviders.SelectMany(l => l.LayerEffectDescriptors).ToList();
Items.AddRange(descriptors.Except(Items));
Items.RemoveRange(Items.Except(descriptors));

View File

@ -13,7 +13,7 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
public class DataModelDebugViewModel : Screen
{
private readonly IDataModelUIService _dataModelUIService;
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private readonly Timer _updateTimer;
private bool _isModuleFilterEnabled;
private DataModelPropertiesViewModel _mainDataModel;
@ -21,10 +21,10 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
private string _propertySearch;
private Module _selectedModule;
public DataModelDebugViewModel(IDataModelUIService dataModelUIService, IPluginService pluginService)
public DataModelDebugViewModel(IDataModelUIService dataModelUIService, IPluginManagementService pluginManagementService)
{
_dataModelUIService = dataModelUIService;
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
_updateTimer = new Timer(500);
DisplayName = "Data model";
@ -77,8 +77,8 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
GetDataModel();
_updateTimer.Start();
_updateTimer.Elapsed += OnUpdateTimerOnElapsed;
_pluginService.PluginEnabled += PluginServiceOnPluginToggled;
_pluginService.PluginDisabled += PluginServiceOnPluginToggled;
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginManagementToggled;
_pluginManagementService.PluginDisabled += PluginManagementServiceOnPluginManagementToggled;
PopulateModules();
@ -89,8 +89,8 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
{
_updateTimer.Stop();
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
_pluginService.PluginEnabled -= PluginServiceOnPluginToggled;
_pluginService.PluginDisabled -= PluginServiceOnPluginToggled;
_pluginManagementService.PluginEnabled -= PluginManagementServiceOnPluginManagementToggled;
_pluginManagementService.PluginDisabled -= PluginManagementServiceOnPluginManagementToggled;
base.OnDeactivate();
}
@ -110,14 +110,14 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
: _dataModelUIService.GetMainDataModelVisualization();
}
private void PluginServiceOnPluginToggled(object? sender, PluginEventArgs e)
private void PluginManagementServiceOnPluginManagementToggled(object? sender, PluginEventArgs e)
{
PopulateModules();
}
private void PopulateModules()
{
Modules = _pluginService.GetPluginsOfType<Module>().Where(p => p.IsEnabled).ToList();
Modules = _pluginManagementService.GetPluginsOfType<Module>().Where(p => p.IsEnabled).ToList();
}
}
}

View File

@ -25,7 +25,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
private List<Tuple<string, int>> _targetFrameRates;
private readonly PluginSetting<LayerBrushReference> _defaultLayerBrushDescriptor;
public GeneralSettingsTabViewModel(IDialogService dialogService, IDebugService debugService, ISettingsService settingsService, IPluginService pluginService)
public GeneralSettingsTabViewModel(IDialogService dialogService, IDebugService debugService, ISettingsService settingsService, IPluginManagementService pluginManagementService)
{
DisplayName = "GENERAL";
@ -46,7 +46,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.General
// Anything else is kinda broken right now
SampleSizes = new List<int> {1, 9};
List<LayerBrushProvider> layerBrushProviders = pluginService.GetPluginsOfType<LayerBrushProvider>();
List<LayerBrushProvider> layerBrushProviders = pluginManagementService.GetPluginsOfType<LayerBrushProvider>();
LayerBrushDescriptors = new BindableCollection<LayerBrushDescriptor>(layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors));
_defaultLayerBrushDescriptor = _settingsService.GetSetting("ProfileEditor.DefaultLayerBrushDescriptor", new LayerBrushReference

View File

@ -10,17 +10,17 @@ namespace Artemis.UI.Screens.Settings.Tabs.Modules
public class ModuleOrderTabViewModel : Screen, IDropTarget
{
private readonly IModuleService _moduleService;
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private readonly DefaultDropHandler _defaultDropHandler;
private readonly List<ModuleOrderModuleViewModel> _modules;
public ModuleOrderTabViewModel(IPluginService pluginService, IModuleService moduleService)
public ModuleOrderTabViewModel(IPluginManagementService pluginManagementService, IModuleService moduleService)
{
DisplayName = "MODULE PRIORITY";
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
_moduleService = moduleService;
_modules = new List<ModuleOrderModuleViewModel>(pluginService.GetPluginsOfType<Module>().Select(m => new ModuleOrderModuleViewModel(m)));
_modules = new List<ModuleOrderModuleViewModel>(pluginManagementService.GetPluginsOfType<Module>().Select(m => new ModuleOrderModuleViewModel(m)));
_defaultDropHandler = new DefaultDropHandler();
NormalModules = new BindableCollection<ModuleOrderModuleViewModel>();

View File

@ -9,15 +9,15 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
{
public class PluginSettingsTabViewModel : Conductor<PluginSettingsViewModel>.Collection.AllActive
{
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private readonly ISettingsVmFactory _settingsVmFactory;
private BindableCollection<PluginSettingsViewModel> _plugins;
public PluginSettingsTabViewModel(IPluginService pluginService, ISettingsVmFactory settingsVmFactory)
public PluginSettingsTabViewModel(IPluginManagementService pluginManagementService, ISettingsVmFactory settingsVmFactory)
{
DisplayName = "PLUGINS";
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
_settingsVmFactory = settingsVmFactory;
}
@ -29,7 +29,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
Items.Clear();
await Task.Delay(200);
List<PluginSettingsViewModel> instances = _pluginService.GetAllPluginInfo().Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p.Instance)).ToList();
List<PluginSettingsViewModel> instances = _pluginManagementService.GetAllPluginInfo().Select(p => _settingsVmFactory.CreatePluginSettingsViewModel(p.Plugin)).ToList();
foreach (PluginSettingsViewModel pluginSettingsViewModel in instances)
Items.Add(pluginSettingsViewModel);
});

View File

@ -26,7 +26,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
{
private readonly IDialogService _dialogService;
private readonly ILogger _logger;
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private readonly ISnackbarMessageQueue _snackbarMessageQueue;
private readonly IWindowManager _windowManager;
private bool _enabling;
@ -37,7 +37,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
ILogger logger,
IWindowManager windowManager,
IDialogService dialogService,
IPluginService pluginService,
IPluginManagementService pluginManagementService,
ISnackbarMessageQueue snackbarMessageQueue)
{
PluginImplementation = pluginImplementation;
@ -46,7 +46,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
_logger = logger;
_windowManager = windowManager;
_dialogService = dialogService;
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
_snackbarMessageQueue = snackbarMessageQueue;
}
@ -183,7 +183,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
try
{
_pluginService.EnablePlugin(PluginImplementation);
_pluginManagementService.EnablePluginImplementation(PluginImplementation);
}
catch (Exception e)
{
@ -195,7 +195,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
}
}
else
_pluginService.DisablePlugin(PluginImplementation);
_pluginManagementService.DisablePluginImplementation(PluginImplementation);
NotifyOfPropertyChange(nameof(IsEnabled));
NotifyOfPropertyChange(nameof(CanOpenSettings));
@ -208,7 +208,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
bool restart = false;
// If any plugin already requires a restart, don't ask the user again
bool restartQueued = _pluginService.GetAllPluginInfo().Any(p => p.Instance != null && !p.IsEnabled && p.Instance.IsEnabled);
bool restartQueued = _pluginManagementService.GetAllPluginInfo().Any(p => p.Plugin != null && !p.IsEnabled && p.Plugin.IsEnabled);
// If the plugin isn't enabled (load failed), it can be disabled without a restart
if (!restartQueued && PluginImplementation.IsEnabled)
{
@ -220,7 +220,7 @@ namespace Artemis.UI.Screens.Settings.Tabs.Plugins
);
}
_pluginService.DisablePlugin(PluginImplementation);
_pluginManagementService.DisablePluginImplementation(PluginImplementation);
if (restart)
{
_logger.Debug("Restarting for device provider disable {pluginInfo}", PluginImplementation.PluginInfo);

View File

@ -29,18 +29,18 @@ namespace Artemis.UI.Screens.Sidebar
private readonly Timer _activeModulesUpdateTimer;
private readonly IKernel _kernel;
private readonly IModuleVmFactory _moduleVmFactory;
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private string _activeModules;
private bool _isSidebarOpen;
private IScreen _selectedItem;
private BindableCollection<INavigationItem> _sidebarItems;
private Dictionary<INavigationItem, Module> _sidebarModules;
public SidebarViewModel(IKernel kernel, IEventAggregator eventAggregator, IModuleVmFactory moduleVmFactory, IPluginService pluginService)
public SidebarViewModel(IKernel kernel, IEventAggregator eventAggregator, IModuleVmFactory moduleVmFactory, IPluginManagementService pluginManagementService)
{
_kernel = kernel;
_moduleVmFactory = moduleVmFactory;
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
SidebarModules = new Dictionary<INavigationItem, Module>();
SidebarItems = new BindableCollection<INavigationItem>();
@ -49,8 +49,8 @@ namespace Artemis.UI.Screens.Sidebar
_activeModulesUpdateTimer.Start();
_activeModulesUpdateTimer.Elapsed += ActiveModulesUpdateTimerOnElapsed;
_pluginService.PluginEnabled += PluginServiceOnPluginEnabled;
_pluginService.PluginDisabled += PluginServiceOnPluginDisabled;
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginManagementEnabled;
_pluginManagementService.PluginDisabled += PluginManagementServiceOnPluginManagementDisabled;
SetupSidebar();
eventAggregator.Subscribe(this);
@ -96,8 +96,8 @@ namespace Artemis.UI.Screens.Sidebar
SelectedItem?.Deactivate();
SelectedItem = null;
_pluginService.PluginEnabled -= PluginServiceOnPluginEnabled;
_pluginService.PluginDisabled -= PluginServiceOnPluginDisabled;
_pluginManagementService.PluginEnabled -= PluginManagementServiceOnPluginManagementEnabled;
_pluginManagementService.PluginDisabled -= PluginManagementServiceOnPluginManagementDisabled;
_activeModulesUpdateTimer.Stop();
_activeModulesUpdateTimer.Elapsed -= ActiveModulesUpdateTimerOnElapsed;
@ -118,7 +118,7 @@ namespace Artemis.UI.Screens.Sidebar
// Add all activated modules
SidebarItems.Add(new DividerNavigationItem());
SidebarItems.Add(new SubheaderNavigationItem {Subheader = "Modules"});
List<Module> modules = _pluginService.GetPluginsOfType<Module>().ToList();
List<Module> modules = _pluginManagementService.GetPluginsOfType<Module>().ToList();
foreach (Module module in modules)
AddModule(module);
@ -210,15 +210,15 @@ namespace Artemis.UI.Screens.Sidebar
#region Event handlers
private void PluginServiceOnPluginEnabled(object sender, PluginEventArgs e)
private void PluginManagementServiceOnPluginManagementEnabled(object sender, PluginEventArgs e)
{
if (e.PluginInfo.Instance is Module module)
if (e.PluginInfo.Plugin is Module module)
AddModule(module);
}
private void PluginServiceOnPluginDisabled(object sender, PluginEventArgs e)
private void PluginManagementServiceOnPluginManagementDisabled(object sender, PluginEventArgs e)
{
if (e.PluginInfo.Instance is Module module)
if (e.PluginInfo.Plugin is Module module)
RemoveModule(module);
}

View File

@ -10,13 +10,13 @@ namespace Artemis.UI.Screens.Splash
public class SplashViewModel : Screen
{
private readonly ICoreService _coreService;
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private string _status;
public SplashViewModel(ICoreService coreService, IPluginService pluginService)
public SplashViewModel(ICoreService coreService, IPluginManagementService pluginManagementService)
{
_coreService = coreService;
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
Status = "Initializing Core";
}
@ -37,46 +37,46 @@ namespace Artemis.UI.Screens.Splash
protected override void OnInitialActivate()
{
_coreService.Initialized += OnCoreServiceOnInitialized;
_pluginService.CopyingBuildInPlugins += OnPluginServiceOnCopyingBuildInPlugins;
_pluginService.PluginLoading += OnPluginServiceOnPluginLoading;
_pluginService.PluginLoaded += OnPluginServiceOnPluginLoaded;
_pluginService.PluginEnabling += PluginServiceOnPluginEnabling;
_pluginService.PluginEnabled += PluginServiceOnPluginEnabled;
_pluginManagementService.CopyingBuildInPlugins += OnPluginManagementServiceOnCopyingBuildInPluginsManagement;
_pluginManagementService.PluginLoading += OnPluginManagementServiceOnPluginManagementLoading;
_pluginManagementService.PluginLoaded += OnPluginManagementServiceOnPluginManagementLoaded;
_pluginManagementService.PluginEnabling += PluginManagementServiceOnPluginManagementEnabling;
_pluginManagementService.PluginEnabled += PluginManagementServiceOnPluginManagementEnabled;
base.OnInitialActivate();
}
protected override void OnClose()
{
_coreService.Initialized -= OnCoreServiceOnInitialized;
_pluginService.CopyingBuildInPlugins -= OnPluginServiceOnCopyingBuildInPlugins;
_pluginService.PluginLoading -= OnPluginServiceOnPluginLoading;
_pluginService.PluginLoaded -= OnPluginServiceOnPluginLoaded;
_pluginService.PluginEnabling -= PluginServiceOnPluginEnabling;
_pluginService.PluginEnabled -= PluginServiceOnPluginEnabled;
_pluginManagementService.CopyingBuildInPlugins -= OnPluginManagementServiceOnCopyingBuildInPluginsManagement;
_pluginManagementService.PluginLoading -= OnPluginManagementServiceOnPluginManagementLoading;
_pluginManagementService.PluginLoaded -= OnPluginManagementServiceOnPluginManagementLoaded;
_pluginManagementService.PluginEnabling -= PluginManagementServiceOnPluginManagementEnabling;
_pluginManagementService.PluginEnabled -= PluginManagementServiceOnPluginManagementEnabled;
base.OnClose();
}
private void OnPluginServiceOnPluginLoaded(object sender, PluginEventArgs args)
private void OnPluginManagementServiceOnPluginManagementLoaded(object sender, PluginEventArgs args)
{
Status = "Initializing UI";
}
private void OnPluginServiceOnPluginLoading(object sender, PluginEventArgs args)
private void OnPluginManagementServiceOnPluginManagementLoading(object sender, PluginEventArgs args)
{
Status = "Loading plugin: " + args.PluginInfo.Name;
}
private void PluginServiceOnPluginEnabled(object sender, PluginEventArgs args)
private void PluginManagementServiceOnPluginManagementEnabled(object sender, PluginEventArgs args)
{
Status = "Initializing UI";
}
private void PluginServiceOnPluginEnabling(object sender, PluginEventArgs args)
private void PluginManagementServiceOnPluginManagementEnabling(object sender, PluginEventArgs args)
{
Status = "Enabling plugin: " + args.PluginInfo.Name;
}
private void OnPluginServiceOnCopyingBuildInPlugins(object sender, EventArgs args)
private void OnPluginManagementServiceOnCopyingBuildInPluginsManagement(object sender, EventArgs args)
{
Status = "Updating built-in plugins";
}

View File

@ -13,19 +13,19 @@ namespace Artemis.UI.Services
{
private readonly IDataModelUIService _dataModelUIService;
private readonly IProfileEditorService _profileEditorService;
private readonly IPluginService _pluginService;
private readonly IPluginManagementService _pluginManagementService;
private bool _registeredBuiltInDataModelDisplays;
private bool _registeredBuiltInDataModelInputs;
private bool _registeredBuiltInPropertyEditors;
public RegistrationService(IDataModelUIService dataModelUIService, IProfileEditorService profileEditorService, IPluginService pluginService)
public RegistrationService(IDataModelUIService dataModelUIService, IProfileEditorService profileEditorService, IPluginManagementService pluginManagementService)
{
_dataModelUIService = dataModelUIService;
_profileEditorService = profileEditorService;
_pluginService = pluginService;
_pluginManagementService = pluginManagementService;
LoadPluginModules();
pluginService.PluginLoaded += PluginServiceOnPluginLoaded;
pluginManagementService.PluginLoaded += PluginServiceOnPluginLoaded;
}
public void RegisterBuiltInDataModelDisplays()
@ -80,7 +80,7 @@ namespace Artemis.UI.Services
private void LoadPluginModules()
{
foreach (PluginInfo pluginInfo in _pluginService.GetAllPluginInfo())
foreach (PluginInfo pluginInfo in _pluginManagementService.GetAllPluginInfo())
pluginInfo.Kernel.Load(new[] { new PluginUIModule(pluginInfo) });
}
}