public override void ProcessCapturesInternal(IEnumerable <FileInfo> captureFiles)
        {
            var storeCapturesBlock                = this.CreateStoreActionBlock <PmCaptureBase>(out Task storeCapturesAwait);
            var storeL3ConversationBlock          = this.CreateStoreActionBlock <L3Conversation>(out Task storeL3Await);
            var storeL3StatisticsBlock            = this.CreateStoreActionBlock <L3ConversationStatistics>(out var storeL3StatisticsAwait);
            var storeL4ConversationBlock          = this.CreateStoreActionBlock <L4Conversation>(out Task storeL4Await);
            var storeL4StatisticsBlock            = this.CreateStoreActionBlock <L4ConversationStatistics>(out Task storeL4StatisticsAwait);
            var storeL7ConversationBlock          = this.CreateStoreActionBlock <L7Conversation>(out Task storeL7Await);
            var storeL7StatisticsBlock            = this.CreateStoreActionBlock <L7ConversationStatistics>(out Task storeL7StatisticsAwait);
            var storeFramesBlock                  = this.CreateStoreActionBlock <PmFrameBase>(out Task storeFramesAwait);
            var storeL7PdusBlock                  = this.CreateStoreActionBlock <L7PDU>(out Task storeL7PdusAwait);
            var storePmCaptureL3ConversationBlock = this.CreateStoreToJunctionActionBlock <PmCaptureL3Conversation>(out Task pmCaptureL3ConversationAwait);
            var storePmCaptureL4ConversationBlock = this.CreateStoreToJunctionActionBlock <PmCaptureL4Conversation>(out Task pmCaptureL4ConversationAwait);
            var storePmCaptureL7ConversationBlock = this.CreateStoreToJunctionActionBlock <PmCaptureL7Conversation>(out Task pmCaptureL7ConversationAwait);

            var captureProcessorBlock = this.CaptureProcessorBlockFactory.Create();

            captureProcessorBlock.LinkTo(storeCapturesBlock, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });

            var l3L4ConversationTracker = this.L3L4ConversationTrackerBlockFactory.Create();

            captureProcessorBlock.LinkTo(l3L4ConversationTracker, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });

            l3L4ConversationTracker.LinkTo(storeL3ConversationBlock, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });
            l3L4ConversationTracker.LinkTo(storePmCaptureL3ConversationBlock, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });
            l3L4ConversationTracker.LinkTo(storeL3StatisticsBlock, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });
            l3L4ConversationTracker.LinkTo(storePmCaptureL4ConversationBlock, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });
            l3L4ConversationTracker.LinkTo(storeL4StatisticsBlock, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });

            var l7ConversationTrackerBlock = this.L7ConversationTrackerBlockFactory.Create();

            l3L4ConversationTracker.LinkTo <PmFrameBase>(l7ConversationTrackerBlock);
            l3L4ConversationTracker.LinkTo <L4ConversationExtended>(l7ConversationTrackerBlock);

            l3L4ConversationTracker.Completion.ContinueWith(t =>
            {
                if (t.IsFaulted)
                {
                    ((IDataflowBlock)l7ConversationTrackerBlock).Fault(t.Exception);
                }
                else
                {
                    l7ConversationTrackerBlock.Complete();
                }
            });

            var l7ConversationBroadcaster = new BroadcastBlock <L7Conversation>(null);

            l7ConversationTrackerBlock.LinkTo(l7ConversationBroadcaster, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });
            l7ConversationTrackerBlock.LinkTo(storeFramesBlock, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });
            l7ConversationTrackerBlock.LinkTo(storeL7StatisticsBlock, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });
            l7ConversationTrackerBlock.LinkTo(storeL4ConversationBlock, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });
            l7ConversationTrackerBlock.LinkTo(storeL7PdusBlock, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });
            l7ConversationTrackerBlock.LinkTo(storePmCaptureL7ConversationBlock, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });

            l7ConversationBroadcaster.LinkTo(storeL7ConversationBlock, new DataflowLinkOptions()
            {
                PropagateCompletion = true
            });
            L7DvbS2GseDecapsulatorBlock l7DvbS2GseDecapsulator = null;
            BufferBlock <PmFrameBase>   decapsulatedFrames     = null;

            if (NetfoxSettings.Default.DecapsulateGseOverUdp)
            {
                l7DvbS2GseDecapsulator = new L7DvbS2GseDecapsulatorBlock();
                decapsulatedFrames     = new BufferBlock <PmFrameBase>();
                l7ConversationBroadcaster.LinkTo(l7DvbS2GseDecapsulator, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });
                l7DvbS2GseDecapsulator.LinkTo(decapsulatedFrames, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });
            }

            foreach (var captureFile in captureFiles)
            {
                captureProcessorBlock.SendAsync(captureFile).Wait();
            }
            captureProcessorBlock.Complete();

            Task.WhenAll(
                storeCapturesAwait,
                storeL3Await,
                storeL3StatisticsAwait,
                storeL4Await,
                storeL4StatisticsAwait,
                storeL7Await,
                storeL7StatisticsAwait,
                storeFramesAwait,
                storeL7PdusAwait,
                pmCaptureL3ConversationAwait,
                pmCaptureL4ConversationAwait,
                pmCaptureL7ConversationAwait
                ).Wait();
            if (!NetfoxSettings.Default.DecapsulateGseOverUdp)
            {
                return;
            }

            l7DvbS2GseDecapsulator?.Completion.Wait();
            if (decapsulatedFrames != null && decapsulatedFrames.TryReceiveAll(out var frames))
            {
                this.ProcessFramesInternal(frames);
            }
        }
        /// <summary>
        /// Create pipeline of custom TPL blocks and process provided <paramref name="frames"/>.
        /// Results of parsing and tracking are stored using custom ActionBlocks obtained from
        /// <see cref="CreateStoreActionBlock{T}"/> and <see cref="CreateStoreToJunctionActionBlock{T}"/> methods. Should
        /// any encapsulated frames (tunnels) be found, they are decapsulated and stored in local variable
        /// <c>BufferBlock&lt;PmFrameBase&gt; decapsulatedFrames</c>. In case of traffic tunneled inside several
        /// layers of tunnels, body of this method iterates to process each tunnel.
        /// </summary>
        /// <param name="frames">Frames to be processed.</param>
        /// <seealso cref="ProcessCapturesInternal"/>
        private void ProcessFramesInternal([Required] IList <PmFrameBase> frames)
        {
            while (!frames.IsNullOrEmpty())
            {
                var storeL3ConversationBlock          = this.CreateStoreActionBlock <L3Conversation>(out Task storeL3Await);
                var storeL3StatisticsBlock            = this.CreateStoreActionBlock <L3ConversationStatistics>(out var storeL3StatisticsAwait);
                var storeL4ConversationBlock          = this.CreateStoreActionBlock <L4Conversation>(out Task storeL4Await);
                var storeL4StatisticsBlock            = this.CreateStoreActionBlock <L4ConversationStatistics>(out Task storeL4StatisticsAwait);
                var storeL7ConversationBlock          = this.CreateStoreActionBlock <L7Conversation>(out Task storeL7Await);
                var storeL7StatisticsBlock            = this.CreateStoreActionBlock <L7ConversationStatistics>(out Task storeL7StatisticsAwait);
                var storeFramesBlock                  = this.CreateStoreActionBlock <PmFrameBase>(out Task storeFramesAwait);
                var storeL7PdusBlock                  = this.CreateStoreActionBlock <L7PDU>(out Task storeL7PdusAwait);
                var storePmCaptureL3ConversationBlock = this.CreateStoreToJunctionActionBlock <PmCaptureL3Conversation>(out Task pmCaptureL3ConversationAwait);
                var storePmCaptureL4ConversationBlock = this.CreateStoreToJunctionActionBlock <PmCaptureL4Conversation>(out Task pmCaptureL4ConversationAwait);
                var storePmCaptureL7ConversationBlock = this.CreateStoreToJunctionActionBlock <PmCaptureL7Conversation>(out Task pmCaptureL7ConversationAwait);

                var l3L4ConversationTracker = this.L3L4ConversationTrackerBlockFactory.Create();

                l3L4ConversationTracker.LinkTo(storeL3ConversationBlock, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });
                l3L4ConversationTracker.LinkTo(storePmCaptureL3ConversationBlock, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });
                l3L4ConversationTracker.LinkTo(storeL3StatisticsBlock, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });
                l3L4ConversationTracker.LinkTo(storePmCaptureL4ConversationBlock, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });

                var l7ConversationTrackerBlock = this.L7ConversationTrackerBlockFactory.Create();
                l3L4ConversationTracker.LinkTo <PmFrameBase>(l7ConversationTrackerBlock);
                l3L4ConversationTracker.LinkTo(storeL4StatisticsBlock, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });
                l3L4ConversationTracker.LinkTo <L4ConversationExtended>(l7ConversationTrackerBlock);

                l3L4ConversationTracker.Completion.ContinueWith(t =>
                {
                    if (t.IsFaulted)
                    {
                        ((IDataflowBlock)l7ConversationTrackerBlock).Fault(t.Exception);
                    }
                    else
                    {
                        l7ConversationTrackerBlock.Complete();
                    }
                });

                var l7ConversationBroadcaster = new BroadcastBlock <L7Conversation>(null);
                l7ConversationTrackerBlock.LinkTo(l7ConversationBroadcaster, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });
                l7ConversationTrackerBlock.LinkTo(storeFramesBlock, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });
                l7ConversationTrackerBlock.LinkTo(storeL7StatisticsBlock, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });
                l7ConversationTrackerBlock.LinkTo(storeL4ConversationBlock, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });
                l7ConversationTrackerBlock.LinkTo(storeL7PdusBlock, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });
                l7ConversationTrackerBlock.LinkTo(storePmCaptureL7ConversationBlock, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });

                l7ConversationBroadcaster.LinkTo(storeL7ConversationBlock, new DataflowLinkOptions()
                {
                    PropagateCompletion = true
                });
                L7DvbS2GseDecapsulatorBlock l7DvbS2GseDecapsulator = null;
                BufferBlock <PmFrameBase>   decapsulatedFrames     = null;
                if (NetfoxSettings.Default.DecapsulateGseOverUdp)
                {
                    l7DvbS2GseDecapsulator = new L7DvbS2GseDecapsulatorBlock();
                    decapsulatedFrames     = new BufferBlock <PmFrameBase>();
                    l7ConversationBroadcaster.LinkTo(l7DvbS2GseDecapsulator, new DataflowLinkOptions()
                    {
                        PropagateCompletion = true
                    });
                    l7DvbS2GseDecapsulator.LinkTo(decapsulatedFrames, new DataflowLinkOptions()
                    {
                        PropagateCompletion = true
                    });
                }

                foreach (var frame in frames)
                {
                    frame.IndexFrame(null).Wait(); // NOTE: IndexFrame called with null is insecure. Parameter `IPmCaptureProcessorBlockBase captureProcessorBlockBase`
                    // is inside IndexFrame method currently used for parsing GRE and IP6in4. But here, after reassembling and decapsulation of GSE packets, we do not
                    // have capture processor available. In order to instantiate new one, we would need `FileInfo fileInfo` of capture file, which we do not have.
                    l3L4ConversationTracker.SendAsync(frame).Wait();
                }
                l3L4ConversationTracker.Complete();

                Task.WhenAll(
                    storeL3Await,
                    storeL3StatisticsAwait,
                    storeL4Await,
                    storeL4StatisticsAwait,
                    storeL7Await,
                    storeL7StatisticsAwait,
                    storeFramesAwait,
                    storeL7PdusAwait,
                    pmCaptureL3ConversationAwait,
                    pmCaptureL4ConversationAwait,
                    pmCaptureL7ConversationAwait
                    ).Wait();

                if (!NetfoxSettings.Default.DecapsulateGseOverUdp)
                {
                    return;
                }

                l7DvbS2GseDecapsulator?.Completion.Wait();
                decapsulatedFrames?.TryReceiveAll(out frames);
            }
        }