From b3855dde0cb86819d17391432ad566564e4b66d7 Mon Sep 17 00:00:00 2001
From: peizhiyong <zhiyong.pei@broconcentric.com>
Date: 星期二, 19 八月 2025 10:06:57 +0800
Subject: [PATCH] 连续NG报警功能,单机台生产数据划分白夜班功能以及S3S5相同位置缺陷放射位置优化

---
 src/Bro.M141.Process/MyMQTT.cs                           |    2 
 src/Bro.M141.Process/UI/FrmContinuousNGAlarm.Designer.cs |   95 +++++++
 src/Bro.M141.Process/M141Process_Mysql.cs                |    2 
 src/Bro.M141.Process/M141Config.cs                       |   26 ++
 src/Bro.M141.Process/Bro.M141.Process.csproj             |   10 
 src/Bro.M141.Process/UI/FrmContinuousNGAlarm.resx        |  120 ++++++++++
 src/Bro.M141.Process/M141Process.cs                      |  379 +++++++++++++++++++++++++++++-
 src/Bro.M141.Process/M141Process_ImageCheck.cs           |   12 
 src/Bro.M141.Process/UI/FrmContinuousNGAlarm.cs          |   45 +++
 9 files changed, 661 insertions(+), 30 deletions(-)

diff --git a/src/Bro.M141.Process/Bro.M141.Process.csproj b/src/Bro.M141.Process/Bro.M141.Process.csproj
index b92ffe5..376bc90 100644
--- a/src/Bro.M141.Process/Bro.M141.Process.csproj
+++ b/src/Bro.M141.Process/Bro.M141.Process.csproj
@@ -16,6 +16,16 @@
 	</Target>
 
 	<!--<ItemGroup>
+	  <Compile Include="UI\FrmContinuousNGAlarm.cs" />
+	  <Compile Include="UI\FrmContinuousNGAlarm.Designer.cs" />
+	</ItemGroup>
+
+	<ItemGroup>
+	  <EmbeddedResource Include="UI\FrmContinuousNGAlarm.resx" />
+	</ItemGroup>-->
+
+
+	<!--<ItemGroup>
 	  <Compile Include="MyMQTT.cs" />
 	</ItemGroup>-->
 
diff --git a/src/Bro.M141.Process/M141Config.cs b/src/Bro.M141.Process/M141Config.cs
index 0000efa..576f8cd 100644
--- a/src/Bro.M141.Process/M141Config.cs
+++ b/src/Bro.M141.Process/M141Config.cs
@@ -104,7 +104,33 @@
             }
         }
 
+        [Category("杩炵画NG鎶ヨ璁剧疆")]
+        [Description("杩炵画NG鎶ヨ鎬诲紑鍏炽�倀rue锛氬紑鍚繛缁璑G鎶ヨ銆�false锛氬叧闂繛缁璑G鎶ヨ鏈哄埗")]
+        [DisplayName("杩炵画NG鎶ヨ鎬诲紑鍏�")]
+        public bool IsEnableContinuousNGAlarm { get; set; } = true;
 
+        [Category("杩炵画NG鎶ヨ璁剧疆")]
+        [Description("杩炵画NG鎶ヨ鍦板潃銆傚嚭鐜拌繛缁璑G鏃讹紝涓婁綅鏈哄悜璇ュ湴鍧�鍐欏叆1锛屽彇娑堟椂锛屽悜璇ュ湴鍧�鍐欏叆0銆�")]
+        [DisplayName("杩炵画NG鎶ヨ鍦板潃")]
+        public int ContinuousNGAlarmAddress { get; set; } = 0;
+
+        //[Category("杩炵画NG鎶ヨ璁剧疆")]
+        //[Description("杩炵画NG鎶ヨ涓婁紶MES鐨勬柟娉曞湴鍧�銆�")]
+        //[DisplayName("杩炵画NG鎶ヨ涓婁紶MES鐨勬柟娉曞湴鍧�")]
+        //public string ContinuousNGAlarmMESFunc { get; set; } = "";
+
+        [Category("杩炵画NG鎶ヨ璁剧疆")]
+        [Description("杩炵画NG鎶ヨ鏄惁闇�瑕佷汉宸ュ浣�")]
+        [DisplayName("杩炵画NG鎶ヨ鏄惁闇�瑕佷汉宸ュ浣�")]
+        public bool IsOperatorReset { get; set; } = false;
+
+
+        [Category("杩炵画NG鎶ヨ璁剧疆")]
+        [Description("杩炵画NG鎶ヨ璁剧疆闆嗗悎銆傝缃繛缁璑G鎶ヨ鐨勫紑鍏筹紝绫诲瀷锛屾暟閲忓拰鏃堕棿闃堝�笺�傛坊鍔犳垨鍒犻櫎鐩戞帶闇�瑕佽蒋浠堕噸鍚敓鏁堛��")]
+        [DisplayName("杩炵画NG鎶ヨ璁剧疆闆嗗悎")]
+        [TypeConverter(typeof(CollectionCountConvert))]
+        [Editor(typeof(ComplexCollectionEditor<ContinuousNGAlarm>), typeof(UITypeEditor))]
+        public List<ContinuousNGAlarm> ContinuousNGAlarmColletion { get; set; } = new List<ContinuousNGAlarm>();
 
         [Category("浣嶇疆搴﹁缃�")]
         [Description("浜у搧娴嬮噺鐐逛綅闆嗗悎")]
