예제 #1
0
	def is_slicer_configured(self):
		cura_engine = normalize_path(self._settings.get(["cura_engine"]))
		if self._is_engine_configured(cura_engine=cura_engine):
			return True
		else:
			self._logger.info("Path to CuraEngine has not been configured yet or does not exist (currently set to %r), Cura will not be selectable for slicing" % cura_engine)
			return False
예제 #2
0
 def is_slicer_configured(self):
     cura_engine = normalize_path(self._settings.get(["cura_engine"]))
     if self._is_engine_configured(cura_engine=cura_engine):
         return True
     else:
         self._logger.info(
             "Path to CuraEngine has not been configured yet or does not exist (currently set to %r), Cura will not be selectable for slicing"
             % cura_engine)
         return False
예제 #3
0
    def do_slice(self,
                 model_path,
                 printer_profile,
                 machinecode_path=None,
                 profile_path=None,
                 position=None,
                 on_progress=None,
                 on_progress_args=None,
                 on_progress_kwargs=None):
        if not profile_path:
            profile_path = self._settings.get(["default_profile"])
        if not machinecode_path:
            path, _ = os.path.splitext(model_path)
            machinecode_path = path + ".gco"

        if position and isinstance(
                position, dict) and "x" in position and "y" in position:
            posX = position["x"]
            posY = position["y"]
        elif printer_profile["volume"][
                "formFactor"] == "circular" or printer_profile["volume"][
                    "origin"] == "center":
            posX = 0
            posY = 0
        else:
            posX = printer_profile["volume"]["width"] / 2.0
            posY = printer_profile["volume"]["depth"] / 2.0

        self._slic3r_logger.info(
            "### Slicing %s to %s using profile stored at %s" %
            (model_path, machinecode_path, profile_path))

        executable = normalize_path(self._settings.get(["slic3r_engine"]))
        if not executable:
            return False, "Path to Slic3r is not configured "

        import sarge

        working_dir, _ = os.path.split(executable)
        args = [
            '"%s"' % executable, '--load',
            '"%s"' % profile_path, '--print-center',
            '"%f,%f"' % (posX, posY), '-o',
            '"%s"' % machinecode_path,
            '"%s"' % model_path
        ]

        command = " ".join(args)
        self._logger.info("Running %r in %s" % (command, working_dir))
        try:
            p = sarge.run(command,
                          cwd=working_dir,
                          async=True,
                          stdout=sarge.Capture(),
                          stderr=sarge.Capture())
            p.wait_events()
            try:
                with self._slicing_commands_mutex:
                    self._slicing_commands[machinecode_path] = p.commands[0]

                line_seen = False
                while p.returncode is None:
                    stdout_line = p.stdout.readline(timeout=0.5)
                    stderr_line = p.stderr.readline(timeout=0.5)

                    if not stdout_line and not stderr_line:
                        if line_seen:
                            break
                        else:
                            continue

                    line_seen = True
                    if stdout_line:
                        self._slic3r_logger.debug("stdout: " +
                                                  stdout_line.strip())
                    if stderr_line:
                        self._slic3r_logger.debug("stderr: " +
                                                  stderr_line.strip())
            finally:
                p.close()

            with self._cancelled_jobs_mutex:
                if machinecode_path in self._cancelled_jobs:
                    self._slic3r_logger.info("### Cancelled")
                    raise octoprint.slicing.SlicingCancelled()

            self._slic3r_logger.info("### Finished, returncode %d" %
                                     p.returncode)
            if p.returncode == 0:
                return True, None
            else:
                self._logger.warn(
                    "Could not slice via Slic3r, got return code %r" %
                    p.returncode)
                return False, "Got returncode %r" % p.returncode

        except octoprint.slicing.SlicingCancelled as e:
            raise e
        except:
            self._logger.exception(
                "Could not slice via Slic3r, got an unknown error")
            return False, "Unknown error, please consult the log file"

        finally:
            with self._cancelled_jobs_mutex:
                if machinecode_path in self._cancelled_jobs:
                    self._cancelled_jobs.remove(machinecode_path)
            with self._slicing_commands_mutex:
                if machinecode_path in self._slicing_commands:
                    del self._slicing_commands[machinecode_path]

            self._slic3r_logger.info("-" * 40)
예제 #4
0
 def is_slicer_configured(self):
     slic3r_engine = normalize_path(self._settings.get(["slic3r_engine"]))
     return slic3r_engine is not None and os.path.exists(slic3r_engine)
