5 Replies Latest reply on Nov 4, 2016 3:30 PM by Rick Davin

    Issue writing a matlab dll routine on PI as a custom data reference

    PabloSatler

      Hi,

       

      I'm writing a matlab dll routine to be executed on PI System Explorer (PSE) as a custom data reference. The goal of the data reference (DR) is to get a range of values from a tag and then feed the matlab routine as an array, which is going to return a value as an answer. This value then should be written on an attribute (that uses the DR), storing both the value itself and its timestamp, so we can have a times series data. The initial idea would be this routine to be called every time a client user hits the "attributes" tab on PSE, and the latest result be visible on that tab, like a snapshot value.

      The routine is working, though it always store the same timesstamp (01/01/1970), even when using an AFValue with Timestamp = AFTime.Now. So, what can I do to fix that, so that we have a history of the values stored?

      I also would like to know which method is internally called when we hit the "attributes" tab on PSE. Using the "Attach to Process" feature of Visual Studio I noticed that the GetValue method was the one called, so I put my matlab routine there. However, I'm not sure if there is the right place to do so. I also expected the method GetValues (plural) to be called, once I'm using a range (AFTimeRange) to retrieve the values from the input tag, though such method was never called. Is there any way to get a custom method to be called by the PI System whenever a certain event happens, like the user hitting the attributes tab on PSE? 

       

      The documentation I am using as reference to build my custom data reference are this white paper and these videos. So, if anyone has any other piece of information about implementing custom data references on AF, please share with me.

       

      Thanks,

        • Re: Issue writing a matlab dll routine on PI as a custom data reference
          Rick Davin

          Hi Pablo,

           

          Let's start with the easy stuff.  Yes, clicking the "Attributes" tab in PSE essentially calls the GetValue method so for debugging you were doing the right thing.  GetValues would be called if you right-click on the attribute and go to Time Series Data.

           

          As for the timestamp, could you share the GetValue code?  You should be able to get history, though technically it's not stored like a PI point.  Rather the history generated dynamically by your data reference.  Are you making a simple GetValue() call without parameters?  I don't care about the MatLab portion.  Mainly want to see anywhere your use an AFTime in GetValue and also anywhere you may return from GetValue.

           

          Rick

            • Re: Issue writing a matlab dll routine on PI as a custom data reference
              PabloSatler

              Hello Rick,

              thanks for the reply. The GetValue code is below.

               

              Just a clarification:

              My AFTimeRange values are static for test purposes. I intend to put them user editable afterwards. Also, I noticed that right-clicking and selecting Time Series Data calls GetValues(..), which is calling GetValue again, but with a timeContext as AFTime. So, I'm currently receiving "no data" for Time Series Data, but this is because I didn't implement the else part yet. Though, I thought it would at least print the values previously calculated by the routine, but that's not what's happenning. Also, as you can see below, I was trying to use the GetValues from the AFDataReference, but got no success, so I used the one from AFAttribute.

               

              I'm pretty new to this data reference stuff, so I'm kind of clueless on how to proceed or if I'm doing the things the right way. = )

               

              public override AFValue GetValue(object context, object timeContext, AFAttributeList inputAttributes, AFValues inputValues)
                      {
                          if (timeContext == null)
                          {
                              //timeContext = AFTime.Now;
                              var attrib = inputAttributes[0];
                              AFTimeRange range = new AFTimeRange("*-1h", "*");
                              var values = attrib.GetValues(range, 0, attrib.DefaultUOM); // getValues not from AFDataReference
                              //var values = this.GetValues(context, range, 0, inputAttributes, inputValues);
              
                              //####### Calling Matlab's routine ########
                              var nc = 0.75; //nivel de confianca (trust level)
                              var alfa = 0.7; //parametro do filtro (filter parameters)
                              AFValue matlabResult = new AFValue();
                              matlabResult = this.rotinaMatlab(values, nc, alfa); //this is where the matlab routine is used/called
                              matlabResult.Timestamp = AFTime.Now; //trying to set a timestamp
                              return matlabResult;
                          }
                          else
                          {
                              return base.GetValue(context, timeContext, inputAttributes, inputValues);
                          }
                      }
              
                • Re: Issue writing a matlab dll routine on PI as a custom data reference
                  Rick Davin

                  The problem is that you are setting matlabResult instead of matlabResult.Value.

                   

                  While your time range is statically determined (for time being), you should adjust to not necessarily end with "*".  Plus what happens if someone wants data for a specific timestamp?  Then the timeContext isn't null, but you'd be passing them the base implementation which skips the matlab calc.  Yikes!

                   

                  public override AFValue GetValue(object context, object timeContext, AFAttributeList inputAttributes, AFValues inputValues)
                  {
                      //First, let's decide on the timestamp, which is called endTime
                      AFTime endTime = AFTime.Now.ToPIPrecision()
                      if (timeContext is AFTime)
                      {
                          endTime = (AFTime)timeContext;
                      }
                      AFTime startTime = new AFTime( endTime.UtcTime.AddHours(-1) );
                  
                  
                      var attrib = inputAttributes[0];
                      AFTimeRange range = new AFTimeRange(startTime, endTime);
                      var values = attrib.GetValues(range, 0, attrib.DefaultUOM); // getValues not from AFDataReference 
                                                                                  //var values = this.GetValues(context, range, 0, inputAttributes, inputValues); 
                  
                  
                      //####### Calling Matlab's routine ######## 
                      var nc = 0.75; //nivel de confianca (trust level) 
                      var alfa = 0.7; //parametro do filtro (filter parameters) 
                      AFValue matlabResult = new AFValue();
                      matlabResult.Value = this.rotinaMatlab(values, nc, alfa); //this is where the matlab routine is used/called 
                      matlabResult.Timestamp = endTime; //trying to set a timestamp 
                      return matlabResult;
                  }
                  

                   

                  The last part can be simplified using this AFValue constructor:

                   

                  //####### Calling Matlab's routine ########  
                  var nc = 0.75; //nivel de confianca (trust level)  
                  var alfa = 0.7; //parametro do filtro (filter parameters)  
                  return new AFValue(this.rotinaMatlab(values, nc, alfa), endTime);