using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Threading; using PropertyGridLib.Attributes; using TeamAAS.FlowEditor.Execution; using TeamAAS.FlowEditor.Models; using TeamAAS.FlowEditor.Plugins; using Plugins.Vm.Models; namespace Plugins.Vm { // ───────────────────────────────────────────────────────────────────── // VM 模块算子(算子级节点,操作习惯对齐 Halcon/Vp 算子): // - 属性面板开放简单配置:方案路径 / 流程名 / 结果键声明 / 失败策略; // - "高级编辑"按钮调起 VisionMaster 客户端打开该方案 —— 模块增删、参数调整、 // 调试全部在 VM 原生界面完成(与 VP 每个工具单独编辑的体验对齐); // - 运行时加载方案 → 运行指定流程 → 按声明的结果键读取模块结果(图像键走 // GetOutputImageV2,其余键走 GetOutputValue/GetResultValue,均经 VmRuntime 容错)。 // ───────────────────────────────────────────────────────────────────── [Serializable] [Plugin("VM模块", PluginCategory.Vm模块, typeof(VmModuleModel), "Hexagon", NodeShape = NodeCategory.Normal, IsSubFlowNode = true, VisionCategory = VisionPlugin.其他, Description = "VisionMaster 模块算子:绑定方案与流程,属性面板声明结果键;高级编辑调起 VM 客户端改方案")] public class VmModulePlugin : BasePlugin { public override List DeclareOutputs() { var list = new List { new OutputField("结果", typeof(bool)), }; var keys = Model?.ResultKeys ?? new List(); foreach (var key in keys) { if (string.IsNullOrWhiteSpace(key)) continue; list.Add(new OutputField(key, typeof(object))); } return list; } public override NodeRunStatus PluginRun(CancellationToken token, out Dictionary results) { var res = new Dictionary(); results = res; NodeRunStatus Fail(string m) { res["Error"] = m; res["结果"] = false; Log(1, $"VM模块失败: {m}", TeamAAS.LogLevel.Error); return NodeRunStatus.Failed; } try { if (string.IsNullOrWhiteSpace(Model.SolutionPath)) return Fail("未配置 VM 方案路径"); if (string.IsNullOrWhiteSpace(Model.ProcedureName)) return Fail("未配置流程名"); Log(3, $"VM模块:加载方案 {Model.SolutionPath},流程 [{Model.ProcedureName}]"); var solution = VmRuntime.LoadSolution(Model.SolutionPath); var procedure = VmRuntime.GetProcedure(solution, Model.ProcedureName); if (procedure == null) return Fail($"方案中不存在流程 [{Model.ProcedureName}]"); if (!VmRuntime.Run(procedure)) return Fail("VM 流程运行失败"); // 按声明的结果键读取模块结果(图像/数值通用,经 VmRuntime 容错) int got = 0; foreach (var key in Model.ResultKeys ?? new List()) { if (string.IsNullOrWhiteSpace(key)) continue; var value = VmRuntime.GetModuleOutput(procedure, key); res[key] = value; if (value != null) got++; Log(4, $"结果键[{key}] = {(value?.GetType().Name ?? "null")}"); } res["结果"] = true; Log(2, $"VM模块完成:取到 {got}/{(Model.ResultKeys?.Count ?? 0)} 个结果键"); return NodeRunStatus.Success; } catch (Exception ex) { return Fail(ex.Message); } } } } namespace Plugins.Vm.Models { using TeamAAS.FlowEditor.Plugins; /// VM 模块算子模型。 [Serializable] public class VmModuleModel : BasePluginModel { [Category("I.方案")] [DisplayName("1.VM方案路径")] [Description("海康 VisionMaster 方案(.sol)文件路径;运行前由适配层加载")] public string SolutionPath { get; set; } = ""; [Category("I.方案")] [DisplayName("2.流程名")] [Description("方案内要执行的流程名称")] public string ProcedureName { get; set; } = ""; [Category("II.结果")] [DisplayName("1.结果键列表")] [Description("要读取的模块结果终端名(每行一个,如 ImageData/Score);运行后每个键成为本节点输出")] public List ResultKeys { get; set; } = new List(); [Category("III.选项")] [DisplayName("1.VM客户端路径")] [Description("VisionMaster 客户端可执行文件路径(高级编辑用);留空则用系统文件关联打开方案")] public string VmClientPath { get; set; } = ""; // ── 高级编辑:调起 VisionMaster 客户端打开方案 ── [Category("IV.高级编辑")] [DisplayName("1.打开 VM 编辑")] [Description("调起 VisionMaster 客户端打开该方案(模块增删/参数调整/调试在 VM 原生界面完成)")] [Button("高级编辑", nameof(OpenAdvancedEditor))] public object AdvancedEditButton { get; set; } public void OpenAdvancedEditor() { // 经安全封装调起 VisionMaster:扩展名白名单(仅 .sol)+ 参数列表启动,防命令注入 VmSolutionLauncher.OpenSolution(SolutionPath, VmClientPath); } } }