public async Task Check_WriteItemsInOrderAsync_Fails()
        {
            // Arrange
            var items = new IPlcItem[]
            {
                new BytePlcItem(0, 0),
                new BytePlcItem(0, 0),
                new BytePlcItem(0, 0),
            };
            var plc = Mock.Of <IPlc>();

            Mock.Get(plc)
            .SetupSequence(p => p.WriteItemsAsync(It.IsAny <ICollection <IPlcItem> >(), CancellationToken.None))
            .ReturnsAsync(true)
            .ReturnsAsync(false)
            .ReturnsAsync(true)
            ;

            // Act
            var result = await plc.WriteItemsInOrderAsync(items);

            // Assert
            Assert.False(result);
            Mock.Get(plc).Verify(p => p.WriteItemsAsync(It.IsAny <ICollection <IPlcItem> >(), CancellationToken.None), Times.Exactly(2));
        }
Пример #2
0
        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));
        }
Пример #3
0
 public void RemovePlcItem(IPlcItem plcItem)
 {
     lock (_lock)
     {
         _plcItems.Remove(plcItem);
     }
 }
Пример #4
0
 /// <inheritdoc />
 public void UnMonitorItem(IPlcItem plcItem)
 {
     lock (_lock)
     {
         var hashValue = plcItem.GetHashCode();
         if (_plcItemHandlers.TryGetValue(hashValue, out var handler))
         {
             handler.RemovePlcItem(plcItem);
         }
     }
 }
        /// <summary>
        /// Gets the polling frequency for the <paramref name="plcItem"/>.
        /// </summary>
        /// <param name="plcItem"> The <see cref="IPlcItem"/> whose polling frequency to get. </param>
        /// <returns> The polling frequency. </returns>
        public TimeSpan GetPollingFrequencyForPlcItem(IPlcItem plcItem)
        {
            // Use the defined value for the item if available.
            if (plcItem.Identifier != null && base.Contains(plcItem.Identifier))
            {
                return(PlcItemMonitorConfigurations.ValidatePollingFrequency(TimeSpan.FromMilliseconds(base[plcItem.Identifier].PollingFrequency)));
            }

            // Otherwise the default polling frequency is used.
            return(PlcItemMonitorConfigurations.DefaultPollingFrequency);
        }
Пример #6
0
 private static void TransferValue(IPlcItem plcItem, byte[] data)
 {
     if (!plcItem.Value.HandlesFullBytes && plcItem.Value.Length > 1)
     {
         var booleans = DataConverter.ToBooleans(data).Skip((byte)plcItem.BitPosition).Take((int)plcItem.Value.Length).ToArray();
         plcItem.Value.TransferValuesFrom(booleans);
     }
     else
     {
         plcItem.Value.TransferValuesFrom(data);
     }
 }
