From dbae9c048fa0cd67c2e1161e5b6b693f87064154 Mon Sep 17 00:00:00 2001
From: wells.liu <wells.liu@broconcentric.com>
Date: 星期三, 01 七月 2020 17:53:15 +0800
Subject: [PATCH] Merge branch 'master' of http://gitblit.broconcentric.com:8088/r/M071

---
 src/Bro.Device.Gocator/GocatorConfig.cs          |    9 
 src/Bro.UI.Model.Winform/UI/CanvasImage.cs       |   10 
 src/Bro.M071.Process/M071Process.cs              |  195 +++++++++++++----
 src/Bro.UI.Model.Winform/UI/Canvas.cs            |   30 +-
 src/Bro.Device.Gocator/Bro.Device.Gocator.csproj |    5 
 src/Bro.Device.Gocator/GocatorDriver.cs          |  263 ++++++++++++++++++++---
 src/Bro.M071.Process/M071Config.cs               |   14 +
 src/Bro.Process/ProcessControl.cs                |    4 
 src/Bro.Common.Device/DeviceBase/CameraBase.cs   |   68 +++--
 src/Bro.M071.Process/M071Models.cs               |   28 ++
 src/Bro.Device.HikCamera/HikCameraDriver.cs      |    6 
 11 files changed, 488 insertions(+), 144 deletions(-)

diff --git a/src/Bro.Common.Device/DeviceBase/CameraBase.cs b/src/Bro.Common.Device/DeviceBase/CameraBase.cs
index b479b33..4204c9b 100644
--- a/src/Bro.Common.Device/DeviceBase/CameraBase.cs
+++ b/src/Bro.Common.Device/DeviceBase/CameraBase.cs
@@ -71,7 +71,7 @@
         #endregion
 
         #region 鍥剧墖鐩稿叧
-        public event Action<CameraBase, Bitmap, string> OnImageUpdated;
+        public Action<CameraBase, Bitmap, string> OnImageUpdated;
 
         public Action<CameraBase, HObject, string> OnHImageOutput { get; set; }
         public event Action OnCameraOpModeChanged;
@@ -141,22 +141,21 @@
         /// </summary>
         /// <returns></returns>
         public abstract void Snapshot();
+        public abstract IImageSet Snapshot(IOperationConfig config);
 
-        public virtual ImageSet Snapshot(IOperationConfig config)
+        public virtual void InitialImageSet(IImageSet set, ImageSaveOption saveOption)
         {
             OpMode = CameraOpMode.SingleSnapShot;
 
-            ImageSet set = new ImageSet();
-            CameraOprerationConfigBase opConfig = config as CameraOprerationConfigBase;
-            set.ImageSaveOption.DataFrom(opConfig.ImageSaveOption);
+            //CameraOprerationConfigBase opConfig = config as CameraOprerationConfigBase;
+            set.ImageSaveOption.DataFrom(saveOption);
 
             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;
+            _imageSetList.Add(set);
         }
 
         /// <summary>
@@ -172,13 +171,13 @@
         #endregion
 
         #region 鍩哄厓澶勭悊
-        public event Action<CameraBase, List<IShapeElement>, string> OnElementsUpdated;
+        public Action<CameraBase, List<IShapeElement>, string> OnElementsUpdated;
         #endregion
 
         #region 鍥剧墖缂撳瓨/淇濆瓨
-        readonly ConcurrentBag<ImageSet> _imageSetList = new ConcurrentBag<ImageSet>();
+        public readonly ConcurrentBag<IImageSet> _imageSetList = new ConcurrentBag<IImageSet>();
 
-        public void NewImageSet(ImageSet set)
+        public void NewImageSet(IImageSet set)
         {
             set.IsOriginSaved = !set.ImageSaveOption.IsSaveOriginImage;
             set.IsFitSaved = !set.ImageSaveOption.IsSaveFitImage;
@@ -202,7 +201,7 @@
                 OnImageUpdated?.Invoke(this, showImage, imgSetId);
             });
 
-            ImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId);
+            IImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId);
 
             if (set == null)
                 return;
@@ -225,10 +224,6 @@
                         {
                             LogAsync(DateTime.Now, $"{set.Id}鍘熷浘淇濆瓨澶辫触", ex.GetExceptionMessage());
                         }
-                        //finally
-                        //{
-                        //    set.IsOriginSaved = true;
-                        //}
                     }
                     set.IsOriginSaved = true;
                     ClearImageSet(set);
@@ -241,7 +236,7 @@
             if (eleList.Count > 0)
                 OnElementsUpdated?.BeginInvoke(this, eleList, imgSetId, null, null);
 
-            ImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId);
+            IImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId);
             if (set == null)
                 return;
 
@@ -305,7 +300,7 @@
         {
             await Task.Run(() =>
             {
-                ImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId);
+                IImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId);
                 if (set == null)
                     return;
 
@@ -341,7 +336,7 @@
             });
         }
 
