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:
parent
6732710d6f
commit
71d73c6562
9 changed files with 54 additions and 27 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = ()
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in a new issue