Last active 1 month ago

Revision 3c042a598edd2a9e95a4eacf6704e64f2d3f3a57

RunoutTime.py Raw
1import tkinter as tk
2from tkinter import ttk
3
4UPDATE_MS = 1000 # 1 second
5
6
7class Station:
8 def __init__(self, name, max_qty, start_qty, rate_per_min, refill_amount):
9 self.name = name
10 self.max_qty = max_qty
11 self.start_qty = start_qty
12 self.current_qty = start_qty
13 self.rate_per_min = rate_per_min
14 self.refill_amount = refill_amount
15
16 def reset(self):
17 self.current_qty = self.start_qty
18
19 def add(self):
20 self.current_qty = min(
21 self.max_qty,
22 self.current_qty + self.refill_amount
23 )
24
25 @property
26 def minutes_remaining(self):
27 if self.rate_per_min <= 0:
28 return 9999
29 return self.current_qty / self.rate_per_min
30
31
32class RunoutSimulator:
33
34 def __init__(self, root):
35 self.root = root
36 self.root.title("Runout Simulator")
37
38 self.running = False
39 self.speed_factor = 1.0
40
41 self.stations = [
42 Station("Tray Infeed", 135, 135, 11, 27),
43 Station("IFU Loader", 2000, 2000, 60, 100),
44 Station("Carton Magazine", 1000, 1000, 60, 100),
45 Station("Case Magazine", 100, 100, 3, 10),
46 Station("Pallet Capacity", 48, 48, 3, 1),
47 Station("Spare Buffer", 500, 500, 20, 50),
48 ]
49
50 self.station_widgets = []
51
52 self.build_ui()
53 self.update_display()
54
55 def build_ui(self):
56
57 controls = tk.Frame(self.root)
58 controls.pack(fill="x", padx=10, pady=5)
59
60 tk.Button(
61 controls,
62 text="Start",
63 command=self.start
64 ).pack(side="left")
65
66 tk.Button(
67 controls,
68 text="Pause",
69 command=self.pause
70 ).pack(side="left", padx=5)
71
72 tk.Button(
73 controls,
74 text="Reset",
75 command=self.reset
76 ).pack(side="left")
77
78 tk.Button(
79 controls,
80 text="Speed +10%",
81 command=self.speed_up
82 ).pack(side="left", padx=20)
83
84 tk.Button(
85 controls,
86 text="Speed -10%",
87 command=self.speed_down
88 ).pack(side="left")
89
90 self.speed_label = tk.Label(
91 controls,
92 text="Line Speed: 100%"
93 )
94 self.speed_label.pack(side="left", padx=20)
95
96 for station in self.stations:
97
98 frame = tk.LabelFrame(
99 self.root,
100 text=station.name,
101 padx=5,
102 pady=5
103 )
104 frame.pack(
105 fill="x",
106 padx=10,
107 pady=5
108 )
109
110 bar = ttk.Progressbar(
111 frame,
112 maximum=station.max_qty,
113 length=400
114 )
115 bar.pack(anchor="w")
116
117 qty_label = tk.Label(frame)
118 qty_label.pack(anchor="w")
119
120 time_label = tk.Label(frame)
121 time_label.pack(anchor="w")
122
123 refill_btn = tk.Button(
124 frame,
125 text=f"+{station.refill_amount}",
126 command=station.add
127 )
128 refill_btn.pack(anchor="e")
129
130 self.station_widgets.append(
131 (bar, qty_label, time_label)
132 )
133
134 self.runout_label = tk.Label(
135 self.root,
136 font=("Arial", 12, "bold"),
137 justify="left"
138 )
139 self.runout_label.pack(
140 anchor="w",
141 padx=10,
142 pady=10
143 )
144
145 def start(self):
146 if not self.running:
147 self.running = True
148 self.tick()
149
150 def pause(self):
151 self.running = False
152
153 def reset(self):
154 self.running = False
155
156 for station in self.stations:
157 station.reset()
158
159 self.speed_factor = 1.0
160 self.update_display()
161
162 def speed_up(self):
163 self.speed_factor *= 1.10
164 self.update_display()
165
166 def speed_down(self):
167 self.speed_factor *= 0.90
168 self.update_display()
169
170 def tick(self):
171
172 if not self.running:
173 return
174
175 for station in self.stations:
176
177 usage_per_second = (
178 station.rate_per_min
179 * self.speed_factor
180 / 60
181 )
182
183 station.current_qty = max(
184 0,
185 station.current_qty - usage_per_second
186 )
187
188 self.update_display()
189
190 self.root.after(
191 UPDATE_MS,
192 self.tick
193 )
194
195 def update_display(self):
196
197 for station, widgets in zip(
198 self.stations,
199 self.station_widgets):
200
201 bar, qty_label, time_label = widgets
202
203 bar["value"] = station.current_qty
204
205 qty_label.config(
206 text=f"Qty: {station.current_qty:.1f}/{station.max_qty}"
207 )
208
209 time_label.config(
210 text=f"Remaining: {station.minutes_remaining:.1f} min"
211 )
212
213 self.speed_label.config(
214 text=f"Line Speed: {self.speed_factor * 100:.0f}%"
215 )
216
217 ranking = sorted(
218 self.stations,
219 key=lambda s: s.minutes_remaining
220 )
221
222 text = "Runout Order\n\n"
223
224 for i, station in enumerate(ranking, start=1):
225 text += (
226 f"{i}. {station.name}"
227 f" - {station.minutes_