using System.Net.Sockets; using System.Text; namespace Housing.Services; public sealed class TcpScpiClient : IScpiClient { private readonly TcpClient _tcpClient; private readonly NetworkStream _stream; private readonly int _timeoutMilliseconds; private TcpScpiClient(TcpClient tcpClient, int timeoutMilliseconds) { _tcpClient = tcpClient; _stream = _tcpClient.GetStream(); _timeoutMilliseconds = timeoutMilliseconds; } public static async Task ConnectAsync(string host, int port, int timeoutMilliseconds) { var tcpClient = new TcpClient(); using var cancellation = new CancellationTokenSource(timeoutMilliseconds); try { await tcpClient.ConnectAsync(host, port, cancellation.Token); } catch (OperationCanceledException) { tcpClient.Dispose(); throw new TimeoutException($"{host}:{port} 장비 연결 시간이 초과되었습니다."); } catch { tcpClient.Dispose(); throw; } return new TcpScpiClient(tcpClient, timeoutMilliseconds); } public async Task SendAsync(string command) { using var cancellation = new CancellationTokenSource(_timeoutMilliseconds); var bytes = Encoding.ASCII.GetBytes(command.Trim() + "\n"); await _stream.WriteAsync(bytes, cancellation.Token); await _stream.FlushAsync(cancellation.Token); } public async Task QueryAsync(string command) { await SendAsync(command); using var cancellation = new CancellationTokenSource(_timeoutMilliseconds); var buffer = new byte[1024]; var response = new StringBuilder(); while (true) { int bytesRead; try { bytesRead = await _stream.ReadAsync(buffer, cancellation.Token); } catch (OperationCanceledException) { throw new TimeoutException($"{command} 응답 시간이 초과되었습니다."); } if (bytesRead == 0) { break; } var chunk = Encoding.ASCII.GetString(buffer, 0, bytesRead); response.Append(chunk); if (chunk.Contains('\n')) { break; } } return response.ToString().Trim(); } public void Dispose() { _stream.Dispose(); _tcpClient.Dispose(); } }