@@ -8,7 +8,7 @@ def get_all_engines():
|
||||
PrusaSlicerEngine()
|
||||
]
|
||||
|
||||
def get_slicer_engine(engine_name="cura"):
|
||||
def get_slicer_engine(engine_name="prusa", print_config_folder=None):
|
||||
"""
|
||||
Factory function to retrieve the requested slicing engine instance.
|
||||
Valid names: 'cura', 'prusa_slicer'
|
||||
@@ -16,9 +16,9 @@ def get_slicer_engine(engine_name="cura"):
|
||||
engine_name = engine_name.lower().strip()
|
||||
|
||||
if engine_name in ['cura', 'cura_engine', 'curaengine']:
|
||||
return CuraEngine()
|
||||
return CuraEngine(print_config_folder)
|
||||
elif engine_name in ['prusa', 'prusa_slicer', 'prusaslicer']:
|
||||
return PrusaSlicerEngine()
|
||||
return PrusaSlicerEngine(print_config_folder)
|
||||
else:
|
||||
# Default fallback
|
||||
return CuraEngine()
|
||||
return PrusaSlicerEngine(print_config_folder)
|
||||
|
||||
@@ -6,11 +6,12 @@ import configparser
|
||||
from app.utils.conf_parse import ConfParse
|
||||
|
||||
class CuraEngine:
|
||||
def __init__(self):
|
||||
def __init__(self, print_config_folder=None):
|
||||
self.name = "cura"
|
||||
self.display_name = "UltiMaker Cura"
|
||||
self.is_available = self._check_available()
|
||||
|
||||
self.print_config_folder = os.path.join(print_config_folder, "cura_engine") if print_config_folder else None
|
||||
|
||||
def _check_available(self):
|
||||
try:
|
||||
# check if CuraEngine is available in PATH
|
||||
@@ -21,13 +22,6 @@ class CuraEngine:
|
||||
self.display_name = "UltiMaker Cura"
|
||||
self.is_available = self._check_available()
|
||||
|
||||
def _check_available(self):
|
||||
try:
|
||||
# check if CuraEngine is available in PATH
|
||||
result = subprocess.run(["CuraEngine", "help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
return result.returncode == 0 or b"Usage:" in result.stdout or b"Usage:" in result.stderr
|
||||
except (FileNotFoundError, OSError):
|
||||
return False
|
||||
|
||||
def slice(self, app, stl_filepath, gcode_filepath, **kwargs):
|
||||
"""
|
||||
@@ -41,23 +35,28 @@ class CuraEngine:
|
||||
|
||||
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')
|
||||
printers_path = os.path.join(self.print_config_folder, 'printers') if self.print_config_folder else None
|
||||
extruders_path = os.path.join(self.print_config_folder, 'extruders') if self.print_config_folder else None
|
||||
materials_path = os.path.join(self.print_config_folder, 'materials') if self.print_config_folder else None
|
||||
presets_path = os.path.join(self.print_config_folder, 'quality') if self.print_config_folder else None
|
||||
variants_path = os.path.join(self.print_config_folder, 'variants') if self.print_config_folder else None
|
||||
|
||||
env = os.environ.copy()
|
||||
env["CURA_ENGINE_SEARCH_PATH"] = f"{printers_path}:{extruders_path}:{materials_path}:{presets_path}:{variants_path}"
|
||||
|
||||
|
||||
from app.models import SystemConfig
|
||||
db_printer = SystemConfig.query.filter_by(key='default_printer').first()
|
||||
p_val = db_printer.value if db_printer and db_printer.value else 'creality_ender3v3se.def.json'
|
||||
if not p_val.endswith('.def.json'): p_val += '.def.json'
|
||||
|
||||
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")
|
||||
os.path.join(printers_path, p_val)
|
||||
]
|
||||
|
||||
|
||||
inst_files_list = []
|
||||
quality_type = None
|
||||
@@ -82,19 +81,19 @@ class CuraEngine:
|
||||
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")
|
||||
v_path = os.path.join(variants_path, "creality", f"{p_val.replace('.def.json', '')}_{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)
|
||||
t_path = os.path.join(self.print_config_folder, 'supports', 'tree.inst.cfg') if self.print_config_folder else None
|
||||
if t_path and 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)
|
||||
n_path = os.path.join(self.print_config_folder, 'supports', 'normal.inst.cfg') if self.print_config_folder else None
|
||||
if n_path and 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)
|
||||
g_path = os.path.join(self.print_config_folder, 'creality', 'globals', f"{quality_type}.inst.cfg") if self.print_config_folder else None
|
||||
if g_path and 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)
|
||||
@@ -192,10 +191,10 @@ class CuraEngine:
|
||||
except Exception as e:
|
||||
app.logger.error(f"Failed to delete temp JSON config {tmp_def_path}: {e}")
|
||||
|
||||
def get_quality_presets(self, app):
|
||||
def get_quality_presets(self):
|
||||
try:
|
||||
path = os.path.join(app.root_path, '..', 'print_config', 'cura_engine', 'quality', 'creality', 'presets')
|
||||
if not os.path.exists(path): return []
|
||||
path = os.path.join(self.print_config_folder, 'quality', 'creality', 'presets') if self.print_config_folder else None
|
||||
if not path or not os.path.exists(path): return []
|
||||
files = [f for f in os.listdir(path) if f.endswith('.inst.cfg')]
|
||||
presets = []
|
||||
for f in files:
|
||||
@@ -205,7 +204,7 @@ class CuraEngine:
|
||||
except:
|
||||
return []
|
||||
|
||||
def get_support_patterns(self, app):
|
||||
def get_support_patterns(self):
|
||||
return [
|
||||
{'id': 'tree', 'name': 'Tree'},
|
||||
{'id': 'lines', 'name': 'Lines'},
|
||||
@@ -219,10 +218,10 @@ class CuraEngine:
|
||||
{'id': 'octagon', 'name': 'Octagon'}
|
||||
]
|
||||
|
||||
def get_materials(self, app):
|
||||
def get_materials(self):
|
||||
try:
|
||||
path = os.path.join(app.root_path, '..', 'print_config', 'cura_engine', 'materials')
|
||||
if not os.path.exists(path): return []
|
||||
path = os.path.join(self.print_config_folder, 'materials') if self.print_config_folder else None
|
||||
if not path or not os.path.exists(path): return []
|
||||
files = [f for f in os.listdir(path) if f.endswith('.inst.cfg')]
|
||||
materials = []
|
||||
for f in files:
|
||||
@@ -231,3 +230,33 @@ class CuraEngine:
|
||||
return materials
|
||||
except:
|
||||
return []
|
||||
|
||||
def get_bed_dimensions(self):
|
||||
from app.models import SystemConfig
|
||||
import json
|
||||
try:
|
||||
db_printer = SystemConfig.query.filter_by(key='default_printer').first()
|
||||
p_val = db_printer.value if db_printer and db_printer.value else 'creality_ender3v3se.def.json'
|
||||
if not p_val.endswith('.def.json'): p_val += '.def.json'
|
||||
path = os.path.join(self.print_config_folder, 'printers', p_val)
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
w = data['overrides']['machine_width']['default_value']
|
||||
h = data['overrides']['machine_depth']['default_value']
|
||||
hd = data['overrides']['machine_height']['default_value']
|
||||
return w, h, hd
|
||||
except:
|
||||
pass
|
||||
return 220, 220, 250
|
||||
def get_all_printers(self):
|
||||
try:
|
||||
path = os.path.join(self.print_config_folder, 'printers') if self.print_config_folder else None
|
||||
if not path or not os.path.exists(path): return []
|
||||
files = [f for f in os.listdir(path) if f.endswith('.inst.cfg')]
|
||||
printers = []
|
||||
for f in files:
|
||||
printers.append({'id': f, 'name': f.replace('..def.json', '').replace('generic_', 'Generic ').replace('_', ' ').title()})
|
||||
printers.sort(key=lambda x: x['name'])
|
||||
return printers
|
||||
except:
|
||||
return []
|
||||
@@ -4,11 +4,12 @@ import configparser
|
||||
import uuid
|
||||
|
||||
class PrusaSlicerEngine:
|
||||
def __init__(self):
|
||||
def __init__(self, print_config_folder=None):
|
||||
self.name = "prusa_slicer"
|
||||
self.display_name = "PrusaSlicer"
|
||||
self.is_available = self._check_available()
|
||||
|
||||
self.print_config_folder = os.path.join(print_config_folder, 'prusa_slicer') if print_config_folder else None
|
||||
|
||||
def _check_available(self):
|
||||
try:
|
||||
result = subprocess.run(["prusa-slicer", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
@@ -47,18 +48,22 @@ class PrusaSlicerEngine:
|
||||
# print(support_pattern)
|
||||
all_configs = {}
|
||||
|
||||
printer_ini = os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', 'printers', 'Ender3_V3_SE.ini')
|
||||
if os.path.exists(printer_ini):
|
||||
from app.models import SystemConfig
|
||||
db_printer = SystemConfig.query.filter_by(key='default_printer').first()
|
||||
p_val = db_printer.value if db_printer and db_printer.value else 'Ender3_V3_SE'
|
||||
if not p_val.endswith('.ini'): p_val += '.ini'
|
||||
printer_ini = os.path.join(self.print_config_folder, 'printers', p_val) if self.print_config_folder else None
|
||||
if printer_ini and os.path.exists(printer_ini):
|
||||
self.add_ini_keys(printer_ini, 'settings', all_configs)
|
||||
|
||||
if quality_preset:
|
||||
q_ini = os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', 'quality', f"{quality_preset}.ini")
|
||||
if os.path.exists(q_ini):
|
||||
q_ini = os.path.join(self.print_config_folder, 'quality', f"{quality_preset}.ini") if self.print_config_folder else None
|
||||
if q_ini and os.path.exists(q_ini):
|
||||
self.add_ini_keys(q_ini, 'settings', all_configs)
|
||||
|
||||
if material_preset:
|
||||
m_ini = os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', 'materials', f"{material_preset}.ini")
|
||||
if os.path.exists(m_ini):
|
||||
m_ini = os.path.join(self.print_config_folder, 'materials', f"{material_preset}.ini") if self.print_config_folder else None
|
||||
if m_ini and os.path.exists(m_ini):
|
||||
self.add_ini_keys(m_ini, 'settings', all_configs)
|
||||
|
||||
if infill_density is not None:
|
||||
@@ -69,20 +74,19 @@ class PrusaSlicerEngine:
|
||||
if support_enable == 'buildplate':
|
||||
command.append("--support-material-buildplate-only")
|
||||
# PrusaSlicer equivalent for tree supports => organic
|
||||
support_pattern_ini = os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', 'supports', f'{support_pattern}.ini')
|
||||
if os.path.exists(support_pattern_ini):
|
||||
support_pattern_ini = os.path.join(self.print_config_folder, 'supports', f'{support_pattern}.ini') if self.print_config_folder else None
|
||||
if support_pattern_ini and os.path.exists(support_pattern_ini):
|
||||
self.add_ini_keys(support_pattern_ini, 'settings', all_configs)
|
||||
else:
|
||||
# Load the default no_support.ini if no support is enabled
|
||||
no_support_ini = os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', 'supports', 'no_support.ini')
|
||||
if os.path.exists(no_support_ini):
|
||||
no_support_ini = os.path.join(self.print_config_folder, 'supports', 'no_support.ini') if self.print_config_folder else None
|
||||
if no_support_ini and os.path.exists(no_support_ini):
|
||||
self.add_ini_keys(no_support_ini, 'settings', all_configs)
|
||||
else:
|
||||
all_configs['support_material'] = '0'
|
||||
|
||||
tmp_ini_filename = f"tmp_{uuid.uuid4().hex}.ini"
|
||||
tmp_ini_path = os.path.join(app.config['UPLOAD_FOLDER'], tmp_ini_filename)
|
||||
print(f'****tmp_ini_path: {tmp_ini_path}')
|
||||
with open(tmp_ini_path, 'w') as f:
|
||||
for key, value in all_configs.items():
|
||||
f.write(f"{key} = {value}\n")
|
||||
@@ -108,11 +112,11 @@ class PrusaSlicerEngine:
|
||||
app.logger.error(f"PrusaSlicer Exception: {e}")
|
||||
return False, str(e)
|
||||
|
||||
def get_quality_presets(self, app):
|
||||
all_files = [f for f in os.listdir(os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer',"quality")) if f.endswith('.ini')]
|
||||
def get_quality_presets(self):
|
||||
all_files = [f for f in os.listdir(os.path.join(self.print_config_folder, "quality")) if f.endswith('.ini')] if self.print_config_folder else []
|
||||
quality_presets = []
|
||||
for file in all_files:
|
||||
with open(os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', "quality", file), 'r') as f:
|
||||
with open(os.path.join(self.print_config_folder, "quality", file), 'r') as f:
|
||||
config = configparser.ConfigParser()
|
||||
config.read_file(f)
|
||||
if 'metadata' in config:
|
||||
@@ -122,11 +126,11 @@ class PrusaSlicerEngine:
|
||||
})
|
||||
return quality_presets
|
||||
|
||||
def get_support_patterns(self, app):
|
||||
all_files = [f for f in os.listdir(os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer',"supports")) if f.endswith('.ini')]
|
||||
def get_support_patterns(self):
|
||||
all_files = [f for f in os.listdir(os.path.join(self.print_config_folder,"supports")) if f.endswith('.ini')] if self.print_config_folder else []
|
||||
support_presets = []
|
||||
for file in all_files:
|
||||
with open(os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', "supports", file), 'r') as f:
|
||||
with open(os.path.join(self.print_config_folder, "supports", file), 'r') as f:
|
||||
config = configparser.ConfigParser()
|
||||
config.read_file(f)
|
||||
if 'metadata' in config:
|
||||
@@ -136,15 +140,54 @@ class PrusaSlicerEngine:
|
||||
})
|
||||
return support_presets
|
||||
|
||||
def get_materials(self, app):
|
||||
def get_materials(self):
|
||||
all_files = [f for f in os.listdir(os.path.join(self.print_config_folder, "materials")) if f.endswith('.ini')] if self.print_config_folder else []
|
||||
materials = []
|
||||
for file in all_files:
|
||||
with open(os.path.join(self.print_config_folder, "materials", file), 'r') as f:
|
||||
config = configparser.ConfigParser()
|
||||
config.read_file(f)
|
||||
if 'metadata' in config:
|
||||
materials.append({
|
||||
'id': file.replace('.ini', ''),
|
||||
'name': config['metadata'].get('show_name', file.replace('.ini', '').replace('_', ' '))
|
||||
})
|
||||
return materials
|
||||
|
||||
|
||||
def get_bed_dimensions(self):
|
||||
from app.models import SystemConfig
|
||||
import configparser
|
||||
try:
|
||||
path = os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', 'materials')
|
||||
if not os.path.exists(path): return []
|
||||
files = [f for f in os.listdir(path) if f.endswith('.ini')]
|
||||
materials = []
|
||||
for f in files:
|
||||
materials.append({'id': f.replace('.ini', ''), 'name': f.replace('.ini', '').replace('_', ' ')})
|
||||
materials.sort(key=lambda x: x['name'])
|
||||
return materials
|
||||
db_printer = SystemConfig.query.filter_by(key='default_printer').first()
|
||||
p_val = db_printer.value if db_printer and db_printer.value else 'Ender3_V3_SE.ini'
|
||||
if not p_val.endswith('.ini'): p_val += '.ini'
|
||||
path = os.path.join(self.print_config_folder, 'printers', p_val)
|
||||
config = configparser.ConfigParser()
|
||||
config.read(path)
|
||||
if 'settings' in config and 'bed_shape' in config['settings']:
|
||||
# format is usually like 0x0,220x0,220x220,0x220
|
||||
coords = config['settings']['bed_shape'].split(',')
|
||||
max_x = max([float(c.split('x')[0]) for c in coords])
|
||||
max_y = max([float(c.split('x')[1]) for c in coords])
|
||||
# height
|
||||
h = 250
|
||||
if 'max_print_height' in config['settings']:
|
||||
h = float(config['settings']['max_print_height'])
|
||||
return max_x, max_y, h
|
||||
except:
|
||||
return []
|
||||
pass
|
||||
return 220, 220, 250
|
||||
def get_all_printers(self):
|
||||
all_files = [f for f in os.listdir(os.path.join(self.print_config_folder, "printers")) if f.endswith('.ini')] if self.print_config_folder else []
|
||||
printers = []
|
||||
for file in all_files:
|
||||
with open(os.path.join(self.print_config_folder, "printers", file), 'r') as f:
|
||||
config = configparser.ConfigParser()
|
||||
config.read_file(f)
|
||||
if 'metadata' in config:
|
||||
printers.append({
|
||||
'id': file.replace('.ini', ''),
|
||||
'name': config['metadata'].get('show_name', file.replace('.ini', '').replace('_', ' '))
|
||||
})
|
||||
return printers
|
||||
|
||||
@@ -6,7 +6,7 @@ from app.utils.conf_parse import ConfParse
|
||||
import json
|
||||
import uuid
|
||||
import configparser
|
||||
|
||||
from app.utils.slice_engines import get_slicer_engine
|
||||
|
||||
import os
|
||||
|
||||
@@ -44,17 +44,15 @@ def slice_stl_task(file_id, stl_filepath, quality_preset=None, material_preset=N
|
||||
|
||||
# Remove DB session to avoid locking the sqlite db during long slicing operations
|
||||
db.session.remove()
|
||||
|
||||
from app.utils.slice_engines import get_slicer_engine
|
||||
|
||||
|
||||
try:
|
||||
# Optionally fetch the preferred engine from db conf or just default to cura
|
||||
# For now default to cura or whichever is passed via kwargs if implemented later
|
||||
# Optionally fetch the preferred engine from db conf or just default to prusa
|
||||
# For now default to prusa or whichever is passed via kwargs if implemented later
|
||||
conf_engine = SystemConfig.query.filter_by(key='slicer_engine').first()
|
||||
engine_name = conf_engine.value if conf_engine and conf_engine.value else "cura"
|
||||
engine_name = conf_engine.value if conf_engine and conf_engine.value else "prusa"
|
||||
db.session.remove()
|
||||
|
||||
slicer = get_slicer_engine(engine_name)
|
||||
slicer = get_slicer_engine(engine_name,app.config['PRINT_CONFIG_FOLDER'])
|
||||
|
||||
success, err_msg = slicer.slice(
|
||||
app=app,
|
||||
|
||||
Reference in New Issue
Block a user