using Bro.Common.Factory;
|
using Bro.Common.Helper;
|
using Bro.Common.Interface;
|
using Bro.Common.Model.Interface;
|
using HalconDotNet;
|
using Newtonsoft.Json;
|
//using MvCamCtrl.NET;
|
using System;
|
using System.Collections.Generic;
|
using System.ComponentModel;
|
using System.Configuration;
|
using System.Drawing;
|
using System.Drawing.Design;
|
using System.Drawing.Imaging;
|
using System.IO;
|
using System.Linq;
|
using System.Runtime.InteropServices;
|
using System.Runtime.Serialization.Formatters.Binary;
|
using System.Text;
|
using System.Threading;
|
using System.Threading.Tasks;
|
using System.Windows.Forms;
|
using System.Windows.Forms.Design;
|
using static Bro.Common.Helper.EnumHelper;
|
|
namespace Bro.Common.Base
|
{
|
public delegate void OnUpdateCameraImageDelegate(CameraBase camera, Bitmap image, string imagePath);
|
public abstract class CameraBase : DeviceBase
|
{
|
#region DeviceBase
|
protected override void Start()
|
{
|
if (clearImageTimer == null)
|
{
|
clearImageTimer = new System.Threading.Timer((o) => { ClearImage(); }, null, 0, 1000 * 60 * 120);
|
}
|
}
|
|
protected override void Stop()
|
{
|
if (clearImageTimer != null)
|
{
|
clearImageTimer.Change(-1, -1);
|
clearImageTimer.Dispose();
|
clearImageTimer = null;
|
}
|
}
|
#endregion
|
|
//public Action<string, string> OnLog;
|
|
/// <summary>
|
/// 运行模式 扫描or拍照
|
/// </summary>
|
public EnumHelper.CameraOpMode OpMode { get; set; } = EnumHelper.CameraOpMode.Snapshot;
|
|
/// <summary>
|
/// 扫描到的图片
|
/// </summary>
|
public virtual object ImageObj { get; set; } = null;
|
|
public AutoResetEvent ImageGetHandle = new AutoResetEvent(false);
|
public AutoResetEvent ImageSaveDoneHandle = new AutoResetEvent(false);
|
|
private Bitmap showImage;
|
/// <summary>
|
/// 输出显示的图片
|
/// </summary>
|
public virtual Bitmap ShowImage
|
{
|
get
|
{
|
return showImage;
|
}
|
set
|
{
|
try
|
{
|
showImage = value;
|
|
if (showImage != null)
|
{
|
UpdateShowImage?.Invoke(this, new Bitmap(showImage), null);
|
}
|
|
if (OpMode == EnumHelper.CameraOpMode.Snapshot)
|
{
|
ImageGetHandle.Set();
|
}
|
}
|
catch (Exception ex)
|
{
|
OnLog?.Invoke(DateTime.Now, this, $"相机异常信息:{ex.GetExceptionMessage()}");
|
}
|
}
|
}
|
|
#region 使用指针操作图像的必须信息
|
///// <summary>
|
///// 图片内容的指针
|
///// </summary>
|
//public IntPtr ImagePtr { get; set; } = IntPtr.Zero;
|
|
//public int ImagePtrSize { get; set; } = 0;
|
|
//public int? ImageWidth
|
//{
|
// get
|
// {
|
// return ShowImage?.Width;
|
// }
|
//}
|
|
//public int? ImageHeight
|
//{
|
// get
|
// {
|
// return ShowImage?.Height;
|
// }
|
//}
|
|
#endregion
|
|
private string imageFilePath;
|
/// <summary>
|
/// 扫描图片保存文件路径
|
/// </summary>
|
public string ImageFilePath
|
{
|
get
|
{
|
return imageFilePath;
|
}
|
set
|
{
|
|
if (imageFilePath != value)
|
{
|
imageFilePath = value;
|
}
|
}
|
}
|
|
|
/// <summary>
|
/// 一次性抓拍
|
/// </summary>
|
/// <returns></returns>
|
public abstract void Snapshot();
|
|
public abstract void Snapshot(out HObject hImage);
|
|
public abstract void Snapshot(IOperationConfig config, out HObject hImage);
|
|
/// <summary>
|
/// 开始扫描
|
/// </summary>
|
/// <returns></returns>
|
public abstract void StartScan();
|
|
/// <summary>
|
/// 结束扫描
|
/// </summary>
|
public abstract void StopScan();
|
|
|
/// <summary>
|
/// 释放图片数据
|
/// </summary>
|
protected virtual void FreeImage()
|
{
|
if (ShowImage != null)
|
{
|
ShowImage.Dispose();
|
}
|
}
|
|
/// <summary>
|
/// 从设备硬件信息获取设备设置参数
|
/// </summary>
|
/// <returns></returns>
|
public abstract IDeviceConfig GetDeviceConfigFromDevice();
|
|
public abstract IOperationConfig GetOperationConfigFromDevice();
|
|
public abstract void UploadOperationConfig(IOperationConfig config);
|
|
public event OnUpdateCameraImageDelegate UpdateShowImage;
|
|
public abstract HalconRelatedCameraInitialConfigBase IConfig { get; }
|
|
protected virtual async void SaveImage(Bitmap map, HalconRelatedCameraOprerationConfigBase opConfig = null)
|
{
|
await Task.Run(() =>
|
{
|
if ((opConfig?.IsSaveImage) ?? false)
|
{
|
string subDir = (opConfig ?? new HalconRelatedCameraOprerationConfigBase()).ImageSaveSubDirectory;
|
if (string.IsNullOrWhiteSpace(subDir))
|
{
|
subDir = DateTime.Now.ToString("yyyyMMdd");
|
}
|
else
|
{
|
subDir = Path.Combine(DateTime.Now.ToString("yyyyMMdd"), subDir);
|
}
|
|
string imgDir = Path.Combine(IConfig.ImgDirectory, subDir);
|
|
DirectoryInfo dir = new DirectoryInfo(imgDir);
|
if (!dir.Exists)
|
{
|
dir.Create();
|
}
|
|
try
|
{
|
//if ((opConfig?.IsSaveImage) ?? false)
|
{
|
//ImageSaveDoneHandle.Reset();
|
|
string fileName = DateTime.Now.ToString("HHmmssfff");
|
|
if (opConfig.ImageType == ImageType.Bmp)
|
{
|
ImageFilePath = Path.Combine(imgDir, $"{fileName}.bmp");
|
map.Save(ImageFilePath, ImageFormat.Bmp);
|
}
|
else
|
{
|
ImageFilePath = Path.Combine(imgDir, $"{fileName}.jpg");
|
map.Save(ImageFilePath, ImageFormat.Jpeg);
|
}
|
|
ImageSaveDoneHandle.Set();
|
}
|
}
|
catch (Exception ex)
|
{
|
}
|
}
|
});
|
}
|
|
#region 定时清理图片
|
static object _clearImageLock = new object();
|
|
protected void ClearImage()
|
{
|
if (IConfig.SaveImageDayLimit > 0)
|
{
|
lock (_clearImageLock)
|
{
|
OnLog?.Invoke(DateTime.Now, this, $"ClearImage Start。 {this.IConfig.CameraIP}");
|
|
try
|
{
|
DirectoryInfo dirImage = new DirectoryInfo(IConfig.ImgDirectory);
|
if (dirImage.Exists)
|
{
|
DateTime dtNow = DateTime.Now;
|
|
var folderList = dirImage.GetDirectories().ToList();
|
|
folderList = folderList.Where(f =>
|
{
|
if (f.Name.Length < 8)
|
return false;
|
|
DateTime dt = DateTime.Now;
|
string name = f.Name.Substring(0, 4) + "/" + f.Name.Substring(4, 2) + "/" + f.Name.Substring(6);
|
return DateTime.TryParse(name, out dt);
|
}).OrderBy(u => u.Name).ToList();
|
|
if (folderList.Count > IConfig.SaveImageDayLimit)
|
{
|
folderList.Take(folderList.Count - IConfig.SaveImageDayLimit).ToList().ForEach(f =>
|
{
|
f.Delete(true);
|
});
|
}
|
}
|
}
|
catch (Exception ex)
|
{
|
OnLog?.Invoke(DateTime.Now, this, $"ClearImage Exception\r\n{ex.GetExceptionMessage()}");
|
}
|
|
OnLog?.Invoke(DateTime.Now, this, $"ClearImage Done\r\n {this.IConfig.CameraIP}");
|
}
|
}
|
}
|
|
private System.Threading.Timer clearImageTimer = null;
|
#endregion
|
}
|
|
public class CameraOperationConfigBase : OperationConfigBase, IComparer<CameraOperationConfigBase>, IComparable<CameraOperationConfigBase>
|
{
|
/// <summary>
|
/// 运行模式
|
/// </summary>
|
public EnumHelper.CameraOpMode OpMode { get; set; } = EnumHelper.CameraOpMode.Snapshot;
|
|
#region 运行参数
|
/// <summary>
|
/// 该参数表示哪些值参与设置
|
/// </summary>
|
public string ConfigItems { get; set; } = "[]"; //= "['BalanceRed','BalanceGreen','BalanceBlue','Exposure','Saturation','Gain','WhiteBalance']";
|
|
[CameraPara("红平衡", "BalanceRed", 2, 1, 1000)]
|
public double BalanceRed { get; set; } = 1;
|
|
[CameraPara("绿平衡", "BalanceGreen", 2, 1, 1000)]
|
public double BalanceGreen { get; set; } = 1;
|
|
[CameraPara("蓝平衡", "BalanceBlue", 2, 1, 1000)]
|
public double BalanceBlue { get; set; } = 1;
|
|
[CameraPara("白平衡", "WhiteBalance", 2, 0, 1000)]
|
public double WhiteBalance { get; set; } = 1;
|
|
[CameraPara("曝光时间", "Exposure", 60000, 1, 1)]
|
public double Exposure { get; set; } = 10;
|
|
[CameraPara("饱和度", "Saturation", 2, 1, 1000)]
|
public double Saturation { get; set; } = 1;
|
|
[CameraPara("增益补偿", "Gain", 200, 0, 1)]
|
public double Gain { get; set; } = 1;
|
|
[CameraPara("采像宽度", "Width", 10000, 0, 1)]
|
public int Width { get; set; } = 0;
|
|
[CameraPara("采像高度", "Height", 10000, 0, 1)]
|
public int Height { get; set; } = 0;
|
|
[ConfigOutputResource("OutputResultCommon")]
|
public override int ResultOutput { get; set; }
|
|
public int Compare(CameraOperationConfigBase x, CameraOperationConfigBase y)
|
{
|
//List<string> yItems = JsonConvert.DeserializeObject<List<string>>(y.ConfigItems);
|
string yItems = y.ConfigItems.Trim(new char[] { '[', ']' });
|
|
if (string.IsNullOrWhiteSpace(yItems))
|
{
|
return 0;
|
}
|
else
|
{
|
if (yItems.Contains("BalanceBlue"))
|
{
|
if (x.BalanceBlue != y.BalanceBlue)
|
{
|
return 1;
|
}
|
}
|
|
if (yItems.Contains("BalanceGreen"))
|
{
|
if (x.BalanceGreen != y.BalanceGreen)
|
{
|
return 1;
|
}
|
}
|
|
if (yItems.Contains("BalanceRed"))
|
{
|
if (x.BalanceRed != y.BalanceRed)
|
{
|
return 1;
|
}
|
}
|
|
if (yItems.Contains("WhiteBalance"))
|
{
|
if (x.WhiteBalance != y.WhiteBalance)
|
{
|
return 1;
|
}
|
}
|
|
if (yItems.Contains("Exposure"))
|
{
|
if (x.Exposure != y.Exposure)
|
{
|
return 1;
|
}
|
}
|
|
if (yItems.Contains("Saturation"))
|
{
|
if (x.Saturation != y.Saturation)
|
{
|
return 1;
|
}
|
}
|
|
if (yItems.Contains("Gain"))
|
{
|
if (x.Gain != y.Gain)
|
{
|
return 1;
|
}
|
}
|
|
if (yItems.Contains("Width"))
|
{
|
if (x.Width != y.Width)
|
{
|
return 1;
|
}
|
}
|
|
if (yItems.Contains("Height"))
|
{
|
if (x.Height != y.Height)
|
{
|
return 1;
|
}
|
}
|
|
return 0;
|
}
|
}
|
|
/// <summary>
|
/// y作为原始数据 当前数据如果ConfigItem为空时,默认无变化
|
/// </summary>
|
/// <param name="y">原始数据</param>
|
/// <returns></returns>
|
public int CompareTo(CameraOperationConfigBase y)
|
{
|
string xItems = ConfigItems.Trim(new char[] { '[', ']' });
|
|
if (string.IsNullOrWhiteSpace(xItems))
|
{
|
return 0;
|
}
|
else
|
{
|
if (xItems.Contains("BalanceBlue"))
|
{
|
if (BalanceBlue != y.BalanceBlue)
|
{
|
return 1;
|
}
|
}
|
|
if (xItems.Contains("BalanceGreen"))
|
{
|
if (BalanceGreen != y.BalanceGreen)
|
{
|
return 1;
|
}
|
}
|
|
if (xItems.Contains("BalanceRed"))
|
{
|
if (BalanceRed != y.BalanceRed)
|
{
|
return 1;
|
}
|
}
|
|
if (xItems.Contains("WhiteBalance"))
|
{
|
if (WhiteBalance != y.WhiteBalance)
|
{
|
return 1;
|
}
|
}
|
|
if (xItems.Contains("Exposure"))
|
{
|
if (Exposure != y.Exposure)
|
{
|
return 1;
|
}
|
}
|
|
if (xItems.Contains("Saturation"))
|
{
|
if (Saturation != y.Saturation)
|
{
|
return 1;
|
}
|
}
|
|
if (xItems.Contains("Gain"))
|
{
|
if (Gain != y.Gain)
|
{
|
return 1;
|
}
|
}
|
|
if (xItems.Contains("Width"))
|
{
|
if (Width != y.Width)
|
{
|
return 1;
|
}
|
}
|
|
if (xItems.Contains("Height"))
|
{
|
if (Height != y.Height)
|
{
|
return 1;
|
}
|
}
|
|
return 0;
|
}
|
}
|
|
public override bool Equals(object obj)
|
{
|
CameraOperationConfigBase cocb = obj as CameraOperationConfigBase;
|
if (cocb == null)
|
{
|
return false;
|
}
|
|
return CompareTo(cocb) == 0;
|
}
|
|
#endregion
|
}
|
|
public class CameraDeviceConfigBase : DeviceConfigBase
|
{
|
[DefaultValue("['MaxNumBuffer','GammaValue','ReverseX','ReverseY']")]
|
public string ConfigItems { get; set; } = "['MaxNumBuffer','GammaValue','ReverseX','ReverseY']";
|
|
public long MaxNumBuffer { get; set; }
|
|
public double GammaValue { get; set; } = double.MinValue;
|
|
public bool ReverseX { get; set; }
|
|
public bool ReverseY { get; set; }
|
}
|
|
public class CameraInitialConfigBase : InitialConfigBase
|
{
|
/// <summary>
|
/// 该参数表示哪些参数值对本相机有效
|
/// </summary>
|
public string EnableItems { get; set; }
|
}
|
|
public class CameraInputConfigBase : InputConfigBase
|
{
|
}
|
|
#region HalconRelatedCameara
|
|
public class HalconRelatedCameraOprerationConfigBase : OperationConfigBase, INotifyPropertyChanged, IComplexDisplay, IHalconToolPath
|
{
|
private float exposure = 0;
|
/// <summary>
|
/// 曝光
|
/// </summary>
|
[Category("取像配置")]
|
[Description("曝光")]
|
public float Exposure
|
{
|
get => exposure;
|
set
|
{
|
if (exposure != value)
|
{
|
exposure = value;
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Exposure"));
|
}
|
}
|
}
|
|
/// <summary>
|
/// 增益
|
/// </summary>
|
[Category("取像配置")]
|
[Description("增益")]
|
public float Gain { get; set; }
|
|
[Category("取像配置")]
|
[Description("曝光修改后等待时间,单位ms。针对部分场景确保曝光修改及时生效。")]
|
public int ExposureWaitTime { get; set; }
|
|
[Category("取像配置")]
|
[Description("拍摄后曝光值。可在一次拍摄后将曝光修改为下次拍摄的曝光值,节约曝光生效时间。")]
|
public float ExposureAfterSnap { get; set; }
|
|
[Browsable(false)]
|
public override int ResultOutput { get; set; }
|
|
[Category("算法配置")]
|
[Description("算法路径")]
|
[Editor(typeof(FileDialogEditor), typeof(UITypeEditor))]
|
public string AlgorithemPath { get; set; }
|
|
[Category("延时配置")]
|
[Description("拍照前延时")]
|
public int DelayBefore { get; set; } = 0;
|
|
[Category("延时配置")]
|
[Description("拍照后延时")]
|
public int DelayAfter { get; set; } = 0;
|
|
[Category("图片保存")]
|
[Description("图片保存文件夹分路径")]
|
public string ImageSaveSubDirectory { get; set; }
|
|
[Category("图片保存")]
|
[Description("是否保存图片")]
|
public bool IsSaveImage { get; set; } = true;
|
|
[Category("图片保存")]
|
[Description("图片保存格式")]
|
public ImageType ImageType { get; set; } = ImageType.Jpeg;
|
|
[Category("图片保存")]
|
[Description("是否保存拟合/结果图片")]
|
public bool IsSaveFitImage { get; set; } = false;
|
|
[Category("图片保存")]
|
[Description("是否保存NG图片")]
|
public bool IsSaveNGImage { get; set; } = false;
|
|
[Category("图片保存")]
|
[Description("拟合/结果图片保存路径")]
|
[Editor(typeof(FoldDialogEditor), typeof(UITypeEditor))]
|
public string FitImagePath { get; set; }
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
public string GetDisplayText()
|
{
|
return $"{(IsEnabled ? "" : "停用 ")}曝光:{Exposure.ToString("f2")}";
|
}
|
|
public List<string> GetHalconToolPathList()
|
{
|
return new List<string>() { AlgorithemPath };
|
}
|
}
|
|
public class HalconRelatedCameraInitialConfigBase : InitialConfigBase
|
{
|
[Category("相机设置")]
|
[Description("驱动类型")]
|
[TypeConverter(typeof(CameraTypeConverter))]
|
public new string DriverType { get; set; } = "HikCamera";
|
|
[Category("相机设置")]
|
[Description("相机序列号")]
|
[TypeConverter(typeof(HalconSerialNumConverter))]
|
public virtual string SerialNum { get; set; }
|
|
[Category("相机设置")]
|
[Description("相机IP地址")]
|
public string CameraIP { get; set; }
|
|
[Category("相机设置")]
|
[Description("上位机IP地址")]
|
public string ComputerIP { get; set; }
|
|
[Category("相机设置")]
|
[Description("视频模式扫描间隔 单位毫秒")]
|
public int ScanInterval { get; set; } = 100;
|
|
[Category("保存设置")]
|
[Description("采图保存目录")]
|
[Editor(typeof(FoldDialogEditor), typeof(UITypeEditor))]
|
public string ImgDirectory { get; set; } = @"../Images";
|
|
[Category("保存设置")]
|
[Description("图片保存天数")]
|
[Browsable(false)]
|
public int SaveImageDayLimit { get; set; } = 0;
|
|
[Category("拍摄设置")]
|
[Description("默认曝光值。相机开启后就设置该曝光值。")]
|
public float DefaultExposure { get; set; }
|
}
|
|
public class HalconSerialNumConverter : ComboBoxItemTypeConvert
|
{
|
public override void GetConvertHash()
|
{
|
HTuple deviceList = null;
|
|
AutoResetEvent handle = new AutoResetEvent(false);
|
|
Task.Run(() =>
|
{
|
HOperatorSet.InfoFramegrabber("GigEVision", "info_boards", out HTuple deviceInfo, out deviceList);
|
handle.Set();
|
});
|
|
handle.WaitOne(5000);
|
if (deviceList != null)
|
{
|
for (int i = 0; i < deviceList.Length; i++)
|
{
|
var list = deviceList[i].S.Split(new char[] { ':', '|' }, StringSplitOptions.RemoveEmptyEntries);
|
|
_hash[list[1].Trim()] = list[3].Trim() + "--" + list[1].Trim();
|
}
|
}
|
}
|
}
|
#endregion
|
|
public class CameraTypeConverter : StringConverter
|
{
|
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
|
{
|
return true;
|
}
|
|
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
|
{
|
return true;
|
}
|
|
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
|
{
|
List<string> devices = DeviceFactory.GetAllSupportDeviceTypeNames();
|
|
devices = devices.Where(u => u.EndsWith("Camera")).ToList();
|
|
return new StandardValuesCollection(devices);
|
}
|
}
|
|
public class CameraInitialConfigEditor : UITypeEditor
|
{
|
public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
|
{
|
return UITypeEditorEditStyle.Modal;
|
}
|
|
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
|
{
|
IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
|
|
if (edSvc != null)
|
{
|
Form form = new Form();
|
form.FormBorderStyle = FormBorderStyle.SizableToolWindow;
|
form.StartPosition = FormStartPosition.CenterParent;
|
form.Text = context.PropertyDescriptor.DisplayName + "--" + context.PropertyDescriptor.Description;
|
|
PropertyGrid pg = new PropertyGrid();
|
pg.Dock = DockStyle.Fill;
|
pg.ToolbarVisible = false;
|
|
string driverType = (value as HalconRelatedCameraInitialConfigBase).DriverType;
|
|
if (!string.IsNullOrWhiteSpace(driverType))
|
{
|
IInitialConfig initial = ConfigFactory.GetInitialConfig(driverType);
|
initial.DataFrom(value);
|
value = initial;
|
}
|
|
pg.SelectedObject = value;
|
|
form.Controls.Add(pg);
|
|
form.ShowDialog();
|
|
value = pg.SelectedObject;
|
return value;
|
}
|
|
return base.EditValue(context, provider, value);
|
}
|
}
|
|
public static class CameraHelper
|
{
|
public static CameraBase GetCameraInstance(string driverType)
|
{
|
return DeviceFactory.GetDeviceInstanceByTypeName(driverType) as CameraBase;
|
}
|
}
|
}
|