How to pause a queue ,run different stuff and resume a queue in a plugin?

Hi,
i try to write a plugin for the MMU2, but for this i have to handle the communication in the middle of a print. If a Filament change command is detected T1 or so, i have to strip it from the queue, pause it, send commands to the printer for unloading, send commands to the MMU2 (wait for the OK) for the filament change, send commands to the printer for grabbing the filament and then resume the print.

the communication with the printer should be no problem, because that are predefined gcodes (not in case of some error handling).
the print could be paused with the @pause command?
but how to resume it?

how can i send commands directly to the printer without the queue?

any ideas?

i tried octoprint.printer PrinterInterface as described in the manual (internal modules)
commands and get a notImplementedError()

What slicer are you using? My understanding of the MMU2 is that all the communication is handled by gcode commands inserted into the sliced output, and OctoPrint shouldn't have to get involved.

Plugins interact with the queue of gcode commands using:
http://docs.octoprint.org/en/master/plugins/hooks.html#octoprint-comm-protocol-gcode-phase
and the only phase that allows substituting multiple commands for one is the queueing phase.

Hi,
Ok to explain more..
i don't own a prusa printer
in the original concept as i understand it the mmu2 is connected to the printer. All printer commands are handled via gcodes. but the loading and unloading of the filament is handled by the printer firmware. you have to send a command to the mmu2 ..wait for "ok".
this should be an approach to use the mmu2 with other printers which have no firmware support for the mmu2 and/or no free serial port
i have the mmu2 connected via USB to the raspberry octoprint instance
if a toolchange is detected i want to handle the filament change and then resume the print.
first i will try it with slic3r prusa edition but i want to use also cura or the normal slic3r
the only thing i found so far ist to replace the toolchange "T0" "T1" .... command with @pause and to use the rest api to send @resume afterwards as the internal module to send commands is not implemented in octoprint.
any ideas without using pause and resume?

See Support for additional commands may be added by plugins by implementing a handler for the octoprint.comm.protocol.action hook.

If you are editing the gcode to replace T0, T1, etc. then you might be able to use the GCode System Commands plugin.

Without editing the gcode, you could modify a plugin like https://github.com/b-morgan/OctoPrint-FanSpeedMirror to monitor the gcode for the T0, T1, etc. commands and execute code on the RPi. You would probably have to make changes so that the plugin doesn't continue the gcode until after the RPi code returns. In the current implementation, it didn't matter.

Thanks for your reply,
i have already implemented the

	def rewrite_T_command(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs):
		self._logger.info("command queued %s" % cmd)
		if (gcode and cmd == "T0") or (gcode and cmd == "T1") or (gcode and cmd == "T2") or (gcode and cmd == "T3") or (gcode and cmd == "T4"):
			global next_filament
			global old_filament
			old_filament = next_filament
			next_filament = gcode[-1:]
			self._logger.info("toolchange detected %s" % next_filament)
			cmd = "@pause"
			_toolchange_detected = True
		return cmd,

with the
"octoprint.comm.protocol.gcode.queuing": __plugin_implementation__.rewrite_T_command

when i use the pause/resume command the scripts will run if the user has defined one.
in the other direction i only found the Rest API to send @resume to the printer

def send_printer_command(cmd, tags):
	#_printer = octoprint.printer.PrinterInterface()
	#self._printer.commands(cmd, None)
	data = {
		'command': "@resume"
	}

	req = urllib2.Request('http://127.0.0.1:5000/api/printer/command')
	req.add_header('Content-Type', 'application/json')
	req.add_header('X-Api-Key', 'F481C3535CA1458FA8875F8B37EFFD88')

	response = urllib2.urlopen(req, json.dumps(data))

yes the problem is to pause and continue the gcode

You have defined a sequence of events:

  1. Trigger on Tn command
  2. Pause it
  3. Send commands to the printer for unloading (GCode?)
  4. Send command to the MMU2 (RPi code?)
  5. Send commands to the printer for grabbing filament (GCode?)
  6. Resume

I think you can switch steps 2 and 3 by returning a (long) list of commands consisting of the step 3 commands, the @pause (or a replacement like the @wait example), and the step 5 commands.

Either the replacement code for the Tn commands or code in the modified @pause uses import subprocess calls to execute a script on the RPi to do the MMU2 part which ends with the Rest API send a resume.

The only worry I'd have is all the comments in the OctoPrint documentation about "taking too long". @foosel would have to comment on if this approach would violate those constraints.

1 Like

While I might still be misunderstanding the problem space, my approach would be

  1. Define your own @ command that triggers the MMU2 command
  2. Trigger on Tn command
  3. rewrite that to a list of @pause, unloading GCODE, @mmu2_command (or whatever its called), loading GCODE, @resume

Or alternatively do it like Octolapse by @FormerLurker and put the job on hold temporarily.

The problem with "taking too long" in these handlers is that you effectively block the printer combination while you are busy. If you want to block the print job anyhow that's usually not an issue, though it also means emergency codes and such can't be sent, and at some point you'll also start triggering timeouts. Hence the job_on_hold option.

@foosel thanks the "job on hold" is a good way i think. i searched the manual, but when i tried

	#_printer = octoprint.printer.PrinterInterface()
	#self._printer.commands(cmd, None)

i got a "not implemeted error"
i am a little confused beause thats in the code of my octoprint instance at C:\Octoprint\venv\Lib\site-packages\octoprint\printer\ _init_.py (i know that is the example for job on hold but my commands looks the same way)

	def job_on_hold(self, blocking=True, *args, **kwargs):
		"""
		Contextmanager that allows executing code while printing while making sure that no commands from the file
		being printed are continued to be sent to the printer. Note that this will only work for local files,
		NOT SD files.

		Example:

		.. code-block:: python

		   with printer.job_on_hold():
		       park_printhead()
		       take_snapshot()
		       send_printhead_back()

		It should be used sparingly and only for very specific situations (such as parking the print head somewhere,
		taking a snapshot from the webcam, then continuing). If you abuse this, you WILL cause print quality issues!

		A lock is in place that ensures that the context can only actually be held by one thread at a time. If you
		don't want to block on acquire, be sure to set ``blocking`` to ``False`` and catch the ``RuntimeException`` thrown
		if the lock can't be acquired.

		Args:
			blocking (bool): Whether to block while attempting to acquire the lock (default) or not
		"""
		raise NotImplementedError()

whats wrong here ?? did i something wrong ?

i thought starting a thread in the handler could be a way to avoid to long processing times?

You get the (one and only) printer instance injected as self._printer. The interface is really just that, an interface, the actual printer implementation of it will be determined by OctoPrint and injected into registered plugin implementations.

Let me know if you need any help with this. I've worked with job_on_hold for a good while now, and feel like I've got a handle on it.

1 Like

Thanks i hope you do not regret this offer later on :wink:
i have a small sample already running. Later i have to get sure no "deadlock" happens if there is an exception in the code.
it looks like the settings part is the most work on a simple plugin.