diff --git a/SettingModule/SettingModule.cs b/SettingModule/SettingModule.cs index 56f2440..32ec4e9 100644 --- a/SettingModule/SettingModule.cs +++ b/SettingModule/SettingModule.cs @@ -1,4 +1,5 @@ using SettingModule.Views; +using SettingModule.Views.Dialogs; using System.Reflection; namespace SettingModule @@ -14,6 +15,10 @@ namespace SettingModule public void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation("SettingView"); + + // 设备连接配置弹窗 + containerRegistry.RegisterDialog("TCPConfig"); + containerRegistry.RegisterDialog("SerialPortConfig"); } } } diff --git a/SettingModule/ViewModels/Dialogs/SerialPortConfigViewModel.cs b/SettingModule/ViewModels/Dialogs/SerialPortConfigViewModel.cs new file mode 100644 index 0000000..3b1473e --- /dev/null +++ b/SettingModule/ViewModels/Dialogs/SerialPortConfigViewModel.cs @@ -0,0 +1,164 @@ +using System.Collections.ObjectModel; +using System.IO.Ports; +using System.Linq; +using System.Windows.Input; +using UIShare.PubEvent; +using UIShare.UIViewModel; +using UIShare.ViewModelBase; + +namespace SettingModule.ViewModels.Dialogs +{ + /// + /// 串口连接配置对话框 VM。 + /// 通过 DialogParameters 接收宿主 DeviceInfoModel;保存时把副本写回宿主。 + /// + public class SerialPortConfigViewModel : DialogViewModelBase + { + #region 属性 + + private string _title = "串口连接配置"; + public string Title + { + get => _title; + set => SetProperty(ref _title, value); + } + + private SerialPortConnectionConfig _config = new(); + public SerialPortConnectionConfig Config + { + get => _config; + set => SetProperty(ref _config, value); + } + + /// 当前可用串口列表(OnDialogOpened 刷新)。 + public ObservableCollection AvailablePorts { get; } = new(); + + public ObservableCollection CommonBaudRates { get; } = new() + { + 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 230400, 460800, 921600 + }; + + public ObservableCollection DataBitsList { get; } = new() { 5, 6, 7, 8 }; + + public ObservableCollection StopBitsList { get; } = new() + { + "One", "OnePointFive", "Two" + }; + + public ObservableCollection ParityList { get; } = new() + { + "None", "Odd", "Even", "Mark", "Space" + }; + + public ObservableCollection CommonTimeouts { get; } = new() + { + 500, 1000, 2000, 3000, 5000, 10000 + }; + + private string _errorMessage = string.Empty; + public string ErrorMessage + { + get => _errorMessage; + set => SetProperty(ref _errorMessage, value); + } + + #endregion + + #region 命令 + public ICommand SaveCommand { get; } + public ICommand CancelCommand { get; } + public ICommand RefreshPortsCommand { get; } + #endregion + + private DeviceInfoModel? _hostDevice; + + public SerialPortConfigViewModel(IContainerProvider containerProvider) : base(containerProvider) + { + SaveCommand = new DelegateCommand(OnSave); + CancelCommand = new DelegateCommand(OnCancel); + RefreshPortsCommand = new DelegateCommand(RefreshPorts); + } + + private void RefreshPorts() + { + AvailablePorts.Clear(); + + + // 若当前选中的串口不在可用列表中,仍保留显示,便于离线编辑 + if (!string.IsNullOrEmpty(Config.PortName) && !AvailablePorts.Contains(Config.PortName)) + AvailablePorts.Insert(0, Config.PortName); + } + + private bool Validate(out string error) + { + error = string.Empty; + if (string.IsNullOrWhiteSpace(Config.PortName)) + { + error = "串口名称不能为空"; + return false; + } + if (Config.BaudRate <= 0) + { + error = "波特率必须大于 0"; + return false; + } + if (Config.DataBits < 5 || Config.DataBits > 8) + { + error = "数据位必须在 5 - 8 之间"; + return false; + } + if (Config.ReadTimeout < 0 || Config.WriteTimeout < 0) + { + error = "超时时间不能为负数"; + return false; + } + return true; + } + + private void OnSave() + { + if (!Validate(out var error)) + { + ErrorMessage = error; + return; + } + ErrorMessage = string.Empty; + + if (_hostDevice != null) + { + _hostDevice.SerialPortConfig ??= new SerialPortConnectionConfig(); + Config.CopyTo(_hostDevice.SerialPortConfig); + _hostDevice.ConnectionType = "Serial"; + } + + RequestClose.Invoke(ButtonResult.OK); + } + + private void OnCancel() => RequestClose.Invoke(ButtonResult.Cancel); + + #region Prism Dialog 规范 + public override void OnDialogOpened(IDialogParameters parameters) + { + _eventAggregator.GetEvent().Publish(true); + + if (parameters.ContainsKey("Device")) + { + _hostDevice = parameters.GetValue("Device"); + Title = $"串口连接配置 - {_hostDevice?.DeviceName}"; + Config = new SerialPortConnectionConfig(_hostDevice?.SerialPortConfig); + } + else if (parameters.ContainsKey("Config")) + { + Config = new SerialPortConnectionConfig(parameters.GetValue("Config")); + } + + RefreshPorts(); + } + + public override void OnDialogClosed() + { + _eventAggregator.GetEvent().Publish(false); + } + #endregion + } +} diff --git a/SettingModule/ViewModels/Dialogs/TCPConfigViewModel.cs b/SettingModule/ViewModels/Dialogs/TCPConfigViewModel.cs new file mode 100644 index 0000000..a45b8fc --- /dev/null +++ b/SettingModule/ViewModels/Dialogs/TCPConfigViewModel.cs @@ -0,0 +1,139 @@ +using System.Collections.ObjectModel; +using System.Windows.Input; +using UIShare.PubEvent; +using UIShare.UIViewModel; +using UIShare.ViewModelBase; + + +namespace SettingModule.ViewModels.Dialogs +{ + /// + /// TCP 连接配置对话框 VM。 + /// 通过 DialogParameters 接收宿主 DeviceInfoModel;保存时把副本写回宿主。 + /// + public class TCPConfigViewModel : DialogViewModelBase + { + #region 属性 + + private string _title = "TCP 连接配置"; + public string Title + { + get => _title; + set => SetProperty(ref _title, value); + } + + /// 编辑用的副本,取消时不会污染宿主对象。 + private TcpConnectionConfig _config = new(); + public TcpConnectionConfig Config + { + get => _config; + set => SetProperty(ref _config, value); + } + + /// 常用 Modbus / 自定义协议端口建议。 + public ObservableCollection CommonPorts { get; } = new() + { + 502, 102, 80, 8080, 5020, 4840 + }; + + /// 常用超时(毫秒)。 + public ObservableCollection CommonTimeouts { get; } = new() + { + 500, 1000, 2000, 3000, 5000, 10000 + }; + + private string _errorMessage = string.Empty; + public string ErrorMessage + { + get => _errorMessage; + set => SetProperty(ref _errorMessage, value); + } + + #endregion + + #region 命令 + public ICommand SaveCommand { get; } + public ICommand CancelCommand { get; } + #endregion + + // 用于保存时把副本回写到原对象 + private DeviceInfoModel? _hostDevice; + + public TCPConfigViewModel(IContainerProvider containerProvider) : base(containerProvider) + { + SaveCommand = new DelegateCommand(OnSave); + CancelCommand = new DelegateCommand(OnCancel); + } + + private bool Validate(out string error) + { + error = string.Empty; + if (string.IsNullOrWhiteSpace(Config.IPAddress)) + { + error = "IP 地址不能为空"; + return false; + } + if (!System.Net.IPAddress.TryParse(Config.IPAddress, out _)) + { + error = "IP 地址格式不正确"; + return false; + } + if (Config.Port <= 0 || Config.Port > 65535) + { + error = "端口范围应在 1 - 65535"; + return false; + } + if (Config.SendTimeout < 0 || Config.ReceiveTimeout < 0) + { + error = "超时时间不能为负数"; + return false; + } + return true; + } + + private void OnSave() + { + if (!Validate(out var error)) + { + ErrorMessage = error; + return; + } + ErrorMessage = string.Empty; + + // 把副本写回宿主 + if (_hostDevice != null) + { + _hostDevice.TcpConfig ??= new TcpConnectionConfig(); + Config.CopyTo(_hostDevice.TcpConfig); + _hostDevice.ConnectionType = "TCP"; + } + + RequestClose.Invoke(ButtonResult.OK); + } + + private void OnCancel() => RequestClose.Invoke(ButtonResult.Cancel); + + #region Prism Dialog 规范 + public override void OnDialogOpened(IDialogParameters parameters) + { + _eventAggregator.GetEvent().Publish(true); + + if (parameters.ContainsKey("Device")) + { + _hostDevice = parameters.GetValue("Device"); + Title = $"TCP 连接配置 - {_hostDevice?.DeviceName}"; + Config = new TcpConnectionConfig(_hostDevice?.TcpConfig); + } + else if (parameters.ContainsKey("Config")) + { + Config = new TcpConnectionConfig(parameters.GetValue("Config")); + } + } + + public override void OnDialogClosed() + { + _eventAggregator.GetEvent().Publish(false); + } + #endregion + } +} diff --git a/SettingModule/ViewModels/SettingViewModel.cs b/SettingModule/ViewModels/SettingViewModel.cs index 38c0da9..2b9aefe 100644 --- a/SettingModule/ViewModels/SettingViewModel.cs +++ b/SettingModule/ViewModels/SettingViewModel.cs @@ -5,7 +5,6 @@ using UIShare.GlobalVariable; using UIShare.PubEvent; using UIShare.UIViewModel; using UIShare.ViewModelBase; - namespace SettingModule.ViewModels { /// @@ -74,8 +73,16 @@ namespace SettingModule.ViewModels public ICommand RefreshCommand { get; } public ICommand SaveCommand { get; } public ICommand ResetCommand { get; } + /// 打开当前选中设备的连接配置对话框(按 ConnectionType 选择 TCP / 串口)。 + public ICommand OpenConnectionConfigCommand { get; } #endregion + /// 支持的连接方式列表,供"连接方式"ComboBox 使用。 + public ObservableCollection ConnectionTypes { get; } = new() + { + "None", "TCP", "Serial" + }; + public SettingViewModel(IContainerExtension container) : base(container) { _scope = container.CreateScope(); @@ -89,6 +96,7 @@ namespace SettingModule.ViewModels RefreshCommand = new DelegateCommand(OnExpand); SaveCommand = new DelegateCommand(OnSave); ResetCommand = new DelegateCommand(OnReset); + OpenConnectionConfigCommand = new DelegateCommand(OnOpenConnectionConfig); // 进来默认选中第一个,让右侧不为空 if (DeviceList.Count > 0) @@ -146,6 +154,40 @@ namespace SettingModule.ViewModels } StatusMessage = $"已重置设备 [{SelectedDevice.DeviceName}] 的配置"; } + + /// + /// 打开"连接配置"对话框。按 SelectedDevice.ConnectionType 决定开 TCP 还是串口对话框。 + /// + private void OnOpenConnectionConfig() + { + if (SelectedDevice == null) + { + StatusMessage = "请先在左侧选择设备"; + return; + } + + var dialogName = SelectedDevice.ConnectionType switch + { + "TCP" => "TCPConfig", + "Serial" => "SerialPortConfig", + _ => string.Empty + }; + + if (string.IsNullOrEmpty(dialogName)) + { + StatusMessage = "当前设备未配置连接方式(请先选择 TCP 或 Serial)"; + return; + } + + var p = new DialogParameters { { "Device", SelectedDevice } }; + _dialogService.ShowDialog(dialogName, p, r => + { + if (r.Result == ButtonResult.OK) + { + StatusMessage = $"已更新 [{SelectedDevice.DeviceName}] 的连接参数({DateTime.Now:HH:mm:ss})"; + } + }); + } #endregion #region 导航 diff --git a/SettingModule/Views/Dialogs/SerialPortConfigView.xaml b/SettingModule/Views/Dialogs/SerialPortConfigView.xaml new file mode 100644 index 0000000..56effb7 --- /dev/null +++ b/SettingModule/Views/Dialogs/SerialPortConfigView.xaml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +