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

use read_to_end always return TimedOut err #148

Open
lethargy123 opened this issue Jan 8, 2024 · 2 comments
Open

use read_to_end always return TimedOut err #148

lethargy123 opened this issue Jan 8, 2024 · 2 comments

Comments

@lethargy123
Copy link

I use serialport-rs on windows11, in use read_to_end(SerialPort tarit) always return TimedOut err
err:called Result::unwrap() on an Err value: Custom { kind: TimedOut, error: "Operation timed out" }

@sirhcel
Copy link
Contributor

sirhcel commented Jan 12, 2024

Thank you for bringing this up! Read::read_to_end seems like a tricky task for a serial interface because I don't see what the natural end-of-file (EOF) would be in this case.

I bet your are encountering a phenomenon from the default implementation of Read::read_to_end. This might not be intuitive but it seems to do its job according to its spec. How should serialport's read_to_end work in your opinion? How would you define end-of-file for a serial stream?

@RossSmyth
Copy link
Contributor

RossSmyth commented Mar 27, 2024

Yeah I reached for this API as well when I first started doing serialport operations in Rust. Mainly because I was biased by pySerial & other libraries. The way I initially expected it to work was the following:

  1. Set the timeout to some value
  2. Call read_to_end
  3. When the read timeout occurs, that would count as an "end"

This is similar to how the pySerial port.read() API works with interbyte timeouts. That way reading a whole "packet" just works. Because for all of my use cases the host (the computer running Python/Rust code) drives the interactions. The device never sends an unsolicited serial message. So the loop of

  1. Send a message to the device
  2. Block on waiting for each byte with a timeout until timeout occurs
  3. Parse message

works for pretty much all of my use cases

Recreating something like that isn't too hard. I use something similar to this in a couple projects

fn read_until_timeout<const BUFF: usize>(
    port: &mut dyn SerialPort,
    length_limit: Option<NonZeroUsize>,
) -> std::io::Result<Box<[u8]>> {
    let mut output = Vec::with_capacity(BUFF);
    let mut buf = [0_u8; BUFF];

    loop {
        let res = port.read(&mut buf[..]);

        match res {
            // Returning zero means EOF. I'm not sure if a serial device can receive EOF
            Ok(0) => break Ok(output.into()),

            // Put some data in to the buffer
            Ok(fill) => {
                output.extend_from_slice(&buf[..fill]);

                if let Some(max_size) = length_limit {
                    if output.len() == max_size.into() {
                        output.truncate(max_size.into());
                        break Ok(output.into());
                    }
                }
            }
            // Timeout is a success case, so return the data.
            Err(err) if std::io::ErrorKind::TimedOut == err.kind() => break Ok(output.into()),
            Err(err) => break Err(err),
        }
    }
}

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

3 participants