Dreamine.PLC.Omron.Fins 1.0.1
Dreamine.PLC.Omron.Fins 산업 자동화 및 I/O 기능을 제공합니다.
로딩중...
검색중...
일치하는것 없음
UdpOmronFinsTransport.cs
이 파일의 문서화 페이지로 가기
1using System.Net;
2using System.Net.Sockets;
3using Dreamine.PLC.Abstractions.Results;
5
7
17{
26 private readonly SemaphoreSlim _syncLock = new(1, 1);
44 private UdpClient? _udpClient;
53 private IPEndPoint? _remoteEndPoint;
62 private bool _disposed;
63
89 {
90 _options = options ?? throw new ArgumentNullException(nameof(options));
91 }
92
101 public bool IsConnected => _udpClient is not null && _remoteEndPoint 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 if (_options.Port is <= 0 or > 65535)
155 {
156 return PlcResult.Failure($"Invalid FINS UDP port: {_options.Port}.");
157 }
158
159 var addresses = await Dns.GetHostAddressesAsync(_options.Host, cancellationToken).ConfigureAwait(false);
160 if (addresses.Length == 0)
161 {
162 return PlcResult.Failure($"Failed to resolve FINS host: {_options.Host}.");
163 }
164
165 _remoteEndPoint = new IPEndPoint(addresses[0], _options.Port);
166 _udpClient = new UdpClient();
168 return PlcResult.Success();
169 }
170 catch (OperationCanceledException ex)
171 {
172 CloseCore();
173 return PlcResult.Failure(ex.Message);
174 }
175 catch (Exception ex)
176 {
177 CloseCore();
178 return PlcResult.Failure(ex.Message);
179 }
180 finally
181 {
182 _syncLock.Release();
183 }
184 }
185
226 public async Task<PlcResult> DisconnectAsync(CancellationToken cancellationToken = default)
227 {
228 await _syncLock.WaitAsync(cancellationToken).ConfigureAwait(false);
229 try
230 {
231 CloseCore();
232 return PlcResult.Success();
233 }
234 finally
235 {
236 _syncLock.Release();
237 }
238 }
239
320 public async Task<PlcResult<byte[]>> SendAndReceiveAsync(
321 IReadOnlyList<byte> requestFrame,
322 int receiveTimeoutMs,
323 int retryCount,
324 CancellationToken cancellationToken = default)
325 {
326 ArgumentNullException.ThrowIfNull(requestFrame);
327
328 await _syncLock.WaitAsync(cancellationToken).ConfigureAwait(false);
329 try
330 {
332 if (_udpClient is null)
333 {
334 return PlcResult<byte[]>.Failure("The FINS UDP transport is not connected.");
335 }
336
337 var request = requestFrame as byte[] ?? requestFrame.ToArray();
338 var attempts = Math.Max(1, retryCount);
339 Exception? lastException = null;
340
341 for (var attempt = 0; attempt < attempts; attempt++)
342 {
343 try
344 {
345 using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
346 timeoutCts.CancelAfter(receiveTimeoutMs);
347
348 await _udpClient.SendAsync(request, timeoutCts.Token).ConfigureAwait(false);
349 var result = await _udpClient.ReceiveAsync(timeoutCts.Token).ConfigureAwait(false);
350 return PlcResult<byte[]>.Success(result.Buffer);
351 }
352 catch (OperationCanceledException ex)
353 {
354 lastException = ex;
355 }
356 catch (Exception ex)
357 {
358 lastException = ex;
359 }
360 }
361
362 return PlcResult<byte[]>.Failure(lastException?.Message ?? "FINS UDP receive timeout.");
363 }
364 finally
365 {
366 _syncLock.Release();
367 }
368 }
369
394 public async ValueTask DisposeAsync()
395 {
396 if (_disposed)
397 {
398 return;
399 }
400
401 await DisconnectAsync().ConfigureAwait(false);
402 _syncLock.Dispose();
403 _disposed = true;
404 GC.SuppressFinalize(this);
405 }
406
415 private void CloseCore()
416 {
417 _udpClient?.Dispose();
418 _udpClient = null;
419 _remoteEndPoint = null;
420 }
421
438 private void ThrowIfDisposed()
439 {
440 ObjectDisposedException.ThrowIf(_disposed, this);
441 }
442}
async Task< PlcResult< byte[]> > SendAndReceiveAsync(IReadOnlyList< byte > requestFrame, int receiveTimeoutMs, int retryCount, CancellationToken cancellationToken=default)
async Task< PlcResult > DisconnectAsync(CancellationToken cancellationToken=default)
async Task< PlcResult > ConnectAsync(CancellationToken cancellationToken=default)