diff --git a/src/Bro.M141.Process/M141Process.cs b/src/Bro.M141.Process/M141Process.cs
index 3fbe71a..e0696b7 100644
--- a/src/Bro.M141.Process/M141Process.cs
+++ b/src/Bro.M141.Process/M141Process.cs
@@ -5,10 +5,12 @@
 using Bro.Device.InsCamera;
 using Bro.M135.Common;
 using Bro.M135.DBManager;
+using Bro.M141.Process.UI;
 using Bro.Process;
 using Bro.Process.DataBase.Models;
 using Bro.UI.Model.Winform;
 using HalconDotNet;
+using MySql.Data.MySqlClient;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
 using NPOI.SS.Formula.Functions;
@@ -56,6 +58,8 @@
         public M141Process_Mysql mysqlhelper = new M141Process_Mysql();
 
         public event Action RerefreshBasketcodeUI;
+
+        public Action<bool, string> OnContinuousNGAlarmRaised;
 
         public void RerefreshBasketcode()
         {
@@ -133,10 +137,10 @@
                 mqtt.Connect(M141Config.MESchannel);
             }
 
-
+            InitialContinuousNGAlarm();         
         }
 
-
+        
         public override void Close()
         {
             devicestate = false;
@@ -571,11 +575,8 @@
                                 double y1 = item1.Rect.Point_LU.Y + item1.Rect.Height / 2.0;
                                 num++;
 
-                                //HOperatorSet.AffineTransPoint2d(new HTuple(products[0].Centermatrix.ToArray()), x1, y1, out HTuple qx, out HTuple qy);
-                                //HOperatorSet.ProjectiveTransPixel(new HTuple(products[0].Centermatrix.ToArray()), x1, y1, out HTuple qx, out HTuple qy);
-
                                 LogAsync(DateTime.Now, EnumHelper.LogLevel.Detail, $"{products[0].SN}  S3S5妫�娴�  鍘熷潗鏍噞num}   {x1},{y1}");
-                                HOperatorSet.ProjectiveTransPixel(new HTuple(products[0].Centermatrix.ToArray()), x1, y1, out HTuple qx, out HTuple qy);
+                                HOperatorSet.ProjectiveTransPixel(new HTuple(products[0].Centermatrix.ToArray()), y1, x1, out HTuple qx, out HTuple qy);
                                 LogAsync(DateTime.Now, EnumHelper.LogLevel.Detail, $"{products[0].SN}  S3S5妫�娴�  鏂板潗鏍噞num}   {qx},{qy}");
 
 
@@ -589,6 +590,7 @@
                                 {
                                     item1.IsAbandoned = false;
                                     item1.FinalResult = ResultState.NG;
+
                                     //products[0].Result = M141Config.defectname;
                                     LogAsync(DateTime.Now, EnumHelper.LogLevel.Action, $"浜у搧{products[0].PID}_{products[0].SEQUENCE}宸ヤ綅{measureBind.WorkPosition}   S3S5缁勫悎妫�娴嬫鍑虹己闄�:{item1.NetName}锛屼骇鍝佺粨鏋滀负{products[0].Result}");
                                     break;
@@ -741,7 +743,7 @@
                                         post = M141Config.ImageFormatOK.ToString().ToLower();
                                     }
 
-                                    string ngImageFile = Path.Combine(folder, $"{id}.{post}");                                    
+                                    string ngImageFile = Path.Combine(folder, $"{id}.{post}");
                                     //var bitmap = imgSet.HImage.ConvertHImageToBitmap();
                                     //bitmap.Save(ngImageFile, M141Config.ImageFormatOK);
                                     //LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"{id}OK鍥剧墖宸蹭繚瀛�");
@@ -749,7 +751,7 @@
                                     try
                                     {
                                         LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"{id}OK鍥剧墖娴嬭瘯杞瓨{ngImageFile}");
-                                        imgSet.HImage.WriteImage(M141Config.ImageFormatOK.ToString().ToLower(), 0, ngImageFile);                                      
+                                        imgSet.HImage.WriteImage(M141Config.ImageFormatOK.ToString().ToLower(), 0, ngImageFile);
                                     }
                                     catch (Exception)
                                     {
@@ -1239,13 +1241,66 @@
 
 
                         mysqlhelper.UpdateProduct(p);
+
+                      
+
                         if (positionSet.IsLastPosition)
                         {
-                            UpdateProductResultAsync(p);
+                           
+
+                            //鐝缁熻鏃堕棿鍒掑垎
+                            if (M141Config.WorkShiftList.Count == 0)
+                            {
+                                //鐢熸垚涓�涓姤琛�
+                                string name = $"ProductRecord_{DateTime.Now.ToString("yyyyMMdd")}.csv";
+                                LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"鍒涘缓{name}鏁版嵁鎶ヨ〃");
+                                UpdateProductResultAsync(p, name);    
+                            }
+                            else
+                            {
+                                foreach (var item in M141Config.WorkShiftList)
+                                {
+                                    DateTime now = DateTime.Now;
+
+                                    if (item.ShiftTime_Start < item.ShiftTime_End)
+                                    {
+                                        if (now.TimeOfDay >= item.ShiftTime_Start.TimeOfDay && now.TimeOfDay < item.ShiftTime_End.TimeOfDay)
+                                        {
+                                            //鐢熸垚涓�涓姤琛�
+                                            string name = $"ProductRecord_{DateTime.Now.ToString("yyyyMMdd")}_{item.ShiftName}.csv";
+                                            LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"鍒涘缓{name}鏁版嵁鎶ヨ〃");
+                                            UpdateProductResultAsync(p, name);
+                                        }
+                                    }
+                                    else
+                                    {
+                                        if (now.TimeOfDay >= item.ShiftTime_Start.TimeOfDay )
+                                        {
+                                            //鐢熸垚涓�涓姤琛�
+                                            string name = $"ProductRecord_{DateTime.Now.ToString("yyyyMMdd")}_{item.ShiftName}.csv";
+                                            LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"鍒涘缓{name}鏁版嵁鎶ヨ〃");
+                                            UpdateProductResultAsync(p, name);
+                                        }
+                                        if (now.TimeOfDay < item.ShiftTime_End.TimeOfDay)
+                                        {
+                                            // 鐢熸垚涓�涓姤琛�
+                                            string name = $"ProductRecord_{DateTime.Now.AddDays(-1).ToString("yyyyMMdd")}_{item.ShiftName}.csv";
+                                            LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"鍒涘缓{name}鏁版嵁鎶ヨ〃");
+                                            UpdateProductResultAsync(p, name);
+                                        }
+                                    }
+                                }
+                            }
+
+                            //UpdateProductResultAsync(p);
+
                             mysqlhelper.NewForAll(p, M141Config.StationCode, M141Config.defectname);
