分离cura,加入prusa,未测试

This commit is contained in:
2026-04-18 23:40:03 +08:00
parent 6981553101
commit 0416922a94
100 changed files with 196 additions and 320 deletions

View File

@@ -0,0 +1,17 @@
from .cura_engine import CuraEngine
from .prusa_slicer_engine import PrusaSlicerEngine
def get_slicer_engine(engine_name="cura"):
"""
Factory function to retrieve the requested slicing engine instance.
Valid names: 'cura', 'prusa_slicer'
"""
engine_name = engine_name.lower().strip()
if engine_name in ['cura', 'cura_engine', 'curaengine']:
return CuraEngine()
elif engine_name in ['prusa', 'prusa_slicer', 'prusaslicer']:
return PrusaSlicerEngine()
else:
# Default fallback
return CuraEngine()

View File

@@ -0,0 +1,168 @@
import os
import subprocess
import json
import uuid
import configparser
from app.utils.conf_parse import ConfParse
class CuraEngine:
def __init__(self):
self.name = "cura"
def slice(self, app, stl_filepath, gcode_filepath, **kwargs):
"""
Slices via CuraEngine.
Returns (success_bool, error_msg_if_any)
"""
quality_preset = kwargs.get('quality_preset')
infill_density = kwargs.get('infill_density')
support_enable = kwargs.get('support_enable')
support_pattern = kwargs.get('support_pattern')
tmp_def_path = None
try:
base_config_path = os.path.abspath(os.path.join(app.root_path, '..', 'print_config'))
print_config_path = os.path.join(base_config_path, 'cura_engine')
printers_path = os.path.join(print_config_path, 'printers')
extruders_path = os.path.join(print_config_path, 'extruders')
materials_path = os.path.join(print_config_path, 'materials')
presets_path = os.path.join(print_config_path, 'quality')
variants_path = os.path.join(print_config_path, 'variants')
env = os.environ.copy()
env["CURA_ENGINE_SEARCH_PATH"] = f"{printers_path}:{extruders_path}:{materials_path}:{presets_path}:{variants_path}"
def_files = [
os.path.join(printers_path, "fdmprinter.def.json"),
os.path.join(printers_path, "fdmextruder.def.json"),
os.path.join(printers_path, "creality_base.def.json"),
os.path.join(printers_path, "creality_ender3v3se.def.json")
]
inst_files_list = []
quality_type = None
preset_path = None
if quality_preset:
config = configparser.ConfigParser()
preset_path = os.path.join(presets_path, 'creality', 'presets', quality_preset)
if os.path.exists(preset_path):
config.read(preset_path)
material_type = config.get('metadata', 'material', fallback=None)
variant_type = config.get('metadata', 'variant', fallback=None)
quality_type = config.get('metadata', 'quality_type', fallback=None)
if material_type:
m_path = os.path.join(materials_path, f"{material_type}.inst.cfg")
if os.path.exists(m_path): inst_files_list.append(m_path)
if variant_type:
variant_d = variant_type.split("mm")[0]
v_path = os.path.join(variants_path, "creality", f"creality_ender3v3se_{variant_d}.inst.cfg")
if os.path.exists(v_path): inst_files_list.append(v_path)
if support_pattern == 'tree':
t_path = os.path.join(print_config_path, 'supports', 'tree.inst.cfg')
if os.path.exists(t_path): inst_files_list.append(t_path)
elif support_pattern and support_pattern != 'false':
n_path = os.path.join(print_config_path, 'supports', 'normal.inst.cfg')
if os.path.exists(n_path): inst_files_list.append(n_path)
if quality_preset and quality_type:
g_path = os.path.join(presets_path, 'creality', 'globals', f"{quality_type}.inst.cfg")
if os.path.exists(g_path): inst_files_list.append(g_path)
if quality_preset and preset_path and os.path.exists(preset_path):
inst_files_list.append(preset_path)
p = ConfParse(def_files)
settings_with_inst = p.add_inst_cfg(inst_files_list)
if infill_density is not None:
if "infill_sparse_density" not in settings_with_inst: settings_with_inst["infill_sparse_density"] = {}
settings_with_inst["infill_sparse_density"]["value"] = str(infill_density)
if "infill_line_distance" not in settings_with_inst: settings_with_inst["infill_line_distance"] = {}
settings_with_inst["infill_line_distance"]["value"] = str(100 / int(infill_density)) if int(infill_density) > 0 else "9999"
if support_enable is not None:
if "support_enable" not in settings_with_inst: settings_with_inst["support_enable"] = {}
settings_with_inst["support_enable"]["value"] = True if support_enable in ['true', 'buildplate'] else False
if "support_type" not in settings_with_inst: settings_with_inst["support_type"] = {}
settings_with_inst["support_type"]["value"] = "'buildplate'" if support_enable == 'buildplate' else "'everywhere'"
if support_pattern == 'tree':
if "support_structure" not in settings_with_inst: settings_with_inst["support_structure"] = {}
settings_with_inst["support_structure"]["value"] = "'tree'"
elif support_pattern and "support_pattern" in settings_with_inst and "options" in settings_with_inst["support_pattern"] and support_pattern in settings_with_inst["support_pattern"]["options"].keys():
if "support_structure" not in settings_with_inst: settings_with_inst["support_structure"] = {}
settings_with_inst["support_structure"]["value"] = "'normal'"
if "support_pattern" not in settings_with_inst: settings_with_inst["support_pattern"] = {}
settings_with_inst["support_pattern"]["value"] = f"'{support_pattern}'"
res = p.parse_configs(settings_with_inst)
override_dict = {}
for k, v in res.items():
if v.get("enabled", True):
val = v.get("value", None)
if val is not None:
override_dict[k] = {"value": val, "default_value": val}
elif "default_value" in v:
override_dict[k] = {"default_value": v["default_value"], "value": v["default_value"]}
tmp_def_filename = f"tmp_{uuid.uuid4().hex}.def.json"
tmp_def_path = os.path.join(app.config['UPLOAD_FOLDER'], tmp_def_filename)
tmp_def_obj = {
"version": 2,
"name": "TempProfile",
"inherits": "fdmprinter",
"metadata": {
"visible": True,
"author": "System",
"manufacturer": "System",
"file_formats": "text/x-gcode",
"first_start_actions": ["MachineSettingsAction"],
"has_materials": True,
"has_variants": True,
"has_machine_quality": True,
"variants_name": "Nozzle Size",
"preferred_variant_name": "0.4mm Nozzle",
"preferred_quality_type": "standard",
"preferred_material": "generic_pla",
},
"overrides": override_dict
}
pretty_json = json.dumps(tmp_def_obj, indent=4)
with open(tmp_def_path, "w") as f:
f.write(pretty_json)
command = [
"CuraEngine", "slice",
"-j", tmp_def_path,
"-l", stl_filepath,
"-o", gcode_filepath
]
app.logger.info(f"Running command: {' '.join(command)}")
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
stdout, stderr = process.communicate()
if process.returncode == 0:
return True, None
else:
err_msg = stderr.decode() if stderr else "Unknown CuraEngine error"
app.logger.error(f"CuraEngine Error: {err_msg}")
return False, err_msg
except Exception as e:
app.logger.error(f"CuraEngine Exception: {e}")
return False, str(e)
finally:
if tmp_def_path and os.path.exists(tmp_def_path):
try:
os.remove(tmp_def_path)
except Exception as e:
app.logger.error(f"Failed to delete temp JSON config {tmp_def_path}: {e}")

