Dreamine.PLC.Core 1.0.1
Dreamine.PLC.Core 산업 자동화 및 I/O 기능을 제공합니다.
로딩중...
검색중...
일치하는것 없음
PlcClientBase.cs
이 파일의 문서화 페이지로 가기
1using Dreamine.PLC.Abstractions.Clients;
2using Dreamine.PLC.Abstractions.Connections;
3using Dreamine.PLC.Abstractions.Devices;
4using Dreamine.PLC.Abstractions.Results;
7
9
18public abstract class PlcClientBase : IPlcClient
19{
28 private readonly AsyncLock _syncLock = new();
37 private volatile bool _disposed;
46 private int _disposeGuard; // 0=alive, 1=disposing or disposed
47
56 public PlcConnectionState State { get; private set; } = PlcConnectionState.Disconnected;
57
66 public event EventHandler<PlcConnectionState>? StateChanged;
67
108 public async Task<PlcResult> ConnectAsync(CancellationToken cancellationToken = default)
109 {
110 using (await _syncLock.LockAsync(cancellationToken).ConfigureAwait(false))
111 {
113
114 if (State == PlcConnectionState.Connected)
115 {
116 return PlcResult.Success();
117 }
118
119 SetState(PlcConnectionState.Connecting);
120
121 try
122 {
123 var result = await ConnectCoreAsync(cancellationToken).ConfigureAwait(false);
124
125 SetState(result.IsSuccess
126 ? PlcConnectionState.Connected
127 : PlcConnectionState.Faulted);
128
129 return result;
130 }
131 catch (OperationCanceledException ex)
132 {
133 SetState(PlcConnectionState.Disconnected);
134 return PlcResult.Failure(ex.Message);
135 }
136 catch (Exception ex)
137 {
138 SetState(PlcConnectionState.Faulted);
139 return PlcResult.Failure(ex.Message);
140 }
141 }
142 }
143
176 public async Task<PlcResult> DisconnectAsync(CancellationToken cancellationToken = default)
177 {
178 using (await _syncLock.LockAsync(cancellationToken).ConfigureAwait(false))
179 {
180 if (_disposed || State == PlcConnectionState.Disconnected)
181 {
182 return PlcResult.Success();
183 }
184
185 var previousState = State;
186 SetState(PlcConnectionState.Disconnecting);
187
188 try
189 {
190 var result = await DisconnectCoreAsync(cancellationToken).ConfigureAwait(false);
191
192 SetState(PlcConnectionState.Disconnected);
193 return result;
194 }
195 catch (OperationCanceledException ex)
196 {
197 SetState(previousState);
198 return PlcResult.Failure(ex.Message);
199 }
200 catch (Exception ex)
201 {
202 SetState(PlcConnectionState.Faulted);
203 return PlcResult.Failure(ex.Message);
204 }
205 }
206 }
207
254 public async Task<PlcResult<bool[]>> ReadBitsAsync(
255 PlcAddress address,
256 int count,
257 CancellationToken cancellationToken = default)
258 {
260
261 var validationResult = ValidateReadRequest(address, count);
262 if (!validationResult.IsSuccess)
263 {
264 return PlcResult<bool[]>.Failure(validationResult.Message ?? "Invalid PLC bit read request.", validationResult.ErrorCode);
265 }
266
267 if (State != PlcConnectionState.Connected)
268 {
269 return PlcResult<bool[]>.Failure("The PLC client is not connected.");
270 }
271
272 try
273 {
274 return await ReadBitsCoreAsync(address, count, cancellationToken).ConfigureAwait(false);
275 }
276 catch (OperationCanceledException ex)
277 {
278 return PlcResult<bool[]>.Failure(ex.Message);
279 }
280 catch (Exception ex)
281 {
282 SetState(PlcConnectionState.Faulted);
283 return PlcResult<bool[]>.Failure(ex.Message);
284 }
285 }
286
333 public async Task<PlcResult<short[]>> ReadWordsAsync(
334 PlcAddress address,
335 int count,
336 CancellationToken cancellationToken = default)
337 {
339
340 var validationResult = ValidateReadRequest(address, count);
341 if (!validationResult.IsSuccess)
342 {
343 return PlcResult<short[]>.Failure(validationResult.Message ?? "Invalid PLC word read request.", validationResult.ErrorCode);
344 }
345
346 if (State != PlcConnectionState.Connected)
347 {
348 return PlcResult<short[]>.Failure("The PLC client is not connected.");
349 }
350
351 try
352 {
353 return await ReadWordsCoreAsync(address, count, cancellationToken).ConfigureAwait(false);
354 }
355 catch (OperationCanceledException ex)
356 {
357 return PlcResult<short[]>.Failure(ex.Message);
358 }
359 catch (Exception ex)
360 {
361 SetState(PlcConnectionState.Faulted);
362 return PlcResult<short[]>.Failure(ex.Message);
363 }
364 }
365
412 public async Task<PlcResult> WriteBitsAsync(
413 PlcAddress address,
414 IReadOnlyList<bool> values,
415 CancellationToken cancellationToken = default)
416 {
418
419 var validationResult = ValidateWriteBitRequest(address, values);
420 if (!validationResult.IsSuccess)
421 {
422 return validationResult;
423 }
424
425 if (State != PlcConnectionState.Connected)
426 {
427 return PlcResult.Failure("The PLC client is not connected.");
428 }
429
430 try
431 {
432 return await WriteBitsCoreAsync(address, values, cancellationToken).ConfigureAwait(false);
433 }
434 catch (OperationCanceledException ex)
435 {
436 return PlcResult.Failure(ex.Message);
437 }
438 catch (Exception ex)
439 {
440 SetState(PlcConnectionState.Faulted);
441 return PlcResult.Failure(ex.Message);
442 }
443 }
444
491 public async Task<PlcResult> WriteWordsAsync(
492 PlcAddress address,
493 IReadOnlyList<short> values,
494 CancellationToken cancellationToken = default)
495 {
497
498 var validationResult = ValidateWriteWordRequest(address, values);
499 if (!validationResult.IsSuccess)
500 {
501 return validationResult;
502 }
503
504 if (State != PlcConnectionState.Connected)
505 {
506 return PlcResult.Failure("The PLC client is not connected.");
507 }
508
509 try
510 {
511 return await WriteWordsCoreAsync(address, values, cancellationToken).ConfigureAwait(false);
512 }
513 catch (OperationCanceledException ex)
514 {
515 return PlcResult.Failure(ex.Message);
516 }
517 catch (Exception ex)
518 {
519 SetState(PlcConnectionState.Faulted);
520 return PlcResult.Failure(ex.Message);
521 }
522 }
523
540 public virtual async ValueTask DisposeAsync()
541 {
542 if (Interlocked.CompareExchange(ref _disposeGuard, 1, 0) != 0)
543 {
544 return;
545 }
546
547 // Set _disposed before disconnecting so ThrowIfDisposed blocks concurrent callers immediately.
548 // DisconnectAsync checks _disposed and would short-circuit, so we disconnect directly here.
549 _disposed = true;
550
551 try
552 {
553 using (await _syncLock.LockAsync(CancellationToken.None).ConfigureAwait(false))
554 {
555 if (State is not (PlcConnectionState.Disconnected or PlcConnectionState.Disconnecting))
556 {
557 SetState(PlcConnectionState.Disconnecting);
558
559 try
560 {
561 await DisconnectCoreAsync(CancellationToken.None).ConfigureAwait(false);
562 }
563 catch
564 {
565 // Ignore disconnect errors during disposal.
566 }
567
568 SetState(PlcConnectionState.Disconnected);
569 }
570 }
571 }
572 catch
573 {
574 // Ignore lock acquisition errors during disposal.
575 }
576
577 GC.SuppressFinalize(this);
578 }
579
603 protected abstract Task<PlcResult> ConnectCoreAsync(CancellationToken cancellationToken);
604
628 protected abstract Task<PlcResult> DisconnectCoreAsync(CancellationToken cancellationToken);
629
667 protected abstract Task<PlcResult<bool[]>> ReadBitsCoreAsync(
668 PlcAddress address,
669 int count,
670 CancellationToken cancellationToken);
671
709 protected abstract Task<PlcResult<short[]>> ReadWordsCoreAsync(
710 PlcAddress address,
711 int count,
712 CancellationToken cancellationToken);
713
751 protected abstract Task<PlcResult> WriteBitsCoreAsync(
752 PlcAddress address,
753 IReadOnlyList<bool> values,
754 CancellationToken cancellationToken);
755
793 protected abstract Task<PlcResult> WriteWordsCoreAsync(
794 PlcAddress address,
795 IReadOnlyList<short> values,
796 CancellationToken cancellationToken);
797
814 protected void SetState(PlcConnectionState state)
815 {
816 if (State == state)
817 {
818 return;
819 }
820
821 State = state;
822 StateChanged?.Invoke(this, state);
823 }
824
854 private static PlcResult ValidateReadRequest(PlcAddress address, int count)
855 {
856 var addressResult = PlcValidation.ValidateAddress(address);
857 if (!addressResult.IsSuccess)
858 {
859 return addressResult;
860 }
861
862 return PlcValidation.ValidateCount(count);
863 }
864
894 private static PlcResult ValidateWriteBitRequest(PlcAddress address, IReadOnlyList<bool> values)
895 {
896 var addressResult = PlcValidation.ValidateAddress(address);
897 if (!addressResult.IsSuccess)
898 {
899 return addressResult;
900 }
901
902 return PlcValidation.ValidateBitValues(values);
903 }
904
934 private static PlcResult ValidateWriteWordRequest(PlcAddress address, IReadOnlyList<short> values)
935 {
936 var addressResult = PlcValidation.ValidateAddress(address);
937 if (!addressResult.IsSuccess)
938 {
939 return addressResult;
940 }
941
942 return PlcValidation.ValidateWordValues(values);
943 }
944
961 private void ThrowIfDisposed()
962 {
963 ObjectDisposedException.ThrowIf(_disposed, GetType().Name);
964 }
965}
async Task< PlcResult > DisconnectAsync(CancellationToken cancellationToken=default)
Task< PlcResult > WriteBitsCoreAsync(PlcAddress address, IReadOnlyList< bool > values, CancellationToken cancellationToken)
async Task< PlcResult > WriteBitsAsync(PlcAddress address, IReadOnlyList< bool > values, CancellationToken cancellationToken=default)
Task< PlcResult > ConnectCoreAsync(CancellationToken cancellationToken)
Task< PlcResult< short[]> > ReadWordsCoreAsync(PlcAddress address, int count, CancellationToken cancellationToken)
async Task< PlcResult< short[]> > ReadWordsAsync(PlcAddress address, int count, CancellationToken cancellationToken=default)
static PlcResult ValidateReadRequest(PlcAddress address, int count)
async Task< PlcResult< bool[]> > ReadBitsAsync(PlcAddress address, int count, CancellationToken cancellationToken=default)
async Task< PlcResult > WriteWordsAsync(PlcAddress address, IReadOnlyList< short > values, CancellationToken cancellationToken=default)
Task< PlcResult< bool[]> > ReadBitsCoreAsync(PlcAddress address, int count, CancellationToken cancellationToken)
virtual async ValueTask DisposeAsync()
async Task< PlcResult > ConnectAsync(CancellationToken cancellationToken=default)
void SetState(PlcConnectionState state)
EventHandler< PlcConnectionState >? StateChanged
Task< PlcResult > WriteWordsCoreAsync(PlcAddress address, IReadOnlyList< short > values, CancellationToken cancellationToken)
static PlcResult ValidateWriteWordRequest(PlcAddress address, IReadOnlyList< short > values)
Task< PlcResult > DisconnectCoreAsync(CancellationToken cancellationToken)
static PlcResult ValidateWriteBitRequest(PlcAddress address, IReadOnlyList< bool > values)
static PlcResult ValidateAddress(PlcAddress address)
static PlcResult ValidateBitValues(IReadOnlyList< bool > values)
static PlcResult ValidateCount(int count)
static PlcResult ValidateWordValues(IReadOnlyList< short > values)