long candy ready to test
This commit is contained in:
@@ -3,6 +3,7 @@ using OpenCvSharp;
|
||||
using Serilog;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
using Newtonsoft.Json;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using CameraSettings = Inspectron.HawkEye.Protocol.CameraSettings;
|
||||
@@ -28,31 +29,54 @@ namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye
|
||||
IPAddress.Parse(_settings.Adapter));
|
||||
_client.ImageReceived += _client_ImageReceived;
|
||||
_client.SettingsReceived += _client_SettingsReceived;
|
||||
_client.CalibrationReceived += _client_CalibrationReceived;
|
||||
_client.SupportsCalibration = _settings.IsColor;
|
||||
_client.Connect();
|
||||
Log.Information("Connected to Hawkeye camera at {Adapter}", _settings.Adapter, 27001);
|
||||
|
||||
}
|
||||
|
||||
private void _client_CalibrationReceived(List<Dictionary<double, double>> obj)
|
||||
{
|
||||
Log.Information("Calibtating...");
|
||||
_bayerFilter = new OpenCVBayerProcessor();
|
||||
_bayerFilter.SetCalibration(obj);
|
||||
}
|
||||
|
||||
private void _client_SettingsReceived(CameraSettings obj)
|
||||
{
|
||||
ApplySettings(obj);
|
||||
}
|
||||
public void ApplySettings(CameraSettings obj)
|
||||
{
|
||||
_cameraSettings = UICameraSettings.LoadSettingsLocal(_settings.SettingsFile).ToCameraSettings();
|
||||
_client.ApplySettings(_cameraSettings);
|
||||
_cameraSettings= obj;
|
||||
if (File.Exists(_settings?.SettingsFile))
|
||||
{
|
||||
_cameraSettings = UICameraSettings.LoadSettingsLocal(_settings.SettingsFile).ToCameraSettings();
|
||||
_client.ApplySettings(_cameraSettings);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public Task<Mat> GetImage(CancellationToken token)
|
||||
{
|
||||
|
||||
_lastImage = null;
|
||||
if (_imageAquisitionTaskSource != null && !_imageAquisitionTaskSource.Task.IsCanceled)
|
||||
if (_imageAquisitionTaskSource != null && !_imageAquisitionTaskSource.Task.IsCanceled && !_imageAquisitionTaskSource.Task.IsCompleted)
|
||||
{
|
||||
_imageAquisitionTaskSource.SetCanceled(CancellationToken.None);
|
||||
_imageAquisitionTaskSource = null;
|
||||
|
||||
}
|
||||
_imageAquisitionTaskSource= new TaskCompletionSource<Mat>();
|
||||
if (token.CanBeCanceled)
|
||||
{
|
||||
token.Register(() =>
|
||||
{
|
||||
_imageAquisitionTaskSource.TrySetCanceled(token);
|
||||
});
|
||||
}
|
||||
_client.Trigger();
|
||||
return _imageAquisitionTaskSource.Task;
|
||||
|
||||
@@ -61,31 +85,52 @@ namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye
|
||||
private TaskCompletionSource<Mat>? _imageAquisitionTaskSource;
|
||||
private Mat _lastImage;
|
||||
byte[] _flipBuffer = new byte[2000 * 2000];
|
||||
private OpenCVBayerProcessor _bayerFilter;
|
||||
|
||||
private void _client_ImageReceived(byte[] obj)
|
||||
{
|
||||
Log.Debug("Got image on {adapter}", _settings.Adapter);
|
||||
FlipLines(obj, _flipBuffer);
|
||||
obj = _flipBuffer;
|
||||
var pinnedArray = GCHandle.Alloc(obj, GCHandleType.Pinned);
|
||||
var pointer = pinnedArray.AddrOfPinnedObject();
|
||||
|
||||
Mat image = new Mat(_cameraSettings.ImageSettings.Lines, _cameraSettings.ImageSettings.SensorWidth,
|
||||
MatType.CV_8UC1, pointer);
|
||||
|
||||
var xCrop = _cameraSettings.ImageSettings.SensorWidth - _cameraSettings.ImageWidth - _cameraSettings.OffsetX;
|
||||
|
||||
// crop the image with opencv
|
||||
_lastImage = image[0, _cameraSettings.ImageSettings.Lines, xCrop,
|
||||
xCrop + _cameraSettings.ImageWidth].Clone();
|
||||
|
||||
if (_cameraSettings.BayerFilter)
|
||||
try
|
||||
{
|
||||
_lastImage = BayerFilter(_lastImage);
|
||||
Log.Debug("Got image on {adapter}", _settings.Adapter);
|
||||
//FlipLines(obj, _flipBuffer);
|
||||
|
||||
var pinnedArray = GCHandle.Alloc(obj, GCHandleType.Pinned);
|
||||
var pointer = pinnedArray.AddrOfPinnedObject();
|
||||
|
||||
|
||||
Mat image = new Mat(_cameraSettings.ImageSettings.Lines, _cameraSettings.ImageSettings.SensorWidth,
|
||||
MatType.CV_8UC1, pointer);
|
||||
|
||||
|
||||
|
||||
var xCrop = _cameraSettings.ImageSettings.SensorWidth - _cameraSettings.ImageWidth -
|
||||
_cameraSettings.OffsetX;
|
||||
|
||||
// crop the image with opencv
|
||||
_lastImage = image[0, _cameraSettings.ImageSettings.Lines, xCrop,
|
||||
xCrop + _cameraSettings.ImageWidth].Clone();
|
||||
|
||||
|
||||
|
||||
|
||||
if (_cameraSettings.BayerFilter)
|
||||
{
|
||||
_lastImage = _bayerFilter.ProcessBayerImageWithChannelControl(_lastImage);
|
||||
}
|
||||
|
||||
// rotate 90 degrees CCW
|
||||
Cv2.Rotate(_lastImage, _lastImage, RotateFlags.Rotate90Counterclockwise);
|
||||
|
||||
//_lastImage = _lastImage.CvtColor(ColorConversionCodes.BGR2RGB);
|
||||
|
||||
_imageAquisitionTaskSource!.SetResult(_lastImage);
|
||||
|
||||
pinnedArray.Free();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error processing image from Hawkeye camera at {Adapter}", _settings.Adapter);
|
||||
}
|
||||
|
||||
_imageAquisitionTaskSource!.SetResult(_lastImage);
|
||||
|
||||
pinnedArray.Free();
|
||||
}
|
||||
|
||||
Mat BayerFilter(Mat image)
|
||||
@@ -96,8 +141,7 @@ namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye
|
||||
throw new ArgumentException("Input image is null or empty.", nameof(image));
|
||||
|
||||
Mat bgrImage = new Mat();
|
||||
// Use OpenCV's demosaicing function for Bayer BG pattern
|
||||
Cv2.CvtColor(image, bgrImage, ColorConversionCodes.BayerBG2BGR);
|
||||
Cv2.CvtColor(image, bgrImage, ColorConversionCodes.BayerRG2BGR);
|
||||
|
||||
return bgrImage;
|
||||
}
|
||||
@@ -127,7 +171,7 @@ namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye
|
||||
|
||||
public void InitializeModule()
|
||||
{
|
||||
|
||||
Open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,13 @@ public class HawkeyeSettings(string CameraName): ISettings
|
||||
[File("*.jcnf")]
|
||||
public string SettingsFile { get; set; }
|
||||
|
||||
public bool IsColor { get; set; }=true;
|
||||
|
||||
public void RegisterSettings(InspectronSettings settings)
|
||||
{
|
||||
settings.RegisterSimple(this, () => Adapter, $"{CameraName}/Sources/Hawkeye", nameof(Adapter));
|
||||
settings.RegisterSimple(this, () => IsColor, $"{CameraName}/Sources/Hawkeye", nameof(IsColor));
|
||||
|
||||
settings.RegisterSimple(this, () => SettingsFile, $"{CameraName}/Sources/Hawkeye", nameof(SettingsFile));
|
||||
}
|
||||
|
||||
|
||||
124
Hawkeye.VisionBuilder.UI.Sources.Hawkeye/OpenCVBayerFilter.cs
Normal file
124
Hawkeye.VisionBuilder.UI.Sources.Hawkeye/OpenCVBayerFilter.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using NLog.Filters;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye;
|
||||
|
||||
public class OpenCVBayerProcessor
|
||||
{
|
||||
private SplineInterpolator GreenInterpolation { get; set; }
|
||||
private SplineInterpolator RedInterpolation { get; set; }
|
||||
private SplineInterpolator BlueInterpolation { get; set; }
|
||||
|
||||
// Cached lookup tables
|
||||
private byte[] redLut;
|
||||
private byte[] greenLut;
|
||||
private byte[] blueLut;
|
||||
private Mat _lookupTable;
|
||||
|
||||
public OpenCVBayerProcessor()
|
||||
{
|
||||
// Initialize with default linear interpolation (same as your defaults)
|
||||
GreenInterpolation = new SplineInterpolator(new Dictionary<double, double>()
|
||||
{{0,0}, { 100, 100 }, { 255,255}});
|
||||
RedInterpolation = new SplineInterpolator(new Dictionary<double, double>()
|
||||
{{0,0}, { 100, 100 }, { 255,255}});
|
||||
BlueInterpolation = new SplineInterpolator(new Dictionary<double, double>()
|
||||
{{0,0}, { 100, 100 }, { 255,255}});
|
||||
|
||||
UpdateLookupTables();
|
||||
}
|
||||
|
||||
public void SetInterpolators(SplineInterpolator red, SplineInterpolator green, SplineInterpolator blue)
|
||||
{
|
||||
RedInterpolation = red;
|
||||
GreenInterpolation = green;
|
||||
BlueInterpolation = blue;
|
||||
UpdateLookupTables();
|
||||
}
|
||||
|
||||
public void SetCalibration(List<Dictionary<double, double>> calibration)
|
||||
{
|
||||
RedInterpolation = new SplineInterpolator(calibration[0]);
|
||||
GreenInterpolation = new SplineInterpolator(calibration[1]);
|
||||
BlueInterpolation = new SplineInterpolator(calibration[2]);
|
||||
UpdateLookupTables();
|
||||
}
|
||||
|
||||
private void UpdateLookupTables()
|
||||
{
|
||||
redLut = Enumerable.Range(0, 256).Select(x => (byte)RedInterpolation.GetValue(x)).ToArray();
|
||||
greenLut = Enumerable.Range(0, 256).Select(x => (byte)GreenInterpolation.GetValue(x)).ToArray();
|
||||
blueLut = Enumerable.Range(0, 256).Select(x => (byte)BlueInterpolation.GetValue(x)).ToArray();
|
||||
|
||||
// Create lookup table for all three channels
|
||||
_lookupTable = new Mat(1, 256, MatType.CV_8UC3);
|
||||
|
||||
// Use the Mat indexer for safe access
|
||||
var indexer = _lookupTable.GetGenericIndexer<Vec3b>();
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
indexer[0, i] = new Vec3b(blueLut[i], greenLut[i], redLut[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public Mat ProcessBayerImage(Mat grayImage)
|
||||
{
|
||||
// Step 1: Apply Bayer demosaicing with RG pattern (matching your pattern)
|
||||
Mat colorImage = new Mat();
|
||||
Cv2.CvtColor(grayImage, colorImage, ColorConversionCodes.BayerBG2BGR);
|
||||
|
||||
// Step 2: Apply color interpolation/correction using lookup tables
|
||||
Mat correctedImage = ApplyColorCorrection(colorImage);
|
||||
|
||||
return correctedImage;
|
||||
}
|
||||
|
||||
private Mat ApplyColorCorrection(Mat colorImage)
|
||||
{
|
||||
|
||||
|
||||
// Apply the lookup table
|
||||
Mat result = new Mat();
|
||||
Cv2.LUT(colorImage, _lookupTable, result);
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Alternative: Apply correction per channel if you need more control
|
||||
public Mat ProcessBayerImageWithChannelControl(Mat grayImage)
|
||||
{
|
||||
// Step 1: Demosaic
|
||||
Mat colorImage = new Mat();
|
||||
Cv2.CvtColor(grayImage, colorImage, ColorConversionCodes.BayerBG2BGR);
|
||||
|
||||
// Step 2: Split channels
|
||||
Mat[] channels = Cv2.Split(colorImage);
|
||||
|
||||
// Step 3: Apply individual LUTs to each channel
|
||||
Mat blueCorrected = new Mat();
|
||||
Mat greenCorrected = new Mat();
|
||||
Mat redCorrected = new Mat();
|
||||
|
||||
Mat blueLutMat = new Mat(1, 256, MatType.CV_8U, blueLut);
|
||||
Mat greenLutMat = new Mat(1, 256, MatType.CV_8U, greenLut);
|
||||
Mat redLutMat = new Mat(1, 256, MatType.CV_8U, redLut);
|
||||
|
||||
Cv2.LUT(channels[0], blueLutMat, blueCorrected);
|
||||
Cv2.LUT(channels[1], greenLutMat, greenCorrected);
|
||||
Cv2.LUT(channels[2], redLutMat, redCorrected);
|
||||
|
||||
// Step 4: Merge channels back
|
||||
Mat result = new Mat();
|
||||
Cv2.Merge(new Mat[] { blueCorrected, greenCorrected, redCorrected }, result);
|
||||
|
||||
// Cleanup
|
||||
foreach (var channel in channels) channel.Dispose();
|
||||
blueCorrected.Dispose();
|
||||
greenCorrected.Dispose();
|
||||
redCorrected.Dispose();
|
||||
colorImage.Dispose();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
138
Hawkeye.VisionBuilder.UI.Sources.Hawkeye/SplineInterpolator.cs
Normal file
138
Hawkeye.VisionBuilder.UI.Sources.Hawkeye/SplineInterpolator.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye;
|
||||
|
||||
public class SplineInterpolator
|
||||
{
|
||||
private readonly Dictionary<double, double> _nodes;
|
||||
private readonly double[] _keys;
|
||||
|
||||
private readonly double[] _values;
|
||||
|
||||
private readonly double[] _h;
|
||||
|
||||
private readonly double[] _a;
|
||||
|
||||
/// <summary>
|
||||
/// Class constructor.
|
||||
/// </summary>
|
||||
/// <param name="nodes">Collection of known points for further interpolation.
|
||||
/// Should contain at least two items.</param>
|
||||
public SplineInterpolator(Dictionary<double, double> nodes)
|
||||
{
|
||||
if (nodes == null)
|
||||
{
|
||||
throw new ArgumentNullException("nodes");
|
||||
}
|
||||
|
||||
_nodes = nodes;
|
||||
|
||||
var n = nodes.Count;
|
||||
|
||||
if (n < 2)
|
||||
{
|
||||
throw new ArgumentException("At least two point required for interpolation.");
|
||||
}
|
||||
|
||||
_keys = nodes.Keys.ToArray();
|
||||
_values = nodes.Values.ToArray();
|
||||
_a = new double[n];
|
||||
_h = new double[n];
|
||||
|
||||
for (int i = 1; i < n; i++)
|
||||
{
|
||||
_h[i] = _keys[i] - _keys[i - 1];
|
||||
}
|
||||
|
||||
if (n > 2)
|
||||
{
|
||||
var sub = new double[n - 1];
|
||||
var diag = new double[n - 1];
|
||||
var sup = new double[n - 1];
|
||||
|
||||
for (int i = 1; i <= n - 2; i++)
|
||||
{
|
||||
diag[i] = (_h[i] + _h[i + 1]) / 3;
|
||||
sup[i] = _h[i + 1] / 6;
|
||||
sub[i] = _h[i] / 6;
|
||||
_a[i] = (_values[i + 1] - _values[i]) / _h[i + 1] - (_values[i] - _values[i - 1]) / _h[i];
|
||||
}
|
||||
|
||||
SolveTridiag(sub, diag, sup, ref _a, n - 2);
|
||||
}
|
||||
}
|
||||
|
||||
public double[] Keys => _keys;
|
||||
|
||||
public double[] Values => _values;
|
||||
|
||||
public Dictionary<double, double> Nodes => _nodes;
|
||||
|
||||
/// <summary>
|
||||
/// Gets interpolated value for specified argument.
|
||||
/// </summary>
|
||||
/// <param name="key">Argument value for interpolation. Must be within
|
||||
/// the interval bounded by lowest ang highest <see cref="_keys"/> values.</param>
|
||||
public double GetValue(double key)
|
||||
{
|
||||
int gap = 0;
|
||||
var previous = double.MinValue;
|
||||
|
||||
|
||||
if (key > _keys.Max()) key = _keys.Max();
|
||||
if (key < _keys.Min()) key = _keys.Min();
|
||||
|
||||
for (int i = 0; i < _keys.Length; i++)
|
||||
{
|
||||
if (Math.Abs(_keys[i] - key) < 0.001)
|
||||
{
|
||||
return _values[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// At the end of this iteration, "gap" will contain the index of the interval
|
||||
// between two known values, which contains the unknown z, and "previous" will
|
||||
// contain the biggest z value among the known samples, left of the unknown z
|
||||
for (int i = 0; i < _keys.Length; i++)
|
||||
{
|
||||
if (_keys[i] < key && _keys[i] > previous)
|
||||
{
|
||||
previous = _keys[i];
|
||||
gap = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
var x1 = key - previous;
|
||||
var x2 = _h[gap] - x1;
|
||||
|
||||
var res = ((-_a[gap - 1] / 6 * (x2 + _h[gap]) * x1 + _values[gap - 1]) * x2 +
|
||||
(-_a[gap] / 6 * (x1 + _h[gap]) * x2 + _values[gap]) * x1) / _h[gap];
|
||||
if (res > 255) res = 255;
|
||||
if (res < 0) res = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Solve linear system with tridiagonal n*n matrix "a"
|
||||
/// using Gaussian elimination without pivoting.
|
||||
/// </summary>
|
||||
private static void SolveTridiag(double[] sub, double[] diag, double[] sup, ref double[] b, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 2; i <= n; i++)
|
||||
{
|
||||
sub[i] = sub[i] / diag[i - 1];
|
||||
diag[i] = diag[i] - sub[i] * sup[i - 1];
|
||||
b[i] = b[i] - sub[i] * b[i - 1];
|
||||
}
|
||||
|
||||
b[n] = b[n] / diag[n];
|
||||
|
||||
for (i = n - 1; i >= 1; i--)
|
||||
{
|
||||
b[i] = (b[i] - sup[i] * b[i + 1]) / diag[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
using Hawkeye.VisionBuilder.Workflow.Datatypes;
|
||||
using Hawkeye.VisionBuilder.Workflow.Operations.Attributes;
|
||||
using ILGPU.Runtime.Cuda;
|
||||
using Microsoft.ML.OnnxRuntime;
|
||||
using Microsoft.ML.OnnxRuntime.Tensors;
|
||||
using Microsoft.Scripting.Runtime;
|
||||
using OpenCvSharp;
|
||||
namespace Hawkeye.VisionBuilder.Workflow.Operations.AI;
|
||||
|
||||
[Category("AI")]
|
||||
public class OnnxModelOperation:BaseOperation
|
||||
{
|
||||
|
||||
private FilePath _modelFilePath = new FilePath(){Format = "ONNX file(*.onnx)|*.onnx" };
|
||||
|
||||
|
||||
public FilePath ModelFilePath
|
||||
{
|
||||
get => _modelFilePath;
|
||||
set
|
||||
{
|
||||
_modelFilePath = value;
|
||||
_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool _initialized = false;
|
||||
private InferenceSession _AIsession;
|
||||
private string _inputName;
|
||||
private int[] _dimensions;
|
||||
|
||||
protected override void InterpretInternal(Context context)
|
||||
{
|
||||
if (!CheckImageExists(context)) return;
|
||||
if (!CheckColorful(context)) return;
|
||||
|
||||
if (!_initialized)
|
||||
{
|
||||
if (!File.Exists(ModelFilePath?.Path))
|
||||
{
|
||||
this.SetError($"Model file \"{ModelFilePath?.Path}\" not found");
|
||||
return;
|
||||
}
|
||||
|
||||
var opts = new SessionOptions();
|
||||
//opts.AppendExecutionProvider_DML(0);
|
||||
_AIsession = new InferenceSession(ModelFilePath?.Path, opts);
|
||||
_inputName = _AIsession.InputMetadata.First().Key;
|
||||
_dimensions = _AIsession.InputMetadata.First().Value.Dimensions;
|
||||
foreach (var input in _AIsession.InputMetadata)
|
||||
{
|
||||
Console.WriteLine($"Name: {input.Key}");
|
||||
Console.WriteLine($" Type: {input.Value.ElementType}");
|
||||
Console.WriteLine($" Shape: [{string.Join(", ", input.Value.Dimensions)}]");
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
var currentImage = context.ActiveImage;
|
||||
|
||||
var rightColor = currentImage.ImageData.CvtColor(ColorConversionCodes.BGR2RGB);
|
||||
|
||||
// convert to float32
|
||||
var floatImage = new Mat();
|
||||
rightColor.ConvertTo(floatImage, MatType.CV_32FC3, 1.0 / 255);
|
||||
// to array
|
||||
|
||||
var width = _dimensions[1];
|
||||
var height = _dimensions[2];
|
||||
|
||||
// check if need resize
|
||||
if (floatImage.Width != width || floatImage.Height != height)
|
||||
Cv2.Resize(floatImage, floatImage, new OpenCvSharp.Size(width, height));
|
||||
|
||||
|
||||
float[] data = new float[3 * width * height];
|
||||
int idx = 0;
|
||||
var rows = floatImage.Rows;
|
||||
var cols = floatImage.Cols;
|
||||
for (int y = 0; y < rows; y++)
|
||||
{
|
||||
for (int x = 0; x < cols; x++)
|
||||
{
|
||||
Vec3f pixel = floatImage.At<Vec3f>(y, x);
|
||||
for (int c = 0; c < 3; c++) // channels
|
||||
{
|
||||
data[idx++] = pixel[c]; // width*height*channel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var inputTensor = new DenseTensor<float>(
|
||||
data,
|
||||
new int[] { 1, floatImage.Width, floatImage.Height, 3 }
|
||||
);
|
||||
|
||||
var inputs = new List<NamedOnnxValue> {
|
||||
NamedOnnxValue.CreateFromTensor("input", inputTensor)
|
||||
};
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
using var results = _AIsession.Run(inputs);
|
||||
sw.Stop();
|
||||
Console.WriteLine($"Inference time: {sw.ElapsedMilliseconds} ms");
|
||||
var output = results.First().AsEnumerable<float>().ToArray();
|
||||
|
||||
// convert output to Mat and normalize to 0-255
|
||||
var outputMat = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_32FC1);
|
||||
outputMat.SetArray<float>(output);
|
||||
|
||||
outputMat.ConvertTo(outputMat, MatType.CV_8UC1, 255);
|
||||
|
||||
// resize back to original size
|
||||
Cv2.Resize(outputMat, outputMat, new OpenCvSharp.Size(currentImage.ImageData.Width, currentImage.ImageData.Height));
|
||||
|
||||
|
||||
context.ActiveImage = new HawkeyeImage()
|
||||
{
|
||||
ImageData = outputMat
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public override void Save(Dictionary<string, object> dict)
|
||||
{
|
||||
base.Save(dict);
|
||||
dict[nameof(ModelFilePath)] = ModelFilePath?.Path;
|
||||
}
|
||||
|
||||
public override void Load(Dictionary<string, object> dict)
|
||||
{
|
||||
base.Load(dict);
|
||||
if (dict.ContainsKey(nameof(ModelFilePath)))
|
||||
ModelFilePath.Path = dict[nameof(ModelFilePath)].ToString();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoCompleteMenu-ScintillaNET" Version="2.1.0" />
|
||||
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.ML.OnnxRuntime.Gpu" Version="1.22.1" />
|
||||
<PackageReference Include="OpenCvSharp4" Version="4.6.0.20220608" />
|
||||
<PackageReference Include="OpenCvSharp4.Extensions" Version="4.6.0.20220608" />
|
||||
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.6.0.20220608" />
|
||||
|
||||
@@ -264,6 +264,7 @@ namespace Hawkeye.VisionBuilder
|
||||
var ext = Path.GetExtension(dialog.FileName);
|
||||
if (ext == ".jhrcp")
|
||||
{
|
||||
_workflowList.RecipeImage = _workflowList.Context.LastCameraImage?.ImageData;
|
||||
_workflowList.SaveJSON(dialog.FileName);
|
||||
}
|
||||
else
|
||||
|
||||
28
Plugins/CandyboxPlugin/CandyboxPlugin.csproj
Normal file
28
Plugins/CandyboxPlugin/CandyboxPlugin.csproj
Normal file
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutDir>..\..\VisionBuilder.UI.Windows.Test\bin\Debug\Data\Plugins\CandyboxPlugin</OutDir>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.6.0.20220608" >
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</PackageReference>
|
||||
<ProjectReference Include="..\..\framework\Inspectron.Settings\Inspectron.Settings.csproj">
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj">
|
||||
<Private>false</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
7
Plugins/CandyboxPlugin/Class1.cs
Normal file
7
Plugins/CandyboxPlugin/Class1.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace CandyboxPlugin
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<OutDir>..\..\..\VisionBuilder.UI.Windows.Test\bin\Debug\Data\Plugins\PackstrasseBarcodeReader</OutDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.Ports" Version="7.0.0">
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\framework\Inspectron.Settings\Inspectron.Settings.csproj">
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj">
|
||||
<Private>False</Private>
|
||||
<ExcludeAssets>runtime</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.IO.Ports;
|
||||
using System.Security.AccessControl;
|
||||
using System.Text;
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
|
||||
namespace PackstrasseBarcodeReader
|
||||
{
|
||||
public class PackstrasseBarcodeReaderModule : IVisionBuilderModule
|
||||
{
|
||||
private readonly PackstrasseBarcodeReaderSettings _settings;
|
||||
private readonly IRecognitionControl _recognitionControl;
|
||||
private SerialPort _port;
|
||||
|
||||
|
||||
public PackstrasseBarcodeReaderModule(PackstrasseBarcodeReaderSettings settings, IRecognitionControl recognitionControl)
|
||||
{
|
||||
_settings = settings;
|
||||
_recognitionControl = recognitionControl;
|
||||
}
|
||||
|
||||
private void _port_DataReceived(object sender, SerialDataReceivedEventArgs e)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
byte[] buffer = new byte[_port.BytesToRead];
|
||||
_port.Read(buffer, 0, _port.BytesToRead);
|
||||
var data = Encoding.ASCII.GetString(buffer).Trim();
|
||||
var recipes = _recognitionControl.GetRecipesData();
|
||||
|
||||
string searchString = data;
|
||||
if (_settings.BarcodeRecipeMappings.Any(x=>x.Barcode==searchString))
|
||||
{
|
||||
searchString = _settings.BarcodeRecipeMappings.First(x => x.Barcode == searchString).RecipeName;
|
||||
}
|
||||
|
||||
var existingRecipe=recipes.FirstOrDefault(x => x.RecipeName == searchString);
|
||||
if (existingRecipe == null)
|
||||
{
|
||||
Log.Warning("No matching recipe found for data: {Data}", data);
|
||||
return;
|
||||
}
|
||||
|
||||
_recognitionControl.SetRecipe(existingRecipe);
|
||||
}
|
||||
|
||||
|
||||
public void InitializeModule()
|
||||
{
|
||||
try
|
||||
{
|
||||
_port = new SerialPort(_settings.ComPort, 9600);
|
||||
_port.Open();
|
||||
_port.DataReceived += _port_DataReceived;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Warning(e, "Failed to open com-port");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using Inspectron.Settings;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using VisionBuilder.UI.Common;
|
||||
|
||||
namespace PackstrasseBarcodeReader;
|
||||
|
||||
public class PackstrasseBarcodeReaderSettings(string CameraName):ISettings
|
||||
{
|
||||
|
||||
public string ComPort { get; set; }="COM3";
|
||||
|
||||
public List<BarcodeRecipeMapping> BarcodeRecipeMappings { get; set; } = new List<BarcodeRecipeMapping>();
|
||||
|
||||
public void RegisterSettings(InspectronSettings settings)
|
||||
{
|
||||
settings.RegisterSimple(this, () => ComPort, CameraName+"/Barcode reader", nameof(ComPort));
|
||||
settings.RegisterSimple(this, () => BarcodeRecipeMappings, CameraName+"/Barcode reader", nameof(BarcodeRecipeMappings));
|
||||
}
|
||||
}
|
||||
|
||||
public class BarcodeRecipeMapping
|
||||
{
|
||||
public string Barcode { get; set; }
|
||||
public string RecipeName { get; set; }
|
||||
}
|
||||
|
||||
public class ListBarcodeRecipeMappingConverter: ITypeConverter
|
||||
{
|
||||
public object ConvertFrom(object value)
|
||||
{
|
||||
if (value is string json)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<List<BarcodeRecipeMapping>>(json, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true,
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
});
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to deserialize BarcodeRecipeMapping list from JSON.");
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException("Value must be a JSON string.");
|
||||
}
|
||||
|
||||
public object ConvertTo(object value, Type destinationType)
|
||||
{
|
||||
if (value is List<BarcodeRecipeMapping> list && destinationType == typeof(string))
|
||||
{
|
||||
return JsonSerializer.Serialize(list, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = false,
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
});
|
||||
}
|
||||
throw new InvalidOperationException("Value must be a List<BarcodeRecipeMapping> and destinationType must be string.");
|
||||
|
||||
}
|
||||
}
|
||||
21
Plugins/Pralinen/PackstrasseBarcodeReader/Plugin.cs
Normal file
21
Plugins/Pralinen/PackstrasseBarcodeReader/Plugin.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Inspectron.Settings;
|
||||
using Ninject;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Plugins;
|
||||
|
||||
namespace PackstrasseBarcodeReader;
|
||||
|
||||
public class Plugin : IPlugin
|
||||
{
|
||||
public void RegisterGlobalModules(IKernel kernel)
|
||||
{
|
||||
TypeConverterRegistry.Register<List<BarcodeRecipeMapping>>(new ListBarcodeRecipeMappingConverter());
|
||||
}
|
||||
|
||||
public void RegisterCameraModules(IKernel kernel, string cameraName)
|
||||
{
|
||||
kernel.Bind<PackstrasseBarcodeReaderSettings, ISettings>()
|
||||
.ToConstant(new PackstrasseBarcodeReaderSettings(cameraName));
|
||||
kernel.RegisterModule<PackstrasseBarcodeReaderModule>();
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<OutDir>D:\Inspectron\Hawkeye\code\VisionBuilder5\VisionBuilder.UI\VisionBuilder.UI.Windows.Test\bin\Debug\Data\Plugins\B24SiemensPlugin</OutDir>
|
||||
<OutDir>..\..\..\VisionBuilder.UI.Windows.Test\bin\Debug\Data\Plugins\B24SiemensPlugin</OutDir>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\framework\Inspectron.Settings\Inspectron.Settings.csproj">
|
||||
|
||||
@@ -14,6 +14,7 @@ public static class ModuleExtensions
|
||||
{
|
||||
self.Bind<CameraSettings, ISettings>().ToConstant(new CameraSettings(cameraName));
|
||||
self.Bind<EmulationSettings, ISettings>().ToConstant(new EmulationSettings(cameraName));
|
||||
self.Bind<HawkeyeSettings, ISettings>().ToConstant(new HawkeyeSettings(cameraName));
|
||||
self.Bind<IDSImageSourceSettings, ISettings>().ToConstant(new IDSImageSourceSettings(cameraName));
|
||||
self.Bind<SingleCameraVM>().ToSelf().InSingletonScope();
|
||||
return self;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace VisionBuilder.UI.Common.Services;
|
||||
|
||||
public interface IPasswordInputService
|
||||
{
|
||||
string GetPassword();
|
||||
}
|
||||
@@ -14,13 +14,16 @@ public class UIConfiguration: ISettings
|
||||
|
||||
public string AdminPassword { get; set; } = "";
|
||||
|
||||
public bool ProtectSettingsWithPassword { get; set; }=false;
|
||||
|
||||
public List<ErrorShortName> ErrorShortNames { get; set; } = new List<ErrorShortName>();
|
||||
|
||||
public void RegisterSettings(InspectronSettings settings)
|
||||
{
|
||||
settings.RegisterSimple(this, () => this.MaxErrorCount, "Errors", "Max errors count", "UI");
|
||||
settings.RegisterSimple(this, () => this.AdminPassword, "System", "Password", "UI");
|
||||
settings.RegisterSimple(this, () => this.ErrorShortNames, "Errors", "Error short names", "UI");
|
||||
settings.RegisterSimple(this, () => this.MaxErrorCount, "Errors", "Max errors count");
|
||||
settings.RegisterSimple(this, () => this.AdminPassword, "System", "Password");
|
||||
settings.RegisterSimple(this, () => this.ProtectSettingsWithPassword, "System", nameof(ProtectSettingsWithPassword));
|
||||
settings.RegisterSimple(this, () => this.ErrorShortNames, "Errors", "Error short names");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Hawkeye.VisionBuilder.UI.RecipeConverter\Hawkeye.VisionBuilder.UI.RecipeConverter.csproj" />
|
||||
<ProjectReference Include="..\VisionBuilder.UI.IOCommander\VisionBuilder.UI.IOCommander.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
13
VisionBuilder.UI.Recipes.HawkeyeRecipe/EmptyImageSource.cs
Normal file
13
VisionBuilder.UI.Recipes.HawkeyeRecipe/EmptyImageSource.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using OpenCvSharp;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
|
||||
namespace VisionBuilder.UI.Recipes.HawkeyeRecipe;
|
||||
|
||||
public class EmptyImageSource: IImageSource
|
||||
{
|
||||
|
||||
public Task<Mat> GetImage(CancellationToken token)
|
||||
{
|
||||
return Task.FromResult(new Mat(480, 640, MatType.CV_8UC3, Scalar.Black));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
using System.Diagnostics;
|
||||
using System.CodeDom;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Hawkeye.VisionBuilder.Workflow;
|
||||
using Hawkeye.VisionBuilder.Workflow.Datatypes;
|
||||
using Hawkeye.VisionBuilder.Workflow.Operations;
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.ViewModel.Classes;
|
||||
@@ -86,6 +89,25 @@ namespace VisionBuilder.UI.Recipes.HawkeyeRecipe
|
||||
_startTime = DateTime.Now;
|
||||
IsRunning = true;
|
||||
_cts = new CancellationTokenSource();
|
||||
|
||||
|
||||
Log.Information($"Warming up");
|
||||
try
|
||||
{
|
||||
var swWarmup = Stopwatch.StartNew();
|
||||
// warmup run
|
||||
_workflow.ImageSource = new EmptyImageSource();
|
||||
_workflow.Context = new Context();
|
||||
_workflow.Context.CancellationToken = CancellationToken.None;
|
||||
_workflow.Execute(); // Warmup run
|
||||
swWarmup.Stop();
|
||||
Log.Information($"Warmup run took {swWarmup.ElapsedMilliseconds} ms");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error during warmup run");
|
||||
}
|
||||
|
||||
_loopTask = Task.Run(() => Loop(_cts.Token), _cts.Token);
|
||||
|
||||
SessionStarted?.Invoke(new SessionStartedEvent
|
||||
@@ -174,11 +196,19 @@ namespace VisionBuilder.UI.Recipes.HawkeyeRecipe
|
||||
var annotated = _workflow.Context.ActiveImage.ImageData.Clone();
|
||||
var canvas = new OpenCVCanvas(annotated);
|
||||
_workflow.Context.GraphicsElements.ForEach(x => x.Draw(canvas));
|
||||
|
||||
TimeSpan cameraTime = TimeSpan.FromMilliseconds(0);
|
||||
var cameraOperation = _workflow.Operations.FirstOrDefault(x => x is GetImageOperation);
|
||||
if (cameraOperation != null)
|
||||
{
|
||||
cameraTime=cameraOperation.ExecutionTime;
|
||||
}
|
||||
|
||||
var processingResult = new ImageProcessedEvent()
|
||||
{
|
||||
SessionStart = _startTime,
|
||||
RecipeName = _currentRecipe.RecipeName,
|
||||
AnalysisTime = sw.Elapsed,
|
||||
AnalysisTime = sw.Elapsed- cameraTime,
|
||||
ErrorNames = errorNames.ToList(),
|
||||
HasError = errorNames.Length > 0,
|
||||
ImageSource = _recognitionConfiguration.CameraName,
|
||||
@@ -186,7 +216,10 @@ namespace VisionBuilder.UI.Recipes.HawkeyeRecipe
|
||||
ImageAnalysis = annotated
|
||||
};
|
||||
|
||||
var swProcessed = Stopwatch.StartNew();
|
||||
ImageProcessed(processingResult);
|
||||
swProcessed.Stop();
|
||||
Log.Information($"ImageProcessed event handled in {swProcessed.ElapsedMilliseconds} ms");
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Inspectron.Fastbuffer.Filesystems;
|
||||
using Lindt.Colorballs.Duo.Utils;
|
||||
using OpenCvSharp;
|
||||
using Serilog;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Commands;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
@@ -12,26 +13,58 @@ public class VisionBuilderRingbuffer:IVisionBuilderModule
|
||||
{
|
||||
private readonly VisionBuilderRingbufferSettings _settings;
|
||||
private readonly IRecognitionControl _recognitionControl;
|
||||
private readonly ILoadingService _loadingService;
|
||||
|
||||
public VisionBuilderRingbuffer(VisionBuilderRingbufferSettings settings, IRecognitionControl recognitionControl)
|
||||
public VisionBuilderRingbuffer(VisionBuilderRingbufferSettings settings, IRecognitionControl recognitionControl, ILoadingService loadingService)
|
||||
{
|
||||
_settings = settings;
|
||||
_recognitionControl = recognitionControl;
|
||||
|
||||
_loadingService = loadingService;
|
||||
}
|
||||
public void InitializeModule()
|
||||
{
|
||||
_recognitionControl.SessionStarted += _recognitionControl_SessionStarted;
|
||||
_recognitionControl.SessionEnded += _recognitionControl_SessionEnded;
|
||||
_recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed;
|
||||
}
|
||||
|
||||
private void _recognitionControl_SessionEnded(SessionEndedEvent obj)
|
||||
{
|
||||
_loadingService.StartLoading("Processing ringbuffer");
|
||||
Log.Information("Processing ringbuffer");
|
||||
try
|
||||
{
|
||||
CleanUp();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.Information("Finished processing ringbuffer");
|
||||
_loadingService.StopLoading("Processing ringbuffer");
|
||||
}
|
||||
}
|
||||
|
||||
private void _recognitionControl_ImageProcessed(VisionBuilder.UI.Common.Commands.ImageProcessedEvent obj)
|
||||
{
|
||||
if (obj.HasError)
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
try
|
||||
{
|
||||
WriteBadResult(obj);
|
||||
return;
|
||||
if (obj.HasError)
|
||||
{
|
||||
WriteBadResult(obj);
|
||||
return;
|
||||
}
|
||||
WriteGoodResult(obj);
|
||||
}
|
||||
WriteGoodResult(obj);
|
||||
finally
|
||||
{
|
||||
sw.Stop();
|
||||
Console.WriteLine($"Ringbuffer: wrote image in {sw.ElapsedMilliseconds}ms");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void _recognitionControl_SessionStarted(VisionBuilder.UI.Common.Commands.SessionStartedEvent obj)
|
||||
@@ -44,10 +77,19 @@ public class VisionBuilderRingbuffer:IVisionBuilderModule
|
||||
private DateTime _startTime;
|
||||
private string _product;
|
||||
|
||||
private void CleanUp()
|
||||
{
|
||||
_goodRingbufferIsolated?.Dispose();
|
||||
foreach (var ringbuffer in _badRingbuffers)
|
||||
{
|
||||
ringbuffer.Value.Dispose();
|
||||
}
|
||||
_badRingbuffers.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
|
||||
@@ -58,8 +100,7 @@ public class VisionBuilderRingbuffer:IVisionBuilderModule
|
||||
try
|
||||
{
|
||||
var image = result.ImageOriginal;
|
||||
// save to memory stream
|
||||
_goodRingbufferIsolated.Write(image.ToBytes(), filename);
|
||||
_goodRingbufferIsolated.Write(image, filename);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -87,9 +128,7 @@ public class VisionBuilderRingbuffer:IVisionBuilderModule
|
||||
var ringbuffer = GetBadRingbuffer(defectName);
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
ringbuffer.Write(image.ToBytes(), filename);
|
||||
ringbuffer.Write(image, filename);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -116,15 +155,10 @@ public class VisionBuilderRingbuffer:IVisionBuilderModule
|
||||
_startTime = DateTime.Now;
|
||||
_product = product;
|
||||
_path = PathSettingsUtils.ConvertVariables(Path.Combine(_settings.Root,_settings.GoodImagesPathTemplate), _startTime, _startTime, _product,cameraName:_settings.CameraName);
|
||||
_goodRingbufferIsolated?.Dispose();
|
||||
|
||||
_goodRingbufferIsolated = new IsolatedRingbuffer(_path, _settings.MaxGood, new AsyncFilesystem());
|
||||
|
||||
foreach (var ringbuffer in _badRingbuffers)
|
||||
{
|
||||
ringbuffer.Value.Dispose();
|
||||
}
|
||||
|
||||
_badRingbuffers.Clear();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,12 @@ Specifies directory, in which MaxBad and MaxGood are applied.
|
||||
|
||||
[SettingDescription("Specifies directory, relative to the Root")]
|
||||
[SettingPreview(typeof(VisionBuilderRingbufferSettings), nameof(ImagesPathTemplatePreview))]
|
||||
public string GoodImagesPathTemplate { get; set; } = @"\$RECIPE_NAME";
|
||||
public string GoodImagesPathTemplate { get; set; } = @"$RECIPE_NAME";
|
||||
|
||||
[SettingDescription("Specifies directory, relative to the Root")]
|
||||
[SettingPreview(typeof(VisionBuilderRingbufferSettings),nameof(ImagesPathTemplatePreview))]
|
||||
public string BadImagesPathTemplate { get; set; } =
|
||||
@"\$RECIPE_NAME\$DEFECT_NAME";
|
||||
@"$RECIPE_NAME\$DEFECT_NAME";
|
||||
|
||||
|
||||
[SettingPreview(typeof(VisionBuilderRingbufferSettings), nameof(ImagesPathTemplatePreview))]
|
||||
|
||||
@@ -3,6 +3,7 @@ using Lindt.Colorballs.Duo.Utils;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using Ninject;
|
||||
@@ -44,6 +45,7 @@ namespace VisionBuilder.UI.Statistics
|
||||
{
|
||||
var endedAt = obj.SessionEnded;
|
||||
_endTime = endedAt;
|
||||
Log.Information("Start processing statistics");
|
||||
_loadingService.StartLoading("Writing statistics for " + _settings.CameraName);
|
||||
try
|
||||
{
|
||||
@@ -56,15 +58,19 @@ namespace VisionBuilder.UI.Statistics
|
||||
finally
|
||||
{
|
||||
_loadingService.StopLoading("Writing statistics for " + _settings.CameraName);
|
||||
Log.Information("End processing statistics");
|
||||
}
|
||||
}
|
||||
|
||||
private void _recognitionControl_ImageProcessed(ImageProcessedEvent obj)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
if (obj.HasError)
|
||||
{
|
||||
RecordBadImage(obj.ErrorNames[0]);
|
||||
}
|
||||
sw.Stop();
|
||||
Console.WriteLine($"Recording statistics took {sw.ElapsedMilliseconds} ms");
|
||||
}
|
||||
|
||||
private void _recognitionControl_SessionStarted(SessionStartedEvent obj)
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
using Inspectron.Settings;
|
||||
|
||||
namespace VisionBuilder.UI.Windows.Test;
|
||||
|
||||
public class AppSettings
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using MaterialSkin;
|
||||
using MaterialSkin.Controls;
|
||||
using Microsoft.VisualBasic.Logging;
|
||||
using OpenCvSharp;
|
||||
using VisionBuilder.UI.Common;
|
||||
using VisionBuilder.UI.Common.Services;
|
||||
using VisionBuilder.UI.Common.ViewModel;
|
||||
|
||||
namespace VisionBuilder.UI.Windows.Test
|
||||
@@ -7,10 +12,14 @@ namespace VisionBuilder.UI.Windows.Test
|
||||
public partial class Form1 : MaterialForm
|
||||
{
|
||||
private readonly MainWindowVM _mainWindowVm;
|
||||
private readonly IPasswordInputService _passwordInputService;
|
||||
private readonly UIConfiguration _uiConfiguration;
|
||||
|
||||
public Form1(MainWindowVM mainWindowVm)
|
||||
public Form1(MainWindowVM mainWindowVm, IPasswordInputService passwordInputService, UIConfiguration uiConfiguration)
|
||||
{
|
||||
_mainWindowVm = mainWindowVm;
|
||||
_passwordInputService = passwordInputService;
|
||||
_uiConfiguration = uiConfiguration;
|
||||
MaterialSkin.MaterialSkinManager.ConfigureForInspectron();
|
||||
InitializeComponent();
|
||||
singleCameraControl1.SetViewModel(mainWindowVm.SingleCameraVms[0]);
|
||||
@@ -54,6 +63,12 @@ namespace VisionBuilder.UI.Windows.Test
|
||||
|
||||
private void btnSettings_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_uiConfiguration.ProtectSettingsWithPassword)
|
||||
{
|
||||
var passwordOk = _passwordInputService.GetPassword() == _uiConfiguration.AdminPassword;
|
||||
if (!passwordOk)
|
||||
return;
|
||||
}
|
||||
_mainWindowVm.SettingsCommand.Execute(null);
|
||||
}
|
||||
|
||||
|
||||
BIN
VisionBuilder.UI.Windows.Test/Inspectron icon-512.ico
Normal file
BIN
VisionBuilder.UI.Windows.Test/Inspectron icon-512.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 103 KiB |
@@ -97,9 +97,11 @@ namespace VisionBuilder.UI.Windows.Test
|
||||
kernelCamera1.Get<SingleCameraVM>()
|
||||
];
|
||||
|
||||
var form = mainKernel.Get<Form1>();
|
||||
|
||||
settings.LoadSettings();
|
||||
Application.Run(new Form1(mainWindowVm));
|
||||
|
||||
Application.Run(form);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,13 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<ApplicationIcon>Inspectron icon-512.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Inspectron icon-512.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Ninject.Extensions.ChildKernel" Version="3.3.0" />
|
||||
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.6.0.20220608" />
|
||||
@@ -51,7 +51,7 @@
|
||||
lblCameraName.Font = new Font("Segoe UI Semibold", 12F, FontStyle.Bold, GraphicsUnit.Point, 204);
|
||||
lblCameraName.Location = new Point(8, 8);
|
||||
lblCameraName.Name = "lblCameraName";
|
||||
lblCameraName.Size = new Size(616, 21);
|
||||
lblCameraName.Size = new Size(744, 21);
|
||||
lblCameraName.TabIndex = 2;
|
||||
lblCameraName.Text = "CameraName";
|
||||
lblCameraName.TextAlign = ContentAlignment.TopCenter;
|
||||
@@ -60,7 +60,7 @@
|
||||
//
|
||||
label1.Anchor = AnchorStyles.Top | AnchorStyles.Right;
|
||||
label1.Font = new Font("Segoe UI", 12F);
|
||||
label1.Location = new Point(656, 8);
|
||||
label1.Location = new Point(784, 0);
|
||||
label1.Name = "label1";
|
||||
label1.Size = new Size(408, 21);
|
||||
label1.TabIndex = 4;
|
||||
@@ -72,7 +72,7 @@
|
||||
previewWindow1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
|
||||
previewWindow1.Location = new Point(0, 32);
|
||||
previewWindow1.Name = "previewWindow1";
|
||||
previewWindow1.Size = new Size(624, 456);
|
||||
previewWindow1.Size = new Size(752, 456);
|
||||
previewWindow1.SizeMode = PictureBoxSizeMode.Zoom;
|
||||
previewWindow1.TabIndex = 5;
|
||||
previewWindow1.TabStop = false;
|
||||
@@ -82,7 +82,7 @@
|
||||
materialDivider1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
materialDivider1.BackColor = Color.FromArgb(55, 71, 79);
|
||||
materialDivider1.Depth = 0;
|
||||
materialDivider1.Location = new Point(632, 32);
|
||||
materialDivider1.Location = new Point(760, 24);
|
||||
materialDivider1.MouseState = MaterialSkin.MouseState.HOVER;
|
||||
materialDivider1.Name = "materialDivider1";
|
||||
materialDivider1.Size = new Size(1, 456);
|
||||
@@ -92,7 +92,7 @@
|
||||
// errorPreview1
|
||||
//
|
||||
errorPreview1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
errorPreview1.Location = new Point(648, 32);
|
||||
errorPreview1.Location = new Point(776, 24);
|
||||
errorPreview1.Name = "errorPreview1";
|
||||
errorPreview1.Size = new Size(416, 456);
|
||||
errorPreview1.TabIndex = 7;
|
||||
@@ -101,9 +101,9 @@
|
||||
//
|
||||
stats1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
stats1.BackColor = Color.White;
|
||||
stats1.Location = new Point(1080, 32);
|
||||
stats1.Location = new Point(1200, 32);
|
||||
stats1.Name = "stats1";
|
||||
stats1.Size = new Size(320, 456);
|
||||
stats1.Size = new Size(232, 456);
|
||||
stats1.TabIndex = 8;
|
||||
//
|
||||
// btnStart
|
||||
@@ -128,11 +128,11 @@
|
||||
flowLayoutPanel1.Controls.Add(flowLayoutPanel2);
|
||||
flowLayoutPanel1.Dock = DockStyle.Right;
|
||||
flowLayoutPanel1.FlowDirection = FlowDirection.TopDown;
|
||||
flowLayoutPanel1.Location = new Point(1408, 0);
|
||||
flowLayoutPanel1.Location = new Point(1448, 0);
|
||||
flowLayoutPanel1.Name = "flowLayoutPanel1";
|
||||
flowLayoutPanel1.Padding = new Padding(8, 24, 8, 8);
|
||||
flowLayoutPanel1.RightToLeft = RightToLeft.Yes;
|
||||
flowLayoutPanel1.Size = new Size(280, 498);
|
||||
flowLayoutPanel1.Size = new Size(240, 498);
|
||||
flowLayoutPanel1.TabIndex = 10;
|
||||
//
|
||||
// flowLayoutPanel2
|
||||
@@ -140,7 +140,7 @@
|
||||
flowLayoutPanel2.Controls.Add(btnSelectRecipe);
|
||||
flowLayoutPanel2.Controls.Add(btnStart);
|
||||
flowLayoutPanel2.Controls.Add(btnStop);
|
||||
flowLayoutPanel2.Location = new Point(61, 27);
|
||||
flowLayoutPanel2.Location = new Point(21, 27);
|
||||
flowLayoutPanel2.Name = "flowLayoutPanel2";
|
||||
flowLayoutPanel2.Size = new Size(200, 237);
|
||||
flowLayoutPanel2.TabIndex = 12;
|
||||
@@ -184,7 +184,7 @@
|
||||
materialDivider2.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right;
|
||||
materialDivider2.BackColor = Color.FromArgb(55, 71, 79);
|
||||
materialDivider2.Depth = 0;
|
||||
materialDivider2.Location = new Point(1064, 32);
|
||||
materialDivider2.Location = new Point(1192, 24);
|
||||
materialDivider2.MouseState = MaterialSkin.MouseState.HOVER;
|
||||
materialDivider2.Name = "materialDivider2";
|
||||
materialDivider2.Size = new Size(1, 456);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Ninject;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using VisionBuilder.UI.Common.Services;
|
||||
using VisionBuilder.UI.Common.ViewModel.Interfaces.UI;
|
||||
using VisionBuilder.UI.Windows.Settings;
|
||||
|
||||
@@ -13,6 +14,7 @@ public static class ModuleExtensions
|
||||
self.Bind<ILoadingService>().To<WindowsLoadingService>().InSingletonScope();
|
||||
self.Bind<IRecipeSelectionDialogService>().To<WindowsRecipeSelectionDialogService>().InSingletonScope();
|
||||
self.Bind<IImagePreviewService>().To<WindowsImagePreviewService>().InSingletonScope();
|
||||
self.Bind<IPasswordInputService>().To<WindowsPasswordInputService>().InSingletonScope();
|
||||
return self;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using MaterialSkin.Core.Controls;
|
||||
using VisionBuilder.UI.Common.Services;
|
||||
|
||||
namespace VisionBuilder.UI.Windows.Settings;
|
||||
|
||||
public class WindowsPasswordInputService: IPasswordInputService
|
||||
{
|
||||
public string GetPassword()
|
||||
{
|
||||
if (MaterialInputBox.Prompt("Password required", "", out var password, true) == DialogResult.OK)
|
||||
{
|
||||
return password;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.UI.Windows",
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MaterialSkin.Core", "framework\MaterialSkin.Core\MaterialSkin.Core.csproj", "{D5FD2E9D-DA4F-1343-47E0-FBC473A149BC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.UI.Windows.Test", "VisionBuilder.UI.Windows.Test\VisionBuilder.UI.Windows.Test.csproj", "{7697A266-A721-4D74-9C5C-0D4F2F6BBF68}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisionBuilder.UI.Windows.Uno", "VisionBuilder.UI.Windows.Test\VisionBuilder.UI.Windows.Uno.csproj", "{7697A266-A721-4D74-9C5C-0D4F2F6BBF68}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inspectron.Settings", "framework\Inspectron.Settings\Inspectron.Settings.csproj", "{E286CE4C-B68A-94B6-F477-0DBF42358009}"
|
||||
EndProject
|
||||
@@ -87,6 +87,10 @@ Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "PythonModelAPI", "PythonMod
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PralinenPLC", "Plugins\Pralinen\PralinenPLC\PralinenPLC.csproj", "{19E9B1A2-FBC6-2668-EDDF-1B3A6828184A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackstrasseBarcodeReader", "Plugins\Pralinen\PackstrasseBarcodeReader\PackstrasseBarcodeReader.csproj", "{B20D7E75-1C19-632D-21D2-2663B0329C5E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CandyboxPlugin", "Plugins\CandyboxPlugin\CandyboxPlugin.csproj", "{31C47198-DEC2-4073-A0C2-220549502CD1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -223,6 +227,14 @@ Global
|
||||
{19E9B1A2-FBC6-2668-EDDF-1B3A6828184A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{19E9B1A2-FBC6-2668-EDDF-1B3A6828184A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{19E9B1A2-FBC6-2668-EDDF-1B3A6828184A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B20D7E75-1C19-632D-21D2-2663B0329C5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B20D7E75-1C19-632D-21D2-2663B0329C5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B20D7E75-1C19-632D-21D2-2663B0329C5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B20D7E75-1C19-632D-21D2-2663B0329C5E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{31C47198-DEC2-4073-A0C2-220549502CD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{31C47198-DEC2-4073-A0C2-220549502CD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{31C47198-DEC2-4073-A0C2-220549502CD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{31C47198-DEC2-4073-A0C2-220549502CD1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -256,6 +268,8 @@ Global
|
||||
{44D1BA17-FB52-40A2-9D99-E49DA56C10C2} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
||||
{B4949493-C2CD-4786-860D-DAACD39A662D} = {9FA47F4A-8F44-4F55-B6F7-99C962E9F18F}
|
||||
{19E9B1A2-FBC6-2668-EDDF-1B3A6828184A} = {583A77DF-A293-4F3E-AB96-7310BC495822}
|
||||
{B20D7E75-1C19-632D-21D2-2663B0329C5E} = {583A77DF-A293-4F3E-AB96-7310BC495822}
|
||||
{31C47198-DEC2-4073-A0C2-220549502CD1} = {583A77DF-A293-4F3E-AB96-7310BC495822}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3CE42AE5-D79F-4E97-A246-AA8FD228B677}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
using Inspectron.Fastbuffer.Interfaces;
|
||||
using System.Collections.Concurrent;
|
||||
using OpenCvSharp;
|
||||
using Serilog;
|
||||
|
||||
namespace Inspectron.Fastbuffer.Filesystems;
|
||||
|
||||
public class AsyncFilesystem : IFilesystem
|
||||
{
|
||||
private readonly DirectFilesystem _directFilesystem = new();
|
||||
private readonly ConcurrentDictionary<string, byte[]> _buffer = new();
|
||||
private readonly BlockingCollection<(string path, byte[] data)> _writeQueue = new();
|
||||
private readonly List<string> _buffer = new();
|
||||
private readonly BlockingCollection<(string path, Mat data)> _writeQueue = new();
|
||||
private readonly Thread _backgroundThread;
|
||||
private readonly object _lock = new();
|
||||
|
||||
@@ -18,27 +20,17 @@ public class AsyncFilesystem : IFilesystem
|
||||
_backgroundThread.Start();
|
||||
}
|
||||
|
||||
public void Write(string path, byte[] data)
|
||||
public void Write(string path, Mat data)
|
||||
{
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_buffer[path] = data;
|
||||
_buffer.Add(path);
|
||||
_writeQueue.Add((path, data));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Read(string path)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_buffer.TryGetValue(path, out var data))
|
||||
{
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return _directFilesystem.Read(path);
|
||||
}
|
||||
|
||||
public void Delete(string path)
|
||||
{
|
||||
@@ -46,8 +38,9 @@ public class AsyncFilesystem : IFilesystem
|
||||
lock (_lock)
|
||||
{
|
||||
// pats is in buffer, so it's not written to disk yet
|
||||
if (_buffer.TryRemove(path, out _))
|
||||
if (_buffer.Contains(path))
|
||||
{
|
||||
_buffer.Remove(path);
|
||||
return;
|
||||
}
|
||||
_directFilesystem.Delete(path);
|
||||
@@ -61,8 +54,9 @@ public class AsyncFilesystem : IFilesystem
|
||||
{
|
||||
_directFilesystem.Write(path, data);
|
||||
lock (_lock)
|
||||
_buffer.TryRemove(path, out _);
|
||||
|
||||
_buffer.Remove(path);
|
||||
if (_writeQueue.Count > 5)
|
||||
Log.Warning($"AsyncFilesystem write queue size: {_writeQueue.Count}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
using Inspectron.Fastbuffer.Interfaces;
|
||||
using OpenCvSharp;
|
||||
using Serilog;
|
||||
|
||||
namespace Inspectron.Fastbuffer.Filesystems;
|
||||
|
||||
public class DirectFilesystem:IFilesystem
|
||||
{
|
||||
public void Write(string path, byte[] data)
|
||||
public void Write(string path, Mat data)
|
||||
{
|
||||
// ensure path
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
File.WriteAllBytes(path, data);
|
||||
var bytes = data.ToBytes();
|
||||
File.WriteAllBytes(path, bytes);
|
||||
}
|
||||
|
||||
public byte[] Read(string path)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Text;
|
||||
using Inspectron.Fastbuffer.Interfaces;
|
||||
using OpenCvSharp;
|
||||
using Serilog;
|
||||
|
||||
namespace Inspectron.Fastbuffer;
|
||||
@@ -112,7 +113,7 @@ public class IndexFile
|
||||
|
||||
}
|
||||
|
||||
public void WriteNextArtifact(byte[] data, string fileName)
|
||||
public void WriteNextArtifact(Mat data, string fileName)
|
||||
{
|
||||
var id = GetNextId();
|
||||
Log.Debug("Writing artifact {id}",id);
|
||||
@@ -132,19 +133,5 @@ public class IndexFile
|
||||
fileStream.Write(padding, 0, padding.Length);
|
||||
}
|
||||
|
||||
public byte[] ReadArtifact(int id)
|
||||
{
|
||||
using var fileStream = File.Open(_indexFilePath, FileMode.Open);
|
||||
fileStream.Seek(id * RecordSize+ HeaderSize, SeekOrigin.Begin);
|
||||
var bytes = new byte[RecordSize];
|
||||
fileStream.Read(bytes, 0, RecordSize);
|
||||
var fileName = Encoding.UTF8.GetString(bytes).TrimEnd('\0');
|
||||
var filePath = Path.Combine(_bufferPath, fileName);
|
||||
return _filesystem.Read(filePath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenCvSharp4" Version="4.6.0.20220608" />
|
||||
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
namespace Inspectron.Fastbuffer.Interfaces;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace Inspectron.Fastbuffer.Interfaces;
|
||||
|
||||
public interface IFilesystem:IDisposable
|
||||
{
|
||||
public void Write(string path, byte[] data);
|
||||
public byte[] Read(string path);
|
||||
public void Write(string path, Mat data);
|
||||
public void Delete(string path);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Inspectron.Fastbuffer.Interfaces;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace Inspectron.Fastbuffer;
|
||||
|
||||
@@ -26,7 +27,7 @@ public class IsolatedRingbuffer: IDisposable
|
||||
|
||||
}
|
||||
|
||||
public void Write(byte[] data, string fileName)
|
||||
public void Write(Mat data, string fileName)
|
||||
{
|
||||
_index.WriteNextArtifact(data, fileName);
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
using Inspectron.Fastbuffer.Interfaces;
|
||||
|
||||
namespace Inspectron.Fastbuffer
|
||||
{
|
||||
public class RingbufferRepository
|
||||
{
|
||||
private readonly string _path;
|
||||
|
||||
private readonly string _goodIndexPath;
|
||||
private readonly string _badIndexPath;
|
||||
private readonly IndexFile _goodIndex;
|
||||
private readonly IndexFile _badIndex;
|
||||
public const string BufferDirectory = ".rb";
|
||||
public const string GoodIndexFile = "good.idx";
|
||||
public const string BadIndexFile = "bad.idx";
|
||||
public RingbufferRepository(string path, int goodBufferSize, int badBufferSize, IFilesystem filesystem)
|
||||
{
|
||||
_path = path;
|
||||
|
||||
_goodIndexPath = Path.Combine(_path, BufferDirectory, GoodIndexFile);
|
||||
_badIndexPath = Path.Combine(_path, BufferDirectory, BadIndexFile);
|
||||
Directory.CreateDirectory(Path.Combine(_path,BufferDirectory));
|
||||
_goodIndex = new IndexFile(_goodIndexPath, goodBufferSize, filesystem);
|
||||
_badIndex = new IndexFile(_badIndexPath, badBufferSize, filesystem);
|
||||
|
||||
var id= _goodIndex.GetCurrentArtifactId();
|
||||
Console.WriteLine(@"RB size: "+id);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var artifact = _goodIndex.GetArtifactPath(i);
|
||||
Console.WriteLine(@"Artifact: "+artifact);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IndexFile GoodIndex => _goodIndex;
|
||||
|
||||
public IndexFile BadIndex => _badIndex;
|
||||
|
||||
public void WriteGood(byte[] data, string fileName)
|
||||
{
|
||||
_goodIndex.WriteNextArtifact(data, fileName);
|
||||
}
|
||||
|
||||
public void WriteBad(byte[] data, string fileName)
|
||||
{
|
||||
_badIndex.WriteNextArtifact(data, fileName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user