예제 #5
0
    def do_slice(self,
                 model_path,
                 printer_profile,
                 machinecode_path=None,
                 profile_path=None,
                 position=None,
                 on_progress=None,
                 on_progress_args=None,
                 on_progress_kwargs=None):
        try:
            with self._job_mutex:

                executable = normalize_path(
                    self._settings.get(["cura_engine_path"]))
                if not executable:
                    self._logger.error(u"Path to CuraEngine is not configured")
                    return False, "Path to CuraEngine is not configured"

                working_dir = os.path.dirname(executable)

                if not profile_path:
                    profile_path = self._settings.get(["default_profile"])
                profile_dict = get_profile_dict_from_yaml(profile_path)

                if "material_diameter" in profile_dict:
                    filament_diameter = float(
                        profile_dict["material_diameter"])
                else:
                    filament_diameter = None

                if on_progress:
                    if not on_progress_args:
                        on_progress_args = ()
                    if not on_progress_kwargs:
                        on_progress_kwargs = dict()

                command_args = self._build_command(executable, model_path,
                                                   printer_profile,
                                                   machinecode_path,
                                                   profile_dict, position)

                self._logger.info(u"Running %r in %s" %
                                  (" ".join(command_args), working_dir))

                import sarge
                p = sarge.run(command_args,
                              cwd=working_dir,
                              async=True,
                              stdout=sarge.Capture(),
                              stderr=sarge.Capture())
                p.wait_events()
                self._slicing_commands[machinecode_path] = p.commands[0]

            returncode, analysis = self._parse_slicing_output(
                p,
                on_progress,
                on_progress_args,
                on_progress_kwargs,
                filament_diameter=filament_diameter)

            with self._job_mutex:
                if machinecode_path in self._cancelled_jobs:
                    self._cura_engine_logger.info(u"### Cancelled")
                    raise octoprint.slicing.SlicingCancelled()

            self._cura_engine_logger.info(u"### Finished, returncode %d" %
                                          returncode)
            if returncode == 0:
                self._logger.info(u"Slicing complete.")
                return True, dict(analysis=analysis)
            else:
                self._logger.warn(
                    u"Could not slice via Cura, got return code %r" %
                    returncode)
                return False, "Got return code %r" % returncode

        except octoprint.slicing.SlicingCancelled as e:
            raise e
        except:
            self._logger.exception(
                u"Could not slice via Cura Engine, got an unknown error")
            return False, "Unknown error, please consult the log file"

        finally:
            with self._job_mutex:
                if machinecode_path in self._cancelled_jobs:
                    self._cancelled_jobs.remove(machinecode_path)
                if machinecode_path in self._slicing_commands:
                    del self._slicing_commands[machinecode_path]

            self._cura_engine_logger.info("-" * 40)
예제 #6
0
 def _is_engine_configured(self, cura_engine=None):
     if cura_engine is None:
         cura_engine = normalize_path(self._settings.get(["cura_engine"]))
     return cura_engine is not None and os.path.isfile(
         cura_engine) and os.access(cura_engine, os.X_OK)
