0% found this document useful (0 votes)
6 views9 pages

Python Code OEE

The document is a Python script for monitoring Overall Equipment Effectiveness (OEE) of a rotary oven using a graphical user interface (GUI) built with Tkinter. It includes functionalities for logging stoppages, calculating OEE metrics, and generating reports, while interacting with GPIO pins for machine status monitoring. Key features include Excel logging for OEE data and stoppage logs, as well as visual representation of OEE trends using Matplotlib.

Uploaded by

Naga1989
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views9 pages

Python Code OEE

The document is a Python script for monitoring Overall Equipment Effectiveness (OEE) of a rotary oven using a graphical user interface (GUI) built with Tkinter. It includes functionalities for logging stoppages, calculating OEE metrics, and generating reports, while interacting with GPIO pins for machine status monitoring. Key features include Excel logging for OEE data and stoppage logs, as well as visual representation of OEE trends using Matplotlib.

Uploaded by

Naga1989
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

RESTRICTED

#!/usr/bin/env python3
import os, time, datetime, tkinter as tk
from tkinter import ttk, messagebox
from PIL import Image, ImageTk
import [Link] as GPIO
import openpyxl

# Matplotlib for the trend chart


import matplotlib
[Link]("Agg")
from [Link] import Figure
from [Link].backend_tkagg import FigureCanvasTkAgg

# =========================
# GPIO Pin Assignments
# =========================
CYCLE_START_PIN = 18 # FALLING edge = cycle START
CYCLE_COMPLETE_PIN = 24 # LOW = normal CYCLE COMPLETE
EMERGENCY_STOP_PIN = 25 # LOW = EMERGENCY STOP
MACHINE_FAULT_PIN = 26 # LOW = MACHINE FAULT

EXCEL_REPORT = "oee_daily_report.xlsx"

# =========================
# TPM Loss Categories (16 Major Losses)
# =========================
LOSS_CATEGORIES = [
"Machine Breakdown",
"Minor Stoppages",
"Setup & Adjustment",
"Cutting Tool Replacement",
"Startup Loss",
"Speed Reduction",
"Defects & Rework",
"Planned Shutdown Loss",
"Management Loss",
"Operating Motion Loss",
"Line Organization Loss",
"Logistics Loss",
"Measurement & Adjustment Loss",
"Energy Loss",
"Consumable Loss",
"Yield Loss"
]
RESTRICTED

# =========================
# Excel Initialization & Logging
# =========================
def initialize_excel():
if not [Link](EXCEL_REPORT):
wb = [Link]()
ws1 = [Link]
[Link] = "OEE Data"
[Link]([
"Date/Time","OEE (%)","Availability (%)","Performance (%)","Quality (%)",
"Cycle Day","Actual Cycle Time (min)",
*LOSS_CATEGORIES
])
ws2 = wb.create_sheet(title="Stoppage Log")
[Link](["Date/Time","Cycle Day","Loss Category","Duration (min)","Remarks"])
[Link](EXCEL_REPORT)

def log_oee_summary(row):
initialize_excel()
wb = openpyxl.load_workbook(EXCEL_REPORT)
ws = wb["OEE Data"]
[Link](row)
[Link](EXCEL_REPORT)

def log_stoppage_entries(entries):
initialize_excel()
wb = openpyxl.load_workbook(EXCEL_REPORT)
ws = wb["Stoppage Log"]
for e in entries:
[Link]([
e["timestamp"].strftime("%Y-%m-%d %H:%M:%S"),
e["timestamp"].day,
e["category"],
e["duration"],
e["remarks"]
])
[Link](EXCEL_REPORT)

# =========================
# OEE Calculation Functions
# =========================
def calc_availability(planned, losses):
return max(0.0, (planned - losses)/planned) if planned>0 else 0.0
RESTRICTED

def calc_performance(ideal, actual):


return ideal/actual if actual>0 else 0.0

def calc_quality(good, total):


return good/total if total>0 else 0.0

def calc_oee(a,p,q):
return a*p*q*100

# =========================
# Main App
# =========================
class OEEApp:
def _init_(self, root):
[Link] = root
[Link]("Rotary Oven OEE Monitor")
[Link]("1200x850")

