This commit is contained in:
meelstorm
2025-10-01 08:17:10 +02:00
parent 1aecb79ade
commit 63afb2dcfb
17 changed files with 168 additions and 55 deletions

View File

@@ -1,11 +1,22 @@
using System.Xml;
using Hawkeye.VisionBuilder.Workflow;
WorkflowList workflowList = new WorkflowList();
var file = File.OpenRead("gold_b97.hrcp");
using (var br = new BinaryReader(file))
var files = Directory.GetFiles(AppContext.BaseDirectory, "*.hrcp", SearchOption.TopDirectoryOnly);
foreach (var file in files)
{
workflowList.Load(br,new OperationDiscoveryService(workflowList));
try
{
Console.WriteLine($"Converting {file}");
var f = File.OpenRead(file);
WorkflowList list = new WorkflowList();
list.Load(new BinaryReader(f),new OperationDiscoveryService(list));
list.SaveJSON(Path.GetFileNameWithoutExtension(file)+".jhrcp");
}
catch (Exception ex)
{
Console.WriteLine($"Error processing file {file}: {ex.Message}");
}
}

View File

@@ -59,10 +59,10 @@ namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye
if (File.Exists(_settings?.SettingsFile))
{
_cameraSettings = UICameraSettings.LoadSettingsLocal(_settings.SettingsFile).ToCameraSettings();
_client.ApplySettings(_cameraSettings);
}
_client.ApplySettings(_cameraSettings);
}
public Task<Mat> GetImage(CancellationToken token)
@@ -98,6 +98,7 @@ namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye
try
{
Log.Debug("Got image on {adapter}", _settings.Adapter);
Log.Debug("Bytes: {bytes}", obj.Length);
//FlipLines(obj, _flipBuffer);
var pinnedArray = GCHandle.Alloc(obj, GCHandleType.Pinned);
@@ -113,9 +114,9 @@ namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye
_cameraSettings.OffsetX;
// crop the image with opencv
_lastImage = image[0, _cameraSettings.ImageSettings.Lines, xCrop,
xCrop + _cameraSettings.ImageWidth].Clone();
//_lastImage = image[0, _cameraSettings.ImageSettings.Lines, xCrop,
// xCrop + _cameraSettings.ImageWidth].Clone();
_lastImage = image;

View File

@@ -13,22 +13,23 @@ public class CandyboxBarcodeReader : IVisionBuilderModule
private readonly ILoadingService _loadingService;
private readonly CandyboxBarcodeReaderSettings _settings;
private readonly IRecognitionControl _recognitionControl;
private readonly SingleCameraVM _singleCameraVm;
private readonly VisionBuilderStatistics _statistics;
private SerialPort _port;
public EReaderState _readerState = EReaderState.ReadingRecipe;
private MaterialCancellableLoader? _loader;
public CandyboxBarcodeReader(CandyboxBarcodeReaderSettings settings, IRecognitionControl recognitionControl, VisionBuilderStatistics statistics)
public CandyboxBarcodeReader(CandyboxBarcodeReaderSettings settings, SingleCameraVM singleCameraVm, VisionBuilderStatistics statistics)
{
_settings = settings;
_recognitionControl = recognitionControl;
_singleCameraVm = singleCameraVm;
_statistics = statistics;
}
private void _port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
Thread.Sleep(500);
if(_singleCameraVm.IsRunning)return;
if(_readerState == EReaderState.ReadingRecipe)
ReadRecipe();
if (_readerState == EReaderState.ReadingAUF)
@@ -37,14 +38,21 @@ public class CandyboxBarcodeReader : IVisionBuilderModule
private void ReadRecipe()
{
if (_recognitionControl.IsRunning) return;
if (_singleCameraVm.IsRunning) return;
byte[] buffer = new byte[_port.BytesToRead];
_port.Read(buffer, 0, _port.BytesToRead);
var data = Encoding.ASCII.GetString(buffer).Trim();
var recipes = _recognitionControl.GetRecipesData();
if (string.IsNullOrEmpty(data))
{
Log.Warning("Received empty recipe");
return;
}
var vm = _singleCameraVm.GetRecipeSelectionVm();
var recipes = vm.Recipes;
string searchString = data;
Log.Information("Read Recipe: {Recipe}", data);
if (_settings.BarcodeRecipeMappings.Any(x => x.Barcode == searchString))
{
searchString = _settings.BarcodeRecipeMappings.First(x => x.Barcode == searchString).RecipeName;
@@ -56,20 +64,29 @@ public class CandyboxBarcodeReader : IVisionBuilderModule
Log.Warning("No matching recipe found for data: {Data}", data);
return;
}
_recognitionControl.SetRecipe(existingRecipe);
_readerState = EReaderState.ReadingAUF;
vm.SelectedRecipe = vm.Recipes.FirstOrDefault(r => r.RecipeName == existingRecipe.RecipeName);
_singleCameraVm.SynchronizationContext!.Post((_) =>
{
_loader = new MaterialCancellableLoader();
_loader.StartLoading("Waiting for AUF...");
_loader.LoadingCancelled += CancelWaiting;
_singleCameraVm.ProcessRecipeSelectionVm(vm);
}, null);
Application.DoEvents();
_readerState = EReaderState.ReadingAUF;
}
private void CancelWaiting(object? sender, string e)
{
_readerState = EReaderState.ReadingRecipe;
_loader.Close();
_singleCameraVm.SynchronizationContext!.Post((_) =>
{
_loader.FinishLoading("Waiting for AUF...");
_loader.LoadingCancelled -= CancelWaiting;
_loader = null;
}, null);
_readerState = EReaderState.ReadingRecipe;
}
private void ReadAUF()
@@ -77,17 +94,22 @@ public class CandyboxBarcodeReader : IVisionBuilderModule
byte[] buffer = new byte[_port.BytesToRead];
_port.Read(buffer, 0, _port.BytesToRead);
var data = Encoding.ASCII.GetString(buffer).Trim();
if (string.IsNullOrEmpty(data))
{
Log.Warning("Received empty AUF");
return;
}
Log.Information("Read AUF: {AUF}", data);
if (_loader != null)
{
_loader.Close();
_loader.LoadingCancelled -= CancelWaiting;
_loader = null;
CancelWaiting(this, string.Empty);
}
_statistics.Metadata = data;
_recognitionControl.Start();
_readerState = EReaderState.ReadingAUF;
_=_singleCameraVm.Start().ConfigureAwait(false);
Application.DoEvents();
_readerState = EReaderState.ReadingRecipe;
}

