before candybox editor update

This commit is contained in:
meelstorm
2025-09-16 10:42:43 +02:00
parent e5746ef766
commit dc12b3e51a
103 changed files with 96379 additions and 343 deletions

View File

@@ -0,0 +1,191 @@
using System.Diagnostics;
using OpenCvSharp;
using Serilog;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.ViewModel.Classes;
namespace VisionBuilder.UI.Common.Processing;
public abstract class BaseRecognitionControl: IRecognitionControl
{
private readonly BaseRecognitionControlSettings _settings;
private readonly ILoadingService _loadingService;
private CancellationTokenSource _cts;
private Task _loopTask;
public BaseRecognitionControl(BaseRecognitionControlSettings settings, ILoadingService loadingService)
{
_settings = settings;
_loadingService = loadingService;
}
public abstract List<RecipeData> GetRecipesData();
RecipeData _currentRecipe;
public void SetRecipe(RecipeData recipe)
{
// error if running
if (IsRunning)
{
throw new InvalidOperationException("Cannot change recipe while recognition is running.");
}
_currentRecipe = recipe;
}
public bool IsRunning { get; set; }
public bool IsPaused { get; set; }
public RecipeData CurrentRecipe => _currentRecipe;
private DateTime _startTime;
public void Start()
{
if (_currentRecipe == null)
{
throw new InvalidOperationException("No recipe set.");
}
if (IsRunning)
{
throw new InvalidOperationException("Recognition is already running.");
}
_loadingService.StartLoading($"Starting recipe {_currentRecipe.RecipeName} on {_settings.CameraName}...");
Initialize(_currentRecipe);
_startTime = DateTime.Now;
IsRunning = true;
_cts = new CancellationTokenSource();
Log.Information($"Warming up");
try
{
var swWarmup = Stopwatch.StartNew();
WarmUp();
swWarmup.Stop();
Log.Information($"Warmup run took {swWarmup.ElapsedMilliseconds} ms");
}
catch (Exception ex)
{
Log.Error(ex, "Error during warmup run");
}
_loopTask = Task.Run(() => Loop(_cts.Token), _cts.Token);
SessionStarted?.Invoke(new SessionStartedEvent
{
RecipeName = _currentRecipe.RecipeName,
SessionStarted = _startTime,
});
_loadingService.StopLoading($"Starting recipe {_currentRecipe.RecipeName} on {_settings.CameraName}...");
}
protected abstract void Initialize(RecipeData currentRecipe);
protected abstract void WarmUp();
protected abstract (Mat originalImage, Mat analysisImage, TimeSpan processingTime, TimeSpan acquisitionTime, string[] errorNames)? ProcessImage(CancellationToken token);
public void Stop()
{
if (!IsRunning)
return;
_loadingService.StartLoading($"Stopping {_settings.CameraName}");
_cts?.Cancel();
try
{
_loopTask?.Wait();
}
catch (TaskCanceledException)
{
// Ignore cancellation exceptions
}
catch (AggregateException ex) when (ex.Flatten().InnerExceptions.Any(x=>x is TaskCanceledException))
{
// Ignore cancellation exceptions
}
catch(Exception ex)
{
Log.Error(ex, "Error while stopping recognition loop");
}
finally
{
IsRunning = false;
_cts?.Dispose();
_cts = null;
_loopTask = null;
}
SessionEnded?.Invoke(new SessionEndedEvent
{
SessionEnded = DateTime.Now
});
_loadingService.StopLoading($"Stopping {_settings.CameraName}");
}
public void Pause()
{
IsPaused = true;
}
public void Resume()
{
IsPaused = false;
}
private async Task Loop(CancellationToken token)
{
// Start the recognition process using the Hawkeye library
while (!token.IsCancellationRequested)
{
if (IsPaused)
{
await Task.Delay(100, token);
continue;
}
var processed = ProcessImage(token);
if (processed == null)
{
return;
}
if (processed.Value.processingTime.Milliseconds < _settings.MinimumProcessingTime)
{
Thread.Sleep(_settings.MinimumProcessingTime - (int)processed.Value.processingTime.Milliseconds);
}
var processingResult = new ImageProcessedEvent()
{
SessionStart = _startTime,
RecipeName = _currentRecipe.RecipeName,
AnalysisTime = processed.Value.processingTime - processed.Value.acquisitionTime,
ErrorNames = processed.Value.errorNames.ToList(),
HasError = processed.Value.errorNames.Length > 0,
ImageSource = _settings.CameraName,
ImageOriginal = processed.Value.originalImage,
ImageAnalysis = processed.Value.analysisImage
};
var swProcessed = Stopwatch.StartNew();
ImageProcessed(processingResult);
swProcessed.Stop();
Log.Information($"ImageProcessed event handled in {swProcessed.ElapsedMilliseconds} ms");
}
}
public event Action<ImageProcessedEvent> ImageProcessed = delegate { };
public event Action<SessionStartedEvent>? SessionStarted = delegate { };
public event Action<SessionEndedEvent>? SessionEnded = delegate { };
}

View File

@@ -0,0 +1,17 @@
using Inspectron.Settings;
namespace VisionBuilder.UI.Common.Processing;
public abstract class BaseRecognitionControlSettings(string cameraName) : ISettings
{
public string CameraName { get; } = cameraName;
public int MinimumProcessingTime { get; set; }
public string RecipeNameFilter { get; set; } = "*";
public virtual void RegisterSettings(InspectronSettings settings)
{
settings.RegisterSimple(this, () => this.MinimumProcessingTime, CameraName + "/Hawkeye recognition", "Minimum processing time (ms)");
settings.RegisterSimple(this, () => this.RecipeNameFilter, CameraName + "/Hawkeye recognition", nameof(RecipeNameFilter));
}
}

View File

@@ -1,6 +1,6 @@
using OpenCvSharp;
namespace VisionBuilder.UI.Common.RecipeProcessing;
namespace VisionBuilder.UI.Common.Processing;
public interface IImageSource
{

View File

@@ -1,4 +1,4 @@
namespace VisionBuilder.UI.Common.RecipeProcessing;
namespace VisionBuilder.UI.Common.Processing;
public interface ILoadingService
{

View File

@@ -1,11 +1,11 @@
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.ViewModel.Classes;
namespace VisionBuilder.UI.Common.RecipeProcessing;
namespace VisionBuilder.UI.Common.Processing;
public interface IRecognitionControl
{
public bool IsRunning { get; }
List<RecipeData> GetRecipesData();
void SetRecipe(RecipeData recipe);
void Start();