Exemple #1
0
        private InstantDataTarget InternalUnregisterInstantDataTarget(Guid registrationToken)
        {
            lock (this.instantStreamReaders)
            {
                for (int index = this.instantStreamReaders.Count - 1; index >= 0; index--)
                {
                    // Unregister the target from the instant stream reader
                    InstantDataTarget target = this.instantStreamReaders[index].UnregisterInstantDataTarget(registrationToken);

                    if (target != null)
                    {
                        // If the instant stream reader now has no data providers, remove it from the collection
                        if (!this.instantStreamReaders[index].HasAdaptingDataProviders)
                        {
                            this.instantStreamReaders.RemoveAt(index);
                        }

                        // If there's no instant stream readers, remove the index view
                        if (this.instantStreamReaders.Count <= 0)
                        {
                            this.instantIndexView = null;
                        }

                        return(target);
                    }
                }

                return(null);
            }
        }
        /// <summary>
        /// Reads instant data from the stream at the given cursor time and pushes it to all registered adapting data providers.
        /// </summary>
        /// <param name="reader">The simple reader that will read the data.</param>
        /// <param name="cursorTime">The cursor time at which to read the data.</param>
        /// <param name="indexCache">The stream reader's index cache.</param>
        public void ReadInstantData(ISimpleReader reader, DateTime cursorTime, ObservableKeyedCache <DateTime, IndexEntry> indexCache)
        {
            // Get the index of the data, given the cursor time
            int index = IndexHelper.GetIndexForTime(cursorTime, indexCache?.Count ?? 0, (idx) => indexCache[idx].OriginatingTime, this.CursorEpsilon);

            T          data       = default;
            IndexEntry indexEntry = default;

            if (index >= 0)
            {
                // Get the index entry
                indexEntry = indexCache[index];

                // Read the data
                data = reader.Read <T>(indexEntry);
            }

            // Notify each adapting data provider of the new data
            foreach (IAdaptingInstantDataProvider <T> adaptingInstantDataProvider in this.dataProviders.ToList())
            {
                adaptingInstantDataProvider.PushData(data, indexEntry);
            }

            // Release the reference to the local copy of the data if it's shared
            if (this.isSharedType && data != null)
            {
                (data as IDisposable).Dispose();
            }
        }
Exemple #3
0
        /// <summary>
        /// Reads instant data from the stream at the given cursor time and pushes it to all registered adapting data providers.
        /// </summary>
        /// <param name="reader">The simple reader that will read the data.</param>
        /// <param name="cursorTime">The cursor time at which to read the data.</param>
        /// <param name="indexCache">The stream reader's index cache.</param>
        public void ReadInstantData(ISimpleReader reader, DateTime cursorTime, ObservableKeyedCache <DateTime, IndexEntry> indexCache)
        {
            // Get the index of the data, given the cursor time
            int index = IndexHelper.GetIndexForTime(cursorTime, indexCache?.Count ?? 0, (idx) => indexCache[idx].OriginatingTime, this.CursorEpsilon);

            T          data       = default;
            IndexEntry indexEntry = default;

            if (index >= 0)
            {
                // Get the index entry
                indexEntry = indexCache[index];

                // Read the data
                data = reader.Read <T>(indexEntry);
            }

            // Notify all registered adapting data providers of the new data.  If the data is Shared<T> then perform a deep clone
            // (which resolves to an AddRef() for this type) for each provider we call.  The providers are responsible for releasing
            // their reference to the data once they're done with it.
            if (this.isSharedType && data != null)
            {
                Parallel.ForEach(this.dataProviders.ToList(), provider => provider.PushData(data.DeepClone <T>(), indexEntry));

                // Release the reference to the local copy of the data
                (data as IDisposable).Dispose();
            }
            else
            {
                Parallel.ForEach(this.dataProviders.ToList(), provider => provider.PushData(data, indexEntry));
            }
        }
