before candybox editor update
This commit is contained in:
108
Plugins/CandyboxPlugin/Modules/Barcode/CandyboxBarcodeReader.cs
Normal file
108
Plugins/CandyboxPlugin/Modules/Barcode/CandyboxBarcodeReader.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System.IO.Ports;
|
||||
using System.Text;
|
||||
using MaterialSkin.Controls;
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Statistics;
|
||||
|
||||
namespace CandyboxPlugin.Modules.Barcode;
|
||||
|
||||
public class CandyboxBarcodeReader : IVisionBuilderModule
|
||||
{
|
||||
private readonly ILoadingService _loadingService;
|
||||
|
||||
private readonly CandyboxBarcodeReaderSettings _settings;
|
||||
private readonly IRecognitionControl _recognitionControl;
|
||||
private readonly VisionBuilderStatistics _statistics;
|
||||
private SerialPort _port;
|
||||
public EReaderState _readerState = EReaderState.ReadingRecipe;
|
||||
private MaterialCancellableLoader? _loader;
|
||||
|
||||
public CandyboxBarcodeReader(CandyboxBarcodeReaderSettings settings, IRecognitionControl recognitionControl, VisionBuilderStatistics statistics)
|
||||
{
|
||||
_settings = settings;
|
||||
_recognitionControl = recognitionControl;
|
||||
_statistics = statistics;
|
||||
}
|
||||
|
||||
private void _port_DataReceived(object sender, SerialDataReceivedEventArgs e)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
if(_readerState == EReaderState.ReadingRecipe)
|
||||
ReadRecipe();
|
||||
if (_readerState == EReaderState.ReadingAUF)
|
||||
ReadAUF();
|
||||
}
|
||||
|
||||
private void ReadRecipe()
|
||||
{
|
||||
if (_recognitionControl.IsRunning) return;
|
||||
|
||||
byte[] buffer = new byte[_port.BytesToRead];
|
||||
_port.Read(buffer, 0, _port.BytesToRead);
|
||||
var data = Encoding.ASCII.GetString(buffer).Trim();
|
||||
var recipes = _recognitionControl.GetRecipesData();
|
||||
|
||||
string searchString = data;
|
||||
if (_settings.BarcodeRecipeMappings.Any(x => x.Barcode == searchString))
|
||||
{
|
||||
searchString = _settings.BarcodeRecipeMappings.First(x => x.Barcode == searchString).RecipeName;
|
||||
}
|
||||
|
||||
var existingRecipe = recipes.FirstOrDefault(x => x.RecipeName == searchString);
|
||||
if (existingRecipe == null)
|
||||
{
|
||||
Log.Warning("No matching recipe found for data: {Data}", data);
|
||||
return;
|
||||
}
|
||||
|
||||
_recognitionControl.SetRecipe(existingRecipe);
|
||||
_readerState = EReaderState.ReadingAUF;
|
||||
_loader = new MaterialCancellableLoader();
|
||||
_loader.StartLoading("Waiting for AUF...");
|
||||
_loader.LoadingCancelled += CancelWaiting;
|
||||
}
|
||||
|
||||
private void CancelWaiting(object? sender, string e)
|
||||
{
|
||||
_readerState = EReaderState.ReadingRecipe;
|
||||
_loader.Close();
|
||||
_loader.LoadingCancelled -= CancelWaiting;
|
||||
_loader = null;
|
||||
}
|
||||
|
||||
private void ReadAUF()
|
||||
{
|
||||
byte[] buffer = new byte[_port.BytesToRead];
|
||||
_port.Read(buffer, 0, _port.BytesToRead);
|
||||
var data = Encoding.ASCII.GetString(buffer).Trim();
|
||||
if (_loader != null)
|
||||
{
|
||||
_loader.Close();
|
||||
_loader.LoadingCancelled -= CancelWaiting;
|
||||
_loader = null;
|
||||
}
|
||||
|
||||
|
||||
_statistics.Metadata = data;
|
||||
_recognitionControl.Start();
|
||||
_readerState = EReaderState.ReadingAUF;
|
||||
}
|
||||
|
||||
|
||||
public void InitializeModule()
|
||||
{
|
||||
try
|
||||
{
|
||||
_port = new SerialPort(_settings.ComPort, 9600);
|
||||
_port.Open();
|
||||
_port.DataReceived += _port_DataReceived;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warning(e, "Failed to open com-port");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using Inspectron.Settings;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using VisionBuilder.UI.Common;
|
||||
|
||||
namespace CandyboxPlugin.Modules.Barcode;
|
||||
|
||||
public class CandyboxBarcodeReaderSettings(string CameraName) : ISettings
|
||||
{
|
||||
public string ComPort { get; set; } = "COM3";
|
||||
|
||||
public List<BarcodeRecipeMapping> BarcodeRecipeMappings { get; set; } = new List<BarcodeRecipeMapping>();
|
||||
|
||||
public void RegisterSettings(InspectronSettings settings)
|
||||
{
|
||||
settings.RegisterSimple(this, () => ComPort, CameraName + "/Barcode reader", nameof(ComPort));
|
||||
settings.RegisterSimple(this, () => BarcodeRecipeMappings, CameraName + "/Barcode reader", nameof(BarcodeRecipeMappings));
|
||||
}
|
||||
}
|
||||
|
||||
public class BarcodeRecipeMapping
|
||||
{
|
||||
public string Barcode { get; set; }
|
||||
public string RecipeName { get; set; }
|
||||
}
|
||||
|
||||
public class ListBarcodeRecipeMappingConverter : ITypeConverter
|
||||
{
|
||||
public object ConvertFrom(object value)
|
||||
{
|
||||
if (value is string json)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<List<BarcodeRecipeMapping>>(json, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
});
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to deserialize BarcodeRecipeMapping list from JSON.");
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException("Value must be a JSON string.");
|
||||
}
|
||||
|
||||
public object ConvertTo(object value, Type destinationType)
|
||||
{
|
||||
if (value is List<BarcodeRecipeMapping> list && destinationType == typeof(string))
|
||||
{
|
||||
return JsonSerializer.Serialize(list, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = false,
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
});
|
||||
}
|
||||
throw new InvalidOperationException("Value must be a List<BarcodeRecipeMapping> and destinationType must be string.");
|
||||
|
||||
}
|
||||
}
|
||||
7
Plugins/CandyboxPlugin/Modules/Barcode/EReaderState.cs
Normal file
7
Plugins/CandyboxPlugin/Modules/Barcode/EReaderState.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace CandyboxPlugin.Modules.Barcode;
|
||||
|
||||
public enum EReaderState
|
||||
{
|
||||
ReadingRecipe,
|
||||
ReadingAUF
|
||||
}
|
||||
105
Plugins/CandyboxPlugin/Modules/CandyboxImageProcessingControl.cs
Normal file
105
Plugins/CandyboxPlugin/Modules/CandyboxImageProcessingControl.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using CandyboxPlugin.Recipe;
|
||||
using Hawkeye.VisionBuilder.UI.Sources.Hawkeye;
|
||||
using Newtonsoft.Json;
|
||||
using OpenCvSharp;
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Common.ViewModel.Classes;
|
||||
|
||||
namespace CandyboxPlugin.Module;
|
||||
|
||||
public class CandyboxImageProcessingControl : BaseRecognitionControl
|
||||
{
|
||||
private readonly IImageSource _imageSource;
|
||||
private HistoRecipe _histoRecipe;
|
||||
|
||||
public CandyboxImageProcessingControl(CandyboxRecognitionControlSettings settings, IImageSource imageSource, ILoadingService loadingService) : base(settings, loadingService)
|
||||
{
|
||||
_imageSource = imageSource;
|
||||
}
|
||||
|
||||
private const string RECIPES_DIR = @"..\Data\Recipes";
|
||||
private const string SAMPLES_DIR = @"..\Data\Samples";
|
||||
|
||||
public override List<RecipeData> GetRecipesData()
|
||||
{
|
||||
var recipes = new List<RecipeData>();
|
||||
var recipeFiles = Directory.GetFiles(RECIPES_DIR, "*.json");
|
||||
foreach (var file in recipeFiles)
|
||||
{
|
||||
var name = Path.GetFileNameWithoutExtension(file);
|
||||
|
||||
Mat? image = null;
|
||||
var filePatterns = new[]
|
||||
{
|
||||
name + ".bmp",
|
||||
"r" + name + ".bmp",
|
||||
name.Replace("recipe", "") + ".bmp"
|
||||
};
|
||||
|
||||
foreach (var pattern in filePatterns)
|
||||
{
|
||||
var filePath = Path.Combine(SAMPLES_DIR, pattern);
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
image = Cv2.ImRead(filePath);
|
||||
break; // Exit loop after finding the first matching file
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Debug($"File not found: {filePath}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
recipes.Add(new RecipeData
|
||||
{
|
||||
RecipeName = name,
|
||||
Image = image
|
||||
});
|
||||
}
|
||||
return recipes;
|
||||
}
|
||||
|
||||
protected override void Initialize(RecipeData currentRecipe)
|
||||
{
|
||||
_histoRecipe = new HistoRecipe(currentRecipe.RecipeName);
|
||||
if (_imageSource is HawkeyeCameraImageSource hawkeye)
|
||||
{
|
||||
var settings = hawkeye.Settings;
|
||||
settings.ImageSettings = settings.ImageSettings with {Lines = _histoRecipe.GetCameraWidth()};
|
||||
hawkeye.ApplySettings(settings);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WarmUp()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override (Mat originalImage, Mat analysisImage, TimeSpan processingTime, TimeSpan acquisitionTime, string[] errorNames)?
|
||||
ProcessImage(CancellationToken token)
|
||||
{
|
||||
var swTotal = System.Diagnostics.Stopwatch.StartNew();
|
||||
var swAcquision = System.Diagnostics.Stopwatch.StartNew();
|
||||
var image = _imageSource.GetImage(token).Result;
|
||||
swAcquision.Stop();
|
||||
if (image==null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var res=_histoRecipe.ProcessImage(image);
|
||||
swTotal.Stop();
|
||||
|
||||
var allErrors =
|
||||
res.ErrorPoints.Select(x => x.ToString())
|
||||
.Concat(
|
||||
res.ErrorReason.Where(x=>x != EErrorReason.Good).Select(x=>x.ToString())
|
||||
).ToArray();
|
||||
|
||||
return (image, res.ProcessedImage, swTotal.Elapsed, swAcquision.Elapsed, allErrors);
|
||||
|
||||
}
|
||||
}
|
||||
30
Plugins/CandyboxPlugin/Modules/CandyboxLearningTool.cs
Normal file
30
Plugins/CandyboxPlugin/Modules/CandyboxLearningTool.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using CandyboxPlugin.Recipe;
|
||||
using Inspectron.HawkEye.View;
|
||||
using Lindt.Candybox.Demo;
|
||||
using OpenCvSharp;
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
||||
using VisionBuilder.UI.Windows.Settings;
|
||||
|
||||
namespace CandyboxPlugin.Module;
|
||||
|
||||
public class CandyboxLearningTool: ILearningTool
|
||||
{
|
||||
|
||||
private ImagePreview _activeWindow;
|
||||
public CandyboxLearningTool(IImagePreviewService imagePreviewService)
|
||||
{
|
||||
_activeWindow = (imagePreviewService as WindowsImagePreviewService)!.CurrentWindow!;
|
||||
}
|
||||
|
||||
public bool IsLearningEnabled(string recipeName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Learn(string recipeName, Mat image)
|
||||
{
|
||||
var editor = new MapEditor(image, new HistoRecipe(recipeName));
|
||||
editor.ShowDialog(_activeWindow);
|
||||
}
|
||||
}
|
||||
13
Plugins/CandyboxPlugin/Modules/CandyboxRecipeCreationTool.cs
Normal file
13
Plugins/CandyboxPlugin/Modules/CandyboxRecipeCreationTool.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using MaterialSkin.Core.Controls;
|
||||
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
||||
|
||||
namespace CandyboxPlugin.Module;
|
||||
|
||||
public class CandyboxRecipeCreationTool:IRecipeCreationTool
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
public bool CreateRecipe(out string recipeName)
|
||||
{
|
||||
return MaterialInputBox.Prompt("Recipe name","", out recipeName)==DialogResult.OK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using VisionBuilder.UI.Common.Processing;
|
||||
|
||||
namespace CandyboxPlugin.Module;
|
||||
|
||||
public class CandyboxRecognitionControlSettings : BaseRecognitionControlSettings
|
||||
{
|
||||
public CandyboxRecognitionControlSettings(string cameraName) : base(cameraName)
|
||||
{
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user