check point

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

View File

@@ -0,0 +1,57 @@
namespace VisionBuilder.UI.Windows.Components
{
partial class ErrorPreview
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
flowLayoutPanel1 = new FlowLayoutPanel();
SuspendLayout();
//
// flowLayoutPanel1
//
flowLayoutPanel1.AutoScroll = true;
flowLayoutPanel1.Dock = DockStyle.Fill;
flowLayoutPanel1.Location = new Point(0, 0);
flowLayoutPanel1.Name = "flowLayoutPanel1";
flowLayoutPanel1.Size = new Size(584, 786);
flowLayoutPanel1.TabIndex = 0;
//
// ErrorPreview
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
Controls.Add(flowLayoutPanel1);
Name = "ErrorPreview";
Size = new Size(584, 786);
ResumeLayout(false);
}
#endregion
private FlowLayoutPanel flowLayoutPanel1;
}
}

View File

@@ -0,0 +1,135 @@
using System.Collections.ObjectModel;
using Microsoft.VisualBasic.Logging;
using System.Drawing.Drawing2D;
using OpenCvSharp.Extensions;
using VisionBuilder.UI.Common.ViewModel;
using VisionBuilder.UI.Common.ViewModel.Classes;
using System.Collections.Specialized;
namespace VisionBuilder.UI.Windows.Components
{
public partial class ErrorPreview : UserControl
{
private ErrorsVM _errorPreviewVM;
// Keep track of subscription to avoid duplicate handlers
private ObservableCollection<ErrorData> _subscribedErrors;
public void SetViewModel(ErrorsVM errorPreviewVm)
{
// Unsubscribe from previous Errors collection if needed
if (_subscribedErrors != null)
_subscribedErrors.CollectionChanged -= Errors_CollectionChanged;
_errorPreviewVM = errorPreviewVm;
if (_errorPreviewVM != null)
{
_subscribedErrors = _errorPreviewVM.Errors;
_subscribedErrors.CollectionChanged += Errors_CollectionChanged;
// Initial sync: clear and add all
flowLayoutPanel1.Controls.Clear();
foreach (var error in _subscribedErrors)
{
AddErrorPreviewInternal(error);
}
}
}
public ErrorPreview()
{
InitializeComponent();
}
private Font _numberedFont = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Regular);
private void AddErrorPreviewInternal(ErrorData errorData)
{
var pb = new PictureBox();
var width = 293 / 2;
var height = 220 / 2;
pb.Size = new Size(width, height + 20);
var numberedImage = new Bitmap(width, height + 20);
var text = DateTime.Now.ToString("yy-MM-dd") + " " + DateTime.Now.ToString("T");
using (Graphics g = Graphics.FromImage(numberedImage))
{
g.Clear(Color.White);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
try
{
g.DrawImage(errorData.ImageAnalysis.ToBitmap(), 0, 0, width, height);
}
catch (Exception ex)
{
}
var measure = g.MeasureString(text, _numberedFont);
g.TranslateTransform(width / 2 - measure.Width / 2, height);
g.DrawString(text, _numberedFont, Brushes.Black, 0, 0);
}
pb.Image = numberedImage;
pb.SizeMode = PictureBoxSizeMode.Zoom;
pb.Cursor = Cursors.Hand;
pb.Click += (sender, args) =>
{
_errorPreviewVM.ShowImage(errorData);
};
var tag = (errorData, text);
pb.Tag = tag;
flowLayoutPanel1.Controls.Add(pb);
flowLayoutPanel1.Controls.SetChildIndex(pb, 0);
try
{
flowLayoutPanel1.Refresh();
}
catch
{
}
}
private void Errors_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new NotifyCollectionChangedEventHandler(Errors_CollectionChanged), sender, e);
return;
}
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (ErrorData newError in e.NewItems)
{
AddErrorPreviewInternal(newError);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (ErrorData oldError in e.OldItems)
{
// Remove the PictureBox whose Tag contains the oldError
foreach (Control ctrl in flowLayoutPanel1.Controls)
{
if (ctrl is PictureBox pb && pb.Tag is ValueTuple<ErrorData, string> tag && tag.Item1 == oldError)
{
flowLayoutPanel1.Controls.Remove(pb);
pb.Dispose();
break;
}
}
}
}
else if (e.Action == NotifyCollectionChangedAction.Reset)
{
flowLayoutPanel1.Controls.Clear();
}
}
}
}

View 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>

View File

