diff --git a/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/RIZO_Application.Modules.ModuleName.csproj b/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/RIZO_Application.Modules.ModuleName.csproj index 2469eda..833d84e 100644 --- a/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/RIZO_Application.Modules.ModuleName.csproj +++ b/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/RIZO_Application.Modules.ModuleName.csproj @@ -5,6 +5,7 @@ + diff --git a/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/ViewModels/MqttControlViewModel.cs b/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/ViewModels/MqttControlViewModel.cs index ecde1f4..56f86f7 100644 --- a/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/ViewModels/MqttControlViewModel.cs +++ b/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/ViewModels/MqttControlViewModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text.Json; using System.Threading.Tasks; +using System.Windows; using MQTTnet.Client; using Prism.Events; using Prism.Regions; @@ -17,7 +18,7 @@ namespace RIZO_Application.Modules.ModuleName.ViewModels private readonly IEventAggregator _eventAggregator; private MqttHelper? _mqttHelper; private SubscriptionToken _token; - + public MqttControlViewModel( IRegionManager regionManager, IEventAggregator eventAggregator) @@ -40,7 +41,7 @@ namespace RIZO_Application.Modules.ModuleName.ViewModels _mqttHelper = new MqttHelper(serverUrl, 1883, clientId); _mqttHelper.MessageReceived += HandleMqttMessage; _mqttHelper.ConnectionFailed += HandleMqttConnectionFailed; - + _mqttHelper.Disconnected += Publish; if (await ConnectMqttAsync()) { await SubscribeToTopicsAsync(); @@ -81,6 +82,7 @@ namespace RIZO_Application.Modules.ModuleName.ViewModels { _eventAggregator.GetEvent().Publish($"订阅:{printTopic}"); } + } private async Task PublishInitialMessageAsync() @@ -165,7 +167,14 @@ namespace RIZO_Application.Modules.ModuleName.ViewModels _eventAggregator.GetEvent().Publish($"发布扫描消息时出错: {ex.Message}"); } } - + public void Publish(string message, int retries, int maxRetries) + { + _eventAggregator.GetEvent().Publish(message); + if (retries>=maxRetries) + { + MessageBox.Show("MQTT已掉线,请停止扫码"); + } + } public async void Destroy() { _token?.Dispose(); diff --git a/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/ViewModels/PrintControlViewModel.cs b/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/ViewModels/PrintControlViewModel.cs index 1218ccf..78d1529 100644 --- a/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/ViewModels/PrintControlViewModel.cs +++ b/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/ViewModels/PrintControlViewModel.cs @@ -5,11 +5,9 @@ using Prism.Events; using Prism.Regions; using RIZO_Application.Core; using RIZO_Application.Core.Mvvm; -using RIZO_Helper.Tools; using RIZO_Application.Infrastructure.Model; using System.Linq; using System.Reflection; -using Microsoft.Extensions.Primitives; namespace RIZO_Application.Modules.ModuleName.ViewModels { @@ -19,7 +17,6 @@ namespace RIZO_Application.Modules.ModuleName.ViewModels private readonly BartenderPrintHelper _printHelper; private SubscriptionToken _printEventToken; private bool _isDisposed; - public PrintControlViewModel( IRegionManager regionManager, IEventAggregator eventAggregator, @@ -120,6 +117,10 @@ namespace RIZO_Application.Modules.ModuleName.ViewModels subStringValues: Intersect, copies: 1); + //bool printSuccess = barTenderPrinter.PrintLabel(templatePath: printDto.Path, + // subStringValues: Intersect, + // copies: 1); + if (printSuccess) { _eventAggregator.GetEvent().Publish($"打印成功: {printDto.Name}"); diff --git a/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/ViewModels/ScanControlViewModel.cs b/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/ViewModels/ScanControlViewModel.cs index 3bff303..0bbf742 100644 --- a/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/ViewModels/ScanControlViewModel.cs +++ b/RIZO_Application/Modules/RIZO_Application.Modules.ModuleName/ViewModels/ScanControlViewModel.cs @@ -10,6 +10,7 @@ using RIZO_Helper.Tools; using System.Windows; using System.IO.Ports; using System.Windows.Documents; +using System.Management; namespace RIZO_Application.Modules.ModuleName.ViewModels { @@ -21,6 +22,8 @@ namespace RIZO_Application.Modules.ModuleName.ViewModels private bool _isConnected; private string _lastLabel; private DateTime _lastScanTime; + private ManagementEventWatcher watcher= new ManagementEventWatcher(new WqlEventQuery( + "SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2 OR EventType = 3")); // 串口连接状态属性 public bool IsConnected { @@ -52,11 +55,44 @@ namespace RIZO_Application.Modules.ModuleName.ViewModels : base(regionManager) { + watcher.EventArrived += new EventArrivedEventHandler(DeviceChanged); + watcher.Start(); + _eventAggregator = eventAggregator; IsConnected = false; // 初始状态为未连接 InitializeScanHelperAsync().ConfigureAwait(false); } + private void DeviceChanged(object sender, EventArrivedEventArgs e) + { + // 1. 获取设备变化前的端口列表 + string[] portsBefore = SerialPort.GetPortNames(); + + // 2. 等待系统完成端口更新(重要!) + System.Threading.Thread.Sleep(500); + + // 3. 获取设备变化后的端口列表 + string[] portsAfter = SerialPort.GetPortNames(); + + // 4. 检测新增的端口 + foreach (string port in portsAfter) + { + if (Array.IndexOf(portsBefore, port) == -1) + { + + } + } + + // 5. 检测移除的端口 + foreach (string port in portsBefore) + { + if (Array.IndexOf(portsAfter, port) == -1) + { + + } + } + } + private async Task InitializeScanHelperAsync() { try @@ -69,18 +105,17 @@ namespace RIZO_Application.Modules.ModuleName.ViewModels } } - public async Task StartComScan() + public async Task StartComScan(string comName = "COM1",int baudRate = 9600) { if (_isDisposed) throw new ObjectDisposedException(nameof(ScanControlViewModel)); - string comName = "COM1"; - int baudRate = 9600; - if (SerialConfigs.Current != null) - { - comName = SerialConfigs.Current.ComName ?? string.Empty; - baudRate = SerialConfigs.Current.BaudRate ?? 9600; - } + + //if (SerialConfigs.Current != null) + //{ + // comName = SerialConfigs.Current.ComName ?? string.Empty; + // baudRate = SerialConfigs.Current.BaudRate ?? 9600; + //} _eventAggregator.GetEvent().Publish($"串口扫码枪初始化……串口:{comName} 波特率:{baudRate}"); diff --git a/RIZO_Application/RIZO_Application.Infrastructure/Util/MqttHelper/MqttHelper.cs b/RIZO_Application/RIZO_Application.Infrastructure/Util/MqttHelper/MqttHelper.cs index c78e372..1edfe7f 100644 --- a/RIZO_Application/RIZO_Application.Infrastructure/Util/MqttHelper/MqttHelper.cs +++ b/RIZO_Application/RIZO_Application.Infrastructure/Util/MqttHelper/MqttHelper.cs @@ -13,12 +13,18 @@ namespace RIZO_Helper.Tools private readonly IMqttClient _mqttClient; private MqttClientOptions _options; private bool _isDisposed; - private readonly SemaphoreSlim _connectionLock = new SemaphoreSlim(1, 1); + + // 用于确保只有一个后台重连任务运行 + private bool _isReconnecting; + private readonly object _reconnectLock = new object(); // 定义消息接收事件 public event EventHandler? MessageReceived; public event EventHandler? ConnectionFailed; + public Action Disconnected; + + public MqttHelper(string server, int port = 1883, string clientId = "wpf-demo", bool cleanSession = true) { if (string.IsNullOrEmpty(server)) @@ -37,8 +43,9 @@ namespace RIZO_Helper.Tools _mqttClient.DisconnectedAsync += async e => { + Disconnected.Invoke($"MQTT连接断开,原因: {e.Reason}", 0, 1); Debug.WriteLine($"MQTT连接断开,原因: {e.Reason}"); - await ReconnectWithBackoffAsync(); + await StartReconnectIfNeededAsync(); }; _mqttClient.ApplicationMessageReceivedAsync += e => @@ -58,35 +65,28 @@ namespace RIZO_Helper.Tools if (_mqttClient.IsConnected) return true; - await _connectionLock.WaitAsync(cancellationToken); + Debug.WriteLine($"正在连接MQTT服务器: {_options.ChannelOptions}"); + try { - if (_mqttClient.IsConnected) - return true; - - Debug.WriteLine($"正在连接MQTT服务器: {_options.ChannelOptions}"); - - try - { - await _mqttClient.ConnectAsync(_options, cancellationToken); - Debug.WriteLine("MQTT连接成功"); - return true; - } - catch (OperationCanceledException) - { - Debug.WriteLine("MQTT连接操作被取消"); - throw; - } - catch (Exception ex) - { - Debug.WriteLine($"MQTT连接失败: {ex.Message}"); - ConnectionFailed?.Invoke(this, ex); - return false; - } + await _mqttClient.ConnectAsync(_options, cancellationToken); + Debug.WriteLine("MQTT连接成功"); + return true; } - finally + catch (OperationCanceledException) { - _connectionLock.Release(); + Debug.WriteLine("MQTT连接操作被取消"); + throw; + } + catch (Exception ex) + { + Debug.WriteLine($"MQTT连接失败: {ex.Message}"); + ConnectionFailed?.Invoke(this, ex); + + // 连接失败时启动重连 + await StartReconnectIfNeededAsync(); + + return false; } } @@ -187,6 +187,35 @@ namespace RIZO_Helper.Tools } } + // 启动重连任务(如果没有正在运行的重连任务) + private async Task StartReconnectIfNeededAsync() + { + if (_isDisposed) + return; + + // 使用锁确保只有一个重连任务启动 + lock (_reconnectLock) + { + if (_isReconnecting) + return; + + _isReconnecting = true; + } + + try + { + await ReconnectWithBackoffAsync(); + } + finally + { + // 无论重连成功或失败,都标记为重连已完成 + lock (_reconnectLock) + { + _isReconnecting = false; + } + } + } + private async Task ReconnectWithBackoffAsync() { if (_isDisposed) @@ -198,7 +227,9 @@ namespace RIZO_Helper.Tools while (retries < maxRetries && !_isDisposed) { + // 指数退避算法,避免频繁重试 int delayMs = baseDelayMs * (int)Math.Pow(2, retries); + Disconnected.Invoke($"将在 {delayMs}ms 后尝试重新连接MQTT服务器 (尝试 {retries + 1}/{maxRetries})", retries+1, maxRetries); Debug.WriteLine($"将在 {delayMs}ms 后尝试重新连接MQTT服务器 (尝试 {retries + 1}/{maxRetries})"); await Task.Delay(delayMs); @@ -242,7 +273,7 @@ namespace RIZO_Helper.Tools try { // 先取消事件订阅,防止在释放过程中触发事件 - _mqttClient.DisconnectedAsync -= async e => await ReconnectWithBackoffAsync(); + _mqttClient.DisconnectedAsync -= async e => await StartReconnectIfNeededAsync(); _mqttClient.ApplicationMessageReceivedAsync -= e => { MessageReceived?.Invoke(this, e); @@ -255,7 +286,6 @@ namespace RIZO_Helper.Tools // 释放资源 _mqttClient.Dispose(); - _connectionLock.Dispose(); } catch (Exception ex) {