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