plugin selection

This commit is contained in:
meelstorm
2025-08-19 14:27:36 +02:00
parent 6ef6aced8d
commit 2f570545ab
9 changed files with 144 additions and 22 deletions

View File

@@ -0,0 +1,12 @@
namespace VisionBuilder.UI.Common.Attributes;
[AttributeUsage(AttributeTargets.Class)]
public class ModulePriorityAttribute: Attribute
{
public int Priority { get; }
public ModulePriorityAttribute(int priority)
{
Priority = priority;
}
}

View File

@@ -2,6 +2,8 @@
using Ninject;
using Ninject.Extensions.ChildKernel;
using Serilog;
using System.ComponentModel;
using VisionBuilder.UI.Common.Attributes;
using VisionBuilder.UI.Common.Plugins;
namespace VisionBuilder.UI.Common;
@@ -24,12 +26,23 @@ public static class Extensions
self.Bind<T, IVisionBuilderModule>().To<T>().InSingletonScope();
}
private static int GetModulePriority(IVisionBuilderModule module)
{
var priorityAttribute = module.GetType().GetCustomAttributes(typeof(ModulePriorityAttribute), true)
.FirstOrDefault() as ModulePriorityAttribute;
return priorityAttribute?.Priority ?? 0;
}
private static List<Type> _initializedModules = new List<Type>();
public static void InitializeModules(this IKernel self)
{
var serviceTypes = self.GetAll(typeof(IVisionBuilderModule)).Select(x => (IVisionBuilderModule)x).ToList();
var serviceTypes = self.GetAll(typeof(IVisionBuilderModule)).Select(x => (IVisionBuilderModule)x)
.Where(x => !_initializedModules.Contains(x.GetType()))
.OrderByDescending(GetModulePriority)
.ToList();
foreach (var service in serviceTypes)
{
try
@@ -45,12 +58,22 @@ public static class Extensions
Log.Error($"Error initializing module {service.GetType().Name}: {e}", e);
}
_initializedModules.Add(service.GetType());
}
}
public static IKernel LoadGlobalPlugins(this IKernel self)
public static IKernel UsePlugins(this IKernel self)
{
var plugins = PluginLoader.Instance.Plugins;
TypeDescriptor.AddAttributes(typeof(List<PluginSettings.PluginFlag>), new TypeConverterAttribute(typeof(PluginFlagConverter)));
self.Bind<PluginSettings, ISettings>().ToConstant(new PluginSettings());
self.RegisterModule<PluginLoader>();
return self;
}
public static IKernel InitializeGlobalPlugins(this IKernel self)
{
var plugins = self.Get<PluginLoader>().Plugins;
foreach (var plugin in plugins)
{
try
@@ -66,9 +89,9 @@ public static class Extensions
return self;
}
public static IKernel LoadCameraPlugins(this IChildKernel self, string cameraName)
public static IKernel InitializeCameraPlugins(this IChildKernel self, string cameraName)
{
var plugins = PluginLoader.Instance.Plugins;
var plugins = self.Get<PluginLoader>().Plugins;
foreach (var plugin in plugins)
{
try

View File

@@ -2,32 +2,56 @@
using Inspectron.Settings;
using Ninject;
using Serilog;
using VisionBuilder.UI.Common.Attributes;
namespace VisionBuilder.UI.Common.Plugins;
public class PluginLoader
[ModulePriority(1000)]
public class PluginLoader:IVisionBuilderModule
{
private static PluginLoader _instance;
public static PluginLoader Instance => _instance ??= new PluginLoader();
private readonly PluginSettings _settings;
const string PLUGINS_DIRECTORY = "Plugins";
private PluginLoader()
public PluginLoader(PluginSettings settings)
{
Initialize();
_settings = settings;
}
public List<IPlugin> Plugins { get; } = new List<IPlugin>();
public void Initialize()
private bool _isInitialized;
public void InitializeModule()
{
if (_isInitialized)
{
return;
}
_isInitialized = true;
Log.Information("Loading plugins...");
var pluginsPath = Path.Combine("..", "Data", PLUGINS_DIRECTORY);
Directory.CreateDirectory(pluginsPath);
var pluginFolders = Directory.GetDirectories(pluginsPath);
foreach (string folder in pluginFolders)
{
var pluginName = Path.GetFileNameWithoutExtension(folder);
if (_settings.Plugins.All(p => p.Name != pluginName))
{
Log.Warning("Discovered new plugin:{PluginName}. Adding to settings and skipping.", pluginName);
_settings.Plugins.Add(new PluginSettings.PluginFlag { Name = pluginName, Enabled = false });
continue;
}
if (_settings.Plugins.Where(p => p.Name == pluginName).All(x=>!x.Enabled))
{
Log.Information("Skipping plugin {PluginName} as it is not enabled in settings.", pluginName);
continue;
}
var assemblyPath = Path.Combine(folder, Path.GetFileNameWithoutExtension(folder) + ".dll");
if (!File.Exists(assemblyPath))
@@ -58,10 +82,12 @@ public class PluginLoader
if (type.IsAssignableTo(typeof(IPlugin)) && !type.IsAbstract)
{
Plugins.Add((IPlugin)Activator.CreateInstance(type));
Log.Information("Discovered plugin: {PluginName}", type.Name);
Log.Information("Loaded plugin: {PluginName}", type.Name);
}
}
}
}

View File

@@ -0,0 +1,50 @@
using System.ComponentModel;
using System.Globalization;
using Inspectron.Settings;
using VisionBuilder.UI.Common.Utils;
namespace VisionBuilder.UI.Common.Plugins;
public class PluginSettings:ISettings
{
public class PluginFlag
{
public bool Enabled { get; set; }
public string Name { get; set; }
public override string ToString()
{
return $"{(Enabled ? "" : "(Disabled)")}{Name}";
}
}
public List<PluginFlag> Plugins { get; set; } = new List<PluginFlag>();
public void RegisterSettings(InspectronSettings settings)
{
settings.RegisterSimple(this, () => this.Plugins,"Plugins",nameof(Plugins));
}
}
public class PluginFlagConverter : GeneralListTypeConverter<PluginSettings.PluginFlag>
{
protected override PluginSettings.PluginFlag ConvertFromString(ITypeDescriptorContext context, CultureInfo culture, string stringValue)
{
if (string.IsNullOrEmpty(stringValue))
return null;
var parts = stringValue.Split(',');
if (parts.Length == 2)
{
return new PluginSettings.PluginFlag
{
Name = parts[0].Trim(),
Enabled = bool.Parse(parts[1].Trim())
};
}
return null;
}
protected override string ConvertToString(ITypeDescriptorContext context, CultureInfo culture, PluginSettings.PluginFlag errorShortName)
{
return $"{errorShortName.Name}, {errorShortName.Enabled}";
}
}