using Bro.Common.Base;
|
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
|
{
|
[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<IShapeElement> OnElementUpdated;
|
public event Action<MachineState> 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.AlignName}未设置可用拍照点位");
|
|
if (u.ImageSeq < 1)
|
throw new ProcessException($"{u.AlignName}图片序号小于1");
|
|
var algo = Config.KeyAlgorithemCollection.FirstOrDefault(a => a.Id == u.KeyAlgorithemId);
|
if (algo == null)
|
throw new ProcessException($"{u.AlignName}未设置检测算法");
|
|
u.KeyAlgorithemPath = algo.AlgorithemPath;
|
LoadHalconTool(u.KeyAlgorithemPath, u.AlignName);
|
|
var resultSet = Config.KeyResultCollection.FirstOrDefault(r => r.Id == u.KeyResultId);
|
if (resultSet == null)
|
throw new ProcessException($"{u.AlignName}未设置检测结果配置");
|
|
u.KeyResultList = new List<string>(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<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();
|
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;
|
|
string imgSetId = "";
|
HImage hImage = null;
|
try
|
{
|
hImage = CollectHImage(camera, s.CameraOp.OpConfig, out imgSetId);
|
}
|
catch (ProcessException pEx)
|
{
|
pEx.Level = ExceptionLevel.Fatal;
|
throw pEx;
|
}
|
|
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);
|
}
|
|
#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);
|
//输出图形基元到界面 todo
|
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);
|
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
|
|
//Excel报表输出 todo
|
|
//数据库保存 todo
|
|
SaveWholeImage(pMeasure);
|
|
productionList.RemoveAll(p => p.Barcode == pMeasure.Barcode);
|
pMeasure.Dispose();
|
}
|
}
|
|
#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 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 = hImage;
|
|
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"] = 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.AlignName))}未在图片获取序列中", "");
|
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<string, double> resultDict = null;
|
|
var keyBindList = keyBindCollection.Where(u => u.Key == k.Key).ToList();
|
|
string keyToolKey = k.AlignName + "|" + k.KeyAlgorithemPath;
|
if (!_halconToolDict.ContainsKey(keyToolKey))
|
{
|
LogAsync(DateTime.Now, $"{k.AlignName}检测算法未初始化", "");
|
}
|
else
|
{
|
_halconToolDict[keyToolKey].InputImageDic["INPUT_Image"] = image;
|
if (!_halconToolDict[keyToolKey].RunProcedure(out string error))
|
{
|
LogAsync(DateTime.Now, $"{k.AlignName}检测算法异常,{error}", "");
|
}
|
else
|
{
|
var results = _halconToolDict[keyToolKey].GetResultTuple("OUTPUT_Results").HTupleToDouble();
|
if (results.Count == 0 || results.Any(u => u < 0))
|
{
|
LogAsync(DateTime.Now, $"{k.AlignName}检测结果异常", "");
|
}
|
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
|
}
|
}
|