From 81480d62561dcb75a1fc968814ace1368fe5181b Mon Sep 17 00:00:00 2001
From: wells.liu <wells.liu@broconcentric.com>
Date: 星期四, 16 七月 2020 11:57:08 +0800
Subject: [PATCH] Merge branch 'master' of http://gitblit.broconcentric.com:8088/r/M071

---
 src/Bro.Device.GTSCard/GTSCardDriver.cs                         |  245 +++++---
 src/Bro.M071.Process/UI/M071_MainForm.cs                        |   24 
 src/Bro.UI.Model.Winform/UI/CanvasImage.cs                      |    3 
 src/Bro.Common.Device/DeviceBase/MotionCardBase.cs              |    2 
 src/Bro.M071.Process/M071Process_MotionCard.cs                  |  430 +++++++------
 src/Bro.M071.Process/UI/M071_MainForm.Designer.cs               |   20 
 src/Bro.UI.Model.Winform/CommonHelper.cs                        |    7 
 src/Bro.M071.Process/packages.config                            |    1 
 src/Bro.UI.Config/MainFrm.cs                                    |    9 
 src/Bro.M071.DBManager/ExcelExportHelper.cs                     |   28 
 src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.cs          |  161 +++++
 src/Bro.M071.Process/M071Process.cs                             |  412 +++++++++++++
 src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.resx        |  120 ++++
 src/Bro.Device.Gocator/GocatorDriver.cs                         |    2 
 src/Bro.UI.Model.Winform/ElementBase.cs                         |    2 
 src/Bro.M071.Process/Bro.M071.Process.csproj                    |   15 
 src/Bro.Common.Device/DeviceBase/HDevEngineTool.cs              |    2 
 src/Bro.M071.Process/M071Config.cs                              |    9 
 src/Bro.M071.Process/M071Models.cs                              |   26 
 src/Bro.M071.Model/Model/MeasurementUnitResult.cs               |    4 
 src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.Designer.cs |  133 ++++
 src/Bro.Common.Model/Model/MotionCardRelated.cs                 |   49 +
 22 files changed, 1,336 insertions(+), 368 deletions(-)

diff --git a/src/Bro.Common.Device/DeviceBase/HDevEngineTool.cs b/src/Bro.Common.Device/DeviceBase/HDevEngineTool.cs
index 80164bd..f1f385d 100644
--- a/src/Bro.Common.Device/DeviceBase/HDevEngineTool.cs
+++ b/src/Bro.Common.Device/DeviceBase/HDevEngineTool.cs
@@ -250,7 +250,7 @@
 
             for (int i = 0; i < tuple.Length; i++)
             {
-                list.Add(tuple[i]);
+                list.Add(tuple[i].D);
             }
 
             return list;
diff --git a/src/Bro.Common.Device/DeviceBase/MotionCardBase.cs b/src/Bro.Common.Device/DeviceBase/MotionCardBase.cs
index 52e6582..f5214fe 100644
--- a/src/Bro.Common.Device/DeviceBase/MotionCardBase.cs
+++ b/src/Bro.Common.Device/DeviceBase/MotionCardBase.cs
@@ -131,7 +131,7 @@
         /// <summary>
         /// 鎭㈠绔嬪嵆鏆傚仠
         /// </summary>
-        public abstract void ResetImmediatePause();
+        public abstract void ResetImmediatePause(bool isResumeMoving);
         #endregion
     }
 
diff --git a/src/Bro.Common.Model/Model/MotionCardRelated.cs b/src/Bro.Common.Model/Model/MotionCardRelated.cs
index 1037012..8d9ce6b 100644
--- a/src/Bro.Common.Model/Model/MotionCardRelated.cs
+++ b/src/Bro.Common.Model/Model/MotionCardRelated.cs
@@ -86,6 +86,13 @@
         //[Description("IsUseWarning锛氭槸鍚﹀惎鐢ㄦ姤璀�")]
         //public bool IsUseWarning { get; set; } = false;
 
+        [Category("鍘熺偣閰嶇疆")]
+        [DisplayName("鍥炲師鐐瑰弬鏁�")]
+        [Description("鍥炲師鐐瑰弬鏁�")]
+        [TypeConverter(typeof(ComplexObjectConvert))]
+        [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))]
+        public GoHomePara GoHomePara { get; set; } = new GoHomePara();
+
         [Category("鏆傚仠閰嶇疆")]
         [DisplayName("鏄惁鍚敤绔嬪嵆鏆傚仠")]
         [Description("IsImmediatePause锛氭槸鍚﹀惎鐢ㄧ珛鍗虫殏鍋�")]
@@ -154,25 +161,19 @@
         [Category("鍥炲師鐐瑰弬鏁�")]
         [DisplayName("鍥炲師鐐规柟寮�")]
         [Description("HomeMode锛氬洖鍘熺偣鏂瑰紡 锛圚OME_MODE_LIMIT = 10; HOME_MODE_LIMIT_HOME = 11; HOME_MODE_LIMIT_INDEX = 12; HOME_MODE_LIMIT_HOME_INDEX = 13;HOME_MODE_HOME = 20;HOME_MODE_HOME_INDEX = 22;HOME_MODE_INDEX = 30;")]
-        public short HomeMode { get; set; } = 11;
-
-        [Category("鍥炲師鐐瑰弬鏁�")]
-        [DisplayName("鍥炲師鐐规柟鍚�")]
-        [Description("HomeDir锛�1 姝e悜锛�-1 璐熷悜")]
-        public short HomeDir { get; set; } = 1;
+        public short HomeMode { get; set; } = 11; 
 
         [Category("鍥炲師鐐瑰弬鏁�")]
         [DisplayName("杈圭紭")]
         [Description("edge锛氳竟缂�")]
         public short Edge { get; set; } = 0;
 
-        [Category("鍥炲師鐐瑰弬鏁�")]
+        [Category("鍥炲師鐐归�熷害")]
         [DisplayName("鍥炲師鐐规渶浣庨�熷害")]
         [Description("LowVelocity锛氶�熷害,涓�0鏃惰〃绀轰笉淇敼褰撳墠璁剧疆")]
         public double LowVelocity { get; set; } = 50;
 
-
-        [Category("鍥炲師鐐瑰弬鏁�")]
+        [Category("鍥炲師鐐归�熷害")]
         [DisplayName("鍥炲師鐐规渶楂橀�熷害")]
         [Description("HighVelocity锛氶�熷害,涓�0鏃惰〃绀轰笉淇敼褰撳墠璁剧疆")]
         public double HighVelocity { get; set; } = 50;
@@ -182,16 +183,30 @@
         //[Description("SearchHomeDistance锛氭悳绱㈣窛绂�")]
         //public int SearchHomeDistance { get; set; } = 9999999;
 
-        [Category("鍥炲師鐐瑰弬鏁�")]
+        [Category("鍥炲師鐐瑰亸绉�")]
         [DisplayName("鍋忕Щ璺濈")]
         [Description("HomeOffset锛氬亸绉昏窛绂�")]
         public int HomeOffset { get; set; } = 0;
 
-        [Category("鍥炲師鐐瑰弬鏁�")]
+        [Category("鍥炲師鐐瑰亸绉�")]
         [DisplayName("璺宠繃姝ラ暱")]
         [Description("EscapeStep锛氳烦杩囨闀�")]
         public int EscapeStep { get; set; } = 1000;
 
+        [Category("鍥炲師鐐规柟鍚�")]
+        [DisplayName("璧峰杩愬姩鏂瑰悜")]
+        [Description("鍥炲師鐐规椂锛岃酱鐨勫垵濮嬭繍鍔ㄦ柟鍚戯細true 姝e悜锛宖alse 璐熷悜")]
+        public bool IsHomeDirPositive { get; set; } = true;
+
+        [Category("鍥炲師鐐规柟鍚�")]
+        [DisplayName("鍘熺偣鎹曟崏鏂瑰悜")]
+        [Description("鎹曟崏鍘熺偣鏃讹紝杞寸殑杩愬姩鏂瑰悜锛歵rue 姝e悜锛宖alse 璐熷悜")]
+        public bool IsCaptureDirPositive { get; set; } = true;
+
+        [Category("鍥炲師鐐硅秴鏃�")]
+        [DisplayName("鍥炲師鐐硅秴鏃�")]
+        [Description("鍥炲師鐐硅秴鏃讹紝鍗曚綅绉�")]
+        public int GoHomeTimeOut { get; set; } = 60;
     }
 
     /// <summary>
@@ -352,12 +367,12 @@
         //}
 
         //private GoHomePara goHomePara = new GoHomePara();
-        [Category("杩愬姩閰嶇疆")]
-        [DisplayName("鍥炲師鐐瑰弬鏁�")]
-        [Description("GoHomePara锛氶�熷害鍙傛暟")]
-        [TypeConverter(typeof(ComplexObjectConvert))]
-        [Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))]
-        public GoHomePara GoHomePara { get; set; } = new GoHomePara();
+        //[Category("杩愬姩閰嶇疆")]
+        //[DisplayName("鍥炲師鐐瑰弬鏁�")]
+        //[Description("GoHomePara锛氶�熷害鍙傛暟")]
+        //[TypeConverter(typeof(ComplexObjectConvert))]
+        //[Editor(typeof(PropertyObjectEditor), typeof(UITypeEditor))]
+        //public GoHomePara GoHomePara { get; set; } = new GoHomePara();
         //{
         //    get => goHomePara;
         //    set
diff --git a/src/Bro.Device.GTSCard/GTSCardDriver.cs b/src/Bro.Device.GTSCard/GTSCardDriver.cs
index a917333..d87e661 100644
--- a/src/Bro.Device.GTSCard/GTSCardDriver.cs
+++ b/src/Bro.Device.GTSCard/GTSCardDriver.cs
@@ -46,6 +46,8 @@
         /// 杩愬姩杞寸珛鍗虫殏鍋�
         /// </summary>
         Dictionary<int, ManualResetEvent> axisImmediatePauseHandleDict = new Dictionary<int, ManualResetEvent>();
+        Dictionary<int, bool> axisImmediatePauseFlag = new Dictionary<int, bool>();
+        Dictionary<int, bool> axisPauseResumeFlag = new Dictionary<int, bool>();
 
         //Dictionary<int, CancellationTokenSource> axisMoveCancelDict = new Dictionary<int, CancellationTokenSource>();
 
