paneldue: implement queues for command handling

This implements two queues, one for commands that execute locally and a second
for gcode commands that execute on the Klippy host.  While it is valid for a local
command to execute concurrently with a remote command, commands of the same type
(local or remote) should execute sequentially.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>

Update paneldue.py
This commit is contained in:
Arksine 2020-12-11 09:01:24 -05:00
parent eea2abd60e
commit ba4d771d10
1 changed files with 52 additions and 27 deletions

View File

@ -9,6 +9,7 @@ import time
import json import json
import errno import errno
import logging import logging
import asyncio
from collections import deque from collections import deque
from utils import ServerError from utils import ServerError
from tornado import gen from tornado import gen
@ -165,6 +166,9 @@ class PanelDue:
self.is_ready = False self.is_ready = False
self.is_shutdown = False self.is_shutdown = False
self.initialized = False self.initialized = False
self.cq_busy = self.gq_busy = False
self.command_queue = []
self.gc_queue = []
self.last_printer_state = 'O' self.last_printer_state = 'O'
self.last_update_time = 0. self.last_update_time = 0.
@ -351,11 +355,9 @@ class PanelDue:
logging.info("PanelDue: " + msg) logging.info("PanelDue: " + msg)
raise PanelDueError(msg) raise PanelDueError(msg)
self._run_gcode(line[line_index+1:cs_index]) script = line[line_index+1:cs_index]
else: else:
self._run_gcode(line) script = line
def _run_gcode(self, script):
# Execute the gcode. Check for special RRF gcodes that # Execute the gcode. Check for special RRF gcodes that
# require special handling # require special handling
parts = script.split() parts = script.split()
@ -376,7 +378,7 @@ class PanelDue:
return return
params["arg_" + arg] = val params["arg_" + arg] = val
func = self.direct_gcodes[cmd] func = self.direct_gcodes[cmd]
func(**params) self.queue_command(func, **params)
return return
# Prepare GCodes that require special handling # Prepare GCodes that require special handling
@ -386,18 +388,44 @@ class PanelDue:
if not script: if not script:
return return
elif script in RESTART_GCODES: self.queue_gcode(script)
self.ioloop.spawn_callback(self.klippy_apis.do_restart, script)
return
self.ioloop.spawn_callback(self._send_klippy_gcode, script)
async def _send_klippy_gcode(self, script): def queue_gcode(self, script):
try: self.gc_queue.append(script)
await self.klippy_apis.run_gcode(script) if not self.gq_busy:
except self.server.error: self.gq_busy = True
msg = f"Error executing script {script}" self.ioloop.spawn_callback(self._process_gcode_queue)
self.handle_gcode_response("!! " + msg)
logging.exception(msg) async def _process_gcode_queue(self):
while self.gc_queue:
script = self.gc_queue.pop(0)
try:
if script in RESTART_GCODES:
await self.klippy_apis.do_restart(script)
else:
await self.klippy_apis.run_gcode(script)
except self.server.error:
msg = f"Error executing script {script}"
self.handle_gcode_response("!! " + msg)
logging.exception(msg)
self.gq_busy = False
def queue_command(self, cmd, *args, **kwargs):
self.command_queue.append((cmd, args, kwargs))
if not self.cq_busy:
self.cq_busy = True
self.ioloop.spawn_callback(self._process_command_queue)
async def _process_command_queue(self):
while self.command_queue:
cmd, args, kwargs = self.command_queue.pop(0)
try:
ret = cmd(*args, **kwargs)
if asyncio.iscoroutine(ret):
await ret
except Exception:
logging.exception("Error processing command")
self.cq_busy = False
def _clean_filename(self, filename): def _clean_filename(self, filename):
# Remove quotes and whitespace # Remove quotes and whitespace
@ -419,11 +447,11 @@ class PanelDue:
def _prepare_M23(self, args): def _prepare_M23(self, args):
filename = self._clean_filename(args[0]) filename = self._clean_filename(args[0])
return "M23 " + filename return f"M23 {filename}"
def _prepare_M32(self, args): def _prepare_M32(self, args):
filename = self._clean_filename(args[0]) filename = self._clean_filename(args[0])
return "SDCARD_PRINT_FILE FILENAME=" + filename return f"SDCARD_PRINT_FILE FILENAME=\"{filename}\""
def _prepare_M98(self, args): def _prepare_M98(self, args):
macro = args[0][1:].strip(" \"\t\n") macro = args[0][1:].strip(" \"\t\n")
@ -463,6 +491,7 @@ class PanelDue:
mbox['msgBox.title'] = title mbox['msgBox.title'] = title
mbox['msgBox.controls'] = 0 mbox['msgBox.controls'] = 0
mbox['msgBox.timeout'] = 0 mbox['msgBox.timeout'] = 0
logging.debug(f"Creating PanelDue Confirmation: {mbox}")
self.write_response(mbox) self.write_response(mbox)
def handle_gcode_response(self, response): def handle_gcode_response(self, response):
@ -490,8 +519,6 @@ class PanelDue:
return 'S' return 'S'
printer_state = self.printer_state printer_state = self.printer_state
p_busy = printer_state['idle_timeout'].get(
'state', 'Idle') == "Printing"
sd_state = printer_state['print_stats'].get('state', "standby") sd_state = printer_state['print_stats'].get('state', "standby")
if sd_state == "printing": if sd_state == "printing":
if self.last_printer_state == 'A': if self.last_printer_state == 'A':
@ -500,17 +527,15 @@ class PanelDue:
# Printing # Printing
return 'P' return 'P'
elif sd_state == "paused": elif sd_state == "paused":
if p_busy and self.last_printer_state != 'A': p_active = printer_state['idle_timeout'].get(
'state', 'Idle') == "Printing"
if p_active and self.last_printer_state != 'A':
# Pausing # Pausing
return 'D' return 'D'
else: else:
# Paused # Paused
return 'A' return 'A'
if p_busy:
# Printer is "busy"
return 'B'
return 'I' return 'I'
def _run_paneldue_M408(self, arg_r=None, arg_s=1): def _run_paneldue_M408(self, arg_r=None, arg_s=1):
@ -678,7 +703,7 @@ class PanelDue:
response['files'] = flist response['files'] = flist
self.write_response(response) self.write_response(response)
def _run_paneldue_M30(self, arg_p=None): async def _run_paneldue_M30(self, arg_p=None):
# Delete a file. Clean up the file name and make sure # Delete a file. Clean up the file name and make sure
# it is relative to the "gcodes" root. # it is relative to the "gcodes" root.
path = arg_p path = arg_p
@ -690,7 +715,7 @@ class PanelDue:
if not path.startswith("gcodes/"): if not path.startswith("gcodes/"):
path = "gcodes/" + path path = "gcodes/" + path
self.ioloop.spawn_callback(self.file_manager.delete_file, path) await self.file_manager.delete_file(path)
def _run_paneldue_M36(self, arg_p=None): def _run_paneldue_M36(self, arg_p=None):
response = {} response = {}