예제 #7
0
    def do_slice(self,
                 model_path,
                 printer_profile,
                 machinecode_path=None,
                 profile_path=None,
                 position=None,
                 on_progress=None,
                 on_progress_args=None,
                 on_progress_kwargs=None):
        try:
            with self._job_mutex:
                if not profile_path:
                    profile_path = self._settings.get(["default_profile"])
                if not machinecode_path:
                    path, _ = os.path.splitext(model_path)
                    machinecode_path = path + ".gco"

                if position and isinstance(
                        position,
                        dict) and "x" in position and "y" in position:
                    pos_x = position["x"]
                    pos_y = position["y"]
                else:
                    pos_x = None
                    pos_y = None

                if on_progress:
                    if not on_progress_args:
                        on_progress_args = ()
                    if not on_progress_kwargs:
                        on_progress_kwargs = dict()

                self._cura_logger.info(
                    u"### Slicing {} to {} using profile stored at {}".format(
                        to_unicode(model_path, errors="replace"),
                        to_unicode(machinecode_path, errors="replace"),
                        to_unicode(profile_path, errors="replace")))

                executable = normalize_path(self._settings.get(["cura_engine"
                                                                ]))
                if not executable:
                    return False, u"Path to CuraEngine is not configured "

                working_dir = os.path.dirname(executable)

                slicing_profile = Profile(self._load_profile(profile_path),
                                          printer_profile, pos_x, pos_y)

                # NOTE: We can assume an extruder count of 1 here since the only way we currently
                # support dual extrusion in this implementation is by using the second extruder for support (which
                # the engine conversion will automatically detect and adapt accordingly).
                #
                # We currently do only support STL files as sliceables, which by default can only contain one mesh,
                # so no risk of having to slice multi-objects at the moment, which would necessitate a full analysis
                # of the objects to slice to determine amount of needed extruders to use here. If we ever decide to
                # also support dual extrusion slicing (including composition from multiple STLs or support for OBJ or
                # AMF files and the like), this code needs to be adapted!
                #
                # The extruder count is needed to decide which start/end gcode will be used from the Cura profile.
                # Stock Cura implementation counts the number of objects in the scene for this (and also takes a look
                # at the support usage, like the engine conversion here does). We only ever have one object.
                engine_settings = self._convert_to_engine(profile_path,
                                                          printer_profile,
                                                          pos_x=pos_x,
                                                          pos_y=pos_y,
                                                          used_extruders=1)

                # Start building the argument list for the CuraEngine command execution
                args = [executable, '-v', '-p']

                # Add the settings (sorted alphabetically) to the command
                for k, v in sorted(engine_settings.items(),
                                   key=lambda s: s[0]):
                    args += ["-s", "%s=%s" % (k, str(v))]
                args += ["-o", machinecode_path, model_path]

                self._logger.info(u"Running {!r} in {}".format(
                    u" ".join(
                        map(lambda x: to_unicode(x, errors="replace"), args)),
                    working_dir))

                import sarge
                p = sarge.run(args,
                              cwd=working_dir,
                              async=True,
                              stdout=sarge.Capture(),
                              stderr=sarge.Capture())
                p.wait_events()
                self._slicing_commands[machinecode_path] = p.commands[0]

            try:
                layer_count = None
                step_factor = dict(inset=0, skin=1, export=2)
                analysis = None
                while p.returncode is None:
                    line = p.stderr.readline(timeout=0.5)
                    if not line:
                        p.commands[0].poll()
                        continue

                    line = to_unicode(line, errors="replace")
                    self._cura_logger.debug(line.strip())

                    if on_progress is not None:
                        # The Cura slicing process has three individual steps, each consisting of <layer_count> substeps:
                        #
                        #   - inset
                        #   - skin
                        #   - export
                        #
                        # So each layer will be processed three times, once for each step, resulting in a total amount of
                        # substeps of 3 * <layer_count>.
                        #
                        # The CuraEngine reports the calculated layer count and the continuous progress on stderr.
                        # The layer count gets reported right at the beginning in a line of the format:
                        #
                        #   Layer count: <layer_count>
                        #
                        # The individual progress per each of the three steps gets reported on stderr in a line of
                        # the format:
                        #
                        #   Progress:<step>:<current_layer>:<layer_count>
                        #
                        # Thus, for determining the overall progress the following formula applies:
                        #
                        #   progress = <step_factor> * <layer_count> + <current_layer> / <layer_count> * 3
                        #
                        # with <step_factor> being 0 for "inset", 1 for "skin" and 2 for "export".

                        if line.startswith(
                                u"Layer count:") and layer_count is None:
                            try:
                                layer_count = float(
                                    line[len(u"Layer count:"):].strip())
                            except:
                                pass

                        elif line.startswith(u"Progress:"):
                            split_line = line[len(u"Progress:"):].strip(
                            ).split(":")
                            if len(split_line) == 3:
                                step, current_layer, _ = split_line
                                try:
                                    current_layer = float(current_layer)
                                except:
                                    pass
                                else:
                                    if not step in step_factor:
                                        continue
                                    on_progress_kwargs["_progress"] = (
                                        step_factor[step] * layer_count +
                                        current_layer) / (layer_count * 3)
                                    on_progress(*on_progress_args,
                                                **on_progress_kwargs)

                        elif line.startswith(u"Print time:"):
                            try:
                                print_time = int(
                                    line[len(u"Print time:"):].strip())
                                if analysis is None:
                                    analysis = dict()
                                analysis["estimatedPrintTime"] = print_time
                            except:
                                pass

                        # Get the filament usage

                        elif line.startswith(u"Filament:") or line.startswith(
                                u"Filament2:"):
                            if line.startswith(u"Filament:"):
                                filament_str = line[len(u"Filament:"):].strip()
                                tool_key = "tool0"
                            else:
                                filament_str = line[len(u"Filament2:"):].strip(
                                )
                                tool_key = "tool1"

                            try:
                                filament = int(filament_str)

                                if analysis is None:
                                    analysis = dict()
                                if not "filament" in analysis:
                                    analysis["filament"] = dict()
                                if not tool_key in analysis["filament"]:
                                    analysis["filament"][tool_key] = dict()

                                if slicing_profile.get_float(
                                        "filament_diameter") is not None:
                                    if slicing_profile.get(
                                            "gcode_flavor"
                                    ) == GcodeFlavors.ULTIGCODE or slicing_profile.get(
                                            "gcode_flavor"
                                    ) == GcodeFlavors.REPRAP_VOLUME:
                                        analysis["filament"][
                                            tool_key] = _get_usage_from_volume(
                                                filament,
                                                slicing_profile.get_float(
                                                    "filament_diameter"))
                                    else:
                                        analysis["filament"][
                                            tool_key] = _get_usage_from_length(
                                                filament,
                                                slicing_profile.get_float(
                                                    "filament_diameter"))

                            except:
                                pass
            finally:
                p.close()

            with self._job_mutex:
                if machinecode_path in self._cancelled_jobs:
                    self._cura_logger.info(u"### Cancelled")
                    raise octoprint.slicing.SlicingCancelled()

            self._cura_logger.info(u"### Finished, returncode %d" %
                                   p.returncode)
            if p.returncode == 0:
                return True, dict(analysis=analysis)
            else:
                self._logger.warn(
                    u"Could not slice via Cura, got return code %r" %
                    p.returncode)
                return False, "Got returncode %r" % p.returncode

        except octoprint.slicing.SlicingCancelled as e:
            raise e
        except:
            self._logger.exception(
                u"Could not slice via Cura, got an unknown error")
            return False, "Unknown error, please consult the log file"

        finally:
            with self._job_mutex:
                if machinecode_path in self._cancelled_jobs:
                    self._cancelled_jobs.remove(machinecode_path)
                if machinecode_path in self._slicing_commands:
                    del self._slicing_commands[machinecode_path]

            self._cura_logger.info("-" * 40)
