Merge branch 'devel' of github.com:foosel/OctoPrint into fixControl
This commit is contained in:
commit
d4a96c53c3
12 changed files with 213 additions and 96 deletions
|
|
@ -75,6 +75,12 @@ Issue a connection command
|
|||
disconnect
|
||||
Instructs OctoPrint to disconnect from the printer.
|
||||
|
||||
fake_ack
|
||||
Fakes an acknowledgement message for OctoPrint in case one got lost on the serial line and the communication
|
||||
with the printer since stalled. This should only be used in "emergencies" (e.g. to save prints), the reason
|
||||
for the lost acknowledgement should always be properly investigated and removed instead of depending on this
|
||||
"symptom solver".
|
||||
|
||||
**Example Connect Request**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
|
@ -114,7 +120,24 @@ Issue a connection command
|
|||
|
||||
HTTP/1.1 204 No Content
|
||||
|
||||
:json string command: The command to issue, either ``connect`` or ``disconnect``
|
||||
**Example FakeAck Request**
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/connection HTTP/1.1
|
||||
Host: example.com
|
||||
Content-Type: application/json
|
||||
X-Api-Key: abcdef...
|
||||
|
||||
{
|
||||
"command": "fake_ack"
|
||||
}
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 204 No Content
|
||||
|
||||
:json string command: The command to issue, either ``connect``, ``disconnect`` or ``fake_ack``.
|
||||
:json string port: ``connect`` command: The port to connect to. If left out either the existing ``portPreference``
|
||||
will be used, or if that is not available OctoPrint will attempt auto detection. Must be part
|
||||
of the available ports.
|
||||
|
|
|
|||
|
|
@ -373,10 +373,18 @@ Use the following settings to enable or disable OctoPrint features:
|
|||
# during connect.
|
||||
waitForStartOnConnect: false
|
||||
|
||||
# Specifies whether OctoPrint should send linenumber + checksum with every command. Needed for
|
||||
# Specifies whether OctoPrint should send linenumber + checksum with every printer command. Needed for
|
||||
# successful communication with Repetier firmware
|
||||
alwaysSendChecksum: false
|
||||
|
||||
# Specifies whether OctoPrint should also send linenumber + checksum with commands that are *not*
|
||||
# detected as valid GCODE (as in, they do not match the regular expression "^\s*([GM]\d+|T)").
|
||||
sendChecksumWithUnknownCommands: false
|
||||
|
||||
# Specifies whether OctoPrint should also use up acknowledgments ("ok") for commands that are *not*
|
||||
# detected as valid GCODE (as in, they do not match the regular expression "^\s*([GM]\d+|T)").
|
||||
unknownCommandsNeedAck: false
|
||||
|
||||
# Whether to ignore the first ok after a resend response. Needed for successful communication with
|
||||
# Repetier firmware
|
||||
swallowOkAfterResend: false
|
||||
|
|
|
|||
|
|
@ -542,10 +542,11 @@ class PluginManager(object):
|
|||
url=module_pkginfo.home_page,
|
||||
license=module_pkginfo.license
|
||||
))
|
||||
package_name = module_pkginfo.name
|
||||
|
||||
plugin = self._import_plugin_from_module(key, **kwargs)
|
||||
if plugin:
|
||||
plugin.origin = ("entry_point", group, module_name)
|
||||
plugin.origin = ("entry_point", group, module_name, package_name)
|
||||
plugin.enabled = False
|
||||
result[key] = plugin
|
||||
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ class TemplatePlugin(OctoPrintPlugin, ReloadNeedingPlugin):
|
|||
The right part of the navigation bar located at the top of the UI can be enriched with additional links. Note that
|
||||
with the current implementation, plugins will always be located *to the left* of the existing links.
|
||||
|
||||
The included template must be called ``<pluginname>_navbar.jinja2`` (e.g. ``myplugin_navbar.jinja2``) unless
|
||||
The included template must be called ``<plugin identifier>_navbar.jinja2`` (e.g. ``myplugin_navbar.jinja2``) unless
|
||||
overridden by the configuration supplied through :func:`get_template_configs`.
|
||||
|
||||
The template will be already wrapped into the necessary structure, plugins just need to supply the pure content. The
|
||||
|
|
@ -192,7 +192,7 @@ class TemplatePlugin(OctoPrintPlugin, ReloadNeedingPlugin):
|
|||
The left side bar containing Connection, State and Files sections can be enriched with additional sections. Note
|
||||
that with the current implementations, plugins will always be located *beneath* the existing sections.
|
||||
|
||||
The included template must be called ``<pluginname>_sidebar.jinja2`` (e.g. ``myplugin_sidebar.jinja2``) unless
|
||||
The included template must be called ``<plugin identifier>_sidebar.jinja2`` (e.g. ``myplugin_sidebar.jinja2``) unless
|
||||
overridden by the configuration supplied through :func:`get_template_configs`.
|
||||
|
||||
The template will be already wrapped into the necessary structure, plugins just need to supply the pure content. The
|
||||
|
|
@ -204,7 +204,7 @@ class TemplatePlugin(OctoPrintPlugin, ReloadNeedingPlugin):
|
|||
plugins. Note that with the current implementation, plugins will always be located *to the right* of the existing
|
||||
tabs.
|
||||
|
||||
The included template must be called ``<pluginname>_tab.jinja2`` (e.g. ``myplugin_tab.jinja2``) unless
|
||||
The included template must be called ``<plugin identifier>_tab.jinja2`` (e.g. ``myplugin_tab.jinja2``) unless
|
||||
overridden by the configuration supplied through :func:`get_template_configs`.
|
||||
|
||||
The template will be already wrapped into the necessary structure, plugins just need to supply the pure content. The
|
||||
|
|
@ -216,7 +216,7 @@ class TemplatePlugin(OctoPrintPlugin, ReloadNeedingPlugin):
|
|||
will always be listed beneath the "Plugins" header in the settings link list, ordered alphabetically after
|
||||
their displayed name.
|
||||
|
||||
The included template must be called ``<pluginname>_settings.jinja2`` (e.g. ``myplugin_settings.jinja2``) unless
|
||||
The included template must be called ``<plugin identifier>_settings.jinja2`` (e.g. ``myplugin_settings.jinja2``) unless
|
||||
overridden by the configuration supplied through :func:`get_template_configs`.
|
||||
|
||||
The template will be already wrapped into the necessary structure, plugins just need to supply the pure content. The
|
||||
|
|
@ -272,11 +272,11 @@ class TemplatePlugin(OctoPrintPlugin, ReloadNeedingPlugin):
|
|||
* - template
|
||||
- Name of the template to inject, default value depends on the ``type``:
|
||||
|
||||
* ``navbar``: ``<pluginname>_navbar.jinja2``
|
||||
* ``sidebar``: ``<pluginname>_sidebar.jinja2``
|
||||
* ``tab``: ``<pluginname>_tab.jinja2``
|
||||
* ``settings``: ``<pluginname>_settings.jinja2``
|
||||
* ``generic``: ``<pluginname>.jinja2``
|
||||
* ``navbar``: ``<plugin identifier>_navbar.jinja2``
|
||||
* ``sidebar``: ``<plugin identifier>_sidebar.jinja2``
|
||||
* ``tab``: ``<plugin identifier>_tab.jinja2``
|
||||
* ``settings``: ``<plugin identifier>_settings.jinja2``
|
||||
* ``generic``: ``<plugin identifier>.jinja2``
|
||||
|
||||
* - suffix
|
||||
- Suffix to attach to the component identifier and the div identifier of the injected template. Will be
|
||||
|
|
@ -311,7 +311,7 @@ class TemplatePlugin(OctoPrintPlugin, ReloadNeedingPlugin):
|
|||
(and the divs will be ``tab_plugin_myplugin_1st`` and ``tab_plugin_myplugin_2nd``).
|
||||
|
||||
* - div
|
||||
- Id for the div containing the component. If not provided, defaults to ``<type>_plugin_<pluginname>`` plus
|
||||
- Id for the div containing the component. If not provided, defaults to ``<type>_plugin_<plugin identifier>`` plus
|
||||
the ``suffix`` if provided or required.
|
||||
* - replaces
|
||||
- Id of the component this one replaces, might be either one of the core components or a component
|
||||
|
|
@ -370,7 +370,7 @@ class TemplatePlugin(OctoPrintPlugin, ReloadNeedingPlugin):
|
|||
.. note::
|
||||
|
||||
As already outlined above, each template type has a default template name (i.e. the default navbar template
|
||||
of a plugin is called ``<pluginname>_navbar.jinja2``), which may be overridden using the template configuration.
|
||||
of a plugin is called ``<plugin identifier>_navbar.jinja2``), which may be overridden using the template configuration.
|
||||
If a plugin needs to include more than one template of a given type, it needs to provide an entry for each of
|
||||
those, since the implicit default template will only be included automatically if no other templates of that
|
||||
type are defined.
|
||||
|
|
|
|||
|
|
@ -149,6 +149,11 @@ class VirtualPrinter():
|
|||
self._sendOk()
|
||||
continue
|
||||
|
||||
if data.strip() == "version":
|
||||
from octoprint._version import get_versions
|
||||
self.outgoing.put("OctoPrint VirtualPrinter v" + get_versions()["version"])
|
||||
continue
|
||||
|
||||
if len(data.strip()) > 0 and self._okBeforeCommandOutput:
|
||||
self._sendOk()
|
||||
|
||||
|
|
@ -394,7 +399,6 @@ class VirtualPrinter():
|
|||
pass
|
||||
|
||||
if tool >= settings().getInt(["devel", "virtualPrinter", "numExtruders"]):
|
||||
self._sendOk()
|
||||
return
|
||||
|
||||
try:
|
||||
|
|
@ -406,7 +410,6 @@ class VirtualPrinter():
|
|||
self._waitForHeatup("tool%d" % tool)
|
||||
if settings().getBoolean(["devel", "virtualPrinter", "repetierStyleTargetTemperature"]):
|
||||
self.outgoing.put("TargetExtr%d:%d" % (tool, self.targetTemp[tool]))
|
||||
self._sendOk()
|
||||
|
||||
def _parseBedCommand(self, line):
|
||||
try:
|
||||
|
|
@ -418,7 +421,6 @@ class VirtualPrinter():
|
|||
self._waitForHeatup("bed")
|
||||
if settings().getBoolean(["devel", "virtualPrinter", "repetierStyleTargetTemperature"]):
|
||||
self.outgoing.put("TargetBed:%d" % self.bedTargetTemp)
|
||||
self._sendOk()
|
||||
|
||||
def _performMove(self, line):
|
||||
matchX = re.search("X([0-9.]+)", line)
|
||||
|
|
|
|||
|
|
@ -97,6 +97,14 @@ class PrinterInterface(object):
|
|||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def fake_ack(self):
|
||||
"""
|
||||
Fakes an acknowledgement for the communication layer. If the communication between OctoPrint and the printer
|
||||
gets stuck due to lost "ok" responses from the server due to communication issues, this can be used to get
|
||||
things going again.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def commands(self, commands):
|
||||
"""
|
||||
Sends the provided ``commands`` to the printer.
|
||||
|
|
|
|||
|
|
@ -212,6 +212,12 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback):
|
|||
return self._comm.getTransport()
|
||||
getTransport = util.deprecated("getTransport has been renamed to get_transport", since="1.2.0-dev-590", includedoc="Replaced by :func:`get_transport`")
|
||||
|
||||
def fake_ack(self):
|
||||
if self._comm is None:
|
||||
return
|
||||
|
||||
self._comm.fakeOk()
|
||||
|
||||
def commands(self, commands):
|
||||
"""
|
||||
Sends one or more gcode commands to the printer.
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ def connectionState():
|
|||
def connectionCommand():
|
||||
valid_commands = {
|
||||
"connect": [],
|
||||
"disconnect": []
|
||||
"disconnect": [],
|
||||
"fake_ack": []
|
||||
}
|
||||
|
||||
command, data, response = get_json_command_from_request(request, valid_commands)
|
||||
|
|
@ -68,6 +69,8 @@ def connectionCommand():
|
|||
printer.connect(port=port, baudrate=baudrate, profile=printerProfile)
|
||||
elif command == "disconnect":
|
||||
printer.disconnect()
|
||||
elif command == "fake_ack":
|
||||
printer.fake_ack()
|
||||
|
||||
return NO_CONTENT
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,8 @@ default_settings = {
|
|||
"temperatureGraph": True,
|
||||
"waitForStartOnConnect": False,
|
||||
"alwaysSendChecksum": False,
|
||||
"sendChecksumWithUnknownCommands": False,
|
||||
"unknownCommandsNeedAck": False,
|
||||
"sdSupport": True,
|
||||
"sdAlwaysAvailable": False,
|
||||
"swallowOkAfterResend": True,
|
||||
|
|
|
|||
|
|
@ -173,6 +173,16 @@ $(function() {
|
|||
}
|
||||
};
|
||||
|
||||
self.fakeAck = function() {
|
||||
$.ajax({
|
||||
url: API_BASEURL + "connection",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
contentType: "application/json; charset=UTF-8",
|
||||
data: JSON.stringify({"command": "fake_ack"})
|
||||
});
|
||||
};
|
||||
|
||||
self.handleKeyDown = function(event) {
|
||||
var keyCode = event.keyCode;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,3 +20,12 @@
|
|||
<small class="muted">{{ _('Hint: Use the arrow up/down keys to recall commands sent previously') }}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div><small><a href="#" class="muted" onclick="$(this).children().toggleClass('icon-caret-right icon-caret-down').parent().parent().parent().next().slideToggle('fast')"><i class="icon-caret-right"></i> {{ _('Advanced options') }}</a></small></div>
|
||||
<div class="hide">
|
||||
<button class="btn btn-block" type="button" data-bind="click: fakeAck, enable: isOperational() && loginState.isUser()">{{ _("Fake Acknowledgement") }}</button>
|
||||
<small class="muted">{{ _("If acknowledgements (\"ok\"s) sent by the firmware get lost due to issues with the serial communication to your printer, OctoPrint's communication with it can become stuck. If that happens, this can help. Please be advised that such occurences hint at general communication issues with your printer which will probably negatively influence your printing results and which you should therefore try to resolve!") }}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -155,6 +155,8 @@ class MachineCom(object):
|
|||
self._timeout = None
|
||||
|
||||
self._alwaysSendChecksum = settings().getBoolean(["feature", "alwaysSendChecksum"])
|
||||
self._sendChecksumWithUnknownCommands = settings().getBoolean(["feature", "sendChecksumWithUnknownCommands"])
|
||||
self._unknownCommandsNeedAck = settings().getBoolean(["feature", "unknownCommandsNeedAck"])
|
||||
self._currentLine = 1
|
||||
self._resendDelta = None
|
||||
self._lastLines = deque([], 50)
|
||||
|
|
@ -410,6 +412,9 @@ class MachineCom(object):
|
|||
def setTemperatureOffset(self, offsets):
|
||||
self._tempOffsets.update(offsets)
|
||||
|
||||
def fakeOk(self):
|
||||
self._clear_to_send.set()
|
||||
|
||||
def sendCommand(self, cmd, cmd_type=None, processed=False):
|
||||
cmd = cmd.encode('ascii', 'replace')
|
||||
if not processed:
|
||||
|
|
@ -420,7 +425,7 @@ class MachineCom(object):
|
|||
if self.isPrinting() and not self.isSdFileSelected():
|
||||
self._commandQueue.put((cmd, cmd_type))
|
||||
elif self.isOperational():
|
||||
self._sendCommand((cmd, cmd_type))
|
||||
self._sendCommand(cmd, cmd_type=cmd_type)
|
||||
|
||||
def sendGcodeScript(self, scriptName, replacements=None):
|
||||
context = dict()
|
||||
|
|
@ -520,7 +525,7 @@ class MachineCom(object):
|
|||
self.sendCommand(line)
|
||||
|
||||
# now make sure we actually do something, up until now we only filled up the queue
|
||||
self._sendFromQueue(sendChecksum=True)
|
||||
self._sendFromQueue()
|
||||
except:
|
||||
self._logger.exception("Error while trying to start printing")
|
||||
self._errorValue = get_exception_string()
|
||||
|
|
@ -621,7 +626,7 @@ class MachineCom(object):
|
|||
self.sendCommand(line)
|
||||
|
||||
# now make sure we actually do something, up until now we only filled up the queue
|
||||
self._sendFromQueue(sendChecksum=True)
|
||||
self._sendFromQueue()
|
||||
|
||||
eventManager().fire(Events.PRINT_RESUMED, payload)
|
||||
elif pause and self.isPrinting():
|
||||
|
|
@ -786,12 +791,12 @@ class MachineCom(object):
|
|||
#Start monitoring the serial port.
|
||||
self._timeout = get_new_timeout("communication")
|
||||
|
||||
startSeen = not settings().getBoolean(["feature", "waitForStartOnConnect"])
|
||||
startSeen = False
|
||||
supportRepetierTargetTemp = settings().getBoolean(["feature", "repetierTargetTemp"])
|
||||
|
||||
# enqueue an M105 first thing
|
||||
self._sendCommand("M105")
|
||||
if startSeen:
|
||||
if not settings().getBoolean(["feature", "waitForStartOnConnect"]):
|
||||
self._sendCommand("M110")
|
||||
self._clear_to_send.set()
|
||||
|
||||
while self._monitoring_active:
|
||||
|
|
@ -1013,7 +1018,7 @@ class MachineCom(object):
|
|||
self._baudrateDetectRetry -= 1
|
||||
self._serial.write('\n')
|
||||
self._log("Baudrate test retry: %d" % (self._baudrateDetectRetry))
|
||||
self._sendCommand("M105")
|
||||
self._sendCommand("M110")
|
||||
self._clear_to_send.set()
|
||||
else:
|
||||
baudrate = self._baudrateDetectList.pop(0)
|
||||
|
|
@ -1024,11 +1029,11 @@ class MachineCom(object):
|
|||
self._baudrateDetectRetry = 5
|
||||
self._timeout = get_new_timeout("communication")
|
||||
self._serial.write('\n')
|
||||
self._sendCommand("M105")
|
||||
self._sendCommand("M110")
|
||||
self._clear_to_send.set()
|
||||
except:
|
||||
self._log("Unexpected error while setting baudrate: %d %s" % (baudrate, get_exception_string()))
|
||||
elif 'start' in line or ('ok' in line and 'T:' in line):
|
||||
elif 'start' in line or 'ok' in line:
|
||||
self._onConnected()
|
||||
self._clear_to_send.set()
|
||||
|
||||
|
|
@ -1036,8 +1041,9 @@ class MachineCom(object):
|
|||
elif self._state == self.STATE_CONNECTING:
|
||||
if "start" in line and not startSeen:
|
||||
startSeen = True
|
||||
self._sendCommand("M110")
|
||||
self._clear_to_send.set()
|
||||
elif "ok" in line and startSeen:
|
||||
elif "ok" in line:
|
||||
self._onConnected()
|
||||
elif time.time() > self._timeout:
|
||||
self.close()
|
||||
|
|
@ -1074,7 +1080,7 @@ class MachineCom(object):
|
|||
self._resendNextCommand()
|
||||
|
||||
else:
|
||||
if self._sendFromQueue(sendChecksum=True):
|
||||
if self._sendFromQueue():
|
||||
pass
|
||||
elif not self.isSdPrinting():
|
||||
self._sendNext()
|
||||
|
|
@ -1164,9 +1170,18 @@ class MachineCom(object):
|
|||
eventManager().fire(Events.CONNECTED, payload)
|
||||
self.sendGcodeScript("afterPrinterConnected", replacements=dict(event=payload))
|
||||
|
||||
def _sendFromQueue(self, sendChecksum=False):
|
||||
def _sendFromQueue(self):
|
||||
if not self._commandQueue.empty() and not self.isStreaming():
|
||||
self._sendCommand(self._commandQueue.get(), sendChecksum)
|
||||
entry = self._commandQueue.get()
|
||||
if isinstance(entry, tuple):
|
||||
if not len(entry) == 2:
|
||||
return False
|
||||
cmd, cmd_type = entry
|
||||
else:
|
||||
cmd = entry
|
||||
cmd_type = None
|
||||
|
||||
self._sendCommand(cmd, cmd_type=cmd_type)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
|
@ -1325,7 +1340,7 @@ class MachineCom(object):
|
|||
with self._sendNextLock:
|
||||
line = self._getNext()
|
||||
if line is not None:
|
||||
self._sendCommand(line, True)
|
||||
self._sendCommand(line)
|
||||
self._callback.on_comm_progress()
|
||||
|
||||
def _handleResendRequest(self, line):
|
||||
|
|
@ -1385,64 +1400,53 @@ class MachineCom(object):
|
|||
self._lastResendNumber = None
|
||||
self._currentResendCount = 0
|
||||
|
||||
def _sendCommand(self, cmd_tuple, sendChecksum=False):
|
||||
if isinstance(cmd_tuple, tuple):
|
||||
cmd, cmd_type = cmd_tuple
|
||||
else:
|
||||
cmd = cmd_tuple
|
||||
cmd_type = None
|
||||
|
||||
def _sendCommand(self, cmd, cmd_type=None):
|
||||
# Make sure we are only handling one sending job at a time
|
||||
with self._sendingLock:
|
||||
if self._serial is None:
|
||||
return
|
||||
|
||||
if not self.isStreaming():
|
||||
for hook in self._gcode_hooks:
|
||||
hook_cmd = self._gcode_hooks[hook](self, cmd, cmd_type=cmd_type, send_checksum=sendChecksum)
|
||||
for name, hook in self._gcode_hooks.items():
|
||||
hook_cmd = hook(self, cmd, cmd_type=cmd_type)
|
||||
|
||||
if hook_cmd is None:
|
||||
cmd = None
|
||||
|
||||
# hook might have returned (cmd, sendChecksum) or (cmd, sendChecksum, cmd_type), split that
|
||||
if isinstance(hook_cmd, tuple):
|
||||
elif isinstance(hook_cmd, tuple):
|
||||
if len(hook_cmd) == 2:
|
||||
hook_cmd, cmd_type = hook_cmd
|
||||
elif len(hook_cmd) == 3:
|
||||
hook_cmd, cmd_type, sendChecksum = hook_cmd
|
||||
|
||||
# hook might have returned None for cmd, if so stop processing, we won't send this command
|
||||
# to the printer
|
||||
if hook_cmd is None:
|
||||
cmd = None
|
||||
break
|
||||
# legacy hook handler, ignore returned send_checksum
|
||||
hook_cmd, cmd_type, _ = hook_cmd
|
||||
|
||||
# if hook_cmd is a string, we'll replace cmd with it (it's been rewritten by the hook handler
|
||||
elif isinstance(hook_cmd, basestring):
|
||||
if isinstance(hook_cmd, basestring):
|
||||
cmd = hook_cmd
|
||||
|
||||
else:
|
||||
self._logger.warn("Hook {name} returned unintelligible result, ignoring it: {hook_cmd!r}".format(**locals()))
|
||||
continue
|
||||
|
||||
if cmd is None:
|
||||
break
|
||||
|
||||
# try to parse the cmd and extract the gcode type
|
||||
gcode = self._regex_command.search(cmd)
|
||||
if gcode:
|
||||
gcode = gcode.group(1)
|
||||
if cmd is not None:
|
||||
gcode = self._regex_command.search(cmd)
|
||||
if gcode:
|
||||
gcode = gcode.group(1)
|
||||
|
||||
# fire events if necessary
|
||||
if gcode in gcodeToEvent:
|
||||
eventManager().fire(gcodeToEvent[gcode])
|
||||
# fire events if necessary
|
||||
if gcode in gcodeToEvent:
|
||||
eventManager().fire(gcodeToEvent[gcode])
|
||||
|
||||
# send it through the specific handler if it exists
|
||||
gcodeHandler = "_gcode_" + gcode
|
||||
if hasattr(self, gcodeHandler):
|
||||
cmd = getattr(self, gcodeHandler)(cmd)
|
||||
# send it through the specific handler if it exists
|
||||
cmd = self._process_command_phase("queue", cmd, gcode=gcode)
|
||||
|
||||
if cmd is not None:
|
||||
self._doSend(cmd, send_checksum=sendChecksum, cmd_type=cmd_type)
|
||||
|
||||
def _doSend(self, cmd, send_checksum=False, cmd_type=None):
|
||||
if send_checksum or self._alwaysSendChecksum:
|
||||
lineNumber = self._currentLine
|
||||
self._addToLastLines(cmd)
|
||||
self._currentLine += 1
|
||||
self._enqueue_for_sending(cmd, linenumber=lineNumber, command_type=cmd_type)
|
||||
else:
|
||||
self._enqueue_for_sending(cmd, command_type=cmd_type)
|
||||
self._enqueue_for_sending(cmd, command_type=cmd_type)
|
||||
|
||||
def gcode_command_for_cmd(self, cmd):
|
||||
"""
|
||||
|
|
@ -1497,20 +1501,62 @@ class MachineCom(object):
|
|||
if not self._send_queue_active:
|
||||
break
|
||||
|
||||
# fetch command and optional linenumber from queue, send it
|
||||
# fetch command and optional linenumber from queue
|
||||
command, linenumber, _ = entry
|
||||
|
||||
# some firmwares (e.g. Smoothie) might support additional in-band communication that will not
|
||||
# stick to the acknowledgement behaviour of GCODE, so we check here if we have a GCODE command
|
||||
# at hand here and only clear our clear_to_send flag later if that's the case
|
||||
gcode_match = self._regex_command.search(command)
|
||||
is_gcode = gcode_match is not None
|
||||
|
||||
if is_gcode:
|
||||
# trigger "sending" phase
|
||||
command = self._process_command_phase("sending", command, gcode=gcode_match.group(1))
|
||||
|
||||
if linenumber is not None:
|
||||
# line number predetermined, use that
|
||||
self._doSendWithChecksum(command, linenumber)
|
||||
else:
|
||||
self._doSendWithoutChecksum(command)
|
||||
if (is_gcode or self._sendChecksumWithUnknownCommands) and (self.isPrinting() or self._alwaysSendChecksum):
|
||||
linenumber = self._currentLine
|
||||
self._addToLastLines(command)
|
||||
self._currentLine += 1
|
||||
self._doSendWithChecksum(command, linenumber)
|
||||
else:
|
||||
self._doSendWithoutChecksum(command)
|
||||
|
||||
# we just used up one ok, clear it, wait for the next clear
|
||||
self._clear_to_send.clear()
|
||||
use_up_clear = not self._unknownCommandsNeedAck
|
||||
if is_gcode:
|
||||
# trigger "sent" phase and use up one "ok"
|
||||
self._process_command_phase("sent", command, gcode=gcode_match.group(1))
|
||||
use_up_clear = True
|
||||
|
||||
# if we need to use up a clear, do that now
|
||||
if use_up_clear:
|
||||
self._clear_to_send.clear()
|
||||
|
||||
# wait for the next clear
|
||||
self._clear_to_send.wait()
|
||||
except:
|
||||
self._logger.exception("Caught an exception in the send loop")
|
||||
self._log("Closing down send loop")
|
||||
|
||||
def _process_command_phase(self, phase, command, gcode=None):
|
||||
if gcode is None:
|
||||
gcode_match = self._regex_command.search(command)
|
||||
if gcode_match is None:
|
||||
return command
|
||||
|
||||
gcode = gcode.group(1)
|
||||
|
||||
# send it through the specific handler if it exists
|
||||
gcodeHandler = "_gcode_" + gcode + "_" + phase
|
||||
if hasattr(self, gcodeHandler):
|
||||
command = getattr(self, gcodeHandler)(command)
|
||||
|
||||
return command
|
||||
|
||||
##~~ actual sending via serial
|
||||
|
||||
def _doSendWithChecksum(self, cmd, lineNumber):
|
||||
|
|
@ -1538,13 +1584,13 @@ class MachineCom(object):
|
|||
|
||||
##~~ command handlers
|
||||
|
||||
def _gcode_T(self, cmd):
|
||||
def _gcode_T_sent(self, cmd):
|
||||
toolMatch = self._regex_paramTInt.search(cmd)
|
||||
if toolMatch:
|
||||
self._currentTool = int(toolMatch.group(1))
|
||||
return cmd
|
||||
|
||||
def _gcode_G0(self, cmd):
|
||||
def _gcode_G0_sent(self, cmd):
|
||||
if 'Z' in cmd:
|
||||
match = self._regex_paramZFloat.search(cmd)
|
||||
if match:
|
||||
|
|
@ -1556,14 +1602,14 @@ class MachineCom(object):
|
|||
except ValueError:
|
||||
pass
|
||||
return cmd
|
||||
_gcode_G1 = _gcode_G0
|
||||
_gcode_G0_sent = _gcode_G0_sent
|
||||
|
||||
def _gcode_M0(self, cmd):
|
||||
def _gcode_M0_queue(self, cmd):
|
||||
self.setPause(True)
|
||||
return "M105" # Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause.
|
||||
_gcode_M1 = _gcode_M0
|
||||
return None # Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause.
|
||||
_gcode_M1_queued = _gcode_M0_queue
|
||||
|
||||
def _gcode_M104(self, cmd):
|
||||
def _gcode_M104_sent(self, cmd):
|
||||
toolNum = self._currentTool
|
||||
toolMatch = self._regex_paramTInt.search(cmd)
|
||||
if toolMatch:
|
||||
|
|
@ -1581,7 +1627,7 @@ class MachineCom(object):
|
|||
pass
|
||||
return cmd
|
||||
|
||||
def _gcode_M140(self, cmd):
|
||||
def _gcode_M140_sent(self, cmd):
|
||||
match = self._regex_paramSInt.search(cmd)
|
||||
if match:
|
||||
try:
|
||||
|
|
@ -1595,19 +1641,19 @@ class MachineCom(object):
|
|||
pass
|
||||
return cmd
|
||||
|
||||
def _gcode_M109(self, cmd):
|
||||
def _gcode_M109_sent(self, cmd):
|
||||
self._heatupWaitStartTime = time.time()
|
||||
self._blocking_command = True
|
||||
self._heating = True
|
||||
return self._gcode_M104(cmd)
|
||||
return self._gcode_M104_sent(cmd)
|
||||
|
||||
def _gcode_M190(self, cmd):
|
||||
def _gcode_M190_sent(self, cmd):
|
||||
self._heatupWaitStartTime = time.time()
|
||||
self._blocking_command = True
|
||||
self._heating = True
|
||||
return self._gcode_M140(cmd)
|
||||
return self._gcode_M140_sent(cmd)
|
||||
|
||||
def _gcode_M110(self, cmd):
|
||||
def _gcode_M110_sending(self, cmd):
|
||||
newLineNumber = None
|
||||
match = self._regex_paramNInt.search(cmd)
|
||||
if match:
|
||||
|
|
@ -1619,20 +1665,19 @@ class MachineCom(object):
|
|||
newLineNumber = 0
|
||||
|
||||
# send M110 command with new line number
|
||||
self._enqueue_for_sending(cmd, linenumber=newLineNumber)
|
||||
self._currentLine = newLineNumber + 1
|
||||
self._currentLine = newLineNumber
|
||||
|
||||
# after a reset of the line number we have no way to determine what line exactly the printer now wants
|
||||
self._lastLines.clear()
|
||||
self._resendDelta = None
|
||||
|
||||
return None
|
||||
return cmd
|
||||
|
||||
def _gcode_M112(self, cmd): # It's an emergency what todo? Canceling the print should be the minimum
|
||||
def _gcode_M112_queue(self, cmd): # It's an emergency what todo? Canceling the print should be the minimum
|
||||
self.cancelPrint()
|
||||
return cmd
|
||||
|
||||
def _gcode_G4(self, cmd):
|
||||
def _gcode_G4_sent(self, cmd):
|
||||
# we are intending to dwell for a period of time, increase the timeout to match
|
||||
cmd = cmd.upper()
|
||||
p_idx = cmd.find('P')
|
||||
|
|
@ -1648,12 +1693,12 @@ class MachineCom(object):
|
|||
self._blocking_command = True
|
||||
return cmd
|
||||
|
||||
def _gcode_G28(self, cmd):
|
||||
def _gcode_G28_sending(self, cmd):
|
||||
self._blocking_command = True
|
||||
return cmd
|
||||
_gcode_G29 = _gcode_G28
|
||||
_gcode_G30 = _gcode_G28
|
||||
_gcode_G32 = _gcode_G28
|
||||
_gcode_G29_sending = _gcode_G28_sending
|
||||
_gcode_G30_sending = _gcode_G28_sending
|
||||
_gcode_G32_sending = _gcode_G28_sending
|
||||
|
||||
### MachineCom callback ################################################################################################
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue