Creating event frames with okish names using analysis
Analysis has gone a long way in creating event frames that we are happier with, but as always, please votes for all the ways you think they should be getting better in our feedback system!
In this article, I want to give two tips on improving the eventframe names created by the analysis service.
The goal is to create event frames with names like using AF configurations and 4 lines of AF SDK:
Current state of naming
As you may know, to help in the naming of event frames, analysis allows to save the name of the start trigger to an event frame attribute. We can then use those attribute values in the naming pattern of the event frame.
An issue arrises when multiple start triggers are defined in order to create child event frames. In this case, this is how the event frames will be created:
- A start trigger becomes true and an event frame A is created.
- A second start trigger becomes true, then the following occurs
- Event frame A is closed
- Event frame B is created and event frame A is moved to be a child of event frame B. Event frame B also has the same name and start time as event frame A.
- Event frame C is created with the starttime which is the endtime of event frame A.
As the parent and first event frame will have the same name, one name as to be sacrificied, typically the compromise is to have all child names to be terrible.
PSE has the ability to reevaluate names. This is a manual process, but if we could automate this and create a naming scheme that is good for both parent and childs, we would have a solution.
Automatically reevalute event frames name
We will need a bit of AF SDK here (as far as I've looked, this is not exposed via PI Web API or Powershell yet)
This is done via the AFNameSubsitution.ResolveName method.
Here is a sample code, that loops over all the recent event frames and re-evaluates their names.
var af = (new PISystems()).DefaultPISystem;
var db = af.Databases["Test"];
using (var search = new AFEventFrameSearch(db, "FindRecentEvent", @"Start:>='*-3d' Template:'Event With Names'"))
foreach (AFEventFrame item in search.FindObjects(fullLoad: true))
You would have to play around with the Start and End parameter to grab just the event frames you want to update. Or you could create an event watcher that grabs all event frames as they are created or closed and reevaluates their names.
Small tricks to build a good name
To build a good name, we need the ability to distinguish if we are a child or parent event frame and base on that select the correct name.
Sadly, we don't have many tools to do this. Formula only works with numerical values, string builder don't has if statements, output expressions in the analysis formular work with both types, but the close values of the last event frame and the first will be the same. Here is what I came up with, but hopefully somebody in the comments can point out a better way.
- Add a "_" at the start of each trigger name in the analysis and store that trigger name (note that underscores don't show up in the analysis pane)
- Now as the parent event frame will have this Trigger attribute a blank string and child event frames will start with a underscore, we can build a 0/1 flag to distiguish the two. This is simply a string builder who will have type Int32, and replaces the first most character if it is a underscore with a 1. Note that an empty string gets evaluated to a 0
- Create the name if we have a parent event frame or a child.
- Now you can create your actual name attribute
And we are done!
An other issues with event analysis is that each child event frames are created back to back. In the real world, there is often a rest or preparation period between two subbatches. If you have gone this far to fix the names, create child event frames for all subbatches and rest periods and add a few lines to the above script to delete all unneeded event frames.
You would then have your events look like this in PI Vision
Sample code to delete event frames:
List<Guid> ids = null;
using (var search = new AFEventFrameSearch(db, "FindBlankRecentEvent", @"Start:>='*-3d' Name:'Blank'"))
while ((ids = search.FindObjectIds(pageSize: 2000).Take(2000).ToList()).Count > 0)