22 Replies Latest reply on Feb 1, 2013 7:11 AM by KydeX

    Changing my ACE code from real-time to history based event detection.

    KydeX

      Hi.

       

      I am in a project where we have ACE modules looking at several inputs and turning time series of data into events that we then output back to PI in output tags. At the moment I am using natural triggering and running a calculation cycle every time there is a change in one of the input points.

       

      Now, this is system was ok for testing, but it has a weakness that if some connection is down, while the data is being buffered, our code won't work properly. Same if we wanna run the code on a different server with burst replication.

       

      So I would like my code to maybe run in certain clock intervals and then run some sort of loop that looks at all changes in the inputs during the time since that last calculation ran, and then detect all events chronologically. Does anybody have a good idea on how to do this?

       

      regards,

       

      Olav

        • Re: Changing my ACE code from real-time to history based event detection.

          You can use recorded values for this:

           

           

          RecordedValues Method (PIData object)

           

           

          This method returns compressed values for the requested time range from the archive as a PIValues collection.

          Syntax

          object.RecordedValuesStartTime, EndTime, BoundaryType, [FilterExp], [ShowFiltered], [AsyncStatus]

           


           

          It's a PISDK call for data and you can set your endtime as '*' and starttime as being '* - <CLOCK INTERVAL>'  make sure to set your boundarytype as inside you don't read values twice

            • Re: Changing my ACE code from real-time to history based event detection.
              KydeX

              Thank you very much. Would you mind writing a short example code, if you have the time?

                • Re: Changing my ACE code from real-time to history based event detection.
                  Asle Frantzen

                  Hi Olav

                   

                  You'll need to create a PISDK object in your ACE code, before you can use any methods from it. In this blog post I describe how to create a reference to a PISystem object (AF) from ACE, you can modify that to use PISDK instead:

                   

                  vcampus.osisoft.com/.../storing-af-calculations-to-a-pi-tag.aspx

                   

                  Also - there a lots of good information on how to create PISDK objects in the help files coming along with the pi client. Go to About-PISDK (or PI SDK Utility) and open the help files from there.

                  • Re: Changing my ACE code from real-time to history based event detection.

                    This is a VBA code I wrote to run on an 8 hour clock.  It deletes values from an output tag that do not match the timestamp of at least one of the input tags:

                     

                    Sub archivecleaner()

                     

                    ListBox1.Clear

                     

                    Dim inputtag As PIPoint
                    Dim inputtag1 As PIPoint
                    Dim outputtag As PIPoint
                    Dim testvals As PIValues
                    Dim checkval As Double
                    Dim checkval1 As Double
                    Dim removeval As Double
                    Dim testvaltime As Variant
                    Dim i As Integer

                     

                    Set inputtag = Servers.DefaultServer.PIPoints.Item("sinusoid")
                    Set inputtag1 = Servers.DefaultServer.PIPoints.Item("cdt158")
                    Set outputtag = Servers.DefaultServer.PIPoints.Item("testpointarchiveedits")

                     

                    Set testvals = outputtag.Data.RecordedValues("*-8h", "*", btInside)

                     

                    For i = 1 To testvals.Count
                    testvaltime = testvals.Item(i).TimeStamp

                     

                    On Error Resume Next

                    checkval = 0

                    checkval1 = 0

                    checkval = inputtag.Data.ArcValue(testvaltime, rtCompressed)

                    checkval1 = inputtag1.Data.ArcValue(testvaltime, rtCompressed)

                    If checkval = 0 And checkval1 = 0 Then
                    outputtag.Data.RemoveValues testvaltime, testvaltime, drRemoveAll
                    End If
                    Next i

                     

                    End Sub

                     

                     

                     

                    I would not ever recommend using this code since it deletes values but at least you can see how recorded values is used

                      • Re: Changing my ACE code from real-time to history based event detection.
                        KydeX

                        Thanks for your help both. I have somewhere to start off now. I will not use your code directly, Dmitri, I do not want to delete anything :)

                          • Re: Changing my ACE code from real-time to history based event detection.
                            KydeX

                            I am trying these methods, but I am getting stuck already at connecting to the server. I have stripped the code down to minimum, but when running the debugger, I only get the message "failed to create object". If I omit the "as new" statement on the PISDK object, the debugger will start, but then I will get a null reference exception on the myServer definition. Any tips on this? And yes, I am a beginner at this :)

                             

                            Imports OSIsoft.PI.ACE

                             

                            Imports PISDK

                             

                            Imports PISDKCommon

                             

                            Public Class Test_Class

                             

                               Inherits PIACENetClassModule

                             

                               'Declared variables for PISDK communication

                             

                               Private mySDK As New PISDK.PISDK

                             

                               Private myServer As PISDK.Server

                             

                               Public Overrides Sub ACECalculations()

                             

                                   'Initialize server

                             

                                   If myServer Is Nothing Then

                             

                                       myServer = mySDK.Servers.DefaultServer

                             

                                   End If

                             

                                   'Open connection to server

                             

                                   If myServer.Connected = False Then

                             

                                       myServer.Open()

                             

                                   End If

                              • Re: Changing my ACE code from real-time to history based event detection.
                                Asle Frantzen
                                Dim sdk As PISDK.PISDK
                                Dim server As PISDK.Server
                                Dim points As PISDK.PIPoints
                                Dim point As PISDK.PIPoint
                                Dim myValue As PISDK.PIValue
                                
                                sdk = New PISDK.PISDK
                                server = sdk.Servers.DefaultServer
                                points = server.PIPoints
                                point = points(tag)
                                myValue = point.Data.ArcValue(ParseTime("*"), RetrievalTypeConstants.rtAtOrBefore)
                                

                                 

                                  • Re: Changing my ACE code from real-time to history based event detection.
                                    KydeX

                                    Thx, I got a little bit further, the debugger now starts, but stops at the line "sdk = New PISDK:PISDK". This is the exception detail:

                                     

                                    System.InvalidCastException was unhandled by user code

                                     

                                     Message=Unable to cast object of type 'PISDK.PISDKClass' to type 'PISDK.PISDKClass'.

                                     

                                     Source=Test_Exe

                                     

                                     StackTrace:

                                     

                                          at PIACE.Test_Exe.Test_Class.ACECalculations() in C:\Program Files\PIPC\ACE\ClassLibraries\Test_Exe\Test_Class.vb:line 32

                                     

                                          at PIACEClassLibraryHost.PIACEHost.PIACENetSchedule..ctor(String ServerName, String PIACEClassLibraryName, String ACEModuleName, String ACEContext)

                                     

                                     InnerException:

                                      • Re: Changing my ACE code from real-time to history based event detection.
                                        KydeX

                                        Update: I tried to create a separate VB.NET project which is not part of the ACE calculations and then I just tested with a windows form button that references a separate class and connects to the server. Your code example then works. I haven't found the reason for it not to work inside the ACE calculation, but I will keep trying.

                                          • Re: Changing my ACE code from real-time to history based event detection.
                                            Asle Frantzen

                                            Try create a new, empty ACE project and test the PI SDK code. It should work. Sometimes I find that abandoning the old ACE project and just recreate it and paste the code back in, makes my life much easier.

                                            • Re: Changing my ACE code from real-time to history based event detection.

                                              Hello Olav,

                                               

                                              Olav Vatne

                                              I haven't found the reason for it not to work inside the ACE calculation, but I will keep trying.

                                               

                                              Are you debugging through PI ACE Wizard (Tools -> PIACEWizard -> Debug)?

                                               

                                              If so, please try testing it (Tools -> PIACEWizard -> Test ...). Does that work?

                                                • Re: Changing my ACE code from real-time to history based event detection.
                                                  KydeX

                                                  I've tried both. I get the same error message, saying Unable to cast object of type 'PISDK.PISDKClass' to type 'PISDK.PISDKClass'.

                                                   

                                                  I tried creating a blank ace module and pasting the code in there, and it still doesn't work. Something strange there..

                                                    • Re: Changing my ACE code from real-time to history based event detection.

                                                      You've added a reference to OSIsoft.PISDK library?

                                                       

                                                      Is your OS 32 or 64 bit?

                                                       

                                                      If it is 64 bit, did you possibly chose the 64 bit OSIsoft.PISDK library?

                                                       

                                                      I assume you have also added the corresponding Import

                                                       
                                                      Imports PISDK
                                                      

                                                       

                                                        • Re: Changing my ACE code from real-time to history based event detection.
                                                          KydeX

                                                          I'ts a 32 bit OS on my developer laptop, but the server is running 64 bit, do I then have to specify 64 bit? Anyway, I made sure I've done all the other steps. And, as I said, it works if I just generate a general VB.NET project. But inside the ACE Calculation sub it doesn't work.

                                                            • Re: Changing my ACE code from real-time to history based event detection.

                                                              Hello Olav,

                                                               

                                                              Sorry about the inconvenience. No, the 32 bit OSIsoft.PISDK library is the right one. It doesn't matter if your PI Server is 32 or 64 bit. More of interest is that PI ACE is 32 bit (to my knowledge).

                                                               

                                                              I believe, we will do hard with further troubleshooting your issue here in the Forum.

                                                               

                                                              Can you please open a ticket at OSIsoft TechSupport?

                                                               

                                                              For sure we a curious what your issue is about. Please update us.

                                                                • Re: Changing my ACE code from real-time to history based event detection.
                                                                  KydeX

                                                                  Thx, I have contacted techsupport on the matter. I will post back here whenever I have a response, or figure out a solution.

                                                                    • Re: Changing my ACE code from real-time to history based event detection.
                                                                      KydeX

                                                                      I have now had some good assistance from Martin Lehmann at Osisoft Techsupport. It turns out that I had used references from the COM library instead of the .NET library. the references from the .NET library were missing on my system because I did not have the most recent version of the PISDK. After downloading and installing the latest PISDK and choosing the correct references the code works just fine.

                                                                       

                                                                      Thx to Osisoft for great customer service!

                                                                       

                                                                      Now I can go back to solving the task that I originally started the thread about :)

                                                                        • Re: Changing my ACE code from real-time to history based event detection.

                                                                          Thank you for the update Olav.

                                                                            • Re: Changing my ACE code from real-time to history based event detection.
                                                                              KydeX

                                                                              OK, since this is kind of a sidetrack project to what I'm working on at the moment, I haven't had much time to test further. But now I tried a short piece of code using the "RecordedValues" method. It looks like this:

                                                                               

                                                                              'Declare Variables

                                                                               

                                                                                 Dim mySDK As PISDK.PISDK

                                                                               

                                                                                 Dim myServer As PISDK.Server

                                                                               

                                                                                 Dim points As PISDK.PIPoints

                                                                               

                                                                                 Dim point As PISDK.PIPoint

                                                                               

                                                                                 Dim myValue As PISDK.PIValue

                                                                               

                                                                                 Dim SINUSOID_Copy As PIPoint

                                                                               

                                                                                 Dim TestVals As PIValues

                                                                               

                                                                                 Public Overrides Sub ACECalculations()

                                                                               

                                                                                     'Initialize server

                                                                               

                                                                                     mySDK = New PISDK.PISDK

                                                                               

                                                                                     myServer = mySDK.Servers.DefaultServer

                                                                               

                                                                                     points = myServer.PIPoints

                                                                               

                                                                                     SINUSOID_Copy = points("SINUSOID")

                                                                               

                                                                                     TestVals = SINUSOID_Copy.Data.RecordedValues("*-8h", "*", BoundaryTypeConstants.btInside)

                                                                               

                                                                                     'Open connection to server

                                                                               

                                                                                     If myServer.Connected = False Then

                                                                               

                                                                                         myServer.Open()

                                                                               

                                                                                     End If

                                                                               

                                                                                     'For each loop test

                                                                               

                                                                                     For Each item As PIValue In TestVals

                                                                               

                                                                                         RecordedValues_Test.Value = item.Value

                                                                               

                                                                                         RecordedValues_Test.PutValue()

                                                                               

                                                                                     Next

                                                                               

                                                                                 End Sub

                                                                               

                                                                              Now, this works, as in it copies the compressed SINUSOID data for the last 8 hours and outputs it to the ACE output Recordedvalues_test. However, the timestamp for all values will be the time of the execution. I want to retain the original timestamps, but I have found no way of doing this. I have tried the format "Recordedvalues_Test.value(item.timestamp)" I even tried just putting in (Now), but all attempts at giving the value a specific timestamp just results in "calc failed". Is there any way of doing this? I can't find much information on ACE timestamps in the manual.

                                                                                • Re: Changing my ACE code from real-time to history based event detection.
                                                                                  Dan Fishman

                                                                                  The code with RecordedValues_Test.Value(item.timestamp) should be working.

                                                                                   

                                                                                  Do you add the correct reference to PITimeServer?

                                                                                    • Re: Changing my ACE code from real-time to history based event detection.
                                                                                      mhamel

                                                                                      @Olav: The PI ACE engine manipulates timestamps in number of seconds elapsed since 1970. This value is double and the fractional part is used for subseconds. To perform more complex calculations with time that are always accurate you can make use of the PITimeServer interop assembly. The PI SDK help that you can find under the folder .\PIPC\Help would guide you through how to do this. Also, I suggest you take the time to read the PI Application Development Course Workbook to learn more on timestamp manipulation; you can find this one here.

                                                                                       

                                                                                      I have added an excerpt of code showing how I would do it. I want to bring to your attention that PI ACE does not always make use of the default PI Server. PI ACE stores its operational information within a given PI Server but can use several others as a source and destination for calculation. Using the default PI Server might conduct your calculation module to fail at certain times in future. Each PIACEPoint owns a property telling from which PI Server it comes from. Using direct time reference as you did will prevent the calculation module to give accurate answers if latency is found on the computer or when a recalculation is requested.

                                                                                       
                                                                                      'Class-level Members
                                                                                      Private _InputPoint1 As PIACEPoint
                                                                                      Private _OutputPoint1 As PIACEPoint
                                                                                      Private _MyPISDK As PISDK.PISDK
                                                                                      Private _PIServer as PISDK.Server
                                                                                      
                                                                                      '...
                                                                                      
                                                                                      Public Overrides Sub ACECalculations()
                                                                                           'Objects
                                                                                           Dim PIServerName As String
                                                                                           Dim InputPoint1_wPISDK As PISDK.PIPoint
                                                                                           Dim MyValues As PISDK.PIValues
                                                                                           Dim MyValue As PISDK.PIValue
                                                                                           Dim t1 As New PITimeServer.PITime
                                                                                           Dim t2 As New PITimeServer.PITime
                                                                                           Dim tf As New PITimeServer.PITimeFormat
                                                                                           
                                                                                           'Set your time range from now returned by the ExeTime property of your
                                                                                           'input point. Using '*' or Now won't be accurate all the time as the PI ACE Module
                                                                                           'can be delayed in time for different reasons.
                                                                                           t2.UTCSeconds = _InputPoint1.ExeTime
                                                                                           tf.UTCSeconds = t2.UTCSeconds
                                                                                           t1.LocalDate = tf.AddIntervals("hour", -8)
                                                                                           
                                                                                           'Initialize the connection again
                                                                                           _MyPISDK = New PISDK.PISDK
                                                                                           
                                                                                           'Get the PI Server name from which the PIACEPoint is defined.
                                                                                           PIServerName = _InputPoint1.Server
                                                                                           
                                                                                           'Open the connection to right PI Server.
                                                                                           _PIServer = _MyPISDK.Servers(PIServerName)     
                                                                                           If _PIServer.Connected = False Then
                                                                                                _PIServer.Open()
                                                                                           End If
                                                                                           
                                                                                           'Get a PISDK.PIPoint reference using the tag name of the input point 1.
                                                                                           InputPoint1_wPISDK = _PIServer.PIPoints(_InputPoint1.Tag)
                                                                                           
                                                                                           'Extract events up to 8 hours in the past.
                                                                                           MyValues = InputPoint1_wPISDK.Data.RecordedValues(t1, t2, BoundaryTypeConstants.btInside)
                                                                                           
                                                                                           'Browse each value.
                                                                                           For Each MyValue in MyValues
                                                                                                'Assign the time for the output PI Point the same as the original event.
                                                                                                _OutputPoint1.ExeTime = MyValue.TimeStamp.UTCSeconds
                                                                                                '... set the original value too.
                                                                                                _OutputPoint1.Value = MyValue.Value
                                                                                                
                                                                                                'Send the value one by one.
                                                                                                'This technique allows to utilize the buffering mechanism.
                                                                                                _OutputPoint1.PutValue()
                                                                                           Next
                                                                                           
                                                                                           'Kill some unused objects to let the GC do its job.
                                                                                           t1 = Nothing
                                                                                           t2 = Nothing
                                                                                           tf = Nothing
                                                                                           
                                                                                      End Sub
                                                                                      

                                                                                       

                                                                                       

                                                                                      I hope this sheds some light on how PI ACE works.