using HalconDotNet;
|
using LB_VisionProcesses.Cameras;
|
using LB_VisionProcesses.Communicators;
|
using LB_VisionFlowNode;
|
using LB_VisionProcesses;
|
using LB_VisionProcesses.Alogrithms;
|
using LB_VisionProcesses.Processes;
|
using LB_SmartVision.Forms.Pages.SettingPage;
|
using OpenCvSharp;
|
using System.Collections.Concurrent;
|
|
namespace LB_SmartVision.ProcessRun
|
{
|
public class ProcessContext : IFlowContext
|
{
|
/// <summary>
|
/// 通讯集合(Key:通讯名,Value:通讯句柄)
|
/// </summary>
|
public ObservableConcurrentDictionary<string, BaseCommunicator> dicCommunicators { get; set; }
|
|
/// <summary>
|
/// 相机集合(Key:相机SN,Value:相机句柄)
|
/// </summary>
|
public ObservableConcurrentDictionary<string, BaseCamera> dicCameras { get; set; }
|
|
public ConcurrentDictionary<string, List<Tuple<string, string>>> dicInputsMapping { get; set; }
|
|
public ConcurrentDictionary<string, List<string>> dicOutputsMapping { get; set; }
|
|
/// <summary>
|
/// 运行流程
|
/// </summary>
|
public ConcurrentDictionary<string, IProcess> dicContext = new ConcurrentDictionary<string, IProcess>();
|
|
/// <summary>
|
/// 运行标记
|
/// </summary>
|
public bool bRuning = false;
|
|
/// <summary>
|
/// 运行完成
|
/// </summary>
|
public bool bCompleted = true;
|
|
/// <summary>
|
/// 运行结果
|
/// </summary>
|
public bool Result = false;
|
|
/// <summary>
|
/// 运行消息
|
/// </summary>
|
public string Msg = string.Empty;
|
|
/// <summary>
|
/// 运行时间
|
/// </summary>
|
public double RunTime = 0;
|
|
/// <summary>
|
/// 开始时间
|
/// </summary>
|
public DateTime StartTime = DateTime.Now;
|
|
private HTuple ho_ImageWidth = new HTuple();
|
|
private HTuple ho_ImageHeight = new HTuple();
|
|
/// <summary>
|
/// 用于绘制结果图的窗口控件
|
/// </summary>
|
private HWindowControl hWindowControl = new HWindowControl();
|
|
public ProcessContext(ObservableConcurrentDictionary<string, BaseCamera> dicCameras, ObservableConcurrentDictionary<string, BaseCommunicator> dicCommunicators
|
, ConcurrentDictionary<string, List<Tuple<string, string>>> dicInputsMapping, ConcurrentDictionary<string, List<string>> dicOutputsMapping)
|
{
|
this.dicCameras = dicCameras;
|
this.dicCommunicators = dicCommunicators;
|
this.dicInputsMapping = dicInputsMapping;
|
this.dicOutputsMapping = dicOutputsMapping;
|
|
hWindowControl.Size = new System.Drawing.Size(1920, 1080);
|
|
TAlgorithm.set_display_font(hWindowControl.HalconWindow, 1080 / 20, "mono", "true", "false");
|
HOperatorSet.SetLineWidth(hWindowControl.HalconWindow, 5);
|
|
InitializeMethods();
|
InitializeDelegates();
|
|
mappingEngine = new ExpressionTreeMappingEngine(dicContext);
|
}
|
|
// 初始化
|
ExpressionTreeMappingEngine mappingEngine = null;
|
|
/// <summary>
|
/// 编译映射
|
/// </summary>
|
public void CompileMappings()
|
{
|
mappingEngine = new ExpressionTreeMappingEngine(dicContext);
|
mappingEngine.CompileMappings(dicInputsMapping);
|
}
|
|
/// <summary>
|
/// 更新输入(高性能执行)
|
/// </summary>
|
/// <param name="process"></param>
|
public void UpdateInputs(IProcess process)
|
{
|
mappingEngine.UpdateInputs(process);
|
}
|
|
public bool GetValue(string map, out object obj)
|
{
|
obj = null;
|
if (string.IsNullOrEmpty(map))
|
return false;
|
|
try
|
{
|
string[] arrMap = map.Split(".");
|
if (arrMap.Length <= 1)
|
return false;
|
|
string IndexProcessName = arrMap[0];
|
string IndexIsInputOrOutput = arrMap[1];
|
string IndexValueName = arrMap.Length == 3 ? arrMap[2] : "";
|
|
//这里的o是你所指向的输出流程
|
object o = dicContext[IndexProcessName];
|
|
if (o != null && o is IProcess i)
|
{
|
if (i.strProcessName != IndexProcessName)
|
return false;
|
|
if (IndexValueName == "Result")
|
{
|
switch (IndexIsInputOrOutput)
|
{
|
case "Inputs":
|
//实际上不允许索引的值是输入
|
obj = i.Result;
|
break;
|
case "Outputs":
|
obj = i.Result;
|
break;
|
default:
|
return false;
|
}
|
}
|
else
|
{
|
//赋值参数
|
switch (IndexIsInputOrOutput)
|
{
|
case "Inputs":
|
obj = i.Params.Inputs[IndexValueName];
|
break;
|
case "Outputs":
|
obj = i.Params.Outputs[IndexValueName];
|
break;
|
default:
|
return false;
|
}
|
}
|
return true;
|
}
|
else
|
return false;
|
}
|
catch { obj = null; return false; }
|
}
|
|
public bool GetStringOutput(string map, out string str)
|
{
|
bool res = GetValue(map, out object obj);
|
str = ProcessParams.ConvertToString(obj);
|
return res;
|
}
|
|
public bool GetImage(Layout layout, out HObject InputImage, out HObject RecordImage)
|
{
|
InputImage = null; RecordImage = null;
|
try
|
{
|
if (layout == null)
|
return false;
|
|
string ProcessName = layout.ProcessName;
|
string InputImageMap = layout.InputImage;
|
string RecordImageMap = layout.RecordImage;
|
|
string[] arrOutputs = new string[] { };
|
string IndexProcessName = string.Empty;
|
string IndexIsInputOrOutput = string.Empty;
|
string IndexValueName = string.Empty;
|
|
#region 获取输入图片
|
if (dicContext.ContainsKey(InputImageMap))
|
{
|
object o_InputImage = ((IProcess)dicContext[InputImageMap]).OutputImage;
|
if (o_InputImage is HObject ho_image && ho_image.IsInitialized())
|
InputImage = ho_image;
|
else if (o_InputImage is Bitmap)
|
{
|
//将Mat转换为HObject
|
TAlgorithm.Bitmap2HObject((Bitmap)o_InputImage, out HObject ho_RecordImage);
|
InputImage = ho_RecordImage;
|
}
|
else if (o_InputImage is Mat)
|
{
|
//将Mat转换为HObject
|
TAlgorithm.Mat2HObject((Mat)o_InputImage, out HObject ho_RecordImage);
|
InputImage = ho_RecordImage;
|
}
|
|
if (InputImage != null && InputImage.IsInitialized())
|
{
|
HOperatorSet.GetImageSize(InputImage, out ho_ImageWidth, out ho_ImageHeight);
|
|
//图片尺寸变化才更新窗口尺寸[提高速度]
|
if ((ho_ImageWidth.Length > 0 && ho_ImageWidth.TupleInt() != hWindowControl.Size.Width)
|
|| (ho_ImageHeight.Length > 0 && ho_ImageHeight.TupleInt() != hWindowControl.Size.Height))
|
{
|
hWindowControl.Invoke(new Action(() =>
|
{
|
hWindowControl.Size = new System.Drawing.Size(ho_ImageWidth, ho_ImageHeight);
|
}));
|
}
|
}
|
|
if (InputImage != null)
|
TAlgorithm.DispImage(InputImage, hWindowControl.HalconWindow);
|
}
|
#endregion
|
|
#region 绘制结果图
|
string[] RecordImageMaps = RecordImageMap.Split(';');
|
int FontSize = ho_ImageHeight.I / 80 > 0 ? ho_ImageHeight.I / 80 : 8;
|
int MsgSize = ho_ImageHeight.I / 40 > 0 ? ho_ImageHeight.I / 80 : 20;
|
|
TAlgorithm.set_display_font(hWindowControl.HalconWindow, FontSize, "mono", "true", "false");
|
|
foreach (var item in RecordImageMaps)
|
{
|
if (dicContext.ContainsKey(item))
|
{
|
ObjectRecord objectRecord = ((IProcess)dicContext[item]).Record;
|
if (objectRecord != null)
|
{
|
objectRecord.Display(hWindowControl.HalconWindow);
|
objectRecord.Dispose();
|
}
|
}
|
}
|
|
TAlgorithm.set_display_font(hWindowControl.HalconWindow, MsgSize, "mono", "true", "false");
|
|
if (Result)
|
{
|
Msg = "运行成功";
|
HOperatorSet.SetColor(hWindowControl.HalconWindow, "green");
|
}
|
else
|
HOperatorSet.SetColor(hWindowControl.HalconWindow, "red");
|
|
TAlgorithm.DispMsg(Msg, hWindowControl.HalconWindow, Result ? "green" : "red", 0, 0);
|
HOperatorSet.DumpWindowImage(out RecordImage, hWindowControl.HalconWindow);
|
#endregion
|
|
return true;
|
}
|
catch { return false; }
|
}
|
|
public bool GetCsv(CsvSetting csv, out List<string> DataTitle, out Dictionary<string, object> ResultData)
|
{
|
// 数据标题
|
DataTitle = (new string[] { "名称", "时间", "耗时", "结果", "原因" }).ToList();
|
|
ResultData = new Dictionary<string, object>();
|
try
|
{
|
ResultData.Add("名称", csv.ProcessName);
|
ResultData.Add("时间", $"'{DateTime.Now.ToString("HH:mm:ss.ff")}");
|
ResultData.Add("耗时", $"{RunTime.ToString("F2")}");
|
ResultData.Add("结果", Result);
|
ResultData.Add("原因", Msg);
|
|
for (int i = 0; i < csv.Others.Count; i++)
|
{
|
string title = $"数据{i}";
|
string map = csv.Others[i];
|
GetValue(map, out object obj);
|
if (obj == null)
|
continue;
|
|
Type type = obj.GetType();
|
switch (type)
|
{
|
case Type t when t == typeof(double):
|
ResultData.Add(title, ((double)obj).ToString("F4"));
|
DataTitle.Add(title);
|
break;
|
default:
|
ResultData.Add(title, ProcessParams.ConvertToString(obj));
|
DataTitle.Add(title);
|
break;
|
}
|
}
|
|
return true;
|
}
|
catch { return false; }
|
}
|
|
/// <summary>
|
/// 非分支节点的统一调用方式
|
/// </summary>
|
private void RunNodeAsync(FlowNode node)
|
{
|
// 走默认分支
|
node.BranchIndex = "0";
|
string ProcessName = node.Text;
|
try
|
{
|
if (dicContext.TryGetValue(ProcessName, out IProcess obj)
|
&& obj is IProcess process)
|
{
|
process.InputImage = null;
|
UpdateInputs(process);
|
|
// 不同节点跳过的方式不同
|
if (node.Break)
|
{
|
switch (node.Description)
|
{
|
case "T306通讯":
|
node.BranchIndex = "0";
|
node.Result = true;
|
process.Result = false;
|
process.Msg = "T306通讯未启用";
|
return;
|
default:
|
node.BranchIndex = "0";
|
node.Result = true;
|
process.Result = true;
|
process.Msg = string.Empty;
|
return;
|
}
|
}
|
|
if (!process.Run())
|
{
|
node.Result = false;
|
Result &= false;
|
|
if (!string.IsNullOrEmpty(process.Msg)
|
&& !Msg.Contains($"[{process.strProcessName}]{process.Msg}"))
|
Msg += $"[{process.strProcessName}]{process.Msg}";
|
}
|
else
|
node.Result = true;
|
}
|
}
|
catch (Exception ex)
|
{
|
node.Result = false;
|
Result &= false;
|
Msg += $"[{ProcessName}]运行发生意外,{ex.Message}";
|
}
|
}
|
|
#region 相机工具
|
|
[Node("相机取图", "相机", "Basic", "相机取图")]
|
public void 相机取图(FlowNode node) { RunNodeAsync(node); }
|
|
|
#endregion
|
|
|
#region 通讯工具
|
[Node("通讯测试", "通讯", "Basic", "通讯测试")]
|
public void 通讯测试(FlowNode node) { RunNodeAsync(node); }
|
|
//[Node("西门子S7", "通讯", "Basic", "西门子S7")]
|
//public void 西门子S7(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("T306通讯", "通讯", "Basic", "T306通讯")]
|
public void T306通讯(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("ModbusRTU", "通讯", "Basic", "ModbusRTU")]
|
public void ModbusRTU(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("ModbusTCP", "通讯", "Basic", "ModbusTCP")]
|
public void ModbusTCP(FlowNode node) { RunNodeAsync(node); }
|
#endregion
|
|
#region Halcon2D算法
|
[Node("Halcon2D_模板匹配工具", "Halcon2D算法", "Basic", "Halcon2D_模板匹配工具")]
|
public void Halcon2D_模板匹配工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("Halcon2D_多模板匹配工具", "Halcon2D算法", "Basic", "Halcon2D_多模板匹配工具")]
|
public void Halcon2D_多模板匹配工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("Halcon2D_斑点工具", "Halcon2D算法", "Basic", "Halcon2D_斑点工具")]
|
public void Halcon2D_斑点工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("Halcon2D_读码工具", "Halcon2D算法", "Basic", "Halcon2D_读码工具")]
|
public void Halcon2D_读码工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("Halcon2D_找线工具", "Halcon2D算法", "Basic", "Halcon2D_找线工具")]
|
public void Halcon2D_找线工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("Halcon2D_图像增强工具", "Halcon2D算法", "Basic", "Halcon2D_图像增强工具")]
|
public void Halcon2D_图像增强工具(FlowNode node) { RunNodeAsync(node); }
|
|
#endregion
|
|
#region 分割模型
|
|
[Node("语义分割工具", "BigModel", "Basic", "语义分割工具")]
|
public void 语义分割工具(FlowNode node) { RunNodeAsync(node); }
|
|
#endregion
|
|
#region OpenCvSharp算法
|
[Node("OpenCvSharp_模板匹配工具", "OpenCvSharp算法", "Basic", "OpenCvSharp_模板匹配工具")]
|
public void OpenCvSharp_模板匹配工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("OpenCvSharp_斑点工具", "OpenCvSharp算法", "Basic", "OpenCvSharp_斑点工具")]
|
public void OpenCvSharp_斑点工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("OpenCvSharp_找线工具", "OpenCvSharp算法", "Basic", "OpenCvSharp_找线工具")]
|
public void OpenCvSharp_找线工具(FlowNode node) { RunNodeAsync(node); }
|
#endregion
|
|
#region 图像处理工具
|
[Node("单图像处理工具", "图像处理工具", "Basic", "单图像处理工具")]
|
public void 单图像处理工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("点云转换工具", "图像处理工具", "Basic", "点云转换工具")]
|
public void 点云转换工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("畸变矫正工具", "图像处理工具", "Basic", "畸变矫正工具")]
|
public void 畸变矫正工具(FlowNode node) { RunNodeAsync(node); }
|
#endregion
|
|
#region 工具
|
[Node("脚本", "工具", "Basic", "脚本")]
|
public void 脚本(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("轮胎计数", "工具", "Basic", "轮胎计数")]
|
public void 轮胎计数(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("创建绘制工具", "工具", "Basic", "创建绘制工具")]
|
public void 创建绘制工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("创建固定跟随", "工具", "Basic", "创建固定跟随")]
|
public void 创建固定跟随(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("创建线段工具", "工具", "Basic", "创建线段工具")]
|
public void 创建线段工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("创建垂线工具", "工具", "Basic", "创建垂线工具")]
|
public void 创建垂线工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("线线交点工具", "工具", "Basic", "线线交点工具")]
|
public void 线线交点工具(FlowNode node) { RunNodeAsync(node); }
|
|
[Node("延时", "工具", "Basic", "延时")]
|
public void 延时(FlowNode node) { RunNodeAsync(node); }
|
#endregion
|
|
|
[Node("开始", "控制", "Logic", "开始")]
|
public override void 开始(FlowNode node)
|
{
|
StartTime = DateTime.Now;
|
RunTime = 0;
|
|
Result = true;
|
bRuning = true;
|
bCompleted = false;
|
Msg = string.Empty;
|
|
HOperatorSet.ClearWindow(hWindowControl.HalconWindow);
|
HOperatorSet.SetColor(hWindowControl.HalconWindow, "green");
|
}
|
|
[Node("结束", "控制", "Logic", "结束")]
|
public override void 结束(FlowNode node)
|
{
|
bRuning = false;
|
bCompleted = true;
|
|
if (Result)
|
Msg = "运行成功";
|
|
RunTime = (DateTime.Now - StartTime).TotalMilliseconds;
|
}
|
|
[Node("分支", "控制", "Logic", "分支")]
|
public override void 分支(FlowNode node)
|
{
|
string ProcessName = node.Text;
|
node.BranchIndex = "-1";
|
node.Result = true;
|
try
|
{
|
if (dicContext.TryGetValue(ProcessName, out IProcess obj) && obj is ScriptTool script)
|
{
|
if (node.Break)
|
{
|
node.BranchIndex = "0";
|
node.Result = true;
|
script.Result = true;
|
script.Msg = string.Empty;
|
return;
|
}
|
|
if (script.Params.Outputs.Count >= 2)
|
{
|
RunNodeAsync(node);
|
if (!script.Result)
|
{
|
node.Result = false;
|
Result &= false;
|
Msg = $"[{ProcessName}]{script.Msg}";
|
}
|
|
for (int i = 0; i < script.Params.Outputs.Count; i++)
|
{
|
if (script.Params.Outputs[$"Branch{i}"] != null
|
&& Convert.ToBoolean(script.Params.Outputs[$"Branch{i}"]))
|
{
|
node.BranchIndex = i.ToString();
|
return;
|
}
|
}
|
|
node.Result = false;
|
Result &= false;
|
Msg = $"[{ProcessName}]无满足条件的分支";
|
}
|
else
|
{
|
node.Result = false;
|
Result &= false;
|
Msg = $"[{ProcessName}]分支数量不为2";
|
}
|
}
|
else
|
{
|
node.Result = false;
|
Result &= false;
|
}
|
}
|
catch (Exception ex)
|
{
|
node.Result = false;
|
Result &= false;
|
Msg = $"[{ProcessName}]运行发生意外,{ex.Message}";
|
}
|
}
|
|
[Node("多分支", "控制", "Logic", "多分支")]
|
public override void 多分支(FlowNode node)
|
{
|
if (node.Break)
|
{
|
node.BranchIndex = "0";
|
node.Result = true;
|
return;
|
}
|
|
string ProcessName = node.Text;
|
node.BranchIndex = "-1";
|
node.Result = true;
|
try
|
{
|
if (dicContext.TryGetValue(ProcessName, out IProcess obj) && obj is ScriptTool script)
|
{
|
if (node.Break)
|
{
|
node.BranchIndex = "0";
|
node.Result = true;
|
script.Result = true;
|
script.Msg = string.Empty;
|
return;
|
}
|
|
if (script.Params.Outputs.Count >= 1)
|
{
|
RunNodeAsync(node);
|
if (!script.Result)
|
{
|
node.Result = false;
|
Result &= false;
|
Msg = $"[{ProcessName}]{script.Msg}";
|
}
|
|
for (int i = 0; i < script.Params.Outputs.Count; i++)
|
{
|
if (script.Params.Outputs[$"Branch{i}"] != null
|
&& Convert.ToBoolean(script.Params.Outputs[$"Branch{i}"]))
|
{
|
node.BranchIndex = i.ToString();
|
return;
|
}
|
}
|
|
node.Result = false;
|
Result &= false;
|
Msg = $"[{ProcessName}]无满足条件的分支";
|
}
|
else
|
{
|
node.Result = false;
|
Result &= false;
|
Msg = $"[{ProcessName}]多分支数量小于1";
|
}
|
}
|
else
|
{
|
node.Result = false;
|
Result &= false;
|
}
|
}
|
catch (Exception ex)
|
{
|
node.Result = false;
|
Result &= false;
|
Msg = $"[{ProcessName}]运行发生意外,{ex.Message}";
|
}
|
}
|
}
|
}
|