queryTagHistory DurationOn and DurationOff

Hello,
I have numerous Boolean tags tracking pilot statuses for various flares. I need to measure the number of seconds every day that the pilot is on and off. Currently, I'm using a QueryTagHistory using the DurationOn and DurationOff aggregation modes for a query of a duration of 24 hours (8AM to 8AM). The DurationOff is pulling 0 for almost every day (because the flare has been on). But the DurationOn is pulling various values close to 86,400 seconds (24 hours) but not quite. Some days it's exactly 86,400, but most days it is below. Any ideas why I am getting some days with below 24 hours of time logged? Is there anything I can do to fix this so that DurationOn and DurationOff add up to 24 hours?
Thanks for the help!

I have given up on the historian for jobs like this. As far as I can see there is no way to get tag history properly interpolated for hour (or any other) boundaries. The calculations appear to be done on the first and last readings inside the selected range and the system does not extrapolate out to the edges of the selected range. The result is what you are experiencing, missing periods of time.

I've raised this a couple of times on the forum but haven't seen a satisfactory answer why IA think it should work the way it does or what use it is.

On the most recent project I'm working on I've used Scheduled Gateway Events to log tag values on the hour (actually every 15 minutes) into a dedicated table and this has worked very well.

2 Likes

I just did a quick check on our data and we didn't have the same experience, duration + durationOff = 86400 always for every machine. We have machines that run intermittently and machines that run near constantly, but same result. I've shared the code we're using, let me know if it's any different for you guys:

def duration(tag, startTime, endTime, sampleSize):
	pyData = system.dataset.toPyDataSet(system.tag.queryTagHistory(paths=[tag], startDate=startTime, endDate=endTime, returnSize=sampleSize, aggregationMode="DurationOn", returnFormat='Wide'))
	value = 0
	for row in pyData:
		if row[1] != None:
			if isinstance(int(row[1]), int):
				value += row[1]
			
	return value
		
	
def durationOff(tag, startTime, endTime, sampleSize):
	pyData = system.dataset.toPyDataSet(system.tag.queryTagHistory(paths=[tag], startDate=startTime, endDate=endTime, returnSize=sampleSize, aggregationMode="DurationOff", returnFormat='Wide'))
	value = 0
	for row in pyData:
		if row[1] != None:
			value += row[1]
				
	return value

And this is the code I used for checking, every 'Total' was 0:

import system
from java.util import Calendar
from historical import duration, durationOff

# Function to parse 'cell', 'area', and 'machine' identifiers from a machine tag
def extract_info_from_tag(tag):
    parts = tag.split('/')
    if len(parts) >= 5:
        return parts[1], parts[2], parts[3]
    else:
        return "Unknown", "Unknown", "Unknown"

# Calculate start and end times
calendar = Calendar.getInstance()
calendar.add(Calendar.DATE, -1)
yesterday = calendar.getTime()
startTime = system.date.midnight(yesterday)
endTime = system.date.addDays(startTime, 1)

machine_tags = [
    # tags for all machines, can't share publicly
    ]

for machine_tag in machine_tags:
        # Calculate run seconds
        runSeconds = duration(machine_tag, startTime, endTime, 1)
    
        # Calculate downtime seconds
        downtimeSeconds = durationOff(machine_tag, startTime, endTime, 1)
    
        # Extract cell, area, and machine info
        cell, area, machine = extract_info_from_tag(machine_tag)

        # Print info
        print("Cell: {}, Area: {}, Machine: {}, Run seconds: {}, Downtime seconds: {}, Total - 86400: {}".format(cell, area, machine, runSeconds, downtimeSeconds, runSeconds + downtimeSeconds - 86400))