View File

@@ -0,0 +1,57 @@
import os
import subprocess
class PrusaSlicerEngine:
def __init__(self):
self.name = "prusa_slicer"
def slice(self, app, stl_filepath, gcode_filepath, **kwargs):
"""
Slices via prusa-slicer CLI mapping standard kwargs to PRUSA parameters where possible.
"""
try:
# Base command
command = [
"prusa-slicer",
"-g", stl_filepath,
"--output", gcode_filepath
]
# Map quality, infill, supports to PrusaSlicer CLI arguments.
# Example defaults, normally these would load from an .ini or be dynamically matched.
quality_preset = kwargs.get('quality_preset')
infill_density = kwargs.get('infill_density')
support_enable = kwargs.get('support_enable')
support_pattern = kwargs.get('support_pattern')
if infill_density is not None:
command.extend(["--fill-density", f"{infill_density}%"])
if support_enable and support_enable != 'false':
command.append("--support-material")
if support_enable == 'buildplate':
command.append("--support-material-buildplate-only")
# PrusaSlicer equivalent for tree supports => organic
if support_pattern == 'tree':
command.extend(["--support-material-style", "organic"])
elif support_pattern:
pass # mapped to default/grid in prusa CLI without explicit config
else:
pass # Prusa defaults to no supports unless specified
env = os.environ.copy()
app.logger.info(f"Running command: {' '.join(command)}")
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
stdout, stderr = process.communicate()
if process.returncode == 0:
return True, None
else:
err_msg = stderr.decode() if stderr else "Unknown prusa-slicer error"
app.logger.error(f"PrusaSlicer Error: {err_msg}")
return False, err_msg
except Exception as e:
app.logger.error(f"PrusaSlicer Exception: {e}")
return False, str(e)