176 lines
6.1 KiB
C#
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 = "..\\Data\\Recipes\\" + currentRecipe.RecipeName + ".jhrcp";
|
|
_currentRecipeFilePath = Path.GetFullPath(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();
|
|
}
|
|
}
|
|
}
|