/// <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; } }
private DataStoreReader FindDataStoreReader(StreamBinding streamBinding, bool createDataStoreReader = true) { if (streamBinding == null) { throw new ArgumentNullException(nameof(streamBinding)); } lock (this.dataStoreReaders) { DataStoreReader dataStoreReader = new DataStoreReader(streamBinding.StoreName, streamBinding.StorePath, streamBinding.SimpleReaderType); var key = Tuple.Create(dataStoreReader.StoreName, dataStoreReader.StorePath); if (this.dataStoreReaders.Contains(key)) { // dispose existing dataStoreReader before re-assignment dataStoreReader.Dispose(); dataStoreReader = this.dataStoreReaders[key]; } else if (createDataStoreReader) { this.dataStoreReaders.Add(dataStoreReader); } else { // dispose existing dataStoreReader before re-assignment dataStoreReader.Dispose(); dataStoreReader = null; } return(dataStoreReader); } }
/// <summary> /// Reads a single message from a stream identified by a stream binding and an index entry. /// </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="indexEntry">The index entry indicating which message to read.</param> /// <returns>The message that was read.</returns> public T Read <T>(StreamBinding streamBinding, IndexEntry indexEntry) { using (ISimpleReader reader = this.simpleReader.OpenNew()) { return(this.GetStreamReader <T>(streamBinding, true).Read <T>(reader, indexEntry)); } }
private StreamSummaryManager FindStreamSummaryManager(StreamBinding streamBinding, bool createStreamSummaryManager = true) { if (streamBinding == null) { throw new ArgumentNullException(nameof(streamBinding)); } lock (this.streamSummaryManagers) { var key = Tuple.Create(streamBinding.StoreName, streamBinding.StorePath, streamBinding.StreamName, streamBinding.StreamAdapterType); StreamSummaryManager streamSummaryManager = default(StreamSummaryManager); if (this.streamSummaryManagers.Contains(key)) { streamSummaryManager = this.streamSummaryManagers[key]; } else if (createStreamSummaryManager) { streamSummaryManager = new StreamSummaryManager( streamBinding.StoreName, streamBinding.StorePath, streamBinding.StreamName, streamBinding.StreamAdapterType); this.streamSummaryManagers.Add(streamSummaryManager); } return(streamSummaryManager); } }
private DataStoreReader FindDataStoreReader(StreamBinding streamBinding, bool createDataStoreReader = true) { if (streamBinding == null) { throw new ArgumentNullException(nameof(streamBinding)); } lock (this.dataStoreReaders) { DataStoreReader dataStoreReader = new DataStoreReader(streamBinding.StoreName, streamBinding.StorePath, streamBinding.SimpleReaderType); var key = Tuple.Create(dataStoreReader.StoreName, dataStoreReader.StorePath); if (this.dataStoreReaders.Contains(key)) { dataStoreReader = this.dataStoreReaders[key]; } else if (createDataStoreReader) { this.dataStoreReaders.Add(dataStoreReader); } else { dataStoreReader = default(DataStoreReader); } return(dataStoreReader); } }
/// <summary> /// Initializes a new instance of the <see cref="StreamBinding"/> class. /// </summary> /// <param name="source">An existing stream binding to clone.</param> /// <param name="summarizerType">The type of the stream summarizer, null if there is none.</param> /// <param name="summarizerArgs">The arguments used when constructing the stream summarizer, null if ther is none.</param> public StreamBinding(StreamBinding source, Type summarizerType, object[] summarizerArgs) : this(source.StreamName, source.PartitionName, source.StoreName, source.StorePath, source.SimpleReaderType, source.StreamAdapterType, summarizerType, summarizerArgs) { this.streamAdapter = source.streamAdapter; // Do not copy this over since the type or args may have changed this.summarizer = null; }
/// <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)); }
/// <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)); }
/// <summary> /// Reads a single message from a stream identified by a stream binding and an index entry. /// </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="indexEntry">The index entry indicating which message to read.</param> /// <returns>The message that was read.</returns> public T Read <T>(StreamBinding streamBinding, IndexEntry indexEntry) { DataStoreReader dataStoreReader = this.FindDataStoreReader(streamBinding, false); if (dataStoreReader == null) { throw new ArgumentOutOfRangeException(nameof(streamBinding), "Stream binding must reference an open data store."); } return(dataStoreReader.Read <T>(streamBinding, indexEntry)); }
private IStreamReader GetStreamReader <T>(StreamBinding streamBinding, bool useIndex) { var streamReader = this.streamReaders.Find(sr => sr.StreamName == streamBinding.StreamName && sr.StreamAdapterType == streamBinding.StreamAdapterType); if (streamReader == null) { streamReader = new StreamReader <T>(streamBinding, useIndex); this.streamReaders.Add(streamReader); } return(streamReader); }
/// <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> /// Gets the simple reader for the specified stream binding. /// </summary> /// <param name="streamBinding">The stream binding.</param> /// <returns>The simple reader.</returns> public ISimpleReader GetReader(StreamBinding streamBinding) { if (streamBinding == null) { throw new ArgumentNullException(nameof(streamBinding)); } var key = Tuple.Create(streamBinding.StoreName, streamBinding.StorePath); if (this.dataStoreReaders.Contains(key)) { return(this.dataStoreReaders[key].GetReader()); } key = Tuple.Create(streamBinding.StoreName, StoreCommon.GetPathToLatestVersion(streamBinding.StoreName, streamBinding.StorePath)); if (this.dataStoreReaders.Contains(key)) { return(this.dataStoreReaders[key].GetReader()); } return(null); }
/// <summary> /// Finds the time of the next data point after the point indicated by the given time. /// </summary> /// <typeparam name="T">The summary data type.</typeparam> /// <param name="streamBinding">The stream binding indicating which stream to read from.</param> /// <param name="time">Time of current data point.</param> /// <param name="interval">The time interval each summary value covers.</param> /// <returns>Time of the next data point.</returns> public DateTime FindNextDataPoint <T>(StreamBinding streamBinding, DateTime time, TimeSpan interval) { var searchInterval = interval; while (searchInterval <= this.MaxSummaryInterval) { var cache = this.GetSummaryCache(streamBinding, searchInterval, false); if (cache != null) { var intervalData = cache.Search <T>(time, StreamSummarySearchMode.Next); var adjustedTime = (intervalData.OriginatingTime >= time) ? intervalData.OriginatingTime : intervalData.EndTime; if (adjustedTime != DateTime.MinValue) { return(adjustedTime + interval); } } searchInterval = TimeSpan.FromTicks(Math.Max(1, searchInterval.Ticks * 2)); } return(time); }
/// <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="streamBinding">The stream binding 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>( StreamBinding streamBinding, 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>(streamBinding, startTime, interval); } if (endTime < DateTime.MaxValue) { // Extend the start time to include the next data point to facilitate continuous plots. endTime = this.FindNextDataPoint <T>(streamBinding, endTime, interval); } return(this.GetSummaryCache(streamBinding, interval).ReadSummary <T>(viewMode, startTime, endTime, tailCount, tailRange)); }
private IStreamSummary GetSummaryCache(StreamBinding streamBinding, TimeSpan interval, bool create = true) { var summaryCache = this.summaryCaches.Find(s => (s.Interval == interval) && (s.SummarizerType == streamBinding.SummarizerType) && StructuralComparisons.StructuralEqualityComparer.Equals(s.Parameters, streamBinding.SummarizerArgs)); if ((summaryCache == null) && create) { summaryCache = typeof(StreamSummary <,>) .MakeGenericType(streamBinding.Summarizer.SourceType, streamBinding.Summarizer.DestinationType) .GetConstructor(new Type[] { typeof(StreamBinding), typeof(TimeSpan), typeof(uint) }) .Invoke(new object[] { streamBinding, interval, DefaultCacheCapacity }) as IStreamSummary; if (summaryCache == null) { throw new InvalidOperationException("Unable to create instance of summary cache"); } this.summaryCaches.Add(summaryCache); } return(summaryCache); }
/// <summary> /// Creates a view of the indices identified by the matching start and end times and asychronously fills it in. /// </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="startTime">Start time of indices to read.</param> /// <param name="endTime">End time of indices to read.</param> /// <returns>Observable view of indices.</returns> public ObservableKeyedCache <DateTime, IndexEntry> .ObservableKeyedView ReadIndex <T>(StreamBinding streamBinding, DateTime startTime, DateTime endTime) { if (endTime < startTime) { throw new ArgumentException("End time must be greater than or equal to start time.", nameof(endTime)); } var dataStoreReader = this.FindDataStoreReader(streamBinding, true); return(dataStoreReader.ReadIndex <T>(streamBinding, startTime, endTime)); }
/// <summary> /// Creates a view of the messages identified by the matching start and end times and asychronously fills it in. /// </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="startTime">Start time of messages to read.</param> /// <param name="endTime">End time of messages to read.</param> /// <returns>Observable view of data.</returns> public ObservableKeyedCache <DateTime, Message <T> > .ObservableKeyedView ReadStream <T>(StreamBinding streamBinding, DateTime startTime, DateTime endTime) { if (endTime < startTime) { throw new ArgumentException("End time must be greater than or equal to start time.", nameof(endTime)); } var dataStoreReader = this.FindDataStoreReader(streamBinding, true); return(dataStoreReader.ReadStream(streamBinding, ObservableKeyedCache <DateTime, Message <T> > .ObservableKeyedView.ViewMode.Fixed, startTime, endTime, 0, null)); }
/// <summary> /// Creates a view of the messages identified by the matching tail count and asychronously fills it in. /// </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="tailCount">Number of messages to included in tail.</param> /// <returns>Observable view of data.</returns> public ObservableKeyedCache <DateTime, Message <T> > .ObservableKeyedView ReadStream <T>(StreamBinding streamBinding, uint tailCount) { if (tailCount == 0) { throw new ArgumentException("Tail count must be greater than 0", nameof(tailCount)); } var dataStoreReader = this.FindDataStoreReader(streamBinding, true); return(dataStoreReader.ReadStream(streamBinding, ObservableKeyedCache <DateTime, Message <T> > .ObservableKeyedView.ViewMode.TailCount, DateTime.MinValue, DateTime.MaxValue, tailCount, null)); }
/// <summary> /// Gets originating time of the message in a stream that's closest to a given time. /// </summary> /// <param name="streamBinding">The stream binding indicating which stream to read from.</param> /// <param name="time">The time for which to return the message with the closest originating time.</param> /// <returns>The originating time of the message closest to time.</returns> internal DateTime?GetOriginatingTimeOfNearestInstantMessage(StreamBinding streamBinding, DateTime time) { return(this.GetExistingStreamReader(streamBinding.StreamName, null).GetOriginatingTimeOfNearestInstantMessage(time)); }
/// <summary> /// Creates a view of the messages identified by the matching tail range and asychronously fills it in. /// </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="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, Func <DateTime, DateTime> tailRange) { if (tailRange == null) { throw new ArgumentNullException(nameof(tailRange)); } var dataStoreReader = this.FindDataStoreReader(streamBinding, true); return(dataStoreReader.ReadStream(streamBinding, ObservableKeyedCache <DateTime, Message <T> > .ObservableKeyedView.ViewMode.TailRange, DateTime.MinValue, DateTime.MaxValue, 0, tailRange)); }
/// <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="streamBinding">The stream binding inidicating which stream to read from.</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> /// <returns>A view over the cached summary data that covers the specified time range.</returns> public ObservableKeyedCache <DateTime, IntervalData <T> > .ObservableKeyedView ReadSummary <T>(StreamBinding streamBinding, DateTime startTime, DateTime endTime, TimeSpan interval) { var viewMode = ObservableKeyedCache <DateTime, IntervalData <T> > .ObservableKeyedView.ViewMode.Fixed; return(this.FindStreamSummaryManager(streamBinding).ReadSummary(streamBinding, viewMode, startTime, endTime, interval, 0, null)); }
/// <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="streamBinding">The stream binding inidicating which stream to read from.</param> /// <param name="interval">The time interval each summary value should cover.</param> /// <param name="tailCount">Number of items to include in view.</param> /// <returns>A view over the cached summary data that covers the specified time range.</returns> public ObservableKeyedCache <DateTime, IntervalData <T> > .ObservableKeyedView ReadSummary <T>(StreamBinding streamBinding, TimeSpan interval, uint tailCount) { var viewMode = ObservableKeyedCache <DateTime, IntervalData <T> > .ObservableKeyedView.ViewMode.TailCount; return(this.FindStreamSummaryManager(streamBinding).ReadSummary(streamBinding, viewMode, DateTime.MinValue, DateTime.MaxValue, interval, tailCount, null)); }
/// <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="streamBinding">The stream binding inidicating which stream to read from.</param> /// <param name="interval">The time interval each summary value should cover.</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>(StreamBinding streamBinding, TimeSpan interval, Func <DateTime, DateTime> tailRange) { var viewMode = ObservableKeyedCache <DateTime, IntervalData <T> > .ObservableKeyedView.ViewMode.TailRange; return(this.FindStreamSummaryManager(streamBinding).ReadSummary(streamBinding, viewMode, DateTime.MinValue, DateTime.MaxValue, interval, 0, tailRange)); }
/// <summary> /// Gets the simple reader for the specified stream binding. /// </summary> /// <param name="streamBinding">The stream binding.</param> /// <returns>The simple reader.</returns> public ISimpleReader GetReader(StreamBinding streamBinding) { return(this.FindDataStoreReader(streamBinding, false)?.GetReader()); }
/// <summary> /// Creates a view of the indices identified by the matching start and end times and asychronously fills it in. /// </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="startTime">Start time of indices to read.</param> /// <param name="endTime">End time of indices to read.</param> /// <returns>Observable view of indices.</returns> public ObservableKeyedCache <DateTime, IndexEntry> .ObservableKeyedView ReadIndex <T>(StreamBinding streamBinding, DateTime startTime, DateTime endTime) { return(this.GetStreamReader <T>(streamBinding, true).ReadIndex(startTime, endTime)); }
/// <summary> /// Initializes a new instance of the <see cref="StreamBinding"/> class. /// </summary> /// <param name="source">An existing stream binding to clone.</param> /// <param name="storeName">The store name.</param> /// <param name="storePath">The store path.</param> public StreamBinding(StreamBinding source, string storeName, string storePath) : this(source.StreamName, source.PartitionName, storeName, storePath, source.SimpleReaderType, source.StreamAdapterType, source.SummarizerType, source.SummarizerArgs) { this.streamAdapter = source.streamAdapter; this.summarizer = source.summarizer; }