#define Online using BarTenderPrint; using CCWin.Win32.Const; using GeBoShi.ImageDefect; using HalconDotNet; using HZH_Controls.Forms; 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.Generic; using System.Data; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; using ToolKits.Disk; 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 UserMgr userMgr; public 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 int listCntMax = 5; private int Cam1Cnt = 0; private int Cam2Cnt = 0; #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) { devIndex = _devIndex; photoIndex = _photoIndex; mat = _mat; } public int devIndex { get; set; } /// /// 0-n /// public int photoIndex { get; set; } public string path { get; set; } public Mat mat { get; set; } } #endregion private Queue _matList1 = new Queue(); private Queue _matList2 = new Queue(); private Service.ProductService PdtService = new Service.ProductService(); private Service.RecordsService RecordService = new Service.RecordsService(); #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 CancellationTokenSource _cts; #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 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 = ""; InitLog("系统开始初始化..."); //Thread.Sleep(200); // 加载系统配置 InitLog("加载系统参数..."); ret = confMgr.LoadSystemConfig(); if (!ret) { throw new Exception("系统参数加载失败..."); } InitLog("系统参数加载完成!"); //根据llog路径,开始记录日志 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(); _Cam1Thread = new Thread(() => { Cam1ThreadFunction(); }); _Cam1Thread.IsBackground = true; _Cam1Thread.Start(); _Cam2Thread = new Thread(() => { Cam2ThreadFunction(); }); _Cam2Thread.IsBackground = true; _Cam2Thread.Start(); } #endregion #region 后台 /// /// 中断工序运行 /// /// private bool isBreakProcessRun() { return statusMgr.Status == SystemStsEnum.Pause || statusMgr.Warning == WarningEnum.High; } /// /// 相机1采图预处理 /// private void Cam1ThreadFunction() { while (true) { try { if (_cts.IsCancellationRequested) break; if (IsRuning) { ////暂停开始 //stopWatch.Start(); do { #region 实时采图 #if Online //采集图片 Acquisition acq = _LinecamDev1.GetFrames(1, 10); if (acq.GrabStatus == "GrabPass") { //显示 OnAutoRuning(new RunEventArgs(1, acq.Image)); lock (lockCurrKey) { //存在数据队列 if (currKey != 0 || htTask.ContainsKey(currKey)) { Mat img = CamDev.HImageToMat(acq.Image.CopyObj(1, -1)); if (_matList1.Count > listCntMax) { _matList1.Dequeue(); System.GC.Collect(); } //预处理 Stopwatch stopWatch = new Stopwatch(); Log($"图像预处理", $"相机1-{Cam1Cnt}"); string time = ""; stopWatch.Start(); int errStep = 0; Mat mat = img; try { errStep = 1; //反转+相机索引调换 //裁边,两侧和中间重合部分 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})"; } Log($"裁边", $"(相机1-图像{Cam1Cnt})-左图去重后:{mat.Width}*{mat.Height}," + $"重复值:{confMgr.SysConfigParams.MidCoin / 2}"); errStep = 4; //左裁边 int marginWidth0; mat = OpencvUtils.getMaxInsetRect2(mat, true, confMgr.SysConfigParams.HolePx, out marginWidth0); errStep = 5; time += $"->相机1裁边({stopWatch.ElapsedMilliseconds})"; } catch (Exception e) { Log($"图像处理", $"异常({errStep}):(相机1-图像{Cam1Cnt})-{e.Message}", WarningEnum.High); } //Cv2.Flip(img, img, FlipMode.XY);//翻转 _matList1.Enqueue(new ScanPhotoInfo(0, Cam1Cnt++, mat.Clone())); } else Log($"相机1", $"(图像)-未扫码,图像丢弃!", WarningEnum.Low); } } #endif #endregion Thread.Sleep(50); } while (!isBreakProcessRun()); //暂停中断 //stopWatch.Stop(); pStopWatch.Stop(); //_isRuning = false; } Thread.Sleep(10); } catch (Exception e) { _isRuning = false; Log("运行报警", "相机1流程运行出错:" + e.Message + "\n", WarningEnum.High); } } } /// /// 相机2采图预处理 /// private void Cam2ThreadFunction() { while (true) { try { if (_cts.IsCancellationRequested) break; if (IsRuning) { ////暂停开始 //stopWatch.Start(); do { #region 实时采图 #if Online //采集图片 Acquisition acq = _LinecamDev2.GetFrames(1, 10); if (acq.GrabStatus == "GrabPass") { //显示 OnAutoRuning(new RunEventArgs(2, acq.Image)); lock (lockCurrKey) { //存在数据队列 if (currKey != 0 || htTask.ContainsKey(currKey)) { Mat img = CamDev.HImageToMat(acq.Image.CopyObj(1, -1)); if (_matList2.Count > listCntMax) { _matList2.Dequeue(); System.GC.Collect(); } //预处理 Stopwatch stopWatch = new Stopwatch(); Log($"图像预处理", $"相机2-{Cam2Cnt}"); string time = ""; stopWatch.Start(); int errStep = 0; Mat mat = img; try { errStep = 1; //反转+相机索引调换 //裁边,两侧和中间重合部分 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})"; } Log($"裁边", $"(相机2-图像{Cam2Cnt})-右图去重后:{mat.Width}*{mat.Height}," + $"重复值:{confMgr.SysConfigParams.MidCoin / 2}"); errStep = 4; //右裁边 int marginWidth0; mat = OpencvUtils.getMaxInsetRect2(mat, false, confMgr.SysConfigParams.HolePx, out marginWidth0); errStep = 5; time += $"->相机2裁边({stopWatch.ElapsedMilliseconds})"; } catch (Exception e) { Log($"图像处理", $"异常({errStep}):(相机2-图像{Cam2Cnt})-{e.Message}", WarningEnum.High); } //Cv2.Flip(img, img, FlipMode.XY);//翻转 _matList2.Enqueue(new ScanPhotoInfo(1, Cam2Cnt++, mat.Clone())); } else Log($"相机2", $"(图像)-未扫码,图像丢弃!", WarningEnum.Low); } } #endif #endregion Thread.Sleep(50); } while (!isBreakProcessRun()); //暂停中断 //stopWatch.Stop(); pStopWatch.Stop(); //_isRuning = false; } Thread.Sleep(10); } catch (Exception e) { _isRuning = false; Log("运行报警", "相机1流程运行出错:" + e.Message + "\n", WarningEnum.High); } } } /// /// 后台运行主线程 /// private void MainThreadFunction() { while (true) { try { if (_cts.IsCancellationRequested) break; if (IsRuning) { ////暂停开始 //stopWatch.Start(); do { #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 处理2次判定 //处理2次判定 #endregion #region 图像裁边预处理 //预处理,队列都有数据,且数据长度一致 #if Online if (_matList1.Count > 0 && _matList2.Count > 0 && (_matList1.Count == _matList2.Count)) { Stopwatch stopWatch = new Stopwatch(); ScanPhotoInfo scanPhotos0 = _matList1.Dequeue(); ScanPhotoInfo scanPhotos1 = _matList2.Dequeue(); Log($"图像拼接处理", $"相机1-{scanPhotos0.photoIndex},相机2-{scanPhotos1.photoIndex}"); string time = ""; stopWatch.Start(); int errStep = 0; try { 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; //反转+相机索引调换 Mat mat0 = scanPhotos1.mat; Mat mat1 = scanPhotos0.mat; //水平合并l Mat mat = OpencvUtils.MergeImage_sameSize(new Mat[] { mat0, mat1 });//这里相机反装,左右反转下 Log($"合并", $"(图像{scanPhotos0.photoIndex})-裁边去孔洞后:({mat0.Width}+{mat1.Width});合并后(去孔洞):{mat.Width}*{mat.Height}"); //float widthRatio = mat.Width * 1.0f / resize.Width;//宽度比例 time += $"->图1+2合并({stopWatch.ElapsedMilliseconds})"; //门幅更新(含两侧孔洞)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 = "E:\\CPL\\测试代码\\革测试\\1-1\\现场原图"; 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(); int 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]}"); curRecord.FacePointList.Add(point); //判定门幅 //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); //计算速度 double lenMi = Math.Round(faceWidthX_cm / 100, 2); curRecord.Len = lenMi; curRecord.TimeLen = pStopWatch.ElapsedMilliseconds / 1000.0d / 60.0d;//总时间 分 //显示速度 OnAutoRuning(new RunEventArgs(lenMi, Math.Round(lenMi / curRecord.TimeLen, 2))); errStep = 9; time += $"->速度刷新({stopWatch.ElapsedMilliseconds})"; //----缺陷队列 //比例缩放图片 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}"); 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, }); 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}"); scanPhotos0.mat.Dispose(); scanPhotos1.mat.Dispose(); scanPhotos0 = scanPhotos1 = null; //task = null; System.GC.Collect(); } } #endregion Thread.Sleep(50); } while (!isBreakProcessRun()); //暂停中断 //stopWatch.Stop(); pStopWatch.Stop(); _isRuning = false; } Thread.Sleep(10); } catch (Exception e) { _isRuning = false; Log("运行报警", "流程运行出错:" + 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; } /// /// 根据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 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 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 { Log($"检测完成", $"图像队列:{res.record.ScannerPhotoFinishCount + 1}/{res.record.ScannerPhotoCount} (图像{res.photoIndex})检测结果:{res.isSucceed}"); //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);//翻转 string dirPath = Util.CreateSubDir(confMgr.SysConfigParams.DefectSrcImag.SavePath, new List { $"{ res.record.BatchId }_{ res.record.ReelId }" }); if (confMgr.SysConfigParams.DefectSrcImag.AutoSave)//保存所有原图 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)}"); //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); step = 2; if (res.excelTable.Rows.Count > 0) { res.record.dicPhoto_Defect[res.photoIndex] = true;//改为此图有瑕疵 //有瑕疵打标图必需保存 Jpeg dirPath = Util.CreateSubDir(confMgr.SysConfigParams.DefectSplicImag.SavePath, new List { $"{res.record.BatchId}_{res.record.ReelId}" }); if (confMgr.SysConfigParams.DefectSplicImag.AutoSave)//保存瑕疵图 OpenCvSharp.Extensions.BitmapConverter.ToBitmap(res.bmpTag).Save($"{dirPath}{res.photoIndex}_tag.jpg", ImageFormat.Jpeg); 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 = BitmapConverter.ToBitmap(res.lstDefectBmp[i]) }; defectInfo.ModifyUserCode = defectInfo.CreateUserCode = res.record.CreateUserCode; step = 6 + i * 10; 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.BatchId}_{res.record.ReelId}" }); 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); 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); } } Log($"检测完成", "更新UI"); //更新UI int bmpHeight = res.bmp.Height; //显示图片 //OnAutoRuning(new RunEventArgs(defectInfo.image)); //显示瑕疵图 OnAutoRuning(new RunEventArgs(dataRowlist)); step = 9; Log($"检测完成", "保存CSV"); //保存CSV dirPath = Util.CreateSubDir(confMgr.SysConfigParams.DefectSrcImag.SavePath, new List { $"{res.record.BatchId}_{res.record.ReelId}" }); bool b = Utils.ExcelUtil.DataTable2CSV($"{dirPath}{res.photoIndex}.csv", res.excelTable); //AddTextEvent(DateTime.Now,$"打标完成", $"{res.tag}.xlsx {(b ? "保存成功!" : "保存失败!")}"); step = 10; //每百米告警判断???在此还是在收到新照片时触发??? if (res.record.ProductInfo.DefectCountLimit > 0 && res.record.DefectTotalCount >= res.record.ProductInfo.DefectCountLimit) { int compLen = 100 * 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($"告警", $"每百米瑕疵数量达到阈值!({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($"告警{Thread.CurrentThread.ManagedThreadId}", $"每{defectWarn.DefectWarnLength}米{GetDefectName(defectWarn.Code)}瑕疵数量达到阈值!({warnDefectCount}>={defectWarn.DefectWarnCnt})", WarningEnum.High); } } } } } } else { Log($"打标失败", $"(图像{res.photoIndex})-瑕疵检测失败", WarningEnum.Low); } 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}"); //this.BeginInvoke(new System.Action(() => //{ // this.lblWaitImageCount.Text = $"{liScannerPhotoCount - liScannerPhotoFinishCount}"; //})); res = null; System.GC.Collect(); } } } #endregion #region 结束验布 public bool DefectEnd(Form fatherFrm) { 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((int)DOName.停止, (int)DOName.停止, true); Task.Run(async () => { await Task.Delay(500); ioCardDev.WriteBitState((int)DOName.停止, (int)DOName.停止, false); }); } //CurrentState = CurrentStateEnum.初始; if (currKey > 0 ) { if (FrmDialog.ShowDialog(fatherFrm, $"是否保存当前检测结果?", "提示", true) == DialogResult.OK) { 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); }); //resetUIValue(); pStopWatch.Stop(); _isAuto = false; //this.btnStart.Enabled = true; //this.btnEnd.Enabled = this.btnPause.Enabled = false;//这里有问题,应该是devPlc回调设置 OnAutoRuning(new RunEventArgs(_isAuto)); } 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) { 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) { 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) { 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) { 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 (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); 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) { sts = true; 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.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); } //关闭相机 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(); } } #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) { 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 = 6;//美尚4通道,其他6通道 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); } /// /// 初始化硬件 /// /// 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.OpenPLC) { //PLC初始化 InitLog("PLC连接初始化..."); if(!plcDev.OpenDev()) { InitLog("PLC连接初始化失败!", "初始化", WarningEnum.High); return ret; } InitLog("PLC连接成功!"); } 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; } 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; 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; //光源初始化 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 启动 /// /// 加载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; 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 (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); }); 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}),加载产品信息({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}"); 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((int)DOName.启动, (int)DOName.启动, true); Task.Run(async () => { await Task.Delay(500); ioCardDev.WriteBitState((int)DOName.启动, (int)DOName.启动, false); }); } else Log("运行", "存在暂停信号!", WarningEnum.Low); } pStopWatch.Restart(); _matList1.Clear(); _matList2.Clear(); Cam1Cnt = 0; Cam2Cnt = 0; System.GC.Collect(); errStep = 16; LedRun(); _isRuning = true; _isAuto = true; statusMgr.GotoAuto(); SendStatus(); CurrProductModel = model; //获取模型label GetDefectAllLabel(); } 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((int)DOName.启动, (int)DOName.启动, true); Task.Run(async () => { await Task.Delay(500); ioCardDev.WriteBitState((int)DOName.启动, (int)DOName.启动, false); }); } } pStopWatch.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.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) { _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) { 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.BatchId}),标准={(char)(model.Grade + 64)} [{item.Code}:{count};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.BatchId})已完成检测。"); htTask.Remove(key); _isDefect = false; } catch (Exception ex) { _isDefect = false; if (model == null) Log("入库失败", $"记录({key})不存在{step}!" + ex.Message, WarningEnum.High); else Log("入库失败", $"({key}) 批号({model.BatchId})检测完成,但保存检测记录失败{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 系统事件 /// /// 流程事件 /// public class RunEventArgs : EventArgs { private int _cmd; public int Cmd { get { return _cmd; } } private int _picIndex; public int PicIndex { get { return _picIndex; } } private HObject _pic; public HObject Pic { get { return _pic; } } public RunEventArgs(int index, HObject 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; } } /// /// 主窗体事件 /// 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; } } public MainEventArgs(int index) { this._showIndex = index; } public MainEventArgs(int index, string message) { this._message = message; this._showIndex = index; } public MainEventArgs(string tag, string message, WarningEnum warning = WarningEnum.Normal) { this._tag = tag; this._message = message; this._showIndex = 0; this._warning = warning; } } /// /// 初始化事件 /// 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 }