Merge branch 'maintenance' into main/supportPasswordManagers

This commit is contained in:
Gina Häußge 2017-01-11 19:03:31 +01:00
commit 881d3bb212
14 changed files with 97 additions and 51 deletions

View file

@ -12,6 +12,7 @@ or **[creating pull requests](#pull-requests)**.
* [Where can I find which version and branch I'm on?](#where-can-i-find-which-version-and-branch-im-on)
* [Where can I find those log files you keep talking about?](#where-can-i-find-those-log-files-you-keep-talking-about)
* [Where can I find my browser's error console?](#where-can-i-find-my-browsers-error-console)
* [Setting up a development environment](#setting-up-a-development-environment)
* [Pull requests](#pull-requests)
* [What do the branches mean?](#what-do-the-branches-mean)
* [How OctoPrint is versioned](#how-octoprint-is-versioned)
@ -235,6 +236,10 @@ find information on how to do just that in the
See [How to open the Javascript Console in different browsers](https://webmasters.stackexchange.com/questions/8525/how-to-open-the-javascript-console-in-different-browsers)
## Setting up a development environment
See [the corresponding chapter in the documentation](http://docs.octoprint.org/en/master/development/index.html#setting-up-a-development-environment).
## Pull requests
1. If you want to add a new feature to OctoPrint, **please always first

View file

@ -86,27 +86,27 @@ following usage examples assume that said `octoprint` script is on your `PATH`.
You can start the server via
octoprint
octoprint serve
By default it binds to all interfaces on port 5000 (so pointing your browser to `http://127.0.0.1:5000`
will do the trick). If you want to change that, use the additional command line parameters `host` and `port`,
which accept the host ip to bind to and the numeric port number respectively. If for example you want the server
to only listen on the local interface on port 8080, the command line would be
octoprint --host=127.0.0.1 --port=8080
octoprint serve --host=127.0.0.1 --port=8080
Alternatively, the host and port on which to bind can be defined via the configuration.
If you want to run OctoPrint as a daemon (only supported on Linux), use
octoprint --daemon {start|stop|restart} [--pid PIDFILE]
octoprint daemon {start|stop|restart} [--pid PIDFILE]
If you do not supply a custom pidfile location via `--pid PIDFILE`, it will be created at `/tmp/octoprint.pid`.
You can also specify the configfile or the base directory (for basing off the `uploads`, `timelapse` and `logs` folders),
e.g.:
octoprint --config /path/to/another/config.yaml --basedir /path/to/my/basedir
octoprint serve --config /path/to/another/config.yaml --basedir /path/to/my/basedir
See `octoprint --help` for further information.

View file

@ -167,7 +167,7 @@ IDE Setup
PyCharm
.......
- "File" > "Open ...", select OctoPrint checkout folder (e.g. ``~/devel/OctoPrint/venv`` or ``C:\Devel\OctoPrint``)
- "File" > "Open ...", select OctoPrint checkout folder (e.g. ``~/devel/OctoPrint`` or ``C:\Devel\OctoPrint``)
- "File" > "Settings ..." > "Project: OctoPrint" > "Project Interpreter" > "Add local ...", select OctoPrint venv
folder (e.g. ``~/devel/OctoPrint/venv`` or ``C:\Devel\OctoPrint\venv``)
- Right click "src" in project tree, mark as source folder

View file

@ -15,6 +15,9 @@ import octoprint_setuptools
# Requirements for our application
INSTALL_REQUIRES = [
"flask>=0.9,<0.11",
"Jinja2>=2.8,<2.9", # Jinja 2.9 has breaking changes WRT template scope - we can't
# guarantee backwards compatibility for plugins and such with that
# version, hence we need to pin to a lower version for now. See #1697
"werkzeug>=0.8.3,<0.9",
"tornado==4.0.2", # pinned for now, we need to migrate to a newer tornado, but due
# to some voodoo needed to get large streamed uploads and downloads

View file

@ -511,7 +511,7 @@ class LocalFileStorage(StorageInterface):
filepath = self.sanitize_path(filepath)
path = self.sanitize_path(path)
return filepath == path or filepath.startswith(path + "/")
return filepath == path or filepath.startswith(path + os.sep)
def file_exists(self, path):
path, name = self.sanitize(path)

View file

@ -266,6 +266,7 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin,
working_dir = os.path.dirname(executable)
slicing_profile = Profile(self._load_profile(profile_path), printer_profile, posX, posY)
engine_settings = self._convert_to_engine(profile_path, printer_profile, posX, posY)
# Start building the argument list for the CuraEngine command execution
@ -375,11 +376,11 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin,
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"))
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, profile.get_float("filament_diameter"))
analysis["filament"][tool_key] = _get_usage_from_length(filament, slicing_profile.get_float("filament_diameter"))
except:
pass

View file

@ -63,12 +63,14 @@ defaults = dict(
solid_top=True,
solid_bottom=True,
fill_overlap=15,
perimeter_before_infill=False,
# speeds
print_speed=50.0,
travel_speed=150.0,
bottom_layer_speed=20.0,
infill_speed=0.0,
solidarea_speed=0.0,
outer_shell_speed=0.0,
inner_shell_speed=0.0,
@ -117,8 +119,11 @@ defaults = dict(
raft_base_linewidth=1.0,
raft_interface_thickness=0.27,
raft_interface_linewidth=0.4,
raft_airgap_all=0.0,
raft_airgap=0.22,
raft_surface_layers=2,
raft_surface_thickness=0.27,
raft_surface_linewidth=0.4,
# repairing
fix_horrible_union_all_type_a=True,
@ -754,9 +759,13 @@ class Profile(object):
prefix = ""
gcode_parameter_key = "S"
if self.get("gcode_flavor") == GcodeFlavors.MACH3:
gcode_parameter_key = "P"
e_steps = self.get_float("steps_per_e")
if e_steps > 0:
prefix += "M92 E{e_steps}\n" % (e_steps)
prefix += "M92 E{e_steps}\n".format(e_steps=e_steps)
temp = self.get_float("print_temperature")
bed_temp = 0
@ -765,30 +774,30 @@ class Profile(object):
include_bed_temp = bed_temp > 0 and not "{print_bed_temperature}" in Profile.regex_strip_comments.sub("", contents)
if include_bed_temp:
prefix += "M140 S{bed_temp}\n".format(bed_temp=bed_temp)
prefix += "M140 {param}{bed_temp}\n".format(param=gcode_parameter_key, bed_temp=bed_temp)
if temp > 0 and not "{print_temperature}" in Profile.regex_strip_comments.sub("", contents):
if extruder_count > 0:
def temp_line(temp, extruder, template):
def temp_line(temp, extruder, param, template):
t = temp
if extruder > 0:
print_temp = self.get_float("print_temperature%d" % (extruder + 1))
if print_temp > 0:
t = print_temp
return template.format(extruder=extruder, temp=t)
return template.format(extruder=extruder, temp=t, param=param)
prefix_preheat = ""
prefix_waitheat = ""
for n in range(0, extruder_count):
if n > 0:
prefix_preheat += temp_line(temp, n, "M104 T{extruder} S{temp}\n")
prefix_waitheat += temp_line(temp, n, "M109 T{extruder} S{temp}\n")
prefix_preheat += temp_line(temp, n, gcode_parameter_key, "M104 T{extruder} {param}{temp}\n")
prefix_waitheat += temp_line(temp, n, gcode_parameter_key, "M109 T{extruder} {param}{temp}\n")
prefix += prefix_preheat + prefix_waitheat + "T0\n"
else:
prefix += "M109 S{temp}\n".format(temp=temp)
prefix += "M109 {param}{temp}\n".format(param=gcode_parameter_key, temp=temp)
if include_bed_temp:
prefix += "M190 S{bed_temp}\n".format(bed_temp=bed_temp)
prefix += "M190 {param}{bed_temp}\n".format(param=gcode_parameter_key, bed_temp=bed_temp)
return prefix
@ -822,7 +831,7 @@ class Profile(object):
if layer_height == 0.0:
return 1
import math
return int(math.ceil(solid_thickness / (layer_height - 0.0001)))
return int(math.ceil((solid_thickness - 0.0001) / layer_height))
def calculate_minimal_extruder_count(self):
extruder_count = self.get("extruder_amount")
@ -871,12 +880,14 @@ class Profile(object):
"downSkinCount": solid_layer_count if self.get_boolean("solid_bottom") else 0,
"upSkinCount": solid_layer_count if self.get_boolean("solid_top") else 0,
"infillOverlap": self.get_int("fill_overlap"),
"perimeterBeforeInfill": 1 if self.get_boolean("perimeter_before_infill") else 0,
"initialSpeedupLayers": int(4),
"initialLayerSpeed": self.get_int("bottom_layer_speed"),
"printSpeed": self.get_int("print_speed"),
"infillSpeed": self.get_int("infill_speed") if self.get_int("infill_speed") > 0 else self.get_int("print_speed"),
"inset0Speed": self.get_int("outer_shell_speed") if self.get_int("outer_shell_speed") > 0 else self.get_int("print_speed"),
"insetXSpeed": self.get_int("inner_shell_speed") if self.get_int("inner_shell_speed") > 0 else self.get_int("print_speed"),
"skinSpeed": self.get_int("solidarea_speed") if self.get_int("solidarea_speed") > 0 > 0 else self.get_int("print_speed"),
"moveSpeed": self.get_int("travel_speed"),
"fanSpeedMin": self.get_int("fan_speed") if self.get_boolean("fan_enabled") else 0,
"fanSpeedMax": self.get_int("fan_speed_max") if self.get_boolean("fan_enabled") else 0,
@ -892,13 +903,14 @@ class Profile(object):
"retractionAmountExtruderSwitch": self.get_microns("retraction_dual_amount"),
"retractionZHop": self.get_microns("retraction_hop"),
"minimalExtrusionBeforeRetraction": self.get_microns("retraction_minimal_extrusion"),
"enableCombing": 1 if self.get("retraction_combing") == RetractionCombingTypes.ALL else (2 if self.get("retraction_combing") == RetractionCombingTypes.NO_SKIN else 0),
"multiVolumeOverlap": self.get_microns("overlap_dual"),
"objectSink": max(0, self.get_microns("object_sink")),
"minimalLayerTime": self.get_int("cool_min_layer_time"),
"minimalFeedrate": self.get_int("cool_min_feedrate"),
"coolHeadLift": 1 if self.get_boolean("cool_head_lift") else 0,
"enableCombing": 1 if self.get("retraction_combing") == RetractionCombingTypes.ALL else (2 if self.get("retraction_combing") == RetractionCombingTypes.NO_SKIN else 0),
# model positioning
"posx": self.get_pos_x() * 1000, # in microns
"posy": self.get_pos_y() * 1000, # in microns
@ -952,12 +964,13 @@ class Profile(object):
settings["raftInterfaceThickness"] = self.get_microns("raft_interface_thickness")
settings["raftInterfaceLinewidth"] = self.get_microns("raft_interface_linewidth")
settings["raftInterfaceLineSpacing"] = self.get_microns("raft_interface_linewidth") * 2
settings["raftAirGapLayer0"] = self.get_microns("raft_airgap")
settings["raftAirGapLayer0"] = self.get_microns("raft_airgap") + self.get_microns("raft_airgap_all")
settings["raftAirGap"] = self.get_microns("raft_airgap_all")
settings["raftBaseSpeed"] = self.get_int("bottom_layer_speed")
settings["raftFanSpeed"] = 100
settings["raftSurfaceThickness"] = settings["raftInterfaceThickness"]
settings["raftSurfaceLinewidth"] = int(edge_width * 1000)
settings["raftSurfaceLineSpacing"] = int(edge_width * 1000 * 0.9)
settings["raftFanSpeed"] = 0
settings["raftSurfaceThickness"] = self.get_microns("raft_surface_thickness")
settings["raftSurfaceLinewidth"] = self.get_microns("raft_surface_linewidth")
settings["raftSurfaceLineSpacing"] = self.get_microns("raft_surface_linewidth")
settings["raftSurfaceLayers"] = self.get_int("raft_surface_layers")
settings["raftSurfaceSpeed"] = self.get_int("bottom_layer_speed")

View file

@ -168,7 +168,7 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback):
#~~ progress plugin reporting
def _reportPrintProgressToPlugins(self, progress):
if not progress or not self._selectedFile or not "sd" in self._selectedFile or not "filename" in self._selectedFile:
if progress is None or not self._selectedFile or not "sd" in self._selectedFile or not "filename" in self._selectedFile:
return
storage = "sdcard" if self._selectedFile["sd"] else "local"

View file

@ -248,8 +248,8 @@ def _verifyFolderExists(origin, foldername):
def _isBusy(target, path):
currentOrigin, currentFilename = _getCurrentFile()
if currentFilename is not None and currentOrigin == target and fileManager.file_in_path(FileDestinations.LOCAL, path, currentFilename) and (printer.is_printing() or printer.is_paused()):
currentOrigin, currentPath = _getCurrentFile()
if currentPath is not None and currentOrigin == target and fileManager.file_in_path(FileDestinations.LOCAL, path, currentPath) and (printer.is_printing() or printer.is_paused()):
return True
return any(target == x[0] and fileManager.file_in_path(FileDestinations.LOCAL, path, x[1]) for x in fileManager.get_busy_files())
@ -705,8 +705,8 @@ def deleteGcodeFile(filename, target):
return make_response("Trying to delete a file that is currently in use: %s" % filename, 409)
# deselect the file if it's currently selected
currentOrigin, currentFilename = _getCurrentFile()
if currentFilename is not None and currentOrigin == target and filename == currentFilename:
currentOrigin, currentPath = _getCurrentFile()
if currentPath is not None and currentOrigin == target and filename == currentPath:
printer.unselect_file()
# delete it
@ -723,8 +723,8 @@ def deleteGcodeFile(filename, target):
return make_response("Trying to delete a folder that contains a file that is currently in use: %s" % filename, 409)
# deselect the file if it's currently selected
currentOrigin, currentFilename = _getCurrentFile()
if currentFilename is not None and currentOrigin == target and fileManager.file_in_path(target, filename, currentFilename):
currentOrigin, currentPath = _getCurrentFile()
if currentPath is not None and currentOrigin == target and fileManager.file_in_path(target, filename, currentPath):
printer.unselect_file()
# delete it
@ -734,8 +734,8 @@ def deleteGcodeFile(filename, target):
def _getCurrentFile():
currentJob = printer.get_current_job()
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"]
if currentJob is not None and "file" in currentJob.keys() and "path" in currentJob["file"] and "origin" in currentJob["file"]:
return currentJob["file"]["origin"], currentJob["file"]["path"]
else:
return None, None

View file

@ -230,7 +230,7 @@ def setTimelapseConfig():
except ValueError:
return make_response("Invalid value for retraction Z-Hop: %r" % data["retractionZHop"])
else:
if retractionZHop > 0:
if retractionZHop >= 0:
config["options"]["retractionZHop"] = retractionZHop
else:
return make_response("Invalid value for retraction Z-Hop: %d" % retractionZHop)

View file

@ -154,12 +154,12 @@ class PrinterStateConnection(sockjs.tornado.SockJSConnection, octoprint.printer.
messages = self._messageBacklog
self._messageBacklog = []
busy_files = [dict(origin=v[0], name=v[1]) for v in self._fileManager.get_busy_files()]
busy_files = [dict(origin=v[0], path=v[1]) for v in self._fileManager.get_busy_files()]
if "job" in data and data["job"] is not None \
and "file" in data["job"] and "name" in data["job"]["file"] and "origin" in data["job"]["file"] \
and data["job"]["file"]["name"] is not None and data["job"]["file"]["origin"] is not None \
and "file" in data["job"] and "path" in data["job"]["file"] and "origin" in data["job"]["file"] \
and data["job"]["file"]["path"] is not None and data["job"]["file"]["origin"] is not None \
and (self._printer.is_printing() or self._printer.is_paused()):
busy_files.append(dict(origin=data["job"]["file"]["origin"], name=data["job"]["file"]["name"]))
busy_files.append(dict(origin=data["job"]["file"]["origin"], path=data["job"]["file"]["path"]))
data.update({
"serverTime": time.time(),

View file

@ -708,11 +708,12 @@ $(function() {
return false;
}
if (entry["type"] == "folder" && entry["children"]) {
var success = entry["name"].toLocaleLowerCase().indexOf(query) > -1;
if (!success && entry["type"] == "folder" && entry["children"]) {
return _.any(entry["children"], recursiveSearch);
} else {
return entry["name"].toLocaleLowerCase().indexOf(query) > -1;
}
return success;
};
self.listHelper.changeSearchFunction(recursiveSearch);

View file

@ -256,8 +256,8 @@ $(function() {
self._processBusyFiles = function(data) {
var busyFiles = [];
_.each(data, function(entry) {
if (entry.hasOwnProperty("name") && entry.hasOwnProperty("origin")) {
busyFiles.push(entry.origin + ":" + entry.name);
if (entry.hasOwnProperty("path") && entry.hasOwnProperty("origin")) {
busyFiles.push(entry.origin + ":" + entry.path);
}
});
self.busyFiles(busyFiles);

View file

@ -260,6 +260,7 @@ class gcode(object):
if ';' in line:
comment = line[line.find(';')+1:].strip()
if comment.startswith("filament_diameter"):
# Slic3r
filamentValue = comment.split("=", 1)[1].strip()
try:
self._filamentDiameter = float(filamentValue)
@ -269,6 +270,7 @@ class gcode(object):
except ValueError:
self._filamentDiameter = 0.0
elif comment.startswith("CURA_PROFILE_STRING") or comment.startswith("CURA_OCTO_PROFILE_STRING"):
# Cura 15.04.* & OctoPrint Cura plugin
if comment.startswith("CURA_PROFILE_STRING"):
prefix = "CURA_PROFILE_STRING:"
else:
@ -280,6 +282,13 @@ class gcode(object):
self._filamentDiameter = float(curaOptions["filament_diameter"])
except:
self._filamentDiameter = 0.0
elif comment.startswith("filamentDiameter,"):
# Simplify3D
filamentValue = comment.split(",", 1)[1].strip()
try:
self._filamentDiameter = float(filamentValue)
except ValueError:
self._filamentDiameter = 0.0
line = line[0:line.find(';')]
G = getCodeInt(line, 'G')
@ -294,15 +303,28 @@ class gcode(object):
e = getCodeFloat(line, 'E')
f = getCodeFloat(line, 'F')
if x is not None or y is not None or z is not None:
# this is a move
move = True
else:
# print head stays on position
move = False
oldPos = pos
newPos = Vector3D(x if x is not None else pos.x,
y if y is not None else pos.y,
z if z is not None else pos.z)
# Use new coordinates if provided. If not provided, use prior coordinates in absolute
# and 0.0 in relative mode.
newPos = Vector3D(x if x is not None else (pos.x if posAbs else 0.0),
y if y is not None else (pos.y if posAbs else 0.0),
z if z is not None else (pos.z if posAbs else 0.0))
if posAbs:
# Absolute mode: scale coordinates and apply offsets
pos = newPos * scale + posOffset
else:
# Relative mode: scale and add to current position
pos += newPos * scale
if f is not None and f != 0:
feedrate = f
@ -310,10 +332,12 @@ class gcode(object):
if absoluteE:
# make sure e is relative
e -= currentE[currentExtruder]
# If move includes extrusion, calculate new min/max coordinates of model
if e > 0.0:
# extrusion -> relevant for print area & dimensions
# If move with extrusion, calculate new min/max coordinates of model
if e > 0.0 and move:
# extrusion and move -> relevant for print area & dimensions
self._minMax.record(pos)
totalExtrusion[currentExtruder] += e
currentE[currentExtruder] += e
maxExtrusion[currentExtruder] = max(maxExtrusion[currentExtruder],
@ -417,7 +441,6 @@ class gcode(object):
if throttle is not None:
throttle()
if self.progressCallback is not None:
self.progressCallback(100.0)