View File

@@ -94,7 +94,7 @@ public class CandyboxImageProcessingControl : BaseRecognitionControl
swTotal.Stop();
var allErrors =
res.ErrorPoints.Select(x => x.ToString())
res.ErrorPoints.Select(x => "Position "+x.ToString())
.Concat(
res.ErrorReason.Where(x=>x != EErrorReason.Good).Select(x=>x.ToString())
).ToArray();

View File

@@ -3,6 +3,7 @@ using Inspectron.Settings;
using Ninject;
using System.Reflection;
using CandyboxPlugin.Modules.Barcode;
using Ninject.Planning.Bindings;
using VisionBuilder.UI.Common;
using VisionBuilder.UI.Common.Plugins;
using VisionBuilder.UI.Common.Processing;
@@ -21,7 +22,13 @@ namespace CandyboxPlugin
public void RegisterCameraModules(IKernel kernel, string cameraName)
{
kernel.Bind<CandyboxRecognitionControlSettings, ISettings>().ToConstant(new CandyboxRecognitionControlSettings(cameraName));
// remove settings binding
var existingRecognitionControlSettings = kernel.GetBindings(typeof(BaseRecognitionControlSettings)).First();
var toRemove = kernel.GetBindings(typeof(ISettings)).First(x => x.ProviderCallback.Target == existingRecognitionControlSettings.ProviderCallback.Target);
kernel.RemoveBinding(toRemove);
kernel.Bind<CandyboxRecognitionControlSettings,BaseRecognitionControlSettings, ISettings>().ToConstant(new CandyboxRecognitionControlSettings(cameraName));
kernel.Rebind<IRecognitionControl>().To<CandyboxImageProcessingControl>().InSingletonScope();

View File

@@ -477,6 +477,8 @@ namespace CandyboxPlugin.Recipe
if(_learnedParameters.CandyParameters.Count==0)errorReason.Add(EErrorReason.NotLearned);
Thread.Sleep(50);
return new ProcessingResult(framedImage, final,
_learnedParameters.CandyParameters.Count == 0 || wasError, (int) sw.ElapsedMilliseconds,
errorReason)
@@ -487,6 +489,7 @@ namespace CandyboxPlugin.Recipe
}
catch(Exception ex)
{
Thread.Sleep(50);
return new ProcessingResult(image, image,
true, (int)sw.ElapsedMilliseconds,
new List<EErrorReason>() {EErrorReason.Unknown})

View File

@@ -10,14 +10,15 @@ namespace PackstrasseBarcodeReader
public class PackstrasseBarcodeReaderModule : IVisionBuilderModule
{
private readonly PackstrasseBarcodeReaderSettings _settings;
private readonly IRecognitionControl _recognitionControl;
private readonly SingleCameraVM _singleCameraVm;
private SerialPort _port;
public PackstrasseBarcodeReaderModule(PackstrasseBarcodeReaderSettings settings, IRecognitionControl recognitionControl)
public PackstrasseBarcodeReaderModule(PackstrasseBarcodeReaderSettings settings, SingleCameraVM singleCameraVm)
{
_settings = settings;
_recognitionControl = recognitionControl;
_singleCameraVm = singleCameraVm;
}
private void _port_DataReceived(object sender, SerialDataReceivedEventArgs e)
@@ -26,7 +27,8 @@ namespace PackstrasseBarcodeReader
byte[] buffer = new byte[_port.BytesToRead];
_port.Read(buffer, 0, _port.BytesToRead);
var data = Encoding.ASCII.GetString(buffer).Trim();
var recipes = _recognitionControl.GetRecipesData();
var vm = _singleCameraVm.GetRecipeSelectionVm();
string searchString = data;
if (_settings.BarcodeRecipeMappings.Any(x=>x.Barcode==searchString))
@@ -34,14 +36,17 @@ namespace PackstrasseBarcodeReader
searchString = _settings.BarcodeRecipeMappings.First(x => x.Barcode == searchString).RecipeName;
}
var existingRecipe=recipes.FirstOrDefault(x => x.RecipeName == searchString);
var existingRecipe= vm.Recipes.FirstOrDefault(x => x.RecipeName == searchString);
if (existingRecipe == null)
{
Log.Warning("No matching recipe found for data: {Data}", data);
return;
}
_recognitionControl.SetRecipe(existingRecipe);
vm.SelectedRecipe = vm.Recipes.FirstOrDefault(r => r.RecipeName == existingRecipe.RecipeName);
_singleCameraVm.SynchronizationContext!.Post((_) =>
{
_singleCameraVm.ProcessRecipeSelectionVm(vm);
}, null);
}

