diff --git a/octoprint/gcodefiles.py b/octoprint/gcodefiles.py
index 7a9fe8eb..c8223e82 100644
--- a/octoprint/gcodefiles.py
+++ b/octoprint/gcodefiles.py
@@ -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
- - has the extension ".gcode" or ".stl"
+ - has any of the extensions listed in SUPPORTED_EXTENSIONS
- exists and is a file (not a directory) if "mustExist" is set to True
diff --git a/octoprint/printer.py b/octoprint/printer.py
index a118fa73..352b7f4f 100644
--- a/octoprint/printer.py
+++ b/octoprint/printer.py
@@ -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):
diff --git a/octoprint/server.py b/octoprint/server.py
index 356c086e..8eef377d 100644
--- a/octoprint/server.py
+++ b/octoprint/server.py
@@ -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:
diff --git a/octoprint/slicers/cura/__init__.py b/octoprint/slicers/cura/__init__.py
index a91b2bae..0851e959 100644
--- a/octoprint/slicers/cura/__init__.py
+++ b/octoprint/slicers/cura/__init__.py
@@ -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()
diff --git a/octoprint/static/js/app/dataupdater.js b/octoprint/static/js/app/dataupdater.js
index 919ea763..226bc6f6 100644
--- a/octoprint/static/js/app/dataupdater.js
+++ b/octoprint/static/js/app/dataupdater.js
@@ -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;
}
}
diff --git a/octoprint/static/js/app/viewmodels/gcodefiles.js b/octoprint/static/js/app/viewmodels/gcodefiles.js
index fdf2c411..c14ad2a2 100644
--- a/octoprint/static/js/app/viewmodels/gcodefiles.js
+++ b/octoprint/static/js/app/viewmodels/gcodefiles.js
@@ -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);
}
});
diff --git a/octoprint/static/js/app/viewmodels/settings.js b/octoprint/static/js/app/viewmodels/settings.js
index c8cdd214..2561e535 100644
--- a/octoprint/static/js/app/viewmodels/settings.js
+++ b/octoprint/static/js/app/viewmodels/settings.js
@@ -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()
}
diff --git a/octoprint/templates/settings.jinja2 b/octoprint/templates/settings.jinja2
index 9d81323c..c8187a7c 100644
--- a/octoprint/templates/settings.jinja2
+++ b/octoprint/templates/settings.jinja2
@@ -14,7 +14,7 @@
Features
Webcam
- Cura
+ Cura
{% if enableAccessControl %}Access Control{% endif %}
Api
@@ -22,8 +22,6 @@
Appearance
-