+
                             if (M141Config.IsfinDevice)
                             {
+
                                 SummaryAllprodata(p);
+
                             }
                         }
                     });
@@ -1281,7 +1336,7 @@
 
                             if (pList[0].Result == "OK")
                             {
-                                M141Config.numpro++;                                
+                                M141Config.numpro++;
                                 Msgreceice = Task.Run(() => mqtt.MESForProduceAsync(pList[0], M141Config.mesnum2.ToString(), M141Config.numpro)).Result;
                             }
                             else
@@ -1290,16 +1345,24 @@
                                 {
                                     Msgreceice = Task.Run(() => mqtt.MESForProduceAsync(pList[0], M141Config.mesnum2.ToString(), M141Config.numpro)).Result;
                                     LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"浜у搧{pList[0].PID}鍚姩NG涓婁紶");
+
                                 }
                                 else
                                 {
                                     LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"浜у搧{pList[0].PID}鍏抽棴NG涓婁紶");
-                                }                              
-                            }                            
+                                }
+                            }
                             M141Config.mesnum2++;
-                            if (Msgreceice == null)
+                            if (Msgreceice == null && !M141Config.ISupNG)
                             {
-                                LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"浜у搧{pList[0].PID}鏁版嵁涓婁紶MES寮傚父 杩斿洖鏁版嵁涓簄ull");
+                                if (!M141Config.ISupNG)
+                                {
+                                    LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"浜у搧{pList[0].PID}鏁版嵁NG锛屽紑鍚叧闂璑G涓婁紶MES");
+                                }
+                                else
+                                {
+                                    LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"浜у搧{pList[0].PID}鏁版嵁涓婁紶MES寮傚父 杩斿洖鏁版嵁涓簄ull");
+                                }
                             }
                             else
                             {
@@ -1363,11 +1426,11 @@
                                             {
                                                 LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"浜у搧{newp.PID}鍏抽棴NG涓婁紶");
                                             }
