using PropertyGridLib.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using TeamAAS.FlowEditor.Execution;
using TeamAAS.FlowEditor.Models;
using TeamAAS.FlowEditor.Plugins;
using Plugins.Standard.Models;
using Plugins.Standard.Views;
namespace Plugins.Standard
{
[Serializable]
///
/// 组合模块插件 - 包含子流程,支持循环执行
///
[Plugin("组合模块", PluginCategory.逻辑判断, typeof(GroupPluginModel), typeof(GroupEditorView),
"FolderOutline",NodeShape = NodeCategory.Group, Description = "组合模块,包含子流程,支持循环执行")]
public class GroupPlugin : 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, kv.ResultType));
}
}
list.Add(new OutputField("实际循环次数", typeof(int)));
list.Add(new OutputField("循环设置", typeof(string)));
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)
{
results = new Dictionary();
Model.EnsureSubGraph();
if (Model.LoopCount == 0)
{
Log(2, "循环次数为 0,跳过组合模块执行");
return NodeRunStatus.Skipped;
}
Log(3, $"组合模块开始,循环设置={(Model.LoopCount == -1 ? "无限" : Model.LoopCount.ToString())},输入 {Model.ModuleInputs?.Count ?? 0} 个、输出 {Model.ModuleOutputs?.Count ?? 0} 个");
Model.ModuleOutputs.ForEach(x => x.ResultList?.Clear());
Model.IsBreakRequested = false;
int maxLoops = Model.LoopCount == -1 ? int.MaxValue : Model.LoopCount;
int actualLoops = 0;
for (int i = 0; i < maxLoops; i++)
{
if (token.IsCancellationRequested)
{
break;
}
CurrentCount = i + 1;
Log(4, $"第 {i + 1} 次循环开始");
foreach (var node in Model.SubGraph.Nodes)
{
node.Status = NodeRunStatus.NotStarted;
node.CostTime = 0;
}
// ResetAllNodes 会 ClearFlow 清掉子流程所有结果,
// 所以输入必须在每次循环的 Reset 之后注册
RegisterInputs();
var executor = new FlowExecutor(Model.SubGraph) { SkipReset = true };
executor.EndNodeEncountered += () =>
{
Model.IsBreakRequested = true;
};
executor.ExecuteAsync(token).Wait();
actualLoops++;
bool IsEnd = Model.IsBreakRequested || i == maxLoops - 1;
SaveOutPut(ref results, IsEnd);
if (Model.IsBreakRequested)
{
results["Message"] = $"第{i + 1}次循环时遇到结束节点";
Log(2, $"第 {i + 1} 次循环遇到结束节点,跳出循环");
break;
}
}
results["实际循环次数"] = actualLoops;
results["循环设置"] = Model.LoopCount == -1 ? "无限" : Model.LoopCount.ToString();
if (token.IsCancellationRequested)
{
Log(1, $"组合模块被取消,已执行 {actualLoops} 次循环", TeamAAS.LogLevel.Warning);
return NodeRunStatus.Failed;
}
Log(2, $"组合模块执行完成,实际循环 {actualLoops} 次");
return NodeRunStatus.Success;
}
///
/// 将 ModuleInputs 映射到 ResultRegistry 子流程区域
/// 内部节点通过 &{输入.变量名} 引用
///
private void RegisterInputs()
{
if (Model?.ModuleInputs == null || Model.ModuleInputs.Count == 0) return;
var subGraphId = Model.SubGraph.GraphId;
var flowId = GetModel.FlowId;
System.Diagnostics.Debug.WriteLine($"[GroupPlugin] RegisterInputs: flowId={flowId}, subGraphId={subGraphId}");
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);
System.Diagnostics.Debug.WriteLine($"[GroupPlugin] {input.InputName} ← formula='{input.SourceValue.Formula}' → value={value} (null? {value == null})");
}
else
{
System.Diagnostics.Debug.WriteLine($"[GroupPlugin] {input.InputName} ← 无公式");
}
RegistryOrDebug.SetInputVariable(subGraphId, input.InputName, value);
}
RegistryOrDebug.GetFlowResults(subGraphId, out var after);
System.Diagnostics.Debug.WriteLine($"[GroupPlugin] 验证子流程「输入」节点: " + (after.ContainsKey("输入") ? $"OK ({after["输入"].Count} vars)" : "MISSING!"));
}
public void SaveOutPut(ref Dictionary results, bool IsEnd)
{
foreach (var kv in Model.ModuleOutputs)
{
if (kv.ResultList.Count == 0)
{
kv.ResultType = this.GetValue(kv.OutVale).GetType();
}
if (!kv.IsEndResult)//如果以列表形式输出,保存每次循环的结果
{
kv.ResultList.Add(GetValue(kv.OutVale));
if (kv.ResultList.Count > kv.MaxResultNumber)
{
kv.ResultList = kv.ResultList.Skip(kv.ResultList.Count - kv.MaxResultNumber).ToList();
}
}
if (IsEnd)
{
if (kv.IsEndResult)//如果是结束节点,保存输出
{
results.Add(kv.OutName, this.GetValue(kv.OutVale));
}
else
{
if (kv.ResultList.Count > 0)
{
// 方法:使用 Enumerable.Cast 的反射调用
var castMethod = typeof(Enumerable)
.GetMethod("Cast")
.MakeGenericMethod(kv.ResultType);
var casted = castMethod.Invoke(null, new object[] { kv.ResultList }); // 返回 IEnumerable
// 然后调用 ToList(也可用反射或直接转为 IEnumerable)
var toListMethod = typeof(Enumerable)
.GetMethod("ToList")
.MakeGenericMethod(kv.ResultType);
var result = toListMethod.Invoke(null, new object[] { casted }); // 返回 List
results.Add(kv.OutName, result);
}
else
results.Add(kv.OutName, kv.ResultList);
}
}
}
}
///
/// 通过公式获取节点执行结果值(优先从子流程节点本地读取)
///
public new T GetValue(FormulaBound formula)
{
if (formula == null)
return default(T);
object _result = ResolveFromSubGraph(formula.Formula);
if (_result == null || _result.ToString() == "")
return formula.Value;
if (_result is T typedResult)
return typedResult;
return (T)System.Convert.ChangeType(_result, typeof(T));
}
///
/// 从子流程节点本地解析 &{节点名.属性名},跨流程 &{流程名.节点名.属性名} 回退到 ResultRegistry
///
private object ResolveFromSubGraph(string formula)
{
if (string.IsNullOrEmpty(formula)) return formula;
var match2 = System.Text.RegularExpressions.Regex.Match(formula, @"^&\{(?[^.}]+)\.(?[^.}]+)\}$");
if (match2.Success)
{
var nodeName = match2.Groups["node"].Value;
var propName = match2.Groups["prop"].Value;
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 match3 = System.Text.RegularExpressions.Regex.Match(formula, @"^&\{(?[^.}]+)\.(?[^.}]+)\.(?[^.}]+)\}$");
if (match3.Success)
{
return RegistryOrDebug.GetValue(
match3.Groups["flow"].Value,
match3.Groups["node"].Value,
match3.Groups["prop"].Value);
}
return formula;
}
}
}