| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- namespace TeamAAS.Vision
- {
- /// <summary>
- /// 视觉图层总线(ILayerPublisher 的默认实现 + 全局入口):
- /// - 节点运行完调 <see cref="Publish"/>(线程安全,后台线程可调);
- /// - 显示端(编辑器大预览窗、主页画面、图层 ComboBox)订阅 <see cref="LayerPublished"/> / <see cref="FlowCleared"/>;
- /// - 内存策略:按 (flowId, nodeName) 保存“最近一次运行”的图层集;同一 RunStamp 重复发布=整体替换,
- /// 新 RunStamp 发布=先释放旧批次 owned 对象再替换 → 图层永不无限叠加。
- /// 阶段1 为纯托管对象总线,不依赖任何视觉库;平台显示控件负责解释 Content 并管理非托管对象的生命周期标记(Owned)。
- /// </summary>
- public sealed class VisionLayerStore : ILayerPublisher
- {
- private static readonly Lazy<VisionLayerStore> _instance =
- new Lazy<VisionLayerStore>(() => new VisionLayerStore(), isThreadSafe: true);
- /// <summary>全局默认图层总线。</summary>
- public static VisionLayerStore Instance => _instance.Value;
- private readonly object _sync = new object();
- /// <summary>key = flowId|nodeName → 该节点最近一次运行的发布参数。</summary>
- private readonly Dictionary<string, LayerPublishArgs> _latest =
- new Dictionary<string, LayerPublishArgs>(StringComparer.OrdinalIgnoreCase);
- /// <summary>图层发布事件(显示端订阅;发布线程直接回调,订阅方自行切 UI 线程)。</summary>
- public event Action<LayerPublishArgs> LayerPublished;
- /// <summary>图层清除事件(参数:flowId;nodeName 为 null 表示整流程清除)。</summary>
- public event Action<string, string> FlowCleared;
- /// <summary>owned 对象释放钩子:由宿主注入(如 HalconRuntime.Dispose),SDK 不依赖视觉库。</summary>
- public static Action<object> Disposer { get; set; }
- /// <inheritdoc />
- public void Publish(LayerPublishArgs args)
- {
- if (args == null || args.Layers.Count == 0) return;
- var key = Key(args.FlowId, args.NodeName);
- lock (_sync)
- {
- if (_latest.TryGetValue(key, out var old))
- {
- bool sameRun = string.Equals(old.RunStamp, args.RunStamp, StringComparison.Ordinal);
- if (!sameRun)
- ReleaseOwned(old); // 跨批次:旧批次自有对象立即释放,防止图像堆积
- }
- _latest[key] = args;
- }
- var handler = LayerPublished;
- if (handler != null) handler(args);
- }
- /// <summary>取指定节点最近一次发布的图层(显示端点选节点时用)。无则返回 null。</summary>
- public LayerPublishArgs GetLatest(string flowId, string nodeName)
- {
- lock (_sync)
- {
- return _latest.TryGetValue(Key(flowId, nodeName), out var args) ? args : null;
- }
- }
- /// <summary>取指定流程全部节点的最近图层(图层 ComboBox 数据源)。</summary>
- public List<LayerPublishArgs> GetFlowLayers(string flowId)
- {
- lock (_sync)
- {
- return _latest.Where(kv => kv.Key.StartsWith((flowId ?? "") + "|", StringComparison.OrdinalIgnoreCase))
- .Select(kv => kv.Value)
- .ToList();
- }
- }
- /// <summary>
- /// 按节点名取最近一次发布(不限流程)。编辑器 FlowId 与发布 FlowId 不一致时
- /// (旧流程文件图 Id 漂移等)的兜底显示查找,保证"点节点看图"始终可用。
- /// </summary>
- public LayerPublishArgs GetLatestByNode(string nodeName)
- {
- if (string.IsNullOrEmpty(nodeName)) return null;
- lock (_sync)
- {
- LayerPublishArgs best = null;
- foreach (var args in _latest.Values)
- {
- if (args == null || !string.Equals(args.NodeName, nodeName, StringComparison.OrdinalIgnoreCase)) continue;
- if (best == null || string.CompareOrdinal(args.RunStamp, best.RunStamp) >= 0)
- best = args;
- }
- return best;
- }
- }
- /// <summary>全部发布记录(跨流程;编辑器图 Id 漂移时的兜底展示数据源)。</summary>
- public List<LayerPublishArgs> GetAllLatest()
- {
- lock (_sync)
- {
- return _latest.Values.ToList();
- }
- }
- /// <summary>清空全部图层并释放 owned 对象(新一轮总运行开始时用)。</summary>
- public void ClearAll()
- {
- lock (_sync)
- {
- foreach (var args in _latest.Values)
- ReleaseOwned(args);
- _latest.Clear();
- }
- var handler = FlowCleared;
- if (handler != null) handler(null, null);
- }
- /// <inheritdoc />
- public void Clear(string flowId, string nodeName = null)
- {
- lock (_sync)
- {
- if (nodeName == null)
- {
- var keys = _latest.Keys.Where(k => k.StartsWith((flowId ?? "") + "|", StringComparison.OrdinalIgnoreCase)).ToList();
- foreach (var k in keys) { ReleaseOwned(_latest[k]); _latest.Remove(k); }
- }
- else
- {
- var key = Key(flowId, nodeName);
- if (_latest.TryGetValue(key, out var args)) { ReleaseOwned(args); _latest.Remove(key); }
- }
- }
- var handler = FlowCleared;
- if (handler != null) handler(flowId, nodeName);
- }
- private static void ReleaseOwned(LayerPublishArgs args)
- {
- if (args?.Layers == null) return;
- var dispose = Disposer;
- if (dispose == null) return;
- foreach (var layer in args.Layers)
- {
- if (layer != null && layer.Owned && layer.Content != null)
- {
- try { dispose(layer.Content); } catch { }
- layer.Content = null;
- }
- }
- }
- private static string Key(string flowId, string nodeName) => (flowId ?? "") + "|" + (nodeName ?? "");
- }
- }
|