using Autofac; using Bro.Common.Helper; using Bro.Common.Interface; using Bro.Common.Model; using HalconDotNet; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Design; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using static Bro.Common.Helper.EnumHelper; namespace Bro.Common.Base { public abstract class CameraBase : DeviceBase { private const int IMAGESETLIFETIME = 10; #region DeviceBase protected override void Start() { if (clearImageTimer == null) { clearImageTimer = new System.Threading.Timer((o) => { ClearImage(); }, null, 0, 1000 * 60 * 120); } //if (clearImageSetTimer == null) //{ // clearImageSetTimer = new System.Threading.Timer((o) => { ClearImageSetPeriodically(); }, null, 0, 1000 * IMAGESETLIFETIME); //} } protected override void Stop() { if (clearImageTimer != null) { clearImageTimer.Change(-1, -1); clearImageTimer.Dispose(); clearImageTimer = null; } //if (clearImageSetTimer != null) //{ // clearImageSetTimer.Change(-1, -1); // clearImageSetTimer.Dispose(); // clearImageSetTimer = null; //} } #endregion #region 配置相关 public virtual CameraInitialConfigBase IConfig { get { return InitialConfig as CameraInitialConfigBase; } } public abstract IOperationConfig GetOperationConfigFromDevice(); public abstract void UploadOperationConfig(IOperationConfig config); #endregion #region 图片相关 public event Action OnImageUpdated; public Action OnHImageOutput { get; set; } public event Action OnCameraOpModeChanged; private EnumHelper.CameraOpMode opMode = CameraOpMode.SingleSnapShot; /// /// 运行模式 扫描or拍照 /// public EnumHelper.CameraOpMode OpMode { get => opMode; set { if (opMode != value) { opMode = value; OnCameraOpModeChanged?.BeginInvoke(null, null); } if (opMode == CameraOpMode.ContinuousMode) { Task.Run(() => { while (CurrentState == DeviceState.DSOpen && opMode == CameraOpMode.ContinuousMode) { Snapshot(); ImageShowedHandle.WaitOne(); } }); } } } public AutoResetEvent ImageShowedHandle = new AutoResetEvent(false); //private Bitmap showImage; /// /// 输出显示的图片 /// //public virtual Bitmap ShowImage //{ // get // { // return showImage; // } // set // { // try // { // showImage = value; // if (showImage != null) // { // OnImageUpdated?.Invoke(this, new Bitmap(showImage)); // } // } // catch (Exception ex) // { // LogAsync(DateTime.Now, $"图片显示异常信息", ex.GetExceptionMessage()); // } // } //} /// /// 一次性抓拍 /// /// public abstract void Snapshot(); public virtual ImageSet Snapshot(IOperationConfig config) { OpMode = CameraOpMode.SingleSnapShot; ImageSet set = new ImageSet(); CameraOprerationConfigBase opConfig = config as CameraOprerationConfigBase; set.ImageSaveOption.DataFrom(opConfig.ImageSaveOption); set.IsOriginSaved = !set.ImageSaveOption.IsSaveOriginImage; set.IsFitSaved = !set.ImageSaveOption.IsSaveFitImage; set.IsAddtionalSaved = !string.IsNullOrWhiteSpace(set.ImageSaveOption.AddtionalSaveType); set.OnImageSetTimeout += ImageSet_OnImageSetTimeout; _imageSetList.Add(set); return set; } /// /// 释放图片数据 /// protected virtual void FreeImage() { //if (ShowImage != null) //{ // ShowImage.Dispose(); //} } #endregion #region 基元处理 public event Action, string> OnElementsUpdated; #endregion #region 图片缓存/保存 readonly ConcurrentBag _imageSetList = new ConcurrentBag(); public void NewImageSet(ImageSet set) { set.IsOriginSaved = !set.ImageSaveOption.IsSaveOriginImage; set.IsFitSaved = !set.ImageSaveOption.IsSaveFitImage; set.IsAddtionalSaved = !string.IsNullOrWhiteSpace(set.ImageSaveOption.AddtionalSaveType); set.OnImageSetTimeout += ImageSet_OnImageSetTimeout; _imageSetList.Add(set); } //public virtual async void UpdateFitElements(List eleList, string imgSetId) //{ // await Task.Run(() => // { // OnElementsUpdated?.BeginInvoke(this, eleList, imgSetId, null, null); // }); //} public virtual async void SaveOriginImage(Bitmap map, Bitmap showImage, string imgSetId) { Task.Run(() => { OnImageUpdated?.Invoke(this, showImage, imgSetId); }); ImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId); if (set == null) return; set.Image = map; await Task.Run(() => { lock (set.SaveLock) { if (set.ImageSaveOption.IsSaveOriginImage && !set.IsOriginSaved) { string imgDir = CheckImageDirectory(set.ImageSaveOption.ImageSaveSubDirectory, "Origin"); try { SaveImageByNameAndType(map, set.Id, set.ImageSaveOption.ImageFormat, imgDir); } catch (Exception ex) { LogAsync(DateTime.Now, $"{set.Id}原图保存失败", ex.GetExceptionMessage()); } //finally //{ // set.IsOriginSaved = true; //} } set.IsOriginSaved = true; ClearImageSet(set); } }); } public virtual async void SaveFitImage(List eleList, string imgSetId) { if (eleList.Count > 0) OnElementsUpdated?.BeginInvoke(this, eleList, imgSetId, null, null); ImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId); if (set == null) return; if (eleList.Count <= 0) { set.IsFitSaved = true; return; } //set.IsInvokeSaveFit = true; await Task.Run(() => { lock (set.SaveLock) { if (set.ImageSaveOption.IsSaveFitImage && !set.IsFitSaved) { string imgDir = CheckImageDirectory(set.ImageSaveOption.ImageSaveSubDirectory, "FitResult"); while (set.Image == null) { Thread.Sleep(50); } try { Bitmap map = new Bitmap(set.Image.Width, set.Image.Height); using (Graphics g = Graphics.FromImage(map)) { g.DrawImage(set.Image, 0, 0); eleList.ForEach(e => { e.State = ElementState.Normal; e.Draw(g); }); } SaveImageByNameAndType(map, set.Id, set.ImageSaveOption.ImageFormat, imgDir); } catch (Exception ex) { LogAsync(DateTime.Now, $"{set.Id}拟合图片保存失败", ex.GetExceptionMessage()); } //finally //{ // set.IsFitSaved = true; //} } set.IsFitSaved = true; ClearImageSet(set); } }); } /// /// 保存特定类型图片,例如NG图片 /// /// 图片类型说明 例如“NG” /// 图片ID public virtual async void SaveSelectedImage(string prefix, string imgSetId) { await Task.Run(() => { ImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId); if (set == null) return; lock (set.SaveLock) { if (set.ImageSaveOption.AddtionalSaveType.Contains(prefix) && !set.IsAddtionalSaved) { string imgDir = CheckImageDirectory(set.ImageSaveOption.ImageSaveSubDirectory, prefix); while (set.Image == null) { Thread.Sleep(50); } try { SaveImageByNameAndType(set.Image, set.Id, set.ImageSaveOption.ImageFormat, imgDir); //set.IsNGSaved = true; } catch (Exception ex) { LogAsync(DateTime.Now, $"{set.Id}{prefix}图片保存失败", ex.GetExceptionMessage()); } //finally //{ // set.IsAddtionalSaved = true; //} } set.IsAddtionalSaved = true; ClearImageSet(set); } }); } private string CheckImageDirectory(string subDir, string prefix) { 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, prefix); DirectoryInfo dir = new DirectoryInfo(imgDir); if (!dir.Exists) { dir.Create(); } return imgDir; } private void SaveImageByNameAndType(Bitmap map, string imageName, ImageFormat imgFormat, string imageDir) { string filePath = Path.Combine(imageDir, $"{imageName}.{imgFormat.ToString().ToLower()}"); map.Save(filePath, imgFormat); } //private Timer clearImageSetTimer = null; public void ClearImageSet(ImageSet set) { try { //if ((!set.ImageSaveOption.IsSaveOriginImage || set.IsOriginSaved) // && (!set.ImageSaveOption.IsSaveFitImage || set.IsFitSaved) // && (!set.ImageSaveOption.IsSaveNGImage || set.IsNGSaved)) if (set.IsOriginSaved && set.IsFitSaved && set.IsAddtionalSaved) { _imageSetList.TryTake(out set); set.Dispose(); //LogAsync(DateTime.Now, $"移除图片信息,当前缓存数量:{_imageSetList.Count}", ""); } //bool flag = false; //do //{ // flag = _imageSetList.TryTake(out set); //} while (!flag); //set.Dispose(); //LogAsync(DateTime.Now, $"移除图片信息,当前缓存数量:{_imageSetList.Count}", ""); } catch (Exception ex) { LogAsync(DateTime.Now, $"清理图片缓存异常,当前缓存数量{_imageSetList.Count}", ex.GetExceptionMessage()); } } public void ClearImageSet(string imgSetId) { ImageSet set = _imageSetList.First(u => u.Id == imgSetId); if (set != null) ClearImageSet(set); } private void ImageSet_OnImageSetTimeout(ImageSet set) { ClearImageSet(set); } //private void ClearImageSetPeriodically() //{ // try // { // DateTime dt = DateTime.Now; // foreach (ImageSet set in _imageSetList) // { // if ((dt - set.InitialTime).TotalSeconds > IMAGESETLIFETIME) // { // ImageSet temp = set; // _imageSetList.TryTake(out temp); // temp.Dispose(); // } // } // } // catch (Exception ex) // { // LogAsync(DateTime.Now, $"定时清理图片缓存异常,当前缓存数量{_imageSetList.Count}", ex.GetExceptionMessage()); // } //} #endregion #region 定时清理图片 static readonly object _clearImageLock = new object(); protected void ClearImage() { if (IConfig.SaveImageDayLimit > 0) { lock (_clearImageLock) { LogAsync(DateTime.Now, $"ClearImage Start", ""); 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) { LogAsync(DateTime.Now, $"ClearImage异常", ex.GetExceptionMessage()); } LogAsync(DateTime.Now, $"ClearImage Done", ""); } } } private System.Threading.Timer clearImageTimer = null; #endregion } #region Config [Device("Camera", "相机取像配置", EnumHelper.DeviceAttributeType.OperationConfig)] public class CameraOprerationConfigBase : OperationConfigBase, INotifyPropertyChanged, IComplexDisplay, IHalconToolPath { private float exposure = 0; /// /// 曝光 /// [Category("取像配置")] [Description("曝光")] [DisplayName("曝光")] public float Exposure { get => exposure; set { if (exposure != value) { exposure = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Exposure")); } } } /// /// 增益 /// [Category("取像配置")] [Description("增益")] [DisplayName("增益")] public float Gain { get; set; } [Category("取像配置")] [Description("曝光修改后等待时间,单位ms。针对部分场景确保曝光修改及时生效。")] [DisplayName("曝光等待")] public int ExposureWaitTime { get; set; } [Category("取像配置")] [Description("拍摄后曝光值。可在一次拍摄后将曝光修改为下次拍摄的曝光值,节约曝光生效时间。")] [DisplayName("拍摄后曝光")] public float ExposureAfterSnap { get; set; } [Category("算法配置")] [Description("算法路径")] [DisplayName("算法路径")] [Editor(typeof(FileDialogEditor), typeof(UITypeEditor))] public string AlgorithemPath { get; set; } = ""; [Category("保存设置")] [Description("图片保存配置")] [DisplayName("图片保存配置")] [TypeConverter(typeof(ComplexObjectConvert))] [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))] public ImageSaveOption ImageSaveOption { get; set; } = new ImageSaveOption(); [Category("延时配置")] [Description("拍照前延时")] [DisplayName("前延时")] public int DelayBefore { get; set; } = 0; [Category("延时配置")] [Description("拍照后延时")] [DisplayName("后延时")] public int DelayAfter { get; set; } = 0; public event PropertyChangedEventHandler PropertyChanged; public string GetDisplayText() { return $"曝光:{Exposure.ToString("f2")} 算法:{AlgorithemPath}"; } public List GetHalconToolPathList() { return new List() { AlgorithemPath }; } } public class CameraInitialConfigBase : InitialConfigBase { [Category("驱动类型")] [Description("驱动类型")] [DisplayName("驱动类型")] [Browsable(true)] [TypeConverter(typeof(CameraTypeConverter))] public override string DriverType { get; set; } [Category("相机设置")] [Description("相机序列号")] [DisplayName("序列号")] [TypeConverter(typeof(HalconSerialNumConverter))] public virtual string SerialNum { get; set; } [Category("相机设置")] [Description("相机IP地址")] [DisplayName("相机IP地址")] public string CameraIP { get; set; } [Category("相机设置")] [Description("上位机IP地址")] [DisplayName("上位机IP地址")] public string ComputerIP { get; set; } [Category("保存设置")] [Description("图片保存目录")] [DisplayName("图片保存目录")] [Editor(typeof(FoldDialogEditor), typeof(UITypeEditor))] public string ImgDirectory { get; set; } = @"../Images"; [Category("保存设置")] [Description("图片保存天数")] [DisplayName("图片保存天数")] public int SaveImageDayLimit { get; set; } = 0; [Category("保存设置")] [Description("图片保存默认配置,主要用于硬触发等没有明确操作配置说明的图片保存")] [DisplayName("图片保存默认配置")] [TypeConverter(typeof(ComplexObjectConvert))] [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))] public ImageSaveOption ImageSaveOption { get; set; } = new ImageSaveOption(); [Category("拍摄设置")] [Description("默认曝光值,相机开启后就设置该曝光值")] [DisplayName("默认曝光值")] public float DefaultExposure { get; set; } } #endregion #region Converter & Editor public class HalconSerialNumConverter : ComboBoxItemTypeConvert { public override Hashtable GetConvertHash(ITypeDescriptorContext context) { Hashtable table = new Hashtable(); 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); table[list[1].Trim()] = list[3].Trim() + "--" + list[1].Trim(); } } return table; } } public class CameraTypeConverter : DeviceTypeConverter { } public class CameraInitialConfigEditor : DeviceInitialConfigEditor { } public class CameraDeviceConverter : ComboBoxItemTypeConvert { public override Hashtable GetConvertHash(ITypeDescriptorContext context) { Hashtable table = new Hashtable(); using (var scope = GlobalVar.Container.BeginLifetimeScope()) { var config = scope.Resolve(); config.CameraConfigCollection.ForEach(camera => { table[camera.Id] = camera.Name; }); } return table; } } #endregion #region 辅助类 public class ImageSaveOption : IComplexDisplay { [Category("图片保存")] [Description("图片保存文件夹子文件夹名称")] [DisplayName("子目录")] public string ImageSaveSubDirectory { get; set; } [Category("图片保存")] [Description("是否保存原始图片")] [DisplayName("保存原始图片")] public bool IsSaveOriginImage { get; set; } = true; [Category("图片保存")] [Description("图片保存格式")] [DisplayName("图片保存格式")] public ImageFormat ImageFormat { get; set; } = ImageFormat.Jpeg; [Category("图片保存")] [Description("是否保存拟合/结果图片")] [DisplayName("保存拟合图片")] public bool IsSaveFitImage { get; set; } = false; //[Category("图片保存")] //[Description("是否保存NG图片")] //[DisplayName("保存NG图片")] //public bool IsSaveNGImage { get; set; } = false; [Category("图片保存")] [Description("附加保存图片类型,多类型使用\",\"间隔。如要保存OK和NG图片,填写\"OK,NG\"")] [DisplayName("附加保存图片类型")] public string AddtionalSaveType { get; set; } = ""; public string GetDisplayText() { //return $"子目录:{ImageSaveSubDirectory}{(IsSaveOriginImage ? " 保存原图" : "")}{(string.IsNullOrWhiteSpace(AddtionalSaveType) ? "" : ("保存"{AddtionalSaveType}"图片"))} {(IsSaveFitImage ? " 保存拟合图片" : "")}"; string txt = ""; if (!string.IsNullOrWhiteSpace(ImageSaveSubDirectory)) { txt += $"子目录:{ImageSaveSubDirectory}"; } if (IsSaveOriginImage) { txt += " 保存原图"; } if (!string.IsNullOrWhiteSpace(AddtionalSaveType)) { txt += $" 保存{AddtionalSaveType}图片"; } if (IsSaveFitImage) { txt += " 保存拟合图片"; } return txt; } } public class ImageSet : IDisposable { public DateTime InitialTime { get; set; } = DateTime.Now; private string id = ""; public string Id { get { if (string.IsNullOrWhiteSpace(id)) { id = InitialTime.ToString("HHmmssfff"); } return id; } } public HImage HImage { get; set; } = null; public Bitmap Image { get; set; } = null; //public List EleList { get; set; } = new List(); //public OutputResult Result { get; set; } = OutputResult.OK; #region Save Option & Result public ImageSaveOption ImageSaveOption { get; set; } = new ImageSaveOption(); public bool IsOriginSaved { get; set; } = false; //public bool IsInvokeSaveFit { get; set; } = false; public bool IsFitSaved { get; set; } = false; //public bool IsInvokeSaveNG { get; set; } = false; public bool IsAddtionalSaved { get; set; } = false; public object SaveLock = new object(); public event Action OnImageSetTimeout; private Timer autoDisposeTimer = null; private void OnAutoDispose(object state) { OnImageSetTimeout?.Invoke(this); } public ImageSet() { autoDisposeTimer = new Timer(OnAutoDispose, null, -1, 10 * 1000); } public void Dispose() { autoDisposeTimer.Change(-1, -1); autoDisposeTimer.Dispose(); Image?.Dispose(); Image = null; HImage?.Dispose(); HImage = null; } #endregion } #endregion }