diff --git a/Cardiopulmonarybypasssystems/MainWindow.xaml b/Cardiopulmonarybypasssystems/MainWindow.xaml
index 4416f41..1f3b4c5 100644
--- a/Cardiopulmonarybypasssystems/MainWindow.xaml
+++ b/Cardiopulmonarybypasssystems/MainWindow.xaml
@@ -41,85 +41,12 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
+
+
+
@@ -158,7 +85,7 @@
-
+
@@ -182,7 +109,7 @@
-
+
@@ -224,7 +152,7 @@
-
+
@@ -252,17 +180,10 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
@@ -415,41 +336,76 @@
-
-
-
-
+
+
+
+
+
+
+
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
@@ -457,7 +413,7 @@
-
+
@@ -465,7 +421,7 @@
-
+
@@ -474,33 +430,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
-
-
-
+
+
-
+
+
+
+
+
+
+
diff --git a/Cardiopulmonarybypasssystems/Services/MockModbusTelemetryService.cs b/Cardiopulmonarybypasssystems/Services/MockModbusTelemetryService.cs
index 719d46f..f3e94f7 100644
--- a/Cardiopulmonarybypasssystems/Services/MockModbusTelemetryService.cs
+++ b/Cardiopulmonarybypasssystems/Services/MockModbusTelemetryService.cs
@@ -1,4 +1,6 @@
using System.Net.Sockets;
+using System.Threading;
+using System.Threading.Tasks;
using Cardiopulmonarybypasssystems.Models;
using NModbus;
@@ -11,6 +13,8 @@ public sealed class MockModbusTelemetryService : IModbusTelemetryService, IDispo
private const byte SlaveId = 1;
private const ushort ProximalPressureRegister = 1330;
private const ushort DistalPressureRegister = 1380;
+ private static readonly TimeSpan ConnectionAttemptTimeout = TimeSpan.FromMilliseconds(300);
+ private static readonly TimeSpan ConnectionRetryInterval = TimeSpan.FromSeconds(5);
private readonly object _syncRoot = new();
private readonly Random _random = new();
@@ -32,16 +36,18 @@ public sealed class MockModbusTelemetryService : IModbusTelemetryService, IDispo
private TcpClient? _tcpClient;
private IModbusMaster? _master;
private bool _connectionInitialized;
+ private Task? _connectionTask;
+ private DateTime _nextConnectionAttemptUtc = DateTime.MinValue;
public IReadOnlyList GetChannels()
{
- EnsureConnected();
+ EnsureConnectionScheduled();
return _channels;
}
public IReadOnlyList UpdateChannels()
{
- EnsureConnected();
+ EnsureConnectionScheduled();
lock (_syncRoot)
{
@@ -74,14 +80,19 @@ public sealed class MockModbusTelemetryService : IModbusTelemetryService, IDispo
{
lock (_syncRoot)
{
- _master?.Dispose();
- _tcpClient?.Dispose();
- _master = null;
- _tcpClient = null;
+ ReleaseConnection();
}
}
- private void EnsureConnected()
+ private void ReleaseConnection()
+ {
+ _master?.Dispose();
+ _tcpClient?.Dispose();
+ _master = null;
+ _tcpClient = null;
+ }
+
+ private void EnsureConnectionScheduled()
{
lock (_syncRoot)
{
@@ -90,20 +101,50 @@ public sealed class MockModbusTelemetryService : IModbusTelemetryService, IDispo
return;
}
- Dispose();
-
- try
+ if (_connectionTask is { IsCompleted: false })
{
- _tcpClient = new TcpClient();
- _tcpClient.Connect(IpAddress, Port);
- _master = _factory.CreateMaster(_tcpClient);
- _connectionInitialized = true;
+ return;
}
- catch
+
+ if (DateTime.UtcNow < _nextConnectionAttemptUtc)
{
- _master = null;
- _tcpClient?.Dispose();
- _tcpClient = null;
+ return;
+ }
+
+ _nextConnectionAttemptUtc = DateTime.UtcNow.Add(ConnectionRetryInterval);
+ _connectionTask = Task.Run(ConnectWithTimeout);
+ }
+ }
+
+ private void ConnectWithTimeout()
+ {
+ TcpClient? tcpClient = null;
+
+ try
+ {
+ tcpClient = new TcpClient();
+ using var cancellation = new CancellationTokenSource(ConnectionAttemptTimeout);
+ tcpClient.ConnectAsync(IpAddress, Port, cancellation.Token).GetAwaiter().GetResult();
+ tcpClient.ReceiveTimeout = (int)ConnectionAttemptTimeout.TotalMilliseconds;
+ tcpClient.SendTimeout = (int)ConnectionAttemptTimeout.TotalMilliseconds;
+ var master = _factory.CreateMaster(tcpClient);
+
+ lock (_syncRoot)
+ {
+ ReleaseConnection();
+ _tcpClient = tcpClient;
+ _master = master;
+ _connectionInitialized = true;
+ tcpClient = null;
+ }
+ }
+ catch
+ {
+ tcpClient?.Dispose();
+
+ lock (_syncRoot)
+ {
+ ReleaseConnection();
}
}
}
@@ -126,7 +167,12 @@ public sealed class MockModbusTelemetryService : IModbusTelemetryService, IDispo
}
catch
{
- Dispose();
+ lock (_syncRoot)
+ {
+ ReleaseConnection();
+ _nextConnectionAttemptUtc = DateTime.MinValue;
+ }
+
SimulatePressureChannels();
}
}
diff --git a/Cardiopulmonarybypasssystems/ViewModels/MainViewModel.cs b/Cardiopulmonarybypasssystems/ViewModels/MainViewModel.cs
index f7ae580..7449ea1 100644
--- a/Cardiopulmonarybypasssystems/ViewModels/MainViewModel.cs
+++ b/Cardiopulmonarybypasssystems/ViewModels/MainViewModel.cs
@@ -191,6 +191,8 @@ public partial class MainViewModel : ObservableObject
public string PumpFlowDisplay => $"{PumpFlow:F2} L/min";
public string DrainageFlowDisplay => $"{DrainageFlow:F2} L/min";
public string ReturnFlowDisplay => $"{ReturnFlow:F2} L/min";
+ public string ProximalPressureDisplay => $"{ChannelValue("近端压力"):F1} mmHg";
+ public string DistalPressureDisplay => $"{ChannelValue("远端压力"):F1} mmHg";
public string FlowImbalanceDisplay => $"{Math.Abs(PumpFlow - ReturnFlow):F2} L/min";
public string PumpFlowLoadDisplay => $"{ChannelNormalizedValue("主泵流量"):P0} 量程";
public string DrainageFlowLoadDisplay => $"{ChannelNormalizedValue("静脉引流流量"):P0} 量程";
@@ -569,9 +571,11 @@ public partial class MainViewModel : ObservableObject
private void RefreshTelemetry()
{
var alarms = _telemetryService.UpdateChannels();
+ AlarmMessages.Clear();
+
foreach (var alarm in alarms.OrderByDescending(a => a.Timestamp))
{
- AlarmMessages.Insert(0, alarm);
+ AlarmMessages.Add(alarm);
}
RefreshTelemetryPanel();
@@ -591,6 +595,8 @@ public partial class MainViewModel : ObservableObject
OnPropertyChanged(nameof(PumpFlowDisplay));
OnPropertyChanged(nameof(DrainageFlowDisplay));
OnPropertyChanged(nameof(ReturnFlowDisplay));
+ OnPropertyChanged(nameof(ProximalPressureDisplay));
+ OnPropertyChanged(nameof(DistalPressureDisplay));
OnPropertyChanged(nameof(RealtimeRecirculationDisplay));
OnPropertyChanged(nameof(FlowImbalanceDisplay));
OnPropertyChanged(nameof(NegativeAssistPressureDisplay));