예제 #8
0
 def is_slicer_configured(self):
     cura_engine = normalize_path(self._settings.get(["cura_engine"]))
     return self._is_engine_configured(cura_engine=cura_engine)
예제 #9
0
	def _is_engine_configured(self, cura_engine=None):
		if cura_engine is None:
			cura_engine = normalize_path(self._settings.get(["cura_engine"]))
		return cura_engine is not None and os.path.isfile(cura_engine) and os.access(cura_engine, os.X_OK)
예제 #10
0
	def do_slice(self, model_path, printer_profile, machinecode_path=None, profile_path=None, position=None, on_progress=None, on_progress_args=None, on_progress_kwargs=None):
		try:
			with self._job_mutex:
				if not profile_path:
					profile_path = self._settings.get(["default_profile"])
				if not machinecode_path:
					path, _ = os.path.splitext(model_path)
					machinecode_path = path + ".gco"

				if position and isinstance(position, dict) and "x" in position and "y" in position:
					pos_x = position["x"]
					pos_y = position["y"]
				else:
					pos_x = None
					pos_y = None

				if on_progress:
					if not on_progress_args:
						on_progress_args = ()
					if not on_progress_kwargs:
						on_progress_kwargs = dict()

				self._cura_logger.info(u"### Slicing %s to %s using profile stored at %s" % (model_path, machinecode_path, profile_path))

				executable = normalize_path(self._settings.get(["cura_engine"]))
				if not executable:
					return False, "Path to CuraEngine is not configured "

				working_dir = os.path.dirname(executable)

				slicing_profile = Profile(self._load_profile(profile_path), printer_profile, pos_x, pos_y)

				# NOTE: We can assume an extruder count of 1 here since the only way we currently
				# support dual extrusion in this implementation is by using the second extruder for support (which
				# the engine conversion will automatically detect and adapt accordingly).
				#
				# We currently do only support STL files as sliceables, which by default can only contain one mesh,
				# so no risk of having to slice multi-objects at the moment, which would necessitate a full analysis
				# of the objects to slice to determine amount of needed extruders to use here. If we ever decide to
				# also support dual extrusion slicing (including composition from multiple STLs or support for OBJ or
				# AMF files and the like), this code needs to be adapted!
				#
				# The extruder count is needed to decide which start/end gcode will be used from the Cura profile.
				# Stock Cura implementation counts the number of objects in the scene for this (and also takes a look
				# at the support usage, like the engine conversion here does). We only ever have one object.
				engine_settings = self._convert_to_engine(profile_path, printer_profile,
				                                          pos_x=pos_x, pos_y=pos_y,
				                                          used_extruders=1)

				# Start building the argument list for the CuraEngine command execution
				args = [executable, '-v', '-p']

				# Add the settings (sorted alphabetically) to the command
				for k, v in sorted(engine_settings.items(), key=lambda s: s[0]):
					args += ["-s", "%s=%s" % (k, str(v))]
				args += ["-o", machinecode_path, model_path]

				self._logger.info(u"Running %r in %s" % (" ".join(args), working_dir))

				import sarge
				p = sarge.run(args, cwd=working_dir, async=True, stdout=sarge.Capture(), stderr=sarge.Capture())
				p.wait_events()
				self._slicing_commands[machinecode_path] = p.commands[0]

			try:
				layer_count = None
				step_factor = dict(
					inset=0,
					skin=1,
					export=2
				)
				analysis = None
				while p.returncode is None:
					line = p.stderr.readline(timeout=0.5)
					if not line:
						p.commands[0].poll()
						continue

					line = octoprint.util.to_unicode(line, errors="replace")
					self._cura_logger.debug(line.strip())

					if on_progress is not None:
						# The Cura slicing process has three individual steps, each consisting of <layer_count> substeps:
						#
						#   - inset
						#   - skin
						#   - export
						#
						# So each layer will be processed three times, once for each step, resulting in a total amount of
						# substeps of 3 * <layer_count>.
						#
						# The CuraEngine reports the calculated layer count and the continuous progress on stderr.
						# The layer count gets reported right at the beginning in a line of the format:
						#
						#   Layer count: <layer_count>
						#
						# The individual progress per each of the three steps gets reported on stderr in a line of
						# the format:
						#
						#   Progress:<step>:<current_layer>:<layer_count>
						#
						# Thus, for determining the overall progress the following formula applies:
						#
						#   progress = <step_factor> * <layer_count> + <current_layer> / <layer_count> * 3
						#
						# with <step_factor> being 0 for "inset", 1 for "skin" and 2 for "export".

						if line.startswith(u"Layer count:") and layer_count is None:
							try:
								layer_count = float(line[len(u"Layer count:"):].strip())
							except:
								pass

						elif line.startswith(u"Progress:"):
							split_line = line[len(u"Progress:"):].strip().split(":")
							if len(split_line) == 3:
								step, current_layer, _ = split_line
								try:
									current_layer = float(current_layer)
								except:
									pass
								else:
									if not step in step_factor:
										continue
									on_progress_kwargs["_progress"] = (step_factor[step] * layer_count + current_layer) / (layer_count * 3)
									on_progress(*on_progress_args, **on_progress_kwargs)

						elif line.startswith(u"Print time:"):
							try:
								print_time = int(line[len(u"Print time:"):].strip())
								if analysis is None:
									analysis = dict()
								analysis["estimatedPrintTime"] = print_time
							except:
								pass

						# Get the filament usage

						elif line.startswith(u"Filament:") or line.startswith(u"Filament2:"):
							if line.startswith(u"Filament:"):
								filament_str = line[len(u"Filament:"):].strip()
								tool_key = "tool0"
							else:
								filament_str = line[len(u"Filament2:"):].strip()
								tool_key = "tool1"

							try:
								filament = int(filament_str)

								if analysis is None:
									analysis = dict()
								if not "filament" in analysis:
									analysis["filament"] = dict()
								if not tool_key in analysis["filament"]:
									analysis["filament"][tool_key] = dict()

								if slicing_profile.get_float("filament_diameter") is not None:
									if slicing_profile.get("gcode_flavor") == GcodeFlavors.ULTIGCODE or slicing_profile.get("gcode_flavor") == GcodeFlavors.REPRAP_VOLUME:
										analysis["filament"][tool_key] = _get_usage_from_volume(filament, slicing_profile.get_float("filament_diameter"))
									else:
										analysis["filament"][tool_key] = _get_usage_from_length(filament, slicing_profile.get_float("filament_diameter"))

							except:
								pass
			finally:
				p.close()

			with self._job_mutex:
				if machinecode_path in self._cancelled_jobs:
					self._cura_logger.info(u"### Cancelled")
					raise octoprint.slicing.SlicingCancelled()

			self._cura_logger.info(u"### Finished, returncode %d" % p.returncode)
			if p.returncode == 0:
				return True, dict(analysis=analysis)
			else:
				self._logger.warn(u"Could not slice via Cura, got return code %r" % p.returncode)
				return False, "Got returncode %r" % p.returncode

		except octoprint.slicing.SlicingCancelled as e:
			raise e
		except:
			self._logger.exception(u"Could not slice via Cura, got an unknown error")
			return False, "Unknown error, please consult the log file"

		finally:
			with self._job_mutex:
				if machinecode_path in self._cancelled_jobs:
					self._cancelled_jobs.remove(machinecode_path)
				if machinecode_path in self._slicing_commands:
					del self._slicing_commands[machinecode_path]

			self._cura_logger.info("-" * 40)