@@ -145,16 +147,22 @@
             {
                 foreach (var preCheck in operationSet.PreCheckIOCollection)
                 {
-                    int timeout = operationSet.PreCheckIOTimeout;
+                    _pauseHandle.Wait();
+
                     IOValue? ioData = null;
-                    while (CurrentState == DeviceState.DSOpen)
+                    if (CurrentState == DeviceState.DSOpen)
                     {
-                        Thread.Sleep(10);
-                        ioData = MonitorValues.FirstOrDefault(u => u.IONum == preCheck.IOItem.IONum && u.IOType == preCheck.IOItem.IOType)?.Value;//IO 鏄紑銆佸叧 浠嶮onitorValues 鑾峰彇
-                        timeout -= 10;
-                        if (preCheck.CheckValue == ioData || (operationSet.PreCheckIOTimeout > 0 && timeout < 0))
+                        int timeout = operationSet.PreCheckIOTimeout;
+
+                        while (CurrentState == DeviceState.DSOpen)
                         {
-                            break;
+                            Thread.Sleep(10);
+                            ioData = MonitorValues.FirstOrDefault(u => u.IONum == preCheck.IOItem.IONum && u.IOType == preCheck.IOItem.IOType)?.Value;//IO 鏄紑銆佸叧 浠嶮onitorValues 鑾峰彇
+                            timeout -= 10;
+                            if (preCheck.CheckValue == ioData || (operationSet.PreCheckIOTimeout > 0 && timeout < 0))
+                            {
+                                break;
+                            }
                         }
                     }
 
@@ -170,10 +178,15 @@
             // 2.鏉垮崱杩愬姩
             if (CurrentState == DeviceState.DSOpen)
             {
-                responseMessage = MoveToPoint(new MotionOperationCollection() { MovingOps = operationSet.MovingOps });
-                if (!responseMessage.Result)
+                _pauseHandle.Wait();
+
+                if (CurrentState == DeviceState.DSOpen)
                 {
-                    return responseMessage;
+                    responseMessage = MoveToPoint(new MotionOperationCollection() { MovingOps = operationSet.MovingOps });
+                    if (!responseMessage.Result)
+                    {
+                        return responseMessage;
+                    }
                 }
             }
 
@@ -181,18 +194,24 @@
             // 3.IO杈撳嚭 涓嶉渶瑕佽秴鏃�
             if (CurrentState == DeviceState.DSOpen)
             {
+
                 foreach (var ioOutput in operationSet.IOOutputCollection)
                 {
-                    WriteOutput((short)ioOutput.IOItem.IONum, ioOutput.CheckValue);
+                    _pauseHandle.Wait();
 
-                    //var ioData = MonitorValues.FirstOrDefault(u => u.IONum == ioOutput.IOItem.IONum && u.IOType == ioOutput.IOItem.IOType)?.Value;//IO 鏄紑銆佸叧 浠嶮onitorValues 鑾峰彇
+                    if (CurrentState == DeviceState.DSOpen)
+                    {
+                        WriteOutput((short)ioOutput.IOItem.IONum, ioOutput.CheckValue);
 
-                    //if (ioOutput.CheckValue != ioData)
-                    //{
-                    //    responseMessage.Result = false;
-                    //    responseMessage.Message = $"IO杈撳嚭涓嶉�氳繃锛岄厤缃細{ioOutput.GetDisplayText()}锛屽綋鍓嶅�硷細{ioData}";
-                    //    return responseMessage;
-                    //}
+                        //var ioData = MonitorValues.FirstOrDefault(u => u.IONum == ioOutput.IOItem.IONum && u.IOType == ioOutput.IOItem.IOType)?.Value;//IO 鏄紑銆佸叧 浠嶮onitorValues 鑾峰彇
+
+                        //if (ioOutput.CheckValue != ioData)
+                        //{
+                        //    responseMessage.Result = false;
+                        //    responseMessage.Message = $"IO杈撳嚭涓嶉�氳繃锛岄厤缃細{ioOutput.GetDisplayText()}锛屽綋鍓嶅�硷細{ioData}";
+                        //    return responseMessage;
+                        //}
+                    }
                 }
             }
 
@@ -228,6 +247,8 @@
         #endregion
 
         #region ImmediatePause
+        ManualResetEventSlim _pauseHandle = new ManualResetEventSlim(true);
+
         /// <summary>
         /// 鍚姩绔嬪嵆鏆傚仠
         /// </summary>
@@ -236,12 +257,11 @@
             if (!_isResetting)
             {
                 var immediatePauseAxis = IConfig.AxisSettings.FindAll(a => a.IsAxisEnabled && a.IsImmediatePause).Select(u => u.AxisIndex).ToList();
-
+                _pauseHandle.Reset();
                 immediatePauseAxis.ForEach(async axisIndex =>
                 {
                     axisImmediatePauseHandleDict[axisIndex].Reset();
-
-                    //axisMoveCancelDict[axisIndex].Cancel();
+                    axisImmediatePauseFlag[axisIndex] = true;
 
                     await MoveStop(axisIndex, 0);//鎵�鏈夎酱閮芥殏鍋�
                 });
@@ -251,15 +271,22 @@
         /// <summary>
         /// 鎭㈠绔嬪嵆鏆傚仠
         /// </summary>
-        public override void ResetImmediatePause()
+        public override void ResetImmediatePause(bool isResumeMoving)
         {
             var immediatePauseAxis = IConfig.AxisSettings.FindAll(a => a.IsAxisEnabled && a.IsImmediatePause).Select(u => u.AxisIndex).ToList();
-
+            _pauseHandle.Set();
             immediatePauseAxis.ForEach(axisIndex =>
             {
-                //axisMoveCancelDict[axisIndex] = new CancellationTokenSource();
-
+                axisImmediatePauseFlag[axisIndex] = false;
                 axisImmediatePauseHandleDict[axisIndex].Set();
+                if (isResumeMoving)
+                {
+                    axisPauseResumeFlag[axisIndex] = true;
+                }
+                else
+                {
+                    axisPauseResumeFlag[axisIndex] = false;
+                }
             });
         }
         #endregion
@@ -347,19 +374,11 @@
             ResponseMessage responseMessage = new ResponseMessage();
             if (opConfig is MotionOperationCollection gtsOperationCollection)
             {
-                //List<Task<bool>> taskList = new List<Task<bool>>();
-                //foreach (var movingOp in gtsOperationCollection.MovingOps)
-                //{
-                //    var task = SingleAxisMoving(movingOp);
-                //    taskList.Add(task);
-                //    task.Start();
-                //}
-                //Task.WaitAll(taskList.ToArray());                
-                //responseMessage.Result = taskList.All(u => u.GetAwaiter().GetResult());
-
                 List<bool> resultList = new List<bool>();
                 Parallel.ForEach(gtsOperationCollection.MovingOps, movingOp =>
                 {
+                    axisImmediatePauseFlag[movingOp.AxisIndex] = false;
+                    axisPauseResumeFlag[movingOp.AxisIndex] = true;
                     resultList.Add(SingleAxisMoving(movingOp).Result);
                 });
                 responseMessage.Result = resultList.All(u => u == true);
@@ -418,63 +437,64 @@
         public override Task<bool> SingleAxisMoving(MovingOption optionPara)
         {
             return Task.Run(() =>
-             {
-                 axisImmediatePauseHandleDict[optionPara.AxisIndex].WaitOne();
-                 bool isSuccessAndStop = false;
-                 try
-                 {
-                     if (IConfig.AxisSettings.FirstOrDefault(a => a.AxisIndex == optionPara.AxisIndex)?.IsAxisEnabled ?? false)
-                     {
-                         // string _position = "";
-                         string motionType = optionPara.MoveMode == EnumHelper.MotionMode.Normal ? (optionPara.IsAbsolute ? "Abs" : "Rel") : optionPara.MoveMode.ToString();
+            {
+                bool isSuccessAndStop = false;
+                do
+                {
+                    axisImmediatePauseHandleDict[optionPara.AxisIndex].WaitOne();
 
-                         // _position = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff},{optionPara.AxisIndex},{motionType},{GetPosition(optionPara.AxisIndex)},{GetPrfPosition(optionPara.AxisIndex)},{optionPara.Destination},";
+                    if (!axisPauseResumeFlag[optionPara.AxisIndex])
+                        return true;
 
-                         switch (optionPara.MoveMode)
-                         {
-                             case MotionMode.Normal:
-                                 {
-                                     if (_isResetting)
-                                     {
-                                         LogAsync(DateTime.Now, "澶嶄綅涓惎鍔ㄨ繍鍔ㄥ紓甯�", optionPara.AxisIndex + "鍚姩杩愬姩寮傚父");
-                                         return false;
-                                     }
+                    try
+                    {
+                        if (IConfig.AxisSettings.FirstOrDefault(a => a.AxisIndex == optionPara.AxisIndex)?.IsAxisEnabled ?? false)
+                        {
+                            string motionType = optionPara.MoveMode == EnumHelper.MotionMode.Normal ? (optionPara.IsAbsolute ? "Abs" : "Rel") : optionPara.MoveMode.ToString();
 
-                                     if (optionPara.IsAbsolute)
-                                     {
-                                         isSuccessAndStop = P2PMoveAbs(optionPara);
-                                     }
-                                     else
-                                     {
-                                         isSuccessAndStop = P2PMoveRel(optionPara);
-                                     }
+                            switch (optionPara.MoveMode)
+                            {
+                                case MotionMode.Normal:
+                                    {
+                                        if (_isResetting)
+                                        {
+                                            LogAsync(DateTime.Now, "澶嶄綅涓惎鍔ㄨ繍鍔ㄥ紓甯�", optionPara.AxisIndex + "鍚姩杩愬姩寮傚父");
+                                            return false;
+                                        }
 
-                                 }
-                                 break;
-                             case MotionMode.FindOri:
-                                 {
-                                     //isSuccessAndStop = GoHome(optionPara);  
-                                     isSuccessAndStop = P2PGoHome(optionPara);
-                                 }
-                                 break;
-                             case MotionMode.Jog:
-                                 {
-                                     isSuccessAndStop = JogMove(optionPara);
-                                 }
-                                 break;
-                         }
-                         //_position += $"{GetPosition(optionPara.AxisIndex)},";
-                         //_position += $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}";
-                         //LogAsync(DateTime.Now, "", _position);
-                     }
-                 }
-                 catch (Exception ex)
-                 {
-                     isSuccessAndStop = false;
-                     LogAsync(DateTime.Now, $"杞磠optionPara.AxisIndex}杩愬姩寮傚父", ex.GetExceptionMessage());
-                 }
-                 return isSuccessAndStop;
-             });
+                                        if (optionPara.IsAbsolute)
+                                        {
+                                            isSuccessAndStop = P2PMoveAbs(optionPara);
+                                        }
+                                        //else
+                                        //{
+                                        //    isSuccessAndStop = P2PMoveRel(optionPara);
+                                        //}
+
+                                    }
+                                    break;
+                                case MotionMode.FindOri:
+                                    {
+                                        //isSuccessAndStop = GoHome(optionPara);  
+                                        isSuccessAndStop = P2PGoHome(optionPara);
+                                    }
+                                    break;
+                                case MotionMode.Jog:
+                                    {
+                                        isSuccessAndStop = JogMove(optionPara);
+                                    }
+                                    break;
+                            }
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+                        isSuccessAndStop = false;
+                        LogAsync(DateTime.Now, $"杞磠optionPara.AxisIndex}杩愬姩寮傚父", ex.GetExceptionMessage());
+                    }
+                } while (axisImmediatePauseFlag[optionPara.AxisIndex]);
+                return isSuccessAndStop;
+            });
         }
 
         /// <summary>
@@ -868,7 +888,11 @@
         {
             try
             {
-                StartCapture:
+                var goHomePara = IConfig.AxisSettings.FirstOrDefault(u => u.AxisIndex == movingOption.AxisIndex).GoHomePara;
+                bool homeDirection = goHomePara.IsHomeDirPositive;
+                bool isRightLimitReached = false;
+
+            StartCapture:
 
                 PositionReset(movingOption.AxisIndex, 1);
                 ClearStatus(movingOption.AxisIndex, 1);
@@ -887,24 +911,24 @@
                 // 璁剧疆鐐逛綅妯″紡杩愬姩鍙傛暟
                 sRtn = GTSCardAPI.GT_SetTrapPrm((short)IConfig.CardNum, (short)movingOption.AxisIndex, ref trapPrm);
                 // 璁剧疆鐐逛綅妯″紡鐩爣閫熷害锛屽嵆鍥炲師鐐归�熷害
-                sRtn = GTSCardAPI.GT_SetVel((short)IConfig.CardNum, (short)movingOption.AxisIndex, movingOption.GoHomePara.HighVelocity);
+                sRtn = GTSCardAPI.GT_SetVel((short)IConfig.CardNum, (short)movingOption.AxisIndex, goHomePara.HighVelocity);
 
                 // 璁剧疆鐐逛綅妯″紡鐩爣浣嶇疆锛屽嵆鍘熺偣鎼滅储璺濈
-                sRtn = GTSCardAPI.GT_SetPos((short)IConfig.CardNum, (short)movingOption.AxisIndex, movingOption.GoHomePara.HomeDir == 1 ? 99999999 : -99999999);
+                sRtn = GTSCardAPI.GT_SetPos((short)IConfig.CardNum, (short)movingOption.AxisIndex, homeDirection ? 99999999 : -99999999);
                 // 鍚姩杩愬姩
                 sRtn = GTSCardAPI.GT_Update((short)IConfig.CardNum, 1 << (movingOption.AxisIndex - 1));
 
-                int repeatTime = 1000;
+                int repeatTime = goHomePara.GoHomeTimeOut * 1000;
                 short capture;
                 int pos;
                 uint clk;//鏃堕挓鍙傛暟
                 do
                 {
-                    Thread.Sleep(20);
+                    Thread.Sleep(IConfig.MonitorInterval);
                     // 璇诲彇鎹曡幏鐘舵��
                     GTSCardAPI.GT_GetCaptureStatus((short)IConfig.CardNum, (short)movingOption.AxisIndex, out capture, out pos, 1, out clk);
                     isStop = IsStop((short)movingOption.AxisIndex);
-                    repeatTime--;
+                    repeatTime -= IConfig.MonitorInterval;
                 } while (!(isStop || capture == 1 || repeatTime <= 0));
 
                 if (repeatTime <= 0)
@@ -921,7 +945,22 @@
 
                     if (((axisStatus.AxisStatus & 0x20) != 0) || ((axisStatus.AxisStatus & 0x40) != 0))
                     {
-                        movingOption.GoHomePara.HomeDir = (short)(movingOption.GoHomePara.HomeDir == 1 ? -1 : 1);
+                        //movingOption.GoHomePara.HomeDir = (short)(movingOption.GoHomePara.HomeDir == 1 ? -1 : 1);
+
+                        //姝i檺浣�
+                        if ((axisStatus.AxisStatus & 0x20) != 0 && !goHomePara.IsCaptureDirPositive)
+                        {
+                            isRightLimitReached = true;
+                        }
+
+
+                        //璐熼檺浣�
+                        if ((axisStatus.AxisStatus & 0x40) != 0 && goHomePara.IsCaptureDirPositive)
+                        {
+                            isRightLimitReached = true;
+                        }
+
+                        homeDirection = !homeDirection;
                         goto StartCapture;
                     }
 
@@ -930,12 +969,18 @@
 
                 if (capture == 1)
                 {
+                    if (!isRightLimitReached)
+                    {
+                        capture = 0;
+                        goto StartCapture;
+                    }
+
                     //鍏坰top
                     MoveStop((short)movingOption.AxisIndex, 0);
                     ClearStatus((short)movingOption.AxisIndex, 1);
                     //宸茬粡鎹曡幏鍒癏ome鎵嶅彲浠ュ洖闆� 闃舵2
                     // 杩愬姩鍒�"鎹曡幏浣嶇疆+鍋忕Щ閲�"
-                    sRtn = GTSCardAPI.GT_SetPos((short)IConfig.CardNum, (short)movingOption.AxisIndex, pos + movingOption.GoHomePara.HomeOffset);
+                    sRtn = GTSCardAPI.GT_SetPos((short)IConfig.CardNum, (short)movingOption.AxisIndex, pos + goHomePara.HomeOffset);
                     // 鍦ㄨ繍鍔ㄧ姸鎬佷笅鏇存柊鐩爣浣嶇疆
                     sRtn = GTSCardAPI.GT_Update((short)IConfig.CardNum, 1 << (movingOption.AxisIndex - 1));
                     isStop = false;
@@ -1282,10 +1327,16 @@
             int axis_sts;
             var axisSettings = IConfig.AxisSettings.FindAll(u => u.IsAxisEnabled);
             ClearStatus(1, axisSettings.Count);
+
+            if (AxisStatusList.Count == 0)
+            {
+                Thread.Sleep(10);
+            }
+
             foreach (var axisSetting in axisSettings)
             {
                 //axis_sts = GetAxisStatus((short)axisSetting.AxisIndex);
-                axis_sts = AxisStatusList.FirstOrDefault(u => u.AxisIndex == axisSetting.AxisIndex).AxisStatus;
+                axis_sts = AxisStatusList.FirstOrDefault(u => u.AxisIndex == axisSetting.AxisIndex)?.AxisStatus ?? 0;
                 if ((axis_sts & 0x200) == 0)
                 {
                     var rst = GTSCardAPI.GT_AxisOn((short)IConfig.CardNum, (short)axisSetting.AxisIndex);
diff --git a/src/Bro.Device.Gocator/GocatorDriver.cs b/src/Bro.Device.Gocator/GocatorDriver.cs
index 8e0bef5..ae21a3f 100644
--- a/src/Bro.Device.Gocator/GocatorDriver.cs
+++ b/src/Bro.Device.Gocator/GocatorDriver.cs
@@ -110,8 +110,6 @@
                             {
                                 imgSet.HImage = new HImage();
                                 imgSet.HImage.GenImage1("uint2", (int)width, zoomHeight, zoomPtr);
-                                //imgSet.HImage = imgSet.HImage.ZoomImageSize((int)width, zoomHeight, "constant");
-
 
                                 imgSet.HImage_2 = new HImage();
                                 imgSet.HImage_2.GenImage1("uint2", (int)width, zoomHeight, zoomPtr);
diff --git a/src/Bro.M071.DBManager/ExcelExportHelper.cs b/src/Bro.M071.DBManager/ExcelExportHelper.cs
index ce6958e..b804e74 100644
--- a/src/Bro.M071.DBManager/ExcelExportHelper.cs
+++ b/src/Bro.M071.DBManager/ExcelExportHelper.cs
@@ -12,20 +12,23 @@
 
     public class ExcelExportSet
     {
-        public List<string> Worksheets { get; set; }
+        public List<string> Worksheets { get; set; } = new List<string>();
 
         /// <summary>
         /// Key锛� Worksheet鐨勫悕绉� Value:Worksheet瀵瑰簲鐨勫垪鍚嶉泦鍚�(key 涓鸿瀵煎嚭鐨勫垪鍚� value 涓哄鍑哄悗鏄剧ず鐨勫垪鍚�)
         /// </summary>
-        public Dictionary<string, Dictionary<string, string>> WorksheetColumns { get; set; }
-        public Dictionary<string, DataTable> WorksheetDataTable { get; set; }
+        public Dictionary<string, Dictionary<string, string>> WorksheetColumns { get; set; } = new Dictionary<string, Dictionary<string, string>>();
 
-        public ExcelExportSet()
-        {
-            Worksheets = new List<string>();
-            WorksheetColumns = new Dictionary<string, Dictionary<string, string>>();
-            WorksheetDataTable = new Dictionary<string, DataTable>();
-        }
+        public Dictionary<string, Dictionary<string, string>> WorksheetRows { get; set; } = new Dictionary<string, Dictionary<string, string>>();
+
+        public Dictionary<string, DataTable> WorksheetDataTable { get; set; } = new Dictionary<string, DataTable>();
+
+        //public ExcelExportSet()
+        //{
+        //    Worksheets = new List<string>();
+        //    WorksheetColumns = new Dictionary<string, Dictionary<string, string>>();
+        //    WorksheetDataTable = new Dictionary<string, DataTable>();
+        //}
 
     }
 
@@ -107,7 +110,7 @@
                     {
                         // autofit width of cells with small content 
                         ExcelRange columnCells = workSheet.Cells[workSheet.Dimension.Start.Row, columnIndex, workSheet.Dimension.End.Row, columnIndex];
-                        int maxLength = columnCells.Max(cell => cell.Value.ToString().Count());
+                        int maxLength = columnCells.Max(cell => (cell.Value ?? "").ToString().Count());
                         if (maxLength < 150)
                         {
                             workSheet.Column(columnIndex).AutoFit();
@@ -258,13 +261,14 @@
         {
             try
             {
-                return obj.ToString();
+                return (obj ?? "").ToString();
             }
             catch (Exception)
             {
                 return "";
             }
         }
-
     }
+
+    
 }
diff --git a/src/Bro.M071.Model/Model/MeasurementUnitResult.cs b/src/Bro.M071.Model/Model/MeasurementUnitResult.cs
index fb523c9..3ae292f 100644
--- a/src/Bro.M071.Model/Model/MeasurementUnitResult.cs
+++ b/src/Bro.M071.Model/Model/MeasurementUnitResult.cs
@@ -48,6 +48,10 @@
         public string ProductionCode { get; set; }
         [NotMapped]
         public string ProductionBarcode { get; set; }
+        [NotMapped]
+        public string Keys { get; set; }
+        [NotMapped]
+        public string Positions { get; set; }
     }
 
     public class MeasurementUnitResultRequest : BaseRequest
diff --git a/src/Bro.M071.Process/Bro.M071.Process.csproj b/src/Bro.M071.Process/Bro.M071.Process.csproj
index e8c6f29..d744dfa 100644
--- a/src/Bro.M071.Process/Bro.M071.Process.csproj
+++ b/src/Bro.M071.Process/Bro.M071.Process.csproj
@@ -81,6 +81,9 @@
     <Reference Include="Autofac, Version=4.9.4.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
       <HintPath>..\..\packages\Autofac.4.9.4\lib\net45\Autofac.dll</HintPath>
     </Reference>
+    <Reference Include="EPPlus, Version=4.5.3.3, Culture=neutral, PublicKeyToken=ea159fdaa78159a1, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\EPPlus.4.5.3.3\lib\net40\EPPlus.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>
@@ -95,8 +98,11 @@
     <Reference Include="PostSharp, Version=6.2.7.0, Culture=neutral, PublicKeyToken=b13fd38b8f9c99d7, processorArchitecture=MSIL">
       <HintPath>..\..\packages\PostSharp.Redist.6.2.7\lib\net45\PostSharp.dll</HintPath>
     </Reference>
+    <Reference Include="PresentationCore" />
     <Reference Include="System" />
+    <Reference Include="System.configuration" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Security" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />
@@ -131,6 +137,12 @@
     <Compile Include="UI\M071_MainForm.Designer.cs">
       <DependentUpon>M071_MainForm.cs</DependentUpon>
     </Compile>
+    <Compile Include="UI\M071_PatchInsertMeasurement.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="UI\M071_PatchInsertMeasurement.Designer.cs">
+      <DependentUpon>M071_PatchInsertMeasurement.cs</DependentUpon>
+    </Compile>
     <Compile Include="UI\M071_ShortcutFrm.cs">
       <SubType>Form</SubType>
     </Compile>
@@ -152,6 +164,9 @@
     <EmbeddedResource Include="UI\M071_MainForm.resx">
       <DependentUpon>M071_MainForm.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="UI\M071_PatchInsertMeasurement.resx">
+      <DependentUpon>M071_PatchInsertMeasurement.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="UI\M071_ShortcutFrm.resx">
       <DependentUpon>M071_ShortcutFrm.cs</DependentUpon>
     </EmbeddedResource>
diff --git a/src/Bro.M071.Process/M071Config.cs b/src/Bro.M071.Process/M071Config.cs
index fa7ad15..9edb448 100644
--- a/src/Bro.M071.Process/M071Config.cs
+++ b/src/Bro.M071.Process/M071Config.cs
@@ -41,12 +41,19 @@
         public List<KeyResult> KeyResultCollection { get; set; } = new List<KeyResult>();
         #endregion
 
-        [Category("鑳屾櫙鍥剧墖璁剧疆")]
+        [Category("鏄剧ず閰嶇疆")]
         [Description("杩愯鑳屾櫙鍥剧墖璺緞")]
         [Editor(typeof(FileDialogEditor), typeof(UITypeEditor))]
         [DisplayName("鑳屾櫙鍥剧墖")]
         public string BackgroundImagePath { get; set; }
 
+        [Category("鏄剧ず閰嶇疆")]
+        [Description("閿洏鍏抽敭閿綅甯冨眬鏄剧ず")]
+        [TypeConverter(typeof(CollectionCountConvert))]
+        [Editor(typeof(ComplexCollectionEditor<KeyLocation>), typeof(UITypeEditor))]
+        [DisplayName("閿綅鏄剧ず")]
+        public List<KeyLocation> KeyLocationCollection { get; set; } = new List<KeyLocation>();
+
         [Category("妫�娴嬭缃�")]
         [Description("鎷嶆憚鐐逛綅璁剧疆闆嗗悎")]
         [TypeConverter(typeof(CollectionCountConvert))]
diff --git a/src/Bro.M071.Process/M071Models.cs b/src/Bro.M071.Process/M071Models.cs
index beb5008..a56f47c 100644
--- a/src/Bro.M071.Process/M071Models.cs
+++ b/src/Bro.M071.Process/M071Models.cs
@@ -17,6 +17,28 @@
 
 namespace Bro.M071.Process
 {
+    public class KeyLocation : IComplexDisplay
+    {
+        [Category("閿悕閰嶇疆")]
+        [Description("鍗曢敭閿悕")]
+        [TypeConverter(typeof(KeyNameDictConverter))]
+        public string Key { get; set; }
+
+        [Category("浣嶇疆閰嶇疆")]
+        [Description("鍗曢敭鍦ㄩ敭鐩樺钩闈㈠浘涓婄殑浣嶇疆鏄剧ず")]
+        public Rectangle KeyRect { get; set; } = new Rectangle();
+
+        [Category("浣嶇疆閰嶇疆")]
+        [Description("鍗曢敭涔嬪悗闂撮殭浣嶇疆")]
+        public Rectangle IntervalRect { get; set; } = new Rectangle();
+
+        public string GetDisplayText()
+        {
+            return $"{Key}:{KeyRect.X},{KeyRect.Y},{KeyRect.Width},{KeyRect.Height}";
+        }
+    }
+
+
     public class KeyAlgorithem : IComplexDisplay
     {
         [Browsable(false)]
@@ -282,7 +304,7 @@
 
         #region 鏄剧ず
         [Browsable(false)]
-        public Rectangle DisplayLocation { get; set; } = new Rectangle();
+        public Rectangle DisplayLocation { get; set; } = new Rectangle(10, 10, 100, 100);
         #endregion
 
         #region 妫�娴嬬粨鏋�
@@ -391,7 +413,7 @@
             {
                 if (valuePairs == null || valuePairs.Count == 0)
                 {
-                    IsDone = false; 
+                    IsDone = false;
                     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsDone"));
                     return;
                 }
diff --git a/src/Bro.M071.Process/M071Process.cs b/src/Bro.M071.Process/M071Process.cs
index 70d01cd..325cccc 100644
--- a/src/Bro.M071.Process/M071Process.cs
+++ b/src/Bro.M071.Process/M071Process.cs
@@ -9,6 +9,8 @@
 using Bro.Process;
 using HalconDotNet;
 using Newtonsoft.Json;
+using OfficeOpenXml;
+using OfficeOpenXml.Style;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -61,7 +63,6 @@
 
             Reset(null, null, null);
             FullReset(null);
-
         }
 
         private void InitialMotionCardBaseAxisAlarm()
@@ -82,7 +83,7 @@
         private void InitialSetting()
         {
             //鏁版嵁搴撹縼绉绘鏌�
-            DatabaseInitialize.Initialize();
+            //DatabaseInitialize.Initialize();
 
             MotionCardSettingCheck();
 
@@ -196,7 +197,7 @@
             }
 
             MachineState = MachineState.Running;
-            OnMeasureStart?.BeginInvoke(null, null);
+            OnMeasureStart?.Invoke();
 
             var measurements = Config.MeasurementUnitCollection.Where(u => u.IsEnabled).ToList().DeepSerializeClone();
             measurements.ForEach(m =>
@@ -226,9 +227,12 @@
 
             Config.SnapshotPointCollection.Where(u => u.IsEnabled).ToList().ForEach(s =>
                   {
-                      _pauseHandle.WaitHandle.WaitOne();
+                      _pausedHandle.Wait();
 
-                      if (MachineState != MachineState.Running)
+                      if (MachineState == MachineState.Ready)
+                          return;
+
+                      if (MachineState != MachineState.Running && MachineState != MachineState.Pause)
                       {
                           throw new ProcessException("鏈哄彴鐘舵�佷笉鍦ㄨ繍琛屼腑锛岄��鍑烘娴�");
                       }
@@ -268,7 +272,7 @@
                           return;
                       }
 
-                      RunImageHandle(camera, s.CameraOp.OpConfig, set, s.Id, s.Name, pMeasure.Measurements);
+                      RunImageHandle(s.CameraOp.OpConfig, set, s.Id, s.Name, pMeasure.Measurements);
                   });
 
             BarCode = "";
@@ -336,10 +340,14 @@
         {
             if (sender is ProductionMeasurement pMeasure)
             {
-                lock (pMeasure)
+                var production = productionList.FirstOrDefault(u => u.Barcode == pMeasure.Barcode);
+                if (production == null)
+                    return;
+
+                lock (production)
                 {
                     //妫�鏌ユ槸鍚﹀叏閮ㄥ畬鎴�
-                    pMeasure.Measurements.ForEach(m =>
+                    pMeasure.Measurements?.ForEach(m =>
                     {
                         if (m.KeyUnitCollection.All(k => k.IsDone != null))
                         {
@@ -368,7 +376,7 @@
                                         }
                                         else
                                         {
-                                            m.Spec.ActualValue = _halconToolDict[toolKey].GetResultTuple("OUTPUT_Result").D;
+                                            m.Spec.ActualValue = double.Parse(_halconToolDict[toolKey].GetResultTuple("OUTPUT_Result").D.ToString("f2"));
                                             LogAsync(DateTime.Now, $"{m.GetDisplayText()}鏁版嵁{m.Spec.ActualValue}锛岀粨鏋渰(m.Spec.MeasureResult == null ? "TBD" : (m.Spec.MeasureResult == true ? "OK" : "NG"))}", "");
                                         }
                                     }
@@ -381,7 +389,7 @@
                                 indicator.ResultState = m.Spec.MeasureResult;
                                 pMeasure.ElementList.Add(indicator);
                                 //杈撳嚭鍥惧舰鍩哄厓鍒扮晫闈� 
-                                OnElementUpdated?.BeginInvoke(indicator, null, null);
+                                OnElementUpdated?.Invoke(indicator);
 
                                 SaveKeyImages(pMeasure.Barcode, m);
 
@@ -415,7 +423,9 @@
                 }
 
                 //Excel鎶ヨ〃杈撳嚭 锛堝崟涓骇鍝佺殑excel瀵煎嚭锛�
-                ExportProductionExcel(measurementUnitResultAndKeyUnitDataSet);
+                //ExportProductionExcel(measurementUnitResultAndKeyUnitDataSet);
+
+                ExportProductionInColumns(measurementUnitResultAndKeyUnitDataSet);
                 ////鏁版嵁搴撲繚瀛� 
                 //SaveProductionData(measurementUnitResultAndKeyUnitDataSet);
 
@@ -494,9 +504,20 @@
                     MeasurementUnitResult measurementUnitResult = new MeasurementUnitResult();
                     measurementUnitResult.ProductionMeasurementRecordsId = productionMeasurementRecords.ID;
                     measurementUnitResult.ProductionBarcode = productionMeasurementRecords.ProductionBarcode;
-                    measurementUnitResult.MeasurementName = measurementUnit.Name;
+                    measurementUnitResult.MeasurementName = measurementUnit.GetDisplayText();
+
+                    if (measurementUnit.MeasureType == "Alignment" || measurementUnit.MeasureType == "Slant")
+                    {
+                        measurementUnitResult.Keys = string.Join("-", measurementUnit.KeyUnitCollection.Select(u => u.Key));
+                        measurementUnitResult.Positions = string.Join("-", measurementUnit.KeyUnitCollection.Select(u => u.KeyResultId));
+                    }
+                    else if (measurementUnit.MeasureType == "RowAlignment")
+                    {
+                        measurementUnitResult.Keys = measurementUnit.KeyUnitCollection[0].Key + "~" + measurementUnit.KeyUnitCollection[measurementUnit.KeyUnitCollection.Count() - 1].Key;
+                        measurementUnitResult.Keys = measurementUnit.KeyUnitCollection[0].KeyResultId + "~" + measurementUnit.KeyUnitCollection[measurementUnit.KeyUnitCollection.Count() - 1].KeyResultId;
+                    }
                     measurementUnitResult.MeasurementType = measurementUnit.MeasureType;
-                    measurementUnitResult.MeasurementValue = measurementUnit.Spec.ActualValue.ToString();
+                    measurementUnitResult.MeasurementValue = measurementUnit.Spec.ActualValue == null ? "NA" : measurementUnit.Spec.ActualValue.Value.ToString("f2");
                     measurementUnitResult.MeasurementResult = measurementUnit.Spec.MeasureResult.Value ? "OK" : "NG";
 
                     measurementUnitResults.Add(measurementUnitResult);
@@ -512,7 +533,7 @@
                                 keyUnitData.ProductionBarcode = productionMeasurementRecords.ProductionBarcode;
                                 keyUnitData.Key = keyUnit.Key;
                                 keyUnitData.MeasurementItem = keyValue.Key;
-                                keyUnitData.ItemValue = keyValue.Value.ToString();
+                                keyUnitData.ItemValue = keyValue.Value == null ? "NA" : keyValue.Value.Value.ToString("f2");
                                 keyUnitDatas.Add(keyUnitData);
 
                                 MeasurementAndKeyDataRelation measurementAndKeyDataRelation = new MeasurementAndKeyDataRelation();
@@ -577,6 +598,342 @@
                 fs.Flush();
                 fs.Close();
             });
+        }
+
+        static object excelExportLock = new object();
+        private async void ExportProductionInColumns(ProductionMeasurementUnitResultAndKeyUnitDataSet exportData)
+        {
+            if (!Config.IsCSVOutputEnabled)
+                return;
+
+            await Task.Run(() =>
+            {
+                lock (excelExportLock)
+                {
+                    if (!Directory.Exists(Config.LogPath))
+                    {
+                        Directory.CreateDirectory(Config.LogPath);
+                    }
+                    var fileName = Path.Combine(Config.LogPath, $"LDSData_{DateTime.Now.ToString("yyyyMMdd")}.xlsx");
+                    //bool isExisted = File.Exists(fileName);
+                    using (ExcelPackage package = new ExcelPackage(new FileInfo(fileName)))
+                    {
+                        ExcelWorksheet rawDataSheet = null;
+                        ExcelWorksheet slantSheet = null;
+                        ExcelWorksheet alignmentSheet = null;
+                        ExcelWorksheet rowAlignmentSheet = null;
+
+                        if (!package.Workbook.Worksheets.Any(s => s.Name == "RawData"))
+                        {
+                            package.Workbook.Worksheets.Add("RawData");
+                            rawDataSheet = package.Workbook.Worksheets["RawData"];
+                            for (int i = 0; i < Config.KeyNameCollection.Count; i++)
+                            {
+                                var cell = rawDataSheet.Cells[i + 3, 1];
+                                cell.Value = Config.KeyNameCollection[i];
+                                SetTitleCell(cell, false);
+                            }
+                        }
+                        rawDataSheet = package.Workbook.Worksheets["RawData"];
+
+                        if (!package.Workbook.Worksheets.Any(s => s.Name == "Slant"))
+                        {
+                            package.Workbook.Worksheets.Add("Slant");
+                            slantSheet = package.Workbook.Worksheets["Slant"];
+
+                            var keyCell = slantSheet.Cells[2, 1];
+                            keyCell.Value = "Key";
+                            SetTitleCell(keyCell);
+
+                            for (int i = 0; i < Config.KeyNameCollection.Count; i++)
+                            {
+                                var cell = slantSheet.Cells[i + 3, 1];
+                                cell.Value = Config.KeyNameCollection[i];
+                                SetTitleCell(cell, false);
+                            }
+                        }
+                        slantSheet = package.Workbook.Worksheets["Slant"];
+
+                        if (!package.Workbook.Worksheets.Any(s => s.Name == "Alignment"))
+                        {
+                            package.Workbook.Worksheets.Add("Alignment");
+                            alignmentSheet = package.Workbook.Worksheets["Alignment"];
+
+                            var keysCell = alignmentSheet.Cells[2, 1];
+                            keysCell.Value = "Keys";
+                            SetTitleCell(keysCell);
+
+                            var positionCell = alignmentSheet.Cells[2, 2];
+                            positionCell.Value = "Positions";
+                            SetTitleCell(positionCell);
+
+                            var alignmentMeasures = exportData.MeasurementUnitResultList.Where(u => u.MeasurementType == "Alignment").ToList();
+                            for (int i = 0; i < alignmentMeasures.Count(); i++)
+                            {
+                                var cellKeys = alignmentSheet.Cells[i + 3, 1];
+                                cellKeys.Value = alignmentMeasures[i].Keys;
+                                SetTitleCell(cellKeys, false);
+
+                                var cellPosition = alignmentSheet.Cells[i + 3, 2];
+                                cellPosition.Value = alignmentMeasures[i].Positions;
+                                SetTitleCell(cellPosition, false);
+                            }
+                        }
+                        alignmentSheet = package.Workbook.Worksheets["Alignment"];
+
+                        if (!package.Workbook.Worksheets.Any(s => s.Name == "RowAlignment"))
+                        {
+                            package.Workbook.Worksheets.Add("RowAlignment");
+                            rowAlignmentSheet = package.Workbook.Worksheets["RowAlignment"];
+
+                            var keysCell = rowAlignmentSheet.Cells[2, 1];
+                            keysCell.Value = "Keys";
+                            SetTitleCell(keysCell);
+
+                            var positionCell = rowAlignmentSheet.Cells[2, 2];
+                            positionCell.Value = "Positions";
+                            SetTitleCell(positionCell);
+
+                            var rowAlignmentMeasures = exportData.MeasurementUnitResultList.Where(u => u.MeasurementType == "RowAlignment").ToList();
+                            for (int i = 0; i < rowAlignmentMeasures.Count(); i++)
+                            {
+                                var cellKeys = rowAlignmentSheet.Cells[i + 3, 1];
+                                cellKeys.Value = rowAlignmentMeasures[i].Keys;
+                                SetTitleCell(cellKeys, false);
+
+                                var cellPosition = rowAlignmentSheet.Cells[i + 3, 2];
+                                cellPosition.Value = rowAlignmentMeasures[i].Positions;
+                                SetTitleCell(cellPosition, false);
+                            }
+                        }
+                        rowAlignmentSheet = package.Workbook.Worksheets["RowAlignment"];
+
+                        #region RawData
+                        {
+                            //rawDataSheet = package.Workbook.Worksheets["RawData"];
+
+                            int rowDataStartCol = rawDataSheet.Dimension.Columns;
+                            var barcodeCell = rawDataSheet.Cells[1, rowDataStartCol + 1, 1, rowDataStartCol + 4];
+                            barcodeCell.Merge = true;
+                            barcodeCell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
+                            barcodeCell.Value = exportData.ProductionMeasurementRecord.ProductionBarcode;
+                            SetTitleCell(barcodeCell);
+
+                            var z1Cell = rawDataSheet.Cells[2, rowDataStartCol + 1];
+                            z1Cell.Value = "Z1";
+                            SetTitleCell(z1Cell);
+
+                            var z2Cell = rawDataSheet.Cells[2, rowDataStartCol + 2];
+                            z2Cell.Value = "Z2";
+                            SetTitleCell(z2Cell);
+
+                            var z3Cell = rawDataSheet.Cells[2, rowDataStartCol + 3];
+                            z3Cell.Value = "Z3";
+                            SetTitleCell(z3Cell);
+
+                            var z4Cell = rawDataSheet.Cells[2, rowDataStartCol + 4];
+                            z4Cell.Value = "Z4";
+                            SetTitleCell(z4Cell);
+
+                            exportData.KeyUnitDataList.ForEach(k =>
+                            {
+                                int keyIndex = Config.KeyNameCollection.IndexOf(k.Key);
+                                if (keyIndex < 0)
+                                {
+                                    LogAsync(DateTime.Now, "Excel瀵煎嚭閿欒", $"{k.Key}鐨凴awData鏈兘鑾峰彇琛屼俊鎭紝鏈鍑�");
+                                    return;
+                                }
+
+                                int zIndex = int.Parse(k.MeasurementItem.Substring(1));
+
+                                var cell = rawDataSheet.Cells[keyIndex + 1 + 2, rowDataStartCol + zIndex];
+                                cell.Value = string.IsNullOrWhiteSpace(k.ItemValue) ? "NA" : k.ItemValue;
+                            });
+                        }
+                        #endregion
+
+                        #region Slant
+                        var slantMeasures = exportData.MeasurementUnitResultList.Where(u => u.MeasurementType == "Slant").ToList();
+                        if (slantMeasures.Count > 0)
+                        {
+                            int slantStartCol = slantSheet.Dimension.Columns;
+
+                            var barcodeCell = slantSheet.Cells[1, slantStartCol + 1, 1, slantStartCol + 2];
+                            barcodeCell.Merge = true;
+                            barcodeCell.Value = exportData.ProductionMeasurementRecord.ProductionBarcode;
+                            SetTitleCell(barcodeCell);
+
+                            var valueCell = slantSheet.Cells[2, slantStartCol + 1];
+                            valueCell.Value = "Value";
+                            SetTitleCell(valueCell);
+
+                            var resultCell = slantSheet.Cells[2, slantStartCol + 2];
+                            resultCell.Value = "Result";
+                            SetTitleCell(resultCell);
+
+                            slantMeasures.ForEach(m =>
+                            {
+                                int rowIndex = Config.KeyNameCollection.IndexOf(m.Keys);
+                                if (rowIndex < 0)
+                                {
+                                    LogAsync(DateTime.Now, "Excel瀵煎嚭閿欒", $"{m.Keys}鐨凷lant鏈兘鑾峰彇琛屼俊鎭紝鏈鍑�");
+                                    return;
+                                }
+
+                                var cellValue = slantSheet.Cells[rowIndex + 1 + 2, slantStartCol + 1];
+                                var cellResult = slantSheet.Cells[rowIndex + 1 + 2, slantStartCol + 2];
+
+                                cellValue.Value = m.MeasurementValue;
+                                cellResult.Value = m.MeasurementResult;
+
+                                if (m.MeasurementResult != "OK")
+                                {
+                                    SetNGCell(cellValue);
+                                    SetNGCell(cellResult);
+                                }
+                            });
+                        }
+                        #endregion
+
+                        #region Alignment
+                        {
+                            var alignmentMeasures = exportData.MeasurementUnitResultList.Where(u => u.MeasurementType == "Alignment").ToList();
+                            if (alignmentMeasures.Count > 0)
+                            {
+                                List<string> keysList = new List<string>();
+                                int allRowNums = alignmentSheet.Dimension.Rows;
+                                int aligneStartCol = alignmentSheet.Dimension.Columns;
+
+                                for (int i = 3; i <= allRowNums; i++)
+                                {
+                                    string keys = alignmentSheet.Cells[i, 1].Value.ToString();
+                                    string position = alignmentSheet.Cells[i, 2].Value.ToString();
+
+                                    keysList.Add($"{keys}_{position}");
+                                }
+
+                                var barcodeCell = alignmentSheet.Cells[1, aligneStartCol + 1, 1, aligneStartCol + 2];
+                                barcodeCell.Merge = true;
+                                barcodeCell.Value = exportData.ProductionMeasurementRecord.ProductionBarcode;
+                                SetTitleCell(barcodeCell);
+
+                                var valueCell = alignmentSheet.Cells[2, aligneStartCol + 1];
+                                valueCell.Value = "Value";
+                                SetTitleCell(valueCell);
+
+                                var resultCell = alignmentSheet.Cells[2, aligneStartCol + 2];
+                                resultCell.Value = "Result";
+                                SetTitleCell(resultCell);
+
+                                alignmentMeasures.ForEach(a =>
+                                {
+                                    int rowIndex = keysList.IndexOf($"{a.Keys}_{a.Positions}");
+                                    if (rowIndex < 0)
+                                    {
+                                        LogAsync(DateTime.Now, "Excel瀵煎嚭閿欒", $"{a.Keys}_{a.Positions}鐨凙lignment鏈兘鑾峰彇琛屼俊鎭紝鏈鍑�");
+                                        return;
+                                    }
+
+                                    var cellValue = alignmentSheet.Cells[rowIndex + 1 + 2, aligneStartCol + 1];
+                                    var cellResult = alignmentSheet.Cells[rowIndex + 1 + 2, aligneStartCol + 2];
+
+                                    cellValue.Value = a.MeasurementValue;
+                                    cellResult.Value = a.MeasurementResult;
+
+                                    if (a.MeasurementResult != "OK")
+                                    {
+                                        SetNGCell(cellValue);
+                                        SetNGCell(cellResult);
+                                    }
+                                });
+                            }
+                        }
+                        #endregion
+
+                        #region RowAlignment
+                        {
+                            var rowAlignmentMeasures = exportData.MeasurementUnitResultList.Where(u => u.MeasurementType == "RowAlignment").ToList();
+                            if (rowAlignmentMeasures.Count > 0)
+                            {
+                                List<string> keysList = new List<string>();
+                                int allRowNums = rowAlignmentSheet.Dimension.Rows;
+                                int aligneStartCol = rowAlignmentSheet.Dimension.Columns;
+
+                                for (int i = 3; i <= allRowNums; i++)
+                                {
+                                    string keys = alignmentSheet.Cells[i, 1].Value.ToString();
+                                    string position = alignmentSheet.Cells[i, 2].Value.ToString();
+
+                                    keysList.Add($"{keys}_{position}");
+                                }
+
+                                var barcodeCell = rowAlignmentSheet.Cells[1, aligneStartCol + 1, 1, aligneStartCol + 2];
+                                barcodeCell.Merge = true;
+                                barcodeCell.Value = exportData.ProductionMeasurementRecord.ProductionBarcode;
+                                SetTitleCell(barcodeCell);
+
+                                var valueCell = rowAlignmentSheet.Cells[2, aligneStartCol + 1];
+                                valueCell.Value = "Value";
+                                SetTitleCell(valueCell);
+
+                                var resultCell = rowAlignmentSheet.Cells[2, aligneStartCol + 2];
+                                resultCell.Value = "Result";
+                                SetTitleCell(resultCell);
+
+                                rowAlignmentMeasures.ForEach(a =>
+                                {
+                                    int rowIndex = keysList.IndexOf($"{a.Keys}_{a.Positions}");
+                                    if (rowIndex < 0)
+                                    {
+                                        LogAsync(DateTime.Now, "Excel瀵煎嚭閿欒", $"{a.Keys}_{a.Positions}鐨凴owAlignment鏈兘鑾峰彇琛屼俊鎭紝鏈鍑�");
+                                        return;
+                                    }
+
+                                    var cellValue = rowAlignmentSheet.Cells[rowIndex + 1 + 2, aligneStartCol + 1];
+                                    var cellResult = rowAlignmentSheet.Cells[rowIndex + 1 + 2, aligneStartCol + 2];
+
+                                    cellValue.Value = a.MeasurementValue;
+                                    cellResult.Value = a.MeasurementResult;
+
+                                    if (a.MeasurementResult != "OK")
+                                    {
+                                        SetNGCell(cellValue);
+                                        SetNGCell(cellResult);
+                                    }
+                                });
+                            }
+                        }
+                        #endregion
+
+                        package.Save();
+                    };
+                }
+            });
+        }
+
+        private void SetNGCell(ExcelRange cell)
+        {
+            cell.Style.Font.Color.SetColor(Color.White);
+            cell.Style.Font.Bold = true;
+            cell.Style.Fill.PatternType = ExcelFillStyle.Solid;
+            cell.Style.Fill.BackgroundColor.SetColor(Color.Red);
+        }
+
+        private void SetTitleCell(ExcelRange cell, bool isCenterAlign = true)
+        {
+            cell.Style.Font.Color.SetColor(Color.White);
+            cell.Style.Font.Bold = true;
+            cell.Style.Fill.PatternType = ExcelFillStyle.Solid;
+            cell.Style.Fill.BackgroundColor.SetColor(Color.FromArgb(31, 73, 125));
+            cell.AutoFitColumns();
+            if (isCenterAlign)
+            {
+                cell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
+            }
+            else
+            {
+                cell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Left;
+            }
         }
 
         #region 鍥惧儚淇濆瓨
@@ -670,7 +1027,7 @@
         }
         #endregion
 
-        private async void RunImageHandle(CameraBase camera, IOperationConfig opConfig, IImageSet imgSet, string snapshotId, string snapshotName, List<MeasurementUnit> measureList)
+        private async void RunImageHandle(IOperationConfig opConfig, IImageSet imgSet, string snapshotId, string snapshotName, List<MeasurementUnit> measureList)
         {
             await Task.Run(() =>
              {
@@ -691,6 +1048,9 @@
                          keyBindCollection.ForEach(k => k.FillKeyValues(null));
                          return;
                      }
+
+                     string fileName = Path.Combine(Config.ImageSaveFolder, "BeforeRun", $"{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.tif");
+                     imgSet.HImage.WriteImage("tiff", 0, fileName);
 
                      _halconToolDict[toolKey].InputImageDic["INPUT_Image"] = imgSet.HImage;
                      if (!_halconToolDict[toolKey].RunProcedure(out string error))
@@ -723,19 +1083,22 @@
                           });
                  }
 
-                 string dir = Path.Combine(Config.ImageSaveFolder, "Clips", $"{snapshotName}_{DateTime.Now.ToString("HHmmss")}");
+                 string dir = Path.Combine(Config.ImageSaveFolder, "Clips", $"{DateTime.Now.ToString("yyyyMMdd")}", $"{snapshotName}_{DateTime.Now.ToString("HHmmss")}");
                  if (!Directory.Exists(dir))
                  {
                      Directory.CreateDirectory(dir);
                  }
 
-                 Parallel.For(1, count.I + 1, (i) =>
-                 //for (int i = 1; i <= count.I; i++)
+                 //Parallel.For(1, count.I + 1, (i) =>
+                 for (int i = 1; i <= count.I; i++)
                  {
                      HOperatorSet.SelectObj(images, out HObject image, i);
 
                      string fileName = Path.Combine(dir, $"{i}.tif");
-                     image.ConvertHObjectToHImage().WriteImage("tiff", 0, fileName);
+                     using (HImage temp = image.ConvertHObjectToHImage())
+                     {
+                         temp.WriteImage("tiff", 0, fileName);
+                     }
 
                      keys.Where(u => u.ImageSeq == i).ToList().ForEach(k =>
                          {
@@ -753,19 +1116,22 @@
                                  _halconToolDict[keyToolKey].InputImageDic["INPUT_Image"] = image;
                                  _halconToolDict[keyToolKey].InputTupleDic["INPUT_Resolution_X"] = scanParam.Resolution_X / 1000000.0;
                                  _halconToolDict[keyToolKey].InputTupleDic["INPUT_Resolution_Z"] = scanParam.Resolution_Z / 1000000.0;
+                                 _halconToolDict[keyToolKey].InputTupleDic["INPUT_ImageId"] = $"{k.AliasName}_{DateTime.Now.ToString("HHmmssfff")}.tif";
                                  if (!_halconToolDict[keyToolKey].RunProcedure(out string error))
                                  {
                                      LogAsync(DateTime.Now, $"{k.AliasName}妫�娴嬬畻娉曞紓甯革紝{error}", "");
                                  }
                                  else
                                  {
-                                     var results = _halconToolDict[keyToolKey].GetResultTuple("OUTPUT_Results").HTupleToDouble();
+                                     var results = _halconToolDict[keyToolKey].GetResultTuple("OUTPUT_Results").DArr.ToList();
                                      if (results.Count == 0 || results.Any(u => u < 0))
                                      {
-                                         LogAsync(DateTime.Now, $"{k.AliasName}妫�娴嬬粨鏋滃紓甯�", "");
+                                         LogAsync(DateTime.Now, $"{k.AliasName}鍘熷鏁版嵁寮傚父", "");
                                      }
                                      else
                                      {
+                                         //results = results.Select(u => double.Parse(u.ToString("f2"))).ToList();
+                                         LogAsync(DateTime.Now, $"{k.AliasName}鍘熷鏁版嵁", $"{string.Join(" ", results)}");
                                          resultDict = k.KeyResultList.ToDictionary(u => u, u =>
                                               {
                                                   int index = k.KeyResultList.IndexOf(u);
@@ -782,9 +1148,9 @@
                              });
                          });
 
-                     //image.Dispose();
+                     image.Dispose();
                  }
-                 );
+                 //);
 
                  //if (count.I != 1)
                  //{
diff --git a/src/Bro.M071.Process/M071Process_MotionCard.cs b/src/Bro.M071.Process/M071Process_MotionCard.cs
index 2821708..c85443d 100644
--- a/src/Bro.M071.Process/M071Process_MotionCard.cs
+++ b/src/Bro.M071.Process/M071Process_MotionCard.cs
@@ -20,6 +20,7 @@
         const int FULLRESETTIME = 5;
 
         object machineStateLock = new object();
+        //MachineState _machineStatePre = MachineState.Unknown;
         MachineState machineState = MachineState.Unknown;
         public MachineState MachineState
         {
@@ -31,100 +32,121 @@
 
                 machineState = value;
 
-                switch (machineState)
+                Task.Run(() =>
                 {
-                    case MachineState.Ready:
-                        lock (machineStateLock)
-                        {
-                            SwitchBeep(false);
-                            SwitchLightRed(false);
-                            SwitchLightYellow(false);
-                        }
-
-                        Task.Run(() =>
-                        {
-
-                            while (MachineState == MachineState.Ready)
+                    switch (machineState)
+                    {
+                        case MachineState.Ready:
+                            lock (machineStateLock)
                             {
-                                lock (machineStateLock)
-                                {
-                                    SwitchLightGreen(true);
-                                    Thread.Sleep(1000);
-                                    SwitchLightGreen(false);
-                                    Thread.Sleep(1000);
-                                }
+                                SwitchBeep(false);
+                                SwitchLightRed(false);
+                                SwitchLightYellow(false);
                             }
-                        });
 
-                        break;
-                    case MachineState.Running:
-                        lock (machineStateLock)
-                        {
-                            SwitchBeep(false);
-                            SwitchLightRed(false);
-                            SwitchLightYellow(false);
-                            SwitchLightGreen(true);
-                        }
-                        break;
-                    case MachineState.Alarm:
-                        lock (machineStateLock)
-                        {
-                            SwitchBeep(true);
-                            SwitchLightRed(true);
-                            SwitchLightYellow(false);
-                            SwitchLightGreen(false);
-                        }
-                        break;
-                    case MachineState.Pause:
-                        lock (machineStateLock)
-                        {
-                            SwitchBeep(false);
-                            SwitchLightRed(false);
-                        }
-                        Task.Run(() =>
-                        {
-                            while (MachineState == MachineState.Pause)
+                            Task.Run(() =>
                             {
-                                lock (machineStateLock)
+                                while (MachineState == MachineState.Ready)
                                 {
-                                    SwitchLightYellow(true);
-                                    SwitchLightGreen(true);
-                                    Thread.Sleep(1000);
-                                    SwitchLightYellow(false);
-                                    SwitchLightGreen(false);
-                                    Thread.Sleep(1000);
+                                    lock (machineStateLock)
+                                    {
+                                        SwitchLightGreen(true);
+                                        Thread.Sleep(1000);
+                                        SwitchLightGreen(false);
+                                        Thread.Sleep(1000);
+                                    }
                                 }
-                            }
-                        });
-                        break;
-                    case MachineState.Resetting:
-                        lock (machineStateLock)
-                        {
-                            SwitchBeep(false);
-                            SwitchLightRed(false);
-                            SwitchLightGreen(false);
-                        }
-                        Task.Run(() =>
-                        {
-                            while (MachineState == MachineState.Resetting)
-                            {
-                                lock (machineStateLock)
-                                {
+                            });
 
-                                    SwitchLightYellow(true);
-                                    Thread.Sleep(1000);
-                                    SwitchLightYellow(false);
-                                    Thread.Sleep(1000);
-                                }
+                            break;
+                        case MachineState.Running:
+                            lock (machineStateLock)
+                            {
+                                SwitchBeep(false);
+                                SwitchLightRed(false);
+                                SwitchLightYellow(false);
+                                SwitchLightGreen(true);
                             }
-                        });
-                        break;
-                    default:
-                        break;
-                }
+                            break;
+                        case MachineState.Alarm:
+                            lock (machineStateLock)
+                            {
+                                SwitchBeep(true);
+                                SwitchLightRed(true);
+                                SwitchLightYellow(false);
+                                SwitchLightGreen(false);
+                            }
+                            break;
+                        case MachineState.Pause:
+                            lock (machineStateLock)
+                            {
+                                SwitchBeep(false);
+                                SwitchLightRed(false);
+                            }
+                            Task.Run(() =>
+                            {
+                                while (MachineState == MachineState.Pause)
+                                {
+                                    lock (machineStateLock)
+                                    {
+                                        SwitchLightYellow(true);
+                                        SwitchLightGreen(true);
+                                        Thread.Sleep(1000);
+                                        SwitchLightYellow(false);
+                                        SwitchLightGreen(false);
+                                        Thread.Sleep(1000);
+                                    }
+                                }
+                            });
+                            break;
+                        case MachineState.Resetting:
+                            lock (machineStateLock)
+                            {
+                                SwitchBeep(false);
+                                SwitchLightRed(false);
+                                SwitchLightGreen(false);
+                            }
+                            Task.Run(() =>
+                            {
+                                while (MachineState == MachineState.Resetting)
+                                {
+                                    lock (machineStateLock)
+                                    {
+                                        SwitchLightYellow(true);
+                                        Thread.Sleep(1000);
+                                        SwitchLightYellow(false);
+                                        Thread.Sleep(1000);
+                                    }
+                                }
+                            });
+                            break;
+                        default:
+                            break;
+                    }
+                });
 
                 OnMachineStateChanged?.Invoke(machineState);
             }
+        }
+
+        private void Pause()
+        {
+            #region 鏉垮崱鏆傚仠鍔ㄤ綔
+            outputCtrlCard.SetImmediatePause();
+            #endregion
+
+            //_pauseHandle.WaitHandle.Reset();
+            //_pauseHandle.WaitResult = true;
+        }
+
+        private void Resume(bool isResumeContinueMoving)
+        {
+            #region 鏉垮崱鎭㈠鍔ㄤ綔
+            outputCtrlCard.ResetImmediatePause(isResumeContinueMoving);
+            #endregion
+
+            //_pauseHandle.WaitHandle.Set();
+            //_pauseHandle.WaitResult = false;
         }
 
         private void MotionCardSettingCheck()
@@ -203,24 +225,6 @@
         [ProcessMethod("MotionCardBase", "Reset", "绠�鍗曞浣嶆搷浣�", InvokeType.TestInvoke)]
         public ProcessResponse Reset(IOperationConfig opConfig, IDevice invokeDevice, IDevice sourceDevice)
         {
-            //if (opConfig == null)
-            //{
-            //    var monitorSet = Config.MonitorSetCollection.FirstOrDefault(u => u.MethodCode == "Reset");
-            //    if (monitorSet == null)
-            //        throw new ProcessException("鏈厤缃粯璁ゅ浣嶆搷浣�");
-
-            //    opConfig = monitorSet.OpConfig;
-            //    if (opConfig == null)
-            //        throw new ProcessException("鏈厤缃浣嶆搷浣滃叿浣撻厤缃姩浣�");
-
-            //    if (invokeDevice == null)
-            //    {
-            //        invokeDevice = DeviceCollection.FirstOrDefault(u => u.Id == monitorSet.InvokeDevice);
-            //        if (invokeDevice == null)
-            //            throw new ProcessException("鏈厤缃浣嶆搷浣滄墽琛岃澶�");
-            //    }
-            //}
-
             if (ResetTimer == null)
             {
                 ResetTimer = new Timer(FullReset, null, -1, -1);
@@ -245,26 +249,23 @@
             (invokeDevice as MotionCardBase).ResetAlarm();
 
             RaisedAlarm("");
-            MachineState = MachineState.Ready;
+
+            if (MachineState != MachineState.Pause)
+            {
+                MachineState = MachineState.Ready;
+            }
+            else
+            {
+                LogAsync(DateTime.Now, "璁惧鏆傚仠涓紝鏃犳硶澶嶄綅", "");
+                return new ProcessResponse(true);
+            }
 
             if (IsEmergencyStopped)
             {
                 RaisedAlarm("鎬ュ仠鎸夐挳鏈仮澶嶏紝璇锋墽琛屽ぇ澶嶄綅");
                 MachineState = MachineState.Alarm;
+                return new ProcessResponse(true);
             }
-
-
-
-            //if (opConfig.InputPara?.Count > 0)
-            //{
-            //    //澶у浣嶄俊鍙�
-            //    ResetTimer.Change(-1, opConfig.InputPara[0] == 1 ? FULLRESETTIME * 1000 : -1);
-            //}
-
-            //if (invokeDevice is MotionCardBase motionCard)
-            //{
-            //    motionCard.Run(opConfig);
-            //}
 
             LogAsync(DateTime.Now, "鏅�氬浣嶅姩浣滃畬鎴�", "");
 
@@ -287,28 +288,16 @@
         [ProcessMethod("MotionCardBase", "FullReset", "澶у浣嶆搷浣�", InvokeType.TestInvoke)]
         public ProcessResponse FullReset(IOperationConfig opConfig, IDevice invokeDevice, IDevice sourceDevice)
         {
-            //if (opConfig == null)
-            //{
-            //    var monitorSet = Config.MonitorSetCollection.FirstOrDefault(u => u.MethodCode == "FullReset");
-            //    if (monitorSet == null)
-            //        throw new ProcessException("鏈厤缃粯璁ゅぇ澶嶄綅鎿嶄綔");
+            if (!IsAllowedWork)
+            {
+                LogAsync(DateTime.Now, $"{SafetyMsg}锛屽ぇ澶嶄綅澶辫触", "");
+                return new ProcessResponse(false);
+            }
 
-            //    opConfig = monitorSet.OpConfig;
-            //    if (opConfig == null)
-            //        throw new ProcessException("鏈厤缃ぇ澶嶄綅鎿嶄綔鍏蜂綋閰嶇疆鍔ㄤ綔");
-
-            //    if (invokeDevice == null)
-            //    {
-            //        invokeDevice = DeviceCollection.FirstOrDefault(u => u.Id == monitorSet.InvokeDevice);
-            //        if (invokeDevice == null)
-            //            throw new ProcessException("鏈厤缃ぇ澶嶄綅鎿嶄綔鎵ц璁惧");
-            //    }
-            //}
-
-            //if (invokeDevice is MotionCardBase motionCard)
-            //{
-            //    motionCard.Run(opConfig);
-            //}
+            if (MachineState == MachineState.Pause)
+            {
+                Resume(false);
+            }
 
             MachineState = MachineState.Resetting;
             MotionCardDefaultRun("FullReset", ref opConfig, ref invokeDevice);
@@ -335,12 +324,12 @@
         /// WaitHandle 鏆傚仠鍙ユ焺  榛樿涓洪潪闃诲 鍙墽琛�
         /// WaitResult 鏆傚仠鏍囧織 true 姝e父鎵ц  false 鏆傚仠涓�
         /// </summary>
-        ManualWaitConfirm _pauseHandle = new ManualWaitConfirm()
-        {
-            WaitHandle = new ManualResetEvent(true),
-            WaitResult = true,
-        };
-        MachineState _machineStateBeforePause = MachineState.Unknown;
+        //ManualWaitConfirm _pauseHandle = new ManualWaitConfirm()
+        //{
+        //    WaitHandle = new ManualResetEvent(true),
+        //    WaitResult = true,
+        //};
+
         List<MachineState> _statesAllowPause = new List<MachineState>() { MachineState.Running, MachineState.Ready, MachineState.Pause };
         [ProcessMethod("", "SwitchJobStatus", "娴佺▼鐘舵�佸垏鎹�", InvokeType.TestInvoke)]
         public ProcessResponse SwitchJobStatus(IOperationConfig opConfig, IDevice invokeDevice, IDevice sourceDevice)
@@ -358,10 +347,7 @@
                 throw new ProcessException("鏈幏鍙栨澘鍗¤澶�");
 
             bool? isToPause = null; //true 鏆傚仠 false 缁х画
-            //if (opConfig.InputPara != null && opConfig.InputPara.Count > 0)
-            //{
-            //    isToPause = opConfig.InputPara[0] == 1;
-            //}
+
             if (opConfig.InputPara[0] == 10)
             {
                 isToPause = false;
@@ -373,59 +359,41 @@
 
             if (isToPause == null)
             {
-                if (!_pauseHandle.WaitResult)
-                {
-                    #region 鏉垮崱鏆傚仠鍔ㄤ綔
-                    motionDevice.SetImmediatePause();
-                    #endregion
-
-                    _pauseHandle.WaitHandle.Reset();
-                    _pauseHandle.WaitResult = true;
-                    _machineStateBeforePause = MachineState;
-                    MachineState = MachineState.Pause;
-                }
-                else if (!_pauseHandle.WaitResult)
-                {
-                    #region 鏉垮崱鎭㈠鍔ㄤ綔
-                    motionDevice.ResetImmediatePause();
-                    #endregion
-
-                    _pauseHandle.WaitHandle.Set();
-                    _pauseHandle.WaitResult = false;
-                    MachineState = _machineStateBeforePause;
-                }
+                IsManualPaused = !IsManualPaused;
             }
             else
             {
-                if (isToPause.Value)
-                {
-                    if (!_pauseHandle.WaitResult)
-                    {
-                        #region 鏉垮崱鏆傚仠鍔ㄤ綔
-                        motionDevice.SetImmediatePause();
-                        #endregion
+                //if (isToPause.Value)
+                //{
+                //    if (!_pauseHandle.WaitResult)
+                //    {
+                //        //#region 鏉垮崱鏆傚仠鍔ㄤ綔
+                //        //motionDevice.SetImmediatePause();
+                //        //#endregion
 
-                        _pauseHandle.WaitHandle.Reset();
-                        _pauseHandle.WaitResult = true;
-                        MachineState = MachineState.Pause;
-                    }
-                }
-                else
-                {
-                    if (!_pauseHandle.WaitResult)
-                    {
-                        #region 鏉垮崱鎭㈠鍔ㄤ綔
-                        motionDevice.ResetImmediatePause();
-                        #endregion
+                //        //_pauseHandle.WaitHandle.Reset();
+                //        //_pauseHandle.WaitResult = true;
+                //        MachineState = MachineState.Pause;
+                //    }
+                //}
+                //else
+                //{
+                //    if (!_pauseHandle.WaitResult)
+                //    {
+                //        //#region 鏉垮崱鎭㈠鍔ㄤ綔
+                //        //motionDevice.ResetImmediatePause();
+                //        //#endregion
 
-                        _pauseHandle.WaitHandle.Set();
-                        _pauseHandle.WaitResult = false;
-                        MachineState = _machineStateBeforePause;
-                    }
-                }
+                //        //_pauseHandle.WaitHandle.Set();
+                //        //_pauseHandle.WaitResult = false;
+                //        MachineState = _machineStatePre;
+                //    }
+                //}
+
+                IsManualPaused = isToPause.Value;
             }
 
-            return new ProcessResponse(_pauseHandle.WaitResult);
+            return new ProcessResponse(IsManualPaused);
         }
 
         ////[ProcessMethod("", "PauseJob", "鏆傚仠娴佺▼", InvokeType.TestInvoke)]
@@ -633,6 +601,7 @@
         {
             get => !(IsSafetyBeamTrigged || IsSafetyDoorTrigged || IsEmergencyStopped);
         }
+
         string SafetyMsg
         {
             get => $"{(IsSafetyBeamTrigged ? "瀹夊叏鍏夊箷" : "")}{(IsSafetyDoorTrigged ? " 瀹夊叏闂�" : "")}{(IsEmergencyStopped ? " 鎬ュ仠鎸夐挳" : "")}瑙﹀彂涓�";
@@ -641,21 +610,85 @@
         #region 瀹夊叏闂� & 瀹夊叏鍏夌嚎
         bool isSafetyDoorTrigged = false;
         bool isSafetyBeamTrigged = false;
+        bool isManualPaused = false;
 
         public bool IsSafetyDoorTrigged
         {
-            get => !Config.IsSafetyDoorBlocked && isSafetyDoorTrigged;
-            set => isSafetyDoorTrigged = value;
+            get => (!Config.IsSafetyDoorBlocked) && isSafetyDoorTrigged;
+            set
+            {
+                isSafetyDoorTrigged = value;
+                CheckMachinePauseState();
+            }
         }
         public bool IsSafetyBeamTrigged
         {
-            get => !Config.IsSafetyBeamBlocked && isSafetyBeamTrigged;
-            set => isSafetyBeamTrigged = value;
+            get => (!Config.IsSafetyBeamBlocked) && isSafetyBeamTrigged;
+            set
+            {
+                isSafetyBeamTrigged = value;
+                CheckMachinePauseState();
+            }
+        }
+
+        public bool IsManualPaused
+        {
+            get => isManualPaused;
+            set
+            {
+                isManualPaused = value;
+                CheckMachinePauseState();
+            }
+        }
+
+        ManualResetEventSlim _pausedHandle = new ManualResetEventSlim(true);
+        MachineState _machineStateBeforePause = MachineState.Unknown;
+        private void CheckMachinePauseState()
+        {
+            //await Task.Run(() =>
+            {
+                if (IsMachinePaused)
+                {
+                    if (MachineState == MachineState.Ready || MachineState == MachineState.Running)
+                    {
+                        _machineStateBeforePause = MachineState;
+                    }
+                    MachineState = MachineState.Pause;
+                    _pausedHandle.Reset();
+
+                    if (_machineStateBeforePause == MachineState.Running)
+                    {
+                        Pause();
+                    }
+                }
+                else
+                {
+                    if (MachineState == MachineState.Pause)
+                    {
+                        if (_machineStateBeforePause == MachineState.Running)
+                        {
+                            Resume(true);
+                        }
+                        _pausedHandle.Set();
+                        RaisedAlarm("");
+                        MachineState = _machineStateBeforePause;
+                    }
+                }
+            }
+            //);
+        }
+
+        public bool IsMachinePaused
+        {
+            get => IsSafetyBeamTrigged || IsSafetyDoorTrigged || IsManualPaused;
         }
 
         [ProcessMethod("", "SafetyDoorSignal", "瀹夊叏闂ㄤ俊鍙风洃鎺э紝姝e父ON锛孫FF鏃舵姤璀�", InvokeType.TestInvoke)]
         public ProcessResponse SafetyDoorSignal(IOperationConfig opConfig, IDevice invokeDevice, IDevice sourceDevice)
         {
+            //if (MachineState != MachineState.Running && MachineState != MachineState.Pause)
+            //    return new ProcessResponse(true);
+
             if (opConfig.InputPara == null || opConfig.InputPara.Count == 0)
                 throw new ProcessException("瀹夊叏闂ㄧ洃鎺ф湭閰嶇疆杈撳叆淇″彿");
 
@@ -664,7 +697,6 @@
             if (IsSafetyDoorTrigged)
             {
                 RaisedAlarm("瀹夊叏闂ㄦ湭姝e父鍏抽棴");
-                MachineState = MachineState.Alarm;
             }
 
             return new ProcessResponse(true);
@@ -673,9 +705,6 @@
         [ProcessMethod("", "SafetyBeamSignal", "瀹夊叏鍏夊箷淇″彿鐩戞帶锛屾甯窸N锛孫FF鏃舵姤璀�", InvokeType.TestInvoke)]
         public ProcessResponse SafetyBeamSignal(IOperationConfig opConfig, IDevice invokeDevice, IDevice sourceDevice)
         {
-            if (MachineState != MachineState.Running && MachineState != MachineState.Alarm)
-                return new ProcessResponse(true);
-
             if (opConfig.InputPara == null || opConfig.InputPara.Count == 0)
                 throw new ProcessException("瀹夊叏鍏夊箷鐩戞帶鏈厤缃緭鍏ヤ俊鍙�");
 
@@ -684,7 +713,6 @@
             if (IsSafetyBeamTrigged)
             {
                 RaisedAlarm("瀹夊叏鍏夌嚎琚伄鎸�");
-                MachineState = MachineState.Alarm;
             }
 
             return new ProcessResponse(true);
@@ -719,7 +747,7 @@
 
             IsEmergencyStopped = opConfig.InputPara[0] == 0;
 
-            if (IsEmergencyStopped)
+            if (isEmergencyStopped)
             {
                 RaisedAlarm("鎬ュ仠鎸夐挳琚媿涓�");
                 MachineState = MachineState.Alarm;
diff --git a/src/Bro.M071.Process/UI/M071_MainForm.Designer.cs b/src/Bro.M071.Process/UI/M071_MainForm.Designer.cs
index f72a914..bbe64d2 100644
--- a/src/Bro.M071.Process/UI/M071_MainForm.Designer.cs
+++ b/src/Bro.M071.Process/UI/M071_MainForm.Designer.cs
@@ -48,6 +48,7 @@
             this.lvMeasures = new System.Windows.Forms.ListView();
             this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
             this.propGridKeyIndicator = new System.Windows.Forms.PropertyGrid();
+            this.tsmiRefreshLabels = new System.Windows.Forms.ToolStripMenuItem();
             this.contextMenuStrip1.SuspendLayout();
             this.plImage.SuspendLayout();
             this.tscEditLocation.ContentPanel.SuspendLayout();
@@ -64,15 +65,16 @@
             this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
             this.tsmiShowToolBar,
             this.tsmiShowStatusBar,
-            this.tsmiShowEditor});
+            this.tsmiShowEditor,
+            this.tsmiRefreshLabels});
             this.contextMenuStrip1.Name = "contextMenuStrip1";
-            this.contextMenuStrip1.Size = new System.Drawing.Size(149, 70);
+            this.contextMenuStrip1.Size = new System.Drawing.Size(181, 114);
             // 
             // tsmiShowToolBar
             // 
             this.tsmiShowToolBar.CheckOnClick = true;
             this.tsmiShowToolBar.Name = "tsmiShowToolBar";
-            this.tsmiShowToolBar.Size = new System.Drawing.Size(148, 22);
+            this.tsmiShowToolBar.Size = new System.Drawing.Size(180, 22);
             this.tsmiShowToolBar.Text = "鏄剧ず宸ュ叿鏉�";
             this.tsmiShowToolBar.CheckedChanged += new System.EventHandler(this.tsmiShowToolBar_CheckedChanged);
             // 
@@ -80,7 +82,7 @@
             // 
             this.tsmiShowStatusBar.CheckOnClick = true;
             this.tsmiShowStatusBar.Name = "tsmiShowStatusBar";
-            this.tsmiShowStatusBar.Size = new System.Drawing.Size(148, 22);
+            this.tsmiShowStatusBar.Size = new System.Drawing.Size(180, 22);
             this.tsmiShowStatusBar.Text = "鏄剧ず鐘舵�佹爮";
             this.tsmiShowStatusBar.CheckedChanged += new System.EventHandler(this.tsmiShowStatusBar_CheckedChanged);
             // 
@@ -88,7 +90,7 @@
             // 
             this.tsmiShowEditor.CheckOnClick = true;
             this.tsmiShowEditor.Name = "tsmiShowEditor";
-            this.tsmiShowEditor.Size = new System.Drawing.Size(148, 22);
+            this.tsmiShowEditor.Size = new System.Drawing.Size(180, 22);
             this.tsmiShowEditor.Text = "缂栬緫鏄剧ず鐐逛綅";
             this.tsmiShowEditor.CheckedChanged += new System.EventHandler(this.tsmiShowEditor_CheckedChanged);
             // 
@@ -291,6 +293,13 @@
             this.propGridKeyIndicator.TabIndex = 0;
             this.propGridKeyIndicator.ToolbarVisible = false;
             // 
+            // tsmiRefreshLabels
+            // 
+            this.tsmiRefreshLabels.Name = "tsmiRefreshLabels";
+            this.tsmiRefreshLabels.Size = new System.Drawing.Size(180, 22);
+            this.tsmiRefreshLabels.Text = "鍒锋柊鏄剧ず鐐逛綅";
+            this.tsmiRefreshLabels.Click += new System.EventHandler(this.tsmiRefreshLabels_Click);
+            // 
             // M071_MainForm
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -343,5 +352,6 @@
         private System.Windows.Forms.Button btnStartMeasure;
         private System.Windows.Forms.Button btnReset;
         private System.Windows.Forms.ColumnHeader columnHeader1;
+        private System.Windows.Forms.ToolStripMenuItem tsmiRefreshLabels;
     }
 }
\ No newline at end of file
diff --git a/src/Bro.M071.Process/UI/M071_MainForm.cs b/src/Bro.M071.Process/UI/M071_MainForm.cs
index 6aa59b8..efadaed 100644
--- a/src/Bro.M071.Process/UI/M071_MainForm.cs
+++ b/src/Bro.M071.Process/UI/M071_MainForm.cs
@@ -136,6 +136,27 @@
             tscEditLocation.Visible = tsmiShowEditor.Checked;
             tscEditLocation.BringToFront();
         }
+
+        private void tsmiRefreshLabels_Click(object sender, EventArgs e)
+        {
+            cvImage.Elements.Clear();
+            lvMeasures.Items.Clear();
+
+            Config.MeasurementUnitCollection.ForEach(u =>
+            {
+                if (!u.IsEnabled)
+                    return;
+
+                var ele = new KeyIndicator(u.Id, u.DisplayLocation);
+                cvImage.Elements.Add(ele);
+
+                ListViewItem item = new ListViewItem(u.GetDisplayText());
+                item.Tag = u.Id;
+                lvMeasures.Items.Add(item);
+            });
+
+            this.Invalidate();
+        }
         #endregion
 
         #region 鏍囩缂栬緫鍖�
@@ -292,7 +313,7 @@
         {
             if (lblMachineState.IsHandleCreated)
             {
-                lblMachineState.BeginInvoke(new Action(() =>
+                lblMachineState.Invoke(new Action(() =>
                 {
                     switch (state)
                     {
@@ -453,5 +474,6 @@
             Process_M071.ResetTimer.Change(-1, -1);
         }
         #endregion
+
     }
 }
diff --git a/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.Designer.cs b/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.Designer.cs
new file mode 100644
index 0000000..9a393c9
--- /dev/null
+++ b/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.Designer.cs
@@ -0,0 +1,133 @@
+锘縩amespace Bro.M071.Process.UI
+{
+    partial class M071_PatchInsertMeasurement
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.label1 = new System.Windows.Forms.Label();
+            this.cboMeasureType = new System.Windows.Forms.ComboBox();
+            this.label2 = new System.Windows.Forms.Label();
+            this.cboStartKey = new System.Windows.Forms.ComboBox();
+            this.label3 = new System.Windows.Forms.Label();
+            this.cboEndKey = new System.Windows.Forms.ComboBox();
+            this.btnInsertMeasurement = new System.Windows.Forms.Button();
+            this.SuspendLayout();
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Location = new System.Drawing.Point(22, 14);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(55, 13);
+            this.label1.TabIndex = 0;
+            this.label1.Text = "娴嬮噺绫诲瀷";
+            // 
+            // cboMeasureType
+            // 
+            this.cboMeasureType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.cboMeasureType.FormattingEnabled = true;
+            this.cboMeasureType.Location = new System.Drawing.Point(95, 11);
+            this.cboMeasureType.Name = "cboMeasureType";
+            this.cboMeasureType.Size = new System.Drawing.Size(121, 21);
+            this.cboMeasureType.TabIndex = 1;
+            // 
+            // label2
+            // 
+            this.label2.AutoSize = true;
+            this.label2.Location = new System.Drawing.Point(22, 53);
+            this.label2.Name = "label2";
+            this.label2.Size = new System.Drawing.Size(43, 13);
+            this.label2.TabIndex = 0;
+            this.label2.Text = "璧峰閿�";
+            // 
+            // cboStartKey
+            // 
+            this.cboStartKey.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.cboStartKey.FormattingEnabled = true;
+            this.cboStartKey.Location = new System.Drawing.Point(95, 50);
+            this.cboStartKey.Name = "cboStartKey";
+            this.cboStartKey.Size = new System.Drawing.Size(121, 21);
+            this.cboStartKey.TabIndex = 1;
+            // 
+            // label3
+            // 
+            this.label3.AutoSize = true;
+            this.label3.Location = new System.Drawing.Point(22, 92);
+            this.label3.Name = "label3";
+            this.label3.Size = new System.Drawing.Size(43, 13);
+            this.label3.TabIndex = 0;
+            this.label3.Text = "缁撴潫閿�";
+            // 
+            // cboEndKey
+            // 
+            this.cboEndKey.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.cboEndKey.FormattingEnabled = true;
+            this.cboEndKey.Location = new System.Drawing.Point(95, 89);
+            this.cboEndKey.Name = "cboEndKey";
+            this.cboEndKey.Size = new System.Drawing.Size(121, 21);
+            this.cboEndKey.TabIndex = 1;
+            // 
+            // btnInsertMeasurement
+            // 
+            this.btnInsertMeasurement.Location = new System.Drawing.Point(25, 133);
+            this.btnInsertMeasurement.Name = "btnInsertMeasurement";
+            this.btnInsertMeasurement.Size = new System.Drawing.Size(192, 36);
+            this.btnInsertMeasurement.TabIndex = 2;
+            this.btnInsertMeasurement.Text = "鐢熸垚妫�娴嬮」";
+            this.btnInsertMeasurement.UseVisualStyleBackColor = true;
+            this.btnInsertMeasurement.Click += new System.EventHandler(this.btnInsertMeasurement_Click);
+            // 
+            // M071_PatchInsertMeasurement
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(800, 488);
+            this.Controls.Add(this.btnInsertMeasurement);
+            this.Controls.Add(this.cboEndKey);
+            this.Controls.Add(this.cboStartKey);
+            this.Controls.Add(this.cboMeasureType);
+            this.Controls.Add(this.label3);
+            this.Controls.Add(this.label2);
+            this.Controls.Add(this.label1);
+            this.Name = "M071_PatchInsertMeasurement";
+            this.Text = "M071_PatchInsertMeasurement";
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.ComboBox cboMeasureType;
+        private System.Windows.Forms.Label label2;
+        private System.Windows.Forms.ComboBox cboStartKey;
+        private System.Windows.Forms.Label label3;
+        private System.Windows.Forms.ComboBox cboEndKey;
+        private System.Windows.Forms.Button btnInsertMeasurement;
+    }
+}
\ No newline at end of file
diff --git a/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.cs b/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.cs
new file mode 100644
index 0000000..59f538a
--- /dev/null
+++ b/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.cs
@@ -0,0 +1,161 @@
+锘縰sing Bro.Common.Helper;
+using Bro.UI.Model.Winform;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace Bro.M071.Process.UI
+{
+    [MenuNode("M071_PatchInsertMeasurement", "鎵归噺鐢熸垚妫�娴嬮」", 4, "M071Node", true)]
+    public partial class M071_PatchInsertMeasurement : MenuFrmBase
+    {
+        public M071_PatchInsertMeasurement()
+        {
+            InitializeComponent();
+        }
+
+        M071Config Config => Process?.IConfig as M071Config;
+        M071Process Process_M071 => Process as M071Process;
+
+        public override void OnProcessUpdated()
+        {
+            base.OnProcessUpdated();
+
+            if (Config == null)
+                return;
+
+            this.Invoke(new Action(() =>
+            {
+                UIHelper.SetCombo(cboMeasureType, Config.MeasureTypeCollection, "Code", "Code");
+                UIHelper.SetCombo(cboStartKey, new List<string>(Config.KeyNameCollection), "", "");
+                UIHelper.SetCombo(cboEndKey, new List<string>(Config.KeyNameCollection), "", "");
+            }));
+        }
+
+        private async void btnInsertMeasurement_Click(object sender, EventArgs e)
+        {
+            btnInsertMeasurement.Enabled = false;
+            btnInsertMeasurement.Text = "鐢熸垚涓�傘�傘��";
+
+            string measureType = cboMeasureType.Text;
+            string startKey = cboStartKey.Text;
+            string endKey = cboEndKey.Text;
+
+            await PatchInsertMeasurement(measureType, startKey, endKey).ContinueWith(t =>
+            {
+                MessageBox.Show(t.Result);
+            });
+
+            btnInsertMeasurement.Enabled = true;
+            btnInsertMeasurement.Text = "鐢熸垚妫�娴嬮」";
+        }
+
+        private async Task<string> PatchInsertMeasurement(string measureType, string startKey, string endKey)
+        {
+            return await Task.Run(() =>
+             {
+                 int startIndex = Config.KeyNameCollection.IndexOf(startKey);
+                 int endIndex = Config.KeyNameCollection.IndexOf(endKey);
+
+                 KeyLocation startRect = Config.KeyLocationCollection.FirstOrDefault(u => u.Key == startKey);
+                 //KeyLocation endRect = Config.KeyLocationCollection.FirstOrDefault(u => u.Key == endKey);
+                 if (startRect == null)
+                 {
+                     return $"鏈厤缃畕(startRect == null ? startKey : "")}鐨勬樉绀轰綅缃�";
+                 }
+
+                 if (startIndex > endIndex)
+                 {
+                     return "璧峰閿笉鑳藉皬浜庣粨鏉熼敭";
+                 }
+
+                 switch (measureType)
+                 {
+                     case "Slant":
+                         {
+                             for (int i = startIndex; i <= endIndex; i++)
+                             {
+                                 int curKeyEdge_X = (i - startIndex + 1) * (startRect.KeyRect.Width + startRect.IntervalRect.Width) + startRect.KeyRect.X - startRect.IntervalRect.Width;
+
+                                 int lableX = (curKeyEdge_X + curKeyEdge_X - startRect.KeyRect.Width - 25) / 2;
+                                 int lable_Up_Y = startRect.KeyRect.Y + (startRect.KeyRect.Height - 25) / 2;
+
+                                 MeasurementUnit mUnitUp = new MeasurementUnit();
+                                 mUnitUp.MeasureType = measureType;
+                                 mUnitUp.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i], KeyResultId = "All" });
+                                 mUnitUp.DisplayLocation = new Rectangle(lableX, lable_Up_Y, 25, 25);
+                                 Config.MeasurementUnitCollection.Add(mUnitUp);
+                             }
+                         }
+                         break;
+                     case "Alignment":
+                         {
+                             for (int i = startIndex; i < endIndex; i++)
+                             {
+                                 int curKeyEdge_X = (i - startIndex + 1) * (startRect.KeyRect.Width + startRect.IntervalRect.Width) + startRect.KeyRect.X - startRect.IntervalRect.Width;
+                                 int nextKeyStart_X = curKeyEdge_X + startRect.IntervalRect.Width;
+
+                                 int lableX = (curKeyEdge_X + nextKeyStart_X - 25) / 2;
+                                 int lable_Up_Y = startRect.KeyRect.Y + 4;
+                                 int lable_Down_y = startRect.KeyRect.Y + startRect.KeyRect.Height - 4 - 12;
+
+                                 MeasurementUnit mUnitUp = new MeasurementUnit();
+                                 mUnitUp.MeasureType = measureType;
+                                 mUnitUp.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i], KeyResultId = "Z2" });
+                                 mUnitUp.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i + 1], KeyResultId = "Z1" });
+                                 mUnitUp.DisplayLocation = new Rectangle(lableX, lable_Up_Y, 25, 12);
+                                 Config.MeasurementUnitCollection.Add(mUnitUp);
+
+                                 MeasurementUnit mUnitDown = new MeasurementUnit();
+                                 mUnitDown.MeasureType = measureType;
+                                 mUnitDown.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i], KeyResultId = "Z4" });
+                                 mUnitDown.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i + 1], KeyResultId = "Z3" });
+                                 mUnitDown.DisplayLocation = new Rectangle(lableX, lable_Down_y, 25, 12);
+                                 Config.MeasurementUnitCollection.Add(mUnitDown);
+                             }
+                         }
+                         break;
+                     case "RowAlignment":
+                         {
+                             int start_X = startRect.KeyRect.X + startRect.KeyRect.Width / 3;
+                             int end_X = (startRect.KeyRect.Width + startRect.IntervalRect.Width) * (endIndex - startIndex) + startRect.KeyRect.X + startRect.KeyRect.Width * 2 / 3;
+
+                             int lable_Up_Y = startRect.KeyRect.Y - 6;
+                             int lable_Down_Y = startRect.KeyRect.Y + startRect.KeyRect.Height - 6;
+
+                             MeasurementUnit mUnitUp = new MeasurementUnit();
+                             mUnitUp.MeasureType = measureType;
+                             for (int i = startIndex; i < endIndex; i++)
+                             {
+                                 mUnitUp.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i], KeyResultId = "Z2" });
+                                 mUnitUp.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i + 1], KeyResultId = "Z1" });
+                             }
+
+                             mUnitUp.DisplayLocation = new Rectangle(start_X, lable_Up_Y, end_X - start_X, 12);
+                             Config.MeasurementUnitCollection.Add(mUnitUp);
+
+                             MeasurementUnit mUnitDown = new MeasurementUnit();
+                             mUnitDown.MeasureType = measureType;
+                             for (int i = startIndex; i < endIndex; i++)
+                             {
+                                 mUnitDown.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i], KeyResultId = "Z4" });
+                                 mUnitDown.KeyUnitCollection.Add(new KeyUnitBind() { Key = Config.KeyNameCollection[i + 1], KeyResultId = "Z3" });
+                             }
+
+                             mUnitDown.DisplayLocation = new Rectangle(start_X, lable_Down_Y, end_X - start_X, 12);
+                             Config.MeasurementUnitCollection.Add(mUnitDown);
+                         }
+                         break;
+                 }
+
+                 return "宸叉壒閲忕敓鎴愭娴嬮」锛岃鍒伴厤缃晫闈㈡煡鐪嬪拰淇濆瓨";
+             });
+        }
+    }
+}
diff --git a/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.resx b/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/src/Bro.M071.Process/UI/M071_PatchInsertMeasurement.resx
@@ -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>
\ No newline at end of file
diff --git a/src/Bro.M071.Process/packages.config b/src/Bro.M071.Process/packages.config
index 2ebb67c..3ce0179 100644
--- a/src/Bro.M071.Process/packages.config
+++ b/src/Bro.M071.Process/packages.config
@@ -2,6 +2,7 @@
 <packages>
   <package id="Autofac" version="4.9.4" targetFramework="net452" />
   <package id="DockPanelSuite" version="3.0.6" targetFramework="net452" />