Exemple #4
0
        public void Initialize()
        {
            this.basis = new SortedList <double, double>();

            var eventCount = 0;
            var itemCount  = 0;
            NotifyCollectionChangedEventHandler testCollectionChanged = (s, e) => { this.CollectionChangedHandler(e, ref eventCount, ref itemCount); };

            this.test = new ObservableKeyedCache <double, double>((d) => d);
            this.test.DetailedCollectionChanged += testCollectionChanged;

            double[] values = { 15, 65, -1, 2, 44, 100, 123, -456, 0, 10 };
            foreach (var value in values)
            {
                this.basis.Add(value, value);
                this.test.Add(value);
            }

            Assert.AreEqual(eventCount, values.Length);
            Assert.AreEqual(itemCount, values.Length);
            Assert.AreEqual(this.basis.Count, values.Length);
            UnitTestHelper.AssertAreEqual(this.basis.Values, this.test, values.Length);

            this.test.DetailedCollectionChanged -= testCollectionChanged;
        }
Exemple #5
0
        /// <inheritdoc/>
        public void UnregisterStreamValueSubscriber <TTarget>(Guid registrationToken)
        {
            lock (this.publishers)
            {
                foreach (var epsilonTimeIntervalPublishers in this.publishers.Values)
                {
                    foreach (var publisher in epsilonTimeIntervalPublishers)
                    {
                        if (publisher.HasSubscriber(registrationToken))
                        {
                            publisher.UnregisterSubscriber(registrationToken);
                        }
                    }

                    epsilonTimeIntervalPublishers.RemoveAll(publisher => !publisher.HasSubscribers);
                }

                foreach (var epsilonTimeInterval in this.publishers.Keys.ToArray())
                {
                    if (!this.publishers[epsilonTimeInterval].Any())
                    {
                        this.publishers.Remove(epsilonTimeInterval);
                    }
                }
            }

            // If no publishers remain, remove the index view
            if (!this.publishers.Any())
            {
                this.indexView      = null;
                this.indexViewRange = new NavigatorRange(DateTime.MinValue, DateTime.MinValue);
            }
        }
Exemple #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="StreamCache{T}"/> class.
        /// </summary>
        /// <param name="streamName">the name of the stream to read.</param>
        /// <param name="streamAdapter">the stream adapter to convert data from the stream into the type required by clients of this stream reader.</param>
        public StreamCache(string streamName, IStreamAdapter streamAdapter /*, object[] streamAdapterParameters*/)
        {
            if (string.IsNullOrWhiteSpace(streamName))
            {
                throw new ArgumentNullException(nameof(streamName));
            }

            this.StreamName    = streamName;
            this.StreamAdapter = streamAdapter;

            this.pool = PoolManager.Instance.GetPool <T>();

            this.readRequestsInternal = new List <ReadRequest>();
            this.readRequests         = new ReadOnlyCollection <ReadRequest>(this.readRequestsInternal);

            this.bufferLock  = new object();
            this.dataBuffer  = new List <Message <T> >(1000);
            this.indexBuffer = new List <StreamCacheEntry>(1000);

            var itemComparer = Comparer <Message <T> > .Create((m1, m2) => m1.OriginatingTime.CompareTo(m2.OriginatingTime));

            var indexComarer = Comparer <StreamCacheEntry> .Create((i1, i2) => i1.OriginatingTime.CompareTo(i2.OriginatingTime));

            this.data  = new ObservableKeyedCache <DateTime, Message <T> >(null, itemComparer, m => m.OriginatingTime);
            this.index = new ObservableKeyedCache <DateTime, StreamCacheEntry>(null, indexComarer, ie => ie.OriginatingTime);

            this.instantIndexView = null;

            this.instantStreamReaders = new List <EpsilonInstantStreamReader <T> >();

            if (this.needsDisposing)
            {
                this.data.CollectionChanged += this.OnCollectionChanged;
            }
        }
