From f92ff3e864c209e33ed7987f6701a51732c06410 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Mon, 25 Apr 2016 15:53:02 +0200 Subject: [PATCH 1/3] Only talk to the CuraEngine socket from the same (Main) thread, and be a lot more careful about handling the StartSliceJob when restarting CuraEngine. Fixes CURA-1434 --- .../CuraEngineBackend/CuraEngineBackend.py | 22 ++- plugins/CuraEngineBackend/StartSliceJob.py | 128 +++++++++--------- 2 files changed, 83 insertions(+), 67 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index f61e3fde81..09817aa995 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -74,6 +74,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 @@ -153,14 +154,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") @@ -174,13 +180,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 c1d7932302..f4f096f54c 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -27,87 +27,94 @@ class GcodeStartEndFormatter(Formatter): Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end gcode", key) return "{" + str(key) + "}" -## Job class 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, sliceMessage, settingsMessage): super().__init__() self._scene = Application.getInstance().getController().getScene() self._profile = profile - self._socket = socket + self._slice_message = sliceMessage + self._settings_message = settingsMessage + 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 = [] - - 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) - slice_message = self._socket.createMessage("cura.proto.Slice") + verts = numpy.array(mesh_data.getVertices()) + verts[:,[1,2]] = verts[:,[2,1]] + verts[:,1] *= -1 - 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 object in group: - mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation()) + obj.vertices = verts - obj = group_message.addRepeatedMessage("objects") - obj.id = id(object) + self._handlePerObjectSettings(object, obj) - verts = numpy.array(mesh_data.getVertices()) - verts[:,[1,2]] = verts[:,[2,1]] - verts[:,1] *= -1 + Job.yieldThread() - obj.vertices = verts - - self._handlePerObjectSettings(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 @@ -117,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: From 4d11bdd366fe2dc56ac078df60fce0aa22ffcc79 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 26 Apr 2016 12:09:11 +0200 Subject: [PATCH 2/3] Changed height CURA-1429 --- resources/machines/ultimaker2_extended_plus.json | 2 +- resources/machines/ultimaker2plus.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) 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 0a344d4aa6..ce9ea57f26 100644 --- a/resources/machines/ultimaker2plus.json +++ b/resources/machines/ultimaker2plus.json @@ -18,7 +18,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 }, From dff976f1975b066a6fa39aa25b37210258c3f664 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Tue, 26 Apr 2016 15:20:29 +0200 Subject: [PATCH 3/3] Minor code style fix. Contributes to CURA-1434 --- plugins/CuraEngineBackend/StartSliceJob.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index f4f096f54c..597a7c7da4 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -29,13 +29,13 @@ class GcodeStartEndFormatter(Formatter): ## Job class that builds up the message of scene data to send to CuraEngine. class StartSliceJob(Job): - def __init__(self, profile, sliceMessage, settingsMessage): + def __init__(self, profile, slice_message, settings_message): super().__init__() self._scene = Application.getInstance().getController().getScene() self._profile = profile - self._slice_message = sliceMessage - self._settings_message = settingsMessage + self._slice_message = slice_message + self._settings_message = settings_message self._is_cancelled = False def getSettingsMessage(self):