You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
5.1 KiB
151 lines
5.1 KiB
using System.Diagnostics;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
|
|
namespace Housing.Services;
|
|
|
|
public sealed class TcpStartSignalWatcher
|
|
{
|
|
private readonly StartSignalSettings _settings;
|
|
|
|
public TcpStartSignalWatcher(StartSignalSettings settings)
|
|
{
|
|
_settings = settings;
|
|
}
|
|
|
|
public async Task WaitForStartAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_settings.Host))
|
|
{
|
|
throw new InvalidOperationException("Hardware.ini [StartSignal] Host 값을 LAN 장비 IP로 설정하세요.");
|
|
}
|
|
|
|
using var client = new TcpClient();
|
|
await ConnectAsync(client, cancellationToken);
|
|
|
|
using var stream = client.GetStream();
|
|
var stopwatch = Stopwatch.StartNew();
|
|
|
|
if (string.IsNullOrWhiteSpace(_settings.ReadCommand))
|
|
{
|
|
await WaitForEventMessageAsync(stream, stopwatch, cancellationToken);
|
|
return;
|
|
}
|
|
|
|
await WaitForPolledResponseAsync(stream, stopwatch, cancellationToken);
|
|
}
|
|
|
|
private async Task ConnectAsync(TcpClient client, CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
if (_settings.TimeoutMilliseconds > 0)
|
|
{
|
|
timeout.CancelAfter(_settings.TimeoutMilliseconds);
|
|
}
|
|
|
|
await client.ConnectAsync(_settings.Host, _settings.Port, timeout.Token);
|
|
}
|
|
catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested)
|
|
{
|
|
throw new TimeoutException($"LAN 시작 신호 장비 연결 시간 초과: {_settings.Host}:{_settings.Port}", ex);
|
|
}
|
|
catch (SocketException ex)
|
|
{
|
|
throw new InvalidOperationException($"LAN 시작 신호 장비 연결 실패: {_settings.Host}:{_settings.Port}, {ex.Message}", ex);
|
|
}
|
|
}
|
|
|
|
private async Task WaitForEventMessageAsync(NetworkStream stream, Stopwatch stopwatch, CancellationToken cancellationToken)
|
|
{
|
|
while (true)
|
|
{
|
|
ThrowIfTimedOut(stopwatch, "LAN 메시지 수신 대기");
|
|
|
|
var response = await ReadResponseAsync(stream, cancellationToken);
|
|
if (_settings.IsActive(response))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task WaitForPolledResponseAsync(NetworkStream stream, Stopwatch stopwatch, CancellationToken cancellationToken)
|
|
{
|
|
if (_settings.RequireInactiveBeforeStart)
|
|
{
|
|
while (_settings.IsActive(await QueryAsync(stream, cancellationToken)))
|
|
{
|
|
ThrowIfTimedOut(stopwatch, "기존 LAN 시작 상태 해제 대기");
|
|
await DelayAsync(cancellationToken);
|
|
}
|
|
}
|
|
|
|
while (!_settings.IsActive(await QueryAsync(stream, cancellationToken)))
|
|
{
|
|
ThrowIfTimedOut(stopwatch, "LAN 시작 응답 대기");
|
|
await DelayAsync(cancellationToken);
|
|
}
|
|
}
|
|
|
|
private async Task<string> QueryAsync(NetworkStream stream, CancellationToken cancellationToken)
|
|
{
|
|
var command = _settings.ReadCommand.Trim() + _settings.GetTerminator();
|
|
var bytes = Encoding.ASCII.GetBytes(command);
|
|
await stream.WriteAsync(bytes, cancellationToken);
|
|
await stream.FlushAsync(cancellationToken);
|
|
return await ReadResponseAsync(stream, cancellationToken);
|
|
}
|
|
|
|
private async Task<string> ReadResponseAsync(NetworkStream stream, CancellationToken cancellationToken)
|
|
{
|
|
var buffer = new byte[1024];
|
|
var response = new StringBuilder();
|
|
|
|
using var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
timeout.CancelAfter(_settings.ReadTimeoutMilliseconds);
|
|
|
|
try
|
|
{
|
|
while (true)
|
|
{
|
|
var count = await stream.ReadAsync(buffer, timeout.Token);
|
|
if (count == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
var chunk = Encoding.ASCII.GetString(buffer, 0, count);
|
|
response.Append(chunk);
|
|
if (chunk.Contains('\n') || chunk.Contains('\r') || !stream.DataAvailable)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
|
|
{
|
|
return response.ToString();
|
|
}
|
|
|
|
return response.ToString();
|
|
}
|
|
|
|
private async Task DelayAsync(CancellationToken cancellationToken)
|
|
{
|
|
await Task.Delay(_settings.PollIntervalMilliseconds, cancellationToken);
|
|
}
|
|
|
|
private void ThrowIfTimedOut(Stopwatch stopwatch, string state)
|
|
{
|
|
if (_settings.TimeoutMilliseconds <= 0 ||
|
|
stopwatch.ElapsedMilliseconds <= _settings.TimeoutMilliseconds)
|
|
{
|
|
return;
|
|
}
|
|
|
|
throw new TimeoutException(
|
|
$"LAN 시작 신호 대기 시간 초과: {state}, 대상={_settings.Host}:{_settings.Port}, 제한={_settings.TimeoutMilliseconds}ms");
|
|
}
|
|
}
|
|
|