-        private string CheckImageDirectory(string subDir, string prefix)
+        protected string CheckImageDirectory(string subDir, string prefix)
         {
             if (string.IsNullOrWhiteSpace(subDir))
             {
@@ -363,7 +358,7 @@
             return imgDir;
         }
 
-        private void SaveImageByNameAndType(Bitmap map, string imageName, ImageFormat imgFormat, string imageDir)
+        protected void SaveImageByNameAndType(Bitmap map, string imageName, ImageFormat imgFormat, string imageDir)
         {
             string filePath = Path.Combine(imageDir, $"{imageName}.{imgFormat.ToString().ToLower()}");
             map.Save(filePath, imgFormat);
@@ -371,7 +366,7 @@
 
         //private Timer clearImageSetTimer = null;
 
-        public void ClearImageSet(ImageSet set)
+        public void ClearImageSet(IImageSet set)
         {
             try
             {
@@ -402,7 +397,7 @@
 
         public void ClearImageSet(string imgSetId)
         {
-            ImageSet set = _imageSetList.First(u => u.Id == imgSetId);
+            IImageSet set = _imageSetList.First(u => u.Id == imgSetId);
             if (set != null)
                 ClearImageSet(set);
         }
@@ -548,6 +543,7 @@
         {
             await Task.Run(() =>
             {
+                Thread.Sleep(1000);
                 int widthIn4 = (int)Math.Ceiling(width / 4.0) * 4;
 
                 Bitmap bmp = new Bitmap(widthIn4, height, PixelFormat.Format48bppRgb);
@@ -575,15 +571,14 @@
                       {
                           int index = (i + 1) % width + widthIn4 * ((i + 1) / width) - 1;
 
-                          showBitmapBuffer[index * 6] = data[i * 2];
-                          showBitmapBuffer[index * 6 + 1] = data[i * 2 + 1];
+                          showBitmapBuffer[index * 6] = bitmapBuffer[index * 6] = data[i * 2];
+                          showBitmapBuffer[index * 6 + 1] = bitmapBuffer[index * 6 + 1] = data[i * 2 + 1];
                       });
                 }
-                
+
                 bmp.UnlockBits(bitmapData);
                 showImage.UnlockBits(showImageData);
 
-                //showImage.Save(@"D:\1.bmp", ImageFormat.Bmp);
                 SaveOriginImage(bmp, showImage, imgSetId);
             });
         }
@@ -838,7 +833,24 @@
         }
     }
 
-    public class ImageSet : IDisposable
+    public interface IImageSet : IDisposable
+    {
+        DateTime InitialTime { get; set; }
+        string Id { get; }
+        HImage HImage { get; set; }
+        Bitmap Image { get; set; }
+
+        object SaveLock { get; set; }
+
+        ImageSaveOption ImageSaveOption { get; set; }
+        bool IsOriginSaved { get; set; }
+        bool IsFitSaved { get; set; }
+        bool IsAddtionalSaved { get; set; }
+
+        event Action<ImageSet> OnImageSetTimeout;
+    }
+
+    public class ImageSet : IImageSet
     {
         public DateTime InitialTime { get; set; } = DateTime.Now;
 
@@ -873,7 +885,7 @@
         //public bool IsInvokeSaveNG { get; set; } = false;
         public bool IsAddtionalSaved { get; set; } = false;
 
-        public object SaveLock = new object();
+        public object SaveLock { get; set; } = new object();
 
         public event Action<ImageSet> OnImageSetTimeout;
         private Timer autoDisposeTimer = null;
@@ -888,7 +900,7 @@
             autoDisposeTimer = new Timer(OnAutoDispose, null, -1, 10 * 1000);
         }
 
