using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Threading;
using System.Windows.Forms;
using Cognex.VisionPro;
using PropertyGridLib.Attributes;
using PropertyGridLib.Controls;
using System.ComponentModel;
using TeamAAS.Camera.Images;
using TeamAAS.FlowEditor.Execution;
using TeamAAS.FlowEditor.Models;
using TeamAAS.FlowEditor.Plugins;
using TeamAAS.FlowEngine.FormulaData;
using Plugins.Vpp.Converters;
namespace Plugins.Vpp
{
///
/// VisionPro 原生工具反射支撑(算子级节点的基础设施)。
/// 设计:不依赖具体工具程序集(CogBlob/PMAlign/Caliper…),按工具类名在已加载程序集中
/// 反射创建/运行/取结果 —— 新增"某个 VP 工具"的算子节点时零引用成本;
/// 工具实例挂在模型上(object 承载),配置随流程文件(BinaryFormatter)整体持久化,
/// 与 VppToolBlockModel.ToolBlock 同一持久化模式。
///
internal static class VpToolSupport
{
///
/// VisionPro 工具/显示程序集清单(简单名)。这些程序集是懒加载的:应用没显式用到之前
/// 不在 AppDomain 里,反射扫描会"找不到 CogBlobTool"。按类型名查工具前先主动加载一轮,
/// 加载失败(本机未装 VisionPro)静默跳过。
///
private static readonly string[] CognexToolAssemblies =
{
"Cognex.VisionPro.ToolGroup",
"Cognex.VisionPro.Blob",
"Cognex.VisionPro.Blob.Controls",
"Cognex.VisionPro.PMAlign",
"Cognex.VisionPro.PMAlign.Controls",
"Cognex.VisionPro.Caliper",
"Cognex.VisionPro.Caliper.Controls",
"Cognex.VisionPro.ID",
"Cognex.VisionPro.Display.Controls",
"Cognex.VisionPro.Controls",
"Cognex.VisionPro.ToolGroup.Controls",
};
private static void EnsureCognexAssembliesLoaded()
{
foreach (var name in CognexToolAssemblies)
{
try { Assembly.Load(name); } catch { /* 未安装/缺失 → 跳过 */ }
}
}
/// 按简单类名(如 "CogBlobTool")在已加载程序集中查找 ICogTool 实现。
/// 找不到时先主动加载 VisionPro 工具程序集再重扫一次(懒加载问题)。
public static Type FindToolType(string simpleName)
{
var found = FindToolTypeInLoaded(simpleName);
if (found != null) return found;
EnsureCognexAssembliesLoaded();
return FindToolTypeInLoaded(simpleName);
}
private static Type FindToolTypeInLoaded(string simpleName)
{
Type found = null;
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
if (asm.IsDynamic) continue;
foreach (var t in asm.GetTypes())
{
if (t.Name == simpleName && typeof(ICogTool).IsAssignableFrom(t) && !t.IsAbstract)
return t;
if (t.Name == simpleName) found = t;
}
}
catch { }
}
return found;
}
/// 创建默认工具实例。失败抛异常(节点报错并提示)。
public static object CreateTool(string simpleName)
{
var type = FindToolType(simpleName)
?? throw new InvalidOperationException($"未找到 VisionPro 工具类型 {simpleName}(检查 VPP 运行库是否已加载)");
return Activator.CreateInstance(type);
}
///
/// 设置输入图像(GenImage → ICogImage 自动转换)。
/// 兼容 ICogRecord(自动抽取其中的 ICogImage,如绑定到工具块的「输出图层N」);
/// 类型不符时抛出带实际类型信息的异常(而不是反射的"Object 无法转换 ICogImage")。
///
public static void SetInputImage(object tool, object image)
{
if (tool == null || image == null) return;
object cog;
if (image is GenImage gi)
cog = GenImageConverter.ToCogImage(gi);
else if (image is ICogImage ci)
cog = ci;
else if (image is ICogRecord rec)
{
cog = FindRecordImage(rec) ?? throw new InvalidOperationException(
$"输入图像绑定到了记录({rec.GetType().Name}),但其中没有可用的 ICogImage(检查「输入图像」应绑定图像输出而非记录/图层)");
}
else
cog = image;
var prop = tool.GetType().GetProperty("InputImage");
var expect = prop?.PropertyType;
if (cog != null && expect != null && !expect.IsInstanceOfType(cog))
throw new InvalidOperationException(
$"输入图像类型不符:实际 {cog.GetType().Name},需要 {expect.Name}(检查「输入图像」绑定来源是否为图像输出)");
prop?.SetValue(tool, cog);
}
/// 从工具运行记录里递归找第一张 ICogImage(记录树:Content 或 SubRecords)。
private static ICogImage FindRecordImage(ICogRecord record)
{
if (record == null) return null;
try
{
if (record.Content is ICogImage img) return img;
var subs = record.SubRecords;
if (subs != null)
{
foreach (ICogRecord sub in subs)
{
var found = FindRecordImage(sub);
if (found != null) return found;
}
}
}
catch { }
return null;
}
/// 运行工具(ICogTool.Run)。
public static void Run(object tool) => ((ICogTool)tool).Run();
///
/// 按属性路径取值,支持 索引 与 0参方法:如 "Results.Count"、"Results[0].Score"、
/// "Results[0].GetPose.TranslationX"(GetPose 为 0 参方法时自动调用)。任一环节失败返回 null。
///
public static object GetPath(object root, string path)
{
try
{
object cur = root;
foreach (var seg in path.Split('.'))
{
if (cur == null) return null;
var m = System.Text.RegularExpressions.Regex.Match(seg, @"^(?[^\[]+)(\[(?\d+)\])?$");
// 方法段带 "()"(如 GetBlobs())——成员查找用裸名(GetBlobs)
var name = m.Groups["name"].Value.Replace("()", "");
cur = GetMember(cur, name);
if (cur == null) return null;
if (m.Groups["idx"].Success && int.TryParse(m.Groups["idx"].Value, out var idx))
cur = GetIndexed(cur, idx);
}
return cur;
}
catch { return null; }
}
private static object GetMember(object obj, string name)
{
if (obj == null || string.IsNullOrEmpty(name)) return null;
var type = obj.GetType();
var prop = type.GetProperty(name);
if (prop != null) return prop.GetValue(obj);
var field = type.GetField(name);
if (field != null) return field.GetValue(obj);
// GetXxx 属性不存在时尝试同名 GetXxx() 方法(如 Blob:GetBlobs/GetBlobMeasure)
var method = type.GetMethod(name, Type.EmptyTypes)
?? (name.StartsWith("Get") ? null : type.GetMethod("Get" + name, Type.EmptyTypes));
if (method != null)
{
try { return method.Invoke(obj, null); } catch { return null; }
}
// 集合默认项:名字为空/First 时取第 0 项
if (name == "First") return GetIndexed(obj, 0);
return null;
}
private static object GetIndexed(object obj, int index)
{
try
{
if (obj is System.Collections.IList list) return index < list.Count ? list[index] : null;
var indexer = obj?.GetType().GetProperties()
.FirstOrDefault(p => p.GetIndexParameters().Length == 1 && p.GetIndexParameters()[0].ParameterType == typeof(int));
if (indexer != null && CountOf(obj) > index) return indexer.GetValue(obj, new object[] { index });
}
catch { }
return null;
}
private static int CountOf(object obj)
{
try
{
var count = obj?.GetType().GetProperty("Count")?.GetValue(obj);
return count is int i ? i : 0;
}
catch { return 0; }
}
///
/// 打开工具的原生编辑器(VisionPro 惯例:CogXxxTool ↔ CogXxxEditV2 控件,Subject 属性挂工具)。
/// 编辑控件可能与工具不在同一程序集(如 CogBlobTool 的 EditV2 在 ToolGroup.Controls),
/// 因此在全部已加载程序集中按类名查找。找不到返回 false(节点日志提示改用属性面板)。
///
public static bool OpenNativeEditor(object tool, string title)
{
if (tool == null) return false;
var type = tool.GetType();
var baseName = type.Name; // 如 CogBlobTool
var shortName = baseName.Replace("Tool", ""); // VisionPro 编辑控件命名惯例去掉 Tool:CogBlobEditV2
foreach (var suffix in new[] { "EditV2", "Edit", "EditControl" })
{
var ctlType = FindControlType(baseName + suffix)
?? FindControlType(shortName + suffix);
if (ctlType == null) continue;
try
{
var ctl = (Control)Activator.CreateInstance(ctlType);
ctlType.GetProperty("Subject")?.SetValue(ctl, tool);
using (var form = new Form { Text = title ?? type.Name, Width = 1280, Height = 860, StartPosition = FormStartPosition.CenterScreen })
{
ctl.Dock = DockStyle.Fill;
form.Controls.Add(ctl);
Views.CogGdiRender.Apply(form); // 编辑器内部显示控件统一切 GDI 渲染(远程环境白屏修复)
form.ShowDialog();
}
return true;
}
catch { }
}
return false;
}
/// 在全部已加载程序集中按简单类名查找 WinForms 控件类型。
/// 找不到时先主动加载 VisionPro 编辑控件程序集再重扫一次(懒加载问题)。
private static Type FindControlType(string simpleName)
{
var found = FindControlTypeInLoaded(simpleName);
if (found != null) return found;
EnsureCognexAssembliesLoaded();
return FindControlTypeInLoaded(simpleName);
}
private static Type FindControlTypeInLoaded(string simpleName)
{
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
if (asm.IsDynamic) continue;
foreach (var t in asm.GetTypes())
{
if (t.Name == simpleName && typeof(Control).IsAssignableFrom(t) && !t.IsAbstract)
return t;
}
}
catch { }
}
return null;
}
///
/// 学习工具属性树的可读标量路径(迁移 VisionPro「添加终端」的选树体验):
/// 递归展开公共属性与字段(深度≤5、总量封顶 300),记录 基元/字符串/日期 值的路径;
/// 集合展开前 3 个元素标 [i](如 Results.GetBlobs[0].CenterOfMassX)。
/// 值为 null 的属性也登记(路径有效,只是此刻无值——Blob 未启用的测量项即此类)。
///
public static List LearnTerminalPaths(object root)
{
var paths = new List();
if (root == null) return paths;
var seen = new HashSet