Fixed a couple of merge issues, changed way Cura is called (just using "python" won't work in environments where the default python executable doesn't bring the necessary dependencies to run Cura, now using Cura startup script instead), introduced some messaging to notify UI what's going on.

This commit is contained in:
Gina Häußge 2013-09-09 16:51:46 +02:00
parent b352078917
commit f07305ae4f
9 changed files with 128 additions and 86 deletions

View file

@ -11,7 +11,9 @@ import time
import logging
import octoprint.util as util
import octoprint.util.gcodeInterpreter as gcodeInterpreter
from octoprint.settings import settings
from octoprint.events import eventManager
from werkzeug.utils import secure_filename
@ -141,16 +143,11 @@ class GcodeManager:
return self.processGcode(absolutePath)
curaEnabled = self._settings.get(["cura", "enabled"])
if isSTLFileName(filename) and curaEnabled and local:
gcodePath = util.genGcodeFileName(absolutePath)
callBackArgs = [gcodePath]
callBack = self.processGcode
self.processSTL(absolutePath, callBack, callBackArgs)
if curaEnabled and isSTLFileName(filename) and local:
self.processStl(absolutePath)
return filename
def getFutureFileName(self, file):
if not file:
return None
@ -161,16 +158,22 @@ class GcodeManager:
return self._getBasicFilename(absolutePath)
def processSTL(self, absolutePath, callBack, callBackArgs):
def processStl(self, absolutePath):
from octoprint.slicers.cura import CuraFactory
cura = CuraFactory.create_slicer()
gcodePath = util.genGcodeFileName(absolutePath)
config = self._settings.get(["cura", "config"])
cura.process_file(
config, gcodePath, absolutePath, callBack, callBackArgs)
def stlProcessed(stlPath, gcodePath):
eventManager().fire("SlicingDone", {"stl": self._getBasicFilename(stlPath), "gcode": self._getBasicFilename(gcodePath)})
self.processGcode(gcodePath)
eventManager().fire("SlicingStarted", {"stl": self._getBasicFilename(absolutePath), "gcode": self._getBasicFilename(gcodePath)})
cura.process_file(config, gcodePath, absolutePath, stlProcessed, [absolutePath, gcodePath])
def processGcode(self, absolutePath):
if absolutePath is None:
return None
@ -205,12 +208,9 @@ class GcodeManager:
if absolutePath is None:
return
# The files may or may not actually exits on the HD.
try:
os.remove(absolutePath)
os.remove(absolutePath)
if os.path.exists(stlPath):
os.remove(stlPath)
except OSError:
pass
if filename in self._metadata.keys():
del self._metadata[filename]
@ -223,7 +223,7 @@ class GcodeManager:
Ensures that the file
<ul>
<li>has the extension ".gcode" or ".stl"</li>
<li>has any of the extensions listed in SUPPORTED_EXTENSIONS</li>
<li>exists and is a file (not a directory) if "mustExist" is set to True</li>
</ul>

View file