예제 #11
0
	def do_slice(self, model_path, printer_profile, machinecode_path=None, profile_path=None, position=None, on_progress=None, on_progress_args=None, on_progress_kwargs=None):
		if not profile_path:
			profile_path = self._settings.get(["default_profile"])
		if not machinecode_path:
			path, _ = os.path.splitext(model_path)
			machinecode_path = path + ".gco"
		
		if position and isinstance(position, dict) and "x" in position and "y" in position:
			posX = position["x"]
			posY = position["y"]
		elif printer_profile["volume"]["formFactor"] == "circular" or printer_profile["volume"]["origin"] == "center" :
			posX = 0
			posY = 0
		else:
			posX = printer_profile["volume"]["width"] / 2.0
			posY = printer_profile["volume"]["depth"] / 2.0
		
		self._slic3r_logger.info("### Slicing %s to %s using profile stored at %s" % (model_path, machinecode_path, profile_path))

		executable = normalize_path(self._settings.get(["slic3r_engine"]))
		if not executable:
			return False, "Path to Slic3r is not configured "

		import sarge

		working_dir, _ = os.path.split(executable)
		args = ['"%s"' % executable, '--load', '"%s"' % profile_path, '--print-center', '"%f,%f"' % (posX, posY), '-o', '"%s"' % machinecode_path, '"%s"' % model_path]

		command = " ".join(args)
		self._logger.info("Running %r in %s" % (command, working_dir))
		try:
			if parse_version(sarge.__version__) >= parse_version('0.1.5'): # Because in version 0.1.5 the name was changed in sarge.
				async_kwarg = 'async_'
			else:
				async_kwarg = 'async'
			p = sarge.run(command, cwd=working_dir, stdout=sarge.Capture(), stderr=sarge.Capture(), **{async_kwarg: True})
			p.wait_events()
			try:
				with self._slicing_commands_mutex:
					self._slicing_commands[machinecode_path] = p.commands[0]

				line_seen = False
				while p.returncode is None:
					stdout_line = p.stdout.readline(timeout=0.5)
					stderr_line = p.stderr.readline(timeout=0.5)

					if not stdout_line and not stderr_line:
						if line_seen:
							break
						else:
							continue

					line_seen = True
					if stdout_line:
						self._slic3r_logger.debug("stdout: " + stdout_line.strip())
					if stderr_line:
						self._slic3r_logger.debug("stderr: " + stderr_line.strip())
			finally:
				p.close()

			with self._cancelled_jobs_mutex:
				if machinecode_path in self._cancelled_jobs:
					self._slic3r_logger.info("### Cancelled")
					raise octoprint.slicing.SlicingCancelled()

			self._slic3r_logger.info("### Finished, returncode %d" % p.returncode)
			if p.returncode == 0:
				analysis = get_analysis_from_gcode(machinecode_path)
				self._slic3r_logger.info("Analysis found in gcode: %s" % str(analysis))
				if analysis:
				  analysis = {'analysis': analysis}
				return True, analysis
			else:
				self._logger.warn("Could not slice via Slic3r, got return code %r" % p.returncode)
				return False, "Got returncode %r" % p.returncode

		except octoprint.slicing.SlicingCancelled as e:
			raise e
		except:
			self._logger.exception("Could not slice via Slic3r, got an unknown error")
			return False, "Unknown error, please consult the log file"

		finally:
			with self._cancelled_jobs_mutex:
				if machinecode_path in self._cancelled_jobs:
					self._cancelled_jobs.remove(machinecode_path)
			with self._slicing_commands_mutex:
				if machinecode_path in self._slicing_commands:
					del self._slicing_commands[machinecode_path]

			self._slic3r_logger.info("-" * 40)
