using Bro.Common.Base; using Bro.Common.Helper; using Bro.Common.Interface; using Bro.Common.Model; using Bro.Device.InsCamera; using Bro.M141.Process; using Bro.UI.Model.Winform; using HalconDotNet; using SixLabors.ImageSharp; using System.Text.RegularExpressions; using static Bro.Common.Helper.EnumHelper; namespace Bro.M141_AOI2.Process { [Process("AOI2", EnumHelper.DeviceAttributeType.Device)] public class AOI2Process : M141Process { #region constructor public AOI2Process() : base() { } public AOI2Process(string productCode) : base(productCode) { } #endregion AOI2Config AOI2Config => Config as AOI2Config; InsCameraDriver _insCameraDriver = null; InsCameraInitialConfig _insCameraConfig = null; public override void Open() { base.Open(); _insCameraDriver = DeviceCollection.FirstOrDefault(u => u is InsCameraDriver) as InsCameraDriver; if (_insCameraDriver == null) { LogAsync(DateTime.Now, LogLevel.Warning, $"未配置苏映视微距相机设备"); } else { _insCameraDriver.OnImageSetOutput += InsImageSetProcess; } //_insCameraConfig = Config.CameraConfigCollection.FirstOrDefault(x => x is InsCameraInitialConfig) as InsCameraInitialConfig; //if (_insCameraConfig == null) //{ // LogAsync(DateTime.Now, LogLevel.Warning, $"未配置苏映视微距相机设备"); //} } public override void Close() { if (_insCameraDriver != null) { _insCameraDriver.OnImageSetOutput -= InsImageSetProcess; } base.Close(); } List insimage = new List(); /// /// 工位4,接收imageSet中的原图和融合图,并进行相应检测 /// /// /// public void InsImageSetProcess(CameraBase camera, IImageSet imageSet) { DateTime dt = DateTime.Now; insimage.Add(imageSet); //var images = imageSet as MultiImageSet; //for (int i = 0; i < images.FusionImages.Count; i++) //{ //var im = images.FusionImages[i]; //if (im!=null) //{ // HOperatorSet.WriteImage(im, "png", 0, "D:\\PROJECTS\\M141\\Images\\20241010\\P4\\Origin\\33.png"); //} //} } #region position0 [ProcessMethod("", "SycnPointsData_P0", "同步工位0检测点位", InvokeType.TestInvoke)] public ResponseMessage SycnPointsData_P0(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = new ResponseMessage(); var allPoints = AOI2Config.CheckPoints_P1.Where(u => u.IsEnabled); string pointDataStr = string.Join(",", allPoints.Select(u => $"{u.PointPosition.X.ToString("f3")},{u.PointPosition.Y.ToString("f3")},{u.PointZ.ToString("f3")}")); pointDataStr = $"1#,Points,{allPoints.Count()},{pointDataStr}"; msg.DataStr = pointDataStr; msg.IsReply = true; var triggerDatas = config.TriggerStr.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (triggerDatas.Length < 2) { throw new ProcessException($"触发文本{config.TriggerStr}解析失败,数据长度小于2"); } string positionValue = triggerDatas[0]; int checkIndex = -1; if (!int.TryParse(triggerDatas[1], out checkIndex)) { throw new ProcessException($"触发文本{config.TriggerStr}解析失败,未能获取检测序号"); } var positionSet = M141Config.WorkPositionCollection.Where(u => u.IsEnabled).FirstOrDefault(u => u.TriggerValue == positionValue); if (positionSet == null) { throw new ProcessException($"触发文本{config.TriggerStr}未能获取{positionValue}对应的可用工位信息"); } var measureBinds = M141Config.MeasureBindCollection.Where(u => u.WorkPosition == positionSet.PositionName && u.CheckIndex == checkIndex).ToList(); if (measureBinds.Count == 0) { throw new ProcessException($"未能获取工位{positionSet.PositionName}的第{checkIndex}检测配置信息"); } //检测前清理图片缓存 measureBinds.Select(u => u.CameraId).Distinct().ToList().ForEach(u => { var camera = DeviceCollection.FirstOrDefault(c => c.Id == u) as CameraBase; camera.ReleaseImageWaitHandle(); camera.ClearImageBufferQueue(); LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"{camera.Name}相机工位1检测前清理缓存"); }); return msg; } [ProcessMethod("", "PositionCheck_P0", "工位0扫码", InvokeType.TestInvoke)] public ResponseMessage PositionCheck_P0(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { //ResponseMessage msg = new ResponseMessage(); //msg.IsReply = true; //msg.DataStr = "1#," + config.TriggerStr.Split(',')[1] + ",NG"; //return msg; ResponseMessage msg = RunImageCheck(config); msg.IsReply = false; //msg.DataStr = "1#,"+ config.TriggerStr.Split(',')[1] + ",OK"; if (msg.Result != 1) { msg.IsReply = true; if (config.TriggerStr.Split(',')[1].Contains("1")) { msg.DataStr = "0#,Scan1,ScanNG1"; } else { msg.DataStr = "0#,Scan2,ScanNG2"; } } return msg; } [ProcessMethod("ImageCheck", "GetProductBarcode_P0", "工位0获取产品条码", InvokeType.TestInvoke)] public ResponseMessage GetProductBarcode_P0(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = new ResponseMessage(); if (config is IImageCheckOperationConfig opConfig) { var results = opConfig.Products.Select(u => { DetectResult result = new DetectResult(); result.PID = u.PID; result.Specs = GetSpecListFromConfigSelection(opConfig.SpecCollection); return result; }).ToList(); msg.DataObj = results; var tool = GetHalconTool(null, "", opConfig.AlgorithemPath); var ret = tool.RunProcedure(null, new Dictionary() { { "INPUT_Image", opConfig.ImageSet.HImage } }, new List() { "OUTPUT_Flag", "OUTPUT_Results", "OUTPUT_Barcode" }); if (!ret.Item1 || ret.Item2["OUTPUT_Flag"].I != 1) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"工位0条码检测工具运行失败,{ret.Item4}"); msg.Result = -1; return msg; } FillSpecResults(results[0].PID, results[0].Specs, ret.Item2["OUTPUT_Results"].HTupleToDouble(), opConfig.Products[0].SEQUENCE); var barcodeSpec = results[0].Specs.FirstOrDefault(u => u.Code == AOI2Config.BarcodeSpecCode); string barcode = ret.Item2["OUTPUT_Barcode"].S; opConfig.Products[0].SN = barcode; opConfig.Products[0].Details.ForEach(u => u.SN = barcode); LogAsync(DateTime.Now, EnumHelper.LogLevel.Action, $"产品{opConfig.Products[0].PID}条码获取为{barcode}"); UpdateProductSNIntoDB(opConfig.Products[0].PID, barcode); if (!string.IsNullOrWhiteSpace(AOI2Config.BarcodeRegex)) { bool isOK = Regex.IsMatch(barcode, AOI2Config.BarcodeRegex); if (!isOK) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Warning, $"产品{opConfig.Products[0].PID}条码{barcode}未能通过验证,判定条码NG"); if (barcodeSpec == null) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Error, $"未能获取产品条码标准,请检查流程配置中是否选择条码标准或条码检测操作中是否添加条码标准"); results[0].Specs.ForEach(u => u.ActualValue = -999); } else { barcodeSpec.ActualValue = -999; } } } } else { LogAsync(DateTime.Now, EnumHelper.LogLevel.Error, $"产品条码获取失败1"); } return msg; } #endregion #region position1 [ProcessMethod("", "SycnPointsData_P1", "同步工位1检测点位", InvokeType.TestInvoke)] public ResponseMessage SycnPointsData_P1(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = new ResponseMessage(); var allPoints = AOI2Config.CheckPoints_P1.Where(u => u.IsEnabled); string pointDataStr = string.Join(",", allPoints.Select(u => $"{u.PointPosition.X.ToString("f3")},{u.PointPosition.Y.ToString("f3")},{u.PointZ.ToString("f3")}")); pointDataStr = $"1#,Points,{allPoints.Count()},{pointDataStr}"; msg.DataStr = pointDataStr; msg.IsReply = true; var triggerDatas = config.TriggerStr.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (triggerDatas.Length < 2) { throw new ProcessException($"触发文本{config.TriggerStr}解析失败,数据长度小于2"); } string positionValue = triggerDatas[0]; int checkIndex = -1; if (!int.TryParse(triggerDatas[1], out checkIndex)) { throw new ProcessException($"触发文本{config.TriggerStr}解析失败,未能获取检测序号"); } var positionSet = M141Config.WorkPositionCollection.Where(u => u.IsEnabled).FirstOrDefault(u => u.TriggerValue == positionValue); if (positionSet == null) { throw new ProcessException($"触发文本{config.TriggerStr}未能获取{positionValue}对应的可用工位信息"); } var measureBinds = M141Config.MeasureBindCollection.Where(u => u.WorkPosition == positionSet.PositionName && u.CheckIndex == checkIndex).ToList(); if (measureBinds.Count == 0) { throw new ProcessException($"未能获取工位{positionSet.PositionName}的第{checkIndex}检测配置信息"); } //检测前清理图片缓存 measureBinds.Select(u => u.CameraId).Distinct().ToList().ForEach(u => { var camera = DeviceCollection.FirstOrDefault(c => c.Id == u) as CameraBase; camera.ReleaseImageWaitHandle(); camera.ClearImageBufferQueue(); LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"{camera.Name}相机工位1检测前清理缓存"); }); return msg; } [ProcessMethod("", "PositionCheck_P1", "工位1检测", InvokeType.TestInvoke)] public ResponseMessage PositionCheck_P1(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { //ResponseMessage msg = new ResponseMessage(); //msg.IsReply = true; //msg.DataStr = "1#," + config.TriggerStr.Split(',')[1] + ",NG"; //return msg; ResponseMessage msg = RunImageCheck(config); msg.IsReply = false; //msg.DataStr = "1#,"+ config.TriggerStr.Split(',')[1] + ",OK"; if (msg.Result != 1) { msg.IsReply = true; msg.DataStr = "1#," + config.TriggerStr.Split(',')[1] + ",NG"; } return msg; } #endregion #region position2 bool _isLastCheckFlag = false; [ProcessMethod("", "SycnPointsData_P2", "同步工位2检测点位", InvokeType.TestInvoke)] public ResponseMessage SycnPointsData_P2(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = new ResponseMessage(); var allPoints = AOI2Config.CheckPoints_P2.Where(u => u.IsEnabled); string pointDataStr = string.Join(",", allPoints.Select(u => $"{u.PointPosition.X.ToString("f3")},{u.PointPosition.Y.ToString("f3")},{u.PointZ.ToString("f3")}")); pointDataStr = $"2#,Points,{allPoints.Count()},{pointDataStr}"; msg.DataStr = pointDataStr; msg.IsReply = true; var triggerDatas = config.TriggerStr.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (triggerDatas.Length < 2) { throw new ProcessException($"触发文本{config.TriggerStr}解析失败,数据长度小于2"); } string positionValue = triggerDatas[0]; int checkIndex = -1; if (!int.TryParse(triggerDatas[1], out checkIndex)) { throw new ProcessException($"触发文本{config.TriggerStr}解析失败,未能获取检测序号"); } var positionSet = M141Config.WorkPositionCollection.Where(u => u.IsEnabled).FirstOrDefault(u => u.TriggerValue == positionValue); if (positionSet == null) { throw new ProcessException($"触发文本{config.TriggerStr}未能获取{positionValue}对应的可用工位信息"); } var measureBinds = M141Config.MeasureBindCollection.Where(u => u.WorkPosition == positionSet.PositionName && u.CheckIndex == checkIndex).ToList(); if (measureBinds.Count == 0) { throw new ProcessException($"未能获取工位{positionSet.PositionName}的第{checkIndex}检测配置信息"); } //检测前清理图片缓存 measureBinds.Select(u => u.CameraId).Distinct().ToList().ForEach(u => { var camera = DeviceCollection.FirstOrDefault(c => c.Id == u) as CameraBase; camera.ReleaseImageWaitHandle(); camera.ClearImageBufferQueue(); LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"{camera.Name}相机工位2检测前清理缓存"); }); return msg; } [ProcessMethod("", "PositionCheck_P2", "工位2检测", InvokeType.TestInvoke)] public ResponseMessage PositionCheck_P2(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = RunImageCheck(config); msg.IsReply = true; msg.DataStr = "2#,P,OK"; if (msg.Result != 1) { msg.DataStr = "2#,P,NG"; } return msg; } [ProcessMethod("", "PositionCheck_P2_F", "工位2检测飞拍", InvokeType.TestInvoke)] public ResponseMessage PositionCheck_P2_F(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = new ResponseMessage(); msg.Result = 1; msg.IsReply = false; List measureBinds = new List(); string inputSequence = ""; try { RunImageCheckPreTreat(config, out measureBinds, out inputSequence); } catch (Exception ex) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"检测预处理异常,{ex.GetExceptionMessage()}"); msg.Result = -1; msg.Message = ex.Message; msg.IsReply = true; msg.DataStr = $"2#,NG.NG,No Read,No Read,NG,NG"; return msg; } int measureNums = measureBinds.Count; _isLastCheckFlag = false; measureBinds.GroupBy(u => u.CameraId).AsParallel().ForAll(c => { var camera = DeviceCollection.FirstOrDefault(u => u.Id == c.Key) as CameraBase; foreach (var i in c.ToList().OrderBy(u => u.ImageIndex).ToList()) { var products = i.ProductIndices.Select(pi => { return FindProductBySequence($"{inputSequence}_{pi}", true); }).ToList(); IImageSet imgSet = null; try { imgSet = CollectHImage(camera, i.SnapshotOpConfig); if (imgSet==null||imgSet.HImage==null) { imgSet = CollectHImage(camera, i.SnapshotOpConfig); } } catch (Exception ex) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"工位2{camera.Name}取像{i.ImageIndex}异常,{ex.GetExceptionMessage()}"); } if (_isLastCheckFlag) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Error, $"当前流程已过期,获取图片不执行检测"); //Interlocked.Decrement(ref measureNums); measureNums = 0; break; } else { RunImageCheckAsync(products, config.TriggerStr, config.TriggerSource, imgSet, i).ContinueWith(t => { Interlocked.Decrement(ref measureNums); }); } } }); while (measureNums > 0 && !_isLastCheckFlag) { Thread.Sleep(50); } if (_isLastCheckFlag) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Error, $"当前流程已过期,退出检测,不执行反馈"); msg.IsReply = false; return msg; } //检测完成后清理图片缓存 measureBinds.Select(u => u.CameraId).Distinct().ToList().ForEach(u => { var camera = DeviceCollection.FirstOrDefault(c => c.Id == u) as CameraBase; camera.ClearImageBufferQueue(); LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"{camera.Name}相机工位2检测后清理缓存"); }); string positionName = measureBinds[0].WorkPosition; var pList = CheckPositionDoneAsync(positionName, inputSequence, config, new List()).GetAwaiter().GetResult(); //string replyData = $"2#,C"; //pList.ForEach(p => //{ // var isOK = p.GetPositionResult(AOI2Config.StationCode, positionName, out _); // //replyData += $",{(isOK ? "OK" : "NG")}"; //}); //replyData += $",{string.Join(",", pList.Select(u => u.SN))},{string.Join(",", pList.Select(u => u.IsPreStationOK ? "OK" : "NG"))}"; //ReplyTcpData(positionName, config.TriggerSource, replyData); //msg.IsReply = false; return msg; } [ProcessMethod("ImageCheck", "GetProductBarcode_P2", "工位2获取产品条码", InvokeType.TestInvoke)] public ResponseMessage GetProductBarcode_P2(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = new ResponseMessage(); if (config is IImageCheckOperationConfig opConfig) { var results = opConfig.Products.Select(u => { DetectResult result = new DetectResult(); result.PID = u.PID; result.Specs = GetSpecListFromConfigSelection(opConfig.SpecCollection); return result; }).ToList(); msg.DataObj = results; var tool = GetHalconTool(null, "", opConfig.AlgorithemPath); var ret = tool.RunProcedure(null, new Dictionary() { { "INPUT_Image", opConfig.ImageSet.HImage } }, new List() { "OUTPUT_Flag", "OUTPUT_Results", "OUTPUT_Barcode" }); if (!ret.Item1 || ret.Item2["OUTPUT_Flag"].I != 1) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"工位2条码检测工具运行失败,{ret.Item4}"); msg.Result = -1; return msg; } FillSpecResults(results[0].PID, results[0].Specs, ret.Item2["OUTPUT_Results"].HTupleToDouble(), opConfig.Products[0].SEQUENCE); var barcodeSpec = results[0].Specs.FirstOrDefault(u => u.Code == AOI2Config.BarcodeSpecCode); string barcode = ret.Item2["OUTPUT_Barcode"].S; opConfig.Products[0].SN = barcode; opConfig.Products[0].Details.ForEach(u => u.SN = barcode); LogAsync(DateTime.Now, EnumHelper.LogLevel.Action, $"产品{opConfig.Products[0].PID}条码获取为{barcode}"); UpdateProductSNIntoDB(opConfig.Products[0].PID, barcode); if (!string.IsNullOrWhiteSpace(AOI2Config.BarcodeRegex)) { bool isOK = Regex.IsMatch(barcode, AOI2Config.BarcodeRegex); if (!isOK) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Warning, $"产品{opConfig.Products[0].PID}条码{barcode}未能通过验证,判定条码NG"); if (barcodeSpec == null) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Error, $"未能获取产品条码标准,请检查流程配置中是否选择条码标准或条码检测操作中是否添加条码标准"); results[0].Specs.ForEach(u => u.ActualValue = -999); } else { barcodeSpec.ActualValue = -999; } } } //if (AOI2Config.IsEnabelMESUpload) //{ // if (barcodeSpec == null || barcodeSpec.MeasureResult != true) // { // opConfig.Products[0].IsPreStationOK = false; // LogAsync(DateTime.Now, EnumHelper.LogLevel.Error, $"产品{opConfig.Products[0].PID}条码获取失败,默认前站检测NG"); // } // else // { // var res = CheckProductSNByMES(new OperationConfigBase() { TriggerStr = barcode }, null, null); // opConfig.Products[0].IsPreStationOK = res.Result == 1; // } //} //else //{ //opConfig.Products[0].IsPreStationOK = true; //} } return msg; } #endregion #region position 3 [ProcessMethod("", "PositionCheck_P3", "工位3检测", InvokeType.TestInvoke)] public ResponseMessage PositionCheck_P3(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = RunImageCheck(config); msg.IsReply = false; if (msg.Result != 1) { msg.IsReply = true; msg.DataStr = "3#,NG,NG"; } return msg; } [ProcessMethod("ImageCheck", "CheckLineProfile_P3", "工位3检测产品线轮廓度", InvokeType.TestInvoke)] public ResponseMessage CheckLineProfile_P3(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = new ResponseMessage(); try { if (config is IImageCheckOperationConfig opConfig) { var results = opConfig.Products.Select(u => { DetectResult result = new DetectResult(); result.PID = u.PID; result.Specs = GenerateSpecByMeasureItems(AOI2Config.MeasureItemBinds); return result; }).ToList(); msg.DataObj = results; #region 计算轮廓点的线段 List segLineDatas = new List(); AOI2Config.MeasurePointCollection.ForEach(p => { if (p.ContourEdge == ContourEdge.X) { segLineDatas.Add(p.X); segLineDatas.Add(p.Y); segLineDatas.Add(p.X + AOI2Config.CountLineShift); segLineDatas.Add(p.Y); } else { segLineDatas.Add(p.X); segLineDatas.Add(p.Y); segLineDatas.Add(p.X); segLineDatas.Add(p.Y + AOI2Config.CountLineShift); } }); #endregion List contourPoints = AOI2Config.MeasurePointCollection.Select(u => u.Copy()).ToList(); //LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"图片id 2 {opConfig.ImageSet.Id}"); var tool = GetHalconTool(null, "", opConfig.AlgorithemPath); var ret = tool.RunProcedure(new Dictionary() { { "INPUT_SegLines", segLineDatas.ToArray() } }, new Dictionary() { { "INPUT_Image", opConfig.ImageSet.HImage } }, new List() { "OUTPUT_Results", "OUTPUT_Barcode", "OUTPUT_PointStandard", "OUTPUT_PointCatch" }, null); if (ret == null) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"脚本{opConfig.AlgorithemPath}运行异常,返回值为null"); } else if (!ret.Item1) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"脚本{opConfig.AlgorithemPath}运行异常,{ret.Item4}"); } else { var pResult = results[0]; var p = opConfig.Products[0]; //根据当前产品索引,获取对应产品的补偿值配置 var pResultOffsets = AOI2Config.Product_PointsOffsetList.FirstOrDefault(pp => pp.ProductIndex.ToString() == p.SEQUENCE.Split("_")[p.SEQUENCE.Split("_").Count() - 1]); List datas = ret.Item2["OUTPUT_Results"].HTupleToDouble(); //string barcode = ret.Item2["OUTPUT_Barcode"].S; var barcodeSpec = results[0].Specs.FirstOrDefault(u => u.Code == AOI2Config.BarcodeSpecCode); string barcode = ret.Item2["OUTPUT_Barcode"].S.Replace(" ", "").ToLower() == "noread" ? opConfig.Products[0].SN.ToUpper() : ret.Item2["OUTPUT_Barcode"].S.ToUpper(); opConfig.Products[0].SN = barcode; opConfig.Products[0].Details.ForEach(u => u.SN = barcode); LogAsync(DateTime.Now, EnumHelper.LogLevel.Action, $"产品{opConfig.Products[0].PID}条码获取为{barcode}"); UpdateProductSNIntoDB(opConfig.Products[0].PID, barcode); if (!string.IsNullOrWhiteSpace(AOI2Config.BarcodeRegex)) { bool isOK = Regex.IsMatch(barcode, AOI2Config.BarcodeRegex); if (!isOK) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Warning, $"产品{opConfig.Products[0].PID}条码{barcode}未能通过验证,判定条码NG"); if (barcodeSpec == null) { //LogAsync(DateTime.Now, EnumHelper.LogLevel.Error, $"未能获取产品条码标准,请检查流程配置中是否选择条码标准或条码检测操作中是否添加条码标准"); //results[0].Specs.ForEach(u => u.ActualValue = -999); } else { barcodeSpec.ActualValue = -999; } } } LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"计算轮廓度,输入点位:{string.Join(",", segLineDatas.Select(u => u.ToString("f3")))};输出原始数据:{string.Join(",", string.Join(",", datas.Select(u => u.ToString("f3"))))}"); List Pointdatas11 = new List(); List eleList = new List(); List pointsred = ret.Item2["OUTPUT_PointCatch"].HTupleToDouble(); List pointsgreen = ret.Item2["OUTPUT_PointStandard"].HTupleToDouble(); for (int i = 0; i < pointsred.Count / 2; i++) { PointIndicator point = new PointIndicator(new List() { pointsred[2*i+1], pointsred[2*i], }); point.Pen = new Pen(System.Drawing.Color.Red, 1); eleList.Add(point); //LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"RED{i}"); } for (int i = 0; i < pointsgreen.Count / 2; i++) { PointIndicator point = new PointIndicator(new List() { pointsgreen[2*i+1], pointsgreen[2*i], }); point.Pen = new Pen(System.Drawing.Color.Green, 1); eleList.Add(point); //LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"GREEN{i}"); } if (invokeDevice is CameraBase cam) { //LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"拟合图"); cam.SaveFitImage(eleList, opConfig.ImageSet); } else { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"设备名称{invokeDevice.Name}"); } if (datas.Count != AOI2Config.MeasurePointCollection.Count * 2) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"轮廓度点位数据数量{datas.Count},和预置点位数量{AOI2Config.MeasurePointCollection.Count}不一致"); contourPoints.ForEach(u => { u.ActualX = -999; u.ActualY = -999; u.CalculateError(true); }); } else { for (int i = 0; i < contourPoints.Count; i++) { //初始化补偿值,并根据当前处理的点位名称、及补偿开关,获取补偿值进行相关计算 double offsetX = 0.0, offsetY = 0.0; if (pResultOffsets != null) { var checkPointsOffset = pResultOffsets.CheckPointsOffsetList.FirstOrDefault(c => c.ReadyToOffsetMeasurePointName == contourPoints[i].Name); if (checkPointsOffset != null && checkPointsOffset.IsOffsetCalculate == true) { offsetX = checkPointsOffset.OffsetX; offsetY = checkPointsOffset.OffsetY; } contourPoints[i].ActualX = datas[i * 2] + offsetX; contourPoints[i].ActualY = datas[i * 2 + 1] + offsetY; contourPoints[i].CalculateError(true); } else { contourPoints[i].ActualX = datas[i * 2] + offsetX; contourPoints[i].ActualY = datas[i * 2 + 1] + offsetY; contourPoints[i].CalculateError(true); } Pointdatas11.Add(new Pointdata { Code = contourPoints[i].Name, ValueX = contourPoints[i].ActualX, ValueY = contourPoints[i].ActualY, }); } p.DataTag = contourPoints; var tempDatas = p.SEQUENCE.Split(new char[] { '_' }, StringSplitOptions.RemoveEmptyEntries).ToList(); string positionNum = tempDatas[tempDatas.Count - 1]; ContourPointDataRecord(contourPoints, pResult.PID, positionNum); } pResult.Specs.ForEach(s => { var point = contourPoints.FirstOrDefault(u => u.Name == s.Code); if (point != null) { s.ActualValue = Math.Abs(point.Error) * 2.0; LogAsync(DateTime.Now, EnumHelper.LogLevel.Action, $"产品{pResult.PID}检测项{s.Code}赋值{s.GetMeasureValueStr()},结果{s.GetMeasureResultStr()}"); } else { var measureItem = AOI2Config.MeasureItemBinds.FirstOrDefault(u => u.FAIName == s.Code || u.SpecCode == s.Code); if (measureItem != null) { var pointDatas = measureItem.MeasurePointNameCollection.Select(u => { var mPoint = contourPoints.FirstOrDefault(p => p.Name == u.MeasurePointName); if (mPoint != null) { return Math.Abs(mPoint.Error); } else { return 999; } }); s.ActualValue = pointDatas.Max() * 2.0; LogAsync(DateTime.Now, EnumHelper.LogLevel.Action, $"产品{pResult.PID}检测项{s.Code}赋值{s.GetMeasureValueStr()},结果{s.GetMeasureResultStr()}"); } } //Pointdatas11.Add(new Pointdata() //{ // Code = s.Code, // Value = Convert.ToDouble(s.GetMeasureValueStr()), // Stand = s.StandardValue, // Min = s.StandardValue - s.Tolrenance_Negative, // Max = s.StandardValue + s.Tolrenance_Positive, // ispoint = false, //}); }); if (M141Pointlist.Count > 500) { M141Pointlist.Add(Pointdatas11); M141Pointlist.RemoveAt(0); } else { M141Pointlist.Add(Pointdatas11); } lock (o_numrefresh) { if (numrefresh > 10) { PointRefreshUI(); numrefresh = 0; } else { numrefresh++; } } } //opConfig.ImageSet.HImage?.Dispose(); //opConfig.ImageSet.HImage = null; } } catch (Exception re) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"{re.ToString()}"); } return msg; } object o_numrefresh = new object(); int numrefresh = 0; string _contourPointDataHead = ""; private async void ContourPointDataRecord(List points, string pid, string positionNum) { await Task.Run(() => { try { if (string.IsNullOrWhiteSpace(_contourPointDataHead)) { _contourPointDataHead = "Time,PID,Position,"; points.OrderBy(u => u.Name).ToList().ForEach(u => { _contourPointDataHead += $"{u.Name}_X,{u.Name}_Y,{u.Name}_X实际值,{u.Name}_Y实际值,{u.Name}误差,"; }); } DateTime dt = DateTime.Now; string data = $"{dt.ToString("HH:mm:ss.fff")}T,{pid},{positionNum},"; points.OrderBy(u => u.Name).ToList().ForEach(u => { data += $"{u.X.ToString("f3")},{u.Y.ToString("f3")},{u.ActualX.ToString("f3")},{u.ActualY.ToString("f3")},{u.Error.ToString("f3")},"; }); CSVRecordAsync($"ContourPointRecord_{dt.ToString("yyyyMMdd")}.csv", data, _contourPointDataHead); } catch { } }); } // 合并到 CheckLineProfile_P3 [ProcessMethod("ImageCheck", "GetProductBarcode_P3", "工位3获取产品条码", InvokeType.TestInvoke)] public ResponseMessage GetProductBarcode_P3(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = new ResponseMessage(); if (config is IImageCheckOperationConfig opConfig) { var results = opConfig.Products.Select(u => { DetectResult result = new DetectResult(); result.PID = u.PID; result.Specs = GetSpecListFromConfigSelection(opConfig.SpecCollection); return result; }).ToList(); msg.DataObj = results; var tool = GetHalconTool(null, "", opConfig.AlgorithemPath); var ret = tool.RunProcedure(null, new Dictionary() { { "INPUT_Image", opConfig.ImageSet.HImage } }, new List() { "OUTPUT_Flag", "OUTPUT_Results", "OUTPUT_Barcode" }); if (!ret.Item1 || ret.Item2["OUTPUT_Flag"].I != 1) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"工位2条码检测工具运行失败,{ret.Item4}"); msg.Result = -1; return msg; } FillSpecResults(results[0].PID, results[0].Specs, ret.Item2["OUTPUT_Results"].HTupleToDouble(), opConfig.Products[0].SEQUENCE); var barcodeSpec = results[0].Specs.FirstOrDefault(u => u.Code == AOI2Config.BarcodeSpecCode); string barcode = ret.Item2["OUTPUT_Barcode"].S; opConfig.Products[0].SN = barcode; opConfig.Products[0].Details.ForEach(u => u.SN = barcode); LogAsync(DateTime.Now, EnumHelper.LogLevel.Action, $"产品{opConfig.Products[0].PID}条码获取为{barcode}"); UpdateProductSNIntoDB(opConfig.Products[0].PID, barcode); if (!string.IsNullOrWhiteSpace(AOI2Config.BarcodeRegex)) { bool isOK = Regex.IsMatch(barcode, AOI2Config.BarcodeRegex); if (!isOK) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Warning, $"产品{opConfig.Products[0].PID}条码{barcode}未能通过验证,判定条码NG"); if (barcodeSpec == null) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Error, $"未能获取产品条码标准,请检查流程配置中是否选择条码标准或条码检测操作中是否添加条码标准"); results[0].Specs.ForEach(u => u.ActualValue = -999); } else { barcodeSpec.ActualValue = -999; } } } //if (AOI2Config.IsEnabelMESUpload) //{ // if (barcodeSpec == null || barcodeSpec.MeasureResult != true) // { // opConfig.Products[0].IsPreStationOK = false; // LogAsync(DateTime.Now, EnumHelper.LogLevel.Error, $"产品{opConfig.Products[0].PID}条码获取失败,默认前站检测NG"); // } // else // { // var res = CheckProductSNByMES(new OperationConfigBase() { TriggerStr = barcode }, null, null); // opConfig.Products[0].IsPreStationOK = res.Result == 1; // } //} //else //{ // opConfig.Products[0].IsPreStationOK = true; //} } return msg; } #endregion #region position 4 [ProcessMethod("", "PositionCheck_P4", "工位4检测", InvokeType.TestInvoke)] public ResponseMessage PositionCheck_P4(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = RunImageCheck(config); msg.IsReply = false; if (msg.Result != 1) { msg.IsReply = true; msg.DataStr = "4#,NG,NG"; } return msg; } [ProcessMethod("", "PositionCheck_P4_INS", "工位4检测INS", InvokeType.TestInvoke)] public ResponseMessage PositionCheck_P4_INS(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = new ResponseMessage(); msg.Result = 1; msg.IsReply = false; List measureBinds = new List(); string inputSequence = ""; try { RunImageCheckPreTreat(config, out measureBinds, out inputSequence); } catch (Exception ex) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"检测预处理异常,{ex.GetExceptionMessage()}"); msg.Result = -1; msg.Message = ex.Message; msg.IsReply = true; msg.DataStr = $"4#,NG.NG,No Read,No Read,NG,NG"; return msg; } int measureNums = measureBinds.Count; int index = 0; insimage.Clear(); _isLastCheckFlag = false; measureBinds.GroupBy(u => u.CameraId).AsParallel().ForAll(c => { var camera = DeviceCollection.FirstOrDefault(u => u.Id == c.Key) as CameraBase; foreach (var i in c.ToList().OrderBy(u => u.ImageIndex).ToList()) { var products = i.ProductIndices.Select(pi => { return FindProductBySequence($"{inputSequence}_{pi}", true); }).ToList(); IImageSet imgSet = null; try { if (index == 0) { imgSet = CollectHImage(camera, i.SnapshotOpConfig); index++; } else { imgSet = insimage[index - 1]; index++; } } catch (Exception ex) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"工位4{camera.Name}取像{i.ImageIndex}异常,{ex.GetExceptionMessage()}"); } if (_isLastCheckFlag) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Error, $"当前流程已过期,获取图片不执行检测"); //Interlocked.Decrement(ref measureNums); measureNums = 0; break; } else { RunImageCheckAsync(products, config.TriggerStr, config.TriggerSource, imgSet, i).ContinueWith(t => { Interlocked.Decrement(ref measureNums); }); } } }); while (measureNums > 0 && !_isLastCheckFlag) { Thread.Sleep(50); } if (_isLastCheckFlag) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Error, $"当前流程已过期,退出检测,不执行反馈"); msg.IsReply = false; return msg; } //检测完成后清理图片缓存 measureBinds.Select(u => u.CameraId).Distinct().ToList().ForEach(u => { var camera = DeviceCollection.FirstOrDefault(c => c.Id == u) as CameraBase; camera.ClearImageBufferQueue(); LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"{camera.Name}相机工位4检测后清理缓存"); }); string positionName = measureBinds[0].WorkPosition; var pList = CheckPositionDoneAsync(positionName, inputSequence, config, new List()).GetAwaiter().GetResult(); return msg; } [ProcessMethod("ImageCheck", "CheckLineProfile_P4", "工位4检测产品线轮廓度", InvokeType.TestInvoke)] public ResponseMessage CheckLineProfile_P4(IOperationConfig config, IDevice invokeDevice, IDevice sourceDevice) { ResponseMessage msg = new ResponseMessage(); if (config is IImageCheckOperationConfig opConfig) { var results = opConfig.Products.Select(u => { DetectResult result = new DetectResult(); result.PID = u.PID; result.Specs = GenerateSpecByMeasureItems(AOI2Config.MeasureItemBinds); return result; }).ToList(); msg.DataObj = results; #region 计算轮廓点的线段 List segLineDatas = new List(); AOI2Config.MeasurePointCollection.ForEach(p => { if (p.ContourEdge == ContourEdge.X) { segLineDatas.Add(p.X); segLineDatas.Add(p.Y); segLineDatas.Add(p.X + AOI2Config.CountLineShift); segLineDatas.Add(p.Y); } else { segLineDatas.Add(p.X); segLineDatas.Add(p.Y); segLineDatas.Add(p.X); segLineDatas.Add(p.Y + AOI2Config.CountLineShift); } }); #endregion List contourPoints = AOI2Config.MeasurePointCollection.Select(u => u.Copy()).ToList(); var tool = GetHalconTool(null, "", opConfig.AlgorithemPath); var ret = tool.RunProcedure(new Dictionary() { { "INPUT_SegLines", segLineDatas.ToArray() } }, new Dictionary() { { "INPUT_Image", opConfig.ImageSet.HImage } }, new List() { "OUTPUT_Results" }, null); if (!ret.Item1) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"脚本{opConfig.AlgorithemPath}运行异常,{ret.Item4}"); } else { var pResult = results[0]; var p = opConfig.Products[0]; //根据当前产品索引,获取对应产品的补偿值配置 var pResultOffsets = AOI2Config.Product_PointsOffsetList.FirstOrDefault(pp => pp.ProductIndex.ToString() == p.SEQUENCE.Split("_")[p.SEQUENCE.Split("_").Count() - 1]); var datas = ret.Item2[$"OUTPUT_Results"].HTupleToDouble(); LogAsync(DateTime.Now, EnumHelper.LogLevel.Assist, $"计算轮廓度,输入点位:{string.Join(",", segLineDatas.Select(u => u.ToString("f3")))};输出原始数据:{string.Join(",", string.Join(",", datas.Select(u => u.ToString("f3"))))}"); if (datas.Count != AOI2Config.MeasurePointCollection.Count * 2) { LogAsync(DateTime.Now, EnumHelper.LogLevel.Exception, $"轮廓度点位数据数量{datas.Count},和预置点位数量{AOI2Config.MeasurePointCollection.Count}不一致"); contourPoints.ForEach(u => { u.ActualX = -999; u.ActualY = -999; u.CalculateError(true); }); } else { for (int i = 0; i < contourPoints.Count; i++) { //初始化补偿值,并根据当前处理的点位名称、及补偿开关,获取补偿值进行相关计算 double offsetX = 0.0, offsetY = 0.0; if (pResultOffsets != null) { var checkPointsOffset = pResultOffsets.CheckPointsOffsetList.FirstOrDefault(c => c.ReadyToOffsetMeasurePointName == contourPoints[i].Name); if (checkPointsOffset != null && checkPointsOffset.IsOffsetCalculate == true) { offsetX = checkPointsOffset.OffsetX; offsetY = checkPointsOffset.OffsetY; } } contourPoints[i].ActualX = datas[i * 2] + offsetX; contourPoints[i].ActualY = datas[i * 2 + 1] + offsetY; contourPoints[i].CalculateError(true); } p.DataTag = contourPoints; var tempDatas = p.SEQUENCE.Split(new char[] { '_' }, StringSplitOptions.RemoveEmptyEntries).ToList(); string positionNum = tempDatas[tempDatas.Count - 1]; ContourPointDataRecord(contourPoints, pResult.PID, positionNum); } pResult.Specs.ForEach(s => { var point = contourPoints.FirstOrDefault(u => u.Name == s.Code); if (point != null) { s.ActualValue = Math.Abs(point.Error) * 2.0; LogAsync(DateTime.Now, EnumHelper.LogLevel.Action, $"产品{pResult.PID}检测项{s.Code}赋值{s.GetMeasureValueStr()},结果{s.GetMeasureResultStr()}"); } else { var measureItem = AOI2Config.MeasureItemBinds.FirstOrDefault(u => u.FAIName == s.Code || u.SpecCode == s.Code); if (measureItem != null) { var pointDatas = measureItem.MeasurePointNameCollection.Select(u => { var mPoint = contourPoints.FirstOrDefault(p => p.Name == u.MeasurePointName); if (mPoint != null) { return Math.Abs(mPoint.Error); } else { return 999; } }); s.ActualValue = pointDatas.Max() * 2.0; LogAsync(DateTime.Now, EnumHelper.LogLevel.Action, $"产品{pResult.PID}检测项{s.Code}赋值{s.GetMeasureValueStr()},结果{s.GetMeasureResultStr()}"); } } }); } } return msg; } #endregion } }