Last active 1 month ago

wschrab revised this gist 1 month ago. Go to revision

1 file changed, 0 insertions, 0 deletions

RunoutTime2.zip

Binary file changes are not shown

wschrab revised this gist 1 month ago. Go to revision

1 file changed, 0 insertions, 0 deletions

RunoutTime2.zip(file created)

Binary file changes are not shown

wschrab revised this gist 1 month ago. Go to revision

1 file changed, 534 insertions

RunoutTime2.py(file created)

@@ -0,0 +1,534 @@
1 + import tkinter as tk
2 + from tkinter import ttk
3 +
4 + UPDATE_MS = 1000 # 1 second
5 +
6 + TRAY_SIZE = 27
7 + MAX_INFEED_TRAYS = 50
8 + MAX_OUTFEED_TRAYS = 50
9 +
10 + START_CARTONS = 1000
11 + START_IFUS = 2000
12 + START_CASES = 100
13 +
14 + PALLET_CAPACITY = 60
15 +
16 +
17 + class RunoutSimulator:
18 +
19 + def __init__(self, root):
20 + self.root = root
21 + self.root.title("Runout Simulator")
22 +
23 + self.running = False
24 +
25 + self.base_line_speed = 60 # devices/min
26 + self.speed_factor = 1.0
27 + self.device_credit = 0.0
28 +
29 + self.mode = tk.StringVar(value="4-Pack")
30 +
31 + self.reset_values()
32 +
33 + self.build_ui()
34 + self.update_display()
35 +
36 + def reset_values(self):
37 + self.infeed_trays = [TRAY_SIZE] * MAX_INFEED_TRAYS
38 + self.outfeed_trays = 0
39 +
40 + self.cartons = START_CARTONS
41 + self.ifus = START_IFUS
42 + self.cases = START_CASES
43 +
44 + self.current_carton_devices = 0
45 + self.current_case_cartons = 0
46 + self.pallet_cases = 0
47 +
48 + self.device_credit = 0.0
49 + self.status = "Ready"
50 +
51 + def devices_per_carton(self):
52 + if self.mode.get() == "4-Pack":
53 + return 4
54 + return 1
55 +
56 + def cartons_per_case(self):
57 + if self.mode.get() == "4-Pack":
58 + return 18
59 + return 40
60 +
61 + def build_ui(self):
62 +
63 + controls = tk.Frame(self.root)
64 + controls.pack(fill="x", padx=10, pady=5)
65 +
66 + tk.Button(
67 + controls,
68 + text="Start",
69 + command=self.start
70 + ).pack(side="left")
71 +
72 + tk.Button(
73 + controls,
74 + text="Pause",
75 + command=self.pause
76 + ).pack(side="left", padx=5)
77 +
78 + tk.Button(
79 + controls,
80 + text="Reset",
81 + command=self.reset
82 + ).pack(side="left")
83 +
84 + tk.Button(
85 + controls,
86 + text="Speed +10%",
87 + command=self.speed_up
88 + ).pack(side="left", padx=20)
89 +
90 + tk.Button(
91 + controls,
92 + text="Speed -10%",
93 + command=self.speed_down
94 + ).pack(side="left")
95 +
96 + self.speed_label = tk.Label(controls)
97 + self.speed_label.pack(side="left", padx=20)
98 +
99 + mode_frame = tk.Frame(self.root)
100 + mode_frame.pack(fill="x", padx=10, pady=5)
101 +
102 + tk.Label(
103 + mode_frame,
104 + text="Mode:"
105 + ).pack(side="left")
106 +
107 + tk.Radiobutton(
108 + mode_frame,
109 + text="4-Pack",
110 + variable=self.mode,
111 + value="4-Pack",
112 + command=self.change_mode
113 + ).pack(side="left")
114 +
115 + tk.Radiobutton(
116 + mode_frame,
117 + text="1-Pack",
118 + variable=self.mode,
119 + value="1-Pack",
120 + command=self.change_mode
121 + ).pack(side="left")
122 +
123 + # Infeed
124 + self.infeed_frame = tk.LabelFrame(
125 + self.root,
126 + text="Device Infeed",
127 + padx=5,
128 + pady=5
129 + )
130 + self.infeed_frame.pack(fill="x", padx=10, pady=5)
131 +
132 + self.infeed_bar = ttk.Progressbar(
133 + self.infeed_frame,
134 + maximum=MAX_INFEED_TRAYS * TRAY_SIZE
135 + )
136 + self.infeed_bar.pack(fill="x", expand=True)
137 +
138 + self.infeed_label = tk.Label(self.infeed_frame)
139 + self.infeed_label.pack(anchor="w")
140 +
141 + tk.Button(
142 + self.infeed_frame,
143 + text="+5 Trays",
144 + command=self.add_infeed_trays
145 + ).pack(anchor="e")
146 +
147 + # Outfeed
148 + self.outfeed_frame = tk.LabelFrame(
149 + self.root,
150 + text="Tray Outfeed",
151 + padx=5,
152 + pady=5
153 + )
154 + self.outfeed_frame.pack(fill="x", padx=10, pady=5)
155 +
156 + self.outfeed_bar = ttk.Progressbar(
157 + self.outfeed_frame,
158 + maximum=MAX_OUTFEED_TRAYS
159 + )
160 + self.outfeed_bar.pack(fill="x", expand=True)
161 +
162 + self.outfeed_label = tk.Label(self.outfeed_frame)
163 + self.outfeed_label.pack(anchor="w")
164 +
165 + tk.Button(
166 + self.outfeed_frame,
167 + text="Clear Outfeed",
168 + command=self.clear_outfeed
169 + ).pack(anchor="e")
170 +
171 + # Cartons
172 + self.carton_frame = tk.LabelFrame(
173 + self.root,
174 + text="Carton Magazine",
175 + padx=5,
176 + pady=5
177 + )
178 + self.carton_frame.pack(fill="x", padx=10, pady=5)
179 +
180 + self.carton_bar = ttk.Progressbar(
181 + self.carton_frame,
182 + maximum=START_CARTONS
183 + )
184 + self.carton_bar.pack(fill="x", expand=True)
185 +
186 + self.carton_label = tk.Label(self.carton_frame)
187 + self.carton_label.pack(anchor="w")
188 +
189 + tk.Button(
190 + self.carton_frame,
191 + text="+20 Cartons",
192 + command=self.add_cartons
193 + ).pack(anchor="e")
194 +
195 + # IFUs
196 + self.ifu_frame = tk.LabelFrame(
197 + self.root,
198 + text="IFU Loader",
199 + padx=5,
200 + pady=5
201 + )
202 + self.ifu_frame.pack(fill="x", padx=10, pady=5)
203 +
204 + self.ifu_bar = ttk.Progressbar(
205 + self.ifu_frame,
206 + maximum=START_IFUS
207 + )
208 + self.ifu_bar.pack(fill="x", expand=True)
209 +
210 + self.ifu_label = tk.Label(self.ifu_frame)
211 + self.ifu_label.pack(anchor="w")
212 +
213 + tk.Button(
214 + self.ifu_frame,
215 + text="+100 IFUs",
216 + command=self.add_ifus
217 + ).pack(anchor="e")
218 +
219 + # Case Supply
220 + self.case_supply_frame = tk.LabelFrame(
221 + self.root,
222 + text="Case Magazine",
223 + padx=5,
224 + pady=5
225 + )
226 + self.case_supply_frame.pack(fill="x", padx=10, pady=5)
227 +
228 + self.case_supply_bar = ttk.Progressbar(
229 + self.case_supply_frame,
230 + maximum=START_CASES
231 + )
232 + self.case_supply_bar.pack(fill="x", expand = True)
233 +
234 + self.case_supply_label = tk.Label(self.case_supply_frame)
235 + self.case_supply_label.pack(anchor="w")
236 +
237 + tk.Button(
238 + self.case_supply_frame,
239 + text=f"10 Cases",
240 + command=self.add_cases
241 + ).pack(anchor="e")
242 +
243 + # Current carton
244 + self.current_carton_frame = tk.LabelFrame(
245 + self.root,
246 + text="Current Carton",
247 + padx=5,
248 + pady=5
249 + )
250 + self.current_carton_frame.pack(fill="x", padx=10, pady=5)
251 +
252 + self.current_carton_bar = ttk.Progressbar(
253 + self.current_carton_frame
254 + )
255 + self.current_carton_bar.pack(fill="x", expand=True)
256 +
257 + self.current_carton_label = tk.Label(self.current_carton_frame)
258 + self.current_carton_label.pack(anchor="w")
259 +
260 + # Current case
261 + self.current_case_frame = tk.LabelFrame(
262 + self.root,
263 + text="Current Case",
264 + padx=5,
265 + pady=5
266 + )
267 + self.current_case_frame.pack(fill="x", padx=10, pady=5)
268 +
269 + self.current_case_bar = ttk.Progressbar(
270 + self.current_case_frame
271 + )
272 + self.current_case_bar.pack(fill="x", expand=True)
273 +
274 + self.current_case_label = tk.Label(self.current_case_frame)
275 + self.current_case_label.pack(anchor="w")
276 +
277 + # Pallet
278 + self.pallet_frame = tk.LabelFrame(
279 + self.root,
280 + text="Finished Pallet",
281 + padx=5,
282 + pady=5
283 + )
284 + self.pallet_frame.pack(fill="x", padx=10, pady=5)
285 +
286 + self.pallet_bar = ttk.Progressbar(
287 + self.pallet_frame,
288 + maximum=PALLET_CAPACITY
289 + )
290 + self.pallet_bar.pack(fill="x", expand=True)
291 +
292 + self.pallet_label = tk.Label(self.pallet_frame)
293 + self.pallet_label.pack(anchor="w")
294 +
295 + self.pallet_button = tk.Button(
296 + self.pallet_frame,
297 + text="Replace Full Pallet",
298 + command=self.replace_pallet
299 + )
300 + self.pallet_button.pack(anchor="e")
301 +
302 + # Status
303 + self.status_label = tk.Label(
304 + self.root,
305 + font=("Arial", 11, "bold")
306 + )
307 + self.status_label.pack(anchor="w", padx=10, pady=10)
308 +
309 + def start(self):
310 + if not self.running:
311 + self.running = True
312 + self.status = "Running"
313 + self.tick()
314 +
315 + def pause(self):
316 + self.running = False
317 + self.status = "Paused"
318 + self.update_display()
319 +
320 + def reset(self):
321 + self.running = False
322 + self.speed_factor = 1.0
323 + self.reset_values()
324 + self.update_display()
325 +
326 + def change_mode(self):
327 + self.running = False
328 + self.speed_factor = 1.0
329 + self.reset_values()
330 + self.update_display()
331 +
332 + def speed_up(self):
333 + self.speed_factor *= 1.10
334 + self.update_display()
335 +
336 + def speed_down(self):
337 + self.speed_factor *= 0.90
338 + self.update_display()
339 +
340 + def add_infeed_trays(self):
341 + space_available = MAX_INFEED_TRAYS - len(self.infeed_trays)
342 + trays_to_add = min(5, space_available)
343 +
344 + for _ in range(trays_to_add):
345 + self.infeed_trays.append(TRAY_SIZE)
346 +
347 + self.update_display()
348 +
349 + def clear_outfeed(self):
350 + self.outfeed_trays = 0
351 + self.update_display()
352 +
353 + def add_cartons(self):
354 + self.cartons = min(START_CARTONS, self.cartons + 20)
355 + self.update_display()
356 +
357 + def add_ifus(self):
358 + self.ifus = min(START_IFUS, self.ifus + 100)
359 + self.update_display()
360 +
361 + def add_cases(self):
362 + self.cases = min(START_CASES, self.cases + 10)
363 + self.update_display()
364 +
365 + def replace_pallet(self):
366 + if self.pallet_cases >= PALLET_CAPACITY:
367 + self.pallet_cases = 0
368 + self.status = "Ready"
369 + self.update_display()
370 +
371 + def tick(self):
372 +
373 + if not self.running:
374 + return
375 +
376 + current_speed = self.base_line_speed * self.speed_factor
377 + self.device_credit += current_speed / 60
378 +
379 + while self.device_credit >= 1 and self.running:
380 + success = self.process_one_device()
381 +
382 + if success:
383 + self.device_credit -= 1
384 + else:
385 + self.running = False
386 + self.device_credit = 0
387 +
388 + self.update_display()
389 +
390 + if self.running:
391 + self.root.after(UPDATE_MS, self.tick)
392 +
393 + def process_one_device(self):
394 +
395 + if len(self.infeed_trays) == 0:
396 + self.status = "Stopped - No infeed trays"
397 + return False
398 +
399 + if self.pallet_cases >= PALLET_CAPACITY:
400 + self.status = "Stopped - Pallet full"
401 + return False
402 +
403 + active_tray_devices = self.infeed_trays[0]
404 +
405 + if active_tray_devices == 1 and self.outfeed_trays >= MAX_OUTFEED_TRAYS:
406 + self.status = "Stopped - Tray outfeed full"
407 + return False
408 +
409 + will_complete_carton = (
410 + self.current_carton_devices + 1 >= self.devices_per_carton()
411 + )
412 +
413 + if will_complete_carton:
414 + if self.cartons <= 0:
415 + self.status = "Stopped - No cartons"
416 + return False
417 +
418 + if self.ifus <= 0:
419 + self.status = "Stopped - No IFUs"
420 + return False
421 +
422 + # Remove one device from the current tray
423 + self.infeed_trays[0] -= 1
424 +
425 + # Add device to current carton
426 + self.current_carton_devices += 1
427 +
428 + # If tray is empty, move it to outfeed
429 + if self.infeed_trays[0] == 0:
430 + self.infeed_trays.pop(0)
431 + self.outfeed_trays += 1
432 +
433 + # If carton is complete, consume 1 carton and 1 IFU
434 + if self.current_carton_devices >= self.devices_per_carton():
435 + self.current_carton_devices = 0
436 + self.cartons -= 1
437 + self.ifus -= 1
438 +
439 + self.current_case_cartons += 1
440 +
441 + # If case is complete, move 1 case to pallet
442 + if self.current_case_cartons >= self.cartons_per_case():
443 +
444 + if self.cases <= 0:
445 + self.status = "Stopped - No cases"
446 + return False
447 +
448 + self.current_case_cartons = 0
449 + self.cases -= 1
450 + self.pallet_cases += 1
451 +
452 + if self.pallet_cases >= PALLET_CAPACITY:
453 + self.pallet_cases = PALLET_CAPACITY
454 + self.status = "Stopped - Pallet full"
455 + return False
456 +
457 + self.status = "Running"
458 + return True
459 +
460 + def update_display(self):
461 +
462 + current_speed = self.base_line_speed * self.speed_factor
463 +
464 + self.speed_label.config(
465 + text=f"Line Speed: {current_speed:.0f} devices/min "
466 + f"({self.speed_factor * 100:.0f}%)"
467 + )
468 +
469 + total_infeed_devices = sum(self.infeed_trays)
470 + self.infeed_bar["value"] = total_infeed_devices
471 +
472 + if len(self.infeed_trays) > 0:
473 + first_tray_text = f"Current tray: {self.infeed_trays[0]} / {TRAY_SIZE} devices"
474 + else:
475 + first_tray_text = "Current tray: none"
476 +
477 + self.infeed_label.config(
478 + text=f"{len(self.infeed_trays)} trays, "
479 + f"{total_infeed_devices} devices remaining\n"
480 + f"{first_tray_text}"
481 + )
482 +
483 + self.outfeed_bar["value"] = self.outfeed_trays
484 + self.outfeed_label.config(
485 + text=f"{self.outfeed_trays} / {MAX_OUTFEED_TRAYS} empty trays"
486 + )
487 +
488 + self.carton_bar["value"] = self.cartons
489 + self.carton_label.config(
490 + text=f"{self.cartons} / {START_CARTONS} cartons"
491 + )
492 +
493 + self.ifu_bar["value"] = self.ifus
494 + self.ifu_label.config(
495 + text=f"{self.ifus} / {START_IFUS} IFUs"
496 + )
497 +
498 + self.case_supply_bar["value"] = self.cases
499 + self.case_supply_label.config(
500 + text=f"{self.cases} / {START_CASES} cases available"
501 + )
502 +
503 + self.current_carton_bar["maximum"] = self.devices_per_carton()
504 + self.current_carton_bar["value"] = self.current_carton_devices
505 + self.current_carton_label.config(
506 + text=f"{self.current_carton_devices} / "
507 + f"{self.devices_per_carton()} devices"
508 + )
509 +
510 + self.current_case_bar["maximum"] = self.cartons_per_case()
511 + self.current_case_bar["value"] = self.current_case_cartons
512 + self.current_case_label.config(
513 + text=f"{self.current_case_cartons} / "
514 + f"{self.cartons_per_case()} cartons"
515 + )
516 +
517 + self.pallet_bar["value"] = self.pallet_cases
518 + self.pallet_label.config(
519 + text=f"{self.pallet_cases} / {PALLET_CAPACITY} cases"
520 + )
521 +
522 + if self.pallet_cases >= PALLET_CAPACITY:
523 + self.pallet_button.config(state="normal")
524 + else:
525 + self.pallet_button.config(state="disabled")
526 +
527 + self.status_label.config(
528 + text=f"Status: {self.status}"
529 + )
530 +
531 +
532 + root = tk.Tk()
533 + app = RunoutSimulator(root)
534 + root.mainloop()