Пример #7
0
        public void CheckClone()
        {
            // Arrange
            var      data    = Enumerable.Range(byte.MinValue, byte.MaxValue).Select(value => (byte)value).ToArray();
            IPlcItem plcItem = new BytesPlcItem(dataBlock: 1234, position: 10, initialValue: data);             //! Explicitly cast this to 'IPlcItem' so that its 'Value' is a 'BitCollection'.

            // Act
            IPlcItem clonedPlcItem = plcItem.Clone();

            // Assert
            Assert.True(plcItem.Equals(clonedPlcItem));
            Assert.False(Object.ReferenceEquals(plcItem, clonedPlcItem));
            Assert.True(plcItem.Value.Equals(clonedPlcItem.Value));
        }
        public void Check_WriteItemsInOrderAsync_Throws()
        {
            // Arrange
            var items = new IPlcItem[]
            {
                new BytePlcItem(0, 0),
                new BytePlcItem(0, 0),
                new BytePlcItem(0, 0),
            };
            var plc = Mock.Of <IPlc>();

            Mock.Get(plc)
            .SetupSequence(p => p.WriteItemsAsync(It.IsAny <ICollection <IPlcItem> >(), CancellationToken.None))
            .ReturnsAsync(true)
            .Returns(() => throw new WritePlcException(new IPlcItem[0], new (IPlcItem FailedItem, string ErrorMessage)[0]))
Пример #9
0
        /// <summary>
        /// Adds the <paramref name="plcItem"/> to the internal collection of monitored <see cref="IPlcItem"/>s.
        /// </summary>
        /// <param name="plcItem"> The additional plc item to monitor. </param>
        public void AddPlcItem(IPlcItem plcItem)
        {
            lock (_lock)
            {
                // Don't allow the same item twice.
                //! The comparison must be done via referential equals, because the items themselves implement IEquatable and therefore may be equal, but not the same.
                foreach (var existingPlcItem in _plcItems)
                {
                    if (Object.ReferenceEquals(existingPlcItem, plcItem))
                    {
                        return;
                    }
                }

                _plcItems.Add(plcItem);
            }
        }
Пример #10
0
        /// <summary>
        /// Gets the <see cref="DataRegion"/> for the <paramref name="plcItem"/>.
        /// </summary>
        /// <param name="plcItem"> The <see cref="IPlcItem"/> for which to get the <see cref="DataRegion"/>. </param>
        /// <returns> The matching <see cref="DataRegion"/>. </returns>
        /// <remarks> The <see cref="DataRegion"/> will automatically be extended to satisfy the range of the <paramref name="plcItem"/>. </remarks>
        private DataRegion GetDataRegion(IPlcItem plcItem)
        {
            DataRegion dataRegion;

            switch (plcItem.Type)
            {
            case PlcItemType.Input:
            {
                dataRegion = this.InputDataHolder.Value;
                break;
            }

            case PlcItemType.Output:
            {
                dataRegion = this.OutputDataHolder.Value;
                break;
            }

            case PlcItemType.Flags:
            {
                dataRegion = this.FlagsDataHolder.Value;
                break;
            }

            default:
            case PlcItemType.Data:
            {
                lock (_dataBlockLock)
                {
                    if (!this.DataBlockDataHolders.TryGetValue(plcItem.DataBlock, out dataRegion))
                    {
                        dataRegion = new DataRegion();
                        this.DataBlockDataHolders.Add(plcItem.DataBlock, dataRegion);
                    }
                }
                break;
            }
            }

            dataRegion.TryExtend((uint)(plcItem.Position * 8 + (byte)plcItem.BitPosition + plcItem.Value.Length));
            return(dataRegion);
        }
Пример #11
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="plc"> <see cref="IPlc"/> instance used to poll for changes in the monitored <see cref="IPlcItem"/>s. </param>
        /// <param name="plcItem"> The first <see cref="IPlcItem"/> to monitor. </param>
        /// <param name="pollingFrequency"> The frequency at which to poll for changes. </param>
        public PlcItemMonitorHandler(IPlc plc, IPlcItem plcItem, TimeSpan pollingFrequency)
        {
            // Save parameters.
            _plc = plc;
            _pollingFrequency = pollingFrequency;

            // Initialize fields.
            _lock = new object();
            //_cancellationLock = new object();

            // Create a placeholder plc item that is used to update the value.
            _placeholderItem = plcItem.Clone($"PLACEHOLDER_{plcItem.GetHashCode()}");

            // Add the passed plc item as first one to the internal collection.
            _plcItems = new List <IPlcItem>()
            {
                plcItem
            };

            // Start monitoring.
            this.Start();
        }
Пример #12
0
        /// <inheritdoc />
        public void MonitorItem(IPlcItem plcItem)
        {
            // The hash code must consist of the hash from the plc item and the polling frequency.
            var plcItemHash = plcItem.GetHashCode();
            var pollingFrequency = _plcItemMonitorConfigurations.GetPollingFrequencyForPlcItem(plcItem);
            var hashValue = new { plcItemHash, pollingFrequency }.GetHashCode();

            lock (_lock)
            {
                // Check if such a handler exist.
                if (_plcItemHandlers.TryGetValue(hashValue, out var handler))
                {
                    // YES: Just add the item.
                    handler.AddPlcItem(plcItem);
                }
                else
                {
                    // NO: Create and save the handler.
                    handler = new PlcItemMonitorHandler(_plc, plcItem, pollingFrequency);
                    _plcItemHandlers.Add(hashValue, handler);
                }
            }
        }
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="plcItem"> The <see cref="IPlcItem"/> that changed. </param>
 /// <param name="oldValue"> The old value of the plc item. </param>
 /// <param name="newValue"> The new value of the plc item. </param>
 public PlcItemChangedEventArgs(IPlcItem plcItem, TValue oldValue, TValue newValue)
 {
     this.PlcItem  = plcItem;
     this.OldValue = oldValue;
     this.NewValue = newValue;
 }
Пример #14
0
        /// <summary>
        /// Reads the value of the <paramref name="plcItem"/> from the plc.
        /// </summary>
        /// <param name="plc"> The extended <see cref="IPlcItem"/> instance. </param>
        /// <typeparam name="TValue"> The type of the <see cref="IPlcItem{TValue}.Value"/>. </typeparam>
        /// <param name="plcItem"> The <see cref="IPlcItem{TValue}"/> to read. </param>
        /// <param name="cancellationToken"> An optional <see cref="CancellationToken"/> for cancelling the read operation. </param>
        /// <returns> An awaitable task containing the result as <typeparamref name="TValue"/>. </returns>
        /// <exception cref="ReadPlcException"> Thrown if an exception occurred while reading. </exception>
        public static async Task <TValue> ReadItemAsync <TValue>(this IPlc plc, IPlcItem <TValue> plcItem, CancellationToken cancellationToken = default)
        {
            await plc.ReadItemAsync(plcItem as IPlcItem, cancellationToken);

            return(plcItem.Value);
        }
Пример #15
0
        /// <summary>
        /// Reads the value of the <paramref name="plcItem"/> from the plc.
        /// </summary>
        /// <param name="plc"> The extended <see cref="IPlcItem"/> instance. </param>
        /// <param name="plcItem"> The <see cref="IPlcItem"/> to read. </param>
        /// <param name="cancellationToken"> An optional <see cref="CancellationToken"/> for cancelling the read operation. </param>
        /// <returns> An awaitable task containing the result as <see cref="Byte"/> array. </returns>
        /// <exception cref="ReadPlcException"> Thrown if an exception occurred while reading. </exception>
        public static async Task <BitCollection> ReadItemAsync(this IPlc plc, IPlcItem plcItem, CancellationToken cancellationToken = default)
        {
            await plc.ReadItemsAsync(new[] { plcItem }, cancellationToken);

            return(plcItem.Value);
        }
Пример #16
0
 /// <summary>
 /// Writes the <paramref name="plcItem"/> to the plc and afterwards reads and compares the written data for validation.
 /// </summary>
 /// <param name="plc"> The extended <see cref="IPlcItem"/> instance. </param>
 /// <param name="plcItem"> The <see cref="IPlcItem"/> to write. </param>
 /// <param name="cancellationToken"> An optional <see cref="CancellationToken"/> for cancelling the write operation. </param>
 /// <returns> An awaitable task yielding <c>True</c> on success, otherwise <c>False</c>. </returns>
 /// <exception cref="WritePlcException"> Thrown if an exception occurred while writing. </exception>
 public static async Task <bool> WriteItemWithValidationAsync(this IPlc plc, IPlcItem plcItem, CancellationToken cancellationToken = default)
 => await plc.WriteItemsWithValidationAsync(new[] { plcItem }, cancellationToken);
        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();
            }
        }
        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();
            }
        }
