Estimated Completion Time: 4 - 6 hrs
Objective: Develop a custom data reference named "Limit Check" that checks the status of an attribute against its limit Traits. The resulting AFValue.Value will contain an Int32 of this set of acceptable values:
|-1||Lo violated but not LoLo|
|0||No limits violated, i.e. Normal|
|1||Hi violated but not HiHi|
Restriction: While your CDR must implement a GetValue method, you are not allowed to call GetValue on the measurement attribute or any of the limit trait attributes! (Hint: the GetInputs method will be where you define what attributes are required for the CDR, and you rely on the internals of the AFDataReference to provide the data to your calculation method.)
Such a data reference fulfils a very basic need for anyone who has ever worked with process data: you want to see if a measurement is performing within limits. This is a very easy state. It would be easy to code this to work with 1 known scenario (all 4 limits are defined, and all have good data). It's easy to write something that works only with good data. For this CDR, you need to consider what to do with bad or missing data, so now the challenge is not as easy! The considerations below are to stimulate thought on the expected behavior of the CDR along with what code you envision would be needed to produce such behavior.
Before writing a single line of code, take 10 to 15 minutes to reflect upon the following questions.
- Could this just as easily be done with Asset Analyses?
- How will the measurement attribute be declared to the CDR? In the ConfigString? If so, how?
- Must the limit traits also need to be defined in the ConfigString? Can they be automatically determined? Should they?
- Should any parameters within your ConfigString apply substitution syntax?
- What should you do if the measurement attribute lacks specific a Trait attribute? What happens if it only has a Hi? How would you code for that?
- What if it does have a Trait attribute, but that limit's value is bad (digital state) or NaN?
- Should your limits checks be inclusive (greater than or equal to, less than or equal to) or exclusive (greater than, less than)? Another way to state that is do you consider the limit violated when you meet its value or exceed its value?
- Should the CDR have a rigorous check that the measurement attribute and limit traits all have the same UOM? What if a UOM is null? Should you thrown an exception or assume null is compatible with as the measurement attribute?
- Should your CDR implement GetValues?
- Should your CDR implement Rich Data Access calls such as RecordedValue?
Before reading further, pause here and take time to seriously ponder the above. Suggestions and hints are below.
Ready to move on? Okay, let's acknowledge the intent of the course is not to have you spend too much time writing a CDR. There is no reason to get "lost in the weeds" on making the perfect DR. We do want is for you to gain experience on designing and creating one. And part of that valuable experience is stumbling upon some pain points. But we do not want you to spend a week doing so. With that in mind, let's look at some of the considerations and settle on some appropriate shortcuts.
First off, for the brevity of the exercise, you will need to code a GetValue method, along with ConfigString, GetInputs, and SupportedMethods. Those are the minimum methods needed to be overridden. Don't worry about overriding any GetValues or any Rich Data Access calls. If accounting for missing limits or bad data is taking too much time (pain points), then try to get your CDR working with exactly all 4 limits. There are no reasons to be rigorous with UOM checking since a restriction of limit traits is they must have the same UOM as their parent attribute, which happens to be our measurement attribute.
Could an Analysis be used? Without a doubt, such checks could be done with an Analysis. But not as easily. If an attribute only had a Hi, it would need a different analysis than an attribute that has a Hi and a Low, and one that has all 4 limit traits. How much time and effort do you want to put into customizing different analyses for different scenarios? The nice thing about the CDR is that you can write one set of code that accounts for all the scenarios.
How will the measurement attribute be declared in the CDR? It's essentially the only parameter we need at the moment, so you could get by with the ConfigString only containing the name of the measurement attribute. However, I am partial to "name=value;" pairs and it allows for more flexibility if we ever add any parameters in the future. You are free to use either technique. I choose to use "MeasAttr=name-of-measAttr".
Tip: if you want to refer to the parent of Attribute without spelling it out over and over, you could use "MeasAttr=..". The relative dotted notation also makes it easier and quicker to build templates because you are liberated from typing specific attribute names.
You may not think it is possible, but you do not have to declare the measurement attribute with the ConfigString! How would the CDR know which to use? The CDR has its own Attribute property, and you could restrict your CDR to only work for child attributes. You could then determine the measurement attribute by using Attribute.Parent. While this would make your ConfigString easier to manage, it also imposes a very strict constraint on the CDR. Personally, I prefer using the ConfigString so that anyone reviewing the configuration clearly sees your intent (e.g. MeasAttr=..). Less mystery now equals less confusion later.
The limit traits can and should be automatically determined based on the measurement attribute. This makes the CDR so much easier to use and makes your ConfigString cleaner. Hints: review help on these useful things:
- AFAttributeTrait class, particularly these properties: LimitLo, LimitHi, LimitLoLo, and LimitHiHi.
- AFAttribute.GetAttributesByTrait method may come in handy in your GetInputs implementation.
- AFAttribute.Trait property may come in handy in your GetValue implementation.
As of now, we only have the measurement attribute name to worry about in the ConfigString. Even still, we should be more accommodating and allow a bit of substitution on the name. This gives the AFDatabase admin the capability to use %variable% (with or without @ sign) when defining templates, which makes the CDR that much friendlier and useful. And the code needed is only 1 line.
Should violating a limit be inclusive or exclusive? I vote inclusive, but you really should match your company's strategy. Bonus challenge: consider having a default behavior but override based on a ConfigString setting.
Should you implement GetValues or Rich Data Access calls? Actually, yes, but you may be surprised about how little code is required. To implement these GetValues all you have to do is declare it within in the AFDataReference.SupportedMethods method (see AFDataReferenceMethod enumeration). You don't have to add any code to override it! You may just use the base implementation built into the AFDataReference class. Isn't that nice! Likewise, you can implement RDA calls by declaring them in AFDataReference.SupportedDataMethods method (see AFDataMethods enumeration), and once again, no additional code is needed as you may rely upon the base implementation. For RDA, all you need to add is:
public override AFDataMethods SupportedDataMethods => DefaultSupportedDataMethods;
What if a limit is missing or bad? What about the other considerations? We are offering discussions on such topics in a separate "Spoilers" link below. If you find yourself taking too long on this exercise, go ahead and it get working for a specific case, such as all limit traits are defined. If you are still stuck and need more help, click on the link below.
The links below contain code solutions in case you get stuck.
Please use the discussion forum to discuss or ask any questions about Exercise 4