Exemple #7
0
        /// <summary>
        /// Initializes a new instance of the <see cref="StreamReader{T}"/> class.
        /// </summary>
        /// <param name="streamBinding">Stream binding used to indentify stream.</param>
        /// <param name="useIndex">Indicates stream reader should use index for access.</param>
        public StreamReader(StreamBinding streamBinding, bool useIndex)
        {
            this.streamBinding = streamBinding;
            this.useIndex      = useIndex;
            this.pool          = PoolManager.Instance.GetPool <T>();

            this.readRequestsInternal = new List <Tuple <DateTime, DateTime, uint, Func <DateTime, DateTime> > >();
            this.readRequests         = new ReadOnlyCollection <Tuple <DateTime, DateTime, uint, Func <DateTime, DateTime> > >(this.readRequestsInternal);

            this.bufferLock  = new object();
            this.dataBuffer  = new List <Message <T> >(1000);
            this.indexBuffer = new List <IndexEntry>(1000);

            var itemComparer = Comparer <Message <T> > .Create((m1, m2) => m1.OriginatingTime.CompareTo(m2.OriginatingTime));

            var indexComarer = Comparer <IndexEntry> .Create((i1, i2) => i1.OriginatingTime.CompareTo(i2.OriginatingTime));

            this.data  = new ObservableKeyedCache <DateTime, Message <T> >(null, itemComparer, m => m.OriginatingTime);
            this.index = new ObservableKeyedCache <DateTime, IndexEntry>(null, indexComarer, ie => ie.OriginatingTime);

            if (this.needsDisposing)
            {
                this.data.CollectionChanged += this.OnCollectionChanged;
            }
        }
Exemple #8
0
        public void EmptyTest()
        {
            var basisEmpty = new SortedList <double, double>();
            var testEmpty  = new ObservableKeyedCache <double, double>((d) => d);

            Assert.AreEqual(testEmpty.Count, 0);
            Assert.AreEqual(basisEmpty.Count, testEmpty.Count);
        }
Exemple #9
0
 /// <summary>
 /// Creates a view of the messages identified by the matching parameters and asynchronously fills it in.
 /// View mode can be one of three values:
 ///     Fixed - fixed range based on start and end times
 ///     TailCount - sliding dynamic range that includes the tail of the underlying data based on quantity
 ///     TailRange - sliding dynamic range that includes the tail of the underlying data based on function.
 /// </summary>
 /// <typeparam name="T">The type of the message to read.</typeparam>
 /// <param name="streamBinding">The stream binding indicating which stream to read from.</param>
 /// <param name="viewMode">Mode the view will be created in.</param>
 /// <param name="startTime">Start time of messages to read.</param>
 /// <param name="endTime">End time of messages to read.</param>
 /// <param name="tailCount">Number of messages to included in tail.</param>
 /// <param name="tailRange">Function to determine range included in tail.</param>
 /// <returns>Observable view of data.</returns>
 internal ObservableKeyedCache <DateTime, Message <T> > .ObservableKeyedView ReadStream <T>(
     StreamBinding streamBinding,
     ObservableKeyedCache <DateTime, Message <T> > .ObservableKeyedView.ViewMode viewMode,
     DateTime startTime,
     DateTime endTime,
     uint tailCount,
     Func <DateTime, DateTime> tailRange)
 {
     return(this.GetOrCreateStreamReader <T>(streamBinding.StreamName, streamBinding.StreamAdapter).ReadStream <T>(viewMode, startTime, endTime, tailCount, tailRange));
 }
Exemple #10
0
 /// <inheritdoc />
 public void Dispose()
 {
     lock (this.bufferLock)
     {
         this.summaryCache?.Clear();
         this.summaryCache = null;
         this.summaryDataBuffer?.Clear();
         this.summaryDataBuffer = null;
     }
 }
