Skip to content

Commit c60f648

Browse files
committed
Added Ability to read interpolated Values from PI
1 parent c776403 commit c60f648

File tree

2 files changed

+124
-12
lines changed

2 files changed

+124
-12
lines changed

Source/Libraries/Adapters/PIAdapters/PIBufferInputAdapter.cs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -231,17 +231,21 @@ private void GetPIConnection()
231231
m_connection.Open();
232232
}
233233

234-
private IEnumerable<AFValue> ReadData(AFTime startTime, AFTime endTime, PIPointList points)
234+
private IEnumerable<AFValue> ReadData(AFTime startTime, AFTime endTime, PIPointList points, int sampleRate)
235235
{
236-
return new TimeSortedValueScanner
237-
{
238-
Points = points,
239-
StartTime = startTime,
240-
EndTime = endTime,
241-
DataReadExceptionHandler = ex => OnProcessException(MessageLevel.Warning, ex)
242-
}
243-
.Read(PageFactor);
244-
}
236+
TimeSortedValueScanner scanner = new TimeSortedValueScanner
237+
{
238+
Points = points,
239+
StartTime = startTime,
240+
EndTime = endTime,
241+
DataReadExceptionHandler = ex => OnProcessException(MessageLevel.Warning, ex)
242+
};
243+
244+
if (sampleRate <= 0)
245+
return scanner.Read(PageFactor);
246+
else
247+
return scanner.ReadInterpolated(new AFTimeSpan(new TimeSpan(0,0,sampleRate)), PageFactor);
248+
}
245249

246250

247251
/// <summary>
@@ -251,7 +255,7 @@ private IEnumerable<AFValue> ReadData(AFTime startTime, AFTime endTime, PIPointL
251255
/// <param name="end">The end time for the data requested.</param>
252256
/// <param name="tags">The list of PI Tags of the data requested separated by ;.</param>
253257
/// <returns>A string representing the read buffer data as comma-separated values in form tag:data1,time1;data2,time2 \newLine</returns>
254-
public string ReadBuffer(DateTime start, DateTime end, string tags)
258+
public string ReadBuffer(DateTime start, DateTime end, string tags, int interpolationInterval)
255259
{
256260
if (start.Kind == DateTimeKind.Unspecified)
257261
start = DateTime.SpecifyKind(start, DateTimeKind.Utc);
@@ -294,7 +298,7 @@ public string ReadBuffer(DateTime start, DateTime end, string tags)
294298
m_startTime = start < DateTime.MinValue ? DateTime.MinValue : start > DateTime.MaxValue ? DateTime.MaxValue : start;
295299
m_stopTime = end < DateTime.MinValue ? DateTime.MinValue : end > DateTime.MaxValue ? DateTime.MaxValue : end;
296300

297-
m_dataReader = ReadData(m_startTime, m_stopTime, points).GetEnumerator();
301+
m_dataReader = ReadData(m_startTime, m_stopTime, points, interpolationInterval).GetEnumerator();
298302

299303
while (m_dataReader.MoveNext())
300304
{
@@ -316,6 +320,7 @@ public string ReadBuffer(DateTime start, DateTime end, string tags)
316320
tagList.Select(item => $"{item.Key}:{string.Join(";", item.Value.Select(v => $"{v.Item2},{v.Item1}"))}"));
317321
}
318322

323+
319324
/// <inheritdoc/>
320325
protected override void AttemptConnection() {}
321326

Source/Libraries/Adapters/PIAdapters/TimeSortedValueScanner.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)