ringbuffer and statistics
This commit is contained in:
@@ -1,11 +1,12 @@
|
|||||||
using OpenCvSharp;
|
using OpenCvSharp;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
|
using VisionBuilder.UI.Common;
|
||||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||||
|
|
||||||
namespace Hawkeye.VisionBuilder.UI.Sources.Emulation
|
namespace Hawkeye.VisionBuilder.UI.Sources.Emulation
|
||||||
{
|
{
|
||||||
public class EmulationCameraImageSource:IImageSource, IInitializable
|
public class EmulationCameraImageSource:IImageSource, IVisionBuilderModule
|
||||||
{
|
{
|
||||||
private readonly EmulationSettings _settings;
|
private readonly EmulationSettings _settings;
|
||||||
private readonly string _name;
|
private readonly string _name;
|
||||||
@@ -91,7 +92,7 @@ namespace Hawkeye.VisionBuilder.UI.Sources.Emulation
|
|||||||
private int _index = 0;
|
private int _index = 0;
|
||||||
|
|
||||||
|
|
||||||
public void Initialize()
|
public void InitializeModule()
|
||||||
{
|
{
|
||||||
if(string.IsNullOrEmpty(_settings.EmulationPath)) return;
|
if(string.IsNullOrEmpty(_settings.EmulationPath)) return;
|
||||||
SetFolderAbsolute(_settings.EmulationPath);
|
SetFolderAbsolute(_settings.EmulationPath);
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
41
VisionBuilder.UI.Camera/ModuleExtensions.cs
Normal file
41
VisionBuilder.UI.Camera/ModuleExtensions.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using Hawkeye.VisionBuilder.UI.Sources.Emulation;
|
||||||
|
using Ninject;
|
||||||
|
using Ninject.Extensions.ChildKernel;
|
||||||
|
using VisionBuilder.UI.Common;
|
||||||
|
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||||
|
|
||||||
|
namespace Lindt.Colorballs.Duo.Utils;
|
||||||
|
|
||||||
|
public static class ModuleExtensions
|
||||||
|
{
|
||||||
|
public static IChildKernel RegisterCamera(this IChildKernel self, string cameraName)
|
||||||
|
{
|
||||||
|
self.Bind<CameraSettings, ISettings>().ToConstant(new CameraSettings(cameraName));
|
||||||
|
self.Bind<EmulationSettings, ISettings>().ToConstant(new EmulationSettings(cameraName));
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
private static void BindCameras(CameraSettings cameraSettings, IChildKernel kernel)
|
||||||
|
{
|
||||||
|
switch (cameraSettings.CameraSource)
|
||||||
|
{
|
||||||
|
case CameraSettings.EImageSource.Emulation:
|
||||||
|
|
||||||
|
kernel.Bind<IImageSource, IVisionBuilderModule>().To<EmulationCameraImageSource>().InSingletonScope();
|
||||||
|
break;
|
||||||
|
case CameraSettings.EImageSource.Hawkeye:
|
||||||
|
break;
|
||||||
|
case CameraSettings.EImageSource.IDS:
|
||||||
|
break;
|
||||||
|
case CameraSettings.EImageSource.Basler:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static IChildKernel UseCamera(this IChildKernel self)
|
||||||
|
{
|
||||||
|
var cameraSettings = self.Get<CameraSettings>();
|
||||||
|
BindCameras(cameraSettings, self);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
VisionBuilder.UI.Camera/VisionBuilder.UI.Camera.csproj
Normal file
14
VisionBuilder.UI.Camera/VisionBuilder.UI.Camera.csproj
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Hawkeye.VisionBuilder.UI.Sources.Emulation\Hawkeye.VisionBuilder.UI.Sources.Emulation.csproj" />
|
||||||
|
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -20,12 +20,12 @@ public class CameraSettings : ISettings
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EImageSource Camera1Source { get; set; }
|
public EImageSource CameraSource { get; set; }
|
||||||
public string Camera1Label { get; set; }
|
public string CameraLabel { get; set; }
|
||||||
|
|
||||||
public void RegisterSettings(InspectronSettings settings)
|
public void RegisterSettings(InspectronSettings settings)
|
||||||
{
|
{
|
||||||
settings.RegisterSimple(this, () => this.Camera1Source, CameraName, "Image source");
|
settings.RegisterSimple(this, () => this.CameraSource, CameraName, "Image source");
|
||||||
settings.RegisterSimple(this, () => this.Camera1Label, CameraName, "Label");
|
settings.RegisterSimple(this, () => this.CameraLabel, CameraName, "Label");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Inspectron.Settings;
|
using Inspectron.Settings;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
|
using Ninject.Extensions.ChildKernel;
|
||||||
|
|
||||||
namespace VisionBuilder.UI.Common;
|
namespace VisionBuilder.UI.Common;
|
||||||
|
|
||||||
@@ -14,6 +15,29 @@ public static class Extensions
|
|||||||
{
|
{
|
||||||
settingsInstance.RegisterSettings(self.Get<InspectronSettings>());
|
settingsInstance.RegisterSettings(self.Get<InspectronSettings>());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterModule<T>(this IKernel self) where T : IVisionBuilderModule
|
||||||
|
{
|
||||||
|
self.Bind<T, IVisionBuilderModule>().To<T>().InSingletonScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitializeModules(this IKernel self)
|
||||||
|
{
|
||||||
|
|
||||||
|
var serviceTypes = self.GetAll(typeof(IVisionBuilderModule)).Select(x => (IVisionBuilderModule)x).ToList();
|
||||||
|
foreach (var service in serviceTypes)
|
||||||
|
{
|
||||||
|
if (service != null)
|
||||||
|
{
|
||||||
|
service.InitializeModule();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
6
VisionBuilder.UI.Common/IVisionBuilderModule.cs
Normal file
6
VisionBuilder.UI.Common/IVisionBuilderModule.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace VisionBuilder.UI.Common;
|
||||||
|
|
||||||
|
public interface IVisionBuilderModule
|
||||||
|
{
|
||||||
|
public void InitializeModule();
|
||||||
|
}
|
||||||
@@ -1,7 +1,32 @@
|
|||||||
namespace VisionBuilder.UI.Common;
|
namespace Lindt.Colorballs.Duo.Utils;
|
||||||
|
|
||||||
public static class PathSettingsUtils
|
public static class PathSettingsUtils
|
||||||
{
|
{
|
||||||
|
public const string INSTUCTIONS = @"
|
||||||
|
You can use the following system variables:
|
||||||
|
|
||||||
|
$RECIPE_NAME - name of the current recipe
|
||||||
|
$DEFECT_NAME - name of the current defect(for bad images)
|
||||||
|
$CAMERA_NAME - name of the camera
|
||||||
|
|
||||||
|
$HOUR - hour of the image
|
||||||
|
$MINUTE - minute of the image
|
||||||
|
$SECOND - second of the image
|
||||||
|
$MS - milliseconds of the image
|
||||||
|
|
||||||
|
$DAY - day of the image
|
||||||
|
$MONTH_NUM - number of the month of the image
|
||||||
|
$MONTH_NAME - name of the month of the image
|
||||||
|
$YEAR - year of the image
|
||||||
|
|
||||||
|
$SS_HOUR - hour of the session start
|
||||||
|
$SS_MINUTE - minute of the session start
|
||||||
|
$SS_SECOND - second of the session start
|
||||||
|
|
||||||
|
$SS_DAY - day of the session start
|
||||||
|
$SS_MONTH_NUM - number of the month of the session start
|
||||||
|
$SS_MONTH_NAME - name of the month of the session start
|
||||||
|
$SS_YEAR - year of the session start";
|
||||||
public static string ConvertVariables(
|
public static string ConvertVariables(
|
||||||
string text,
|
string text,
|
||||||
DateTime imageTime,
|
DateTime imageTime,
|
||||||
@@ -25,7 +50,8 @@ public static class PathSettingsUtils
|
|||||||
["$MS"] = imageTime.Millisecond.ToString("D3"),
|
["$MS"] = imageTime.Millisecond.ToString("D3"),
|
||||||
|
|
||||||
["$DAY"] = imageTime.Day.ToString("D2"),
|
["$DAY"] = imageTime.Day.ToString("D2"),
|
||||||
["$MONTH"] = imageTime.Month.ToString("D2"),
|
["$MONTH_NUM"] = imageTime.Month.ToString("D2"),
|
||||||
|
["$MONTH_NAME"] = imageTime.ToString("MMMM"),
|
||||||
["$YEAR"] = imageTime.Year.ToString(),
|
["$YEAR"] = imageTime.Year.ToString(),
|
||||||
|
|
||||||
["$SS_HOUR"] = sessionTime.Hour.ToString("D2"),
|
["$SS_HOUR"] = sessionTime.Hour.ToString("D2"),
|
||||||
@@ -33,7 +59,8 @@ public static class PathSettingsUtils
|
|||||||
["$SS_SECOND"] = sessionTime.Second.ToString("D2"),
|
["$SS_SECOND"] = sessionTime.Second.ToString("D2"),
|
||||||
|
|
||||||
["$SS_DAY"] = sessionTime.Day.ToString("D2"),
|
["$SS_DAY"] = sessionTime.Day.ToString("D2"),
|
||||||
["$SS_MONTH"] = sessionTime.ToString("MMMM"),
|
["$SS_MONTH_NUM"] = sessionTime.Month.ToString("D2"),
|
||||||
|
["$SS_MONTH_NAME"] = sessionTime.ToString("MMMM"),
|
||||||
["$SS_YEAR"] = sessionTime.Year.ToString()
|
["$SS_YEAR"] = sessionTime.Year.ToString()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace VisionBuilder.UI.Common
|
|||||||
[NotifyCanExecuteChangedFor(nameof(StopCommand))]
|
[NotifyCanExecuteChangedFor(nameof(StopCommand))]
|
||||||
private bool _isRunning;
|
private bool _isRunning;
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private string _cameraName;
|
private string _cameraLabel;
|
||||||
|
|
||||||
|
|
||||||
public bool CanStop => IsRunning;
|
public bool CanStop => IsRunning;
|
||||||
@@ -56,7 +56,7 @@ namespace VisionBuilder.UI.Common
|
|||||||
StatisticsVm = new StatisticsVM();
|
StatisticsVm = new StatisticsVM();
|
||||||
PreviewVm = new PreviewVM();
|
PreviewVm = new PreviewVM();
|
||||||
|
|
||||||
CameraName = cameraSettings.CameraName;
|
CameraLabel = cameraSettings.CameraLabel;
|
||||||
|
|
||||||
recognitionControl.ImageProcessed += Handle;
|
recognitionControl.ImageProcessed += Handle;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
<PackageReference Include="Ninject" Version="3.3.6" />
|
<PackageReference Include="Ninject" Version="3.3.6" />
|
||||||
|
<PackageReference Include="Ninject.Extensions.ChildKernel" Version="3.3.0" />
|
||||||
<PackageReference Include="OpenCvSharp4" Version="4.6.0.20220608" />
|
<PackageReference Include="OpenCvSharp4" Version="4.6.0.20220608" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
23
VisionBuilder.UI.Common/VisionBuilder.cs
Normal file
23
VisionBuilder.UI.Common/VisionBuilder.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Inspectron.Settings;
|
||||||
|
using Ninject;
|
||||||
|
using Ninject.Extensions.ChildKernel;
|
||||||
|
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
||||||
|
|
||||||
|
namespace VisionBuilder.UI.Common;
|
||||||
|
|
||||||
|
public static class VisionBuilder
|
||||||
|
{
|
||||||
|
public static IKernel CreateMainKernel(InspectronSettings settings)
|
||||||
|
{
|
||||||
|
StandardKernel mainKernel = new StandardKernel();
|
||||||
|
|
||||||
|
// GLOBAL SETTINGS //
|
||||||
|
mainKernel.Bind<InspectronSettings>().ToConstant(settings);
|
||||||
|
mainKernel.Bind<UIConfiguration, ISettings>().ToConstant(new UIConfiguration());
|
||||||
|
mainKernel.Bind<ILearningTool>().To<NoLearning>().InSingletonScope();
|
||||||
|
// --- //
|
||||||
|
|
||||||
|
return mainKernel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
VisionBuilder.UI.Console/ModuleExtensions.cs
Normal file
15
VisionBuilder.UI.Console/ModuleExtensions.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Ninject.Extensions.ChildKernel;
|
||||||
|
using VisionBuilder.UI.Common;
|
||||||
|
using VisionBuilder.UI.Console;
|
||||||
|
|
||||||
|
namespace VisionBuilder.UI.Statistics;
|
||||||
|
|
||||||
|
public static class ModuleExtensions
|
||||||
|
{
|
||||||
|
public static IChildKernel UseConsole(this IChildKernel self)
|
||||||
|
{
|
||||||
|
self.Bind<VisionBuilderConsoleSettings, ISettings>().ToConstant(new VisionBuilderConsoleSettings());
|
||||||
|
self.RegisterModule<VisionBuilderConsole>();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
VisionBuilder.UI.Console/VisionBuilder.UI.Console.csproj
Normal file
17
VisionBuilder.UI.Console/VisionBuilder.UI.Console.csproj
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
36
VisionBuilder.UI.Console/VisionBuilderConsole.cs
Normal file
36
VisionBuilder.UI.Console/VisionBuilderConsole.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using Ninject;
|
||||||
|
using Serilog;
|
||||||
|
using VisionBuilder.UI.Common;
|
||||||
|
|
||||||
|
namespace VisionBuilder.UI.Console
|
||||||
|
{
|
||||||
|
public class VisionBuilderConsole: IVisionBuilderModule
|
||||||
|
{
|
||||||
|
private readonly VisionBuilderConsoleSettings _settings;
|
||||||
|
|
||||||
|
public VisionBuilderConsole(VisionBuilderConsoleSettings settings)
|
||||||
|
{
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeModule()
|
||||||
|
{
|
||||||
|
if (_settings.CreateConsoleWindow)
|
||||||
|
{
|
||||||
|
WinConsole.Initialize();
|
||||||
|
}
|
||||||
|
// configure Serilog to use console
|
||||||
|
if (_settings.UseConsole)
|
||||||
|
{
|
||||||
|
Serilog.Log.Logger = new Serilog.LoggerConfiguration()
|
||||||
|
.MinimumLevel.Debug()
|
||||||
|
.WriteTo.Console()
|
||||||
|
.CreateLogger();
|
||||||
|
|
||||||
|
Log.Information("Console initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
VisionBuilder.UI.Console/VisionBuilderConsoleSettings.cs
Normal file
18
VisionBuilder.UI.Console/VisionBuilderConsoleSettings.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Inspectron.Settings;
|
||||||
|
using Inspectron.Settings.Attributes;
|
||||||
|
using Serilog;
|
||||||
|
using VisionBuilder.UI.Common;
|
||||||
|
|
||||||
|
namespace VisionBuilder.UI.Console;
|
||||||
|
|
||||||
|
public class VisionBuilderConsoleSettings:ISettings
|
||||||
|
{
|
||||||
|
public bool UseConsole { get; set; } = false;
|
||||||
|
[SettingDescription("Create a console window for non-console application")]
|
||||||
|
public bool CreateConsoleWindow { get; set; }
|
||||||
|
public void RegisterSettings(InspectronSettings settings)
|
||||||
|
{
|
||||||
|
settings.RegisterSimple(this, () => this.UseConsole, "Console", nameof(UseConsole));
|
||||||
|
settings.RegisterSimple(this, () => this.CreateConsoleWindow, "Console", nameof(CreateConsoleWindow));
|
||||||
|
}
|
||||||
|
}
|
||||||
118
VisionBuilder.UI.Console/WinConsole.cs
Normal file
118
VisionBuilder.UI.Console/WinConsole.cs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
|
||||||
|
namespace VisionBuilder.UI.Console
|
||||||
|
{
|
||||||
|
public static class WinConsole
|
||||||
|
{
|
||||||
|
public static void Initialize(bool alwaysCreateNewConsole = true, bool minimizeWindow = false)
|
||||||
|
{
|
||||||
|
bool consoleAttached = true;
|
||||||
|
if (alwaysCreateNewConsole
|
||||||
|
|| (AttachConsole(ATTACH_PARRENT) == 0
|
||||||
|
&& Marshal.GetLastWin32Error() != ERROR_ACCESS_DENIED))
|
||||||
|
{
|
||||||
|
consoleAttached = AllocConsole() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (consoleAttached)
|
||||||
|
{
|
||||||
|
InitializeOutStream();
|
||||||
|
InitializeInStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minimizeWindow)
|
||||||
|
{
|
||||||
|
const int SW_MINIMIZE = 6;
|
||||||
|
var handle = GetConsoleWindow();
|
||||||
|
ShowWindow(handle, SW_MINIMIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MinimizeWindow()
|
||||||
|
{
|
||||||
|
const int SW_MINIMIZE = 6;
|
||||||
|
var handle = GetConsoleWindow();
|
||||||
|
ShowWindow(handle, SW_MINIMIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern IntPtr GetConsoleWindow();
|
||||||
|
private static void InitializeOutStream()
|
||||||
|
{
|
||||||
|
var fs = CreateFileStream("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, FileAccess.Write);
|
||||||
|
if (fs != null)
|
||||||
|
{
|
||||||
|
var writer = new StreamWriter(fs) { AutoFlush = true };
|
||||||
|
System.Console.SetOut(writer);
|
||||||
|
System.Console.SetError(writer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void InitializeInStream()
|
||||||
|
{
|
||||||
|
var fs = CreateFileStream("CONIN$", GENERIC_READ, FILE_SHARE_READ, FileAccess.Read);
|
||||||
|
if (fs != null)
|
||||||
|
{
|
||||||
|
System.Console.SetIn(new StreamReader(fs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode,
|
||||||
|
FileAccess dotNetFileAccess)
|
||||||
|
{
|
||||||
|
var file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero), true);
|
||||||
|
if (!file.IsInvalid)
|
||||||
|
{
|
||||||
|
var fs = new FileStream(file, dotNetFileAccess);
|
||||||
|
return fs;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Win API Functions and Constants
|
||||||
|
[DllImport("kernel32.dll",
|
||||||
|
EntryPoint = "AllocConsole",
|
||||||
|
SetLastError = true,
|
||||||
|
CharSet = CharSet.Auto,
|
||||||
|
CallingConvention = CallingConvention.StdCall)]
|
||||||
|
private static extern int AllocConsole();
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll",
|
||||||
|
EntryPoint = "AttachConsole",
|
||||||
|
SetLastError = true,
|
||||||
|
CharSet = CharSet.Auto,
|
||||||
|
CallingConvention = CallingConvention.StdCall)]
|
||||||
|
private static extern UInt32 AttachConsole(UInt32 dwProcessId);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll",
|
||||||
|
EntryPoint = "CreateFileW",
|
||||||
|
SetLastError = true,
|
||||||
|
CharSet = CharSet.Auto,
|
||||||
|
CallingConvention = CallingConvention.StdCall)]
|
||||||
|
private static extern IntPtr CreateFileW(
|
||||||
|
string lpFileName,
|
||||||
|
UInt32 dwDesiredAccess,
|
||||||
|
UInt32 dwShareMode,
|
||||||
|
IntPtr lpSecurityAttributes,
|
||||||
|
UInt32 dwCreationDisposition,
|
||||||
|
UInt32 dwFlagsAndAttributes,
|
||||||
|
IntPtr hTemplateFile
|
||||||
|
);
|
||||||
|
|
||||||
|
private const UInt32 GENERIC_WRITE = 0x40000000;
|
||||||
|
private const UInt32 GENERIC_READ = 0x80000000;
|
||||||
|
private const UInt32 FILE_SHARE_READ = 0x00000001;
|
||||||
|
private const UInt32 FILE_SHARE_WRITE = 0x00000002;
|
||||||
|
private const UInt32 OPEN_EXISTING = 0x00000003;
|
||||||
|
private const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80;
|
||||||
|
private const UInt32 ERROR_ACCESS_DENIED = 5;
|
||||||
|
|
||||||
|
private const UInt32 ATTACH_PARRENT = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
15
VisionBuilder.UI.Recipes.HawkeyeRecipe/ModuleExtensions.cs
Normal file
15
VisionBuilder.UI.Recipes.HawkeyeRecipe/ModuleExtensions.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Ninject.Extensions.ChildKernel;
|
||||||
|
using VisionBuilder.UI.Common;
|
||||||
|
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||||
|
|
||||||
|
namespace VisionBuilder.UI.Recipes.HawkeyeRecipe;
|
||||||
|
|
||||||
|
public static class ModuleExtensions
|
||||||
|
{
|
||||||
|
public static IChildKernel UseHawkeyeRecipes(this IChildKernel self, string cameraName)
|
||||||
|
{
|
||||||
|
self.Bind<HawkeyeRecognitionSettings, ISettings>().ToConstant(new HawkeyeRecognitionSettings(cameraName));
|
||||||
|
self.Bind<IRecognitionControl>().To<HawkeyeRecognitionControl>().InSingletonScope();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
VisionBuilder.UI.Ringbuffer/ModuleExtensions.cs
Normal file
14
VisionBuilder.UI.Ringbuffer/ModuleExtensions.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Ninject.Extensions.ChildKernel;
|
||||||
|
using VisionBuilder.UI.Common;
|
||||||
|
|
||||||
|
namespace VisionBuilder.UI.Ringbuffer;
|
||||||
|
|
||||||
|
public static class ModuleExtensions
|
||||||
|
{
|
||||||
|
public static IChildKernel UseRingbuffer(this IChildKernel self, string cameraName)
|
||||||
|
{
|
||||||
|
self.Bind<VisionBuilderRingbufferSettings, ISettings>().ToConstant(new VisionBuilderRingbufferSettings(cameraName));
|
||||||
|
self.RegisterModule<VisionBuilderRingbuffer>();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
using Inspectron.Fastbuffer;
|
using Inspectron.Fastbuffer;
|
||||||
using Inspectron.Fastbuffer.Filesystems;
|
using Inspectron.Fastbuffer.Filesystems;
|
||||||
using System.Drawing;
|
using Lindt.Colorballs.Duo.Utils;
|
||||||
using System.Drawing.Imaging;
|
|
||||||
using OpenCvSharp;
|
using OpenCvSharp;
|
||||||
|
using VisionBuilder.UI.Common;
|
||||||
using VisionBuilder.UI.Common.Commands;
|
using VisionBuilder.UI.Common.Commands;
|
||||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||||
|
|
||||||
namespace Lindt.Colorballs.Duo.Utils;
|
namespace VisionBuilder.UI.Ringbuffer;
|
||||||
|
|
||||||
public class VisionBuilderRingbuffer
|
public class VisionBuilderRingbuffer:IVisionBuilderModule
|
||||||
{
|
{
|
||||||
private readonly VisionBuilderRingbufferSettings _settings;
|
private readonly VisionBuilderRingbufferSettings _settings;
|
||||||
private readonly IRecognitionControl _recognitionControl;
|
private readonly IRecognitionControl _recognitionControl;
|
||||||
@@ -17,10 +17,13 @@ public class VisionBuilderRingbuffer
|
|||||||
{
|
{
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
_recognitionControl = recognitionControl;
|
_recognitionControl = recognitionControl;
|
||||||
|
|
||||||
|
}
|
||||||
|
public void InitializeModule()
|
||||||
|
{
|
||||||
_recognitionControl.SessionStarted += _recognitionControl_SessionStarted;
|
_recognitionControl.SessionStarted += _recognitionControl_SessionStarted;
|
||||||
_recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed;
|
_recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _recognitionControl_ImageProcessed(VisionBuilder.UI.Common.Commands.ImageProcessedEvent obj)
|
private void _recognitionControl_ImageProcessed(VisionBuilder.UI.Common.Commands.ImageProcessedEvent obj)
|
||||||
{
|
{
|
||||||
if (obj.HasError)
|
if (obj.HasError)
|
||||||
@@ -124,4 +127,6 @@ public class VisionBuilderRingbuffer
|
|||||||
_badRingbuffers.Clear();
|
_badRingbuffers.Clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
using Inspectron.Settings;
|
using Inspectron.Settings;
|
||||||
using Inspectron.Settings.Attributes;
|
using Inspectron.Settings.Attributes;
|
||||||
using System.ComponentModel;
|
using Lindt.Colorballs.Duo.Utils;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using VisionBuilder.UI.Common;
|
using VisionBuilder.UI.Common;
|
||||||
|
|
||||||
namespace Lindt.Colorballs.Duo.Utils;
|
namespace VisionBuilder.UI.Ringbuffer;
|
||||||
|
|
||||||
public class VisionBuilderRingbufferSettings : ISettings
|
public class VisionBuilderRingbufferSettings : ISettings
|
||||||
{
|
{
|
||||||
@@ -23,48 +22,25 @@ public class VisionBuilderRingbufferSettings : ISettings
|
|||||||
[SettingDescription(
|
[SettingDescription(
|
||||||
@"
|
@"
|
||||||
Specifies directory, in which MaxBad and MaxGood are applied.
|
Specifies directory, in which MaxBad and MaxGood are applied.
|
||||||
You can use the following system variables:
|
"+ PathSettingsUtils.INSTUCTIONS
|
||||||
|
|
||||||
$RECIPE_NAME - name of the current recipe
|
|
||||||
$DEFECT_NAME - name of the current defect(for bad images)
|
|
||||||
$CAMERA_NAME - name of the camera
|
|
||||||
|
|
||||||
$HOUR - hour of the image
|
|
||||||
$MINUTE - minute of the image
|
|
||||||
$SECOND - second of the image
|
|
||||||
$MS - milliseconds of the image
|
|
||||||
|
|
||||||
$DAY - day of the image
|
|
||||||
$MONTH - month of the image
|
|
||||||
$YEAR - year of the image
|
|
||||||
|
|
||||||
$SS_HOUR - hour of the session start
|
|
||||||
$SS_MINUTE - minute of the session start
|
|
||||||
$SS_SECOND - second of the session start
|
|
||||||
|
|
||||||
$SS_DAY - day of the session start
|
|
||||||
$SS_MONTH - month of the session start
|
|
||||||
$SS_YEAR - year of the session start
|
|
||||||
|
|
||||||
"
|
|
||||||
)]
|
)]
|
||||||
[SettingPreview(typeof(VisionBuilderRingbufferSettings), nameof(ImagesPathTemplatePreview))]
|
[SettingPreview(typeof(VisionBuilderRingbufferSettings), nameof(ImagesPathTemplatePreview))]
|
||||||
public string Root { get; set; } = @"..\Data\Ringbuffer\$SS_MONTH $SS_YEAR";
|
public string Root { get; set; } = @"..\Data\Ringbuffer\$SS_MONTH_NAME $SS_YEAR";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[SettingDescriptionAttribute("Specifies directory, relative to the Root")]
|
[SettingDescription("Specifies directory, relative to the Root")]
|
||||||
[SettingPreview(typeof(VisionBuilderRingbufferSettings), nameof(ImagesPathTemplatePreview))]
|
[SettingPreview(typeof(VisionBuilderRingbufferSettings), nameof(ImagesPathTemplatePreview))]
|
||||||
public string GoodImagesPathTemplate { get; set; } = @"\$RECIPE_NAME";
|
public string GoodImagesPathTemplate { get; set; } = @"\$RECIPE_NAME";
|
||||||
|
|
||||||
[SettingDescriptionAttribute("Specifies directory, relative to the Root")]
|
[SettingDescription("Specifies directory, relative to the Root")]
|
||||||
[SettingPreview(typeof(VisionBuilderRingbufferSettings),nameof(ImagesPathTemplatePreview))]
|
[SettingPreview(typeof(VisionBuilderRingbufferSettings),nameof(ImagesPathTemplatePreview))]
|
||||||
public string BadImagesPathTemplate { get; set; } =
|
public string BadImagesPathTemplate { get; set; } =
|
||||||
@"\$RECIPE_NAME\$DEFECT_NAME";
|
@"\$RECIPE_NAME\$DEFECT_NAME";
|
||||||
|
|
||||||
|
|
||||||
[SettingPreview(typeof(VisionBuilderRingbufferSettings), nameof(ImagesPathTemplatePreview))]
|
[SettingPreview(typeof(VisionBuilderRingbufferSettings), nameof(ImagesPathTemplatePreview))]
|
||||||
public string FilenameTemplate { get; set; } = "$YEAR-$MONTH-$DAY $HOUR-$MINUTE-$SECOND $MS";
|
public string FilenameTemplate { get; set; } = "$YEAR-$MONTH_NUM-$DAY $HOUR-$MINUTE-$SECOND $MS";
|
||||||
|
|
||||||
|
|
||||||
public void RegisterSettings(InspectronSettings settings)
|
public void RegisterSettings(InspectronSettings settings)
|
||||||
|
|||||||
14
VisionBuilder.UI.Statistics/ModuleExtensions.cs
Normal file
14
VisionBuilder.UI.Statistics/ModuleExtensions.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Ninject.Extensions.ChildKernel;
|
||||||
|
using VisionBuilder.UI.Common;
|
||||||
|
|
||||||
|
namespace VisionBuilder.UI.Statistics;
|
||||||
|
|
||||||
|
public static class ModuleExtensions
|
||||||
|
{
|
||||||
|
public static IChildKernel UseStatistics(this IChildKernel self, string cameraName)
|
||||||
|
{
|
||||||
|
self.Bind<VisionBuilderStatisticsSettings, ISettings>().ToConstant(new VisionBuilderStatisticsSettings(cameraName));
|
||||||
|
self.RegisterModule<VisionBuilderStatistics>();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,9 +13,9 @@ using VisionBuilder.UI.Common.RecipeProcessing;
|
|||||||
|
|
||||||
namespace VisionBuilder.UI.Statistics
|
namespace VisionBuilder.UI.Statistics
|
||||||
{
|
{
|
||||||
public class VisionBuilderStatistics:IInitializable
|
public class VisionBuilderStatistics:IVisionBuilderModule,IDisposable
|
||||||
{
|
{
|
||||||
private CsvStatistics _csvStatistics;
|
private CsvStatistics? _csvStatistics;
|
||||||
private readonly VisionBuilderStatisticsSettings _settings;
|
private readonly VisionBuilderStatisticsSettings _settings;
|
||||||
private string _filePath;
|
private string _filePath;
|
||||||
private readonly IRecognitionControl _recognitionControl;
|
private readonly IRecognitionControl _recognitionControl;
|
||||||
@@ -29,7 +29,7 @@ namespace VisionBuilder.UI.Statistics
|
|||||||
private const string MONTHLY_FILE_NAME = "statistics_month.csv";
|
private const string MONTHLY_FILE_NAME = "statistics_month.csv";
|
||||||
private const string EMPTY_DATE = "";
|
private const string EMPTY_DATE = "";
|
||||||
private const string EMPTY_TIME = "";
|
private const string EMPTY_TIME = "";
|
||||||
|
public bool DoNotDeleteTempFiles { get; set; } = false;
|
||||||
|
|
||||||
public VisionBuilderStatistics(VisionBuilderStatisticsSettings settings, IRecognitionControl recognitionControl, ILoadingService loadingService)
|
public VisionBuilderStatistics(VisionBuilderStatisticsSettings settings, IRecognitionControl recognitionControl, ILoadingService loadingService)
|
||||||
{
|
{
|
||||||
@@ -37,18 +37,20 @@ namespace VisionBuilder.UI.Statistics
|
|||||||
_recognitionControl = recognitionControl;
|
_recognitionControl = recognitionControl;
|
||||||
_loadingService = loadingService;
|
_loadingService = loadingService;
|
||||||
|
|
||||||
_recognitionControl.SessionStarted += _recognitionControl_SessionStarted;
|
|
||||||
_recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed;
|
|
||||||
_recognitionControl.SessionEnded += _recognitionControl_SessionEnded;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _recognitionControl_SessionEnded(SessionEndedEvent obj)
|
private void _recognitionControl_SessionEnded(SessionEndedEvent obj)
|
||||||
{
|
{
|
||||||
|
var endedAt = obj.SessionEnded;
|
||||||
|
_endTime = endedAt;
|
||||||
_loadingService.StartLoading("Writing statistics for " + _settings.CameraName);
|
_loadingService.StartLoading("Writing statistics for " + _settings.CameraName);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_csvStatistics.WriteLine([_startTime.ToString("d"), _startTime.ToString("T"), DateTime.Now.ToString("d"), DateTime.Now.ToString("T"), "", "", ""]);
|
_csvStatistics.WriteLine([_startTime.ToString("d"), _startTime.ToString("T"), endedAt.ToString("d"), endedAt.ToString("T"), "", "", ""]);
|
||||||
_csvStatistics.Dispose();
|
_csvStatistics.Dispose();
|
||||||
|
_csvStatistics = null;
|
||||||
|
|
||||||
WriteFinalStatistics();
|
WriteFinalStatistics();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -67,10 +69,10 @@ namespace VisionBuilder.UI.Statistics
|
|||||||
|
|
||||||
private void _recognitionControl_SessionStarted(SessionStartedEvent obj)
|
private void _recognitionControl_SessionStarted(SessionStartedEvent obj)
|
||||||
{
|
{
|
||||||
_startTime = DateTime.Now;
|
_startTime = obj.SessionStarted;
|
||||||
_currentRecipe = obj.RecipeName;
|
_currentRecipe = obj.RecipeName;
|
||||||
_fileName = "tmp_"+Guid.NewGuid().ToString()+".csv";
|
_fileName = "tmp_"+Guid.NewGuid().ToString()+".csv";
|
||||||
_directoryPath = PathSettingsUtils.ConvertVariables(_settings.PathTemplate, DateTime.Now, DateTime.Now,
|
_directoryPath = PathSettingsUtils.ConvertVariables(_settings.PathTemplate, _startTime, _startTime,
|
||||||
obj.RecipeName, cameraName: _settings.CameraName);
|
obj.RecipeName, cameraName: _settings.CameraName);
|
||||||
_filePath = Path.Combine(_directoryPath, _fileName);
|
_filePath = Path.Combine(_directoryPath, _fileName);
|
||||||
var headers = new[] { _settings.StartDateColumnName, _settings.StartTimeColumnName, _settings.EndDateColumnName, _settings.EndTimeColumnName, _settings.ErrorTimeColumnName, _settings.ProductColumnName, _settings.ErrorColumnName };
|
var headers = new[] { _settings.StartDateColumnName, _settings.StartTimeColumnName, _settings.EndDateColumnName, _settings.EndTimeColumnName, _settings.ErrorTimeColumnName, _settings.ProductColumnName, _settings.ErrorColumnName };
|
||||||
@@ -84,6 +86,8 @@ namespace VisionBuilder.UI.Statistics
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConcurrentDictionary<string, int> _defects = new();
|
ConcurrentDictionary<string, int> _defects = new();
|
||||||
|
private DateTime _endTime;
|
||||||
|
|
||||||
private void RecordBadImage(string defectName)
|
private void RecordBadImage(string defectName)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -96,50 +100,60 @@ namespace VisionBuilder.UI.Statistics
|
|||||||
_defects.AddOrUpdate(defectName, 1, (key, oldValue) => oldValue + 1);
|
_defects.AddOrUpdate(defectName, 1, (key, oldValue) => oldValue + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteMonthStatistics(string filename)
|
private void WriteMonthStatistics()
|
||||||
{
|
{
|
||||||
// read second line to get the start time
|
|
||||||
var lines = File.ReadAllLines(filename);
|
|
||||||
var lastLine = lines[^1];
|
|
||||||
if (lines.Length < 2) return; // no data to write
|
|
||||||
var startDate = DateTime.Parse(lastLine.Split(',')[0]);
|
|
||||||
var startTime = DateTime.Parse(lastLine.Split(',')[1]);
|
|
||||||
var endDate = DateTime.Parse(lastLine.Split(',')[2]);
|
|
||||||
var endTime = DateTime.Parse(lastLine.Split(',')[3]);
|
|
||||||
|
|
||||||
// all must be non-empty
|
var startDate = _startTime.Date;
|
||||||
if (startDate == DateTime.MinValue || startTime == DateTime.MinValue || endDate == DateTime.MinValue || endTime == DateTime.MinValue)
|
var startTime = _startTime;
|
||||||
|
var endDate = _endTime.Date;
|
||||||
|
var endTime = _endTime;
|
||||||
|
|
||||||
|
var monthFilePath = Path.Combine(_directoryPath, MONTHLY_FILE_NAME);
|
||||||
|
|
||||||
|
var monthStatistics = new CsvStatistics(monthFilePath, [
|
||||||
|
"Date start","Time start","Date end", "Time end" ,"Product","Error type","Error count"
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach (var defect in _defects)
|
||||||
{
|
{
|
||||||
Log.Error("Invalid date or time in statistics file: " + filename);
|
monthStatistics.WriteLine(
|
||||||
throw new InvalidOperationException("Invalid date or time in statistics file: " + filename);
|
[startDate.ToString("d"), startTime.ToString("T"), endDate.ToString("d"), endTime.ToString("T"), _currentRecipe, defect.Key, defect.Value.ToString()]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
monthStatistics.Dispose();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WriteFinalStatistics()
|
private void WriteFinalStatistics()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var tmpFiles = Directory.GetFiles(_directoryPath, "tmp_*");
|
Log.Debug("Writing month statistics");
|
||||||
// order by creation time
|
WriteMonthStatistics();
|
||||||
Array.Sort(tmpFiles, (x, y) => File.GetCreationTime(x).CompareTo(File.GetCreationTime(y)));
|
|
||||||
foreach (var file in tmpFiles)
|
|
||||||
{
|
|
||||||
var finalFileName = Path.Combine(_directoryPath, BY_EVENTS_FILE_NAME);
|
|
||||||
var lines = File.ReadAllLines(file);
|
|
||||||
File.AppendAllLines(finalFileName, lines.Skip(1)); // skip header
|
|
||||||
WriteMonthStatistics(file);
|
|
||||||
File.Delete(file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error("Error writing statistics for " + _settings.CameraName+ e);
|
Log.Error("Error writing statistics for " + _settings.CameraName+ "\n"+ e);
|
||||||
}
|
}
|
||||||
|
_csvStatistics?.Dispose();
|
||||||
|
_csvStatistics = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize()
|
public void InitializeModule()
|
||||||
{
|
{
|
||||||
WriteFinalStatistics();
|
_recognitionControl.SessionStarted += _recognitionControl_SessionStarted;
|
||||||
|
_recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed;
|
||||||
|
_recognitionControl.SessionEnded += _recognitionControl_SessionEnded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_csvStatistics?.Dispose();
|
||||||
|
_recognitionControl.SessionStarted -= _recognitionControl_SessionStarted;
|
||||||
|
_recognitionControl.ImageProcessed -= _recognitionControl_ImageProcessed;
|
||||||
|
_recognitionControl.SessionEnded -= _recognitionControl_SessionEnded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Inspectron.Settings;
|
using Inspectron.Settings;
|
||||||
using Inspectron.Settings.Attributes;
|
using Inspectron.Settings.Attributes;
|
||||||
|
using Lindt.Colorballs.Duo.Utils;
|
||||||
using VisionBuilder.UI.Common;
|
using VisionBuilder.UI.Common;
|
||||||
|
|
||||||
namespace VisionBuilder.UI.Statistics;
|
namespace VisionBuilder.UI.Statistics;
|
||||||
@@ -16,31 +17,10 @@ public class VisionBuilderStatisticsSettings: ISettings
|
|||||||
[SettingDescription(
|
[SettingDescription(
|
||||||
@"
|
@"
|
||||||
Specifies directory, in which MaxBad and MaxGood are applied.
|
Specifies directory, in which MaxBad and MaxGood are applied.
|
||||||
You can use the following system variables:
|
"+PathSettingsUtils.INSTUCTIONS
|
||||||
|
|
||||||
$RECIPE_NAME - name of the current recipe
|
|
||||||
$DEFECT_NAME - name of the current defect(for bad images)
|
|
||||||
$CAMERA_NAME - name of the camera
|
|
||||||
|
|
||||||
$HOUR - hour of the image
|
|
||||||
$MINUTE - minute of the image
|
|
||||||
$SECOND - second of the image
|
|
||||||
$MS - milliseconds of the image
|
|
||||||
|
|
||||||
$DAY - day of the image
|
|
||||||
$MONTH - month of the image
|
|
||||||
$YEAR - year of the image
|
|
||||||
|
|
||||||
$SS_HOUR - hour of the session start
|
|
||||||
$SS_MINUTE - minute of the session start
|
|
||||||
$SS_SECOND - second of the session start
|
|
||||||
|
|
||||||
$SS_DAY - day of the session start
|
|
||||||
$SS_MONTH - month of the session start
|
|
||||||
$SS_YEAR - year of the session start
|
|
||||||
"
|
|
||||||
)]
|
)]
|
||||||
public string PathTemplate { get; set; }= @$"..\Data\Statistics\$SS_MONTH $SS_YEAR\$RECIPE_NAME\";
|
[SettingPreview(typeof(VisionBuilderStatisticsSettings), nameof(PathTemplatePreview))]
|
||||||
|
public string PathTemplate { get; set; }= @$"..\Data\Statistics\$SS_MONTH_NUM $SS_YEAR\$RECIPE_NAME\";
|
||||||
|
|
||||||
public string StartDateColumnName { get; set; } = "Start Date";
|
public string StartDateColumnName { get; set; } = "Start Date";
|
||||||
public string StartTimeColumnName { get; set; } = "Start time";
|
public string StartTimeColumnName { get; set; } = "Start time";
|
||||||
@@ -61,4 +41,10 @@ $SS_YEAR - year of the session start
|
|||||||
settings.RegisterSimple(this,()=>this.ProductColumnName,CameraName+"/"+"Statistics",nameof(ProductColumnName));
|
settings.RegisterSimple(this,()=>this.ProductColumnName,CameraName+"/"+"Statistics",nameof(ProductColumnName));
|
||||||
settings.RegisterSimple(this,()=>this.ErrorColumnName,CameraName+"/"+"Statistics",nameof(ErrorColumnName));
|
settings.RegisterSimple(this,()=>this.ErrorColumnName,CameraName+"/"+"Statistics",nameof(ErrorColumnName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string PathTemplatePreview(object template)
|
||||||
|
{
|
||||||
|
return PathSettingsUtils.ConvertVariables((string)template, DateTime.Now, DateTime.Now, "my recipe",
|
||||||
|
"my defect", "my camera");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\VisionBuilder.UI.Statistics\VisionBuilder.UI.Statistics.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
471
VisionBuilder.UI.Tests/VisionBuilderStatisticsTests.cs
Normal file
471
VisionBuilder.UI.Tests/VisionBuilderStatisticsTests.cs
Normal file
@@ -0,0 +1,471 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Reflection;
|
||||||
|
using VisionBuilder.UI.Common;
|
||||||
|
using VisionBuilder.UI.Common.Commands;
|
||||||
|
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||||
|
using VisionBuilder.UI.Common.ViewModel.Classes;
|
||||||
|
using VisionBuilder.UI.Statistics;
|
||||||
|
|
||||||
|
namespace VisionBuilder.UI.Tests;
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class VisionBuilderStatisticsTests
|
||||||
|
{
|
||||||
|
private string _tempDirectory;
|
||||||
|
private VisionBuilderStatisticsSettings _settings;
|
||||||
|
private TestRecognitionControl _recognitionControl;
|
||||||
|
private TestLoadingService _loadingService;
|
||||||
|
|
||||||
|
[TestInitialize]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_tempDirectory = Path.Combine(Path.GetTempPath(), "VisionBuilderStatisticsTests", Guid.NewGuid().ToString());
|
||||||
|
Directory.CreateDirectory(_tempDirectory);
|
||||||
|
|
||||||
|
_settings = new VisionBuilderStatisticsSettings("TestCamera")
|
||||||
|
{
|
||||||
|
PathTemplate = _tempDirectory,
|
||||||
|
StartDateColumnName = "Start Date",
|
||||||
|
StartTimeColumnName = "Start time",
|
||||||
|
EndDateColumnName = "End Date",
|
||||||
|
EndTimeColumnName = "End time",
|
||||||
|
ErrorTimeColumnName = "Error time",
|
||||||
|
ProductColumnName = "Product",
|
||||||
|
ErrorColumnName = "Error"
|
||||||
|
};
|
||||||
|
|
||||||
|
_recognitionControl = new TestRecognitionControl();
|
||||||
|
_loadingService = new TestLoadingService();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCleanup]
|
||||||
|
public void Cleanup()
|
||||||
|
{
|
||||||
|
if (Directory.Exists(_tempDirectory))
|
||||||
|
{
|
||||||
|
Directory.Delete(_tempDirectory, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void SessionStarted_CreatesTemporaryFile_WithCorrectHeaders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService);
|
||||||
|
statistics.InitializeModule();
|
||||||
|
var sessionStartEvent = new SessionStartedEvent
|
||||||
|
{
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStarted = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
_recognitionControl.TriggerSessionStarted(sessionStartEvent);
|
||||||
|
statistics.Dispose(); // Ensure file is written and closed
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var tmpFiles = Directory.GetFiles(_tempDirectory, "tmp_*.csv");
|
||||||
|
Assert.AreEqual(1, tmpFiles.Length, "Should create exactly one temporary file");
|
||||||
|
|
||||||
|
var lines = File.ReadAllLines(tmpFiles[0]);
|
||||||
|
Assert.IsTrue(lines.Length >= 2, "Should have header and at least one data line");
|
||||||
|
|
||||||
|
var expectedHeaders = new[] { "Start Date", "Start time", "End Date", "End time", "Error time", "Product", "Error" };
|
||||||
|
var actualHeaders = lines[0].Split(',');
|
||||||
|
CollectionAssert.AreEqual(expectedHeaders, actualHeaders, "Headers should match settings");
|
||||||
|
|
||||||
|
var firstDataLine = lines[1].Split(',');
|
||||||
|
Assert.IsTrue(DateTime.TryParse(firstDataLine[0], out _), "First column should be a valid date");
|
||||||
|
Assert.IsTrue(DateTime.TryParse(firstDataLine[1], out _), "Second column should be a valid time");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ImageProcessed_WithError_RecordsDefectInFile()
|
||||||
|
{
|
||||||
|
var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService);
|
||||||
|
statistics.InitializeModule();
|
||||||
|
// Arrange
|
||||||
|
var sessionStartEvent = new SessionStartedEvent
|
||||||
|
{
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStarted = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
var imageProcessedEvent = new ImageProcessedEvent
|
||||||
|
{
|
||||||
|
HasError = true,
|
||||||
|
ErrorNames = new List<string> { "TestDefect" },
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStart = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
_recognitionControl.TriggerSessionStarted(sessionStartEvent);
|
||||||
|
_recognitionControl.TriggerImageProcessed(imageProcessedEvent);
|
||||||
|
statistics.Dispose(); // Ensure file is written and closed
|
||||||
|
// Assert
|
||||||
|
var tmpFiles = Directory.GetFiles(_tempDirectory, "tmp_*.csv");
|
||||||
|
var lines = File.ReadAllLines(tmpFiles[0]);
|
||||||
|
|
||||||
|
Assert.IsTrue(lines.Length >= 3, "Should have header, session start, and error record");
|
||||||
|
|
||||||
|
var errorLine = lines[2].Split(',');
|
||||||
|
Assert.AreEqual("TestDefect", errorLine[6], "Error name should be recorded in correct column");
|
||||||
|
Assert.AreEqual("TestRecipe", errorLine[5], "Recipe name should be recorded in correct column");
|
||||||
|
Assert.IsTrue(DateTime.TryParse(errorLine[4], out _), "Error time should be valid");
|
||||||
|
|
||||||
|
// Verify defect counting using reflection
|
||||||
|
var defectsField = typeof(VisionBuilderStatistics).GetField("_defects", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
var defects = (ConcurrentDictionary<string, int>)defectsField.GetValue(statistics);
|
||||||
|
Assert.AreEqual(1, defects["TestDefect"], "Defect count should be incremented");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ImageProcessed_WithoutError_DoesNotRecordDefect()
|
||||||
|
{
|
||||||
|
var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService);
|
||||||
|
statistics.InitializeModule();
|
||||||
|
// Arrange
|
||||||
|
var sessionStartEvent = new SessionStartedEvent
|
||||||
|
{
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStarted = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
var imageProcessedEvent = new ImageProcessedEvent
|
||||||
|
{
|
||||||
|
HasError = false,
|
||||||
|
ErrorNames = new List<string>(),
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStart = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
_recognitionControl.TriggerSessionStarted(sessionStartEvent);
|
||||||
|
_recognitionControl.TriggerImageProcessed(imageProcessedEvent);
|
||||||
|
statistics.Dispose(); // Ensure file is written and closed
|
||||||
|
// Assert
|
||||||
|
var tmpFiles = Directory.GetFiles(_tempDirectory, "tmp_*.csv");
|
||||||
|
var lines = File.ReadAllLines(tmpFiles[0]);
|
||||||
|
|
||||||
|
Assert.AreEqual(2, lines.Length, "Should only have header and session start line");
|
||||||
|
|
||||||
|
// Verify defect counting using reflection
|
||||||
|
var defectsField = typeof(VisionBuilderStatistics).GetField("_defects", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
var defects = (ConcurrentDictionary<string, int>)defectsField.GetValue(statistics);
|
||||||
|
Assert.AreEqual(0, defects.Count, "No defects should be recorded");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void SessionEnded_WritesEndTimeAndDisposesCsv()
|
||||||
|
{
|
||||||
|
var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService)
|
||||||
|
{
|
||||||
|
DoNotDeleteTempFiles = true
|
||||||
|
};
|
||||||
|
statistics.InitializeModule();
|
||||||
|
// Arrange
|
||||||
|
var sessionStartEvent = new SessionStartedEvent
|
||||||
|
{
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStarted = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
var sessionEndEvent = new SessionEndedEvent
|
||||||
|
{
|
||||||
|
SessionEnded = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
_recognitionControl.TriggerSessionStarted(sessionStartEvent);
|
||||||
|
_recognitionControl.TriggerSessionEnded(sessionEndEvent);
|
||||||
|
statistics.Dispose(); // Ensure file is written and closed
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var tmpFiles = Directory.GetFiles(_tempDirectory, "tmp_*.csv");
|
||||||
|
var lines = File.ReadAllLines(tmpFiles[0]);
|
||||||
|
|
||||||
|
Assert.IsTrue(lines.Length >= 3, "Should have header, session start, and session end");
|
||||||
|
|
||||||
|
var endLine = lines[2].Split(',');
|
||||||
|
Assert.IsTrue(DateTime.TryParse(endLine[2], out _), "End date should be valid");
|
||||||
|
Assert.IsTrue(DateTime.TryParse(endLine[3], out _), "End time should be valid");
|
||||||
|
|
||||||
|
Assert.IsTrue(_loadingService.StartLoadingCalled, "Should start loading during session end");
|
||||||
|
Assert.IsTrue(_loadingService.StopLoadingCalled, "Should stop loading after session end");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void WriteFinalStatistics_ConsolidatesTemporaryFiles()
|
||||||
|
{
|
||||||
|
var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService);
|
||||||
|
statistics.InitializeModule();
|
||||||
|
// Arrange
|
||||||
|
var sessionStartEvent = new SessionStartedEvent
|
||||||
|
{
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStarted = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
var imageProcessedEvent = new ImageProcessedEvent
|
||||||
|
{
|
||||||
|
HasError = true,
|
||||||
|
ErrorNames = new List<string> { "TestDefect" },
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStart = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
var sessionEndEvent = new SessionEndedEvent
|
||||||
|
{
|
||||||
|
SessionEnded = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
_recognitionControl.TriggerSessionStarted(sessionStartEvent);
|
||||||
|
_recognitionControl.TriggerImageProcessed(imageProcessedEvent);
|
||||||
|
_recognitionControl.TriggerSessionEnded(sessionEndEvent);
|
||||||
|
|
||||||
|
statistics.Dispose(); // Ensure file is written and closed
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var tmpFiles = Directory.GetFiles(_tempDirectory, "tmp_*.csv");
|
||||||
|
Assert.AreEqual(0, tmpFiles.Length, "Temporary files should be deleted after consolidation");
|
||||||
|
|
||||||
|
var finalFile = Path.Combine(_tempDirectory, "statistics_by_events.csv");
|
||||||
|
Assert.IsTrue(File.Exists(finalFile), "Final statistics file should be created");
|
||||||
|
|
||||||
|
var finalLines = File.ReadAllLines(finalFile);
|
||||||
|
Assert.IsTrue(finalLines.Length >= 2, "Final file should contain session data");
|
||||||
|
|
||||||
|
// Verify the defect was recorded in the final file
|
||||||
|
var defectLine = finalLines.FirstOrDefault(l => l.Contains("TestDefect"));
|
||||||
|
Assert.IsNotNull(defectLine, "Defect should be recorded in final file");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void MultipleDefects_CountedCorrectly()
|
||||||
|
{
|
||||||
|
var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService);
|
||||||
|
statistics.InitializeModule();
|
||||||
|
// Arrange
|
||||||
|
var sessionStartEvent = new SessionStartedEvent
|
||||||
|
{
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStarted = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
_recognitionControl.TriggerSessionStarted(sessionStartEvent);
|
||||||
|
|
||||||
|
// Process multiple images with same defect
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
_recognitionControl.TriggerImageProcessed(new ImageProcessedEvent
|
||||||
|
{
|
||||||
|
HasError = true,
|
||||||
|
ErrorNames = new List<string> { "DefectA" },
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStart = DateTime.Now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process images with different defect
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
_recognitionControl.TriggerImageProcessed(new ImageProcessedEvent
|
||||||
|
{
|
||||||
|
HasError = true,
|
||||||
|
ErrorNames = new List<string> { "DefectB" },
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStart = DateTime.Now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
statistics.Dispose(); // Ensure file is written and closed
|
||||||
|
// Assert
|
||||||
|
var defectsField = typeof(VisionBuilderStatistics).GetField("_defects", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
var defects = (ConcurrentDictionary<string, int>)defectsField.GetValue(statistics);
|
||||||
|
|
||||||
|
Assert.AreEqual(3, defects["DefectA"], "DefectA should be counted correctly");
|
||||||
|
Assert.AreEqual(2, defects["DefectB"], "DefectB should be counted correctly");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void PathTemplate_WithVariables_ReplacedCorrectly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var customSettings = new VisionBuilderStatisticsSettings("TestCamera")
|
||||||
|
{
|
||||||
|
PathTemplate = Path.Combine(_tempDirectory, "$RECIPE_NAME", "$SS_YEAR", "$SS_MONTH")
|
||||||
|
};
|
||||||
|
|
||||||
|
var customStatistics = new VisionBuilderStatistics(customSettings, _recognitionControl, _loadingService);
|
||||||
|
customStatistics.InitializeModule();
|
||||||
|
var testDate = new DateTime(2023, 5, 15, 10, 30, 0);
|
||||||
|
var sessionStartEvent = new SessionStartedEvent
|
||||||
|
{
|
||||||
|
RecipeName = "MyRecipe",
|
||||||
|
SessionStarted = new DateTime(2023, 5, 15, 10, 30, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
_recognitionControl.TriggerSessionStarted(sessionStartEvent);
|
||||||
|
customStatistics.Dispose(); // Ensure file is written and closed
|
||||||
|
// Assert
|
||||||
|
var expectedPath = Path.Combine(_tempDirectory, "MyRecipe", testDate.ToString("yyyy"), testDate.ToString("MMMM"));
|
||||||
|
Assert.IsTrue(Directory.Exists(expectedPath), "Directory should be created with expanded variables");
|
||||||
|
|
||||||
|
var tmpFiles = Directory.GetFiles(expectedPath, "tmp_*.csv");
|
||||||
|
Assert.AreEqual(1, tmpFiles.Length, "Temporary file should be created in correct directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void CustomColumnNames_UsedInHeaders()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var customSettings = new VisionBuilderStatisticsSettings("TestCamera")
|
||||||
|
{
|
||||||
|
PathTemplate = _tempDirectory,
|
||||||
|
StartDateColumnName = "Custom Start Date",
|
||||||
|
StartTimeColumnName = "Custom Start Time",
|
||||||
|
EndDateColumnName = "Custom End Date",
|
||||||
|
EndTimeColumnName = "Custom End Time",
|
||||||
|
ErrorTimeColumnName = "Custom Error Time",
|
||||||
|
ProductColumnName = "Custom Product",
|
||||||
|
ErrorColumnName = "Custom Error"
|
||||||
|
};
|
||||||
|
|
||||||
|
var customStatistics = new VisionBuilderStatistics(customSettings, _recognitionControl, _loadingService);
|
||||||
|
customStatistics.InitializeModule();
|
||||||
|
var sessionStartEvent = new SessionStartedEvent
|
||||||
|
{
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStarted = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
_recognitionControl.TriggerSessionStarted(sessionStartEvent);
|
||||||
|
|
||||||
|
customStatistics.Dispose();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var tmpFiles = Directory.GetFiles(_tempDirectory, "tmp_*.csv");
|
||||||
|
var lines = File.ReadAllLines(tmpFiles[0]);
|
||||||
|
|
||||||
|
var expectedHeaders = new[] { "Custom Start Date", "Custom Start Time", "Custom End Date", "Custom End Time", "Custom Error Time", "Custom Product", "Custom Error" };
|
||||||
|
var actualHeaders = lines[0].Split(',');
|
||||||
|
CollectionAssert.AreEqual(expectedHeaders, actualHeaders, "Custom column names should be used in headers");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void WriteMonthStatistics_CreatesMonthlyFileWithDefectCounts()
|
||||||
|
{
|
||||||
|
var statistics = new VisionBuilderStatistics(_settings, _recognitionControl, _loadingService);
|
||||||
|
statistics.InitializeModule();
|
||||||
|
// Arrange
|
||||||
|
var sessionStartEvent = new SessionStartedEvent
|
||||||
|
{
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStarted = new DateTime(2023, 5, 15, 10, 30, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
var imageProcessedEvent1 = new ImageProcessedEvent
|
||||||
|
{
|
||||||
|
HasError = true,
|
||||||
|
ErrorNames = new List<string> { "DefectA" },
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStart = new DateTime(2023, 5, 15, 10, 30, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
var imageProcessedEvent2 = new ImageProcessedEvent
|
||||||
|
{
|
||||||
|
HasError = true,
|
||||||
|
ErrorNames = new List<string> { "DefectB" },
|
||||||
|
RecipeName = "TestRecipe",
|
||||||
|
SessionStart = new DateTime(2023, 5, 15, 10, 30, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
var sessionEndEvent = new SessionEndedEvent
|
||||||
|
{
|
||||||
|
SessionEnded = new DateTime(2023, 5, 15, 11, 45, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
_recognitionControl.TriggerSessionStarted(sessionStartEvent);
|
||||||
|
_recognitionControl.TriggerImageProcessed(imageProcessedEvent1);
|
||||||
|
_recognitionControl.TriggerImageProcessed(imageProcessedEvent1); // Same defect again
|
||||||
|
_recognitionControl.TriggerImageProcessed(imageProcessedEvent2);
|
||||||
|
_recognitionControl.TriggerSessionEnded(sessionEndEvent);
|
||||||
|
statistics.Dispose();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var monthlyFile = Path.Combine(_tempDirectory, "statistics_month.csv");
|
||||||
|
Assert.IsTrue(File.Exists(monthlyFile), "Monthly statistics file should be created");
|
||||||
|
|
||||||
|
var monthlyLines = File.ReadAllLines(monthlyFile);
|
||||||
|
Assert.IsTrue(monthlyLines.Length >= 3, "Should have header and defect records");
|
||||||
|
|
||||||
|
var expectedHeaders = new[] { "Date start", "Time start", "Date end", "Time end", "Product", "Error type", "Error count" };
|
||||||
|
var actualHeaders = monthlyLines[0].Split(',');
|
||||||
|
CollectionAssert.AreEqual(expectedHeaders, actualHeaders, "Monthly file should have correct headers");
|
||||||
|
|
||||||
|
// Verify defect counts
|
||||||
|
var defectALine = monthlyLines.FirstOrDefault(l => l.Contains("DefectA"));
|
||||||
|
var defectBLine = monthlyLines.FirstOrDefault(l => l.Contains("DefectB"));
|
||||||
|
|
||||||
|
Assert.IsNotNull(defectALine, "DefectA should be recorded in monthly file");
|
||||||
|
Assert.IsNotNull(defectBLine, "DefectB should be recorded in monthly file");
|
||||||
|
|
||||||
|
var defectAData = defectALine.Split(',');
|
||||||
|
var defectBData = defectBLine.Split(',');
|
||||||
|
|
||||||
|
Assert.AreEqual("2", defectAData[6], "DefectA count should be 2");
|
||||||
|
Assert.AreEqual("1", defectBData[6], "DefectB count should be 1");
|
||||||
|
Assert.AreEqual("TestRecipe", defectAData[4], "Product name should be correct");
|
||||||
|
Assert.AreEqual("TestRecipe", defectBData[4], "Product name should be correct");
|
||||||
|
|
||||||
|
// Verify dates and times
|
||||||
|
Assert.IsTrue(DateTime.TryParse(defectAData[0], out var startDate), "Start date should be valid");
|
||||||
|
Assert.IsTrue(DateTime.TryParse(defectAData[1], out var startTime), "Start time should be valid");
|
||||||
|
Assert.IsTrue(DateTime.TryParse(defectAData[2], out var endDate), "End date should be valid");
|
||||||
|
Assert.IsTrue(DateTime.TryParse(defectAData[3], out var endTime), "End time should be valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test implementation of IRecognitionControl using reflection approach
|
||||||
|
public class TestRecognitionControl : IRecognitionControl
|
||||||
|
{
|
||||||
|
public event Action<ImageProcessedEvent> ImageProcessed = delegate { };
|
||||||
|
public event Action<SessionStartedEvent> SessionStarted = delegate { };
|
||||||
|
public event Action<SessionEndedEvent> SessionEnded = delegate { };
|
||||||
|
|
||||||
|
public List<RecipeData> GetRecipesData() => new List<RecipeData>();
|
||||||
|
public void SetRecipe(RecipeData recipe) { }
|
||||||
|
public void Start() { }
|
||||||
|
public void Stop() { }
|
||||||
|
|
||||||
|
public void TriggerImageProcessed(ImageProcessedEvent eventArgs) => ImageProcessed(eventArgs);
|
||||||
|
public void TriggerSessionStarted(SessionStartedEvent eventArgs) => SessionStarted(eventArgs);
|
||||||
|
public void TriggerSessionEnded(SessionEndedEvent eventArgs) => SessionEnded(eventArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test implementation of ILoadingService
|
||||||
|
public class TestLoadingService : ILoadingService
|
||||||
|
{
|
||||||
|
public bool StartLoadingCalled { get; private set; }
|
||||||
|
public bool StopLoadingCalled { get; private set; }
|
||||||
|
public string LastLoadingMessage { get; private set; }
|
||||||
|
|
||||||
|
public void StartLoading(string message)
|
||||||
|
{
|
||||||
|
StartLoadingCalled = true;
|
||||||
|
LastLoadingMessage = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopLoading(string message)
|
||||||
|
{
|
||||||
|
StopLoadingCalled = true;
|
||||||
|
LastLoadingMessage = message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,14 +11,19 @@ using Inspectron.Settings;
|
|||||||
using Inspectron.Settings.Windows.Configuration;
|
using Inspectron.Settings.Windows.Configuration;
|
||||||
using Lindt.Colorballs.Duo.Utils;
|
using Lindt.Colorballs.Duo.Utils;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
|
using Ninject.Extensions.ChildKernel;
|
||||||
using OpenCvSharp;
|
using OpenCvSharp;
|
||||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||||
|
using VisionBuilder.UI.Console;
|
||||||
using VisionBuilder.UI.Recipes.HawkeyeRecipe;
|
using VisionBuilder.UI.Recipes.HawkeyeRecipe;
|
||||||
|
using VisionBuilder.UI.Ringbuffer;
|
||||||
|
using VisionBuilder.UI.Statistics;
|
||||||
|
|
||||||
namespace VisionBuilder.UI.Windows.Test
|
namespace VisionBuilder.UI.Windows.Test
|
||||||
{
|
{
|
||||||
internal static class Program
|
internal static class Program
|
||||||
{
|
{
|
||||||
|
private const string CAMERA1 = "Camera 1";
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The main entry point for the application.
|
/// The main entry point for the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -29,58 +34,42 @@ namespace VisionBuilder.UI.Windows.Test
|
|||||||
// see https://aka.ms/applicationconfiguration.
|
// see https://aka.ms/applicationconfiguration.
|
||||||
ApplicationConfiguration.Initialize();
|
ApplicationConfiguration.Initialize();
|
||||||
|
|
||||||
|
|
||||||
StandardKernel kernelCamera1 = new StandardKernel();
|
|
||||||
|
|
||||||
|
|
||||||
// SETTINGS //
|
|
||||||
InspectronSettings settings = new InspectronSettings("..\\Config\\AppSettings.xml");
|
InspectronSettings settings = new InspectronSettings("..\\Config\\AppSettings.xml");
|
||||||
kernelCamera1.Bind<InspectronSettings>().ToConstant(settings);
|
|
||||||
kernelCamera1.Bind<UIConfiguration,ISettings>().ToConstant(new UIConfiguration());
|
|
||||||
kernelCamera1.Bind<CameraSettings, ISettings>().ToConstant(new CameraSettings("Camera 1"));
|
|
||||||
kernelCamera1.Bind<EmulationSettings, ISettings>().ToConstant(new EmulationSettings("Camera 1"));
|
|
||||||
kernelCamera1.Bind<HawkeyeRecognitionSettings, ISettings>().ToConstant(new HawkeyeRecognitionSettings("Camera 1"));
|
|
||||||
kernelCamera1.Bind<VisionBuilderRingbufferSettings, ISettings>().ToConstant(new VisionBuilderRingbufferSettings("Camera 1"));
|
|
||||||
|
|
||||||
|
var mainKernel = Common.VisionBuilder.CreateMainKernel(settings);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// CAMERA 1 //
|
||||||
|
ChildKernel kernelCamera1 = new ChildKernel(mainKernel);
|
||||||
|
mainKernel.UseWindowsServices();
|
||||||
|
|
||||||
|
// MODULES //
|
||||||
|
kernelCamera1
|
||||||
|
.RegisterCamera(CAMERA1)
|
||||||
|
.UseStatistics(CAMERA1)
|
||||||
|
.UseRingbuffer(CAMERA1)
|
||||||
|
.UseHawkeyeRecipes(CAMERA1)
|
||||||
|
.UseConsole();
|
||||||
|
|
||||||
|
|
||||||
|
// LOAD SETTINGS //
|
||||||
kernelCamera1.RegisterSettings();
|
kernelCamera1.RegisterSettings();
|
||||||
settings.LoadSettings();
|
settings.LoadSettings();
|
||||||
|
|
||||||
|
// Create camera and bind it
|
||||||
|
// Must be done after settings are loaded
|
||||||
|
kernelCamera1.UseCamera();
|
||||||
|
|
||||||
// --- //
|
|
||||||
|
|
||||||
// SOURCES //
|
|
||||||
var cameraSettings = kernelCamera1.Get<CameraSettings>();
|
|
||||||
switch (cameraSettings.Camera1Source)
|
|
||||||
{
|
|
||||||
case CameraSettings.EImageSource.Emulation:
|
|
||||||
kernelCamera1.Bind<IImageSource>().To<EmulationCameraImageSource>().InSingletonScope();
|
|
||||||
break;
|
|
||||||
case CameraSettings.EImageSource.Hawkeye:
|
|
||||||
break;
|
|
||||||
case CameraSettings.EImageSource.IDS:
|
|
||||||
break;
|
|
||||||
case CameraSettings.EImageSource.Basler:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
// --- //
|
|
||||||
|
|
||||||
// SERVICES //
|
kernelCamera1.InitializeModules();
|
||||||
kernelCamera1.Bind<ISettingsService>().To<SettingsService>().InSingletonScope();
|
|
||||||
kernelCamera1.Bind<IRecognitionControl>().To<HawkeyeRecognitionControl>().InSingletonScope();
|
|
||||||
kernelCamera1.Bind<VisionBuilderRingbuffer>().ToSelf().InSingletonScope();
|
|
||||||
kernelCamera1.Bind<ILoadingService>().To<LoadingService>().InSingletonScope();
|
|
||||||
|
|
||||||
kernelCamera1.Get<VisionBuilderRingbuffer>(); // initialize the ringbuffer
|
var mainWindowVm = mainKernel.Get<MainWindowVM>();
|
||||||
|
mainWindowVm.SingleCameraVms = [
|
||||||
|
kernelCamera1.Get<SingleCameraVM>()
|
||||||
|
];
|
||||||
|
|
||||||
var mainWindowVm = new MainWindowVM(kernelCamera1.Get<ISettingsService>())
|
|
||||||
{
|
|
||||||
SingleCameraVms = new ObservableCollection<SingleCameraVM>()
|
|
||||||
{
|
|
||||||
new SingleCameraVM(kernelCamera1.Get<CameraSettings>(),kernelCamera1.Get<UIConfiguration>(),new RecipeSelectionDialogService(),kernelCamera1.Get<IRecognitionControl>(), new ImagePreviewService(), new NoLearning())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
settings.LoadSettings();
|
settings.LoadSettings();
|
||||||
Application.Run(new Form1(mainWindowVm));
|
Application.Run(new Form1(mainWindowVm));
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Ninject.Extensions.ChildKernel" Version="3.3.0" />
|
||||||
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.6.0.20220608" />
|
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.6.0.20220608" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -17,9 +18,12 @@
|
|||||||
<ProjectReference Include="..\framework\Inspectron.Settings\Inspectron.Settings.csproj" />
|
<ProjectReference Include="..\framework\Inspectron.Settings\Inspectron.Settings.csproj" />
|
||||||
<ProjectReference Include="..\framework\MaterialSkin.Core\MaterialSkin.Core.csproj" />
|
<ProjectReference Include="..\framework\MaterialSkin.Core\MaterialSkin.Core.csproj" />
|
||||||
<ProjectReference Include="..\Hawkeye.VisionBuilder.UI.Sources.Emulation\Hawkeye.VisionBuilder.UI.Sources.Emulation.csproj" />
|
<ProjectReference Include="..\Hawkeye.VisionBuilder.UI.Sources.Emulation\Hawkeye.VisionBuilder.UI.Sources.Emulation.csproj" />
|
||||||
|
<ProjectReference Include="..\VisionBuilder.UI.Camera\VisionBuilder.UI.Camera.csproj" />
|
||||||
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
||||||
|
<ProjectReference Include="..\VisionBuilder.UI.Console\VisionBuilder.UI.Console.csproj" />
|
||||||
<ProjectReference Include="..\VisionBuilder.UI.Recipes.HawkeyeRecipe\VisionBuilder.UI.Recipes.HawkeyeRecipe.csproj" />
|
<ProjectReference Include="..\VisionBuilder.UI.Recipes.HawkeyeRecipe\VisionBuilder.UI.Recipes.HawkeyeRecipe.csproj" />
|
||||||
<ProjectReference Include="..\VisionBuilder.UI.Ringbuffer\VisionBuilder.UI.Ringbuffer.csproj" />
|
<ProjectReference Include="..\VisionBuilder.UI.Ringbuffer\VisionBuilder.UI.Ringbuffer.csproj" />
|
||||||
|
<ProjectReference Include="..\VisionBuilder.UI.Statistics\VisionBuilder.UI.Statistics.csproj" />
|
||||||
<ProjectReference Include="..\VisionBuilder.UI.Windows\VisionBuilder.UI.Windows.csproj" />
|
<ProjectReference Include="..\VisionBuilder.UI.Windows\VisionBuilder.UI.Windows.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
120
VisionBuilder.UI.Windows/Components/PreviewWindow.resx
Normal file
120
VisionBuilder.UI.Windows/Components/PreviewWindow.resx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
@@ -31,7 +31,7 @@ namespace VisionBuilder.UI.Windows.Components
|
|||||||
btnStop.Command=_singleCameraVm.StopCommand;
|
btnStop.Command=_singleCameraVm.StopCommand;
|
||||||
btnSelectRecipe.Command=_singleCameraVm.SelectRecipeCommand;
|
btnSelectRecipe.Command=_singleCameraVm.SelectRecipeCommand;
|
||||||
|
|
||||||
lblCameraName.DataBindings.Add("Text", _singleCameraVm, nameof(_singleCameraVm.CameraName), true, DataSourceUpdateMode.OnPropertyChanged);
|
lblCameraName.DataBindings.Add("Text", _singleCameraVm, nameof(_singleCameraVm.CameraLabel), true, DataSourceUpdateMode.OnPropertyChanged);
|
||||||
btnSelectRecipe.DataBindings.Add("Text", _singleCameraVm, nameof(_singleCameraVm.CurrentRecipeName), true, DataSourceUpdateMode.OnPropertyChanged);
|
btnSelectRecipe.DataBindings.Add("Text", _singleCameraVm, nameof(_singleCameraVm.CurrentRecipeName), true, DataSourceUpdateMode.OnPropertyChanged);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
18
VisionBuilder.UI.Windows/ModuleExtensions.cs
Normal file
18
VisionBuilder.UI.Windows/ModuleExtensions.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Ninject;
|
||||||
|
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||||
|
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
||||||
|
using VisionBuilder.UI.Windows.Settings;
|
||||||
|
|
||||||
|
namespace VisionBuilder.UI.Windows;
|
||||||
|
|
||||||
|
public static class ModuleExtensions
|
||||||
|
{
|
||||||
|
public static IKernel UseWindowsServices(this IKernel self)
|
||||||
|
{
|
||||||
|
self.Bind<ISettingsService>().To<WindowsSettingsService>().InSingletonScope();
|
||||||
|
self.Bind<ILoadingService>().To<WindowsLoadingService>().InSingletonScope();
|
||||||
|
self.Bind<IRecipeSelectionDialogService>().To<WindowsRecipeSelectionDialogService>().InSingletonScope();
|
||||||
|
self.Bind<IImagePreviewService>().To<WindowsImagePreviewService>().InSingletonScope();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using MaterialSkin.Controls;
|
|
||||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
|
||||||
|
|
||||||
namespace VisionBuilder.UI.Windows.Settings;
|
|
||||||
|
|
||||||
public class LoadingService: ILoadingService
|
|
||||||
{
|
|
||||||
public void StartLoading(string title)
|
|
||||||
{
|
|
||||||
MaterialLoader.Instance.StartLoading(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StopLoading(string title)
|
|
||||||
{
|
|
||||||
MaterialLoader.Instance.FinishLoading(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,10 +5,10 @@ using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
|||||||
|
|
||||||
namespace VisionBuilder.UI.Windows.Settings;
|
namespace VisionBuilder.UI.Windows.Settings;
|
||||||
|
|
||||||
public class ImagePreviewService: IImagePreviewService
|
public class WindowsImagePreviewService: IImagePreviewService
|
||||||
{
|
{
|
||||||
|
|
||||||
public ImagePreviewService()
|
public WindowsImagePreviewService()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
28
VisionBuilder.UI.Windows/Services/WindowsLoadingService.cs
Normal file
28
VisionBuilder.UI.Windows/Services/WindowsLoadingService.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using MaterialSkin.Controls;
|
||||||
|
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||||
|
|
||||||
|
namespace VisionBuilder.UI.Windows.Settings;
|
||||||
|
|
||||||
|
public class WindowsLoadingService: ILoadingService
|
||||||
|
{
|
||||||
|
public void StartLoading(string title)
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
MaterialLoader.Instance.StartLoading(title);
|
||||||
|
Application.DoEvents(); // Ensure the UI updates immediately
|
||||||
|
Thread.Sleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopLoading(string title)
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
MaterialLoader.Instance.FinishLoading(title);
|
||||||
|
Application.DoEvents(); // Ensure the UI updates immediately
|
||||||
|
Thread.Sleep(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ using VisionBuilder.UI.Windows.Dialogs;
|
|||||||
|
|
||||||
namespace VisionBuilder.UI.Windows.Settings;
|
namespace VisionBuilder.UI.Windows.Settings;
|
||||||
|
|
||||||
public class RecipeSelectionDialogService: IRecipeSelectionDialogService
|
public class WindowsRecipeSelectionDialogService: IRecipeSelectionDialogService
|
||||||
{
|
{
|
||||||
public bool SelectRecipe(RecipeSelectionVM recipeSelectionVm)
|
public bool SelectRecipe(RecipeSelectionVM recipeSelectionVm)
|
||||||
{
|
{
|
||||||
@@ -5,12 +5,12 @@ using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
|||||||
|
|
||||||
namespace VisionBuilder.UI.Windows.Settings;
|
namespace VisionBuilder.UI.Windows.Settings;
|
||||||
|
|
||||||
public class SettingsService: ISettingsService
|
public class WindowsSettingsService: ISettingsService
|
||||||
{
|
{
|
||||||
private readonly UIConfiguration _uiConfiguration;
|
private readonly UIConfiguration _uiConfiguration;
|
||||||
private readonly InspectronSettings _settings;
|
private readonly InspectronSettings _settings;
|
||||||
|
|
||||||
public SettingsService(UIConfiguration uiConfiguration, InspectronSettings settings)
|
public WindowsSettingsService(UIConfiguration uiConfiguration, InspectronSettings settings)
|
||||||
{
|
{
|
||||||
_uiConfiguration = uiConfiguration ?? throw new ArgumentNullException(nameof(uiConfiguration));
|
_uiConfiguration = uiConfiguration ?? throw new ArgumentNullException(nameof(uiConfiguration));
|
||||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||||
@@ -51,6 +51,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inspectron.Fastbuffer", "fr
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{C828783C-1CE1-4245-8731-4AE18A28F590}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{C828783C-1CE1-4245-8731-4AE18A28F590}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.UI.Console", "VisionBuilder.UI.Console\VisionBuilder.UI.Console.csproj", "{4B1817EF-A3D1-48CC-BE22-4138740E450F}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.UI.Camera", "VisionBuilder.UI.Camera\VisionBuilder.UI.Camera.csproj", "{FBDCC120-B6B3-4A28-8845-DF1690F619C1}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.UI.IOCommander", "VisionBuilder.UI.IOCommander\VisionBuilder.UI.IOCommander.csproj", "{E0F75E78-F7FA-4B7D-BA5F-A8A91C0A9AD9}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -137,6 +143,18 @@ Global
|
|||||||
{0A23E56A-DF4C-5174-18DF-18A771957AF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{0A23E56A-DF4C-5174-18DF-18A771957AF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{0A23E56A-DF4C-5174-18DF-18A771957AF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{0A23E56A-DF4C-5174-18DF-18A771957AF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{0A23E56A-DF4C-5174-18DF-18A771957AF4}.Release|Any CPU.Build.0 = Release|Any CPU
|
{0A23E56A-DF4C-5174-18DF-18A771957AF4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4B1817EF-A3D1-48CC-BE22-4138740E450F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4B1817EF-A3D1-48CC-BE22-4138740E450F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4B1817EF-A3D1-48CC-BE22-4138740E450F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4B1817EF-A3D1-48CC-BE22-4138740E450F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{FBDCC120-B6B3-4A28-8845-DF1690F619C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FBDCC120-B6B3-4A28-8845-DF1690F619C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FBDCC120-B6B3-4A28-8845-DF1690F619C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FBDCC120-B6B3-4A28-8845-DF1690F619C1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{E0F75E78-F7FA-4B7D-BA5F-A8A91C0A9AD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{E0F75E78-F7FA-4B7D-BA5F-A8A91C0A9AD9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{E0F75E78-F7FA-4B7D-BA5F-A8A91C0A9AD9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{E0F75E78-F7FA-4B7D-BA5F-A8A91C0A9AD9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -155,6 +173,9 @@ Global
|
|||||||
{51C2BC65-5E5D-E72A-988D-215E192DE889} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
{51C2BC65-5E5D-E72A-988D-215E192DE889} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
||||||
{1F9BB3BD-65C3-4B96-A880-4908CF6D9754} = {C828783C-1CE1-4245-8731-4AE18A28F590}
|
{1F9BB3BD-65C3-4B96-A880-4908CF6D9754} = {C828783C-1CE1-4245-8731-4AE18A28F590}
|
||||||
{0A23E56A-DF4C-5174-18DF-18A771957AF4} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
{0A23E56A-DF4C-5174-18DF-18A771957AF4} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
||||||
|
{4B1817EF-A3D1-48CC-BE22-4138740E450F} = {C828783C-1CE1-4245-8731-4AE18A28F590}
|
||||||
|
{FBDCC120-B6B3-4A28-8845-DF1690F619C1} = {C828783C-1CE1-4245-8731-4AE18A28F590}
|
||||||
|
{E0F75E78-F7FA-4B7D-BA5F-A8A91C0A9AD9} = {C828783C-1CE1-4245-8731-4AE18A28F590}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {3CE42AE5-D79F-4E97-A246-AA8FD228B677}
|
SolutionGuid = {3CE42AE5-D79F-4E97-A246-AA8FD228B677}
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ public class DefaultControlFactory : IControlFactory
|
|||||||
getPreview = value => (string)method.Invoke(null, new[] { value });
|
getPreview = value => (string)method.Invoke(null, new[] { value });
|
||||||
previewLabel = new Label
|
previewLabel = new Label
|
||||||
{
|
{
|
||||||
AutoSize = true,
|
AutoSize = false,
|
||||||
|
Size = new System.Drawing.Size(600, 30),
|
||||||
Padding = new Padding(15, 0, 0, 0),
|
Padding = new Padding(15, 0, 0, 0),
|
||||||
Font = new System.Drawing.Font("Segoe UI", 10, System.Drawing.FontStyle.Italic),
|
Font = new System.Drawing.Font("Segoe UI", 10, System.Drawing.FontStyle.Italic),
|
||||||
Text = getPreview(initialValue)
|
Text = getPreview(initialValue)
|
||||||
@@ -95,7 +96,7 @@ public class DefaultControlFactory : IControlFactory
|
|||||||
private Control CreateStringControl(string description, object initialValue, Action<object> valueChangedCallback,
|
private Control CreateStringControl(string description, object initialValue, Action<object> valueChangedCallback,
|
||||||
string settingDescription, Label previewLabel, Func<object, string> getPreview)
|
string settingDescription, Label previewLabel, Func<object, string> getPreview)
|
||||||
{
|
{
|
||||||
var textBox = new TextBox { Text = initialValue as string, Width = 400 };
|
var textBox = new TextBox { Text = initialValue as string, Width = 600 };
|
||||||
var label = new Label { Text = description, AutoSize = true, Padding = new Padding(0, 5, 0, 0) };
|
var label = new Label { Text = description, AutoSize = true, Padding = new Padding(0, 5, 0, 0) };
|
||||||
|
|
||||||
textBox.TextChanged += (s, e) =>
|
textBox.TextChanged += (s, e) =>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ public partial class OptionsWindow
|
|||||||
flowLayoutPanelSettings.FlowDirection = FlowDirection.TopDown;
|
flowLayoutPanelSettings.FlowDirection = FlowDirection.TopDown;
|
||||||
flowLayoutPanelSettings.Location = new Point(220, 12);
|
flowLayoutPanelSettings.Location = new Point(220, 12);
|
||||||
flowLayoutPanelSettings.Name = "flowLayoutPanelSettings";
|
flowLayoutPanelSettings.Name = "flowLayoutPanelSettings";
|
||||||
|
flowLayoutPanelSettings.Padding = new Padding(0, 0, 0, 15);
|
||||||
flowLayoutPanelSettings.Size = new Size(852, 582);
|
flowLayoutPanelSettings.Size = new Size(852, 582);
|
||||||
flowLayoutPanelSettings.TabIndex = 1;
|
flowLayoutPanelSettings.TabIndex = 1;
|
||||||
flowLayoutPanelSettings.WrapContents = false;
|
flowLayoutPanelSettings.WrapContents = false;
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class CsvStatistics
|
|||||||
{
|
{
|
||||||
_csv.Dispose();
|
_csv.Dispose();
|
||||||
_stream.Dispose();
|
_stream.Dispose();
|
||||||
|
_fileStream.Close();
|
||||||
_fileStream.Dispose();
|
_fileStream.Dispose();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,5 +2,6 @@
|
|||||||
|
|
||||||
public class StatisticsRecord
|
public class StatisticsRecord
|
||||||
{
|
{
|
||||||
|
public DateTime Time { get; set; }
|
||||||
public string[] Values { get; set; }
|
public string[] Values { get; set; }
|
||||||
}
|
}
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
ControlBox = false;
|
ControlBox = false;
|
||||||
Name = "MaterialLoader";
|
Name = "MaterialLoader";
|
||||||
StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||||
|
TopMost = true;
|
||||||
ResumeLayout(false);
|
ResumeLayout(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user