-        public void Dispose()
+        public virtual void Dispose()
         {
             autoDisposeTimer.Change(-1, -1);
             autoDisposeTimer.Dispose();
diff --git a/src/Bro.Device.Gocator/Bro.Device.Gocator.csproj b/src/Bro.Device.Gocator/Bro.Device.Gocator.csproj
index ea7b73b..8a7b1b1 100644
--- a/src/Bro.Device.Gocator/Bro.Device.Gocator.csproj
+++ b/src/Bro.Device.Gocator/Bro.Device.Gocator.csproj
@@ -57,11 +57,16 @@
     <Reference Include="GoSdkNet">
       <HintPath>..\..\libs\gocator\GoSdkNet.dll</HintPath>
     </Reference>
+    <Reference Include="halcondotnet, Version=12.0.0.0, Culture=neutral, PublicKeyToken=4973bed59ddbf2b8, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\libs\halcon12\halcondotnet.dll</HintPath>
+    </Reference>
     <Reference Include="kApiNet">
       <HintPath>..\..\libs\gocator\kApiNet.dll</HintPath>
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Drawing" />
     <Reference Include="System.Windows.Forms" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
diff --git a/src/Bro.Device.Gocator/GocatorConfig.cs b/src/Bro.Device.Gocator/GocatorConfig.cs
index b6d73ac..bc3053d 100644
--- a/src/Bro.Device.Gocator/GocatorConfig.cs
+++ b/src/Bro.Device.Gocator/GocatorConfig.cs
@@ -24,6 +24,7 @@
         [Category("璁$畻璁剧疆")]
         [Description("true锛氳绠楁椂浣跨敤鍔犻�熷櫒锛宖alse锛氫笉浣跨敤鍔犻�熷櫒")]
         [DefaultValue(false)]
+        [Browsable(false)]
         public bool IsUseAccelerator { get; set; } = false;
 
         [Category("鎷嶆憚璁剧疆")]
@@ -37,10 +38,10 @@
         [DefaultValue(1000)]
         public int SnapshotTimeout { get; set; } = 1000;
 
-        //[Category("閲囧浘妯″紡")]
-        //[Description("鏄惁纭Е鍙戞ā寮忋�倀rue锛氱‖瑙﹀彂锛沠alse锛氳蒋瑙﹀彂")]
-        //[DisplayName("纭Е鍙�")]
-        //public bool IsHardwareTrigger { get; set; } = false;
+        [Category("閲囧浘妯″紡")]
+        [Description("寮傛閲囧浘鏃讹紝鏄惁纭Е鍙戞ā寮忋�倀rue锛氱‖瑙﹀彂锛沠alse锛氳蒋瑙﹀彂")]
+        [DisplayName("纭Е鍙�")]
+        public bool IsHardwareTrigger { get; set; } = false;
 
         [Category("閲囧浘妯″紡")]
         [Description("true锛氬紓姝�/琚姩閲囧浘妯″紡  false锛氬悓姝�/涓诲姩閲囧浘妯″紡")]
diff --git a/src/Bro.Device.Gocator/GocatorDriver.cs b/src/Bro.Device.Gocator/GocatorDriver.cs
index bdfac10..39fb4a2 100644
--- a/src/Bro.Device.Gocator/GocatorDriver.cs
+++ b/src/Bro.Device.Gocator/GocatorDriver.cs
@@ -1,16 +1,21 @@
 锘縰sing Bro.Common.Base;
 using Bro.Common.Helper;
 using Bro.Common.Interface;
+using HalconDotNet;
 using Lmi3d.GoSdk;
 using Lmi3d.GoSdk.Messages;
 using Lmi3d.Zen;
 using Lmi3d.Zen.Io;
 using System;
 using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
 using System.Linq;
 using System.Runtime.InteropServices;
 using System.Text;
+using System.Threading;
 using System.Threading.Tasks;
+using static Bro.Common.Helper.EnumHelper;
 
 namespace Bro.Device.Gocator
 {
@@ -30,14 +35,23 @@
 
         public override void Snapshot()
         {
+            GoDataSet dataSet = null;
             if (IIConfig.IsAsyncMode)
             {
+                if (!IIConfig.IsHardwareTrigger)
+                {
+                    _snapFlag = true;
+                    if (!_snapHandle.WaitOne(IIConfig.SnapshotTimeout))
+                    {
+                        LogAsync(DateTime.Now, $"{Name}鑾峰彇鍥惧儚瓒呮椂", "");
+                        return;
+                    }
 
+                    dataSet = _currentData;
+                }
             }
             else
             {
-                GoDataSet dataSet = null;
-
                 try
                 {
                     dataSet = system.ReceiveData(IIConfig.SnapshotTimeout);
@@ -47,33 +61,23 @@
                     LogAsync(DateTime.Now, $"{Name}鑾峰彇鍥惧儚寮傚父", ex.GetExceptionMessage());
                     return;
                 }
-
-                HandleGoData(dataSet);
             }
+
+            HandleGoData(dataSet);
         }
 
-        private void HandleGoData(GoDataSet dataSet)
+        private void HandleGoData(GoDataSet dataSet, GoImageSet imgSet = null)
         {
             if (dataSet == null)
+            {
                 return;
+            }
 
             for (UInt32 i = 0; i < dataSet.Count; i++)
             {
                 GoDataMsg dataObj = (GoDataMsg)dataSet.Get(i);
                 switch (dataObj.MessageType)
                 {
-                    //case GoDataMessageType.Stamp:
-                    //    {
-                    //        GoStampMsg stampMsg = (GoStampMsg)dataObj;
-                    //        for (UInt32 j = 0; j < stampMsg.Count; j++)
-                    //        {
-                    //            GoStamp stamp = stampMsg.Get(j);
-                    //            Console.WriteLine("Frame Index = {0}", stamp.FrameIndex);
-                    //            Console.WriteLine("Time Stamp = {0}", stamp.Timestamp);
-                    //            Console.WriteLine("Encoder Value = {0}", stamp.Encoder);
-                    //        }
-                    //    }
-                    //    break;
                     case GoDataMessageType.Surface:
                         {
                             GoSurfaceMsg surfaceMsg = (GoSurfaceMsg)dataObj;
@@ -82,43 +86,83 @@
                             long bufferSize = width * height;
                             IntPtr bufferPointer = surfaceMsg.Data;
 
-                            //Console.WriteLine("Whole Part Height Map received:");
-                            //Console.WriteLine(" Buffer width: {0}", width);
-                            //Console.WriteLine(" Buffer height: {0}", height);
+                            if (imgSet != null)
+                            {
+                                imgSet.HImage = new HImage();
+                                imgSet.HImage.GenImage1("uint2", (int)width, (int)height, bufferPointer);
 
-                            //short[] ranges = new short[bufferSize];
-                            //Marshal.Copy(bufferPointer, ranges, 0, ranges.Length);
+                                imgSet.HImage_2 = new HImage();
+                                imgSet.HImage_2.GenImage1("uint2", (int)width, (int)height, bufferPointer);
+                            }
 
-                            Generate16GrayImageByPointer((int)(width / 1), (int)(height / 1), bufferPointer, "");
+                            Generate16GrayImageByPointer((int)width, (int)height, bufferPointer, imgSet?.Id);
                         }
                         break;
-                        //case GoDataMessageType.SurfaceIntensity:
-                        //    {
-                        //        GoSurfaceIntensityMsg surfaceMsg = (GoSurfaceIntensityMsg)dataObj;
-                        //        long width = surfaceMsg.Width;
-                        //        long height = surfaceMsg.Length;
-                        //        long bufferSize = width * height;
-                        //        IntPtr bufferPointeri = surfaceMsg.Data;
-
-                        //        //Console.WriteLine("Whole Part Intensity Image received:");
-                        //        //Console.WriteLine(" Buffer width: {0}", width);
-                        //        //Console.WriteLine(" Buffer height: {0}", height);
-                        //        //byte[] ranges = new byte[bufferSize];
-                        //        //Marshal.Copy(bufferPointeri, ranges, 0, ranges.Length);
-                        //        //GenerateGrayImageByPointer((int)width, (int)height, bufferPointeri, "");
-                        //    }
-                        //    break;
                 }
             }
         }
 
-        public override ImageSet Snapshot(IOperationConfig config)
+        public override IImageSet Snapshot(IOperationConfig config)
         {
-            ImageSet imgSet = base.Snapshot(config);
+            if (!IIConfig.IsAsyncMode)
+            {
+                if (config is GocatorOperationConfig opConfig)
+                {
+                    if (opConfig.IsOpenConnection)
+                    {
+                        system.Start();
+                    }
+                    else
+                    {
+                        system.Stop();
+                    }
+
+                    if (!opConfig.IsSnapshotAction)
+                    {
+                        return null;
+                    }
+                }
+            }
+
+            GoImageSet imgSet = new GoImageSet();
+            InitialImageSet(imgSet, (config as CameraOprerationConfigBase).ImageSaveOption);
+
+            GoDataSet dataSet = null;
+            if (IIConfig.IsAsyncMode)
+            {
+                if (!IIConfig.IsHardwareTrigger)
+                {
+                    _snapFlag = true;
+                    if (!_snapHandle.WaitOne(IIConfig.SnapshotTimeout))
+                    {
+                        LogAsync(DateTime.Now, $"{Name}鑾峰彇鍥惧儚瓒呮椂", "");
+                        return null;
+                    }
+
+                    dataSet = _currentData;
+                }
+            }
+            else
+            {
+                try
+                {
+                    dataSet = system.ReceiveData(IIConfig.SnapshotTimeout);
+                }
+                catch (Exception ex)
+                {
+                    LogAsync(DateTime.Now, $"{Name}鑾峰彇鍥惧儚寮傚父", ex.GetExceptionMessage());
+                    return null;
+                }
+            }
+
+            HandleGoData(dataSet, imgSet);
+            if (imgSet.HImage == null)
+            {
+                LogAsync(DateTime.Now, $"{Name}鏈兘鑾峰彇HImage鍥惧儚", "");
+            }
 
             return imgSet;
         }
-
 
         float _currentExposure = 0;
         string _currentJob = "";
@@ -193,7 +237,15 @@
             _currentJob = sensor.DefaultJob;
             if (!string.IsNullOrWhiteSpace(IIConfig.DefaultJob) && _currentJob != IIConfig.DefaultJob)
             {
-                _currentJob = sensor.DefaultJob = IIConfig.DefaultJob;
+                //_currentJob = sensor.DefaultJob = IIConfig.DefaultJob;
+                string currentJob = IIConfig.DefaultJob;
+                bool isChanged = false;
+                sensor.LoadedJob(ref currentJob, ref isChanged);
+
+                if (!isChanged)
+                {
+                    throw new ProcessException($"{Name}鏈垚鍔熷垏鎹㈣嚦浠诲姟{IIConfig.DefaultJob}");
+                }
             }
 
             sensor.Flush();
@@ -240,6 +292,10 @@
             get => InitialConfig as GocatorInitialConfig;
         }
 
+        volatile bool _snapFlag = false;
+        readonly ManualResetEvent _snapHandle = new ManualResetEvent(false);
+
+        GoDataSet _currentData = null;
         /// <summary>
         /// 寮傛妯″紡鑾峰彇鏁版嵁
         /// </summary>
@@ -247,6 +303,127 @@
         private void onData(KObject data)
         {
             GoDataSet dataSet = (GoDataSet)data;
+
+            if (IIConfig.IsHardwareTrigger)
+            {
+                GoImageSet imgSet = new GoImageSet();
+                InitialImageSet(imgSet, IConfig.ImageSaveOption);
+
+                HandleGoData(dataSet, imgSet);
+
+                if (imgSet.HImage != null)
+                {
+                    OnHImageOutput?.BeginInvoke(this, imgSet.HImage, imgSet.Id, null, null);
+                }
+                else
+                {
+                    LogAsync(DateTime.Now, $"{Name}鑾峰彇HImage澶辫触", "");
+                }
+            }
+            else
+            {
+                if (_snapFlag)
+                {
+                    _snapFlag = false;
+
+                    _currentData = dataSet.Clone<GoDataSet>();
+                    _snapHandle.Set();
+                }
+            }
+        }
+
+        #region 閲嶅啓鍥剧墖淇濆瓨鎿嶄綔
+        public override async void SaveOriginImage(Bitmap map, Bitmap showImage, string imgSetId)
+        {
+            Task.Run(() =>
+            {
+                OnImageUpdated?.Invoke(this, showImage, imgSetId);
+            });
+
+            GoImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId) as GoImageSet;
+
+            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);
+                            string filePath = Path.Combine(imgDir, $"{set.Id}.tif");
+                            set.HImage_2.WriteImage("tiff", 0, filePath);
+                        }
+                        catch (Exception ex)
+                        {
+                            LogAsync(DateTime.Now, $"{set.Id}鍘熷浘淇濆瓨澶辫触", ex.GetExceptionMessage());
+                        }
+                    }
+                    set.IsOriginSaved = true;
+                    ClearImageSet(set);
+                }
+            });
+        }
+
+        /// <summary>
+        /// 淇濆瓨鐗瑰畾绫诲瀷鍥剧墖锛屼緥濡侼G鍥剧墖
+        /// </summary>
+        /// <param name="prefix">鍥剧墖绫诲瀷璇存槑 渚嬪鈥淣G鈥�</param>
+        /// <param name="imgSetId">鍥剧墖ID</param>
+        public override async void SaveSelectedImage(string prefix, string imgSetId)
+        {
+            await Task.Run(() =>
+            {
+                GoImageSet set = _imageSetList.FirstOrDefault(u => u.Id == imgSetId) as GoImageSet;
+                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
+                        {
+                            string filePath = Path.Combine(imgDir, $"{set.Id}.tif");
+                            set.HImage_2.WriteImage("tiff", 0, filePath);
+                        }
+                        catch (Exception ex)
+                        {
+                            LogAsync(DateTime.Now, $"{set.Id}{prefix}鍥剧墖淇濆瓨澶辫触", ex.GetExceptionMessage());
+                        }
+                    }
+                    set.IsAddtionalSaved = true;
+                    ClearImageSet(set);
+                }
+            });
+        }
+        #endregion
+    }
+
+    public class GoImageSet : ImageSet
+    {
+        public HImage HImage_2 { get; set; }
+
+        public override void Dispose()
+        {
+            base.Dispose();
+
+            HImage_2?.Dispose();
+            HImage_2 = null;
         }
     }
 }
