using System; using System.Collections.Generic; using System.Threading; using System.Windows; using Cognex.VisionPro; using Plugins.Vpp.Converters; using Plugins.Vpp.Models; using Plugins.Vpp.Views; using TeamAAS.Camera.Images; using TeamAAS.FlowEditor.Execution; using TeamAAS.FlowEditor.Models; using TeamAAS.FlowEditor.Plugins; using TeamAAS.FlowEngine.Execution; namespace Plugins.Vpp { /// /// VP 图像显示节点:把上游输出的图层(ICogRecord)/图像(ICogImage)/相机图像(GenImage) /// 显示到指定的画面框(视觉窗体);每个显示项可独立链接 OK/NG 结果(可不链接), /// 驱动该画面框右上角的状态圆点(绿/红)。 /// 参照 VisionKit 的 DisplayImageKitService 实现。 /// [Serializable] [Plugin("VP图像显示", PluginCategory.Vpp模块, typeof(VppDisplayModel), "Monitor", Description = "把图层/图像显示到主页画面框;每项可独立链接 OK/NG 结果驱动状态圆点(可不链接)")] public class VppDisplayPlugin : 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(VppDisplayModel 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) { results = new Dictionary(); try { var windows = UCDispVpp.Instance.vppWindow; int displayed = 0; Log(3, $"开始显示,显示项 {Model.DisplayItems?.Count ?? 0} 个,可用画面框 {windows?.Count ?? 0} 个"); foreach (var kv in BuildItemKeys(Model)) { var item = kv.Value; // 默认每个显示项输出为空(未链接/未显示) results[kv.Key] = null; 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) { Log(2, $"显示项[{kv.Key}] 画面框为空,已跳过", TeamAAS.LogLevel.Warning); continue; } object value = GetResolve(item.Source); results[kv.Key] = value; // 输出该画面显示的图层,供下游索引 var toShow = value; Application.Current?.Dispatcher.Invoke(() => { if (toShow is ICogRecord record) win.RefreshRecordDisplay(record); else if (toShow is ICogImage image) win.RefreshImageDisplay(image); else if (toShow is GenImage genImage) win.RefreshImageDisplay(GenImageConverter.ToCogImage(genImage)); }); displayed++; Log(4, $"显示项[{kv.Key}] → 画面{item.FrameNo},图层类型 {(value?.GetType().Name ?? "null")}"); // 2) OK/NG 圆点(该项链接了才处理;为空 = 圆点保持原状) if (!string.IsNullOrWhiteSpace(item.OkNgBinding?.Formula)) { bool ok = GetValue(item.OkNgBinding); win.SetLable(ok ? VppWindow.VppResultType.OK : VppWindow.VppResultType.NG); Log(4, $"显示项[{kv.Key}] OK/NG = {(ok ? "OK" : "NG")}"); } } GC.Collect(); results["显示画面数"] = displayed; results["结果"] = true; Log(2, $"显示完成,共刷新 {displayed} 个画面"); return NodeRunStatus.Success; } catch (Exception ex) { results["Error"] = ex.Message; results["结果"] = false; Log(1, $"VP图像显示异常: {ex.Message}", TeamAAS.LogLevel.Error); return NodeRunStatus.Failed; } } } }