/// <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(); } }
private bool AnnotationIntersectsWith(TimeInterval timeInterval) { // If there's no annotations at all, we're done if ((this.Data == null) || (this.Data.Count <= 0)) { return(false); } // Find the nearest annotation to the left edge of the interval int index = IndexHelper.GetIndexForTime(timeInterval.Left, this.Data.Count, (idx) => this.Data[idx].Data.Interval.Right, SnappingBehavior.Nearest); // Check if the annotation intersects with the interval, then keep walking to the right until // we find an annotation within the interval or we go past the right hand side of the interval. while (index < this.Data.Count) { TimeIntervalAnnotation annotation = this.Data[index].Data; // Check if the annotation intersects with the interval if (timeInterval.IntersectsWith(annotation.Interval)) { return(true); } // Check if the annotation is completely to the right of the interval if (timeInterval.Right <= annotation.Interval.Left) { return(false); } index++; } return(false); }
/// <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)); } }
/// <inheritdoc/> public void ReadAndPublishStreamValue(IStreamReader streamReader, DateTime dateTime) { foreach (var epsilonTimeInterval in this.publishers.Keys) { // Get the index of the data, given the cursor time, and the epsilon interval. int index = IndexHelper.GetIndexForTime(dateTime, epsilonTimeInterval, this.index?.Count ?? 0, (idx) => this.index[idx].OriginatingTime); if (index >= 0) { // Get the index entry var indexedStreamReaderThunk = this.index[index]; // Read the data (this allocates) var data = indexedStreamReaderThunk.Read(streamReader); // Publish the value foreach (var publisher in this.publishers[epsilonTimeInterval]) { publisher.PublishValue(true, data, indexedStreamReaderThunk.OriginatingTime, indexedStreamReaderThunk.CreationTime); } // Deallocate to control the lifetime of the read object this.Deallocator?.Invoke(data); } else { // Cache miss, attempt to seek directly. The cache miss could happen because the // the stream index data structure is being populated, or because the epsilon interval // is set such that no data point is found. In either case, a direct read is // attempted. streamReader.Seek(dateTime + epsilonTimeInterval, true); streamReader.OpenStream( this.StreamName, (data, envelope) => { foreach (var publisher in this.publishers[epsilonTimeInterval]) { publisher.PublishValue(true, data, envelope.OriginatingTime, envelope.CreationTime); } }, this.Allocator, this.Deallocator); // This will force the read of the next message. If a message within the seek // constraints is found, the delegate passed to OpenStream above is executed // to capture the data. If no message exists in that specified seek interval // the delegate does not execute and found below becomes false. var found = streamReader.MoveNext(out var envelope); // If no message was found if (!found) { // Then signal the data providers that to message is available foreach (var publisher in this.publishers[epsilonTimeInterval]) { publisher.PublishValue(false, default, default, default);
/// <inheritdoc /> public DateTime?GetOriginatingTimeOfNearestInstantMessage(DateTime time) { int index = IndexHelper.GetIndexForTime(time, this.instantIndexView.Count, (idx) => this.instantIndexView[idx].OriginatingTime, SnappingBehavior.Nearest); if (index >= 0) { return(this.instantIndexView[index].OriginatingTime); } 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="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(); } }
private bool AnnotationIntersectsWith(TimeInterval timeInterval) { // If there's no annotations at all, we're done if ((this.Data == null) || (this.Data.Count <= 0)) { return(false); } // Find the nearest annotation to the left edge of the interval int index = IndexHelper.GetIndexForTime(timeInterval.Left, this.Data.Count, (idx) => this.Data[idx].Data.Interval.Right, NearestMessageType.Nearest); // Check if the annotation intersects with the interval, then keep walking to the right until // we find an annotation within the interval or we go past the right hand side of the interval. while (index < this.Data.Count) { TimeIntervalAnnotation annotation = this.Data[index].Data; // Check if the annotation is completely to the left of the interval // NOTE: By default time intervals are inclusive of their endpoints, so abutting time intervals will // test as intersecting. Use a non-inclusive time interval so that we can let annotations abut. if (timeInterval.IntersectsWith(new TimeInterval(annotation.Interval.Left, false, annotation.Interval.Right, false))) { return(true); } // Check if the annotation is completely to the right of the interval if (timeInterval.Right <= annotation.Interval.Left) { return(false); } index++; } return(false); }
/// <inheritdoc/> public override DateTime?GetTimeOfNearestMessage(DateTime time, NearestMessageType snappingBehavior) { int index = IndexHelper.GetIndexForTime(time, this.data.Count, (idx) => this.data[idx].OriginatingTime, snappingBehavior); return((index >= 0) ? this.data[index].OriginatingTime : null); }
/// <inheritdoc/> protected override int GetIndexForTime(DateTime currentTime, int count, Func <int, DateTime> timeAtIndex) { return(IndexHelper.GetIndexForTime(currentTime, count, timeAtIndex)); }