494 lines
17 KiB
C#
494 lines
17 KiB
C#
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();
|
||
}
|
||
}
|
||
}
|
||
}
|