SissiFeng commited on
Commit
6384181
·
verified ·
1 Parent(s): 055d7c6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +189 -115
app.py CHANGED
@@ -47,8 +47,6 @@ latest_data = {
47
  "status": "N/A",
48
  "update_time": "Waiting for data...",
49
  "image_url": "N/A",
50
- "progress": 0,
51
- "message": "",
52
  }
53
 
54
  bambu_client = None
@@ -96,61 +94,36 @@ def bambu_on_message(client, userdata, message):
96
  logger.error(f"Error parsing MQTT message: {e}")
97
 
98
 
99
- def rpi_on_message(client, userdata, msg):
100
  global latest_data
101
-
102
  try:
103
- payload = msg.payload.decode("utf-8")
104
- logger.info(f"Received message from RPI: {payload}")
105
-
106
- data = json.loads(payload)
107
- status = data.get("status", "Unknown")
108
-
109
- latest_data["status"] = status
110
- latest_data["update_time"] = data.get("update_time", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
111
-
112
- if "nozzle_temperature" in data:
113
- latest_data["nozzle_temperature"] = data["nozzle_temperature"]
114
-
115
- if "bed_temperature" in data:
116
- latest_data["bed_temperature"] = data["bed_temperature"]
117
-
118
- if "error" in data:
119
- logger.error(f"Error from RPI: {data['error']}")
120
-
121
- if status == "Ready":
122
- latest_data["progress"] = 0
123
- latest_data["message"] = "Printer ready"
124
- elif status == "Processing":
125
- latest_data["progress"] = 25
126
- latest_data["message"] = "Processing G-code..."
127
-
128
- except Exception as e:
129
- logger.error(f"Error processing message from RPI: {e}")
130
-
131
- try:
132
- result = json.loads(payload)
133
-
134
- if "status" in result:
135
- status = result["status"]
136
- latest_data["status"] = status
137
-
138
- if "command" in result and result["command"] == "capture_image" and result.get("auto_triggered", False):
139
- logger.info("receive capture command")
140
- threading.Thread(target=handle_auto_capture, args=(result,), daemon=True).start()
141
-
142
- except json.JSONDecodeError:
143
- logger.error(f"Invalid JSON in message: {payload}")
144
-
145
  except Exception as e:
146
- logger.error(f"Error processing RPI message: {e}")
147
 
148
 
149
  def get_data(serial=DEFAULT_SERIAL):
150
  global bambu_client, response_topic
151
 
 
 
 
 
 
 
 
 
 
 
152
  if bambu_client is None:
153
- create_client("bambu")
 
 
 
 
 
154
 
155
  request_topic = f"bambu_a1_mini/request/{serial}"
156
  response_topic = f"bambu_a1_mini/response/{serial}"
@@ -177,15 +150,12 @@ def get_data(serial=DEFAULT_SERIAL):
177
 
178
 
179
  def send_print_parameters(nozzle_temp, bed_temp, print_speed, fan_speed):
180
- global rpi_client
181
 
182
  serial = DEFAULT_SERIAL
183
-
184
  logger.info(
185
- f"Sending parameters to RPi for G-code generation: nozzle={nozzle_temp}, bed={bed_temp}, "
186
- f"speed={print_speed}, fan={fan_speed}"
187
  )
188
-
189
  try:
190
  params = {
191
  "nozzle_temp": nozzle_temp,
@@ -196,18 +166,13 @@ def send_print_parameters(nozzle_temp, bed_temp, print_speed, fan_speed):
196
 
197
  request_topic = f"bambu_a1_mini/request/{serial}"
198
 
199
- if rpi_client:
200
- rpi_client.publish(
201
  request_topic,
202
- json.dumps({"command": "generate_gcode", "parameters": params}),
203
  )
204
- logger.info("Parameters sent successfully to RPi for G-code generation")
205
-
206
- global latest_data
207
- latest_data["status"] = "Sent"
208
- latest_data["update_time"] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
209
-
210
- return "Parameters sent successfully to RPi. Status: Sent"
211
  else:
212
  logger.warning("MQTT not connected, parameters not sent")
213
  return "MQTT not connected, parameters not sent"
@@ -215,12 +180,6 @@ def send_print_parameters(nozzle_temp, bed_temp, print_speed, fan_speed):
215
  logger.error(f"Error sending parameters: {e}")
216
  return f"Error sending parameters: {e}"
217
 
218
-
219
- latest_data["status"] = "Sending"
220
- latest_data["progress"] = 10
221
- latest_data["message"] = "Sending parameters to printer..."
222
-
223
-
224
 
225
  def get_image_base64(image):
226
  if image is None:
@@ -241,21 +200,35 @@ def get_image_base64(image):
241
  return None
242
 
243
 
244
- def handle_auto_capture(message):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  try:
246
- logger.info(f"receive capture command: {message}")
247
-
248
- print_job = message.get("print_job", "unknown_job")
249
- timestamp = message.get("timestamp", time.strftime("%Y-%m-%d %H:%M:%S"))
250
-
251
- latest_data["auto_capture_requested"] = True
252
- latest_data["last_capture_job"] = print_job
253
- latest_data["last_capture_time"] = timestamp
254
-
255
- return capture_image()
256
  except Exception as e:
257
- logger.error(f"error: {e}")
258
- return None, f"capture failed: {str(e)}"
259
 
260
 
261
  def capture_image(url=None, use_test_image=False, test_image_name=None):
@@ -310,17 +283,6 @@ def capture_image(url=None, use_test_image=False, test_image_name=None):
310
  else:
311
  raise Exception("url is 'N/A'")
312
 
313
- def update_print_status():
314
- global latest_data
315
- return (
316
- latest_data["status"],
317
- latest_data["nozzle_temperature"],
318
- latest_data["bed_temperature"],
319
- latest_data["update_time"],
320
- latest_data.get("progress", 0),
321
- latest_data.get("message", ""),
322
- )
323
-
324
 
325
  def health_check():
326
  status = {
@@ -367,7 +329,7 @@ with demo:
367
  <div style="position: relative; width: 100%; padding-top: 56.25%;">
368
  <iframe
369
  style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
370
- src="https://www.youtube.com/embed/x8Iii1cKWB8"
371
  title="Bambu A1mini Livestream"
372
  frameborder="0"
373
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
@@ -442,6 +404,123 @@ with demo:
442
  logger.error(f"Error in capture_frame: {e}")
443
  return {"success": False, "error": str(e)}
444
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445
 
446
  def api_send_print_parameters(
447
  nozzle_temp=200, bed_temp=60, print_speed=60, fan_speed=100
@@ -465,6 +544,20 @@ with demo:
465
  api_name="capture_frame",
466
  )
467
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
 
469
  get_data_api = demo.load(
470
  fn=api_get_data, inputs=None, outputs=api_json_output, api_name="get_data"
@@ -482,27 +575,6 @@ with demo:
482
  api_name="send_print_parameters",
483
  )
484
 
485
- get_status_api = demo.load(
486
- fn=update_print_status,
487
- inputs=[],
488
- outputs=[
489
- gr.Textbox(label="Status"),
490
- gr.Textbox(label="Nozzle Temperature"),
491
- gr.Textbox(label="Bed Temperature"),
492
- gr.Textbox(label="Update Time"),
493
- gr.Number(label="Progress"),
494
- gr.Textbox(label="Message"),
495
- ],
496
- api_name="get_status",
497
- )
498
-
499
- health_check_api = demo.load(
500
- fn=health_check,
501
- inputs=[],
502
- outputs=gr.JSON(),
503
- api_name="health_check",
504
- )
505
-
506
  if __name__ == "__main__":
507
  logger.info("Starting Bambu A1 Mini Print Control application")
508
 
@@ -510,12 +582,14 @@ if __name__ == "__main__":
510
  logger.info("Initializing Bambu MQTT client")
511
  create_client("bambu")
512
  except Exception as e:
 
513
  logger.error(f"Failed to initialize Bambu MQTT: {e}")
514
 
515
  try:
516
  logger.info("Initializing RPI MQTT client")
517
  create_client("rpi")
518
  except Exception as e:
 
519
  logger.error(f"Failed to initialize RPI MQTT: {e}")
520
 
521
  demo.queue().launch(
 
47
  "status": "N/A",
48
  "update_time": "Waiting for data...",
49
  "image_url": "N/A",
 
 
50
  }
51
 
52
  bambu_client = None
 
94
  logger.error(f"Error parsing MQTT message: {e}")
95
 
96
 
97
+ def rpi_on_message(client, userdata, message):
98
  global latest_data
99
+ logger.info("Received message")
100
  try:
101
+ data = json.loads(message.payload)
102
+ latest_data["image_url"] = data.get("image_url", "N/A")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  except Exception as e:
104
+ logger.error(f"Error parsing MQTT message: {e}")
105
 
106
 
107
  def get_data(serial=DEFAULT_SERIAL):
108
  global bambu_client, response_topic
109
 
110
+ retries = 5
111
+ while bambu_client is None and retries > 0:
112
+ try:
113
+ create_client("bambu")
114
+ except Exception as e:
115
+ bambu_client = None
116
+ logger.error(f"Failed to initialize Bambu MQTT: {e}")
117
+
118
+ retries -= 1
119
+
120
  if bambu_client is None:
121
+ return (
122
+ latest_data["status"],
123
+ latest_data["bed_temperature"],
124
+ latest_data["nozzle_temperature"],
125
+ latest_data["update_time"],
126
+ )
127
 
128
  request_topic = f"bambu_a1_mini/request/{serial}"
129
  response_topic = f"bambu_a1_mini/response/{serial}"
 
150
 
151
 
152
  def send_print_parameters(nozzle_temp, bed_temp, print_speed, fan_speed):
153
+ global bambu_client
154
 
155
  serial = DEFAULT_SERIAL
 
156
  logger.info(
157
+ f"Sending parameters to {serial}: nozzle={nozzle_temp}, bed={bed_temp}, speed={print_speed}, fan={fan_speed}"
 
158
  )
 
159
  try:
160
  params = {
161
  "nozzle_temp": nozzle_temp,
 
166
 
167
  request_topic = f"bambu_a1_mini/request/{serial}"
168
 
169
+ if bambu_client:
170
+ bambu_client.publish(
171
  request_topic,
172
+ json.dumps({"command": "set_parameters", "parameters": params}),
173
  )
174
+ logger.info("Parameters sent successfully")
175
+ return "Parameters sent successfully"
 
 
 
 
 
176
  else:
177
  logger.warning("MQTT not connected, parameters not sent")
178
  return "MQTT not connected, parameters not sent"
 
180
  logger.error(f"Error sending parameters: {e}")
181
  return f"Error sending parameters: {e}"
182
 
 
 
 
 
 
 
183
 
184
  def get_image_base64(image):
185
  if image is None:
 
200
  return None
201
 
202
 
203
+ def get_test_image(image_name=None):
204
+ test_dir = os.path.join(os.path.dirname(__file__), "test_images")
205
+
206
+ if not os.path.exists(test_dir):
207
+ logger.error(f"Test images directory not found: {test_dir}")
208
+ return None
209
+
210
+ image_files = [
211
+ f
212
+ for f in os.listdir(test_dir)
213
+ if f.lower().endswith((".png", ".jpg", ".jpeg", ".bmp"))
214
+ ]
215
+
216
+ if not image_files:
217
+ logger.error("No test images found")
218
+ return None
219
+
220
+ if image_name and image_name in image_files:
221
+ image_path = os.path.join(test_dir, image_name)
222
+ else:
223
+ image_path = os.path.join(test_dir, random.choice(image_files))
224
+
225
+ logger.info(f"Using test image: {image_path}")
226
+
227
  try:
228
+ return Image.open(image_path)
 
 
 
 
 
 
 
 
 
229
  except Exception as e:
230
+ logger.error(f"Failed to open test image: {e}")
231
+ return None
232
 
233
 
234
  def capture_image(url=None, use_test_image=False, test_image_name=None):
 
283
  else:
284
  raise Exception("url is 'N/A'")
285
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
  def health_check():
288
  status = {
 
329
  <div style="position: relative; width: 100%; padding-top: 56.25%;">
330
  <iframe
331
  style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;"
332
+ src="https://www.youtube.com/embed/K1TJie3NmHM"
333
  title="Bambu A1mini Livestream"
334
  frameborder="0"
335
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
 
404
  logger.error(f"Error in capture_frame: {e}")
405
  return {"success": False, "error": str(e)}
406
 
407
+ def api_lambda(
408
+ img_data=None,
409
+ param_1=200,
410
+ param_2=60,
411
+ param_3=60,
412
+ param_4=100,
413
+ use_test_image=False,
414
+ test_image_name=None,
415
+ ):
416
+ logger.info(
417
+ f"API call: lambda with params: {param_1}, {param_2}, {param_3}, {param_4}, use_test_image: {use_test_image}, test_image_name: {test_image_name}"
418
+ )
419
+ try:
420
+ img = None
421
+
422
+ if use_test_image:
423
+ logger.info(f"Lambda using test image: {test_image_name}")
424
+ img = get_test_image(test_image_name)
425
+
426
+ elif (
427
+ img_data
428
+ and isinstance(img_data, str)
429
+ and (img_data.startswith("http://") or img_data.startswith("https://"))
430
+ ):
431
+ logger.info(f"Lambda received image URL: {img_data}")
432
+ img = capture_image(img_data)
433
+
434
+ elif img_data and isinstance(img_data, str):
435
+ try:
436
+ logger.info("Lambda received base64 image data")
437
+ img_bytes = base64.b64decode(img_data)
438
+ img = Image.open(io.BytesIO(img_bytes))
439
+ except Exception as e:
440
+ logger.error(f"Failed to decode base64 image: {e}")
441
+
442
+ if img is None:
443
+ logger.info("No valid image data received, using default test image")
444
+ img = get_test_image()
445
+
446
+ if img:
447
+ img_array = np.array(img)
448
+
449
+ quality_level = "low"
450
+ if 190 <= param_1 <= 210 and param_3 <= 50 and param_4 >= 80:
451
+ quality_level = "high"
452
+ elif 185 <= param_1 <= 215 and param_3 <= 70 and param_4 >= 60:
453
+ quality_level = "medium"
454
+
455
+ if quality_level == "high":
456
+ missing_rate = 0.02
457
+ excess_rate = 0.01
458
+ stringing_rate = 0.01
459
+ elif quality_level == "medium":
460
+ missing_rate = 0.05
461
+ excess_rate = 0.03
462
+ stringing_rate = 0.02
463
+ else: # low
464
+ missing_rate = 0.10
465
+ excess_rate = 0.07
466
+ stringing_rate = 0.05
467
+
468
+ uniformity_score = 1.0 - (missing_rate + excess_rate + stringing_rate)
469
+
470
+ print_quality_score = 1.0 - (
471
+ missing_rate * 2.0 + excess_rate * 1.5 + stringing_rate * 1.0
472
+ )
473
+ print_quality_score = max(0, min(1, print_quality_score))
474
+
475
+ print_speed_score = param_3 / 150.0
476
+ print_speed_score = max(0, min(1, print_speed_score))
477
+
478
+ material_efficiency_score = 1.0 - excess_rate * 3.0
479
+ material_efficiency_score = max(0, min(1, material_efficiency_score))
480
+
481
+ total_performance_score = (
482
+ 0.5 * print_quality_score
483
+ + 0.3 * print_speed_score
484
+ + 0.2 * material_efficiency_score
485
+ )
486
+
487
+ img_draw = img.copy()
488
+ draw = ImageDraw.Draw(img_draw)
489
+ draw.text(
490
+ (10, 10), f"Quality: {quality_level.upper()}", fill=(255, 0, 0)
491
+ )
492
+ draw.text((10, 30), f"Missing: {missing_rate:.2f}", fill=(255, 0, 0))
493
+ draw.text((10, 50), f"Excess: {excess_rate:.2f}", fill=(255, 0, 0))
494
+ draw.text(
495
+ (10, 70), f"Stringing: {stringing_rate:.2f}", fill=(255, 0, 0)
496
+ )
497
+
498
+ result = {
499
+ "success": True,
500
+ "missing_rate": missing_rate,
501
+ "excess_rate": excess_rate,
502
+ "stringing_rate": stringing_rate,
503
+ "uniformity_score": uniformity_score,
504
+ "print_quality_score": print_quality_score,
505
+ "print_speed_score": print_speed_score,
506
+ "material_efficiency_score": material_efficiency_score,
507
+ "total_performance_score": total_performance_score,
508
+ }
509
+
510
+ if img_draw.mode == "RGBA":
511
+ img_draw = img_draw.convert("RGB")
512
+
513
+ buffered = io.BytesIO()
514
+ img_draw.save(buffered, format="JPEG")
515
+ img_str = base64.b64encode(buffered.getvalue()).decode()
516
+ result["image"] = img_str
517
+
518
+ return result
519
+ else:
520
+ return {"success": False, "error": "Failed to get image"}
521
+ except Exception as e:
522
+ logger.error(f"Error in lambda: {e}")
523
+ return {"error": str(e)}
524
 
525
  def api_send_print_parameters(
526
  nozzle_temp=200, bed_temp=60, print_speed=60, fan_speed=100
 
544
  api_name="capture_frame",
545
  )
546
 
547
+ lambda_api = demo.load(
548
+ fn=api_lambda,
549
+ inputs=[
550
+ gr.Textbox(label="Image Base64 or URL"),
551
+ gr.Number(label="Nozzle Temperature", value=200),
552
+ gr.Number(label="Bed Temperature", value=60),
553
+ gr.Number(label="Print Speed", value=60),
554
+ gr.Number(label="Fan Speed", value=100),
555
+ gr.Checkbox(label="Use Test Image", value=False),
556
+ gr.Textbox(label="Test Image Name", value=""),
557
+ ],
558
+ outputs=api_json_output,
559
+ api_name="lambda",
560
+ )
561
 
562
  get_data_api = demo.load(
563
  fn=api_get_data, inputs=None, outputs=api_json_output, api_name="get_data"
 
575
  api_name="send_print_parameters",
576
  )
577
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578
  if __name__ == "__main__":
579
  logger.info("Starting Bambu A1 Mini Print Control application")
580
 
 
582
  logger.info("Initializing Bambu MQTT client")
583
  create_client("bambu")
584
  except Exception as e:
585
+ bambu_client = None
586
  logger.error(f"Failed to initialize Bambu MQTT: {e}")
587
 
588
  try:
589
  logger.info("Initializing RPI MQTT client")
590
  create_client("rpi")
591
  except Exception as e:
592
+ rpi_client = None
593
  logger.error(f"Failed to initialize RPI MQTT: {e}")
594
 
595
  demo.queue().launch(