예제 #12
0
	def is_slicer_configured(self):
		slic3r_engine = normalize_path(self._settings.get(["slic3r_engine"]))
		return slic3r_engine is not None and os.path.exists(slic3r_engine)
예제 #13
0
    def do_slice(self,
                 model_path,
                 printer_profile,
                 machinecode_path=None,
                 profile_path=None,
                 position=None,
                 on_progress=None,
                 on_progress_args=None,
                 on_progress_kwargs=None):
        if not profile_path:
            profile_path = self._settings.get(["default_profile"])
        if not machinecode_path:
            path, _ = os.path.splitext(model_path)
            machinecode_path = path + ".gco"

        if position and isinstance(
                position, dict) and "x" in position and "y" in position:
            posX = position["x"]
            posY = position["y"]
        elif printer_profile["volume"][
                "formFactor"] == "circular" or printer_profile["volume"][
                    "origin"] == "center":
            posX = 0
            posY = 0
        else:
            posX = printer_profile["volume"]["width"] / 2.0
            posY = printer_profile["volume"]["depth"] / 2.0

        self._slic3r_logger.info(
            "### Slicing %s to %s using profile stored at %s" %
            (model_path, machinecode_path, profile_path))

        executable = normalize_path(self._settings.get(["slic3r_engine"]))
        if not executable:
            return False, "Path to Slic3r is not configured "

        args = [
            '"%s"' % executable, '--load',
            '"%s"' % profile_path, '--print-center',
            '"%f,%f"' % (posX, posY), '-o',
            '"%s"' % machinecode_path,
            '"%s"' % model_path
        ]

        try:
            import subprocess

            help_process = subprocess.Popen((executable, '--help'),
                                            stdout=subprocess.PIPE)
            help_text = help_process.communicate()[0]

            if help_text.startswith('PrusaSlicer-2'):
                args = [
                    '"%s"' % executable, '--slice --load',
                    '"%s"' % profile_path, '--center',
                    '"%f,%f"' % (posX, posY), '-o',
                    '"%s"' % machinecode_path,
                    '"%s"' % model_path
                ]
                self._logger.info("Running Prusa Slic3r >= 2")
        except:
            self._logger.info("Error during Prusa Slic3r detection")

        import sarge

        working_dir, _ = os.path.split(executable)

        command = " ".join(args)
        self._logger.info("Running %r in %s" % (command, working_dir))
        try:
            if parse_version(sarge.__version__) >= parse_version(
                    '0.1.5'
            ):  # Because in version 0.1.5 the name was changed in sarge.
                async_kwarg = 'async_'
            else:
                async_kwarg = 'async'
            p = sarge.run(command,
                          cwd=working_dir,
                          stdout=sarge.Capture(),
                          stderr=sarge.Capture(),
                          **{async_kwarg: True})
            p.wait_events()
            last_error = ""
            try:
                with self._slicing_commands_mutex:
                    self._slicing_commands[machinecode_path] = p.commands[0]

                line_seen = False
                while p.returncode is None:
                    stdout_line = p.stdout.readline(timeout=0.5)
                    stderr_line = p.stderr.readline(timeout=0.5)

                    if not stdout_line and not stderr_line:
                        if line_seen:
                            break
                        else:
                            continue

                    line_seen = True
                    if stdout_line:
                        self._slic3r_logger.debug("stdout: " +
                                                  stdout_line.strip())
                    if stderr_line:
                        self._slic3r_logger.debug("stderr: " +
                                                  stderr_line.strip())
                        if (len(stderr_line.strip()) > 0):
                            last_error = stderr_line.strip()
            finally:
                p.close()

            with self._cancelled_jobs_mutex:
                if machinecode_path in self._cancelled_jobs:
                    self._slic3r_logger.info("### Cancelled")
                    raise octoprint.slicing.SlicingCancelled()

            self._slic3r_logger.info("### Finished, returncode %d" %
                                     p.returncode)
            if p.returncode == 0:
                analysis = get_analysis_from_gcode(machinecode_path)
                self._slic3r_logger.info("Analysis found in gcode: %s" %
                                         str(analysis))
                if analysis:
                    analysis = {'analysis': analysis}
                return True, analysis
            else:
                self._logger.warn(
                    "Could not slice via Slic3r, got return code %r" %
                    p.returncode)
                self._logger.warn("Error was: %s" % last_error)
                return False, "Got returncode %r: %s" % (p.returncode,
                                                         last_error)

        except octoprint.slicing.SlicingCancelled as e:
            raise e
        except:
            self._logger.exception(
                "Could not slice via Slic3r, got an unknown error")
            return False, "Unknown error, please consult the log file"

        finally:
            with self._cancelled_jobs_mutex:
                if machinecode_path in self._cancelled_jobs:
                    self._cancelled_jobs.remove(machinecode_path)
            with self._slicing_commands_mutex:
                if machinecode_path in self._slicing_commands:
                    del self._slicing_commands[machinecode_path]

            self._slic3r_logger.info("-" * 40)
