[Docs] Documented octoprint.comm.protocol.gcode hook
Experimenting with including examples stored on github, let's see if RTD likes that.
This commit is contained in:
parent
af7d2bb8c7
commit
c6e4057add
4 changed files with 145 additions and 7 deletions
|
|
@ -32,7 +32,8 @@ needs_sphinx = '1.3'
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
extensions = ['codeblockext', 'sphinx.ext.todo', 'sphinx.ext.autodoc', 'sphinxcontrib.httpdomain', 'sphinx.ext.napoleon']
|
extensions = ['codeblockext', 'onlineinclude', 'sphinx.ext.todo', 'sphinx.ext.autodoc', 'sphinxcontrib.httpdomain',
|
||||||
|
'sphinx.ext.napoleon']
|
||||||
todo_include_todos = True
|
todo_include_todos = True
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,54 @@ Available plugin hooks
|
||||||
octoprint.comm.protocol.gcode
|
octoprint.comm.protocol.gcode
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
|
.. py:function:: hook(comm_instance, cmd, cmd_type=None, with_checksum=None)
|
||||||
|
|
||||||
|
Preprocess and optionally suppress a GCODE command before it is being sent to the printer.
|
||||||
|
|
||||||
|
Hook handlers may use this to rewrite or completely suppress certain commands before they enter the send queue of
|
||||||
|
the communication layer. The hook handler will be called with the ``cmd`` to be sent to the printer as well as
|
||||||
|
the parameters for sending like ``cmd_type`` and ``with_checksum``.
|
||||||
|
|
||||||
|
If the handler does not wish to handle the command, it should simply perform a ``return cmd`` as early as possible,
|
||||||
|
that will ensure that no changes are applied to the command.
|
||||||
|
|
||||||
|
If the handler wishes to suppress sending of the command altogether, it should return None instead. That will tell
|
||||||
|
OctoPrint that the ``cmd`` has been scraped altogether and not send anything.
|
||||||
|
|
||||||
|
More granular manipulation of the sending logic is possible by not just returning ``cmd`` (be it the original, a
|
||||||
|
rewritten variant or a None value) but a 2-tuple (cmd, cmd_type) or a 3-tuple (cmd, cmd_type, with_checksum). This
|
||||||
|
allows to also rewrite the ``cmd_type`` and the ``with_checksum`` parameter used for sending. Note that the latter
|
||||||
|
should only be necessary in very rare circumstances, since usually plugins should not need to have to decide whether
|
||||||
|
a command should be sent with a checksum or not.
|
||||||
|
|
||||||
|
Defining a ``cmd_type`` other than None will make sure OctoPrint takes care of only having one command of that type
|
||||||
|
in its sending queue. Predefined types are ``temperature_poll`` for temperature polling via ``M105`` and
|
||||||
|
``sd_status_poll`` for polling the SD printing status via ``M27``.
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
The following hook handler replaces all ``M107`` ("Fan Off", deprecated) with an ``M106 S0`` ("Fan On" with speed
|
||||||
|
parameter)
|
||||||
|
|
||||||
|
.. onlineinclude:: https://raw.githubusercontent.com/OctoPrint/Plugin-Examples/master/rewrite_m107.py
|
||||||
|
:linenos:
|
||||||
|
:lines: 3-
|
||||||
|
|
||||||
|
:param object comm_instance: The :class:`~octoprint.util.comm.MachineCom` instance which triggered the hook.
|
||||||
|
:param str cmd: The GCODE command for which the hook was triggered. This is the full command as taken either
|
||||||
|
from the currently streamed GCODE file or via other means (e.g. user input our status polling).
|
||||||
|
:param str cmd_type: Type of command, ``temperature_poll`` for temperature polling or ``sd_status_poll`` for SD
|
||||||
|
printing status polling.
|
||||||
|
:param boolean with_checksum: Whether the ``cmd`` was to be sent with a checksum (True) or not (False)
|
||||||
|
:return: A rewritten ``cmd``, a tuple of ``cmd`` and ``cmd_type`` or ``cmd``, ``cmd_type`` and ``with_checksum``
|
||||||
|
or None to suppress sending of the ``cmd`` to the printer. See above for details.
|
||||||
|
|
||||||
.. _sec-plugins-hook-comm-protocol-action:
|
.. _sec-plugins-hook-comm-protocol-action:
|
||||||
|
|
||||||
octoprint.comm.protocol.action
|
octoprint.comm.protocol.action
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
.. py:function:: hook(comm_instance, line, action)
|
.. py:function:: hook(comm_instance, line, action, *args, **kwargs)
|
||||||
|
|
||||||
React to a :ref:`action command <>` received from the printer.
|
React to a :ref:`action command <>` received from the printer.
|
||||||
|
|
||||||
|
|
@ -32,7 +74,7 @@ octoprint.comm.protocol.action
|
||||||
octoprint.comm.protocol.scripts
|
octoprint.comm.protocol.scripts
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
.. py:function:: hook(comm_instance, script_type, script_name)
|
.. py:function:: hook(comm_instance, script_type, script_name, *args, **kwargs)
|
||||||
|
|
||||||
Return a prefix to prepend and a postfix to append to the script ``script_name`` of type ``type``. Handlers should
|
Return a prefix to prepend and a postfix to append to the script ``script_name`` of type ``type``. Handlers should
|
||||||
make sure to only proceed with returning additional scripts if the ``script_type`` and ``script_name`` match
|
make sure to only proceed with returning additional scripts if the ``script_type`` and ``script_name`` match
|
||||||
|
|
@ -50,7 +92,7 @@ octoprint.comm.protocol.scripts
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
def hook(comm_instance, script_type, script_name):
|
def handler(comm_instance, script_type, script_name):
|
||||||
if not script_type == "gcode" or not script_name == "afterPrinterConnected":
|
if not script_type == "gcode" or not script_name == "afterPrinterConnected":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
@ -58,7 +100,8 @@ octoprint.comm.protocol.scripts
|
||||||
postfix = ["M117 Connected", "M117 to OctoPrint"]
|
postfix = ["M117 Connected", "M117 to OctoPrint"]
|
||||||
return prefix, postfix
|
return prefix, postfix
|
||||||
|
|
||||||
__plugin_hooks__ = {"octoprint.comm.protocol.scripts": hook}
|
__plugin_name__ = "Example: GCODE script hook demo"
|
||||||
|
__plugin_hooks__ = {"octoprint.comm.protocol.scripts": handler}
|
||||||
|
|
||||||
:param MachineCom comm_instance: The :class:`~octoprint.util.comm.MachineCom` instance which triggered the hook.
|
:param MachineCom comm_instance: The :class:`~octoprint.util.comm.MachineCom` instance which triggered the hook.
|
||||||
:param str script_type: The type of the script for which the hook was called, currently only "gcode" is supported here.
|
:param str script_type: The type of the script for which the hook was called, currently only "gcode" is supported here.
|
||||||
|
|
|
||||||
56
docs/sphinxext/onlineinclude.py
Normal file
56
docs/sphinxext/onlineinclude.py
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
# coding=utf-8
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
__author__ = "Gina Häußge <osd@foosel.net>"
|
||||||
|
__license__ = 'The MIT License <http://opensource.org/licenses/MIT>'
|
||||||
|
__copyright__ = "Copyright (C) 2015 Gina Häußge - Released under terms of the MIT License"
|
||||||
|
|
||||||
|
|
||||||
|
import codecs
|
||||||
|
import urllib2
|
||||||
|
|
||||||
|
from sphinx.directives import LiteralInclude, dedent_lines
|
||||||
|
|
||||||
|
cache = dict()
|
||||||
|
|
||||||
|
class OnlineIncludeDirective(LiteralInclude):
|
||||||
|
|
||||||
|
def read_with_encoding(self, filename, document, codec_info, encoding):
|
||||||
|
global cache
|
||||||
|
|
||||||
|
f = None
|
||||||
|
try:
|
||||||
|
if not self.arguments[0] in cache:
|
||||||
|
f = codecs.StreamReaderWriter(urllib2.urlopen(self.arguments[0]), codec_info[2],
|
||||||
|
codec_info[3], 'strict')
|
||||||
|
lines = f.readlines()
|
||||||
|
cache[self.arguments[0]] = lines
|
||||||
|
else:
|
||||||
|
lines = cache[self.arguments[0]]
|
||||||
|
|
||||||
|
lines = dedent_lines(lines, self.options.get('dedent'))
|
||||||
|
return lines
|
||||||
|
except (IOError, OSError, urllib2.URLError):
|
||||||
|
return [document.reporter.warning(
|
||||||
|
'Include file %r not found or reading it failed' % self.arguments[0],
|
||||||
|
line=self.lineno)]
|
||||||
|
except UnicodeError:
|
||||||
|
return [document.reporter.warning(
|
||||||
|
'Encoding %r used for reading included file %r seems to '
|
||||||
|
'be wrong, try giving an :encoding: option' %
|
||||||
|
(encoding, self.arguments[0]))]
|
||||||
|
finally:
|
||||||
|
if f is not None:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
def visit_onlineinclude(translator, node):
|
||||||
|
translator.visit_literal_block(node)
|
||||||
|
|
||||||
|
def depart_onlineinclude(translator, node):
|
||||||
|
translator.depart_literal_block(node)
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.add_directive("onlineinclude", OnlineIncludeDirective)
|
||||||
|
|
||||||
|
handler = (visit_onlineinclude, depart_onlineinclude)
|
||||||
|
app.add_node(OnlineIncludeDirective, html=handler, latex=handler, text=handler)
|
||||||
|
|
@ -1362,16 +1362,35 @@ class MachineCom(object):
|
||||||
|
|
||||||
if not self.isStreaming():
|
if not self.isStreaming():
|
||||||
for hook in self._gcode_hooks:
|
for hook in self._gcode_hooks:
|
||||||
hook_cmd = self._gcode_hooks[hook](self, cmd)
|
hook_cmd = self._gcode_hooks[hook](self, cmd, cmd_type=cmd_type, send_checksum=sendChecksum)
|
||||||
if hook_cmd and isinstance(hook_cmd, basestring):
|
|
||||||
|
# hook might have returned (cmd, sendChecksum) or (cmd, sendChecksum, cmd_type), split that
|
||||||
|
if 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
|
||||||
|
|
||||||
|
# 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):
|
||||||
cmd = hook_cmd
|
cmd = hook_cmd
|
||||||
|
|
||||||
|
# try to parse the cmd and extract the gcode type
|
||||||
gcode = self._regex_command.search(cmd)
|
gcode = self._regex_command.search(cmd)
|
||||||
if gcode:
|
if gcode:
|
||||||
gcode = gcode.group(1)
|
gcode = gcode.group(1)
|
||||||
|
|
||||||
|
# fire events if necessary
|
||||||
if gcode in gcodeToEvent:
|
if gcode in gcodeToEvent:
|
||||||
eventManager().fire(gcodeToEvent[gcode])
|
eventManager().fire(gcodeToEvent[gcode])
|
||||||
|
|
||||||
|
# send it through the specific handler if it exists
|
||||||
gcodeHandler = "_gcode_" + gcode
|
gcodeHandler = "_gcode_" + gcode
|
||||||
if hasattr(self, gcodeHandler):
|
if hasattr(self, gcodeHandler):
|
||||||
cmd = getattr(self, gcodeHandler)(cmd)
|
cmd = getattr(self, gcodeHandler)(cmd)
|
||||||
|
|
@ -1388,6 +1407,25 @@ class MachineCom(object):
|
||||||
else:
|
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):
|
||||||
|
"""
|
||||||
|
Tries to parse the provided ``cmd`` and extract the GCODE command identifier from it (e.g. "G0" for "G0 X10.0").
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
cmd (str): The command to try to parse.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str or None: The GCODE command identifier if it could be parsed, or None if not.
|
||||||
|
"""
|
||||||
|
if not cmd:
|
||||||
|
return None
|
||||||
|
|
||||||
|
gcode = self._regex_command.search(cmd)
|
||||||
|
if not gcode:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return gcode.group(1)
|
||||||
|
|
||||||
##~~ send loop handling
|
##~~ send loop handling
|
||||||
|
|
||||||
def _enqueue_for_sending(self, command, linenumber=None, command_type=None):
|
def _enqueue_for_sending(self, command, linenumber=None, command_type=None):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue