12 Replies Latest reply on Mar 18, 2019 5:22 PM by Rick Davin

    Are there SDK calls that can retrieve the Archive Properties?

    Francis2

      Are there SDK calls that can get to any of this information?

       

      Arc property.jpg

        • Re: Are there SDK calls that can retrieve the Archive Properties?
          Rick Davin

          No.  Those properties are only available to a PI Admin or someone with appropriate level of security.

           

          When I was a customer, I dabbled with a half-baked idea of using the .NET System.Diagnostics.Process to run issue an arlist call behind-the-scenes, and quietly parse the captured console input to variables to show to end-users.  Again, the security stopped me dead in my tracks.  As regular users could not see this privileged info, I abandoned the project.  That was a few years ago.

           

          Recently, I tried to revive this idea by once again using the Process class, but this time instead of calling arlist to call the PowerShell cmdlets instead.  Once again, the problem was security on the archive files.  If I was a PI Admin, I can see them.  If I am not a PI Admin, I can't.  So yet again I abandoned the project.

          2 of 2 people found this helpful
          • Re: Are there SDK calls that can retrieve the Archive Properties?
            Francis2

            The idea behind the SDK call would be to be able to calculate the amount of storage space per minute/hour/day generated by a system.

            Currently, 'Primary Archive % used' and 'Time to Archive Shift' performance counters are available but that is not enough to do the calculation(s), because it is missing the size of the arc-file. Technically if it is a set size it would not be an issue but if the archives are set by time to shift (like every week) then it is variable. Also getting the data through the PI Perfmon means it need to run on every system (Calculations for a fleet of servers).

            So the idea is to make an SDK call and get the 'size' of the primary archive, the '% used' and the 'current duration' of the archive.

            To generate the 'size' you need 'current duration' devided by '% used' times 100.

            2000 MB needs 75 hours / 38 % * 100 = 197.4 Hours or 10.13 MB per hour.

            If I can make this call per server in the fleet, I simply have to add each result to know how much data is created per hour over the entire fleet at the moment of the call.

            Why I would want to know this number ... I can give you many reasons but without it... it is just another dream..

            Maybe someone will get the inception ...

              • Re: Are there SDK calls that can retrieve the Archive Properties?
                Rick Davin

                If you are the intended audience for your custom app, then PowerShell cmdlets may have some of that information.  Or you would be able to use your privileged access to run on the PI Data Archive itself, where the OS would have access to the file system.

                • Re: Are there SDK calls that can retrieve the Archive Properties?
                  Eugene Lee

                  The Powershell cmdlet Get-PIArchiveFileInfo might have just what you need.

                   

                  2 of 2 people found this helpful
                    • Re: Are there SDK calls that can retrieve the Archive Properties?
                      Rick Davin

                      UPDATE: What follows below is one way, but slightly roundabout.  There's got to be a better way.  Rather than dumping a single PS command to the console window, which forces everything to string, you should be able to capture the object the Get-PIArchiveFileInfo cmdlet sends.  Somewhere inside that cmdlet is the actual object type (UInt32, bool, etc.). 

                       

                      Following up on Eugene Lee 's post, here's a Proof-of-Concept code snippet to show how to invoke the PowerShell cmdlet in a C# application.  As the code comments state, there are a LOT OF ASSUMPTIONS to make this code work.  The output will be a List in archive index order.  So this reads all archives, with each one being a separate list item.  The contents of each list item is a case-insensitive dictionary where the Key is the trimmed string to the left of the first colon, and the Value is the trimmed text to the right of the first colon.  Note that date & time strings will contain additional colons.  Remember we are parsing console output, and that output is TEXT STRINGS.  If you want to parse into type-specific objects, that exercise is left to you.

                       

                       

                      // The index of the list corresponds to the archive index.
                      public static IList<IDictionary<string, string>> GetPIArchiveFileInfo(string piDataArchiveName)
                      {
                          //=======================================================================================
                          // This assumes A LOT and is offered as Proof-of-Concept and/or Learning Example.
                          //    * OSIsoft PowerShell cmdlet's have been loaded 
                          //    * powershell.exe resides on the PATH
                          //    * executing user has PIAdmin permissions
                          //---------------------------------------------------------------------------------------
                          // It is left to the reader to test and debug further and to add better error checking.
                          // It is also left to the reader to assign type-specific values, e.g. string "False"
                          // to a Boolean false.
                          //=======================================================================================
                      
                          string script = $"Get-PIArchiveFileInfo -Connection (Connect-PIDataArchive -PIDataArchiveMachineName {piDataArchiveName})";
                      
                          string[] lines = null;
                      
                          using (Process process = new Process())
                          {
                              process.StartInfo = new ProcessStartInfo()
                              {
                                  UseShellExecute = false,
                                  RedirectStandardOutput = true,
                                  WindowStyle = ProcessWindowStyle.Hidden,
                                  CreateNoWindow = false,
                                  FileName = "powershell.exe",
                                  Arguments = " /C " + script
                              };
                      
                              process.Start();
                              lines = process.StandardOutput
                                              .ReadToEnd()
                                              .Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
                              process.WaitForExit();
                              process.Close();
                          }
                      
                          List<IDictionary<string, string>> list = new List<IDictionary<string, string>>();
                          Dictionary <string, string> dict = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
                      
                          foreach (string line in lines)
                          {
                              // Blank lines separate archive entries.
                              // Be sure to write previous dictionary to the output list.
                              if (string.IsNullOrWhiteSpace(line))
                              {
                                  if (dict.Count > 0) // flush to list
                                  {
                                      list.Add(dict);
                                      dict = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
                                  }
                                  continue;
                              }
                      
                              string[] tokens = line.Split(new char[] { ':' }, count: 2);
                              // Both the Key and Value are strings and need to be trimmed.
                              // It is left to reader to parse values into type-specific objects.
                              dict[tokens[0].Trim()] = (tokens.Length == 2) ? tokens[1].Trim() : null;
                          }
                      
                          if (dict.Count > 0) // flush last archive dict to output list
                          {
                              list.Add(dict);
                          }
                      
                          return list;
                      }
                      

                       

                       

                      Other than peeking at the output list in my Locals windows, I have not attempted, nor will I attempt, even a tiny bit of testing or debugging.  So USE AT YOUR OWN RISK.

                       

                      If you do attempt to parse into type-specific objects, keep in mind a few cautions:

                      • What you think looks like an int (Int32) may very likely be a long (Int64) because some values can be greater than Int32.MaxValue.
                      • When parsing date and times, consider the DateTimeKind.  As is, it is a text string.  AFTime would parse that as Local time.  DateTime would parse it with Kind of Unspecified, which may not be what you want.

                       

                      My recommendation is that you should perform most of the processing in a PowerShell script.  Not everything has to be a .NET executable.  It is possible to run the PS script as a scheduled task, if that's something you would need.

                        • Re: Are there SDK calls that can retrieve the Archive Properties?
                          Rick Davin

                          What makes this difficult, and again why I consider my code gymnastics to be a half-baked idea, is because trying to sync up output from PowerShell inside a .NET application requires either (a) working from strings at the console window, or (b) even greater code gymnastics to perform interprocess communications (perhaps pipes or WCF).  All this is easier to deal with simply inside a PowerShell script.

                           

                          With PowerShell, a curious person may check the return type from Get-PIArchiveFileInfo.  It's type is Object[], where each item in the object array is info for a different archive index.  If you then check the data type of each item, you discover it is PIArchiveFileInfo17, which is based upon OSIsoft.PI.Net.PIArchiveFileInfo16.  Note that we do not recommend you ever work directly with the OSIsoft.PI.Net.dll, which you may find difficult anyway since the methods are completely UNDOCUMENTED.

                           

                          For example, if against our advice you attempted to reference the OSIsoft.PI.Net.dll into your C# project, and then tried to create an instance for PIArchiveFileInfo17, you will get a message that it does not take 0 arguments in a constructor.  Okay, then how many arguments does it take, and what type are they, and in what order?  Remember that UNDOCUMENTED blurb above.  Well, that class is undocumented so it essentially useless to you.

                           

                          Since PowerShell already has the PIArchiveFileInfo17 class defined, if you did all your processing inside of PowerShell then you would be able to use the class with its type-specific properties.  That or you would have to meticulously duplicate that class in C# with the understanding that future releases of OSIsoft.PowerShell or AF SDK may include a change to class and cmdlet.

                      • Re: Are there SDK calls that can retrieve the Archive Properties?
                        gregor

                        Hello Francis,

                         

                        If you intend asking an enhancement, please consider posting an enhancement request at uservoice.

                         

                        The information you are looking for definitely belongs to the category PI System Management. For this reason, I believe a request to enhance information available through PI (Archive) Subsystem Performance Counters has higher chances than the ask to make the information available through AF SDK. By the way, one of the great things with Windows Performance Counters is that they also can be collected remotely if the user account used for the purpose is member of the local "Performance Monitor Users" group.

                         

                        I am also a little uncertain if the way you approach the challenge is the right one. It may be nice to know the size of the primary archive but what the value of this information if a PI Data Archive has empty archives prepared to shift into or - which we hope will not be the case - is configured to overwrite the oldest historical archive at the next shift. What if someone decides to copy a larger amount of files to the drive hosting the archive files of a PI Data Archive node? Wouldn't it make more sense to monitor the available disk space (through Windows Performance Counters) and having PI Notifications set up to alert about the available disk space dropping below a certain threshold?

                         

                        If you are looking for a programmatic approach to get detailed information about the primary archive, invoking the Get-PIArchiveFileInfo as suggested by Eugene Lee is likely the best option.