Last active 1 month ago

wschrab revised this gist 1 month ago. Go to revision

1 file changed, 0 insertions, 0 deletions

RunoutTime.Py renamed to RunoutTime.py

File renamed without changes

wschrab revised this gist 1 month ago. Go to revision

1 file changed, 227 insertions

RunoutTime.Py(file created)

@@ -0,0 +1,227 @@
1 + import tkinter as tk
2 + from tkinter import ttk
3 +
4 + UPDATE_MS = 1000 # 1 second
5 +
6 +
7 + class 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 +
32 + class 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_
Newer Older