-                                            
+
                                         }
                                         catch
                                         {
-
+                                            LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"浜у搧{i + "_1"}涓婁紶澶辫触");
                                         }
                                     }
                                 }
@@ -1375,7 +1438,7 @@
                                 {
                                     LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"鐢熶骇杩囩▼涓湭澶卞幓浜у搧");
                                 }
-                                else if ( differ<0 && differ>-29998)
+                                else if (differ < 0 && differ > -29998)
                                 {
                                     LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"PlcNumForAll涓簕PlcNumForAll}锛宯umplca涓簕numplca}");
                                     //浜у搧浠庢柊璁℃暟鏃�
@@ -1404,7 +1467,7 @@
                                             //var tems = Task.Run(() => mqtt.MESForProduceAsync(newp, M141Config.mesnum2.ToString(), M141Config.numpro)).Result;
                                             //M141Config.mesnum2++;
                                             //LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"PlcNumForAll锛屽墠绔橬G鎺掓枡涓攑lc瑙﹀彂娓呴浂浜у搧{newp.PID}鏁版嵁涓婁紶,缁撴灉涓簕newp.Result}");
-                                        
+
                                         }
                                         catch
                                         {
@@ -1452,8 +1515,6 @@
 
                     //ReplyPlcData(positionName, config.TriggerValue);
 
-                   
-
                     return pList;
 
                 }
