Taking the sum of time that a machine is on(1), displaying value on label

hello, I am creating a dashboard for four machines. We run them at the same time every week, 6am Monday to 6 am Friday. I would like to write a script to connect to historian and sum the time that it is running, excluding the time it is false. I would then like to take the number of hours run and divide by the hours scheduled (binded textfield). I would like to output this percentage to a component. I have been trying to figure it out for a week but am still in the learning phase.

How is the data structured in the historian? Is it a simple true/false flag for running vs. not running, for each machine? Sounds like you are hoping to track OEE, or at least availability. I've done something similar by using a time series chart in Perspective and using a simple Boolean tag for running vs. not running. I get timestamps every time that tag changes in value, so from there it is simple to bind to this tag (or rather, its history), grab appropriate timestamps from the historian, and populate the time series chart. I'm including an example of how this looks.

It really all depends on how your data is structured and how you want your end result dashboard to look. You will probably have to provide more specific details to make any decent progress on here.


Historian uses analog compression and doesn't interpolate to give you values at the shift start and shift end.

Create your own table and log on machine run status change and on schedule every 15 minutes or whatever suits your expected shift patterns. Create columns in the table for t_stamp, machineId, runStatus, cycleCount, goodPartCount, stopReasonCode, productCode and anything else that you might want hourly/daily/monthly reports on. The counters should be "never-resetting".

Create a UDT tag structure that will cope with your setup and then create one instance of that for each machine. (An "enabled" boolean in each UDT can be useful too.) If you put all the machine instances in one tag folder you can then script to loop through all the tags and, if they're enabled, log to your table.

It will make reporting very simple and accurate.


I'm very new to ignition, so I have been doing my best to understand how things are able to speak to each other.
Can I use this code to schedule a log update? Where do I put it?:
def refreshGraph():
# Your code to refresh the graph data here

def scheduleRefresh():
# Schedule the refresh every 15 minutes
system.util.invokeLater(refreshGraph, 900000) # 900000 ms = 15 minutes

Call the function to schedule the refresh


What do you mean by the counters are never resetting?

Hi, Nathaniel. Please see Wiki - how to post code on this forum and then hit the pencil icon under your post to fix it. Preserving the indentation on Python scripting is important.

Also, there's general Reply button on the thread but there's also one on each post and when you use that the user gets a notification and it's obvious then who you are replying to.

There should be counters in the PLC that record the total number of parts, etc., since the machine was built. Record this count value rather than the batch or hourly count because then you can get the count difference for any interval by simple subtraction of the end and start values for the period of interest.

Have you created your own history table yet?

1 Like

Thank you, I don't think that will work though. I am recording time using the total_true_duration() function. Heres the script. I have it in the scheduled gateway event and am binding the output to memory tag, that I will pass to my component text.

def onScheduledEvent():
	from datetime import timedelta
	# Define the tag path to monitor and the memory tag path to write the total true duration
	tag_path = "[Sample_Tags]Devices/R101 RUN"
	memory_tag_path = "[Sample_Tags]Devices/R101 memory tag"
	# Initialize variables
	tag_state = False
	time_of_last_change = None
	total_true_duration = timedelta()
	# Function to update the total true duration
	def update_total_true_duration(current_time):
	    global time_of_last_change, total_true_duration
	    if tag_state:
	        if time_of_last_change is not None:
	            total_true_duration += current_time - time_of_last_change
	            print("Total true duration:", total_true_duration)
	        time_of_last_change = current_time
	        time_of_last_change = None
	# Function to collect data
	def collect_data():
	    global tag_state
	    # Get the current state of the tag
	    tag_value = system.tag.read(tag_path).value
	    # Update tag_state
	    tag_state = tag_value
	    # Update the total true duration
	# Schedule the script to run periodically
	system.util.invokeLater(collect_data, 1000)
	# Function to calculate total true duration in hours
	def calculate_total_true_duration_hours(total_true_duration):
	    # Calculate total true duration in hours
	    total_true_duration_hours = total_true_duration.total_seconds() / 3600
	    return total_true_duration_hours
	# Function to write total true duration to a memory tag
	def write_to_memory_tag(total_true_duration):
	    # Calculate total true duration in hours
	    total_true_duration_hours = calculate_total_true_duration_hours(total_true_duration)
	    # Write the total true duration to the memory tag
	    system.tag.write(memory_tag_path, total_true_duration_hours)
	# Schedule the script to write to memory tag periodically
	def scheduled_write_to_memory_tag():
	# Schedule the script to write to memory tag periodically
	system.util.invokeLater(scheduled_write_to_memory_tag, 10000)  # Adjust the interval as needed

Attached scrrenshots:

Have you looked for errors in your gateway log? { system.util.invokeLater() doesn't exist in gateway scope. }

You should not be relying on jython variables for data collection. Simply record the changes to [Sample_Tags]Devices/R101 RUN to your database with timestamps. SQL's LEAD() function makes short work of extracting the deltas.