Dreamine.PLC.Omron.Fins 1.0.1
Dreamine.PLC.Omron.Fins 산업 자동화 및 I/O 기능을 제공합니다.
로딩중...
검색중...
일치하는것 없음
TcpOmronFinsTransport.cs
이 파일의 문서화 페이지로 가기
1using System.Net.Sockets;
2using Dreamine.PLC.Abstractions.Results;
5
7
17{
26 private readonly SemaphoreSlim _syncLock = new(1, 1);
44 private TcpClient? _tcpClient;
53 private NetworkStream? _stream;
62 private bool _disposed;
63
89 {
90 _options = options ?? throw new ArgumentNullException(nameof(options));
91 }
92
101 public bool IsConnected => _tcpClient?.Connected == true && _stream is not null;
102
143 public async Task<PlcResult> ConnectAsync(CancellationToken cancellationToken = default)
144 {
145 await _syncLock.WaitAsync(cancellationToken).ConfigureAwait(false);
146 try
147 {
149 if (IsConnected)
150 {
151 return PlcResult.Success();
152 }
153
154 await CloseCoreAsync().ConfigureAwait(false);
155 _tcpClient = new TcpClient { NoDelay = true };
156
157 using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
158 timeoutCts.CancelAfter(_options.ConnectTimeoutMs);
159 await _tcpClient.ConnectAsync(_options.Host, _options.Port, timeoutCts.Token).ConfigureAwait(false);
160 _stream = _tcpClient.GetStream();
161 return PlcResult.Success();
162 }
163 catch (OperationCanceledException ex)
164 {
165 await CloseCoreAsync().ConfigureAwait(false);
166 return PlcResult.Failure(ex.Message);
167 }
168 catch (Exception ex)
169 {
170 await CloseCoreAsync().ConfigureAwait(false);
171 return PlcResult.Failure(ex.Message);
172 }
173 finally
174 {
175 _syncLock.Release();
176 }
177 }
178
219 public async Task<PlcResult> DisconnectAsync(CancellationToken cancellationToken = default)
220 {
221 await _syncLock.WaitAsync(cancellationToken).ConfigureAwait(false);
222 try
223 {
224 await CloseCoreAsync().ConfigureAwait(false);
225 return PlcResult.Success();
226 }
227 finally
228 {
229 _syncLock.Release();
230 }
231 }
232
305 public async Task<PlcResult<byte[]>> SendAndReceiveAsync(
306 IReadOnlyList<byte> requestFrame,
307 int receiveTimeoutMs,
308 int retryCount,
309 CancellationToken cancellationToken = default)
310 {
311 ArgumentNullException.ThrowIfNull(requestFrame);
312
313 await _syncLock.WaitAsync(cancellationToken).ConfigureAwait(false);
314 try
315 {
317 if (_stream is null || _tcpClient is null || !_tcpClient.Connected)
318 {
319 return PlcResult<byte[]>.Failure("The FINS TCP transport is not connected.");
320 }
321
322 var packet = OmronFinsTcpPacket.Wrap(requestFrame);
323 using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
324 timeoutCts.CancelAfter(receiveTimeoutMs);
325
326 await _stream.WriteAsync(packet, timeoutCts.Token).ConfigureAwait(false);
327 await _stream.FlushAsync(timeoutCts.Token).ConfigureAwait(false);
328
329 var responsePacket = await ReceiveFinsTcpPacketAsync(_stream, timeoutCts.Token).ConfigureAwait(false);
330 return PlcResult<byte[]>.Success(OmronFinsTcpPacket.Extract(responsePacket));
331 }
332 catch (OperationCanceledException ex)
333 {
334 return PlcResult<byte[]>.Failure(ex.Message);
335 }
336 catch (Exception ex)
337 {
338 return PlcResult<byte[]>.Failure(ex.Message);
339 }
340 finally
341 {
342 _syncLock.Release();
343 }
344 }
345
370 public async ValueTask DisposeAsync()
371 {
372 if (_disposed)
373 {
374 return;
375 }
376
377 await DisconnectAsync().ConfigureAwait(false);
378 _syncLock.Dispose();
379 _disposed = true;
380 GC.SuppressFinalize(this);
381 }
382
439 private static async Task<byte[]> ReceiveFinsTcpPacketAsync(NetworkStream stream, CancellationToken cancellationToken)
440 {
441 var header = new byte[16];
442 await ReadExactlyAsync(stream, header, cancellationToken).ConfigureAwait(false);
443
444 var length = OmronFinsEndian.ReadInt32(header, 4);
445 if (length < 8)
446 {
447 throw new InvalidOperationException($"Invalid FINS/TCP packet length: {length}.");
448 }
449
450 var bodyLength = length - 8;
451 var body = new byte[bodyLength];
452 await ReadExactlyAsync(stream, body, cancellationToken).ConfigureAwait(false);
453
454 var packet = new byte[header.Length + body.Length];
455 Buffer.BlockCopy(header, 0, packet, 0, header.Length);
456 Buffer.BlockCopy(body, 0, packet, header.Length, body.Length);
457 return packet;
458 }
459
516 private static async Task ReadExactlyAsync(NetworkStream stream, byte[] buffer, CancellationToken cancellationToken)
517 {
518 var offset = 0;
519 while (offset < buffer.Length)
520 {
521 var read = await stream.ReadAsync(buffer.AsMemory(offset, buffer.Length - offset), cancellationToken).ConfigureAwait(false);
522 if (read == 0)
523 {
524 throw new IOException("The remote FINS endpoint closed the TCP connection.");
525 }
526
527 offset += read;
528 }
529 }
530
547 private async Task CloseCoreAsync()
548 {
549 _stream?.Dispose();
550 _tcpClient?.Dispose();
551 _stream = null;
552 _tcpClient = null;
553 await Task.CompletedTask.ConfigureAwait(false);
554 }
555
572 private void ThrowIfDisposed()
573 {
574 ObjectDisposedException.ThrowIf(_disposed, this);
575 }
576}
static int ReadInt32(ReadOnlySpan< byte > buffer, int offset)
static byte[] Extract(ReadOnlySpan< byte > packet)
static byte[] Wrap(IReadOnlyList< byte > finsFrame)
async Task< PlcResult > DisconnectAsync(CancellationToken cancellationToken=default)
static async Task ReadExactlyAsync(NetworkStream stream, byte[] buffer, CancellationToken cancellationToken)
async Task< PlcResult > ConnectAsync(CancellationToken cancellationToken=default)
static async Task< byte[]> ReceiveFinsTcpPacketAsync(NetworkStream stream, CancellationToken cancellationToken)
async Task< PlcResult< byte[]> > SendAndReceiveAsync(IReadOnlyList< byte > requestFrame, int receiveTimeoutMs, int retryCount, CancellationToken cancellationToken=default)