예제 #14
0
	def is_slicer_configured(self):
		cura_engine = normalize_path(self._settings.get(["cura_engine"]))
		return self._is_engine_configured(cura_engine=cura_engine)
예제 #15
0
	def do_slice(self, model_path, printer_profile, machinecode_path=None, profile_path=None, position=None, on_progress=None, on_progress_args=None, on_progress_kwargs=None):
		try:
			with self._job_mutex:

				executable = normalize_path(self._settings.get(["cura_engine_path"]))
				if not executable:
					self._logger.error(u"Path to CuraEngine is not configured")
					return False, "Path to CuraEngine is not configured"

				working_dir = os.path.dirname(executable)

				if not profile_path:
					profile_path = self._settings.get(["default_profile"])
				profile_dict = get_profile_dict_from_yaml(profile_path)

				if "material_diameter" in profile_dict:
					filament_diameter = float(profile_dict["material_diameter"])
				else:
					filament_diameter = None

				if on_progress:
					if not on_progress_args:
						on_progress_args = ()
					if not on_progress_kwargs:
						on_progress_kwargs = dict()

				command_args = self._build_command(executable, model_path, printer_profile, machinecode_path, profile_dict, position)

				self._logger.info(u"Running %r in %s" % (" ".join(command_args), working_dir))

				import sarge
				p = sarge.run(command_args, cwd=working_dir, async=True, stdout=sarge.Capture(), stderr=sarge.Capture())
				p.wait_events()
				self._slicing_commands[machinecode_path] = p.commands[0]

			returncode, analysis = self._parse_slicing_output(p, on_progress, on_progress_args, on_progress_kwargs, filament_diameter=filament_diameter)

			with self._job_mutex:
				if machinecode_path in self._cancelled_jobs:
					self._cura_engine_logger.info(u"### Cancelled")
					raise octoprint.slicing.SlicingCancelled()

			self._cura_engine_logger.info(u"### Finished, returncode %d" % returncode)
			if returncode == 0:
				self._logger.info(u"Slicing complete.")
				return True, dict(analysis=analysis)
			else:
				self._logger.warn(u"Could not slice via Cura, got return code %r" % returncode)
				return False, "Got return code %r" % returncode

		except octoprint.slicing.SlicingCancelled as e:
			raise e
		except:
			self._logger.exception(u"Could not slice via Cura Engine, got an unknown error")
			return False, "Unknown error, please consult the log file"

		finally:
			with self._job_mutex:
				if machinecode_path in self._cancelled_jobs:
					self._cancelled_jobs.remove(machinecode_path)
				if machinecode_path in self._slicing_commands:
					del self._slicing_commands[machinecode_path]

			self._cura_engine_logger.info("-" * 40)
