@@ -165,5 +165,112 @@ public IEnumerable<AFValue> Read(int pageFactor = 1)
165165 while ( enumerators . Count > 0 ) ;
166166 }
167167
168+ /// <summary>
169+ /// Reads all <see cref="AFValue"/> instances in time sorted order as a yielding enumerable using PI InterpolatedValues API.
170+ /// </summary>
171+ /// <param name="pageFactor">Defines a paging factor used to load more data into a page.</param>
172+ /// <param name="interval">Defines the interval used for interpolation.</param>
173+ /// <returns>Each recorded <see cref="AFValue"/> in time-sorted order for the specified <see cref="Points"/> and time-range.</returns>
174+ ///
175+ public IEnumerable < AFValue > ReadInterpolated ( AFTimeSpan interval , int pageFactor = 1 )
176+ {
177+ PIPagingConfiguration config = new ( PIPageType . TagCount , Points . Count * pageFactor < 1 ? 1 : pageFactor ) ;
178+ List < IEnumerator < AFValue > > enumerators = [ ] ;
179+
180+ try
181+ {
182+ // Setup enumerators for each set of points that have data
183+ foreach ( AFValues scanner in Points . InterpolatedValues ( new AFTimeRange ( StartTime , EndTime ) , interval , null , false , config ) )
184+ {
185+ IEnumerator < AFValue > enumerator = scanner . GetEnumerator ( ) ;
186+
187+ // Add enumerator to the list if it has at least one value
188+ if ( enumerator . MoveNext ( ) )
189+ enumerators . Add ( enumerator ) ;
190+ }
191+ }
192+ catch ( OperationCanceledException )
193+ {
194+ // Errors that occur during bulk calls get trapped here, actual error is stored on the PIPagingConfiguration object
195+ DataReadExceptionHandler ( config . Error ) ;
196+ }
197+ catch ( Exception ex )
198+ {
199+ DataReadExceptionHandler ( ex ) ;
200+ }
201+
202+ if ( enumerators . Count == 0 )
203+ yield break ;
204+
205+ List < int > completed = [ ] ;
206+
207+ // Start publishing data points in time-sorted order
208+ do
209+ {
210+ AFTime publishTime = AFTime . MaxValue ;
211+ AFValue dataPoint ;
212+
213+ // Find minimum publication time for current values
214+ foreach ( IEnumerator < AFValue > enumerator in enumerators )
215+ {
216+ dataPoint = enumerator . Current ;
217+
218+ if ( dataPoint ? . Timestamp < publishTime )
219+ publishTime = dataPoint . Timestamp ;
220+ }
221+
222+ int index = 0 ;
223+
224+ // Publish all values at the current time
225+ foreach ( IEnumerator < AFValue > enumerator in enumerators )
226+ {
227+ bool enumerationComplete = false ;
228+ dataPoint = enumerator . Current ;
229+
230+ if ( dataPoint ? . Timestamp <= publishTime )
231+ {
232+ // Attempt to advance to next data point, tracking completed enumerators
233+ if ( ! enumerator . MoveNext ( ) )
234+ {
235+ enumerationComplete = true ;
236+ completed . Add ( index ) ;
237+ }
238+
239+ yield return dataPoint ;
240+
241+ // Make sure any point IDs with duplicated times directly follow
242+ if ( ! enumerationComplete )
243+ {
244+ while ( enumerator . Current ? . Timestamp <= publishTime )
245+ {
246+ yield return enumerator . Current ;
247+
248+ if ( ! enumerator . MoveNext ( ) )
249+ {
250+ completed . Add ( index ) ;
251+ break ;
252+ }
253+ }
254+ }
255+ }
256+
257+ index ++ ;
258+ }
259+
260+ if ( completed . Count == 0 )
261+ continue ;
262+
263+ // Remove completed enumerators
264+ completed . Sort ( ) ;
265+
266+ for ( int i = completed . Count - 1 ; i >= 0 ; i -- )
267+ enumerators . RemoveAt ( completed [ i ] ) ;
268+
269+ completed . Clear ( ) ;
270+ }
271+ while ( enumerators . Count > 0 ) ;
272+ }
273+
274+
168275 #endregion
169276}
0 commit comments