VmPlugins.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using TeamAAS.FlowEditor.Execution;
  5. using TeamAAS.FlowEditor.Models;
  6. using TeamAAS.FlowEditor.Plugins;
  7. using Plugins.Vm.Models;
  8. using Plugins.Vm.Views;
  9. namespace Plugins.Vm
  10. {
  11. /// <summary>
  12. /// VM(海康 VisionMaster)流程/工具块节点。镜像 VppToolBlockPlugin 的行为:
  13. /// 配置输入映射 → 运行 VM 流程 → 收集输出终端与图层。所有 VM SDK 调用经 <see cref="VmRuntime"/> 适配层。
  14. /// </summary>
  15. [Serializable]
  16. [Plugin("VM工具块", PluginCategory.Vm模块, typeof(VmToolBlockModel), typeof(VmToolBlockEditView),
  17. "CogOutline",
  18. NodeShape = NodeCategory.Normal, Description = "海康 VisionMaster 流程节点 - 双击用 VM 自带界面配置流程,运行时执行 VM 方案")]
  19. public class VmToolBlockPlugin : BasePlugin<VmToolBlockModel>
  20. {
  21. /// <summary>
  22. /// 动态输出声明(跑之前就能被绑定树索引):
  23. /// 1) 用户声明的输出终端名(<see cref="VmToolBlockModel.OutputNames"/>);
  24. /// 2) 输出图层N——数量取决于 VM 流程内部,运行后学习到的快照 <see cref="VmToolBlockModel.LastLayerCount"/>;
  25. /// 3) 固定状态输出 结果。
  26. /// </summary>
  27. public override List<OutputField> DeclareOutputs()
  28. {
  29. var list = new List<OutputField>();
  30. if (Model?.OutputNames != null)
  31. {
  32. foreach (var name in Model.OutputNames)
  33. if (!string.IsNullOrWhiteSpace(name))
  34. list.Add(new OutputField(name, typeof(object)));
  35. }
  36. int layers = Model?.LastLayerCount ?? 0;
  37. for (int i = 1; i <= layers; i++)
  38. list.Add(new OutputField($"输出图层{i}", typeof(object)));
  39. list.Add(new OutputField("结果", typeof(bool)));
  40. return list;
  41. }
  42. public override NodeRunStatus PluginRun(CancellationToken token, out Dictionary<string, object> results)
  43. {
  44. results = new Dictionary<string, object>();
  45. try
  46. {
  47. Log(3, $"开始执行 VM 流程[{Model.ProcedureName}],输入映射 {Model.InputMappings?.Count ?? 0} 项");
  48. // 加载方案/流程(句柄缓存在模型的 [NonSerialized] 字段)
  49. // 方案路径/流程名变化时自动重载(调试期改方案后无需重启软件)
  50. if (Model.Solution != null &&
  51. (!string.Equals(Model.LoadedSolutionPath, Model.SolutionPath, StringComparison.OrdinalIgnoreCase)
  52. || !string.Equals(Model.LoadedProcedureName, Model.ProcedureName, StringComparison.Ordinal)))
  53. {
  54. Model.Solution = null;
  55. Model.Procedure = null;
  56. }
  57. if (Model.Solution == null && !string.IsNullOrWhiteSpace(Model.SolutionPath))
  58. {
  59. Model.Solution = VmRuntime.LoadSolution(Model.SolutionPath);
  60. Model.LoadedSolutionPath = Model.SolutionPath;
  61. Model.Procedure = null;
  62. }
  63. if (Model.Procedure == null)
  64. {
  65. Model.Procedure = VmRuntime.GetProcedure(Model.Solution, Model.ProcedureName);
  66. Model.LoadedProcedureName = Model.ProcedureName;
  67. }
  68. // 1. 输入映射
  69. var inputs = new Dictionary<string, object>();
  70. foreach (var mapping in Model.InputMappings ?? new List<VmInputMapping>())
  71. {
  72. if (string.IsNullOrWhiteSpace(mapping.InputName)) continue;
  73. object value = !string.IsNullOrWhiteSpace(mapping.Source?.Formula)
  74. ? RegistryOrDebug.ResolveFormula(GetModel.FlowId, mapping.Source.Formula)
  75. : mapping.Source?.Value;
  76. inputs[mapping.InputName] = value;
  77. Log(4, $"输入终端[{mapping.InputName}] ← {(value?.GetType().Name ?? "null")}");
  78. }
  79. VmRuntime.SetInputs(Model.Procedure, inputs);
  80. // 2. 运行
  81. bool accept = VmRuntime.Run(Model.Procedure);
  82. Log(3, $"VM 流程运行结束,Accept={accept}");
  83. // 3. 输出终端
  84. var outputs = VmRuntime.GetOutputs(Model.Procedure) ?? new Dictionary<string, object>();
  85. foreach (var kv in outputs)
  86. {
  87. results[kv.Key] = kv.Value;
  88. Log(4, $"输出终端[{kv.Key}] = {kv.Value}");
  89. }
  90. // 4. 输出图层,并学习图层数量供下次 DeclareOutputs 预声明
  91. var layerList = VmRuntime.GetLayers(Model.Procedure) ?? new List<object>();
  92. for (int i = 0; i < layerList.Count; i++)
  93. results[$"输出图层{i + 1}"] = layerList[i];
  94. if (layerList.Count != Model.LastLayerCount)
  95. Model.LastLayerCount = layerList.Count;
  96. Log(3, $"收集输出图层 {layerList.Count} 个");
  97. // 4.5 图层发布到视觉显示总线:VM流程编辑器的视觉显示/图层筛选可直接查看
  98. try
  99. {
  100. var args = new TeamAAS.Vision.LayerPublishArgs
  101. {
  102. FlowId = GetModel.FlowId,
  103. NodeName = GetModel.NodeName,
  104. RunStamp = "0",
  105. };
  106. for (int i = 0; i < layerList.Count; i++)
  107. {
  108. if (layerList[i] == null) continue;
  109. var name = layerList.Count == 1 ? "图像" : $"图层{i + 1}";
  110. args.Layers.Add(new TeamAAS.Vision.LayerItem(
  111. name,
  112. i == 0 ? TeamAAS.Vision.LayerKind.Image : TeamAAS.Vision.LayerKind.Other,
  113. layerList[i], owned: false));
  114. }
  115. if (args.Layers.Count > 0)
  116. TeamAAS.Vision.VisionLayerStore.Instance.Publish(args);
  117. }
  118. catch { /* 显示发布失败不影响流程 */ }
  119. // 5. 结果
  120. if (!accept)
  121. {
  122. results["VM异常"] = "VM 流程未通过";
  123. results["结果"] = false;
  124. Log(1, "VM 流程未通过", TeamAAS.LogLevel.Error);
  125. return NodeRunStatus.Failed;
  126. }
  127. results["结果"] = true;
  128. Log(2, $"VM 流程执行成功,图层 {layerList.Count} 个");
  129. return NodeRunStatus.Success;
  130. }
  131. catch (Exception ex)
  132. {
  133. results["VM异常"] = ex.Message;
  134. results["结果"] = false;
  135. Log(1, $"VM 流程执行异常: {ex.Message}", TeamAAS.LogLevel.Error);
  136. return NodeRunStatus.Failed;
  137. }
  138. }
  139. }
  140. }