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;
}
}
}
}