Exemple #11
0
 /// <summary>
 /// Creates a view of the messages identified by the matching parameters and asynchronously fills it in.
 /// View mode can be one of three values:
 ///     Fixed - fixed range based on start and end times
 ///     TailCount - sliding dynamic range that includes the tail of the underlying data based on quantity
 ///     TailRange - sliding dynamic range that includes the tail of the underlying data based on function
 /// </summary>
 /// <typeparam name="T">The type of the message to read.</typeparam>
 /// <param name="streamBinding">The stream binding inidicating which stream to read from.</param>
 /// <param name="viewMode">Mode the view will be created in</param>
 /// <param name="startTime">Start time of messages to read.</param>
 /// <param name="endTime">End time of messages to read.</param>
 /// <param name="tailCount">Number of messages to included in tail.</param>
 /// <param name="tailRange">Function to determine range included in tail.</param>
 /// <returns>Observable view of data.</returns>
 public ObservableKeyedCache <DateTime, Message <T> > .ObservableKeyedView ReadStream <T>(
     StreamBinding streamBinding,
     ObservableKeyedCache <DateTime, Message <T> > .ObservableKeyedView.ViewMode viewMode,
     DateTime startTime,
     DateTime endTime,
     uint tailCount,
     Func <DateTime, DateTime> tailRange)
 {
     return(this.GetStreamReader <T>(streamBinding, false).ReadStream <T>(viewMode, startTime, endTime, tailCount, tailRange));
 }
Exemple #12
0
        /// <summary>
        /// Initializes a new instance of the <see cref="StreamIntervalProvider{T}"/> class.
        /// </summary>
        /// <param name="streamSource">The stream source.</param>
        public StreamIntervalProvider(StreamSource streamSource)
            : base(streamSource)
        {
            this.StreamAdapter = streamSource.StreamAdapter;

            var itemComparer = Comparer <Message <T> > .Create((m1, m2) => m1.OriginatingTime.CompareTo(m2.OriginatingTime));

            this.data = new ObservableKeyedCache <DateTime, Message <T> >(null, itemComparer, m => m.OriginatingTime);
            this.data.CollectionChanged += this.OnCollectionChanged;
            this.dataBuffer              = new List <Message <T> >(1000);
        }
Exemple #13
0
        /// <summary>
        /// Initializes a new instance of the <see cref="StreamValueProvider{T}"/> class.
        /// </summary>
        /// <param name="streamSource">The stream source.</param>
        public StreamValueProvider(StreamSource streamSource)
            : base(streamSource)
        {
            this.indexView  = null;
            this.publishers = new Dictionary <RelativeTimeInterval, List <IStreamValuePublisher <TSource> > >();

            var indexComparer = Comparer <MessageIndex <TSource> > .Create((i1, i2) => i1.OriginatingTime.CompareTo(i2.OriginatingTime));

            this.index       = new ObservableKeyedCache <DateTime, MessageIndex <TSource> >(null, indexComparer, ie => ie.OriginatingTime);
            this.indexBuffer = new List <MessageIndex <TSource> >(1000);
        }
Exemple #14
0
        /// <inheritdoc />
        public void UnregisterInstantDataTarget(Guid registrationToken)
        {
            this.InternalUnregisterInstantDataTarget(registrationToken);

            // If no instant visualization objects are now using
            // this stream reader, remove the instant index view
            if (this.instantStreamReaders.Count <= 0)
            {
                this.instantIndexView      = null;
                this.currentIndexViewRange = new NavigatorRange(DateTime.MinValue, DateTime.MinValue);
            }
        }
Exemple #15
0
        /// <inheritdoc />
        public ObservableKeyedCache <DateTime, Message <TItem> > .ObservableKeyedView ReadStream <TItem>(
            ObservableKeyedCache <DateTime, Message <TItem> > .ObservableKeyedView.ViewMode viewMode,
            DateTime startTime,
            DateTime endTime,
            uint tailCount,
            Func <DateTime, DateTime> tailRange)
        {
            lock (this.readRequestsInternal)
            {
                this.readRequestsInternal.AddRange(this.ComputeReadRequests(startTime, endTime, false));
            }

            return((this.data as ObservableKeyedCache <DateTime, Message <TItem> >).GetView(viewMode, startTime, endTime, tailCount, tailRange));
        }
