26 Replies Latest reply on Aug 7, 2017 7:19 PM by Saken

    Error message when try to get all attributes in DB have analysis

    Saken

      Hi,

      I have following code to get all attributes in DB have analysis, see attachment code:

       

           static void Main(string[] args)
              {
                  var piSystems = new PISystems();
                  var piSystem = piSystems["pi_server_name"];
                  piSystem.Connect();
                  var db = piSystem.Databases["Machine Reliability"];
                  var analysisOutputs = new List<AFAttribute>();
                  AFNamedCollectionList<OSIsoft.AF.Analysis.AFAnalysis> search = OSIsoft.AF.Analysis.AFAnalysis.FindAnalyses(db, "*", AFSearchField.Name, AFSortField.Name, AFSortOrder.Ascending, Int32.MaxValue);
      
                  foreach (var analysis in search)
                  {
                      var configuration = analysis.AnalysisRule.GetConfiguration();
                      foreach (var output in configuration.ResolvedOutputs)
      
                      {
                          analysisOutputs.Add((AFAttribute)output.Attribute);
                      }
                  }
      
                  foreach (var attribute in analysisOutputs)
                  {
                      Console.WriteLine(attribute.Name);
                  }
              }
      

       

       

      And it retrieves data but stops with message, see attachment console output:

       

      Area Availability Average

      { cut to reduce list }

      Area Availability Average

      Area Reliability Average

      { cut to reduce list }

      Area Runtime Average

      Area Trips Total Count

      { cut to reduce list }

      Area Trips Total Count

       

       

      Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.

         at PI_project.Program.Main(String[] args) in c:\users\mskn\documents\visual studio 2017\Projects\PI project\PI project\Program.cs:line 36

      Press any key to continue . . .

       

       

      please advice what's wrong here.

       

      Also how to get full path to element attributes in result?

       

       

      thanks,

      S

        • Re: Error message when try to get all attributes in DB have analysis
          pmartin

          Hi Saken,

           

          The error you are seeing is a NullReferenceException.  This means that one or more of the values in your analysisOutput array is a null value.  I suspect that this is because you have an AnalysisRule that is not mapped to an Attribute.  In order to avoid this, you should ensure the value of analysis is not null before writing to the analysisOutput array.

           

          As for getting the path - both attributes and analyses have a GetPath() method that returns the string path to the attribute or analysis.

          3 of 3 people found this helpful
            • Re: Error message when try to get all attributes in DB have analysis
              Saken

              actually what I need is to retrieve analysis have no PI point mapped to attribute, can someone point me to af sdk code example to get such result.

               

              thanks,

              S

                • Re: Error message when try to get all attributes in DB have analysis
                  Rick Davin

                  Hi Saken,

                   

                  Each analysis has an AnalysisRule property which amazingly enough returns the AFAnalysisRule object used for that analysis.  Each AnalysisRule has a GetOutputs method.  Additionally, the AnalysisRule has a GetOutputsForAnalyses and GetOutputsForAnalysisTemplates methods. 

                   

                  Anyway, if you call GetOutputs you get back an AFAttributeList of the attributes that are mapped as outputs.  You would then check each attribute to check the attribute's DataReferencePlugin property.  If none of the attributes use the AFDataReference.GetPIPointDataReference plugin, you would then consider that analysis to have no PIPoint outputs.

                    • Re: Error message when try to get all attributes in DB have analysis
                      Saken

                      thanks Rick,

                      can you give code example?

                        • Re: Error message when try to get all attributes in DB have analysis
                          Rick Davin

                          Hi Saken,

                           

                          I'm not exactly clear on what it is you really want to do.  For my code snippet, I will not loop every AFAnalysis, but rather the AnalysisTemplates.  This should be a shorter list to process and much quicker.  If I have 1000 analysis instances on the same analysis template, I really only need to know the about the output on the one template.   Furthermore, since you are omitting outputs that are mapped as PIPoint, I have included which DataReferencePlugIn is in effect.

                           

                          public void ScanForNonPIPointOutputs(AFDatabase database)
                          {
                              var search = new AFAnalysisTemplateSearch(database, "All AnalysisTemplates", "*");
                              search.CacheTimeout = TimeSpan.FromMinutes(5);
                          
                          
                              var analyses = search.FindAnalysisTemplates();
                              var keyset = AFAnalysisRule.GetOutputsForAnalysisTemplates(analyses);
                              var dict = keyset.Results;
                          
                          
                              Console.WriteLine("Analysis Outputs not mapped to a PIPoint");
                              var found = 0;
                              foreach (var pair in dict)
                              {
                                  var attributeTemplate = pair.Key;
                                  if (attributeTemplate.DataReferencePlugIn == AFDataReference.GetPIPointDataReference())
                                  {
                                      continue; //to next pair
                                  }
                                  found++;
                                  var analysisTemplate = pair.Value;
                                  var elementTemplate = analysisTemplate.Target;
                                  Console.WriteLine();
                                  Console.WriteLine($"AnalysisTemplate:       '{analysisTemplate.Name}'");
                                  Console.WriteLine($"   ElementTemplate:     '{elementTemplate.GetPath(database)}'");
                                  Console.WriteLine($"   AttributeTemplate:   '{attributeTemplate.GetPath(elementTemplate)}'");
                                  Console.WriteLine($"   DataReferencePlugIn: '{attributeTemplate.DataReferencePlugIn?.Name}'");
                              }
                              Console.WriteLine();
                              Console.WriteLine($"Found = {found}");
                          }
                          

                           

                           

                          If you are using AF 2.9.x or later, there are 2 things to mention: (1) Good for you!, and (2) the AFSearch objects are now disposable.  Therefore, this code would be preferred for AF 2.9.x or later.

                           

                          public void ScanForNonPIPointOutputs(AFDatabase database)
                          {
                              using (var search = new AFAnalysisTemplateSearch(database, "All AnalysisTemplates", "*"))
                              {
                                  search.CacheTimeout = TimeSpan.FromMinutes(5);
                          
                                  var analyses = search.FindAnalysisTemplates();
                                  var keyset = AFAnalysisRule.GetOutputsForAnalysisTemplates(analyses);
                                  var dict = keyset.Results;
                          
                                  Console.WriteLine("Analysis Outputs not mapped to a PIPoint");
                                  var found = 0;
                                  foreach (var pair in dict)
                                  {
                                      var attributeTemplate = pair.Key;
                                      if (attributeTemplate.DataReferencePlugIn == AFDataReference.GetPIPointDataReference())
                                      {
                                          continue; //to next pair
                                      }
                                      found++;
                                      var analysisTemplate = pair.Value;
                                      var elementTemplate = analysisTemplate.Target;
                                      Console.WriteLine();
                                      Console.WriteLine($"AnalysisTemplate:       '{analysisTemplate.Name}'");
                                      Console.WriteLine($"   ElementTemplate:     '{elementTemplate.GetPath(database)}'");
                                      Console.WriteLine($"   AttributeTemplate:   '{attributeTemplate.GetPath(elementTemplate)}'");
                                      Console.WriteLine($"   DataReferencePlugIn: '{attributeTemplate.DataReferencePlugIn?.Name}'");
                                  }
                                  Console.WriteLine();
                                  Console.WriteLine($"Found = {found}");
                              }
                          }
                          
                          • Re: Error message when try to get all attributes in DB have analysis
                            Rick Davin

                            Hi Saken,

                             

                            I did some more digging and discovered that GetOutputs method is a holdover for Notifications 1.x.  Your original code was very, very close and you were using the preferred calls.  What you were missing is allowing an output to not be an attribute, that is to say the AFAnalysisRuleResolvedOutput object's Attribute property can be null.  That's because outputs can vary (see AFAnalysisOutputType enumeration) as well as be intermediate outputs that feed into other calculations.  Here is a reworking:

                             

                            public void ScanForNonPIPointOutputs2(AFDatabase database)
                            {
                                // First build a dictionary keyed by Analysis containing output attributes that aren't using PI Point data reference.
                                var analysisOutputs = new Dictionary<AFAnalysis, List<AFAttribute>>();
                            
                                using (var search = new AFAnalysisSearch(database, "All Analyses", "*"))
                                {
                                    search.CacheTimeout = TimeSpan.FromMinutes(5);
                                    var analyses = search.FindAnalyses(fullLoad: true);
                                    foreach (var analysis in analyses)
                                    {
                                        var configuration = analysis.AnalysisRule.GetConfiguration();
                                        var sublist = new List<AFAttribute>();
                                        foreach (var output in configuration.ResolvedOutputs)
                                        {
                                            var attribute = output.Attribute as AFAttribute;
                                            if (attribute != null && attribute.DataReferencePlugIn != AFDataReference.GetPIPointDataReference())
                                            {
                                                sublist.Add(attribute);
                                            }
                                        }
                                        if (sublist.Count > 0)
                                        {
                                            analysisOutputs.Add(analysis, sublist);
                                        }
                                    }
                                }
                            
                                // Second, display dictionary to console
                                foreach (var pair in analysisOutputs)
                                {
                                    var analysis = pair.Key;
                                    var targetElement = analysis.Target as AFBaseElement;
                                            
                                    Console.WriteLine();
                                    Console.WriteLine($"Analysis:               '{analysis.Name}'");
                                    Console.WriteLine($"   AnalysisTemplate:    '{analysis.Template?.GetPath(database)}'");
                                    Console.WriteLine($"   Target          :    '{targetElement?.GetPath(database)}'");
                            
                                    foreach (var attribute in pair.Value)
                                    {
                                        AFObject relative = (AFObject)targetElement ?? database;
                                        Console.WriteLine($"   AttributeTemplate:   '{attribute.GetPath(relative)}'");
                                        Console.WriteLine($"   DataReferencePlugIn: '{attribute.DataReferencePlugIn?.Name}'");
                                    }
                                }
                            }
                            

                             

                             

                            You will observe that line 17 is where I check to (a) make sure the attribute is NOT null, and (2) that it does not reference the PI Point data reference.

                             

                            I was using AF 2.9.1, where AFSearch is now disposable.  That's why line 06 is a using.  If you are on AF 2.8, you would have to change line 06, 07, and 46.

                            2 of 2 people found this helpful
                              • Re: Error message when try to get all attributes in DB have analysis
                                Saken

                                Thanks a lot Rick for code,

                                I was trying to run from VB C# and here are the errors I get, see attachment code below.

                                 

                                errors.png

                                 

                                using System;
                                using System.Collections.Generic;
                                using System.Linq;
                                using System.Text;
                                using System.Threading.Tasks;
                                using OSIsoft.AF;
                                using OSIsoft.AF.Asset;
                                
                                namespace PI_project
                                {
                                    class Program
                                    {
                                        static void Main(string[] args)
                                        {
                                            var piSystems = new PISystems();
                                            var piSystem = piSystems["our_pi_serevr_name"];
                                            piSystem.Connect();
                                            var db = piSystem.Databases["our_db_name"];
                                            AFNamedCollectionList<OSIsoft.AF.Analysis.AFAnalysis> search = OSIsoft.AF.Analysis.AFAnalysis.FindAnalyses(db, "*", AFSearchField.Name, AFSortField.Name, AFSortOrder.Ascending, Int32.MaxValue);
                                
                                            public void ScanForNonPIPointOutputs2(AFDatabase database)
                                            {
                                                // First build a dictionary keyed by Analysis containing output attributes that aren't using PI Point data reference.
                                                var analysisOutputs = new Dictionary<AFAnalysis, List<AFAttribute>>();
                                
                                                using (var search = new AFAnalysisSearch(database, "All Analyses", "*"))
                                                {
                                                    search.CacheTimeout = TimeSpan.FromMinutes(5);
                                                    var analyses = search.FindAnalyses(fullLoad: true);
                                                    foreach (var analysis in analyses)
                                                    {
                                                        var configuration = analysis.AnalysisRule.GetConfiguration();
                                                        var sublist = new List<AFAttribute>();
                                                        foreach (var output in configuration.ResolvedOutputs)
                                                        {
                                                            var attribute = output.Attribute as AFAttribute;
                                                            if (attribute != null && attribute.DataReferencePlugIn != AFDataReference.GetPIPointDataReference())
                                                            {
                                                                sublist.Add(attribute);
                                                            }
                                                        }
                                                        if (sublist.Count > 0)
                                                        {
                                                            analysisOutputs.Add(analysis, sublist);
                                                        }
                                                    }
                                                }
                                
                                                // Second, display dictionary to console
                                                foreach (var pair in analysisOutputs)
                                                {
                                                    var analysis = pair.Key;
                                                    var targetElement = analysis.Target as AFBaseElement;
                                
                                                    Console.WriteLine();
                                                    Console.WriteLine($"Analysis:               '{analysis.Name}'");
                                                    Console.WriteLine($"   AnalysisTemplate:    '{analysis.Template?.GetPath(database)}'");
                                                    Console.WriteLine($"   Target          :    '{targetElement?.GetPath(database)}'");
                                
                                                    foreach (var attribute in pair.Value)
                                                    {
                                                        AFObject relative = (AFObject)targetElement ?? database;
                                                        Console.WriteLine($"   AttributeTemplate:   '{attribute.GetPath(relative)}'");
                                                        Console.WriteLine($"   DataReferencePlugIn: '{attribute.DataReferencePlugIn?.Name}'");
                                                    }
                                                }
                                            }
                                
                                        }
                                    }
                                }
                                
                                  • Re: Error message when try to get all attributes in DB have analysis
                                    Rick Davin

                                    I would strongly encourage you to understand which language you are coding in.  You said VB but it is C#.

                                     

                                    Add required usings:

                                     

                                    using OSIsoft.AF.Analysis;

                                    using OSIsoft.AF.Search;

                                     

                                    Also the problem is not the public access modifier for method ScanForNonPIPointOutputs2.  Rather it's that you've tried embedding a method INSIDE another method (Main).   Move ScanForNonPIPointOutputs2 outside of Main, and it should work.

                                      • Re: Error message when try to get all attributes in DB have analysis
                                        Saken

                                        Rick,

                                        it's C# inside visual basic project.

                                        I confused where main part supposed to be with all server name and db declared.

                                        Can you attach whole code?

                                         

                                        thanks,

                                        S

                                          • Re: Error message when try to get all attributes in DB have analysis
                                            Rick Davin

                                            Hello Saken,

                                             

                                            Your code is C# inside a Visual Studio project.  I did include all the code you attached to the example_ana_no_pipoint.txt.zip file above. 

                                              • Re: Error message when try to get all attributes in DB have analysis
                                                Saken

                                                Rick,

                                                I still don't see changes in file, can you attach as new file?

                                                 

                                                thanks

                                                  • Re: Error message when try to get all attributes in DB have analysis
                                                    Rick Davin

                                                    Hi Saken,

                                                     

                                                    I think you are confused.  I made NO changes to your attached file.  I merely copied the ENTIRE contents of the file to your post.  If something is missing from your post, it was missing from YOUR OWN file.

                                                     

                                                    As you are a developer, particularly a C# developer, I am sure you are finely aware that with your code little things matter in big ways.  You must agree with this point, right?  C# is case sensitive, so capitalization matters.  Spelling of objects mater.  Every blank, comma, period, or semi-colon could make a big difference.  Along those lines, every opening or closing brace matters tremendously.  Check the closing brace for your Main().  Then move the ScanForNonPIPointOutputs2 method to be outside of that closing brace.

                                                    1 of 1 people found this helpful
                                                      • Re: Error message when try to get all attributes in DB have analysis
                                                        Saken

                                                        Hi Rick.

                                                        thanks for reply, yea, you are right, I absolutely agree.

                                                        Sorry for confusion but it seems this is not what I was looking for, I try to get  list of analysis have attributes where values like 'PI Point not found',

                                                        see attachment.

                                                         

                                                        PI point not found.png

                                                         

                                                        Saken

                                                          • Re: Error message when try to get all attributes in DB have analysis
                                                            Rick Davin

                                                            Well that does change things a bit.

                                                             

                                                            public void ScanForOutputsMissingPIPoints(AFDatabase database)
                                                            {
                                                                // First build a dictionary keyed by Analysis containing output attributes that aren't using PI Point data reference.
                                                                var analysisOutputs = new Dictionary<AFAnalysis, List<AFAttribute>>();
                                                            
                                                                using (var search = new AFAnalysisSearch(database, "All Analyses", "*"))
                                                                {
                                                                    search.CacheTimeout = TimeSpan.FromMinutes(5);
                                                                    var analyses = search.FindAnalyses(fullLoad: true);
                                                                    foreach (var analysis in analyses)
                                                                    {
                                                                        var configuration = analysis.AnalysisRule.GetConfiguration();
                                                                        var sublist = new List<AFAttribute>();
                                                                        foreach (var output in configuration.ResolvedOutputs)
                                                                        {
                                                                            var attribute = output.Attribute as AFAttribute;
                                                                            if (attribute != null
                                                                                && attribute.DataReferencePlugIn == AFDataReference.GetPIPointDataReference()
                                                                                && attribute.PIPoint == null)
                                                                            {
                                                                                sublist.Add(attribute);
                                                                            }
                                                                        }
                                                                        if (sublist.Count > 0)
                                                                        {
                                                                            analysisOutputs.Add(analysis, sublist);
                                                                        }
                                                                    }
                                                                }
                                                            
                                                                // Second, display dictionary to console
                                                                foreach (var pair in analysisOutputs)
                                                                {
                                                                    var analysis = pair.Key;
                                                                    var targetElement = analysis.Target as AFBaseElement;
                                                            
                                                                    Console.WriteLine();
                                                                    Console.WriteLine($"Analysis:              {analysis.Name}");
                                                                    Console.WriteLine($"  AnalysisTemplate:    {analysis.Template?.GetPath(database)}");
                                                                    Console.WriteLine($"  Target          :    {targetElement?.GetPath(database)}");
                                                            
                                                                    foreach (var attribute in pair.Value)
                                                                    {
                                                                        AFObject relative = (AFObject)targetElement ?? database;
                                                                        Console.WriteLine($"  Attribute      :    {attribute.GetPath(relative)}");
                                                                        Console.WriteLine($"  ConfigString    :    {attribute.ConfigString}");
                                                                    }
                                                                }
                                                            }
                                                            

                                                             

                                                             

                                                            I removed single quotes from lines 38-46.  Since all items should be PI points, there is no need to display the data reference.  However, since these would be "problem" PI points, I am displaying the respective ConfigString.

                                                             

                                                            The biggest change would be lines 17-19 as this is what filters the list.

                                                            Line 17 - attribute cannot be null

                                                            Line 18 - attribute must use PIPoint Data Reference

                                                            Line 19 - and resulting attribute.PIPoint cannot be null

                                                             

                                                            I leave it to you to integrate this into your current Visual Studio project as you deem most appropriate.

                                                              • Re: Error message when try to get all attributes in DB have analysis
                                                                Mike Zboray

                                                                Note that accessing each attribute's PIPoint property will make a remote call for each attribute. If there are thousands of attributes you may want to build an AFAttributeList and use GetPIPoint to load them in bulk.

                                                                1 of 1 people found this helpful
                                                                • Re: Error message when try to get all attributes in DB have analysis
                                                                  Saken

                                                                  Thanks for code Rick,

                                                                  when I run with new code it gives following error message, see attachment.

                                                                   

                                                                  thanks Kenji,

                                                                  I still want to get Rick's code working but you still can research how to get analysis status.

                                                                   

                                                                   

                                                                  Saken

                                                                    • Re: Error message when try to get all attributes in DB have analysis
                                                                      Kenji Hashimoto

                                                                      I found following post to get a status of analysis. Thank you Patrice Thivierge

                                                                      https://pisquare.osisoft.com/message/72925-re-calc-failed-output-error-message-through-afsdk#comment-72925

                                                                      This is my example code.

                                                                      var state = new AFAnalysisRuleState(analysis.AnalysisRule.GetConfiguration());
                                                                      analysis.AnalysisRule.Run(state);
                                                                      

                                                                      After running the code, state.EvaluationError shows Error message.

                                                                      You can use if sentence to check there are an error.

                                                                      if (state.EvaluationError != null)

                                                                        • Re: Error message when try to get all attributes in DB have analysis
                                                                          Kenji Hashimoto

                                                                          Adding, for avoiding an error in the code, 

                                                                          You can use Try and catch method to avoid the code stopping.

                                                                          https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-catch

                                                                           

                                                                          (Screenshot is Saken's error message)

                                                                            • Re: Error message when try to get all attributes in DB have analysis
                                                                              Rick Davin

                                                                              Dang.  Mike Zboray recommend bulk calls for performance, but there's another advantage to bulk calls: I seem to recall the individual call can fail but the bulk call suppresses the exception.

                                                                               

                                                                              Before we do more code, let's back up just a bit.  On one hand, we have a bunch of attributes that aren't resolving properly to a PIPoint.  Saken is interested in these particular attributes because they just happen to be outputs to an analysis.  But do we really need to impose the restriction that it be an analysis output?  What about smurfed-up PIPoints that aren't an analysis output?  We would catch all the problem attributes if we scanned the full element hierarchy directly without regard to analyses.

                                                                               

                                                                              If someone came to me and said "find me all PIPoint attributes that aren't properly mapped back to PI", not only would I scan all elements of the database (again, without concern for analyses), I would also output to a tab-delimited CSV file that I could later import into Excel.

                                                                                • Re: Error message when try to get all attributes in DB have analysis
                                                                                  Rick Davin

                                                                                  I created an entire new class.  You may want to change the namespace accordingly.

                                                                                   

                                                                                  using System;
                                                                                  using System.Collections.Generic;
                                                                                  using System.Linq;
                                                                                  using System.Text;
                                                                                  using System.Threading.Tasks;
                                                                                  
                                                                                  using OSIsoft.AF;
                                                                                  using OSIsoft.AF.PI;
                                                                                  using OSIsoft.AF.Asset;
                                                                                  using OSIsoft.AF.Search;
                                                                                  using System.IO;
                                                                                  
                                                                                  namespace PISquare_Example
                                                                                  {
                                                                                      public class MisconfiguredPIPointsSearch
                                                                                      {
                                                                                          public AFDatabase Database { get; }
                                                                                  
                                                                                          public const int ElementsPageSize = 1000;
                                                                                          public const int AttributesPageSize = 5000;
                                                                                          public const char DefaultColumnDelimiter = '\t';
                                                                                  
                                                                                          public MisconfiguredPIPointsSearch(AFDatabase database)
                                                                                          {
                                                                                              Database = database;
                                                                                          }
                                                                                  
                                                                                          public static IDictionary<AFElement, IList<AFAttribute>> Crawl(AFDatabase database) => new MisconfiguredPIPointsSearch(database).Crawl();
                                                                                  
                                                                                          public static void SaveCrawlToFile(AFDatabase database, string csvFilename, char delimiter = DefaultColumnDelimiter) => new MisconfiguredPIPointsSearch(database).SaveCrawlToFile(csvFilename, delimiter);
                                                                                  
                                                                                          public void SaveCrawlToFile(string csvFilename, char delimiter = DefaultColumnDelimiter)
                                                                                          {
                                                                                              var dict = Crawl();
                                                                                              using (var writer = new StreamWriter(csvFilename, append: false))
                                                                                              {
                                                                                                  //Write header row
                                                                                                  writer.WriteLine("Element Path{0}Attribute Path{0}ConfigString", delimiter);
                                                                                                  foreach (var kvp in dict)
                                                                                                  {
                                                                                                      var element = kvp.Key;
                                                                                                      foreach (var attribute in kvp.Value)
                                                                                                      {
                                                                                                          writer.WriteLine("{1}{0}{2}{0}{3}", delimiter, element.GetPath(Database), attribute.GetPath(element), attribute.ConfigString);
                                                                                                      }
                                                                                                  }
                                                                                              }
                                                                                          }
                                                                                  
                                                                                          public IDictionary<AFElement, IList<AFAttribute>> Crawl()
                                                                                          {
                                                                                              IDictionary<AFElement, IList<AFAttribute>> dict = new Dictionary<AFElement, IList<AFAttribute>>();
                                                                                              var attributes = new List<AFAttribute>();
                                                                                           
                                                                                              using (var search = new AFElementSearch(Database, "", ""))
                                                                                              {
                                                                                                  search.CacheTimeout = TimeSpan.FromMinutes(10);
                                                                                                  var elements = search.FindElements(fullLoad: true, pageSize: ElementsPageSize);
                                                                                                  foreach (var element in elements)
                                                                                                  {
                                                                                                      var sublist = GetAttributesUsingPIPointDR(element.Attributes);
                                                                                                      if (sublist != null && sublist.Count > 0)
                                                                                                      {
                                                                                                          attributes.AddRange(sublist);
                                                                                                          if (attributes.Count >= AttributesPageSize)
                                                                                                          {
                                                                                                              ApplyBulkPIPointValidation(dict, attributes);
                                                                                                              attributes = new List<AFAttribute>();
                                                                                                          }
                                                                                                      }
                                                                                                  }
                                                                                              }
                                                                                  
                                                                                              if (attributes.Count > 0)
                                                                                              {
                                                                                                  ApplyBulkPIPointValidation(dict, attributes);
                                                                                                  attributes = null;
                                                                                              }
                                                                                  
                                                                                              RemoveEmptyEntries(dict);
                                                                                  
                                                                                              return dict;
                                                                                          }
                                                                                  
                                                                                          // Don't think we need this.  Including it just in case ...
                                                                                          private static void RemoveEmptyEntries(IDictionary<AFElement, IList<AFAttribute>> dictionary)
                                                                                          {
                                                                                              if (dictionary == null || dictionary.Count == 0)
                                                                                              {
                                                                                                  return;
                                                                                              }
                                                                                              // ToList() required to produce independent list of keys
                                                                                              var keys = dictionary.Keys.ToList();
                                                                                              foreach (var key in keys)
                                                                                              {
                                                                                                  var innerList = dictionary[key];
                                                                                                  if (innerList == null || innerList.Count == 0)
                                                                                                  {
                                                                                                      dictionary.Remove(key);
                                                                                                  }
                                                                                              }
                                                                                          }
                                                                                  
                                                                                          private static List<AFAttribute> GetAttributesUsingPIPointDR(IEnumerable<AFAttribute> attributes)
                                                                                          {
                                                                                              var list = new List<AFAttribute>();
                                                                                              foreach (var attribute in attributes)
                                                                                              {
                                                                                                  if (UsesPIPointDR(attribute))
                                                                                                  {
                                                                                                      list.Add(attribute);
                                                                                                  }
                                                                                                  if (attribute.HasChildren)
                                                                                                  {
                                                                                                      var sublist = GetAttributesUsingPIPointDR(attribute.Attributes);
                                                                                                      if (sublist.Count > 0)
                                                                                                      {
                                                                                                          list.AddRange(sublist);
                                                                                                      }
                                                                                                  }
                                                                                              }
                                                                                              return list;
                                                                                          }
                                                                                  
                                                                                          private static bool UsesPIPointDR(AFAttribute attribute) => attribute.DataReferencePlugIn == AFDataReference.GetPIPointDataReference();
                                                                                  
                                                                                          private static void ApplyBulkPIPointValidation(IDictionary<AFElement, IList<AFAttribute>> dict, IEnumerable<AFAttribute> attributes)
                                                                                          {
                                                                                              var bulk = new AFAttributeList(attributes);
                                                                                              var map = bulk.GetPIPoint();
                                                                                              foreach (var kvp in map.Results)
                                                                                              {
                                                                                                  // kvp.Value is the PIPoint
                                                                                                  if (kvp.Value != null)
                                                                                                  {
                                                                                                      // The PIPoint is good so we can remove it from the list.
                                                                                                      bulk.Remove(kvp.Key);
                                                                                                  }
                                                                                              }
                                                                                              if (bulk.Count > 0)
                                                                                              {
                                                                                                  TransferToDictionary(dict, bulk);
                                                                                              }
                                                                                          }
                                                                                  
                                                                                          private static void TransferToDictionary(IDictionary<AFElement, IList<AFAttribute>> dict, IEnumerable<AFAttribute> attributes)
                                                                                          {
                                                                                              foreach (var attribute in attributes)
                                                                                              {
                                                                                                  var element = (AFElement)attribute.Element;
                                                                                                  IList<AFAttribute> innerList = null;
                                                                                                  if (!dict.TryGetValue(element, out innerList))
                                                                                                  {
                                                                                                      innerList = new List<AFAttribute>();
                                                                                                  }
                                                                                                  innerList.Add(attribute);
                                                                                                  dict[element] = innerList;
                                                                                              }
                                                                                          }
                                                                                      }
                                                                                  }
                                                                                  

                                                                                   

                                                                                   

                                                                                  The most convenient way to call it would be:

                                                                                   

                                                                                  AFDatabase db = something valid;

                                                                                  string outputFilename = "bogusTags.csv"; // or whatever you prefer

                                                                                   

                                                                                  MisconfiguredPIPointsSearch.SaveCrawlToFile(db, outputFilename);

                                                                                  3 of 3 people found this helpful
                                                                                    • Re: Error message when try to get all attributes in DB have analysis
                                                                                      Saken

                                                                                      thanks Rick,

                                                                                      that gives Attributes with "PI Point not found"  but how I can get list of analysis attached to these attributes?

                                                                                       

                                                                                      thanks,

                                                                                      S

                                                                                        • Re: Error message when try to get all attributes in DB have analysis
                                                                                          Rick Davin

                                                                                          Hi Saken,

                                                                                           

                                                                                          You could modify the code I presented on July 27:

                                                                                           

                                                                                          • Create a Hashset<AFAttribute> for all attributes that use the PIPoint DR but also have a null PIPoint property.
                                                                                            • This is more efficient for lookups than a List
                                                                                            • You can extract the Element property from an attribute
                                                                                            • You can extract the Database property from the first attribute in the HashSet

                                                                                           

                                                                                          UPDATE: You should add 2 methods to July 27 code.

                                                                                           

                                                                                          public static HashSet<AFAttribute> CrawlToHashSet(AFDatabase database)
                                                                                          {
                                                                                              var instance = new MisconfiguredPIPointsSearch(database);
                                                                                              return instance.ToHashSet(instance.Crawl());
                                                                                          }
                                                                                          
                                                                                          private HashSet<AFAttribute> ToHashSet(IDictionary<AFElement, IList<AFAttribute>> dict)
                                                                                          {
                                                                                              var result = new HashSet<AFAttribute>();
                                                                                              // Both if conditionals are added for safety in case anyone copies the code out of context.
                                                                                              // For the dictionary built via Crawl method, these checks are not needed.
                                                                                              foreach (var entry in dict)
                                                                                              {
                                                                                                  var attributes = entry.Value;
                                                                                                  if (attributes != null)
                                                                                                  {
                                                                                                      foreach (var attribute in attributes)
                                                                                                      {
                                                                                                          if (!result.Contains(attribute))
                                                                                                          {
                                                                                                              result.Add(attribute);
                                                                                                          }
                                                                                                      }
                                                                                                  }
                                                                                              }
                                                                                              return result;
                                                                                          }
                                                                                          

                                                                                           

                                                                                          For example, let's say you did the above and saved it in a variable such as:

                                                                                           

                                                                                          HashSet<AFAttribute> missingPIPoints = MisconfiguredPIPointsSearch.CrawlToHashSet(database);
                                                                                          

                                                                                           

                                                                                          You would use that variable with the code I posted on July 26.  Notably you should change this section:

                                                                                           

                                                                                              foreach (var output in configuration.ResolvedOutputs)
                                                                                              {
                                                                                                  var attribute = output.Attribute as AFAttribute;
                                                                                                  if (attribute != null && missingPIPoints.Contains(attribute))
                                                                                                  {
                                                                                                      sublist.Add(attribute);
                                                                                                  }
                                                                                              }