@@ -1480,6 +1541,7 @@
 
         public void SummaryAllprodata(ProductModel p)
         {
+            
             _taskFactory.StartNew(() =>
             {
                 try
@@ -1503,6 +1565,7 @@
                             newp.Details.AddRange(item.Details);
                         }
                     }
+
                     //LogAsync(DateTime.Now, EnumHelper.LogLevel.Error, $"AllDeviceProductRecord浠庢暟鎹簱鑾峰彇鍒版暟鎹畕p.SEQUENCE} plist鏁伴噺{plist.Count} Details鏁伴噺{newp.Details.Count}");
 
                     //newp.Details.AddRange(p.Details);
@@ -1525,10 +1588,284 @@
 
 
         #endregion
+
+        List<DefectNGRecord> DefectNGRecordList = new List<DefectNGRecord>();
+
+        public void InitialContinuousNGAlarm()
+        {
+            DefectNGRecordList = M141Config.ContinuousNGAlarmColletion.Where(u => u.IsEnabled).Select(u =>
+            {
+                DefectNGRecord record = new DefectNGRecord();
+                record.DefectName = u.DefectType;
+                record.AlarmSetting = u;
+                return record;
+            }).ToList();
+            OnContinuousNGAlarmRaised = NGAlarmRaised;
+            if (M141Config.ContinuousNGAlarmAddress > 0 && Plc1 != null)
+            {
+                if (!Plc1.WriteSingleAddress(M141Config.ContinuousNGAlarmAddress, 0, out string error))
+                {
+                    LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"杩炵画NG鐩戞帶閫氱煡PLC閲嶇疆鎶ヨ澶辫触锛寋error}");
+                }
+            }
+        }
+        public async void CheckContinuousNGAlarmAsync(ProductModel product)
+        {
+            await Task.Run(() =>
+            {
+                if (!M141Config.IsEnableContinuousNGAlarm)
+                {
+                    LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"杩炵画NG鎶ヨ鎬诲紑鍏冲凡鍏抽棴");
+                    return;
+                }
+
+                if (DefectNGRecordList.Count == 0)
+                    return;
+
+                string allMsg = "";
+                bool isAlarmRaised = false;
+                int alarmType = 0;
+                string ngItem = "";
+                try
+                {
+                    LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"杩炵画NG鏁版嵁璁板綍");
+                    DefectNGRecordList.ForEach(d =>
+                    {
+                            string alarmMsg = "";
+                            int alarmTypeTemp = 0;
+                            
+                            if (product.Result == "OK")
+                            {
+                                if (d.CheckIsAlarmRaised(product.Result == "OK", out alarmMsg, out alarmTypeTemp))
+                                {
+                                    allMsg += $"{alarmMsg}\r\n";
+
+                                    isAlarmRaised = true;
+                                    ngItem = "浜у搧缁撴灉";
+                                    alarmType = alarmTypeTemp;
+                                    LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"{product.PID}鏁版嵁缁撴灉涓簕product.Result}鍙備笌杩炵画NG缁熻");
+                                }
+                            }
+                            else
+                            {
+                                if (product.Result.Contains(d.DefectName))
+                                {
+                                    if (d.CheckIsAlarmRaised(!product.Result.Contains(d.DefectName), out alarmMsg, out alarmTypeTemp))
+                                    {
+                                        allMsg += $"{alarmMsg}\r\n";
+                                        isAlarmRaised = true;
+                                        ngItem = "浜у搧缁撴灉";
+                                        alarmType = alarmTypeTemp;
+                                        LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"{product.PID}鏁版嵁缁撴灉涓簕product.Result}鍙備笌杩炵画NG缁熻");
+                                    }
+                                }                               
+                            }
+                    });
+                }
+                catch (Exception ex)
+                {
+                    LogAsync(DateTime.Now, EnumHelper.LogLevel.Warning, $"杩炵画NG鏁版嵁璁板綍澶辫触");
+                }
+                if (isAlarmRaised)
+                {                     
+                    LogAsync(DateTime.Now, EnumHelper.LogLevel.Warning, $"杩炵画NG鐩戞帶鎶ヨ锛寋allMsg}");
+                    if (M141Config.IsOperatorReset)
+                    {
+                        if (M141Config.ContinuousNGAlarmAddress > 0 && Plc1 != null)
+                        {
+                            if (!Plc1.WriteSingleAddress(M141Config.ContinuousNGAlarmAddress, 1, out string error))
+                            {
+                                LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"杩炵画NG鐩戞帶閫氱煡PLC閲嶇疆鎶ヨ澶辫触锛寋error}");
+                            }
+                        }
+                        OnContinuousNGAlarmRaised?.Invoke(true, allMsg);
+                    }
+                    else
+                    {
+                        Task.Delay(50).Wait();
+                        ResetContinuousNGAlarm();
+                    }                 
+                }
+            });
+        }
+
+        object _continuousNGAlarmLock = new object();
+
+        FrmContinuousNGAlarm _continuousNGAlarmFrm = null;
+        private async void NGAlarmRaised(bool isRaiseAlarm, string alarmMsg)
+        {
+            await Task.Run(() =>
+            {
+                if (isRaiseAlarm)
+                {
+                    if (_continuousNGAlarmFrm == null)
+                    {
+                        lock (_continuousNGAlarmLock)
+                        {
+                            if (_continuousNGAlarmFrm == null)
+                            {
+                                _continuousNGAlarmFrm = new FrmContinuousNGAlarm();
+
+                                _continuousNGAlarmFrm.TopMost = true;
+                                
+                                _continuousNGAlarmFrm.FormClosed += _continuousNGAlarmFrm_FormClosed;
+                               
+                                Task.Run(() =>
+                                {
+                                    _continuousNGAlarmFrm.ShowDialog();
+                                    
+                                });                               
+                            }
+                        }
+                    }
+                    Task.Delay(100).Wait();
+                    _continuousNGAlarmFrm.ShowAlarmMsg(alarmMsg);
+                }
+                else
+                {
+                    if (_continuousNGAlarmFrm != null)
+                    {
+                        _continuousNGAlarmFrm.Close();
+                    }
+                
+                }
+            });
+
+        }
+
+        private void _continuousNGAlarmFrm_FormClosed(object? sender, FormClosedEventArgs e)
+        {
+            ResetContinuousNGAlarm();
+            LogAsync(DateTime.Now, EnumHelper.LogLevel.Action, $"杩炵画NG鎶ヨ宸插浣�");           
+            _continuousNGAlarmFrm = null;
+        }
+
+        public void ResetContinuousNGAlarm()
+        {
+            //杩炵画NG澶嶄綅
+            DefectNGRecordList.Where(u => u.IsAlarmRaised).ToList().ForEach(u => u.ResetAlarm());
+            if (M141Config.ContinuousNGAlarmAddress > 0 && Plc1 != null)
+            {
+                if (!Plc1.WriteSingleAddress(M141Config.ContinuousNGAlarmAddress, 0, out string error))
+                {
+                    LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"杩炵画NG鐩戞帶閫氱煡PLC閲嶇疆鎶ヨ澶辫触锛寋error}");
+                }
+            }
+           
+        }
+
+        [ProcessMethod("", "ContinuousNGAlarmTest", "杩炵画NG鎶ヨ娴嬭瘯", InvokeType.TestInvoke)]
+        public ResponseMessage ContinuousNGAlarmTest(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice)
+        {
+            ProductModel p = new ProductModel();
+
+            p.Result = config.TriggerStr;
+
+            CheckContinuousNGAlarmAsync(p);
+
+            return new ResponseMessage();
+        }
     }
 
