10 Replies Latest reply on Nov 17, 2009 4:54 PM by spilon

    Basic Unit Conversion from Module


      I'm very new to ACE and trying to develop a very basic calculation where I find all the TEMP tags in a module and convert their units.  Note that I do not plan to use the wizard to import the points since there are a large number of tags. 


      I'm trying to figure out why my DEGC variable is not returning a value even through the MyInputAlias string is valid.  Should be pretty straightforward and probably just missing something obvious since I'm not too familiar with all the properties/methods available yet. 


      Any suggestions are greatly appreciated. 

        • Re: Basic Unit Conversion from Module

          If you thing it may be something straightforward let us cover the basics first


          Have you checked that the Pi point exists?
          Does it have a snapshot value?
          Does it have a recorded value?
          Does the PI ACE application have permissions to see that point?
          Does the point fail in run and in debug/test?

            • Re: Basic Unit Conversion from Module

              Cristobal Escamilla

              let us cover the basics first


              How can you imagine the code like this


              Private MyInput As PIACEPoint
              Private MyResult As PIACEPoint

              Protected Overrides Sub InitializePIACEPoints()
                 MyInput = GetPIACEPoint("MyInput")
                 MyResult = GetPIACEPoint("MyResult")

              End Sub


              might work?
              How might GetPIACEPoint("any string") distinguish between an Alias or a PI Tag or between an Input and an Output point?


              It has to look up the "any string" in the ACE internal Module, related to the context (not in the Context module itself).
              And there it isn't, unfortunately.


              Techsupport knows:


              Problem: Getting NULL returned from GetPIACEPoint

              Customer is using GetPIACEPoint outside of the supported ACE functionality.

              Answer: GetPIACEPoint should only be used by the ACE wizard.

              Brianne's approach looks really convincingly easy, we should dig deeper how to make it (or something similar) work. 

                • Re: Basic Unit Conversion from Module

                  As Michael pointed out, this is not a regular feature of PI ACE and there are other behind-the-scene things that need to be taken care of. See this discussion thread, where we discussed the topic of using tags without using the PI ACE Wizard at length.


                  That said, it seems to me like this is an operation you will want to perform only once, correct? E.g. you changed sensors in the field, they now record in another Unit of Measurement and you want to convert their previously recorded values. Is that a fair assumption?


                  If that's the case, then you might want to look into other techniques than using PI ACE... just a plain PI SDK application from scratch wouldn't be too hard to build. In a good number of cases, it is good to leverage the the "framework" and the set of functionality offered by PI ACE (Connection management, Scheduling management, Buffering management, etc.). But in some others - like a one-timer update of values - it is an overkill and trying to go outside the beaten path results in time wasted.


                  Other techniques might as simple as a PIConfig script that extracts the values into a CSV file and then update them after you made the change in Excel. Or a simple PI OLEDB "UPDATE" query...


                  Can you provide additional information on what it is you are trying to do, and we'll help you find the best approach.

                    • Re: Basic Unit Conversion from Module

                      I guess I did not divulge enough details about issue I am trying to solve, primarily b/c I didn't want someone to hand me the answer, but rather guide or tell me where to hunt.  It is not a one-time conversion, I constantly want both sets of data.  The international sites want to see DEGC and the centralized performance monitoring folks in the US want to see everything in DEGF, regardless if its coming from site in CA or Asia, to always think in same terms.  This will ultimately apply to all EGU, not just temp. 


                      First issue was that not all aliases existed in all instances of the modules (the reason I was initially trying to stay away from the wizard), but I'm coming to conclusion that I may be best off creating dummy tags assigned to those aliases that are not used for that particular instance of the module. 


                      I also first started out planning to have two modules, one that was site units and one that was our units, with identical alias names, but realized that could also be issue considering large number of tags that didn't need conversion would exist in both modules (maintenance concerns about changing tag in one module and not changing in other). 


                      Other unique challenges include fact that tags are updated at different frequencies (reason to go with natural triggers instead of scheduled). 


                      I'm ultimately hoping to figure out who triggered routine, if its got a valid value, and perform conversion.  I know this doesn't look like where code is headed, but I've tried several approaches and this was just a small, simplified, proof-of-concept attempt to work out kinks of one of the approaches before hitting the full scope. 


                      I appreciate those who have had the patience to deal with my ineptitude and provide suggestions. 

                        • Re: Basic Unit Conversion from Module

                          Somehow I remember that PI AF was indeed intended to do this on th fly (converting to any Unit of Messure you have defined in AF) it might be better on in AF since you wouldn't have to program this.


                          However I couldn't find a document that explains that. it is covered briefly by the Units of Measurement in AF vs. Engineering Units in PI White paper that is available in the vCampus Library, White papers and tutorials, PI AF.


                          You could give it a try.

                            • Re: Basic Unit Conversion from Module


                              I appreciate those who have had the patience to deal with my ineptitude and provide suggestions.
                              Hey no worries, that's what we're here for   Please keep the questions coming!


                              Depending on the number of tags that represents and which products your company is using, you might to consider a couple other, non-programmatic approaches (don't get me wrong: I love programming... but it's always better for a company to have less code to maintain and deploy, especially when out-of-the-box features allow you to do the same):

                              • As Cristobal pointed out, the preferred way to deal with Units of Measurement (and UoM conversions) is through AF - but wait: don't go and create your own application like in the paper just yet... you might not need it.

                                Essentially, you could first create an asset hierarchy in AF with attributes pointing to your PI Points, and the right UoM defined. Then people can use that in PI ProcessBook displays and request the UoM they want - the AF Data Set in ProcessBook (v3.2 and later) will handle the UoM conversion for you.
                              • Another option might be to create corresponding Performance Equations (PE) points to "accompany" your regular PI Points. These would be naturally-triggered (i.e. when the regular PI Point gets a new value) and would do the conversion on the fly. Then people would use either the .DEGC or the .DEGF point in their PI ProcessBook displays, PI DataLink reports, etc.

                              Should you need assistance on non-programmatic issues like creating PE points or setting up your AF hierarchy, please do not hesitate to contact our technical support team.


                              Hope this helps!

                                • Re: Basic Unit Conversion from Module

                                  Just so the wheel doesn't get reinvented (a frequenct side effect of having so many smart people in one place!):


                                  ACE was recomended to Brianne specifically because of the HA nature of their environment, and the particular set of client tools that they'll be using.  The resutls need to be stored back into PI tags (which rules out AF) and they need to match exactly across their collectives (bye bye PEs).


                                  What Brianne is trying to do is cut down on the number of ACE contexts she has to manage.  Suppose you had a bunch of heat exchangers.  The number of thermocouples per heat exchanger varies, but the calculation you want to do (Celcius to Farenheit) is the same.  She wants to have a context per heat exchanger - not a context per thermocouple.  Granted, having a context per thermocouple is the way ACE was designed, and would make it so that using the wizard would work perfectly.  However, this could lead to 1000's of context definitions for an otherwise simple calculation.


                                  Anyway, I think what she needs is an example of the SDK code needed to take the reference (which ACE will provide) to a context's own module, then go find the alias collection which belongs to that module.  How to loop through the collection and do the conversion could be a seperate thread in the SDK group if more help is needed there.


                                  Also, do we need to tender any advice about writting values to PI from within an ACE calc, but using one's own SDK code?  n-way buffering impacts?


                                  There may be an existing example of how to use the PI SDK to work with MDB from within ACE already somewhere.  I think Michael also has a good observation that this is a pretty common use case, and either the vCampus team (or possisbly the product team) should look at what the actual best practice for handling this would be.

                                    • Re: Basic Unit Conversion from Module

                                      Matt Heere

                                      I think what she needs is an example of the SDK code needed to take the reference (which ACE will provide) to a context's own module, then go find the alias collection which belongs to that module


                                      Then she probably wants to declare a PISDK.PIModule (requires a reference to OSIsoft.PISDK) and then some kind of collection to hold the "custom-defined PIACEPoints". Most likely these declarations will be at the class level:

                                      Private _module As PISDK.PIModule
                                      Private _PIACEPoints AsNew System.Collections.Hashtable

                                      Then she needs to:

                                      1. Get a hold of the actual PI Module the current context is running against (with the help of the PIACEBIFunctions.GetPIModuleFromPath method and the Context property)
                                      2. Loop through the collection of aliases in that module
                                      3. Create PIACEPoints off of these aliases
                                      4. Populate the collection (e.g. HashTable) she previously created to hold these objects
                                      _module = PIACEBIFunctions.GetPIModuleFromPath(Context)
                                      ForEach tempAlias As PISDK.PIAlias In _module.PIAliases
                                          _PIACEPoints.Add(tempAlias.Name, _
                                              New PIACEPoint(tempAlias.DataSource.Server.Name, _
                                                                     tempAlias.DataSource.Name, _

                                      Since PI ACE was designed as a high frequency, very efficient, computing/calculation engine, it is recommended that any initialization actions be taken in the ModuleDependentInitialization method. That way, each iteration of the calculation (i.e. the ACECalculations method) performs just what is needed in terms of application or calculation logic. If you expect the ModuleDB structure to change and need to perform these "heavy" tasks in ACECalculations (e.g. initializing PIACEPoint's, reading the ModuleDB), then you need to take the possible delay in consideration and code the rest of your calculation accordingly.


                                      A more efficient approach might be to perform that initialization once in ModuleDependentInitialization and then rely on an event pipe to monitor ModuleDB changes and initialize PIACEPoints accordingly. Please note that I did not test this approach myself (although I don't see why it wouldn't work) and I would recommend significant testing before to deploy such a solution in a Production environment...


                                      Matt Heere

                                      do we need to tender any advice about writting values to PI from within an ACE calc, but using one's own SDK code?  n-way buffering impacts?


                                      Anybody using PI ACE to perform some calc should be leveraging PI ACE's writing capabilities (i.e. through a PIACEPoint's properties and methods) rather than using some custom PI SDK code. Precisely because n-way buffering is not yet available for PI SDK.

                            • Re: Basic Unit Conversion from Module

                              Thanks Michael, I have not played with PI ACE enough I see, and was the second thing I was going to discard after making sure the point existed. However you proved faster!

                          • Re: Basic Unit Conversion from Module


                            I'm very new to ACE and trying to develop a very basic calculation where I find all the TEMP tags in a module and convert their units.  Note that I do not plan to use the wizard to import the points since there are a large number of tags. 


                            I'm trying to figure out why my DEGC variable is not returning a value even though the MyInputAlias string is valid. 


                            As you are very new to ACE, something very basic in the beginning:
                            The basic idea with ACE is : one calculation per context, similar calculations in multiple contexts.


                            In your case the calculation "Turn centigrade into Farenheit" would require just one input and one output, no loops, no GetPIACEPoint() calls.
                            And you'd leave it to a PI System manager to setup a module and an ACE context for each pair of Points.


                            I guess you don't need vCampus help for this.
                            After you done that and are not satisfied with it, you take the Object Browser and spy inside OSIsoft.PIACENet
                            For the OSIsoft.PI.ACE.PIACEPoint you find three flavors of New(), where


                             MyInput = New PIACEPoint(context, inputAliasName, TagAliasUsedType.TagAliasAsInput)
                             MyOutput = New PIACEPoint(context, outputAliasName, TagAliasUsedType.TagAliasAsOutput)


                            should be the core of what you're looking for.
                            Once you got a PIACEPoint object, you might try to use
                            MyInput.SetTag(context, anotherAliasname)


                            You should additionally spy more and find PIACENetInternalFunctions.PIACEAliasFunctions.IsPIAlias(ByVal context As String, ByVal tagAlias As String)
                            which might return TagOrAlias.Unknown if your assumed Output alias does not exist.


                            Now how does the Scheduler know which PI tags should get data sent at the end?
                            It doesn't. You'll have to tweak that as well, setting PIACEPoint.SendDataToPI to true and calling .PutValue() manually. 

                            And of course, we're still in the  -- children don't do this at home --  state.
                            You should create a persistent list of your PIACEPoints and not recreate that on every run.

                            One should not post "solutions", but this is what I created to be half way sure about my post:


                            Public Class DynamicTags
                                Inherits PIACENetClassModule

                                Private Class CalcPair
                                    Dim Input As PIACEPoint
                                    Dim Output As PIACEPoint
                                    Sub New(ByVal ctx As String, ByVal inp As String, ByVal out As String)
                                        Input = New PIACEPoint(ctx, inp, TagAliasUsedType.TagAliasAsInput)
                                        Output = New PIACEPoint(ctx, out, TagAliasUsedType.TagAliasAsOutput)
                                    End Sub
                                    Function Calculate() As Single
                                        Dim result As Single = Input.Value / 5 * 9 + 32
                                        Output.Value = result
                                        Output.SendDataToPI = True
                                        Output.PutValue() ' directly sending, even in Debug !!!
                                        'Output.SendDataToPI = False
                                        Return result
                                    End Function
                                End Class

                                Dim initialized As Boolean

                                Private mContext As PISDK.PIModule
                                Dim CalculationPairs(0) As CalcPair

                                '      Tag Name/VB Variable Name Correspondence Table
                                ' Tag Name                                VB Variable Name
                                ' ------------------------------------------------------------
                                Public Overrides Sub ACECalculations()
                                    If Not initialized Then MyInitialization() ' makes debugging easier
                                    For Each p As CalcPair In CalculationPairs
                                        If p Is Nothing Then Exit For

                                End Sub

                                Private Sub MyInitialization()
                                    mContext = PIACEBIFunctions.GetPIModuleFromPath(Context)
                                    ReDim CalculationPairs(mContext.PIAliases.Count / 2)
                                    Dim i As Integer = 0
                                    For Each a As PISDK.PIAlias In mContext.PIAliases
                                        If a.Name.EndsWith(".degC") Then
                                            Dim aout As String = a.Name.Replace(".degC", ".degF")
                                            If PIACENetInternalFunctions.PIACEAliasFunctions.IsPIAlias(Context, aout) = TagOrAlias.PIAlias Then
                                                CalculationPairs(i) = New CalcPair(Context, a.Name, aout)
                                                i += 1
                                            End If
                                        End If
                                    initialized = true
                                End Sub
                                Protected Overrides Sub InitializePIACEPoints()
                                End Sub

                                ' User-written module dependent initialization code
                                Protected Overrides Sub ModuleDependentInitialization()
                                    initialized = False
                                End Sub