正在改尺寸测量

This commit is contained in:
2025-04-18 14:06:48 +08:00
16 changed files with 1840 additions and 200 deletions

View File

@ -11,15 +11,7 @@
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>

View File

@ -16,54 +16,61 @@ namespace DHSoftware.Views
private Bitmap _currentImage;
private float _scale = 1.0f;
private PointF _offset = PointF.Empty;
private RectangleF _roiRect;
private PointF _roiStart;
private bool _isDrawing;
private Point _dragStart;
private bool _isDragging;
private Pen _roiPen = new Pen(Color.Red, 2);
private readonly object _imageLock = new object();
#endregion
#region
#region
public Bitmap Image
{
get => _currentImage;
get
{
lock (_imageLock)
{
return _currentImage?.Clone() as Bitmap;
}
}
set
{
// 记录旧状态
var oldSize = _currentImage?.Size ?? Size.Empty;
var oldScale = _scale;
var oldOffset = _offset;
Bitmap newImage = value?.Clone() as Bitmap;
Bitmap oldImageToDispose = null;
_currentImage?.Dispose();
_currentImage = value;
if (_currentImage != null)
lock (_imageLock)
{
if (_currentImage.Size != oldSize)
// 交换图像引用
oldImageToDispose = _currentImage;
_currentImage = newImage;
if (_currentImage != null)
{
// 尺寸不同时重置ROI、自动适配
_roiRect = RectangleF.Empty;
AutoFit();
}
else
{
// 尺寸相同时:保留缩放和偏移
_scale = oldScale;
_offset = oldOffset;
ClampOffset();
if (oldImageToDispose?.Size != _currentImage.Size)
{
AutoFit();
}
else
{
ClampOffset();
}
}
}
pictureBox.Invalidate();
// 在锁外安全释放旧图像
if (oldImageToDispose != null)
{
// 使用BeginInvoke确保在UI线程释放资源
BeginInvoke(new Action(() =>
{
oldImageToDispose.Dispose();
oldImageToDispose = null;
}));
}
SafeInvalidate();
}
}
#endregion
public RectangleF CurrentROI => _roiRect;
#endregion
public ImageViewerControl()
{
InitializeComponents();
@ -73,15 +80,13 @@ namespace DHSoftware.Views
#region
private void InitializeComponents()
{
// 主显示区域
pictureBox = new PictureBox
{
Dock = DockStyle.Fill,
BackColor = Color.DarkGray,
Cursor = Cursors.Cross
Cursor = Cursors.Hand
};
// 状态栏
statusLabel = new Label
{
Dock = DockStyle.Bottom,
@ -91,7 +96,6 @@ namespace DHSoftware.Views
Font = new Font("Consolas", 10)
};
// 事件绑定
pictureBox.MouseDown += PictureBox_MouseDown;
pictureBox.MouseMove += PictureBox_MouseMove;
pictureBox.MouseUp += PictureBox_MouseUp;
@ -106,8 +110,8 @@ namespace DHSoftware.Views
{
typeof(PictureBox).GetMethod("SetStyle",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance)
?.Invoke(pictureBox, new object[] {
System.Reflection.BindingFlags.Instance)?
.Invoke(pictureBox, new object[] {
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint,
true
@ -118,189 +122,225 @@ namespace DHSoftware.Views
#region
private void AutoFit()
{
if (_currentImage == null) return;
lock (_imageLock)
{
if (_currentImage == null) return;
const float marginRatio = 0.1f;
float marginWidth = Width * marginRatio;
float marginHeight = Height * marginRatio;
const float marginRatio = 0.1f;
float marginWidth = Width * marginRatio;
float marginHeight = Height * marginRatio;
_scale = Math.Min(
(Width - marginWidth * 2) / _currentImage.Width,
(Height - marginHeight * 2) / _currentImage.Height
);
_scale = Math.Min(
(Width - marginWidth * 2) / _currentImage.Width,
(Height - marginHeight * 2) / _currentImage.Height
);
_offset.X = marginWidth + (Width - marginWidth * 2 - _currentImage.Width * _scale) / 2;
_offset.Y = marginHeight + (Height - marginHeight * 2 - _currentImage.Height * _scale) / 2;
_offset.X = marginWidth + (Width - marginWidth * 2 - _currentImage.Width * _scale) / 2;
_offset.Y = marginHeight + (Height - marginHeight * 2 - _currentImage.Height * _scale) / 2;
ClampOffset();
}
}
private void PictureBox_Paint(object sender, PaintEventArgs e)
{
if (_currentImage == null) return;
Bitmap drawImage = null;
RectangleF destRect;
float scale;
PointF offset;
// 绘制图像
var destRect = new RectangleF(
_offset.X,
_offset.Y,
_currentImage.Width * _scale,
_currentImage.Height * _scale);
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
e.Graphics.DrawImage(_currentImage, destRect);
// 绘制ROI
if (!_roiRect.IsEmpty)
// 创建临时绘图参数
lock (_imageLock)
{
var displayRect = new RectangleF(
_roiRect.X * _scale + _offset.X,
_roiRect.Y * _scale + _offset.Y,
_roiRect.Width * _scale,
_roiRect.Height * _scale);
if (_currentImage == null) return;
using (var pen = new Pen(_roiPen.Color, _roiPen.Width / _scale))
{
e.Graphics.DrawRectangle(pen,
displayRect.X,
displayRect.Y,
displayRect.Width,
displayRect.Height);
}
// 创建绘图副本
drawImage = _currentImage.Clone() as Bitmap;
scale = _scale;
offset = _offset;
destRect = new RectangleF(
offset.X,
offset.Y,
drawImage.Width * scale,
drawImage.Height * scale);
}
try
{
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
e.Graphics.DrawImage(drawImage, destRect);
}
finally
{
drawImage?.Dispose();
}
}
#endregion
#region
#region
private void PictureBox_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && _currentImage != null)
if (InvokeRequired) return;
lock (_imageLock)
{
_roiStart = ClampCoordinates(ConvertToImageCoords(e.Location));
_isDrawing = true;
}
else if (e.Button == MouseButtons.Right)
{
_dragStart = e.Location;
_isDragging = true;
if (_currentImage == null) return;
if (e.Button == MouseButtons.Left)
{
_dragStart = e.Location;
_isDragging = true;
}
}
}
private void PictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (_isDragging)
{
_offset.X += e.X - _dragStart.X;
_offset.Y += e.Y - _dragStart.Y;
_dragStart = e.Location;
ClampOffset();
pictureBox.Invalidate();
}
else if (_isDrawing && _currentImage != null)
{
PointF current = ClampCoordinates(ConvertToImageCoords(e.Location));
_roiRect = new RectangleF(
Math.Min(_roiStart.X, current.X),
Math.Min(_roiStart.Y, current.Y),
Math.Abs(_roiStart.X - current.X),
Math.Abs(_roiStart.Y - current.Y)
);
pictureBox.Invalidate();
}
if (InvokeRequired) return;
UpdateStatus(e.Location);
lock (_imageLock)
{
if (_currentImage == null) return;
if (_isDragging)
{
_offset.X += e.X - _dragStart.X;
_offset.Y += e.Y - _dragStart.Y;
_dragStart = e.Location;
ClampOffset();
SafeInvalidate();
}
UpdateStatus(e.Location);
}
}
private void PictureBox_MouseUp(object sender, MouseEventArgs e)
{
_isDragging = false;
_isDrawing = false;
lock (_imageLock)
{
_isDragging = false;
}
}
private void PictureBox_MouseWheel(object sender, MouseEventArgs e)
{
if (_currentImage == null) return;
if (InvokeRequired) return;
PointF mousePos = e.Location;
PointF imgPosBefore = ConvertToImageCoords(mousePos);
lock (_imageLock)
{
if (_currentImage == null) return;
if (imgPosBefore.X < 0 || imgPosBefore.X > _currentImage.Width ||
imgPosBefore.Y < 0 || imgPosBefore.Y > _currentImage.Height) return;
PointF imgPos = ConvertToImageCoords(e.Location);
if (imgPos.X < 0 || imgPos.X > _currentImage.Width ||
imgPos.Y < 0 || imgPos.Y > _currentImage.Height)
return;
float zoom = e.Delta > 0 ? 1.1f : 0.9f;
float newScale = Math.Clamp(_scale * zoom, 0.1f, 10f);
float zoom = e.Delta > 0 ? 1.1f : 0.9f;
float newScale = Math.Clamp(_scale * zoom, 0.1f, 10f);
_offset.X = mousePos.X - imgPosBefore.X * newScale;
_offset.Y = mousePos.Y - imgPosBefore.Y * newScale;
_offset.X = e.Location.X - imgPos.X * newScale;
_offset.Y = e.Location.Y - imgPos.Y * newScale;
_scale = newScale;
ClampOffset();
pictureBox.Invalidate();
_scale = newScale;
ClampOffset();
SafeInvalidate();
}
}
#endregion
#region
private PointF ConvertToImageCoords(PointF mousePos)
private PointF ConvertToImageCoords(Point mousePos)
{
return new PointF(
(mousePos.X - _offset.X) / _scale,
(mousePos.Y - _offset.Y) / _scale);
}
private PointF ClampCoordinates(PointF point)
{
return new PointF(
Math.Max(0, Math.Min(_currentImage.Width, point.X)),
Math.Max(0, Math.Min(_currentImage.Height, point.Y)));
lock (_imageLock)
{
if (_currentImage == null) return PointF.Empty;
return new PointF(
(mousePos.X - _offset.X) / _scale,
(mousePos.Y - _offset.Y) / _scale);
}
}
private void ClampOffset()
{
if (_currentImage == null) return;
lock (_imageLock)
{
if (_currentImage == null) return;
float imgWidth = _currentImage.Width * _scale;
float imgHeight = _currentImage.Height * _scale;
float imgWidth = _currentImage.Width * _scale;
float imgHeight = _currentImage.Height * _scale;
if (imgWidth <= Width)
{
_offset.X = Math.Clamp(_offset.X, 0, Width - imgWidth);
}
else
{
_offset.X = Math.Clamp(_offset.X, Width - imgWidth, 0);
}
_offset.X = Math.Clamp(_offset.X,
imgWidth > Width ? Width - imgWidth : 0,
imgWidth > Width ? 0 : Width - imgWidth);
if (imgHeight <= Height)
{
_offset.Y = Math.Clamp(_offset.Y, 0, Height - imgHeight);
}
else
{
_offset.Y = Math.Clamp(_offset.Y, Height - imgHeight, 0);
_offset.Y = Math.Clamp(_offset.Y,
imgHeight > Height ? Height - imgHeight : 0,
imgHeight > Height ? 0 : Height - imgHeight);
}
}
private void UpdateStatus(Point mousePos)
{
if (_currentImage == null) return;
if (InvokeRequired)
{
BeginInvoke(new Action<Point>(UpdateStatus), mousePos);
return;
}
PointF imgPos = ConvertToImageCoords(mousePos);
bool inImage = imgPos.X >= 0 && imgPos.X <= _currentImage.Width &&
imgPos.Y >= 0 && imgPos.Y <= _currentImage.Height;
lock (_imageLock)
{
if (_currentImage == null)
{
statusLabel.Text = "无有效图像";
return;
}
string roiInfo = _roiRect.IsEmpty ?
"未选择区域" :
$"选区: X={_roiRect.X:0} Y={_roiRect.Y:0} {_roiRect.Width:0}x{_roiRect.Height:0}";
PointF imgPos = ConvertToImageCoords(mousePos);
bool inImage = imgPos.X >= 0 && imgPos.X <= _currentImage.Width &&
imgPos.Y >= 0 && imgPos.Y <= _currentImage.Height;
statusLabel.Text = inImage ?
$"坐标: ({imgPos.X:0}, {imgPos.Y:0}) | 缩放: {_scale * 100:0}% | {roiInfo}" :
$"图像尺寸: {_currentImage.Width}x{_currentImage.Height} | 缩放: {_scale * 100:0}% | {roiInfo}";
statusLabel.Text = inImage ?
$"坐标: ({imgPos.X:F1}, {imgPos.Y:F1}) | 缩放: {_scale * 100:0}%" :
$"图像尺寸: {_currentImage.Width}x{_currentImage.Height} | 缩放: {_scale * 100:0}%";
}
}
public void ClearROI()
private void SafeInvalidate()
{
_roiRect = RectangleF.Empty;
pictureBox.Invalidate(); // 触发重绘
UpdateStatus(Point.Empty); // 更新状态栏
if (InvokeRequired)
{
BeginInvoke(new Action(pictureBox.Invalidate));
}
else
{
pictureBox.Invalidate();
}
}
#endregion
public Bitmap GetCurrentSnapshot()
{
lock (_imageLock)
{
// 返回深拷贝防止原始图像被修改
return _currentImage?.Clone() as Bitmap;
}
}
#endregion
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
lock (_imageLock)
{
_currentImage?.Dispose();
_currentImage = null;
}
base.Dispose(disposing);
}
}
}

View File

@ -0,0 +1,189 @@
namespace DHSoftware.Views
{
partial class SavePositionControl
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
panel1 = new AntdUI.Panel();
sltName = new AntdUI.Select();
label1 = new AntdUI.Label();
iptPosition = new AntdUI.Input();
label3 = new AntdUI.Label();
divider1 = new AntdUI.Divider();
stackPanel1 = new AntdUI.StackPanel();
button_cancel = new AntdUI.Button();
button_ok = new AntdUI.Button();
divider2 = new AntdUI.Divider();
lbTitleName = new AntdUI.Label();
panel1.SuspendLayout();
stackPanel1.SuspendLayout();
SuspendLayout();
//
// panel1
//
panel1.Controls.Add(sltName);
panel1.Controls.Add(label1);
panel1.Controls.Add(iptPosition);
panel1.Controls.Add(label3);
panel1.Controls.Add(divider1);
panel1.Controls.Add(stackPanel1);
panel1.Controls.Add(divider2);
panel1.Controls.Add(lbTitleName);
panel1.Dock = DockStyle.Fill;
panel1.Location = new Point(0, 0);
panel1.Name = "panel1";
panel1.Padding = new Padding(12);
panel1.Shadow = 6;
panel1.Size = new Size(500, 243);
panel1.TabIndex = 0;
panel1.Text = "panel1";
//
// sltName
//
sltName.Dock = DockStyle.Top;
sltName.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
sltName.Location = new Point(18, 196);
sltName.MaxCount = 20;
sltName.Name = "sltName";
sltName.Radius = 3;
sltName.Size = new Size(464, 38);
sltName.TabIndex = 24;
//
// label1
//
label1.Dock = DockStyle.Top;
label1.Font = new Font("Microsoft YaHei UI", 12F, FontStyle.Regular, GraphicsUnit.Point, 134);
label1.Location = new Point(18, 172);
label1.Name = "label1";
label1.Size = new Size(464, 24);
label1.TabIndex = 23;
label1.Text = "工位名称";
//
// iptPosition
//
iptPosition.Dock = DockStyle.Top;
iptPosition.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
iptPosition.Location = new Point(18, 134);
iptPosition.Name = "iptPosition";
iptPosition.Radius = 3;
iptPosition.Size = new Size(464, 38);
iptPosition.TabIndex = 22;
//
// label3
//
label3.Dock = DockStyle.Top;
label3.Font = new Font("Microsoft YaHei UI", 12F, FontStyle.Regular, GraphicsUnit.Point, 134);
label3.Location = new Point(18, 110);
label3.Name = "label3";
label3.Size = new Size(464, 24);
label3.TabIndex = 21;
label3.Text = "当前位置";
//
// divider1
//
divider1.Dock = DockStyle.Top;
divider1.Location = new Point(18, 98);
divider1.Name = "divider1";
divider1.Size = new Size(464, 12);
divider1.TabIndex = 20;
//
// stackPanel1
//
stackPanel1.Controls.Add(button_cancel);
stackPanel1.Controls.Add(button_ok);
stackPanel1.Dock = DockStyle.Top;
stackPanel1.Location = new Point(18, 54);
stackPanel1.Name = "stackPanel1";
stackPanel1.RightToLeft = RightToLeft.No;
stackPanel1.Size = new Size(464, 44);
stackPanel1.TabIndex = 19;
stackPanel1.Text = "stackPanel1";
//
// button_cancel
//
button_cancel.BorderWidth = 1F;
button_cancel.Font = new Font("Microsoft YaHei UI", 9F);
button_cancel.Ghost = true;
button_cancel.Location = new Point(84, 3);
button_cancel.Name = "button_cancel";
button_cancel.Size = new Size(75, 38);
button_cancel.TabIndex = 1;
button_cancel.Text = "取消";
//
// button_ok
//
button_ok.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
button_ok.Location = new Point(3, 3);
button_ok.Name = "button_ok";
button_ok.Size = new Size(75, 38);
button_ok.TabIndex = 0;
button_ok.Text = "确定";
button_ok.Type = AntdUI.TTypeMini.Primary;
//
// divider2
//
divider2.Dock = DockStyle.Top;
divider2.Location = new Point(18, 42);
divider2.Name = "divider2";
divider2.Size = new Size(464, 12);
divider2.TabIndex = 18;
//
// lbTitleName
//
lbTitleName.Dock = DockStyle.Top;
lbTitleName.Font = new Font("Microsoft YaHei UI", 12F, FontStyle.Regular, GraphicsUnit.Point, 134);
lbTitleName.Location = new Point(18, 18);
lbTitleName.Name = "lbTitleName";
lbTitleName.Size = new Size(464, 24);
lbTitleName.TabIndex = 17;
lbTitleName.Text = "保存当前位置操作";
//
// SavePositionControl
//
Controls.Add(panel1);
Name = "SavePositionControl";
Size = new Size(500, 243);
panel1.ResumeLayout(false);
stackPanel1.ResumeLayout(false);
ResumeLayout(false);
}
#endregion
private AntdUI.Panel panel1;
private AntdUI.Label lbTitleName;
private AntdUI.Input iptPosition;
private AntdUI.Label label3;
private AntdUI.Divider divider1;
private AntdUI.StackPanel stackPanel1;
private AntdUI.Button button_cancel;
private AntdUI.Button button_ok;
private AntdUI.Divider divider2;
private AntdUI.Select sltName;
private AntdUI.Label label1;
}
}

