paneldue: execute commands synchronously
This prevents gcode requests from blocking temperature updates, accessing the file list, etc. Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
70501772c1
commit
eea2abd60e
|
@ -13,7 +13,6 @@ from collections import deque
|
|||
from utils import ServerError
|
||||
from tornado import gen
|
||||
from tornado.ioloop import IOLoop
|
||||
from tornado.locks import Lock
|
||||
|
||||
MIN_EST_TIME = 10.
|
||||
INITIALIZE_TIMEOUT = 10.
|
||||
|
@ -30,12 +29,11 @@ class SerialConnection:
|
|||
self.paneldue = paneldue
|
||||
self.port = config.get('serial')
|
||||
self.baud = config.getint('baud', 57600)
|
||||
self.sendlock = Lock()
|
||||
self.partial_input = b""
|
||||
self.ser = self.fd = None
|
||||
self.connected = False
|
||||
self.busy = False
|
||||
self.pending_lines = []
|
||||
self.send_busy = False
|
||||
self.send_buffer = b""
|
||||
self.attempting_connect = True
|
||||
self.ioloop.spawn_callback(self._connect)
|
||||
|
||||
|
@ -48,6 +46,7 @@ class SerialConnection:
|
|||
self.ser.close()
|
||||
self.ser = None
|
||||
self.partial_input = b""
|
||||
self.send_buffer = b""
|
||||
self.paneldue.initialized = False
|
||||
logging.info("PanelDue Disconnected")
|
||||
if reconnect and not self.attempting_connect:
|
||||
|
@ -98,22 +97,14 @@ class SerialConnection:
|
|||
return
|
||||
|
||||
# Remove null bytes, separate into lines
|
||||
data = data.strip(b'\x00\xFF')
|
||||
data = data.strip(b'\x00')
|
||||
lines = data.split(b'\n')
|
||||
lines[0] = self.partial_input + lines[0]
|
||||
self.partial_input = lines.pop()
|
||||
self.pending_lines.extend(lines)
|
||||
if self.busy:
|
||||
return
|
||||
self.busy = True
|
||||
self.ioloop.spawn_callback(self._process_commands)
|
||||
|
||||
async def _process_commands(self):
|
||||
while self.pending_lines:
|
||||
line = self.pending_lines.pop(0)
|
||||
line = line.strip().decode()
|
||||
for line in lines:
|
||||
try:
|
||||
await self.paneldue.process_line(line)
|
||||
line = line.strip().decode('utf-8', 'ignore')
|
||||
self.paneldue.process_line(line)
|
||||
except ServerError:
|
||||
logging.exception(
|
||||
f"GCode Processing Error: {line}")
|
||||
|
@ -121,28 +112,31 @@ class SerialConnection:
|
|||
f"!! GCode Processing Error: {line}")
|
||||
except Exception:
|
||||
logging.exception("Error during gcode processing")
|
||||
self.busy = False
|
||||
|
||||
async def send(self, data):
|
||||
if self.connected:
|
||||
async with self.sendlock:
|
||||
while data:
|
||||
try:
|
||||
sent = os.write(self.fd, data)
|
||||
except os.error as e:
|
||||
if e.errno == errno.EBADF or e.errno == errno.EPIPE:
|
||||
sent = 0
|
||||
else:
|
||||
await gen.sleep(.001)
|
||||
continue
|
||||
if sent:
|
||||
data = data[sent:]
|
||||
else:
|
||||
logging.exception(
|
||||
"Error writing data, closing serial connection")
|
||||
self.disconnect(reconnect=True)
|
||||
return
|
||||
def send(self, data):
|
||||
self.send_buffer += data
|
||||
if not self.send_busy:
|
||||
self.send_busy = True
|
||||
self.ioloop.spawn_callback(self._do_send)
|
||||
|
||||
async def _do_send(self):
|
||||
while self.send_buffer:
|
||||
try:
|
||||
sent = os.write(self.fd, self.send_buffer)
|
||||
except os.error as e:
|
||||
if e.errno == errno.EBADF or e.errno == errno.EPIPE:
|
||||
sent = 0
|
||||
else:
|
||||
await gen.sleep(.001)
|
||||
continue
|
||||
if sent:
|
||||
self.send_buffer = self.send_buffer[sent:]
|
||||
else:
|
||||
logging.exception(
|
||||
"Error writing data, closing serial connection")
|
||||
self.disconnect(reconnect=True)
|
||||
return
|
||||
self.send_busy = False
|
||||
|
||||
class PanelDue:
|
||||
def __init__(self, config):
|
||||
|
@ -171,7 +165,7 @@ class PanelDue:
|
|||
self.is_ready = False
|
||||
self.is_shutdown = False
|
||||
self.initialized = False
|
||||
self.last_printer_state = 'C'
|
||||
self.last_printer_state = 'O'
|
||||
self.last_update_time = 0.
|
||||
|
||||
# Set up macros
|
||||
|
@ -296,31 +290,32 @@ class PanelDue:
|
|||
self.is_shutdown = False
|
||||
self.is_ready = True
|
||||
|
||||
async def _process_klippy_shutdown(self):
|
||||
def _process_klippy_shutdown(self):
|
||||
self.is_shutdown = True
|
||||
|
||||
async def _process_klippy_disconnect(self):
|
||||
# Tell the PD that we are shutting down
|
||||
await self.write_response({'status': 'S'})
|
||||
self.is_ready = False
|
||||
def _process_klippy_disconnect(self):
|
||||
# Tell the PD that the printer is "off"
|
||||
self.write_response({'status': 'O'})
|
||||
self.last_printer_state = 'O'
|
||||
self.is_shutdown = self.is_shutdown = False
|
||||
|
||||
async def handle_status_update(self, status):
|
||||
def handle_status_update(self, status):
|
||||
for obj, items in status.items():
|
||||
if obj in self.printer_state:
|
||||
self.printer_state[obj].update(items)
|
||||
else:
|
||||
self.printer_state[obj] = items
|
||||
|
||||
async def paneldue_beep(self, frequency, duration):
|
||||
def paneldue_beep(self, frequency, duration):
|
||||
duration = int(duration * 1000.)
|
||||
await self.write_response(
|
||||
self.write_response(
|
||||
{'beep_freq': frequency, 'beep_length': duration})
|
||||
|
||||
async def process_line(self, line):
|
||||
def process_line(self, line):
|
||||
self.debug_queue.append(line)
|
||||
# If we find M112 in the line then skip verification
|
||||
if "M112" in line.upper():
|
||||
await self.klippy_apis.emergency_stop()
|
||||
self.ioloop.spawn_callback(self.klippy_apis.emergency_stop)
|
||||
return
|
||||
|
||||
if self.enable_checksum:
|
||||
|
@ -356,11 +351,11 @@ class PanelDue:
|
|||
logging.info("PanelDue: " + msg)
|
||||
raise PanelDueError(msg)
|
||||
|
||||
await self._run_gcode(line[line_index+1:cs_index])
|
||||
self._run_gcode(line[line_index+1:cs_index])
|
||||
else:
|
||||
await self._run_gcode(line)
|
||||
self._run_gcode(line)
|
||||
|
||||
async def _run_gcode(self, script):
|
||||
def _run_gcode(self, script):
|
||||
# Execute the gcode. Check for special RRF gcodes that
|
||||
# require special handling
|
||||
parts = script.split()
|
||||
|
@ -381,7 +376,7 @@ class PanelDue:
|
|||
return
|
||||
params["arg_" + arg] = val
|
||||
func = self.direct_gcodes[cmd]
|
||||
await func(**params)
|
||||
func(**params)
|
||||
return
|
||||
|
||||
# Prepare GCodes that require special handling
|
||||
|
@ -392,9 +387,11 @@ class PanelDue:
|
|||
if not script:
|
||||
return
|
||||
elif script in RESTART_GCODES:
|
||||
await self.klippy_apis.do_restart(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):
|
||||
try:
|
||||
await self.klippy_apis.run_gcode(script)
|
||||
except self.server.error:
|
||||
|
@ -466,7 +463,7 @@ class PanelDue:
|
|||
mbox['msgBox.title'] = title
|
||||
mbox['msgBox.controls'] = 0
|
||||
mbox['msgBox.timeout'] = 0
|
||||
self.ioloop.spawn_callback(self.write_response, mbox)
|
||||
self.write_response(mbox)
|
||||
|
||||
def handle_gcode_response(self, response):
|
||||
# Only queue up "non-trivial" gcode responses. At the
|
||||
|
@ -480,9 +477,9 @@ class PanelDue:
|
|||
self.last_gcode_response = response
|
||||
return
|
||||
|
||||
async def write_response(self, response):
|
||||
def write_response(self, response):
|
||||
byte_resp = json.dumps(response) + "\r\n"
|
||||
await self.ser_conn.send(byte_resp.encode())
|
||||
self.ser_conn.send(byte_resp.encode())
|
||||
|
||||
def _get_printer_status(self):
|
||||
# PanelDue States applicable to Klipper:
|
||||
|
@ -516,7 +513,7 @@ class PanelDue:
|
|||
|
||||
return 'I'
|
||||
|
||||
async def _run_paneldue_M408(self, arg_r=None, arg_s=1):
|
||||
def _run_paneldue_M408(self, arg_r=None, arg_s=1):
|
||||
response = {}
|
||||
sequence = arg_r
|
||||
response_type = arg_s
|
||||
|
@ -530,19 +527,16 @@ class PanelDue:
|
|||
response['dir'] = "/macros"
|
||||
response['files'] = list(self.available_macros.keys())
|
||||
self.initialized = True
|
||||
|
||||
if not self.is_ready:
|
||||
self.last_printer_state = 'S' if self.is_shutdown else 'C'
|
||||
self.last_printer_state = 'O'
|
||||
response['status'] = self.last_printer_state
|
||||
await self.write_response(response)
|
||||
self.write_response(response)
|
||||
return
|
||||
|
||||
# Send gcode responses
|
||||
if sequence is not None and self.last_gcode_response:
|
||||
# Send gcode responses
|
||||
response['seq'] = sequence + 1
|
||||
response['resp'] = self.last_gcode_response
|
||||
self.last_gcode_response = None
|
||||
|
||||
if response_type == 1:
|
||||
# Extended response Request
|
||||
response['myName'] = self.machine_name
|
||||
|
@ -642,9 +636,9 @@ class PanelDue:
|
|||
# is strange about this, and displays it as a full screen
|
||||
# notification
|
||||
self.last_message = msg
|
||||
await self.write_response(response)
|
||||
self.write_response(response)
|
||||
|
||||
async def _run_paneldue_M20(self, arg_p, arg_s=0):
|
||||
def _run_paneldue_M20(self, arg_p, arg_s=0):
|
||||
response_type = arg_s
|
||||
if response_type != 2:
|
||||
logging.info(
|
||||
|
@ -682,9 +676,9 @@ class PanelDue:
|
|||
flist = self.file_manager.list_dir(path, simple_format=True)
|
||||
if flist:
|
||||
response['files'] = flist
|
||||
await self.write_response(response)
|
||||
self.write_response(response)
|
||||
|
||||
async def _run_paneldue_M30(self, arg_p=None):
|
||||
def _run_paneldue_M30(self, arg_p=None):
|
||||
# Delete a file. Clean up the file name and make sure
|
||||
# it is relative to the "gcodes" root.
|
||||
path = arg_p
|
||||
|
@ -696,9 +690,9 @@ class PanelDue:
|
|||
|
||||
if not path.startswith("gcodes/"):
|
||||
path = "gcodes/" + path
|
||||
await self.file_manager.delete_file(path)
|
||||
self.ioloop.spawn_callback(self.file_manager.delete_file, path)
|
||||
|
||||
async def _run_paneldue_M36(self, arg_p=None):
|
||||
def _run_paneldue_M36(self, arg_p=None):
|
||||
response = {}
|
||||
filename = arg_p
|
||||
sd_status = self.printer_state.get('virtual_sdcard', {})
|
||||
|
@ -713,12 +707,11 @@ class PanelDue:
|
|||
if not filename or not active:
|
||||
# Either no file printing or no virtual_sdcard
|
||||
response['err'] = 1
|
||||
await self.write_response(response)
|
||||
self.write_response(response)
|
||||
return
|
||||
else:
|
||||
response['fileName'] = filename.split("/")[-1]
|
||||
|
||||
|
||||
# For consistency make sure that the filename begins with the
|
||||
# "gcodes/" root. The M20 HACK should add this in some cases.
|
||||
# Ideally we would add support to the PanelDue firmware that
|
||||
|
@ -749,9 +742,9 @@ class PanelDue:
|
|||
response['printTime'] = int(est_time + .5)
|
||||
else:
|
||||
response['err'] = 1
|
||||
await self.write_response(response)
|
||||
self.write_response(response)
|
||||
|
||||
async def close(self):
|
||||
def close(self):
|
||||
self.ser_conn.disconnect()
|
||||
msg = "\nPanelDue GCode Dump:"
|
||||
for i, gc in enumerate(self.debug_queue):
|
||||
|
|
Loading…
Reference in New Issue