Exemple #16
0
        /// <inheritdoc/>
        public void OnInstantViewRangeChanged(TimeInterval viewRange)
        {
            // Check if the navigator view range exceeds the current range of the data index
            if (viewRange.Left < this.currentIndexViewRange.StartTime || viewRange.Right > this.currentIndexViewRange.EndTime)
            {
                // Set a new data index range thats extends to the left and right of the navigator view by the navigator view
                // duration so that we're not constantly needing to initiate an index read every time the navigator moves.
                TimeSpan viewDuration = viewRange.Span;
                this.currentIndexViewRange.SetRange(
                    viewRange.Left > DateTime.MinValue + viewDuration ? viewRange.Left - viewDuration : DateTime.MinValue,
                    viewRange.Right < DateTime.MaxValue - viewDuration ? viewRange.Right + viewDuration : DateTime.MaxValue);

                this.instantIndexView = this.ReadIndex(this.currentIndexViewRange.StartTime, this.currentIndexViewRange.EndTime);
            }
        }
        public void TryGetValueTest()
        {
            double value = 0;
            Assert.IsTrue(this.test.TryGetValue(100, out value));
            Assert.AreEqual(value, 100);
            Assert.IsTrue(this.test.TryGetValue(15, out value));
            Assert.AreEqual(value, 15);
            Assert.IsTrue(this.test.TryGetValue(10, out value));
            Assert.AreEqual(value, 10);
            Assert.IsFalse(this.test.TryGetValue(101, out value));
            Assert.AreEqual(value, 0);

            // Empty test
            var empty = new ObservableKeyedCache<double, double>((d) => d);
            Assert.IsFalse(empty.TryGetValue(0, out value));
        }
Exemple #18
0
        /// <summary>
        /// Initializes a new instance of the <see cref="StreamSummary{TSrc, TDest}"/> class.
        /// </summary>
        /// <param name="streamBinding">Stream binding indicating which stream to summarize.</param>
        /// <param name="interval">The time interval over which summary <see cref="IntervalData"/> values are calculated.</param>
        /// <param name="maxCacheSize">The maximum amount of data to cache before purging older summarized data.</param>
        public StreamSummary(StreamBinding streamBinding, TimeSpan interval, uint maxCacheSize)
        {
            this.streamBinding = streamBinding;
            this.interval      = interval;
            this.maxCacheSize  = maxCacheSize;

            this.summaryDataBuffer = new List <List <IntervalData <TDest> > >();

            this.keySelector = s => Summarizer <TSrc, TDest> .GetIntervalStartTime(s.OriginatingTime, interval);

            this.itemComparer = Comparer <IntervalData <TDest> > .Create((r1, r2) => this.keySelector(r1).CompareTo(this.keySelector(r2)));

            this.summaryCache = new ObservableKeyedCache <DateTime, IntervalData <TDest> >(null, this.itemComparer, this.keySelector);

            this.activeStreamViews  = new Dictionary <Tuple <DateTime, DateTime, uint, Func <DateTime, DateTime> >, ObservableKeyedCache <DateTime, Message <TSrc> > .ObservableKeyedView>();
            this.cachedSummaryViews = new Dictionary <Tuple <DateTime, DateTime, uint, Func <DateTime, DateTime> >, ObservableKeyedCache <DateTime, IntervalData <TDest> > .ObservableKeyedView>();

            // Cache the summarizer (cast to the correct type) to call its methods later on without dynamic binding
            this.summarizer = this.StreamBinding.Summarizer as ISummarizer <TSrc, TDest>;
        }
        /// <summary>
        /// Reads instant data from the stream at the given cursor time and pushes it to all registered adapting data providers.
        /// </summary>
        /// <param name="streamReader">The stream reader that will read the data.</param>
        /// <param name="cursorTime">The cursor time at which to read the data.</param>
        /// <param name="streamCache">The stream reader's cache.</param>
        public void ReadInstantData(IStreamReader streamReader, DateTime cursorTime, ObservableKeyedCache <DateTime, StreamCacheEntry> streamCache)
        {
            // Get the index of the data, given the cursor time
            int index = IndexHelper.GetIndexForTime(cursorTime, streamCache?.Count ?? 0, (idx) => streamCache[idx].OriginatingTime, this.CursorEpsilon);

            T data = default;
            StreamCacheEntry cacheEntry = default;

            if (index >= 0)
            {
                // Get the index entry
                cacheEntry = streamCache[index];

                // Read the data
                data = cacheEntry.Read <T>(streamReader);
            }
            else
            {
                // cache miss, attempt to seek directly while the cache is presumably being populated
                streamReader.Seek(cursorTime + this.CursorEpsilon, true);
                streamReader.OpenStream <T>(this.streamName, (m, e) =>
                {
                    cacheEntry = new StreamCacheEntry(null, e.CreationTime, e.OriginatingTime);
                    data       = m;
                });
                streamReader.MoveNext(out var envelope);
            }

            // Notify each adapting data provider of the new data
            foreach (IAdaptingInstantDataProvider <T> adaptingInstantDataProvider in this.dataProviders.ToList())
            {
                adaptingInstantDataProvider.PushData(data, cacheEntry);
            }

            // Release the reference to the local copy of the data if it's shared
            if (this.isSharedType && data != null)
            {
                (data as IDisposable).Dispose();
            }
        }