View File

@ -0,0 +1,116 @@

using System.Text.RegularExpressions;
using DH.Commons.Base;
using DH.Commons.Enums;
using DH.Commons.Helper;
using DH.Commons.Models;
namespace DHSoftware.Views
{
public partial class SavePositionControl : UserControl
{
private AntdUI.Window window;
public bool submit;
public int Position;
public SavePositionControl(AntdUI.Window _window,int _position)
{
this.window = _window;
InitializeComponent();
Position=_position;
sltName.Items.Clear();
var targetFields = GetSpecificIOFields();
foreach(var item in targetFields)
{
sltName.Items.Add(item);
}
iptPosition.Text= Position.ToString();
// 绑定事件
BindEventHandler();
}
private void BindEventHandler()
{
button_ok.Click += Button_ok_Click;
button_cancel.Click += Button_cancel_Click;
}
private void Button_cancel_Click(object sender, EventArgs e)
{
submit = false;
this.Dispose();
}
private void Button_ok_Click(object sender, EventArgs e)
{
iptPosition.Status = AntdUI.TType.None;
if (String.IsNullOrEmpty(sltName.Text))
{
sltName.Status = AntdUI.TType.Error;
AntdUI.Message.warn(window, "请选择工位!", autoClose: 3);
return;
}
//根据工位查找点位
PLCItem? pLCItem = ConfigModel.PLCBaseList?
.FirstOrDefault()?
.PLCItemList?
.Where(it=>it.Name==sltName.Text).FirstOrDefault();
if (pLCItem == null)
{
AntdUI.Message.warn(window, $"未找到{sltName.Text}地址,请检查该地址是否存在于点位表!", autoClose: 3);
return;
}
PLCItem? pLCItem1 = ConfigModel.GlobalList?
.FirstOrDefault()?
.StartProcessList?
.Where(it=>it.Name ==sltName.Text).FirstOrDefault();
if (pLCItem1 == null)
{
pLCItem1=new PLCItem();
pLCItem1.Name = pLCItem.Name;
pLCItem1.Address = pLCItem.Address;
pLCItem1.Value = iptPosition.Text;
pLCItem1.Type = pLCItem.Type;
pLCItem1.StartExecute = true;
ConfigModel.GlobalList?
.FirstOrDefault()?
.StartProcessList?.Add(pLCItem1);
}
else
{
pLCItem1.Value = iptPosition.Text;
}
ConfigHelper.SaveConfig();
AntdUI.Message.success(window, "保存成功!", autoClose: 3);
submit = true;
this.Dispose();
}
public static List<string> GetSpecificIOFields()
{
return Enum.GetNames(typeof(EnumPLCOutputIO))
.Where(name =>
Regex.IsMatch(name, @"^工位[1-9]|10$") || // 匹配工位1-10
name == "OK脉冲" ||
name == "NG脉冲")
.OrderBy(name =>
{
// 对工位进行数字排序
if (name.StartsWith("工位"))
{
return int.Parse(name.Substring(2));
}
return 99; // OK/NG放在最后
})
.ToList();
}
}
}

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,379 @@
namespace DHSoftware.Views
{
partial class VisualLocalizationWindow
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
titlebar = new AntdUI.PageHeader();
panel1 = new AntdUI.Panel();
iptThreshold = new AntdUI.InputNumber();
label6 = new AntdUI.Label();
btnSelectBackImg = new AntdUI.Button();
btnSelectModel = new AntdUI.Button();
iptBackImg = new AntdUI.Input();
label3 = new AntdUI.Label();
iptModel = new AntdUI.Input();
label2 = new AntdUI.Label();
sltCameraName = new AntdUI.Select();
label1 = new AntdUI.Label();
panel2 = new AntdUI.Panel();
iptSpeed = new AntdUI.InputNumber();
label7 = new AntdUI.Label();
btnSaveImg = new AntdUI.Button();
btnSavePos = new AntdUI.Button();
btnReverse = new AntdUI.Button();
btnForward = new AntdUI.Button();
btnLocalization = new AntdUI.Button();
btnAcquisition = new AntdUI.Button();
iptPosition = new AntdUI.InputNumber();
label5 = new AntdUI.Label();
sltDirection = new AntdUI.Select();
label4 = new AntdUI.Label();
imageViewerControl1 = new ImageViewerControl();
panel1.SuspendLayout();
panel2.SuspendLayout();
SuspendLayout();
//
// titlebar
//
titlebar.BackColor = Color.FromArgb(46, 108, 227);
titlebar.DividerShow = true;
titlebar.DividerThickness = 0F;
titlebar.Dock = DockStyle.Top;
titlebar.Font = new Font("Microsoft YaHei UI", 12F, FontStyle.Regular, GraphicsUnit.Point, 134);
titlebar.ForeColor = Color.White;
titlebar.Location = new Point(0, 0);
titlebar.Mode = AntdUI.TAMode.Dark;
titlebar.Name = "titlebar";
titlebar.ShowButton = true;
titlebar.ShowIcon = true;
titlebar.Size = new Size(1210, 37);
titlebar.SubText = "视觉定位系统";
titlebar.TabIndex = 1;
titlebar.Text = "山东迭慧智能科技有限公司";
//
// panel1
//
panel1.Controls.Add(iptThreshold);
panel1.Controls.Add(label6);
panel1.Controls.Add(btnSelectBackImg);
panel1.Controls.Add(btnSelectModel);
panel1.Controls.Add(iptBackImg);
panel1.Controls.Add(label3);
panel1.Controls.Add(iptModel);
panel1.Controls.Add(label2);
panel1.Controls.Add(sltCameraName);
panel1.Controls.Add(label1);
panel1.Dock = DockStyle.Top;
panel1.Location = new Point(0, 37);
panel1.Name = "panel1";
panel1.Size = new Size(1210, 56);
panel1.TabIndex = 2;
panel1.Text = "panel1";
//
// iptThreshold
//
iptThreshold.Location = new Point(1094, 6);
iptThreshold.Name = "iptThreshold";
iptThreshold.Size = new Size(107, 43);
iptThreshold.TabIndex = 15;
iptThreshold.Text = "0";
//
// label6
//
label6.BackColor = SystemColors.Window;
label6.Location = new Point(1044, 18);
label6.Name = "label6";
label6.Size = new Size(57, 23);
label6.TabIndex = 14;
label6.Text = "定位阈值";
//
// btnSelectBackImg
//
btnSelectBackImg.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
btnSelectBackImg.Location = new Point(963, 11);
btnSelectBackImg.Name = "btnSelectBackImg";
btnSelectBackImg.Size = new Size(75, 38);
btnSelectBackImg.TabIndex = 13;
btnSelectBackImg.Text = "打开";
btnSelectBackImg.Type = AntdUI.TTypeMini.Primary;
//
// btnSelectModel
//
btnSelectModel.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
btnSelectModel.Location = new Point(525, 9);
btnSelectModel.Name = "btnSelectModel";
btnSelectModel.Size = new Size(75, 38);
btnSelectModel.TabIndex = 12;
btnSelectModel.Text = "打开";
btnSelectModel.Type = AntdUI.TTypeMini.Primary;
//
// iptBackImg
//
iptBackImg.Location = new Point(694, 6);
iptBackImg.Name = "iptBackImg";
iptBackImg.PlaceholderText = "请选择背景图片";
iptBackImg.Size = new Size(263, 43);
iptBackImg.TabIndex = 10;
//
// label3
//
label3.BackColor = SystemColors.Window;
label3.Location = new Point(606, 18);
label3.Name = "label3";
label3.Size = new Size(82, 23);
label3.TabIndex = 9;
label3.Text = "背景图片路径";
//
// iptModel
//
iptModel.Location = new Point(256, 6);
iptModel.Name = "iptModel";
iptModel.PlaceholderText = "请选择算法";
iptModel.Size = new Size(263, 43);
iptModel.TabIndex = 3;
//
// label2
//
label2.BackColor = SystemColors.Window;
label2.Location = new Point(193, 18);
label2.Name = "label2";
label2.Size = new Size(57, 23);
label2.TabIndex = 2;
label2.Text = "算法路径";
//
// sltCameraName
//
sltCameraName.List = true;
sltCameraName.Location = new Point(72, 7);
sltCameraName.MaxCount = 10;
sltCameraName.Name = "sltCameraName";
sltCameraName.PlaceholderText = "请选择相机";
sltCameraName.Size = new Size(115, 43);
sltCameraName.TabIndex = 1;
//
// label1
//
label1.BackColor = SystemColors.Window;
label1.Location = new Point(9, 18);
label1.Name = "label1";
label1.Size = new Size(57, 23);
label1.TabIndex = 0;
label1.Text = "相机名称";
//
// panel2
//
panel2.Controls.Add(iptSpeed);
panel2.Controls.Add(label7);
panel2.Controls.Add(btnSaveImg);
panel2.Controls.Add(btnSavePos);
panel2.Controls.Add(btnReverse);
panel2.Controls.Add(btnForward);
panel2.Controls.Add(btnLocalization);
panel2.Controls.Add(btnAcquisition);
panel2.Controls.Add(iptPosition);
panel2.Controls.Add(label5);
panel2.Controls.Add(sltDirection);
panel2.Controls.Add(label4);
panel2.Dock = DockStyle.Top;
panel2.Location = new Point(0, 93);
panel2.Name = "panel2";
panel2.Size = new Size(1210, 56);
panel2.TabIndex = 3;
panel2.Text = "panel2";
//
// iptSpeed
//
iptSpeed.Location = new Point(256, 6);
iptSpeed.Name = "iptSpeed";
iptSpeed.Size = new Size(107, 43);
iptSpeed.TabIndex = 20;
iptSpeed.Text = "0";
//
// label7
//
label7.BackColor = SystemColors.Window;
label7.Location = new Point(193, 18);
label7.Name = "label7";
label7.Size = new Size(57, 23);
label7.TabIndex = 19;
label7.Text = "转盘速度";
//
// btnSaveImg
//
btnSaveImg.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
btnSaveImg.Location = new Point(1126, 11);
btnSaveImg.Name = "btnSaveImg";
btnSaveImg.Size = new Size(75, 38);
btnSaveImg.TabIndex = 18;
btnSaveImg.Text = "保存图像";
btnSaveImg.Type = AntdUI.TTypeMini.Primary;
//
// btnSavePos
//
btnSavePos.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
btnSavePos.Location = new Point(1013, 11);
btnSavePos.Name = "btnSavePos";
btnSavePos.Size = new Size(75, 38);
btnSavePos.TabIndex = 17;
btnSavePos.Text = "保存定位";
btnSavePos.Type = AntdUI.TTypeMini.Primary;
//
// btnReverse
//
btnReverse.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
btnReverse.Location = new Point(904, 11);
btnReverse.Name = "btnReverse";
btnReverse.Size = new Size(75, 38);
btnReverse.TabIndex = 16;
btnReverse.Text = "转盘反转";
btnReverse.Type = AntdUI.TTypeMini.Primary;
//
// btnForward
//
btnForward.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
btnForward.Location = new Point(804, 11);
btnForward.Name = "btnForward";
btnForward.Size = new Size(75, 38);
btnForward.TabIndex = 15;
btnForward.Text = "转盘正转";
btnForward.Type = AntdUI.TTypeMini.Primary;
//
// btnLocalization
//
btnLocalization.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
btnLocalization.Location = new Point(704, 11);
btnLocalization.Name = "btnLocalization";
btnLocalization.Size = new Size(75, 38);
btnLocalization.TabIndex = 14;
btnLocalization.Text = "开始定位";
btnLocalization.Type = AntdUI.TTypeMini.Primary;
//
// btnAcquisition
//
btnAcquisition.Font = new Font("Microsoft YaHei UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 134);
btnAcquisition.Location = new Point(606, 11);
btnAcquisition.Name = "btnAcquisition";
btnAcquisition.Size = new Size(75, 38);
btnAcquisition.TabIndex = 13;
btnAcquisition.Text = "开始采集";
btnAcquisition.Type = AntdUI.TTypeMini.Primary;
//
// iptPosition
//
iptPosition.Location = new Point(425, 6);
iptPosition.Name = "iptPosition";
iptPosition.ReadOnly = true;
iptPosition.Size = new Size(175, 43);
iptPosition.TabIndex = 7;
iptPosition.Text = "0";
//
// label5
//
label5.BackColor = SystemColors.Window;
label5.Location = new Point(369, 18);
label5.Name = "label5";
label5.Size = new Size(59, 23);
label5.TabIndex = 6;
label5.Text = "当前位置";
//
// sltDirection
//
sltDirection.Items.AddRange(new object[] { "正方向", "反方向" });
sltDirection.List = true;
sltDirection.Location = new Point(72, 6);
sltDirection.MaxCount = 10;
sltDirection.Name = "sltDirection";
sltDirection.PlaceholderText = "请选择方向";
sltDirection.Size = new Size(115, 43);
sltDirection.TabIndex = 5;
//
// label4
//
label4.BackColor = SystemColors.Window;
label4.Location = new Point(9, 18);
label4.Name = "label4";
label4.Size = new Size(57, 23);
label4.TabIndex = 4;
label4.Text = "转盘方向";
//
// imageViewerControl1
//
imageViewerControl1.Dock = DockStyle.Fill;
imageViewerControl1.Image = null;
imageViewerControl1.Location = new Point(0, 149);
imageViewerControl1.Name = "imageViewerControl1";
imageViewerControl1.Size = new Size(1210, 558);
imageViewerControl1.TabIndex = 4;
//
// VisualLocalizationWindow
//
AutoScaleDimensions = new SizeF(7F, 17F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1210, 707);
Controls.Add(imageViewerControl1);
Controls.Add(panel2);
Controls.Add(panel1);
Controls.Add(titlebar);
Name = "VisualLocalizationWindow";
Text = "CCD光学筛选定位系统";
WindowState = FormWindowState.Maximized;
panel1.ResumeLayout(false);
panel2.ResumeLayout(false);
ResumeLayout(false);
}
#endregion
private AntdUI.PageHeader titlebar;
private AntdUI.Panel panel1;
private AntdUI.Select sltCameraName;
private AntdUI.Label label1;
private AntdUI.Input iptModel;
private AntdUI.Label label2;
private AntdUI.Input iptBackImg;
private AntdUI.Label label3;
private AntdUI.Panel panel2;
private AntdUI.Label label4;
private AntdUI.Select sltDirection;
private AntdUI.InputNumber iptPosition;
private AntdUI.Label label5;
private AntdUI.Button btnSelectBackImg;
private AntdUI.Button btnSelectModel;
private AntdUI.Button btnAcquisition;
private AntdUI.Button btnLocalization;
private AntdUI.Button btnReverse;
private AntdUI.Button btnForward;
private AntdUI.Button btnSaveImg;
private AntdUI.Button btnSavePos;
private ImageViewerControl imageViewerControl1;
private AntdUI.InputNumber iptThreshold;
private AntdUI.Label label6;
private AntdUI.InputNumber iptSpeed;
private AntdUI.Label label7;
}
}

