More experimenting with comm queueing, seems to work fine now
Next up: actual print
This commit is contained in:
parent
801f9adfd7
commit
116a540abd
3 changed files with 105 additions and 74 deletions
|
|
@ -533,7 +533,7 @@ def address_for_client(host, port):
|
|||
class CountedEvent(object):
|
||||
|
||||
def __init__(self, value=0, max=None, name=None):
|
||||
logger_name = __name__ + ".CountedEvent" + ("[{name}]".format(name=name) if name is not None else "")
|
||||
logger_name = __name__ + ".CountedEvent" + (".{name}".format(name=name) if name is not None else "")
|
||||
self._logger = logging.getLogger(logger_name)
|
||||
|
||||
self._counter = 0
|
||||
|
|
|
|||
|
|
@ -152,6 +152,8 @@ class MachineCom(object):
|
|||
self._pauseWaitTimeLost = 0.0
|
||||
self._currentExtruder = 0
|
||||
|
||||
self._blocking_command = False
|
||||
|
||||
self._timeout = None
|
||||
|
||||
self._alwaysSendChecksum = settings().getBoolean(["feature", "alwaysSendChecksum"])
|
||||
|
|
@ -211,11 +213,13 @@ class MachineCom(object):
|
|||
self._sendingLock = threading.Lock()
|
||||
|
||||
# monitoring thread
|
||||
self._monitoring_active = True
|
||||
self.monitoring_thread = threading.Thread(target=self._monitor, name="comm._monitor")
|
||||
self.monitoring_thread.daemon = True
|
||||
self.monitoring_thread.start()
|
||||
|
||||
# sending thread
|
||||
self._send_queue_active = True
|
||||
self.sending_thread = threading.Thread(target=self._send_loop, name="comm.sending_thread")
|
||||
self.sending_thread.daemon = True
|
||||
self.sending_thread.start()
|
||||
|
|
@ -374,6 +378,9 @@ class MachineCom(object):
|
|||
except:
|
||||
pass
|
||||
|
||||
self._monitoring_active = False
|
||||
self._send_queue_active = False
|
||||
|
||||
printing = self.isPrinting() or self.isPaused()
|
||||
if self._serial is not None:
|
||||
if isError:
|
||||
|
|
@ -492,7 +499,7 @@ class MachineCom(object):
|
|||
self.sendCommand("M26 S0")
|
||||
self._currentFile.setFilepos(0)
|
||||
self.sendCommand("M24")
|
||||
self.sendCommand("M27")
|
||||
self._poll_sd_status()
|
||||
else:
|
||||
line = self._getNext()
|
||||
if line is not None:
|
||||
|
|
@ -553,6 +560,11 @@ class MachineCom(object):
|
|||
if self.isSdFileSelected():
|
||||
self.sendCommand("M25") # pause print
|
||||
self.sendCommand("M26 S0") # reset position in file to byte 0
|
||||
if self._sd_status_timer is not None:
|
||||
try:
|
||||
self._sd_status_timer.cancel()
|
||||
except:
|
||||
pass
|
||||
|
||||
payload = {
|
||||
"file": self._currentFile.getFilename(),
|
||||
|
|
@ -755,14 +767,10 @@ class MachineCom(object):
|
|||
#Start monitoring the serial port.
|
||||
self._timeout = get_new_timeout("communication")
|
||||
|
||||
tempRequestTimeout = get_new_timeout("temperature")
|
||||
sdStatusRequestTimeout = get_new_timeout("sdStatus")
|
||||
|
||||
startSeen = not settings().getBoolean(["feature", "waitForStartOnConnect"])
|
||||
swallowOk = False
|
||||
supportRepetierTargetTemp = settings().getBoolean(["feature", "repetierTargetTemp"])
|
||||
|
||||
while True:
|
||||
while self._monitoring_active:
|
||||
try:
|
||||
line = self._readline()
|
||||
if line is None:
|
||||
|
|
@ -826,6 +834,7 @@ class MachineCom(object):
|
|||
##~~ process oks
|
||||
if line.strip().startswith("ok"):
|
||||
self._clear_to_send.set()
|
||||
self._blocking_command = False
|
||||
|
||||
##~~ Temperature processing
|
||||
if ' T:' in line or line.startswith('T:') or ' T0:' in line or line.startswith('T0:'):
|
||||
|
|
@ -922,6 +931,11 @@ class MachineCom(object):
|
|||
"origin": self._currentFile.getFileLocation(),
|
||||
"time": self.getPrintTime()
|
||||
})
|
||||
if self._sd_status_timer is not None:
|
||||
try:
|
||||
self._sd_status_timer.cancel()
|
||||
except:
|
||||
pass
|
||||
elif 'Done saving file' in line:
|
||||
self.refreshSdFiles()
|
||||
|
||||
|
|
@ -1018,69 +1032,36 @@ class MachineCom(object):
|
|||
|
||||
### Operational
|
||||
elif self._state == self.STATE_OPERATIONAL or self._state == self.STATE_PAUSED:
|
||||
#Request the temperature on comm timeout (every 5 seconds) when we are not printing.
|
||||
if line == "":
|
||||
#self.sendCommand("M105")
|
||||
#tempRequestTimeout = get_new_timeout("temperature")
|
||||
pass
|
||||
|
||||
# if we still have commands to process, process them
|
||||
elif "ok" in line:
|
||||
if swallowOk:
|
||||
swallowOk = False
|
||||
else:
|
||||
if self._resendDelta is not None:
|
||||
self._resendNextCommand()
|
||||
elif self._sendFromQueue():
|
||||
pass
|
||||
if "ok" in line:
|
||||
# if we still have commands to process, process them
|
||||
if self._resendDelta is not None:
|
||||
self._resendNextCommand()
|
||||
elif self._sendFromQueue():
|
||||
pass
|
||||
|
||||
# resend -> start resend procedure from requested line
|
||||
elif line.lower().startswith("resend") or line.lower().startswith("rs"):
|
||||
if settings().get(["feature", "swallowOkAfterResend"]):
|
||||
swallowOk = True
|
||||
self._handleResendRequest(line)
|
||||
|
||||
### Printing
|
||||
elif self._state == self.STATE_PRINTING:
|
||||
if line == "" and time.time() > self._timeout:
|
||||
self._log("Communication timeout during printing, forcing a line")
|
||||
#line = 'ok'
|
||||
self._clear_to_send.set()
|
||||
if not self._blocking_command:
|
||||
self._log("Communication timeout during printing, forcing a line")
|
||||
self._clear_to_send.set()
|
||||
else:
|
||||
self._logger.debug("Ran into a communication timeout, but a blocking command is currently active")
|
||||
|
||||
if "ok" in line:
|
||||
if swallowOk:
|
||||
swallowOk = False
|
||||
if self._resendDelta is not None:
|
||||
self._resendNextCommand()
|
||||
else:
|
||||
if self._resendDelta is not None:
|
||||
self._resendNextCommand()
|
||||
else:
|
||||
#if self._heatupWaitStartTime is None:
|
||||
# if time.time() > tempRequestTimeout:
|
||||
# self.sendCommand("M105")
|
||||
# tempRequestTimeout = get_new_timeout("temperature")
|
||||
|
||||
# if self.isSdPrinting() and time.time() > sdStatusRequestTimeout:
|
||||
# self.sendCommand("M27")
|
||||
# sdStatusRequestTimeout = get_new_timeout("sdStatus")
|
||||
|
||||
if self._sendFromQueue(sendChecksum=True):
|
||||
pass
|
||||
elif not self.isSdPrinting():
|
||||
self._sendNext()
|
||||
|
||||
elif line == "" and self.isSdPrinting():
|
||||
pass
|
||||
#if time.time() > tempRequestTimeout:
|
||||
# self.sendCommand("M105")
|
||||
# tempRequestTimeout = get_new_timeout("temperature")
|
||||
|
||||
#if time.time() > sdStatusRequestTimeout:
|
||||
# self.sendCommand("M27")
|
||||
# sdStatusRequestTimeout = get_new_timeout("sdStatus")
|
||||
if self._sendFromQueue(sendChecksum=True):
|
||||
pass
|
||||
elif not self.isSdPrinting():
|
||||
self._sendNext()
|
||||
|
||||
elif line.lower().startswith("resend") or line.lower().startswith("rs"):
|
||||
if settings().get(["feature", "swallowOkAfterResend"]):
|
||||
swallowOk = True
|
||||
self._handleResendRequest(line)
|
||||
except:
|
||||
self._logger.exception("Something crashed inside the serial connection loop, please report this in OctoPrint's bug tracker:")
|
||||
|
|
@ -1096,18 +1077,12 @@ class MachineCom(object):
|
|||
self._send_queue.put((command, linenumber))
|
||||
|
||||
def _send_loop(self):
|
||||
while True:
|
||||
if self._send_queue.qsize() == 0:
|
||||
# queue is empty, wait a bit before checking again
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
|
||||
while self._send_queue_active:
|
||||
try:
|
||||
entry = self._send_queue.get(block=False)
|
||||
except queue.Empty:
|
||||
entry = None
|
||||
entry = self._send_queue.get()
|
||||
if not self._send_queue_active:
|
||||
break
|
||||
|
||||
if entry is not None:
|
||||
command, linenumber = entry
|
||||
if linenumber is not None:
|
||||
self._doSendWithChecksum(command, linenumber)
|
||||
|
|
@ -1115,11 +1090,13 @@ class MachineCom(object):
|
|||
self._doSendWithoutChecksum(command)
|
||||
self._clear_to_send.clear()
|
||||
self._clear_to_send.wait()
|
||||
except:
|
||||
self._logger.exception("Caught an exception in the send loop")
|
||||
self._log("")
|
||||
self._logger.info("Closing down send loop")
|
||||
|
||||
def _poll_temperature(self):
|
||||
print("temperature poll timer hit")
|
||||
if self.isOperational() and not self.isStreaming() and not self._heatupWaitStartTime:
|
||||
print("sending temperature poll")
|
||||
if self.isOperational() and not self.isStreaming() and not self._blocking_command:
|
||||
self.sendCommand("M105")
|
||||
|
||||
interval = get_interval("temperature")
|
||||
|
|
@ -1127,9 +1104,7 @@ class MachineCom(object):
|
|||
self._temperature_timer.start()
|
||||
|
||||
def _poll_sd_status(self):
|
||||
print("sd status poll timer hit")
|
||||
if self.isOperational() and self.isSdPrinting() and not self._heatupWaitStartTime:
|
||||
print("sending sd status poll")
|
||||
if self.isOperational() and self.isSdPrinting() and not self._blocking_command:
|
||||
self.sendCommand("M27")
|
||||
|
||||
interval = get_interval("sdStatus")
|
||||
|
|
@ -1138,13 +1113,14 @@ class MachineCom(object):
|
|||
|
||||
def _onConnected(self):
|
||||
self._poll_temperature()
|
||||
self._poll_sd_status()
|
||||
|
||||
self._changeState(self.STATE_OPERATIONAL)
|
||||
|
||||
if self._sdAvailable:
|
||||
self.refreshSdFiles()
|
||||
else:
|
||||
self.initSdCard()
|
||||
|
||||
payload = dict(port=self._port, baudrate=self._baudrate)
|
||||
eventManager().fire(Events.CONNECTED, payload)
|
||||
self.sendGcodeScript("afterPrinterConnected", replacements=dict(event=payload))
|
||||
|
|
@ -1458,10 +1434,12 @@ class MachineCom(object):
|
|||
|
||||
def _gcode_M109(self, cmd):
|
||||
self._heatupWaitStartTime = time.time()
|
||||
self._blocking_command = True
|
||||
return self._gcode_M104(cmd)
|
||||
|
||||
def _gcode_M190(self, cmd):
|
||||
self._heatupWaitStartTime = time.time()
|
||||
self._blocking_command = True
|
||||
return self._gcode_M140(cmd)
|
||||
|
||||
def _gcode_M110(self, cmd):
|
||||
|
|
@ -1502,8 +1480,15 @@ class MachineCom(object):
|
|||
# dwell time is specified in seconds
|
||||
_timeout = int(cmd[s_idx+1:])
|
||||
self._timeout = get_new_timeout("communication") + _timeout
|
||||
self._blocking_command = True
|
||||
return cmd
|
||||
|
||||
def _gcode_G28(self, cmd):
|
||||
self._blocking_command = True
|
||||
return cmd
|
||||
_gcode_G29 = _gcode_G28
|
||||
_gcode_G30 = _gcode_G28
|
||||
|
||||
### MachineCom callback ################################################################################################
|
||||
|
||||
class MachineComPrintCallback(object):
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ from serial import SerialTimeoutException
|
|||
from octoprint.settings import settings
|
||||
|
||||
class VirtualPrinter():
|
||||
command_regex = re.compile("[GM]\d+")
|
||||
sleep_regex = re.compile("sleep (\d+)")
|
||||
sleep_after_regex = re.compile("sleep_after ([GM]\d+) (\d+)")
|
||||
sleep_after_next_regex = re.compile("sleep_after_next ([GM]\d+) (\d+)")
|
||||
|
||||
def __init__(self, read_timeout=5.0, write_timeout=10.0):
|
||||
self._read_timeout = read_timeout
|
||||
self._write_timeout = write_timeout
|
||||
|
|
@ -59,6 +64,9 @@ class VirtualPrinter():
|
|||
|
||||
self._incoming_lock = threading.RLock()
|
||||
|
||||
self._sleepAfterNext = dict()
|
||||
self._sleepAfter = dict()
|
||||
|
||||
waitThread = threading.Thread(target=self._sendWaitAfterTimeout)
|
||||
waitThread.start()
|
||||
|
||||
|
|
@ -235,6 +243,22 @@ class VirtualPrinter():
|
|||
# simulate reprap buffered commands via a Queue with maxsize which internally simulates the moves
|
||||
self.buffered.put(data)
|
||||
|
||||
if len(self._sleepAfter) or len(self._sleepAfterNext):
|
||||
command_match = VirtualPrinter.command_regex.match(data)
|
||||
if command_match is not None:
|
||||
command = command_match.group(0)
|
||||
|
||||
interval = None
|
||||
if command in self._sleepAfter:
|
||||
interval = self._sleepAfter[command]
|
||||
elif command in self._sleepAfterNext:
|
||||
interval = self._sleepAfterNext[command]
|
||||
del self._sleepAfterNext[command]
|
||||
|
||||
if interval is not None:
|
||||
self.outgoing.put("// sleeping for {interval} seconds".format(interval=interval))
|
||||
time.sleep(interval)
|
||||
|
||||
if len(data.strip()) > 0:
|
||||
self._sendOk()
|
||||
|
||||
|
|
@ -245,6 +269,28 @@ class VirtualPrinter():
|
|||
self.outgoing.put("// action:resume")
|
||||
elif data == "action_disconnect":
|
||||
self.outgoing.put("// action:disconnect")
|
||||
else:
|
||||
try:
|
||||
sleep_match = VirtualPrinter.sleep_regex.match(data)
|
||||
sleep_after_match = VirtualPrinter.sleep_after_regex.match(data)
|
||||
sleep_after_next_match = VirtualPrinter.sleep_after_next_regex.match(data)
|
||||
|
||||
if sleep_match is not None:
|
||||
interval = int(sleep_match.group(1))
|
||||
self.outgoing.put("// sleeping for {interval} seconds".format(interval=interval))
|
||||
time.sleep(interval)
|
||||
elif sleep_after_match is not None:
|
||||
command = sleep_after_match.group(1)
|
||||
interval = int(sleep_after_match.group(2))
|
||||
self._sleepAfter[command] = interval
|
||||
self.outgoing.put("// going to sleep {interval} seconds after each {command}".format(**locals()))
|
||||
elif sleep_after_next_match is not None:
|
||||
command = sleep_after_next_match.group(1)
|
||||
interval = int(sleep_after_next_match.group(2))
|
||||
self._sleepAfterNext[command] = interval
|
||||
self.outgoing.put("// going to sleep {interval} seconds after next {command}".format(**locals()))
|
||||
except:
|
||||
pass
|
||||
|
||||
def _listSd(self):
|
||||
self.outgoing.put("Begin file list")
|
||||
|
|
|
|||
Loading…
Reference in a new issue