diff --git a/src/Bro.Device.HikCamera/HikCameraDriver.cs b/src/Bro.Device.HikCamera/HikCameraDriver.cs
index 02864f5..f308873 100644
--- a/src/Bro.Device.HikCamera/HikCameraDriver.cs
+++ b/src/Bro.Device.HikCamera/HikCameraDriver.cs
@@ -392,9 +392,11 @@
             }
         }
 
-        public override ImageSet Snapshot(IOperationConfig config)
+        public override IImageSet Snapshot(IOperationConfig config)
         {
-            ImageSet set = base.Snapshot(config);
+            ImageSet set = new ImageSet();
+            InitialImageSet(set, (config as CameraOprerationConfigBase).ImageSaveOption);
+
             MyCamera.MV_FRAME_OUT frameInfo = new MyCamera.MV_FRAME_OUT();
             nRet = MyCamera.MV_OK;
 
diff --git a/src/Bro.M071.Process/M071Config.cs b/src/Bro.M071.Process/M071Config.cs
index 2cdacf0..b3ae3f3 100644
--- a/src/Bro.M071.Process/M071Config.cs
+++ b/src/Bro.M071.Process/M071Config.cs
@@ -1,4 +1,5 @@
-锘縰sing Bro.Common.Helper;
+锘縰sing Bro.Common.Base;
+using Bro.Common.Helper;
 using Bro.Common.Model;
 using Bro.Process;
 using System;
@@ -62,5 +63,16 @@
         [TypeConverter(typeof(CollectionCountConvert))]
         [Editor(typeof(ComplexCollectionEditor<MeasurementUint>), typeof(UITypeEditor))]
         public List<MeasurementUint> MeasurementUnitCollection { get; set; } = new List<MeasurementUint>();
+
+        [Category("鍥剧墖淇濆瓨閰嶇疆")]
+        [Description("鍗曢敭鍥剧墖淇濆瓨閰嶇疆")]
+        [TypeConverter(typeof(ComplexObjectConvert))]
+        [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))]
+        public ImageSaveOption ImageSaveOption { get; set; } = new ImageSaveOption();
+
+        [Category("鍥剧墖淇濆瓨閰嶇疆")]
+        [Description("鍗曢敭鍥剧墖淇濆瓨鐩綍璺緞")]
+        [Editor(typeof(FoldDialogEditor),typeof(UITypeEditor))]
+        public string ImageSaveFolder { get; set; } = "";
     }
 }