+    public class DefectNGRecord
+    {
+        public string DefectName { get; set; }
+        public List<DateTime> NGRecords { get; set; } = new List<DateTime>();
+        public int ContinuousNGNum { get; set; } = 0;
+        public ContinuousNGAlarm AlarmSetting { get; set; }
 
+        private object _lockObj = new object();
 
+        public bool IsAlarmRaised = false;
 
+        bool _isContinuousAlarm = false;
 
-}
+        bool _timeAlarm = false;
+
+        public bool CheckIsAlarmRaised(bool isOK, out string alarmMsg, out int alarmType)
+        {
+            alarmType = 0;
+            alarmMsg = "";
+            bool isAlarmRasied = false;
+
+            if (IsAlarmRaised)
+                return false;
+
+            lock (_lockObj)
+            {
+                if (IsAlarmRaised)
+                    return false;
+
+                if (isOK)
+                {
+                    ContinuousNGNum = 0;
+                }
+                else
+                {
+                    //杩炵画NG鏁伴噺闃堝��
+                    if (AlarmSetting.ContinuousNumThreshold > 0)
+                    {
+                        ContinuousNGNum++;
+                        CommonLogger.LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"{DefectName}杩炵画NG鏁伴噺涓猴細{ContinuousNGNum}涓�");
+                    }
+                    //鏃堕棿鍐匩G鏁伴噺闃堝��
+                    if (AlarmSetting.TimePeriodNumThresold > 0)
+                    {
+                        NGRecords.Add(DateTime.Now);
+                    }
+                }
+                
+                if (NGRecords.Count >= AlarmSetting.TimePeriodNumThresold && NGRecords.Count > 0)
+                {
+                    
+                    NGRecords = NGRecords.Skip(NGRecords.Count - AlarmSetting.TimePeriodNumThresold).OrderBy(u => u).ToList();
+
+                    int timeInMinute = (int)Math.Ceiling((NGRecords[NGRecords.Count - 1] - NGRecords[0]).TotalMinutes);
+                    //鐩戞帶鏃堕棿娈�
+                    if (timeInMinute <= AlarmSetting.TimePeriod)
+                    {
+                        isAlarmRasied = true;
+                        alarmMsg += $"{DefectName}{timeInMinute}鍒嗛挓鍐匩G{NGRecords.Count}涓� ";
+                        alarmType = AlarmSetting.TimePeriodAlarmType;
+                        _timeAlarm = true;
+                    }
+                }
+
+                if (ContinuousNGNum >= AlarmSetting.ContinuousNumThreshold)
+                {
+                    isAlarmRasied = true;
+                    alarmMsg += $"{DefectName}杩炵画NG{ContinuousNGNum}涓� ";
+                    alarmType = AlarmSetting.ContinuousAlarmType;
+                    _isContinuousAlarm = true;
+                }
+                IsAlarmRaised = isAlarmRasied;
+                return isAlarmRasied;
+            }
+        }
+
+        public void ResetAlarm()
+        {
+            string msg = "";
+            lock (_lockObj)
+            {
+                IsAlarmRaised = false;                            
+                if (_isContinuousAlarm)
+                {                
+                    _isContinuousAlarm = false;
+                    ContinuousNGNum = 0;
+                    msg += "杩炵画NG鎶ヨ ";
+                }
+
+                if (_timeAlarm)
+                {
+                    
+                    _timeAlarm = false;
+                    NGRecords.Clear();
+                    msg += "鏃舵鍐匩G鎶ヨ ";
+                }
+            }
+            CommonLogger.LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"{DefectName}{msg}宸查噸缃�");
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/src/Bro.M141.Process/M141Process_ImageCheck.cs b/src/Bro.M141.Process/M141Process_ImageCheck.cs
index 91dc61d..854498c 100644
--- a/src/Bro.M141.Process/M141Process_ImageCheck.cs
+++ b/src/Bro.M141.Process/M141Process_ImageCheck.cs
@@ -867,17 +867,20 @@
 
 
 
