From e761b24c953c2ac475b8c06e65d12dbeb8e42c5d Mon Sep 17 00:00:00 2001 From: meelstorm <8780022+TesAnti@users.noreply.github.com> Date: Mon, 21 Jul 2025 12:07:33 +0200 Subject: [PATCH] iocommander input and virtual input --- .../EmulationCameraImageSource.cs | 6 +- .../Hawkeye.VisionBuilder.Workflow.csproj | 1 - VisionBuilder.UI.Camera/ModuleExtensions.cs | 1 + VisionBuilder.UI.Common/Extensions.cs | 13 +- .../VisionBuilder.UI.Common.csproj | 1 + .../IOCommanderVirtualInput.Designer.cs | 49 +++++++ .../IOCommanderVirtualInput.cs | 96 ++++++++++++++ .../IOCommanderVirtualInput.resx | 120 ++++++++++++++++++ .../IOCommanderVirtualInputFactory.cs | 28 ++++ .../ModuleExtensions.cs | 7 + .../VirtualInputSettings.cs | 13 ++ .../DefaultIOCommanderFactory.cs | 19 +++ VisionBuilder.UI.IOCommander/IOCommander.cs | 3 +- .../Interfaces/IIOCommander.cs | 10 ++ .../Interfaces/IIOCommanderFactory.cs | 6 + .../ModuleExtensions.cs | 1 + .../Modules/IOCommanderCameraModule.cs | 36 +++++- .../Modules/IOCommanderModule.cs | 20 +-- .../Settings/IOCommanderSettings.cs | 2 - VisionBuilder.UI.Windows.Test/Program.cs | 1 + .../Components/SingleCameraControl.cs | 6 + 21 files changed, 418 insertions(+), 21 deletions(-) create mode 100644 VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInput.Designer.cs create mode 100644 VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInput.cs create mode 100644 VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInput.resx create mode 100644 VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInputFactory.cs create mode 100644 VisionBuilder.UI.IOCommander.Windows/VirtualInputSettings.cs create mode 100644 VisionBuilder.UI.IOCommander/DefaultIOCommanderFactory.cs create mode 100644 VisionBuilder.UI.IOCommander/Interfaces/IIOCommander.cs create mode 100644 VisionBuilder.UI.IOCommander/Interfaces/IIOCommanderFactory.cs diff --git a/Hawkeye.VisionBuilder.UI.Sources.Emulation/EmulationCameraImageSource.cs b/Hawkeye.VisionBuilder.UI.Sources.Emulation/EmulationCameraImageSource.cs index c7d5d2c..4ad59a8 100644 --- a/Hawkeye.VisionBuilder.UI.Sources.Emulation/EmulationCameraImageSource.cs +++ b/Hawkeye.VisionBuilder.UI.Sources.Emulation/EmulationCameraImageSource.cs @@ -36,11 +36,7 @@ namespace Hawkeye.VisionBuilder.UI.Sources.Emulation //clear the channel if it is full if (_channel.Reader.Count > 0) { - while (await _channel.Reader.WaitToReadAsync(token)) - { - if (_channel.Reader.TryRead(out _)) continue; - break; - } + while (_channel.Reader.TryRead(out _)) { } } //get the next image await Task.Run(new Action(() => _ = GetImageInternal(CancellationToken.None)), token); diff --git a/Hawkeye.VisionBuilder.Workflow/Hawkeye.VisionBuilder.Workflow.csproj b/Hawkeye.VisionBuilder.Workflow/Hawkeye.VisionBuilder.Workflow.csproj index f0fb47d..d3e6325 100644 --- a/Hawkeye.VisionBuilder.Workflow/Hawkeye.VisionBuilder.Workflow.csproj +++ b/Hawkeye.VisionBuilder.Workflow/Hawkeye.VisionBuilder.Workflow.csproj @@ -18,7 +18,6 @@ - diff --git a/VisionBuilder.UI.Camera/ModuleExtensions.cs b/VisionBuilder.UI.Camera/ModuleExtensions.cs index d9181a6..bbf123a 100644 --- a/VisionBuilder.UI.Camera/ModuleExtensions.cs +++ b/VisionBuilder.UI.Camera/ModuleExtensions.cs @@ -12,6 +12,7 @@ public static class ModuleExtensions { self.Bind().ToConstant(new CameraSettings(cameraName)); self.Bind().ToConstant(new EmulationSettings(cameraName)); + self.Bind().ToSelf().InSingletonScope(); return self; } private static void BindCameras(CameraSettings cameraSettings, IChildKernel kernel) diff --git a/VisionBuilder.UI.Common/Extensions.cs b/VisionBuilder.UI.Common/Extensions.cs index 47c61b5..312673c 100644 --- a/VisionBuilder.UI.Common/Extensions.cs +++ b/VisionBuilder.UI.Common/Extensions.cs @@ -1,6 +1,7 @@ using Inspectron.Settings; using Ninject; using Ninject.Extensions.ChildKernel; +using Serilog; namespace VisionBuilder.UI.Common; @@ -28,10 +29,18 @@ public static class Extensions var serviceTypes = self.GetAll(typeof(IVisionBuilderModule)).Select(x => (IVisionBuilderModule)x).ToList(); foreach (var service in serviceTypes) { - if (service != null) + try { - service.InitializeModule(); + if (service != null) + { + service.InitializeModule(); + } } + catch (Exception e) + { + Log.Error($"Error initializing module {service.GetType().Name}: {e.Message}", e); + } + } } diff --git a/VisionBuilder.UI.Common/VisionBuilder.UI.Common.csproj b/VisionBuilder.UI.Common/VisionBuilder.UI.Common.csproj index b0a2cfb..58a79d0 100644 --- a/VisionBuilder.UI.Common/VisionBuilder.UI.Common.csproj +++ b/VisionBuilder.UI.Common/VisionBuilder.UI.Common.csproj @@ -11,6 +11,7 @@ + diff --git a/VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInput.Designer.cs b/VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInput.Designer.cs new file mode 100644 index 0000000..4376e34 --- /dev/null +++ b/VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInput.Designer.cs @@ -0,0 +1,49 @@ +namespace VisionBuilder.UI.IOCommander.Windows +{ + partial class IOCommanderVirtualInput + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + SuspendLayout(); + // + // IOCommanderVirtualInput + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(200, 500); + ControlBox = false; + FormBorderStyle = FormBorderStyle.FixedToolWindow; + MaximizeBox = false; + MinimizeBox = false; + Name = "IOCommanderVirtualInput"; + Text = "IO Commander Virtual Input"; + ResumeLayout(false); + } + + #endregion + } +} \ No newline at end of file diff --git a/VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInput.cs b/VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInput.cs new file mode 100644 index 0000000..a9125f3 --- /dev/null +++ b/VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInput.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using VisionBuilder.UI.Common; +using VisionBuilder.UI.IOCommander.Interfaces; + +namespace VisionBuilder.UI.IOCommander.Windows +{ + public partial class IOCommanderVirtualInput : Form, IIOCommander + { + private CheckBox[] _checkBoxes; + + public IOCommanderVirtualInput() + { + InitializeComponent(); + InitializeCheckboxes(); + } + + public BitArray PinState { get; set; } = new BitArray(20); + public event Action? OnPinChanged = delegate { }; + + public void SetPins(int pin, bool state) + { + // This is a dummy function as per requirement + } + + private void InitializeCheckboxes() + { + // Set form properties + this.Text = "IO Commander Virtual Input"; + this.ClientSize = new Size(200, 500); + this.FormBorderStyle = FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.StartPosition = FormStartPosition.Manual; + this.Location = new Point(350, 50); // Set a fixed position for the form + + // Create a panel to contain checkboxes with scrolling if needed + Panel panel = new Panel + { + Dock = DockStyle.Fill, + AutoScroll = true + }; + this.Controls.Add(panel); + + // Initialize checkboxes + _checkBoxes = new CheckBox[20]; + + for (int i = 0; i < 20; i++) + { + int pinNumber = i + 1; // Pins are 1-indexed + _checkBoxes[i] = new CheckBox + { + Text = pinNumber.ToString(), + Location = new Point(20, 20 + (i * 22)), + Size = new Size(150, 20), + Tag = pinNumber, // Store the pin number in Tag for easy reference + }; + + // Add event handler + _checkBoxes[i].CheckedChanged += CheckBox_CheckedChanged; + + panel.Controls.Add(_checkBoxes[i]); + } + } + + private void CheckBox_CheckedChanged(object? sender, EventArgs e) + { + if (sender is CheckBox checkBox) + { + int pin = (int)checkBox.Tag; + bool state = checkBox.Checked; + + // Update the BitArray + PinState.Set(pin - 1, state); // Adjust for 0-based indexing in BitArray + + // Trigger the event + OnPinChanged?.Invoke(pin, state); + } + } + + + protected override void OnShown(EventArgs e) + { + TopMost= true; + Focus(); + TopMost = true; + } + } +} diff --git a/VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInput.resx b/VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInput.resx new file mode 100644 index 0000000..8b2ff64 --- /dev/null +++ b/VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInput.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInputFactory.cs b/VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInputFactory.cs new file mode 100644 index 0000000..89c6022 --- /dev/null +++ b/VisionBuilder.UI.IOCommander.Windows/IOCommanderVirtualInputFactory.cs @@ -0,0 +1,28 @@ +using VisionBuilder.UI.IOCommander.Interfaces; +using VisionBuilder.UI.IOCommander.Settings; + +namespace VisionBuilder.UI.IOCommander.Windows; + +public class IOCommanderVirtualInputFactory: IIOCommanderFactory +{ + private readonly VirtualInputSettings _settings; + private readonly DefaultIOCommanderFactory _defaultFactory; + + public IOCommanderVirtualInputFactory(VirtualInputSettings settings, IOCommanderSettings ioCommanderSettings) + { + _settings = settings; + _defaultFactory = new DefaultIOCommanderFactory(ioCommanderSettings); + } + + public IIOCommander Create() + { + if (_settings.Enabled) + { + var res = new IOCommanderVirtualInput(); + res.Show(); + return res; + } + + return _defaultFactory.Create(); + } +} \ No newline at end of file diff --git a/VisionBuilder.UI.IOCommander.Windows/ModuleExtensions.cs b/VisionBuilder.UI.IOCommander.Windows/ModuleExtensions.cs index 8ec85b6..a9e421c 100644 --- a/VisionBuilder.UI.IOCommander.Windows/ModuleExtensions.cs +++ b/VisionBuilder.UI.IOCommander.Windows/ModuleExtensions.cs @@ -18,4 +18,11 @@ public static class ModuleExtensions self.Bind().ToConstant(new WindowsIOCommanderDebugViewSettings()); return self; } + + public static IKernel UseIOCommanderVirtualInput(this IKernel self) + { + self.Bind().To().InSingletonScope(); + self.Rebind().To().InSingletonScope(); + return self; + } } \ No newline at end of file diff --git a/VisionBuilder.UI.IOCommander.Windows/VirtualInputSettings.cs b/VisionBuilder.UI.IOCommander.Windows/VirtualInputSettings.cs new file mode 100644 index 0000000..425d873 --- /dev/null +++ b/VisionBuilder.UI.IOCommander.Windows/VirtualInputSettings.cs @@ -0,0 +1,13 @@ +using Inspectron.Settings; +using VisionBuilder.UI.Common; + +namespace VisionBuilder.UI.IOCommander.Windows; + +public class VirtualInputSettings: ISettings +{ + public bool Enabled { get; set; } + public void RegisterSettings(InspectronSettings settings) + { + settings.RegisterSimple(this,()=>this.Enabled,"IOCommander/Virtual input",nameof(Enabled)); + } +} \ No newline at end of file diff --git a/VisionBuilder.UI.IOCommander/DefaultIOCommanderFactory.cs b/VisionBuilder.UI.IOCommander/DefaultIOCommanderFactory.cs new file mode 100644 index 0000000..c521412 --- /dev/null +++ b/VisionBuilder.UI.IOCommander/DefaultIOCommanderFactory.cs @@ -0,0 +1,19 @@ +using VisionBuilder.UI.IOCommander.Interfaces; +using VisionBuilder.UI.IOCommander.Settings; + +namespace VisionBuilder.UI.IOCommander; + +public class DefaultIOCommanderFactory: IIOCommanderFactory +{ + private readonly IOCommanderSettings _settings; + + public DefaultIOCommanderFactory(IOCommanderSettings settings) + { + _settings = settings; + } + + public IIOCommander Create() + { + return new IOCommander(_settings.Port); + } +} \ No newline at end of file diff --git a/VisionBuilder.UI.IOCommander/IOCommander.cs b/VisionBuilder.UI.IOCommander/IOCommander.cs index 4de7a2f..85934ea 100644 --- a/VisionBuilder.UI.IOCommander/IOCommander.cs +++ b/VisionBuilder.UI.IOCommander/IOCommander.cs @@ -1,9 +1,10 @@ using System.Collections; using System.IO.Ports; +using VisionBuilder.UI.IOCommander.Interfaces; namespace VisionBuilder.UI.IOCommander; -public class IOCommander +public class IOCommander : IIOCommander { SerialPort _port; diff --git a/VisionBuilder.UI.IOCommander/Interfaces/IIOCommander.cs b/VisionBuilder.UI.IOCommander/Interfaces/IIOCommander.cs new file mode 100644 index 0000000..0f3f0bd --- /dev/null +++ b/VisionBuilder.UI.IOCommander/Interfaces/IIOCommander.cs @@ -0,0 +1,10 @@ +using System.Collections; + +namespace VisionBuilder.UI.IOCommander.Interfaces; + +public interface IIOCommander +{ + BitArray PinState { get; } + event Action OnPinChanged; + void SetPins(int pin, bool state); +} \ No newline at end of file diff --git a/VisionBuilder.UI.IOCommander/Interfaces/IIOCommanderFactory.cs b/VisionBuilder.UI.IOCommander/Interfaces/IIOCommanderFactory.cs new file mode 100644 index 0000000..373d0fc --- /dev/null +++ b/VisionBuilder.UI.IOCommander/Interfaces/IIOCommanderFactory.cs @@ -0,0 +1,6 @@ +namespace VisionBuilder.UI.IOCommander.Interfaces; + +public interface IIOCommanderFactory +{ + public IIOCommander Create(); +} \ No newline at end of file diff --git a/VisionBuilder.UI.IOCommander/ModuleExtensions.cs b/VisionBuilder.UI.IOCommander/ModuleExtensions.cs index 6a22782..6630f1b 100644 --- a/VisionBuilder.UI.IOCommander/ModuleExtensions.cs +++ b/VisionBuilder.UI.IOCommander/ModuleExtensions.cs @@ -22,6 +22,7 @@ public static class ModuleExtensions self.Bind().To().InSingletonScope(); self.Bind().To().InSingletonScope(); self.Bind().To().InSingletonScope(); + self.Bind().To().InSingletonScope(); return self; } diff --git a/VisionBuilder.UI.IOCommander/Modules/IOCommanderCameraModule.cs b/VisionBuilder.UI.IOCommander/Modules/IOCommanderCameraModule.cs index ea0ea84..ae7ee25 100644 --- a/VisionBuilder.UI.IOCommander/Modules/IOCommanderCameraModule.cs +++ b/VisionBuilder.UI.IOCommander/Modules/IOCommanderCameraModule.cs @@ -14,22 +14,54 @@ public class IOCommanderCameraModule: IVisionBuilderModule private readonly IOCommanderCameraSettings _cameraSettings; private readonly IRecognitionControl _recognitionControl; private readonly MainWindowVM _mainWindowVm; + private readonly SingleCameraVM _cameraVm; - public IOCommanderCameraModule(IOCommanderModule mainModule, IOCommanderSettings settings, IOCommanderCameraSettings cameraSettings, IRecognitionControl recognitionControl, MainWindowVM mainWindowVm) + public IOCommanderCameraModule(IOCommanderModule mainModule, IOCommanderSettings settings, IOCommanderCameraSettings cameraSettings, IRecognitionControl recognitionControl, MainWindowVM mainWindowVm, SingleCameraVM cameraVm) { _mainModule = mainModule; _settings = settings; _cameraSettings = cameraSettings; _recognitionControl = recognitionControl; _mainWindowVm = mainWindowVm; + _cameraVm = cameraVm; _recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed; _recognitionControl.SessionStarted += _recognitionControl_SessionStarted; _recognitionControl.SessionEnded += _recognitionControl_SessionEnded; + mainModule.OnPinChangedEvent += MainModule_OnPinChangedEvent; } - + + private void ProcessCommand(EGPIOCommand command) + { + switch (command) + { + case EGPIOCommand.Start: + if(_cameraVm.StartCommand.CanExecute(null)) + { + _cameraVm.StartCommand.Execute(null); + } + break; + case EGPIOCommand.Stop: + if(_cameraVm.StopCommand.CanExecute(null)) + { + _cameraVm.StopCommand.Execute(null); + } + break; + default: + throw new ArgumentOutOfRangeException(nameof(command), command, null); + } + } + + private void MainModule_OnPinChangedEvent(int arg1, bool arg2) + { + var @event = _cameraSettings.GPIOCommands.FirstOrDefault(x => x.Pin == arg1 && x.OnHigh == arg2); + if (@event != null) + { + ProcessCommand(@event.Command); + } + } private void EmitEvent(EProcessingEvent @event) { diff --git a/VisionBuilder.UI.IOCommander/Modules/IOCommanderModule.cs b/VisionBuilder.UI.IOCommander/Modules/IOCommanderModule.cs index 9dedc8f..2bbb91e 100644 --- a/VisionBuilder.UI.IOCommander/Modules/IOCommanderModule.cs +++ b/VisionBuilder.UI.IOCommander/Modules/IOCommanderModule.cs @@ -12,28 +12,32 @@ namespace VisionBuilder.UI.IOCommander.Modules; public class IOCommanderModule:IVisionBuilderModule { private readonly IOCommanderSettings _settings; + private readonly IIOCommanderFactory _factory; private readonly IIOCommanderDebugViewService _debugViewService; - private IOCommander? _ioCommander; - public IOCommanderModule(IOCommanderSettings settings, IIOCommanderDebugViewService debugViewService) + private IIOCommander? _ioCommander; + public IOCommanderModule(IOCommanderSettings settings, IIOCommanderFactory factory, IIOCommanderDebugViewService debugViewService) { _settings = settings; + _factory = factory; _debugViewService = debugViewService; } public void InitializeModule() { - if (!_settings.EmulationMode) - { - _ioCommander = new IOCommander(_settings.Port); - _ioCommander.OnPinChanged += OnPinChanged; - } + _ioCommander = _factory.Create(); + _ioCommander.OnPinChanged += OnPinChanged; - if(_settings.ShowDebugView)_debugViewService.ShowDebugView(); + if (_settings.ShowDebugView)_debugViewService.ShowDebugView(); } ConcurrentDictionary _pinStates = new ConcurrentDictionary(); + public event Action? OnPinChangedEvent + { + add => _ioCommander!.OnPinChanged += value; + remove => _ioCommander!.OnPinChanged -= value; + } private void OnPinChanged(int arg1, bool arg2) { _pinStates.TryAdd(arg1, arg2); diff --git a/VisionBuilder.UI.IOCommander/Settings/IOCommanderSettings.cs b/VisionBuilder.UI.IOCommander/Settings/IOCommanderSettings.cs index a8b807d..7424f59 100644 --- a/VisionBuilder.UI.IOCommander/Settings/IOCommanderSettings.cs +++ b/VisionBuilder.UI.IOCommander/Settings/IOCommanderSettings.cs @@ -8,7 +8,6 @@ public class IOCommanderSettings: ISettings { public IOCommanderCameraSettings[] Cameras { get; } public int PulseLength { get; set; } = 100; - public bool EmulationMode { get; set; } = true; public bool ShowDebugView { get; set; } = false; public string Port { get; set; }="COM3"; @@ -27,7 +26,6 @@ public class IOCommanderSettings: ISettings { settings.RegisterSimple(this, () => Port, "IOCommander", nameof(Port)); settings.RegisterSimple(this, () => PulseLength, "IOCommander", nameof(PulseLength)); - settings.RegisterSimple(this, () => EmulationMode, "IOCommander", nameof(EmulationMode)); settings.RegisterSimple(this, () => ShowDebugView, "IOCommander", nameof(ShowDebugView)); foreach (IOCommanderProcessingEvent @event in GlobalEvents) diff --git a/VisionBuilder.UI.Windows.Test/Program.cs b/VisionBuilder.UI.Windows.Test/Program.cs index ab793b2..329f0d6 100644 --- a/VisionBuilder.UI.Windows.Test/Program.cs +++ b/VisionBuilder.UI.Windows.Test/Program.cs @@ -43,6 +43,7 @@ namespace VisionBuilder.UI.Windows.Test mainKernel .UseIOCommander([CAMERA1]) .UseIOCommanderWindowsDebug() + .UseIOCommanderVirtualInput() .UseConsole() .UseWindowsServices(); diff --git a/VisionBuilder.UI.Windows/Components/SingleCameraControl.cs b/VisionBuilder.UI.Windows/Components/SingleCameraControl.cs index ed33986..04ace7c 100644 --- a/VisionBuilder.UI.Windows/Components/SingleCameraControl.cs +++ b/VisionBuilder.UI.Windows/Components/SingleCameraControl.cs @@ -23,6 +23,12 @@ namespace VisionBuilder.UI.Windows.Components this.stats1.SetViewModel(singleCameraVm.StatisticsVm); SetBindings(); + _singleCameraVm.PropertyChanged += SingleCameraVm_PropertyChanged; + } + + private void SingleCameraVm_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + } private void SetBindings()