wschrab revised this gist 1 month ago. Go to revision

1 file changed, 493 deletions

RunoutTime2.py (file deleted)

@@ -1,493 +0,0 @@
1 - import tkinter as tk
2 - from tkinter import ttk
3 -
4 - UPDATE_MS = 1000 # 1 second
5 -
6 - TRAY_SIZE = 27
7 - MAX_INFEED_TRAYS = 50
8 - MAX_OUTFEED_TRAYS = 50
9 -
10 - START_CARTONS = 1000
11 - START_IFUS = 2000
12 -
13 - PALLET_CAPACITY = 60
14 -
15 -
16 - class RunoutSimulator:
17 -
18 - def __init__(self, root):
19 - self.root = root
20 - self.root.title("Runout Simulator")
21 -
22 - self.running = False
23 -
24 - self.base_line_speed = 60 # devices/min
25 - self.speed_factor = 1.0
26 - self.device_credit = 0.0
27 -
28 - self.mode = tk.StringVar(value="4-Pack")
29 -
30 - self.reset_values()
31 -
32 - self.build_ui()
33 - self.update_display()
34 -
35 - def reset_values(self):
36 - self.infeed_trays = [TRAY_SIZE] * MAX_INFEED_TRAYS
37 - self.outfeed_trays = 0
38 -
39 - self.cartons = START_CARTONS
40 - self.ifus = START_IFUS
41 -
42 - self.current_carton_devices = 0
43 - self.current_case_cartons = 0
44 - self.pallet_cases = 0
45 -
46 - self.device_credit = 0.0
47 - self.status = "Ready"
48 -
49 - def devices_per_carton(self):
50 - if self.mode.get() == "4-Pack":
51 - return 4
52 - return 1
53 -
54 - def cartons_per_case(self):
55 - if self.mode.get() == "4-Pack":
56 - return 18
57 - return 40
58 -
59 - def build_ui(self):
60 -
61 - controls = tk.Frame(self.root)
62 - controls.pack(fill="x", padx=10, pady=5)
63 -
64 - tk.Button(
65 - controls,
66 - text="Start",
67 - command=self.start
68 - ).pack(side="left")
69 -
70 - tk.Button(
71 - controls,
72 - text="Pause",
73 - command=self.pause
74 - ).pack(side="left", padx=5)
75 -
76 - tk.Button(
77 - controls,
78 - text="Reset",
79 - command=self.reset
80 - ).pack(side="left")
81 -
82 - tk.Button(
83 - controls,
84 - text="Speed +10%",
85 - command=self.speed_up
86 - ).pack(side="left", padx=20)
87 -
88 - tk.Button(
89 - controls,
90 - text="Speed -10%",
91 - command=self.speed_down
92 - ).pack(side="left")
93 -
94 - self.speed_label = tk.Label(controls)
95 - self.speed_label.pack(side="left", padx=20)
96 -
97 - mode_frame = tk.Frame(self.root)
98 - mode_frame.pack(fill="x", padx=10, pady=5)
99 -
100 - tk.Label(
101 - mode_frame,
102 - text="Mode:"
103 - ).pack(side="left")
104 -
105 - tk.Radiobutton(
106 - mode_frame,
107 - text="4-Pack",
108 - variable=self.mode,
109 - value="4-Pack",
110 - command=self.change_mode
111 - ).pack(side="left")
112 -
113 - tk.Radiobutton(
114 - mode_frame,
115 - text="1-Pack",
116 - variable=self.mode,
117 - value="1-Pack",
118 - command=self.change_mode
119 - ).pack(side="left")
120 -
121 - # Infeed
122 - self.infeed_frame = tk.LabelFrame(
123 - self.root,
124 - text="Device Infeed",
125 - padx=5,
126 - pady=5
127 - )
128 - self.infeed_frame.pack(fill="x", padx=10, pady=5)
129 -
130 - self.infeed_bar = ttk.Progressbar(
131 - self.infeed_frame,
132 - maximum=MAX_INFEED_TRAYS * TRAY_SIZE
133 - )
134 - self.infeed_bar.pack(fill="x", expand=True)
135 -
136 - self.infeed_label = tk.Label(self.infeed_frame)
137 - self.infeed_label.pack(anchor="w")
138 -
139 - tk.Button(
140 - self.infeed_frame,
141 - text="+5 Trays",
142 - command=self.add_infeed_trays
143 - ).pack(anchor="e")
144 -
145 - # Outfeed
146 - self.outfeed_frame = tk.LabelFrame(
147 - self.root,
148 - text="Tray Outfeed",
149 - padx=5,
150 - pady=5
151 - )
152 - self.outfeed_frame.pack(fill="x", padx=10, pady=5)
153 -
154 - self.outfeed_bar = ttk.Progressbar(
155 - self.outfeed_frame,
156 - maximum=MAX_OUTFEED_TRAYS
157 - )
158 - self.outfeed_bar.pack(fill="x", expand=True)
159 -
160 - self.outfeed_label = tk.Label(self.outfeed_frame)
161 - self.outfeed_label.pack(anchor="w")
162 -
163 - tk.Button(
164 - self.outfeed_frame,
165 - text="Clear Outfeed",
166 - command=self.clear_outfeed
167 - ).pack(anchor="e")
168 -
169 - # Cartons
170 - self.carton_frame = tk.LabelFrame(
171 - self.root,
172 - text="Carton Magazine",
173 - padx=5,
174 - pady=5
175 - )
176 - self.carton_frame.pack(fill="x", padx=10, pady=5)
177 -
178 - self.carton_bar = ttk.Progressbar(
179 - self.carton_frame,
180 - maximum=START_CARTONS
181 - )
182 - self.carton_bar.pack(fill="x", expand=True)
183 -
184 - self.carton_label = tk.Label(self.carton_frame)
185 - self.carton_label.pack(anchor="w")
186 -
187 - tk.Button(
188 - self.carton_frame,
189 - text="+20 Cartons",
190 - command=self.add_cartons
191 - ).pack(anchor="e")
192 -
193 - # IFUs
194 - self.ifu_frame = tk.LabelFrame(
195 - self.root,
196 - text="IFU Loader",
197 - padx=5,
198 - pady=5
199 - )
200 - self.ifu_frame.pack(fill="x", padx=10, pady=5)
201 -
202 - self.ifu_bar = ttk.Progressbar(
203 - self.ifu_frame,
204 - maximum=START_IFUS
205 - )
206 - self.ifu_bar.pack(fill="x", expand=True)
207 -
208 - self.ifu_label = tk.Label(self.ifu_frame)
209 - self.ifu_label.pack(anchor="w")
210 -
211 - tk.Button(
212 - self.ifu_frame,
213 - text="+100 IFUs",
214 - command=self.add_ifus
215 - ).pack(anchor="e")
216 -
217 - # Current carton
218 - self.current_carton_frame = tk.LabelFrame(
219 - self.root,
220 - text="Current Carton",
221 - padx=5,
222 - pady=5
223 - )
224 - self.current_carton_frame.pack(fill="x", padx=10, pady=5)
225 -
226 - self.current_carton_bar = ttk.Progressbar(
227 - self.current_carton_frame
228 - )
229 - self.current_carton_bar.pack(fill="x", expand=True)
230 -
231 - self.current_carton_label = tk.Label(self.current_carton_frame)
232 - self.current_carton_label.pack(anchor="w")
233 -
234 - # Current case
235 - self.current_case_frame = tk.LabelFrame(
236 - self.root,
237 - text="Current Case",
238 - padx=5,
239 - pady=5
240 - )
241 - self.current_case_frame.pack(fill="x", padx=10, pady=5)
242 -
243 - self.current_case_bar = ttk.Progressbar(
244 - self.current_case_frame
245 - )
246 - self.current_case_bar.pack(fill="x", expand=True)
247 -
248 - self.current_case_label = tk.Label(self.current_case_frame)
249 - self.current_case_label.pack(anchor="w")
250 -
251 - # Pallet
252 - self.pallet_frame = tk.LabelFrame(
253 - self.root,
254 - text="Finished Pallet",
255 - padx=5,
256 - pady=5
257 - )
258 - self.pallet_frame.pack(fill="x", padx=10, pady=5)
259 -
260 - self.pallet_bar = ttk.Progressbar(
261 - self.pallet_frame,
262 - maximum=PALLET_CAPACITY
263 - )
264 - self.pallet_bar.pack(fill="x", expand=True)
265 -
266 - self.pallet_label = tk.Label(self.pallet_frame)
267 - self.pallet_label.pack(anchor="w")
268 -
269 - self.pallet_button = tk.Button(
270 - self.pallet_frame,
271 - text="Replace Full Pallet",
272 - command=self.replace_pallet
273 - )
274 - self.pallet_button.pack(anchor="e")
275 -
276 - # Status
277 - self.status_label = tk.Label(
278 - self.root,
279 - font=("Arial", 11, "bold")
280 - )
281 - self.status_label.pack(anchor="w", padx=10, pady=10)
282 -
283 - def start(self):
284 - if not self.running:
285 - self.running = True
286 - self.status = "Running"
287 - self.tick()
288 -
289 - def pause(self):
290 - self.running = False
291 - self.status = "Paused"
292 - self.update_display()
293 -
294 - def reset(self):
295 - self.running = False
296 - self.speed_factor = 1.0
297 - self.reset_values()
298 - self.update_display()
299 -
300 - def change_mode(self):
301 - self.running = False
302 - self.speed_factor = 1.0
303 - self.reset_values()
304 - self.update_display()
305 -
306 - def speed_up(self):
307 - self.speed_factor *= 1.10
308 - self.update_display()
309 -
310 - def speed_down(self):
311 - self.speed_factor *= 0.90
312 - self.update_display()
313 -
314 - def add_infeed_trays(self):
315 - space_available = MAX_INFEED_TRAYS - len(self.infeed_trays)
316 - trays_to_add = min(5, space_available)
317 -
318 - for _ in range(trays_to_add):
319 - self.infeed_trays.append(TRAY_SIZE)
320 -
321 - self.update_display()
322 -
323 - def clear_outfeed(self):
324 - self.outfeed_trays = 0
325 - self.update_display()
326 -
327 - def add_cartons(self):
328 - self.cartons = min(START_CARTONS, self.cartons + 20)
329 - self.update_display()
330 -
331 - def add_ifus(self):
332 - self.ifus = min(START_IFUS, self.ifus + 100)
333 - self.update_display()
334 -
335 - def replace_pallet(self):
336 - if self.pallet_cases >= PALLET_CAPACITY:
337 - self.pallet_cases = 0
338 - self.status = "Ready"
339 - self.update_display()
340 -
341 - def tick(self):
342 -
343 - if not self.running:
344 - return
345 -
346 - current_speed = self.base_line_speed * self.speed_factor
347 - self.device_credit += current_speed / 60
348 -
349 - while self.device_credit >= 1 and self.running:
350 - success = self.process_one_device()
351 -
352 - if success:
353 - self.device_credit -= 1
354 - else:
355 - self.running = False
356 - self.device_credit = 0
357 -
358 - self.update_display()
359 -
360 - if self.running:
361 - self.root.after(UPDATE_MS, self.tick)
362 -
363 - def process_one_device(self):
364 -
365 - if len(self.infeed_trays) == 0:
366 - self.status = "Stopped - No infeed trays"
367 - return False
368 -
369 - if self.pallet_cases >= PALLET_CAPACITY:
370 - self.status = "Stopped - Pallet full"
371 - return False
372 -
373 - active_tray_devices = self.infeed_trays[0]
374 -
375 - if active_tray_devices == 1 and self.outfeed_trays >= MAX_OUTFEED_TRAYS:
376 - self.status = "Stopped - Tray outfeed full"
377 - return False
378 -
379 - will_complete_carton = (
380 - self.current_carton_devices + 1 >= self.devices_per_carton()
381 - )
382 -
383 - if will_complete_carton:
384 - if self.cartons <= 0:
385 - self.status = "Stopped - No cartons"
386 - return False
387 -
388 - if self.ifus <= 0:
389 - self.status = "Stopped - No IFUs"
390 - return False
391 -
392 - # Remove one device from the current tray
393 - self.infeed_trays[0] -= 1
394 -
395 - # Add device to current carton
396 - self.current_carton_devices += 1
397 -
398 - # If tray is empty, move it to outfeed
399 - if self.infeed_trays[0] == 0:
400 - self.infeed_trays.pop(0)
401 - self.outfeed_trays += 1
402 -
403 - # If carton is complete, consume 1 carton and 1 IFU
404 - if self.current_carton_devices >= self.devices_per_carton():
405 - self.current_carton_devices = 0
406 - self.cartons -= 1
407 - self.ifus -= 1
408 -
409 - self.current_case_cartons += 1
410 -
411 - # If case is complete, move 1 case to pallet
412 - if self.current_case_cartons >= self.cartons_per_case():
413 - self.current_case_cartons = 0
414 - self.pallet_cases += 1
415 -
416 - if self.pallet_cases >= PALLET_CAPACITY:
417 - self.pallet_cases = PALLET_CAPACITY
418 - self.status = "Stopped - Pallet full"
419 - return False
420 -
421 - self.status = "Running"
422 - return True
423 -
424 - def update_display(self):
425 -
426 - current_speed = self.base_line_speed * self.speed_factor
427 -
428 - self.speed_label.config(
429 - text=f"Line Speed: {current_speed:.0f} devices/min "
430 - f"({self.speed_factor * 100:.0f}%)"
431 - )
432 -
433 - total_infeed_devices = sum(self.infeed_trays)
434 - self.infeed_bar["value"] = total_infeed_devices
435 -
436 - if len(self.infeed_trays) > 0:
437 - first_tray_text = f"Current tray: {self.infeed_trays[0]} / {TRAY_SIZE} devices"
438 - else:
439 - first_tray_text = "Current tray: none"
440 -
441 - self.infeed_label.config(
442 - text=f"{len(self.infeed_trays)} trays, "
443 - f"{total_infeed_devices} devices remaining\n"
444 - f"{first_tray_text}"
445 - )
446 -
447 - self.outfeed_bar["value"] = self.outfeed_trays
448 - self.outfeed_label.config(
449 - text=f"{self.outfeed_trays} / {MAX_OUTFEED_TRAYS} empty trays"
450 - )
451 -
452 - self.carton_bar["value"] = self.cartons
453 - self.carton_label.config(
454 - text=f"{self.cartons} / {START_CARTONS} cartons"
455 - )
456 -
457 - self.ifu_bar["value"] = self.ifus
458 - self.ifu_label.config(
459 - text=f"{self.ifus} / {START_IFUS} IFUs"
460 - )
461 -
462 - self.current_carton_bar["maximum"] = self.devices_per_carton()
463 - self.current_carton_bar["value"] = self.current_carton_devices
464 - self.current_carton_label.config(
465 - text=f"{self.current_carton_devices} / "
466 - f"{self.devices_per_carton()} devices"
467 - )
468 -
469 - self.current_case_bar["maximum"] = self.cartons_per_case()
470 - self.current_case_bar["value"] = self.current_case_cartons
471 - self.current_case_label.config(
472 - text=f"{self.current_case_cartons} / "
473 - f"{self.cartons_per_case()} cartons"
474 - )
475 -
476 - self.pallet_bar["value"] = self.pallet_cases
477 - self.pallet_label.config(
478 - text=f"{self.pallet_cases} / {PALLET_CAPACITY} cases"
479 - )
480 -
481 - if self.pallet_cases >= PALLET_CAPACITY:
482 - self.pallet_button.config(state="normal")
483 - else:
484 - self.pallet_button.config(state="disabled")
485 -
486 - self.status_label.config(
487 - text=f"Status: {self.status}"
488 - )
489 -
490 -
491 - root = tk.Tk()
492 - app = RunoutSimulator(root)
493 - root.mainloop()

