166 lines
6.2 KiB
Python
166 lines
6.2 KiB
Python
import subprocess
|
|
import os
|
|
import json
|
|
import uuid
|
|
import configparser
|
|
from huey import SqliteHuey
|
|
from app import create_app
|
|
from app.models import db, PrintFile, SystemConfig
|
|
from app.utils.conf_parse import ConfParse
|
|
from app.utils.slice_engines import get_slicer_engine
|
|
from app.utils.stl_merger import merge_stls
|
|
from app.utils.stl_simplifier import simplify_stl
|
|
|
|
|
|
# Ensure instance directory exists
|
|
instance_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), '..', 'instance')
|
|
os.makedirs(instance_dir, exist_ok=True)
|
|
huey_db_path = os.path.join(instance_dir, 'huey_queue.db')
|
|
|
|
huey = SqliteHuey(filename=huey_db_path)
|
|
|
|
def get_gcode_dir(app):
|
|
with app.app_context():
|
|
conf = SystemConfig.query.filter_by(key='gcode_upload_folder').first()
|
|
if conf and conf.value and os.path.exists(conf.value):
|
|
return conf.value
|
|
return app.config['UPLOAD_FOLDER']
|
|
|
|
|
|
@huey.task()
|
|
def slice_stl_task(file_id, stl_filepath, quality_preset=None, material_preset=None, infill_density=None, support_enable=None, support_pattern=None, delete_stl=False):
|
|
# This is run by the Huey worker
|
|
# We need to create an app context to interact with the database
|
|
app = create_app()
|
|
with app.app_context():
|
|
print_file = PrintFile.query.get(file_id)
|
|
if not print_file:
|
|
return
|
|
|
|
# Cache variables and commit slicing status
|
|
gcode_filename = print_file.filename.rsplit('.', 1)[0] + '.gcode'
|
|
gcode_filepath = os.path.join(get_gcode_dir(app), gcode_filename)
|
|
print_file.status = 'slicing'
|
|
db.session.commit()
|
|
|
|
# Remove DB session to avoid locking the sqlite db during long slicing operations
|
|
db.session.remove()
|
|
|
|
try:
|
|
# 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 "prusa"
|
|
db.session.remove()
|
|
|
|
slicer = get_slicer_engine(engine_name,app.config['PRINT_CONFIG_FOLDER'])
|
|
|
|
success, err_msg = slicer.slice(
|
|
app=app,
|
|
stl_filepath=stl_filepath,
|
|
gcode_filepath=gcode_filepath,
|
|
quality_preset=quality_preset,
|
|
material_preset=material_preset,
|
|
infill_density=infill_density,
|
|
support_enable=support_enable,
|
|
support_pattern=support_pattern
|
|
)
|
|
|
|
# Re-fetch print_file and update status
|
|
print_file = PrintFile.query.get(file_id)
|
|
if not print_file:
|
|
return
|
|
|
|
if success:
|
|
print_file.status = 'sliced'
|
|
else:
|
|
print_file.status = 'failed'
|
|
app.logger.error(f"Slicing Task Failed: {err_msg}")
|
|
|
|
except Exception as e:
|
|
print_file = PrintFile.query.get(file_id)
|
|
if print_file:
|
|
print_file.status = 'failed'
|
|
app.logger.error(f"Subprocess Exception: {e}")
|
|
|
|
finally:
|
|
if delete_stl and os.path.exists(stl_filepath):
|
|
try:
|
|
os.remove(stl_filepath)
|
|
except Exception as e:
|
|
app.logger.error(f"Failed to delete temp STL {stl_filepath}: {e}")
|
|
|
|
db.session.commit()
|
|
db.session.remove()
|
|
|
|
|
|
@huey.task()
|
|
def merge_and_slice_task(file_id, inputs, merged_filepath, quality_preset=None, material_preset=None, infill_density=None, support_enable=None, support_pattern=None, delete_stl=False):
|
|
app = create_app()
|
|
with app.app_context():
|
|
print_file = PrintFile.query.get(file_id)
|
|
if not print_file:
|
|
return
|
|
|
|
db.session.remove()
|
|
|
|
try:
|
|
merge_stls(inputs, merged_filepath)
|
|
|
|
# Now trigger the regular slicing task
|
|
# We can just call the slicing logic or enqueue it
|
|
slice_stl_task(file_id, merged_filepath, quality_preset, material_preset, infill_density, support_enable, support_pattern, delete_stl=delete_stl)
|
|
except Exception as e:
|
|
print_file = PrintFile.query.get(file_id)
|
|
if print_file:
|
|
print_file.status = 'failed'
|
|
db.session.commit()
|
|
app.logger.error(f"Merge Exception: {e}")
|
|
finally:
|
|
db.session.remove()
|
|
|
|
@huey.task()
|
|
def simplify_stl_task(file_id, filepath):
|
|
app = create_app()
|
|
with app.app_context():
|
|
print_file = PrintFile.query.get(file_id)
|
|
if not print_file:
|
|
return
|
|
|
|
try:
|
|
file_size_mb = os.path.getsize(filepath) / (1024 * 1024)
|
|
|
|
configs = {c.key: c.value for c in SystemConfig.query.all()}
|
|
skip_size = float(configs.get('proxy_skip_size_mb', '5.0'))
|
|
|
|
proxy_path = filepath + '.proxy.stl'
|
|
|
|
if file_size_mb <= skip_size:
|
|
# File is small enough, no proxy needed
|
|
print_file.status = 'uploaded'
|
|
db.session.commit()
|
|
return
|
|
|
|
# Aim for approx 7.5 MB for the proxy
|
|
target_mb = 7.5
|
|
keep_ratio = target_mb / file_size_mb
|
|
if keep_ratio > 1.0:
|
|
keep_ratio = 1.0
|
|
elif keep_ratio < 0.01:
|
|
keep_ratio = 0.01
|
|
|
|
app.logger.info(f"Simplifying {filepath}... Size: {file_size_mb:.2f}MB, Target Ratio: {keep_ratio:.3f}")
|
|
|
|
simplify_stl(filepath, proxy_path, keep_ratio=keep_ratio)
|
|
|
|
except Exception as e:
|
|
app.logger.error(f"Simplify task error: {e}")
|
|
|
|
# Update status to uploaded regardless of success or failure of proxy generation
|
|
# So the user can still slice or download it
|
|
print_file = PrintFile.query.get(file_id)
|
|
if print_file:
|
|
print_file.status = 'uploaded'
|
|
db.session.commit()
|
|
db.session.remove()
|