io commander
This commit is contained in:
7
VisionBuilder.UI.IOCommander/EGPIOCommand.cs
Normal file
7
VisionBuilder.UI.IOCommander/EGPIOCommand.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace VisionBuilder.UI.IOCommander;
|
||||
|
||||
public enum EGPIOCommand
|
||||
{
|
||||
Start,
|
||||
Stop,
|
||||
}
|
||||
12
VisionBuilder.UI.IOCommander/EProcessingEvent.cs
Normal file
12
VisionBuilder.UI.IOCommander/EProcessingEvent.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace VisionBuilder.UI.IOCommander;
|
||||
|
||||
public enum EProcessingEvent
|
||||
{
|
||||
ProgramStarted,
|
||||
ProgramEnded,
|
||||
SessionStarted,
|
||||
SessionEnded,
|
||||
ErrorOccurred,
|
||||
GoodOccured,
|
||||
TestMode
|
||||
}
|
||||
119
VisionBuilder.UI.IOCommander/IOCommander.cs
Normal file
119
VisionBuilder.UI.IOCommander/IOCommander.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System.Collections;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace VisionBuilder.UI.IOCommander;
|
||||
|
||||
public class IOCommander
|
||||
{
|
||||
|
||||
SerialPort _port;
|
||||
public IOCommander(string COMPort)
|
||||
{
|
||||
|
||||
_port = new SerialPort(COMPort);
|
||||
_port.BaudRate = 57600;
|
||||
_port.ReadTimeout = 100;
|
||||
_port.WriteTimeout = 100;
|
||||
_port.Open();
|
||||
Task.Factory.StartNew(CheckPins, TaskCreationOptions.LongRunning);
|
||||
}
|
||||
|
||||
private int _currentPinsState = 0;
|
||||
public BitArray PinState { get; private set; }
|
||||
private void CheckPins()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (!_port.IsOpen) return;
|
||||
try
|
||||
{
|
||||
var req = new byte[] { 1, 0 };
|
||||
_port.Write(req, 0, req.Length);
|
||||
var readState = ~(_port.ReadByte());
|
||||
BitArray arrreadState = new BitArray(new byte[] { (byte)readState });
|
||||
PinState = new BitArray(new byte[] { (byte)readState }); ;
|
||||
string buf = "";
|
||||
foreach (bool b in arrreadState)
|
||||
{
|
||||
buf += b ? 1 : 0;
|
||||
}
|
||||
//Console.WriteLine(buf);
|
||||
if (_currentPinsState != readState)
|
||||
{
|
||||
|
||||
|
||||
BitArray arrcurrentPinsState = new BitArray(new byte[] { (byte)_currentPinsState });
|
||||
|
||||
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (arrcurrentPinsState[i] != arrreadState[i])
|
||||
OnPinChanged(i, arrreadState[i]);
|
||||
}
|
||||
}
|
||||
|
||||
_currentPinsState = readState;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
|
||||
}
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_port.IsOpen) _port.Close();
|
||||
}
|
||||
|
||||
public event Action<int, bool> OnPinChanged = delegate { };
|
||||
List<int> _readPins = new List<int>();
|
||||
private int _pinsState = 0;
|
||||
|
||||
|
||||
public void SetPins(int pin, bool state)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
//Log.Logger.ForContext<IOCommander>().Verbose("Setting pin {pin} to {state}", pin, state);
|
||||
|
||||
if (state)
|
||||
_pinsState |= (1 << pin);
|
||||
else
|
||||
_pinsState &= ~(1 << pin);
|
||||
var data = new byte[] { 0x00, (byte)_pinsState };
|
||||
try
|
||||
{
|
||||
_port.Write(data, 0, data.Length);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPins(int pin)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (!_readPins.Contains(pin))
|
||||
{
|
||||
_readPins.Add(pin);
|
||||
_port.WriteLine("$pinmo:" + pin + ";");
|
||||
|
||||
}
|
||||
|
||||
_port.WriteLine("$pinh:" + pin + ";");
|
||||
}
|
||||
}
|
||||
}
|
||||
20
VisionBuilder.UI.IOCommander/IOCommanderCommand.cs
Normal file
20
VisionBuilder.UI.IOCommander/IOCommanderCommand.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Inspectron.Settings.Attributes;
|
||||
|
||||
namespace VisionBuilder.UI.IOCommander;
|
||||
|
||||
public class IOCommanderCommand
|
||||
{
|
||||
public int Pin { get; set; }
|
||||
|
||||
[SettingDescription("Should be triggered when crossing from low to high or opposite?")]
|
||||
public bool OnHigh { get; set; }
|
||||
|
||||
public EGPIOCommand Command { get; set; }
|
||||
|
||||
|
||||
|
||||
override public string ToString()
|
||||
{
|
||||
return $"Pin: {Pin}, OnHigh: {OnHigh}, Command: {Command}";
|
||||
}
|
||||
}
|
||||
16
VisionBuilder.UI.IOCommander/IOCommanderPinAction.cs
Normal file
16
VisionBuilder.UI.IOCommander/IOCommanderPinAction.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace VisionBuilder.UI.IOCommander;
|
||||
|
||||
public class IOCommanderPinAction
|
||||
{
|
||||
public int Pin { get; set; }
|
||||
public bool Low { get; set; }
|
||||
public bool Pulse { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Pin: {Pin}, {(Low?"Low":"High")}, Pulse: {Pulse}";
|
||||
}
|
||||
}
|
||||
116
VisionBuilder.UI.IOCommander/IOCommanderProcessingEvent.cs
Normal file
116
VisionBuilder.UI.IOCommander/IOCommanderProcessingEvent.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace VisionBuilder.UI.IOCommander;
|
||||
|
||||
public class IOCommanderProcessingEvent
|
||||
{
|
||||
public IOCommanderProcessingEvent(EProcessingEvent @event)
|
||||
{
|
||||
Event = @event;
|
||||
}
|
||||
|
||||
public EProcessingEvent Event { get; set; }
|
||||
public List<IOCommanderPinAction> Actions { get; set; } = new List<IOCommanderPinAction>();
|
||||
}
|
||||
|
||||
public class IOCommanderPinActionListConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string stringValue)
|
||||
{
|
||||
// Check if we're converting to a single IOCommanderPinAction
|
||||
if (context?.PropertyDescriptor?.PropertyType == typeof(IOCommanderPinAction))
|
||||
{
|
||||
return ConvertFromString(context, culture, stringValue);
|
||||
}
|
||||
|
||||
// Otherwise, convert to a list of IOCommanderPinAction
|
||||
var actions = new List<IOCommanderPinAction>();
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
{
|
||||
var lines = stringValue.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var action = ConvertFromString(context, culture, line.Trim());
|
||||
if (action != null)
|
||||
actions.Add(action);
|
||||
}
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
// Handle single IOCommanderPinAction
|
||||
if (value is IOCommanderPinAction action)
|
||||
{
|
||||
return ConvertToString(context, culture, action);
|
||||
}
|
||||
|
||||
// Handle list of IOCommanderPinAction
|
||||
if (value is List<IOCommanderPinAction> actions)
|
||||
{
|
||||
var lines = actions.Select(a =>
|
||||
ConvertToString(context, culture, a)
|
||||
).Where(line => !string.IsNullOrEmpty(line));
|
||||
|
||||
return string.Join("\n", lines);
|
||||
}
|
||||
}
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a single string to IOCommanderPinAction
|
||||
/// </summary>
|
||||
private IOCommanderPinAction ConvertFromString(ITypeDescriptorContext context, CultureInfo culture, string stringValue)
|
||||
{
|
||||
if (string.IsNullOrEmpty(stringValue))
|
||||
return null;
|
||||
|
||||
var parts = stringValue.Split(',');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
if (int.TryParse(parts[0], out int pin) &&
|
||||
bool.TryParse(parts[1], out bool inverted) &&
|
||||
bool.TryParse(parts[2], out bool pulse))
|
||||
{
|
||||
return new IOCommanderPinAction
|
||||
{
|
||||
Pin = pin,
|
||||
Low = inverted,
|
||||
Pulse = pulse
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a single IOCommanderPinAction to string
|
||||
/// </summary>
|
||||
private string ConvertToString(ITypeDescriptorContext context, CultureInfo culture, IOCommanderPinAction action)
|
||||
{
|
||||
if (action == null)
|
||||
return null;
|
||||
|
||||
return $"{action.Pin},{action.Low},{action.Pulse}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace VisionBuilder.UI.IOCommander.Interfaces;
|
||||
|
||||
public interface IIOCommanderDebugViewService
|
||||
{
|
||||
public void SetInputPinStatus(int pin, bool state);
|
||||
public void SetOutputPinStatus(int pin, bool state);
|
||||
public void ShowDebugView();
|
||||
}
|
||||
36
VisionBuilder.UI.IOCommander/ModuleExtensions.cs
Normal file
36
VisionBuilder.UI.IOCommander/ModuleExtensions.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using Ninject.Extensions.ChildKernel;
|
||||
using System.ComponentModel;
|
||||
using Ninject;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.IOCommander.Interfaces;
|
||||
using VisionBuilder.UI.IOCommander.Modules;
|
||||
using VisionBuilder.UI.IOCommander.Settings;
|
||||
|
||||
namespace VisionBuilder.UI.IOCommander;
|
||||
|
||||
public static class ModuleExtensions
|
||||
{
|
||||
public static IKernel UseIOCommander(this IKernel self, string[] cameraNames)
|
||||
{
|
||||
// for settings to work properly
|
||||
TypeDescriptor.AddAttributes(typeof(List<IOCommanderPinAction>), new TypeConverterAttribute(typeof(IOCommanderPinActionListConverter)));
|
||||
|
||||
TypeDescriptor.AddAttributes(typeof(List<IOCommanderCommand>), new TypeConverterAttribute(typeof(IOCommanderCommandListConverter)));
|
||||
|
||||
List<IOCommanderCameraSettings> cameras = cameraNames.Select(name => new IOCommanderCameraSettings(name)).ToList();
|
||||
self.Bind<IOCommanderSettings, ISettings>().ToConstant(new IOCommanderSettings(cameras.ToArray()));
|
||||
self.Bind<IOCommanderModule,IVisionBuilderModule>().To<IOCommanderModule>().InSingletonScope();
|
||||
self.Bind<IOCommanderProgramModule, IVisionBuilderModule>().To<IOCommanderProgramModule>().InSingletonScope();
|
||||
self.Bind<IIOCommanderDebugViewService>().To<NoDebugView>().InSingletonScope();
|
||||
return self;
|
||||
}
|
||||
|
||||
public static IChildKernel UseCameraIOCommander(this IChildKernel self, string cameraName)
|
||||
{
|
||||
var ioCommanderSettings = self.Get<IOCommanderSettings>();
|
||||
var cameraSettings=ioCommanderSettings.Cameras.First(x => x.CameraName == cameraName);
|
||||
self.Bind<IOCommanderCameraSettings, ISettings>().ToConstant(cameraSettings);
|
||||
self.Bind<IOCommanderCameraModule, IVisionBuilderModule>().To<IOCommanderCameraModule>().InSingletonScope();
|
||||
return self;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using System.Runtime;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.ViewModel;
|
||||
using VisionBuilder.UI.IOCommander.Settings;
|
||||
|
||||
namespace VisionBuilder.UI.IOCommander.Modules;
|
||||
|
||||
public class IOCommanderCameraModule: IVisionBuilderModule
|
||||
{
|
||||
private readonly IOCommanderModule _mainModule;
|
||||
private readonly IOCommanderSettings _settings;
|
||||
private readonly IOCommanderCameraSettings _cameraSettings;
|
||||
private readonly IRecognitionControl _recognitionControl;
|
||||
private readonly MainWindowVM _mainWindowVm;
|
||||
|
||||
public IOCommanderCameraModule(IOCommanderModule mainModule, IOCommanderSettings settings, IOCommanderCameraSettings cameraSettings, IRecognitionControl recognitionControl, MainWindowVM mainWindowVm)
|
||||
{
|
||||
_mainModule = mainModule;
|
||||
_settings = settings;
|
||||
_cameraSettings = cameraSettings;
|
||||
_recognitionControl = recognitionControl;
|
||||
_mainWindowVm = mainWindowVm;
|
||||
|
||||
_recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed;
|
||||
_recognitionControl.SessionStarted += _recognitionControl_SessionStarted;
|
||||
_recognitionControl.SessionEnded += _recognitionControl_SessionEnded;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void EmitEvent(EProcessingEvent @event)
|
||||
{
|
||||
|
||||
var @eventActions = _cameraSettings.ProcessingEvents.First(x => x.Event == @event);
|
||||
var actions = @eventActions.Actions;
|
||||
if (_mainWindowVm.TestMode)
|
||||
{
|
||||
var testModeActions = _cameraSettings.TestModeOverride;
|
||||
var testModePins = testModeActions.Select(x => x.Pin).ToList();
|
||||
actions = actions.Where(x => !testModePins.Contains(x.Pin)).ToList();
|
||||
actions.AddRange(testModeActions);
|
||||
}
|
||||
|
||||
|
||||
foreach (var action in actions)
|
||||
{
|
||||
_mainModule.SetOutput(action.Pin, !action.Low);
|
||||
if (action.Pulse)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(_settings.PulseLength);
|
||||
_mainModule.SetOutput(action.Pin, action.Low);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeModule()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void _recognitionControl_SessionEnded(SessionEndedEvent obj)
|
||||
{
|
||||
EmitEvent(EProcessingEvent.SessionEnded);
|
||||
|
||||
}
|
||||
|
||||
private void _recognitionControl_SessionStarted(SessionStartedEvent obj)
|
||||
{
|
||||
EmitEvent(EProcessingEvent.SessionStarted);
|
||||
}
|
||||
|
||||
private void _recognitionControl_ImageProcessed(Common.Commands.ImageProcessedEvent obj)
|
||||
{
|
||||
|
||||
if (obj.HasError)
|
||||
{
|
||||
EmitEvent(EProcessingEvent.ErrorOccurred);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitEvent(EProcessingEvent.GoodOccured);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
62
VisionBuilder.UI.IOCommander/Modules/IOCommanderModule.cs
Normal file
62
VisionBuilder.UI.IOCommander/Modules/IOCommanderModule.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System.Collections.Concurrent;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.ViewModel;
|
||||
using VisionBuilder.UI.IOCommander.Interfaces;
|
||||
using VisionBuilder.UI.IOCommander.Settings;
|
||||
|
||||
namespace VisionBuilder.UI.IOCommander.Modules;
|
||||
|
||||
|
||||
public class IOCommanderModule:IVisionBuilderModule
|
||||
{
|
||||
private readonly IOCommanderSettings _settings;
|
||||
private readonly IIOCommanderDebugViewService _debugViewService;
|
||||
|
||||
private IOCommander? _ioCommander;
|
||||
public IOCommanderModule(IOCommanderSettings settings, IIOCommanderDebugViewService debugViewService)
|
||||
{
|
||||
_settings = settings;
|
||||
_debugViewService = debugViewService;
|
||||
}
|
||||
|
||||
public void InitializeModule()
|
||||
{
|
||||
if (!_settings.EmulationMode)
|
||||
{
|
||||
_ioCommander = new IOCommander(_settings.Port);
|
||||
_ioCommander.OnPinChanged += OnPinChanged;
|
||||
}
|
||||
|
||||
if(_settings.ShowDebugView)_debugViewService.ShowDebugView();
|
||||
|
||||
|
||||
}
|
||||
ConcurrentDictionary<int, bool> _pinStates = new ConcurrentDictionary<int, bool>();
|
||||
private void OnPinChanged(int arg1, bool arg2)
|
||||
{
|
||||
_pinStates.TryAdd(arg1, arg2);
|
||||
_debugViewService.SetInputPinStatus(arg1, arg2);
|
||||
}
|
||||
|
||||
public void SetOutput(int pin, bool value)
|
||||
{
|
||||
if (_ioCommander != null)
|
||||
{
|
||||
_ioCommander.SetPins(pin, value);
|
||||
}
|
||||
_debugViewService.SetOutputPinStatus(pin,value);
|
||||
}
|
||||
|
||||
public bool GetInput(int pin)
|
||||
{
|
||||
if (_ioCommander == null) return false;
|
||||
var found=_pinStates.TryGetValue(pin, out var value);
|
||||
if (found)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.ViewModel;
|
||||
using VisionBuilder.UI.IOCommander.Settings;
|
||||
|
||||
namespace VisionBuilder.UI.IOCommander.Modules;
|
||||
|
||||
public class IOCommanderProgramModule: IVisionBuilderModule
|
||||
{
|
||||
private readonly IOCommanderSettings _settings;
|
||||
private readonly IOCommanderModule _mainModule;
|
||||
private readonly MainWindowVM _mainWindowVm;
|
||||
|
||||
public IOCommanderProgramModule(IOCommanderSettings settings, IOCommanderModule mainModule, MainWindowVM mainWindowVm)
|
||||
{
|
||||
_settings = settings;
|
||||
_mainModule = mainModule;
|
||||
_mainWindowVm = mainWindowVm;
|
||||
_mainWindowVm.ProgramStarted += _mainWindowVm_ProgramStarted;
|
||||
_mainWindowVm.ProgramClosed += _mainWindowVm_ProgramClosed;
|
||||
}
|
||||
|
||||
private void EmitEvent(EProcessingEvent @event)
|
||||
{
|
||||
var actions = _settings.GlobalEvents.First(x => x.Event == @event);
|
||||
foreach (var action in actions.Actions)
|
||||
{
|
||||
_mainModule.SetOutput(action.Pin,!action.Low);
|
||||
if (action.Pulse)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(_settings.PulseLength);
|
||||
_mainModule.SetOutput(action.Pin, action.Low);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void _mainWindowVm_ProgramClosed()
|
||||
{
|
||||
EmitEvent(EProcessingEvent.ProgramEnded);
|
||||
}
|
||||
|
||||
|
||||
private void _mainWindowVm_ProgramStarted()
|
||||
{
|
||||
EmitEvent(EProcessingEvent.ProgramStarted);
|
||||
}
|
||||
|
||||
public void InitializeModule()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
22
VisionBuilder.UI.IOCommander/NoDebugView.cs
Normal file
22
VisionBuilder.UI.IOCommander/NoDebugView.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.IOCommander.Interfaces;
|
||||
|
||||
namespace VisionBuilder.UI.IOCommander;
|
||||
|
||||
public class NoDebugView: IIOCommanderDebugViewService
|
||||
{
|
||||
public void SetInputPinStatus(int pin, bool state)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetOutputPinStatus(int pin, bool state)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void ShowDebugView()
|
||||
{
|
||||
Log.Warning("Debug view is not available.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
using Inspectron.Settings;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using Inspectron.Settings.Attributes;
|
||||
using VisionBuilder.UI.Common;
|
||||
|
||||
namespace VisionBuilder.UI.IOCommander.Settings
|
||||
{
|
||||
public class IOCommanderCameraSettings:ISettings
|
||||
{
|
||||
public string CameraName { get; }
|
||||
|
||||
|
||||
public List<IOCommanderProcessingEvent> ProcessingEvents { get; set; } = [
|
||||
new (EProcessingEvent.SessionStarted),
|
||||
new (EProcessingEvent.SessionEnded),
|
||||
new (EProcessingEvent.ErrorOccurred),
|
||||
new (EProcessingEvent.GoodOccured),
|
||||
];
|
||||
|
||||
[SettingDescription(@"
|
||||
Overrides pin values during test mode.
|
||||
")]
|
||||
public List<IOCommanderPinAction> TestModeOverride { get; set; } = new List<IOCommanderPinAction>();
|
||||
|
||||
public List<IOCommanderCommand> GPIOCommands { get; set; } = new List<IOCommanderCommand>();
|
||||
|
||||
public IOCommanderCameraSettings(string cameraName)
|
||||
{
|
||||
CameraName = cameraName;
|
||||
}
|
||||
|
||||
public void RegisterSettings(InspectronSettings settings)
|
||||
{
|
||||
foreach (var processingEvent in ProcessingEvents)
|
||||
{
|
||||
settings.RegisterSimple(processingEvent, () => processingEvent.Actions, CameraName + "/IOCommander/Outputs/"+processingEvent.Event.ToString(), nameof(processingEvent.Actions));
|
||||
}
|
||||
|
||||
settings.RegisterSimple(this, () => TestModeOverride, CameraName + "/IOCommander/Outputs/" + nameof(TestModeOverride), nameof(TestModeOverride));
|
||||
|
||||
settings.RegisterSimple(this, () => GPIOCommands, CameraName + "/IOCommander/Inputs", nameof(GPIOCommands));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class IOCommanderCommandListConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (value is string stringValue)
|
||||
{
|
||||
// Check if we're converting to a single IOCommanderCommand
|
||||
if (context?.PropertyDescriptor?.PropertyType == typeof(IOCommanderCommand))
|
||||
{
|
||||
return ConvertFromString(context, culture, stringValue);
|
||||
}
|
||||
|
||||
// Otherwise, convert to a list of IOCommanderCommand
|
||||
var commands = new List<IOCommanderCommand>();
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
{
|
||||
var lines = stringValue.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var command = ConvertFromString(context, culture, line.Trim());
|
||||
if (command != null)
|
||||
commands.Add(command);
|
||||
}
|
||||
}
|
||||
return commands;
|
||||
}
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
// Handle single IOCommanderCommand
|
||||
if (value is IOCommanderCommand command)
|
||||
{
|
||||
return ConvertToString(context, culture, command);
|
||||
}
|
||||
|
||||
// Handle list of IOCommanderCommand
|
||||
if (value is List<IOCommanderCommand> commands)
|
||||
{
|
||||
var lines = commands.Select(c =>
|
||||
ConvertToString(context, culture, c)
|
||||
).Where(line => !string.IsNullOrEmpty(line));
|
||||
|
||||
return string.Join("\n", lines);
|
||||
}
|
||||
}
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a single string to IOCommanderCommand
|
||||
/// </summary>
|
||||
private IOCommanderCommand ConvertFromString(ITypeDescriptorContext context, CultureInfo culture, string stringValue)
|
||||
{
|
||||
if (string.IsNullOrEmpty(stringValue))
|
||||
return null;
|
||||
|
||||
var parts = stringValue.Split(',');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
if (int.TryParse(parts[0], out int pin) &&
|
||||
bool.TryParse(parts[1], out bool onHigh) &&
|
||||
Enum.TryParse<EGPIOCommand>(parts[2], out EGPIOCommand command))
|
||||
{
|
||||
return new IOCommanderCommand
|
||||
{
|
||||
Pin = pin,
|
||||
OnHigh = onHigh,
|
||||
Command = command
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a single IOCommanderCommand to string
|
||||
/// </summary>
|
||||
private string ConvertToString(ITypeDescriptorContext context, CultureInfo culture, IOCommanderCommand command)
|
||||
{
|
||||
if (command == null)
|
||||
return null;
|
||||
|
||||
return $"{command.Pin},{command.OnHigh},{command.Command}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
38
VisionBuilder.UI.IOCommander/Settings/IOCommanderSettings.cs
Normal file
38
VisionBuilder.UI.IOCommander/Settings/IOCommanderSettings.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Inspectron.Settings;
|
||||
using Inspectron.Settings.Attributes;
|
||||
using VisionBuilder.UI.Common;
|
||||
|
||||
namespace VisionBuilder.UI.IOCommander.Settings;
|
||||
|
||||
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";
|
||||
|
||||
public List<IOCommanderProcessingEvent> GlobalEvents { get; set; } =
|
||||
[
|
||||
new(EProcessingEvent.ProgramStarted),
|
||||
new(EProcessingEvent.ProgramEnded),
|
||||
];
|
||||
|
||||
public IOCommanderSettings(IOCommanderCameraSettings[] cameras)
|
||||
{
|
||||
Cameras = cameras;
|
||||
}
|
||||
|
||||
public void RegisterSettings(InspectronSettings settings)
|
||||
{
|
||||
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)
|
||||
{
|
||||
settings.RegisterSimple(@event, () => @event.Actions, "IOCommander/Global outputs/" + @event.Event.ToString(), nameof(@event.Actions));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.Ports" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\framework\Inspectron.Settings\Inspectron.Settings.csproj" />
|
||||
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user