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

@@ -10,7 +10,7 @@ namespace TestPlugin
{ {
public void InitializeModule() public void InitializeModule()
{ {
Console.WriteLine("Global plugin initialized"); Console.WriteLine("Global HELLO from test plugin");
} }
} }
@@ -18,7 +18,7 @@ namespace TestPlugin
{ {
public void InitializeModule() public void InitializeModule()
{ {
Console.WriteLine("Camera plugin initialized"); Console.WriteLine("Camera HELLO from test plugin");
} }
} }
public class TestPlugin: IPlugin public class TestPlugin: IPlugin

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;
using Ninject.Extensions.ChildKernel; using Ninject.Extensions.ChildKernel;
using Serilog; using Serilog;
using System.ComponentModel;
using VisionBuilder.UI.Common.Attributes;
using VisionBuilder.UI.Common.Plugins; using VisionBuilder.UI.Common.Plugins;
namespace VisionBuilder.UI.Common; namespace VisionBuilder.UI.Common;
@@ -24,12 +26,23 @@ public static class Extensions
self.Bind<T, IVisionBuilderModule>().To<T>().InSingletonScope(); 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) 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) foreach (var service in serviceTypes)
{ {
try try
@@ -45,12 +58,22 @@ public static class Extensions
Log.Error($"Error initializing module {service.GetType().Name}: {e}", e); 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) foreach (var plugin in plugins)
{ {
try try
@@ -66,9 +89,9 @@ public static class Extensions
return self; 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) foreach (var plugin in plugins)
{ {
try try

View File

@@ -2,32 +2,56 @@
using Inspectron.Settings; using Inspectron.Settings;
using Ninject; using Ninject;
using Serilog; using Serilog;
using VisionBuilder.UI.Common.Attributes;
namespace VisionBuilder.UI.Common.Plugins; namespace VisionBuilder.UI.Common.Plugins;
public class PluginLoader [ModulePriority(1000)]
public class PluginLoader:IVisionBuilderModule
{ {
private static PluginLoader _instance; private readonly PluginSettings _settings;
public static PluginLoader Instance => _instance ??= new PluginLoader();
const string PLUGINS_DIRECTORY = "Plugins"; const string PLUGINS_DIRECTORY = "Plugins";
private PluginLoader() public PluginLoader(PluginSettings settings)
{ {
Initialize(); _settings = settings;
} }
public List<IPlugin> Plugins { get; } = new List<IPlugin>(); public List<IPlugin> Plugins { get; } = new List<IPlugin>();
private bool _isInitialized;
public void Initialize() public void InitializeModule()
{ {
if (_isInitialized)
{
return;
}
_isInitialized = true;
Log.Information("Loading plugins..."); Log.Information("Loading plugins...");
var pluginsPath = Path.Combine("..", "Data", PLUGINS_DIRECTORY); var pluginsPath = Path.Combine("..", "Data", PLUGINS_DIRECTORY);
Directory.CreateDirectory(pluginsPath); Directory.CreateDirectory(pluginsPath);
var pluginFolders = Directory.GetDirectories(pluginsPath); var pluginFolders = Directory.GetDirectories(pluginsPath);
foreach (string folder in pluginFolders) 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"); var assemblyPath = Path.Combine(folder, Path.GetFileNameWithoutExtension(folder) + ".dll");
if (!File.Exists(assemblyPath)) if (!File.Exists(assemblyPath))
@@ -58,10 +82,12 @@ public class PluginLoader
if (type.IsAssignableTo(typeof(IPlugin)) && !type.IsAbstract) if (type.IsAssignableTo(typeof(IPlugin)) && !type.IsAbstract)
{ {
Plugins.Add((IPlugin)Activator.CreateInstance(type)); 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}";
}
}

View File

@@ -1,9 +1,11 @@
using Ninject; using Ninject;
using Serilog; using Serilog;
using VisionBuilder.UI.Common; using VisionBuilder.UI.Common;
using VisionBuilder.UI.Common.Attributes;
namespace VisionBuilder.UI.Console namespace VisionBuilder.UI.Console
{ {
[ModulePriority(100000)]
public class VisionBuilderConsole: IVisionBuilderModule public class VisionBuilderConsole: IVisionBuilderModule
{ {
private readonly VisionBuilderConsoleSettings _settings; private readonly VisionBuilderConsoleSettings _settings;

View File

@@ -26,6 +26,7 @@ public class IOCommanderModule:IVisionBuilderModule
public void InitializeModule() public void InitializeModule()
{ {
if(!_settings.Enabled)return;
try try
{ {
_ioCommander = _factory.Create(); _ioCommander = _factory.Create();

View File

@@ -10,6 +10,7 @@ public class IOCommanderSettings: ISettings
public int PulseLength { get; set; } = 100; public int PulseLength { get; set; } = 100;
public bool ShowDebugView { get; set; } = false; public bool ShowDebugView { get; set; } = false;
public string Port { get; set; }="COM3"; public string Port { get; set; }="COM3";
public bool Enabled { get; set; }
public List<IOCommanderProcessingEvent> GlobalEvents { get; set; } = public List<IOCommanderProcessingEvent> GlobalEvents { get; set; } =
[ [

View File

@@ -47,7 +47,8 @@ namespace VisionBuilder.UI.Windows.Test
.UseIOCommanderWindowsDebug() .UseIOCommanderWindowsDebug()
.UseIOCommanderVirtualInput() .UseIOCommanderVirtualInput()
.UseWindowsServices() .UseWindowsServices()
.LoadGlobalPlugins() .UsePlugins()
; ;
mainKernel.Get<ILoadingService>().StartLoading("Camera initialization"); mainKernel.Get<ILoadingService>().StartLoading("Camera initialization");
@@ -64,12 +65,9 @@ namespace VisionBuilder.UI.Windows.Test
.UseRingbuffer(CAMERA1) .UseRingbuffer(CAMERA1)
.UseHawkeyeRecipes(CAMERA1) .UseHawkeyeRecipes(CAMERA1)
.UseCameraIOCommander(CAMERA1) .UseCameraIOCommander(CAMERA1)
.LoadCameraPlugins(CAMERA1)
; ;
// LOAD SETTINGS // // LOAD SETTINGS //
mainKernel.RegisterSettings(); mainKernel.RegisterSettings();
kernelCamera1.RegisterSettings(); kernelCamera1.RegisterSettings();
@@ -79,7 +77,16 @@ namespace VisionBuilder.UI.Windows.Test
// Must be done after settings are loaded // Must be done after settings are loaded
kernelCamera1.UseCamera(); kernelCamera1.UseCamera();
// initialize modules
mainKernel.InitializeModules();
kernelCamera1.InitializeModules();
settings.LoadSettings(); // load settings for plugins as they are loaded after modules
mainKernel.InitializeGlobalPlugins();
kernelCamera1.InitializeCameraPlugins(CAMERA1);
// initialize plugins the same way as modules(loaded modules are skipped)
mainKernel.InitializeModules(); mainKernel.InitializeModules();
kernelCamera1.InitializeModules(); kernelCamera1.InitializeModules();