11 Replies Latest reply on Sep 7, 2012 8:46 PM by fbatista

    Dynamic inclusion of PI tags in a PI ACE calculation

    fbatista

      Hi folks,

       

      I am building a PI ACE calc to perform calculations on tags that are not initially specified in the PI ACE calculation configuration. This is how I am planning it:

       

      - Calculation context is started

       

      - In the initialization phase, it search for tags and build a point list for the tags that match a certain attribute mask (using PI-SDK)

       

      - Every N minutes (clock based), perform the calcs and output the results.

       

      - A separate thread would be continuously searching for new tags to be included in the next calculation time

       

      However, I believe I should leverage the power of PI point caching in ACE, and this is one thing that SDK is not going to give me. Is there any way to add PI ACE points on the fly into the calculation?

       

      Also, I am concerned about performance. The type of calculation (formula used) will depend on some tag attributes. Is it a good practice to read a pipoint attribute every time a perform a calculation or it would be better to store the specific attribute value in arrays (considering that reading an array is usually faster than reading an object attribute)? Any recommendation regarding pipoint attribute reading performance?

       

      Best regards,

       

      Fabiano

       

       

       

       

        • Re: Dynamic inclusion of PI tags in a PI ACE calculation
          Asle Frantzen

          Hi Fabiano

           

          Just a quick word on how I did it when I needed to have a variable amount of input tags per context, for a context based ACE calculation.

           

          I think you need to initialize the number of input tags you're going to use, in the actual code. A good thing here is that you can initialize aliases in the module database. If you create a setup with the maximum number of aliases/tags needed, you can fill PI tags into the aliases you need and just fill a dummy-tag into the rest of them.

           

          Make sure you create a dummy-tag which has some default value, and which is never being updated.

           

          Then you can either create a module database property specifying the number of actual aliases/tags in use, and just use those aliases in your code. Or you can search through all your aliases and just ignore using the ones containing the dummy-tag.

           

          It's not 100% what you need, but maybe you can use it to get one step closer to your goal.

            • Re: Dynamic inclusion of PI tags in a PI ACE calculation
              matthew.rivett

              PI ACE can do anything!  As Asle mentioned depending how you are triggering the calculation this may or may not work for you.  I load tag dynamically and it works fine because it's clock based.  Haven't seen any performance issues.

               

              This example loads aliases from the module database but you could search for tags using the SDK instead.   A bus can have different numbers of generators so it had to be dynamic.

               

              I give up on posting code properly.  I'm not sure what I'm doing wrong...

               

              #Region "INITIALIZATION"

               

               

               

                  Private Sub LoadGenerators()

               

                      ' load all the generators for this bus

               

                      Try

               

               

               

                          ' first I need to get the generator module below the current bus module

               

                          Dim modGenerators As PISDK.PIModule

               

                          modGenerators = PIACEBIFunctions.GetPIModuleFromPath(Context & "\generator")

               

               

               

                          GeneratorsOnThisBus = New GeneratorsOnBus()

               

                          Dim GeneratorOnThisBus As GeneratorOnBus

               

               

               

                          ' loop through all the modules in the generator module 

               

                          ' create a new GeneratorOnBus Object to be used in the vstatt calculation

               

                          ' add it to the collection of all generators on this bus

               

                          For Each modGenerator As PISDK.PIModule In modGenerators.PIModules

               

                              GeneratorOnThisBus = New GeneratorOnBus(New PIACEPoint(Context & "\generator\" & modGenerator.Name, "g_mw"), New PIACEPoint(Context & "\generator\" & modGenerator.Name, "g_mr"), New PIACEPoint(Context & "\generator\" & modGenerator.Name, "hmrlm"), New PIACEPoint(Context & "\generator\" & modGenerator.Name, "lmrlm"))

               

                              GeneratorsOnThisBus.Add(GeneratorOnThisBus)

               

                          Next

               

               

               

                      Catch ex As Exception

               

                          PIACEBIFunctions.LogPIACEMessage(OSIsoft.PI.ACE.MessageLevel.mlErrors, "GPM ACE ERROR in vstatt check " & logFileLocation, Context)

               

                          logMessage("PI ACE Error in """ & Context & """ LoadGenerators" & vbCrLf & vbCrLf & ex.ToString)

               

                      End Try

               

                  End Sub

               

                  Protected Overrides Sub InitializePIACEPoints()

               

                      Try

               

                          av_kv = GetPIACEPoint("av_kv")

               

                          l_kv = GetPIACEPoint("l_kv")

               

                          u_kv = GetPIACEPoint("u_kv")

               

                          vstatt = GetPIACEPoint("vstatt")

               

                      Catch ex As Exception

               

                          logMessage(System.DateTime.Now & " Error initializing PI ACE Points: " & ex.ToString)

               

                      End Try

               

               

               

                  End Sub

               

                  Protected Overrides Sub ModuleDependentInitialization()

               

                      Try

               

                          ' MyBase.CalculationLimit = 1000

               

                          client = New SmtpClient("mail.com")

               

                          previousMessage = System.DateTime.Now

               

                          LoadGenerators()

               

                      Catch ex As Exception

               

                          logMessage(System.DateTime.Now & " Error in ModuleDependentInitialization: " & ex.ToString)

               

                      End Try

               

               

               

                  End Sub

               

              #End Region

               

               

               

               

               

              #Region "GeneratorOnBus Class"

               

              Public Class GeneratorOnBus

               

                  Implements IDisposable

               

                  Private _g_mw As PIACEPoint

               

                  Private _g_mr As PIACEPoint

               

                  Private _hmrlm As PIACEPoint

               

                  Private _lmrlm As PIACEPoint

               

                  Public Sub Dispose() Implements IDisposable.Dispose

               

                      _g_mw.Dispose()

               

                      _g_mr.Dispose()

               

                      _hmrlm.Dispose()

               

                      _lmrlm.Dispose()

               

                      _g_mw = Nothing

               

                      _g_mr = Nothing

               

                      _hmrlm = Nothing

               

                      _lmrlm = Nothing

               

                  End Sub

               

                  Public ReadOnly Property g_mr() As PIACEPoint

               

                      Get

               

                          Return _g_mr

               

                      End Get

               

                  End Property

               

                  Public ReadOnly Property g_mw() As PIACEPoint

               

                      Get

               

                          Return _g_mw

               

                      End Get

               

                  End Property

               

                  Public ReadOnly Property hmrlm() As PIACEPoint

               

                      Get

               

                          Return _hmrlm

               

                      End Get

               

                  End Property

               

                  Public ReadOnly Property lmrlm() As PIACEPoint

               

                      Get

               

                          Return _lmrlm

               

                      End Get

               

                  End Property

               

                  Public Sub New(ByVal PT_g_mw As PIACEPoint, ByVal PT_g_mr As PIACEPoint, ByVal PT_hmrlm As PIACEPoint, ByVal PT_lmrlm As PIACEPoint)

               

                      _g_mw = PT_g_mw

               

                      _g_mr = PT_g_mr

               

                      _hmrlm = PT_hmrlm

               

                      _lmrlm = PT_lmrlm

               

                  End Sub

               

              End Class

               

              #End Region

               

               

               

              #Region "GeneratorsOnBus Collection Class"

               

              <System.Reflection.DefaultMemberAttribute("Item")> _

               

              Public NotInheritable Class GeneratorsOnBus

               

                  Inherits CollectionBase

               

                  Public Sub New()

               

                      MyBase.New()

               

                  End Sub

               

                  Public Property Item(ByVal index As Integer) As GeneratorOnBus

               

                      Get

               

                          Return CType(InnerList(index), GeneratorOnBus)

               

                      End Get

               

                      Set(ByVal Value As GeneratorOnBus)

               

                          InnerList(index) = Value

               

                      End Set

               

                  End Property

               

                  Public Sub Insert(ByVal index As Integer, ByVal GeneratorOnBus As GeneratorOnBus)

               

                      InnerList.Insert(index, GeneratorOnBus)

               

                  End Sub

               

                  Public Function Add(ByVal GeneratorOnBus As GeneratorOnBus) As Integer

               

                      Return InnerList.Add(GeneratorOnBus)

               

                  End Function

               

                  Public Sub Remove(ByVal GeneratorOnBus As GeneratorOnBus)

               

                      InnerList.Remove(GeneratorOnBus)

               

                  End Sub

               

              End Class

               

              #End Region

               

               

                • Re: Dynamic inclusion of PI tags in a PI ACE calculation
                  fbatista

                  Hi Asle and Matt, thank you for your reply. In my case I am trying to build a calculation that does not require any dummy tags to be created in advance. It is a sort of "totalizer" based on ACE basically (so I can have all the benefits of ACE, such as scalability, recalculation, etc). The user will just have to create a regular tag with some specific attributes, and then the PI ACE calculation would pick that tag on the fly. I have coded it using purelly PI-SDK, but using PI ACE I will have the caching feature provided by this tool.

                   

                  My problem was when trying to define a PIACEPoint directly in the code, instead of using a PISDK.Pipoint. For testing purposes, without using the PIACE wizard for the tag creation, I tried to manually define a tag in the "Initailize PIACEPoint() sub. Here is my basic testing code:

                   

                  Public Class testclass

                   

                     Inherits PIACENetClassModule

                   

                     Private mytag As New PIACEPoint

                   

                     '

                   

                     '      Tag Name/VB Variable Name Correspondence Table

                   

                     ' Tag Name                                VB Variable Name

                   

                     ' ------------------------------------------------------------

                   

                     '

                   

                     Public Overrides Sub ACECalculations()

                   

                         mytag.Value = 2

                   

                     End Sub

                   

                     Protected Overrides Sub InitializePIACEPoints()

                   

                         mytag = GetPIACEPoint("sinusoid")

                   

                     End Sub

                   

                     '

                   

                     ' User-written module dependent initialization code

                   

                     '

                   

                     Protected Overrides Sub ModuleDependentInitialization()

                   

                     End Sub

                   

                     '

                   

                     ' User-written module dependent termination code

                   

                     '

                   

                     Protected Overrides Sub ModuleDependentTermination()

                   

                     End Sub

                   

                  End Class

                   

                  When I run the code, I get "Object reference not set to an instance of an object" error, even though the point was instanciated by using the "New" keyword.

                   

                  Any ideas if I can "manually" initialize a point this way"? What I am doing wrong here?

                    • Re: Dynamic inclusion of PI tags in a PI ACE calculation
                      matthew.rivett

                      GetPIACEPoint is looking for a "SourceCodeName" which is created by ACE when you created a new calculation.  

                       

                      7484.sourcecodename.JPG

                       

                      I am beginning to think I was wrong when I said you could do this using the SDK to get tag names and created PIACEPoints.  It looks like you need to use the MDB to use PIACEPoints.  Even if you create a new PIACEPoint like I did in my code you still need to provide an alias in the MDB for it. 

                       

                      My code is dynamic in a way that the number of objects can be different but the objects always contain the same aliases.  I really should have read the posts a little closer.

                       

                      For a calculation that has a different number of inputs we do as Asle suggested.  Create a bunch of aliases and use a placeholder tag in the ones we don't need to use.  It's not great but I don't know of any better way.

                        • Re: Dynamic inclusion of PI tags in a PI ACE calculation

                          Ive built projects that piggyback ACE and dynamically create 1,000's of tags but only ever have one Input...more common than you think amongst clients because of the API fanning.

                           

                          You should be able to create a new PIACEPoint and use the SetTag method (from my memory). Don't create a new instance in the class level variable, that might be your problem here.

                            • Re: Dynamic inclusion of PI tags in a PI ACE calculation
                              matthew.rivett

                              Rhys is correct.  The properties are called Context and Alias but you can use server and tag name instead.

                                 Public Overrides Sub ACECalculations()

                               

                                     Dim ptTest As PIACEPoint

                               

                                     Try

                               

                                         ptTest = New PIACEPoint("\\DOR\", "SINUSOID")

                               

                                         Debug.Print(ptTest.Server)

                               

                                         Debug.Print(ptTest.Tag)

                               

                                         Debug.Print(ptTest.Value)

                               

                                     Catch ex As Exception

                               

                                     End Try

                               

                                 End Sub

                               

                               

                                • Re: Dynamic inclusion of PI tags in a PI ACE calculation
                                  fbatista

                                  Thank you guys for the detailed explanation and example.

                                   

                                  As I mentioned before, PI ACE will build the cache using values for the input tags that are created by the ACE Wizard. If I add the tag manually (using the code provided by Matt), how can we assure that this tag is being considered an input point by ACE (so that its values will be in the cache)? I am not what attribute (if any) ACE uses to inform a PIACEPoint is a input tag.

                                    • Re: Dynamic inclusion of PI tags in a PI ACE calculation
                                      matthew.rivett

                                      I haven't really done any testing with caching.  There's another constructor that you can specify an alias type.  Maybe this is all you need to do?  Someone else will need to verify.

                                       

                                       New PIACEPoint("\\DOR\", "SINUSOID", TagAliasUsedType.TagAliasAsInput)

                                       

                                       

                                        • Re: Dynamic inclusion of PI tags in a PI ACE calculation
                                          fbatista

                                          Thanks Matt. It makes sense for me. I believe this parameter will do the job.

                                            • Re: Dynamic inclusion of PI tags in a PI ACE calculation
                                              nagarwal

                                              Hi Fabiano - As others have pointed you can either use the PIACEPoint constructor or the SetTag() method to dynamically create PIACEPoint instances. You can create PIACEPoints based on tags or aliases. See code below.

                                               

                                               

                                               

                                               

                                               
                                                 Protected Overrides Sub ModuleDependentInitialization()
                                                    'Create PIACEPoint based on a tag using the constructor
                                                    tag1 = New PIACEPoint("\\mypiserver", "sinusoid", TagAliasUsedType.TagAliasAsInput)
                                              
                                                    'Create PIACEPoint based on a tag using the SetTag method
                                                    tag2 = New PIACEPoint()
                                                    tag2.SetTag("\\mypiserver", "sinusoidu", TagAliasUsedType.TagAliasAsInput)
                                              
                                                    'Create PIACEPoint based on an alias relative to the context using the constructor
                                                    'Context="\\mypiserver\Aquariums\Aquarium1"; has an alias named PumpPower
                                                    tag3 = New PIACEPoint(Context, "PumpPower", TagAliasUsedType.TagAliasAsInput)
                                              
                                                    'Create PIACEPoint based on an alias relative to the context using the SetTag method
                                                    'Context="\\mypiserver\Aquariums\Aquarium1"; has an alias named LightPower
                                                    tag4 = New PIACEPoint()
                                                    tag4.SetTag(Context, "LightPower", TagAliasUsedType.TagAliasAsInput)
                                                 End Sub
                                              

                                               Any PIACEPoint instances created using TagAliasUsedType.TagAliasAsInput or TagAliasUsedType.TagAliasAsBoth would automatically be signed up with ACE Cache (provided you have it enabled). Make sure you initialize these instances in the ModuleDependenInitialization() method - as you want them to be created during initialization and signup for updates. 

                                               

                                              Hope this helps.

                                               

                                              Regards,

                                               

                                              Nitin