View File

@@ -12,8 +12,8 @@ public abstract class BaseRecognitionControlSettings(string cameraName) : ISetti
public virtual void RegisterSettings(InspectronSettings settings)
{
settings.RegisterSimple(this, () => this.MinimumProcessingTime, CameraName + "/Hawkeye recognition", "Minimum processing time (ms)");
settings.RegisterSimple(this, () => this.RecipeNameFilter, CameraName + "/Hawkeye recognition", nameof(RecipeNameFilter));
settings.RegisterSimple(this, () => this.ErrorsInSequenceAlarm, CameraName + "/Hawkeye recognition", nameof(ErrorsInSequenceAlarm));
settings.RegisterSimple(this, () => this.MinimumProcessingTime, CameraName + "/Recognition", "Minimum processing time (ms)");
settings.RegisterSimple(this, () => this.RecipeNameFilter, CameraName + "/Recognition", nameof(RecipeNameFilter));
settings.RegisterSimple(this, () => this.ErrorsInSequenceAlarm, CameraName + "/Recognition", nameof(ErrorsInSequenceAlarm));
}
}

View File

@@ -16,6 +16,10 @@ public class UIConfiguration: ISettings
public bool ProtectSettingsWithPassword { get; set; }=false;
public bool ShowPauseButton { get; set; }=false;
public bool ShowTestModeButton { get; set; } = false;
public List<ErrorShortName> ErrorShortNames { get; set; } = new List<ErrorShortName>();
public void RegisterSettings(InspectronSettings settings)
@@ -24,6 +28,8 @@ public class UIConfiguration: ISettings
settings.RegisterSimple(this, () => this.AdminPassword, "System", "Password");
settings.RegisterSimple(this, () => this.ProtectSettingsWithPassword, "System", nameof(ProtectSettingsWithPassword));
settings.RegisterSimple(this, () => this.ErrorShortNames, "Errors", "Error short names");
settings.RegisterSimple(this, () => this.ShowPauseButton, "System", nameof(ShowPauseButton));
settings.RegisterSimple(this, () => this.ShowTestModeButton, "System", nameof(ShowTestModeButton));
}

