| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- 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
- {
- /// <summary>
- /// Halcon 流程容器节点。镜像 GroupPlugin(组合模块)的行为:
- /// 配置输入映射(把主流程图像等注入子流程)→ 运行 HALCON 子流程(可循环)→ 收集输出映射回主流程。
- /// 双击打开 <see cref="HalconFlowEditorView"/>:一个只含 HALCON 节点(按 VisionPlugin 分类)的流程编辑器。
- /// </summary>
- [Serializable]
- [Plugin("Halcon流程", PluginCategory.Halocn模块, typeof(HalconFlowModel), typeof(HalconFlowEditorView),
- "HexagonOutline", NodeShape = NodeCategory.Group,
- Description = "HALCON 视觉子流程容器 - 图像经输入映射进入,内部用 HALCON 节点处理,结果映射回主流程")]
- public class HalconFlowPlugin : BasePlugin<HalconFlowModel>
- {
- public override List<OutputField> DeclareOutputs()
- {
- var list = new List<OutputField>();
- 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<string, object> results)
- {
- var res = new Dictionary<string, object>();
- 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);
- }
- }
- /// <summary>
- /// 把 ModuleInputs 映射到子流程注册表的「输入」区,内部节点通过 &{输入.变量名} 引用。
- /// </summary>
- 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")}");
- }
- }
- /// <summary>把每个 ModuleOutput 的公式在子流程内解析后写入结果(键=OutName)。</summary>
- private void SaveOutputs(Dictionary<string, object> 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")}");
- }
- }
- /// <summary>
- /// 解析 &{节点名.属性名}(从子流程节点本地 RawResults 读),
- /// 或 &{流程名.节点名.属性名}(回退到注册表);无公式时返回 fallback。
- /// </summary>
- private object ResolveFromSubGraph(string formula, object fallback)
- {
- if (string.IsNullOrEmpty(formula)) return fallback;
- var m2 = Regex.Match(formula, @"^&\{(?<node>[^.}]+)\.(?<prop>[^.}]+)\}$");
- 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, @"^&\{(?<flow>[^.}]+)\.(?<node>[^.}]+)\.(?<prop>[^.}]+)\}$");
- if (m3.Success)
- return RegistryOrDebug.GetValue(m3.Groups["flow"].Value, m3.Groups["node"].Value, m3.Groups["prop"].Value);
- return formula;
- }
- }
- }
|