fix(printer): 修复A1打印发送,绕过bambulabs_api的ftp URL问题,改用sdcard路径
This commit is contained in:
@@ -75,7 +75,7 @@
|
|||||||
"gui.output.printer.ip": "IP Address",
|
"gui.output.printer.ip": "IP Address",
|
||||||
"gui.output.printer.access_code": "Access Code",
|
"gui.output.printer.access_code": "Access Code",
|
||||||
"gui.output.printer.serial": "Serial",
|
"gui.output.printer.serial": "Serial",
|
||||||
"gui.output.printer.send.tooltip": "Send the generated 3MF to an A1 printer over LAN and start printing",
|
"gui.output.printer.send.tooltip": "Send the generated 3MF to an A1 printer over LAN and start printing (LAN only — computer and printer must be on the same local network)",
|
||||||
|
|
||||||
"gui.dialog.individual_batch.title": "Individual Batch Mode",
|
"gui.dialog.individual_batch.title": "Individual Batch Mode",
|
||||||
"gui.dialog.individual_batch.text": "Individual batch mode treats every input row as a separate build.\n\nExample: if you add 20 single-plate 3MF files and set copies to 5, Build 3MF will create 20 separate packed files. Each output contains only that source file repeated 5 times.\n\nThis is useful for quickly batch-converting many independent 3MF jobs into multi-copy SwapMod packs. It does not combine all input files into one 3MF.",
|
"gui.dialog.individual_batch.text": "Individual batch mode treats every input row as a separate build.\n\nExample: if you add 20 single-plate 3MF files and set copies to 5, Build 3MF will create 20 separate packed files. Each output contains only that source file repeated 5 times.\n\nThis is useful for quickly batch-converting many independent 3MF jobs into multi-copy SwapMod packs. It does not combine all input files into one 3MF.",
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
"gui.output.printer.ip": "IP 地址",
|
"gui.output.printer.ip": "IP 地址",
|
||||||
"gui.output.printer.access_code": "访问码",
|
"gui.output.printer.access_code": "访问码",
|
||||||
"gui.output.printer.serial": "序列号",
|
"gui.output.printer.serial": "序列号",
|
||||||
"gui.output.printer.send.tooltip": "通过局域网将生成的 3MF 发送到 A1 打印机并开始打印",
|
"gui.output.printer.send.tooltip": "通过局域网将生成的 3MF 发送到 A1 打印机并开始打印(仅支持本地局域网,电脑与打印机需在同一网络下)",
|
||||||
|
|
||||||
"gui.dialog.individual_batch.title": "独立批处理模式",
|
"gui.dialog.individual_batch.title": "独立批处理模式",
|
||||||
"gui.dialog.individual_batch.text": "独立批处理模式将每一行输入作为独立的构建任务。\n\n示例:如果你添加了 20 个单板 3MF 文件并设置份数为 5,点击\"构建 3MF\"将创建 20 个独立的打包文件。每个输出文件仅包含该源文件重复 5 次的内容。\n\n这个功能适用于快速将大量独立的 3MF 作业批量转换为多份数的 SwapMod 包。它不会将所有输入文件合并到一个 3MF 中。",
|
"gui.dialog.individual_batch.text": "独立批处理模式将每一行输入作为独立的构建任务。\n\n示例:如果你添加了 20 个单板 3MF 文件并设置份数为 5,点击\"构建 3MF\"将创建 20 个独立的打包文件。每个输出文件仅包含该源文件重复 5 次的内容。\n\n这个功能适用于快速将大量独立的 3MF 作业批量转换为多份数的 SwapMod 包。它不会将所有输入文件合并到一个 3MF 中。",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PrinterConfig:
|
class PrinterConfig:
|
||||||
"""打印机连接配置。"""
|
"""打印机连接配置。"""
|
||||||
@@ -11,32 +12,94 @@ class PrinterConfig:
|
|||||||
access_code: str = "" # 局域网访问码
|
access_code: str = "" # 局域网访问码
|
||||||
serial: str = "" # 打印机序列号
|
serial: str = "" # 打印机序列号
|
||||||
|
|
||||||
|
|
||||||
|
def check_printer_ready(config: PrinterConfig) -> bool:
|
||||||
|
"""检查打印机是否空闲可接收新任务。
|
||||||
|
|
||||||
|
返回 True 表示打印机空闲,False 表示正忙或离线。
|
||||||
|
"""
|
||||||
|
from bambulabs_api import Printer as BambuPrinter
|
||||||
|
from bambulabs_api.states_info import GcodeState
|
||||||
|
printer = BambuPrinter(config.ip, config.access_code, config.serial)
|
||||||
|
try:
|
||||||
|
printer.connect()
|
||||||
|
state = printer.get_state()
|
||||||
|
return state == GcodeState.IDLE
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
printer.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
def _start_print_for_a1(printer, file_name: str, plate_number: int = 1) -> None:
|
||||||
|
"""发送打印机开始打印的 MQTT 指令,使用 A1 专用的 sdcard URL 格式。
|
||||||
|
|
||||||
|
bambulabs_api 的 start_print 方法硬编码了 ftp:/// 前缀,
|
||||||
|
这对 A1 不生效。A1 需要 file:///sdcard/ 前缀。
|
||||||
|
本函数绕过 start_print,直接构造正确的 MQTT 载荷。
|
||||||
|
"""
|
||||||
|
plate_location = f"Metadata/plate_{int(plate_number)}.gcode"
|
||||||
|
payload = {
|
||||||
|
"print": {
|
||||||
|
"command": "project_file",
|
||||||
|
"param": plate_location,
|
||||||
|
"file": file_name,
|
||||||
|
"bed_leveling": True,
|
||||||
|
"bed_type": "textured_plate",
|
||||||
|
"flow_cali": True,
|
||||||
|
"vibration_cali": True,
|
||||||
|
"url": f"file:///sdcard/{file_name}",
|
||||||
|
"layer_inspect": False,
|
||||||
|
"sequence_id": "10000000",
|
||||||
|
"use_ams": True,
|
||||||
|
"ams_mapping": [0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# 调用名称改写后的私有方法发送原始 JSON 荷载
|
||||||
|
printer.mqtt_client._PrinterMQTTClient__publish_command(payload)
|
||||||
|
|
||||||
|
|
||||||
def send_to_printer(file_path: Path, config: PrinterConfig) -> bool:
|
def send_to_printer(file_path: Path, config: PrinterConfig) -> bool:
|
||||||
"""将 3MF 文件上传至打印机并开始打印。
|
"""将 3MF 文件上传至 A1 打印机并通过局域网开始打印。
|
||||||
|
|
||||||
|
发送前会检查打印机状态;正忙则拒绝发送。
|
||||||
|
⚠️ 仅支持本地局域网,电脑与打印机必须在同一网络下。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
file_path: 要发送的 3MF 文件路径
|
file_path: 要发送的 3MF 文件路径
|
||||||
config: 打印机连接配置
|
config: 打印机连接配置
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True 表示成功,False 表示失败
|
True 表示成功
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ImportError: bambulabs_api 未安装
|
ImportError: bambulabs_api 未安装
|
||||||
Exception: 连接或上传失败
|
RuntimeError: 打印机正忙或上传/打印失败
|
||||||
"""
|
"""
|
||||||
# 延迟导入,避免在没有 bambulabs_api 时报错
|
|
||||||
from bambulabs_api import Printer as BambuPrinter
|
from bambulabs_api import Printer as BambuPrinter
|
||||||
|
from bambulabs_api.states_info import GcodeState
|
||||||
|
|
||||||
printer = BambuPrinter(config.ip, config.access_code, config.serial)
|
printer = BambuPrinter(config.ip, config.access_code, config.serial)
|
||||||
try:
|
try:
|
||||||
printer.connect()
|
printer.connect()
|
||||||
|
|
||||||
|
# 检查打印机是否空闲
|
||||||
|
state = printer.get_state()
|
||||||
|
if state != GcodeState.IDLE:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"打印机当前状态为 {state.value},"
|
||||||
|
f"无法开始新任务。请等待当前打印完成。"
|
||||||
|
)
|
||||||
|
|
||||||
file_name = file_path.name
|
file_name = file_path.name
|
||||||
with open(file_path, "rb") as f:
|
with open(file_path, "rb") as f:
|
||||||
import io
|
import io
|
||||||
result = printer.upload_file(io.BytesIO(f.read()), file_name)
|
result = printer.upload_file(io.BytesIO(f.read()), file_name)
|
||||||
if "226" not in result:
|
if "226" not in result:
|
||||||
raise RuntimeError(f"上传文件失败: {result}")
|
raise RuntimeError(f"上传文件失败: {result}")
|
||||||
printer.start_print(file_name, plate_number=1)
|
|
||||||
|
# 使用 A1 专用的 MQTT 指令开始打印,而非内置的 start_print
|
||||||
|
_start_print_for_a1(printer, file_name, plate_number=1)
|
||||||
return True
|
return True
|
||||||
finally:
|
finally:
|
||||||
printer.disconnect()
|
printer.disconnect()
|
||||||
|
|||||||
Reference in New Issue
Block a user