# GPIO init
[Link]([Link])
for pin in (CYCLE_START_PIN, CYCLE_COMPLETE_PIN, EMERGENCY_STOP_PIN,
MACHINE_FAULT_PIN):
[Link](pin, [Link], pull_up_down=GPIO.PUD_UP)

# State
self.prev_start = [Link](CYCLE_START_PIN)==[Link]
self.prev_break = False
self.cycle_in_progress = False
self.cycle_start_time = None
self.actual_cycle_time = 0.0

# Stoppages & losses


self.stoppage_log = []
self.loss_totals = {cat:0.0 for cat in LOSS_CATEGORIES}
self.loss_vars = {cat:[Link](0.0) for cat in LOSS_CATEGORIES}

# OEE history
self.oee_history = [] # list of (timestamp, oee)

# Operator inputs
self.planned_time = [Link](480.0) # minutes
self.ideal_cycle = [Link](0.5) # minutes
self.total_parts = [Link](0)
RESTRICTED

self.good_parts = [Link](0)

self.build_widgets()
self.poll_gpio()

def build_widgets(self):
frm = [Link]([Link], padding=10)
[Link](sticky="nsew")

# Title & OEE summary


[Link](frm, text="Rotary Oven OEE Monitor", font=("Arial",16,"bold"))\
.grid(row=0,column=0,columnspan=4,pady=5)
self.lbl_oee = [Link](frm, text="OEE: --%", font=("Arial",24,"bold"))
self.lbl_oee.grid(row=1,column=0,columnspan=4,pady=5)
self.lbl_avail = [Link](frm, text="Availability: --%"); self.lbl_avail.grid(row=2,column=0,sticky="w")
self.lbl_perf = [Link](frm, text="Performance: --%"); self.lbl_perf .grid(row=3,column=0,sticky="w")
self.lbl_qual = [Link](frm, text="Quality: --%"); self.lbl_qual .grid(row=4,column=0,sticky="w")
self.lbl_act = [Link](frm, text="Actual Cycle Time: -- min")
self.lbl_act.grid(row=5,column=0,sticky="w")

# Tower lamp
lamp = [Link](frm, text="Status")
[Link](row=2,column=3,rowspan=4,padx=10,sticky="ne")
self.l_red = [Link](lamp,text="EMER STOP", width=12, bg="gray",fg="white")
self.l_amber = [Link](lamp,text="MACH FAULT", width=12, bg="gray")
self.l_green = [Link](lamp,text="RUNNING", width=12, bg="gray",fg="white")
self.l_blue = [Link](lamp,text="COMPLETE", width=12, bg="gray",fg="white")
for w in (self.l_red,self.l_amber,self.l_green,self.l_blue): [Link](pady=2)

# Inputs & Losses


inp = [Link](frm, text="Inputs & TPM Losses")
[Link](row=6,column=0,columnspan=4,sticky="ew",pady=10)
# operator inputs
labels = ["Planned Time (min):","Ideal Cycle (min):","Total Parts:","Good Parts:"]
vars = [self.planned_time,self.ideal_cycle,self.total_parts,self.good_parts]
for i,(lbl,var) in enumerate(zip(labels,vars)):
[Link](inp,text=lbl).grid(row=i,column=0,sticky="w")
[Link](inp,textvariable=var,width=10).grid(row=i,column=1,padx=5)
# loss totals
for idx,cat in enumerate(LOSS_CATEGORIES):
r = idx//2; c = (idx%2)*3+2
[Link](inp,text=cat+":").grid(row=r,column=c,sticky="e",padx=(20,2))
[Link](inp,textvariable=self.loss_vars[cat],width=8,state="readonly")\
.grid(row=r,column=c+1)
RESTRICTED

[Link](inp, text="Log Stoppage ", command=self.open_log_dialog)\


.grid(row=8,column=0,columnspan=2,pady=5)

# Stoppage Log
logf = [Link](frm,text="Stoppage Log")
[Link](row=7,column=0,columnspan=4,sticky="nsew",padx=5,pady=5)
cols=("Time","Day","Category","Duration","Remarks")
[Link] = [Link](logf,columns=cols,show="headings",height=6)
for c in cols:
[Link](c,text=c); [Link](c,width=100,anchor="center")
[Link](sticky="nsew")
sb = [Link](logf,orient="vertical",command=[Link])
[Link](row=0,column=1,sticky="ns")
[Link](yscrollcommand=[Link])

