13 Replies Latest reply on Dec 3, 2010 9:04 AM by tinklerj

    ACE & Dynamic Input Tag Sets

    isolutions

      Hi,

       

      I've a situation where there are a number of tags that I want to utilize as inputs to an ACE calc. My issue is that the set of tags grows periodically and is not known at configuration time. I am not currently leveraging AF or the module database. Is there a way to have ACE perform a tag search at module initialization & bind a set of tags during startup into an IList(for instance)? My calc could then iterate over the tags and query input data each time the calc is fired. My instinct is to use the SDK for this. Are there other options?

       

      Feels like I am working against, or at cross-purposes, with the framework in this scenaro. What are other people doing?

       

       

       

       

        • Re: ACE & Dynamic Input Tag Sets

          Roger Chow

          Feels like I am working against, or at cross-purposes, with the framework in this scenaro.
          You are, indeed, working at cross-purpose of what PI ACE is meant to do

           

          Roger Chow

          My instinct is to use the SDK for this
          That's absolutely correct: the PI SDK is meant for that kind of operations (e.g. search for tags, read/write data from/to these tags, etc.)

           

          Roger Chow

          What are other people doing?

          Well, follow your instinct
          Depending on what need to do, you might want to build a standalone application that does that - as opposed to using the framework that PI ACE is setting up for you.

           

          If for some reason you really want/need to stick with PI ACE, then you might want to read this discussion thread where we discussed that at length. We also discussed in this other discussion thread. Seems like a popular topic these days!

          • Re: ACE & Dynamic Input Tag Sets

            We have used and in parts enhanced a home grown tool for a client where a user friendly front end has been built for calculations, which are calculated outside of ACE, ACE is just used as the scheduler and to write the values back to the PI system...I demonstrated this to OSI some time ago.  Now perhaps there are big overlaps with AF but the main issue with ACE (at the time) is that most users needed to review calculations, some needed to edit them with an approval process and "super users" needed to implement them at a click of a button.

             

            All this is implemented using Oracle, PISDK and PIACE.  All calculation algorithms are stored in Oracle and executed by each ACE executable, which is just a container to load the appropriate calculation groups, according to the ACE context.

            • Re: ACE & Dynamic Input Tag Sets
              njacobs

              Roger Chow

              What are other people doing?

               

              There are two approaches. First, if contexts have a variable number of inputs, but there is a known maximum number of inputs, create a debug/test context that actually has the maximum defined. You need to test and debug with this context, but actual calculations will be fine with fewer than the maximum. So long as you have one good tag, it'll just work.

               

              The second approach is to simply create the PIACEPoints dynamically in code. You will have to take care of the initialization of those PIACEPoints yourself (which for inputs simply means calling SetTag()).

               

              Using SDK PIPonts is another option. It's reasonable for inputs, (though PIACEPoint was designed as a more lightweight object than PIPoint). Using SDK points for output presents a problem because we currently do not buffer data from the SDK.

               

              --NJ

              • Re: ACE & Dynamic Input Tag Sets
                tinklerj

                Roger,

                 

                Here's my two penn'orth:

                 

                I have worked on a couple of ACE projects where the set of inputs/outputs could not be defined at configuration time. I know OSI didn't really intend ACE for this situation so can't really be expected to provide support for it.  By trial and error I have found an approach that works for me.  Can't just use SDK PIPoint because of known issues (no buffering / replication; poor write performance)

                 

                My approach was to define a DynamicPoint object which extends PIACEPoint.  This way I can add useful custom methods and properties reflecting its dynamic nature, such as Exists, PIServer (returns a SDK PIServer object not a string), etc.  To support this I added a Val property (optional timestamp) that can be used instead of Value.  If the tag does not exist it returns Nothing rather than causing an exception.

                 

                Initialization:

                 

                Object creator specifies whether input, output or both.

                 

                Call MyBase.SetTag to set alias or tag for PIACEPoint.  Unfortunately this produces an error in the PI message log when the supplied tag/alias does not exist (which is not an error in this environment) - I intend to improve this to check existence before calling SetTag.

                MyBase.Clamping(TagAliasUsedType.TagAliasAsInput) = ClampingOptions.NoClamping
                MyBase.BadValSub(TagAliasUsedType.TagAliasAsInput) = "No Substitution"
                MyBase.Clamping(TagAliasUsedType.TagAliasAsOutput) = ClampingOptions.NoClamping
                MyBase.BadValSub(TagAliasUsedType.TagAliasAsOutput) = "No Substitution"
                MyBase.ApplyOutputTagOptions()

                MyBase.SendDataToPI = True ' for output points

                Reading data:

                myDynPoint.ExeTime = Me.ExeTime   ' ExeTime is property of PIACENetClassModule
                value = MyDynPoint.Val(Me.ExeTime)

                 

                Writing data:

                If myDynPoint.Exists Then
                   myDynPoint.ExeTime = Me.ExeTime
                   myDynPoint.Val(Me.ExeTime) = 1.5  ' example
                   myDynPoint.PutValue()
                End If

                 

                For some reason, I don't get or set correct values unless the ExeTime property of base PIACEPoint is set to current ExeTime.

                 

                Other techniques explicitly supported by DynamicPoint:

                • check if the tag is called "NotApplicable" and set the DynamicPoint to non-existent if so (allows placeholder aliases)
                • if an alias is supplied, log for a PIProperty tree in the same PIModule as the alias and with the same name, which can be used to supply additional settings for the DynamicPoint, or override tag attributes such as Units, etc.
                • simplify setting/getting of value flags and annotations

                Notes on dynamic PIACEPoint objects:

                1. Expensive to create, so store at module level for persistence (like ACE does).
                2. 2. Must set ExeTime property, set Value then call PutValue method explicitly to write data to PI.  Just providing timestamp to Value doesn't work on its own.
                3. Likewise set ExeTime before reading Value.
                4. If you set SendDataToPI property to False, you must explicitly set it back to True before PutValue().
                5. Does not appear in structural info, so cannot use in Wizard Test mode.  (Can resort to good old NotApplicable tag as dummy placeholders).
                  • Re: ACE & Dynamic Input Tag Sets
                    andreas

                    Jeremy,

                     

                    Jeremy Tinkler

                    poor write performance

                     

                    please let us talk about that. What issues do you experience in your application? We might be able to discuss this and solve the problems or be able to provide some feedback to the PI SDK development team.

                     

                    regards,

                      • Re: ACE & Dynamic Input Tag Sets
                        tinklerj

                        Andreas,

                         

                        Poor performance was not the major issue, it was the inability to do buffering/replication using SDK.  However, about 4 years ago I did a comparative test (managed code) with PI-API and PI-SDK sending the same data as fast as possible for multiple tags to a PI server, using the following call sequences for each data value (leaving out a lot of error checking, handling of different data types, lock calls for thread safety, etc):

                         

                        For each sequence, the inputs are sServer (string), sTagName (string), dtTimeStamp (DateTime), oValue (object).  All writes would have been new snapshot values.

                         

                        PI-API: (PIAPI is a static class used to define PInvoke calls and other PI-API definitions such as PITIMESTAMP for C#)

                        int piStat;
                        int istat=0;
                        int iPt;
                        double odrval=0;
                        short flags=0;
                        PIAPI.PITIMESTAMP timestamp;

                        timestamp.year = dtTimeStamp.Year;
                        //...etc
                        timestamp.second = (double)dtTimeStamp.Second + ( (double)dtTimeStamp.Millisecond / 1000 );

                        piStat = PIAPI.piut_setservernode( sServer );
                        piStat = PIAPI.pipt_findpoint( sTagName, out iPt );
                        piStat = PIAPI.pipt_pointtypex( iPt, out typex);
                        odrval = (double)oValue;
                        // make piut_setservernode/pisn_getsnapshotx call to get snapshot time
                        // if (input time later than snapshot) {
                        piStat = PIAPI.pisn_putsnapshotx( iPt, ref odrval, null, null, null, ref istat, ref flags, ref timestamp );
                        // } else {
                        piStat = PIAPI.piar_putarcvaluex( iPt, PIAPI.PIreplacemode.ARCREPLACE, ref odrval, null, null, null, ref istat, ref flags, ref timestamp );
                        // }

                        PI-SDK: (mySDK (PISDK.PISDK) is preserved between calls)

                        PISDK.PIValue PIval;
                        PISDK.Server PIsrv;
                        PISDK.PIPoint PIpt;
                        PIsrv = mySDK.Servers[ sServer ];
                        PIsrv.Open( "" );
                        PIpt = PIsrv.PIPoints[ sTagName ];
                        PIval = PIpt.Data.Snapshot;
                        PIval.Value = (double)oValue;
                        PIpt.Data.UpdateValue(PIval.Value, dtTimeStamp, PISDK.DataMergeConstants.dmReplaceDuplicates, null );

                        Admittedly I didn't compare any of the multiple send functions/methods.

                         

                        I don't remember the exact figures but with PI-API it was of the order of 1000 events/sec sent; with PI-SDK it was 13.  Sorry I can't give you the exact SDK version number, but it was the latest one around in mid-2005.

                         

                        Once the new SDK appears with buffering/replication support, I will revisit this comparative test.

                         

                        Have you any comparative API/SDK performance figures yourself?

                         

                        NB this test was NOT in the ACE environment.

                         

                        --- Jeremy

                          • Re: ACE & Dynamic Input Tag Sets
                            cescamilla

                            Wow, it's been 4 years since that test you ran, it would be really interesting to see that comparison ran with the current PI SDK. As you may know, the PI SDK has been enhanced in many ways since that release.

                             

                            BTW, did you try using the UpdateValues instead? sending a group of tags is way more efficient than sending the data one by one.

                            • Re: ACE & Dynamic Input Tag Sets
                              andreas

                              Jeremy,

                               

                              That's a huge difference. However, you should always use the queue versions. I did some brief tests around a year ago - with the queued version of the PI API and with sending values collections with the PI SDK. Without the exact code and/or hardware the absolute number probably do not mean much - but here they are: 16k/s values with the PI SDK and 35k/s with the PI API. So there is a difference, however not nearly as large as with your tests.

                               

                              Regards,

                               

                               

                                • Re: ACE & Dynamic Input Tag Sets
                                  gpriyanka

                                  Jeremy

                                  To support this I added a Val property (optional timestamp) that can be used instead of Value.  If the tag does not exist it returns Nothing rather than causing an exception.

                                   

                                  Jeremy,

                                   

                                  How did you add the custom Val property? Can you send me a sample of a code where you imported that tags within the calculation.

                                   

                                  Thanks,

                                   

                                  Priyanka

                                    • Re: ACE & Dynamic Input Tag Sets
                                      tinklerj

                                      Priyanka Gupta

                                      How did you add the custom Val property? Can you send me a sample of a code where you imported that tags within the calculation

                                       

                                      Well, that's quite a lot of code but here are some snippets:

                                      Option Strict On
                                      Imports OSIsoft.PI.ACE
                                      Imports OSIsoft.PI.ACE.PIACEBIFunctions
                                      Imports PISDK
                                      Imports PITimeServer
                                      Imports System.Threading

                                      Public Class DynamicPoint
                                          Inherits PIACEPoint
                                          Shared myLock As Object = New Object()    ' For thread safety
                                          Private m_exists As Boolean = False
                                      ...
                                          Sub New()
                                             MyBase.New()
                                             m_exists = False
                                          End Sub
                                          Public Sub AddAlias(ByVal context As String, ByVal aliasPath As String, Optional ByVal tagType As Integer = 3)
                                          ' ... code to initialize base PIACEPoint eg. calling MyBase.SetTag()
                                          ' tagType 1=input only, 2=output only
                                          End Sub
                                      ...
                                          Public Property Val(Optional ByVal timestamp As Object = Nothing) As Object
                                             Get
                                                If m_exists Then
                                                   If timestamp Is Nothing Then
                                                      Return MyBase.Value()
                                                   Else
                                                      Return MyBase.Value(timestamp)
                                                   End If
                                                Else
                                                   Return Nothing
                                                End If
                                             End Get

                                             Set(ByVal value As Object)
                                                If m_exists Then
                                                   If timestamp Is Nothing Then
                                                      MyBase.Value = value
                                                   Else
                                                      MyBase.Value(timestamp) = value
                                                   End If
                                                   If (MyBase.TagUsedAsType And TagAliasUsedType.TagAliasAsOutput) > 0 Then
                                                      MyBase.SendDataToPI = True
                                                   End If
                                                End If
                                             End Set

                                          End Property
                                      ....
                                      End Class

                                      Could you clarify the second part of your question?

                                        • Re: ACE & Dynamic Input Tag Sets
                                          lgmbarata

                                          Hi,

                                           

                                          This topic has been going on for some time and it is helping me develop some large calculations with 400+ tags. There is one issue I couldn't solve related to the tags that are both input and output on the program.

                                           

                                          I initialize the PIACEPoint object as in the code below and  I can read its data from PI but I am unable to write back to the tag. I use this same code to write data to output tags only and it works fine.

                                           
                                          ' Initialize the dynamic tag object
                                          PI01MBCDUAPCDbg = New PIACEPoint(SERVERNAME, "01MBCDUAPCDbg", TagAliasUsedType.TagAliasAsBoth)
                                          PI01MBCDUAPCDbg.SendDataToPI = True
                                          
                                          ' Write information back to PI
                                          PI01MBCDUAPCDbg.ExeTime = ExeTime
                                          PI01MBCDUAPCDbg.Value = "ON"
                                          PI01MBCDUAPCDbg.PutValue()
                                          

                                           I also need this value to be stored on cache because this new value (and other process tags also) are checked again in other parts of the program.

                                           

                                          Has anyone seen this before? Do you know how to handle input/output point objects?

                                           

                                          Thanks,

                                           

                                          Luiz

                                            • Re: ACE & Dynamic Input Tag Sets
                                              Ahmad Fattahi

                                              Points that are both inputs and outputs have a hybrid behavior. They behave like outputs in that their value will be sent to PI at the end of the calculation. They behave like inputs in that a read of the value returns the archive value in PI. Therefore, any attempt to read the value after setting it in code effectively overwrites the value. It is very easy to inadvertently read the value without knowing it. Consider a simple example, where the calculation simply increments the value of an input/output point:

                                               
                                              PIACEPoint.Value = PIACEPoint.value + 1
                                              debug.writeline "value is: " & PIACEPoint.value.ToString
                                              

                                               

                                               

                                              Whoops! The debug output reads the value, resetting it, so the message printed will contain the original value, before it was incremented. The same problem may occur in response to things like hovering the mouse over the value symbol in the debugger, or having the PIACEPoint displayed in the debugger watch window. So...the correct way to deal with input/output points is

                                              1. Read the input value into a temporary variable.
                                              2. Perform the calculations using the temporary variable
                                              3. Assign the value of the temporary variable to PIACEPoint.value at the end of the ACECalculation() subroutine.