Exemple #20
0
        /// <inheritdoc />
        public ObservableKeyedCache <DateTime, IntervalData <TItem> > .ObservableKeyedView ReadSummary <TItem>(
            ObservableKeyedCache <DateTime, IntervalData <TItem> > .ObservableKeyedView.ViewMode viewMode,
            DateTime startTime,
            DateTime endTime,
            uint tailCount,
            Func <DateTime, DateTime> tailRange)
        {
            if (viewMode == ObservableKeyedCache <DateTime, IntervalData <TItem> > .ObservableKeyedView.ViewMode.TailRange)
            {
                // Just read directly from the stream with the same tail range in live mode
                this.ReadStream(tailRange);
            }
            else if (viewMode == ObservableKeyedCache <DateTime, IntervalData <TItem> > .ObservableKeyedView.ViewMode.TailCount)
            {
                // We should read enough of the stream to generate the last tailCount intervals. So take the product of our
                // summarization interval and tailCount, and use that interval as the tail range to read from the stream.
                TimeSpan tailInterval = TimeSpan.FromTicks(this.Interval.Ticks * tailCount);
                this.ReadStream(last => last - tailInterval);
            }
            else if (viewMode == ObservableKeyedCache <DateTime, IntervalData <TItem> > .ObservableKeyedView.ViewMode.Fixed)
            {
                // Ranges for which we have not yet computed summary data.
                foreach (var range in this.ComputeRangeRequests(startTime, endTime))
                {
                    this.ReadStream(range.Item1, range.Item2);
                }
            }
            else
            {
                throw new NotSupportedException($"Summarization not yet supported in {viewMode} view mode.");
            }

            // Get or create the summary view from the cache
            return(this.GetCachedSummaryView(
                       (ObservableKeyedCache <DateTime, IntervalData <TDest> > .ObservableKeyedView.ViewMode)viewMode,
                       startTime,
                       endTime,
                       tailCount,
                       tailRange) as ObservableKeyedCache <DateTime, IntervalData <TItem> > .ObservableKeyedView);
        }
