Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raise TimeoutError on timeout #18

Closed
Bengt opened this issue Oct 4, 2015 · 3 comments
Closed

Raise TimeoutError on timeout #18

Bengt opened this issue Oct 4, 2015 · 3 comments

Comments

@Bengt
Copy link

Bengt commented Oct 4, 2015

When I set a timeout like this:

ser = serial.Serial('/dev/ttyS1', 19200, timeout=1)

pyserial just retries reads like this:

character = ser.read()

I need to be able to know, when the read has timed out to resend my last message. Please raise a TimeoutError, so I can use pyserial like this:

while True:
    try:
        character = console.read()
    except TimeoutError:
        console.write(message)
        character = console.read()

I think that it is unpythonic to silently drop errors like a timeout. When a user of pyserial invokes a read(), the expected outcome is that it returns the data that has been read. So hitting the timeout is an error.

@Bengt
Copy link
Author

Bengt commented Oct 4, 2015

I found a workaround of using threads that communicate over queues:

from threading import Thread

import serial
from queue import Queue


def _reader(console, queue_read):
    while True:
        try:
            response = console.readline().decode('ASCII')
        except TypeError:
            pass
        response = response.strip()
        if response != '':
            queue_read.put(response)

def _writer(console, queue_write):
    while True:
        message = queue_write.get()
        console.writelines([message.encode('ASCII')])

console = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
queue_read = Queue(maxsize=1)
queue_write = Queue(maxsize=1)

Thread(target=_reader, args=([console, queue_read])).start()
Thread(target=_writer, args=([console, queue_write])).start()

while True:
    # TODO calculate conversation starter

    message = '123;123;123'
    queue_write.put(message)

    pulse_durations = queue_read.get()
    print(pulse_durations)

    # TODO calculate response

@zsquareplusc
Copy link
Member

You can detect that there was a timeout by checking the length of returned bytes versus the number of bytes requested. It is more like an EOF in Python, rather than a timeout error. That is also how some protocols are using the timeout, as packet delimiter.

An exception would be inconvenient in many cases. E.g. consider reading more than one byte but fewer arrive, the timeout strikes. Then you'd still expect to get the bytes, but how? Attached to the exception? Probably not where most people would expect to find data. When they would be returned with the current read() call, then the timeout exception would need be raised in the next call which is certainly also not what one would expect.

@Bengt
Copy link
Author

Bengt commented Oct 6, 2015

Thanks for your reply, @zsquareplusc. Checking the length of the returned bytes did not work for me. console.read() timed out repeatedly and there were never any bytes returned.

If I requested a specified number of bytes with a timeout, I would either expect that exact number of bytes or an exception. I am not sure if it would also be OK to return all bytes that have been read up to the timeout. That would definitely need to be documented and users would need to check whether the received data is complete. I think there is little value in aborted data, because the timeout can strike at any given instance, so users would need to program every eventuality. In practice this means to handle as many cases as characters requested. Restarting the communication should be easier to program in most cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants