PI Notifications, PI Web Parts and PI OLEDB Enterprise are both using GetValue and GetValues, PI ProcessBook is using almost all the time the GetValues. Those are the ones I know. Any future application developed on top of PI AF will also require both. PI System Explorer (PSE) is using the GetValue method often to display the result inside the right pane. If you intend to use a Data Reference in your project to solve real-life problem, you should foresee to implement both GetValue and GetValues methods. The ramifications of not implementing the GetValue method depends on the PI System related application. If in the purpose of your project it does not make sense to return a single value, you will have to implement the SupportedMethods and do not return the AFDataReferenceMethod.GetValue value. By doing such, some applications relying on the GetValue would certainly throw an exception.
The minimal required ones in a practical way are:
' Methods Public Function GetValue(ByVal context As Object, ByVal timeContext As Object, ByVal inputAttributes As AFAttributeList, ByVal inputValues As AFValues) As AFValue Public Function GetInputs(ByVal context As Object) As AFAttributeList ' Properties Public Property ConfigString As String Public ReadOnly Property SupportedContexts As AFDataReferenceContext Public ReadOnly Property SupportedMethods As AFDataReferenceMethod
For a complete list of overridable members, consult this list below.
' Methods Public Sub CreateConfig() Public Function GetInputs(ByVal context As Object) As AFAttributeList Public Function GetValue(ByVal context As Object, ByVal timeContext As Object, ByVal inputAttributes As AFAttributeList, ByVal inputValues As AFValues) As AFValue Public Function GetValues(ByVal context As Object, ByVal timeContext As AFTimeRange, ByVal numberOfValues As Integer, ByVal inputAttributes As AFAttributeList, ByVal inputValues As AFValues()) As AFValues Public Sub SetValue(ByVal context As Object, ByVal newValue As AFValue) ' Properties Public Property Attribute As AFAttribute Public Property ConfigString As String Public ReadOnly Property EditorType As Type Public ReadOnly Property IsConfigured As Boolean Public ReadOnly Property ServerTime As AFTime Public ReadOnly Property ServerTimeZone As AFTimeZone Public ReadOnly Property Span As Double Public ReadOnly Property [Step] As Boolean Public ReadOnly Property SupportedContexts As AFDataReferenceContext Public ReadOnly Property SupportedMethods As AFDataReferenceMethod Public Property Template As AFAttributeTemplate Public Property UOM As UOM Public ReadOnly Property ValueEditorType As Type Public ReadOnly Property Zero As Double
Let me know if that answered your question.
Thank you for your speedy and detailed response. My question really only pertained to the static version of the GetValue method. I fully intend to override the instance versions of the GetValue function.
I noticed that your post excluded the static version of the GetValue method from your list of overridable members. On review of the PI AFSDK Reference, I noted that this function is not virtual (of course, since it's static). My mistake...
The static GetValue method has a different role. The prototype looks like this:
Or in C#
This shared method is intended gets the value for multiple attributes based upon the data reference configuration within the specified context. The attributes argument represents the list of attributes to obtain a value from. The returned collection of AFValues will be specified in the same order as the list of attributes provided.
This static GetValue method is declared as a static member of an inheritable class (AFDataReference) and get be “override” in a certain extent. In fact, it consists of hiding the base class static method in the derived class. This method provides a mechanism to read multiple attribute values in one call, giving the data reference an opportunity to provide for faster performance than if retrieved by individual calls. Because this method is static, it must be overridden using the new keyword in C#, or Shadows in VB.NET. If this method is not provided in the Data Reference implementation, then the SDK will invoke the GetValue call for each attribute.
In VB.NET it gives:
Or in C#:
If you do not implement your version of this shared method, the SDK will invoke the GetValue call for each attribute. Does it answer your question better?
Huh. That's interesting. Does the SDK use Reflection to determine whether or not our custom Data Reference has implemented a static GetValue method? If not, how does it call it? In what situations might this be called?
Zev, you are right the reflection principle is used to determine if a static GetValue method is provided in your custom data reference. If none is implemented, a default internal implementation will call the individual GetValue method per attribute. This is normally slower than going through the static GetValue method. This is normally used when you invoke the GetValue method of an AFAttributeList object.
Thanks Mathieu for that neat "under-the-covers" look!
Is this actually used in any of the client applications you mentioned above or is it strictly included for custom development?
For example, this faster method of getting values for multiple attributes in an AFAttributeList is implemented in the PI Point Data Reference. I have not seen this implementation in any of the other official OSIsoft AF Data Reference (Table Lookup, Formula). I have not seen a use of it in client applications for now, but I am convinced the time where this feature will be leveraged is not so far.
Probably, a member of the SDK team could chime in and complete my answer.
First, one correction - in order for this method to be called, you must set the "MultipleAttributes" flag in the DR's SupportedMethods property.
AF Client applications indirectly call this through AFAttributeList.GetValue. Actually, even a simple call to a single attribute, like a Formula Data Refrence, can result in AFAttributeList.GetValue being called for the Formula's inputs. While Formula and Table Lookup do not implement the multiple GetValue function, they do implement the "GetInputs" function, which allows the AFSDK to pre-fetch this data in this optimal way. AFSDK evaluates the full list of inputs prior to retrieving data so to cause as few round-trips to the PI Server as possible.
Some added information/clarification on Mathieu's post, because this is a tricky subject and comes down to some OOP principles.
For this explanation I'm using C# keywords as an example, the same applies to VB.NET using corresponding keywords.
In C# you can create virtual methods, these can be overriden in derived classes using the override keyword. When you override an existing (virtual) method, you are creating a new implementation of a method with the same signature.
.NET does not support virtual static methods. Why not? It does not make any sense. Say I would have a static HelperClass.HelpMethod(). Why would I want some random derived class' method implementation to be called. This is not logical. If you have a base class HelperClass and some derived class called HelperDerivedClass you would just call either HelperClass.HelpMethod() or HelperDerivedClass.HelperMethod.
If you want to achieve something like this, you can use the Singleton pattern or a (normal) class hierarchy with the Factory pattern or Dependancy injection pattern. This way you can achieve different behavior in different parts of your application.
In this case, AFDataReference has a static method called 'GetValue'. It cannot be virtual, so it cannot be overridden in a derived class. What happens if you derive from AFDataReference and want to implement your own version of this static GetValue method? You cannot use the override keyword, and you don't have to. You can just implement the method. This will automatically hide the base class implementation of the static GetValue method. Well, why would you need the new keyword in this case? The compiler will complain with the following warning:
warning CS0108: ''AFDerivedDataReference.GetValue() hides inherited member 'AFDataReference.GetValue(). Use the new keyword if hiding was intended.
This is exactly as we discussed above. Our derived implementation of the static GetValue() method hides the static GetValue() method of the base class. Why is the compiler complaining about the new keyword then? It is only for documentation purposes! Using the new keyword will only suppress this warning, it does not make the methods behave differently. Why are you 'forced' to use it? The C# compiler is just friendly, and wants to make sure you are doing something you explicitly intend to do.
The following includes some assumptions, maybe the AF Developers (Chris Manhard) are at liberty to confirm or elaborate on this.
If AF needs to use the static GetValue() method, it will find out the type of the current DataReference using Reflection. It will then call this static GetValue method (with the right signature) and retrieve the output. This object will always derive from AFDataReference. If no implementation of this static GetValue() method exists in the derived class, the AFDataReference.GetValue() method will be used (basic OOP). If there is an implementation (either with or without the new keyword) than that implementation is called.