Dreamine.PLC.Mitsubishi.MxComponent 1.0.1
Dreamine.PLC.Mitsubishi.MxComponent 산업 자동화 및 I/O 기능을 제공합니다.
로딩중...
검색중...
일치하는것 없음
MitsubishiMxComponentPlcClient.cs
이 파일의 문서화 페이지로 가기
1using System.Globalization;
2using System.Runtime.InteropServices;
3using Dreamine.PLC.Abstractions.Devices;
4using Dreamine.PLC.Abstractions.Results;
5using Dreamine.PLC.Core.Clients;
9
11
20public sealed class MitsubishiMxComponentPlcClient : PlcClientBase
21{
39 private readonly IComObjectFactory _factory;
48 private object? _component;
49
75 : this(options, new DefaultComObjectFactory())
76 {
77 }
78
112 {
113 _options = options ?? throw new ArgumentNullException(nameof(options));
114 _factory = factory ?? throw new ArgumentNullException(nameof(factory));
115 }
116
126
175 protected override Task<PlcResult> ConnectCoreAsync(CancellationToken cancellationToken)
176 {
177 cancellationToken.ThrowIfCancellationRequested();
178
179 _component = _factory.Create(_options.ProgId);
180 ComInvoker.SetProperty(_component, "ActLogicalStationNumber", _options.LogicalStationNumber);
181
182 var resultCode = ComInvoker.ToReturnCode(ComInvoker.Invoke(_component, _options.OpenMethodName));
183 return Task.FromResult(ToResult(resultCode, "MX Component open failed."));
184 }
185
226 protected override Task<PlcResult> DisconnectCoreAsync(CancellationToken cancellationToken)
227 {
228 cancellationToken.ThrowIfCancellationRequested();
229
230 if (_component is null)
231 {
232 return Task.FromResult(PlcResult.Success());
233 }
234
235 var resultCode = ComInvoker.ToReturnCode(ComInvoker.Invoke(_component, _options.CloseMethodName));
237 return Task.FromResult(ToResult(resultCode, "MX Component close failed."));
238 }
239
304 protected override Task<PlcResult<bool[]>> ReadBitsCoreAsync(
305 PlcAddress address,
306 int count,
307 CancellationToken cancellationToken)
308 {
309 var component = RequireComponent();
310 var device = MitsubishiMxDeviceNameFormatter.Format(address);
311 var buffer = new short[count];
312 var args = new object?[] { device, count, buffer };
313
314 try
315 {
316 cancellationToken.ThrowIfCancellationRequested();
317
318 var resultCode = ComInvoker.ToReturnCode(ComInvoker.InvokeWithByRef(component, _options.ReadDeviceBlock2MethodName, args, 2));
319 if (resultCode == 0 && TryExtractBoolArray(args[2], count, out var values))
320 {
321 return Task.FromResult(PlcResult<bool[]>.Success(values));
322 }
323 }
324 catch (MissingMethodException)
325 {
326 // Fall back to GetDevice below for MX Component variants without block calls.
327 }
328 catch (COMException)
329 {
330 // COM late binding can reject array arguments. Fall back to GetDevice.
331 }
332 catch (InvalidOperationException ex) when (ex.InnerException is COMException)
333 {
334 // ComInvoker wraps COM target invocation failures. Fall back to GetDevice.
335 }
336
337 return ReadBitsOneByOne(component, address, count, cancellationToken);
338 }
339
404 protected override Task<PlcResult<short[]>> ReadWordsCoreAsync(
405 PlcAddress address,
406 int count,
407 CancellationToken cancellationToken)
408 {
409 var component = RequireComponent();
410 var device = MitsubishiMxDeviceNameFormatter.Format(address);
411 var buffer = new short[count];
412 var args = new object?[] { device, count, buffer };
413
414 try
415 {
416 cancellationToken.ThrowIfCancellationRequested();
417
418 var resultCode = ComInvoker.ToReturnCode(ComInvoker.InvokeWithByRef(component, _options.ReadDeviceBlock2MethodName, args, 2));
419 if (resultCode == 0 && TryExtractShortArray(args[2], count, out var values))
420 {
421 return Task.FromResult(PlcResult<short[]>.Success(values));
422 }
423 }
424 catch (MissingMethodException)
425 {
426 // Fall back to GetDevice below for MX Component variants that do not expose block calls through late binding.
427 }
428 catch (COMException)
429 {
430 // COM late binding can reject array arguments for ReadDeviceBlock2. Fall back to GetDevice.
431 }
432 catch (InvalidOperationException ex) when (ex.InnerException is COMException)
433 {
434 // ComInvoker wraps COM target invocation failures. Fall back to GetDevice for array type mismatches.
435 }
436
437 return ReadWordsOneByOne(component, address, count, cancellationToken);
438 }
439
496 protected override Task<PlcResult> WriteBitsCoreAsync(
497 PlcAddress address,
498 IReadOnlyList<bool> values,
499 CancellationToken cancellationToken)
500 {
501 var component = RequireComponent();
502
503 for (var index = 0; index < values.Count; index++)
504 {
505 cancellationToken.ThrowIfCancellationRequested();
506
507 var resultCode = ComInvoker.ToReturnCode(ComInvoker.Invoke(
508 component,
509 _options.WriteDeviceMethodName,
511 values[index] ? 1 : 0));
512
513 if (resultCode != 0)
514 {
515 return Task.FromResult(ToResult(resultCode, "MX Component bit write failed."));
516 }
517 }
518
519 return Task.FromResult(PlcResult.Success());
520 }
521
578 protected override Task<PlcResult> WriteWordsCoreAsync(
579 PlcAddress address,
580 IReadOnlyList<short> values,
581 CancellationToken cancellationToken)
582 {
583 var component = RequireComponent();
584 var device = MitsubishiMxDeviceNameFormatter.Format(address);
585 var data = values.ToArray();
586
587 try
588 {
589 cancellationToken.ThrowIfCancellationRequested();
590
591 var resultCode = ComInvoker.ToReturnCode(ComInvoker.InvokeWithByRef(
592 component,
593 _options.WriteDeviceBlock2MethodName,
594 [device, values.Count, data],
595 2));
596
597 if (resultCode == 0)
598 {
599 return Task.FromResult(PlcResult.Success());
600 }
601 }
602 catch (MissingMethodException)
603 {
604 // Fall back to SetDevice below.
605 }
606 catch (COMException)
607 {
608 // COM late binding can reject array arguments for WriteDeviceBlock2. Fall back to SetDevice.
609 }
610 catch (InvalidOperationException ex) when (ex.InnerException is COMException)
611 {
612 // ComInvoker wraps COM target invocation failures. Fall back to SetDevice for array type mismatches.
613 }
614
615 for (var index = 0; index < values.Count; index++)
616 {
617 cancellationToken.ThrowIfCancellationRequested();
618
619 var resultCode = ComInvoker.ToReturnCode(ComInvoker.Invoke(
620 component,
621 _options.WriteDeviceMethodName,
623 values[index]));
624
625 if (resultCode != 0)
626 {
627 return Task.FromResult(ToResult(resultCode, "MX Component word write failed."));
628 }
629 }
630
631 return Task.FromResult(PlcResult.Success());
632 }
633
650 public override async ValueTask DisposeAsync()
651 {
652 await base.DisposeAsync().ConfigureAwait(false);
654 }
655
720 private Task<PlcResult<short[]>> ReadWordsOneByOne(
721 object component,
722 PlcAddress address,
723 int count,
724 CancellationToken cancellationToken)
725 {
726 var values = new short[count];
727
728 for (var index = 0; index < count; index++)
729 {
730 cancellationToken.ThrowIfCancellationRequested();
731
732 var args = new object?[]
733 {
735 0
736 };
737
738 var resultCode = ComInvoker.ToReturnCode(ComInvoker.InvokeWithByRef(component, _options.ReadDeviceMethodName, args, 1));
739 if (resultCode != 0)
740 {
741 return Task.FromResult(PlcResult<short[]>.Failure($"MX Component word read failed. code={resultCode}", resultCode));
742 }
743
744 values[index] = Convert.ToInt16(args[1], CultureInfo.InvariantCulture);
745 }
746
747 return Task.FromResult(PlcResult<short[]>.Success(values));
748 }
749
814 private Task<PlcResult<bool[]>> ReadBitsOneByOne(
815 object component,
816 PlcAddress address,
817 int count,
818 CancellationToken cancellationToken)
819 {
820 var values = new bool[count];
821
822 for (var index = 0; index < count; index++)
823 {
824 cancellationToken.ThrowIfCancellationRequested();
825
826 var args = new object?[]
827 {
829 0
830 };
831
832 var resultCode = ComInvoker.ToReturnCode(ComInvoker.InvokeWithByRef(component, _options.ReadDeviceMethodName, args, 1));
833 if (resultCode != 0)
834 {
835 return Task.FromResult(PlcResult<bool[]>.Failure($"MX Component bit read failed. code={resultCode}", resultCode));
836 }
837
838 values[index] = Convert.ToInt32(args[1], CultureInfo.InvariantCulture) != 0;
839 }
840
841 return Task.FromResult(PlcResult<bool[]>.Success(values));
842 }
843
868 private object RequireComponent()
869 {
870 return _component ?? throw new InvalidOperationException("MX Component is not connected.");
871 }
872
881 private void ReleaseComponent()
882 {
883 if (_component is null)
884 {
885 return;
886 }
887
888 if (OperatingSystem.IsWindows() && Marshal.IsComObject(_component))
889 {
890 Marshal.FinalReleaseComObject(_component);
891 }
892
893 _component = null;
894 }
895
928 private static PlcResult ToResult(int resultCode, string message)
929 {
930 return resultCode == 0
931 ? PlcResult.Success()
932 : PlcResult.Failure($"{message} code={resultCode}", resultCode);
933 }
934
983 private static bool TryExtractShortArray(object? source, int count, out short[] values)
984 {
985 values = new short[count];
986
987 switch (source)
988 {
989 case short[] shorts when shorts.Length >= count:
990 Array.Copy(shorts, values, count);
991 return true;
992 case int[] integers when integers.Length >= count:
993 for (var index = 0; index < count; index++)
994 {
995 values[index] = Convert.ToInt16(integers[index], CultureInfo.InvariantCulture);
996 }
997
998 return true;
999 case Array array when array.Length >= count:
1000 for (var index = 0; index < count; index++)
1001 {
1002 values[index] = Convert.ToInt16(array.GetValue(index), CultureInfo.InvariantCulture);
1003 }
1004
1005 return true;
1006 default:
1007 return false;
1008 }
1009 }
1010
1067 private static bool TryExtractBoolArray(object? source, int count, out bool[] values)
1068 {
1069 values = new bool[count];
1070
1071 switch (source)
1072 {
1073 case bool[] booleans when booleans.Length >= count:
1074 Array.Copy(booleans, values, count);
1075 return true;
1076 case short[] shorts when shorts.Length >= count:
1077 for (var index = 0; index < count; index++)
1078 {
1079 values[index] = shorts[index] != 0;
1080 }
1081
1082 return true;
1083 case int[] integers when integers.Length >= count:
1084 for (var index = 0; index < count; index++)
1085 {
1086 values[index] = integers[index] != 0;
1087 }
1088
1089 return true;
1090 case Array array when array.Length >= count:
1091 for (var index = 0; index < count; index++)
1092 {
1093 values[index] = Convert.ToInt32(array.GetValue(index), CultureInfo.InvariantCulture) != 0;
1094 }
1095
1096 return true;
1097 default:
1098 return false;
1099 }
1100 }
1101}
override Task< PlcResult > DisconnectCoreAsync(CancellationToken cancellationToken)
override Task< PlcResult > WriteWordsCoreAsync(PlcAddress address, IReadOnlyList< short > values, CancellationToken cancellationToken)
override Task< PlcResult< bool[]> > ReadBitsCoreAsync(PlcAddress address, int count, CancellationToken cancellationToken)
static bool TryExtractShortArray(object? source, int count, out short[] values)
static bool TryExtractBoolArray(object? source, int count, out bool[] values)
Task< PlcResult< short[]> > ReadWordsOneByOne(object component, PlcAddress address, int count, CancellationToken cancellationToken)
MitsubishiMxComponentPlcClient(MitsubishiMxComponentOptions options, IComObjectFactory factory)
override Task< PlcResult< short[]> > ReadWordsCoreAsync(PlcAddress address, int count, CancellationToken cancellationToken)
Task< PlcResult< bool[]> > ReadBitsOneByOne(object component, PlcAddress address, int count, CancellationToken cancellationToken)
override Task< PlcResult > WriteBitsCoreAsync(PlcAddress address, IReadOnlyList< bool > values, CancellationToken cancellationToken)
override Task< PlcResult > ConnectCoreAsync(CancellationToken cancellationToken)