@@ -0,0 +1,44 @@
using OpenCvSharp.Extensions;
using VisionBuilder.UI.Common.ViewModel;
namespace VisionBuilder.UI.Windows.Components;
public class PreviewWindow:PictureBox
{
private PreviewVM _previewVm;
public void SetViewModel(PreviewVM previewVm)
{
_previewVm = previewVm;
_previewVm.PropertyChanged += _previewVm_PropertyChanged;
}
private void _previewVm_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(PreviewVM.ImagePreview):
var bmp = _previewVm.ImagePreview?.ToBitmap();
using (Graphics g = Graphics.FromImage(bmp))
{
// draw red border around the image if it's an error
if (_previewVm.IsError)
{
using (Pen pen = new Pen(Color.Red, 5))
{
g.DrawRectangle(pen, 0, 0, bmp.Width - 1, bmp.Height - 1);
}
}
}
Image = bmp;
break;
}
}
public PreviewWindow()
{
SizeMode = PictureBoxSizeMode.Zoom;
}
}

View File

@@ -0,0 +1,215 @@
namespace VisionBuilder.UI.Windows.Components
{
partial class SingleCameraControl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
lblCameraName = new Label();
label1 = new Label();
previewWindow1 = new PreviewWindow();
materialDivider1 = new MaterialSkin.Controls.MaterialDivider();
errorPreview1 = new ErrorPreview();
stats1 = new Stats();
btnStart = new MaterialSkin.Controls.MaterialRaisedButton();
flowLayoutPanel1 = new FlowLayoutPanel();
btnSelectRecipe = new MaterialSkin.Controls.MaterialRaisedButton();
btnStop = new MaterialSkin.Controls.MaterialRaisedButton();
materialDivider2 = new MaterialSkin.Controls.MaterialDivider();
((System.ComponentModel.ISupportInitialize)previewWindow1).BeginInit();
flowLayoutPanel1.SuspendLayout();
SuspendLayout();
//
// lblCameraName
//
lblCameraName.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
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.TabIndex = 2;
lblCameraName.Text = "CameraName";
lblCameraName.TextAlign = ContentAlignment.TopCenter;
//
// label1
//
label1.Anchor = AnchorStyles.Top | AnchorStyles.Right;
label1.Font = new Font("Segoe UI", 12F);
label1.Location = new Point(656, 8);
label1.Name = "label1";
label1.Size = new Size(408, 21);
label1.TabIndex = 4;
label1.Text = "Errors";
label1.TextAlign = ContentAlignment.TopCenter;
//
// previewWindow1
//
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.SizeMode = PictureBoxSizeMode.Zoom;
previewWindow1.TabIndex = 5;
previewWindow1.TabStop = false;
//
// materialDivider1
//
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.MouseState = MaterialSkin.MouseState.HOVER;
materialDivider1.Name = "materialDivider1";
materialDivider1.Size = new Size(1, 456);
materialDivider1.TabIndex = 6;
materialDivider1.Text = "materialDivider1";
//
// errorPreview1
//
errorPreview1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right;
errorPreview1.Location = new Point(648, 32);
errorPreview1.Name = "errorPreview1";
errorPreview1.Size = new Size(416, 456);
errorPreview1.TabIndex = 7;
//
// stats1
//
stats1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right;
stats1.BackColor = Color.White;
stats1.Location = new Point(1080, 32);
stats1.Name = "stats1";
stats1.Size = new Size(320, 456);
stats1.TabIndex = 8;
//
// btnStart
//
btnStart.Anchor = AnchorStyles.Top | AnchorStyles.Right;
btnStart.AutoSizeMode = AutoSizeMode.GrowAndShrink;
btnStart.Depth = 0;
btnStart.DrawBorder = false;
btnStart.Icon = null;
btnStart.Location = new Point(85, 97);
btnStart.MouseState = MaterialSkin.MouseState.HOVER;
btnStart.Name = "btnStart";
btnStart.Primary = true;
btnStart.Size = new Size(176, 64);
btnStart.TabIndex = 9;
btnStart.Text = "Start";
btnStart.UseVisualStyleBackColor = true;
//
// flowLayoutPanel1
//
flowLayoutPanel1.Controls.Add(btnSelectRecipe);
flowLayoutPanel1.Controls.Add(btnStart);
flowLayoutPanel1.Controls.Add(btnStop);
flowLayoutPanel1.Dock = DockStyle.Right;
flowLayoutPanel1.FlowDirection = FlowDirection.TopDown;
flowLayoutPanel1.Location = new Point(1408, 0);
flowLayoutPanel1.Name = "flowLayoutPanel1";
flowLayoutPanel1.Padding = new Padding(8, 24, 8, 8);
flowLayoutPanel1.RightToLeft = RightToLeft.Yes;
flowLayoutPanel1.Size = new Size(280, 498);
flowLayoutPanel1.TabIndex = 10;
//
// btnSelectRecipe
//
btnSelectRecipe.Anchor = AnchorStyles.Top | AnchorStyles.Right;
btnSelectRecipe.AutoSizeMode = AutoSizeMode.GrowAndShrink;
btnSelectRecipe.Depth = 0;
btnSelectRecipe.DrawBorder = true;
btnSelectRecipe.Icon = null;
btnSelectRecipe.Location = new Point(85, 27);
btnSelectRecipe.MouseState = MaterialSkin.MouseState.HOVER;
btnSelectRecipe.Name = "btnSelectRecipe";
btnSelectRecipe.Primary = false;
btnSelectRecipe.Size = new Size(176, 64);
btnSelectRecipe.TabIndex = 11;
btnSelectRecipe.Text = "Select recipe";
btnSelectRecipe.UseVisualStyleBackColor = true;
//
// btnStop
//
btnStop.Anchor = AnchorStyles.Top | AnchorStyles.Right;
btnStop.AutoSizeMode = AutoSizeMode.GrowAndShrink;
btnStop.Depth = 0;
btnStop.DrawBorder = false;
btnStop.Icon = null;
btnStop.Location = new Point(85, 167);
btnStop.MouseState = MaterialSkin.MouseState.HOVER;
btnStop.Name = "btnStop";
btnStop.Primary = true;
btnStop.Size = new Size(176, 64);
btnStop.TabIndex = 10;
btnStop.Text = "Stop";
btnStop.UseVisualStyleBackColor = true;
//
// materialDivider2
//
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.MouseState = MaterialSkin.MouseState.HOVER;
materialDivider2.Name = "materialDivider2";
materialDivider2.Size = new Size(1, 456);
materialDivider2.TabIndex = 11;
materialDivider2.Text = "materialDivider2";
//
// SingleCameraControl
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.White;
Controls.Add(materialDivider2);
Controls.Add(flowLayoutPanel1);
Controls.Add(stats1);
Controls.Add(errorPreview1);
Controls.Add(materialDivider1);
Controls.Add(previewWindow1);
Controls.Add(label1);
Controls.Add(lblCameraName);
Name = "SingleCameraControl";
Size = new Size(1688, 498);
((System.ComponentModel.ISupportInitialize)previewWindow1).EndInit();
flowLayoutPanel1.ResumeLayout(false);
ResumeLayout(false);
}
#endregion
private Label lblCameraName;
private Label label1;
private PreviewWindow previewWindow1;
private MaterialSkin.Controls.MaterialDivider materialDivider1;
private ErrorPreview errorPreview1;
private Stats stats1;
private MaterialSkin.Controls.MaterialRaisedButton btnStart;
private FlowLayoutPanel flowLayoutPanel1;
private MaterialSkin.Controls.MaterialRaisedButton btnStop;
private MaterialSkin.Controls.MaterialRaisedButton btnSelectRecipe;
private MaterialSkin.Controls.MaterialDivider materialDivider2;
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using VisionBuilder.UI.Common;
namespace VisionBuilder.UI.Windows.Components
{
public partial class SingleCameraControl : UserControl
{
private SingleCameraVM _singleCameraVm;
public void SetViewModel(SingleCameraVM singleCameraVm)
{
_singleCameraVm = singleCameraVm;
this.previewWindow1.SetViewModel(singleCameraVm.PreviewVm);
this.errorPreview1.SetViewModel(singleCameraVm.ErrorsVm);
this.stats1.SetViewModel(singleCameraVm.StatisticsVm);
SetBindings();
}
private void SetBindings()
{
btnStart.Command=_singleCameraVm.StartCommand;
btnStop.Command=_singleCameraVm.StopCommand;
btnSelectRecipe.Command=_singleCameraVm.SelectRecipeCommand;
lblCameraName.DataBindings.Add("Text", _singleCameraVm, nameof(_singleCameraVm.CameraName), true, DataSourceUpdateMode.OnPropertyChanged);
btnSelectRecipe.DataBindings.Add("Text", _singleCameraVm, nameof(_singleCameraVm.CurrentRecipeName), true, DataSourceUpdateMode.OnPropertyChanged);
}
public SingleCameraControl()
{
InitializeComponent();
}
}
}

