SissiFeng commited on
Commit
ebe5556
·
1 Parent(s): d98b776
Files changed (1) hide show
  1. app.py +275 -246
app.py CHANGED
@@ -1,37 +1,33 @@
1
  import gradio as gr
2
- import numpy as np
3
- import cv2
4
  import paho.mqtt.client as mqtt
5
  import json
6
  import time
7
  import threading
8
  import os
9
- from io import BytesIO
10
- import zipfile
11
- import logging
12
- from dotenv import load_dotenv
13
- from mo_optimizer import MOPrintOptimizer
14
- from core.analysis import DefectDetector, ImageProcessor
15
- from datetime import datetime
16
- import requests
17
  from PIL import Image
 
 
 
 
18
 
19
- # Load environment variables
20
- load_dotenv()
21
-
22
- # MQTT Configuration - 直接硬编码配置
23
- HOST = "mqtt.bambulab.com"
24
- PORT = 8883
25
- USERNAME = "bblp"
26
- PASSWORD = "bblp"
27
- PRINTER_SERIAL = "0309CA471800852"
28
-
29
- # Setup logging
30
  logging.basicConfig(
31
  level=logging.INFO,
32
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
 
 
 
 
33
  )
34
- logger = logging.getLogger(__name__)
 
 
 
 
 
 
 
35
 
36
  # Global variables
37
  client = None
@@ -42,111 +38,117 @@ latest_data = {
42
  "update_time": "Waiting for data..."
43
  }
44
 
45
- # Initialize analysis components
46
- optimizer = MOPrintOptimizer()
47
- detector = DefectDetector()
48
-
49
- # MQTT Topics structure
50
- WORKFLOW_STATUS = {
51
- 'print_started': False,
52
- 'print_completed': False,
53
- 'image_captured': False,
54
- 'analysis_completed': False,
55
- 'current_square_id': None,
56
- 'start_time': None,
57
- 'completion_time': None
58
- }
59
-
60
- def update_workflow_status(status_type: str, value: bool = True):
61
- """Update workflow status
62
-
63
- Args:
64
- status_type: Type of status to update
65
- value: New status value
66
- """
67
- WORKFLOW_STATUS[status_type] = value
68
- if status_type == 'print_started':
69
- WORKFLOW_STATUS['start_time'] = time.strftime('%Y-%m-%d %H:%M:%S')
70
- elif status_type == 'analysis_completed':
71
- WORKFLOW_STATUS['completion_time'] = time.strftime('%Y-%m-%d %H:%M:%S')
72
-
73
- # Message formats
74
- def create_print_command(params):
75
- return {
76
- 'type': 'print',
77
- 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'),
78
- 'params': {
79
- 'nozzle_temp': params['nozzle_temp'],
80
- 'print_speed': params['print_speed'],
81
- # ... other params
82
- }
83
- }
84
-
85
- def create_capture_command(square_id):
86
- return {
87
- 'type': 'capture',
88
- 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'),
89
- 'square_id': square_id
90
- }
91
-
92
- def create_3mf_package(gcode_content: str, gcode_filename: str = "Metadata/plate_1.gcode") -> BytesIO:
93
- """Create a 3MF package from G-code content"""
94
- zip_buffer = BytesIO()
95
- with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zipf:
96
- zipf.writestr(gcode_filename, gcode_content)
97
- zip_buffer.seek(0)
98
- return zip_buffer
99
-
100
  def create_client(host, port, username, password):
 
101
  global client
102
- client = mqtt.Client()
103
- client.username_pw_set(username, password)
104
- client.tls_set(tls_version=mqtt.ssl.PROTOCOL_TLS)
105
- client.on_connect = on_connect
106
- client.on_message = on_message
107
- client.connect(host, port)
108
- client.loop_start()
 
 
 
 
 
 
 
 
 
109
 
110
  def on_connect(client, userdata, flags, rc):
