using System; using System.Collections.Generic; using System.Threading; using System.Windows; using Plugins.Halcon.Models; using Plugins.Halcon.Views; using TeamAAS.FlowEditor.Execution; using TeamAAS.FlowEditor.Models; using TeamAAS.FlowEditor.Plugins; namespace Plugins.Halcon { /// /// HALCON 图像显示节点(主流程节点,与 VP图像显示/VM图像显示 对等):把 HALCON 流程容器输出的 /// 图像/区域显示到主页画面框(视觉窗体 UCDispHalcon),每项可独立链接叠加区域与 OK/NG 结果驱动状态圆点。 /// 放在主流程调用:图像来源绑定上游「Halcon流程」容器的输出(或任意前序节点的图像/GenImage)。 /// 画面框是主页单例、独立于编辑器——正常运行只往里推图,不由编辑器控制。 /// [Serializable] [Plugin("Halcon图像显示", PluginCategory.Halocn模块, typeof(HalconDisplayModel), "Monitor", NodeShape = NodeCategory.Normal, Description = "主流程节点:把 HALCON 图像/区域显示到主页画面框;每项可独立链接叠加区域与 OK/NG 结果驱动状态圆点")] public class HalconDisplayPlugin : BasePlugin { public override List DeclareOutputs() { var list = new List { new OutputField("显示画面数", typeof(int)), new OutputField("结果", typeof(bool)), }; foreach (var kv in BuildItemKeys(Model)) list.Add(new OutputField(kv.Key, typeof(object))); return list; } /// /// 依据显示项列表生成稳定的「输出键 → 显示项」映射:键优先取目标画面名,重名/空名追加序号去重。 /// 共用,保证键一致。 /// private static List> BuildItemKeys(HalconDisplayModel model) { var map = new List>(); if (model?.DisplayItems == null) return map; var used = new HashSet(); for (int i = 0; i < model.DisplayItems.Count; i++) { var item = model.DisplayItems[i]; if (item == null) continue; string key = item.FrameName; if (string.IsNullOrWhiteSpace(key)) key = $"画面{item.FrameNo}"; string unique = key; int dup = 2; while (!used.Add(unique)) unique = $"{key}({dup++})"; map.Add(new KeyValuePair(unique, item)); } return map; } public override NodeRunStatus PluginRun(CancellationToken token, out Dictionary results) { var res = new Dictionary(); results = res; try { var windows = UCDispHalcon.Instance.halconWindow; int displayed = 0; Log(3, $"开始显示,显示项 {Model.DisplayItems?.Count ?? 0} 个,主页画面框 {windows?.Count ?? 0} 个"); foreach (var kv in BuildItemKeys(Model)) { var item = kv.Value; res[kv.Key] = null; object image = GetResolve(item.Source); object overlay = string.IsNullOrWhiteSpace(item.OverlaySource?.Formula) ? null : GetResolve(item.OverlaySource); res[kv.Key] = image; if (image == null) { Log(2, $"显示项[{kv.Key}] 未解析到图像,已跳过", TeamAAS.LogLevel.Warning); continue; } if (item.FrameNo < 1 || windows == null || windows.Count < item.FrameNo) { Log(2, $"显示项[{kv.Key}] 目标画面序号 {item.FrameNo} 无效或主页画面框不足,已跳过", TeamAAS.LogLevel.Warning); continue; } var win = windows[item.FrameNo - 1]; if (win == null) continue; bool hasOk = !string.IsNullOrWhiteSpace(item.OkNgBinding?.Formula); bool ok = hasOk && GetValue(item.OkNgBinding); Application.Current?.Dispatcher.Invoke(() => { win.RefreshImage(image, overlay); if (hasOk) win.SetLable(ok ? HalconResultType.OK : HalconResultType.NG); }); displayed++; Log(4, $"显示项[{kv.Key}] → 画面{item.FrameNo},图像类型 {(image?.GetType().Name ?? "null")},叠加 {(overlay != null ? "有" : "无")}{(hasOk ? $",OK/NG={(ok ? "OK" : "NG")}" : "")}"); } res["显示画面数"] = displayed; res["结果"] = true; Log(2, $"显示完成,共刷新 {displayed} 个画面"); return NodeRunStatus.Success; } catch (Exception ex) { res["Error"] = ex.Message; res["结果"] = false; Log(1, $"HALCON 图像显示异常: {ex.Message}", TeamAAS.LogLevel.Error); return NodeRunStatus.Failed; } } } }