implement iterator on reader (serial rx)

This commit is contained in:
Vladan Popovic 2022-06-19 03:07:07 +02:00
parent 792eef13ba
commit 08c5cabe6d
2 changed files with 91 additions and 43 deletions

View file

@ -271,7 +271,7 @@ impl Command {
} }
} }
pub fn tcp_send(size: usize) -> Command { pub fn tcp_send_size(size: usize) -> Command {
Command { Command {
text: format!("AT+CIPSEND={}", size), text: format!("AT+CIPSEND={}", size),
timeout: Duration::from_millis(3000), timeout: Duration::from_millis(3000),
@ -279,10 +279,10 @@ impl Command {
} }
} }
pub fn tcp_write(payload: &str) -> Command { pub fn tcp_send() -> Command {
Command { Command {
text: payload.to_string(), text: "AT+CIPSEND".to_string(),
timeout: Duration::from_millis(2000), timeout: Duration::from_millis(3000),
contains: Some(">".to_string()), contains: Some(">".to_string()),
} }
} }

View file

@ -11,7 +11,7 @@ use esp_idf_hal::serial::{self, Rx, Tx};
pub type Result<T> = std::result::Result<T, ModemError>; pub type Result<T> = std::result::Result<T, ModemError>;
pub struct Modem<UART: serial::Uart> { pub struct Modem<UART: serial::Uart> {
rx: Rx<UART>, rx: IterableRx<UART>,
tx: Tx<UART>, tx: Tx<UART>,
} }
@ -31,10 +31,51 @@ impl std::fmt::Display for ModemError {
} }
} }
pub struct IterableRx<UART: serial::Uart> {
inner: Rx<UART>,
timeout: Option<Duration>,
}
impl<UART: serial::Uart> IterableRx<UART> {
fn reset(&mut self, timeout: Duration) -> &mut Self {
self.timeout = Some(timeout);
self
}
}
impl<UART: serial::Uart> Iterator for IterableRx<UART> {
type Item = u8;
/// `nb` returns Ok(byte), or one of Err(WouldBlock) and Err(Other) which isn't of anyone's
/// interest, so the retry mechanism is triggered on _any_ error every 200ms until a byte is
/// received, or the timeout is reached.
fn next(&mut self) -> Option<Self::Item> {
let now = Instant::now();
loop {
let timeout = self.timeout.unwrap_or(Duration::from_millis(0));
match self.inner.read() {
Ok(b) => {
break Some(b)
},
_ => {
print!("got into retry loop in read iterator, ");
if now.elapsed() > timeout {
println!("exiting because timeout expired :(");
break None
}
println!("waiting 200ms ...");
thread::sleep(Duration::from_millis(200));
self.timeout = Some(timeout - now.elapsed());
}
}
}
}
}
impl<UART: serial::Uart> Modem<UART> { impl<UART: serial::Uart> Modem<UART> {
pub fn new(tx: Tx<UART>, rx: Rx<UART>) -> Self { pub fn new(tx: Tx<UART>, rx: Rx<UART>) -> Self {
Self { Self {
rx, rx: IterableRx { inner: rx, timeout: None },
tx, tx,
} }
} }
@ -70,32 +111,23 @@ impl<UART: serial::Uart> Modem<UART> {
} }
/// Reads a whole line (that ends with \\n) within the given `timeout` passed on input. /// Reads a whole line (that ends with \\n) within the given `timeout` passed on input.
/// `nb` returns Ok(byte), or one of Err(WouldBlock) and Err(Other) which isn't of anyone's
/// interest, so the retry mechanism is triggered on _any_ error every 200ms until a byte is
/// received, or the timeout is reached.
/// ///
fn read_line(&mut self, timeout: Duration) -> Result<String> { fn read_line(&mut self, timeout: Duration) -> Result<String> {
let mut buf = String::new(); let line: String = self.rx.reset(timeout)
let start = Instant::now(); .map(|b| char::from(b))
.take_while(|c| *c != '\n')
.collect();
loop { // A necessary check because the actual timeout is in the Iterator implementation. The
match self.rx.read() { // iterator exits when the returned Item is None, which happens when there's no data in
Ok(b) => { // the serial port and the timeout is breached.
print!("{}, ", b); //
buf.push(b as char); // This check here is tested on sim800l and works only because the modem has \r\n as CRLF.
if b == '\n' as u8 { if line.ends_with("\r") {
return Ok(buf); Ok(line)
}
},
_ => {
if Instant::now() > start + timeout {
return Err(ModemError::ReadError);
} }
else { else {
thread::sleep(Duration::from_millis(200)); Err(ModemError::ReadError)
}
},
}
} }
} }
@ -108,25 +140,24 @@ impl<UART: serial::Uart> Modem<UART> {
fn read_response(&mut self, contains: Option<String>, timeout: Duration) -> Result<String> { fn read_response(&mut self, contains: Option<String>, timeout: Duration) -> Result<String> {
let mut response = String::new(); let mut response = String::new();
let start = Instant::now(); let start = Instant::now();
let match_text: String = contains.unwrap_or("".to_string()); let match_text: String = contains.unwrap_or("\n".to_string());
loop { loop {
if let Ok(line) = self.read_line(start + timeout - Instant::now()) { let rdln = self.read_line(start + timeout - Instant::now());
if let Ok(line) = rdln {
println!("Read {} bytes from serial ({})", line.len(), line);
response.push_str(&line); response.push_str(&line);
if line.contains("ERROR") || line.contains(&match_text) { if line.contains("ERROR") || line.contains(&match_text) {
println!("Found match {} for line {} ... exiting response reader now!", match_text, line); println!("Found match {} for line {} ; exiting response reader now ...", match_text, line);
println!("-----------------------------------------------------------"); println!("-----------------------------------------------------------");
break; break Ok(response.to_string())
} }
} else { } else {
if Instant::now() > start + timeout { println!("-----------------------------------------------------------");
return Err(ModemError::TimeoutError); println!("Read line {:?}", rdln);
} break Err(ModemError::TimeoutError)
println!("got into retry loop in read_response");
thread::sleep(Duration::from_millis(200));
} }
} }
Ok(response.to_string())
} }
fn send(&mut self, b: u8) -> Result<()> { fn send(&mut self, b: u8) -> Result<()> {
@ -147,7 +178,24 @@ impl<UART: serial::Uart> Modem<UART> {
println!("-----------------------------------------------------------"); println!("-----------------------------------------------------------");
println!("Sending {} ...", cmd.text); println!("Sending {} ...", cmd.text);
let _ = self.send_bytes(cmd.text.as_bytes())?; let _ = self.send_bytes(cmd.text.as_bytes())?;
self.read_response(cmd.contains.clone(), cmd.timeout.clone()) self.read_response(cmd.contains, cmd.timeout)
}
fn send_command_data(&mut self, cmd: Command, payload: &str) -> Result<String> {
println!("-----------------------------------------------------------");
println!("Sending {} ...", cmd.text);
let _ = self.send_bytes(cmd.text.as_bytes())?;
let prompt: String = self.rx.reset(cmd.timeout)
.map(|b| char::from(b))
.take_while(|c| *c != ' ')
.collect();
if prompt != ">".to_string() {
println!("invalid prompt: {}", prompt);
}
let _ = self.send_bytes(payload.as_bytes())?;
self.read_response(cmd.contains, cmd.timeout)
} }
pub fn get_ip_addr(&mut self) -> Result<String> { pub fn get_ip_addr(&mut self) -> Result<String> {
@ -214,8 +262,8 @@ impl<UART: serial::Uart> Modem<UART> {
} }
pub fn tcp_send(&mut self, payload: &str) -> Result<()> { pub fn tcp_send(&mut self, payload: &str) -> Result<()> {
self.send_command(Command::tcp_send(payload.len()))?; self.send_command(Command::tcp_send_size(payload.len()))?;
self.send_command(Command::tcp_write(payload))?; self.send_command_data(Command::tcp_send(), payload)?;
Ok(()) Ok(())
} }
} }