ResultRegistry.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. using TeamAAS.Communication;
  8. namespace TeamAAS.FlowEditor.Execution
  9. {
  10. /// <summary>
  11. /// 执行结果注册中心 - 存储各节点最新执行结果,支持 &{节点名.属性名} 公式解析
  12. /// 内部结构: Dictionary<GraphId, Dictionary<NodeName, Dictionary<PropName, object>>>
  13. /// 保留键 "Gloab" 用于存储全局变量
  14. ///
  15. /// 实例化设计:主页面运行流程与产品管理编辑/调试流程使用各自独立的注册表实例,
  16. /// 避免 Debug 流程的清理/写入干扰运行中流程的结果缓存(导致内存释放异常)。
  17. /// - <see cref="Main"/>:主页面的运行流程(FlowRunner)专用
  18. /// - <see cref="Debug"/>:产品管理编辑/调试流程专用
  19. /// 产品管理进入监控模式时挂接的是主页面运行态流程,其 Graph.Registry 指向 Main,
  20. /// 因此公式解析/结果读取会自动落到主面对象上。
  21. /// </summary>
  22. public class ResultRegistry
  23. {
  24. /// <summary>主页面运行流程专用注册表</summary>
  25. public static ResultRegistry Main { get; } = new ResultRegistry();
  26. /// <summary>产品管理编辑/调试流程专用注册表</summary>
  27. public static ResultRegistry Debug { get; } = new ResultRegistry();
  28. public const string GlobalVariablesKey = "Gloab";
  29. public readonly Dictionary<string, Dictionary<string, Dictionary<string, object>>> _store
  30. = new Dictionary<string, Dictionary<string, Dictionary<string, object>>>();
  31. private readonly object _lock = new object();
  32. private bool _initialized;
  33. /// <summary>
  34. /// 初始化所有注册表实例:订阅全局变量变化事件,自动刷新 ResultRegistry
  35. /// </summary>
  36. public static void Initialize()
  37. {
  38. Main.InitializeInstance();
  39. Debug.InitializeInstance();
  40. }
  41. /// <summary>
  42. /// 刷新所有注册表实例的全局变量区(全局变量为进程级共享数据)
  43. /// </summary>
  44. public static void RefreshGlobals()
  45. {
  46. Main.RefreshGlobalVariables();
  47. Debug.RefreshGlobalVariables();
  48. }
  49. /// <summary>
  50. /// 初始化:订阅全局变量变化事件,自动刷新本注册表
  51. /// </summary>
  52. public void InitializeInstance()
  53. {
  54. if (_initialized) return;
  55. _initialized = true;
  56. GlobalVariableManager.Instance.VariablesChanged += RefreshGlobalVariables;
  57. RefreshGlobalVariables();
  58. }
  59. /// <summary>
  60. /// 设置节点最新执行结果
  61. /// </summary>
  62. public void SetResult(string graphId, string nodeid, string nodeName, Dictionary<string, object> results)
  63. {
  64. if (string.IsNullOrEmpty(graphId) || string.IsNullOrEmpty(nodeName)) return;
  65. lock (_lock)
  66. {
  67. if (!_store.TryGetValue(graphId, out var flowDict))
  68. {
  69. flowDict = new Dictionary<string, Dictionary<string, object>>();
  70. _store[graphId] = flowDict;
  71. }
  72. flowDict[nodeName] = results ?? new Dictionary<string, object>();
  73. }
  74. }
  75. /// <summary>
  76. /// 向指定流程的「输入」虚拟节点写入单个变量(供 &amp;{输入.变量名} 2段式解析)
  77. /// </summary>
  78. public void SetInputVariable(string graphId, string varName, object value)
  79. {
  80. if (string.IsNullOrEmpty(graphId) || string.IsNullOrEmpty(varName)) return;
  81. lock (_lock)
  82. {
  83. if (!_store.TryGetValue(graphId, out var flowDict))
  84. {
  85. flowDict = new Dictionary<string, Dictionary<string, object>>();
  86. _store[graphId] = flowDict;
  87. }
  88. if (!flowDict.TryGetValue("输入", out var inputDict))
  89. {
  90. inputDict = new Dictionary<string, object>();
  91. flowDict["输入"] = inputDict;
  92. }
  93. inputDict[varName] = value;
  94. }
  95. }
  96. /// <summary>
  97. /// 获取指定流程下所有节点的结果
  98. /// </summary>
  99. public void GetFlowResults(string graphId, out Dictionary<string, Dictionary<string, object>> flowResults)
  100. {
  101. flowResults = new Dictionary<string, Dictionary<string, object>>();
  102. if (string.IsNullOrEmpty(graphId)) return;
  103. lock (_lock)
  104. {
  105. if (_store.TryGetValue(graphId, out var dict))
  106. {
  107. foreach (var kvp in dict)
  108. flowResults[kvp.Key] = kvp.Value;
  109. }
  110. }
  111. }
  112. /// <summary>
  113. /// 获取节点最新结果字典
  114. /// </summary>
  115. public Dictionary<string, object> GetResult(string graphId, string nodeName)
  116. {
  117. lock (_lock)
  118. {
  119. if (_store.TryGetValue(graphId, out var flowDict) && flowDict.TryGetValue(nodeName, out var dict))
  120. return dict;
  121. }
  122. return null;
  123. }
  124. /// <summary>
  125. /// 获取节点某个属性值
  126. /// </summary>
  127. public object GetValue(string graphId, string nodeName, string propertyName)
  128. {
  129. lock (_lock)
  130. {
  131. if (_store.TryGetValue(graphId, out var flowDict)
  132. && flowDict.TryGetValue(nodeName, out var dict)
  133. && dict.TryGetValue(propertyName, out var val))
  134. return val;
  135. }
  136. return null;
  137. }
  138. /// <summary>
  139. /// 刷新全局变量到 _store["Gloab"]
  140. /// </summary>
  141. public void RefreshGlobalVariables()
  142. {
  143. var vars = GlobalVariableManager.Instance.Variables;
  144. lock (_lock)
  145. {
  146. if (!_store.TryGetValue(GlobalVariablesKey, out var gloabDict))
  147. {
  148. gloabDict = new Dictionary<string, Dictionary<string, object>>();
  149. _store[GlobalVariablesKey] = gloabDict;
  150. }
  151. gloabDict.Clear();
  152. foreach (var v in vars)
  153. {
  154. if (string.IsNullOrWhiteSpace(v.Name)) continue;
  155. gloabDict[v.Name] = new Dictionary<string, object>
  156. {
  157. ["Value"] = v.Value,
  158. ["DataType"] = v.DataType,
  159. ["Note"] = v.Note ?? ""
  160. };
  161. }
  162. }
  163. }
  164. /// <summary>
  165. /// 解析公式 &{节点名.属性名}(2段式)或 &{流程名.节点名.属性名}(3段式)
  166. /// 特殊支持 &{Gloab.变量名} 直接获取全局变量值(2段式),或 &{Gloab.变量名.Value}(3段式)
  167. /// </summary>
  168. public object ResolveFormula(string FlowId, string formula)
  169. {
  170. if (string.IsNullOrEmpty(formula)) return formula;
  171. var match2 = Regex.Match(formula, @"^&\{(?<node>[^.}]+)\.(?<prop>[^.}]+)\}$");
  172. var match3 = Regex.Match(formula, @"^&\{(?<flow>[^.}]+)\.(?<node>[^.}]+)\.(?<prop>[^.}]+)\}$");
  173. if (match2.Success)
  174. {
  175. string first = match2.Groups["node"].Value;
  176. string second = match2.Groups["prop"].Value;
  177. if (first == GlobalVariablesKey)
  178. {
  179. return GetValue(GlobalVariablesKey, second, "Value");
  180. }
  181. var result = GetValue(FlowId, first, second);
  182. return result;
  183. }
  184. else if (match3.Success)
  185. {
  186. string flow = match3.Groups["flow"].Value;
  187. string node = match3.Groups["node"].Value;
  188. string prop = match3.Groups["prop"].Value;
  189. if (flow == GlobalVariablesKey)
  190. {
  191. return GetValue(GlobalVariablesKey, node, prop);
  192. }
  193. return GetValue(flow, node, prop);
  194. }
  195. else
  196. return formula;
  197. }
  198. /// <summary>
  199. /// 删除指定流程中某个节点的执行结果
  200. /// </summary>
  201. public void RemoveNodeResult(string graphId, string nodeName)
  202. {
  203. if (string.IsNullOrEmpty(graphId) || string.IsNullOrEmpty(nodeName)) return;
  204. lock (_lock)
  205. {
  206. if (_store.TryGetValue(graphId, out var flowDict))
  207. flowDict.Remove(nodeName);
  208. }
  209. }
  210. /// <summary>
  211. /// 清空指定流程的所有结果
  212. /// </summary>
  213. public void ClearFlow(string graphId)
  214. {
  215. if (string.IsNullOrEmpty(graphId)) return;
  216. lock (_lock)
  217. {
  218. _store.Remove(graphId);
  219. }
  220. }
  221. /// <summary>
  222. /// 完全释放所有流程数据(各流程 GraphId 及其子流程 SubGraphId 条目全部移除),仅保留全局变量区。
  223. /// ClearFlow 只按单个 GraphId 清理,会漏掉组合/循环子流程写入的 SubGraphId 条目;
  224. /// 切换/重载产品、进出监控时用本方法彻底清干净,避免上次产品的数据残留堆积。
  225. /// </summary>
  226. public void ClearAllFlows()
  227. {
  228. lock (_lock)
  229. {
  230. var flowKeys = _store.Keys.Where(k => k != GlobalVariablesKey).ToList();
  231. foreach (var key in flowKeys)
  232. _store.Remove(key);
  233. }
  234. }
  235. /// <summary>
  236. /// 清空所有结果
  237. /// </summary>
  238. public void Clear()
  239. {
  240. lock (_lock)
  241. {
  242. _store.Clear();
  243. }
  244. }
  245. #region 调试监控(供主页“注册表监控”窗口的 PropertyGrid 直接绑定 Main / Debug 显示)
  246. /// <summary>【监控】当前缓存的流程数(不含全局变量区)。若 Debug 持续增长而不回落,通常意味着清理没做好。</summary>
  247. [Category("注册表监控")]
  248. [DisplayName("流程数")]
  249. [ReadOnly(true)]
  250. public int FlowCount
  251. {
  252. get { lock (_lock) return _store.Keys.Count(k => k != GlobalVariablesKey); }
  253. }
  254. /// <summary>【监控】缓存的流程 GraphId 列表(逗号分隔)。</summary>
  255. [Category("注册表监控")]
  256. [DisplayName("流程ID列表")]
  257. [ReadOnly(true)]
  258. public string FlowIds
  259. {
  260. get { lock (_lock) return string.Join(", ", _store.Keys.Where(k => k != GlobalVariablesKey)); }
  261. }
  262. /// <summary>【监控】全局变量条目数。</summary>
  263. [Category("注册表监控")]
  264. [DisplayName("全局变量数")]
  265. [ReadOnly(true)]
  266. public int GlobalVariableCount
  267. {
  268. get { lock (_lock) return _store.TryGetValue(GlobalVariablesKey, out var g) ? g.Count : 0; }
  269. }
  270. /// <summary>【监控】是否已初始化(已订阅全局变量变化事件)。</summary>
  271. [Category("注册表监控")]
  272. [DisplayName("已初始化")]
  273. [ReadOnly(true)]
  274. public bool IsInitialized
  275. {
  276. get { lock (_lock) return _initialized; }
  277. }
  278. /// <summary>
  279. /// 【监控】完整内容快照(多行文本):流程 → 节点 → 结果键值,用于排查 Main / Debug 是否被正确管理。
  280. /// 大对象(图像 / CogRecord 等)只显示类型名,避免 ToString 噪音。
  281. /// </summary>
  282. [Category("注册表监控")]
  283. [DisplayName("内容快照")]
  284. [ReadOnly(true)]
  285. public string Snapshot
  286. {
  287. get
  288. {
  289. lock (_lock)
  290. {
  291. var sb = new StringBuilder();
  292. foreach (var flow in _store)
  293. {
  294. if (flow.Key == GlobalVariablesKey)
  295. {
  296. sb.AppendLine($"[全局变量 Gloab] {flow.Value.Count} 项");
  297. foreach (var v in flow.Value)
  298. {
  299. object gv = v.Value != null && v.Value.TryGetValue("Value", out var raw) ? raw : null;
  300. sb.AppendLine($" {v.Key} = {DescribeValue(gv)}");
  301. }
  302. continue;
  303. }
  304. sb.AppendLine($"[流程 {flow.Key}] 节点 {flow.Value.Count} 个");
  305. foreach (var node in flow.Value)
  306. {
  307. sb.AppendLine($" · {node.Key}({node.Value.Count} 项)");
  308. foreach (var kv in node.Value)
  309. sb.AppendLine($" {kv.Key} = {DescribeValue(kv.Value)}");
  310. }
  311. }
  312. return sb.Length == 0 ? "(空)" : sb.ToString();
  313. }
  314. }
  315. }
  316. /// <summary>监控快照的值格式化:基础类型直接 ToString,集合显示项数,其余大对象只显示类型名。</summary>
  317. private static string DescribeValue(object v)
  318. {
  319. if (v == null) return "null";
  320. var t = v.GetType();
  321. bool basic = t.IsPrimitive || v is string || v is decimal || v is DateTime || t.IsEnum;
  322. if (basic) return v.ToString();
  323. if (v is System.Collections.IEnumerable en)
  324. {
  325. int c = 0;
  326. foreach (var _ in en) c++;
  327. return $"{t.Name}({c} 项)";
  328. }
  329. return t.Name;
  330. }
  331. #endregion
  332. }
  333. }