From 44748f2855ca7799a86a8f07c7494a2135ad1db2 Mon Sep 17 00:00:00 2001 From: meelstorm <8780022+TesAnti@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:14:11 +0200 Subject: [PATCH] 1.0.7 --- .../Program.cs | 37 +++- .../IDSImageSource.cs | 21 +- .../B24SiemensPlugin/B24SiemensPlugin.csproj | 4 + .../B24SiemensPlugin/SiemensCameraModule.cs | 15 +- .../Commands/ImageProcessedEvent.cs | 2 + .../Processing/BaseRecognitionControl.cs | 189 ++++++++++-------- .../ViewModel/SingleCameraVM.cs | 17 +- .../VisionBuilderStatistics.cs | 5 + .../VisionBuilder.UI.Windows.Uno.csproj | 2 +- 9 files changed, 193 insertions(+), 99 deletions(-) diff --git a/Hawkeye.VisionBuilder.UI.RecipeConverter/Program.cs b/Hawkeye.VisionBuilder.UI.RecipeConverter/Program.cs index 96ccebf..bd5f8b0 100644 --- a/Hawkeye.VisionBuilder.UI.RecipeConverter/Program.cs +++ b/Hawkeye.VisionBuilder.UI.RecipeConverter/Program.cs @@ -1,7 +1,33 @@ using System.Xml; using Hawkeye.VisionBuilder.Workflow; -var files = Directory.GetFiles(AppContext.BaseDirectory, "*.hrcp", SearchOption.TopDirectoryOnly); +// Check if directory argument is provided +if (args.Length == 0) +{ + Console.WriteLine("Usage: RecipeConverter.exe "); + Console.WriteLine("Example: RecipeConverter.exe C:\\MyRecipes"); + return; +} + +string targetDirectory = args[0]; + +// Validate directory exists +if (!Directory.Exists(targetDirectory)) +{ + Console.WriteLine($"Error: Directory '{targetDirectory}' does not exist."); + return; +} + +var files = Directory.GetFiles(targetDirectory, "*.hrcp", SearchOption.TopDirectoryOnly); + +if (files.Length == 0) +{ + Console.WriteLine($"No *.hrcp files found in directory '{targetDirectory}'"); + return; +} + +Console.WriteLine($"Found {files.Length} recipe file(s) in '{targetDirectory}'"); + foreach (var file in files) { try @@ -9,8 +35,13 @@ foreach (var file in files) Console.WriteLine($"Converting {file}"); var f = File.OpenRead(file); WorkflowList list = new WorkflowList(); - list.Load(new BinaryReader(f),new OperationDiscoveryService(list)); - list.SaveJSON(Path.GetFileNameWithoutExtension(file)+".jhrcp"); + list.Load(new BinaryReader(f), new OperationDiscoveryService(list)); + + // Save the converted file in the same directory as the source + string outputPath = Path.Combine(targetDirectory, Path.GetFileNameWithoutExtension(file) + ".jhrcp"); + list.SaveJSON(outputPath); + + Console.WriteLine($"Saved: {outputPath}"); } catch (Exception ex) { diff --git a/Hawkeye.VisionBuilder.UI.Sources.IDS/IDSImageSource.cs b/Hawkeye.VisionBuilder.UI.Sources.IDS/IDSImageSource.cs index 2ef616e..037f417 100644 --- a/Hawkeye.VisionBuilder.UI.Sources.IDS/IDSImageSource.cs +++ b/Hawkeye.VisionBuilder.UI.Sources.IDS/IDSImageSource.cs @@ -41,6 +41,7 @@ namespace Inspectron.Camera.UEye { uEye.Defines.ColorMode mode; _camera.PixelFormat.Get(out mode); + int channels = 0; if (mode == uEye.Defines.ColorMode.Mono8) @@ -53,7 +54,7 @@ namespace Inspectron.Camera.UEye } else if (mode == uEye.Defines.ColorMode.BGRA8Packed) { - channels = 3; + channels = 4; } else { @@ -67,9 +68,18 @@ namespace Inspectron.Camera.UEye Byte[] u8img; _camera.Memory.CopyToArray(idx, out u8img); + + + // Warning! This will work only if pitch == width * channels + if (pitch != size.Width * channels) + { + throw new Exception("pitch must be equal to width * channels"); + } + Mat mat = new Mat(size.Height, size.Width,MatType.CV_8UC3); + // copy data to mat + System.Runtime.InteropServices.Marshal.Copy(u8img, 0, mat.Data, u8img.Length); + - Mat mat = new Mat(size.Height,pitch, MatType.CV_8UC(channels)); - mat.SetArray(u8img); return mat; } @@ -88,14 +98,13 @@ namespace Inspectron.Camera.UEye } uEye.Types.SensorInfo info = new uEye.Types.SensorInfo(); _camera.Information.GetSensorInfo(out info); - Console.WriteLine($"Camera {cameraIdx} opened: {info.SensorName} ({info.SensorID})"); + Log.Information($"Camera {cameraIdx} opened: {info.SensorName} ({info.SensorID})"); _camera.Parameter.Load(); - Console.WriteLine($"Camera {cameraIdx} parameters loaded"); + Log.Information($"Camera {cameraIdx} parameters loaded"); _camera.Trigger.Set(uEye.Defines.TriggerMode.Continuous); statusRet = _camera.Memory.Allocate(); - Console.WriteLine("Memory allocated"); if (statusRet != uEye.Defines.Status.Success) { throw new Exception("Allocate Memory failed"); diff --git a/Plugins/Siemens/B24SiemensPlugin/B24SiemensPlugin.csproj b/Plugins/Siemens/B24SiemensPlugin/B24SiemensPlugin.csproj index 10f0a0a..39c8dc7 100644 --- a/Plugins/Siemens/B24SiemensPlugin/B24SiemensPlugin.csproj +++ b/Plugins/Siemens/B24SiemensPlugin/B24SiemensPlugin.csproj @@ -16,5 +16,9 @@ false runtime + + false + runtime + diff --git a/Plugins/Siemens/B24SiemensPlugin/SiemensCameraModule.cs b/Plugins/Siemens/B24SiemensPlugin/SiemensCameraModule.cs index dc86de9..c87ae0d 100644 --- a/Plugins/Siemens/B24SiemensPlugin/SiemensCameraModule.cs +++ b/Plugins/Siemens/B24SiemensPlugin/SiemensCameraModule.cs @@ -1,4 +1,6 @@ -using VisionBuilder.UI.Common; +using Serilog; +using VisionBuilder.UI.Common; +using VisionBuilder.UI.Statistics; namespace B24SiemensPlugin; @@ -6,13 +8,15 @@ public class SiemensCameraModule: IVisionBuilderModule { private readonly SingleCameraVM _singleCameraVm; private readonly B24SiemensSettings _settings; + private readonly VisionBuilderStatistics _statistics; private SiemensServer _server; - public SiemensCameraModule(SingleCameraVM singleCameraVm,B24SiemensSettings settings) + public SiemensCameraModule(SingleCameraVM singleCameraVm,B24SiemensSettings settings, VisionBuilderStatistics statistics) { _singleCameraVm = singleCameraVm; _settings = settings; + _statistics = statistics; } public void InitializeModule() @@ -32,6 +36,11 @@ public class SiemensCameraModule: IVisionBuilderModule } private PLCPacket _server_OnPLCPacket(PLCPacket packet) { + if (_singleCameraVm.SynchronizationContext == null) + { + Log.Warning("Synchronization context is null, cannot update UI from PLC packet."); + return packet; + } packet.KameraStart= _singleCameraVm.IsRunning; var recipe = GetMapping(packet.Materialnummer); var selectedRecipe = _singleCameraVm.SelectedRecipe; @@ -45,7 +54,7 @@ public class SiemensCameraModule: IVisionBuilderModule { _singleCameraVm.ProcessRecipeSelectionVm(vm); }, null); - + _statistics.Metadata = packet.Materialnummer.ToString(); } } diff --git a/VisionBuilder.UI.Common/Commands/ImageProcessedEvent.cs b/VisionBuilder.UI.Common/Commands/ImageProcessedEvent.cs index f9a6b19..3728c97 100644 --- a/VisionBuilder.UI.Common/Commands/ImageProcessedEvent.cs +++ b/VisionBuilder.UI.Common/Commands/ImageProcessedEvent.cs @@ -14,4 +14,6 @@ public class ImageProcessedEvent:IEvent public bool HasError { get; set; } public List ErrorNames { get; set; } public TimeSpan AnalysisTime { get; set; } + public TimeSpan AcquisitionTime { get; set; } + public TimeSpan SleepTime { get; set; } } \ No newline at end of file diff --git a/VisionBuilder.UI.Common/Processing/BaseRecognitionControl.cs b/VisionBuilder.UI.Common/Processing/BaseRecognitionControl.cs index 0cf21f5..0c34ec9 100644 --- a/VisionBuilder.UI.Common/Processing/BaseRecognitionControl.cs +++ b/VisionBuilder.UI.Common/Processing/BaseRecognitionControl.cs @@ -27,12 +27,18 @@ public abstract class BaseRecognitionControl: IRecognitionControl public void SetRecipe(RecipeData recipe) { - // error if running - if (IsRunning) + lock (_lock) { - throw new InvalidOperationException("Cannot change recipe while recognition is running."); + + + // error if running + if (IsRunning) + { + throw new InvalidOperationException("Cannot change recipe while recognition is running."); + } + + _currentRecipe = recipe; } - _currentRecipe = recipe; } public bool IsRunning { get; set; } @@ -44,47 +50,57 @@ public abstract class BaseRecognitionControl: IRecognitionControl private DateTime _startTime; + object _lock = new object(); + public void Start() { - - if (_currentRecipe == null) + lock (_lock) { - throw new InvalidOperationException("No recipe set."); + + + 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}..."); + + + } - 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); @@ -95,50 +111,62 @@ public abstract class BaseRecognitionControl: IRecognitionControl public void Stop() { - if (!IsRunning) - return; - _loadingService.StartLoading($"Stopping {_settings.CameraName}"); - _cts?.Cancel(); - try + lock (_lock) { - _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; - } + if (!IsRunning) + return; - SessionEnded?.Invoke(new SessionEndedEvent - { - SessionEnded = DateTime.Now - }); - _loadingService.StopLoading($"Stopping {_settings.CameraName}"); - IsPaused=false; // ensure paused is reset + + + _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}"); + IsPaused = false; // ensure paused is reset + } } public void Pause() { - IsPaused = true; + lock (_lock) + { + IsPaused = true; + } } public void Resume() { - IsPaused = false; + lock (_lock) + { + IsPaused = false; + } } int _errorsInSequence = 0; @@ -161,10 +189,13 @@ public abstract class BaseRecognitionControl: IRecognitionControl { return; } - + int sleepTime=0; if (processed.Value.processingTime.Milliseconds < _settings.MinimumProcessingTime) { - Thread.Sleep(_settings.MinimumProcessingTime - (int)processed.Value.processingTime.Milliseconds); + + sleepTime = _settings.MinimumProcessingTime - (int)processed.Value.processingTime.Milliseconds; + Log.Debug("Sleeping for {sleepTime}",sleepTime); + Thread.Sleep(sleepTime); } if (processed.Value.errorNames.Length > 0) @@ -185,6 +216,8 @@ public abstract class BaseRecognitionControl: IRecognitionControl SessionStart = _startTime, RecipeName = _currentRecipe.RecipeName, AnalysisTime = processed.Value.processingTime - processed.Value.acquisitionTime, + AcquisitionTime = processed.Value.acquisitionTime, + SleepTime = TimeSpan.FromMilliseconds(sleepTime), ErrorNames = processed.Value.errorNames.ToList(), HasError = processed.Value.errorNames.Length > 0, ImageSource = _settings.CameraName, @@ -195,7 +228,7 @@ public abstract class BaseRecognitionControl: IRecognitionControl var swProcessed = Stopwatch.StartNew(); ImageProcessed(processingResult); swProcessed.Stop(); - Log.Information($"ImageProcessed event handled in {swProcessed.ElapsedMilliseconds} ms"); + Log.Information("ImageProcessed event handled in {processed} ms", swProcessed.ElapsedMilliseconds); } diff --git a/VisionBuilder.UI.Common/ViewModel/SingleCameraVM.cs b/VisionBuilder.UI.Common/ViewModel/SingleCameraVM.cs index 977e326..b9fef56 100644 --- a/VisionBuilder.UI.Common/ViewModel/SingleCameraVM.cs +++ b/VisionBuilder.UI.Common/ViewModel/SingleCameraVM.cs @@ -57,8 +57,8 @@ namespace VisionBuilder.UI.Common public bool CanStop => IsRunning; public bool CanStart => !IsRunning && SelectedRecipe!=null; - public bool CanPause => (IsRunning && !IsPaused)&&_uiConfiguration.ShowPauseButton; - public bool CanResume => (IsRunning && IsPaused) && _uiConfiguration.ShowPauseButton; + public bool CanPause => (IsRunning && !IsPaused); + public bool CanResume => (IsRunning && IsPaused); @@ -129,17 +129,18 @@ namespace VisionBuilder.UI.Common CurrentRecipeName = SelectedRecipe?.RecipeName ?? "Select recipe"; } + + [RelayCommand(CanExecute = nameof(CanStart))] public async Task Start() { - IsRunning = true; - await Task.Run(() => - { - _recognitionControl.Start(); - }); - _handlingEnabled = true; + + IsRunning = true; + await Task.Run(() => { _recognitionControl.Start(); }); + _handlingEnabled = true; + } [RelayCommand(CanExecute = nameof(CanStop))] diff --git a/VisionBuilder.UI.Statistics/VisionBuilderStatistics.cs b/VisionBuilder.UI.Statistics/VisionBuilderStatistics.cs index 4e656d0..5a69ba9 100644 --- a/VisionBuilder.UI.Statistics/VisionBuilderStatistics.cs +++ b/VisionBuilder.UI.Statistics/VisionBuilderStatistics.cs @@ -48,6 +48,11 @@ namespace VisionBuilder.UI.Statistics private void _recognitionControl_SessionEnded(SessionEndedEvent obj) { + if (_csvStatistics == null) + { + Log.Warning("Session ended but CSV statistics is null, nothing to do."); + return; + } var endedAt = obj.SessionEnded; _endTime = endedAt; Log.Information("Start processing statistics"); diff --git a/VisionBuilder.UI.Windows.Test/VisionBuilder.UI.Windows.Uno.csproj b/VisionBuilder.UI.Windows.Test/VisionBuilder.UI.Windows.Uno.csproj index d74e59d..c8125d6 100644 --- a/VisionBuilder.UI.Windows.Test/VisionBuilder.UI.Windows.Uno.csproj +++ b/VisionBuilder.UI.Windows.Test/VisionBuilder.UI.Windows.Uno.csproj @@ -8,7 +8,7 @@ enable Inspectron icon-512.ico false - 1.0.6 + 1.0.7