diff --git a/src/Bro.M071.Process/M071Models.cs b/src/Bro.M071.Process/M071Models.cs
index be596b7..d9a40ac 100644
--- a/src/Bro.M071.Process/M071Models.cs
+++ b/src/Bro.M071.Process/M071Models.cs
@@ -3,6 +3,7 @@
 using Bro.Common.Helper;
 using Bro.Common.Interface;
 using Bro.Common.Model;
+using HalconDotNet;
 using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
@@ -11,6 +12,7 @@
 using System.Drawing.Design;
 using System.Linq;
 using System.Text;
+using System.Threading;
 using System.Threading.Tasks;
 
 namespace Bro.M071.Process
@@ -195,18 +197,20 @@
 
     public class ProductionMeasurement : INotifyPropertyChanged, IDisposable
     {
-        public string Barcode { get; set; }
+        public string Barcode;
 
-        public List<MeasurementUint> Measurements { get; set; } = new List<MeasurementUint>();
+        public List<MeasurementUint> Measurements = new List<MeasurementUint>();
+
+        public List<IShapeElement> ElementList = new List<IShapeElement>();
 
         public event PropertyChangedEventHandler PropertyChanged;
 
         public void Dispose()
         {
-            Barcode = null;
-
             Measurements?.ForEach(m => m?.Dispose());
             Measurements = null;
+
+            Barcode = null;
             GC.Collect();
         }
 
@@ -304,6 +308,10 @@
         [TypeConverter(typeof(KeyUnitResultConverter))]
         public string KeyResult { get; set; } = "";
 
+
+        public List<HImage> KeyImages = new List<HImage>();
+
+        public volatile int ImageSaveStatus = 0;
         //[Browsable(false)]
         //public NoticedDictionary<string, double?> MeasureValueDict { get; set; } = new NoticedDictionary<string, double?>();
 
@@ -335,6 +343,18 @@
 
         public void Dispose()
         {
+            SpinWait wait = new SpinWait();
+            while (ImageSaveStatus != 0)
+            {
+                wait.SpinOnce();
+            }
+
+            KeyImages?.ForEach(i =>
+            {
+                i?.Dispose();
+                i = null;
+            });
+            KeyImages = null;
             MeasureValueDict = null;
         }
 
diff --git a/src/Bro.M071.Process/M071Process.cs b/src/Bro.M071.Process/M071Process.cs
index fbdd8fe..2667c4f 100644
--- a/src/Bro.M071.Process/M071Process.cs
+++ b/src/Bro.M071.Process/M071Process.cs
@@ -7,6 +7,7 @@
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Drawing;
 using System.IO;
 using System.Linq;
 using System.Text;
@@ -200,11 +201,17 @@
                       if (camera == null)
                           return;
 
-                      ImageSet imgSet = camera.Snapshot(s.CameraOp.OpConfig);
-                      if (imgSet == null)
-                          return;
+                      //IImageSet imgSet = camera.Snapshot(s.CameraOp.OpConfig);
+                      //if (imgSet == null)
+                      //    return;
 
-                      RunImageHandle(camera, s.CameraOp.OpConfig, imgSet, s.Id, s.Name, pMeasure.Measurements);
+                      HImage hImage = CollectHImage(camera, s.CameraOp.OpConfig, out string imgSetId);
+                      if (string.IsNullOrWhiteSpace(imgSetId))
+                      {
+                          return;
+                      }
+
+                      RunImageHandle(camera, s.CameraOp.OpConfig, hImage, s.Id, s.Name, pMeasure.Measurements);
                   });
 
             BarCode = "";