+  <package id="EPPlus" version="4.5.3.3" targetFramework="net452" />
   <package id="Newtonsoft.Json" version="11.0.2" targetFramework="net452" />
   <package id="PostSharp.Redist" version="6.2.7" targetFramework="net452" />
   <package id="WeifenLuo.WinFormsUI.Docking" version="2.1.0" targetFramework="net452" />
diff --git a/src/Bro.UI.Config/MainFrm.cs b/src/Bro.UI.Config/MainFrm.cs
index 03faa5b..c734c03 100644
--- a/src/Bro.UI.Config/MainFrm.cs
+++ b/src/Bro.UI.Config/MainFrm.cs
@@ -331,9 +331,14 @@
             {
                 foreach (var dock in dockPanelMain.Contents)
                 {
-                    MenuFrmBase m = dock as MenuFrmBase;
+                    //MenuFrmBase m = dock as MenuFrmBase;
 
-                    m.DownloadProcess(_process);
+                    //m.DownloadProcess(_process);
+
+                    if (dock is MenuFrmBase menuFrm)
+                    {
+                        menuFrm.DownloadProcess(_process);
+                    }
                 }
             }
             catch (Exception ex)
diff --git a/src/Bro.UI.Model.Winform/CommonHelper.cs b/src/Bro.UI.Model.Winform/CommonHelper.cs
index ad96dbc..386a179 100644
--- a/src/Bro.UI.Model.Winform/CommonHelper.cs
+++ b/src/Bro.UI.Model.Winform/CommonHelper.cs
@@ -60,7 +60,12 @@
         /// 1st MouseState 鍒濆鐘舵��
         /// 2nd MouseState 鍙樺寲鐘舵��
         /// </summary>
