VisionLayerStore.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. namespace TeamAAS.Vision
  5. {
  6. /// <summary>
  7. /// 视觉图层总线(ILayerPublisher 的默认实现 + 全局入口):
  8. /// - 节点运行完调 <see cref="Publish"/>(线程安全,后台线程可调);
  9. /// - 显示端(编辑器大预览窗、主页画面、图层 ComboBox)订阅 <see cref="LayerPublished"/> / <see cref="FlowCleared"/>;
  10. /// - 内存策略:按 (flowId, nodeName) 保存“最近一次运行”的图层集;同一 RunStamp 重复发布=整体替换,
  11. /// 新 RunStamp 发布=先释放旧批次 owned 对象再替换 → 图层永不无限叠加。
  12. /// 阶段1 为纯托管对象总线,不依赖任何视觉库;平台显示控件负责解释 Content 并管理非托管对象的生命周期标记(Owned)。
  13. /// </summary>
  14. public sealed class VisionLayerStore : ILayerPublisher
  15. {
  16. private static readonly Lazy<VisionLayerStore> _instance =
  17. new Lazy<VisionLayerStore>(() => new VisionLayerStore(), isThreadSafe: true);
  18. /// <summary>全局默认图层总线。</summary>
  19. public static VisionLayerStore Instance => _instance.Value;
  20. private readonly object _sync = new object();
  21. /// <summary>key = flowId|nodeName → 该节点最近一次运行的发布参数。</summary>
  22. private readonly Dictionary<string, LayerPublishArgs> _latest =
  23. new Dictionary<string, LayerPublishArgs>(StringComparer.OrdinalIgnoreCase);
  24. /// <summary>图层发布事件(显示端订阅;发布线程直接回调,订阅方自行切 UI 线程)。</summary>
  25. public event Action<LayerPublishArgs> LayerPublished;
  26. /// <summary>图层清除事件(参数:flowId;nodeName 为 null 表示整流程清除)。</summary>
  27. public event Action<string, string> FlowCleared;
  28. /// <summary>owned 对象释放钩子:由宿主注入(如 HalconRuntime.Dispose),SDK 不依赖视觉库。</summary>
  29. public static Action<object> Disposer { get; set; }
  30. /// <inheritdoc />
  31. public void Publish(LayerPublishArgs args)
  32. {
  33. if (args == null || args.Layers.Count == 0) return;
  34. var key = Key(args.FlowId, args.NodeName);
  35. lock (_sync)
  36. {
  37. if (_latest.TryGetValue(key, out var old))
  38. {
  39. bool sameRun = string.Equals(old.RunStamp, args.RunStamp, StringComparison.Ordinal);
  40. if (!sameRun)
  41. ReleaseOwned(old); // 跨批次:旧批次自有对象立即释放,防止图像堆积
  42. }
  43. _latest[key] = args;
  44. }
  45. var handler = LayerPublished;
  46. if (handler != null) handler(args);
  47. }
  48. /// <summary>取指定节点最近一次发布的图层(显示端点选节点时用)。无则返回 null。</summary>
  49. public LayerPublishArgs GetLatest(string flowId, string nodeName)
  50. {
  51. lock (_sync)
  52. {
  53. return _latest.TryGetValue(Key(flowId, nodeName), out var args) ? args : null;
  54. }
  55. }
  56. /// <summary>取指定流程全部节点的最近图层(图层 ComboBox 数据源)。</summary>
  57. public List<LayerPublishArgs> GetFlowLayers(string flowId)
  58. {
  59. lock (_sync)
  60. {
  61. return _latest.Where(kv => kv.Key.StartsWith((flowId ?? "") + "|", StringComparison.OrdinalIgnoreCase))
  62. .Select(kv => kv.Value)
  63. .ToList();
  64. }
  65. }
  66. /// <summary>
  67. /// 按节点名取最近一次发布(不限流程)。编辑器 FlowId 与发布 FlowId 不一致时
  68. /// (旧流程文件图 Id 漂移等)的兜底显示查找,保证"点节点看图"始终可用。
  69. /// </summary>
  70. public LayerPublishArgs GetLatestByNode(string nodeName)
  71. {
  72. if (string.IsNullOrEmpty(nodeName)) return null;
  73. lock (_sync)
  74. {
  75. LayerPublishArgs best = null;
  76. foreach (var args in _latest.Values)
  77. {
  78. if (args == null || !string.Equals(args.NodeName, nodeName, StringComparison.OrdinalIgnoreCase)) continue;
  79. if (best == null || string.CompareOrdinal(args.RunStamp, best.RunStamp) >= 0)
  80. best = args;
  81. }
  82. return best;
  83. }
  84. }
  85. /// <summary>全部发布记录(跨流程;编辑器图 Id 漂移时的兜底展示数据源)。</summary>
  86. public List<LayerPublishArgs> GetAllLatest()
  87. {
  88. lock (_sync)
  89. {
  90. return _latest.Values.ToList();
  91. }
  92. }
  93. /// <summary>清空全部图层并释放 owned 对象(新一轮总运行开始时用)。</summary>
  94. public void ClearAll()
  95. {
  96. lock (_sync)
  97. {
  98. foreach (var args in _latest.Values)
  99. ReleaseOwned(args);
  100. _latest.Clear();
  101. }
  102. var handler = FlowCleared;
  103. if (handler != null) handler(null, null);
  104. }
  105. /// <inheritdoc />
  106. public void Clear(string flowId, string nodeName = null)
  107. {
  108. lock (_sync)
  109. {
  110. if (nodeName == null)
  111. {
  112. var keys = _latest.Keys.Where(k => k.StartsWith((flowId ?? "") + "|", StringComparison.OrdinalIgnoreCase)).ToList();
  113. foreach (var k in keys) { ReleaseOwned(_latest[k]); _latest.Remove(k); }
  114. }
  115. else
  116. {
  117. var key = Key(flowId, nodeName);
  118. if (_latest.TryGetValue(key, out var args)) { ReleaseOwned(args); _latest.Remove(key); }
  119. }
  120. }
  121. var handler = FlowCleared;
  122. if (handler != null) handler(flowId, nodeName);
  123. }
  124. private static void ReleaseOwned(LayerPublishArgs args)
  125. {
  126. if (args?.Layers == null) return;
  127. var dispose = Disposer;
  128. if (dispose == null) return;
  129. foreach (var layer in args.Layers)
  130. {
  131. if (layer != null && layer.Owned && layer.Content != null)
  132. {
  133. try { dispose(layer.Content); } catch { }
  134. layer.Content = null;
  135. }
  136. }
  137. }
  138. private static string Key(string flowId, string nodeName) => (flowId ?? "") + "|" + (nodeName ?? "");
  139. }
  140. }