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;
string tempFileName = "";
try
{
Thread.Sleep(50);
opc.Write(OPCOutputTag.DetectionStart4, false);
Thread.Sleep(200);
int detectID = CommonUtil.StepControl.GetDetectSizeStepID();
if (detectID < 1)
{
return;
}
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);
tupleDictionary.Add("INPUT_Type", CommonUtil.DetectionOption == DetectionOption.doStandardBlock ? 2 : 1);
#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.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);
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);
VisionDetect.SaveImageAs(hImage, CommonUtil.ProductSizeImageDir + @"\" + 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)
//{
// if (!isNA)
// {
// ms = MeasureState.NG;
// fileName=VisionDetect.SaveImageAs(hImage, CommonUtil.ProductSizeImageDir + @"\" + tempFileName);
// }
// else
// {
// lengthMeasureState = MeasureState.NA;
// widthMeasureState = MeasureState.NA;
// ms = MeasureState.NA;
// fileName=VisionDetect.SaveImageAs(hImage, CommonUtil.ProductSizeImageDir + $"_{ms.ToString()}\\{ tempFileName}");
// }
//}
//ms = MeasureState.NG;
//fileName = VisionDetect.SaveImageAs(hImage, CommonUtil.ScreenshotsDir + @"_NG\" + tempFileName);
fileName = VisionDetect.SaveImageAs(hImage, CommonUtil.ProductSizeImageDir + @"\" + 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));
fileName = VisionDetect.SaveImageAs(hImage, $"{CommonUtil.ProductSizeImageDir}_NA\\{tempFileName}");
}
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;
}
}
}