import tkinter as tk from tkinter import ttk UPDATE_MS = 1000 # 1 second class Station: def __init__(self, name, max_qty, start_qty, rate_per_min, refill_amount): self.name = name self.max_qty = max_qty self.start_qty = start_qty self.current_qty = start_qty self.rate_per_min = rate_per_min self.refill_amount = refill_amount def reset(self): self.current_qty = self.start_qty def add(self): self.current_qty = min( self.max_qty, self.current_qty + self.refill_amount ) @property def minutes_remaining(self): if self.rate_per_min <= 0: return 9999 return self.current_qty / self.rate_per_min class RunoutSimulator: def __init__(self, root): self.root = root self.root.title("Runout Simulator") self.running = False self.speed_factor = 1.0 self.stations = [ Station("Tray Infeed", 135, 135, 11, 27), Station("IFU Loader", 2000, 2000, 60, 100), Station("Carton Magazine", 1000, 1000, 60, 100), Station("Case Magazine", 100, 100, 3, 10), Station("Pallet Capacity", 48, 48, 3, 1), Station("Spare Buffer", 500, 500, 20, 50), ] self.station_widgets = [] self.build_ui() self.update_display() def build_ui(self): controls = tk.Frame(self.root) controls.pack(fill="x", padx=10, pady=5) tk.Button( controls, text="Start", command=self.start ).pack(side="left") tk.Button( controls, text="Pause", command=self.pause ).pack(side="left", padx=5) tk.Button( controls, text="Reset", command=self.reset ).pack(side="left") tk.Button( controls, text="Speed +10%", command=self.speed_up ).pack(side="left", padx=20) tk.Button( controls, text="Speed -10%", command=self.speed_down ).pack(side="left") self.speed_label = tk.Label( controls, text="Line Speed: 100%" ) self.speed_label.pack(side="left", padx=20) for station in self.stations: frame = tk.LabelFrame( self.root, text=station.name, padx=5, pady=5 ) frame.pack( fill="x", padx=10, pady=5 ) bar = ttk.Progressbar( frame, maximum=station.max_qty, length=400 ) bar.pack(anchor="w") qty_label = tk.Label(frame) qty_label.pack(anchor="w") time_label = tk.Label(frame) time_label.pack(anchor="w") refill_btn = tk.Button( frame, text=f"+{station.refill_amount}", command=station.add ) refill_btn.pack(anchor="e") self.station_widgets.append( (bar, qty_label, time_label) ) self.runout_label = tk.Label( self.root, font=("Arial", 12, "bold"), justify="left" ) self.runout_label.pack( anchor="w", padx=10, pady=10 ) def start(self): if not self.running: self.running = True self.tick() def pause(self): self.running = False def reset(self): self.running = False for station in self.stations: station.reset() self.speed_factor = 1.0 self.update_display() def speed_up(self): self.speed_factor *= 1.10 self.update_display() def speed_down(self): self.speed_factor *= 0.90 self.update_display() def tick(self): if not self.running: return for station in self.stations: usage_per_second = ( station.rate_per_min * self.speed_factor / 60 ) station.current_qty = max( 0, station.current_qty - usage_per_second ) self.update_display() self.root.after( UPDATE_MS, self.tick ) def update_display(self): for station, widgets in zip( self.stations, self.station_widgets): bar, qty_label, time_label = widgets bar["value"] = station.current_qty qty_label.config( text=f"Qty: {station.current_qty:.1f}/{station.max_qty}" ) time_label.config( text=f"Remaining: {station.minutes_remaining:.1f} min" ) self.speed_label.config( text=f"Line Speed: {self.speed_factor * 100:.0f}%" ) ranking = sorted( self.stations, key=lambda s: s.minutes_remaining ) text = "Runout Order\n\n" for i, station in enumerate(ranking, start=1): text += ( f"{i}. {station.name}" f" - {station.minutes_remaining:.1f} min\n" ) self.runout_label.config(text=text) root = tk.Tk() app = RunoutSimulator(root) root.mainloop()