Exemple #21
0
        /// <summary>
        /// Gets a view over the specified time range of the cached summary data.
        /// </summary>
        /// <typeparam name="T">The summary data type.</typeparam>
        /// <param name="streamSource">The stream source indicating which stream to read from.</param>
        /// <param name="viewMode">The view mode, which may be either fixed or live data.</param>
        /// <param name="startTime">The start time of the view range.</param>
        /// <param name="endTime">The end time of the view range.</param>
        /// <param name="interval">The time interval each summary value should cover.</param>
        /// <param name="tailCount">Not yet supported and should be set to zero.</param>
        /// <param name="tailRange">Tail duration function. Computes the view range start time given an end time. Applies to live view mode only.</param>
        /// <returns>A view over the cached summary data that covers the specified time range.</returns>
        public ObservableKeyedCache <DateTime, IntervalData <T> > .ObservableKeyedView ReadSummary <T>(
            StreamSource streamSource,
            ObservableKeyedCache <DateTime, IntervalData <T> > .ObservableKeyedView.ViewMode viewMode,
            DateTime startTime,
            DateTime endTime,
            TimeSpan interval,
            uint tailCount,
            Func <DateTime, DateTime> tailRange)
        {
            if (startTime > DateTime.MinValue)
            {
                // Extend the start time to include the preceding data point to facilitate continuous plots.
                startTime = this.FindPreviousDataPoint <T>(startTime, interval);
            }

            if (endTime < DateTime.MaxValue)
            {
                // Extend the start time to include the next data point to facilitate continuous plots.
                endTime = this.FindNextDataPoint <T>(endTime, interval);
            }

            return(this.GetOrCreateSummaryCache(streamSource, interval).ReadSummary <T>(viewMode, startTime, endTime, tailCount, tailRange));
        }
Exemple #22
0
        /// <inheritdoc />
        public void Dispose()
        {
            lock (this.bufferLock)
            {
                if (this.needsDisposing)
                {
                    this.data.CollectionChanged -= this.OnCollectionChanged;
                    foreach (var message in this.data)
                    {
                        var item = message.Data;
                        (item as IDisposable).Dispose();
                    }

                    foreach (var message in this.dataBuffer)
                    {
                        var item = message.Data;
                        (item as IDisposable).Dispose();
                    }
                }

                this.data.Clear();
                this.data = null;
                this.dataBuffer.Clear();
                this.dataBuffer = null;

                this.index.Clear();
                this.index = null;
                this.indexBuffer.Clear();
                this.indexBuffer = null;

                this.pool?.Dispose();
                this.pool = null;

                this.streamAdapter?.Dispose();
            }
        }