-        public static Action<ElementBase, ElementState, ElementState> ChangeElementsMouseState;
+        public static event Action<ElementBase, ElementState, ElementState> ChangeElementsMouseState;
+
+        public static void TriggerElementsMouseStateChanged(ElementBase ele, ElementState preState, ElementState curState)
+        {
+            ChangeElementsMouseState?.Invoke(ele, preState, curState);
+        }
     }
 
     public class NoticedPoints : List<Point>
diff --git a/src/Bro.UI.Model.Winform/ElementBase.cs b/src/Bro.UI.Model.Winform/ElementBase.cs
index c0feb9d..669a378 100644
--- a/src/Bro.UI.Model.Winform/ElementBase.cs
+++ b/src/Bro.UI.Model.Winform/ElementBase.cs
@@ -191,7 +191,7 @@
                 {
                     ElementState preState = state;
                     Set(ref state, value);
-                    EventRouter.ChangeElementsMouseState?.Invoke(this, preState, state);
+                    EventRouter.TriggerElementsMouseStateChanged(this, preState, state);
                     switch (state)
                     {
                         case ElementState.MouseHover:
diff --git a/src/Bro.UI.Model.Winform/UI/CanvasImage.cs b/src/Bro.UI.Model.Winform/UI/CanvasImage.cs
index 3bd6941..76beeac 100644
--- a/src/Bro.UI.Model.Winform/UI/CanvasImage.cs
+++ b/src/Bro.UI.Model.Winform/UI/CanvasImage.cs
@@ -31,7 +31,8 @@
             MouseMove += Canvas_MouseMove;
             MouseUp += Canvas_MouseUp;
 
-            EventRouter.ChangeElementsMouseState = OnElementChangeMouseState;
+            EventRouter.ChangeElementsMouseState -= OnElementChangeMouseState;
+            EventRouter.ChangeElementsMouseState += OnElementChangeMouseState;
         }
 
         #region Event

--
Gitblit v1.8.0