public async Task Check_Logging_When_Reading() { // Arrange var logger = Mock.Of <ILogger>(); LogManager.LoggerFactory = () => logger; LogManager.LogAllReadAndWriteOperations = true; var byteItem = new BytePlcItem(dataBlock: 0, position: 0, initialValue: Byte.MaxValue); ICollection <IPlcItem> items = new IPlcItem[] { byteItem, byteItem.Clone(), byteItem.Clone() }; var plcMock = new Mock <Plc>(Guid.NewGuid().ToString()); plcMock .Setup(p => p.ReadItemsAsync(It.IsAny <IList <IPlcItem> >(), CancellationToken.None)) #if NET45 .Returns(CompletedTask) #else .Returns(Task.CompletedTask) #endif ; var plc = plcMock.Object; // Act await plc.ReadItemsAsync(items); // Assert Mock.Get(logger).Verify(l => l.Trace(It.IsAny <string>(), It.IsAny <object[]>()), Times.Exactly(items.Count)); }
public async Task Check_If_Writing_Is_Paused_If_Not_Connected() { // Arrange var byteItem = new BytePlcItem(dataBlock: 0, position: 0, initialValue: byte.MaxValue); var plcMock = new Mock <Plc>("MockPlc") { CallBase = true }; plcMock .Setup(p => p.OpenConnection()) .Returns(true) .Verifiable() ; plcMock .Setup(p => p.CloseConnection()) .Returns(true) .Verifiable() ; plcMock .Setup(p => p.PerformReadWriteAsync(It.IsAny <ICollection <IPlcItem> >(), It.IsAny <Plc.PlcItemUsageType>(), CancellationToken.None)) #if NET45 .Returns(CompletedTask) #else .Returns(Task.CompletedTask) #endif .Verifiable() ; var plc = (IPlc)plcMock.Object; // Disconnect before reading. var success = plc.Disconnect(); Assert.True(success); // Execute the write function. var writeTask = plc.WriteItemAsync(byteItem); // Create a new task that automatically ends after some time. var waitTask = Task.Delay(3000); // Wait until one of the tasks finishes. await Task.WhenAny(new[] { writeTask, waitTask }); // The write task should still be running. Assert.False(writeTask.Status == TaskStatus.RanToCompletion); // Connect to the plc. success = plc.Connect(); Assert.True(success); // Now await the write task and check the result. var result = await writeTask; Assert.True(result); Assert.True(byteItem.Value == byte.MaxValue); }
public void Check_Initial_Data() { // Arrange var targetValue = TargetValue; var targetData = new byte[] { TargetValue }; // Act var item = new BytePlcItem(0, 0, targetValue); // Assert: Number → Data Assert.That(item.Value, Is.EqualTo(targetValue)); Assert.That((byte[])((IPlcItem)item).Value, Is.EqualTo(targetData)); }
public void WriteByte() { var writeItem = new BytePlcItem(dataBlock: this.Data.Datablock, position: 4, initialValue: this.Data.WriteBytes[0]); base.ExecuteTest ( async(plc) => { var result = await plc.WriteItemWithValidationAsync(writeItem); Assert.True(result); } ); }
public void Check_FromData() { // Arrange var targetValue = TargetValue; var targetData = new byte[] { TargetValue }; var item = new BytePlcItem(0, 0, (ushort)targetData.Length); // Act: Data → Number ((IPlcItem)item).Value.TransferValuesFrom(targetData); // Assert Assert.That(item.Value, Is.EqualTo(targetValue)); Assert.That((byte[])((IPlcItem)item).Value, Is.EqualTo(targetData)); }
public void Check_ToData() { // Arrange var targetValue = TargetValue; var targetData = new byte[] { TargetValue }; var item = new BytePlcItem(0, 0, (ushort)targetData.Length); // Act: Number → Data item.Value = targetValue; // Assert Assert.That(item.Value, Is.EqualTo(targetValue)); Assert.That((byte[])((IPlcItem)item).Value, Is.EqualTo(targetData)); }
public void Check_Clone() { var item = new BytePlcItem(0, 0, TargetValue); var clone = item.Clone(); // Check equality (Equals is overriden). Assert.AreEqual(item, clone); // Check the value. Assert.AreEqual(item.Value, clone.Value); // Check if both items are different references. Assert.False(Object.ReferenceEquals(item, clone)); }
public void ReadByte() { var byteItem = new BytePlcItem(dataBlock: this.Data.Datablock, position: 0); base.ExecuteTest ( async(plc) => { var result = await plc.ReadItemAsync(byteItem); //var result = await PlcExtensions.ReadItemAsync(plc, byteItem); Assert.True(byteItem.Value == result); Assert.True(byteItem.Value == this.Data.TargetBytes[0]); } ); }
[TestCase(new byte[] { 0 }, 1, new byte[] { 0 })] // This can be written in total. public void Check_If_Limit_Is_Respected_When_Writing(byte[] value, byte targetLength, byte[] targetData) { // Arrange var limit = (uint)2; var numericItem = new BytePlcItem(0, 0); Func <string, IPlcItem <byte[]> > flexiblePlcItemFactory = name => new BytesPlcItem(0, 1, true, new byte[0], name); var dynamicPlcItem = new DynamicPlcItem <byte[]>(numericItem, flexiblePlcItemFactory, 1, limit); // Act dynamicPlcItem.FlexiblePlcItem.Value = value; // Assert Assert.That(numericItem.Value, Is.EqualTo(targetLength)); Assert.True(targetData.SequenceEqual(dynamicPlcItem.FlexiblePlcItem.Value)); }
public async Task Check_That_The_Internal_CancellationTokenSource_Is_Disposed_After_Reading_Or_Writing() { // Arrange using var externalTokenSource = new CancellationTokenSource(); var externalToken = externalTokenSource.Token; var byteItem = new BytePlcItem(dataBlock: 0, position: 0, initialValue: byte.MaxValue); var plcMock = new Mock <Plc>("MockPlc") { CallBase = true }; plcMock .Setup(p => p.OpenConnection()) .Returns(true) .Verifiable() ; plcMock .Setup(p => p.CloseConnection()) .Returns(true) .Verifiable() ; plcMock .Setup(p => p.PerformReadWriteAsync(It.IsAny <ICollection <IPlcItem> >(), It.IsAny <Plc.PlcItemUsageType>(), CancellationToken.None)) #if NET45 .Returns(CompletedTask) #else .Returns(Task.CompletedTask) #endif .Verifiable() ; plcMock .Setup(p => p.LinkedTokenWasCanceled()) .Verifiable() ; using var plc = plcMock.Object; plc.Connect(); // Act await plc.ReadItemAsync(byteItem, CancellationToken.None); // Assert plcMock.Verify(p => p.LinkedTokenWasCanceled(), Times.Once); Assert.False(externalTokenSource.IsCancellationRequested); Assert.False(externalToken.IsCancellationRequested); }
public void Check_If_Disposing_A_Plc_With_Waiting_Items_For_Writing_Throws() { // Arrange var byteItem = new BytePlcItem(dataBlock: 0, position: 0, initialValue: byte.MaxValue); var plcMock = new Mock <Plc>("MockPlc") { CallBase = true }; plcMock .Setup(p => p.OpenConnection()) .Returns(true) .Verifiable() ; plcMock .Setup(p => p.CloseConnection()) .Returns(true) .Verifiable() ; plcMock .Setup(p => p.PerformReadWriteAsync(It.IsAny <ICollection <IPlcItem> >(), It.IsAny <Plc.PlcItemUsageType>(), CancellationToken.None)) #if NET45 .Returns(CompletedTask) #else .Returns(Task.CompletedTask) #endif .Verifiable() ; var plc = (IPlc)plcMock.Object; var writeTask = plc.WriteItemsAsync(new IPlcItem[] { byteItem }); // Act Task.Run ( async() => { await Task.Delay(1000); plc.Dispose(); } ); // Assert Assert.ThrowsAsync <DisposedWritePlcException>(() => writeTask); }
public async Task Check_If_Writing_Is_Canceled_If_Plc_Is_Disposed() { // Arrange var byteItem = new BytePlcItem(dataBlock: 0, position: 0, initialValue: byte.MaxValue); var plcMock = new Mock <Plc>("MockPlc") { CallBase = true }; plcMock .Setup(p => p.OpenConnection()) .Returns(true) .Verifiable() ; plcMock .Setup(p => p.CloseConnection()) .Returns(true) .Verifiable() ; plcMock .Setup(p => p.PerformReadWriteAsync(It.IsAny <ICollection <IPlcItem> >(), It.IsAny <Plc.PlcItemUsageType>(), CancellationToken.None)) #if NET45 .Returns(CompletedTask) #else .Returns(Task.CompletedTask) #endif .Verifiable() ; var plc = (IPlc)plcMock.Object; // Disconnect before writing and then wait a little bit. plc.Disconnect(); var writeTask = plc.WriteItemsAsync(new IPlcItem[] { byteItem }); await Task.Delay(1000); // Act plc.Dispose(); // Assert Assert.ThrowsAsync <DisposedWritePlcException>(() => writeTask); Assert.That(writeTask.Status, Is.EqualTo(TaskStatus.Faulted)); }
public void CheckItemIdentifierAndPlcString() { IPlcItem plcItem; plcItem = new BitPlcItem(dataBlock: 0, position: 0, bitPosition: BitPosition.X1, identifier: "Bit"); Assert.AreEqual("Bit", plcItem.Identifier); Assert.AreEqual("DB0,X0.1,1", plcItem.PlcString); plcItem = new BytePlcItem(dataBlock: 1234, position: 0, identifier: "Byte"); Assert.AreEqual("Byte", plcItem.Identifier); Assert.AreEqual("DB1234,B0,1", plcItem.PlcString); plcItem = new BitsPlcItem(type: PlcItemType.Input, dataBlock: 0, position: 0, bitPosition: BitPosition.X1, bitAmount: 2, identifier: "Bits"); Assert.AreEqual("Bits", plcItem.Identifier); Assert.AreEqual("Input,X0.1,2", plcItem.PlcString); plcItem = new BytePlcItem(type: PlcItemType.Output, dataBlock: 0, position: 0); Assert.AreEqual("Output,B0,1", plcItem.Identifier); Assert.AreEqual(plcItem.Identifier, plcItem.PlcString); }
public DynamicUtf8PlcItem BuildDynamic() { this.Validate(); // ReSharper disable PossibleInvalidOperationException //! The validation method takes care of no value being null. INumericPlcItem numericPlcItem; if (_numericPlcItemType == typeof(BytePlcItem)) { numericPlcItem = new BytePlcItem(base.DataBlock.Value, base.Position.Value, initialValue: (byte)base.ByteAmount); } else if (_numericPlcItemType == typeof(UInt16PlcItem)) { numericPlcItem = new UInt16PlcItem(base.DataBlock.Value, base.Position.Value, initialValue: (UInt16)base.ByteAmount); } else if (_numericPlcItemType == typeof(UInt32PlcItem)) { numericPlcItem = new UInt32PlcItem(base.DataBlock.Value, base.Position.Value, initialValue: (UInt32)base.ByteAmount); } else if (_numericPlcItemType == typeof(WordPlcItem)) { numericPlcItem = new WordPlcItem(base.DataBlock.Value, base.Position.Value, initialValue: (UInt16)base.ByteAmount); } else if (_numericPlcItemType == typeof(DWordPlcItem)) { numericPlcItem = new DWordPlcItem(base.DataBlock.Value, base.Position.Value, initialValue: (UInt32)base.ByteAmount); } else { throw new NotSupportedException($"The numeric part of any dynamic plc item must be an {nameof(INumericPlcItem)}. Currently supported are the following concrete items: {nameof(BytePlcItem)}, {nameof(UInt16PlcItem)}, {nameof(UInt32PlcItem)}, {nameof(WordPlcItem)}, {nameof(DWordPlcItem)}"); } return(new DynamicUtf8PlcItem(numericPlcItem, _lengthFactor, _lengthLimit, base.InitialValue, base.Identifier)); // ReSharper restore PossibleInvalidOperationException }
public async Task MonitorDifferentIntervals() { var monitoredChangesOfFirstItem = 0; var monitoredChangesOfSecondItem = 0; var firstMonitorItemIdentifier = "Monitored item #01"; uint firstMonitorItemInterval = 50; var secondMonitorItemIdentifier = "Monitored item #02"; uint secondMonitorItemInterval = 200; var changes = 100; var secondChanges = changes / ((secondMonitorItemInterval / firstMonitorItemInterval) / 2); var monitoredPlc = new MockPlc().MakeMonitorable ( new Dictionary <string, uint> { { firstMonitorItemIdentifier, firstMonitorItemInterval } , { secondMonitorItemIdentifier, secondMonitorItemInterval } } ); // Create items that must be monitored. var firstMonitoredItem = new BytePlcItem(dataBlock: Data.Datablock, position: Data.StartOfModifiableBytes, identifier: firstMonitorItemIdentifier); var secondMonitoredItem = firstMonitoredItem.Clone(secondMonitorItemIdentifier); // Create the item that is used to manipulate the value. IPlcItem writeItem = firstMonitoredItem.Clone("ChangeItem"); // Set a callback for changes to the items. firstMonitoredItem.ValueChanged += (sender, args) => { monitoredChangesOfFirstItem++; }; secondMonitoredItem.ValueChanged += (sender, args) => { monitoredChangesOfSecondItem++; }; try { // Connect to the plc. monitoredPlc.Connect(); // Start monitoring those items. monitoredPlc.MonitorItem(firstMonitoredItem); monitoredPlc.MonitorItem(secondMonitoredItem); monitoredPlc.Start(); // Manipulate the monitored value. for (byte i = 1; i <= changes; i++) { writeItem.Value.TransferValuesFrom(new[] { i }); await monitoredPlc.WriteItemAsync(writeItem); await Task.Delay((int)firstMonitorItemInterval * 2); // This must be at least the double amount of the polling interval. } // Stop monitoring. monitoredPlc.Stop(); // Check if all changes where registered. Assert.AreEqual(changes, monitoredChangesOfFirstItem); Assert.True(monitoredChangesOfSecondItem >= secondChanges); } finally { monitoredPlc.Dispose(); } }
public async Task MonitorChanges() { var changes = 100; var target = (changes * (changes + 1)) / 2; // Gaußsche Summenformel var monitoredChanges = 0; var collectedValue = 0; var monitorItemIdentifier = "MonitoredItem"; uint monitorItemInterval = 50; var monitoredPlc = new MockPlc().MakeMonitorable ( new Dictionary <string, uint> { { monitorItemIdentifier, monitorItemInterval } } ); // Create the item that must be monitored. var monitorItem = new BytePlcItem(dataBlock: Data.Datablock, position: Data.StartOfModifiableBytes, identifier: monitorItemIdentifier); // Create the item that is used to manipulate the value. IPlcItem writeItem = monitorItem.Clone("ChangeItem"); // Set a callback for changes to the items. monitorItem.ValueChanged += (sender, args) => { monitoredChanges++; collectedValue += args.NewValue; }; try { // Connect to the plc. monitoredPlc.Connect(); // Start monitoring those items. monitoredPlc.MonitorItem(monitorItem); monitoredPlc.Start(); // Manipulate the monitored value. for (byte i = 1; i <= changes; i++) { writeItem.Value.TransferValuesFrom(new[] { i }); await monitoredPlc.WriteItemAsync(writeItem); await Task.Delay((int)monitorItemInterval * 2); // This must be at least the double amount of the polling interval. } // Stop monitoring. monitoredPlc.Stop(); // Further manipulate the value to check if this is not monitored. writeItem.Value.TransferValuesFrom(new[] { byte.MinValue }); writeItem.Value.TransferValuesFrom(new[] { byte.MaxValue }); // Check if all changes where registered. Assert.AreEqual(changes, monitoredChanges); Assert.AreEqual(target, collectedValue); Assert.AreEqual(changes, monitorItem.Value); } finally { monitoredPlc.Dispose(); } }
/// <summary> /// Checks that the <see cref="CancellationTokenSource"/> that is created while reading/writing is properly disposed. /// </summary> protected async Task CheckMemoryUsage(int iterations = 100000) { // Arrange var byteItem = new BytePlcItem(dataBlock: 0, position: 0, initialValue: byte.MaxValue); var plc = this.Plc; plc.Connect(); // Perform some warm-up for (var iteration = 1; iteration <= 500; iteration++) { await plc.ReadItemAsync(byteItem, CancellationToken.None); if (iteration % 100 == 0) { await Task.Delay(250, CancellationToken.None); } } GC.Collect(); GC.WaitForPendingFinalizers(); var initialMemoryUsage = 0d; var finalMemoryUsage = 0d; using (var process = Process.GetCurrentProcess()) { initialMemoryUsage = process.PrivateMemorySize64 / (double)(1024 * 1024); Console.WriteLine($"Initial memory usage: {initialMemoryUsage} MByte"); } // Act for (var iteration = 1; iteration <= iterations; iteration++) { await plc.ReadItemAsync(byteItem, CancellationToken.None); if (iteration % 500 == 0) { await Task.Delay(250, CancellationToken.None); } } GC.Collect(); GC.WaitForPendingFinalizers(); using (var process = Process.GetCurrentProcess()) { finalMemoryUsage = process.PrivateMemorySize64 / (double)(1024 * 1024); Console.WriteLine($"Final memory usage: {finalMemoryUsage} MByte"); } // Assert var difference = finalMemoryUsage - initialMemoryUsage; Console.WriteLine($"Memory increased by: {difference} MByte"); #if NET45 if (difference <= 15) { Assert.Pass(); } else { Assert.Inconclusive("When running in .NET Framework 4.5 environment, the memory usage seems not predictable. Tests with 100.000 iterations repetitively showed, that the memory consumption spikes from 180 MByte after warmup to 620 MByte in a linear fashion but then immediately slows down. The final value is then around 635 MByte. Manual memory snapshots showed no significant increase in allocated objects however."); } #else Assert.That(difference, Is.Not.GreaterThan(15)); //! Both 100.000 and 1.000.000 iterations proofed to not allocate more than 13 MByte. #endif }
public void CheckItemBuilder() { var itemBuilder = new PlcItemBuilder(); var plcItem = itemBuilder .Construct("Generic") .ForData() .AtDatablock(0) .AtPosition(0, BitPosition.X2) .ForBitAmount(3) .Build() ; Assert.AreEqual((uint)3, plcItem.Value.Length); BitsPlcItem bitsItem = itemBuilder .ConstructBitsPlcItem() .ForFlags() .AtPosition(20, BitPosition.X5) .ForBitAmount(5) .Build() ; Assert.AreEqual((uint)5, ((IPlcItem)bitsItem).Value.Length); BitPlcItem bitItem = itemBuilder .ConstructBitPlcItem("Bit") .ForData() .AtDatablock(0) .AtPosition(5) .AsSet() .Build() ; Assert.AreEqual((uint)1, ((IPlcItem)bitItem).Value.Length); BytesPlcItem bytesItem = itemBuilder .ConstructBytesPlcItem(identifier: "Bytes") .ForOutput() .AtPosition(0) .WithInitialValue(new[] { byte.MinValue, byte.MaxValue }) .Build() ; Assert.AreEqual((uint)2, ((IPlcItem)bytesItem).Value.ByteLength); BytePlcItem byteItem = itemBuilder .ConstructBytePlcItem("Byte") .ForInput() .AtPosition(10) .WithInitialValue(Byte.MaxValue) .Build() ; Assert.AreEqual((uint)sizeof(Byte), ((IPlcItem)byteItem).Value.ByteLength); Int16PlcItem int16Item = itemBuilder .ConstructInt16PlcItem("Int16") .AtDatablock(0) .AtPosition(1) .WithoutInitialValue() .Build() ; Assert.AreEqual((uint)sizeof(Int16), ((IPlcItem)int16Item).Value.ByteLength); Int32PlcItem int32Item = itemBuilder .ConstructInt32PlcItem("Int32") .AtDatablock(0) .AtPosition(1) .WithInitialValue(int.MinValue) .Build() ; Assert.AreEqual((uint)sizeof(Int32), ((IPlcItem)int32Item).Value.ByteLength); Int64PlcItem int64Item = itemBuilder .ConstructInt64PlcItem("Int64") .AtDatablock(0) .AtPosition(1) .WithInitialValue(long.MinValue) .Build() ; Assert.AreEqual((uint)sizeof(Int64), ((IPlcItem)int64Item).Value.ByteLength); UInt16PlcItem uInt16Item = itemBuilder .ConstructUInt16PlcItem("UInt16") .AtDatablock(0) .AtPosition(1) .WithoutInitialValue() .Build() ; Assert.AreEqual((uint)sizeof(UInt16), ((IPlcItem)uInt16Item).Value.ByteLength); UInt32PlcItem uInt32PlcItem = itemBuilder .ConstructUInt32PlcItem("UInt32") .AtDatablock(0) .AtPosition(1) .WithInitialValue(uint.MaxValue) .Build() ; Assert.AreEqual((uint)sizeof(UInt32), ((IPlcItem)uInt32PlcItem).Value.ByteLength); UInt64PlcItem uInt64PlcItem = itemBuilder .ConstructUInt64PlcItem("UInt64") .AtDatablock(0) .AtPosition(1) .WithInitialValue(ulong.MaxValue) .Build() ; Assert.AreEqual((uint)sizeof(UInt64), ((IPlcItem)uInt64PlcItem).Value.ByteLength); WordPlcItem wordItem = itemBuilder .ConstructWordPlcItem("Word") .AtDatablock(0) .AtPosition(2) .WithInitialValue(32458) .Build() ; Assert.AreEqual((uint)2, ((IPlcItem)wordItem).Value.ByteLength); DWordPlcItem dwordItem = itemBuilder .ConstructDWordPlcItem("DWord") .AtDatablock(0) .AtPosition(2) .WithInitialValue(uint.MaxValue) .Build() ; Assert.AreEqual((uint)4, ((IPlcItem)dwordItem).Value.ByteLength); LWordPlcItem lwordItem = itemBuilder .ConstructLWordPlcItem("LWord") .AtDatablock(0) .AtPosition(2) .WithInitialValue(ulong.MaxValue) .Build() ; Assert.AreEqual((uint)8, ((IPlcItem)lwordItem).Value.ByteLength); TextPlcItem textItem = itemBuilder .ConstructTextPlcItem("Text") .WithEncoding(Encoding.UTF7) .AtDatablock(0) .AtPosition(3) .WithInitialValue("Some String") .Build() ; Assert.AreEqual((uint)Encoding.UTF7.GetBytes("Some String").Length, ((IPlcItem)textItem).Value.ByteLength); Utf8PlcItem utf8Item = itemBuilder .ConstructUtf8PlcItem("UTF-8") .AtDatablock(0) .AtPosition(4) .WithLength(10) .Build() ; Assert.AreEqual((uint)10, ((IPlcItem)utf8Item).Value.ByteLength); var initialText = "String whose length fits into a single byte."; DynamicUtf8PlcItem secondDynamicUtf8Item = itemBuilder .ConstructUtf8PlcItem("UTF-8") .AtDatablock(0) .AtPosition(4) .WithDynamicItemFromInitialValue(initialText) .BuildDynamic() ; Assert.That(secondDynamicUtf8Item.LengthPlcItem.Value, Is.EqualTo((uint)Encoding.UTF8.GetBytes(initialText).Length)); Assert.AreEqual(initialText, secondDynamicUtf8Item.Value); var items = new [] { plcItem, bitsItem, bitItem, bytesItem, byteItem, int16Item, int32Item, int64Item, uInt16Item, uInt32PlcItem, uInt64PlcItem, wordItem, dwordItem, lwordItem, textItem, utf8Item, secondDynamicUtf8Item, }; foreach (var item in items) { Debug.WriteLine($" -> {item}"); } }