Пример #19
0
        private static IEnumerable <AGL4.DATA_RW40> CreateAgLinkItems(IPlcItem plcItem, PlcItemUsageType usageType)
        {
            var agLinkItem = new AGL4.DATA_RW40
            {
                Offset = plcItem.Position
            };

            switch (plcItem.Type)
            {
            case PlcItemType.Input:
                agLinkItem.DBNr   = 0;
                agLinkItem.OpArea = AGL4.AREA_IN;
                break;

            case PlcItemType.Output:
                agLinkItem.DBNr   = 0;
                agLinkItem.OpArea = AGL4.AREA_OUT;
                break;

            case PlcItemType.Flags:
                agLinkItem.DBNr   = 0;
                agLinkItem.OpArea = AGL4.AREA_FLAG;
                break;

            default:
            case PlcItemType.Data:
                agLinkItem.DBNr   = plcItem.DataBlock;
                agLinkItem.OpArea = AGL4.AREA_DATA;
                break;
            }

            if (plcItem.Value.HandlesFullBytes || (usageType == PlcItemUsageType.Read && plcItem.Value.Length > 1))
            {
                agLinkItem.OpType = AGL4.TYP_BYTE;
                agLinkItem.BitNr  = 0;
                agLinkItem.OpAnz  = (ushort)DataHelper.GetByteAmountForBits(plcItem.Value.Length);
                if (usageType == PlcItemUsageType.Write)
                {
                    agLinkItem.B = plcItem.Value;
                }
                else
                {
                    agLinkItem.B = new byte[agLinkItem.OpAnz];
                }

                yield return(agLinkItem);
            }
            else
            {
                agLinkItem.OpType = AGL4.TYP_BIT;
                agLinkItem.OpAnz  = 1;

                for (byte bitPosition = 0; bitPosition < plcItem.Value.Length; bitPosition++)
                {
                    var bitAgLinkItem = agLinkItem;                     // Value type will be copied on assignment.
                    bitAgLinkItem.BitNr = (ushort)(bitPosition + plcItem.BitPosition);
                    if (usageType == PlcItemUsageType.Write)
                    {
                        // Get the relevant bit of this item and set the AGLink byte accordingly.
                        bitAgLinkItem.B = new byte[] { plcItem.Value[bitPosition] ? (byte)1 : (byte)0 };
                    }
                    else
                    {
                        bitAgLinkItem.B = new byte[1];
                    }

                    yield return(bitAgLinkItem);
                }
            }
        }