-        private void UpdateProductResultAsync(ProductModel p)
+        private void UpdateProductResultAsync(ProductModel p, string name)
         {
             UpdateProductResult(p, out bool isOK);
 
             _taskFactory.StartNew(() =>
             {
                 _csvHead = p.GetCSVHead(ref _specHeadList, ref _positionList);
-                CSVRecordAsync($"ProductRecord_{DateTime.Now.ToString("yyyyMMdd")}.csv", p.GetCSVData(_specHeadList, _positionList), _csvHead);
+                //CSVRecordAsync($"ProductRecord_{DateTime.Now.ToString("yyyyMMdd")}.csv", p.GetCSVData(_specHeadList, _positionList), _csvHead);
+                CSVRecordAsync(name, p.GetCSVData(_specHeadList, _positionList), _csvHead);
                 //_manager_P_Product.UpdateProductResult(p.ID, p.PID, p.SN, p.Result);
             });
 
+            //杩炵画NG鏁版嵁璁板綍
+            CheckContinuousNGAlarmAsync(p);
         }
 
 
@@ -985,10 +988,5 @@
 
             public object data { get; set; }
         }
-
-
-
-
-
     }
 }
diff --git a/src/Bro.M141.Process/M141Process_Mysql.cs b/src/Bro.M141.Process/M141Process_Mysql.cs
index ae1408c..c2b3a8e 100644
--- a/src/Bro.M141.Process/M141Process_Mysql.cs
+++ b/src/Bro.M141.Process/M141Process_Mysql.cs
@@ -241,7 +241,7 @@
                                         //HOperatorSet.AffineTransPoint2d(new HTuple(pro.Centermatrix.ToArray()), x1, y1, out HTuple qx, out HTuple qy);
 
                                         CommonLogger.LogAsync(DateTime.Now, EnumHelper.LogLevel.Detail, $"sql {pro.SN}鍘熷潗鏍噞num}   {x1},{y1}");
-                                        HOperatorSet.ProjectiveTransPixel(new HTuple(pro.Centermatrix.ToArray()), x1, y1, out HTuple qx, out HTuple qy);
+                                        HOperatorSet.ProjectiveTransPixel(new HTuple(pro.Centermatrix.ToArray()), y1, x1, out HTuple qx, out HTuple qy);
                                         CommonLogger.LogAsync(DateTime.Now, EnumHelper.LogLevel.Detail, $"sql {pro.SN}鏂板潗鏍噞num}   {qx},{qy}");
 
                                         Netdefectdetail temc = new Netdefectdetail()
