check point

This commit is contained in:
meelstorm
2025-07-14 12:03:59 +02:00
commit d3cb790bd9
431 changed files with 44078 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
using Inspectron.Settings;
namespace VisionBuilder.UI.Common;
public class CameraSettings : ISettings
{
public string CameraName { get; }
public CameraSettings(string cameraName)
{
CameraName = cameraName;
}
public enum EImageSource
{
Emulation,
Hawkeye,
IDS,
Basler,
}
public EImageSource Camera1Source { get; set; }
public string Camera1Label { get; set; }
public void RegisterSettings(InspectronSettings settings)
{
settings.RegisterSimple(this, () => this.Camera1Source, CameraName, "Image source");
settings.RegisterSimple(this, () => this.Camera1Label, CameraName, "Label");
}
}

View File

@@ -0,0 +1,17 @@
using OpenCvSharp;
using VisionBuilder.UI.Common.Commands.Interfaces;
namespace VisionBuilder.UI.Common.Commands;
public class ImageProcessedEvent:IEvent
{
public DateTime SessionStart { get; set; }
public string ImageSource { get; set; }
public string RecipeName { get; set; }
public Mat ImageOriginal { get; set; }
public Mat ImageAnalysis { get; set; }
public bool HasError { get; set; }
public List<string> ErrorNames { get; set; }
public TimeSpan AnalysisTime { get; set; }
}

View File

@@ -0,0 +1,6 @@
namespace VisionBuilder.UI.Common.Commands.Interfaces;
public interface ICommand
{
}

View File

@@ -0,0 +1,6 @@
namespace VisionBuilder.UI.Common.Commands.Interfaces;
public interface IEvent
{
}

View File

@@ -0,0 +1,6 @@
namespace VisionBuilder.UI.Common.Commands.Interfaces;
public interface IEventHandler<TEvent> where TEvent : IEvent
{
void Handle(TEvent @event);
}

View File

@@ -0,0 +1,8 @@
using VisionBuilder.UI.Common.Commands.Interfaces;
namespace VisionBuilder.UI.Common.Commands;
public class SessionEndedEvent: IEvent
{
public DateTime SessionEnded { get; set; }
}

View File

@@ -0,0 +1,10 @@
using VisionBuilder.UI.Common.Commands.Interfaces;
namespace VisionBuilder.UI.Common.Commands;
public class SessionStartedEvent : IEvent
{
public string RecipeName { get; set; }
public DateTime SessionStarted { get; set; }
}

View File

@@ -0,0 +1,21 @@
using System.Diagnostics.Metrics;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace VisionBuilder.UI.Common;
public partial class ExampleMainViewModel : ObservableObject
{
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(IncrementCommand))]
private int _counter;
[RelayCommand(CanExecute = nameof(CanIncrement))]
private void Increment()
{
Counter++;
}
private bool CanIncrement => this.Counter < 10;
}

View File

@@ -0,0 +1,19 @@
using Inspectron.Settings;
using Ninject;
namespace VisionBuilder.UI.Common;
public static class Extensions
{
public static void RegisterSettings(this IKernel self)
{
// get all types from kernel that implement ISettings
var settingsTypes = self.GetAll(typeof(ISettings)).Select(x=>(ISettings)x).ToList();
// register each type as ISettings
foreach (var settingsInstance in settingsTypes)
{
settingsInstance.RegisterSettings(self.Get<InspectronSettings>());
}
}
}

View File

@@ -0,0 +1,8 @@
using Inspectron.Settings;
namespace VisionBuilder.UI.Common;
public interface ISettings
{
public void RegisterSettings(InspectronSettings settings);
}

View File

