VppDisplayPlugin.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using System.Windows;
  5. using Cognex.VisionPro;
  6. using Plugins.Vpp.Converters;
  7. using Plugins.Vpp.Models;
  8. using Plugins.Vpp.Views;
  9. using TeamAAS.Camera.Images;
  10. using TeamAAS.FlowEditor.Execution;
  11. using TeamAAS.FlowEditor.Models;
  12. using TeamAAS.FlowEditor.Plugins;
  13. using TeamAAS.FlowEngine.Execution;
  14. namespace Plugins.Vpp
  15. {
  16. /// <summary>
  17. /// VP 图像显示节点:把上游输出的图层(ICogRecord)/图像(ICogImage)/相机图像(GenImage)
  18. /// 显示到指定的画面框(视觉窗体);每个显示项可独立链接 OK/NG 结果(可不链接),
  19. /// 驱动该画面框右上角的状态圆点(绿/红)。
  20. /// 参照 VisionKit 的 DisplayImageKitService 实现。
  21. /// </summary>
  22. [Serializable]
  23. [Plugin("VP图像显示", PluginCategory.Vpp模块, typeof(VppDisplayModel),
  24. "Monitor",
  25. Description = "把图层/图像显示到主页画面框;每项可独立链接 OK/NG 结果驱动状态圆点(可不链接)")]
  26. public class VppDisplayPlugin : BasePlugin<VppDisplayModel>
  27. {
  28. /// <summary>
  29. /// 动态输出声明:除固定的 显示画面数/结果 外,为每个“显示项(显示图层)”
  30. /// 按其目标画面名声明一个输出,使这些图层能被下游绑定树索引(跑之前就能绑)。
  31. /// </summary>
  32. public override List<OutputField> DeclareOutputs()
  33. {
  34. var list = new List<OutputField>
  35. {
  36. new OutputField("显示画面数", typeof(int)),
  37. new OutputField("结果", typeof(bool)),
  38. };
  39. // 每个显示项 = 一个图层输出(目标画面名为键,值为实际显示的图层/图像)
  40. foreach (var kv in BuildItemKeys(Model))
  41. list.Add(new OutputField(kv.Key, typeof(object)));
  42. return list;
  43. }
  44. /// <summary>
  45. /// 依据显示项列表生成稳定的“输出键 → 显示项”映射:键优先取目标画面名,
  46. /// 重名/空名时追加序号去重。<see cref="DeclareOutputs"/> 与 <see cref="PluginRun"/> 共用,保证键一致。
  47. /// </summary>
  48. private static List<KeyValuePair<string, VppDisplayItem>> BuildItemKeys(VppDisplayModel model)
  49. {
  50. var map = new List<KeyValuePair<string, VppDisplayItem>>();
  51. if (model?.DisplayItems == null) return map;
  52. var used = new HashSet<string>();
  53. for (int i = 0; i < model.DisplayItems.Count; i++)
  54. {
  55. var item = model.DisplayItems[i];
  56. if (item == null) continue;
  57. string key = item.FrameName;
  58. if (string.IsNullOrWhiteSpace(key)) key = $"画面{item.FrameNo}";
  59. string unique = key;
  60. int dup = 2;
  61. while (!used.Add(unique)) unique = $"{key}({dup++})";
  62. map.Add(new KeyValuePair<string, VppDisplayItem>(unique, item));
  63. }
  64. return map;
  65. }
  66. public override NodeRunStatus PluginRun(CancellationToken token, out Dictionary<string, object> results)
  67. {
  68. results = new Dictionary<string, object>();
  69. try
  70. {
  71. var windows = UCDispVpp.Instance.vppWindow;
  72. int displayed = 0;
  73. Log(3, $"开始显示,显示项 {Model.DisplayItems?.Count ?? 0} 个,可用画面框 {windows?.Count ?? 0} 个");
  74. foreach (var kv in BuildItemKeys(Model))
  75. {
  76. var item = kv.Value;
  77. // 默认每个显示项输出为空(未链接/未显示)
  78. results[kv.Key] = null;
  79. if (item.FrameNo < 1 || windows == null || windows.Count < item.FrameNo)
  80. {
  81. Log(2, $"显示项[{kv.Key}] 目标画面序号 {item.FrameNo} 无效或画面框不足,已跳过", TeamAAS.LogLevel.Warning);
  82. continue;
  83. }
  84. var win = windows[item.FrameNo - 1];
  85. if (win == null)
  86. {
  87. Log(2, $"显示项[{kv.Key}] 画面框为空,已跳过", TeamAAS.LogLevel.Warning);
  88. continue;
  89. }
  90. object value = GetResolve(item.Source);
  91. results[kv.Key] = value; // 输出该画面显示的图层,供下游索引
  92. var toShow = value;
  93. Application.Current?.Dispatcher.Invoke(() =>
  94. {
  95. if (toShow is ICogRecord record)
  96. win.RefreshRecordDisplay(record);
  97. else if (toShow is ICogImage image)
  98. win.RefreshImageDisplay(image);
  99. else if (toShow is GenImage genImage)
  100. win.RefreshImageDisplay(GenImageConverter.ToCogImage(genImage));
  101. });
  102. displayed++;
  103. Log(4, $"显示项[{kv.Key}] → 画面{item.FrameNo},图层类型 {(value?.GetType().Name ?? "null")}");
  104. // 2) OK/NG 圆点(该项链接了才处理;为空 = 圆点保持原状)
  105. if (!string.IsNullOrWhiteSpace(item.OkNgBinding?.Formula))
  106. {
  107. bool ok = GetValue<bool>(item.OkNgBinding);
  108. win.SetLable(ok
  109. ? VppWindow.VppResultType.OK
  110. : VppWindow.VppResultType.NG);
  111. Log(4, $"显示项[{kv.Key}] OK/NG = {(ok ? "OK" : "NG")}");
  112. }
  113. }
  114. GC.Collect();
  115. results["显示画面数"] = displayed;
  116. results["结果"] = true;
  117. Log(2, $"显示完成,共刷新 {displayed} 个画面");
  118. return NodeRunStatus.Success;
  119. }
  120. catch (Exception ex)
  121. {
  122. results["Error"] = ex.Message;
  123. results["结果"] = false;
  124. Log(1, $"VP图像显示异常: {ex.Message}", TeamAAS.LogLevel.Error);
  125. return NodeRunStatus.Failed;
  126. }
  127. }
  128. }
  129. }