Take a look at the PIPoint.RecordedValuesAtTimes method within the OSIsoft.AF.PI namespace. This method allow you to provide an IList<AFTime> and this is where you could enter your start and end times.
If you want to get serious about performance, and your PI Data Archive (PI Server) is 2016 or later, you can page the results. Check out under the OSIsoft.AF.PI namespace, the PIPointList Class and its methods. Look at both the PIPointList.Summaries and the RecordedValuesAtTimes. You also will have to configure the PIPagingConfiguration object.
RecordedValuesAtTimes will give you the interpolated values at the StartTime and EndTime of your time range. If you were wanting the closed archive value, you would need to make 2 additional calls after the Summary.
AFValue firstValue = pivalue.RecordedValue(at.StartTime, AFRetrievalMode.AtOrAfter); AFValue lastValue = pivalue.RecordedValue(at.EndTime, AFRetrievalMode.AtOrBefore);
As your code currently is, you can do a few minor things:
- There is no reason for pp to be a List<PIPoint>. The code shown works fine and may be quicker if you were to change it to:
IEnumerable<PIPoint> pp = PIPoint.FindPIPoints(server, "abcd.*", null);
- Defining the time range may be set before the loop. No need to set it to the same value each iteration through the loop.
- And personally, I would rename the variable "pivalue" to something like "pipt", "point", or "tag". To me the phrase "pivalue" carries a different meaning (similar to AFValue or the PISDK.PIValue).
As Dan suggests, you could find efficiencies by using bulk calls and paging with PIPointList. The things to look out for are having too many PI points in the PIPointList, which could make the Summary call throw an OperationTimeout exception. This is all dependent upon how dense your data may be, how wide the time range is, and how many tags you want to aggregate. So you may have to play around to find the right sweet spot.
To help get you started, you may start off setting pp to the PIPointList with:
PIPointList pp = new PIPointList(PIPoint.FindPIPoints(server, "abcd.*", null));
Then you don't need to loop per tag and make 3 data calls per individual tag. You can issue 3 bulk data calls using PIPointList.Summary and two PIPointList.RecordedValue calls. This means you've only made 4 calls to the PI Data Archive (FindPIPoints, Summary, and 2 X RecordedValue).
This really improved the performance of my application, the only thing i had to do is to upgrade the sdk to latest version to get access to such methods.