@@ -243,72 +250,163 @@
             _pauseHandle.WaitResult = !_pauseHandle.WaitResult;
             return new ProcessResponse(_pauseHandle.WaitResult);
         }
+
         #region 绉佹湁鏂规硶
         private void MeasureProduction_PropertyChanged(object sender, PropertyChangedEventArgs e)
         {
             if (sender is ProductionMeasurement pMeasure)
             {
-                //妫�鏌ユ槸鍚﹀叏閮ㄥ畬鎴�
-                pMeasure.Measurements.ForEach(m =>
+                lock (pMeasure)
                 {
-                    if (m.KeyUnitCollection.All(k => k.IsDone != null))
+                    //妫�鏌ユ槸鍚﹀叏閮ㄥ畬鎴�
+                    pMeasure.Measurements.ForEach(m =>
                     {
-                        if (!m.IsUpdated)
+                        if (m.KeyUnitCollection.All(k => k.IsDone != null))
                         {
-                            if (m.KeyUnitCollection.Any(k => k.IsDone == false))
+                            if (!m.IsUpdated)
                             {
-                                m.Spec.ActualValue = -999;
-                            }
-                            else
-                            {
-                                string toolKey = m.Id + "|" + m.Spec.AlgorithemPath;
-                                if (!_halconToolDict.ContainsKey(toolKey))
+                                if (m.KeyUnitCollection.Any(k => k.IsDone == false))
                                 {
-                                    LogAsync(DateTime.Now, $"{m.GetDisplayText()}{m.Spec.Code}绠楁硶鏈垵濮嬪寲", "");
                                     m.Spec.ActualValue = -999;
                                 }
                                 else
                                 {
-                                    var array = m.KeyUnitCollection.SelectMany(u => u.MeasureValueDict.Values.ToList().ConvertAll(v => v ?? -999)).ToArray();
-                                    _halconToolDict[toolKey].InputTupleDic["INPUT_Params"] = new HTuple(array);
-                                    if (!_halconToolDict[toolKey].RunProcedure(out string error))
+                                    string toolKey = m.Id + "|" + m.Spec.AlgorithemPath;
+                                    if (!_halconToolDict.ContainsKey(toolKey))
                                     {
-                                        LogAsync(DateTime.Now, $"{m.GetDisplayText()}{m.Spec.Code}绠楁硶寮傚父,{error}", "");
+                                        LogAsync(DateTime.Now, $"{m.GetDisplayText()}{m.Spec.Code}绠楁硶鏈垵濮嬪寲", "");
                                         m.Spec.ActualValue = -999;
                                     }
                                     else
                                     {
-                                        m.Spec.ActualValue = _halconToolDict[toolKey].GetResultTuple("OUTPUT_Result").D;
-                                        LogAsync(DateTime.Now, $"{m.GetDisplayText()}鏁版嵁{m.Spec.ActualValue}锛岀粨鏋渰(m.Spec.MeasureResult == null ? "TBD" : (m.Spec.MeasureResult == true ? "OK" : "NG"))}", "");
+                                        var array = m.KeyUnitCollection.SelectMany(u => u.MeasureValueDict.Values.ToList().ConvertAll(v => v ?? -999)).ToArray();
+                                        _halconToolDict[toolKey].InputTupleDic["INPUT_Params"] = new HTuple(array);
+                                        if (!_halconToolDict[toolKey].RunProcedure(out string error))
+                                        {
+                                            LogAsync(DateTime.Now, $"{m.GetDisplayText()}{m.Spec.Code}绠楁硶寮傚父,{error}", "");
+                                            m.Spec.ActualValue = -999;
+                                        }
+                                        else
+                                        {
+                                            m.Spec.ActualValue = _halconToolDict[toolKey].GetResultTuple("OUTPUT_Result").D;
+                                            LogAsync(DateTime.Now, $"{m.GetDisplayText()}鏁版嵁{m.Spec.ActualValue}锛岀粨鏋渰(m.Spec.MeasureResult == null ? "TBD" : (m.Spec.MeasureResult == true ? "OK" : "NG"))}", "");
+                                        }
                                     }
                                 }
+
+                                //杈撳嚭鍥惧舰鍩哄厓鍒扮晫闈� todo
+                                OnElementUpdated?.BeginInvoke(null, null, null);
+
+                                SaveKeyImages(pMeasure.Barcode, m);
+
+                                m.IsUpdated = true;
                             }
-
-                            //杈撳嚭鍥惧舰鍩哄厓鍒扮晫闈� todo
-                            OnElementUpdated?.BeginInvoke(null, null, null);
-
-                            m.IsUpdated = true;
                         }
+                    });
+
+                    if (!pMeasure.Measurements.All(m => m.IsUpdated))
+                    {
+                        return;
                     }
-                });
 
-                if (!pMeasure.Measurements.All(m => m.IsUpdated))
-                {
-                    return;
+
+
+                    //MES杈撳嚭 todo
+
+                    //Excel鎶ヨ〃杈撳嚭 todo
+
+                    //鏁版嵁搴撲繚瀛� todo
+
+                    SaveWholeImage(pMeasure);
+                    pMeasure.Dispose();
                 }
-
-                //MES杈撳嚭 todo
-
-                //Excel鎶ヨ〃杈撳嚭 todo
-
-                //鏁版嵁搴撲繚瀛� todo
-
-                //MeasureDict.Remove(pMeasure.Barcode);
-                pMeasure.Dispose();
             }
         }
 
