#define Online #define Online_defect //#define Online_One using BarTenderPrint; using CCWin.SkinControl; using CCWin.Win32; using CCWin.Win32.Const; using DocumentFormat.OpenXml.Spreadsheet; using DocumentFormat.OpenXml.Wordprocessing; using GeBoShi.ImageDefect; using GeBoShi.UI.InageShow; using GeBoShi.UI.Product; using HalconDotNet; using HZH_Controls; using HZH_Controls.Forms; using K4os.Hash.xxHash; using MaiMuControl.Device; using MaiMuControl.Device.CamDev; using MaiMuControl.Device.IOCardDev; using MaiMuControl.Device.IOCardDev.Advantech; using MaiMuControl.Device.LightDev; using MaiMuControl.Device.LightDev.CST; using MaiMuControl.Device.LightDev.Rsee; using MaiMuControl.Device.PlcDev; using MaiMuControl.SysStatusMgr.CloudMgr; using MaiMuControl.SysStatusMgr.StatusMgr; using MaiMuControl.SysStatusMgr.UserMgr; using MaiMuControl.Utils; using Models; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OpenCvSharp; using OpenCvSharp.Extensions; using S7.Net; using SqlSugar; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.IO.Ports; using System.Linq; using System.Net.Mail; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; using System.Windows.Forms.VisualStyles; using ToolKits.Disk; using ToolKits.Http; using static System.Windows.Forms.VisualStyles.VisualStyleElement.TaskbarClock; namespace GeBoShi.SysCtrl { /// /// 主系统控制 /// public class SysMgr { #region singleton实例化 private static SysMgr _instance; private static readonly object _lock = new object(); public static SysMgr Instance { get { if (_instance == null) { lock (_lock) { if (_instance == null) { _instance = new SysMgr(); } } } return _instance; } } #endregion #region 私有 //系统状态 private StatusMgr statusMgr; public StatusMgr StatusMgr { get { return statusMgr; } } //用户管理 private GeBoShi.UI.User.UserMgr userMgr; public GeBoShi.UI.User.UserMgr UserMgr { get { return userMgr; } } //系统配置管理 private ConfMgr confMgr; //图像处理 private DefectLib defectLib; public DefectLib DefectLib { get { return defectLib; } } //产品检测列表 private List productCodeList = new List(); public List ProductCodeList { get { return productCodeList; } } //产品ID列表 private List productIdList = new List(); public List ProductIdList { get { return productIdList; } } /// /// 当前产品 /// private Models.Product CurrProductModel = null; //数据锁 private object lockCurrKey = new object(); //当前运行数据key private int currKey = 0; //线程管控 private Hashtable htTask = new Hashtable();//默认单线程写入不用lock, 多线程安全同步读取用Synchronized //是否处理完成 private bool _isDefect = false; //计算速度用,暂停时停止计数 private Stopwatch pStopWatch = new Stopwatch(); //计算速度用,计算实时速度 private Stopwatch pRunSpeedWatch = new Stopwatch(); private object lock_defectPuase = new object(); /// /// 二次判定缺陷 /// private List defectPuaseList = new List(); /// /// /// private ConcurrentDictionary defectPuaseImgList = new ConcurrentDictionary(); //实时速度 private double ActiveSpeed = 0; //图片队列 private int _DefectEnd = 0; private double _NarmalWidth = 0; //正常幅宽 private int listCntMax = 60; private int Cam1Cnt = 0; private int Cam2Cnt = 0; int _marginWidth1 = 0; int _marginWidth2 = 0; /// /// 界面二次判定开启 /// private bool DefectPauseForUser = true; /// /// 第一次计米启用 /// private bool JmFtStart = false; /// /// 界面光源值 /// private int FrmLightValue = 0; private string ImgDirSN = ""; #region 处理类型 private class ScanPhotoInfo { /// /// /// /// /// 1-n 第1张会把1改为0 /// public ScanPhotoInfo(int _devIndex, int _photoIndex, string _path) { devIndex = _devIndex; photoIndex = _photoIndex; path = _path; } public ScanPhotoInfo(int _devIndex, int _photoIndex, Mat _mat, bool haveEdge = true) { devIndex = _devIndex; photoIndex = _photoIndex; mat = _mat; EdgeSts = haveEdge; } public int devIndex { get; set; } /// /// 0-n /// public int photoIndex { get; set; } public string path { get; set; } public Mat mat { get; set; } public bool EdgeSts { get; set; } } #endregion private ConcurrentQueue _matList1 = new ConcurrentQueue(); private ConcurrentQueue _matList2 = new ConcurrentQueue(); private Service.ProductService PdtService = new Service.ProductService(); private Service.RecordsService RecordService = new Service.RecordsService(); private bool _isOpenIO; private bool _isHaveImgL; private bool _isHaveImgR; #endregion #region 公开字段 private bool _isInit; /// /// 是否初始化完成 /// public bool IsInit { get { return _isInit; } } private bool _isRuning; /// /// 设备正在运行 /// public bool IsRuning { get { return _isRuning; } } private bool _isAuto; /// /// 设备正在自动化流程中 /// public bool IsAuto { get { return _isAuto; } } #endregion #region 私有流程 //主流程 private Thread _mainThread; private Thread _Cam1Thread; private Thread _Cam2Thread; private Thread _SpeedThread; private Thread _SaveImgThread; private CancellationTokenSource _cts; private Form _FatherFrm; private object _lockOpenCV = new object(); #endregion #region 云端 //局域网云端 private bool init_Cloud; private CloudMgr cloudMgr; private int DailyOutput; #endregion private SysMgr() { _isInit = false; _isRuning = false; _isAuto = false; statusMgr = StatusMgr.Instance; confMgr = ConfMgr.Instance; userMgr = new GeBoShi.UI.User.UserMgr(statusMgr.MySqlIP); _cts = new CancellationTokenSource(); init_Cloud = false; cloudMgr = new CloudMgr(); DailyOutput = 0; Service.InitDB.ConnectionString = confMgr.DBConStr; PdtService = new Service.ProductService(); RecordService = new Service.RecordsService(); } #region 本地云上传 /// /// 上传当日产量 /// private void SendDailyOutput() { //开启云端 if (init_Cloud) { try { if (!cloudMgr.SendTopic("device/attributes", $"{{ \"DailyOutput\": \"{DailyOutput}\"}}")) Log("云端", $"上传失败", WarningEnum.Low); } catch (Exception e) { Log("云端", $"上传失败:{e.Message}", WarningEnum.Low); } } } /// /// 上传正常状态 /// public void SendStatus() { //开启云端 if (init_Cloud) { //上传报警状态和信息 string statusStr = "正常"; switch (StatusMgr.Status) { case SystemStsEnum.Manual: statusStr = "人工操作"; break; case SystemStsEnum.Standby: statusStr = "正常待机"; break; case SystemStsEnum.Initial: statusStr = "初始化"; break; case SystemStsEnum.Auto: statusStr = "自动运行"; break; case SystemStsEnum.Pause: statusStr = "自动暂停"; break; case SystemStsEnum.SetParams: statusStr = "参数设置"; break; case SystemStsEnum.Debug: statusStr = "调试"; break; case SystemStsEnum.Warning: statusStr = "系统报警"; break; case SystemStsEnum.Bootload: statusStr = "Bootload"; break; default: statusStr = "未知"; break; } try { if (!cloudMgr.SendTopic("device/attributes", $"{{\"status\": \"{statusStr}\", \"alm\": \"无报警信息\", " + $"\"name\": \"{confMgr.SysConfigParams.CloudThisName}\", \"DailyOutput\": \"{DailyOutput}\"}}")) Log("云端", $"上传失败", WarningEnum.Low); } catch (Exception e) { Log("云端", $"上传失败:{e.Message}", WarningEnum.Low); } } } #endregion #region 用户+登入+管理 /// /// 登入 /// /// public bool LoginSystem() { return userMgr.UserLoginDialog(); } /// /// 用户权限 /// public void UserPermissiomMgr() { userMgr.RightManageDialog(); } /// /// 用户管理 /// public void UserListMgr() { userMgr.UserManagerDialog(); } #endregion #region 系统初始化&&运行 /// /// 系统初始化 /// /// public bool Initial() { try { bool ret = false; string err = ""; ret = confMgr.LoadSystemConfig(); //根据llog路径,开始记录日志 if (!Directory.Exists(confMgr.SysConfigParams.LogPath)) throw new Exception("日志路径不存在"); statusMgr.StartLog(confMgr.SysConfigParams.LogPath); InitLog("系统开始初始化..."); //Thread.Sleep(200); int minWorker, minIOC; ThreadPool.GetMinThreads(out minWorker, out minIOC); bool trt = ThreadPool.SetMinThreads(25, minIOC); // 加载系统配置 InitLog("加载系统参数..."); //ret = confMgr.LoadSystemConfig(); if (!ret) { throw new Exception("系统参数加载失败..."); } InitLog("系统参数加载完成!"); //根据llog路径,开始记录日志 //if(!Directory.Exists(confMgr.SysConfigParams.LogPath)) // throw new Exception("日志路径不存在"); //statusMgr.StartLog(confMgr.SysConfigParams.LogPath); statusMgr.GotoInitial(); SendStatus(); //Thread.Sleep(200); // 硬件初始化 if (!InitAllDev()) { throw new Exception("硬件初始化失败..."); } InitLog("硬件初始化完成!"); // 加载硬件配置 InitLog("加载硬件驱动参数..."); if (!LoadDevConfig()) { throw new Exception("加载硬件驱动参数失败..."); } InitLog("加载硬件驱动参数完成!"); //Thread.Sleep(200); // 处理运行 InitLog("AI算法核心初始化..."); defectLib = new DefectLib(); if (!defectLib.start()) throw new Exception("外观检测核心初始化失败..."); InitLog("AI算法核心初始化完成!"); LedReady(); //初始化成功 _isInit = true; statusMgr.GotoNormalStandby(); SendStatus(); OnInitRuning(new InitEventArgs("系统初始化完成...", this.IsInit)); Run(); return ret; } catch (Exception ex) { InitLog(ex.Message, "初始化", WarningEnum.High); InitLog("系统初始化失败!", "初始化", WarningEnum.High); //statusMgr.GotoWarning(MaiMuControl.Device.WarningEnum.High, "初始化", ex.Message); return false; } } /// /// 初始化图像处理报警 /// /// public bool InitDefectEvent() { if (defectLib != null) { defectLib.WarningEvent = (warning, msg) => { Log("缺陷检测", msg, warning); }; return true; } return false; } public bool InitCloudConnect() { if (confMgr.SysConfigParams.OpenCloud) { if (cloudMgr.ConnectCloud(confMgr.SysConfigParams.CloudServerIP, confMgr.SysConfigParams.CloudServerPort, confMgr.SysConfigParams.CloudUser, confMgr.SysConfigParams.CloudPassword)) { init_Cloud = true; Log("云端数据", "开启云端连接"); return true; } Log("云端数据", "云端连接失败!", WarningEnum.Low); return false; } return true; } /// /// 运行主线程 /// private void Run() { _mainThread = new Thread(() => { MainThreadFunction(); }); _mainThread.IsBackground = true; _mainThread.Start(); #if Online_One #else _Cam1Thread = new Thread(() => { Cam1ThreadFunction(); }); _Cam1Thread.IsBackground = true; _Cam1Thread.Start(); _Cam2Thread = new Thread(() => { Cam2ThreadFunction(); }); _Cam2Thread.IsBackground = true; _Cam2Thread.Start(); #endif _SpeedThread = new Thread(() => { GetLenAndSpd(); }); _SpeedThread.IsBackground = true; _SpeedThread.Start(); _SaveImgThread = new Thread(() => { SaveImgThreadFunction(); }); _SaveImgThread.IsBackground = true; _SaveImgThread.Start(); } #endregion #region 二次判断开关 public void SetDefectPause(bool val) { DefectPauseForUser = val; } public bool GetDefectPause() { return DefectPauseForUser; } #endregion #region 界面光源设置 public void SetLightValue(int val) { FrmLightValue = val; if(IsInit) lightDev.SetLightDigitalValue(1, val); } #endregion #region 计米器控制 /// /// 计米器清空 /// private void ClearLengthCount() { if (confMgr.SysConfigParams.OpenLengthCount) { byte[] clearData = new byte[] { 0x01, 0x06, 0x00, 0x00, 0x00, 0x01, 0x48, 0x0a }; lengthCounter.Write(clearData, 0, 8); //Thread.Sleep(100); Thread.Sleep(10); byte[] recv = new byte[64]; //string recvdata = serialPort.ReadLine(); //recv = recvdata.ToBytes(); int readCnt = lengthCounter.BytesToRead; lengthCounter.Read(recv, 0, readCnt); } } /// /// 获取计米数据 /// /// private double GetLength() { //bool isFile = true; double length = -1; bool GetData = false; if (lengthCounter.IsOpen) { byte[] data = new byte[] { 0x01, 0x03, 0x00, 0x21, 0x00, 0x02, 0x94, 0x01 }; lengthCounter.Write(data, 0, 8); Thread.Sleep(10); byte[] recv = new byte[64]; //string recvdata = serialPort.ReadLine(); //recv = recvdata.ToBytes(); int readCnt = lengthCounter.BytesToRead; lengthCounter.Read(recv, 0, readCnt); byte[] bytes = new byte[4]; for (int i = 0; i < readCnt; i++) { if (recv[i] == 0x01 && recv[i + 1] == 0x03 && recv[i + 2] == 0x04) { bytes[0] = recv[i + 3]; bytes[1] = recv[i + 4]; bytes[2] = recv[i + 5]; bytes[3] = recv[i + 6]; GetData = true; } } if (GetData) { if (BitConverter.IsLittleEndian) Array.Reverse(bytes); int spddata = BitConverter.ToInt32(bytes, 0); length = spddata / 100.0; //if (isFile) // length = -length; } if (length > 10000) return -1; } return length; } #endregion #region 后台 private object LockSpd = new object(); private void SedSpd(double spd) { lock (LockSpd) { ActiveSpeed = spd; } } private double GetSpd() { double spd; lock (LockSpd) { spd = ActiveSpeed; } return spd; } /// /// 中断工序运行 /// /// private bool isBreakProcessRun() { return statusMgr.Status == SystemStsEnum.Pause || statusMgr.Warning == WarningEnum.High; } /// /// 获取缺陷数据 /// /// /// private List GetDefectListInfo(int liPhotoIndex) { List ret; lock (lock_defectPuase) { ret = defectPuaseList.Where(m => m.PhotoIndex == liPhotoIndex).ToList(); } return ret; } /// /// 获取速度和长度 /// private void GetLenAndSpd() { double stl = 0; //计米起始位置 double etl = 0; //计米结束位置 double spl = 0; //计算速度计米位置 double yqjimi = 0; //计米长度 double cehouDis = 0;//测厚位置记录 double UseTime = 1; double preSpd = 0;//上次速度,防止速度出差 double rioSpd = 0.3;//过冲 while (true) { int spdcnt = 0; try { if (_cts.IsCancellationRequested) break; if (IsRuning) { ////暂停开始 //stopWatch.Start(); do { #if Online if (currKey > 0) { Records curRecord = Hashtable.Synchronized(htTask)[currKey] as Records; #region 实时速度 if (confMgr.SysConfigParams.OpenLengthCount) { double length = GetLength(); if (length >= 0) { if (!JmFtStart) { //第一次记录起始数据 stl = 0; spl = 0; cehouDis = 0; JmFtStart = true; pRunSpeedWatch.Restart(); } else { //1s计算一次实时速度 double dt = pRunSpeedWatch.ElapsedMilliseconds; if (dt / 1000.0d > UseTime) { double spddis = Math.Abs(length - spl); spl = length; double runOnePicTime = dt / 1000.0d /( 60.0d);//总时间 分 double spd = Math.Round(spddis / runOnePicTime, 2); //if (spd !=0&&((Math.Abs(spd - preSpd) / preSpd) > rioSpd)) //{ // spd = (Math.Abs(spd - preSpd))/4 + preSpd; //} preSpd = spd; SedSpd(spd); if (spdcnt > 2) { Log($"计米对比", $"计米器距离{Math.Round(spddis,2)},计米速度{Math.Round(spd,2)},计米时间{Math.Round(runOnePicTime,4)}", WarningEnum.Normal, false); spdcnt = 0; } spdcnt++; //重新计时 pRunSpeedWatch.Restart(); } etl = length;//记录当前读数 yqjimi = Math.Round((etl - stl) / 1.00, 2); //记录长度 } lock (lockCurrKey) { curRecord.Len = yqjimi; curRecord.TimeLen = pStopWatch.ElapsedMilliseconds / 1000.0d / 60.0d;//总时间 分 } double tRunSpd = GetSpd();//1s计算的实时速度 //显示平均速度 OnAutoRuning(new RunEventArgs(yqjimi, tRunSpd)); #region 处理2次判定 //处理2次判定 double aveSpd = Math.Round(yqjimi / (pStopWatch.ElapsedMilliseconds / 1000.0d / (60.0d)), 2); double StopDecRunDis = tRunSpd / 60.0 * confMgr.SysConfigParams.StopDecTime / 2; double atcDis = (confMgr.SysConfigParams.StopLookDis - StopDecRunDis); //计米器判断暂停:瑕疵二次判断 if (confMgr.SysConfigParams.OpenLengthCountStop && DefectPauseForUser && defectPuaseList.Count > 0) { if ((yqjimi - atcDis) > (defectPuaseList[0].Y / 100)) { //if ((yqjimi - atcDis) < (defectPuaseList[0].Y / 100 + 1)) { Log($"二次判断", $"计米器{yqjimi},瑕疵位置{defectPuaseList[0].Y / 100},停车距离{confMgr.SysConfigParams.StopLookDis}m,停车减速时间{confMgr.SysConfigParams.StopDecTime}s,减速位移{StopDecRunDis},实际停车距离{atcDis},实时速度{tRunSpd},匀速{aveSpd}", WarningEnum.Normal, false); List lstEditDefect0 = GetDefectListInfo(defectPuaseList[0].PhotoIndex); Log($"二次判断", $"(图像{defectPuaseList[0].PhotoIndex})已达观察台,瑕疵二次判断=》({string.Join(",", lstEditDefect0.Select(m => m.Code).ToArray())})是否包含在({string.Join(",", curRecord.ProductInfo.DefectPauseOption.ToArray())})中。", WarningEnum.Normal, false); //瑕疵选项过滤 if (curRecord.ProductInfo.DefectPauseOption.Count == 0 || lstEditDefect0.Where(x => curRecord.ProductInfo.DefectPauseOption.Contains(x.Code)).Count() > 0) { Log($"二次判断", $"(图像{defectPuaseList[0].PhotoIndex})需瑕疵二次判断,已达观察台,进入暂停。", WarningEnum.Normal, false); if (lstEditDefect0.Where(x => DefectItemsPuaseNameList.Contains(x.Name)).Count() > 0) { Stop(); //获取完整数据 Thread.Sleep(500); var lstEditDefect = GetDefectListInfo(defectPuaseList[0].PhotoIndex); #region 二级界面 int liPhotoIndex = defectPuaseList[0].PhotoIndex; #region 加载界面 if (defectPuaseImgList.ContainsKey(liPhotoIndex)) { var pimage = (Bitmap)defectPuaseImgList[liPhotoIndex].ToBitmap().Clone(); var pmat = defectPuaseImgList[liPhotoIndex].Clone(); Task.Run(() => { int liDefectCount = lstEditDefect.Count; ImageShowFrm frmDefect = new ImageShowFrm(liPhotoIndex, lstEditDefect, pimage); //frmDefect.Show(); if (frmDefect.ShowDialog() == DialogResult.OK) { SysMgr.Instance.DelDefectEdit(curRecord, frmDefect.lstDel); OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, pmat, frmDefect.lstDel)); //double len = (double)this.lblLen.Tag; //this.reDrawDefectPoints(curRecord.DefectInfoList, new double[] { 0, Math.Round(curRecord.FaceWidthMax + 0.005f, 2) }, new double[] { 0, len }); Log($"二次检测", $"本次忽略{frmDefect.lstDel.Count}个瑕疵,本张图由{liDefectCount} -> {lstEditDefect.Count},总数{curRecord.DefectInfoList.Count}"); } else { OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, pmat, null)); } }); } else Log($"二次检测", $"本次图像已检测完成"); #endregion //OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, defectPuaseImgList[liPhotoIndex])); #endregion } } Log($"二次判断删除", $"{defectPuaseList[0].PhotoIndex}-{testCnt}", WarningEnum.Normal, false); lstEditDefect0 = defectPuaseList.Where(m => m.PhotoIndex == defectPuaseList[0].PhotoIndex).ToList(); if (lstEditDefect0.Count > 0 && defectPuaseImgList.Count > 0) { Mat mt; defectPuaseImgList.TryRemove(lstEditDefect0[0].PhotoIndex, out mt); } //foreach (var item in lstEditDefect0) { lock (lock_defectPuase) { defectPuaseList.RemoveAll(m => m.PhotoIndex == defectPuaseList[0].PhotoIndex); //defectPuaseList.Remove(item); } } } //else //{ // Log($"二次判断", $"速度过快,超过展示位置!实时速度{tRunSpd},{defectPuaseList[0].PhotoIndex}-{testCnt}", WarningEnum.Low, true); //} } } #endregion } } #endregion #endif #region 堆积显示 OnAutoRuning(new RunEventArgs(_matList1.Count, _matList2.Count, defectLib.GetDefectInfo())); //System.GC.Collect(); #endregion #if Online lock (lockCurrKey) { #region 换卷长度提示 if (confMgr.SysConfigParams.OpenLengthCount) { if (curRecord.ProductInfo.residueWarnningLen > 0 && yqjimi > curRecord.ProductInfo.residueWarnningLen) { Log($"告警", $"已经达到换卷长度最大值{curRecord.ProductInfo.residueWarnningLen}-当前长度{yqjimi}-", WarningEnum.High); } } #endregion #region 测厚停止 if (confMgr.SysConfigParams.OpenLengthCount && curRecord.ProductInfo.OpenThicknessDetection) { if (curRecord.ProductInfo.ThicknessDetectionStopDis > 0 && (yqjimi - cehouDis) > curRecord.ProductInfo.ThicknessDetectionStopDis) { Stop(); Log($"测厚提示", $"已经达到测厚位置{curRecord.ProductInfo.ThicknessDetectionStopDis}-当前前进长度{(yqjimi - cehouDis)}-", WarningEnum.High); } } #endregion } } #endif Thread.Sleep(200); } while (!isBreakProcessRun()); //暂停中断 //stopWatch.Stop(); //pStopWatch.Stop(); //_isRuning = false; } Thread.Sleep(10); } catch (Exception e) { _isRuning = false; Log("运行报警", "计米流程运行出错:" + e.Message + "\n", WarningEnum.High); } } } /// /// 相机1采图预处理 /// private void Cam1ThreadFunction() { int errStep = 0; int marginWidth0 = 0; int marginException = 0; #if Online #else string imgfilePath = "D:\\CPL\\img\\L1.bmp"; Mat timg = new Mat(imgfilePath); timg = timg.Flip(FlipMode.XY); #endif while (true) { try { if (_cts.IsCancellationRequested) break; if (IsRuning) { ////暂停开始 //stopWatch.Start(); do { #region 实时采图 #if Online //采集图片 AcquisitionMat acq = _LinecamDev1.GetFrames(100); if (acq.GrabStatus == "GrabPass") { #else Thread.Sleep(2500); AcquisitionMat acq = new AcquisitionMat(); { acq.GrabStatus = "GrabPass"; acq.Image = timg.Clone(); Log($"图像预处理", $"模拟相机1-{Cam1Cnt}", WarningEnum.Normal, true); } if (acq.GrabStatus == "GrabPass") { #endif //专用二次判断处理 #if true #region 处理2次判定 if (!confMgr.SysConfigParams.OpenLengthCountStop) { //处理2次判定 //暂停:瑕疵二次判断 if (DefectPauseForUser ) { errStep = 11; Records curRecord = Hashtable.Synchronized(htTask)[currKey] as Records; int liPhotoIndex = Cam1Cnt - confMgr.SysConfigParams.StopImageCnt; #if false var havePause = curRecord.dicPhoto_Defect.Where(x => x.Key <= liPhotoIndex && x.Value == true); if(havePause != null) { foreach (var item in havePause) { liPhotoIndex = item.Key; break; } } #endif //Log($"二次判断2", $"图像{scanPhotos0.photoIndex} {liPhotoIndex}={scanPhotos0.photoIndex}-{confMgr.SysConfigParams.StopImageCnt};{JsonConvert.SerializeObject(curRecord.dicPhoto_Defect)}", WarningEnum.Normal, false); if (liPhotoIndex >= 0 && curRecord.dicPhoto_Defect.ContainsKey(liPhotoIndex) && curRecord.dicPhoto_Defect[liPhotoIndex] ) { if (defectPuaseList.Count > 0) { errStep = 12; List lstEditDefect = GetDefectListInfo(liPhotoIndex); Log($"二次判断2", $"(图像{liPhotoIndex})已达观察台,瑕疵二次判断=》({string.Join(",", lstEditDefect.Select(m => m.Code).ToArray())})是否包含在({string.Join(",", DefectItemsPuaseNameList.ToArray())})或({string.Join(",", curRecord.ProductInfo.DefectPauseOption.ToArray())})中。", WarningEnum.Normal, false); //瑕疵选项过滤 if (curRecord.ProductInfo.DefectPauseOption.Count == 0 || lstEditDefect.Where(x => curRecord.ProductInfo.DefectPauseOption.Contains(x.Code)).Count() > 0) { Log($"二次判断2", $"(图像{liPhotoIndex})需瑕疵二次判断,已达观察台,进入暂停。速度:{GetSpd()},停车缺陷数:{lstEditDefect.Where(x => DefectItemsPuaseNameList.Contains(x.Name)).Count()}"); // if (lstEditDefect.Where(x => x.Code == "jietou").Count() > 0) //if (lstEditDefect.Where(x => DefectItemsPuaseNameList.Contains(x.Name)).Count() > 0) { Stop(); //获取完整数据 Thread.Sleep(500); lstEditDefect = GetDefectListInfo(liPhotoIndex); //获取完整数据 #region 二级界面 #region 加载界面 if (defectPuaseImgList.ContainsKey(liPhotoIndex)) { Log($"二次判断2", $"(图像{liPhotoIndex})需瑕疵二次判断,缺陷数:{lstEditDefect.Count}"); var pmat = defectPuaseImgList[liPhotoIndex].Clone(); OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, pmat)); #if false var pimage = (Bitmap)defectPuaseImgList[liPhotoIndex].ToBitmap().Clone(); Task.Run(() => { int liDefectCount = lstEditDefect.Count; ImageShowFrm frmDefect = new ImageShowFrm(liPhotoIndex, lstEditDefect, pimage); //if (frmDefect.ShowDialog(_FatherFrm) == DialogResult.OK) if (frmDefect.ShowDialog() == DialogResult.OK) { SysMgr.Instance.DelDefectEdit(curRecord, frmDefect.lstDel); OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, pmat, frmDefect.lstDel)); Log($"二次检测2", $"本次忽略{frmDefect.lstDel.Count}个瑕疵,本张图由{liDefectCount} -> {lstEditDefect.Count},总数{curRecord.DefectInfoList.Count}"); } else { OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, pmat, null)); } }); #endif } else Log($"二次检测2", $"本次图像已检测完成"); #endregion //OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, defectPuaseImgList[liPhotoIndex])); #endregion } errStep = 18; } Log($"二次判断删除", $"{liPhotoIndex}-{testCnt}", WarningEnum.Normal, false); lstEditDefect = defectPuaseList.Where(m => m.PhotoIndex == liPhotoIndex).ToList(); if (lstEditDefect.Count > 0 && defectPuaseImgList.Count > 0) { Mat mt; defectPuaseImgList.TryRemove(lstEditDefect[0].PhotoIndex, out mt); } //foreach (var item in lstEditDefect) { lock (lock_defectPuase) { defectPuaseList.RemoveAll(m => m.PhotoIndex == liPhotoIndex); //defectPuaseList.Remove(item); } } } else Log($"二次判断2", $"(图像{liPhotoIndex})-缺陷未获得,是否包含在({string.Join(",", DefectItemsPuaseNameList.ToArray())})或({string.Join(",", curRecord.ProductInfo.DefectPauseOption.ToArray())})中。", WarningEnum.Low, false); #if false curRecord.dicPhoto_Defect.TryUpdate(liPhotoIndex, false, true); #endif } //else // Log($"二次判断2", $"(图像{liPhotoIndex})-处理未跟上", WarningEnum.Low, false); } //time += $"->处理2次判定({stopWatch.ElapsedMilliseconds})"; } #endregion #endif //预处理 Stopwatch stopWatch = new Stopwatch(); //Log($"图像预处理", $"相机1-{Cam1Cnt}", WarningEnum.Normal, false); string time = ""; stopWatch.Start(); Mat img = acq.Image.Clone(); //图像镜像 if (confMgr.SysConfigParams.Cam1_flipX && confMgr.SysConfigParams.Cam1_flipY) { img = img.Flip(FlipMode.XY); //Cv2.Flip(img, img, FlipMode.XY);//翻转 } else if (confMgr.SysConfigParams.Cam1_flipX) { img = img.Flip(FlipMode.X); //Cv2.Flip(img, img, FlipMode.X);//翻转 } else if (confMgr.SysConfigParams.Cam1_flipY) { img = img.Flip(FlipMode.Y); //Cv2.Flip(img, img, FlipMode.Y);//翻转 } time += $"->相机1翻转({stopWatch.ElapsedMilliseconds})"; //显示 //OnAutoRuning(new RunEventArgs(1, img)); //lock (_lockOpenCV) { //存在数据队列 if (currKey != 0 || htTask.ContainsKey(currKey)) { //保存图片 if (confMgr.SysConfigParams.CamImag.AutoSave && !string.IsNullOrEmpty(confMgr.SysConfigParams.CamImag.SavePath)) { Records curRecord = Hashtable.Synchronized(htTask)[currKey] as Records; string dirPath = Util.CreateSubDir(confMgr.SysConfigParams.CamImag.SavePath, new List { $"{curRecord.BarCode}_{curRecord.BarCodeName}_{ImgDirSN}" }); //OpenCvSharp.Extensions.BitmapConverter.ToBitmap(img).Save($"{dirPath}Cam1-{Cam1Cnt}.bmp", ImageFormat.Bmp); AddImageSave($"{dirPath}Cam1-{Cam1Cnt}.bmp", img); } if (_matList1.Count > listCntMax) { Log($"堆积过多", $"相机1-缓存{_matList1.Count}大于{listCntMax}", WarningEnum.High); //_matList1.Dequeue(); System.GC.Collect(); } Mat mat = img; try { errStep = 1; //反转+相机索引调换 //lock (_lockOpenCV) { //裁边,两侧和中间重合部分 if (confMgr.SysConfigParams.MidCoin > 0)//中间重合部分 { errStep = 3; int width = mat.Width - confMgr.SysConfigParams.MidCoin / 2; mat = OpencvUtils.CutImage(mat, 0, 0, width, mat.Height); time += $"->相机1-去重({stopWatch.ElapsedMilliseconds})"; } } errStep = 4; //左裁边 mat = OpencvUtils.getMaxInsetRect2(mat, true, confMgr.SysConfigParams.HolePx, out marginWidth0); //存储裁边异常相机图 差异5cm if (_marginWidth1 == 0) { _marginWidth1 = marginWidth0; marginException = 0; } if (!confMgr.SysConfigParams.CamImag.AutoSave && Math.Abs(marginWidth0 - _marginWidth1) > (10 * confMgr.SysConfigParams.Cm2px_x) && !string.IsNullOrEmpty(confMgr.SysConfigParams.CamImag.SavePath)) { //边缘异常计数 marginException++; Records curRecord = Hashtable.Synchronized(htTask)[currKey] as Records; string dirPath = Util.CreateSubDir(confMgr.SysConfigParams.CamImag.SavePath, new List { $"{curRecord.BarCode}_{curRecord.BarCodeName}_{ImgDirSN}" }); AddImageSave($"{dirPath}Cam1-{Cam1Cnt}.bmp", img); Log($"图像预处理", $"(相机1-图像{Cam1Cnt})-裁边异常:{marginWidth0}-{_marginWidth1} 》 {10 * confMgr.SysConfigParams.Cm2px_x}", WarningEnum.Normal, false); } else { marginException = 0; _marginWidth1 = marginWidth0; } errStep = 5; time += $"->相机1裁边({stopWatch.ElapsedMilliseconds})"; Log($"图像预处理", $"(相机1-图像{Cam1Cnt})-左图去重后:{mat.Width}*{mat.Height}," + $"重复值:{confMgr.SysConfigParams.MidCoin / 2} + 时间{time}", WarningEnum.Normal, false); //System.GC.Collect(); } catch (Exception e) { Log($"图像处理", $"异常({errStep}):(相机1-图像{Cam1Cnt})-{e.Message}", WarningEnum.High); } //Log($"图像1入队列", $"相机1-{Cam1Cnt}", WarningEnum.Normal, false); //Cv2.Flip(img, img, FlipMode.XY);//翻转 _matList1.Enqueue(new ScanPhotoInfo(0, Cam1Cnt++, mat.Clone(), marginException < 1)); } else Log($"相机1", $"(图像)-未扫码,图像丢弃!", WarningEnum.Low); } } #endregion //System.GC.Collect(); Thread.Sleep(50); } while (!isBreakProcessRun()); //暂停中断 //stopWatch.Stop(); pStopWatch.Stop(); //_isRuning = false; } else { //防止内存爆满 AcquisitionMat acq = _LinecamDev1.GetFrames(10); } Thread.Sleep(50); } catch (Exception e) { _isRuning = false; Log("运行报警", $"相机1流程运行出错{errStep}:" + e.Message + "\n", WarningEnum.High); } } } /// /// 相机2采图预处理 /// private void Cam2ThreadFunction() { int errStep = 0; int marginWidth0 = 0; int marginException = 0; #if Online #else string imgfilePath = "D:\\CPL\\img\\R1.bmp"; Mat timg = new Mat(imgfilePath); timg = timg.Flip(FlipMode.XY); #endif while (true) { try { if (_cts.IsCancellationRequested) break; if (IsRuning) { ////暂停开始 //stopWatch.Start(); do { #region 实时采图 #if Online //采集图片 AcquisitionMat acq = _LinecamDev2.GetFrames(10); if (acq.GrabStatus == "GrabPass") { #else Thread.Sleep(2500); AcquisitionMat acq = new AcquisitionMat(); { acq.GrabStatus = "GrabPass"; acq.Image = timg; Log($"图像预处理", $"模拟相机2-{Cam2Cnt}", WarningEnum.Normal, true); } if (acq.GrabStatus == "GrabPass") { #endif Mat img = acq.Image.Clone(); //预处理 Stopwatch stopWatch = new Stopwatch(); //Log($"图像预处理", $"相机2-{Cam2Cnt}", WarningEnum.Normal, false); string time = ""; stopWatch.Start(); //图像镜像 if (confMgr.SysConfigParams.Cam2_flipX && confMgr.SysConfigParams.Cam2_flipY) { img = img.Flip(FlipMode.XY); //Cv2.Flip(img, img, FlipMode.XY);//翻转 } else if (confMgr.SysConfigParams.Cam2_flipX) { img = img.Flip(FlipMode.X); //Cv2.Flip(img, img, FlipMode.X);//翻转 } else if (confMgr.SysConfigParams.Cam2_flipY) { img = img.Flip(FlipMode.Y); //Cv2.Flip(img, img, FlipMode.Y);//翻转 } time += $"->相机2翻转({stopWatch.ElapsedMilliseconds})"; //显示 //OnAutoRuning(new RunEventArgs(2, img)); //lock (_lockOpenCV) { //存在数据队列 if (currKey != 0 || htTask.ContainsKey(currKey)) { //保存图片 if (confMgr.SysConfigParams.CamImag.AutoSave && !string.IsNullOrEmpty(confMgr.SysConfigParams.CamImag.SavePath)) { Records curRecord = Hashtable.Synchronized(htTask)[currKey] as Records; string dirPath = Util.CreateSubDir(confMgr.SysConfigParams.CamImag.SavePath, new List { $"{curRecord.BarCode}_{curRecord.BarCodeName}_{ImgDirSN}" }); //OpenCvSharp.Extensions.BitmapConverter.ToBitmap(img).Save($"{dirPath}Cam2-{Cam2Cnt}.bmp", ImageFormat.Bmp); AddImageSave($"{dirPath}Cam2-{Cam2Cnt}.bmp", img); } if (_matList2.Count > listCntMax) { Log($"堆积过多", $"相机2-缓存{_matList2.Count}大于{listCntMax}", WarningEnum.High); //_matList2.Dequeue(); System.GC.Collect(); } Mat mat = img; try { errStep = 1; //反转+相机索引调换 //lock (_lockOpenCV) { //裁边,两侧和中间重合部分 if (confMgr.SysConfigParams.MidCoin > 0)//中间重合部分 { errStep = 3; int width = mat.Width - confMgr.SysConfigParams.MidCoin / 2; mat = OpencvUtils.CutImage(mat, confMgr.SysConfigParams.MidCoin / 2, 0, width, mat.Height); time += $"->相机2-去重({stopWatch.ElapsedMilliseconds})"; } } errStep = 4; //右裁边 mat = OpencvUtils.getMaxInsetRect2(mat, false, confMgr.SysConfigParams.HolePx, out marginWidth0); //存储裁边异常相机图 差异5cm if (_marginWidth2 == 0) { marginException = 0; _marginWidth2 = marginWidth0; } if (!confMgr.SysConfigParams.CamImag.AutoSave && Math.Abs(marginWidth0 - _marginWidth2) > (10 * confMgr.SysConfigParams.Cm2px_x) && !string.IsNullOrEmpty(confMgr.SysConfigParams.CamImag.SavePath)) { //边缘异常计数 marginException++; Records curRecord = Hashtable.Synchronized(htTask)[currKey] as Records; string dirPath = Util.CreateSubDir(confMgr.SysConfigParams.CamImag.SavePath, new List { $"{curRecord.BarCode}_{curRecord.BarCodeName}_{ImgDirSN}" }); AddImageSave($"{dirPath}Cam2-{Cam2Cnt}.bmp", img); Log($"图像预处理", $"(相机2-图像{Cam2Cnt})-裁边异常:{marginWidth0}-{_marginWidth2} 》 {10 * confMgr.SysConfigParams.Cm2px_x}", WarningEnum.Normal, false); } else { _marginWidth2 = marginWidth0; } errStep = 5; time += $"->相机2裁边({stopWatch.ElapsedMilliseconds})"; Log($"图像预处理", $"(相机2-图像{Cam2Cnt})-右图去重后:{mat.Width}*{mat.Height}," + $"重复值:{confMgr.SysConfigParams.MidCoin / 2} + 时间{time}", WarningEnum.Normal, false); } catch (Exception e) { Log($"图像处理", $"异常({errStep}):(相机2-图像{Cam2Cnt})-{e.Message}", WarningEnum.High); } //Log($"图像2入队列", $"相机2-{Cam1Cnt}", WarningEnum.Normal, false); //Cv2.Flip(img, img, FlipMode.XY);//翻转 _matList2.Enqueue(new ScanPhotoInfo(1, Cam2Cnt++, mat.Clone(), marginException < 1)); //System.GC.Collect(); } else Log($"相机2", $"(图像)-未扫码,图像丢弃!", WarningEnum.Low); } } #endregion //System.GC.Collect(); Thread.Sleep(50); } while (!isBreakProcessRun()); //暂停中断 //stopWatch.Stop(); pStopWatch.Stop(); //_isRuning = false; } else { //防止内存爆满 AcquisitionMat acq = _LinecamDev2.GetFrames(10); } Thread.Sleep(50); } catch (Exception e) { _isRuning = false; Log("运行报警", "相机1流程运行出错:" + e.Message + "\n", WarningEnum.High); } } } int testCnt = 0; /// /// 后台运行主线程 /// private void MainThreadFunction() { Mat acq1 = new Mat(); Mat acq2 = new Mat(); #if Online #else string imgfilePath1 = "D:\\CPL\\img\\L1.bmp"; string imgfilePath2 = "D:\\CPL\\img\\R1.bmp"; Mat timg1 = new Mat(imgfilePath1); timg1 = timg1.Flip(FlipMode.XY); Mat timg2 = new Mat(imgfilePath2); timg2 = timg1.Flip(FlipMode.XY); #endif while (true) { int errStep = 0; try { if (_cts.IsCancellationRequested) break; if (IsRuning) { ////暂停开始 //stopWatch.Start(); do { #if Online_One #region 等待图片 #if Online //采集图片 AcquisitionMat varacq1 = _LinecamDev1.GetFrames(10); AcquisitionMat varacq2 = _LinecamDev2.GetFrames(10); #else AcquisitionMat varacq1 = new AcquisitionMat(); { varacq1.GrabStatus = "GrabPass"; varacq1.Image = timg1; Log($"图像预处理", $"模拟相机1-{Cam1Cnt}", WarningEnum.Normal, true); } AcquisitionMat varacq2 = new AcquisitionMat(); { varacq2.GrabStatus = "GrabPass"; varacq2.Image = timg2; Log($"图像预处理", $"模拟相机2-{Cam2Cnt}", WarningEnum.Normal, true); } #endif if (varacq1.GrabStatus == "GrabPass") { acq1 = varacq1.Image.Clone(); _isHaveImgL = true; Cam1Cnt++; } if (varacq2.GrabStatus == "GrabPass") { acq2 = varacq2.Image.Clone(); _isHaveImgR = true; Cam2Cnt++; } if (_isHaveImgL && _isHaveImgR) { errStep = 1; Stopwatch stopWatch = new Stopwatch(); string time = ""; Records curRecord = Hashtable.Synchronized(htTask)[currKey] as Records; try { if (Cam1Cnt != Cam2Cnt) { Log($"图像预处理", $"图像错位{Cam1Cnt}-{Cam2Cnt}", WarningEnum.High); continue; } #region 处理2次判定 if (!confMgr.SysConfigParams.OpenLengthCountStop) { //处理2次判定 //暂停:瑕疵二次判断 if (DefectPauseForUser) { stopWatch.Restart(); errStep = 2; int liPhotoIndex = Cam1Cnt - confMgr.SysConfigParams.StopImageCnt - 1; if (liPhotoIndex >= 0 && curRecord.dicPhoto_Defect.ContainsKey(liPhotoIndex) && curRecord.dicPhoto_Defect[liPhotoIndex] && curRecord.DefectInfoList != null) { if (defectPuaseList.Count > 0) { errStep = 3; List lstEditDefect = defectPuaseList.Where(m => m.PhotoIndex == liPhotoIndex).ToList(); Log($"二次判断2", $"(图像{liPhotoIndex})已达观察台,瑕疵二次判断=》({string.Join(",", lstEditDefect.Select(m => m.Code).ToArray())})是否包含在({string.Join(",", curRecord.ProductInfo.DefectPauseOption.ToArray())})中。", WarningEnum.Normal, false); //瑕疵选项过滤 if (curRecord.ProductInfo.DefectPauseOption.Count == 0 || lstEditDefect.Where(x => curRecord.ProductInfo.DefectPauseOption.Contains(x.Code)).Count() > 0) { Log($"二次判断2", $"(图像{liPhotoIndex})需瑕疵二次判断,已达观察台,进入暂停。速度:{GetSpd()}"); // if (lstEditDefect.Where(x => x.Code == "jietou").Count() > 0) if (lstEditDefect.Where(x => DefectItemsPuaseNameList.Contains(x.Name)).Count() > 0) { Stop(); //获取完整数据 Thread.Sleep(500); lstEditDefect = defectPuaseList.Where(m => m.PhotoIndex == liPhotoIndex).ToList(); //获取完整数据 #region 二级界面 #region 加载界面 if (defectPuaseImgList.ContainsKey(liPhotoIndex)) { var pimage = (Bitmap)defectPuaseImgList[liPhotoIndex].ToBitmap().Clone(); var pmat = defectPuaseImgList[liPhotoIndex].Clone(); Task.Run(() => { int liDefectCount = lstEditDefect.Count; ImageShowFrm frmDefect = new ImageShowFrm(lstEditDefect, pimage); //frmDefect.Show(); if (frmDefect.ShowDialog(_FatherFrm) == DialogResult.OK) { SysMgr.Instance.DelDefectEdit(curRecord, frmDefect.lstDel); OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, pmat, frmDefect.lstDel)); //double len = (double)this.lblLen.Tag; //this.reDrawDefectPoints(curRecord.DefectInfoList, new double[] { 0, Math.Round(curRecord.FaceWidthMax + 0.005f, 2) }, new double[] { 0, len }); Log($"二次检测2", $"本次忽略{frmDefect.lstDel.Count}个瑕疵,本张图由{liDefectCount} -> {lstEditDefect.Count},总数{curRecord.DefectInfoList.Count}"); } else { OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, pmat, null)); } }); } else Log($"二次检测2", $"本次图像已检测完成"); #endregion //OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, defectPuaseImgList[liPhotoIndex])); #endregion } errStep = 3; } Log($"二次判断删除", $"{liPhotoIndex}-{testCnt}", WarningEnum.Normal, false); lstEditDefect = defectPuaseList.Where(m => m.PhotoIndex == liPhotoIndex).ToList(); if (lstEditDefect.Count > 0 && defectPuaseImgList.Count > 0) { Mat mt; defectPuaseImgList.TryRemove(lstEditDefect[0].PhotoIndex, out mt); } //foreach (var item in lstEditDefect) { lock (lock_defectPuase) { defectPuaseList.RemoveAll(m => m.PhotoIndex == liPhotoIndex); //defectPuaseList.Remove(item); } } } else Log($"二次判断2", $"(图像{liPhotoIndex})-缺陷未获得", WarningEnum.Low, false); } Log($"图像预处理", $"(图像{Cam1Cnt - 1})-处理2次判定:{stopWatch.ElapsedMilliseconds}", WarningEnum.Normal, false); //else // Log($"二次判断2", $"(图像{liPhotoIndex})-处理未跟上", WarningEnum.Low, false); } //time += $"->处理2次判定({stopWatch.ElapsedMilliseconds})"; } #endregion #region 相机图片保存 errStep = 4; //保存图片 if (confMgr.SysConfigParams.CamImag.AutoSave && !string.IsNullOrEmpty(confMgr.SysConfigParams.CamImag.SavePath)) { stopWatch.Restart(); string dirPath = Util.CreateSubDir(confMgr.SysConfigParams.CamImag.SavePath, new List { $"{curRecord.BarCode}_{curRecord.BarCodeName}" }); AddImageSave($"{dirPath}Cam1-{Cam1Cnt}.bmp", acq1); AddImageSave($"{dirPath}Cam2-{Cam2Cnt}.bmp", acq2); Log($"图像预处理", $"(图像{Cam1Cnt - 1})-相机图片保存:{stopWatch.ElapsedMilliseconds}", WarningEnum.Normal, false); } if (_matList2.Count > listCntMax) { Log($"堆积过多", $"相机-缓存{_matList2.Count}大于{listCntMax}", WarningEnum.High); //_matList2.Dequeue(); System.GC.Collect(); } #endregion //预处理 stopWatch.Restart(); #region 去重 if (confMgr.SysConfigParams.MidCoin > 0)//中间重合部分 { errStep = 6; int width = acq1.Width - confMgr.SysConfigParams.MidCoin / 2; acq1 = OpencvUtils.CutImage(acq1, confMgr.SysConfigParams.MidCoin / 2, 0, width, acq1.Height); acq2 = OpencvUtils.CutImage(acq2, 0, 0, width, acq2.Height); time += $"->相机-去重({stopWatch.ElapsedMilliseconds})"; } #endregion #region 裁边 errStep = 7; //左裁边 int marginWidth0; acq1 = OpencvUtils.getMaxInsetRect2(acq1, false, confMgr.SysConfigParams.HolePx, out marginWidth0); //右裁边 acq2 = OpencvUtils.getMaxInsetRect2(acq2, true, confMgr.SysConfigParams.HolePx, out marginWidth0); time += $"->相机裁边({stopWatch.ElapsedMilliseconds})"; #endregion #region 拼接 errStep = 8; //水平合并l Mat mat; mat = OpencvUtils.MergeImage_sameSize(new Mat[] { acq2, acq1 }); time += $"->图1+2合并({stopWatch.ElapsedMilliseconds})"; #endregion #region 镜像 errStep = 5; //图像镜像 mat = mat.Flip(FlipMode.XY); time += $"->相机翻转({stopWatch.ElapsedMilliseconds})"; #endregion #region 填充 errStep = 9; //比例缩放图片 int xw; int resizeWidth = OpencvUtils.GetWidthForResize(acq1.Width + acq2.Width - confMgr.SysConfigParams.MidCoin); if (resizeWidth == 0) throw new Exception("GetWidthForResize result 0 失败!"); var resize = new System.Drawing.Size(resizeWidth, OpencvUtils.image_height * 2);//固定8192*2张*4096 mat = OpencvUtils.Resize(mat, resize.Width, resize.Height, out xw); time += $"->填充({stopWatch.ElapsedMilliseconds})"; #endregion #region 处理 errStep = 10; defectLib.add(new DefectTask() { modelName = curRecord.ProductInfo.ModelName, record = curRecord, bmp = mat, bmpTag = mat.Clone(), photoIndex = Cam1Cnt - 1,//0-n 首张必需为0,因下面计算长度是从0开始 widthRatio = 1.0f,//等比例缩放,高度不变 qualifiedLimitList = curRecord.ProductInfo.QualifiedLimitList, labelDic = GetDefectCode(), finishEvent = callBackDefectEvent, xw = xw, cm2px_x = confMgr.SysConfigParams.Cm2px_x, cm2px_y = confMgr.SysConfigParams.Cm2px_y, expand_pixel = confMgr.SysConfigParams.Expand_pixel, }); curRecord.ScannerPhotoCount++; errStep = 11; time += $"->加入瑕疵待检队列({stopWatch.ElapsedMilliseconds})"; #endregion #region 门幅 float faceWidthX_cm = (float)Math.Round((Cam1Cnt) * mat.Height * 1.0f / confMgr.SysConfigParams.Cm2px_y, 2); float faceWidthY_cm = (float)Math.Round((mat.Width + confMgr.SysConfigParams.HolePx * 2) * 1.0f / confMgr.SysConfigParams.Cm2px_x, 2); if (curRecord.FaceWidthMin == 0 || curRecord.FaceWidthMin > faceWidthY_cm) curRecord.FaceWidthMin = faceWidthY_cm; if (curRecord.FaceWidthMax < faceWidthY_cm) curRecord.FaceWidthMax = faceWidthY_cm; var point = new float[] { faceWidthX_cm, faceWidthY_cm }; curRecord.FacePointList.Add(point); errStep = 12; //判定门幅 //if (x < XSizeRange[0]) // Log($"绘图", $"门幅宽度超限 1!!!! {x}<{XSizeRange[0]}", WarningEnum.High); //if (x > XSizeRange[1]) // Log($"绘图", $"门幅宽度超限 2!!!! {x}>{XSizeRange[1]}", WarningEnum.High); //if (item[1] < YSizeRange[0]) // Log($"绘图", $"门幅宽度超限 3!!!! {item[1]}<{YSizeRange[0]}", WarningEnum.High); //if (item[1] > YSizeRange[1]) // Log($"绘图", $"门幅宽度超限 4!!!! {item[1]}>{YSizeRange[1]}", WarningEnum.High); //显示门幅绘图 OnAutoRuning(new RunEventArgs(curRecord.FacePointList)); errStep = 13; time += $"->门幅刷新({stopWatch.ElapsedMilliseconds})"; #endregion #region 长度剩余提醒 errStep = 14; //长度剩余提醒 if (CurrProductModel.residueWarnningLen > 0 && curRecord.ErpLen > 0 && CurrProductModel.residueWarnningLen >= curRecord.ErpLen - curRecord.Len) { Log($"长度告警", $"已达剩余长度不足提醒!({curRecord.ErpLen - curRecord.Len}<={CurrProductModel.residueWarnningLen})", WarningEnum.Low); } #endregion } catch (Exception ex) { curRecord.ScannerPhotoFinishCount++;//失败时不能因数量不一致无法保存 Log($"图像预处理", $"异常({errStep}):(图像{Cam1Cnt-1})-{ex.Message}", WarningEnum.High); //string dirPath = FileUtil.initFolder($"{Config.ImagePath}{curRecord.BatchId}_{curRecord.ReelId}\\Err\\"); //OpenCvSharp.Extensions.BitmapConverter.ToBitmap(scanPhotos0.mat).Save($"{dirPath}{scanPhotos0.photoIndex}_0_Step{errStep}.bmp", ImageFormat.Bmp); //OpenCvSharp.Extensions.BitmapConverter.ToBitmap(scanPhotos1.mat).Save($"{dirPath}{scanPhotos1.photoIndex}_1_Step{errStep}.bmp", ImageFormat.Bmp); } finally { Log($"图像预处理", $"(图像{Cam1Cnt - 1})-进度计时:{time}", WarningEnum.Normal, false); //task = null; //System.GC.Collect(); } } else continue; #endregion #else #region 长度剩余提醒 //长度剩余提醒 Records curRecord = Hashtable.Synchronized(htTask)[currKey] as Records; if (CurrProductModel.residueWarnningLen > 0 && curRecord.ErpLen > 0 && CurrProductModel.residueWarnningLen >= curRecord.ErpLen - curRecord.Len) { Log($"长度告警", $"已达剩余长度不足提醒!({curRecord.ErpLen - curRecord.Len}<={CurrProductModel.residueWarnningLen})", WarningEnum.Low); } #endregion #region 图像裁边预处理 //预处理,队列都有数据,且数据长度一致 #if Online_defect ScanPhotoInfo peek1; ScanPhotoInfo peek2; if (_matList1.Count > 0 && _matList2.Count > 0 ) { _matList1.TryPeek(out peek1); _matList2.TryPeek(out peek2); if (peek1 != null && peek2 !=null && peek1.photoIndex != peek2.photoIndex) continue; Stopwatch stopWatch = new Stopwatch(); ScanPhotoInfo scanPhotos0; if(!_matList1.TryDequeue(out scanPhotos0)) Log($"图像出队列", $"异常相机1队列出队失败", WarningEnum.High); ScanPhotoInfo scanPhotos1; if(!_matList2.TryDequeue(out scanPhotos1)) Log($"图像出队列", $"异常相机2队列出队失败", WarningEnum.High); //Log($"图像出队列", $"相机1-{scanPhotos0.photoIndex}", WarningEnum.Normal, false); curRecord.dicPhoto_Defect.TryAdd(scanPhotos0.photoIndex, false);//加入索引,默认无瑕疵 //Log($"图像拼接处理", $"相机1-{scanPhotos0.photoIndex},相机2-{scanPhotos1.photoIndex}", WarningEnum.Normal, false); string time = ""; stopWatch.Start(); try { Mat mat; Mat mat0; Mat mat1; //lock (_lockOpenCV) { if (scanPhotos0.mat.Height != scanPhotos1.mat.Height) { Log($"警告", $"两相机采集图高度不一致({scanPhotos0.photoIndex}),dev1.Height={scanPhotos0.mat.Height},dev2.Height={scanPhotos1.mat.Height},重新resize...", WarningEnum.Low); if (scanPhotos0.mat.Height > scanPhotos1.mat.Height) scanPhotos1.mat = OpencvUtils.ResizeMat(scanPhotos1.mat, scanPhotos0.mat.Width, scanPhotos0.mat.Height); else scanPhotos0.mat = OpencvUtils.ResizeMat(scanPhotos0.mat, scanPhotos1.mat.Width, scanPhotos1.mat.Height); } errStep = 1; //反转+相机索引调换 mat1 = scanPhotos1.mat; mat0 = scanPhotos0.mat; //水平合并l mat = OpencvUtils.MergeImage_sameSize(new Mat[] { mat0, mat1 });//这里相机反装,左右反转下 //Log($"合并", $"(图像{scanPhotos0.photoIndex})-裁边去孔洞后:({mat0.Width}+{mat1.Width});合并后(去孔洞):{mat.Width}*{mat.Height}", WarningEnum.Normal, false); //float widthRatio = mat.Width * 1.0f / resize.Width;//宽度比例 time += $"->图1+2合并({stopWatch.ElapsedMilliseconds})"; } errStep = 2; //门幅更新(含两侧孔洞)x,y cm float faceWidthX_cm = (float)Math.Round((scanPhotos0.photoIndex + 1) * mat.Height * 1.0f / confMgr.SysConfigParams.Cm2px_y, 2); float faceWidthY_cm = (float)Math.Round((mat.Width + confMgr.SysConfigParams.HolePx * 2) * 1.0f / confMgr.SysConfigParams.Cm2px_x, 2); #else string imgfilePath = "C:\\Users\\fang\\Desktop\\新建文件夹\\boyi lou"; if (!Directory.Exists(imgfilePath)) { Log($"图像处理", $"模拟错误-路径错误{imgfilePath}", WarningEnum.High); break; } string[] files = Directory.GetFiles(imgfilePath, $"*.bmp", SearchOption.TopDirectoryOnly); if (files.Length > 0 && Cam1Cnt < files.Length) { Stopwatch stopWatch = new Stopwatch(); string time = ""; //stopWatch.Start(); ScanPhotoInfo scanPhotos0 = new ScanPhotoInfo(0, Cam1Cnt, new Mat(4096, 4096 * 2, MatType.CV_8UC3, new Scalar(0, 0, 0))); ScanPhotoInfo scanPhotos1 = new ScanPhotoInfo(1, Cam1Cnt, new Mat(4096, 4096 * 2, MatType.CV_8UC3, new Scalar(0, 0, 0))); stopWatch.Start(); errStep = 0; try { Log($"图像处理", $"模拟{files[Cam1Cnt]}"); Mat mat = new Mat(files[Cam1Cnt]); Cam1Cnt++; Mat mat0 = scanPhotos1.mat; Mat mat1 = scanPhotos0.mat; float faceWidthX_cm = (float)Math.Round((scanPhotos0.photoIndex + 1) * mat.Height * 1.0f / confMgr.SysConfigParams.Cm2px_y, 2); float faceWidthY_cm = (float)Math.Round((mat.Width + confMgr.SysConfigParams.HolePx * 2) * 1.0f / confMgr.SysConfigParams.Cm2px_x, 2); #endif //显示图片 //OnAutoRuning(new RunEventArgs(mat.Clone())); //faceWidthX_cm = (float)Math.Round(faceWidthX_cm, 2); //faceWidthY_cm = (float)Math.Round(faceWidthY_cm, 2); if (curRecord.FaceWidthMin == 0 || curRecord.FaceWidthMin > faceWidthY_cm) curRecord.FaceWidthMin = faceWidthY_cm; if (curRecord.FaceWidthMax < faceWidthY_cm) curRecord.FaceWidthMax = faceWidthY_cm; var point = new float[] { faceWidthX_cm, faceWidthY_cm };// new System.Drawing.PointF(faceWidthX_cm, faceWidthY_cm); //Log($"门幅", $"(图像{scanPhotos0.photoIndex})-({scanPhotos0.photoIndex})位置:{point[0]}; 幅宽:{point[1]}", WarningEnum.Normal, false); curRecord.FacePointList.Add(point); errStep = 3; //判定门幅 //if (x < XSizeRange[0]) // Log($"绘图", $"门幅宽度超限 1!!!! {x}<{XSizeRange[0]}", WarningEnum.High); //if (x > XSizeRange[1]) // Log($"绘图", $"门幅宽度超限 2!!!! {x}>{XSizeRange[1]}", WarningEnum.High); //if (item[1] < YSizeRange[0]) // Log($"绘图", $"门幅宽度超限 3!!!! {item[1]}<{YSizeRange[0]}", WarningEnum.High); //if (item[1] > YSizeRange[1]) // Log($"绘图", $"门幅宽度超限 4!!!! {item[1]}>{YSizeRange[1]}", WarningEnum.High); //显示门幅绘图 OnAutoRuning(new RunEventArgs(curRecord.FacePointList)); errStep = 7; time += $"->门幅刷新({stopWatch.ElapsedMilliseconds})"; //去除两侧孔洞(门幅计算时不能去除) //if (Config.MarginHoleWidth > 0) // mat = OpenCVUtil.cutImage(mat, Config.MarginHoleWidth, 0, mat.Width - Config.MarginHoleWidth * 2, mat.Height); #region 速度 //计算速度 if (!confMgr.SysConfigParams.OpenLengthCount) { double lenMi = Math.Round(faceWidthX_cm / 100, 2); curRecord.Len = lenMi; curRecord.TimeLen = pStopWatch.ElapsedMilliseconds / 1000.0d / 60.0d;//总时间 分 double aveSpd = Math.Round(lenMi / curRecord.TimeLen, 2); //显示平均速度 OnAutoRuning(new RunEventArgs(lenMi, aveSpd)); errStep = 9; time += $"->速度刷新({stopWatch.ElapsedMilliseconds})"; } #endregion #if false #region 处理2次判定 if (!confMgr.SysConfigParams.OpenLengthCountStop) { //处理2次判定 //暂停:瑕疵二次判断 if (DefectPauseForUser && defectPuaseList.Count > 0) { errStep = 11; int liPhotoIndex = scanPhotos0.photoIndex - confMgr.SysConfigParams.StopImageCnt; //Log($"二次判断2", $"图像{scanPhotos0.photoIndex} {liPhotoIndex}={scanPhotos0.photoIndex}-{confMgr.SysConfigParams.StopImageCnt};{JsonConvert.SerializeObject(curRecord.dicPhoto_Defect)}", WarningEnum.Normal, false); if (liPhotoIndex >= 0 && curRecord.dicPhoto_Defect[liPhotoIndex] && curRecord.DefectInfoList != null) { errStep = 12; List lstEditDefect = defectPuaseList.Where(m => m.PhotoIndex == liPhotoIndex).ToList(); Log($"二次判断2", $"(图像{liPhotoIndex})已达观察台,瑕疵二次判断=》({string.Join(",", lstEditDefect.Select(m => m.Code).ToArray())})是否包含在({string.Join(",", curRecord.ProductInfo.DefectPauseOption.ToArray())})中。", WarningEnum.Normal, false); //瑕疵选项过滤 if (curRecord.ProductInfo.DefectPauseOption.Count == 0 || lstEditDefect.Where(x => curRecord.ProductInfo.DefectPauseOption.Contains(x.Code)).Count() > 0) { Log($"二次判断2", $"(图像{liPhotoIndex})需瑕疵二次判断,已达观察台,进入暂停。"); // if (lstEditDefect.Where(x => x.Code == "jietou").Count() > 0) if (lstEditDefect.Where(x => DefectItemsPuaseNameList.Contains(x.Name)).Count() > 0) { Stop(); //获取完整数据 Thread.Sleep(500); lstEditDefect = defectPuaseList.Where(m => m.PhotoIndex == liPhotoIndex).ToList(); //获取完整数据 #region 二级界面 #region 加载界面 if (defectPuaseImgList.ContainsKey(liPhotoIndex)) { var pimage = (Bitmap)defectPuaseImgList[liPhotoIndex].ToBitmap().Clone(); Task.Run(() => { int liDefectCount = lstEditDefect.Count; ImageShowFrm frmDefect = new ImageShowFrm(lstEditDefect, pimage); //frmDefect.Show(); if (frmDefect.ShowDialog() == DialogResult.OK) { SysMgr.Instance.DelDefectEdit(curRecord, frmDefect.lstDel); OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, defectPuaseImgList[liPhotoIndex], frmDefect.lstDel)); //double len = (double)this.lblLen.Tag; //this.reDrawDefectPoints(curRecord.DefectInfoList, new double[] { 0, Math.Round(curRecord.FaceWidthMax + 0.005f, 2) }, new double[] { 0, len }); Log($"二次检测2", $"本次忽略{frmDefect.lstDel.Count}个瑕疵,本张图由{liDefectCount} -> {lstEditDefect.Count},总数{curRecord.DefectInfoList.Count}"); } else { OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, defectPuaseImgList[liPhotoIndex], null)); } }); } else Log($"二次检测2", $"本次图像已检测完成"); #endregion //OnAutoRuning(new RunEventArgs(liPhotoIndex, lstEditDefect, curRecord, defectPuaseImgList[liPhotoIndex])); #endregion } errStep = 18; } Log($"二次判断删除", $"{liPhotoIndex}-{testCnt}", WarningEnum.Normal, false); lock (lock_defectPuase) { lstEditDefect = defectPuaseList.Where(m => m.PhotoIndex == liPhotoIndex).ToList(); if (lstEditDefect.Count > 0 && defectPuaseImgList.Count > 0) defectPuaseImgList.Remove(lstEditDefect[0].PhotoIndex); foreach (var item in lstEditDefect) { defectPuaseList.Remove(item); } } } } time += $"->处理2次判定({stopWatch.ElapsedMilliseconds})"; } #endregion #endif //幅宽判断是否为结束 if (scanPhotos0.photoIndex > 5 && Math.Abs(_NarmalWidth - faceWidthY_cm) > confMgr.SysConfigParams.EndEdgeOffset && _DefectEnd >= 1) { //出现第二次判断没有边缘 Log($"图像处理", $"边缘异常或者未找到边缘当作空跑处理:(图像{scanPhotos0.photoIndex})", WarningEnum.Normal, true); } else if (scanPhotos1.EdgeSts == false && scanPhotos0.EdgeSts == false && _DefectEnd >= 1) { //出现第二次判断没有边缘 Log($"图像处理", $"边缘异常或者未找到边缘当作空跑处理:(图像{scanPhotos0.photoIndex})", WarningEnum.Normal, true); } else { if (scanPhotos0.photoIndex > 5 && Math.Abs(_NarmalWidth - faceWidthY_cm) > confMgr.SysConfigParams.EndEdgeOffset) _DefectEnd++;//没边计数 else if (scanPhotos1.EdgeSts == false && scanPhotos0.EdgeSts == false) _DefectEnd++;//没边计数 else { _DefectEnd = 0; //有边的情况重置 //计算正常幅宽 _NarmalWidth = faceWidthY_cm; } //----缺陷队列 //比例缩放图片 int xw; int resizeWidth = OpencvUtils.GetWidthForResize(mat0.Width + mat1.Width - confMgr.SysConfigParams.MidCoin); if (resizeWidth == 0) throw new Exception("GetWidthForResize result 0 失败!"); var resize = new System.Drawing.Size(resizeWidth, OpencvUtils.image_height * 2);//固定8192*2张*4096 mat = OpencvUtils.Resize(mat, resize.Width, resize.Height, out xw); //Log($"图像处理", $"(图像{scanPhotos0.photoIndex})-合成图resize后:{mat.Width}*{mat.Height}", WarningEnum.Normal, false); defectLib.add(new DefectTask() { modelName = curRecord.ProductInfo.ModelName, record = curRecord, bmp = mat, bmpTag = mat.Clone(), photoIndex = scanPhotos0.photoIndex,//0-n 首张必需为0,因下面计算长度是从0开始 widthRatio = 1.0f,//等比例缩放,高度不变 qualifiedLimitList = curRecord.ProductInfo.QualifiedLimitList, labelDic = GetDefectCode(), finishEvent = callBackDefectEvent, xw = xw, cm2px_x = confMgr.SysConfigParams.Cm2px_x, cm2px_y = confMgr.SysConfigParams.Cm2px_y, expand_pixel = confMgr.SysConfigParams.Expand_pixel, }); curRecord.ScannerPhotoCount++; errStep = 10; time += $"->加入瑕疵待检队列({stopWatch.ElapsedMilliseconds})"; } } catch (Exception ex) { curRecord.ScannerPhotoFinishCount++;//失败时不能因数量不一致无法保存 Log( $"图像处理", $"异常({errStep}):(图像{scanPhotos0.photoIndex})-{ex.Message}", WarningEnum.High); //string dirPath = FileUtil.initFolder($"{Config.ImagePath}{curRecord.BatchId}_{curRecord.ReelId}\\Err\\"); //OpenCvSharp.Extensions.BitmapConverter.ToBitmap(scanPhotos0.mat).Save($"{dirPath}{scanPhotos0.photoIndex}_0_Step{errStep}.bmp", ImageFormat.Bmp); //OpenCvSharp.Extensions.BitmapConverter.ToBitmap(scanPhotos1.mat).Save($"{dirPath}{scanPhotos1.photoIndex}_1_Step{errStep}.bmp", ImageFormat.Bmp); } finally { Log($"图像处理", $"(图像{scanPhotos0.photoIndex})-进度计时:{time}", WarningEnum.Normal, false); scanPhotos0.mat.Dispose(); scanPhotos1.mat.Dispose(); scanPhotos0 = scanPhotos1 = null; //task = null; //System.GC.Collect(); } } #endregion #endif Thread.Sleep(20); } while (!isBreakProcessRun()); //暂停中断 //stopWatch.Stop(); pStopWatch.Stop(); _isRuning = false; } #if Online_One else { //防止内存爆满 AcquisitionMat tacq1 = _LinecamDev1.GetFrames(10); AcquisitionMat tacq2 = _LinecamDev2.GetFrames(10); } #endif Thread.Sleep(20); } catch (Exception e) { _isRuning = false; Log("运行报警", $"流程运行出错{errStep}:" + e.Message + "\n", WarningEnum.High); } } } #endregion #region 模型label private JArray _defectItemList; /// /// 获取模型对应标签信息 /// private void GetDefectAllLabel() { string labels = CurrProductModel.ModelName.Replace(".trt", ".json"); string configPath = confMgr.SysConfigParams.AIModelPath + $"\\{labels}"; string lsTmp = File.ReadAllText(configPath); JArray defectItemList = JArray.Parse(lsTmp); _defectItemList = defectItemList; //var item = defectItemList.FirstOrDefault(m => m.Value("id") == id); //if (item == null) // return null; //return (JObject)item; } public JArray GetDefectItemList() { return _defectItemList; } /// /// 根据id获取标签信息 /// /// /// public JObject GetDefectLabel(int id) { if (_defectItemList != null && _defectItemList.Count > 0) { var item = _defectItemList.FirstOrDefault(m => m.Value("id") == id); if (item == null) return null; return (JObject)item; } else return null; } /// /// 根据name获取标签信息 /// /// /// public JObject GetDefectLabel(string name) { if (_defectItemList != null && _defectItemList.Count > 0) { var item = _defectItemList.FirstOrDefault(m => m.Value("name") == name); if (item == null) return null; return (JObject)item; } else return null; } public JObject GetDefectLabel(string modelName, string name) { string labels = modelName.Replace(".trt", ".json"); string configPath = confMgr.SysConfigParams.AIModelPath + $"\\{labels}"; string lsTmp = File.ReadAllText(configPath); JArray defectItemList = JArray.Parse(lsTmp); if (defectItemList != null && defectItemList.Count > 0) { var item = defectItemList.FirstOrDefault(m => m.Value("name") == name); if (item == null) return null; return (JObject)item; } else return null; } public string GetDefectName(string code) { if (_defectItemList != null && _defectItemList.Count > 0) { var item = _defectItemList.FirstOrDefault(m => m.Value("code") == code); if (item == null) return null; return item.Value("name"); } else return null; } public string GetDefectName(string modelName, string code) { string labels = modelName.Replace(".trt", ".json"); string configPath = confMgr.SysConfigParams.AIModelPath + $"\\{labels}"; string lsTmp = File.ReadAllText(configPath); JArray defectItemList = JArray.Parse(lsTmp); if (defectItemList != null && defectItemList.Count > 0) { var item = defectItemList.FirstOrDefault(m => m.Value("code") == code); if (item == null) return null; return item.Value("name"); } else return null; } public Dictionary GetDefectCode() { Dictionary dic = new Dictionary(); for (int i = 0; i < _defectItemList.Count; i++) { var tt = _defectItemList[i]; dic.Add(tt.Value("id"), tt.Value("code")); } return dic; } #endregion #region 推理完成处理 private void callBackDefectEvent(DefectTask res) { { int step = 0; try { Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); string time = ""; //Log($"检测完成", $"图像队列:{res.record.ScannerPhotoFinishCount + 1}/{res.record.ScannerPhotoCount} (图像{res.photoIndex})检测结果:{res.isSucceed}", WarningEnum.Normal, false); //string dirPath = FileUtil.initFolder($"{Config.ImagePath}{res.record.BatchId}_{res.record.ReelId}\\"); //string dirSourcePath = FileUtil.initFolder($"{Config.ImagePath}{res.record.BatchId}_{res.record.ReelId}\\源图\\"); //Cv2.Flip(res.bmp, res.bmp, FlipMode.XY);//翻转 //显示打标图片 if (res.excelTable.Rows.Count > 0) OnAutoRuning(new RunEventArgs(res.bmpTag)); else OnAutoRuning(new RunEventArgs(res.bmp)); string dirPath = Util.CreateSubDir(confMgr.SysConfigParams.DefectSrcImag.SavePath, new List { $"{ res.record.BarCode}_{ res.record.BarCodeName}_{ImgDirSN}" }); if (confMgr.SysConfigParams.DefectSrcImag.AutoSave)//保存所有原图 { AddImageSave($"{dirPath}{res.photoIndex}.bmp", res.bmp); //OpenCvSharp.Extensions.BitmapConverter.ToBitmap(res.bmp).Save($"{dirPath}{res.photoIndex}.bmp", ImageFormat.Bmp); } if (res.isSucceed) { step = 1; Log($"检测完成", $"(图像{res.photoIndex})-瑕疵检测完成,共{res.excelTable.Rows.Count}个瑕疵!各环节用时:{string.Join(",", res.stopwatch)}", WarningEnum.Normal, false); //AddTextEvent(DateTime.Now,$"打标完成", $"第 ({res.photoIndex}) 张照片,计算过程:{res.resultInfo}"); //if (!Config.IsSaveAllImage && Config.IsSaveDefectSourceImage) // OpenCvSharp.Extensions.BitmapConverter.ToBitmap(res.bmp).Save($"{dirSourcePath}{res.photoIndex}.bmp", ImageFormat.Bmp); time += $"(图像{res.photoIndex})"; step = 2; if (res.excelTable.Rows.Count > 0) { res.record.dicPhoto_Defect[res.photoIndex] = true;//改为此图有瑕疵 step = 3; res.record.DefectTotalCount += res.excelTable.Rows.Count; if (res.record.DefectInfoList == null) res.record.DefectInfoList = new List(); step = 4; JObject defectNameInfo; DefectInfo defectInfo = null; List dataRowlist = new List(); long preTicks = pStopWatch.ElapsedMilliseconds;// DateTime.Now.Ticks; for (int i = 0; i < res.lstDefectBmp.Count; i++) { step = 5 + i * 10; defectNameInfo = GetDefectLabel(int.Parse(res.excelTable.Rows[i]["类别"].ToString())); if(defectNameInfo == null) { Log($"告警", $"模型标签为空,或未找到标签!", WarningEnum.High); continue; } defectInfo = new DefectInfo { PhotoIndex = res.photoIndex, Code = defectNameInfo.Value("code"), Name = defectNameInfo.Value("name"), X = double.Parse(res.excelTable.Rows[i]["X"].ToString()),//cm Y = Math.Round((res.photoIndex * res.bmp.Height * 1.0d / confMgr.SysConfigParams.Cm2px_y + double.Parse(res.excelTable.Rows[i]["Y"].ToString())), 2),//cm Width = double.Parse(res.excelTable.Rows[i]["W"].ToString()),//cm Height = double.Parse(res.excelTable.Rows[i]["H"].ToString()),//cm ZXD = double.Parse(res.excelTable.Rows[i]["置信度"].ToString()), Contrast = double.Parse(res.excelTable.Rows[i]["对比度"].ToString()), Target = int.Parse(res.excelTable.Rows[i]["目标"].ToString()), image = DefectPauseForUser? res.lstDefectBmp[i].Clone() : null, }; defectInfo.ModifyUserCode = defectInfo.CreateUserCode = res.record.CreateUserCode; step = 6 + i * 10; if (DefectPauseForUser && DefectItemsPuaseNameList.Contains(defectInfo.Name)) { lock (lock_defectPuase) { defectPuaseList.Add(defectInfo); } if (!defectPuaseImgList.ContainsKey(res.photoIndex)) defectPuaseImgList.TryAdd(res.photoIndex, res.bmpTag.Clone()); } res.record.DefectInfoList.Add(defectInfo); defectInfo.uid = preTicks++;// res.record.DefectInfoList.Count;//程序中的唯一索引,用于移除用索引 //AddTextEvent(DateTime.Now,$"打标完成", $"第{i}个缺陷:{ JsonConvert.SerializeObject(defectInfo)}; Y={res.photoIndex * res.bmp.Height * 1.0d / Config.cm2px_y}+{res.excelTable.Rows[i]["Y"].ToString()}"); step = 7 + i * 10; //保存打标小图 if (confMgr.SysConfigParams.DefectSmallImag.AutoSave) { dirPath = Util.CreateSubDir(confMgr.SysConfigParams.DefectSmallImag.SavePath, new List { $"{res.record.BarCode}_{res.record.BarCodeName}_{ImgDirSN}" }); string filename = $"{dirPath}{res.photoIndex}_X{defectInfo.X}_Y{defectInfo.Y}_W{defectInfo.Width}_H{defectInfo.Height}_目标{defectInfo.Target}_类别{defectInfo.Code}_置信度{defectInfo.ZXD}.jpg"; //OpenCvSharp.Extensions.BitmapConverter.ToBitmap(res.lstDefectBmp[i]).Save(filename, ImageFormat.Jpeg); AddImageSave(filename, res.lstDefectBmp[i]); defectInfo.TagFilePath = filename; } step = 8 + i * 10; res.lstDefectBmp[i].Dispose(); dataRowlist.Add(new object[]{ defectInfo.uid,defectInfo.Code, defectInfo.PhotoIndex,defectInfo.Name, defectInfo.CentreX, defectInfo.CentreY / 100,defectInfo.Width * 10,defectInfo.Height * 10, defectInfo.Area * 100, defectInfo.ZXD, defectInfo.Contrast}); step = 9 + i * 10; //告警判断???在此还是在收到新照片时触发??? if (res.record.ProductInfo.DefectAreaLimit > 0 && defectInfo.Area >= res.record.ProductInfo.DefectAreaLimit) { Log($"告警", $"瑕疵面积达到阈值!({defectInfo.Area}>={res.record.ProductInfo.DefectAreaLimit})", WarningEnum.High); } //必要瑕疵警告 if (res.record.ProductInfo.DefectAreaLimit > 0 ) { string[] warnlabels = res.record.ProductInfo.WarnDefect.Split(','); var haveLabel = warnlabels.ToList().Find(ft => ft == defectNameInfo.Value("name")); if (!string.IsNullOrEmpty(haveLabel)) { Log($"告警", $"出现必须报警瑕疵-{haveLabel}", WarningEnum.High); } } } time += $"->打标小图保存({stopWatch.ElapsedMilliseconds})"; //有瑕疵打标图必需保存 Jpeg dirPath = Util.CreateSubDir(confMgr.SysConfigParams.DefectSplicImag.SavePath, new List { $"{res.record.BarCode}_{res.record.BarCodeName}_{ImgDirSN}" }); if (confMgr.SysConfigParams.DefectSplicImag.AutoSave)//保存瑕疵图 { AddImageSave($"{dirPath}{res.photoIndex}_tag.jpg", res.bmpTag); AddImageSave($"{dirPath}{res.photoIndex}_src.bmp", res.bmp); //OpenCvSharp.Extensions.BitmapConverter.ToBitmap(res.bmpTag).Save($"{dirPath}{res.photoIndex}_tag.jpg", ImageFormat.Jpeg); //OpenCvSharp.Extensions.BitmapConverter.ToBitmap(res.bmp).Save($"{dirPath}{res.photoIndex}_src.bmp", ImageFormat.Bmp); } time += $"->瑕疵图保存({stopWatch.ElapsedMilliseconds})"; //Log($"检测完成", "更新UI", WarningEnum.Normal, false); //更新UI int bmpHeight = res.bmp.Height; //显示图片 //OnAutoRuning(new RunEventArgs(defectInfo.image)); //显示瑕疵图 OnAutoRuning(new RunEventArgs(dataRowlist)); step = 9; //Log($"检测完成", "保存CSV", WarningEnum.Normal, false); //保存CSV dirPath = Util.CreateSubDir(confMgr.SysConfigParams.DefectSplicImag.SavePath, new List { $"{res.record.BarCode}_{res.record.BarCodeName}_{ImgDirSN}" }); //bool b = Utils.ExcelUtil.DataTable2CSV($"{dirPath}{res.photoIndex}.csv", res.excelTable); AddImageSave($"{dirPath}{res.photoIndex}.csv", res.excelTable); //AddTextEvent(DateTime.Now,$"打标完成", $"{res.tag}.xlsx {(b ? "保存成功!" : "保存失败!")}"); step = 10; time += $"->CSV保存({stopWatch.ElapsedMilliseconds})"; //每百米告警判断???在此还是在收到新照片时触发??? if (res.record.ProductInfo.DefectCountLimit > 0 && res.record.DefectTotalCount >= res.record.ProductInfo.DefectCountLimit) { //每x米判定所有缺陷数量 if (res.record.ProductInfo.DefectCntLength > 0) { int compLen = (int)res.record.ProductInfo.DefectCntLength * 100;//每百米 to cm int compCount = compLen * confMgr.SysConfigParams.Cm2px_y / res.bmp.Height; //从上次告警后重新开始计算长度及数量 int defectCount = res.record.DefectInfoList.Where(m => m.PhotoIndex >= res.record.preWarningPhotoIndex && m.PhotoIndex >= res.photoIndex + 1 - compCount).Count(); if (defectCount >= res.record.ProductInfo.DefectCountLimit) { res.record.preWarningPhotoIndex = res.photoIndex + 1; Log($"告警", $"每{Math.Round(res.record.ProductInfo.DefectCntLength, 2)}米瑕疵数量达到阈值!({defectCount}>={res.record.ProductInfo.DefectCountLimit})", WarningEnum.High); } } step = 11; //按缺陷计算没X米多少缺陷报警 for (int i = 0; i < res.record.ProductInfo.QualifiedLimitList.Count; i++) { var defectWarn = res.record.ProductInfo.QualifiedLimitList[i]; if (defectWarn.DefectWarnLength > 0 || defectWarn.DefectWarnCnt > 0) { step = 12; int warnLen = defectWarn.DefectWarnLength * 100;//每百米 to cm int warnCount = warnLen * confMgr.SysConfigParams.Cm2px_y / res.bmp.Height; //从上次告警后重新开始计算长度及数量 int warnDefectCount = res.record.DefectInfoList.Where(m => m.PhotoIndex >= res.record.preWarningPhotoIndexByLabel[i] && m.PhotoIndex >= res.photoIndex + 1 - warnCount).Count(); if (warnDefectCount >= defectWarn.DefectWarnCnt) { res.record.preWarningPhotoIndexByLabel[i] = res.photoIndex + 1; Log($"告警", $"每{defectWarn.DefectWarnLength}米{GetDefectName(defectWarn.Code)}瑕疵数量达到阈值!({warnDefectCount}>={defectWarn.DefectWarnCnt})", WarningEnum.High); } } } } time += $"->告警判定({stopWatch.ElapsedMilliseconds})"; } } else { Log($"打标失败", $"(图像{res.photoIndex})-瑕疵检测失败", WarningEnum.Low); } time += $"->完成处理({stopWatch.ElapsedMilliseconds})"; Log($"检测完成2", time, WarningEnum.Normal, false); res.bmp.Dispose(); res.bmpTag.Dispose(); res.bmps_cut = null; res.excelTable.Dispose(); } catch (Exception ex) { Log($"打标失败", $"(图像{res.photoIndex})-瑕疵检测异常({step}):{ex.Message}"); } finally { res.record.ScannerPhotoFinishCount++; int liScannerPhotoFinishCount = res.record.ScannerPhotoFinishCount; int liScannerPhotoCount = res.record.ScannerPhotoCount; //Log($"检测完成", $"{liScannerPhotoFinishCount}/{liScannerPhotoCount}", WarningEnum.Normal, false); //this.BeginInvoke(new System.Action(() => //{ // this.lblWaitImageCount.Text = $"{liScannerPhotoCount - liScannerPhotoFinishCount}"; //})); res = null; System.GC.Collect(); } } } #endregion #region 图片存储 private class SaveImgInfo { public string Path { get; set; } public Mat Img { get; set; } public DataTable DataTable { get; set; } } private ConcurrentQueue _SaveImgQueue = new ConcurrentQueue(); private void AddImageSave(string path, Mat img) { _SaveImgQueue.Enqueue(new SaveImgInfo { Path = path, Img = img.Clone() }); //Thread.Sleep(10); if (_SaveImgQueue.Count > 100) { SaveImgInfo saveImgInfo; _SaveImgQueue.TryDequeue(out saveImgInfo); if (saveImgInfo.Img != null) { saveImgInfo.Img.Dispose(); saveImgInfo.Img = null; System.GC.Collect(); } Log("存图警告", "存图超限-出队", WarningEnum.Normal); } } private void AddImageSave(string path, DataTable dt) { _SaveImgQueue.Enqueue(new SaveImgInfo { Path = path, Img = null, DataTable = dt.Clone() }); } /// /// 图片存储 /// private void SaveImgThreadFunction() { while (true) { try { if (_cts.IsCancellationRequested) break; SaveImgInfo saveImgInfo; if (_SaveImgQueue.TryDequeue(out saveImgInfo)) { if(saveImgInfo.Img != null) { saveImgInfo.Img.ImWrite(saveImgInfo.Path); //OpenCvSharp.Extensions.BitmapConverter.ToBitmap(res.lstDefectBmp[i]).Save(filename, ImageFormat.Jpeg); saveImgInfo.Img.Dispose(); saveImgInfo.Img = null; } else { if(!string.IsNullOrEmpty(saveImgInfo.Path)) { bool b = Utils.ExcelUtil.DataTable2CSV(saveImgInfo.Path, saveImgInfo.DataTable); } } //System.GC.Collect(); } #region 堆积显示 OnAutoRuning(new RunEventArgs(0, 0, _SaveImgQueue.Count)); //处理多余 if(_SaveImgQueue.Count > 100) { _SaveImgQueue.TryDequeue(out saveImgInfo); if (saveImgInfo.Img != null) { saveImgInfo.Img.Dispose(); saveImgInfo.Img = null; System.GC.Collect(); } Log("存图警告", "存图超限-紧急出队", WarningEnum.Normal); } #endregion Thread.Sleep(20); } catch (Exception e) { Log("存图报警", "存图出错:" + e.Message + "\n", WarningEnum.High); } } } #endregion #region 结束验布 public bool Stop() { Log("暂停", "暂停流程"); if (confMgr.SysConfigParams.OpenPLC) { if (plcDev.IsInit()) { plcDev.WriteCoil("DB3.DBX0.0", false);//zt plcDev.WriteCoil("DB3.DBX0.1", true); } else { Log("运行", "PLC连接异常!", WarningEnum.High); return false; } } else if (confMgr.SysConfigParams.OpenIO) { ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.停止), GetIOBitIndex((int)DOName.停止), true); Task.Run(async () => { await Task.Delay(500); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.停止), GetIOBitIndex((int)DOName.停止), false); }); } return true; } public bool DefectEnd(Form fatherFrm) { Log("结束验布", "结束验布!"); Stop(); Thread.Sleep(500); //CurrentState = CurrentStateEnum.初始; _isRuning = false; _isAuto = false; OnAutoRuning(new RunEventArgs(_isAuto)); Thread.Sleep(500); pStopWatch.Stop(); pRunSpeedWatch.Stop(); if (currKey > 0 ) { if (FrmDialog.ShowDialog(fatherFrm, $"是否保存当前检测结果?", "提示", true) == DialogResult.OK) { //Log("验布数据", "等待处理完成!"); //while(_matList1.Count >0 && _matList2.Count>0) // Thread.Sleep(200); //Log("验布数据", "处理完成!"); string szBatchId, szReelId; double ldErpLen; //szBatchId = ; //szReelId = txtReelId.Text.Trim(); //ldErpLen = numErpLen.IsEmpty ? 0 : Convert.ToDouble(numErpLen.Text.Trim()); int myKey = currKey; //Task.Run(() => { saveCurrRecord(myKey); }); saveCurrRecord(myKey); //resetUIValue(); currKey = 0; //this.btnStart.Enabled = true; //this.btnEnd.Enabled = this.btnPause.Enabled = false;//这里有问题,应该是devPlc回调设置 } else { Log("结束验布", "不保存数据结束验布!"); return false; } } else { Log("结束验布", "无数据结束验布!"); _isDefect = false; return false; } return true; } #endregion #region IO解析 private int GetIOPortIndex(int DIDOEnum) { return DIDOEnum / 8; } private int GetIOBitIndex(int DIDOEnum) { return DIDOEnum % 8; } #endregion #region 三色灯 /// /// 三色灯初始化 /// public void LedReady() { if (confMgr.SysConfigParams.OpenIO && _isOpenIO) { ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯绿灯), GetIOBitIndex((int)DOName.三色灯绿灯), false); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯黄灯), GetIOBitIndex((int)DOName.三色灯黄灯), false); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯红灯), GetIOBitIndex((int)DOName.三色灯红灯), false); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯蜂鸣器), GetIOBitIndex((int)DOName.三色灯蜂鸣器), false); } } /// /// 运行状态三色灯 /// public void LedRun() { if (confMgr.SysConfigParams.OpenIO && _isOpenIO) { ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯绿灯), GetIOBitIndex((int)DOName.三色灯绿灯), true); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯黄灯), GetIOBitIndex((int)DOName.三色灯黄灯), false); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯红灯), GetIOBitIndex((int)DOName.三色灯红灯), false); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯蜂鸣器), GetIOBitIndex((int)DOName.三色灯蜂鸣器), false); } } /// /// 暂停状态三色灯 /// public void LedPause() { if (confMgr.SysConfigParams.OpenIO && _isOpenIO) { ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯绿灯), GetIOBitIndex((int)DOName.三色灯绿灯), false); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯黄灯), GetIOBitIndex((int)DOName.三色灯黄灯), false); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯红灯), GetIOBitIndex((int)DOName.三色灯红灯), true); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯蜂鸣器), GetIOBitIndex((int)DOName.三色灯蜂鸣器), false); } } //控制黄灯闪烁 private bool Blink; private int BlinkCnt = 0; //控制蜂鸣间隔 private int BuzzCnt = 0; /// /// 回原状态三色灯 /// /// public void LedRset(bool val) { if (confMgr.SysConfigParams.OpenIO && _isOpenIO) { ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯绿灯), GetIOBitIndex((int)DOName.三色灯绿灯), false); //ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯黄灯), GetIOBitIndex((int)DOName.三色灯黄灯), val); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯红灯), GetIOBitIndex((int)DOName.三色灯红灯), false); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯蜂鸣器), GetIOBitIndex((int)DOName.三色灯蜂鸣器), false); Blink = val; BlinkCnt = 0; } } #endregion #region 报警等、按钮IO /// /// 三色灯报警状态显示 /// /// public bool WarningShowLed(bool DisEnableBuzz) { bool sts = false; if (statusMgr.Warning == WarningEnum.Normal) { if (confMgr.SysConfigParams.OpenIO && _isOpenIO) { if (statusMgr.Status == SystemStsEnum.Auto) ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯绿灯), GetIOBitIndex((int)DOName.三色灯绿灯), true); else ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯绿灯), GetIOBitIndex((int)DOName.三色灯绿灯), false); if (statusMgr.Status == SystemStsEnum.Pause) ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯红灯), GetIOBitIndex((int)DOName.三色灯红灯), true); else ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯红灯), GetIOBitIndex((int)DOName.三色灯红灯), false); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯蜂鸣器), GetIOBitIndex((int)DOName.三色灯蜂鸣器), false); BuzzCnt = 0; //判断黄灯是否需要闪烁,调用200ms一次 if (Blink) { if (BlinkCnt < 3) ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯黄灯), GetIOBitIndex((int)DOName.三色灯黄灯), true); else if (BlinkCnt < 6) ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯黄灯), GetIOBitIndex((int)DOName.三色灯黄灯), false); BlinkCnt++; if (BlinkCnt == 6) BlinkCnt = 0; } else ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯黄灯), GetIOBitIndex((int)DOName.三色灯黄灯), false); } } if (statusMgr.Warning == WarningEnum.Low) { //ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.启动按钮绿灯), GetIOBitIndex((int)DOName.启动按钮绿灯), false); //ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.停止按钮红灯), GetIOBitIndex((int)DOName.停止按钮红灯), true); if (confMgr.SysConfigParams.OpenIO && _isOpenIO) ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯红灯), GetIOBitIndex((int)DOName.三色灯红灯), true); //ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯绿灯), GetIOBitIndex((int)DOName.三色灯绿灯), false); //sts = true; } if (statusMgr.Warning == WarningEnum.High) { //发送停止信号 Stop(); sts = true; if (confMgr.SysConfigParams.OpenIO && _isOpenIO) { ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯红灯), GetIOBitIndex((int)DOName.三色灯红灯), true); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯绿灯), GetIOBitIndex((int)DOName.三色灯绿灯), false); if (confMgr.SysConfigParams.OpenBuzzer && !DisEnableBuzz) { if (BuzzCnt < 3) ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯蜂鸣器), GetIOBitIndex((int)DOName.三色灯蜂鸣器), true); else if (BuzzCnt < 6) ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.三色灯蜂鸣器), GetIOBitIndex((int)DOName.三色灯蜂鸣器), false); BuzzCnt++; if (BuzzCnt == 6) BuzzCnt = 0; } } } return sts; } /// /// 按钮IO检测 /// /// public int ButtonIOTrg(bool DisEnableDoorAlm) { int ret = 0; bool sts; if (confMgr.SysConfigParams.OpenIO && _isOpenIO) { //if (confMgr.SysConfigParams.OpenDoor && !DisEnableDoorAlm) //{ // ioCardDev.GetDIBitState(GetIOPortIndex((int)DIName.门磁), GetIOBitIndex((int)DIName.门磁), out sts); // if ((!sts) && (statusMgr.Status == SystemStsEnum.Auto)) // { // Log("暂停", "门磁报警-门打开", WarningEnum.Low); // statusMgr.GotoPause(); // SendStatus(); // ret = 2; // } //} ioCardDev.GetDIBitState(GetIOPortIndex((int)DIName.暂停按钮), GetIOBitIndex((int)DIName.暂停按钮), out sts); if (sts) { Log("暂停", "手动暂停"); statusMgr.GotoPause(); SendStatus(); ret = 2; } ioCardDev.GetDIBitState(GetIOPortIndex((int)DIName.复位按钮), GetIOBitIndex((int)DIName.复位按钮), out sts); if (sts) { ret = 3; } ioCardDev.GetDIBitState(GetIOPortIndex((int)DIName.启动按钮), GetIOBitIndex((int)DIName.启动按钮), out sts); if (sts) ret = 1; } return ret; } #endregion #region 系统关闭 /// /// 关闭 /// public void Close() { _isInit = false; _isRuning = false; if (null != _cts) { _cts.Cancel(); } if (null != _mainThread) { _mainThread.Join(1000); } if (null != _Cam1Thread) { _Cam1Thread.Join(1000); } if (null != _Cam2Thread) { _Cam2Thread.Join(1000); } if (null != _SpeedThread) { _SpeedThread.Join(1000); } if (null != _SaveImgThread) { _SaveImgThread.Join(1000); } //关闭计米器 if (confMgr.SysConfigParams.OpenLengthCount) lengthCounter.Close(); //关闭相机 LineCamDev1.CloseCamera(); LineCamDev2.CloseCamera(); //关闭光源 for (int i = 0; i < LightChCount; i++) { lightDev.CloseLight(i + 1); } lightDev.CloseDev(); //关闭io if (confMgr.SysConfigParams.OpenIO) { ioCardDev.ResetAllDO(); ioCardDev.CloseBoard(); } if (confMgr.SysConfigParams.OpenPLC) plcDev.CloseDev(); } #endregion #region 日志报警 /// /// 初始化记录报警和显示 /// /// 标头 /// 内容 /// 报警状态 private void InitLog(string msg, string tag = "初始化", WarningEnum warning = WarningEnum.Normal) { OnInitRuning(new InitEventArgs(msg)); statusMgr.GotoWarning(warning, tag, msg); } /// /// 记录报警和显示 /// /// 标头 /// 内容 /// 报警状态 private void Log(string tag, string msg, WarningEnum warning = WarningEnum.Normal, bool show = true) { if(show) OnMainRuning(new MainEventArgs(tag, msg, warning)); statusMgr.GotoWarning(warning, tag, msg); //开启云端 if ((init_Cloud) && (warning != WarningEnum.Normal)) { //上传报警状态和信息 string statusStr = warning == WarningEnum.Normal ? "正常" : warning == WarningEnum.Low ? "警告" : "系统报警"; cloudMgr.SendTopic("device/attributes", $"{{\"status\": \"{statusStr}\", \"alm\": \"{tag}-{msg}\", " + $"\"name\": \"{confMgr.SysConfigParams.CloudThisName}\", \"DailyOutput\": \"{DailyOutput}\"}}"); } } #endregion #region 硬件 #region 硬件型号配置 public LightDevNameEnum SysUseLight = LightDevNameEnum.CST; #endregion #region 硬件字段 private CamDev _LinecamDev1; /// /// io控制卡 /// public CamDev LineCamDev1 { get { return _LinecamDev1; } } private CamDev _LinecamDev2; /// /// io控制卡 /// public CamDev LineCamDev2 { get { return _LinecamDev2; } } private IOCardDev ioCardDev; /// /// io控制卡 /// public IOCardDev IOCardDev { get { return ioCardDev; } } private LightDev lightDev; /// /// 光源控制 /// public LightDev LightDev { get { return lightDev; } } public string LightName { get { return "Light"; } } public int LightChCount = 1;//美尚4通道,其他6通道 /// /// 计米器 /// public SerialPort LengthCounterPort { get { return lengthCounter; } } private SerialPort lengthCounter; private PlcDev plcDev; /// /// PLC控制 /// public PlcDev PlcDev { get { return plcDev; } } private PrintControl printControl; /// /// 打印机模块 /// public PrintControl PrintControl { get { return printControl; } } #endregion #region 初始化和基本电机IO操作 private void InitDev() { ioCardDev = new AdvantechIO(); //CpuType.S71200 = 30 plcDev = new PlcDev(CpuType.S71200, confMgr.SysConfigParams.PLC_IP, (short)confMgr.SysConfigParams.PLC_Rack, (short)confMgr.SysConfigParams.PLC_Solt); _LinecamDev1 = new CamDev(CameraEnumType.IKap, confMgr.SysConfigParams.CamIndex_1.ToString(), confMgr.SysConfigParams.CamPath_1, confMgr.SysConfigParams.CamDev_1); _LinecamDev2 = new CamDev(CameraEnumType.IKap, confMgr.SysConfigParams.CamIndex_2.ToString(), confMgr.SysConfigParams.CamPath_2, confMgr.SysConfigParams.CamDev_2); if (SysUseLight == LightDevNameEnum.CST) lightDev = new CSTLight(LightName, LightChCount); else lightDev = new RseeLight(SysUseLight, LightName); lengthCounter = new SerialPort(confMgr.SysConfigParams.LengthCounterCom, confMgr.SysConfigParams.LengthCounterComBaud); } /// /// 初始化硬件 /// /// private bool InitAllDev() { bool ret = false; InitDev(); #if Online //打印机模块初始化 //InitLog("打印机模块初始化..."); //try //{ // printControl = new PrintControl(); // InitLog("初始化打印机模块成功!"); //} //catch (Exception ex) //{ // printControl = null; // InitLog($"初始化打印机模块失败! {ex.Message}"); //} if (confMgr.SysConfigParams.OpenLengthCount) { lengthCounter.Open(); if (!lengthCounter.IsOpen) { InitLog("计米器初始化失败!", "初始化", WarningEnum.High); return ret; } InitLog("计米器初始化成功!"); } if (confMgr.SysConfigParams.OpenPLC) { //PLC初始化 InitLog("PLC连接初始化..."); if(!plcDev.OpenDev()) { InitLog("PLC连接初始化失败!", "初始化", WarningEnum.High); return ret; } InitLog("PLC连接成功!"); } _isOpenIO = false; if (confMgr.SysConfigParams.OpenIO) { //IO初始化 InitLog("IO板卡初始化..."); if (ioCardDev.InitBoard(MaiMuControl.Device.IOBordType.Advantech) < 0) { InitLog("IO板卡初始化失败!", "初始化", WarningEnum.High); return ret; } if (ioCardDev.OpenBoard(confMgr.SysConfigParams.IODevName, confMgr.SysConfigParams.IOCfgPath) < 0) { InitLog("打开IO板卡失败!", "初始化", WarningEnum.High); return ret; } if (ioCardDev.ResetAllDO() < 0) { InitLog("IO Reset失败!", "初始化", WarningEnum.High); return ret; } _isOpenIO = true; InitLog("初始化IO板卡成功!"); } //相机初始化 if(!_LinecamDev1.InitCamera()) { InitLog("相机1初始化失败!", "初始化", WarningEnum.High); return ret; } if(!_LinecamDev1.OpenCamera()) { InitLog("相机1打开失败!", "初始化", WarningEnum.High); return ret; } _LinecamDev1.IsMirrorX = confMgr.SysConfigParams.Cam1_flipX; _LinecamDev1.IsMirrorY = confMgr.SysConfigParams.Cam1_flipY; _LinecamDev1.WarningEvent = (level, msg) => { Log($"相机1设备事件", msg, level); }; if (!_LinecamDev2.InitCamera()) { InitLog("相机2初始化失败!", "初始化", WarningEnum.High); return ret; } if (!_LinecamDev2.OpenCamera()) { InitLog("相机2打开失败!", "初始化", WarningEnum.High); return ret; } _LinecamDev2.IsMirrorX = confMgr.SysConfigParams.Cam2_flipX; _LinecamDev2.IsMirrorY = confMgr.SysConfigParams.Cam2_flipY; _LinecamDev2.WarningEvent = (level, msg) => { Log($"相机2设备事件", msg, level); }; //光源初始化 InitLog("光源控制器初始化..."); int com_num = int.Parse(confMgr.SysConfigParams.LightCom.Remove(0, 3)); if (lightDev.InitDev(com_num, confMgr.SysConfigParams.LightComBaud) < 0) { InitLog("光源控制器初始化失败!", "初始化", WarningEnum.High); return ret; } InitLog("初始化光源控制器成功!"); //关闭光源 for (int i = 0; i < LightChCount; i++) { lightDev.CloseLight(i + 1); } #endif ret = true; return ret; } /// /// 读取硬件配置 /// private bool LoadDevConfig() { LightParams lightParams = new LightParams(LightName, LightChCount); //LightChCount = 6; //lightParams.DevName = LightName; lightParams.ComName = confMgr.SysConfigParams.LightCom; lightParams.Buad = confMgr.SysConfigParams.LightComBaud; //lightParams.ChCount = LightChCount; lightDev.WriteCfgInfo(confMgr.DevConfigPath, lightParams); return true; } #endregion #endregion #region 硬盘检测 public static bool CheckDisk(IWin32Window owner, int max = 10) { if (!string.IsNullOrEmpty(ConfMgr.Instance.SysConfigParams.DefectSrcImag.SavePath)) { string path = ConfMgr.Instance.SysConfigParams.DefectSrcImag.SavePath; string volume = path.Substring(0, path.IndexOf(':')); long freespace = DiskAPI.GetHardDiskSpace(volume); if (freespace < max) { string tip = $"当前{volume}硬盘容量:{freespace}GB,小于{max}GB。注意清理!!"; FrmDialog.ShowDialog(owner, tip, "警告", true); return false; } } return true; } #endregion #region 初始化事件 /// /// 初始化回调 /// /// /// public delegate void InitEventHandler(Object sender, InitEventArgs e); public event InitEventHandler InitRuning; protected virtual void OnInitRuning(InitEventArgs e) { if (null != InitRuning) { InitRuning(this, e); } } #endregion #region 主窗体显示事件 /// /// 主窗体回调 /// /// /// public delegate void MainEventHandler(Object sender, MainEventArgs e); public event MainEventHandler MainRuning; protected virtual void OnMainRuning(MainEventArgs e) { if (null != MainRuning) { MainRuning(this, e); } } /// /// 流程回调 /// /// /// public delegate void RunEventHandler(Object sender, RunEventArgs e); public event RunEventHandler AutoRuning; protected virtual void OnAutoRuning(RunEventArgs e) { if (null != AutoRuning) { AutoRuning(this, e); } } #endregion #region 加载测试列表 /// /// 加载产品列表 /// public void LoadProductCodeList() { try { productCodeList = PdtService.GetListNav().Select(m => m.Name).ToList(); productIdList = PdtService.GetListNav().Select(m => m.Id).ToList(); } catch (Exception ex) { OnMainRuning(new MainEventArgs("启动", "加载检测标准失败:" + ex.Message, WarningEnum.High)); statusMgr.GotoWarning(WarningEnum.High, "启动", "加载检测标准失败:" + ex.Message); SendStatus(); } } #endregion #region 界面显示 public static void showRowNum_onDataGrid_RowPostPaint(DataGridView dgv, object sender, DataGridViewRowPostPaintEventArgs e) { Rectangle rectangle = new Rectangle(e.RowBounds.Location.X, e.RowBounds.Location.Y, dgv.RowHeadersWidth - 4, e.RowBounds.Height); TextRenderer.DrawText(e.Graphics, (e.RowIndex + 1).ToString(), dgv.RowHeadersDefaultCellStyle.Font, rectangle, dgv.RowHeadersDefaultCellStyle.ForeColor, TextFormatFlags.VerticalCenter | TextFormatFlags.Right); } /// /// IO二进制数据格式化到8位 XXXX10X0 /// /// /// public static string[] IODataFormatBinaryStr(string[] datas, bool clone, char defaultPadChar = 'X') { string[] datas2 = new string[datas.Length]; for (int i = 0; i < datas.Length; i++) { if (clone) { datas2[i] = datas[i].Replace(" ", ""); if (datas2[i].Length > 8) datas2[i] = datas2[i].Substring(datas2[i].Length - 8); else if (datas2[i].Length < 8) datas2[i] = datas2[i].PadLeft(8, defaultPadChar); } else { datas[i] = datas[i].Replace(" ", ""); if (datas[i].Length > 8) datas[i] = datas[i].Substring(datas[i].Length - 8); else if (datas[i].Length < 8) datas[i] = datas[i].PadLeft(8, defaultPadChar); datas2 = datas; } } return datas2; } /// /// /// /// [XXHL XXXX,XXXX XXXX,...] /// [byte,byte,byte,byte] /// public static bool compareIOInput(string[] op_show_list, byte[] currIoDatas) { int isok = 0;//1-true 2-false string IN_OP_SHOW; for (int i = 0; i < currIoDatas.Length; i++) { IN_OP_SHOW = op_show_list[i].Replace(" ", "").PadLeft(8, 'X'); if (IN_OP_SHOW.IndexOf('H') < 0 && IN_OP_SHOW.IndexOf('L') < 0) continue; for (int j = 7; j >= 0; j--) { byte bit = (byte)(1 << 7 - j); if (IN_OP_SHOW[j] == 'H') { if ((bit & currIoDatas[i]) > 0) isok = 1; else { isok = 2; break; } } else if (IN_OP_SHOW[j] == 'L') { if ((currIoDatas[i] ^ (currIoDatas[i] | bit)) > 0) isok = 1; else { isok = 2; break; } } } //已经不符 if (isok == 2) break; } // return isok == 1; } #endregion #region 启动 public bool StartMotion() { if (confMgr.SysConfigParams.OpenAutoRun) { Log("启动", "下发启动指令..."); if (confMgr.SysConfigParams.OpenPLC) { if (plcDev.IsInit()) { plcDev.WriteCoil("DB3.DBX0.1", false); plcDev.WriteCoil("DB3.DBX0.0", true);//启动 } else { Log("运行", "PLC连接异常!", WarningEnum.High); return false; } } else if (confMgr.SysConfigParams.OpenIO) { bool sts; ioCardDev.GetDIBitState(GetIOPortIndex((int)DIName.暂停按钮), GetIOBitIndex((int)DIName.暂停按钮), out sts); if (!sts) { ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.启动), GetIOBitIndex((int)DOName.启动), true); Task.Run(async () => { await Task.Delay(500); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.启动), GetIOBitIndex((int)DOName.启动), false); }); } else Log("运行", "存在暂停信号!", WarningEnum.Low); } } else Log("启动", "检测流程开启..."); return true; } /// /// 加载ERP数据库数据 /// /// /// private DataRow loadErpData(string barCode) { var paramList = new List() { new SugarParameter("@code", barCode) }; Stopwatch stopwatch = Stopwatch.StartNew(); var data = confMgr.execSql(confMgr.SysConfigParams.ErpSql, paramList); if (data != null && data.Rows.Count < 1) { Log("Erp查询结果", $"{barCode}: 时长={stopwatch.ElapsedMilliseconds}ms, 无数据!", WarningEnum.Normal); return null; } Log("Erp查询结果", $"{barCode}: 时长={stopwatch.ElapsedMilliseconds}ms, {JsonConvert.SerializeObject(data.Rows[0])}", WarningEnum.Normal); return data.Rows[0]; } /// /// 自动运行 /// /// public bool StartRun(Form fatherFrm, string sn, ref string DefectName, ref string name, ref string batch, ref string reel, ref string cnt) { bool ret = true; _FatherFrm = fatherFrm; string barCodeName = "", len = "0", batchId = "", reelId = ""; if (string.IsNullOrEmpty(name)) name = "未找到"; //运行前清除过期图片文件 //DateTime st = DateTime.Now; DelectPictureFile(); if ((statusMgr.Status != SystemStsEnum.Standby) && (statusMgr.Status != SystemStsEnum.Pause)) { Log("运行", "系统非可运行状态", WarningEnum.Low); return false; } if (string.IsNullOrEmpty(sn)) { Log("运行", "产品条码为空!", WarningEnum.Low); return false; } //禾欣数据 sn,长度数量, 批号, 卷号 if (!string.IsNullOrWhiteSpace(confMgr.SysConfigParams.ErpDBConStr) && !string.IsNullOrWhiteSpace(confMgr.SysConfigParams.ErpSql) && !string.IsNullOrWhiteSpace(sn)) { Log("扫码", $"产品条码({sn})到ERP查询对应数据...", WarningEnum.Normal); var rowData = this.loadErpData(sn); if (rowData == null) { Log("扫码", $"产品条码({sn})无对应ERP数据,不做响应!", WarningEnum.High); return false; } barCodeName = rowData[0].ToString(); if (rowData.ItemArray.Length > 1) len = rowData[1].ToString(); if (rowData.ItemArray.Length > 2) batchId = rowData[2].ToString(); if (rowData.ItemArray.Length > 3) reelId = rowData[3].ToString(); } else barCodeName = sn;//没有ERP对应关系时直接使用条码做为品名 //Log("调试", $"time1:{(DateTime.Now - st).Milliseconds}"); //st = DateTime.Now; int errStep = 0; try { Product model; //禾欣客户使用 if (confMgr.SysConfigParams.CustomerName == "hexin") { errStep = 1; //SHNY-PX-6-*** string[] codes = barCodeName.Split(new char[] { '-' }); if (codes.Length < 4) { Log("扫码", $"产品品名({barCodeName})格式错误,不做响应!", WarningEnum.Low); return false; } errStep = 2; //加载新产品 string pcode = "1-" + codes[2]; if (codes[1] == "0" || confMgr.SysConfigParams.SuedeList.Contains(codes[1])) pcode = "0-" + codes[2]; model = PdtService.GetModelNav(pcode); //frmProduct.loadProduct(code); name = barCodeName; batch = batchId; reel = reelId; cnt = len; } else if (confMgr.SysConfigParams.CustomerName == "boyi" && confMgr.SysConfigParams.OpenMes) { //博艺mes功能 string mjson = GetMesJson(sn); string mfid = ""; string httprel = ""; if (MesReturn(mjson, out mfid, out httprel)) { Log("Mes", $"Mes反馈({httprel})", WarningEnum.Normal, false); JObject Param = JObject.Parse(httprel); name = Param["List"][0]["Mname"].ToString(); barCodeName = name; len = Param["List"][0]["PQty"].ToString(); batchId = Param["List"][0]["FID"].ToString(); reelId = Param["List"][0]["RawMFid"].ToString(); //根据mfid查询检测标准 model = PdtService.GetModelNavByMFID(mfid); if (model == null) { Log("Mes", $"MFID({mfid})对应配方不存在,请先添加产品配方设置!", WarningEnum.Normal); //弹窗添加配方 MesSelectPdt mesSelectPdt = new MesSelectPdt(mfid); if (mesSelectPdt.ShowDialog() == DialogResult.OK) { model = PdtService.GetModelNavByMFID(mfid); Log("Mes", $"MFID({mfid})绑定配方({model.Name})成功!", WarningEnum.Normal); } else { Log("Mes", $"取消绑定,流程结束!", WarningEnum.High); return false; } } double t; cnt = double.TryParse(cnt, out t) ? cnt : "0"; } else { Log("Mes", $"Mes通信失败!{confMgr.SysConfigParams.MesServerAddr}-{mjson}", WarningEnum.High); return false; } } else { if (string.IsNullOrEmpty(name) || name == "未找到") { Log("运行", "产品品名为空!", WarningEnum.Low); return false; } if (string.IsNullOrEmpty(DefectName)) { Log("运行", "检测标准未选择!", WarningEnum.Low); return false; } //if (string.IsNullOrEmpty(batch)) //{ // Log("运行", "批号为空!", WarningEnum.Low); // return false; //} //if (string.IsNullOrEmpty(reel)) //{ // Log("运行", "卷号为空!", WarningEnum.Low); // return false; //} barCodeName = name; batchId = batch; reelId = reel; double t; cnt = double.TryParse(cnt, out t) ? cnt : "0"; model = PdtService.GetModelNavByName(DefectName); } if (model == null) { Log("扫码", $"编码({sn})对应配方不存在,请先添加产品配方设置,暂停设备!", WarningEnum.High); return false; } DefectName = model.Name; //数据处理 Records record; lock (lockCurrKey) { errStep = 3; //保存,这里队列图片可能还未检测完 if (currKey > 0) { string szBatchId, szReelId; double ldErpLen; szBatchId = batch; szReelId = reel; ldErpLen = Convert.ToDouble(cnt); //BatchId = code,//code[2] //ReelId = "1",//code[3] int mykey = currKey; //Task.Run(() => { saveCurrRecord(mykey); }); saveCurrRecord(mykey); currKey = 0; } errStep = 4; var now = DateTime.Now; currKey = now.Hour * 10000 + now.Minute * 100 + now.Second; //var materialItem = codes[0]+"-"+ codes[1]; //var colorItem = Config.getColorItem(int.Parse(codes[2])); record = new Records() { currKey = currKey, ProductId = model.Id, ProductInfo = model,//后面计算合格时用 Color = model.ColorName, Material = model.Material,//codes[0] + "-" + codes[1],// (materialItem == null ? "未知" : materialItem["name"].ToString()), BarCode = sn, BarCodeName = barCodeName, ErpLen = double.Parse(len), BatchId = batchId, ReelId = reelId, ModifyUserCode = userMgr.LoginUser.Code, CreateUserCode = userMgr.LoginUser.Code, }; htTask.Add(currKey, record); } errStep = 8; // errStep = 9; Log("扫码", $"品名({barCodeName}),条码({sn}),加载产品信息({model.Code})完成,加载配方(光源={model.LightValue},曝光={model.ExposureTime},增益={model.Gain})..."); errStep = 10; #if Online if (model.LightValue > 0)//光源 - 通道0 lightDev.SetLightDigitalValue(1, model.LightValue); errStep = 11; if (model.ExposureTime > 0 || model.Gain > 0)//相机曝光 增益 { LineCamDev1.SetExposure((float)(model.ExposureTime > 0 ? model.ExposureTime : 0)); LineCamDev1.SetGain((float)(model.Gain > 0 ? model.Gain : 0)); LineCamDev2.SetExposure((float)(model.ExposureTime > 0 ? model.ExposureTime : 0)); LineCamDev2.SetGain((float)(model.Gain > 0 ? model.Gain : 0)); } #endif errStep = 15; Log("扫码", $"品名({barCodeName}),配方设置完成:光源={model.LightValue},曝光={model.ExposureTime}"); //显示二次判定项和打标项 OnAutoRuning(new RunEventArgs(model)); //加载所有二次判断 //显示光源亮度 OnAutoRuning(new RunEventArgs(model.LightValue)); //计米器清空 #if Online ClearLengthCount(); #endif JmFtStart = false; Thread.Sleep(1000); //启动ai寻边模型 if (confMgr.SysConfigParams.OpenAIEdge) { Log("加载", $"加载ai寻边模型"); OpencvUtils.LoadEdgeMode(); } //清空相机内存 LineCamDev1.ClearImageQueue(); LineCamDev2.ClearImageQueue(); //if(LineCamDev1.ClearImageQueue()) //{ // Log("内存", $"相机1清空内存失败!", WarningEnum.High); // return false; //} //if (LineCamDev2.ClearImageQueue()) //{ // Log("内存", $"相机2清空内存失败!", WarningEnum.High); // return false; //} //判断是否已经有同名称产品存图 ImgDirSN = DateTime.Now.ToString("HHmmss"); pStopWatch.Restart(); pRunSpeedWatch.Restart(); //清空安全队列 ScanPhotoInfo tryDe; while (_matList1.TryDequeue(out tryDe)) { Thread.Sleep(10); } while (_matList2.TryDequeue(out tryDe)) { Thread.Sleep(10); } _isHaveImgL = false; _isHaveImgR = false; _marginWidth1 = 0; _marginWidth2 = 0; _DefectEnd = 0; _NarmalWidth = 0; Cam1Cnt = 0; Cam2Cnt = 0; defectPuaseList.Clear(); defectPuaseImgList.Clear(); System.GC.Collect(); errStep = 16; LedRun(); _isRuning = true; _isAuto = true; statusMgr.GotoAuto(); SendStatus(); CurrProductModel = model; //获取模型label GetDefectAllLabel(); //电机启动 StartMotion(); } catch (Exception ex) { Log("运行", $"程序错误-{errStep}:" + ex.Message + "\n", WarningEnum.High); } return ret; } /// /// 暂停重启 /// /// public bool ReStartRun() { bool ret = false; Log("启动", "下发启动指令..."); if (confMgr.SysConfigParams.OpenPLC) { if (plcDev.IsInit()) { plcDev.WriteCoil("DB3.DBX0.1", false); plcDev.WriteCoil("DB3.DBX0.0", true);//启动 } else { Log("运行", "PLC连接异常!", WarningEnum.High); return false; } } else if (confMgr.SysConfigParams.OpenIO) { bool sts; ioCardDev.GetDIBitState(GetIOPortIndex((int)DIName.暂停按钮), GetIOBitIndex((int)DIName.暂停按钮), out sts); if (!sts) { ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.启动 ), GetIOBitIndex((int)DOName.启动), true); Task.Run(async () => { await Task.Delay(500); ioCardDev.WriteBitState(GetIOPortIndex((int)DOName.启动), GetIOBitIndex((int)DOName.启动), false); }); } } pStopWatch.Start(); pRunSpeedWatch.Start(); Log("启动", $"暂停 -> 继续"); LedRun(); _isRuning = true; statusMgr.GotoAuto(); SendStatus(); ret = true; return ret; } #endregion #region 文件删除 public void DelectPictureFile() { //删除文件 Task.Factory.StartNew(() => { //图片 if (confMgr.SysConfigParams.DefectSrcImag.AutoDelete) statusMgr.DeleteFiles(confMgr.SysConfigParams.DefectSrcImag.SavePath, confMgr.SysConfigParams.DefectSrcImag.AutoDeleteDays, true); if (confMgr.SysConfigParams.DefectSmallImag.AutoDelete) statusMgr.DeleteFiles(confMgr.SysConfigParams.DefectSmallImag.SavePath, confMgr.SysConfigParams.DefectSmallImag.AutoDeleteDays, true); if (confMgr.SysConfigParams.DefectSplicImag.AutoDelete) statusMgr.DeleteFiles(confMgr.SysConfigParams.DefectSplicImag.SavePath, confMgr.SysConfigParams.DefectSplicImag.AutoDeleteDays, true); if (confMgr.SysConfigParams.CamImag.AutoDelete) statusMgr.DeleteFiles(confMgr.SysConfigParams.CamImag.SavePath, confMgr.SysConfigParams.CamImag.AutoDeleteDays, true); //日志 if (confMgr.SysConfigParams.AutoDeleteLog) statusMgr.DeleteFiles(confMgr.SysConfigParams.LogPath, confMgr.SysConfigParams.AutoDeleteLogData, true); }); } #region 数据保存 private async void saveCurrRecord(int key) { Records model = null; int step = 0; try { _isDefect = true; Log( "入库", $"准备入库key={key}"); //foreach (int itemKey in htTask.Keys) // AddTextEvent(DateTime.Now,"入库", $"htTask {itemKey}"); step = 1; model = Hashtable.Synchronized(htTask)[key] as Records; //model = htTask[key] as Records; step = 2; if (model.Len == 0) { Log("入库完成", $"小于1米不记录"); _isDefect = false; return; } //model.BatchId = batchId; //model.ReelId = reelId; //model.ErpLen = erpLen; while (model.ScannerPhotoCount > model.ScannerPhotoFinishCount) await Task.Delay(100); step = 3; //计算等级标准 List gradeLimitList = model.ProductInfo.GradeLimitList; if (gradeLimitList != null && gradeLimitList.Count > 0) { step = 4; int count; foreach (GradeLimit item in gradeLimitList) { if (item.A == 0 && item.B == 0 && item.C == 0 && item.D == 0 && item.E == 0) { count = model.DefectInfoList.Where(m => m.Code == item.Code).Count(); model.Grade = 1; Log("标准判断", $"({key}) 条码({model.BarCode}),无设置标准={(char)(model.Grade + 64)} [{item.Code}:{count};A<={item.A};B<={item.B};C<={item.C};D<={item.D};E<={item.E}]"); } else { if (model.DefectInfoList != null && model.DefectInfoList.Count > 0) { count = model.DefectInfoList.Where(m => m.Code == item.Code).Count(); if (count <= item.A && model.Grade <= 1) model.Grade = 1; else if (count <= item.B && item.B > 0 && model.Grade <= 2) model.Grade = 2; else if (count <= item.C && item.C > 0 && model.Grade <= 3) model.Grade = 3; else if (count <= item.D && item.D > 0 && model.Grade <= 4) model.Grade = 4; else if (count <= item.E && item.E > 0 && model.Grade <= 5) model.Grade = 5; else if (count > 0) model.Grade = 6;//不合格 Log("标准判断", $"({key}) 条码({model.BarCode}),标准={(char)(model.Grade + 64)} [{item.Code}:{count};A<={item.A};B<={item.B};C<={item.C};D<={item.D};E<={item.E}]"); } else { model.Grade = 1; Log("标准判断", $"({key}) 条码({model.BarCode}),标准={(char)(model.Grade + 64)} [{item.Code}:{0};A<={item.A};B<={item.B};C<={item.C};D<={item.D};E<={item.E}]"); } } } step = 5; } model.Qualified = (model.Grade < 6);//是否合格 if (!RecordService.InsertNav(model)) throw new Exception("写库失败!"); Log("入库完成", $"({key}) 条码({model.BarCode})已完成检测-{model.ScannerPhotoCount}-{model.ScannerPhotoFinishCount}。"); htTask.Remove(key); _isDefect = false; } catch (Exception ex) { _isDefect = false; if (model == null) Log("入库失败", $"记录({key})不存在{step}!" + ex.Message, WarningEnum.High); else Log("入库失败", $"({key}) 条码({model.BarCode})检测完成,但保存检测记录失败{step}:" + ex.Message, WarningEnum.High); } } #endregion #endregion #region 表格事件查询 public DefectInfo GetDefectInfo(long uid) { if (currKey == 0) return null; Records record = Hashtable.Synchronized(htTask)[currKey] as Records; //var defectInfo = record.DefectInfoList[record.DefectInfoList.Count - e.RowIndex-1];//按顺序索引引用 var defectInfo = record.DefectInfoList.FirstOrDefault(m => m.uid == uid); return defectInfo; } #endregion #region 拷贝产品 /// /// 拷贝产品 /// /// /// /// 流程ID /// 流程 /// public bool ProductCopy(List list, int liIndex) { Product newProduct = new Product() { Code = list[liIndex].Code + "_clone", Name = $"{list[liIndex].Name} (克隆)", Spec = list[liIndex].Spec, Material = list[liIndex].Material, Color = list[liIndex].Color, ColorName = list[liIndex].ColorName, ColorValue = list[liIndex].ColorValue, LightValue = list[liIndex].LightValue, ExposureTime = list[liIndex].ExposureTime, Gain = list[liIndex].Gain, TensionValue = list[liIndex].TensionValue, Note = list[liIndex].Note, ModelName = list[liIndex].ModelName, DefectAreaLimit = list[liIndex].DefectAreaLimit, DefectCountLimit = list[liIndex].DefectCountLimit, DefectPauseForUser = list[liIndex].DefectPauseForUser, DefectCntLength = list[liIndex].DefectCntLength, WarnDefect = list[liIndex].WarnDefect, OpenThicknessDetection = list[liIndex].OpenThicknessDetection, ThicknessDetectionStopDis = list[liIndex].ThicknessDetectionStopDis, residueWarnningLen = list[liIndex].residueWarnningLen, QualifiedLimitList = new List(), GradeLimitList = new List(), DefectPauseOption = list[liIndex].DefectPauseOption, //OrderList = new List(), ModifyUserCode = userMgr.LoginUser.Code, CreateUserCode = userMgr.LoginUser.Code }; foreach (var item in list[liIndex].QualifiedLimitList) { newProduct.QualifiedLimitList.Add(new QualifiedLimit() { Code = item.Code, ZXD = item.ZXD, Pid = item.Pid, Name = item.Name, Area = item.Area, ContrastTop = item.ContrastTop, ContrastLower = item.ContrastLower, IsOR = item.IsOR, NameCode = item.NameCode, DefectWarnLength = item.DefectWarnLength, DefectWarnCnt = item.DefectWarnCnt, ModifyUserCode = userMgr.LoginUser.Code, CreateUserCode = userMgr.LoginUser.Code }); } foreach (var item in list[liIndex].GradeLimitList) { newProduct.GradeLimitList.Add(new GradeLimit() { Pid = item.Pid, Code = item.Code, A = item.A, B = item.B, C = item.C, D = item.D, E = item.E, ModifyUserCode = userMgr.LoginUser.Code, CreateUserCode = userMgr.LoginUser.Code }); } bool result = PdtService.InsertNav(newProduct); return result; } #endregion #region 加载二次判定项 /// /// 二次项判断名称列表 /// private List DefectItemsPuaseNameList = new List(); public void LoadDefectItemsPuase(List list) { DefectItemsPuaseNameList.Clear(); foreach (var item in list) { DefectItemsPuaseNameList.Add(item); } } #endregion #region 二次判断完成修改 public Product GetProduct(string name) { return PdtService.GetModelNavByName(name); } /// /// 删除忽略项 /// /// /// public void DelDefectEdit(Records curRecord, List lstDel) { foreach (var item in lstDel) { curRecord.DefectInfoList.Remove(item); } } #endregion #region 颜色判断 /// /// 根据RGB判断颜色 /// /// /// public static string CLRToName(System.Drawing.Color Clr) { double d_r = (double)Clr.R / 83; double d_g = (double)Clr.G / 83; double d_b = (double)Clr.B / 83; int r = (int)Math.Round(d_r); int g = (int)Math.Round(d_g); int b = (int)Math.Round(d_b); string rgb = r.ToString() + "," + g.ToString() + "," + b.ToString(); #region "switch (rgb)" switch (rgb) { case "0,0,0": case "1,1,1": case "2,2,2": case "3,3,3": if (r == 0) return RGBName.Black.GetDescription(); if (r == 1) return RGBName.Gray.GetDescription(); if (r == 2) return RGBName.LightGray.GetDescription(); if (r == 3) return RGBName.White.GetDescription(); break; case "0,0,3": case "0,3,0": case "3,0,0": case "1,1,3": case "1,3,1": case "3,1,1": case "0,1,3": case "0,3,1": case "3,0,1": case "1,0,3": case "1,3,0": case "3,1,0": case "0,0,2": case "0,2,0": case "2,0,0": case "1,1,2": case "1,2,1": case "2,1,1": case "0,1,2": case "0,2,1": case "2,0,1": case "1,0,2": case "1,2,0": case "2,1,0": if (r == 2) return RGBName.DarkRed.GetDescription(); if (g == 2) return RGBName.DarkGreen.GetDescription(); if (b == 2) return RGBName.DarkBlue.GetDescription(); if (r == 3) return RGBName.Red.GetDescription(); if (g == 3) return RGBName.Green.GetDescription(); if (b == 3) return RGBName.Blue.GetDescription(); break; case "0,0,1": case "0,1,0": case "1,0,0": case "2,2,3": case "2,3,2": case "3,2,2": if (r == 1) return RGBName.DarkRed.GetDescription(); if (g == 1) return RGBName.DarkGreen.GetDescription(); if (b == 1) return RGBName.DarkBlue.GetDescription(); if (r == 3) return RGBName.LightRed.GetDescription(); if (g == 3) return RGBName.LightGreen.GetDescription(); if (b == 3) return RGBName.LightBlue.GetDescription(); break; case "1,1,0": case "0,1,1": case "1,0,1": case "2,2,0": case "0,2,2": case "2,0,2": case "2,2,1": case "1,2,2": case "2,1,2": case "3,3,0": case "0,3,3": case "3,0,3": case "3,3,1": case "1,3,3": case "3,1,3": case "3,3,2": case "2,3,3": case "3,2,3": if (r == 1 && g == 1) return RGBName.DarkYellow.GetDescription(); if (g == 1 && b == 1) return RGBName.DarkCyan.GetDescription(); if (r == 1 && b == 1) return RGBName.DarkPurple.GetDescription(); if (r == 2 && g == 2) return RGBName.Yellow.GetDescription(); if (g == 2 && b == 2) return RGBName.Cyan.GetDescription(); if (r == 2 && b == 2) return RGBName.Purple.GetDescription(); if (r == 3 && g == 3) return RGBName.LightYellow.GetDescription(); if (g == 3 && b == 3) return RGBName.LightCyan.GetDescription(); if (r == 3 && b == 3) return RGBName.LightPurple.GetDescription(); break; case "3,2,0": case "3,2,1": return RGBName.Orange.GetDescription(); case "2,3,0": case "2,3,1": return RGBName.YellowGreen.GetDescription(); case "0,3,2": case "1,3,2": return RGBName.CyanGreen.GetDescription(); case "0,2,3": case "1,2,3": return RGBName.CyanBlue.GetDescription(); case "3,0,2": case "3,1,2": return RGBName.PurpleRed.GetDescription(); case "2,0,3": case "2,1,3": return RGBName.PurpleBlue.GetDescription(); default: return "Error"; } #endregion return "Error"; } #endregion #region 博艺Mes public class MesCode { /// /// 类型 /// public string sProName { get; set; } /// /// /// public string sJson { get; set; } } public class MesReturnCode { public string FID { get; set; } public string PCode { get; set; } public string MFID { get; set; } public string Mname { get; set; } public string PColor { get; set; } public string RawMFid { get; set; } public string RawMName { get; set; } public string PPaper { get; set; } public string PQty { get; set; } public string PDDate { get; set; } public string PDes { get; set; } public string CtmFid { get; set; } public string CtmName { get; set; } } public class Root { /// /// /// public List List { get; set; } } private string GetMesJson(string code) { MesCode mesCode = new MesCode(); mesCode.sProName = "API_GetPrdTaskItm"; mesCode.sJson = code; string jsonstr = JsonConvert.SerializeObject(mesCode); return jsonstr; } private bool MesReturn(string json, out string MFID, out string httprel) { MFID = ""; httprel = ""; if (string.IsNullOrEmpty(confMgr.SysConfigParams.MesServerAddr)) return false; string url = confMgr.SysConfigParams.MesServerAddr; string data = json; httprel = HttpApi.Post(url, HttpApi.HttpContentType.application_json, data); if (string.IsNullOrEmpty(httprel)) return false; JObject Param = JObject.Parse(httprel); MFID = Param["List"][0]["MFID"].ToString(); if (string.IsNullOrEmpty(MFID)) return false; return true; } #endregion #region 特殊记录 public void LogMeg(string mesg) { Log("界面记录", mesg, WarningEnum.Normal, false); } #endregion } #region 系统事件 /// /// 流程事件 /// public class RunEventArgs : EventArgs { private int _cmd; public int Cmd { get { return _cmd; } } private int _picIndex; public int PicIndex { get { return _picIndex; } } private Mat _pic; public Mat Pic { get { return _pic; } } public RunEventArgs(int index, Mat pic) { this._cmd = 6; this._picIndex = index; this._pic = pic.Clone(); } private bool _over; public bool Over { get { return _over; } } public RunEventArgs(bool ov) { this._cmd = 10; this._over = ov; } private List _dataRowlist = new List(); public List DataRowlist { get { return _dataRowlist; } } public RunEventArgs(List dataRowlist) { this._cmd = 11; this._dataRowlist = dataRowlist; } private Image _defectimg; public Image Defectimg { get { return _defectimg; } } public RunEventArgs(Image defectimg) { this._cmd = 12; this._defectimg = defectimg; } private List _points; public List Points { get { return _points; } } public RunEventArgs(List pints) { this._cmd = 13; this._points = pints; } private double _len; public double Len { get { return _len; } } private double _speed; public double Speed { get { return _speed; } } public RunEventArgs(double len, double spd) { this._cmd = 14; this._len = len; this._speed = spd; } private Mat _Matimg; public Mat DefectMat { get { return _Matimg; } } public RunEventArgs(Mat defectimg) { this._cmd = 15; this._Matimg = defectimg.Clone(); //this._Matimg = defectimg; } private int _cam1ImgCnt; public int Cam1ImgCnt { get { return _cam1ImgCnt; } } private int _cam2ImgCnt; public int Cam2ImgCnt { get { return _cam2ImgCnt; } } private int _defectImgCnt; public int DeefectImgCnt { get { return _defectImgCnt; } } private int _saveImgCnt; public int SaveImgCnt { get { return _saveImgCnt; } } private string _definfoCnt; public string _DefinfoCnt { get { return _definfoCnt; } } public RunEventArgs(int cnt1, int cnt2, string definfo) { this._cmd = 16; this._cam1ImgCnt = cnt1; this._cam2ImgCnt = cnt2; this._definfoCnt = definfo; } public RunEventArgs(int cnt1, int cnt2, int cnt3) { this._cmd = 20; //this._cam1ImgCnt = cnt1; //this._cam2ImgCnt = cnt2; this._saveImgCnt = cnt3; } private int _lightValue; public int LightValue { get { return _lightValue; } } public RunEventArgs(int val) { this._cmd = 17; this._lightValue = val; } private Product _md; public Product MD { get { return _md; } } public RunEventArgs(Product md) { this._cmd = 18; this._md = md; } private int _imgIndex; public int ImgIndex { get { return _imgIndex; } } private List _lstEditDefect; public List LstEditDefect { get { return _lstEditDefect; } } private List _lstEditInfo; public List LstEditInfo { get { return _lstEditInfo; } } private Records _records; public Records Records { get { return _records; } } private Mat _tagImg; public Mat TagImg { get { return _tagImg; } } public RunEventArgs(int index, List lstEditDefect, Records currRecord, Mat img, List lstEditInfo) { this._cmd = 19; this._imgIndex = index; this._lstEditDefect = lstEditDefect; this._records = currRecord; this._tagImg = img; this._lstEditInfo = lstEditInfo; } public RunEventArgs(int index, List lstEditDefect, Records currRecord, Mat img) { this._cmd = 21; this._imgIndex = index; this._lstEditDefect = lstEditDefect; this._records = currRecord; this._tagImg = img; } } /// /// 主窗体事件 /// public class MainEventArgs : EventArgs { private string _tag; public string Tag { get { return _tag; } } private string _message; public string Message { get { return _message; } } private int _showIndex; public int ShowIndex { get { return _showIndex; } } private WarningEnum _warning; public WarningEnum Warning { get { return _warning; } } private bool _show; public bool show { get { return _show; } } public MainEventArgs(int index) { this._showIndex = index; this._show = true; } public MainEventArgs(int index, string message) { this._message = message; this._showIndex = index; this._show = true; } public MainEventArgs(string tag, string message, WarningEnum warning = WarningEnum.Normal) { this._tag = tag; this._message = message; this._showIndex = 0; this._warning = warning; this._show = true; } public MainEventArgs(string tag, string message, WarningEnum warning = WarningEnum.Normal, bool Show = true) { this._tag = tag; this._message = message; this._showIndex = 0; this._warning = warning; this._show = Show; } } /// /// 初始化事件 /// public class InitEventArgs : EventArgs { private string _message; public string Message { get { return _message; } } private bool _isInitialized; public bool IsInitialized { get { return _isInitialized; } } public InitEventArgs() { } public InitEventArgs(string message, bool isInitialized = false) { this._message = message; this._isInitialized = isInitialized; } } #endregion #region 颜色定义 public enum RGBName { [Description("红")] Red = 1, [Description("绿")] Green, [Description("蓝")] Blue, [Description("深红")] DarkRed, [Description("深绿")] DarkGreen, [Description("深蓝")] DarkBlue, [Description("浅红")] LightRed, [Description("浅绿")] LightGreen, [Description("浅蓝")] LightBlue, [Description("黄")] Yellow, [Description("青")] Cyan, [Description("紫")] Purple, [Description("深黄")] DarkYellow, [Description("深青")] DarkCyan, [Description("深紫")] DarkPurple, [Description("浅黄")] LightYellow, [Description("浅青")] LightCyan, [Description("浅紫")] LightPurple, [Description("橙")] Orange, [Description("黄绿")] YellowGreen, [Description("青绿")] CyanGreen, [Description("青蓝")] CyanBlue, [Description("紫蓝")] PurpleBlue, [Description("紫红")] PurpleRed, [Description("黑")] Black, [Description("白")] White, [Description("灰")] Gray, [Description("浅灰")] LightGray, } static class EnumExtensions { public static string GetDescription(this Enum val) { var field = val.GetType().GetField(val.ToString()); var customAttribute = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)); return customAttribute == null ? val.ToString() : ((DescriptionAttribute)customAttribute).Description; } } #endregion }