@@ -0,0 +1,17 @@
using OpenCvSharp;
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
namespace VisionBuilder.UI.Common;
public class NoLearning:ILearningTool
{
public bool IsLearningEnabled(string recipeName)
{
return false;
}
public void Learn(string recipeName, Mat image)
{
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,47 @@
namespace VisionBuilder.UI.Common;
public static class PathSettingsUtils
{
public static string ConvertVariables(
string text,
DateTime imageTime,
DateTime sessionTime,
string recipeName = "",
string defectName = "",
string cameraName = "")
{
if (string.IsNullOrEmpty(text))
return text;
var replacements = new Dictionary<string, string>
{
["$RECIPE_NAME"] = recipeName,
["$DEFECT_NAME"] = defectName,
["$CAMERA_NAME"] = cameraName,
["$HOUR"] = imageTime.Hour.ToString("D2"),
["$MINUTE"] = imageTime.Minute.ToString("D2"),
["$SECOND"] = imageTime.Second.ToString("D2"),
["$MS"] = imageTime.Millisecond.ToString("D3"),
["$DAY"] = imageTime.Day.ToString("D2"),
["$MONTH"] = imageTime.Month.ToString("D2"),
["$YEAR"] = imageTime.Year.ToString(),
["$SS_HOUR"] = sessionTime.Hour.ToString("D2"),
["$SS_MINUTE"] = sessionTime.Minute.ToString("D2"),
["$SS_SECOND"] = sessionTime.Second.ToString("D2"),
["$SS_DAY"] = sessionTime.Day.ToString("D2"),
["$SS_MONTH"] = sessionTime.ToString("MMMM"),
["$SS_YEAR"] = sessionTime.Year.ToString()
};
foreach (var pair in replacements)
{
text = text.Replace(pair.Key, pair.Value);
}
return text;
}
}

View File

@@ -0,0 +1,9 @@
using OpenCvSharp;
namespace VisionBuilder.UI.Common.RecipeProcessing;
public interface IImageSource
{
string GetName();
Task<Mat> GetImage(CancellationToken token);
}

View File

@@ -0,0 +1,7 @@
namespace VisionBuilder.UI.Common.RecipeProcessing;
public interface ILoadingService
{
void StartLoading(string title);
void StopLoading(string title);
}

View File

@@ -0,0 +1,17 @@
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.ViewModel.Classes;
namespace VisionBuilder.UI.Common.RecipeProcessing;
public interface IRecognitionControl
{
List<RecipeData> GetRecipesData();
void SetRecipe(RecipeData recipe);
void Start();
void Stop();
event Action<ImageProcessedEvent> ImageProcessed;
event Action<SessionStartedEvent> SessionStarted;
event Action<SessionEndedEvent> SessionEnded;
}

View File

@@ -0,0 +1,21 @@
using Inspectron.Settings;
namespace VisionBuilder.UI.Common;
public class UIConfiguration: ISettings
{
/// <summary>
/// Max errors to be displayed in the UI.
/// </summary>
public int MaxErrorCount { get; set; } = 100;
public string AdminPassword { get; set; } = "";
public void RegisterSettings(InspectronSettings settings)
{
settings.RegisterSimple(this, () => this.MaxErrorCount, "Errors", "Max errors count", "UI");
settings.RegisterSimple(this, () => this.AdminPassword, "System", "Password", "UI");
}
}

View File

@@ -0,0 +1,16 @@
using OpenCvSharp;
namespace VisionBuilder.UI.Common.ViewModel.Classes;
public class ErrorData
{
public ErrorData()
{
}
public Mat ImageOriginal { get; set; }
public Mat ImageAnalysis { get; set; }
public string RecipeName { get; set; }
public string Title { get; set; }
}

View File

@@ -0,0 +1,9 @@
using OpenCvSharp;
namespace VisionBuilder.UI.Common.ViewModel.Classes;
public class RecipeData
{
public Mat? Image { get; set; }
public string RecipeName { get; set; }
}

View File

@@ -0,0 +1,102 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using OpenCvSharp;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.Commands.Interfaces;
using VisionBuilder.UI.Common.ViewModel.Classes;
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace VisionBuilder.UI.Common.ViewModel;
public partial class ErrorPreviewVM:ObservableObject
{
private ErrorsVM _errorsVm;
private ErrorData _errorData;
private readonly ILearningTool _learningTool;
[ObservableProperty]
private string _recipeName;
[ObservableProperty]
private bool _learnButtonVisible;
[ObservableProperty]
private bool _isNextImageEnabled;
[ObservableProperty]
private bool _isPreviousImageEnabled;
[ObservableProperty]
private string _imageName;
[ObservableProperty]
private Mat _imageAnalysis;
public ErrorPreviewVM(ErrorsVM errorsVm, ErrorData errorData, string recipeName, UIConfiguration uiConfiguration, ILearningTool learningTool)
{
_errorsVm = errorsVm;
_errorData = errorData;
_recipeName = recipeName;
_learningTool = learningTool;
UpdateData();
}
[RelayCommand(CanExecute = nameof(IsNextImageEnabled))]
public void NextImage()
{
var imageIndex = _errorsVm.Errors.IndexOf(_errorData);
if (imageIndex >= _errorsVm.Errors.Count) return;
imageIndex++;
_errorData = _errorsVm.Errors[imageIndex];
UpdateData();
}
[RelayCommand(CanExecute = nameof(IsPreviousImageEnabled))]
public void PreviousImage()
{
var imageIndex = _errorsVm.Errors.IndexOf(_errorData);
if (imageIndex <= 0) return;
imageIndex--;
_errorData = _errorsVm.Errors[imageIndex];
UpdateData();
}
private void UpdateData()
{
var imageIndex = _errorsVm.Errors.IndexOf(_errorData);
if (imageIndex < 0)
{
IsNextImageEnabled = false;
IsPreviousImageEnabled = false;
ImageName = string.Empty;
ImageAnalysis = null;
return;
}
IsNextImageEnabled = imageIndex < _errorsVm.Errors.Count - 1;
IsPreviousImageEnabled = imageIndex > 0;
ImageName = _errorData.Title;
ImageAnalysis = _errorData.ImageAnalysis;
LearnButtonVisible = _learningTool.IsLearningEnabled(_errorData.RecipeName);
}
[RelayCommand(CanExecute = nameof(LearnButtonVisible))]
public void Learn()
{
_learningTool.Learn(_errorData.RecipeName, _errorData.ImageOriginal);
}
}

View File

@@ -0,0 +1,60 @@
using System.Collections.ObjectModel;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.Commands.Interfaces;
using VisionBuilder.UI.Common.ViewModel.Classes;
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
namespace VisionBuilder.UI.Common.ViewModel;
public class ErrorsVM:IEventHandler<ImageProcessedEvent>, IEventHandler<SessionStartedEvent>
{
private readonly UIConfiguration _uiConfiguration;
private readonly IImagePreviewService _imagePreviewService;
private readonly ILearningTool _learningTool;
public ErrorsVM(UIConfiguration uiConfiguration, IImagePreviewService imagePreviewService, ILearningTool learningTool)
{
_uiConfiguration = uiConfiguration;
_imagePreviewService = imagePreviewService;
_learningTool = learningTool;
}
public ObservableCollection<ErrorData> Errors { get; set; } = new ObservableCollection<ErrorData>();
public void ShowImage(ErrorData errorData)
{
if (errorData == null) return;
var vm = new ErrorPreviewVM(this, errorData, errorData.RecipeName, _uiConfiguration, _learningTool);
_imagePreviewService.ShowImagePreview(vm);
}
public void Handle(ImageProcessedEvent @event)
{
if(!@event.HasError)return;
ErrorData errorData = new ErrorData
{
RecipeName = @event.RecipeName,
ImageOriginal = @event.ImageOriginal,
ImageAnalysis = @event.ImageAnalysis,
Title = @event.ErrorNames +" "+ DateTime.Now.ToString("T")
};
if (Errors.Count >= _uiConfiguration.MaxErrorCount)
{
Errors.RemoveAt(0);
}
Errors.Add(errorData);
}
public void Handle(SessionStartedEvent @event)
{
Errors.Clear();
}
}

View File

@@ -0,0 +1,9 @@
using VisionBuilder.UI.Common.ViewModel.Classes;
namespace VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
public interface IImagePreviewService
{
void ShowImagePreview(ErrorPreviewVM errorPreviewVm);
}

View File

@@ -0,0 +1,9 @@
using OpenCvSharp;
namespace VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
public interface ILearningTool
{
public bool IsLearningEnabled(string recipeName);
public void Learn(string recipeName, Mat image);
}

View File

@@ -0,0 +1,6 @@
namespace VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
public interface IMainWindowService
{
Task RunAsync(MainWindowVM vm);
}

View File

@@ -0,0 +1,6 @@
namespace VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
public interface IRecipeSelectionDialogService
{
bool SelectRecipe(RecipeSelectionVM recipeSelectionVm);
}

View File

@@ -0,0 +1,6 @@
namespace VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
public interface ISettingsService
{
void ShowSettingsDialog();
}

View File

@@ -0,0 +1,6 @@
namespace VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
public interface ISingleCameraCreationService
{
void CreateCamera(SingleCameraVM cameraVm);
}

View File

@@ -0,0 +1,30 @@
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.Commands.Interfaces;
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
namespace VisionBuilder.UI.Common.ViewModel;
public partial class MainWindowVM:ObservableObject
{
private readonly ISettingsService _settingsService;
public MainWindowVM(ISettingsService settingsService)
{
_settingsService = settingsService;
}
public ObservableCollection<SingleCameraVM> SingleCameraVms { get; set; } =
new ObservableCollection<SingleCameraVM>();
[RelayCommand]
public void Settings()
{
_settingsService.ShowSettingsDialog();
}
}

View File

@@ -0,0 +1,18 @@
using CommunityToolkit.Mvvm.ComponentModel;
using OpenCvSharp;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.Commands.Interfaces;
namespace VisionBuilder.UI.Common.ViewModel;
public partial class PreviewVM:ObservableObject, IEventHandler<ImageProcessedEvent>
{
[ObservableProperty] private Mat _imagePreview;
[ObservableProperty] private bool _isError;
public void Handle(ImageProcessedEvent @event)
{
IsError = @event.HasError;
ImagePreview = @event.ImageAnalysis.Clone();
}
}

View File

@@ -0,0 +1,10 @@
using System.Collections.ObjectModel;
using VisionBuilder.UI.Common.ViewModel.Classes;
namespace VisionBuilder.UI.Common.ViewModel;
public class RecipeSelectionVM
{
public ObservableCollection<RecipeData> Recipes { get; set; }
public RecipeData SelectedRecipe { get; set; }
}

View File

@@ -0,0 +1,112 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.Commands.Interfaces;
using VisionBuilder.UI.Common.RecipeProcessing;
using VisionBuilder.UI.Common.ViewModel;
using VisionBuilder.UI.Common.ViewModel.Classes;
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
namespace VisionBuilder.UI.Common
{
public partial class SingleCameraVM : ObservableObject, IEventHandler<ImageProcessedEvent>
{
private readonly IRecipeSelectionDialogService _recipeSelectionDialogService;
private readonly IRecognitionControl _recognitionControl;
public ErrorsVM ErrorsVm { get; private set; }
public StatisticsVM StatisticsVm { get; private set; }
public PreviewVM PreviewVm { get; private set; }
[ObservableProperty]
private string _currentRecipeName = "Select recipe";
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanStart))]
[NotifyPropertyChangedFor(nameof(CanStop))]
[NotifyCanExecuteChangedFor(nameof(SelectRecipeCommand))]
[NotifyCanExecuteChangedFor(nameof(StartCommand))]
[NotifyCanExecuteChangedFor(nameof(StopCommand))]
private bool _isRunning;
[ObservableProperty]
private string _cameraName;
public bool CanStop => IsRunning;
public bool CanStart => !IsRunning && SelectedRecipe!=null;
public bool IsNotRunning => !IsRunning;
private bool _handlingEnabled = true;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanStop))]
[NotifyPropertyChangedFor(nameof(CanStart))]
[NotifyCanExecuteChangedFor(nameof(SelectRecipeCommand))]
[NotifyCanExecuteChangedFor(nameof(StartCommand))]
[NotifyCanExecuteChangedFor(nameof(StopCommand))]
private RecipeData? _selectedRecipe;
public SingleCameraVM(CameraSettings cameraSettings, UIConfiguration uiConfiguration, IRecipeSelectionDialogService recipeSelectionDialogService, IRecognitionControl recognitionControl, IImagePreviewService imagePreviewService, ILearningTool learningTool)
{
_recipeSelectionDialogService = recipeSelectionDialogService;
_recognitionControl = recognitionControl;
ErrorsVm = new ErrorsVM(uiConfiguration, imagePreviewService, learningTool);
StatisticsVm = new StatisticsVM();
PreviewVm = new PreviewVM();
CameraName = cameraSettings.CameraName;
recognitionControl.ImageProcessed += Handle;
recognitionControl.SessionStarted += StatisticsVm.Handle;
recognitionControl.SessionStarted += ErrorsVm.Handle;
}
[RelayCommand(CanExecute = nameof(IsNotRunning))]
public void SelectRecipe()
{
var vm = new RecipeSelectionVM();
vm.Recipes = new ObservableCollection<RecipeData>(_recognitionControl.GetRecipesData());
if (!_recipeSelectionDialogService.SelectRecipe(vm))return;
SelectedRecipe = vm.SelectedRecipe;
_recognitionControl.SetRecipe(SelectedRecipe);
CurrentRecipeName = SelectedRecipe?.RecipeName ?? "Select recipe";
}
[RelayCommand(CanExecute = nameof(CanStart))]
public void Start()
{
_handlingEnabled = true;
_recognitionControl.Start();
IsRunning = true;
}
[RelayCommand(CanExecute = nameof(CanStop))]
public void Stop()
{
_handlingEnabled = false;
_recognitionControl.Stop();
IsRunning = false;
}
public void Handle(ImageProcessedEvent @event)
{
if (!_handlingEnabled) return;
try
{
PreviewVm.Handle(@event);
StatisticsVm.Handle(@event);
ErrorsVm.Handle(@event);
}
catch(Exception e)
{
}
}
}
}

