using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using TeamAAS.Communication.Config; using TeamAAS.Communication.Models; namespace TeamAAS.Communication { /// /// 全局变量管理器。懒汉单例:GlobalVariableManager.Instance。 /// 变量分两级作用域:Global(机器级固定,不随产品变) / Product(随产品变)。 /// 内存里是单一 列表(公式绑定层直接读它,零改动), /// Scope 只决定持久化去向与编辑入口: /// Global 子集存 Config\global_vars.json;Product 子集存 Products\<名>\global_vars.json。 /// public class GlobalVariableManager { #region 单例 private static readonly Lazy _instance = new Lazy(() => new GlobalVariableManager(), isThreadSafe: true); /// 懒汉单例入口。首次访问时初始化,线程安全。 public static GlobalVariableManager Instance => _instance.Value; #endregion private GlobalVariableManager() { Variables.CollectionChanged += (s, e) => { if (e.NewItems != null) foreach (GlobalVariableModel v in e.NewItems) v.PropertyChanged += OnVariablePropertyChanged; if (e.OldItems != null) foreach (GlobalVariableModel v in e.OldItems) v.PropertyChanged -= OnVariablePropertyChanged; if (e.Action != System.Collections.Specialized.NotifyCollectionChangedAction.Replace) VariablesChanged?.Invoke(); }; } private void OnVariablePropertyChanged(object sender, PropertyChangedEventArgs e) { VariablesChanged?.Invoke(); } /// 全部全局变量(Global + 当前产品的 Product)。公式/结果注册表直接读它。 public ObservableCollection Variables { get; } = new ObservableCollection(); public event Action VariablesChanged; #region 变量 CRUD /// 新建变量并加入 live 列表(默认 Product 作用域)。 public void AddVariable(string dataType, VarScope scope = VarScope.Product) { Variables.Add(GlobalVariableModel.Create(dataType, scope, Variables.Count)); } /// /// 按名称写入变量值,供插件/通讯/流程写入任意类型(object 变量可承载任何对象)。 /// 变量不存在时返回 false。写入后自动触发 VariablesChanged → ResultRegistry 刷新公式数据。 /// public bool SetValue(string varName, object value) { if (string.IsNullOrWhiteSpace(varName)) return false; var v = Variables.FirstOrDefault(x => x.Name == varName); if (v == null) return false; v.Value = value; return true; } public object GetValue(string varName) { var v = Variables.FirstOrDefault(x => x.Name == varName); return v?.Value; } public void DeleteVariable(GlobalVariableModel variable) { if (variable == null) return; int idx = Variables.IndexOf(variable); if (idx < 0) return; Variables.RemoveAt(idx); Reindex(); } public void MoveUp(GlobalVariableModel variable) { if (variable == null) return; int idx = Variables.IndexOf(variable); if (idx <= 0) return; Variables.Move(idx, idx - 1); Reindex(); } public void MoveDown(GlobalVariableModel variable) { if (variable == null) return; int idx = Variables.IndexOf(variable); if (idx >= Variables.Count - 1) return; Variables.Move(idx, idx + 1); Reindex(); } private void Reindex() { for (int i = 0; i < Variables.Count; i++) Variables[i].Index = i; } public Dictionary GetAllValuesDict() { var dict = new Dictionary(); foreach (var v in Variables) { if (!string.IsNullOrWhiteSpace(v.Name)) dict[v.Name] = v.Value; } return dict; } public Dictionary> GetAsResultRegistryFormat() { var nodeDict = new Dictionary(); foreach (var v in Variables) { if (!string.IsNullOrWhiteSpace(v.Name)) nodeDict[v.Name] = v.Value; } return new Dictionary> { ["GlobalVariables"] = nodeDict }; } #endregion #region 作用域克隆 / 提交(供编辑器 克隆-编辑-提交,取消可回滚) /// 返回指定作用域变量的深拷贝列表(编辑器工作集,改动不影响 live)。 public List CloneScoped(VarScope scope) => Variables.Where(v => v.Scope == scope).Select(v => v.Clone()).ToList(); /// /// 用给定项替换 live 中该作用域的全部变量(另一作用域保持不动)。 /// 会触发 CollectionChanged → VariablesChanged,ResultRegistry 已订阅并自动刷新公式数据。 /// 必须在 UI 线程调用(Variables 绑定 UI)。 /// public void ReplaceScoped(VarScope scope, IEnumerable items) { for (int i = Variables.Count - 1; i >= 0; i--) if (Variables[i].Scope == scope) Variables.RemoveAt(i); if (items != null) foreach (var v in items) { v.Scope = scope; // 统一盖章,防外部传入 Scope 不一致 Variables.Add(v); } Reindex(); } #endregion #region 持久化:Global 机器级 / Product 产品级子集 /// 机器级 Global 变量文件:Config\global_vars.json。 public static string GlobalFixedVarsFile => TeamAAS.PathHelper.GlobalFixedVarsFile; /// 启动时载入机器级 Global 变量(替换 live 列表中的 Global 子集)。 public void LoadGlobalScoped() { try { var list = new List(); var path = GlobalFixedVarsFile; if (File.Exists(path)) { var json = File.ReadAllText(path); list = JsonConvert.DeserializeObject>(json) ?? new List(); } foreach (var v in list) v.Scope = VarScope.Global; ReplaceScoped(VarScope.Global, list); } catch { } } /// 把 live 列表中 Global 子集写入机器级文件。 public void SaveGlobalScoped() { try { var path = GlobalFixedVarsFile; var dir = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir); var list = Variables.Where(v => v.Scope == VarScope.Global).ToList(); File.WriteAllText(path, JsonConvert.SerializeObject(list, Formatting.Indented)); } catch { } } /// 把 live 列表中 Product 子集序列化到指定产品快照文件。 public void SaveSnapshot(string filePath) { try { var dir = Path.GetDirectoryName(filePath); if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir); var snap = new GlobalVarSnapshot { Variables = Variables.Where(v => v.Scope == VarScope.Product).ToList() }; File.WriteAllText(filePath, JsonConvert.SerializeObject(snap, Formatting.Indented)); } catch { } } /// /// 从产品快照恢复 Product 子集(Global 子集保持不动)。 /// 文件不存在(老产品没存过)时清空 Product 子集——切到新产品应从空开始,而非继承上个产品的变量。 /// 必须在 UI 线程调用。 /// public bool LoadSnapshot(string filePath) { try { var list = new List(); if (File.Exists(filePath)) { var json = File.ReadAllText(filePath); var snap = JsonConvert.DeserializeObject(json); list = snap?.Variables ?? new List(); } foreach (var v in list) v.Scope = VarScope.Product; ReplaceScoped(VarScope.Product, list); return File.Exists(filePath); } catch { return false; } } #endregion /// 读取某产品快照文件的 Product 变量(不改 live 列表);文件不存在返回空列表。 public List ReadSnapshotList(string filePath) { try { if (!File.Exists(filePath)) return new List(); var snap = JsonConvert.DeserializeObject(File.ReadAllText(filePath)); var list = snap?.Variables ?? new List(); foreach (var v in list) v.Scope = VarScope.Product; return list; } catch { return new List(); } } /// 把给定 Product 变量写入某产品快照文件(不改 live 列表)。 public void WriteSnapshotList(string filePath, IEnumerable items) { try { var dir = Path.GetDirectoryName(filePath); if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir); var list = (items ?? Enumerable.Empty()).ToList(); foreach (var v in list) v.Scope = VarScope.Product; var snap = new GlobalVarSnapshot { Variables = list }; File.WriteAllText(filePath, JsonConvert.SerializeObject(snap, Formatting.Indented)); } catch { } } /// 加载全局事件配置(心跳 / 产品切换事件)。静态,与变量作用域无关。 public static GlobalEventConfig LoadGlobalEventConfig() { try { var path = TeamAAS.PathHelper.GlobalEventConfigFile; if (!File.Exists(path)) return new GlobalEventConfig(); var json = File.ReadAllText(path); return JsonConvert.DeserializeObject(json) ?? new GlobalEventConfig(); } catch { return new GlobalEventConfig(); } } } }