PluginDataProvider.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. using HandyControl.Tools.Extension;
  2. using PropertyGridLib.Controls;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using TeamAAS.Communication;
  7. using TeamAAS.FlowEditor.Execution;
  8. using TeamAAS.FlowEditor.Models;
  9. using TeamAAS.FlowEngine;
  10. namespace TeamAAS.FlowEngine.FormulaData
  11. {
  12. /// <summary>
  13. /// 普通节点属性的公式树数据源。基于当前选中节点 <see cref="PluginLoader.SelectFlow"/> 的上下文,
  14. /// 提供三类节点:全局变量(Gloab)、组合模块输入参数(输入)、当前流程所有前置节点输出结果。
  15. /// 自动处理「编辑组合模块内部节点」「编辑组合模块输出属性」「编辑主流程普通节点」三种场景。
  16. /// </summary>
  17. [Serializable]
  18. public class PluginDataProvider : IFormulaTreeProvider
  19. {
  20. /// <summary>
  21. /// 生成公式树节点列表,按 <see cref="PluginLoader.SelectFlow"/> 上下文过滤前置节点。
  22. /// </summary>
  23. public List<FormulaTreeNode> GetFormulaTree(PropertyItem propertyItem)
  24. {
  25. var nodes = new List<FormulaTreeNode>();
  26. try
  27. {
  28. Dictionary<string, Dictionary<string, object>> flowResults;
  29. string findId;
  30. string findName = PluginLoader.Instance.SelectFlow.NodeName;
  31. FlowGraph graph = null;
  32. if (PluginLoader.Instance.SetGroupPluginModel != null)
  33. {
  34. // 编辑组合模块内部节点 — 查 SubGraph 结果(所有节点均为前置,不做过滤)
  35. findId = PluginLoader.Instance.SetGroupPluginModel.GraphId;
  36. findName = PluginLoader.Instance.SetGroupPluginModel.GraphName;
  37. graph = PluginLoader.Instance.SetGroupPluginModel;
  38. }
  39. else if (PluginLoader.Instance.SelectFlow.ToolName == "组合模块")
  40. {
  41. // 编辑组合模块输出属性 — 查 SubGraph 结果,全部节点均为前置
  42. findId = PluginLoader.Instance.SelectFlow.NodeId;
  43. }
  44. else
  45. {
  46. // 普通节点 — 查当前流程结果(key=GraphId)
  47. findId = PluginLoader.Instance.SelectFlow.FlowId;
  48. var tab = PluginLoader.Instance.FlowTabs.FirstOrDefault(t => t.Graph != null && t.Graph.GraphId == findId);
  49. graph = tab != null ? tab.Graph : null;
  50. // 回退查找:子节点的 FlowId 是 SubGraph 的 GraphId,不在 FlowTabs 中
  51. if (graph == null)
  52. {
  53. foreach (var t in PluginLoader.Instance.FlowTabs)
  54. {
  55. if (t?.Graph == null) continue;
  56. graph = FindSubGraphById(t.Graph, findId);
  57. if (graph != null) break;
  58. }
  59. }
  60. }
  61. // 使用选中节点所属流程绑定的注册表(编辑态=Debug,监控态挂接运行图=Main)
  62. var registry = PluginLoader.Instance.SelectFlow?.Registry ?? ResultRegistry.Debug;
  63. registry.GetFlowResults(findId, out flowResults);
  64. // 可见节点名集合(绑定树的一级节点):
  65. // - 普通节点:反向追溯到的前置节点(即使尚未运行、registry 无结果也可见 → 支持“跑之前就能绑”)
  66. // - 编辑组合模块内部:全部子节点均为前置
  67. // - 拿不到 graph 时:退回运行时结果键(保持旧行为)
  68. HashSet<string> visibleNodes;
  69. if (PluginLoader.Instance.SetGroupPluginModel == null)
  70. {
  71. flowResults.DeleteIfExistsKey(findName);
  72. if (graph != null && PluginLoader.Instance.SelectFlow != null)
  73. visibleNodes = GetPredecessorNodeNames(graph, PluginLoader.Instance.SelectFlow.NodeId);
  74. else
  75. visibleNodes = new HashSet<string>(flowResults.Keys);
  76. }
  77. else
  78. {
  79. visibleNodes = new HashSet<string>(flowResults.Keys);
  80. if (graph != null)
  81. foreach (var n in graph.Nodes)
  82. if (!string.IsNullOrEmpty(n?.NodeName)) visibleNodes.Add(n.NodeName);
  83. }
  84. // 声明 schema:节点名 -> (输出键 -> 声明类型),取自各节点插件的 DeclareOutputs()
  85. var schemaByName = new Dictionary<string, Dictionary<string, Type>>();
  86. if (graph != null)
  87. {
  88. foreach (var n in graph.Nodes)
  89. {
  90. if (n?.PluginModel == null || !visibleNodes.Contains(n.NodeName)) continue;
  91. var dict = new Dictionary<string, Type>();
  92. try
  93. {
  94. foreach (var f in n.PluginModel.DeclareOutputs())
  95. if (f != null && !string.IsNullOrEmpty(f.Key)) dict[f.Key] = f.ValueType;
  96. }
  97. catch { }
  98. schemaByName[n.NodeName] = dict;
  99. }
  100. }
  101. if (propertyItem.Value == null)
  102. propertyItem.Value = new object();
  103. var targetType = propertyItem.Value.GetType();
  104. var formulaType = propertyItem.PropertyType;
  105. foreach (var nodeName in visibleNodes)
  106. {
  107. if (string.IsNullOrEmpty(nodeName)) continue;
  108. flowResults.TryGetValue(nodeName, out var runtime);
  109. schemaByName.TryGetValue(nodeName, out var declared);
  110. // 合并“声明键 + 运行时键”;有效类型优先取运行时值类型,值为空则回退到声明类型
  111. var merged = new Dictionary<string, Type>();
  112. if (declared != null)
  113. foreach (var kv in declared) merged[kv.Key] = kv.Value;
  114. if (runtime != null)
  115. foreach (var kv in runtime)
  116. merged[kv.Key] = kv.Value != null
  117. ? kv.Value.GetType()
  118. : (merged.TryGetValue(kv.Key, out var dt) ? dt : null);
  119. var children = merged
  120. .Where(kv => targetType == null || targetType == typeof(object)
  121. || (kv.Value != null && (kv.Value == targetType || kv.Value == formulaType)))
  122. .Select(kv => new FormulaTreeNode
  123. {
  124. Header = kv.Key,
  125. Formula = $"&{{{nodeName}.{kv.Key}}}"
  126. }).ToList();
  127. if (children.Count == 0) continue;
  128. nodes.Add(new FormulaTreeNode { Header = nodeName, Children = children });
  129. }
  130. // ========== 全局变量节点 ==========
  131. try
  132. {
  133. registry.RefreshGlobalVariables();
  134. var gloabNode = new FormulaTreeNode
  135. {
  136. Header = "Gloab",
  137. Children = GlobalVariableManager.Instance.Variables
  138. .Where(v => !string.IsNullOrWhiteSpace(v.Name))
  139. .Select(v => new FormulaTreeNode
  140. {
  141. Header = v.Name,
  142. Formula = $"&{{Gloab.{v.Name}}}"
  143. }).ToList()
  144. };
  145. if (gloabNode.Children.Count > 0)
  146. nodes.Insert(0, gloabNode);
  147. }
  148. catch { }
  149. // ========== 输入参数节点(组合模块内部) ==========
  150. try
  151. {
  152. if (PluginLoader.Instance.SetGroupPluginModel == null)
  153. {
  154. var inputsNode = BuildInputsNode(propertyItem);
  155. if (inputsNode != null && inputsNode.Children.Count > 0)
  156. nodes.Insert(0, inputsNode);
  157. }
  158. }
  159. catch { }
  160. }
  161. catch { }
  162. return nodes;
  163. }
  164. /// <summary>
  165. /// 构建「输入」参数公式树节点 — 从 <see cref="PluginLoader.ParentGroupModel"/> 的 ModuleInputs 反射读取。
  166. /// 用于组合模块内部节点属性编辑。
  167. /// </summary>
  168. private static FormulaTreeNode BuildInputsNode(PropertyItem propertyItem)
  169. {
  170. var parent = PluginLoader.Instance.ParentGroupModel;
  171. if (parent == null) return null;
  172. var moduleInputsProp = parent.GetType().GetProperty("ModuleInputs");
  173. if (moduleInputsProp == null) return null;
  174. var inputs = moduleInputsProp.GetValue(parent) as System.Collections.IList;
  175. if (inputs == null || inputs.Count == 0) return null;
  176. var targetType = propertyItem?.Value?.GetType();
  177. var formulaType = propertyItem?.PropertyType;
  178. var children = new List<FormulaTreeNode>();
  179. foreach (var item in inputs)
  180. {
  181. if (item == null) continue;
  182. var nameProp = item.GetType().GetProperty("InputName");
  183. var dataTypeProp = item.GetType().GetProperty("DataType");
  184. var inputName = nameProp?.GetValue(item) as string;
  185. var dataType = dataTypeProp?.GetValue(item) as string ?? "";
  186. if (string.IsNullOrWhiteSpace(inputName)) continue;
  187. if (targetType != null && targetType != typeof(object) && !string.IsNullOrEmpty(dataType))
  188. {
  189. try
  190. {
  191. var inputType = Type.GetType(dataType) ?? Type.GetType("System." + dataType + ", mscorlib");
  192. if (inputType != null && inputType != typeof(object)
  193. && inputType != targetType && inputType != formulaType)
  194. continue;
  195. }
  196. catch { }
  197. }
  198. children.Add(new FormulaTreeNode
  199. {
  200. Header = string.IsNullOrEmpty(dataType) ? inputName : $"{inputName} ({dataType})",
  201. Formula = $"&{{输入.{inputName}}}"
  202. });
  203. }
  204. if (children.Count == 0) return null;
  205. return new FormulaTreeNode { Header = "输入", Children = children };
  206. }
  207. /// <summary>
  208. /// 在流程图中递归查找 GraphId 匹配的 SubGraph(支持嵌套组合模块)。
  209. /// </summary>
  210. private static FlowGraph FindSubGraphById(FlowGraph parentGraph, string graphId)
  211. {
  212. foreach (var node in parentGraph.Nodes)
  213. {
  214. var model = node.PluginModel?.GetModel;
  215. if (model == null) continue;
  216. var graphProp = model.GetType().GetProperty("SubGraph");
  217. if (graphProp != null)
  218. {
  219. var subGraph = graphProp.GetValue(model) as FlowGraph;
  220. if (subGraph != null)
  221. {
  222. if (subGraph.GraphId == graphId) return subGraph;
  223. var nested = FindSubGraphById(subGraph, graphId);
  224. if (nested != null) return nested;
  225. }
  226. }
  227. }
  228. return null;
  229. }
  230. /// <summary>
  231. /// 获取指定节点的所有前置节点名称(通过连接线反向 BFS 追溯)。
  232. /// </summary>
  233. private static HashSet<string> GetPredecessorNodeNames(FlowGraph graph, string nodeId)
  234. {
  235. var predecessorIds = new HashSet<string>();
  236. var queue = new Queue<string>();
  237. // 从直接上游开始
  238. foreach (var conn in graph.Connections)
  239. {
  240. if (conn.TargetNodeId == nodeId) queue.Enqueue(conn.SourceNodeId);
  241. }
  242. while (queue.Count > 0)
  243. {
  244. var current = queue.Dequeue();
  245. if (predecessorIds.Add(current))
  246. {
  247. foreach (var conn in graph.Connections)
  248. {
  249. if (conn.TargetNodeId == current) queue.Enqueue(conn.SourceNodeId);
  250. }
  251. }
  252. }
  253. // NodeId -> NodeName
  254. var result = new HashSet<string>();
  255. foreach (var node in graph.Nodes)
  256. {
  257. if (predecessorIds.Contains(node.NodeId))
  258. result.Add(node.NodeName);
  259. }
  260. return result;
  261. }
  262. }
  263. }