Exercise 5 - Monitor a PIDataPipe

Version 3

    Estimated Completion Time: 4 - 6 hrs


    Objective: Create a console application to create new PI points, search for PI points, and receive values through EventPipes.


    In this exercise, your console application should:


    1. Create three PI Points using PI AF SDK with Rich Data Access (OSIsoft.AF.PI namespace). These three tags will have the same tag attributes from the PI points that are assigned to the attributes New York_Temperature, New York_Pressure and New York_Humidity and their name should start with "AFSDKWS_". Therefore, the name of a new tag could be AFSDKWS_New York_Temperature. In case the PI points were already created, the end user should be notified of this fact.
    2. Query for "AFSDKWS_*" in order to find the three PI points you have created on the last item, using the method FindPIPoints and store the results in an object form IList<PIPoint>.
    3. The application will then check every second if any of those 3 tags have received a value through EventPipes. Once a new value arrives, its Value and Timestamp should be printed on the screen. In case nothing is received, it should be displayed "No new values" with the current time. This would give an indicator of the frequency of data.
    4. Finally, if a customer presses the ESC key, the application should stop and close automatically. Please refer to the code snippet below:



    Console.WriteLine("Press ESC to stop");
        while (!Console.KeyAvailable)
            // Do Something
    } while (Console.ReadKey(true).Key != ConsoleKey.Escape);




    • The PIServer.CreatePIPoint method can be used to create an individual PIPoint.
    • This PIServer.CreatePIPoints overload can create multiple PIPoints in one bulk call, where each PIPoint has a different set of attribute values.
    • You are strongly encouraged to use the PICommonPointAttributes class and its many members.
    • To get point attributes, you would use the PIPoint.GetAttributes method.  However, that method requires that you have loaded the attributes first.
    • To load point attributes, you can simultaneously load them when finding the points using this FindPIPoints overload.
    • Or if the PI points have already been found, you may use the PIPoint.LoadAttributes method.
    • You probably don't need to use PIPoint.IsAttributeLoaded but its nice to know it exists.


    More Hints:

    A big roadblock to coming up with a solution will require have a some sort of collection of PI point attributes.  There are at least 2 ways to create such a set.  The first way is boring and very rigid.  It would require you listing every column heading found in the PIPointsForPIAFSDKWorkshop spreadsheet.  Something like:


    private static string[] GetExplicitPointAttributes => new string[] { PICommonPointAttributes.Archiving
    , PICommonPointAttributes.CompressionDeviation
    , PICommonPointAttributes.CompressionPercentage
    , PICommonPointAttributes.CompressionMaximum
    , PICommonPointAttributes.CompressionMinimum
    , PICommonPointAttributes.ConversionFactor
    , PICommonPointAttributes.Descriptor
    , PICommonPointAttributes.ExceptionDeviation
    , PICommonPointAttributes.ExceptionPercentage
    , PICommonPointAttributes.ExceptionMaximum
    , PICommonPointAttributes.ExceptionMinimum
    , PICommonPointAttributes.ExtendedDescriptor
    , PICommonPointAttributes.FilterCode
    , PICommonPointAttributes.Location1
    , PICommonPointAttributes.Location2
    , PICommonPointAttributes.Location3
    , PICommonPointAttributes.Location4
    , PICommonPointAttributes.Location5
    , PICommonPointAttributes.PointSource
    , PICommonPointAttributes.PointType
    , PICommonPointAttributes.PointClassName
    , PICommonPointAttributes.Shutdown
    , PICommonPointAttributes.SquareRoot
    , PICommonPointAttributes.SourcePointID
    , PICommonPointAttributes.Step
    , PICommonPointAttributes.TotalCode
    , PICommonPointAttributes.TypicalValue
    , PICommonPointAttributes.UserInt1
    , PICommonPointAttributes.UserInt2
    , PICommonPointAttributes.UserReal1
    , PICommonPointAttributes.UserReal2
    , PICommonPointAttributes.Zero
    , PICommonPointAttributes.Span


    That doesn't look fun.  Another way would be to retrieve all the point attributes belonging the the entire point class.


    private static IList<string> GetPointAttributes(PIServer dataArchive, string ptClassName)
        var ptclass = dataArchive.PointClasses[ptClassName];
        var dict = ptclass.GetAttributes();
        return dict.Keys.ToList();


    Except there is another problem that will trip you up.  The exercise wants you to clone the AFSDKWS_ tags to be similar to the existing New York weather tag.  You don't want a very close duplicate but not an exact clone, which is impossible.  There are just some point attributes that are unique to an individual tag, such as it's name, point id, creation date, change date, etc.  So now we need another method to list such point attributes that need to be excluded in our duplication process:


    // These point attributes are not to be copied when creating a new point.
    private static string[] ExcludedPointAttributes => new string[] { PICommonPointAttributes.Tag
       , PICommonPointAttributes.PointID
       , PICommonPointAttributes.RecordNumber
       , PICommonPointAttributes.CreationDate
       , PICommonPointAttributes.Creator
       , PICommonPointAttributes.ChangeDate
       , PICommonPointAttributes.Changer
       , PICommonPointAttributes.PointClassID
       , PICommonPointAttributes.PointClassRevision


    That gets you close, but you aren't there yet.  You would then need to wire together the  2 previous methods into one call.  Something like:


    IEnumerable<string> classicAttrs = GetPointAttributes(dataArchive, "classic").Except(ExcludedPointAttributes);


    Just be aware that some methods that expect a collection of the point attributes to be a more specific type, such as a string array.  If you come across such a method in your solution, remember that you can use the LINQ ToArray() extension method.


    Please use the discussion forum to discuss or ask any questions about Exercise 5