wschrab revised this gist 1 month ago. Go to revision

1 file changed, 493 insertions

RunoutTime2.py(file created)

@@ -0,0 +1,493 @@
1 + import tkinter as tk
2 + from tkinter import ttk
3 +
4 + UPDATE_MS = 1000 # 1 second
5 +
6 + TRAY_SIZE = 27
7 + MAX_INFEED_TRAYS = 50
8 + MAX_OUTFEED_TRAYS = 50
9 +
10 + START_CARTONS = 1000
11 + START_IFUS = 2000
12 +
13 + PALLET_CAPACITY = 60
14 +
15 +
16 + class RunoutSimulator:
17 +
18 + def __init__(self, root):
19 + self.root = root
20 + self.root.title("Runout Simulator")
21 +
22 + self.running = False
23 +
24 + self.base_line_speed = 60 # devices/min
25 + self.speed_factor = 1.0
26 + self.device_credit = 0.0
27 +
28 + self.mode = tk.StringVar(value="4-Pack")
29 +
30 + self.reset_values()
31 +
32 + self.build_ui()
33 + self.update_display()
34 +
35 + def reset_values(self):
36 + self.infeed_trays = [TRAY_SIZE] * MAX_INFEED_TRAYS
37 + self.outfeed_trays = 0
38 +
39 + self.cartons = START_CARTONS
40 + self.ifus = START_IFUS
41 +
42 + self.current_carton_devices = 0
43 + self.current_case_cartons = 0
44 + self.pallet_cases = 0
45 +
46 + self.device_credit = 0.0
47 + self.status = "Ready"
48 +
49 + def devices_per_carton(self):
50 + if self.mode.get() == "4-Pack":
51 + return 4
52 + return 1
53 +
54 + def cartons_per_case(self):
55 + if self.mode.get() == "4-Pack":
56 + return 18
57 + return 40
58 +
59 + def build_ui(self):
60 +
61 + controls = tk.Frame(self.root)
62 + controls.pack(fill="x", padx=10, pady=5)
63 +
64 + tk.Button(
65 + controls,
66 + text="Start",
67 + command=self.start
68 + ).pack(side="left")
69 +
70 + tk.Button(
71 + controls,
72 + text="Pause",
73 + command=self.pause
74 + ).pack(side="left", padx=5)
75 +
76 + tk.Button(
77 + controls,
78 + text="Reset",
79 + command=self.reset
80 + ).pack(side="left")
81 +
82 + tk.Button(
83 + controls,
84 + text="Speed +10%",
85 + command=self.speed_up
86 + ).pack(side="left", padx=20)
87 +
88 + tk.Button(
89 + controls,
90 + text="Speed -10%",
91 + command=self.speed_down
92 + ).pack(side="left")
93 +
94 + self.speed_label = tk.Label(controls)
95 + self.speed_label.pack(side="left", padx=20)
96 +
97 + mode_frame = tk.Frame(self.root)
98 + mode_frame.pack(fill="x", padx=10, pady=5)
99 +
100 + tk.Label(
101 + mode_frame,
102 + text="Mode:"
103 + ).pack(side="left")
104 +
105 + tk.Radiobutton(
106 + mode_frame,
107 + text="4-Pack",
108 + variable=self.mode,
109 + value="4-Pack",
110 + command=self.change_mode
111 + ).pack(side="left")
112 +
113 + tk.Radiobutton(
114 + mode_frame,
115 + text="1-Pack",
116 + variable=self.mode,
117 + value="1-Pack",
118 + command=self.change_mode
119 + ).pack(side="left")
120 +
121 + # Infeed
122 + self.infeed_frame = tk.LabelFrame(
123 + self.root,
124 + text="Device Infeed",
125 + padx=5,
126 + pady=5
127 + )
128 + self.infeed_frame.pack(fill="x", padx=10, pady=5)
129 +
130 + self.infeed_bar = ttk.Progressbar(
131 + self.infeed_frame,
132 + maximum=MAX_INFEED_TRAYS * TRAY_SIZE
133 + )
134 + self.infeed_bar.pack(fill="x", expand=True)
135 +
136 + self.infeed_label = tk.Label(self.infeed_frame)
137 + self.infeed_label.pack(anchor="w")
138 +
139 + tk.Button(
140 + self.infeed_frame,
141 + text="+5 Trays",
142 + command=self.add_infeed_trays
143 + ).pack(anchor="e")
144 +
145 + # Outfeed
146 + self.outfeed_frame = tk.LabelFrame(
147 + self.root,
148 + text="Tray Outfeed",
149 + padx=5,
150 + pady=5
151 + )
152 + self.outfeed_frame.pack(fill="x", padx=10, pady=5)
153 +
154 + self.outfeed_bar = ttk.Progressbar(
155 + self.outfeed_frame,
156 + maximum=MAX_OUTFEED_TRAYS
157 + )
158 + self.outfeed_bar.pack(fill="x", expand=True)
159 +
160 + self.outfeed_label = tk.Label(self.outfeed_frame)
161 + self.outfeed_label.pack(anchor="w")
162 +
163 + tk.Button(
164 + self.outfeed_frame,
165 + text="Clear Outfeed",
166 + command=self.clear_outfeed
167 + ).pack(anchor="e")
168 +
169 + # Cartons
170 + self.carton_frame = tk.LabelFrame(
171 + self.root,
172 + text="Carton Magazine",
173 + padx=5,
174 + pady=5
175 + )
176 + self.carton_frame.pack(fill="x", padx=10, pady=5)
177 +
178 + self.carton_bar = ttk.Progressbar(
179 + self.carton_frame,
180 + maximum=START_CARTONS
181 + )
182 + self.carton_bar.pack(fill="x", expand=True)
183 +
184 + self.carton_label = tk.Label(self.carton_frame)
185 + self.carton_label.pack(anchor="w")
186 +
187 + tk.Button(
188 + self.carton_frame,
189 + text="+20 Cartons",
190 + command=self.add_cartons
191 + ).pack(anchor="e")
192 +
193 + # IFUs
194 + self.ifu_frame = tk.LabelFrame(
195 + self.root,
196 + text="IFU Loader",
197 + padx=5,
198 + pady=5
199 + )
200 + self.ifu_frame.pack(fill="x", padx=10, pady=5)
201 +
202 + self.ifu_bar = ttk.Progressbar(
203 + self.ifu_frame,
204 + maximum=START_IFUS
205 + )
206 + self.ifu_bar.pack(fill="x", expand=True)
207 +
208 + self.ifu_label = tk.Label(self.ifu_frame)
209 + self.ifu_label.pack(anchor="w")
210 +
211 + tk.Button(
212 + self.ifu_frame,
213 + text="+100 IFUs",
214 + command=self.add_ifus
215 + ).pack(anchor="e")
216 +
217 + # Current carton
218 + self.current_carton_frame = tk.LabelFrame(
219 + self.root,
220 + text="Current Carton",
221 + padx=5,
222 + pady=5
223 + )
224 + self.current_carton_frame.pack(fill="x", padx=10, pady=5)
225 +
226 + self.current_carton_bar = ttk.Progressbar(
227 + self.current_carton_frame
228 + )
229 + self.current_carton_bar.pack(fill="x", expand=True)
230 +
231 + self.current_carton_label = tk.Label(self.current_carton_frame)
232 + self.current_carton_label.pack(anchor="w")
233 +
234 + # Current case
235 + self.current_case_frame = tk.LabelFrame(
236 + self.root,
237 + text="Current Case",
238 + padx=5,
239 + pady=5
240 + )
241 + self.current_case_frame.pack(fill="x", padx=10, pady=5)
242 +
243 + self.current_case_bar = ttk.Progressbar(
244 + self.current_case_frame
245 + )
246 + self.current_case_bar.pack(fill="x", expand=True)
247 +
248 + self.current_case_label = tk.Label(self.current_case_frame)
249 + self.current_case_label.pack(anchor="w")
250 +
251 + # Pallet
252 + self.pallet_frame = tk.LabelFrame(
253 + self.root,
254 + text="Finished Pallet",
255 + padx=5,
256 + pady=5
257 + )
258 + self.pallet_frame.pack(fill="x", padx=10, pady=5)
259 +
260 + self.pallet_bar = ttk.Progressbar(
261 + self.pallet_frame,
262 + maximum=PALLET_CAPACITY
263 + )
264 + self.pallet_bar.pack(fill="x", expand=True)
265 +
266 + self.pallet_label = tk.Label(self.pallet_frame)
267 + self.pallet_label.pack(anchor="w")
268 +
269 + self.pallet_button = tk.Button(
270 + self.pallet_frame,
271 + text="Replace Full Pallet",
272 + command=self.replace_pallet
273 + )
274 + self.pallet_button.pack(anchor="e")
275 +
276 + # Status
277 + self.status_label = tk.Label(
278 + self.root,
279 + font=("Arial", 11, "bold")
280 + )
281 + self.status_label.pack(anchor="w", padx=10, pady=10)
282 +
283 + def start(self):
284 + if not self.running:
285 + self.running = True
286 + self.status = "Running"
287 + self.tick()
288 +
289 + def pause(self):
290 + self.running = False
291 + self.status = "Paused"
292 + self.update_display()
293 +
294 + def reset(self):
295 + self.running = False
296 + self.speed_factor = 1.0
297 + self.reset_values()
298 + self.update_display()
299 +
300 + def change_mode(self):
301 + self.running = False
302 + self.speed_factor = 1.0
303 + self.reset_values()
304 + self.update_display()
305 +
306 + def speed_up(self):
307 + self.speed_factor *= 1.10
308 + self.update_display()
309 +
310 + def speed_down(self):
311 + self.speed_factor *= 0.90
312 + self.update_display()
313 +
314 + def add_infeed_trays(self):
315 + space_available = MAX_INFEED_TRAYS - len(self.infeed_trays)
316 + trays_to_add = min(5, space_available)
317 +
318 + for _ in range(trays_to_add):
319 + self.infeed_trays.append(TRAY_SIZE)
320 +
321 + self.update_display()
322 +
323 + def clear_outfeed(self):
324 + self.outfeed_trays = 0
325 + self.update_display()
326 +
327 + def add_cartons(self):
328 + self.cartons = min(START_CARTONS, self.cartons + 20)
329 + self.update_display()
330 +
331 + def add_ifus(self):
332 + self.ifus = min(START_IFUS, self.ifus + 100)
333 + self.update_display()
334 +
335 + def replace_pallet(self):
336 + if self.pallet_cases >= PALLET_CAPACITY:
337 + self.pallet_cases = 0
338 + self.status = "Ready"
339 + self.update_display()
340 +
341 + def tick(self):
342 +
343 + if not self.running:
344 + return
345 +
346 + current_speed = self.base_line_speed * self.speed_factor
347 + self.device_credit += current_speed / 60
348 +
349 + while self.device_credit >= 1 and self.running:
350 + success = self.process_one_device()
351 +
352 + if success:
353 + self.device_credit -= 1
354 + else:
355 + self.running = False
356 + self.device_credit = 0
357 +
358 + self.update_display()
359 +
360 + if self.running:
361 + self.root.after(UPDATE_MS, self.tick)
362 +
363 + def process_one_device(self):
364 +
365 + if len(self.infeed_trays) == 0:
366 + self.status = "Stopped - No infeed trays"
367 + return False
368 +
369 + if self.pallet_cases >= PALLET_CAPACITY:
370 + self.status = "Stopped - Pallet full"
371 + return False
372 +
373 + active_tray_devices = self.infeed_trays[0]
374 +
375 + if active_tray_devices == 1 and self.outfeed_trays >= MAX_OUTFEED_TRAYS:
376 + self.status = "Stopped - Tray outfeed full"
377 + return False
378 +
379 + will_complete_carton = (
380 + self.current_carton_devices + 1 >= self.devices_per_carton()
381 + )
382 +
383 + if will_complete_carton:
384 + if self.cartons <= 0:
385 + self.status = "Stopped - No cartons"
386 + return False
387 +
388 + if self.ifus <= 0:
389 + self.status = "Stopped - No IFUs"
390 + return False
391 +
392 + # Remove one device from the current tray
393 + self.infeed_trays[0] -= 1
394 +
395 + # Add device to current carton
396 + self.current_carton_devices += 1
397 +
398 + # If tray is empty, move it to outfeed
399 + if self.infeed_trays[0] == 0:
400 + self.infeed_trays.pop(0)
401 + self.outfeed_trays += 1
402 +
403 + # If carton is complete, consume 1 carton and 1 IFU
404 + if self.current_carton_devices >= self.devices_per_carton():
405 + self.current_carton_devices = 0
406 + self.cartons -= 1
407 + self.ifus -= 1
408 +
409 + self.current_case_cartons += 1
410 +
411 + # If case is complete, move 1 case to pallet
412 + if self.current_case_cartons >= self.cartons_per_case():
413 + self.current_case_cartons = 0
414 + self.pallet_cases += 1
415 +
416 + if self.pallet_cases >= PALLET_CAPACITY:
417 + self.pallet_cases = PALLET_CAPACITY
418 + self.status = "Stopped - Pallet full"
419 + return False
420 +
421 + self.status = "Running"
422 + return True
423 +
424 + def update_display(self):
425 +
426 + current_speed = self.base_line_speed * self.speed_factor
427 +
428 + self.speed_label.config(
429 + text=f"Line Speed: {current_speed:.0f} devices/min "
430 + f"({self.speed_factor * 100:.0f}%)"
431 + )
432 +
433 + total_infeed_devices = sum(self.infeed_trays)
434 + self.infeed_bar["value"] = total_infeed_devices
435 +
436 + if len(self.infeed_trays) > 0:
437 + first_tray_text = f"Current tray: {self.infeed_trays[0]} / {TRAY_SIZE} devices"
438 + else:
439 + first_tray_text = "Current tray: none"
440 +
441 + self.infeed_label.config(
442 + text=f"{len(self.infeed_trays)} trays, "
443 + f"{total_infeed_devices} devices remaining\n"
444 + f"{first_tray_text}"
445 + )
446 +
447 + self.outfeed_bar["value"] = self.outfeed_trays
448 + self.outfeed_label.config(
449 + text=f"{self.outfeed_trays} / {MAX_OUTFEED_TRAYS} empty trays"
450 + )
451 +
452 + self.carton_bar["value"] = self.cartons
453 + self.carton_label.config(
454 + text=f"{self.cartons} / {START_CARTONS} cartons"
455 + )
456 +
457 + self.ifu_bar["value"] = self.ifus
458 + self.ifu_label.config(
459 + text=f"{self.ifus} / {START_IFUS} IFUs"
460 + )
461 +
462 + self.current_carton_bar["maximum"] = self.devices_per_carton()
463 + self.current_carton_bar["value"] = self.current_carton_devices
464 + self.current_carton_label.config(
465 + text=f"{self.current_carton_devices} / "
466 + f"{self.devices_per_carton()} devices"
467 + )
468 +
469 + self.current_case_bar["maximum"] = self.cartons_per_case()
470 + self.current_case_bar["value"] = self.current_case_cartons
471 + self.current_case_label.config(
472 + text=f"{self.current_case_cartons} / "
473 + f"{self.cartons_per_case()} cartons"
474 + )
475 +
476 + self.pallet_bar["value"] = self.pallet_cases
477 + self.pallet_label.config(
478 + text=f"{self.pallet_cases} / {PALLET_CAPACITY} cases"
479 + )
480 +
481 + if self.pallet_cases >= PALLET_CAPACITY:
482 + self.pallet_button.config(state="normal")
483 + else:
484 + self.pallet_button.config(state="disabled")
485 +
486 + self.status_label.config(
487 + text=f"Status: {self.status}"
488 + )
489 +
490 +
491 + root = tk.Tk()
492 + app = RunoutSimulator(root)
493 + root.mainloop()