View File

@ -0,0 +1,493 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using AntdUI;
using DH.Commons.Base;
using DH.Commons.Enums;
using DH.Devices.Camera;
using HalconDotNet;
using OpenCvSharp.Extensions;
using Sunny.UI;
using static System.Net.Mime.MediaTypeNames;
namespace DHSoftware.Views
{
public partial class VisualLocalizationWindow : Window
{
//采集状态
private volatile bool isCapturing=false;
//定位状态
private volatile bool isLocationing = false;
//算法
HDevEngine MyEngine = new HDevEngine();
HDevProcedure Procedure;
HDevProcedureCall ProcCall;
//背景图
HImage backImage = new HImage();
//当前相机
Do3ThinkCamera Do3ThinkCamera = new Do3ThinkCamera();
//定时器
private System.Threading.Timer Timer;
public VisualLocalizationWindow()
{
InitializeComponent();
Load += VisualLocalizationWindow_Load;
btnSelectModel.Click += BtnSelectModel_Click;
btnSelectBackImg.Click += BtnSelectBackImg_Click;
btnAcquisition.Click += BtnAcquisition_Click;
btnLocalization.Click += BtnLocalization_Click;
btnForward.MouseDown += BtnForward_MouseDown;
btnForward.MouseUp += BtnForward_MouseUp;
btnReverse.MouseDown += BtnReverse_MouseDown;
btnReverse.MouseUp += BtnReverse_MouseUp;
btnSaveImg.Click += BtnSaveImg_Click;
btnSavePos.Click += BtnSavePos_Click;
}
private void BtnSavePos_Click(object? sender, EventArgs e)
{
var form = new SavePositionControl(this,Convert.ToInt32(iptPosition.Text)) { Size = new Size(300, 300) };
AntdUI.Modal.open(new AntdUI.Modal.Config(this, "", form, TType.None)
{
BtnHeight = 0,
});
if (form.submit)
{
//保存用户操作到文件
VisualLocalization visualLocalization = new VisualLocalization();
visualLocalization.CameraName = sltCameraName.Text;
visualLocalization.ModelPath=iptModel.Text;
visualLocalization.ImgPath=iptBackImg.Text;
visualLocalization.Threshold=iptThreshold.Text;
visualLocalization.Direction=sltDirection.Text;
visualLocalization.Speed=iptSpeed.Text;
visualLocalization.SaveToFile("VisualLocalization.json");
}
}
/// <summary>
/// 保存图像
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnSaveImg_Click(object? sender, EventArgs e)
{
if (!isCapturing)
{
AntdUI.Message.warn(this, $"未开始采集,无法保存图像!", autoClose: 3);
return;
}
Bitmap bitmap =imageViewerControl1.GetCurrentSnapshot();
using (SaveFileDialog saveDialog = new SaveFileDialog())
{
saveDialog.Title = "保存图像文件";
saveDialog.Filter = "JPEG 图像|*.jpg";
saveDialog.FilterIndex = 0;
saveDialog.AddExtension = true;
saveDialog.OverwritePrompt = true;
if (saveDialog.ShowDialog() == DialogResult.OK)
{
bitmap.Save(saveDialog.FileName, ImageFormat.Jpeg);
AntdUI.Message.success(this, $"图像保存成功!", autoClose: 3);
}
else
{
AntdUI.Message.warn(this, $"取消图像保存操作!", autoClose: 3);
}
}
}
/// <summary>
/// 定位
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnLocalization_Click(object? sender, EventArgs e)
{
if (!isCapturing)
{
AntdUI.Message.warn(this, $"未开始采集,无法开始定位!", autoClose: 3);
return;
}
if (!isLocationing)
{
bool direction =sltDirection.SelectedIndex==0?true:false;
if (string.IsNullOrEmpty(iptSpeed.Text))
{
AntdUI.Message.warn(this, $"请输入速度!", autoClose: 3);
return;
}
int speed = 0;
try
{
bool isValid = int.TryParse(iptSpeed.Text, out speed);
if (!isValid)
{
AntdUI.Message.warn(this, $"输入的速度不是有效值!", autoClose: 3);
return;
}
}
catch (Exception ex) { }
MainWindow.Instance.PLC.TurnSpeed(speed);
MainWindow.Instance.PLC.TurnDirection(direction);
MainWindow.Instance.PLC.TurnStart(true);
isLocationing = true;
btnLocalization.Text = "结束定位";
btnLocalization.Type = TTypeMini.Warn;
}
else
{
MainWindow.Instance.PLC.TurnStart(false);
iptPosition.Text= MainWindow.Instance.PLC.ReadVisionPos().ToString();
isLocationing = false;
btnLocalization.Text = "开始定位";
btnLocalization.Type = TTypeMini.Primary;
}
}
/// <summary>
/// 采集
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnAcquisition_Click(object? sender, EventArgs e)
{
if (!isCapturing)
{
if (string.IsNullOrWhiteSpace(sltCameraName.Text))
{
AntdUI.Message.warn(this, $"请选择相机!", autoClose: 3);
return;
}
if (string.IsNullOrWhiteSpace(iptModel.Text))
{
AntdUI.Message.warn(this, $"请选择算法!", autoClose: 3);
return;
}
if (string.IsNullOrWhiteSpace(iptBackImg.Text))
{
AntdUI.Message.warn(this, $"请选择背景图片!", autoClose: 3);
return;
}
// 加载HALCON模型
if (!File.Exists(iptModel.Text))
{
AntdUI.Message.warn(this, $"算法文件不存在!", autoClose: 3);
return;
}
if (!File.Exists(iptBackImg.Text))
{
AntdUI.Message.warn(this, $"图片文件不存在!", autoClose: 3);
return;
}
//获取背景图
backImage = new HImage();
backImage.ReadImage(iptBackImg.Text);
// 从完整路径获取过程名称
string procedureName = Path.GetFileNameWithoutExtension(iptModel.Text);
string procedureDir = Path.GetDirectoryName(iptModel.Text);
// 重新初始化HALCON引擎
MyEngine.SetProcedurePath(procedureDir);
Procedure = new HDevProcedure(procedureName);
ProcCall = new HDevProcedureCall(Procedure);
if (MainWindow.Instance.PLC.Connected)
{
//启用视觉定位
MainWindow.Instance.PLC.VisionPos(true);
}
else
{
AntdUI.Message.warn(this, $"未连接PLC,无法视觉定位!", autoClose: 3);
return;
}
Do3ThinkCamera=MainWindow.Instance.Cameras.Where(it=>it.CameraName==sltCameraName.Text).FirstOrDefault()??new Do3ThinkCamera();
Do3ThinkCamera.OnHImageOutput += OnCameraHImageOutput;
Timer = new System.Threading.Timer(CaptureLoop, null, 0, 50);
isCapturing = true;
btnAcquisition.Text = "结束采集";
btnAcquisition.Type = TTypeMini.Warn;
}
else
{
if (isLocationing)
{
AntdUI.Message.warn(this, $"定位未结束,不能结束采集!", autoClose: 3);
return;
}
MainWindow.Instance.PLC.VisionPos(false);
Do3ThinkCamera.OnHImageOutput -= OnCameraHImageOutput;
Timer?.Dispose();
isCapturing = false;
btnAcquisition.Text = "开始采集";
btnAcquisition.Type = TTypeMini.Primary;
}
}
/// <summary>
/// 触发
/// </summary>
/// <param name="state"></param>
private void CaptureLoop(object? state)
{
Do3ThinkCamera.Snapshot();
}
/// <summary>
/// 反转抬起
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnReverse_MouseUp(object? sender, MouseEventArgs e)
{
if (MainWindow.Instance.PLC.Connected)
{
MainWindow.Instance.PLC.TurnStart(false);
iptPosition.Text = MainWindow.Instance.PLC.ReadVisionPos().ToString();
}
else
{
AntdUI.Message.warn(this, $"未连接PLC", autoClose: 3);
}
}
/// <summary>
/// 反转按下
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnReverse_MouseDown(object? sender, MouseEventArgs e)
{
if (MainWindow.Instance.PLC.Connected)
{
//开启转盘
if (string.IsNullOrEmpty(iptSpeed.Text))
{
AntdUI.Message.warn(this, $"请输入速度!", autoClose: 3);
return;
}
int speed = 0;
try
{
bool isValid = int.TryParse(iptSpeed.Text, out speed);
if (!isValid)
{
AntdUI.Message.warn(this, $"输入的速度不是有效值!", autoClose: 3);
return;
}
}
catch (Exception ex) { }
MainWindow.Instance.PLC.TurnSpeed(speed);
MainWindow.Instance.PLC.TurnDirection(false);
MainWindow.Instance.PLC.TurnStart(true);
iptPosition.Text = MainWindow.Instance.PLC.ReadVisionPos().ToString();
}
else
{
AntdUI.Message.warn(this, $"未连接PLC", autoClose: 3);
}
}
/// <summary>
/// 正转抬起
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnForward_MouseUp(object? sender, MouseEventArgs e)
{
if (MainWindow.Instance.PLC.Connected)
{
MainWindow.Instance.PLC.TurnStart(false);
iptPosition.Text = MainWindow.Instance.PLC.ReadVisionPos().ToString();
}
else
{
AntdUI.Message.warn(this, $"未连接PLC", autoClose: 3);
}
}
/// <summary>
/// 正转按下
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnForward_MouseDown(object? sender, MouseEventArgs e)
{
if (MainWindow.Instance.PLC.Connected)
{
//开启转盘
if (string.IsNullOrEmpty(iptSpeed.Text))
{
AntdUI.Message.warn(this, $"请输入速度!", autoClose: 3);
return;
}
int speed = 0;
try
{
bool isValid = int.TryParse(iptSpeed.Text, out speed);
if (!isValid)
{
AntdUI.Message.warn(this, $"输入的速度不是有效值!", autoClose: 3);
return;
}
}
catch (Exception ex) { }
MainWindow.Instance.PLC.TurnSpeed(speed);
MainWindow.Instance.PLC.TurnDirection(true);
MainWindow.Instance.PLC.TurnStart(true);
iptPosition.Text = MainWindow.Instance.PLC.ReadVisionPos().ToString();
}
else
{
AntdUI.Message.warn(this, $"未连接PLC", autoClose: 3);
}
}
/// <summary>
/// 选择背景图
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnSelectBackImg_Click(object? sender, EventArgs e)
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
// 设置对话框标题
openFileDialog.Title = "选择背景图片";
// 限制文件后缀为 .hdvp
openFileDialog.Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp";
// 禁止多选
openFileDialog.Multiselect = false;
// 显示对话框并等待用户操作
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
string filePath = openFileDialog.FileName;
iptBackImg.Text = filePath;
}
}
}
/// <summary>
/// 选择算法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnSelectModel_Click(object? sender, EventArgs e)
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
// 设置对话框标题
openFileDialog.Title = "选择算法文件";
// 限制文件后缀为 .hdvp
openFileDialog.Filter = "算法文件 (*.hdvp)|*.hdvp";
// 禁止多选
openFileDialog.Multiselect = false;
// 显示对话框并等待用户操作
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
string filePath = openFileDialog.FileName;
iptModel.Text = filePath;
}
}
}
/// <summary>
/// 加载事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void VisualLocalizationWindow_Load(object? sender, EventArgs e)
{
sltDirection.SelectedIndex = 0;
sltCameraName.Items.Clear();
if (MainWindow.Instance.Cameras?.Count > 0)
{
foreach(var cam in MainWindow.Instance.Cameras)
{
sltCameraName.Items.Add(cam.CameraName);
}
}
else
{
AntdUI.Message.warn(this, $"未找到启用相机!", autoClose: 3);
}
}
/// <summary>
/// 窗体对象实例
/// </summary>
private static VisualLocalizationWindow _instance;
internal static VisualLocalizationWindow Instance
{
get
{
if (_instance == null || _instance.IsDisposed)
_instance = new VisualLocalizationWindow();
return _instance;
}
}
/// <summary>
/// 相机回调
/// </summary>
/// <param name="dt"></param>
/// <param name="camera"></param>
/// <param name="imageSet"></param>
private void OnCameraHImageOutput(DateTime dt, CameraBase camera, MatSet imageSet)
{
imageViewerControl1.Image = imageSet._mat.ToBitmap();
HObject obj = OpenCVHelper.MatToHImage(imageSet._mat);
HImage hImage = HalconHelper.ConvertHObjectToHImage(obj);
// 调用 ProcCall 的方法
ProcCall.SetInputIconicParamObject("INPUT_Image", hImage); // 将图像输入Proc
ProcCall.SetInputIconicParamObject("BackGroundPic", backImage);
ProcCall.SetInputCtrlParamTuple("DistThreshold", Convert.ToInt32(iptThreshold.Text));
ProcCall.Execute();
double nNUm = ProcCall.GetOutputCtrlParamTuple("OUTPUT_Flag");
if (nNUm == 0)
{
MainWindow.Instance.PLC.TurnStart(false);
iptPosition.Text = MainWindow.Instance.PLC.ReadVisionPos().ToString();
}
}
}
}

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>