Exemple #23
0
        /// <summary>
        /// Purges the cache of summary views which do not include the specified protected range, but
        /// only if the total number of underlying data items across all the views exceeds the limit.
        /// </summary>
        /// <param name="protectedViewKey">The key associated with the protected view.</param>
        /// <param name="protectedView">The protected view.</param>
        private void PurgeSummaryViews(
            Tuple <DateTime, DateTime, uint, Func <DateTime, DateTime> > protectedViewKey,
            ObservableKeyedCache <DateTime, IntervalData <TDest> > .ObservableKeyedView protectedView)
        {
            // Check if we need to purge
            if (this.cachedSummaryViews.Values.Sum(v => v.Count) <= this.maxCacheSize)
            {
                return;
            }

            // List of cached views, ordered by (startTime, endTime)
            var startTime   = protectedViewKey.Item1;
            var endTime     = protectedViewKey.Item2;
            var cachedViews = this.cachedSummaryViews.OrderBy(v => v.Key.Item1).ThenBy(v => v.Key.Item2).ToList();

            foreach (var existingView in cachedViews)
            {
                // Skip overlapping views and preserve them
                if ((startTime < existingView.Key.Item2) && (endTime > existingView.Key.Item1))
                {
                    continue;
                }

                // Remove all disjoint views that do not touch the current view range
                this.cachedSummaryViews.Remove(existingView.Key);
            }

            // Check whether the cached views still exceed the limit. The above removal may not have
            // removed enough. If so, then simply clear the entire cache, leaving just the current
            // protected view.
            if (this.cachedSummaryViews.Values.Sum(v => v.Count) > this.maxCacheSize)
            {
                this.cachedSummaryViews.Clear();
                this.cachedSummaryViews.Add(protectedViewKey, protectedView);
            }
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="StreamUpdateWithView{T}"/> class.
 /// </summary>
 /// <param name="streamUpdate">An existing stream update to convert to a stream update with view.</param>
 /// <param name="view">The view of the data cache spanning the duration of the update.</param>
 public StreamUpdateWithView(StreamUpdate <T> streamUpdate, ObservableKeyedCache <DateTime, Message <T> > .ObservableKeyedView view)
     : base(streamUpdate.UpdateType, streamUpdate.Message)
 {
     this.View = view;
 }
Exemple #25
0
        /// <summary>
        /// Returns a view over the summary data and ensure that the view is preserved in the cache.
        /// </summary>
        /// <param name="viewMode">The view mode.</param>
        /// <param name="startTime">Start stime of the view.</param>
        /// <param name="endTime">End time of the view.</param>
        /// <param name="tailCount">Number of items to include in view.</param>
        /// <param name="tailRange">Tail duration function.</param>
        /// <returns>The requested summary view.</returns>
        private ObservableKeyedCache <DateTime, IntervalData <TDest> > .ObservableKeyedView GetCachedSummaryView(
            ObservableKeyedCache <DateTime, IntervalData <TDest> > .ObservableKeyedView.ViewMode viewMode,
            DateTime startTime,
            DateTime endTime,
            uint tailCount,
            Func <DateTime, DateTime> tailRange)
        {
            ObservableKeyedCache <DateTime, IntervalData <TDest> > .ObservableKeyedView newView;
            var newViewKey = Tuple.Create(startTime, endTime, tailCount, tailRange);

            if (!this.cachedSummaryViews.TryGetValue(newViewKey, out newView))
            {
                // Create the requested view over the cached summary data.
                newView = this.summaryCache.GetView(viewMode, startTime, endTime, tailCount, tailRange);

                // Retain cached data by maintaining a table of summary views for which we want the data to be retained.
                // This is currently done for fixed mode views only, as the views are constantly being updated in live
                // mode, so it probably makes sense to just defer to the underlying cache to manage data retention.
                if (viewMode == ObservableKeyedCache <DateTime, IntervalData <TDest> > .ObservableKeyedView.ViewMode.Fixed)
                {
                    // List of cached views, ordered by (startTime, endTime)
                    var cachedViews = this.cachedSummaryViews.OrderBy(v => v.Key.Item1).ThenBy(v => v.Key.Item2).ToList();
                    foreach (var existingView in cachedViews)
                    {
                        // Terminate when we have passed the end time of the new view
                        if (existingView.Key.Item1 > endTime)
                        {
                            break;
                        }

                        // Skip views that are disjoint from the new view
                        if (existingView.Key.Item2 < startTime)
                        {
                            continue;
                        }

                        // Extend start time to include existing overlapping view
                        if (startTime > existingView.Key.Item1)
                        {
                            startTime = existingView.Key.Item1;
                        }

                        // Extend end time to include existing overlapping view
                        if (endTime < existingView.Key.Item2)
                        {
                            endTime = existingView.Key.Item2;
                        }

                        // Remove existing overlapping view which will be subsumed by the new range (startTime, endTime)
                        this.cachedSummaryViews.Remove(existingView.Key);
                    }

                    // Get a new view covering the expanded time range
                    var adjustedView = this.summaryCache.GetView(viewMode, startTime, endTime, tailCount, tailRange);

                    // Add it to the table of cached views to preserve the underlying data
                    var adjustedViewKey = Tuple.Create(startTime, endTime, tailCount, tailRange);
                    this.cachedSummaryViews.Add(adjustedViewKey, adjustedView);

                    // Prune the table of cached views to keep them within the cache limit
                    this.PurgeSummaryViews(newViewKey, newView);
                }
            }

            // Remove references to stream views that we no longer need
            this.PurgeStreamViews();

            return(newView);
        }