Do not try to slice on local slicers when printing

Since slicing takes a lot of resources, that might lead to quality loss for the print job.
This commit is contained in:
Gina Häußge 2014-10-21 14:14:00 +02:00
parent 6732710d6f
commit 71d73c6562
9 changed files with 54 additions and 27 deletions

View file

@ -194,11 +194,12 @@ class SlicerPlugin(Plugin):
def is_slicer_configured(self):
return False
def get_slicer_type(self):
return None
def get_slicer_name(self):
return None
def get_slicer_properties(self):
return dict(
type=None,
name=None,
same_device=True,
)
def get_slicer_profile_options(self):
return None

View file

@ -190,11 +190,12 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin,
cura_engine = s.get(["cura_engine"])
return cura_engine is not None and os.path.exists(cura_engine)
def get_slicer_type(self):
return "cura"
def get_slicer_name(self):
return "CuraEngine"
def get_slicer_properties(self):
return dict(
type="cura",
name="CuraEngine",
same_device=True
)
def get_slicer_default_profile(self):
path = s.get(["default_profile"])
@ -214,7 +215,8 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin,
description = profile_dict["_description"]
del profile_dict["_description"]
return octoprint.slicing.SlicingProfile(self.get_slicer_type(), "unknown", profile_dict, display_name=display_name, description=description)
properties = self.get_slicer_properties()
return octoprint.slicing.SlicingProfile(properties["type"], "unknown", profile_dict, display_name=display_name, description=description)
def save_slicer_profile(self, path, profile, allow_overwrite=True, overrides=None):
new_profile = Profile.merge_profile(profile.data, overrides=overrides)

View file

@ -301,14 +301,20 @@ def gcodeFileCommand(filename, target):
del data["slicer"]
if not slicer in slicingManager.registered_slicers:
return make_response("Slicer {slicer} is not available".format(**locals()), 400)
slicer_instance = slicingManager.get_slicer(slicer)
elif "cura" in slicingManager.registered_slicers:
slicer = "cura"
slicer_instance = slicingManager.get_slicer("cura")
else:
return make_response("Cannot slice {filename}, no slicer available".format(**locals()), 415)
if not octoprint.filemanager.valid_file_type(filename, type="stl"):
return make_response("Cannot slice {filename}, not an STL file".format(**locals()), 415)
if slicer_instance.get_slicer_properties()["same_device"] and (printer.isPrinting or printer.isPaused()):
# slicer runs on same device as OctoPrint, slicing while printing is hence disabled
return make_response("Cannot slice on {slicer} while printing due to performance reasons".format(**locals()), 409)
if "gcode" in data.keys() and data["gcode"]:
gcode_name = data["gcode"]
del data["gcode"]
@ -317,6 +323,11 @@ def gcodeFileCommand(filename, target):
name, _ = os.path.splitext(filename)
gcode_name = name + ".gco"
# prohibit overwriting the file that is currently being printed
currentOrigin, currentFilename = _getCurrentFile()
if currentFilename == gcode_name and currentOrigin == target and (printer.isPrinting() or printer.isPaused()):
make_response("Trying to slice into file that is currently being printed: %s" % gcode_name, 409)
if "profile" in data.keys() and data["profile"]:
profile = data["profile"]
del data["profile"]
@ -359,14 +370,8 @@ def deleteGcodeFile(filename, target):
if not _verifyFileExists(target, filename):
return make_response("File not found on '%s': %s" % (target, filename), 404)
currentJob = printer.getCurrentJob()
currentFilename = None
currentOrigin = None
if currentJob is not None and "file" in currentJob.keys() and "name" in currentJob["file"] and "origin" in currentJob["file"]:
currentFilename = currentJob["file"]["name"]
currentOrigin = currentJob["file"]["origin"]
# prohibit deleting the file that is currently being printed
currentOrigin, currentFilename = _getCurrentFile()
if currentFilename == filename and currentOrigin == target and (printer.isPrinting() or printer.isPaused()):
make_response("Trying to delete file that is currently being printed: %s" % filename, 409)
@ -382,3 +387,10 @@ def deleteGcodeFile(filename, target):
return NO_CONTENT
def _getCurrentFile():
currentJob = printer.getCurrentJob()
if currentJob is not None and "file" in currentJob.keys() and "name" in currentJob["file"] and "origin" in currentJob["file"]:
return currentJob["file"]["origin"], currentJob["file"]["name"]
else:
return None, None

View file

@ -22,7 +22,7 @@ def slicingListAll():
for slicer in slicingManager.registered_slicers:
result[slicer] = dict(
key=slicer,
displayName=slicingManager.get_slicer(slicer).get_slicer_name(),
displayName=slicingManager.get_slicer(slicer).get_slicer_properties()["name"],
default=default_slicer == slicer,
profiles=_getSlicingProfilesData(slicer)
)

View file

