using System; using System.Threading.Tasks; using Prism.Commands; using Prism.Mvvm; using XP.Hardware.PLC.Models; namespace XP.Hardware.PLC.Sentry.ViewModels { /// /// 单行信号 ViewModel,封装 SignalEntry 并添加运行时状态 | Single signal row ViewModel wrapping SignalEntry with runtime state /// public class SignalRowViewModel : BindableBase { private readonly SignalEntry _signal; private string _currentValue = "--"; private bool _hasReadError; private object _writeValue; private string _validationError; /// /// 构造函数 | Constructor /// /// 信号定义条目 | Signal definition entry /// 队列写入回调 | Queue write callback /// 直接写入回调 | Direct write callback /// 写入命令是否可用判断 | Write command can-execute predicate public SignalRowViewModel( SignalEntry signal, Action applyAction, Action directWriteAction, Func canExecuteWrite) { _signal = signal ?? throw new ArgumentNullException(nameof(signal)); ApplyCommand = new DelegateCommand( () => applyAction?.Invoke(this), () => canExecuteWrite?.Invoke() ?? false); DirectWriteCommand = new DelegateCommand( () => directWriteAction?.Invoke(this), () => canExecuteWrite?.Invoke() ?? false); } // === 信号定义信息(只读)| Signal definition info (read-only) === /// /// 信号名称 | Signal name /// public string Name => _signal.Name; /// /// 数据类型 | Data type /// public string Type => _signal.Type; /// /// 起始地址 | Start address /// public int StartAddr => _signal.StartAddr; /// /// 长度/索引 | Length/Index /// public string IndexOrLength => _signal.IndexOrLength; /// /// 备注 | Remark /// public string Remark => _signal.Remark; /// /// DB 块号 | DB block number /// public int DBNumber => _signal.DBNumber; /// /// 原始信号定义条目 | Original signal definition entry /// public SignalEntry Signal => _signal; /// /// 格式化的地址显示 | Formatted address display /// bool 类型显示位地址(DB{n}.{addr}.{bit}),其他类型显示字节地址(DB{n}.{addr}) /// public string AddressDisplay => Type?.ToLowerInvariant() == "bool" ? $"DB{DBNumber}.{StartAddr}.{IndexOrLength}" : $"DB{DBNumber}.{StartAddr}"; // === 运行时状态 | Runtime state === /// /// 当前读取值 | Current read value /// public string CurrentValue { get => _currentValue; set => SetProperty(ref _currentValue, value ?? "--"); } /// /// 是否存在读取错误 | Whether read error exists /// public bool HasReadError { get => _hasReadError; set => SetProperty(ref _hasReadError, value); } /// /// 写入值 | Write value /// public object WriteValue { get => _writeValue; set { if (SetProperty(ref _writeValue, value)) { // 写入值变更时清除校验错误 | Clear validation error when write value changes ValidationError = null; } } } /// /// 校验错误信息 | Validation error message /// public string ValidationError { get => _validationError; set => SetProperty(ref _validationError, value); } // === 命令 | Commands === /// /// 队列写入命令 | Queue write command /// public DelegateCommand ApplyCommand { get; } /// /// 直接写入+回读校验命令 | Direct write with verify command /// public DelegateCommand DirectWriteCommand { get; } /// /// 刷新写入命令可用状态 | Refresh write command can-execute state /// public void RefreshCommandState() { ApplyCommand.RaiseCanExecuteChanged(); DirectWriteCommand.RaiseCanExecuteChanged(); } /// /// 校验写入值是否符合信号数据类型要求 | Validate write value against signal data type /// /// 校验错误信息,null 表示通过 | Validation error message, null means passed public string ValidateWriteValue() { if (WriteValue == null) return "写入值不能为空 | Write value cannot be empty"; var strValue = WriteValue.ToString(); if (string.IsNullOrWhiteSpace(strValue)) return "写入值不能为空 | Write value cannot be empty"; switch (Type?.ToLowerInvariant()) { case "bool": if (!bool.TryParse(strValue, out _)) return "布尔值仅接受 True/False | Bool accepts only True/False"; break; case "byte": if (!byte.TryParse(strValue, out _)) return "字节值超出范围 (0-255) | Byte value out of range (0-255)"; break; case "short": if (!short.TryParse(strValue, out _)) return "短整型值超出范围 | Short value out of range"; break; case "int": if (!int.TryParse(strValue, out _)) return "整型值超出范围 | Int value out of range"; break; case "single": if (!float.TryParse(strValue, out _)) return "浮点数格式错误 | Float format error"; break; case "double": if (!double.TryParse(strValue, out _)) return "双精度浮点数格式错误 | Double format error"; break; case "string": // 字符串接受任意非空值 | String accepts any non-empty value break; default: return $"不支持的类型: {Type} | Unsupported type: {Type}"; } return null; } } }