コード例 #1
0
        private async Task PerformWriteAsync(ICollection <IPlcItem> plcItems, CancellationToken cancellationToken)
        {
            const PlcItemUsageType usageType = PlcItemUsageType.Write;
            var underlyingPlc = AgLinkPlc.VerifyConnectivity(this, plcItems, usageType);

            // Create the mapping.
            var(mapping, allAgLinkItems) = AgLinkPlc.CreateMappingAndAgLinkItems(plcItems, usageType);

            // Write to the plc.
            var writeResult = await Task.Run(() => underlyingPlc.WriteMixEx(allAgLinkItems, allAgLinkItems.Length), cancellationToken);

            // Verify the total result.
            //! Ignore the total result and inspect the result of each item individually.
            var result = AgLinkPlc.ConvertToAgLinkResult(writeResult);

            //if (result != AgLinkResult.Success)
            //{
            //	var errorMessage = AgLinkPlc.ErrorMapping.GetErrorMessageForCode(writeResult);
            //	var items = plcItems.Select(item => (item, "General writing error.")).ToArray();
            //	throw new WritePlcException(new IPlcItem[0], items, $"Could not write any items to {this:LOG}. AGLink returned error code '{result}' ({errorMessage}).");
            //}

            // Verify the result of all items.
            var(validItems, failedItems) = AgLinkPlc.VerifyPlcItemResults(mapping, allAgLinkItems, false);
            if (failedItems.Any())
            {
                throw new WritePlcException(validItems, failedItems, $"Some of the items couldn't be written. See the '{nameof(ReadOrWritePlcException.FailedItems)}' property for further information.");
            }
        }
コード例 #2
0
        private async Task PerformReadAsync(ICollection <IPlcItem> plcItems, CancellationToken cancellationToken)
        {
            const PlcItemUsageType usageType = PlcItemUsageType.Read;
            var underlyingPlc = AgLinkPlc.VerifyConnectivity(this, plcItems, usageType);

            // Create the mapping.
            var(mapping, allAgLinkItems) = AgLinkPlc.CreateMappingAndAgLinkItems(plcItems, usageType);

            // Read from the plc.
            //! The return value may be zero even if some or all of the items failed. To get the real result the 'Result' property of the AGLink item (AGL4.DATA_RW40.Result) must be checked.
            var readResult = await Task.Run(() => underlyingPlc.ReadMixEx(allAgLinkItems, allAgLinkItems.Length), cancellationToken);

            // Verify the total result.
            //! Ignore the total result and inspect the result of each item individually.
            var result = AgLinkPlc.ConvertToAgLinkResult(readResult);

            //if (result != AgLinkResult.Success)
            //{
            //	var errorMessage = AgLinkPlc.ErrorMapping.GetErrorMessageForCode(readResult);
            //	var items = plcItems.Select(item => (item, "General reading error.")).ToArray();
            //	throw new ReadPlcException(new IPlcItem[0], items, $"Could not read any items from {this:LOG}. AGLink returned error code '{result}' ({errorMessage}).");
            //}

            // Verify the result of all items and transfer the value.
            var(validItems, failedItems) = AgLinkPlc.VerifyPlcItemResults(mapping, allAgLinkItems, true);
            if (failedItems.Any())
            {
                throw new ReadPlcException(validItems, failedItems, $"Some of the items couldn't be read. See the '{nameof(ReadOrWritePlcException.FailedItems)}' property for further information.");
            }
        }
コード例 #3
0
ファイル: Plc.cs プロジェクト: LittleGitPhoenix/Data.Plc
 /// <summary>
 /// Asynchronously reads or writes the <paramref name="plcItems"/> to / from the plc.
 /// </summary>
 /// <param name="plcItems"> The <see cref="IPlcItem"/>s to write. </param>
 /// <param name="usageType"> The <see cref="PlcItemUsageType"/> defining whether a read or a write operation is performed. </param>
 /// <param name="cancellationToken"> A <see cref="CancellationToken"/> for cancelling the operation. </param>
 protected internal abstract Task PerformReadWriteAsync(ICollection <IPlcItem> plcItems, PlcItemUsageType usageType, CancellationToken cancellationToken);
