Allow definition of gcode scripts to be sent to the printer at various stages of the print process
This commit is contained in:
parent
f8955c0d1b
commit
b7a9be31eb
9 changed files with 154 additions and 77 deletions
|
|
@ -288,7 +288,7 @@ class Printer():
|
|||
|
||||
self._comm.setPause(not self._comm.isPaused())
|
||||
|
||||
def cancelPrint(self, disableMotorsAndHeater=True):
|
||||
def cancelPrint(self):
|
||||
"""
|
||||
Cancel the current printjob.
|
||||
"""
|
||||
|
|
@ -297,13 +297,6 @@ class Printer():
|
|||
|
||||
self._comm.cancelPrint()
|
||||
|
||||
if disableMotorsAndHeater:
|
||||
# disable motors, switch off hotends, bed and fan
|
||||
commands = ["M84"]
|
||||
commands.extend(map(lambda x: "M104 T%d S0" % x, range(settings().getInt(["printerParameters", "numExtruders"]))))
|
||||
commands.extend(["M140 S0", "M106 S0"])
|
||||
self.commands(commands)
|
||||
|
||||
# reset progress, height, print time
|
||||
self._setCurrentZ(None)
|
||||
self._setProgressData(None, None, None, None)
|
||||
|
|
|
|||
|
|
@ -99,11 +99,7 @@ def getSettings():
|
|||
"events": s.get(["system", "events"])
|
||||
},
|
||||
"terminalFilters": s.get(["terminalFilters"]),
|
||||
"cura": {
|
||||
"enabled": s.getBoolean(["cura", "enabled"]),
|
||||
"path": s.get(["cura", "path"]),
|
||||
"config": s.get(["cura", "config"])
|
||||
}
|
||||
"scripts": s.get(["scripts"])
|
||||
}
|
||||
|
||||
def process_plugin_result(name, plugin, result):
|
||||
|
|
@ -206,19 +202,8 @@ def setSettings():
|
|||
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"])
|
||||
|
||||
cura = data.get("cura", None)
|
||||
if cura:
|
||||
path = cura.get("path")
|
||||
if path:
|
||||
s.set(["cura", "path"], path)
|
||||
|
||||
config = cura.get("config")
|
||||
if config:
|
||||
s.set(["cura", "config"], config)
|
||||
|
||||
# Enabled is a boolean so we cannot check that we have a result
|
||||
enabled = cura.get("enabled")
|
||||
s.setBoolean(["cura", "enabled"], enabled)
|
||||
if "scripts" in data:
|
||||
if "gcode" in data["scripts"]: s.set(["scripts", "gcode"], data["scripts"]["gcode"])
|
||||
|
||||
if "plugins" in data:
|
||||
for name, plugin in octoprint.plugin.plugin_manager().get_implementations(octoprint.plugin.SettingsPlugin).items():
|
||||
|
|
|
|||
|
|
@ -161,6 +161,15 @@ default_settings = {
|
|||
{ "name": "Suppress M27 requests/responses", "regex": "(Send: M27)|(Recv: SD printing byte)" }
|
||||
],
|
||||
"plugins": {},
|
||||
"scripts": {
|
||||
"gcode": {
|
||||
"beforePrintStarted": None,
|
||||
"afterPrintDone": None,
|
||||
"afterPrintCancelled": None,
|
||||
"afterPrintPaused": None,
|
||||
"beforePrintResumed": None
|
||||
}
|
||||
},
|
||||
"devel": {
|
||||
"stylesheet": "css",
|
||||
"virtualPrinter": {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -59,7 +59,7 @@ $(function() {
|
|||
settingsDialog.modal()
|
||||
.css({
|
||||
width: 'auto',
|
||||
'margin-left': function() { return -($(this).width() /2); }
|
||||
'margin-left': function() { return -($(this).width() / 2); }
|
||||
});
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -157,9 +157,11 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
|
|||
self.folder_logs = ko.observable(undefined);
|
||||
self.folder_watched = ko.observable(undefined);
|
||||
|
||||
self.cura_enabled = ko.observable(undefined);
|
||||
self.cura_path = ko.observable(undefined);
|
||||
self.cura_config = ko.observable(undefined);
|
||||
self.scripts_gcode_beforePrintStarted = ko.observable(undefined);
|
||||
self.scripts_gcode_afterPrintDone = ko.observable(undefined);
|
||||
self.scripts_gcode_afterPrintCancelled = ko.observable(undefined);
|
||||
self.scripts_gcode_afterPrintPaused = ko.observable(undefined);
|
||||
self.scripts_gcode_beforePrintResumed = ko.observable(undefined);
|
||||
|
||||
self.temperature_profiles = ko.observableArray(undefined);
|
||||
|
||||
|
|
@ -281,9 +283,11 @@ function SettingsViewModel(loginStateViewModel, usersViewModel) {
|
|||
self.folder_logs(response.folder.logs);
|
||||
self.folder_watched(response.folder.watched);
|
||||
|
||||
self.cura_enabled(response.cura.enabled);
|
||||
self.cura_path(response.cura.path);
|
||||
self.cura_config(response.cura.config);
|
||||
self.scripts_gcode_beforePrintStarted(response.scripts.gcode.beforePrintStarted);
|
||||
self.scripts_gcode_afterPrintDone(response.scripts.gcode.afterPrintDone);
|
||||
self.scripts_gcode_afterPrintCancelled(response.scripts.gcode.afterPrintCancelled);
|
||||
self.scripts_gcode_afterPrintPaused(response.scripts.gcode.afterPrintPaused);
|
||||
self.scripts_gcode_beforePrintResumed(response.scripts.gcode.beforePrintResumed);
|
||||
|
||||
self.temperature_profiles(response.temperature.profiles);
|
||||
|
||||
|
|
@ -359,12 +363,16 @@ 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()
|
||||
"terminalFilters": self.terminalFilters(),
|
||||
"scripts": {
|
||||
"gcode": {
|
||||
"beforePrintStarted": self.scripts_gcode_beforePrintStarted(),
|
||||
"afterPrintDone": self.scripts_gcode_afterPrintDone(),
|
||||
"afterPrintCancelled": self.scripts_gcode_afterPrintCancelled(),
|
||||
"afterPrintPaused": self.scripts_gcode_afterPrintPaused(),
|
||||
"beforePrintResumed": self.scripts_gcode_beforePrintResumed()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
|
|
|
|||
|
|
@ -599,7 +599,16 @@ ul.dropdown-menu li a {
|
|||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.border_box {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
textarea.block {
|
||||
.border_box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#drop_overlay {
|
||||
position: fixed;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
<div id="settings_dialog" class="modal hide fade container" tabindex="-1" role="dialog" aria-labelledby="settings_dialog_label" aria-hidden="true">
|
||||
<div id="settings_dialog" class="modal hide fade container" tabindex="-1" role="dialog" aria-labelledby="settings_dialog_label" aria-hidden="true" data-replace="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h3 id="settings_dialog_label">{{ _('OctoPrint Settings') }}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-list span4" id="settingsTabs">
|
||||
<ul class="nav nav-list span3" id="settingsTabs">
|
||||
<li class="nav-header">{{ _('Printer') }}</li>
|
||||
<li class="active"><a href="#settings_serialConnection" data-toggle="tab">{{ _('Serial Connection') }}</a></li>
|
||||
<li><a href="#settings_printerParameters" data-toggle="tab">{{ _('Printer Parameters') }}</a></li>
|
||||
<li><a href="#settings_temperature" data-toggle="tab">{{ _('Temperatures') }}</a></li>
|
||||
<li><a href="#settings_terminalFilters" data-toggle="tab">{{ _('Terminal filters') }}</a></li>
|
||||
<li><a href="#settings_gcodeScripts" data-toggle="tab">{{ _('GCODE Scripts') }}</a></li>
|
||||
<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>
|
||||
|
|
@ -30,7 +31,7 @@
|
|||
{% endif %}
|
||||
</ul>
|
||||
|
||||
<div class="tab-content span8">
|
||||
<div class="tab-content span9">
|
||||
<div class="tab-pane active" id="settings_serialConnection">
|
||||
<form class="form-horizontal">
|
||||
<div class="control-group">
|
||||
|
|
@ -404,6 +405,40 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane" id="settings_gcodeScripts">
|
||||
<form class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Before print job starts') }}</label>
|
||||
<div class="controls">
|
||||
<textarea rows="3" class="block" data-bind="value: scripts_gcode_beforePrintStarted"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('After print job completes') }}</label>
|
||||
<div class="controls">
|
||||
<textarea rows="3" class="block" data-bind="value: scripts_gcode_afterPrintDone"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('After print job is cancelled') }}</label>
|
||||
<div class="controls">
|
||||
<textarea rows="3" class="block" data-bind="value: scripts_gcode_afterPrintCancelled"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('After print job is paused') }}</label>
|
||||
<div class="controls">
|
||||
<textarea rows="3" class="block" data-bind="value: scripts_gcode_afterPrintPaused"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">{{ _('Before print job is resumed') }}</label>
|
||||
<div class="controls">
|
||||
<textarea rows="3" class="block" data-bind="value: scripts_gcode_beforePrintResumed"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tab-pane" id="settings_appearance">
|
||||
<form class="form-horizontal">
|
||||
<div class="control-group">
|
||||
|
|
|
|||
|
|
@ -381,6 +381,24 @@ class MachineCom(object):
|
|||
elif self.isOperational():
|
||||
self._sendCommand(cmd)
|
||||
|
||||
def sendGcodeScript(self, scriptName):
|
||||
gcodeScripts = settings().get(["scripts", "gcode"])
|
||||
|
||||
if scriptName in gcodeScripts and gcodeScripts[scriptName] is not None:
|
||||
script = gcodeScripts[scriptName]
|
||||
else:
|
||||
if scriptName == "afterPrintCancelled":
|
||||
commands = ["M84"]
|
||||
commands.extend(map(lambda x: "M104 T%d S0" % x, range(settings().getInt(["printerParameters", "numExtruders"]))))
|
||||
commands.extend(["M140 S0", "M106 S0"])
|
||||
script = "\n".join(commands)
|
||||
else:
|
||||
return
|
||||
|
||||
scriptLines = map(str.strip, script.split("\n"))
|
||||
for line in scriptLines:
|
||||
self.sendCommand(line)
|
||||
|
||||
def startPrint(self):
|
||||
if not self.isOperational() or self.isPrinting():
|
||||
return
|
||||
|
|
@ -399,13 +417,17 @@ class MachineCom(object):
|
|||
"origin": self._currentFile.getFileLocation()
|
||||
})
|
||||
|
||||
self.sendGcodeScript("beforePrintStarted")
|
||||
|
||||
if self.isSdFileSelected():
|
||||
if wasPaused:
|
||||
self.sendCommand("M26 S0")
|
||||
self._currentFile.setFilepos(0)
|
||||
self.sendCommand("M24")
|
||||
else:
|
||||
self._sendNext()
|
||||
line = self._getNext()
|
||||
if line is not None:
|
||||
self.sendCommand(line)
|
||||
except:
|
||||
self._logger.exception("Error while trying to start printing")
|
||||
self._errorValue = getExceptionString()
|
||||
|
|
@ -459,6 +481,8 @@ class MachineCom(object):
|
|||
self.sendCommand("M25") # pause print
|
||||
self.sendCommand("M26 S0") # reset position in file to byte 0
|
||||
|
||||
self.sendGcodeScript("afterPrintCancelled")
|
||||
|
||||
eventManager().fire(Events.PRINT_CANCELLED, {
|
||||
"file": self._currentFile.getFilename(),
|
||||
"filename": os.path.basename(self._currentFile.getFilename()),
|
||||
|
|
@ -471,10 +495,13 @@ class MachineCom(object):
|
|||
|
||||
if not pause and self.isPaused():
|
||||
self._changeState(self.STATE_PRINTING)
|
||||
self.sendGcodeScript("beforePrintResumed")
|
||||
if self.isSdFileSelected():
|
||||
self.sendCommand("M24")
|
||||
else:
|
||||
self._sendNext()
|
||||
line = self._getNext()
|
||||
if line is not None:
|
||||
self.sendCommand(line)
|
||||
|
||||
eventManager().fire(Events.PRINT_RESUMED, {
|
||||
"file": self._currentFile.getFilename(),
|
||||
|
|
@ -485,6 +512,7 @@ class MachineCom(object):
|
|||
self._changeState(self.STATE_PAUSED)
|
||||
if self.isSdFileSelected():
|
||||
self.sendCommand("M25") # pause print
|
||||
self.sendGcodeScript("afterPrintPaused")
|
||||
|
||||
eventManager().fire(Events.PRINT_PAUSED, {
|
||||
"file": self._currentFile.getFilename(),
|
||||
|
|
@ -878,8 +906,12 @@ class MachineCom(object):
|
|||
|
||||
### Operational
|
||||
elif self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PAUSED:
|
||||
# if we still have commands to process, process them
|
||||
if "ok" in line and not self._commandQueue.empty():
|
||||
self._sendCommand(self._commandQueue.get())
|
||||
|
||||
#Request the temperature on comm timeout (every 5 seconds) when we are not printing.
|
||||
if line == "" or "wait" in line:
|
||||
elif line == "" or "wait" in line:
|
||||
if self._resendDelta is not None:
|
||||
self._resendNextCommand()
|
||||
elif not self._commandQueue.empty():
|
||||
|
|
@ -887,6 +919,7 @@ class MachineCom(object):
|
|||
else:
|
||||
self._sendCommand("M105")
|
||||
tempRequestTimeout = getNewTimeout("temperature")
|
||||
|
||||
# resend -> start resend procedure from requested line
|
||||
elif line.lower().startswith("resend") or line.lower().startswith("rs"):
|
||||
if settings().get(["feature", "swallowOkAfterResend"]):
|
||||
|
|
@ -1018,39 +1051,44 @@ class MachineCom(object):
|
|||
self._log("Recv: %s" % sanitizeAscii(ret))
|
||||
return ret
|
||||
|
||||
def _getNext(self):
|
||||
line = self._currentFile.getNext()
|
||||
if line is None:
|
||||
if self.isStreaming():
|
||||
self._sendCommand("M29")
|
||||
|
||||
remote = self._currentFile.getRemoteFilename()
|
||||
payload = {
|
||||
"local": self._currentFile.getLocalFilename(),
|
||||
"remote": remote,
|
||||
"time": self.getPrintTime()
|
||||
}
|
||||
|
||||
self._currentFile = None
|
||||
self._changeState(self.STATE_OPERATIONAL)
|
||||
self._callback.mcFileTransferDone(remote)
|
||||
eventManager().fire(Events.TRANSFER_DONE, payload)
|
||||
self.refreshSdFiles()
|
||||
else:
|
||||
payload = {
|
||||
"file": self._currentFile.getFilename(),
|
||||
"filename": os.path.basename(self._currentFile.getFilename()),
|
||||
"origin": self._currentFile.getFileLocation(),
|
||||
"time": self.getPrintTime()
|
||||
}
|
||||
self._callback.mcPrintjobDone()
|
||||
self._changeState(self.STATE_OPERATIONAL)
|
||||
eventManager().fire(Events.PRINT_DONE, payload)
|
||||
|
||||
self.sendGcodeScript("afterPrintDone")
|
||||
return line
|
||||
|
||||
def _sendNext(self):
|
||||
with self._sendNextLock:
|
||||
line = self._currentFile.getNext()
|
||||
if line is None:
|
||||
if self.isStreaming():
|
||||
self._sendCommand("M29")
|
||||
|
||||
remote = self._currentFile.getRemoteFilename()
|
||||
payload = {
|
||||
"local": self._currentFile.getLocalFilename(),
|
||||
"remote": remote,
|
||||
"time": self.getPrintTime()
|
||||
}
|
||||
|
||||
self._currentFile = None
|
||||
self._changeState(self.STATE_OPERATIONAL)
|
||||
self._callback.mcFileTransferDone(remote)
|
||||
eventManager().fire(Events.TRANSFER_DONE, payload)
|
||||
self.refreshSdFiles()
|
||||
else:
|
||||
payload = {
|
||||
"file": self._currentFile.getFilename(),
|
||||
"filename": os.path.basename(self._currentFile.getFilename()),
|
||||
"origin": self._currentFile.getFileLocation(),
|
||||
"time": self.getPrintTime()
|
||||
}
|
||||
self._callback.mcPrintjobDone()
|
||||
self._changeState(self.STATE_OPERATIONAL)
|
||||
eventManager().fire(Events.PRINT_DONE, payload)
|
||||
return
|
||||
|
||||
self._sendCommand(line, True)
|
||||
self._callback.mcProgress()
|
||||
line = self._getNext()
|
||||
if line is not None:
|
||||
self._sendCommand(line, True)
|
||||
self._callback.mcProgress()
|
||||
|
||||
def _handleResendRequest(self, line):
|
||||
lineToResend = None
|
||||
|
|
|
|||
Loading…
Reference in a new issue