wschrab revised this gist 1 month ago. Go to revision

No changes

wschrab revised this gist 1 month ago. Go to revision

1 file changed, 15 insertions, 40 deletions

RunoutTime.py

@@ -5,16 +5,15 @@ UPDATE_MS = 1000 # 1 second
5 5
6 6
7 7 class Station:
8 - def __init__(self, name, max_qty, start_qty, rate_per_min, refill_amount):
8 + def __init__(self, name, max_qty, rate_per_min, refill_amount):
9 9 self.name = name
10 10 self.max_qty = max_qty
11 - self.start_qty = start_qty
12 - self.current_qty = start_qty
11 + self.current_qty = max_qty
13 12 self.rate_per_min = rate_per_min
14 13 self.refill_amount = refill_amount
15 14
16 15 def reset(self):
17 - self.current_qty = self.start_qty
16 + self.current_qty = self.max_qty
18 17
19 18 def add(self):
20 19 self.current_qty = min(
@@ -37,13 +36,14 @@ class RunoutSimulator:
37 36
38 37 self.running = False
39 38 self.speed_factor = 1.0
39 + self.base_line_speed = 60 # devices/min
40 40
41 41 self.stations = [
42 - Station("Tray Infeed", 135, 135, 11, 27),
43 - Station("Tray Outfeed", 135, 135, 11, 27),
44 - Station("IFU Loader", 2000, 2000, 60, 100),
45 - Station("Carton Magazine", 1000, 1000, 60, 20),
46 - Station("Case Magazine", 100, 100, 3, 10),
42 + Station("Tray Infeed", 135, 11, 27),
43 + Station("Tray Outfeed", 135, 11, 27),
44 + Station("IFU Loader", 2000, 60, 100),
45 + Station("Carton Magazine", 1000, 60, 20),
46 + Station("Case Magazine", 100, 3, 10),
47 47 ]
48 48
49 49 self.pallet_capacity = 60
@@ -113,9 +113,8 @@ class RunoutSimulator:
113 113 bar = ttk.Progressbar(
114 114 frame,
115 115 maximum=station.max_qty,
116 - length=400
117 116 )
118 - bar.pack(anchor="w")
117 + bar.pack(fill="x", expand=True)
119 118
120 119 qty_label = tk.Label(frame)
121 120 qty_label.pack(anchor="w")
@@ -134,17 +133,6 @@ class RunoutSimulator:
134 133 (bar, qty_label, time_label)
135 134 )
136 135
137 - self.runout_label = tk.Label(
138 - self.root,
139 - font=("Arial", 12, "bold"),
140 - justify="left"
141 - )
142 - self.runout_label.pack(
143 - anchor="w",
144 - padx=10,
145 - pady=10
146 - )
147 -
148 136 pallet_frame = tk.LabelFrame(
149 137 self.root,
150 138 text="Finished Pallet",
@@ -156,9 +144,8 @@ class RunoutSimulator:
156 144 self.pallet_bar = ttk.Progressbar(
157 145 pallet_frame,
158 146 maximum=self.pallet_capacity,
159 - length=400
160 147 )
161 - self.pallet_bar.pack(anchor="w")
148 + self.pallet_bar.pack(fill="x", expand=True)
162 149
163 150 self.pallet_label = tk.Label(pallet_frame)
164 151 self.pallet_label.pack(anchor="w")
@@ -250,25 +237,13 @@ class RunoutSimulator:
250 237 text=f"Remaining: {station.minutes_remaining:.1f} min"
251 238 )
252 239
253 - self.speed_label.config(
254 - text=f"Line Speed: {self.speed_factor * 100:.0f}%"
255 - )
240 + current_speed = self.base_line_speed * self.speed_factor
256 241
257 - ranking = sorted(
258 - self.stations,
259 - key=lambda s: s.minutes_remaining
242 + self.speed_label.config(
243 + text=f"Line Speed: {current_speed:.0f} devices/min "
244 + f"({self.speed_factor * 100:.0f}%)"
260 245 )
261 246
262 - text = "Runout Order\n\n"
263 -
264 - for i, station in enumerate(ranking, start=1):
265 - text += (
266 - f"{i}. {station.name}"
267 - f" - {station.minutes_remaining:.1f} min\n"
268 - )
269 -
270 - self.runout_label.config(text=text)
271 -
272 247 self.pallet_bar["value"] = self.pallet_cases
273 248
274 249 self.pallet_label.config(

wschrab revised this gist 1 month ago. Go to revision

1 file changed, 8 insertions, 5 deletions

RunoutTime.py

@@ -48,7 +48,7 @@ class RunoutSimulator:
48 48
49 49 self.pallet_capacity = 60
50 50 self.pallet_cases = 0
51 - self.pallet_rate = 3.33
51 + self.case_rate = 3
52 52
53 53 self.station_widgets = []
54 54
@@ -184,6 +184,8 @@ class RunoutSimulator:
184 184 for station in self.stations:
185 185 station.reset()
186 186
187 + self.pallet_cases = 0
188 +
187 189 self.speed_factor = 1.0
188 190 self.update_display()
189 191
@@ -217,6 +219,11 @@ class RunoutSimulator:
217 219 station.current_qty - usage_per_second
218 220 )
219 221
222 + self.pallet_cases += (self.case_rate * self.speed_factor / 60)
223 +
224 + if self.pallet_cases > self.pallet_capacity:
225 + self.pallet_cases = self.pallet_capacity
226 +
220 227 self.update_display()
221 228
222 229 self.root.after(
@@ -224,10 +231,6 @@ class RunoutSimulator:
224 231 self.tick
225 232 )
226 233
227 - self.pallet_cases += (self.pallet_rate * self.speed_factor / 60)
228 -
229 - if self.pallet_cases > self.pallet_capacity:
230 - self.pallet_cases = self.pallet_capacity
231 234
232 235 def update_display(self):
233 236

wschrab revised this gist 1 month ago. Go to revision

1 file changed, 52 insertions, 3 deletions

RunoutTime.py

@@ -40,13 +40,16 @@ class RunoutSimulator:
40 40
41 41 self.stations = [
42 42 Station("Tray Infeed", 135, 135, 11, 27),
43 + Station("Tray Outfeed", 135, 135, 11, 27),
43 44 Station("IFU Loader", 2000, 2000, 60, 100),
44 - Station("Carton Magazine", 1000, 1000, 60, 100),
45 + Station("Carton Magazine", 1000, 1000, 60, 20),
45 46 Station("Case Magazine", 100, 100, 3, 10),
46 - Station("Pallet Capacity", 48, 48, 3, 1),
47 - Station("Spare Buffer", 500, 500, 20, 50),
48 47 ]
49 48
49 + self.pallet_capacity = 60
50 + self.pallet_cases = 0
51 + self.pallet_rate = 3.33
52 +
50 53 self.station_widgets = []
51 54
52 55 self.build_ui()
@@ -142,6 +145,31 @@ class RunoutSimulator:
142 145 pady=10
143 146 )
144 147
148 + pallet_frame = tk.LabelFrame(
149 + self.root,
150 + text="Finished Pallet",
151 + padx=5,
152 + pady=5
153 + )
154 + pallet_frame.pack(fill="x", padx=10, pady=5)
155 +
156 + self.pallet_bar = ttk.Progressbar(
157 + pallet_frame,
158 + maximum=self.pallet_capacity,
159 + length=400
160 + )
161 + self.pallet_bar.pack(anchor="w")
162 +
163 + self.pallet_label = tk.Label(pallet_frame)
164 + self.pallet_label.pack(anchor="w")
165 +
166 + self.pallet_button = tk.Button(
167 + pallet_frame,
168 + text="Replace Full Pallet",
169 + command=self.replace_pallet
170 + )
171 + self.pallet_button.pack(anchor="e")
172 +
145 173 def start(self):
146 174 if not self.running:
147 175 self.running = True
@@ -167,6 +195,10 @@ class RunoutSimulator:
167 195 self.speed_factor *= 0.90
168 196 self.update_display()
169 197
198 + def replace_pallet(self):
199 + if self.pallet_cases >= self.pallet_capacity:
200 + self.pallet_cases = 0
201 +
170 202 def tick(self):
171 203
172 204 if not self.running:
@@ -192,6 +224,11 @@ class RunoutSimulator:
192 224 self.tick
193 225 )
194 226
227 + self.pallet_cases += (self.pallet_rate * self.speed_factor / 60)
228 +
229 + if self.pallet_cases > self.pallet_capacity:
230 + self.pallet_cases = self.pallet_capacity
231 +
195 232 def update_display(self):
196 233
197 234 for station, widgets in zip(
@@ -229,6 +266,18 @@ class RunoutSimulator:
229 266
230 267 self.runout_label.config(text=text)
231 268
269 + self.pallet_bar["value"] = self.pallet_cases
270 +
271 + self.pallet_label.config(
272 + text=f"{self.pallet_cases:.1f} / "
273 + f"{self.pallet_capacity} cases"
274 + )
275 +
276 + if self.pallet_cases >= self.pallet_capacity:
277 + self.pallet_button.config(state="normal")
278 + else:
279 + self.pallet_button.config(state="disabled")
280 +
232 281
233 282 root = tk.Tk()
234 283 app = RunoutSimulator(root)

wschrab revised this gist 1 month ago. Go to revision

1 file changed, 9 insertions, 1 deletion

RunoutTime.py

@@ -224,4 +224,12 @@ class RunoutSimulator:
224 224 for i, station in enumerate(ranking, start=1):
225 225 text += (
226 226 f"{i}. {station.name}"
227 - f" - {station.minutes_
227 + f" - {station.minutes_remaining:.1f} min\n"
228 + )
229 +
230 + self.runout_label.config(text=text)
231 +
232 +
233 + root = tk.Tk()
234 + app = RunoutSimulator(root)
235 + root.mainloop()
Newer Older