diff --git a/src/Bro.M141.Process/MyMQTT.cs b/src/Bro.M141.Process/MyMQTT.cs
index 7623fcb..eba6af5 100644
--- a/src/Bro.M141.Process/MyMQTT.cs
+++ b/src/Bro.M141.Process/MyMQTT.cs
@@ -211,7 +211,7 @@
 
             var DefectCodeMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
             {
-                ["ok"] = "",
+                ["OK"] = "",
                 ["寮傝壊"] = "SZ2001",
                 ["纾ㄥ嵃"] = "SZ2012",
                 ["鍘嬩激"] = "SZ2014",
diff --git a/src/Bro.M141.Process/UI/FrmContinuousNGAlarm.Designer.cs b/src/Bro.M141.Process/UI/FrmContinuousNGAlarm.Designer.cs
new file mode 100644
index 0000000..92b981e
--- /dev/null
+++ b/src/Bro.M141.Process/UI/FrmContinuousNGAlarm.Designer.cs
@@ -0,0 +1,95 @@
+锘縩amespace Bro.M141.Process.UI
+{
+    partial class FrmContinuousNGAlarm
+    {
+        /// <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()
+        {
+            label1 = new Label();
+            txtAlarmMsg = new TextBox();
+            button1 = new Button();
+            SuspendLayout();
+            // 
+            // label1
+            // 
+            label1.AutoSize = true;
+            label1.Font = new Font("Microsoft YaHei UI", 12F, FontStyle.Regular, GraphicsUnit.Point);
+            label1.ForeColor = Color.Red;
+            label1.Location = new Point(31, 9);
+            label1.Margin = new Padding(4, 0, 4, 0);
+            label1.Name = "label1";
+            label1.Size = new Size(148, 31);
+            label1.TabIndex = 0;
+            label1.Text = "杩炵画NG鎶ヨ";
+            // 
+            // txtAlarmMsg
+            // 
+            txtAlarmMsg.BackColor = Color.Red;
+            txtAlarmMsg.Font = new Font("Microsoft YaHei UI", 18F, FontStyle.Regular, GraphicsUnit.Point);
+            txtAlarmMsg.Location = new Point(31, 63);
+            txtAlarmMsg.Margin = new Padding(4);
+            txtAlarmMsg.Name = "txtAlarmMsg";
+            txtAlarmMsg.Size = new Size(468, 53);
+            txtAlarmMsg.TabIndex = 1;
+            txtAlarmMsg.TextAlign = HorizontalAlignment.Center;
+            // 
+            // button1
+            // 
+            button1.Location = new Point(440, 172);
+            button1.Name = "button1";
+            button1.Size = new Size(118, 40);
+            button1.TabIndex = 2;
+            button1.Text = "澶嶄綅";
+            button1.UseVisualStyleBackColor = true;
+            button1.Click += button1_Click;
+            // 
+            // FrmContinuousNGAlarm
+            // 
+            AutoScaleDimensions = new SizeF(14F, 31F);
+            AutoScaleMode = AutoScaleMode.Font;
+            ClientSize = new Size(571, 233);
+            Controls.Add(button1);
+            Controls.Add(txtAlarmMsg);
+            Controls.Add(label1);
+            Font = new Font("Microsoft YaHei UI", 12F, FontStyle.Regular, GraphicsUnit.Point);
+            Margin = new Padding(4);
+            Name = "FrmContinuousNGAlarm";
+            ShowInTaskbar = false;
+            StartPosition = FormStartPosition.CenterScreen;
+            Text = "杩炵画NG鎶ヨ鎻愮ず";
+            TopMost = true;
+            Load += FrmContinuousNGAlarm_Load;
+            ResumeLayout(false);
+            PerformLayout();
+        }
+
+        #endregion
+
+        private Label label1;
+        private TextBox txtAlarmMsg;
+        private Button button1;
+    }
+}
\ No newline at end of file
diff --git a/src/Bro.M141.Process/UI/FrmContinuousNGAlarm.cs b/src/Bro.M141.Process/UI/FrmContinuousNGAlarm.cs
new file mode 100644
index 0000000..63e9e79
--- /dev/null
+++ b/src/Bro.M141.Process/UI/FrmContinuousNGAlarm.cs
@@ -0,0 +1,45 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Diagnostics.Eventing.Reader;
+using System.Drawing;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+
+namespace Bro.M141.Process.UI
+{
+    public partial class FrmContinuousNGAlarm : Form
+    {
+        public FrmContinuousNGAlarm()
+        {
+            InitializeComponent();                     
+        }
+
+        public void ShowAlarmMsg(string alarmMsg)
+        {           
+            if (txtAlarmMsg.InvokeRequired)
+            {
+                txtAlarmMsg.Invoke(new Action<string>(ShowAlarmMsg), alarmMsg);
+            }
+            else
+            {
+                txtAlarmMsg.AppendText(alarmMsg);
+            }
+        }
+
+        private void FrmContinuousNGAlarm_Load(object sender, EventArgs e)
+        {            
+           this.TopMost = true;
+        }
+
+        private void button1_Click(object sender, EventArgs e)
+        {
+            this.Close();
+        }
+    }
+}
diff --git a/src/Bro.M141.Process/UI/FrmContinuousNGAlarm.resx b/src/Bro.M141.Process/UI/FrmContinuousNGAlarm.resx
new file mode 100644
index 0000000..8b2ff64
--- /dev/null
+++ b/src/Bro.M141.Process/UI/FrmContinuousNGAlarm.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

--
Gitblit v1.8.0