View 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>

View File

@@ -0,0 +1,95 @@
namespace VisionBuilder.UI.Windows.Components
{
partial class Stats
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
metaPanel = new FlowLayoutPanel();
indicatorPanel = new FlowLayoutPanel();
sideFlowLayoutPanel = new FlowLayoutPanel();
sideFlowLayoutPanel.SuspendLayout();
SuspendLayout();
//
// metaPanel
//
metaPanel.AutoSize = true;
metaPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
metaPanel.BackColor = Color.White;
metaPanel.FlowDirection = FlowDirection.TopDown;
metaPanel.Location = new Point(3, 3);
metaPanel.MinimumSize = new Size(160, 32);
metaPanel.Name = "metaPanel";
metaPanel.Size = new Size(160, 32);
metaPanel.TabIndex = 12;
//
// indicatorPanel
//
indicatorPanel.AutoSize = true;
indicatorPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
indicatorPanel.BackColor = Color.White;
indicatorPanel.FlowDirection = FlowDirection.TopDown;
indicatorPanel.Location = new Point(3, 50);
indicatorPanel.Margin = new Padding(3, 12, 3, 3);
indicatorPanel.MinimumSize = new Size(160, 32);
indicatorPanel.Name = "indicatorPanel";
indicatorPanel.Size = new Size(160, 32);
indicatorPanel.TabIndex = 13;
//
// sideFlowLayoutPanel
//
sideFlowLayoutPanel.AutoSize = true;
sideFlowLayoutPanel.BackColor = Color.White;
sideFlowLayoutPanel.Controls.Add(metaPanel);
sideFlowLayoutPanel.Controls.Add(indicatorPanel);
sideFlowLayoutPanel.Dock = DockStyle.Fill;
sideFlowLayoutPanel.FlowDirection = FlowDirection.TopDown;
sideFlowLayoutPanel.Location = new Point(0, 0);
sideFlowLayoutPanel.Name = "sideFlowLayoutPanel";
sideFlowLayoutPanel.Size = new Size(289, 731);
sideFlowLayoutPanel.TabIndex = 20;
//
// Stats
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
BackColor = Color.White;
Controls.Add(sideFlowLayoutPanel);
Name = "Stats";
Size = new Size(289, 731);
sideFlowLayoutPanel.ResumeLayout(false);
sideFlowLayoutPanel.PerformLayout();
ResumeLayout(false);
PerformLayout();
}
#endregion
private FlowLayoutPanel metaPanel;
private FlowLayoutPanel indicatorPanel;
private FlowLayoutPanel sideFlowLayoutPanel;
}
}

