diff --git a/src/octoprint/plugins/virtual_printer/virtual.py b/src/octoprint/plugins/virtual_printer/virtual.py index 68c7b9e2..ad7350b8 100644 --- a/src/octoprint/plugins/virtual_printer/virtual.py +++ b/src/octoprint/plugins/virtual_printer/virtual.py @@ -41,7 +41,9 @@ class VirtualPrinter(object): 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"])) @@ -107,10 +109,10 @@ class VirtualPrinter(object): self._triggerResendWithTimeoutAt105 = True self._triggeredResendWithTimeoutAt105 = False - readThread = threading.Thread(target=self._processIncoming) + readThread = threading.Thread(target=self._processIncoming, name="octoprint.plugins.virtual_printer.wait_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): @@ -126,17 +128,26 @@ class VirtualPrinter(object): 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._send("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: @@ -258,6 +269,8 @@ class VirtualPrinter(object): if len(data.strip()) > 0 and not self._okBeforeCommandOutput: self._sendOk() + self._logger.info("Closing down read loop") + ##~~ command implementations def _gcode_T(self, code, data): @@ -854,6 +867,8 @@ class VirtualPrinter(object): self._performMove(line) self.buffered.task_done() + self._logger.info("Closing down buffer loop") + def write(self, data): if self._debug_drop_connection: self._logger.info("Debug drop of connection requested, raising SerialTimeoutException") @@ -861,16 +876,17 @@ class VirtualPrinter(object): 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._seriallog.info("<<< {}".format(data.strip())) self._kill() - return + return len(data) try: - self.incoming.put(data, timeout=self._write_timeout) + written = self.incoming.put(data, timeout=self._write_timeout) self._seriallog.info("<<< {}".format(data.strip())) + return written except Queue.Full: self._logger.info("Incoming queue is full, raising SerialTimeoutException") raise SerialTimeoutException() @@ -882,6 +898,7 @@ class VirtualPrinter(object): try: line = self.outgoing.get(timeout=self._read_timeout) self._seriallog.info(">>> {}".format(line.strip())) + self.outgoing.task_done() return line except Queue.Empty: return "" @@ -917,22 +934,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 @@ -941,6 +961,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() @@ -960,3 +982,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 29f6569b..f4a0a57e 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -1940,24 +1940,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