using Newtonsoft.Json; using Prism.Mvvm; using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; namespace TeamAAS.Communication.Models { /// 变量作用域:Global=机器级固定(不随产品变);Product=随产品变(存各产品快照)。 public enum VarScope { Global, Product } /// /// 全局变量。 /// 持久化以 为唯一载体( 标了 JsonIgnore): /// 反序列化时先落 DataType 再由 Text 解析出强类型值,避免 object 属性被 Newtonsoft /// 还原成 long / JArray 而导致下游类型不匹配。 /// [Serializable] public class GlobalVariableModel : BindableBase { /// Text ↔ Value 双向同步的重入保护 [JsonIgnore] private bool _syncing; private int _index; public int Index { get => _index; set => SetProperty(ref _index, value); } private string _name = ""; public string Name { get => _name; set { if (SetProperty(ref _name, value)) { RaisePropertyChanged(nameof(IsValid)); } } } private string _dataType = "int"; public string DataType { get => _dataType; set { if (!SetProperty(ref _dataType, value)) return; // 换类型后同时重置 Value 和 Text:只改 Value 会让"值"列继续显示旧文本, // 与实际值错位(原实现的 Bug) _value = GetDefaultValue(value); var t = ValueToText(_value); if (_text != t) { _text = t; RaisePropertyChanged(nameof(Text)); } RaisePropertyChanged(nameof(DisplayValue)); RaisePropertyChanged(nameof(IsValid)); } } private object _value = 0; /// /// 变量值。可由代码写入任意类型(object 类型变量尤其如此);写入后自动同步 Text 显示。 /// 不参与 JSON 序列化——持久化走 Text,加载时按 DataType 重新解析出强类型值。 /// [JsonIgnore] public object Value { get => _value; set { if (!SetProperty(ref _value, value)) return; SyncTextFromValue(); RaisePropertyChanged(nameof(DisplayValue)); } } private string _text = "0"; /// 值列编辑用的文本表示,也是持久化载体。修改后按 DataType 解析进 Value。 public string Text { get => _text; set { if (!SetProperty(ref _text, value)) return; ParseValue(value); } } private VarScope _scope = VarScope.Product; /// 作用域:Global(机器级固定)/Product(随产品变)。默认 Product,兼容旧快照。 public VarScope Scope { get => _scope; set => SetProperty(ref _scope, value); } private string _note = ""; public string Note { get => _note; set => SetProperty(ref _note, value); } /// 只读展示值:集合统一渲染成 [a, b, c],其余取 ToString。 [JsonIgnore] public string DisplayValue { get { if (_value == null) return ""; if (_value is string s) return s; if (_value is IEnumerable en) return "[" + string.Join(", ", en.Cast().Select(x => x?.ToString() ?? "null")) + "]"; return _value.ToString(); } } [JsonIgnore] public bool IsValid => !string.IsNullOrWhiteSpace(Name); #region 默认值 / 文本互转 /// 该类型新建变量时的初始文本(数组/字符串/object 给空,bool 给 False,数值给 0) public static string GetDefaultText(string dataType) { switch (dataType) { case "bool": return "False"; case "string": case "object": case "short[]": case "int[]": case "double[]": case "string[]": case "bool[]": return ""; default: return "0"; } } private static object GetDefaultValue(string dataType) { switch (dataType) { case "short": return (short)0; case "short[]": return new List(); case "int": return 0; case "int[]": return new List(); case "double": return 0.0; case "double[]": return new List(); case "string": return ""; case "string[]": return new List(); case "bool": return false; case "bool[]": return new List(); // object:任意类型容器,默认空引用(由代码 SetValue 写入实际对象) case "object": return null; default: return 0; } } /// 值 → 文本(集合用逗号连接,bool 用 True/False,数值用不变文化格式) private static string ValueToText(object value) { if (value == null) return ""; if (value is string s) return s; if (value is bool b) return b ? "True" : "False"; if (value is IEnumerable en) return string.Join(",", en.Cast().Select(FormatElement)); return FormatElement(value); } private static string FormatElement(object x) { if (x == null) return ""; if (x is bool b) return b ? "True" : "False"; if (x is IFormattable f) return f.ToString(null, CultureInfo.InvariantCulture); return x.ToString(); } #endregion #region 文本解析 /// /// Text → Value。解析失败时回落到该类型默认值,并把文本框还原为默认值文本 /// (原实现只改 _text 字段不通知,界面仍显示非法输入)。 /// private void ParseValue(string text) { if (_syncing) return; _syncing = true; try { object parsed; if (TryParse(text, _dataType, out parsed)) { _value = parsed; } else { _value = GetDefaultValue(_dataType); var t = ValueToText(_value); if (_text != t) { _text = t; RaisePropertyChanged(nameof(Text)); } } RaisePropertyChanged(nameof(DisplayValue)); } finally { _syncing = false; } } /// Value → Text(代码写入值时同步界面文本) private void SyncTextFromValue() { if (_syncing) return; _syncing = true; try { var t = ValueToText(_value); if (_text != t) { _text = t; RaisePropertyChanged(nameof(Text)); } } finally { _syncing = false; } } private static bool TryParse(string text, string dataType, out object value) { value = null; var s = (text ?? "").Trim(); switch (dataType) { case "short": { short r; if (!short.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out r)) return false; value = r; return true; } case "int": { int r; if (!int.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out r)) return false; value = r; return true; } case "double": { double r; if (!double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out r)) return false; value = r; return true; } case "bool": { bool r; if (!TryParseBool(s, out r)) return false; value = r; return true; } case "string": // 字符串保留原文(不 Trim),避免吃掉用户有意输入的首尾空格 value = text ?? ""; return true; case "object": // 文本编辑时按字符串存;代码可通过 Value/SetValue 写入任意类型对象 value = text ?? ""; return true; case "short[]": return TryParseArray(s, x => { short r; return short.TryParse(x, NumberStyles.Any, CultureInfo.InvariantCulture, out r) ? (object)r : null; }, out value); case "int[]": return TryParseArray(s, x => { int r; return int.TryParse(x, NumberStyles.Any, CultureInfo.InvariantCulture, out r) ? (object)r : null; }, out value); case "double[]": return TryParseArray(s, x => { double r; return double.TryParse(x, NumberStyles.Any, CultureInfo.InvariantCulture, out r) ? (object)r : null; }, out value); case "bool[]": return TryParseArray(s, x => { bool r; return TryParseBool(x, out r) ? (object)r : null; }, out value); case "string[]": return TryParseArray(s, x => (object)x, out value); default: value = text ?? ""; return true; } } /// /// 布尔宽松解析:除 True/False 外还接受 1/0、on/off、yes/no、是/否。 /// 原实现用 bool.Parse,输入 1 会抛异常并把值静默重置为 false。 /// private static bool TryParseBool(string s, out bool result) { result = false; if (string.IsNullOrWhiteSpace(s)) return false; var t = s.Trim(); if (bool.TryParse(t, out result)) return true; switch (t.ToLowerInvariant()) { case "1": case "on": case "yes": case "y": case "是": case "真": result = true; return true; case "0": case "off": case "no": case "n": case "否": case "假": result = false; return true; default: return false; } } /// /// 数组解析:支持中英文逗号/分号分隔,空串 = 空列表;任一元素解析失败则整体失败。 /// 泛型参数保证空列表也是正确的强类型(不能靠首元素推断类型)。 /// 原实现完全没有数组分支,数组类型变量的值永远设不进去。 /// private static bool TryParseArray(string s, Func parseElement, out object value) { value = null; var items = new List(); if (!string.IsNullOrWhiteSpace(s)) { var parts = s.Split(new[] { ',', ',', ';', ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (var p in parts) { var el = parseElement(p.Trim()); if (el == null) return false; items.Add((T)el); } } value = items; return true; } #endregion /// /// 复制一份变量。不用 MemberwiseClone:它会连带复制 PropertyChanged 订阅 /// (克隆体改动会通知原对象的监听者),且 List 值是浅拷贝(与原对象共享同一实例)。 /// public GlobalVariableModel Clone() { // 直接拷字段,绕开各 setter 的联动逻辑,得到与原件完全一致的副本 return new GlobalVariableModel { _index = _index, _name = _name, _dataType = _dataType, _value = CopyValue(_value), _text = _text, _note = _note, _scope = _scope, }; } /// 创建新变量(统一 Index/Name/默认 Text/Scope 构造,供管理器与编辑器共用)。 public static GlobalVariableModel Create(string dataType, VarScope scope, int index) { var v = new GlobalVariableModel { Index = index, Name = $"Var{index + 1}", DataType = dataType, Scope = scope, }; v.Text = GetDefaultText(dataType); return v; } private static object CopyValue(object value) { if (value == null) return null; if (value is List ls) return new List(ls); if (value is List li) return new List(li); if (value is List ld) return new List(ld); if (value is List lb) return new List(lb); if (value is List lstr) return new List(lstr); // 基元/字符串不可变;object 变量持有的任意引用无法安全深拷贝,按原样共享 return value; } } public static class GlobalVariableTypes { public static readonly string[] SimpleTypes = { "short", "int", "double", "string", "bool" }; public static readonly string[] ArrayTypes = { "short[]", "int[]", "double[]", "string[]", "bool[]" }; /// 任意类型:可被代码写入任何对象(图像/集合/自定义类型),文本编辑时按字符串存取 public static readonly string[] AnyTypes = { "object" }; public static readonly string[] AllTypes = SimpleTypes.Concat(ArrayTypes).Concat(AnyTypes).ToArray(); public static string GetDisplayName(string type) { switch (type) { case "short": return "16位整数 (short)"; case "short[]": return "16位整数数组"; case "int": return "整数 (int)"; case "int[]": return "整数数组"; case "double": return "双精度浮点 (double)"; case "double[]": return "双精度浮点数组"; case "string": return "字符串 (string)"; case "string[]": return "字符串数组"; case "bool": return "布尔 (bool)"; case "bool[]": return "布尔数组"; case "object": return "任意对象 (object)"; default: return type; } } } }