更新TCP

This commit is contained in:
GukSang.Jin
2026-05-05 10:01:05 +08:00
parent 289d0dcab7
commit 5e6d87a01d
3 changed files with 222 additions and 0 deletions

View File

@@ -6,15 +6,26 @@ namespace ConeCalorimeter
{
public partial class MainWindow : Window
{
private readonly ITcpDeviceConnectionService _tcpDeviceConnectionService;
public MainWindow()
{
InitializeComponent();
_tcpDeviceConnectionService = new TcpDeviceConnectionService();
_ = _tcpDeviceConnectionService.StartAsync();
var experimentDataService = new ExperimentDataService(new DemoRealtimeDataService());
DataContext = new MainViewModel(
experimentDataService,
new NpoiReportExportService(),
new HelpDialogService());
}
protected override async void OnClosed(EventArgs e)
{
await _tcpDeviceConnectionService.DisposeAsync();
base.OnClosed(e);
}
}
}

View File

@@ -0,0 +1,10 @@
namespace ConeCalorimeter.Services;
public interface ITcpDeviceConnectionService : IAsyncDisposable
{
bool IsConnected { get; }
Task StartAsync();
Task StopAsync();
}

View 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();
}
}