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).
This commit is contained in:
Gina Häußge 2016-07-12 17:48:47 +02:00
parent 0fec3aeca9
commit 2b9aef5874
2 changed files with 55 additions and 23 deletions

View file

@ -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)

View file

@ -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