using Bro.Common.Base; using Bro.Common.Helper; using Bro.Common.Interface; using Bro.Common.Model; using Bro.M071.DBManager; using Bro.M071.Model; using Bro.M071.Model.Model; using Bro.Process; using HalconDotNet; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using static Bro.Common.Helper.EnumHelper; namespace Bro.M071.Process { [Process("M071", EnumHelper.DeviceAttributeType.Device)] public partial class M071Process : ProcessControl { #region 构造函数 public M071Process() : base() { } public M071Process(string productionCode) : base(productionCode) { } #endregion #region 配置 M071Config Config { get => IConfig as M071Config; } #endregion #region 事件 public event Action OnMeasureStart; public event Action OnClearBarcode; public event Action OnElementUpdated; public event Action OnMachineStateChanged; public event Action OnFullResetDone; #endregion public override void Open() { InitialSetting(); base.Open(); SwitchBeep(false); SwitchLightGreen(false); SwitchLightRed(false); SwitchLightYellow(false); Reset(null, null, null); FullReset(null); } private void InitialSetting() { //数据库迁移检查 DatabaseInitialize.Initialize(); MotionCardSettingCheck(); Config.SnapshotPointCollection.ForEach(u => { u.GetHalconToolPathList().ForEach(path => { LoadHalconTool(path, u.Id); }); }); Config.KeyUnitCollection.ForEach(u => { if (!u.IsEnabled) return; var snapshotPoint = Config.SnapshotPointCollection.FirstOrDefault(s => s.Id == u.SnapshotPointId && s.IsEnabled); if (snapshotPoint == null) throw new ProcessException($"{u.AliasName}未设置可用拍照点位"); if (u.ImageSeq < 1) throw new ProcessException($"{u.AliasName}图片序号小于1"); var algo = Config.KeyAlgorithemCollection.FirstOrDefault(a => a.Id == u.KeyAlgorithemId); if (algo == null) throw new ProcessException($"{u.AliasName}未设置检测算法"); u.KeyAlgorithemPath = algo.AlgorithemPath; LoadHalconTool(u.KeyAlgorithemPath, u.AliasName); var resultSet = Config.KeyResultCollection.FirstOrDefault(r => r.Id == u.KeyResultId); if (resultSet == null) throw new ProcessException($"{u.AliasName}未设置检测结果配置"); u.KeyResultList = new List(resultSet.Results); }); Config.MeasurementUnitCollection.ForEach(u => { if (!u.IsEnabled) return; var spec = Config.MeasureTypeCollection.FirstOrDefault(s => s.Code == u.MeasureType); if (spec == null) throw new ProcessException($"{u.GetDisplayText()}未设置检测类型"); u.Spec = new MeasureType(); u.Spec.DataFrom(spec); LoadHalconTool(u.Spec.AlgorithemPath, u.Id); u.KeyUnitCollection.ForEach(b => { var keyRespone = Config.KeyUnitCollection.Where(ku => ku.IsEnabled && ku.Key == b.Key).ToList(); if (keyRespone.Count == 0) throw new ProcessException($"{u.GetDisplayText()}对应的单键不存在或不可用"); if (b.KeyResultId == "All") { keyRespone.SelectMany(kr => kr.KeyResultList).ToList().ForEach(r => { b.MeasureValueDict[r] = null; }); } else { b.MeasureValueDict[b.KeyResultId] = null; } }); }); } #region 流程中抛出异常 public override void ExceptionRaisedInMonitor(Exception ex) { if (ex is ProcessException pEx) { if (pEx.Level >= ExceptionLevel.Fatal) { RaisedAlarm(pEx.Message); MachineState = MachineState.Alarm; } } else { RaisedAlarm(ex.Message); MachineState = MachineState.Alarm; } } #endregion public string BarCode { get; set; } List productionList = new List(); [ProcessMethod("", "StartJob", "开始扫描", InvokeType.TestInvoke)] public ProcessResponse StartJob(IOperationConfig opConfig, IDevice invokeDevice, IDevice sourceDevice) { if (MachineState != MachineState.Ready) throw new ProcessException("机台未就绪,请勿开始测量", null, ExceptionLevel.Fatal); if (string.IsNullOrWhiteSpace(BarCode)) { OnClearBarcode?.Invoke(); throw new ProcessException("未输入产品条码,请勿开始测量"); } MachineState = MachineState.Running; OnMeasureStart?.BeginInvoke(null, null); var measurements = Config.MeasurementUnitCollection.Where(u => u.IsEnabled).ToList().DeepSerializeClone(); measurements.ForEach(m => { m.InitialKeyUnitMeasureChanged(); }); var pMeasure = new ProductionMeasurement() { Barcode = BarCode, Measurements = measurements, StartTime = DateTime.Now, }; var existedProduction = productionList.FirstOrDefault(u => u.Barcode == pMeasure.Barcode); if (existedProduction != null) { productionList.Remove(existedProduction); existedProduction.Dispose(); existedProduction = null; } productionList.Add(pMeasure); pMeasure.InitialMeasurementsPropertyChanged(); pMeasure.PropertyChanged += MeasureProduction_PropertyChanged; Config.SnapshotPointCollection.Where(u => u.IsEnabled).ToList().ForEach(s => { _pauseHandle.WaitHandle.WaitOne(); IDevice device = DeviceCollection.FirstOrDefault(u => u.Id == s.MotionOp.Device); if (device == null) throw new ProcessException($"{s.Name}拍照点位未设置运动设备"); IMotionCard motionDevice = device as IMotionCard; if (motionDevice == null) throw new ProcessException($"{s.Name}拍照点位设置{device.Name}不是运动设备"); var response = motionDevice.Run(s.MotionOp.OpConfig); if (!response.Result) { throw new ProcessException($"{device.Name}异常,{response.Message}", null, ExceptionLevel.Fatal); } CameraBase camera = DeviceCollection.FirstOrDefault(u => u.Id == s.CameraOp.Device) as CameraBase; if (camera == null) return; IImageSet set = null; try { set = CollectHImage(camera, s.CameraOp.OpConfig); } catch (ProcessException pEx) { pEx.Level = ExceptionLevel.Fatal; throw pEx; } if (set == null) { return; } RunImageHandle(camera, s.CameraOp.OpConfig, set, s.Id, s.Name, pMeasure.Measurements); }); BarCode = ""; LogAsync(DateTime.Now, $"{pMeasure.Barcode}测量动作完成", ""); return new ProcessResponse(true); } #region 双手启动 bool isLeftStart = false; bool IsLeftStart { get => isLeftStart; set { isLeftStart = value; StartCheck(); } } bool isRightStart = false; bool IsRightStart { get => isRightStart; set { isRightStart = value; StartCheck(); } } private void StartCheck() { if (isRightStart && isLeftStart) { StartJob(null, null, null); } } [ProcessMethod("", "Start_Left", "左手启动", InvokeType.TestInvoke)] public ProcessResponse Start_Left(IOperationConfig opConfig, IDevice invokeDevice, IDevice sourceDevice) { if (opConfig.InputPara != null && opConfig.InputPara.Count > 0) { IsLeftStart = opConfig.InputPara[0] == 1; } return new ProcessResponse(); } [ProcessMethod("", "Start_Right", "右手启动", InvokeType.TestInvoke)] public ProcessResponse Start_Right(IOperationConfig opConfig, IDevice invokeDevice, IDevice sourceDevice) { if (opConfig.InputPara != null && opConfig.InputPara.Count > 0) { IsRightStart = opConfig.InputPara[0] == 1; } return new ProcessResponse(); } #endregion #region 私有方法 private void MeasureProduction_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (sender is ProductionMeasurement pMeasure) { lock (pMeasure) { //检查是否全部完成 pMeasure.Measurements.ForEach(m => { if (m.KeyUnitCollection.All(k => k.IsDone != null)) { if (!m.IsUpdated) { if (m.KeyUnitCollection.Any(k => k.IsDone == false)) { m.Spec.ActualValue = -999; } else { string toolKey = m.Id + "|" + m.Spec.AlgorithemPath; if (!_halconToolDict.ContainsKey(toolKey)) { LogAsync(DateTime.Now, $"{m.GetDisplayText()}{m.Spec.Code}算法未初始化", ""); m.Spec.ActualValue = -999; } else { var array = m.KeyUnitCollection.SelectMany(u => u.MeasureValueDict.Values.ToList().ConvertAll(v => v ?? -999)).ToArray(); _halconToolDict[toolKey].InputTupleDic["INPUT_Params"] = new HTuple(array); if (!_halconToolDict[toolKey].RunProcedure(out string error)) { LogAsync(DateTime.Now, $"{m.GetDisplayText()}{m.Spec.Code}算法异常,{error}", ""); m.Spec.ActualValue = -999; } else { m.Spec.ActualValue = _halconToolDict[toolKey].GetResultTuple("OUTPUT_Result").D; LogAsync(DateTime.Now, $"{m.GetDisplayText()}数据{m.Spec.ActualValue},结果{(m.Spec.MeasureResult == null ? "TBD" : (m.Spec.MeasureResult == true ? "OK" : "NG"))}", ""); } } } LogAsync(DateTime.Now, $"{m.GetDisplayText()}检测结果", $"{((m.Spec.MeasureResult ?? false) ? "OK" : "NG")}"); IShapeElement indicator = null; pMeasure.ElementList.Add(indicator); //输出图形基元到界面 OnElementUpdated?.BeginInvoke(indicator, null, null); SaveKeyImages(pMeasure.Barcode, m); m.IsUpdated = true; } } }); if (!pMeasure.Measurements.All(m => m.IsUpdated)) { return; } } pMeasure.EndTime = DateTime.Now; bool pResult = pMeasure.Measurements.All(u => u.Spec.MeasureResult == true); pMeasure.PResult = pResult ? "OK" : "NG"; OnUpdateResult?.Invoke(DateTime.Now, pResult ? 1 : 0); OnUpdateCT?.Invoke((float)(pMeasure.EndTime.Value - pMeasure.StartTime.Value).TotalSeconds); LogAsync(DateTime.Now, $"{pMeasure.Barcode} 检测完成,结果 {pMeasure.PResult}", ""); if (MachineState == MachineState.Running) MachineState = MachineState.Ready; var measurementUnitResultAndKeyUnitDataSet = GetMeasurementUnitResultAndKeyUnitData(pMeasure); //MES输出 todo //Excel报表输出 (单个产品的excel导出) ExportProductionExcel(measurementUnitResultAndKeyUnitDataSet); //数据库保存 SaveProductionData(measurementUnitResultAndKeyUnitDataSet); SaveWholeImage(pMeasure); productionList.RemoveAll(p => p.Barcode == pMeasure.Barcode); pMeasure.Dispose(); } } KeyUnitDataManager keyUnitDataManager = new KeyUnitDataManager(); MeasurementUnitResultManager measurementUnitResultManager = new MeasurementUnitResultManager(); MeasurementAndKeyDataRelationManager measurementAndKeyDataRelationManager = new MeasurementAndKeyDataRelationManager(); ProductionMeasurementRecordsManager productionMeasurementRecordsManager = new ProductionMeasurementRecordsManager(); static object dataSaveLock = new object(); private async void SaveProductionData(ProductionMeasurementUnitResultAndKeyUnitDataSet measurementUnitResultAndKeyUnitDataSet) { await Task.Run(() => { try { lock (dataSaveLock) { // 获取 产品数据 并保存 var productionMeasurementRecords = measurementUnitResultAndKeyUnitDataSet.ProductionMeasurementRecord; productionMeasurementRecordsManager.CreateModel(productionMeasurementRecords); // 获取 原始数据 并保存 var keyUnitDatas = measurementUnitResultAndKeyUnitDataSet.KeyUnitDataList; keyUnitDataManager.BatchAddKeyUnitData(keyUnitDatas); // 获取 检测结果数据 并保存 var measurementUnitResults = measurementUnitResultAndKeyUnitDataSet.MeasurementUnitResultList; measurementUnitResultManager.BatchAddMeasurementUnitResult(measurementUnitResults); // 获取 关系数据并保存 var measurementAndKeyDataRelationList = measurementUnitResultAndKeyUnitDataSet.MeasurementAndKeyDataRelationList; measurementAndKeyDataRelationManager.BatchAddMeasurementAndKeyDataRelation(measurementAndKeyDataRelationList); } } catch (Exception ex) { LogAsync(DateTime.Now, "数据保存异常", ex.GetExceptionMessage()); } }); } private ProductionMeasurementUnitResultAndKeyUnitDataSet GetMeasurementUnitResultAndKeyUnitData(ProductionMeasurement pData) { ProductionMeasurementUnitResultAndKeyUnitDataSet measurementUnitResultAndKeyUnitDataSet = new ProductionMeasurementUnitResultAndKeyUnitDataSet(); try { //产品数据 ProductionMeasurementRecords productionMeasurementRecords = new ProductionMeasurementRecords(); //关系数据 List measurementAndKeyDataRelationList = new List(); //原始数据 List keyUnitDatas = new List(); // 单个产品的测量汇总 List measurementUnitResults = new List(); productionMeasurementRecords.ProductionBarcode = pData.Barcode; productionMeasurementRecords.ProductionCode = ProductionCode; productionMeasurementRecords.ProductionResult = pData.PResult; productionMeasurementRecords.OperationStartTime = pData.StartTime.GetValueOrDefault(); productionMeasurementRecords.OperationEndTime = pData.EndTime.GetValueOrDefault(); measurementUnitResultAndKeyUnitDataSet.ProductionMeasurementRecord = productionMeasurementRecords; foreach (var MeasurementUnitResult in pData.Measurements)//获取到单个测量项结果 { MeasurementUnitResult measurementUnitResult = new MeasurementUnitResult(); measurementUnitResult.ProductionMeasurementRecordsId = productionMeasurementRecords.ID; measurementUnitResult.MeasurementName = MeasurementUnitResult.Name; measurementUnitResult.MeasurementType = MeasurementUnitResult.MeasureType; measurementUnitResult.MeasurementValue = ""; measurementUnitResult.MeasurementResult = MeasurementUnitResult.Spec.MeasureResult.Value ? "OK" : "NG"; measurementUnitResults.Add(measurementUnitResult); foreach (var keyUnit in MeasurementUnitResult.KeyUnitCollection)//获取单个键的测量结果 { foreach (var keyValue in keyUnit.MeasureValueDict)//获取单个键的单个测量item 结果 { KeyUnitData keyUnitData = new KeyUnitData(); keyUnitData.Key = keyUnit.Key; keyUnitData.MeasurementItem = keyValue.Key; keyUnitData.ItemValue = keyValue.Value.ToString(); keyUnitDatas.Add(keyUnitData); MeasurementAndKeyDataRelation measurementAndKeyDataRelation = new MeasurementAndKeyDataRelation(); measurementAndKeyDataRelation.MeasurementUnitResultId = measurementUnitResult.ID; measurementAndKeyDataRelation.KeyUnitDataId = keyUnitData.ID; measurementAndKeyDataRelationList.Add(measurementAndKeyDataRelation); } } } measurementUnitResultAndKeyUnitDataSet.ProductionMeasurementRecord = productionMeasurementRecords; measurementUnitResultAndKeyUnitDataSet.MeasurementAndKeyDataRelationList = measurementAndKeyDataRelationList; measurementUnitResultAndKeyUnitDataSet.KeyUnitDataList = keyUnitDatas; measurementUnitResultAndKeyUnitDataSet.MeasurementUnitResultList = measurementUnitResults; } catch (Exception ex) { LogAsync(DateTime.Now, "数据获取异常", ex.GetExceptionMessage()); } return measurementUnitResultAndKeyUnitDataSet; } private async void ExportProductionExcel(ProductionMeasurementUnitResultAndKeyUnitDataSet measurementUnitResultAndKeyUnitDataSet) { await Task.Run(() => { ExcelExportSet excelExportDto = new ExcelExportSet(); excelExportDto.Worksheets = new List() { "原始数据", "检测结果" }; var keyUnitColumns = new Dictionary() { {"Key", "键"}, {"MeasurementItem", "检测项"}, {"ItemValue", "检测值"} }; var measurementUnitResultColumns = new Dictionary() { {"MeasurementName", "检测名称"}, {"MeasurementType", "检测类型"}, {"MeasurementValue", "检测值"}, {"MeasurementResult", "检测结果"}, }; excelExportDto.WorksheetColumns[excelExportDto.Worksheets[0]] = keyUnitColumns; excelExportDto.WorksheetColumns[excelExportDto.Worksheets[1]] = measurementUnitResultColumns; excelExportDto.WorksheetDataTable[excelExportDto.Worksheets[0]] = ExcelExportHelper.ListToDataTable(measurementUnitResultAndKeyUnitDataSet.KeyUnitDataList, keyUnitColumns); excelExportDto.WorksheetDataTable[excelExportDto.Worksheets[1]] = ExcelExportHelper.ListToDataTable(measurementUnitResultAndKeyUnitDataSet.MeasurementUnitResultList, measurementUnitResultColumns); ; byte[] filecontent = ExcelExportHelper.ExportExcel(excelExportDto, false); string dir = Path.Combine(Config.ImageSaveFolder, DateTime.Now.ToString("yyyyMMdd")); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } FileStream fs = new FileStream(Path.Combine(dir, $"{measurementUnitResultAndKeyUnitDataSet.ProductionMeasurementRecord.ProductionBarcode}_{DateTime.Now.ToString("HHmmss")}.xlsx"), FileMode.Create, FileAccess.Write); fs.Write(filecontent, 0, filecontent.Length); fs.Flush(); fs.Close(); }); } #region 图像保存 private void SaveWholeImage(ProductionMeasurement pMeasure) { try { Bitmap backImage = (Bitmap)Bitmap.FromFile(Config.BackgroundImagePath); Bitmap map = new Bitmap(backImage.Width, backImage.Height); using (Graphics g = Graphics.FromImage(map)) { g.DrawImage(backImage, new PointF(0, 0)); pMeasure.ElementList.ForEach(e => { e.Draw(g); }); } string dir = Path.Combine(Config.ImageSaveFolder, "TopView", DateTime.Now.ToString("yyyyMMdd")); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } map.Save(Path.Combine(dir, $"{pMeasure.Barcode}_{DateTime.Now.ToString("HHmmss")}.bmp")); } catch (Exception ex) { LogAsync(DateTime.Now, "整体图片保存异常", ex.GetExceptionMessage()); } } private void SaveKeyImages(string barCode, MeasurementUnit measureUnit) { string measureName = measureUnit.GetDisplayText(); if (Config.ImageSaveOption.IsSaveOriginImage) { measureUnit.KeyUnitCollection.ForEach(u => u.ImageSaveStatus++); string dir = Path.Combine(Config.ImageSaveFolder, "Origin", DateTime.Now.ToString("yyyyMMdd"), barCode, measureUnit.MeasureType); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } SaveKeyImages(measureUnit, measureName, dir); } string result = (measureUnit.Spec.MeasureResult ?? false) ? "OK" : "NG"; if (Config.ImageSaveOption.AddtionalSaveType.ToUpper().Contains(result)) { measureUnit.KeyUnitCollection.ForEach(u => u.ImageSaveStatus++); string dir = Path.Combine(Config.ImageSaveFolder, result, DateTime.Now.ToString("yyyyMMdd"), barCode, measureUnit.MeasureType); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } SaveKeyImages(measureUnit, measureName, dir); } } private async void SaveKeyImages(MeasurementUnit measureUnit, string measureName, string dir) { await Task.Run(() => { measureUnit.KeyUnitCollection.ForEach(u => { int i = 0; u.KeyImages?.ForEach(image => { string fileName = Path.Combine(dir, $"{measureName}_{u.Key}{(i == 0 ? "" : $"-{i}")}_{DateTime.Now.ToString("HHmmss")}.tiff"); image.WriteImage("tiff", 0, fileName); i++; }); u.ImageSaveStatus--; }); }); } #endregion private async void RunImageHandle(CameraBase camera, IOperationConfig opConfig, IImageSet imgSet, string snapshotId, string snapshotName, List measureList) { await Task.Run(() => { var keys = Config.KeyUnitCollection.Where(u => u.IsEnabled && u.SnapshotPointId == snapshotId); var keyBindCollection = measureList.SelectMany(u => u.KeyUnitCollection).Where(u => keys.Any(k => k.Key == u.Key)).ToList(); string toolKey = (opConfig as CameraOprerationConfigBase).AlgorithemPath; HObject images = imgSet.HImage; LaserScanParam scanParam = JsonConvert.DeserializeObject(imgSet.ImageData); if (!string.IsNullOrWhiteSpace(toolKey)) { toolKey = snapshotId + "|" + toolKey; if (!_halconToolDict.ContainsKey(toolKey)) { LogAsync(DateTime.Now, $"{snapshotName}未初始化取图算法配置", ""); keyBindCollection.ForEach(k => k.FillKeyValues(null)); return; } _halconToolDict[toolKey].InputImageDic["INPUT_Image"] = imgSet.HImage; if (!_halconToolDict[toolKey].RunProcedure(out string error)) { LogAsync(DateTime.Now, $"{snapshotName}取图算法异常,{error}", ""); keyBindCollection.ForEach(k => k.FillKeyValues(null)); return; } images = _halconToolDict[toolKey].GetResultObject("OUTPUT_Images"); } HOperatorSet.CountObj(images, out HTuple count); if (count == 0) { LogAsync(DateTime.Now, $"{snapshotName}取图算法未输出图像", ""); keyBindCollection.ForEach(k => k.FillKeyValues(null)); return; } var excludeKeys = keys.Where(u => u.ImageSeq > count).ToList(); if (excludeKeys.Count > 0) { LogAsync(DateTime.Now, $"{string.Join(" ", excludeKeys.Select(u => u.AliasName))}未在图片获取序列中", ""); keyBindCollection.Where(k => excludeKeys.Any(u => u.Key == k.Key)).ToList().ForEach(k => { k.FillKeyValues(null); }); } Parallel.For(1, count.I, (i) => { HOperatorSet.SelectObj(images, out HObject image, i); keys.Where(u => u.ImageSeq == i).ToList().ForEach(k => { Dictionary resultDict = null; var keyBindList = keyBindCollection.Where(u => u.Key == k.Key).ToList(); string keyToolKey = k.AliasName + "|" + k.KeyAlgorithemPath; if (!_halconToolDict.ContainsKey(keyToolKey)) { LogAsync(DateTime.Now, $"{k.AliasName}检测算法未初始化", ""); } else { _halconToolDict[keyToolKey].InputImageDic["INPUT_Image"] = image; _halconToolDict[keyToolKey].InputTupleDic["INPUT_Resolution_X"] = scanParam.Resolution_X; _halconToolDict[keyToolKey].InputTupleDic["INPUT_Resolution_Z"] = scanParam.Resolution_Z; if (!_halconToolDict[keyToolKey].RunProcedure(out string error)) { LogAsync(DateTime.Now, $"{k.AliasName}检测算法异常,{error}", ""); } else { var results = _halconToolDict[keyToolKey].GetResultTuple("OUTPUT_Results").HTupleToDouble(); if (results.Count == 0 || results.Any(u => u < 0)) { LogAsync(DateTime.Now, $"{k.AliasName}检测结果异常", ""); } else { resultDict = k.KeyResultList.ToDictionary(u => u, u => { int index = k.KeyResultList.IndexOf(u); return results[index]; }); } } } keyBindList.ForEach(kb => { kb.KeyImages.Add(image.Clone() as HImage); kb.FillKeyValues(resultDict); }); }); image.Dispose(); }); //if (count.I != 1) //{ // hImage?.Dispose(); // hImage = null; //} }); } #endregion } }