using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using OPCAutomation; using HalconDotNet; using System.Reflection; using System.IO; using System.Threading.Tasks; using HDevEngine; using System.Globalization; using System.Configuration; using Newtonsoft.Json; namespace M423project { /// /// 产品长宽处理类 /// public class SizeDetection { #region 尺寸检测相关私有成员 private OPC opc; private ImageProcess imageProcess; private ConfigStruct opcConfig; private int stationNumber; private DetectionType detectionType; private HWindow halconWindow; private int therold = 30; public bool isPick; private HDevEngineTool _tool; private HTuple rectROI; private HTuple rectThreshold; #endregion /// ///类构造函数 /// /// OPC对象引用 /// 图象处理对象引用 /// 配置信息对象引用 /// 对应工位号(4) /// 检测结果数据存放列表 /// 检测类型Normal、Repeat、Offline,LastProduct,Manual、BIAS //BIAS /// 图象显示对象 public SizeDetection(OPC _opc, ImageProcess _imageProcess, ConfigStruct _opcConfig, int _stationNumber, DetectionType _detectionType, HWindow _halconWindow, int _therold = 30) { opc = _opc; imageProcess = _imageProcess; opcConfig = _opcConfig; stationNumber = _stationNumber; detectionType = _detectionType; halconWindow = _halconWindow; therold = _therold; //加载hdvp文件 _tool = new HDevEngineTool(Environment.CurrentDirectory + "\\Vision\\" + "MeasureLW.hdvp"); //载入hdvp,hdvp的文件名需和内部方法名一致 _tool.LoadProcedure("MeasureLW"); SetMinMaxGap(); #region add by Patrick 2020-2-18 LoadBatteryCheckSetting(); CtrlSetBatteryCheckROI.OnSettingSaved += CtrlSetBatteryCheckROI_OnSettingSaved; ConfigForm.OnConfigSaved += ConfigForm_OnConfigSaved; #endregion } #region add by Patrick 2020-2-18 private void CtrlSetBatteryCheckROI_OnSettingSaved(BatteryCheckSetting setting) { LoadBatteryCheckSetting(setting); } private void ConfigForm_OnConfigSaved(ConfigStruct config) { opcConfig = config; SetMinMaxGap(); } private void LoadBatteryCheckSetting(BatteryCheckSetting setting = null) { if (setting == null) { string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, CtrlSetBatteryCheckROI.SettingPath); if (File.Exists(path)) { using (StreamReader reader = new StreamReader(path, System.Text.Encoding.UTF8)) { var dataLine = reader.ReadToEnd(); setting = JsonConvert.DeserializeObject(dataLine); } } else { setting = new BatteryCheckSetting(); } } if (setting.IsEnabled) { rectROI = new HTuple(setting.StartPoint.Y, setting.StartPoint.X, setting.EndPoint.Y, setting.EndPoint.X); rectThreshold = new HTuple((double)setting.MinArea, (double)setting.MaxArea); } else { rectROI = new HTuple(0.0, 0.0, 0.0, 0.0); rectThreshold = new HTuple(0.0, 0.0); } } #endregion const double MAXGAP = 999999; const double MINGAP = -999999; double maxLength = MAXGAP; double maxWidth = MAXGAP; public double MaxLength { get { return maxLength; } set { if (value > 0) { maxLength = value; } } } public double MaxWidth { get { return maxWidth; } set { if (value > 0) { maxWidth = value; } } } double minLength = MINGAP; double minWidth = MINGAP; public double MinLength { get { return minLength; } set { if (value > 0) { minLength = value; } } } public double MinWidth { get { return minWidth; } set { if (value > 0) { minWidth = value; } } } private void SetMinMaxGap() { MaxLength = opcConfig.MaxLength; MaxWidth = opcConfig.MaxWidth; MinLength = opcConfig.MinLength; MinWidth = opcConfig.MinWidth; // string maxLengthStr = ConfigurationManager.AppSettings["MaxCellLength"]; // string maxWidthStr = ConfigurationManager.AppSettings["MaxCellWidth"]; // string minLengthStr = ConfigurationManager.AppSettings["MinCellLength"]; // string minWidthStr = ConfigurationManager.AppSettings["MinCellWidth"]; // if (!string.IsNullOrWhiteSpace(maxLengthStr)) // { // if (!(double.TryParse(maxLengthStr, out MaxLength) && MaxLength > 0)) // { // MaxLength = MAXGAP; // } // } // if (!string.IsNullOrWhiteSpace(maxWidthStr)) // { // if (!(double.TryParse(maxWidthStr, out MaxWidth) && MaxWidth > 0)) // { // MaxWidth = MAXGAP; // } // } // if (!string.IsNullOrWhiteSpace(minLengthStr)) // { // if (!(double.TryParse(minLengthStr, out MinLength) && MinLength > 0)) // { // MinLength = MINGAP; // } // } // if (!string.IsNullOrWhiteSpace(minWidthStr)) // { // if (!(double.TryParse(minWidthStr, out MinWidth) && MinWidth > 0)) // { // MinWidth = MINGAP; // } // } } public void Execute(int productQty) { DateTime t1 = DateTime.Now; string fileName = string.Empty; double btyLength = CommonUtil.InvalidValue, btyWidth = CommonUtil.InvalidValue, btyCellWidhth = CommonUtil.InvalidValue; MeasureState ms = MeasureState.NA; MeasureState lengthMeasureState = MeasureState.NA; MeasureState widthMeasureState = MeasureState.NA; double isEmpty = 0; //bool isEmpty = false; string timeStr = DateTime.Now.ToString("hh:mm:ss fff"); CommonUtil.WriteLog(LogType.Inf, string.Format("开始检测产品尺寸, 时间:{0},StepControl:{1} ...", timeStr, CommonUtil.StepControl.ToString())); HObject hImage = null; try { Thread.Sleep(50); opc.Write(OPCOutputTag.DetectionStart4, false); Thread.Sleep(200); int detectID = CommonUtil.StepControl.GetDetectSizeStepID(); if (detectID < 1) { return; } string tempFileName = GetCurrentProductSN(detectID); string productNo = GetProductNo(detectID); int times = 3; while (times > 0 && hImage == null) { GC.Collect(); imageProcess.GrapSizeAsync(); int waitCount = 0; while (imageProcess.SizeImageCount <= 0 && waitCount < 10) { Thread.Sleep(50); waitCount++; } if (imageProcess.SizeImageCount > 0) hImage = imageProcess.DequeueSizeImage(); else { imageProcess.CloseSizeCamera(); imageProcess.ClearSizeImage(); Thread.Sleep(1000); imageProcess.OpenSizeCamera(CommonUtil.DetectionOption == DetectionOption.doProduct); } times--; } if (hImage != null) { VisionDetect visionDetect = new VisionDetect(); Dictionary tupleDictionary = new Dictionary(); Dictionary imageDictionary = new Dictionary(); double[] lengthArr = null; double[] widthArr = null; double[] isNaArr = null; imageDictionary.Add("INPUT_Image", hImage); tupleDictionary.Add("OUTPUT_Length", btyLength); tupleDictionary.Add("OUTPUT_Width", btyWidth); tupleDictionary.Add("OUTPUT_IsNa", isEmpty); tupleDictionary.Add("INPUT_Hwindow", halconWindow); #region add by Patrick 2020-2-18 tupleDictionary.Add("OUTPUT_RectArea", new HTuple()); tupleDictionary.Add("INPUT_Rect", rectROI); tupleDictionary.Add("INPUT_RectThreshold", rectThreshold); #endregion _tool.SetDictionary(tupleDictionary, imageDictionary); //执行 _tool.RunProcedure(); lengthArr = _tool.TupleDictionary["OUTPUT_Length"].ToDArr(); widthArr = _tool.TupleDictionary["OUTPUT_Width"].ToDArr(); isNaArr = _tool.TupleDictionary["OUTPUT_IsNa"].ToDArr(); double rectArea = _tool.TupleDictionary["OUTPUT_RectArea"].D; btyLength = lengthArr.Length > 0 ? lengthArr[0] : btyLength; btyWidth = widthArr.Length > 0 ? widthArr[0] : btyWidth; isEmpty = isNaArr.Length > 0 ? isNaArr[0] : isEmpty; //switch (CommonUtil.DetectionOption) //{ // case DetectionOption.doProduct: // visionDetect.M423MeasureLandWJohnny(hImage, halconWindow, ref btyLength, ref btyWidth,ref isEmpty); // break; // case DetectionOption.doStandardBlock: // visionDetect.M423MeasureLandWJohnny(hImage, halconWindow, ref btyLength, ref btyWidth,ref isEmpty); // break; //} bool isNA = Convert.ToBoolean(isEmpty); double compL = 0.0, compW = 0.0; //if (!Convert.ToBoolean(isEmpty)) //{ if (CommonUtil.DetectionOption == DetectionOption.doProduct) { Type t = opcConfig.compensationL.GetType(); FieldInfo fi = t.GetField(string.Format("station{0}", CommonUtil.mainForm.PlateID)); compL = (double)fi.GetValue(opcConfig.compensationL); t = opcConfig.compensationW.GetType(); fi = t.GetField(string.Format("station{0}", CommonUtil.mainForm.PlateID)); compW = (double)fi.GetValue(opcConfig.compensationW); SaveDualDataAsync(tempFileName, hImage, DateTime.Now, compL, compW, productNo, lengthArr, widthArr, rectArea, isNA); btyLength += compL; btyWidth += compW; //限制精度5位 btyLength = Math.Round(btyLength, 5); btyWidth = Math.Round(btyWidth, 5); lengthMeasureState = (btyLength >= opcConfig.batteryLengthLimit.Min && btyLength <= opcConfig.batteryLengthLimit.Max) ? MeasureState.OK : MeasureState.NG; widthMeasureState = (btyWidth >= opcConfig.batteryWidthLimit.Min && btyWidth <= opcConfig.batteryWidthLimit.Max) ? MeasureState.OK : MeasureState.NG; // 目前逻辑中,如果值OK就认为OK 如果值NG的情况下 根据IsNA判断,区分NG和NA if (lengthMeasureState == MeasureState.OK && widthMeasureState == MeasureState.OK) { ms = MeasureState.OK; if (opcConfig.IsSaveSizeOKImage) { DateTime dt = DateTime.Now; if (dt.Second >= 10 && dt.Second <= 15) { VisionDetect.SaveImageAs(hImage, CommonUtil.ScreenshotsDir + @"\" + tempFileName); } } } else { if (btyLength >= MaxLength || btyLength <= MinLength) { lengthMeasureState = MeasureState.NA; ms = MeasureState.NA; } if (btyWidth >= MaxWidth || btyWidth <= MinWidth) { widthMeasureState = MeasureState.NA; ms = MeasureState.NA; } //if (ms != MeasureState.NA) //{ // ms = MeasureState.NG; //} if (ms != MeasureState.NA) { if (!isNA) { ms = MeasureState.NG; } else { lengthMeasureState = MeasureState.NA; widthMeasureState = MeasureState.NA; ms = MeasureState.NA; } } fileName = VisionDetect.SaveImageAs(hImage, CommonUtil.ScreenshotsDir + $"_{ms.ToString()}\\{ tempFileName}"); } } else { Type t = opcConfig.standardCompensationL.GetType(); FieldInfo fi = t.GetField(string.Format("station{0}", CommonUtil.mainForm.PlateID)); compL = (double)fi.GetValue(opcConfig.standardCompensationL); t = opcConfig.standardCompensationW.GetType(); fi = t.GetField(string.Format("station{0}", CommonUtil.mainForm.PlateID)); compW = (double)fi.GetValue(opcConfig.standardCompensationW); btyLength += compL; btyWidth += compW; lengthMeasureState = (btyLength >= opcConfig.standardLengthLimit.Min && btyLength <= opcConfig.standardLengthLimit.Max) ? MeasureState.OK : MeasureState.NG; widthMeasureState = (btyWidth >= opcConfig.standardWidthLimit.Min && btyWidth <= opcConfig.standardWidthLimit.Max) ? MeasureState.OK : MeasureState.NG; if (lengthMeasureState == MeasureState.OK && widthMeasureState == MeasureState.OK) { ms = MeasureState.OK; //VisionDetect.SaveImageAs(hImage, CommonUtil.ScreenshotsDir + @"\" + tempFileName); } else { ms = MeasureState.NG; fileName = VisionDetect.SaveImageAs(hImage, CommonUtil.ScreenshotsDir + @"_NG\" + tempFileName); } } timeStr = DateTime.Now.ToString("hh:mm:ss fff"); CommonUtil.WriteLog(LogType.Inf, string.Format("产品尺寸检测完成:{0:F4}\t{1:F4}, {2}", btyLength, btyWidth, timeStr)); opc.Write(OPCOutputTag.TestResult, true); if (FormCheck._totalQty - FormCheck._finishedQty > 0 && FormCheck._pickCount > 0) { if (FormCheck._lastProductQty + FormCheck._pickCount < productQty + FormCheck._lastBreakProductQty) { FormCheck._lastProductQty = productQty + FormCheck._lastBreakProductQty; } if (productQty + FormCheck._lastBreakProductQty == FormCheck._lastProductQty + FormCheck._pickCount) { //string productNo = GetProductNo(CommonUtil.StepControl.GetDetectSizeStepID()); if (productNo != "NA" && WeekCompare(productNo)) { isPick = true; FormCheck._lastProductQty = productQty + FormCheck._lastBreakProductQty; } } } if (ms == MeasureState.OK && isPick) { FormCheck._finishedQty++; } if (ms == MeasureState.NG && isPick) { FormCheck._lastProductQty = FormCheck._lastProductQty - 2; } } else { opc.Write(OPCOutputTag.TestResult, false); CommonUtil.WriteLog(LogType.Inf, string.Format("没有获取到尺寸图像")); } DateTime t2 = DateTime.Now; TimeSpan ts = t2 - CommonUtil.mainForm.TurnDiskReadyTime; CommonUtil.mainForm.Invoke(new Action(() => { CommonUtil.mainForm.SetSizeTime((int)Math.Round(ts.TotalMilliseconds)); })); } catch (Exception ex) { CommonUtil.WriteLog(LogType.Exc, string.Format("产品尺寸检测出现异常:{0}", ex.Message)); } finally { hImage?.Dispose(); hImage = null; CommonUtil.mainForm.Invoke(new Action(() => CommonUtil.mainForm.SetDetectionSize(btyLength, btyWidth, btyCellWidhth, lengthMeasureState, widthMeasureState, CommonUtil.mainForm.PlateID, fileName, isPick || FormCheck._pickNow))); opc.Write(OPCOutputTag.DetectionOK4, true); CommonUtil.WriteLog(LogType.Exc, "完成向PLC发送DetectionOK4"); if (ms == MeasureState.OK && (FormCheck._pickNow || isPick)) { SpotCheckData checkData = new SpotCheckData(); if (isPick) { Task.Factory.StartNew(() => { FormCheck._detail.Type = "自動拾取"; checkData.SaveSpotCheckDetail(FormCheck._detail); checkData.UpdateFinishedQty(); checkData.UpdateLastProductQty(FormCheck._lastProductQty); checkData.SaveCheckToCsv(FormCheck._detail); }); isPick = false; } else { Task.Factory.StartNew(() => { FormCheck._detail.Type = "手動拾取"; checkData.SaveSpotCheckDetail(FormCheck._detail); checkData.SaveCheckToCsv(FormCheck._detail); }); FormCheck._pickNow = false; } } } } #region DualDataCSV 记录两份检测数据的CSV记录 static object _dualDataLock = new object(); private void SaveDualDataAsync(string tempFileName, HObject hImage, DateTime now, double compL, double compW, string productNo, double[] lengthArr, double[] widthArr, double area, bool isNA) { //if (lengthArr.Length < 2 || widthArr.Length < 2 || lengthArr.Length != widthArr.Length) // return; string data = $"{now.ToString("HH:mm:ss.fff")},{productNo},"; for (int i = 0; i < lengthArr.Length; i++) { double length = lengthArr[i]; double width = widthArr[i]; if (length != 999.999 && length != -999.999) { length += compL; } if (width != 999.999 && width != -999.999) { width += compW; } var lengthResult = (length >= opcConfig.batteryLengthLimit.Min && length <= opcConfig.batteryLengthLimit.Max) ? MeasureState.OK : MeasureState.NG; var widthResult = (width >= opcConfig.batteryWidthLimit.Min && width <= opcConfig.batteryWidthLimit.Max) ? MeasureState.OK : MeasureState.NG; data += length.ToString("f5") + ","; data += lengthResult.ToString() + ","; data += width.ToString("f5") + ","; data += widthResult.ToString() + ","; data += ((lengthResult == MeasureState.OK && widthResult == MeasureState.OK) ? MeasureState.OK : MeasureState.NG).ToString() + ","; if (lengthResult == MeasureState.OK && widthResult == MeasureState.OK) { //VisionDetect.SaveImageAs(hImage, CommonUtil.ScreenshotsDir + @"\" + tempFileName); } else { //string fileName = ""; //fileName = VisionDetect.SaveImageAs(hImage, CommonUtil.ScreenshotsDir + $"{(i + 1).ToString()}_NG\\{ tempFileName}"); VisionDetect.SaveWindowImageAs(halconWindow, CommonUtil.ScreenshotsDir + $"_{(i + 1).ToString()}_NG\\{ tempFileName}"); //VisionDetect.SaveImageAs(hImage, CommonUtil.ProductSizeImageDir + $"_{(i + 1).ToString()}_NG\\{ tempFileName}"); } } data += (area.ToString("f5") + ","); data += (isNA ? "NA" : "" + ","); data = data.TrimEnd(','); lock (_dualDataLock) { string fileName = @"E:\DailyReport"; fileName = Path.Combine(fileName, "DualData_" + DateTime.Now.ToString("yyyyMMdd") + ".csv"); bool isFileExisted = File.Exists(fileName); using (StreamWriter writer = new StreamWriter(fileName, true, System.Text.Encoding.UTF8)) { if (!isFileExisted) { writer.WriteLine(GetCSVHead(lengthArr.Length)); } writer.WriteLine(data); writer.Flush(); writer.Close(); } } } private string GetCSVHead(int dataNums) { string head = "Time,ProductionNo,"; for (int i = 1; i <= dataNums; i++) { head += ("Length_" + i + ","); head += ("LengthResult_" + i + ","); head += ("Width_" + i + ","); head += ("WidthResult_" + i + ","); head += ("Result_" + i + ","); } head += "Area,IsNA"; head = head.TrimEnd(','); return head; } #endregion private string GetCurrentProductSN(int detectID) { var mr = (from x in CommonUtil.mainForm.ProductBindingList where x.DetectID == detectID select x).FirstOrDefault(); return CommonUtil.mainForm.PlateID.ToString() + "_" + mr.ProductNo.Replace("/", "").Replace("\"", "'") + "_" + DateTime.Now.ToString("HHmmssff") + "_" + detectID.ToString(); } public string GetProductNo(int detecId) { var mr = (from x in CommonUtil.mainForm.ProductBindingList where x.DetectID == detecId select x).FirstOrDefault(); return mr.ProductNo.Replace("/", "").Replace("\"", "'"); } public bool WeekCompare(string productNo) { int result = 0; try { string weekYear = GetWeekYear(DateTime.Now); string weekPPID = productNo.Substring(3, 3); if (weekYear.Substring(0, 1) == weekPPID.Substring(0, 1)) { result = Convert.ToInt32(weekYear) - Convert.ToInt32(weekPPID); } else { string lastEnd = GetWeekYear(Convert.ToDateTime((DateTime.Now.Year - 1).ToString() + "-12-31")); result = Convert.ToInt32(lastEnd) - Convert.ToInt32(weekPPID); if (result == 0 && weekYear.Substring(1, 2) == "01") { result = 1; } else { result = 2; } } CommonUtil.WriteLog(LogType.Inf, "WeekCompare判定结果为:" + (result <= 1).ToString() + ",weekYear=" + weekYear + ",weekPPID=" + weekPPID + ",productNo=" + productNo); } catch (Exception ex) { CommonUtil.WriteLog(LogType.Err, string.Format("WeekCompare计算错误:{0}", ex.Message)); return false; } return result <= 1; } public string GetWeekYear(DateTime dt) { GregorianCalendar gc = new GregorianCalendar(); int weekOfYear = gc.GetWeekOfYear(dt, CalendarWeekRule.FirstDay, DayOfWeek.Monday); var weekPlusStr = ConfigurationManager.AppSettings["WeekPlus"]; int weekPlus = 0; if (!int.TryParse(weekPlusStr, out weekPlus)) { weekPlus = 0; } weekOfYear += weekPlus; string week = weekOfYear.ToString().Length == 1 ? "0" + weekOfYear.ToString() : weekOfYear.ToString(); var yearPlusStr = ConfigurationManager.AppSettings["YearPlus"]; int yearPlus = 0; if (!int.TryParse(yearPlusStr, out yearPlus)) { yearPlus = 0; } string year = dt.Year.ToString().Substring(dt.Year.ToString().Length - 1, 1); year = (int.Parse(year) + yearPlus).ToString(); return year + week; } } }