VmModuleNodes.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using System.Threading;
  7. using PropertyGridLib.Attributes;
  8. using TeamAAS.FlowEditor.Execution;
  9. using TeamAAS.FlowEditor.Models;
  10. using TeamAAS.FlowEditor.Plugins;
  11. using Plugins.Vm.Models;
  12. namespace Plugins.Vm
  13. {
  14. // ─────────────────────────────────────────────────────────────────────
  15. // VM 模块算子(算子级节点,操作习惯对齐 Halcon/Vp 算子):
  16. // - 属性面板开放简单配置:方案路径 / 流程名 / 结果键声明 / 失败策略;
  17. // - "高级编辑"按钮调起 VisionMaster 客户端打开该方案 —— 模块增删、参数调整、
  18. // 调试全部在 VM 原生界面完成(与 VP 每个工具单独编辑的体验对齐);
  19. // - 运行时加载方案 → 运行指定流程 → 按声明的结果键读取模块结果(图像键走
  20. // GetOutputImageV2,其余键走 GetOutputValue/GetResultValue,均经 VmRuntime 容错)。
  21. // ─────────────────────────────────────────────────────────────────────
  22. [Serializable]
  23. [Plugin("VM模块", PluginCategory.Vm模块, typeof(VmModuleModel), "Hexagon",
  24. NodeShape = NodeCategory.Normal, IsSubFlowNode = true, VisionCategory = VisionPlugin.其他,
  25. Description = "VisionMaster 模块算子:绑定方案与流程,属性面板声明结果键;高级编辑调起 VM 客户端改方案")]
  26. public class VmModulePlugin : BasePlugin<VmModuleModel>
  27. {
  28. public override List<OutputField> DeclareOutputs()
  29. {
  30. var list = new List<OutputField>
  31. {
  32. new OutputField("结果", typeof(bool)),
  33. };
  34. var keys = Model?.ResultKeys ?? new List<string>();
  35. foreach (var key in keys)
  36. {
  37. if (string.IsNullOrWhiteSpace(key)) continue;
  38. list.Add(new OutputField(key, typeof(object)));
  39. }
  40. return list;
  41. }
  42. public override NodeRunStatus PluginRun(CancellationToken token, out Dictionary<string, object> results)
  43. {
  44. var res = new Dictionary<string, object>();
  45. results = res;
  46. NodeRunStatus Fail(string m) { res["Error"] = m; res["结果"] = false; Log(1, $"VM模块失败: {m}", TeamAAS.LogLevel.Error); return NodeRunStatus.Failed; }
  47. try
  48. {
  49. if (string.IsNullOrWhiteSpace(Model.SolutionPath))
  50. return Fail("未配置 VM 方案路径");
  51. if (string.IsNullOrWhiteSpace(Model.ProcedureName))
  52. return Fail("未配置流程名");
  53. Log(3, $"VM模块:加载方案 {Model.SolutionPath},流程 [{Model.ProcedureName}]");
  54. var solution = VmRuntime.LoadSolution(Model.SolutionPath);
  55. var procedure = VmRuntime.GetProcedure(solution, Model.ProcedureName);
  56. if (procedure == null) return Fail($"方案中不存在流程 [{Model.ProcedureName}]");
  57. if (!VmRuntime.Run(procedure))
  58. return Fail("VM 流程运行失败");
  59. // 按声明的结果键读取模块结果(图像/数值通用,经 VmRuntime 容错)
  60. int got = 0;
  61. foreach (var key in Model.ResultKeys ?? new List<string>())
  62. {
  63. if (string.IsNullOrWhiteSpace(key)) continue;
  64. var value = VmRuntime.GetModuleOutput(procedure, key);
  65. res[key] = value;
  66. if (value != null) got++;
  67. Log(4, $"结果键[{key}] = {(value?.GetType().Name ?? "null")}");
  68. }
  69. res["结果"] = true;
  70. Log(2, $"VM模块完成:取到 {got}/{(Model.ResultKeys?.Count ?? 0)} 个结果键");
  71. return NodeRunStatus.Success;
  72. }
  73. catch (Exception ex)
  74. {
  75. return Fail(ex.Message);
  76. }
  77. }
  78. }
  79. }
  80. namespace Plugins.Vm.Models
  81. {
  82. using TeamAAS.FlowEditor.Plugins;
  83. /// <summary>VM 模块算子模型。</summary>
  84. [Serializable]
  85. public class VmModuleModel : BasePluginModel
  86. {
  87. [Category("I.方案")]
  88. [DisplayName("1.VM方案路径")]
  89. [Description("海康 VisionMaster 方案(.sol)文件路径;运行前由适配层加载")]
  90. public string SolutionPath { get; set; } = "";
  91. [Category("I.方案")]
  92. [DisplayName("2.流程名")]
  93. [Description("方案内要执行的流程名称")]
  94. public string ProcedureName { get; set; } = "";
  95. [Category("II.结果")]
  96. [DisplayName("1.结果键列表")]
  97. [Description("要读取的模块结果终端名(每行一个,如 ImageData/Score);运行后每个键成为本节点输出")]
  98. public List<string> ResultKeys { get; set; } = new List<string>();
  99. [Category("III.选项")]
  100. [DisplayName("1.VM客户端路径")]
  101. [Description("VisionMaster 客户端可执行文件路径(高级编辑用);留空则用系统文件关联打开方案")]
  102. public string VmClientPath { get; set; } = "";
  103. // ── 高级编辑:调起 VisionMaster 客户端打开方案 ──
  104. [Category("IV.高级编辑")]
  105. [DisplayName("1.打开 VM 编辑")]
  106. [Description("调起 VisionMaster 客户端打开该方案(模块增删/参数调整/调试在 VM 原生界面完成)")]
  107. [Button("高级编辑", nameof(OpenAdvancedEditor))]
  108. public object AdvancedEditButton { get; set; }
  109. public void OpenAdvancedEditor()
  110. {
  111. // 经安全封装调起 VisionMaster:扩展名白名单(仅 .sol)+ 参数列表启动,防命令注入
  112. VmSolutionLauncher.OpenSolution(SolutionPath, VmClientPath);
  113. }
  114. }
  115. }