@ -190,9 +190,6 @@ class Printer():
logging.info("Cannot load file: printer not connected or currently busy")
return
if self._comm.isBusy() or self._comm.isStreaming():
return
self._printAfterSelect = printAfterSelect
self._comm.selectFile(filename, sd)
self._setProgressData(0, None, None, None)
@ -491,7 +488,7 @@ class Printer():
callBackArgs = [gcodeFileName, gcodePath]
callBack = self.streamSdFile
self._gcodeManager.processSTL(
self._gcodeManager.processStl(
absolutePath, callBack, callBackArgs)
def streamSdFile(self, filename, path):

View file

@ -81,6 +81,8 @@ class PrinterStateConnection(SockJSConnection):
self._eventManager.fire("ClientOpened")
self._eventManager.subscribe("MovieDone", self._onMovieDone)
self._eventManager.subscribe("SlicingStarted", self._onSlicingStarted)
self._eventManager.subscribe("SlicingDone", self._onSlicingDone)
global timelapse
octoprint.timelapse.notifyCallbacks(timelapse)
@ -93,6 +95,8 @@ class PrinterStateConnection(SockJSConnection):
self._eventManager.fire("ClientClosed")
self._eventManager.unsubscribe("MovieDone", self._onMovieDone)
self._eventManager.unsubscribe("SlicingStarted", self._onSlicingStarted)
self._eventManager.unsubscribe("SlicingDone", self._onSlicingDone)
def on_message(self, message):
pass
@ -121,8 +125,8 @@ class PrinterStateConnection(SockJSConnection):
def sendHistoryData(self, data):
self._emit("history", data)
def sendUpdateTrigger(self, type):
self._emit("updateTrigger", type)
def sendUpdateTrigger(self, type, payload=None):
self._emit("updateTrigger", {"type": type, "payload": payload})
def sendFeedbackCommandOutput(self, name, output):
self._emit("feedbackCommandOutput", {"name": name, "output": output})
@ -145,6 +149,12 @@ class PrinterStateConnection(SockJSConnection):
def _onMovieDone(self, event, payload):
self.sendUpdateTrigger("timelapseFiles")
def _onSlicingStarted(self, event, payload):
self.sendUpdateTrigger("slicingStarted", payload)
def _onSlicingDone(self, event, payload):
self.sendUpdateTrigger("slicingDone", payload)
def _emit(self, type, payload):
self.send({type: payload})
@ -756,11 +766,9 @@ def setSettings():
if "system" in data.keys():
if "actions" in data["system"].keys(): s.set(["system", "actions"], data["system"]["actions"])
if "events" in data["system"].keys(): s.set(["system", "events"], data["system"]["events"])
if "events" in data["system"].keys(): s.set(["system", "events"], data["system"]["events"])
cura = data.get("cura", None)
if cura:
path = cura.get("path")
if path:

View file

@ -2,6 +2,7 @@ __author__ = "Ross Hendrickson savorywatt"
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
import logging
import os
from octoprint.settings import settings
@ -30,6 +31,7 @@ class Cura(object):
raise Exception("Unable to create CuraEngine - no path specified")
self.cura_path = cura_path
self._logger = logging.getLogger(__name__)
def process_file(
@ -48,12 +50,18 @@ class Cura(object):
def start_thread(call_back, call_back_args, call_args, cwd):
import subprocess
process = subprocess.call(call_args, cwd=cwd)
call_back(*call_back_args)
self._logger.info("Running %r in %s" % (call_args, cwd))
try:
subprocess.check_call(call_args, cwd=cwd)
call_back(*call_back_args)
except subprocess.CalledProcessError as (e):
self._logger.warn("Could not slice via Cura, got return code %r" % e.returncode)
args = ['python', '-m', 'Cura.cura', '-i', config, '-s', file_path, '-o', gcode]
executable = self.cura_path
(workingDir, ignored) = os.path.split(executable)
args = [executable, '-i', config, '-s', file_path, '-o', gcode]
thread = threading.Thread(target=start_thread, args=(call_back,
call_back_args, args, self.cura_path))
call_back_args, args, workingDir))
thread.start()

View file