111
- print(f"Connected with result code {rc}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  def on_message(client, userdata, message):
114
- global latest_data
115
- print("Received message")
116
  try:
 
117
  data = json.loads(message.payload)
 
 
 
118
  latest_data["bed_temperature"] = data.get("bed_temperature", "N/A")
119
  latest_data["nozzle_temperature"] = data.get("nozzle_temperature", "N/A")
120
  latest_data["status"] = data.get("status", "N/A")
121
- latest_data["update_time"] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
122
  except Exception as e:
123
- print(f"Error parsing MQTT message: {e}")
124
 
125
- def get_data():
126
- """Request data from the MQTT broker."""
127
- global client
128
- if client is None:
129
- create_client(HOST, PORT, USERNAME, PASSWORD)
130
-
131
- serial = "0309CA471800852"
132
- request_topic = f"bambu_a1_mini/request/{serial}"
133
- response_topic = f"bambu_a1_mini/response/{serial}"
134
-
135
- print(f"Subscribing to {response_topic}")
136
- client.subscribe(response_topic)
137
 
138
- # Send a request to get data
139
- client.publish(request_topic, json.dumps("HI"))
 
140
 
141
- return (
142
- latest_data["status"],
143
- latest_data["bed_temperature"],
144
- latest_data["nozzle_temperature"],
145
- latest_data["update_time"]
146
- )
 
 
 
 
 
 
 
 
 
147
 
148
- def send_print_parameters(nozzle_temp, bed_temp, print_speed, fan_speed):
149
- """Send new print parameters to printer"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  try:
151
  params = {
152
  'nozzle_temp': nozzle_temp,
@@ -155,173 +157,200 @@ def send_print_parameters(nozzle_temp, bed_temp, print_speed, fan_speed):
155
  'fan_speed': fan_speed
156
  }
157
 
158
- serial = PRINTER_SERIAL
159
  request_topic = f"bambu_a1_mini/request/{serial}"
160
- if client:
 
161
  client.publish(request_topic, json.dumps({
162
  'command': 'set_parameters',
163
  'parameters': params
164
  }))
 
165
  return "Parameters sent successfully"
166
- return "MQTT not connected"
 
 
167
  except Exception as e:
 
168
  return f"Error sending parameters: {e}"
169
 
170
- # Gradio interface
171
- with gr.Blocks(title="Bambu A1 Mini Print Analysis") as demo:
172
- gr.Markdown("# Bambu A1 Mini Print Quality Analysis")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
  with gr.Row():
175
- current_nozzle_temp = gr.Textbox(label="Current Nozzle Temperature", value="N/A", interactive=False)
176
- current_bed_temp = gr.Textbox(label="Current Bed Temperature", value="N/A", interactive=False)
177
  current_status = gr.Textbox(label="Printer Status", value="N/A", interactive=False)
 
 
178
  last_update = gr.Textbox(label="Last Update", value="N/A", interactive=False)
179
 
180
- refresh_btn = gr.Button("Refresh Status")
181
-
182
  with gr.Row():
183
- # show S3 image
184
- s3_image = gr.Image(
185
- label="Latest S3 Image",
186
- type="filepath", # use URL
187
- interactive=False
188
- )
189
- captured_image = gr.Image(
190
- label="Current Print Image",
191
- type="numpy"
192
- )
193
 
194
  with gr.Row():
195
  with gr.Column():
196
- # Print parameter inputs
197
  nozzle_temp = gr.Slider(minimum=180, maximum=250, step=1, value=200, label="Nozzle Temperature (°C)")
198
  bed_temp = gr.Slider(minimum=40, maximum=100, step=1, value=60, label="Bed Temperature (°C)")
199
  print_speed = gr.Slider(minimum=20, maximum=150, step=1, value=60, label="Print Speed (mm/s)")
200
  fan_speed = gr.Slider(minimum=0, maximum=100, step=1, value=100, label="Fan Speed (%)")
201
 
202
- # Buttons
203
- with gr.Row():
204
- capture_btn = gr.Button("Capture Image")
205
- analyze_btn = gr.Button("Analyze Print")
206
- send_params_btn = gr.Button("Send Print Parameters")
207
 
208
- with gr.Column():
209
- # Results visualization
210
- result_image = gr.Image(label="Analysis Result")
211
-
212
- # Quality metrics
213
- with gr.Row():
214
- missing_rate = gr.Number(label="Missing Rate", value=0.0)
215
- excess_rate = gr.Number(label="Excess Rate", value=0.0)
216
- with gr.Row():
217
- stringing_rate = gr.Number(label="Stringing Rate", value=0.0)
218
- uniformity_score = gr.Number(label="Uniformity Score", value=0.0)
219
-
220
- # Overall scores
221
- with gr.Row():
222
- quality_output = gr.Number(label="Print Quality Score")
223
- speed_output = gr.Number(label="Print Speed Score")
224
- material_output = gr.Number(label="Material Efficiency Score")
225
- total_output = gr.Number(label="Total Performance Score")
226
-
227
- # Connect inputs to outputs
228
- def capture_frame(img):
229
- """Capture current image for analysis"""
230
- if img is None:
231
  return None
232
- # Convert URL image to numpy array if needed
233
- if isinstance(img, str):
234
- response = requests.get(img)
235
- img = Image.open(BytesIO(response.content))
236
- img = np.array(img)
237
- return img
238
 
239
- capture_btn.click(
240
- fn=capture_frame,
241
- inputs=[s3_image],
242
- outputs=[captured_image]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  )
244
 
245
- analyze_btn.click(
246
- fn=lambda img, *params: (
247
- analyze_print(img, *params),
248
- send_print_command(dict(
249
- nozzle_temp=params[0], # nozzle_temp slider
250
- print_speed=params[1], # print_speed slider
251
- bed_temp=params[1], # bed_temp slider
252
- fan_speed=params[2], # fan_speed slider
253
- ))
254
- ),
255
- inputs=[
256
- captured_image,
257
- nozzle_temp, bed_temp, print_speed, fan_speed
258
- ],
259
- outputs=[
260
- result_image,
261
- missing_rate, excess_rate,
262
- stringing_rate, uniformity_score,
263
- quality_output, speed_output,
264
- material_output, total_output,
265
- current_status
266
- ]
267
  )
268
 
269
- # connect refresh button
270
- refresh_btn.click(fn=get_data, outputs=[current_status, current_bed_temp, current_nozzle_temp, last_update])
271
-
272
- # Auto refresh
273
- def auto_refresh():
274
- while True:
275
- time.sleep(5)
276
- status, bed_temp, nozzle_temp, update_time = get_data()
277
- current_status.value = status
278
- current_bed_temp.value = bed_temp
279
- current_nozzle_temp.value = nozzle_temp
280
- last_update.value = update_time
281
-
282
- threading.Thread(target=auto_refresh, daemon=True).start()
283
-
284
- # 连接按钮
285
  send_params_btn.click(
286
  fn=send_print_parameters,
287
- inputs=[nozzle_temp, bed_temp, print_speed, fan_speed],
288
- outputs=[current_status]
 
289
  )
290
-
291
- def analyze_print(image, nozzle_temp, bed_temp, print_speed, fan_speed):
292
- """Analyze print quality and return evaluation results"""
293
- if image is None:
294
- return None, 0, 0, 0, 0, 0, 0, 0, 0
295
 
296
- # Package current parameters
297
- current_params = {
298
- 'nozzle_temp': float(nozzle_temp),
299
- 'bed_temp': float(bed_temp),
300
- 'print_speed': float(print_speed),
301
- 'fan_speed': float(fan_speed)
302
- }
303
-
304
- # Analyze print quality
305
- analysis_results = detector.analyze_print(image)
306
-
307
- # Get optimization results
308
- results = optimizer.evaluate_objectives(image, current_params)
309
-
310
- # Calculate quality metrics
311
- metrics = detector.calculate_quality_metrics(image)
312
-
313
- return (
314
- analysis_results['binary_mask'], # Visualization
315
- float(metrics['missing_rate']),
316
- float(metrics['excess_rate']),
317
- float(metrics['stringing_rate']),
318
- float(metrics['uniformity_score']),
319
- float(analysis_results['quality_score']),
320
- float(results['objectives']['speed']),
321
- float(results['objectives']['material']),
322
- float(results['objectives']['total'])
323
- )
324
 
325
- # Launch the app
326
  if __name__ == "__main__":
327
- demo.launch()
 
 
1
  import gradio as gr
 
 
2
  import paho.mqtt.client as mqtt
3
  import json
4
  import time
5
  import threading
6
  import os
7
+ import base64
 
 
 
 
 
 
 
8
  from PIL import Image
9
+ import io
10
+ import numpy as np
11
+ import logging
12
+ import sys
13
 
14
+ # 设置详细的日志记录
 
 
 
 
 
 
 
 
 
 
15
  logging.basicConfig(
16
  level=logging.INFO,
17
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
18
+ handlers=[
19
+ logging.StreamHandler(sys.stdout),
20
+ logging.FileHandler('app.log')
21
+ ]
22
  )
23
+ logger = logging.getLogger("bambu-analysis")
24
+
25
+ # Environment Variables - 尝试从环境变量获取,如果没有则使用默认值
26
+ HOST = os.environ.get("host", "mqtt.bambulab.com")
27
+ PORT = int(os.environ.get("port", "8883"))
28
+ USERNAME = os.environ.get("username", "bblp")
29
+ PASSWORD = os.environ.get("password", "bblp")
30
+ DEFAULT_SERIAL = "0309CA471800852"
31
 
32
  # Global variables
33
  client = None
 
38
  "update_time": "Waiting for data..."
39
  }
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  def create_client(host, port, username, password):
42
+ """创建并配置MQTT客户端"""
43
  global client
44
+ try:
45
+ logger.info(f"Creating MQTT client for {host}:{port}")
46
+ client = mqtt.Client()
47
+ client.username_pw_set(username, password)
48
+ client.tls_set(tls_version=mqtt.ssl.PROTOCOL_TLS)
49
+ client.on_connect = on_connect
50
+ client.on_message = on_message
51
+ client.on_disconnect = on_disconnect
52
+
53
+ logger.info("Connecting to MQTT broker...")
54
+ client.connect(host, port)
55
+ client.loop_start()
56
+ return True
57
+ except Exception as e:
58
+ logger.error(f"Failed to create MQTT client: {e}")
59
+ return False
60
 
61
  def on_connect(client, userdata, flags, rc):
62
+ """MQTT连接回调"""
63
+ if rc == 0:
64
+ logger.info("Successfully connected to MQTT broker")
65
+ serial = DEFAULT_SERIAL
66
+ response_topic = f"bambu_a1_mini/response/{serial}"
67
+ request_topic = f"bambu_a1_mini/request/{serial}"
68
+
69
+ try:
70
+ client.subscribe(response_topic)
71
+ logger.info(f"Subscribed to {response_topic}")
72
+
73
+ # 发送初始请求
74
+ client.publish(request_topic, json.dumps("HI"))
75
+ logger.info(f"Sent initial request to {request_topic}")
76
+
77
+ # 更新状态
78
+ global latest_data
79
+ latest_data["status"] = "Connected"
80
+ latest_data["update_time"] = time.strftime("%Y-%m-%d %H:%M:%S")
81
+ except Exception as e:
82
+ logger.error(f"Error in on_connect callback: {e}")
83
+ else:
84
+ logger.error(f"Failed to connect to MQTT broker with code: {rc}")
85
 
86
  def on_message(client, userdata, message):
87
+ """MQTT消息回调"""
 
88
  try:
89
+ logger.info(f"Received message on topic: {message.topic}")
90
  data = json.loads(message.payload)
91
+ logger.debug(f"Message content: {data}")
92
+
93
+ global latest_data
94
  latest_data["bed_temperature"] = data.get("bed_temperature", "N/A")
95
  latest_data["nozzle_temperature"] = data.get("nozzle_temperature", "N/A")
96
  latest_data["status"] = data.get("status", "N/A")
97
+ latest_data["update_time"] = time.strftime("%Y-%m-%d %H:%M:%S")
98
  except Exception as e:
99
+ logger.error(f"Error parsing MQTT message: {e}")
100
 
101
+ def on_disconnect(client, userdata, rc):
102
+ """MQTT断开连接回调"""
103
+ logger.warning(f"Disconnected from MQTT broker with code: {rc}")
 
 
 
 
 
 
 
 
 
104
 
105
+ global latest_data
106
+ latest_data["status"] = "Disconnected"
107
+ latest_data["update_time"] = time.strftime("%Y-%m-%d %H:%M:%S")
108
 
109
+ if rc != 0:
110
+ logger.warning("Unexpected disconnection. Attempting to reconnect...")
111
+ try:
112
+ create_client(HOST, PORT, USERNAME, PASSWORD)
113
+ except Exception as e:
114
+ logger.error(f"Failed to reconnect: {e}")
115
+
116
+ def get_data(serial=DEFAULT_SERIAL):
117
+ """获取打印机状态数据"""
118
+ logger.info("Requesting printer data")
119
+ try:
120
+ global client
121
+ if client is None or not client.is_connected():
122
+ logger.warning("MQTT client not connected. Attempting to connect...")
123
+ create_client(HOST, PORT, USERNAME, PASSWORD)
124
 
125
+ request_topic = f"bambu_a1_mini/request/{serial}"
126
+ response_topic = f"bambu_a1_mini/response/{serial}"
127
+
128
+ logger.info(f"Subscribing to {response_topic}")
129
+ if client and client.is_connected():
130
+ client.subscribe(response_topic)
131
+
132
+ # 发送请求
133
+ logger.info(f"Sending data request to {request_topic}")
134
+ client.publish(request_topic, json.dumps("HI"))
135
+
136
+ return (
137
+ latest_data["status"],
138
+ latest_data["bed_temperature"],
139
+ latest_data["nozzle_temperature"],
140
+ latest_data["update_time"]
141
+ )
142
+ else:
143
+ logger.error("MQTT client not available")
144
+ return ("Disconnected", "N/A", "N/A", "MQTT not connected")
145
+ except Exception as e:
146
+ logger.error(f"Error in get_data: {e}")
147
+ return (f"Error: {str(e)}", "N/A", "N/A", time.strftime("%Y-%m-%d %H:%M:%S"))
148
+
149
+ def send_print_parameters(nozzle_temp, bed_temp, print_speed, fan_speed, serial=DEFAULT_SERIAL):
150
+ """发送打印参数到打印机"""
151
+ logger.info(f"Sending parameters to {serial}: nozzle={nozzle_temp}, bed={bed_temp}, speed={print_speed}, fan={fan_speed}")
152
  try:
153
  params = {
154
  'nozzle_temp': nozzle_temp,
 
157
  'fan_speed': fan_speed
158
  }
159
 
 
160
  request_topic = f"bambu_a1_mini/request/{serial}"
161
+
162
+ if client and client.is_connected():
163
  client.publish(request_topic, json.dumps({
164
  'command': 'set_parameters',
165
  'parameters': params
166
  }))
167
+ logger.info("Parameters sent successfully")
168
  return "Parameters sent successfully"
169
+ else:
170
+ logger.warning("MQTT not connected, parameters not sent")
171
+ return "MQTT not connected, parameters not sent"
172
  except Exception as e:
173
+ logger.error(f"Error sending parameters: {e}")
174
  return f"Error sending parameters: {e}"
175
 
176
+ def get_image_base64(image):
177
+ """将图像转换为base64用于API传输"""
178
+ if image is None:
179
+ logger.warning("No image to encode")
180
+ return None
181
+
182
+ try:
183
+ # 转换为PIL图像
184
+ if isinstance(image, np.ndarray):
185
+ image = Image.fromarray(image)
186
+
187
+ # 转换为base64
188
+ buffer = io.BytesIO()
189
+ image.save(buffer, format="PNG")
190
+ img_str = base64.b64encode(buffer.getvalue()).decode('utf-8')
191
+ logger.info(f"Image encoded to base64 (length: {len(img_str)})")
192
+ return img_str
193
+ except Exception as e:
194
+ logger.error(f"Error encoding image: {e}")
195
+ return None
196
+
197
+ def capture_image():
198
+ """捕获当前打印图像"""
199
+ logger.info("Capturing print image")
200
+ try:
201
+ # 这里应该是实际的相机捕获代码
202
+ # 目前返回一个占位图像
203
+ logger.warning("Using placeholder image (camera not implemented)")
204
+ placeholder = Image.new('RGB', (300, 300), color=(200, 200, 200))
205
+ return placeholder
206
+ except Exception as e:
207
+ logger.error(f"Error capturing image: {e}")
208
+ return None
209
+
210
+ def health_check():
211
+ """健康检查端点"""
212
+ status = {
213
+ "app": "running",
214
+ "time": time.strftime("%Y-%m-%d %H:%M:%S"),
215
+ "mqtt_connected": client is not None and client.is_connected() if client else False,
216
+ "latest_update": latest_data["update_time"]
217
+ }
218
+ logger.info(f"Health check: {status}")
219
+ return status
220
+
221
+ # 初始化MQTT客户端
222
+ try:
223
+ logger.info("Initializing MQTT client")
224
+ mqtt_connected = create_client(HOST, PORT, USERNAME, PASSWORD)
225
+ if not mqtt_connected:
226
+ logger.warning("Starting without MQTT connection")
227
+ except Exception as e:
228
+ logger.error(f"Failed to initialize MQTT: {e}")
229
+ logger.warning("Starting without MQTT connection")
230
+
231
+ # Gradio界面
232
+ with gr.Blocks(title="Bambu A1 Mini Print Control") as demo:
233
+ gr.Markdown("# Bambu A1 Mini Print Control")
234
+
235
+ with gr.Row():
236
+ serial = gr.Textbox(DEFAULT_SERIAL, label="Serial Number")
237
+ refresh_btn = gr.Button("Refresh Status")
238
 
239
  with gr.Row():
 
 
240
  current_status = gr.Textbox(label="Printer Status", value="N/A", interactive=False)
241
+ current_bed_temp = gr.Textbox(label="Current Bed Temperature", value="N/A", interactive=False)
242
+ current_nozzle_temp = gr.Textbox(label="Current Nozzle Temperature", value="N/A", interactive=False)
243
  last_update = gr.Textbox(label="Last Update", value="N/A", interactive=False)
244
 
 
 
245
  with gr.Row():
246
+ captured_image = gr.Image(label="Current Print Image", type="pil")
247
+ capture_btn = gr.Button("Capture Image")
 
 
 
 
 
 
 
 
248
 
249
  with gr.Row():
250
  with gr.Column():
251
+ # 打印参数输入
252
  nozzle_temp = gr.Slider(minimum=180, maximum=250, step=1, value=200, label="Nozzle Temperature (°C)")
253
  bed_temp = gr.Slider(minimum=40, maximum=100, step=1, value=60, label="Bed Temperature (°C)")
254
  print_speed = gr.Slider(minimum=20, maximum=150, step=1, value=60, label="Print Speed (mm/s)")
255
  fan_speed = gr.Slider(minimum=0, maximum=100, step=1, value=100, label="Fan Speed (%)")
256
 
257
+ send_params_btn = gr.Button("Send Print Parameters")
 
 
 
 
258
 
259
+ # API端点
260
+ def api_get_status(serial=DEFAULT_SERIAL):
261
+ """API端点:获取状态"""
262
+ logger.info(f"API call: get_status for {serial}")
263
+ try:
264
+ return get_data(serial)
265
+ except Exception as e:
266
+ logger.error(f"Error in api_get_status: {e}")
267
+ return ("Error", "N/A", "N/A", str(e))
268
+
269
+ def api_get_image():
270
+ """API端点:获取图像"""
271
+ logger.info("API call: get_image")
272
+ try:
273
+ img = capture_image()
274
+ return get_image_base64(img)
275
+ except Exception as e:
276
+ logger.error(f"Error in api_get_image: {e}")
 
 
 
 
 
277
  return None
 
 
 
 
 
 
278
 
279
+ def api_send_parameters(params):
280
+ """API端点:发送参数"""
281
+ logger.info(f"API call: send_parameters with {params}")
282
+ try:
283
+ serial = params.get('serial', DEFAULT_SERIAL)
284
+ return send_print_parameters(
285
+ params.get('nozzle_temp', 200),
286
+ params.get('bed_temp', 60),
287
+ params.get('print_speed', 60),
288
+ params.get('fan_speed', 100),
289
+ serial
290
+ )
291
+ except Exception as e:
292
+ logger.error(f"Error in api_send_parameters: {e}")
293
+ return f"Error: {str(e)}"
294
+
295
+ def api_health_check():
296
+ """API端点:健康检查"""
297
+ logger.info("API call: health_check")
298
+ try:
299
+ return health_check()
300
+ except Exception as e:
301
+ logger.error(f"Error in api_health_check: {e}")
302
+ return {"status": "error", "error": str(e)}
303
+
304
+ # 连接按钮
305
+ refresh_btn.click(
306
+ fn=get_data,
307
+ inputs=[serial],
308
+ outputs=[current_status, current_bed_temp, current_nozzle_temp, last_update],
309
+ api_name="refresh_status"
310
  )
311
 
312
+ capture_btn.click(
313
+ fn=capture_image,
314
+ outputs=[captured_image],
315
+ api_name="capture_image"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  )
317
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  send_params_btn.click(
319
  fn=send_print_parameters,
320
+ inputs=[nozzle_temp, bed_temp, print_speed, fan_speed, serial],
321
+ outputs=[current_status],
322
+ api_name="send_parameters"
323
  )
 
 
 
 
 
324
 
325
+ # 定义API端点
326
+ demo.queue()
327
+ gr.Interface(fn=api_get_status, inputs=[serial], outputs="json", title="Get Printer Status").launch(share=True)
328
+ gr.Interface(fn=api_get_image, inputs=None, outputs="json", title="Get Print Image").launch(share=True)
329
+ gr.Interface(fn=api_send_parameters, inputs="json", outputs="text", title="Send Print Parameters").launch(share=True)
330
+ gr.Interface(fn=api_health_check, inputs=None, outputs="json", title="Health Check").launch(share=True)
331
+
332
+ # 自动刷新线程
333
+ def auto_refresh():
334
+ """自动刷新状态的后台线程"""
335
+ logger.info("Starting auto-refresh thread")
336
+ while True:
337
+ try:
338
+ time.sleep(5)
339
+ logger.debug("Auto-refreshing printer status")
340
+ status, bed_temp, nozzle_temp, update_time = get_data(DEFAULT_SERIAL)
341
+
342
+ # 更新UI
343
+ current_status.update(value=status)
344
+ current_bed_temp.update(value=bed_temp)
345
+ current_nozzle_temp.update(value=nozzle_temp)
346
+ last_update.update(value=update_time)
347
+ except Exception as e:
348
+ logger.error(f"Auto refresh error: {e}")
349
+ time.sleep(10) # 出错后等待更长时间再重试
350
+
351
+ threading.Thread(target=auto_refresh, daemon=True).start()
 
352
 
353
+ # 启动应用
354
  if __name__ == "__main__":
355
+ logger.info("Starting Bambu A1 Mini Print Control application")
356
+ demo.launch(show_error=True) # 启用详细错误报告