-        private async void RunImageHandle(CameraBase camera, IOperationConfig opConfig, ImageSet imgSet, string snapshotId, string snapshotName, List<MeasurementUint> measureList)
+        #region 鍥惧儚淇濆瓨
+        private void SaveWholeImage(ProductionMeasurement pMeasure)
+        {
+            try
+            {
+                Bitmap backImage = (Bitmap)Bitmap.FromFile(Config.BackgroundImagePath);
+
+                Bitmap map = new Bitmap(backImage.Width, backImage.Height);
+                using (Graphics g = Graphics.FromImage(map))
+                {
+                    g.DrawImage(backImage, new PointF(0, 0));
+
+                    pMeasure.ElementList.ForEach(e =>
+                    {
+                        e.Draw(g);
+                    });
+                }
+
+                string dir = Path.Combine(Config.ImageSaveFolder, "TopView", DateTime.Now.ToString("yyyyMMdd"));
+                if (!Directory.Exists(dir))
+                {
+                    Directory.CreateDirectory(dir);
+                }
+
+                map.Save(Path.Combine(dir, $"{pMeasure.Barcode}_{DateTime.Now.ToString("HHmmss")}.bmp"));
+            }
+            catch (Exception ex)
+            {
+                LogAsync(DateTime.Now, "鏁翠綋鍥剧墖淇濆瓨寮傚父", ex.GetExceptionMessage());
+            }
+        }
+
+        private void SaveKeyImages(string barCode, MeasurementUint measureUnit)
+        {
+            string measureName = measureUnit.GetDisplayText();
+            if (Config.ImageSaveOption.IsSaveOriginImage)
+            {
+                measureUnit.KeyUnitCollection.ForEach(u => u.ImageSaveStatus++);
+
+                string dir = Path.Combine(Config.ImageSaveFolder, "Origin", DateTime.Now.ToString("yyyyMMdd"), barCode, measureUnit.MeasureType);
+                if (!Directory.Exists(dir))
+                {
+                    Directory.CreateDirectory(dir);
+                }
+
+                SaveKeyImages(measureUnit, measureName, dir);
+            }
+
+            string result = (measureUnit.Spec.MeasureResult ?? false) ? "OK" : "NG";
+            if (Config.ImageSaveOption.AddtionalSaveType.ToUpper().Contains(result))
+            {
+                measureUnit.KeyUnitCollection.ForEach(u => u.ImageSaveStatus++);
+
+                string dir = Path.Combine(Config.ImageSaveFolder, result, DateTime.Now.ToString("yyyyMMdd"), barCode, measureUnit.MeasureType);
+                if (!Directory.Exists(dir))
+                {
+                    Directory.CreateDirectory(dir);
+                }
+
+                SaveKeyImages(measureUnit, measureName, dir);
+            }
+        }
+
+        private async void SaveKeyImages(MeasurementUint measureUnit, string measureName, string dir)
+        {
+            await Task.Run(() =>
+            {
+                measureUnit.KeyUnitCollection.ForEach(u =>
+                {
+                    int i = 0;
+                    u.KeyImages?.ForEach(image =>
+                    {
+                        string fileName = Path.Combine(dir, $"{measureName}_{u.Key}{(i == 0 ? "" : $"-{i}")}_{DateTime.Now.ToString("HHmmss")}.tiff");
+                        image.WriteImage("tiff", 0, fileName);
+                        i++;
+                    });
+
+                    u.ImageSaveStatus--;
+                });
+            });
+        }
+        #endregion
+
+        private async void RunImageHandle(CameraBase camera, IOperationConfig opConfig, HImage hImage, string snapshotId, string snapshotName, List<MeasurementUint> measureList)
         {
             await Task.Run(() =>
              {
@@ -316,7 +414,7 @@
                  var keyBindCollection = measureList.SelectMany(u => u.KeyUnitCollection).Where(u => keys.Any(k => k.Key == u.Key)).ToList();
 
                  string toolKey = (opConfig as CameraOprerationConfigBase).AlgorithemPath;
-                 HObject images = imgSet.HImage;
+                 HObject images = hImage;
 
                  if (!string.IsNullOrWhiteSpace(toolKey))
                  {
@@ -328,7 +426,7 @@
                          return;
                      }
 
-                     _halconToolDict[toolKey].InputImageDic["INPUT_Image"] = imgSet.HImage;
+                     _halconToolDict[toolKey].InputImageDic["INPUT_Image"] = hImage;
                      if (!_halconToolDict[toolKey].RunProcedure(out string error))
                      {
                          LogAsync(DateTime.Now, $"{snapshotName}鍙栧浘绠楁硶寮傚父锛寋error}", "");
@@ -398,14 +496,21 @@
                                  }
                              }
 
-                             keyBindList.ForEach(kb => kb.FillKeyValues(resultDict));
+                             keyBindList.ForEach(kb =>
+                             {
+                                 kb.KeyImages.Add(image.Clone() as HImage);
+                                 kb.FillKeyValues(resultDict);
+                             });
                          });
 
                      image.Dispose();
                  });
 
                  if (count.I != 1)
