更新TCP
This commit is contained in:
@@ -6,15 +6,26 @@ namespace ConeCalorimeter
|
|||||||
{
|
{
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
|
private readonly ITcpDeviceConnectionService _tcpDeviceConnectionService;
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
_tcpDeviceConnectionService = new TcpDeviceConnectionService();
|
||||||
|
_ = _tcpDeviceConnectionService.StartAsync();
|
||||||
|
|
||||||
var experimentDataService = new ExperimentDataService(new DemoRealtimeDataService());
|
var experimentDataService = new ExperimentDataService(new DemoRealtimeDataService());
|
||||||
DataContext = new MainViewModel(
|
DataContext = new MainViewModel(
|
||||||
experimentDataService,
|
experimentDataService,
|
||||||
new NpoiReportExportService(),
|
new NpoiReportExportService(),
|
||||||
new HelpDialogService());
|
new HelpDialogService());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override async void OnClosed(EventArgs e)
|
||||||
|
{
|
||||||
|
await _tcpDeviceConnectionService.DisposeAsync();
|
||||||
|
base.OnClosed(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
ConeCalorimeter/Services/ITcpDeviceConnectionService.cs
Normal file
10
ConeCalorimeter/Services/ITcpDeviceConnectionService.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace ConeCalorimeter.Services;
|
||||||
|
|
||||||
|
public interface ITcpDeviceConnectionService : IAsyncDisposable
|
||||||
|
{
|
||||||
|
bool IsConnected { get; }
|
||||||
|
|
||||||
|
Task StartAsync();
|
||||||
|
|
||||||
|
Task StopAsync();
|
||||||
|
}
|
||||||
201
ConeCalorimeter/Services/TcpDeviceConnectionService.cs
Normal file
201
ConeCalorimeter/Services/TcpDeviceConnectionService.cs
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
|
namespace ConeCalorimeter.Services;
|
||||||
|
|
||||||
|
public sealed class TcpDeviceConnectionService : ITcpDeviceConnectionService
|
||||||
|
{
|
||||||
|
private const string Host = "192.168.1.10";
|
||||||
|
private const int Port = 502;
|
||||||
|
private static readonly TimeSpan RetryDelay = TimeSpan.FromSeconds(3);
|
||||||
|
private static readonly TimeSpan ConnectTimeout = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
|
private readonly object _syncRoot = new();
|
||||||
|
private CancellationTokenSource? _connectionLoopCancellation;
|
||||||
|
private Task? _connectionLoopTask;
|
||||||
|
private TcpClient? _client;
|
||||||
|
private bool _isConnected;
|
||||||
|
|
||||||
|
public bool IsConnected
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
return _isConnected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
_isConnected = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync()
|
||||||
|
{
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
if (_connectionLoopTask is not null)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
_connectionLoopCancellation = new CancellationTokenSource();
|
||||||
|
_connectionLoopTask = RunConnectionLoopAsync(_connectionLoopCancellation.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StopAsync()
|
||||||
|
{
|
||||||
|
Task? loopTask;
|
||||||
|
CancellationTokenSource? cancellation;
|
||||||
|
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
loopTask = _connectionLoopTask;
|
||||||
|
cancellation = _connectionLoopCancellation;
|
||||||
|
_connectionLoopTask = null;
|
||||||
|
_connectionLoopCancellation = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancellation is not null)
|
||||||
|
{
|
||||||
|
await cancellation.CancelAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseCurrentClient();
|
||||||
|
|
||||||
|
if (loopTask is not null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await loopTask;
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellation?.Dispose();
|
||||||
|
Debug.WriteLine("TCP device connection service stopped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
await StopAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunConnectionLoopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ConnectAsync(cancellationToken);
|
||||||
|
await MonitorConnectionAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"TCP device connection failed: {ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
CloseCurrentClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(RetryDelay, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ConnectAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"Connecting TCP device at {Host}:{Port}.");
|
||||||
|
|
||||||
|
var client = new TcpClient();
|
||||||
|
var connectTask = client.ConnectAsync(Host, Port, cancellationToken).AsTask();
|
||||||
|
var completedTask = await Task.WhenAny(connectTask, Task.Delay(ConnectTimeout, cancellationToken));
|
||||||
|
|
||||||
|
if (!ReferenceEquals(completedTask, connectTask))
|
||||||
|
{
|
||||||
|
client.Dispose();
|
||||||
|
throw new TimeoutException($"TCP device connection timed out after {ConnectTimeout.TotalSeconds:0} seconds.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await connectTask;
|
||||||
|
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
_isConnected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.WriteLine($"TCP device connected at {Host}:{Port}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task MonitorConnectionAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
TcpClient? client;
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
client = _client;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client is null || !IsTcpClientConnected(client))
|
||||||
|
{
|
||||||
|
Debug.WriteLine("TCP device connection lost.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsTcpClientConnected(TcpClient client)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var socket = client.Client;
|
||||||
|
return socket.Connected && !(socket.Poll(0, SelectMode.SelectRead) && socket.Available == 0);
|
||||||
|
}
|
||||||
|
catch (SocketException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CloseCurrentClient()
|
||||||
|
{
|
||||||
|
TcpClient? client;
|
||||||
|
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
client = _client;
|
||||||
|
_client = null;
|
||||||
|
_isConnected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
client?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user