View File

@@ -0,0 +1,115 @@
using System.Drawing.Imaging;
using VisionBuilder.UI.Common.ViewModel;
namespace VisionBuilder.UI.Windows.Components
{
public partial class Stats : UserControl
{
public void SetViewModel(StatisticsVM statisticsVm)
{
_statisticsVm = statisticsVm;
_statisticsVm.PropertyChanged += _statisticsVm_PropertyChanged;
}
private void _statisticsVm_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(StatisticsVM.SessionStarted):
UpdateMeta("Started at", _statisticsVm.SessionStarted, true);
break;
case nameof(StatisticsVM.RecipeName):
UpdateMeta("Recipe", _statisticsVm.RecipeName, true);
break;
case nameof(StatisticsVM.ProcessingTime):
UpdateMeta("Processing time", _statisticsVm.ProcessingTime, true);
break;
case nameof(StatisticsVM.Good):
UpdateMeta("Good", _statisticsVm.Good.ToString(), true);
break;
case nameof(StatisticsVM.Bad):
UpdateMeta("Bad", _statisticsVm.Bad.ToString(), true);
break;
case nameof(StatisticsVM.Total):
UpdateMeta("Total", _statisticsVm.Total.ToString(), true);
break;
case nameof(StatisticsVM.StatisticsDetails):
UpdateMeta("Details", _statisticsVm.StatisticsDetails, false);
break;
}
}
public Stats()
{
InitializeComponent();
_imageGoodIndicator = new Bitmap(32, 32, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(_imageGoodIndicator))
{
g.Clear(Color.Transparent);
g.FillEllipse(Brushes.Green, 0, 0, 32, 32);
}
_imageBadIndicator = new Bitmap(32, 32, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(_imageBadIndicator))
{
g.Clear(Color.Transparent);
g.FillEllipse(Brushes.Red, 0, 0, 32, 32);
}
}
private Dictionary<string, Control> _meta = new Dictionary<string, Control>();
private readonly Bitmap _imageGoodIndicator;
private readonly Bitmap _imageBadIndicator;
private StatisticsVM _statisticsVm;
public void UpdateMeta(string name, string text, bool bold=false)
{
Action a = new Action(() =>
{
if (!_meta.ContainsKey(name))
{
metaPanel.Controls.Add(new Label() { AutoSize = true, Margin = new Padding(0, 8, 0, 0), Text = name + ":", Font = new Font(new FontFamily("Verdana"), 8, FontStyle.Bold) });
_meta[name] = new Label() { AutoSize = true, Margin = new Padding(0) };
_meta[name].Font = new Font(new FontFamily("Verdana"), 8, bold?FontStyle.Bold:FontStyle.Regular);
metaPanel.Controls.Add(_meta[name]);
}
_meta[name].Text = text;
metaPanel.Refresh();
});
if (metaPanel.InvokeRequired)
{
metaPanel.Invoke(a);
}
else
{
a();
}
}
public static string ShortenString(string input, int maxLength)
{
if (input.Length <= maxLength)
{
return input;
}
else
{
return input.Substring(0, maxLength) + "...";
}
}
public void Clear()
{
_meta.Clear();
metaPanel.Controls.Clear();
}
}
}

View 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>