Skip navigation
All Places > PI Developers Club > Blog > 2016 > March
2016

I was browsing the PI Square forums today and came across this discussion: Is it possible to use an event frame time in AF attribute?

I have thought about this for a while and even though I know it is possible I just haven't built one yet. Well today I thought I would see how quickly I could come up with an example Data Reference to answer Aaron's question.

 

It turns out that I built then Data Reference before I had chance to drink my freshly brewed cup of tea, and by the end of the build the tea was still warm!

Anyway, here goes...remember this is experimental as there are LOTS of ways you may want to access data from an Event Frame.

 

For this example I wanted it to be simple. It only needed to find Event Frames for the Element in which the hosting Attribute was a child of. This makes the search fairly straightforward because we'll look for an Event Frames where the Element is referenced. You could of course build out a much more rich Data Reference but I doubt you would do that in 60 seconds.

 

Right, start the clock.

 

- First create a new Class project in Visual Studio.

- Make a reference to the OSIsoft.AFSDK:

- Add some of the usual Using statements and create a GUID for your project (Tools => Create Guid => Option 5):

- Add the Class for your AF Data Reference inheriting the AFDataReference object:

public class EFStats: AFDataReference
    {
    }

 

- Next we need to add a bunch of properties in order for the AF SDK to know how our Data Reference will work:

        public override AFDataReferenceMethod SupportedMethods
        {
            get { return AFDataReferenceMethod.GetValue; }
        }

        public override AFDataMethods SupportedDataMethods
        {
            get { return this.DefaultSupportedDataMethods; }
        }

        public override AFDataReferenceContext SupportedContexts
        {
            get { return AFDataReferenceContext.All; }
        }

 

- After than we'll need to store a simple config string that tells us which Event Frame property we need (by default the StartTime):

string EventFrameProperty = "StartTime";
        public override string ConfigString
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("{0} = {1};", "EF Property", EventFrameProperty);
                return sb.ToString();
            }
            set
            {
                var tokens = value.Split(';');
                foreach (var token in tokens)
                {
                    var key = token.Split('=');
                    switch (key[0].ToLower())
                    {
                        case "ef property":
                            EventFrameProperty = key[1];
                            break;
                    }
                }
                SaveConfigChanges();
            }
        }

 

- So far so good. Now we need to actually go fetch the Event Frame property when the GetValue() method is called. It may or may not be called with a Time Context. Anyway, roughly speaking we can do the following:

public override AFValue GetValue(object context, object timeContext, AFAttributeList inputAttributes, AFValues inputValues)
        {
            AFValue result = new AFValue();

            AFTime time;
            if (timeContext == null)
                time = new AFTime("*");
            else
                time = new AFTime(timeContext);

            AFElement e = (AFElement)this.Attribute.Element;
            var events = AFEventFrame.FindEventFrames(e.Database, null, time, 0, 100, AFEventFrameSearchMode.BackwardFromStartTime, "*", e.Name, null, null, true);
            if (events.Count == 0)
            {
                result.Status = AFValueStatus.Bad;
                result.Value = 0;
                result.Timestamp = new OSIsoft.AF.Time.AFTime("01-Jan-1970");
            }
            else
            {
                result.Status = AFValueStatus.Good;
                AFEventFrame ef = events.First();

                switch(EventFrameProperty.ToLower())
                {
                    case "starttime": result.Value = ef.StartTime;
                        break;
                    case "endtime": result.Value = ef.EndTime;
                        break;
                }
                result.Timestamp = time;
            }
            return result;
        }

 

Cool. That is it, providing an Event Frame exists then it will fetch the property from the 1st Event Frame in the collection.

Performance should be considered based on how busy your Event Frames are, and how you are structuring your Event Frames.

You could be more specific in the Search Criteria (use the config string to specify Event Frame Templates and so on).

 

- Compile the code.

- Register the plugin via RegPlugin.exe.

- Buy me a beer whenever you see me, e.g. at the OSIsoft UC.

 

Stop the clock.

 

(Complete code below)

 

The Genius Group - quite simply the best OSIsoft PI professionals in the world!

Rhys Kirk, PI Genius, rhys@thegeniusgroup.co.uk

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using OSIsoft.AF;
using OSIsoft.AF.Asset;
using OSIsoft.AF.Data;
using OSIsoft.AF.EventFrame;
using OSIsoft.AF.Time;


namespace EventFrameStatistics
{
    [Guid("6B07FCF5-B069-4877-BF48-E0D8D5C42050")]


    public class EFStats: AFDataReference
    {
        public override AFDataReferenceMethod SupportedMethods
        {
            get { return AFDataReferenceMethod.GetValue; }
        }


        public override AFDataMethods SupportedDataMethods
        {
            get { return this.DefaultSupportedDataMethods; }
        }


        public override AFDataReferenceContext SupportedContexts
        {
            get { return AFDataReferenceContext.All; }
        }


        string EventFrameProperty = "StartTime";


        public override string ConfigString
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("{0} = {1};", "EF Property", EventFrameProperty);
                return sb.ToString();
            }
            set
            {
                var tokens = value.Split(';');
                foreach (var token in tokens)
                {
                    var key = token.Split('=');
                    switch (key[0].ToLower())
                    {
                        case "ef property":
                            EventFrameProperty = key[1];
                            break;
                    }
                }
                SaveConfigChanges();
            }
        }


        public override AFValue GetValue(object context, object timeContext, AFAttributeList inputAttributes, AFValues inputValues)
        {
            AFValue result = new AFValue();


            AFTime time;
            if (timeContext == null)
                time = new AFTime("*");
            else
                time = new AFTime(timeContext);


            AFElement e = (AFElement)this.Attribute.Element;
            var events = AFEventFrame.FindEventFrames(e.Database, null, time, 0, 100, AFEventFrameSearchMode.BackwardFromStartTime, "*", e.Name, null, null, true);
            if (events.Count == 0)
            {
                result.Status = AFValueStatus.Bad;
                result.Value = 0;
                result.Timestamp = new OSIsoft.AF.Time.AFTime("01-Jan-1970");
            }
            else
            {
                result.Status = AFValueStatus.Good;
                AFEventFrame ef = events.First();


                switch(EventFrameProperty.ToLower())
                {
                    case "starttime": result.Value = ef.StartTime;
                        break;
                    case "endtime": result.Value = ef.EndTime;
                        break;
                }
                result.Timestamp = time;
            }
            return result;
        }
    }




}

Filter Blog

By date: By tag: