Files
HawkeyeVision/VisionBuilder.UI.Recipes.HawkeyeRecipe/HawkeyeRecognitionControl.cs
EugeneTes 4bd117af47 plugin system docs
custom buttons for plugins
calibration functions for Leeform
2026-03-30 16:15:47 +02:00

176 lines
6.1 KiB
C#

using System.CodeDom;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Hawkeye.VisionBuilder.Workflow;
using Hawkeye.VisionBuilder.Workflow.Datatypes;
using Hawkeye.VisionBuilder.Workflow.Operations;
using OpenCvSharp;
using Serilog;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.Processing;
using VisionBuilder.UI.Common.ViewModel.Classes;
namespace VisionBuilder.UI.Recipes.HawkeyeRecipe
{
public class HawkeyeRecognitionControl: BaseRecognitionControl
{
private readonly IImageSource _imageSource;
private readonly HawkeyeRecognitionSettings _recognitionConfiguration;
private CancellationTokenSource _cts;
private Task _loopTask;
private FileSystemWatcher? _fileWatcher;
private System.Timers.Timer? _debounceTimer;
private string? _pendingHotReloadFile;
private string? _currentRecipeFilePath;
public HawkeyeRecognitionControl(IImageSource imageSource, HawkeyeRecognitionSettings recognitionConfiguration, ILoadingService loadingService): base(recognitionConfiguration,loadingService)
{
_imageSource = imageSource;
_recognitionConfiguration = recognitionConfiguration;
}
public override List<RecipeData> GetRecipesData()
{
LoadingService.StartLoading($"Loading recipes for {_recognitionConfiguration.CameraName}...");
try
{
var files = WorkflowRecipeHelper.ListRecipeFiles(_recognitionConfiguration.RecipeNameFilter);
List<RecipeData> recipes = new List<RecipeData>();
foreach (var file in files)
{
var workFlow = WorkflowList.LoadJSONFromFile(file);
var recipe = new RecipeData
{
RecipeName = Path.GetFileNameWithoutExtension(file),
Image = workFlow.RecipeImage
};
recipes.Add(recipe);
}
return recipes;
}
finally
{
LoadingService.StopLoading($"Loading recipes for {_recognitionConfiguration.CameraName}...");
}
}
private WorkflowList _workflow;
protected override void Initialize(RecipeData currentRecipe)
{
var path = Path.GetFullPath(Path.Combine("..", "Data", "Recipes", currentRecipe.RecipeName + ".jhrcp"));
_currentRecipeFilePath = path;
_workflow = WorkflowList.LoadJSONFromFile(path);
StartFileWatcher(_currentRecipeFilePath);
}
protected override void WarmUp()
{
_workflow.ImageSource = new EmptyImageSource();
_workflow.Context = new Context();
_workflow.Context.CancellationToken = CancellationToken.None;
_workflow.Execute();
}
protected override (Mat originalImage, Mat analysisImage,TimeSpan processingTime, TimeSpan acquisitionTime, string[] errorNames)? ProcessImage(CancellationToken token)
{
var pendingFile = Interlocked.Exchange(ref _pendingHotReloadFile, null);
if (pendingFile != null)
_workflow.HotReloadParameters(pendingFile);
var sw = Stopwatch.StartNew();
_workflow.Context.CancellationToken = token;
_workflow.ImageSource = _imageSource;
try
{
_workflow.Execute();
}
catch (Exception e) when (token.IsCancellationRequested)
{
sw.Stop();
return null;
}
sw.Stop();
var annotated = _workflow.Context.ActiveImage.ImageData.Clone();
var canvas = new OpenCVCanvas(annotated);
_workflow.Context.GraphicsElements.ForEach(x => x.Draw(canvas));
var originalImage = _workflow.Context.LastCameraImage.ImageData;
var analysisImage = annotated;
var cameraOperation = _workflow.Operations.FirstOrDefault(x => x is GetImageOperation);
TimeSpan cameraTime = TimeSpan.FromMilliseconds(0);
if (cameraOperation != null)
{
cameraTime = cameraOperation.ExecutionTime;
}
string[] errorNames = _workflow.Operations.Where(x => !x.Result).Select(x => x.Label).ToArray();
if (_recognitionConfiguration.AlwaysError)
{
errorNames = errorNames.Concat(new[] { "Forced error" }).ToArray();
}
return (originalImage, analysisImage, sw.Elapsed, cameraTime, errorNames);
}
private void StartFileWatcher(string filePath)
{
StopFileWatcher();
var directory = Path.GetDirectoryName(filePath)!;
var fileName = Path.GetFileName(filePath);
_debounceTimer = new System.Timers.Timer(500) { AutoReset = false };
_debounceTimer.Elapsed += (_, _) => RaiseRecipeFileChanged();
_fileWatcher = new FileSystemWatcher(directory, fileName)
{
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
EnableRaisingEvents = true
};
_fileWatcher.Changed += (_, _) =>
{
_debounceTimer.Stop();
_debounceTimer.Start();
};
}
private void StopFileWatcher()
{
if (_fileWatcher != null)
{
_fileWatcher.EnableRaisingEvents = false;
_fileWatcher.Dispose();
_fileWatcher = null;
}
if (_debounceTimer != null)
{
_debounceTimer.Stop();
_debounceTimer.Dispose();
_debounceTimer = null;
}
}
public override void HotReload()
{
Interlocked.Exchange(ref _pendingHotReloadFile, _currentRecipeFilePath);
}
protected override void Cleanup()
{
StopFileWatcher();
}
}
}