View File

@@ -100,7 +100,7 @@ public partial class ErrorPreviewVM:ObservableObject
[RelayCommand(CanExecute = nameof(LearnButtonVisible))]
public void Learn()
{
if (string.IsNullOrEmpty(_uiConfiguration.AdminPassword))
if (!string.IsNullOrEmpty(_uiConfiguration.AdminPassword))
{
var passwordOk= _passwordInputService.GetPassword()==_uiConfiguration.AdminPassword;
if (!passwordOk) return;

View File

@@ -16,6 +16,7 @@ namespace VisionBuilder.UI.Common
{
public SynchronizationContext? SynchronizationContext { get; set; }
private readonly UIConfiguration _uiConfiguration;
private readonly IRecipeSelectionDialogService _recipeSelectionDialogService;
private readonly IRecognitionControl _recognitionControl;
@@ -56,8 +57,10 @@ namespace VisionBuilder.UI.Common
public bool CanStop => IsRunning;
public bool CanStart => !IsRunning && SelectedRecipe!=null;
public bool CanPause => IsRunning && !IsPaused;
public bool CanResume => IsRunning && IsPaused;
public bool CanPause => (IsRunning && !IsPaused)&&_uiConfiguration.ShowPauseButton;
public bool CanResume => (IsRunning && IsPaused) && _uiConfiguration.ShowPauseButton;
public bool IsNotRunning => !IsRunning;
private bool _handlingEnabled = true;
@@ -79,8 +82,10 @@ namespace VisionBuilder.UI.Common
private RecipeData? _selectedRecipe;
public SingleCameraVM(CameraSettings cameraSettings, UIConfiguration uiConfiguration, IRecipeSelectionDialogService recipeSelectionDialogService, IRecognitionControl recognitionControl, IImagePreviewService imagePreviewService, ILearningTool learningTool, IPasswordInputService passwordInputService)
{
_uiConfiguration = uiConfiguration;
_recipeSelectionDialogService = recipeSelectionDialogService;
_recognitionControl = recognitionControl;
@@ -94,6 +99,8 @@ namespace VisionBuilder.UI.Common
recognitionControl.SessionStarted += StatisticsVm.Handle;
recognitionControl.SessionStarted += ErrorsVm.Handle;
}

View File

@@ -8,7 +8,7 @@ public static class ModuleExtensions
{
public static IChildKernel UseHawkeyeRecipes(this IChildKernel self, string cameraName)
{
self.Bind<HawkeyeRecognitionSettings, ISettings>().ToConstant(new HawkeyeRecognitionSettings(cameraName));
self.Bind<HawkeyeRecognitionSettings,BaseRecognitionControlSettings ,ISettings>().ToConstant(new HawkeyeRecognitionSettings(cameraName));
self.Bind<IRecognitionControl>().To<HawkeyeRecognitionControl>().InSingletonScope();
return self;
}

View File

@@ -25,11 +25,14 @@ namespace VisionBuilder.UI.Statistics
private string _currentRecipe;
private string _fileName;
private string _directoryPath;
private int _total = 0;
private const string BY_EVENTS_FILE_NAME = "statistics_by_events.csv";
private const string MONTHLY_FILE_NAME = "statistics_month.csv";
private const string EMPTY_DATE = "";
private const string EMPTY_TIME = "";
private const string DATE_FORMAT = "dd.MM.yyyy";
private const string TIME_FORMAT = "HH:mm:ss";
public bool DoNotDeleteTempFiles { get; set; } = false;
public VisionBuilderStatistics(VisionBuilderStatisticsSettings settings, IRecognitionControl recognitionControl, ILoadingService loadingService)
@@ -51,7 +54,7 @@ namespace VisionBuilder.UI.Statistics
_loadingService.StartLoading("Writing statistics for " + _settings.CameraName);
try
{
_csvStatistics.WriteLine([_startTime.ToString("d"), _startTime.ToString("T"), endedAt.ToString("d"), endedAt.ToString("T"), "", "", ""]);
_csvStatistics.WriteLine([_startTime.ToString(DATE_FORMAT), _startTime.ToString(TIME_FORMAT), endedAt.ToString(DATE_FORMAT), endedAt.ToString(TIME_FORMAT), "", "", ""]);
_csvStatistics.Dispose();
_csvStatistics = null;
@@ -67,19 +70,21 @@ namespace VisionBuilder.UI.Statistics
private void _recognitionControl_ImageProcessed(ImageProcessedEvent obj)
{
var sw = Stopwatch.StartNew();
_total++;
if (obj.HasError)
{
RecordBadImage(obj.ErrorNames[0]);
}
sw.Stop();
Console.WriteLine($"Recording statistics took {sw.ElapsedMilliseconds} ms");
Log.Debug($"Recording statistics took {sw.ElapsedMilliseconds} ms");
}
private void _recognitionControl_SessionStarted(SessionStartedEvent obj)
{
_startTime = obj.SessionStarted;
_total = 0;
_currentRecipe = obj.RecipeName;
_fileName = "tmp_"+Guid.NewGuid().ToString()+".csv";
_fileName = BY_EVENTS_FILE_NAME;
_directoryPath = PathSettingsUtils.ConvertVariables(_settings.PathTemplate, _startTime, _startTime,
obj.RecipeName, cameraName: _settings.CameraName);
_filePath = Path.Combine(_directoryPath, _fileName);
@@ -89,7 +94,7 @@ namespace VisionBuilder.UI.Statistics
Directory.CreateDirectory(Path.GetDirectoryName(_filePath));
}
_csvStatistics = new CsvStatistics(_filePath, headers);
_csvStatistics.WriteLine([_startTime.ToString("d"), _startTime.ToString("T")]); // session started
_csvStatistics.WriteLine([_startTime.ToString(DATE_FORMAT), _startTime.ToString(TIME_FORMAT)]); // session started
_defects = new ConcurrentDictionary<string, int>();
}
@@ -101,7 +106,7 @@ namespace VisionBuilder.UI.Statistics
_csvStatistics.WriteLine(
[_startTime.ToString("d"), _startTime.ToString("T"), EMPTY_DATE, EMPTY_TIME, DateTime.Now.ToString("T"), _currentRecipe, defectName, Metadata]
[_startTime.ToString(DATE_FORMAT), _startTime.ToString(TIME_FORMAT), EMPTY_DATE, EMPTY_TIME, DateTime.Now.ToString(TIME_FORMAT), _currentRecipe, defectName, Metadata]
);
@@ -119,16 +124,56 @@ namespace VisionBuilder.UI.Statistics
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"
]);
monthStatistics.WriteLine(
["Start", _startTime.ToString($"{DATE_FORMAT} {TIME_FORMAT}")]
);
monthStatistics.WriteLine([
_settings.MetadataColumnName, Metadata
]);
monthStatistics.WriteLine(
[_settings.ProductColumnName, _currentRecipe]
);
monthStatistics.WriteLine(
[_settings.ErrorColumnName, "Amount"]
);
foreach (var defect in _defects)
{
monthStatistics.WriteLine(
[startDate.ToString("d"), startTime.ToString("T"), endDate.ToString("d"), endTime.ToString("T"), _currentRecipe, defect.Key, defect.Value.ToString()]
[defect.Key, defect.Value.ToString()]
);
}
var totalDefects = 0;
foreach (var defect in _defects)
{
totalDefects += defect.Value;
}
monthStatistics.WriteLine([]);
monthStatistics.WriteLine(
["Total errors", totalDefects.ToString()]
);
monthStatistics.WriteLine(
["Total produced(including errors)", _total.ToString()]
);
var percentage = _total > 0 ? (double)totalDefects / _total * 100 : 0;
monthStatistics.WriteLine(
["Error rate", percentage.ToString("F2") + "%"]
);
monthStatistics.WriteLine(
[
"End", _endTime.ToString($"{DATE_FORMAT} {TIME_FORMAT}")
]);
monthStatistics.WriteLine([]);
monthStatistics.WriteLine([]);
monthStatistics.Dispose();

View File

@@ -135,14 +135,16 @@
// flowLayoutPanel1
//
flowLayoutPanel1.Anchor = AnchorStyles.Bottom | AnchorStyles.Left;
flowLayoutPanel1.AutoSize = true;
flowLayoutPanel1.AutoSizeMode = AutoSizeMode.GrowAndShrink;
flowLayoutPanel1.BackColor = Color.Transparent;
flowLayoutPanel1.Controls.Add(btnTestMode);
flowLayoutPanel1.Controls.Add(btnSettings);
flowLayoutPanel1.Controls.Add(btnMinimize);
flowLayoutPanel1.Controls.Add(btnExit);
flowLayoutPanel1.Location = new Point(664, 992);
flowLayoutPanel1.Location = new Point(664, 1002);
flowLayoutPanel1.Name = "flowLayoutPanel1";
flowLayoutPanel1.Size = new Size(728, 80);
flowLayoutPanel1.Size = new Size(728, 70);
flowLayoutPanel1.TabIndex = 12;
//
// lblVersion
@@ -170,6 +172,7 @@
Text = "Form1";
flowLayoutPanel1.ResumeLayout(false);
ResumeLayout(false);
PerformLayout();
}
#endregion

View File

@@ -29,6 +29,8 @@ namespace VisionBuilder.UI.Windows.Test
Load += Form1_Load;
_mainWindowVm.PropertyChanged += _mainWindowVm_PropertyChanged;
lblVersion.Text = $"v{Application.ProductVersion.Split('+')[0]}";
if(!_uiConfiguration.ShowTestModeButton)
flowLayoutPanel1.Controls.Remove(btnTestMode);
}

View File

@@ -8,7 +8,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationIcon>Inspectron icon-512.ico</ApplicationIcon>
<Deterministic>false</Deterministic>
<Version>1.0.5</Version>
<Version>1.0.6</Version>
</PropertyGroup>

View File

@@ -48,6 +48,7 @@ namespace VisionBuilder.UI.Windows.Components
btnPause.DataBindings.Add(nameof(btnPause.Visible), _singleCameraVm, nameof(_singleCameraVm.CanPause), true, DataSourceUpdateMode.OnPropertyChanged);
btnResume.DataBindings.Add(nameof(btnResume.Visible), _singleCameraVm, nameof(_singleCameraVm.CanResume), true, DataSourceUpdateMode.OnPropertyChanged);
}
public SingleCameraControl()