@ -74,45 +74,52 @@ function DataUpdater(loginStateViewModel, connectionViewModel, printerStateViewM
self._onmessage = function(e) {
for (var prop in e.data) {
var payload = e.data[prop];
var data = e.data[prop];
switch (prop) {
case "history": {
self.connectionViewModel.fromHistoryData(payload);
self.printerStateViewModel.fromHistoryData(payload);
self.temperatureViewModel.fromHistoryData(payload);
self.controlViewModel.fromHistoryData(payload);
self.terminalViewModel.fromHistoryData(payload);
self.timelapseViewModel.fromHistoryData(payload);
self.gcodeViewModel.fromHistoryData(payload);
self.gcodeFilesViewModel.fromCurrentData(payload);
self.connectionViewModel.fromHistoryData(data);
self.printerStateViewModel.fromHistoryData(data);
self.temperatureViewModel.fromHistoryData(data);
self.controlViewModel.fromHistoryData(data);
self.terminalViewModel.fromHistoryData(data);
self.timelapseViewModel.fromHistoryData(data);
self.gcodeViewModel.fromHistoryData(data);
self.gcodeFilesViewModel.fromCurrentData(data);
break;
}
case "current": {
self.connectionViewModel.fromCurrentData(payload);
self.printerStateViewModel.fromCurrentData(payload);
self.temperatureViewModel.fromCurrentData(payload);
self.controlViewModel.fromCurrentData(payload);
self.terminalViewModel.fromCurrentData(payload);
self.timelapseViewModel.fromCurrentData(payload);
self.gcodeViewModel.fromCurrentData(payload);
self.gcodeFilesViewModel.fromCurrentData(payload);
self.connectionViewModel.fromCurrentData(data);
self.printerStateViewModel.fromCurrentData(data);
self.temperatureViewModel.fromCurrentData(data);
self.controlViewModel.fromCurrentData(data);
self.terminalViewModel.fromCurrentData(data);
self.timelapseViewModel.fromCurrentData(data);
self.gcodeViewModel.fromCurrentData(data);
self.gcodeFilesViewModel.fromCurrentData(data);
break;
}
case "updateTrigger": {
if (payload == "gcodeFiles") {
var type = data["type"];
var payload = data["payload"];
if (type == "gcodeFiles") {
gcodeFilesViewModel.requestData();
} else if (payload == "timelapseFiles") {
} else if (type == "timelapseFiles") {
timelapseViewModel.requestData();
} else if (type == "slicingStarted") {
$.pnotify({title: "Slicing started", text: "Slicing " + payload.stl + " to " + payload.gcode});
} else if (type == "slicingDone") {
$.pnotify({title: "Slicing done", text: "Sliced " + payload.stl + " to " + payload.gcode});
gcodeFilesViewModel.requestData(payload.gcode);
}
break;
}
case "feedbackCommandOutput": {
self.controlViewModel.fromFeedbackCommandData(payload);
self.controlViewModel.fromFeedbackCommandData(data);
break;
}
case "timelapse": {
self.printerStateViewModel.fromTimelapseData(payload);
self.printerStateViewModel.fromTimelapseData(data);
break;
}
}

View file

@ -96,12 +96,15 @@ function GcodeFilesViewModel(printerStateViewModel, loginStateViewModel) {
self.isSdReady(data.flags.sdReady);
}
self.requestData = function() {
self.requestData = function(filenameOverride) {
$.ajax({
url: AJAX_BASEURL + "gcodefiles",
method: "GET",
dataType: "json",
success: function(response) {
if (filenameOverride) {
response.filename = filenameOverride
}
self.fromResponse(response);
}
});

View file

@ -48,6 +48,10 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
self.folder_timelapseTmp = ko.observable(undefined);
self.folder_logs = ko.observable(undefined);
self.cura_enabled = ko.observable(undefined);
self.cura_path = ko.observable(undefined);
self.cura_config = ko.observable(undefined);
self.temperature_profiles = ko.observableArray(undefined);
self.system_actions = ko.observableArray([]);
@ -121,6 +125,10 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
self.folder_timelapseTmp(response.folder.timelapseTmp);
self.folder_logs(response.folder.logs);
self.cura_enabled(response.cura.enabled);
self.cura_path(response.cura.path);
self.cura_config(response.cura.config);
self.temperature_profiles(response.temperature.profiles);
self.system_actions(response.system.actions);
@ -182,6 +190,11 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
"system": {
"actions": self.system_actions()
},
"cura": {
"enabled": self.cura_enabled(),
"path": self.cura_path(),
"config": self.cura_config()
},
"terminalFilters": self.terminalFilters()
}

View file

@ -14,7 +14,7 @@
<li class="nav-header">Features</li>
<li><a href="#settings_features" data-toggle="tab">Features</a></li>
<li><a href="#settings_webcam" data-toggle="tab">Webcam</a></li>
<li><a href="#settings_cura" data-toggle="tab">Cura</a></li>
<li><a href="#settings_cura" data-toggle="tab">Cura</a></li>
{% if enableAccessControl %}<li><a href="#settings_users" data-toggle="tab">Access Control</a></li>{% endif %}
<li><a href="#settings_api" data-toggle="tab">Api</a></li>
<li class="nav-header">OctoPrint</li>
@ -22,8 +22,6 @@
<li><a href="#settings_appearance" data-toggle="tab">Appearance</a></li>
</ul>
<div class="tab-content span8">
<div class="tab-pane active" id="settings_printerParameters">
<div class="tab-content span8">
<div class="tab-pane active" id="settings_serialConnection">
<form class="form-horizontal">
@ -317,28 +315,28 @@
</div>
</form>
</div>
<div class="tab-pane" id="settings_cura">
<form class="form-horizontal">
<div class="control-group">
<label class="control-label" for="settings-curaEnabled">Cura Enabled</label>
<div class="controls">
<input type="checkbox" data-bind="checked: cura_enabled">
</div>
</div>
<div class="control-group">
<label class="control-label" for="settings-curaPath">CuraEngine Path</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: cura_path">
</div>
</div>
<div class="control-group">
<label class="control-label" for="settings-curaConfig">Cura Config</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: cura_config">
</div>
</div>
</form>
</div>
<div class="tab-pane" id="settings_cura">
<form class="form-horizontal">
<div class="control-group">
<label class="control-label" for="settings-curaEnabled">Enable slicing via Cura</label>
<div class="controls">
<input type="checkbox" data-bind="checked: cura_enabled">
</div>
</div>
<div class="control-group">
<label class="control-label" for="settings-curaPath">Path to Cura</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: cura_path">
</div>
</div>
<div class="control-group">
<label class="control-label" for="settings-curaConfig">Path to Cura config</label>
<div class="controls">
<input type="text" class="input-block-level" data-bind="value: cura_config">
</div>
</div>
</form>
</div>
<div class="tab-pane" id="settings_api">
<form class="form-horizontal">
<div class="control-group">

View file

@ -9,6 +9,7 @@ import time
from octoprint.settings import settings
def getFormattedSize(num):
"""
Taken from http://stackoverflow.com/a/1094933/2028598
@ -51,8 +52,6 @@ def getClass(name):
m = getattr(m, comp)
return m
def matchesGcode(line, gcode):
return re.search("^\s*%s\D" % gcode, line, re.I)
def isGcodeFileName(filename):
"""Simple helper to determine if a filename has the .gcode extension.
@ -61,7 +60,8 @@ def isGcodeFileName(filename):
:returns boolean:
"""
return "." in filename and filename.rsplit(".", 1)[1] in ["gcode", "GCODE"]
return "." in filename and filename.rsplit(".", 1)[1].lower() in ["gcode", "gco"]
def isSTLFileName(filename):
"""Simple helper to determine if a filename has the .stl extension.
@ -70,10 +70,10 @@ def isSTLFileName(filename):
:returns boolean:
"""
return "." in filename and filename.rsplit(".", 1)[1] in ["stl", "STL"]
def genGcodeFileName(filename):
return "." in filename and filename.rsplit(".", 1)[1].lower() in ["stl"]
def genGcodeFileName(filename):
if not filename:
return None
@ -81,9 +81,9 @@ def genGcodeFileName(filename):
return filename + ".gcode"
return filename.replace('.stl', '.gcode')
def genStlFileName(filename):
def genStlFileName(filename):
if not filename:
return None
@ -91,7 +91,8 @@ def genStlFileName(filename):
return filename + ".stl"
return filename.replace('.gcode', '.stl')
def isDevVersion():
gitPath = os.path.abspath(os.path.join(os.path.split(os.path.abspath(__file__))[0], "../../.git"))
return os.path.exists(gitPath)
@ -143,3 +144,10 @@ def getFreeBytes(path):
else:
st = os.statvfs(path)
return st.f_bavail * st.f_frsize
def getRemoteAddress(request):
forwardedFor = request.headers.get("X-Forwarded-For", None)
if forwardedFor is not None:
return forwardedFor.split(",")[0]
return request.remote_addr