I’ve been learning about custom history aggregate functions, and tried to implement my own
pctGood. I came up with a version that perfectly matched the results of the built one every single time. Then I noticed that neither version ever returned 100%. Both my version and presumably the built in one work by accumulating milliseconds duration that the tag is “good”, then divide by the time window period which will always be one millisecond longer than the accumulator could possibly be.
I modified my version to return
accumulated_ms / (ms_range - 1) and it started returning 100% perfectly.
I suppose this falls into the category of “errors so small no one ever cared”, but it bugs me.
Here’s my custom aggregator which returns 100% properly:
wrapper = '''python: def myPctGood(qval, interpolated, finished, blockContext, queryContext): prior_state = blockContext.get('prior_state', None) prior_start_ms = blockContext.get('prior_start_ms', None) accumulated_ms = blockContext.get('accumulated_ms', 0) current_state = qval.quality.isGood() current_ms = system.date.toMillis(qval.timestamp) if current_state != prior_state: if prior_state: accumulated_ms += (current_ms - prior_start_ms) blockContext['accumulated_ms'] = accumulated_ms prior_state = current_state prior_start_ms = current_ms blockContext['prior_state'] = prior_state blockContext['prior_start_ms'] = prior_start_ms if finished: if current_state: accumulated_ms += (current_ms - prior_start_ms) return (float(accumulated_ms) / float(blockContext.blockEnd - blockContext.blockStart - 1)) '''
As an aside, I’m a bit horrified by how the
python: custom aggregators are managed. I would feel safer if there were some way to pass in a pure Python lambda or function instead of a string that was compiled at runtime.
EDIT: I wouldn’t be surprised if other built-in history aggregators had the same issue as well.