From 2b9aef58744e7acae388bdc3fe5ca816f892c863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Tue, 12 Jul 2016 17:48:47 +0200 Subject: [PATCH] Keep sending if serial line was only sent partially So far the sending routine did not track the amount of bytes that were actually sent. This is now fixed such that if the number of sent bytes is less then the number of bytes of the whole message, the sending rountine will continue to send the unsent parts of the message until it succeeds (or a timeout occurs). Also adjusted the virtual printer to imitate a real serial interface with regards to having it return the written bytes and allowing a partial sending (returning the number of bytes that actually were sent). --- .../plugins/virtual_printer/virtual.py | 49 ++++++++++++++----- src/octoprint/util/comm.py | 29 ++++++----- 2 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index 1d9adba6..9592af6c 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -30,7 +30,9 @@ class VirtualPrinter(): self._read_timeout = read_timeout self._write_timeout = write_timeout - self.incoming = CharCountingQueue(settings().getInt(["devel", "virtualPrinter", "rxBuffer"]), name="RxBuffer") + self._rx_buffer_size = settings().getInt(["devel", "virtualPrinter", "rxBuffer"]) + + self.incoming = CharCountingQueue(self._rx_buffer_size, name="RxBuffer") self.outgoing = Queue.Queue() self.buffered = Queue.Queue(maxsize=settings().getInt(["devel", "virtualPrinter", "commandBuffer"])) @@ -95,13 +97,13 @@ class VirtualPrinter(): self._triggerResendWithTimeoutAt105 = True self._triggeredResendWithTimeoutAt105 = False - waitThread = threading.Thread(target=self._sendWaitAfterTimeout) + waitThread = threading.Thread(target=self._sendWaitAfterTimeout, name="octoprint.plugins.virtual_printer.wait_thread") waitThread.start() - readThread = threading.Thread(target=self._processIncoming) + readThread = threading.Thread(target=self._processIncoming, name="octoprint.plugins.virtual_printer.read_thread") readThread.start() - bufferThread = threading.Thread(target=self._processBuffer) + bufferThread = threading.Thread(target=self._processBuffer, name="octoprint.plugins.virtual_printer.buffer_thread") bufferThread.start() def __str__(self): @@ -117,17 +119,26 @@ class VirtualPrinter(): def _processIncoming(self): next_wait_timeout = time.time() + self._waitInterval + buf = "" while self.incoming is not None and not self._killed: self._simulateTemps() try: data = self.incoming.get(timeout=0.01) + self.incoming.task_done() except Queue.Empty: if self._sendWait and time.time() > next_wait_timeout: self.outgoing.put("wait") next_wait_timeout = time.time() + self._waitInterval continue + buf += data + if "\n" in buf: + data = buf[:buf.find("\n") + 1] + buf = buf[buf.find("\n") + 1:] + else: + continue + next_wait_timeout = time.time() + self._waitInterval if data is None: @@ -322,6 +333,8 @@ class VirtualPrinter(): if len(data.strip()) > 0 and not self._okBeforeCommandOutput: self._sendOk() + self._logger.info("Closing down read loop") + def _calculate_checksum(self, line): checksum = 0 for c in line: @@ -738,6 +751,9 @@ class VirtualPrinter(): continue self._performMove(line) + self.buffered.task_done() + + self._logger.info("Closing down buffer loop") def write(self, data): if self._debug_drop_connection: @@ -746,14 +762,14 @@ class VirtualPrinter(): with self._incoming_lock: if self.incoming is None or self.outgoing is None: - return + return 0 if "M112" in data and self._supportM112: self._kill() - return + return len(data) try: - self.incoming.put(data, timeout=self._write_timeout) + return self.incoming.put(data, timeout=self._write_timeout, partial=True) except Queue.Full: self._logger.info("Incoming queue is full, raising SerialTimeoutException") raise SerialTimeoutException() @@ -764,6 +780,7 @@ class VirtualPrinter(): try: line = self.outgoing.get(timeout=self._read_timeout) + self.outgoing.task_done() return line except Queue.Empty: return "" @@ -795,22 +812,25 @@ class CharCountingQueue(Queue.Queue): self._size = 0 self._name = name - def put(self, item, block=True, timeout=None): + def put(self, item, block=True, timeout=None, partial=False): self.not_full.acquire() + try: - item_size = self._len(item) + if not self._will_it_fit(item) and partial: + space_left = self.maxsize - self._qsize() + item = item[:space_left] if not block: - if self._qsize() + item_size >= self.maxsize: + if not self._will_it_fit(item): raise Queue.Full elif timeout is None: - while self._qsize() + item_size >= self.maxsize: + while not self._will_it_fit(item): self.not_full.wait() elif timeout < 0: raise ValueError("'timeout' must be a positive number") else: endtime = time.time() + timeout - while self._qsize() + item_size >= self.maxsize: + while not self._will_it_fit(item): remaining = endtime - time.time() if remaining <= 0.0: raise Queue.Full @@ -819,6 +839,8 @@ class CharCountingQueue(Queue.Queue): self._put(item) self.unfinished_tasks += 1 self.not_empty.notify() + + return self._len(item) finally: self.not_full.release() @@ -838,3 +860,6 @@ class CharCountingQueue(Queue.Queue): item = self.queue.popleft() self._size -= self._len(item) return item + + def _will_it_fit(self, item): + return self.maxsize - self._qsize() >= self._len(item) diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index bd84f70a..af6f256f 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1933,24 +1933,31 @@ class MachineCom(object): return self._log("Send: " + str(cmd)) - try: - self._serial.write(cmd + '\n') - except serial.SerialTimeoutException: - self._log("Serial timeout while writing to serial port, trying again.") + + cmd += "\n" + written = 0 + while written < len(cmd): + to_send = cmd[written:] try: - self._serial.write(cmd + '\n') + written += self._serial.write(to_send) + except serial.SerialTimeoutException: + self._log("Serial timeout while writing to serial port, trying again.") + try: + written += self._serial.write(to_send) + except: + if not self._connection_closing: + self._logger.exception("Unexpected error while writing to serial port") + self._log("Unexpected error while writing to serial port: %s" % (get_exception_string())) + self._errorValue = get_exception_string() + self.close(is_error=True) + break except: if not self._connection_closing: self._logger.exception("Unexpected error while writing to serial port") self._log("Unexpected error while writing to serial port: %s" % (get_exception_string())) self._errorValue = get_exception_string() self.close(is_error=True) - except: - if not self._connection_closing: - self._logger.exception("Unexpected error while writing to serial port") - self._log("Unexpected error while writing to serial port: %s" % (get_exception_string())) - self._errorValue = get_exception_string() - self.close(is_error=True) + break ##~~ command handlers