# Buttons
btns = [Link](frm); [Link](row=8,column=0,columnspan=4,pady=5)
[Link](btns,text="Calculate OEE", command=self.compute_oee).grid(row=0,column=0,padx=5)
[Link](btns,text="Generate Report",command=self.generate_report).grid(row=0,column=1,padx=5)

# Trend Chart
chartf = [Link](frm,text="OEE Trend (Last 30)")
[Link](row=9,column=0,columnspan=4,sticky="nsew",padx=5,pady=5)
[Link](9,weight=1); [Link](0,weight=1); [Link](0,weight=1)
[Link] = Figure(figsize=(7,2),dpi=100)
[Link] = [Link].add_subplot(111)
[Link].set_ylim(0,100)
[Link].set_xlabel("Day of Month")
[Link].set_ylabel("OEE (%)")
[Link].set_title("OEE Trend")
[Link] = FigureCanvasTkAgg([Link], master=chartf)
[Link].get_tk_widget().grid(sticky="nsew")

def open_log_dialog(self):
dlg = [Link]([Link]); [Link]("Log a Stoppage")
[Link](dlg,text="Loss Category:").grid(row=0,column=0,padx=5,pady=5)
cat = [Link](value=LOSS_CATEGORIES[0])
[Link](dlg,cat,*LOSS_CATEGORIES).grid(row=0,column=1)
[Link](dlg,text="Duration (min):").grid(row=1,column=0,padx=5,pady=5)
dur = [Link](0.0); [Link](dlg,textvariable=dur).grid(row=1,column=1)
[Link](dlg,text="Remarks:").grid(row=2,column=0,padx=5,pady=5)
rem = [Link](); [Link](dlg,textvariable=rem,width=30).grid(row=2,column=1)
def add():
RESTRICTED

ts = [Link]()
e = {"timestamp":ts,"category":[Link](),
"duration":[Link](),"remarks":[Link]().strip()}
self.stoppage_log.append(e)
self.loss_totals[e["category"]] += e["duration"]
self.loss_vars[e["category"]].set(round(self.loss_totals[e["category"]],2))
[Link]("",[Link],values=(
[Link]("%H:%M:%S"),[Link],e["category"],
f"{e['duration']:.2f}",e["remarks"]
))
[Link]()
[Link](dlg,text="Add",command=add).grid(row=3,column=0,pady=10)
[Link](dlg,text="Cancel",command=[Link]).grid(row=3,column=1,pady=10)

def open_breakdown_dialog(self, category, duration):


dlg = [Link]([Link]); [Link](f"{category} Details")
[Link](dlg,text=f"{category} Duration: {duration:.2f} min").grid(row=0,column=0,columnspan=2,pady=5)
[Link](dlg,text="Remarks:").grid(row=1,column=0,padx=5,pady=5)
rem = [Link]()
[Link](dlg,textvariable=rem,width=30).grid(row=1,column=1,padx=5,pady=5)
def add():
ts = [Link]()
e = {"timestamp":ts,"category":category,
"duration":duration,"remarks":[Link]().strip()}
self.stoppage_log.append(e)
self.loss_totals[category] += duration
self.loss_vars[category].set(round(self.loss_totals[category],2))
[Link]("",[Link],values=(
[Link]("%H:%M:%S"),[Link],category,
f"{duration:.2f}",e["remarks"]
))
# recalc & trend
self.compute_oee()
self.oee_history.append((ts,[Link]))
if len(self.oee_history)>30: self.oee_history.pop(0)
self.draw_trend()
[Link]()
[Link](dlg,text="Add Remark",command=add).grid(row=2,column=0,pady=10)
[Link](dlg,text="Cancel", command=[Link]).grid(row=2,column=1,pady=10)

def open_post_cycle_dialog(self):
dlg = [Link]([Link]); [Link]("Enter Parts Count")
[Link](dlg,text="Total Parts Produced:").grid(row=0,column=0,padx=5,pady=5)
tot = [Link](0); [Link](dlg,textvariable=tot).grid(row=0,column=1)
RESTRICTED

[Link](dlg,text="Good Parts Count:").grid(row=1,column=0,padx=5,pady=5)


