using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using TeamAAS.FlowEditor.Execution;
using TeamAAS.FlowEditor.Models;
using TeamAAS.FlowEditor.Plugins;
using Plugins.Halcon.Models;
using Plugins.Halcon.Views;
namespace Plugins.Halcon
{
///
/// Halcon 流程容器节点。镜像 GroupPlugin(组合模块)的行为:
/// 配置输入映射(把主流程图像等注入子流程)→ 运行 HALCON 子流程(可循环)→ 收集输出映射回主流程。
/// 双击打开 :一个只含 HALCON 节点(按 VisionPlugin 分类)的流程编辑器。
///
[Serializable]
[Plugin("Halcon流程", PluginCategory.Halocn模块, typeof(HalconFlowModel), typeof(HalconFlowEditorView),
"HexagonOutline", NodeShape = NodeCategory.Group,
Description = "HALCON 视觉子流程容器 - 图像经输入映射进入,内部用 HALCON 节点处理,结果映射回主流程")]
public class HalconFlowPlugin : BasePlugin
{
public override List DeclareOutputs()
{
var list = new List();
if (Model?.ModuleOutputs != null)
{
foreach (var kv in Model.ModuleOutputs)
{
if (string.IsNullOrWhiteSpace(kv.OutName)) continue;
list.Add(new OutputField(kv.OutName, typeof(object)));
}
}
list.Add(new OutputField("实际循环次数", typeof(int)));
list.Add(new OutputField("循环设置", typeof(string)));
list.Add(new OutputField("结果", typeof(bool)));
list.Add(new OutputField("Message", typeof(string)));
return list;
}
public override bool InitPlugin()
{
Model.EnsureSubGraph();
return true;
}
[Newtonsoft.Json.JsonIgnore]
public int CurrentCount { get; set; } = 0;
public override NodeRunStatus PluginRun(CancellationToken token, out Dictionary results)
{
var res = new Dictionary();
results = res;
Model.EnsureSubGraph();
NodeRunStatus Fail(string message)
{
res["Error"] = message;
res["结果"] = false;
Log(1, $"HALCON 流程失败: {message}", TeamAAS.LogLevel.Error);
return NodeRunStatus.Failed;
}
if (Model.LoopCount == 0)
{
Log(2, "循环次数为 0,跳过 HALCON 流程执行");
res["结果"] = true;
res["实际循环次数"] = 0;
res["循环设置"] = "0";
return NodeRunStatus.Skipped;
}
if (Model.SubGraph.Nodes.Count == 0)
return Fail("HALCON 子流程为空(双击节点进入编辑器拖入 HALCON 节点)");
Log(3, $"HALCON 流程开始,循环设置={(Model.LoopCount == -1 ? "无限" : Model.LoopCount.ToString())},输入 {Model.ModuleInputs?.Count ?? 0} 个、输出 {Model.ModuleOutputs?.Count ?? 0} 个、子节点 {Model.SubGraph.Nodes.Count} 个");
Model.IsBreakRequested = false;
int maxLoops = Model.LoopCount == -1 ? int.MaxValue : Model.LoopCount;
int actualLoops = 0;
// 新一轮运行:换图层批次并清掉上一轮遗留图层(显示窗只保留当前运行的图层)
HalconEditorPreview.BeginRunCycle();
try
{
for (int i = 0; i < maxLoops; i++)
{
if (token.IsCancellationRequested) break;
CurrentCount = i + 1;
foreach (var node in Model.SubGraph.Nodes)
{
node.Status = NodeRunStatus.NotStarted;
node.CostTime = 0;
}
// ResetAllNodes 会 ClearFlow 清掉子流程所有结果(含刚注册的输入),
// 所以用 SkipReset=true 保留输入变量;节点状态由上面的循环手动复位(与 GroupPlugin 一致)
RegisterInputs();
var executor = new FlowExecutor(Model.SubGraph) { SkipReset = true };
executor.EndNodeEncountered += () => { Model.IsBreakRequested = true; };
executor.ExecuteAsync(token).Wait();
actualLoops++;
if (Model.IsBreakRequested)
{
res["Message"] = $"第 {i + 1} 次循环遇到结束节点";
Log(2, $"第 {i + 1} 次循环遇到结束节点,跳出循环");
break;
}
}
// 收集输出:把子流程内部节点结果映射回主流程
SaveOutputs(res);
res["实际循环次数"] = actualLoops;
res["循环设置"] = Model.LoopCount == -1 ? "无限" : Model.LoopCount.ToString();
if (token.IsCancellationRequested)
{
Log(1, $"HALCON 流程被取消,已执行 {actualLoops} 次循环", TeamAAS.LogLevel.Warning);
res["结果"] = false;
return NodeRunStatus.Failed;
}
res["结果"] = true;
Log(2, $"HALCON 流程执行完成,实际循环 {actualLoops} 次");
return NodeRunStatus.Success;
}
catch (Exception ex)
{
return Fail(ex.Message);
}
}
///
/// 把 ModuleInputs 映射到子流程注册表的「输入」区,内部节点通过 &{输入.变量名} 引用。
///
private void RegisterInputs()
{
if (Model?.ModuleInputs == null || Model.ModuleInputs.Count == 0) return;
var subGraphId = Model.SubGraph.GraphId;
var flowId = GetModel.FlowId;
foreach (var input in Model.ModuleInputs)
{
if (string.IsNullOrWhiteSpace(input.InputName)) continue;
if (input.SourceValue == null) continue;
object value = null;
if (!string.IsNullOrWhiteSpace(input.SourceValue.Formula))
value = RegistryOrDebug.ResolveFormula(flowId, input.SourceValue.Formula);
else
value = input.SourceValue.Value;
RegistryOrDebug.SetInputVariable(subGraphId, input.InputName, value);
Log(4, $"输入[{input.InputName}] ← {(value?.GetType().Name ?? "null")}");
}
}
/// 把每个 ModuleOutput 的公式在子流程内解析后写入结果(键=OutName)。
private void SaveOutputs(Dictionary results)
{
if (Model?.ModuleOutputs == null) return;
foreach (var kv in Model.ModuleOutputs)
{
if (string.IsNullOrWhiteSpace(kv.OutName)) continue;
object value = ResolveFromSubGraph(kv.OutValue?.Formula, kv.OutValue?.Value);
results[kv.OutName] = value;
Log(4, $"输出[{kv.OutName}] = {(value?.ToString() ?? "null")}");
}
}
///
/// 解析 &{节点名.属性名}(从子流程节点本地 RawResults 读),
/// 或 &{流程名.节点名.属性名}(回退到注册表);无公式时返回 fallback。
///
private object ResolveFromSubGraph(string formula, object fallback)
{
if (string.IsNullOrEmpty(formula)) return fallback;
var m2 = Regex.Match(formula, @"^&\{(?[^.}]+)\.(?[^.}]+)\}$");
if (m2.Success)
{
var nodeName = m2.Groups["node"].Value;
var propName = m2.Groups["prop"].Value;
// 「输入」伪节点直接查注册表
if (nodeName == "输入")
return RegistryOrDebug.GetValue(Model.SubGraph.GraphId, "输入", propName);
var node = Model.SubGraph?.Nodes?.FirstOrDefault(n => n.NodeName == nodeName);
if (node?.RawResults != null && node.RawResults.TryGetValue(propName, out var val))
return val;
return null;
}
var m3 = Regex.Match(formula, @"^&\{(?[^.}]+)\.(?[^.}]+)\.(?[^.}]+)\}$");
if (m3.Success)
return RegistryOrDebug.GetValue(m3.Groups["flow"].Value, m3.Groups["node"].Value, m3.Groups["prop"].Value);
return formula;
}
}
}