diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 7a5d332d9e..c5b38034b5 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -71,6 +71,7 @@ class CuraEngineBackend(Backend): self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage self._slicing = False + self._start_slice_job = None self._restart = False self._enabled = True self._always_restart = True @@ -155,14 +156,19 @@ class CuraEngineBackend(Backend): self._slicing = True self.slicingStarted.emit() - job = StartSliceJob.StartSliceJob(self._profile, self._socket) - job.start() - job.finished.connect(self._onStartSliceCompleted) + slice_message = self._socket.createMessage("cura.proto.Slice") + settings_message = self._socket.createMessage("cura.proto.SettingList"); + self._start_slice_job = StartSliceJob.StartSliceJob(self._profile, slice_message, settings_message) + self._start_slice_job.start() + self._start_slice_job.finished.connect(self._onStartSliceCompleted) def _terminate(self): self._slicing = False self._restart = True self._stored_layer_data = [] + if self._start_slice_job is not None: + self._start_slice_job.cancel() + self.slicingCancelled.emit() self.processingProgress.emit(0) Logger.log("d", "Attempting to kill the engine process") @@ -176,13 +182,19 @@ class CuraEngineBackend(Backend): except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this. Logger.log("d", "Exception occured while trying to kill the engine %s", str(e)) - def _onStartSliceCompleted(self, job): - if job.getError() or job.getResult() != True: + # Note that cancelled slice jobs can still call this method. + if self._start_slice_job is job: + self._start_slice_job = None + if job.isCancelled() or job.getError() or job.getResult() != True: if self._message: self._message.hide() self._message = None return + else: + # Preparation completed, send it to the backend. + self._socket.sendMessage(job.getSettingsMessage()) + self._socket.sendMessage(job.getSliceMessage()) def _onSceneChanged(self, source): if type(source) is not SceneNode: diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index f51cff5d93..174f453bf8 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -28,89 +28,93 @@ class GcodeStartEndFormatter(Formatter): Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end gcode", key) return "{" + str(key) + "}" - -## Job that handles sending the current scene data to CuraEngine +## Job class that builds up the message of scene data to send to CuraEngine. class StartSliceJob(Job): - def __init__(self, profile, socket): + def __init__(self, profile, slice_message, settings_message): super().__init__() self._scene = Application.getInstance().getController().getScene() self._profile = profile - self._socket = socket + self._slice_message = slice_message + self._settings_message = settings_message + self._is_cancelled = False + + def getSettingsMessage(self): + return self._settings_message + + def getSliceMessage(self): + return self._slice_message def run(self): - self._scene.acquireLock() + with self._scene.getSceneLock(): + for node in DepthFirstIterator(self._scene.getRoot()): + if node.callDecoration("getLayerData"): + node.getParent().removeChild(node) + break - for node in DepthFirstIterator(self._scene.getRoot()): - if node.callDecoration("getLayerData"): - node.getParent().removeChild(node) - break + object_groups = [] + if self._profile.getSettingValue("print_sequence") == "one_at_a_time": + for node in OneAtATimeIterator(self._scene.getRoot()): + temp_list = [] - object_groups = [] - if self._profile.getSettingValue("print_sequence") == "one_at_a_time": - for node in OneAtATimeIterator(self._scene.getRoot()): + if getattr(node, "_outside_buildarea", False): + continue + + children = node.getAllChildren() + children.append(node) + for child_node in children: + if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None: + temp_list.append(child_node) + + if temp_list: + object_groups.append(temp_list) + Job.yieldThread() + if len(object_groups) == 0: + Logger.log("w", "No objects suitable for one at a time found, or no correct order found") + else: temp_list = [] - - ## Node can't be printed, so don't bother sending it. - if getattr(node, "_outside_buildarea", False): - continue - - children = node.getAllChildren() - children.append(node) - for child_node in children: - if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None: - temp_list.append(child_node) + for node in DepthFirstIterator(self._scene.getRoot()): + if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: + if not getattr(node, "_outside_buildarea", False): + temp_list.append(node) + Job.yieldThread() if temp_list: object_groups.append(temp_list) - Job.yieldThread() - if len(object_groups) == 0: - Logger.log("w", "No objects suitable for one at a time found, or no correct order found") - else: - temp_list = [] - for node in DepthFirstIterator(self._scene.getRoot()): - if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: - if not getattr(node, "_outside_buildarea", False): - temp_list.append(node) - Job.yieldThread() - if temp_list: - object_groups.append(temp_list) + if not object_groups: + return - self._scene.releaseLock() + self._buildSettingsMessage(self._profile) - if not object_groups: - return + for group in object_groups: + group_message = self._slice_message.addRepeatedMessage("object_lists") + if group[0].getParent().callDecoration("isGroup"): + self._handlePerObjectSettings(group[0].getParent(), group_message) + for object in group: + mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation()) - self._sendSettings(self._profile) + obj = group_message.addRepeatedMessage("objects") + obj.id = id(object) + verts = numpy.array(mesh_data.getVertices()) + verts[:,[1,2]] = verts[:,[2,1]] + verts[:,1] *= -1 - slice_message = self._socket.createMessage("cura.proto.Slice") + obj.vertices = verts - for group in object_groups: - group_message = slice_message.addRepeatedMessage("object_lists") - if group[0].getParent().callDecoration("isGroup"): - self._handlePerObjectSettings(group[0].getParent(), group_message) - for current_object in group: - mesh_data = current_object.getMeshData().getTransformed(current_object.getWorldTransformation()) + self._handlePerObjectSettings(object, obj) - obj = group_message.addRepeatedMessage("objects") - obj.id = id(current_object) + Job.yieldThread() - verts = numpy.array(mesh_data.getVertices()) - verts[:,[1,2]] = verts[:,[2,1]] - verts[:,1] *= -1 - - obj.vertices = verts - - self._handlePerObjectSettings(current_object, obj) - - Job.yieldThread() - - Logger.log("d", "Sending data to engine for slicing.") - self._socket.sendMessage(slice_message) - Logger.log("d", "Sending data to engine is completed") self.setResult(True) + def cancel(self): + super().cancel() + self._is_cancelled = True + + def isCancelled(self): + return self._is_cancelled + def _expandGcodeTokens(self, key, value, settings): try: # any setting can be used as a token @@ -120,22 +124,19 @@ class StartSliceJob(Job): Logger.log("w", "Unabled to do token replacement on start/end gcode %s", traceback.format_exc()) return str(value).encode("utf-8") - def _sendSettings(self, profile): - msg = self._socket.createMessage("cura.proto.SettingList") + def _buildSettingsMessage(self, profile): settings = profile.getAllSettingValues(include_machine = True) start_gcode = settings["machine_start_gcode"] settings["material_bed_temp_prepend"] = "{material_bed_temperature}" not in start_gcode settings["material_print_temp_prepend"] = "{material_print_temperature}" not in start_gcode for key, value in settings.items(): - s = msg.addRepeatedMessage("settings") + s = self._settings_message.addRepeatedMessage("settings") s.name = key if key == "machine_start_gcode" or key == "machine_end_gcode": s.value = self._expandGcodeTokens(key, value, settings) else: s.value = str(value).encode("utf-8") - self._socket.sendMessage(msg) - def _handlePerObjectSettings(self, node, message): profile = node.callDecoration("getProfile") if profile: diff --git a/resources/machines/ultimaker2_extended_plus.json b/resources/machines/ultimaker2_extended_plus.json index 318361a894..34a1f894bb 100644 --- a/resources/machines/ultimaker2_extended_plus.json +++ b/resources/machines/ultimaker2_extended_plus.json @@ -11,7 +11,7 @@ "inherits": "ultimaker2plus.json", "machine_settings": { - "machine_height": { "default": 313 }, + "machine_height": { "default": 305}, "machine_show_variants": { "default": true }, "machine_nozzle_head_distance": { "default": 5 }, "machine_nozzle_expansion_angle": { "default": 45 } diff --git a/resources/machines/ultimaker2plus.json b/resources/machines/ultimaker2plus.json index cc86bd8ce3..d9a1fdf22d 100644 --- a/resources/machines/ultimaker2plus.json +++ b/resources/machines/ultimaker2plus.json @@ -17,7 +17,6 @@ "line_width": { "inherit_function": "round(machine_nozzle_size * 0.875, 2)" }, "speed_layer_0": { "default": "20" }, "speed_support": { "inherit_function": "speed_wall_0" }, - "machine_height": { "default": 203 }, "machine_show_variants": { "default": true }, "gantry_height": { "default": 52 }, "machine_nozzle_head_distance": { "default": 5 },