good= [Link](0); [Link](dlg,textvariable=good).grid(row=1,column=1)
def add():
self.total_parts .set([Link]())
self.good_parts .set([Link]())
# now that counts are in, compute & trend
ts = [Link]()
self.compute_oee()
self.oee_history.append((ts,[Link]))
if len(self.oee_history)>30: self.oee_history.pop(0)
self.draw_trend()
[Link]()
[Link](dlg,text="Submit", command=add).grid(row=2,column=0,pady=10)
[Link](dlg,text="Cancel", command=[Link]).grid(row=2,column=1,pady=10)

def poll_gpio(self):
start = [Link](CYCLE_START_PIN)==[Link]
complete= [Link](CYCLE_COMPLETE_PIN)==[Link]
emer = [Link](EMERGENCY_STOP_PIN)==[Link]
fault = [Link](MACHINE_FAULT_PIN)==[Link]
breakdown = emer or fault

# START detection (falling edge)


if not self.cycle_in_progress and start and self.prev_start:
self.cycle_in_progress = True
self.cycle_start_time = [Link]()
self.prev_start = start

# BREAKDOWN detection
if self.cycle_in_progress and breakdown and not self.prev_break:
self.cycle_in_progress = False
dur = ([Link]() - self.cycle_start_time)/60.0
cat = "Emergency Stop" if emer else "Machine Breakdown"
self.open_breakdown_dialog(cat, dur)
self.prev_break = breakdown

# NORMAL complete
if self.cycle_in_progress and complete:
self.cycle_in_progress = False
self.actual_cycle_time = ([Link]() - self.cycle_start_time)/60.0
# update UI display of cycle time
self.lbl_act.config(text=f"Actual Cycle Time: {self.actual_cycle_time:.2f} min")
# then ask parts count
self.open_post_cycle_dialog()
RESTRICTED

# Update tower lamp


self.update_lamp(emer, fault, self.cycle_in_progress, complete)

# repeat
[Link](200, self.poll_gpio)

def update_lamp(self, emer, fault, running, complete):


self.l_red .config(bg="red" if emer else "gray")
self.l_amber.config(bg="orange" if fault else "gray")
self.l_green.config(bg="green" if running else "gray")
self.l_blue .config(bg="blue" if complete and not running else "gray")

def compute_oee(self):
total_losses = sum(self.loss_totals.values())
A = calc_availability(self.planned_time.get(), total_losses)
P = calc_performance(self.ideal_cycle.get(), self.actual_cycle_time)
Q = calc_quality(self.good_parts.get(), self.total_parts.get())
[Link] = calc_oee(A,P,Q)
# update labels
self.lbl_oee .config(text=f"OEE: {[Link]:.1f}%")
self.lbl_avail.config(text=f"Availability: {A*100:.1f}%")
self.lbl_perf .config(text=f"Performance: {P*100:.1f}%")
self.lbl_qual .config(text=f"Quality: {Q*100:.1f}%")

def draw_trend(self):
days = [[Link] for ts,_ in self.oee_history]
vals = [v for _,v in self.oee_history]
[Link]()
[Link].set_ylim(0,100)
[Link].set_xlabel("Day of Month")
[Link].set_ylabel("OEE (%)")
[Link].set_title("OEE Trend")
[Link](days, vals, marker="o", color="blue")
[Link].set_xticks(list(range(1,32)))
[Link]()

def generate_report(self):
now = [Link]()
row = [
[Link]("%Y-%m-%d %H:%M:%S"),
round([Link],1),
round(calc_availability(self.planned_time.get(), sum(self.loss_totals.values()))*100,1),
round(calc_performance(self.ideal_cycle.get(),self.actual_cycle_time)*100,1),
RESTRICTED

round(calc_quality(self.good_parts.get(), self.total_parts.get())*100,1),
[Link],
round(self.actual_cycle_time,2),
*[round(self.loss_totals[cat],2) for cat in LOSS_CATEGORIES]
]
log_oee_summary(row)
log_stoppage_entries(self.stoppage_log)
[Link]("Report","Excel report updated.")

def cleanup(self):
[Link]()

# =========================
# Run
# =========================
if _name=="main_":
root = [Link]()
app = OEEApp(root)
def on_close():
if [Link]("Quit","Exit?"):
[Link]()
[Link]()
[Link]("WM_DELETE_WINDOW", on_close)
[Link]()

You might also like