@ -69,7 +69,7 @@ class SlicingManager(object):
plugins = octoprint.plugin.plugin_manager().get_implementations(octoprint.plugin.SlicerPlugin)
for name, plugin in plugins.items():
if plugin.is_slicer_configured():
self._slicers[plugin.get_slicer_type()] = plugin
self._slicers[plugin.get_slicer_properties()["type"]] = plugin
@property
def slicing_enabled(self):
@ -106,7 +106,7 @@ class SlicingManager(object):
def slicer_worker(slicer, model_path, machinecode_path, profile_name, overrides, callback, callback_args, callback_kwargs):
try:
slicer_name = slicer.get_slicer_type()
slicer_name = slicer.get_slicer_properties()["type"]
with self.temporary_profile(slicer_name, name=profile_name, overrides=overrides) as profile_path:
ok, result = slicer.do_slice(
model_path,

View file

@ -261,6 +261,10 @@ function GcodeFilesViewModel(printerStateViewModel, loginStateViewModel, slicing
return isLoadActionPossible && !self.listHelper.isSelected(data);
};
self.enableSlicing = function(data) {
return self.loginState.isUser() && !(self.isPrinting() || self.isPaused());
};
self.enableAdditionalData = function(data) {
return data["gcodeAnalysis"] || data["prints"] && data["prints"]["last"];
};

View file

@ -237,7 +237,7 @@
<div class="size">{{ _('Size') }}: <span data-bind="text: formatSize(size)"></span></div>
<div class="btn-group action-buttons">
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableRemove($data)) { $root.removeFile($data); } else { return; } }, css: {disabled: !$root.enableRemove($data)}"><i class="icon-trash" title="{{ _('Remove') }}"></i></div>
<div class="btn btn-mini" data-bind="click: function() { $root.sliceFile($data); }"><i class="icon-magic" title="{{ _('Slice') }}"></i></div>
<div class="btn btn-mini" data-bind="click: function() { if ($root.enableSlicing($data)) { $root.sliceFile($data); } else { return; } }, css: {disabled: !$root.enableSlicing($data)}"><i class="icon-magic" title="{{ _('Slice') }}"></i></div>
</div>
</script>

View file

@ -137,12 +137,16 @@ class FileManagerTest(unittest.TestCase):
self.local_storage.add_file.side_effect = add_file
# mock slice method on slicing manager
def slice(slicer_name, source_path, dest_path, profile, done_cb, callback_args=None, overrides=None):
def slice(slicer_name, source_path, dest_path, profile, done_cb, callback_args=None, overrides=None, on_progress=None, on_progress_args=None, on_progress_kwargs=None):
self.assertEquals("some_slicer", slicer_name)
self.assertEquals("prefix/source.file", source_path)
self.assertEquals("tmp.file", dest_path)
self.assertIsNone(profile)
self.assertIsNone(overrides)
self.assertIsNotNone(on_progress)
self.assertIsNotNone(on_progress_args)
self.assertTupleEqual(("some_slicer", octoprint.filemanager.FileDestinations.LOCAL, "source.file", octoprint.filemanager.FileDestinations.LOCAL, "dest.file"), on_progress_args)
self.assertIsNone(on_progress_kwargs)
if not callback_args:
callback_args = ()
@ -199,12 +203,16 @@ class FileManagerTest(unittest.TestCase):
self.local_storage.get_absolute_path.side_effect = get_absolute_path
# mock slice method on slicing manager
def slice(slicer_name, source_path, dest_path, profile, done_cb, callback_args=None, overrides=None):
def slice(slicer_name, source_path, dest_path, profile, done_cb, callback_args=None, overrides=None, on_progress=None, on_progress_args=None, on_progress_kwargs=None):
self.assertEquals("some_slicer", slicer_name)
self.assertEquals("prefix/source.file", source_path)
self.assertEquals("tmp.file", dest_path)
self.assertIsNone(profile)
self.assertIsNone(overrides)
self.assertIsNotNone(on_progress)
self.assertIsNotNone(on_progress_args)
self.assertTupleEqual(("some_slicer", octoprint.filemanager.FileDestinations.LOCAL, "source.file", octoprint.filemanager.FileDestinations.LOCAL, "dest.file"), on_progress_args)
self.assertIsNone(on_progress_kwargs)
if not callback_args:
callback_args = ()

View file

@ -19,7 +19,7 @@ class TestSlicingManager(unittest.TestCase):
self.profile_path = tempfile.mkdtemp()
self.slicer_plugin = mock.MagicMock()
self.slicer_plugin.get_slicer_type.return_value = "mock"
self.slicer_plugin.get_slicer_properties.return_value = dict(type="mock", name="Mock", same_device=True)
# mock plugin manager
self.plugin_manager_patcher = mock.patch("octoprint.plugin.plugin_manager")
@ -45,7 +45,7 @@ class TestSlicingManager(unittest.TestCase):
def get_implementations(*types):
import octoprint.plugin
if octoprint.plugin.SlicerPlugin in types:
return dict(("slicer_" + plugin.get_slicer_type(), plugin) for plugin in plugins)
return dict(("slicer_" + plugin.get_slicer_properties()["type"], plugin) for plugin in plugins)
return dict()
self.plugin_manager.return_value.get_implementations.side_effect = get_implementations
@ -135,7 +135,7 @@ class TestSlicingManager(unittest.TestCase):
mock_thread.mock.start.assert_called_once()
# assert that slicer was called correctly
self.slicer_plugin.do_slice.assert_called_once_with("prefix/source.file", machinecode_path="prefix/dest.file", profile_path="tmp.file")
self.slicer_plugin.do_slice.assert_called_once_with("prefix/source.file", machinecode_path="prefix/dest.file", profile_path="tmp.file", on_progress=None, on_progress_args=None, on_progress_kwargs=None)
# assert that temporary profile was deleted again
mocked_os_remove.assert_called_once_with("tmp.file")