예제 #16
0
    def do_slice(self,
                 model_path,
                 printer_profile,
                 machinecode_path=None,
                 profile_path=None,
                 position=None,
                 on_progress=None,
                 on_progress_args=None,
                 on_progress_kwargs=None):
        try:
            with self._job_mutex:
                if not profile_path:
                    profile_path = self._settings.get(["default_profile"])
                if not machinecode_path:
                    path, _ = os.path.splitext(model_path)
                    machinecode_path = path + ".gco"

                if position and isinstance(
                        position,
                        dict) and "x" in position and "y" in position:
                    posX = position["x"]
                    posY = position["y"]
                else:
                    posX = None
                    posY = None

                if on_progress:
                    if not on_progress_args:
                        on_progress_args = ()
                    if not on_progress_kwargs:
                        on_progress_kwargs = dict()

                self._cura_logger.info(
                    u"### Slicing %s to %s using profile stored at %s" %
                    (model_path, machinecode_path, profile_path))

                executable = normalize_path(self._settings.get(["cura_engine"
                                                                ]))
                if not executable:
                    return False, "Path to CuraEngine is not configured "

                working_dir = os.path.dirname(executable)

                engine_settings = self._convert_to_engine(
                    profile_path, printer_profile, posX, posY)

                # Start building the argument list for the CuraEngine command execution
                args = [executable, '-v', '-p']

                # Add the settings (sorted alphabetically) to the command
                for k, v in sorted(engine_settings.items(),
                                   key=lambda s: s[0]):
                    args += ["-s", "%s=%s" % (k, str(v))]
                args += ["-o", machinecode_path, model_path]

                self._logger.info(u"Running %r in %s" %
                                  (" ".join(args), working_dir))

                import sarge
                p = sarge.run(args,
                              cwd=working_dir,
                              async=True,
                              stdout=sarge.Capture(),
                              stderr=sarge.Capture())
                p.wait_events()
                self._slicing_commands[machinecode_path] = p.commands[0]

            try:
                layer_count = None
                step_factor = dict(inset=0, skin=1, export=2)
                analysis = None
                while p.returncode is None:
                    line = p.stderr.readline(timeout=0.5)
                    if not line:
                        p.commands[0].poll()
                        continue

                    line = octoprint.util.to_unicode(line, errors="replace")
                    self._cura_logger.debug(line.strip())

                    if on_progress is not None:
                        # The Cura slicing process has three individual steps, each consisting of <layer_count> substeps:
                        #
                        #   - inset
                        #   - skin
                        #   - export
                        #
                        # So each layer will be processed three times, once for each step, resulting in a total amount of
                        # substeps of 3 * <layer_count>.
                        #
                        # The CuraEngine reports the calculated layer count and the continuous progress on stderr.
                        # The layer count gets reported right at the beginning in a line of the format:
                        #
                        #   Layer count: <layer_count>
                        #
                        # The individual progress per each of the three steps gets reported on stderr in a line of
                        # the format:
                        #
                        #   Progress:<step>:<current_layer>:<layer_count>
                        #
                        # Thus, for determining the overall progress the following formula applies:
                        #
                        #   progress = <step_factor> * <layer_count> + <current_layer> / <layer_count> * 3
                        #
                        # with <step_factor> being 0 for "inset", 1 for "skin" and 2 for "export".

                        if line.startswith(
                                u"Layer count:") and layer_count is None:
                            try:
                                layer_count = float(
                                    line[len(u"Layer count:"):].strip())
                            except:
                                pass

                        elif line.startswith(u"Progress:"):
                            split_line = line[len(u"Progress:"):].strip(
                            ).split(":")
                            if len(split_line) == 3:
                                step, current_layer, _ = split_line
                                try:
                                    current_layer = float(current_layer)
                                except:
                                    pass
                                else:
                                    if not step in step_factor:
                                        continue
                                    on_progress_kwargs["_progress"] = (
                                        step_factor[step] * layer_count +
                                        current_layer) / (layer_count * 3)
                                    on_progress(*on_progress_args,
                                                **on_progress_kwargs)

                        elif line.startswith(u"Print time:"):
                            try:
                                print_time = int(
                                    line[len(u"Print time:"):].strip())
                                if analysis is None:
                                    analysis = dict()
                                analysis["estimatedPrintTime"] = print_time
                            except:
                                pass

                        # Get the filament usage

                        elif line.startswith(u"Filament:") or line.startswith(
                                u"Filament2:"):
                            if line.startswith(u"Filament:"):
                                filament_str = line[len(u"Filament:"):].strip()
                                tool_key = "tool0"
                            else:
                                filament_str = line[len(u"Filament2:"):].strip(
                                )
                                tool_key = "tool1"

                            try:
                                filament = int(filament_str)

                                if analysis is None:
                                    analysis = dict()
                                if not "filament" in analysis:
                                    analysis["filament"] = dict()
                                if not tool_key in analysis["filament"]:
                                    analysis["filament"][tool_key] = dict()

                                if profile.get_float(
                                        "filament_diameter") != None:
                                    if profile.get(
                                            "gcode_flavor"
                                    ) == GcodeFlavors.ULTIGCODE or profile.get(
                                            "gcode_flavor"
                                    ) == GcodeFlavors.REPRAP_VOLUME:
                                        analysis["filament"][
                                            tool_key] = _get_usage_from_volume(
                                                filament,
                                                profile.get_float(
                                                    "filament_diameter"))
                                    else:
                                        analysis["filament"][
                                            tool_key] = _get_usage_from_length(
                                                filament,
                                                profile.get_float(
                                                    "filament_diameter"))

                            except:
                                pass
            finally:
                p.close()

            with self._job_mutex:
                if machinecode_path in self._cancelled_jobs:
                    self._cura_logger.info(u"### Cancelled")
                    raise octoprint.slicing.SlicingCancelled()

            self._cura_logger.info(u"### Finished, returncode %d" %
                                   p.returncode)
            if p.returncode == 0:
                return True, dict(analysis=analysis)
            else:
                self._logger.warn(
                    u"Could not slice via Cura, got return code %r" %
                    p.returncode)
                return False, "Got returncode %r" % p.returncode

        except octoprint.slicing.SlicingCancelled as e:
            raise e
        except:
            self._logger.exception(
                u"Could not slice via Cura, got an unknown error")
            return False, "Unknown error, please consult the log file"

        finally:
            with self._job_mutex:
                if machinecode_path in self._cancelled_jobs:
                    self._cancelled_jobs.remove(machinecode_path)
                if machinecode_path in self._slicing_commands:
                    del self._slicing_commands[machinecode_path]

            self._cura_logger.info("-" * 40)