连续NG报警功能,单机台生产数据划分白夜班功能以及S3S5相同位置缺陷放射位置优化
3个文件已添加
6个文件已修改
691 ■■■■■ 已修改文件
src/Bro.M141.Process/Bro.M141.Process.csproj 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M141.Process/M141Config.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M141.Process/M141Process.cs 379 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M141.Process/M141Process_ImageCheck.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M141.Process/M141Process_Mysql.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M141.Process/MyMQTT.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M141.Process/UI/FrmContinuousNGAlarm.Designer.cs 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M141.Process/UI/FrmContinuousNGAlarm.cs 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/Bro.M141.Process/UI/FrmContinuousNGAlarm.resx 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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>-->
src/Bro.M141.Process/M141Config.cs
@@ -104,7 +104,33 @@
            }
        }
        [Category("连续NG报警设置")]
        [Description("连续NG报警总开关。true:开启连续NG报警 false:关闭连续NG报警机制")]
        [DisplayName("连续NG报警总开关")]
        public bool IsEnableContinuousNGAlarm { get; set; } = true;
        [Category("连续NG报警设置")]
        [Description("连续NG报警地址。出现连续NG时,上位机向该地址写入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报警设置集合。设置连续NG报警的开关,类型,数量和时间阈值。添加或删除监控需要软件重启生效。")]
        [DisplayName("连续NG报警设置集合")]
        [TypeConverter(typeof(CollectionCountConvert))]
        [Editor(typeof(ComplexCollectionEditor<ContinuousNGAlarm>), typeof(UITypeEditor))]
        public List<ContinuousNGAlarm> ContinuousNGAlarmColletion { get; set; } = new List<ContinuousNGAlarm>();
        [Category("位置度设置")]
        [Description("产品测量点位集合")]
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异常 返回数据为null");
                                if (!M141Config.ISupNG)
                                {
                                    LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"产品{pList[0].PID}数据NG,开启关闭NG上传MES");
                                }
                                else
                                {
                                    LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"产品{pList[0].PID}数据上传MES异常 返回数据为null");
                                }
                            }
                            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},numplca为{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,前站NG排料且plc触发清零产品{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}个");
                    }
                    //时间内NG数量阈值
                    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}分钟内NG{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 += "时段内NG报警 ";
                }
            }
            CommonLogger.LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"{DefectName}{msg}已重置");
        }
    }
}
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; }
        }
    }
}
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()
src/Bro.M141.Process/MyMQTT.cs
@@ -211,7 +211,7 @@
            var DefectCodeMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
            {
                ["ok"] = "",
                ["OK"] = "",
                ["异色"] = "SZ2001",
                ["磨印"] = "SZ2012",
                ["压伤"] = "SZ2014",
src/Bro.M141.Process/UI/FrmContinuousNGAlarm.Designer.cs
New file
@@ -0,0 +1,95 @@
namespace 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;
    }
}
src/Bro.M141.Process/UI/FrmContinuousNGAlarm.cs
New file
@@ -0,0 +1,45 @@
using 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();
        }
    }
}
src/Bro.M141.Process/UI/FrmContinuousNGAlarm.resx
New file
@@ -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>