View File

@@ -0,0 +1,58 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System.Text;
using System.Threading;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.Commands.Interfaces;
namespace VisionBuilder.UI.Common.ViewModel;
public partial class StatisticsVM: ObservableObject,IEventHandler<ImageProcessedEvent>, IEventHandler<SessionStartedEvent>
{
[ObservableProperty] private string _sessionStarted;
[ObservableProperty] private string _recipeName;
[ObservableProperty] private string _processingTime="";
[ObservableProperty] private int _good;
[ObservableProperty] private int _bad;
[ObservableProperty] private int _total;
[ObservableProperty] private string _statisticsDetails="";
private readonly Dictionary<string, int> _errorTypes = new();
public void Handle(ImageProcessedEvent @event)
{
RecipeName= @event.RecipeName;
Total++;
if (!@event.HasError)
{
Good++;
return;
}
Bad++;
foreach (var name in @event.ErrorNames)
{
_errorTypes[name] = _errorTypes.TryGetValue(name, out var count) ? count + 1 : 1;
}
StatisticsDetails = string.Join(Environment.NewLine, _errorTypes.Select(e => $"{e.Key}: {e.Value}"));
ProcessingTime = (int)@event.AnalysisTime.TotalMilliseconds + "ms";
}
public void Handle(SessionStartedEvent @event)
{
// reset everything
_good = 0;
_bad = 0;
_total = 0;
_errorTypes.Clear();
SessionStarted = @event.SessionStarted.ToString("yyyy-MM-dd HH:mm:ss");
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Ninject" Version="3.3.6" />
<PackageReference Include="OpenCvSharp4" Version="4.6.0.20220608" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\framework\Inspectron.Settings\Inspectron.Settings.csproj" />
</ItemGroup>
</Project>