| | |
| | | using Bro.Common.Helper; |
| | | using Bro.Common.Interface; |
| | | using Bro.Common.Model; |
| | | using Bro.M071.Model; |
| | | using Bro.Process; |
| | | using HalconDotNet; |
| | | 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 |
| | | { |
| | |
| | | |
| | | #region 事件 |
| | | public event Action OnMeasureStart; |
| | | public event Action<string> OnBarcodeChanged; |
| | | public event Action OnClearBarcode; |
| | | public event Action<IShapeElement> OnElementUpdated; |
| | | public event Action<MachineState> OnMachineStateChanged; |
| | | public event Action OnFullResetDone; |
| | | #endregion |
| | | |
| | | public override void Open() |
| | |
| | | InitialSetting(); |
| | | |
| | | base.Open(); |
| | | |
| | | Reset(null, null, null); |
| | | FullReset(null); |
| | | } |
| | | |
| | | private void InitialSetting() |
| | | { |
| | | //数据库迁移检查 |
| | | DatabaseInitialize.Initialize(); |
| | | |
| | | MotionCardSettingCheck(); |
| | | |
| | | Config.SnapshotPointCollection.ForEach(u => |
| | | { |
| | | u.GetHalconToolPathList().ForEach(path => |
| | |
| | | if (keyRespone.Count == 0) |
| | | throw new ProcessException($"{u.GetDisplayText()}对应的单键不存在或不可用"); |
| | | |
| | | if (b.KeyResult == "All") |
| | | if (b.KeyResultId == "All") |
| | | { |
| | | keyRespone.SelectMany(kr => kr.KeyResultList).ToList().ForEach(r => |
| | | { |
| | |
| | | } |
| | | else |
| | | { |
| | | b.MeasureValueDict[b.KeyResult] = null; |
| | | b.MeasureValueDict[b.KeyResultId] = null; |
| | | } |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | #region InitialHalconTool |
| | | //protected override void InitialHalconTool() |
| | | //{ |
| | | // base.InitialHalconTool(); |
| | | |
| | | // Config.SnapshotPointCollection.ForEach(u => |
| | | // { |
| | | // u.GetHalconToolPathList().ForEach(path => |
| | | // { |
| | | // if (!string.IsNullOrWhiteSpace(path)) |
| | | // { |
| | | // string directoryPath = Path.GetDirectoryName(path); |
| | | // string fileName = Path.GetFileNameWithoutExtension(path); |
| | | |
| | | // HDevEngineTool tool = new HDevEngineTool(directoryPath); |
| | | // tool.LoadProcedure(fileName); |
| | | |
| | | // //使用“|”作为间隔符 |
| | | // _halconToolDict[u.Id + "|" + path] = tool; |
| | | // } |
| | | // }); |
| | | // }); |
| | | //} |
| | | #endregion |
| | | |
| | | private string barCode = ""; |
| | | public string BarCode |
| | | #region 流程中抛出异常 |
| | | public override void ExceptionRaisedInMonitor(Exception ex) |
| | | { |
| | | get => barCode; |
| | | set |
| | | if (ex is ProcessException pEx) |
| | | { |
| | | if (barCode != value) |
| | | if (pEx.Level >= ExceptionLevel.Fatal) |
| | | { |
| | | barCode = value; |
| | | OnBarcodeChanged?.Invoke(value); |
| | | RaisedAlarm(pEx.Message); |
| | | MachineState = MachineState.Alarm; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | RaisedAlarm(ex.Message); |
| | | MachineState = MachineState.Alarm; |
| | | } |
| | | } |
| | | #endregion |
| | | |
| | | public string BarCode { get; set; } |
| | | |
| | | List<ProductionMeasurement> productionList = new List<ProductionMeasurement>(); |
| | | |
| | | [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(); |
| | |
| | | { |
| | | 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; |
| | |
| | | { |
| | | _pauseHandle.WaitHandle.WaitOne(); |
| | | |
| | | IDevice device = DeviceCollection.FirstOrDefault(u => u.Id == s.MotionDevice); |
| | | IDevice device = DeviceCollection.FirstOrDefault(u => u.Id == s.MotionOp.Device); |
| | | if (device == null) |
| | | throw new ProcessException($"{s.Name}拍照点位未设置运动设备"); |
| | | |
| | | IMotion motionDevice = device as IMotion; |
| | | IMotionCard motionDevice = device as IMotionCard; |
| | | |
| | | if (motionDevice == null) |
| | | throw new ProcessException($"{s.Name}拍照点位设置{device.Name}不是运动设备"); |
| | | |
| | | if (!motionDevice.MoveToPoint(null)) |
| | | var response = motionDevice.Run(s.MotionOp.OpConfig); |
| | | if (!response.Result) |
| | | { |
| | | throw new ProcessException("运动中止", null, ExceptionLevel.Info); |
| | | 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; |
| | | |
| | | ImageSet imgSet = camera.Snapshot(s.CameraOp.OpConfig); |
| | | if (imgSet == null) |
| | | return; |
| | | string imgSetId = ""; |
| | | HImage hImage = null; |
| | | try |
| | | { |
| | | hImage = CollectHImage(camera, s.CameraOp.OpConfig, out imgSetId); |
| | | } |
| | | catch (ProcessException pEx) |
| | | { |
| | | pEx.Level = ExceptionLevel.Fatal; |
| | | throw pEx; |
| | | } |
| | | |
| | | RunImageHandle(camera, s.CameraOp.OpConfig, imgSet, s.Id, s.Name, pMeasure.Measurements); |
| | | if (string.IsNullOrWhiteSpace(imgSetId)) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | RunImageHandle(camera, s.CameraOp.OpConfig, hImage, s.Id, s.Name, pMeasure.Measurements); |
| | | }); |
| | | |
| | | BarCode = ""; |
| | | LogAsync(DateTime.Now, $"{pMeasure.Barcode}测量动作完成", ""); |
| | | |
| | | return new ProcessResponse(true); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 暂停标志 |
| | | /// WaitHandle 暂停句柄 默认为非阻塞 可执行 |
| | | /// WaitResult 暂停标志 true 正常执行 false 暂停中 |
| | | /// </summary> |
| | | ManualWaitConfirm _pauseHandle = new ManualWaitConfirm() |
| | | #region 双手启动 |
| | | bool isLeftStart = false; |
| | | bool IsLeftStart |
| | | { |
| | | WaitHandle = new ManualResetEvent(true), |
| | | WaitResult = true, |
| | | }; |
| | | |
| | | [ProcessMethod("", "PauseJob", "暂停流程", InvokeType.TestInvoke)] |
| | | public ProcessResponse PauseJob(IOperationConfig opConfig, IDevice invokeDevice, IDevice sourceDevice) |
| | | { |
| | | if (_pauseHandle.WaitResult) |
| | | get => isLeftStart; |
| | | set |
| | | { |
| | | #region 板卡暂停动作 |
| | | #endregion |
| | | |
| | | _pauseHandle.WaitHandle.Reset(); |
| | | isLeftStart = value; |
| | | StartCheck(); |
| | | } |
| | | else |
| | | { |
| | | #region 板卡恢复动作 |
| | | #endregion |
| | | |
| | | _pauseHandle.WaitHandle.Set(); |
| | | } |
| | | |
| | | _pauseHandle.WaitResult = !_pauseHandle.WaitResult; |
| | | return new ProcessResponse(_pauseHandle.WaitResult); |
| | | } |
| | | |
| | | 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) |
| | | { |
| | | //检查是否全部完成 |
| | | pMeasure.Measurements.ForEach(m => |
| | | lock (pMeasure) |
| | | { |
| | | if (m.KeyUnitCollection.All(k => k.IsDone != null)) |
| | | //检查是否全部完成 |
| | | pMeasure.Measurements.ForEach(m => |
| | | { |
| | | if (!m.IsUpdated) |
| | | if (m.KeyUnitCollection.All(k => k.IsDone != null)) |
| | | { |
| | | if (m.KeyUnitCollection.Any(k => k.IsDone == false)) |
| | | if (!m.IsUpdated) |
| | | { |
| | | m.Spec.ActualValue = -999; |
| | | } |
| | | else |
| | | { |
| | | string toolKey = m.Id + "|" + m.Spec.AlgorithemPath; |
| | | if (!_halconToolDict.ContainsKey(toolKey)) |
| | | if (m.KeyUnitCollection.Any(k => k.IsDone == false)) |
| | | { |
| | | 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)) |
| | | string toolKey = m.Id + "|" + m.Spec.AlgorithemPath; |
| | | if (!_halconToolDict.ContainsKey(toolKey)) |
| | | { |
| | | LogAsync(DateTime.Now, $"{m.GetDisplayText()}{m.Spec.Code}算法异常,{error}", ""); |
| | | LogAsync(DateTime.Now, $"{m.GetDisplayText()}{m.Spec.Code}算法未初始化", ""); |
| | | 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"))}", ""); |
| | | 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); |
| | | //输出图形基元到界面 todo |
| | | OnElementUpdated?.BeginInvoke(indicator, null, null); |
| | | |
| | | SaveKeyImages(pMeasure.Barcode, m); |
| | | |
| | | m.IsUpdated = true; |
| | | } |
| | | |
| | | //输出图形基元到界面 todo |
| | | OnElementUpdated?.BeginInvoke(null, null, null); |
| | | |
| | | m.IsUpdated = true; |
| | | } |
| | | } |
| | | }); |
| | | }); |
| | | |
| | | if (!pMeasure.Measurements.All(m => m.IsUpdated)) |
| | | { |
| | | return; |
| | | if (!pMeasure.Measurements.All(m => m.IsUpdated)) |
| | | { |
| | | return; |
| | | } |
| | | } |
| | | |
| | | pMeasure.EndTime = DateTime.Now; |
| | | bool pResult = pMeasure.Measurements.All(u => u.Spec.MeasureResult == true); |
| | | OnUpdateResult?.Invoke(DateTime.Now, pResult ? 1 : 0); |
| | | OnUpdateCT?.Invoke((float)(pMeasure.EndTime.Value - pMeasure.StartTime.Value).TotalSeconds); |
| | | |
| | | LogAsync(DateTime.Now, $"{pMeasure.Barcode} 检测完成,结果 {(pResult ? "OK" : "NG")}", ""); |
| | | |
| | | if (MachineState == MachineState.Running) |
| | | MachineState = MachineState.Ready; |
| | | |
| | | //MES输出 todo |
| | | |
| | |
| | | |
| | | //数据库保存 todo |
| | | |
| | | //MeasureDict.Remove(pMeasure.Barcode); |
| | | SaveWholeImage(pMeasure); |
| | | |
| | | productionList.RemoveAll(p => p.Barcode == pMeasure.Barcode); |
| | | pMeasure.Dispose(); |
| | | } |
| | | } |
| | | |
| | | private async void RunImageHandle(CameraBase camera, IOperationConfig opConfig, ImageSet imgSet, string snapshotId, string snapshotName, List<MeasurementUint> measureList) |
| | | #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, HImage hImage, string snapshotId, string snapshotName, List<MeasurementUnit> measureList) |
| | | { |
| | | await Task.Run(() => |
| | | { |
| | |
| | | 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; |
| | | HObject images = hImage; |
| | | |
| | | if (!string.IsNullOrWhiteSpace(toolKey)) |
| | | { |
| | |
| | | return; |
| | | } |
| | | |
| | | _halconToolDict[toolKey].InputImageDic["INPUT_Image"] = imgSet.HImage; |
| | | _halconToolDict[toolKey].InputImageDic["INPUT_Image"] = hImage; |
| | | if (!_halconToolDict[toolKey].RunProcedure(out string error)) |
| | | { |
| | | LogAsync(DateTime.Now, $"{snapshotName}取图算法异常,{error}", ""); |
| | |
| | | } |
| | | } |
| | | |
| | | keyBindList.ForEach(kb => kb.FillKeyValues(resultDict)); |
| | | keyBindList.ForEach(kb => |
| | | { |
| | | kb.KeyImages.Add(image.Clone() as HImage); |
| | | kb.FillKeyValues(resultDict); |
| | | }); |
| | | }); |
| | | |
| | | image.Dispose(); |
| | | }); |
| | | |
| | | if (count.I != 1) |
| | | imgSet.HImage.Dispose(); |
| | | { |
| | | hImage?.Dispose(); |
| | | hImage = null; |
| | | } |
| | | }); |
| | | } |
| | | #endregion |