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 GetRecipesData() { LoadingService.StartLoading($"Loading recipes for {_recognitionConfiguration.CameraName}..."); try { var files = WorkflowRecipeHelper.ListRecipeFiles(_recognitionConfiguration.RecipeNameFilter); List recipes = new List(); 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(); } } }