Пример #20
0
 public ReadPlcItemWrapper(IPlcItem plcItem)
 {
     this.PlcItem = plcItem;
     this.Start   = -1;
     this.Amount  = -1;
 }
Пример #21
0
        /// <summary>
        /// Reads the <paramref name="plcItem"/> from the underlying <see cref="DataRegion"/>s.
        /// </summary>
        /// <param name="plcItem"> The <see cref="IPlcItem"/> to read. </param>
        /// <returns> The value as <see cref="bool"/> array. </returns>
        internal bool[] Read(IPlcItem plcItem)
        {
            var dataHolder = this.GetDataRegion(plcItem);

            return(dataHolder.Read(plcItem.Position * 8 + (byte)plcItem.BitPosition, (int)plcItem.Value.Length));
        }
Пример #22
0
 public void Deconstruct(out IPlcItem plcItem, out int start, out int amount)
 {
     plcItem = this.PlcItem;
     start   = this.Start;
     amount  = this.Amount;
 }
Пример #23
0
        /// <summary>
        /// Writes the <paramref name="plcItem"/> to the underlying <see cref="DataRegion"/>s.
        /// </summary>
        /// <param name="plcItem"> The <see cref="IPlcItem"/> to write. </param>
        /// <returns> <c>True</c> on success, otherwise <c>False</c>. </returns>
        internal bool Write(IPlcItem plcItem)
        {
            var dataHolder = this.GetDataRegion(plcItem);

            return(dataHolder.Write(plcItem.Value, (uint)(plcItem.Position * 8 + (byte)plcItem.BitPosition)));
        }