コード例 #4
0
ファイル: Plc.cs プロジェクト: LittleGitPhoenix/Data.Plc
        private async Task <bool> ExecuteReadWriteAsync(ICollection <IPlcItem> plcItems, PlcItemUsageType usageType, CancellationToken cancellationToken)
        {
            // Allow only items that have a length greater than zero. This is mostly needed for dynamic items.
            plcItems = plcItems.Where(item => item.Value.Length > 0).ToList();
            if (!plcItems.Any())
            {
                return(true);
            }

            var cancellationTokenSource = this.BuildLinkedTokenSource(cancellationToken);

            cancellationToken = cancellationTokenSource.Token;
            try
            {
                while (true)
                {
                    try
                    {
                        cancellationToken.ThrowIfCancellationRequested();

                        lock (_connectionStateChangeLock)
                        {
                            // If no active connection is available, then throw an exception.

                            /*!
                             * This means, that reading/writing items while the plc is not connected, will result in the calling function to indefinitely wait,
                             * because no reconnect will be made when the connection was deliberately not established.
                             */
                            if (this.ConnectionState == PlcConnectionState.Disconnected)
                            {
                                var itemDescriptions = Plc.GetPlcItemDescription(plcItems);
                                throw new NotConnectedPlcException($"Cannot {usageType.ToString().ToLower()} the plc items ({itemDescriptions}) because {this:LOG} is not connected. All items will be put on hold.");
                            }
                        }

                        await Task.Run(() => this.PerformReadWriteAsync(plcItems, usageType, cancellationToken), cancellationToken);

                        break;
                    }
                    // This handles task cancellation.
                    catch (OperationCanceledException)
                    {
                        if (_disposeToken.IsCancellationRequested)
                        {
                            if (usageType == PlcItemUsageType.Read)
                            {
                                throw new DisposedReadPlcException(plcItems);
                            }
                            else
                            {
                                throw new DisposedWritePlcException(plcItems);
                            }
                        }
                        return(false);
                    }
                    // Handle not connected exceptions by trying to re-connect and halting the items until a connection was established.
                    catch (NotConnectedPlcException ex)
                    {
                        this.Logger.Error(ex.Message);

                        // Create a cancellation token source for this handle callback that is linked to the external one.
                        var sleepCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
                        _waitQueue.Enqueue(sleepCancellationTokenSource);
                        var sleepCancellationToken = sleepCancellationTokenSource.Token;

                        // Check if a reconnection could be useful.
                        lock (_connectionStateChangeLock)
                        {
                            // Don't reconnect if the connection is disconnected to begin with.
                            if (this.ConnectionState != PlcConnectionState.Disconnected)
                            {
                                this.OnInterrupted(executeReconnect: true);
                            }
                        }

                        // Indefinitely wait until the task gets canceled.
                        try
                        {
                            await Task.Delay(Timeout.Infinite, sleepCancellationToken);
                        }
                        catch (OperationCanceledException)
                        {
                            // Throw special dispose exception if the dispose token was canceled.
                            if (_disposeToken.IsCancellationRequested)
                            {
                                if (usageType == PlcItemUsageType.Read)
                                {
                                    throw new DisposedReadPlcException(plcItems);
                                }
                                else
                                {
                                    throw new DisposedWritePlcException(plcItems);
                                }
                            }
                            // Stop execution if the original (external) token was canceled.
                            else if (cancellationToken.IsCancellationRequested)
                            {
                                return(false);
                            }
                            // In all other cases just keep going.
                            else
                            {
                                /* ignore */
                            }
                        }

                        this.Logger.Info($"The previously suspended plc items of {this:LOG} will now be handled again.");
                    }
                    // Throw on read or write exceptions.
                    catch (ReadOrWritePlcException)
                    {
                        throw;
                    }
                }

                return(true);
            }
            finally
            {
                cancellationTokenSource.Dispose();
                cancellationTokenSource = null;
#if DEBUG
                this.LinkedTokenWasCanceled();
#endif
            }
        }
コード例 #5
0
ファイル: MockPlc.cs プロジェクト: LittleGitPhoenix/Data.Plc
 /// <inheritdoc />
 protected override async Task PerformReadWriteAsync(ICollection <IPlcItem> plcItems, PlcItemUsageType usageType, CancellationToken cancellationToken)
 {
     try
     {
         if (usageType == PlcItemUsageType.Read)
         {
             await this.PerformReadAsync(plcItems, cancellationToken);
         }
         else
         {
             await this.PerformWriteAsync(plcItems, cancellationToken);
         }
     }
     catch (NullReferenceException)
     {
         throw;
     }
 }
コード例 #6
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);
                }
            }
        }
コード例 #7
0
        private static (ICollection <ReadPlcItemWrapper> Mapping, AGL4.DATA_RW40[] AllAgLinkItems) CreateMappingAndAgLinkItems(ICollection <IPlcItem> plcItems, PlcItemUsageType usageType)
        {
            var mapping = plcItems
                          .Select(plcItem => new ReadPlcItemWrapper(plcItem))
                          .ToArray()
            ;

            // Create all needed AGLink items.
            //! Directly storing the AGLink items in the ReadPlcItemWrapper instance won't work, as AGL4.DATA_RW40 is a struct that would consequently be copied on assignment.
            //! Therefore the ReadPlcItemWrapper only contains the start position and the amount of items in the returned AGLink items array.
            var previousAmount = 0;
            var allAgLinkItems = mapping
                                 .SelectMany
                                 (
                readPlcItemWrapper =>
            {
                var agLinkItems = AgLinkPlc.CreateAgLinkItems(readPlcItemWrapper.PlcItem, usageType).ToArray();

                readPlcItemWrapper.Start  = (previousAmount += agLinkItems.Length) - 1;
                readPlcItemWrapper.Amount = agLinkItems.Length;

                return(agLinkItems);
            }
                                 )
                                 .ToArray()
            ;

            return(mapping, allAgLinkItems);
        }
コード例 #8
0
        private static IAGLink4 VerifyConnectivity(AgLinkPlc plc, ICollection <IPlcItem> plcItems, PlcItemUsageType usageType)
        {
            var underlyingPlc = plc.UnderlyingPlc;

            if (underlyingPlc is null)
            {
                var itemDescriptions = Plc.GetPlcItemDescription(plcItems);
                throw new NotConnectedPlcException($"Cannot {usageType.ToString().ToLower()} the plc items ({itemDescriptions}) because {plc:LOG} is not connected. All items will be put on hold.");
            }

            return(underlyingPlc);
        }