-                     imgSet.HImage.Dispose();
+                 {
+                     hImage?.Dispose();
+                     hImage = null;
+                 }
              });
         }
         #endregion
diff --git a/src/Bro.Process/ProcessControl.cs b/src/Bro.Process/ProcessControl.cs
index 04271dd..35c3af7 100644
--- a/src/Bro.Process/ProcessControl.cs
+++ b/src/Bro.Process/ProcessControl.cs
@@ -581,7 +581,7 @@
         #region 鍥惧儚澶勭悊
         protected HImage CollectHImage(CameraBase camera, IOperationConfig opConfig, out string imgSetId, [CallerMemberName]string methodCode = "")
         {
-            ImageSet set = null;
+            IImageSet set = null;
 
             if (IConfig.IsImageOffline)
             {
@@ -628,7 +628,7 @@
                 TimeRecordCSV(DateTime.Now, camera.Name, methodCode + "閲囧浘", (int)sw.ElapsedMilliseconds);
             }
 
-            imgSetId = set.Id;
+            imgSetId = set?.Id;
             return set.HImage;
         }
 
diff --git a/src/Bro.UI.Model.Winform/UI/Canvas.cs b/src/Bro.UI.Model.Winform/UI/Canvas.cs
index feb020d..40f2b3c 100644
--- a/src/Bro.UI.Model.Winform/UI/Canvas.cs
+++ b/src/Bro.UI.Model.Winform/UI/Canvas.cs
@@ -77,22 +77,26 @@
             set => stsStatus.Visible = value;
         }
 
-        private void OnMouseLocationUpdated(Point screenPoint, Point imagePoint)
+        private async void OnMouseLocationUpdated(Point screenPoint, Point imagePoint, string colorDesc)
         {
-            MouseLocationUpdated(screenPoint, imagePoint);
+            //await Task.Run(() => tsslLocation.Text = $"灞忓箷鍧愭爣X锛歿screenPoint.X}锛孻锛歿screenPoint.Y}    鍥剧墖鍧愭爣X锛歿imagePoint.X}锛孻锛歿imagePoint.Y}   棰滆壊锛歿colorDesc}");
+            this.Invoke(new Action(() =>
+            {
+                tsslLocation.Text = $"灞忓箷鍧愭爣X锛歿screenPoint.X}锛孻锛歿screenPoint.Y}    鍥剧墖鍧愭爣X锛歿imagePoint.X}锛孻锛歿imagePoint.Y}   棰滆壊锛歿colorDesc}";
+            }));
         }
 
-        private void MouseLocationUpdated(Point screenPoint, Point imagePoint)
-        {
-            if (InvokeRequired)
-            {
-                Invoke(new Action<Point, Point>(MouseLocationUpdated), screenPoint, imagePoint);
-            }
-            else
-            {
-                tsslLocation.Text = $"灞忓箷鍧愭爣X锛歿screenPoint.X}锛孻锛歿screenPoint.Y}    鍥剧墖鍧愭爣X锛歿imagePoint.X}锛孻锛歿imagePoint.Y}";
-            }
-        }
+        //private void MouseLocationUpdated(Point screenPoint, Point imagePoint, string colorDesc)
+        //{
+        //    if (InvokeRequired)
+        //    {
+        //        Invoke(new Action<Point, Point, string>(MouseLocationUpdated), screenPoint, imagePoint);
+        //    }
+        //    else
+        //    {
+        //        tsslLocation.Text = $"灞忓箷鍧愭爣X锛歿screenPoint.X}锛孻锛歿screenPoint.Y}    鍥剧墖鍧愭爣X锛歿imagePoint.X}锛孻锛歿imagePoint.Y}   棰滆壊锛歿colorDesc}";
+        //    }
+        //}
         #endregion
 
         #region 灞炴��
diff --git a/src/Bro.UI.Model.Winform/UI/CanvasImage.cs b/src/Bro.UI.Model.Winform/UI/CanvasImage.cs
index e31ebeb..55af243 100644
--- a/src/Bro.UI.Model.Winform/UI/CanvasImage.cs
+++ b/src/Bro.UI.Model.Winform/UI/CanvasImage.cs
@@ -35,7 +35,7 @@
         #region Event
         public Action<MouseState> OnMouseStateChanged;
         public Action<IShapeElement> DrawTemplateChanged = null;
-        public Action<Point, Point> OnMouseLocationUpdated;
+        public Action<Point, Point, string> OnMouseLocationUpdated;
         #endregion
 
         private MouseState mouseState = MouseState.Normal;
@@ -540,7 +540,13 @@
             //}
 
             //DisplayMouseLocation(e.Location);
-            OnMouseLocationUpdated?.BeginInvoke(e.Location, ToMapPoint(e.Location), null, null);
+            Point mapPoint = ToMapPoint(e.Location);
+            Color color = Color.Transparent;
+            if (MAP != null && mapPoint.X > 0 && mapPoint.X < MAP.Width && mapPoint.Y > 0 && mapPoint.Y < MAP.Height)
+            {
+                color = MAP.GetPixel(mapPoint.X, mapPoint.Y);
+            }
+            OnMouseLocationUpdated?.BeginInvoke(e.Location, mapPoint, color.Name, null, null);
 
             if (MouseState != MouseState.SelectionZoneDoing)
             {

--
Gitblit v1.8.0