improve error handling and request timing

This commit is contained in:
Robert 2022-12-17 19:01:47 +01:00
parent 4a5a7e0f74
commit 0055db7445
4 changed files with 101 additions and 30 deletions

View file

@ -1,9 +1,11 @@
use std::error::Error;
use b15f::B15F; use b15f::B15F;
fn main() -> Result<(), Box<dyn Error>>{ fn main() {
let drv = B15F::new()?; let _drv = match B15F::new() {
Ok(val) => val,
Ok(()) Err(error) => {
eprintln!("{error}");
return;
}
};
} }

View file

@ -5,6 +5,7 @@
use std::{process::Command, time::Duration, fmt::Debug, thread::sleep}; use std::{process::Command, time::Duration, fmt::Debug, thread::sleep};
use rand::Rng; use rand::Rng;
use serialport::SerialPort; use serialport::SerialPort;
use crate::error::Error;
use crate::{request::Request, build_request}; use crate::{request::Request, build_request};
@ -44,7 +45,7 @@ impl B15F {
/// ///
/// let drv = B15F::new().unwrap(); /// let drv = B15F::new().unwrap();
/// ``` /// ```
pub fn new() -> Result<B15F, String> { pub fn new() -> Result<B15F, Error> {
let port = B15F::init_connection()?; let port = B15F::init_connection()?;
let mut drv =B15F { let mut drv =B15F {
@ -54,7 +55,7 @@ impl B15F {
log_start!("Testing connection"); log_start!("Testing connection");
let mut tries = 3; let mut tries = 3;
while tries > 0 { while tries > 0 {
drv.discard(); drv.discard()?;
match drv.test_connection() { match drv.test_connection() {
Ok(()) => break, Ok(()) => break,
@ -65,12 +66,12 @@ impl B15F {
} }
if tries == 0 { if tries == 0 {
panic!("Testing connection failed!"); return Err("Connection test failed. Are you using the newest version?".into());
} }
log_end!("Ok!"); log_end!("Ok!");
let info = drv.get_board_info(); let info = drv.get_board_info()?;
log!("AVR firmware version: {} built at {} ({})", info[0], info[1], info[2]); log!("AVR firmware version: {} built at {} ({})", info[0], info[1], info[2]);
// let avr_commit_hash = info[3]; // let avr_commit_hash = info[3];
@ -82,7 +83,7 @@ impl B15F {
Ok(drv) Ok(drv)
} }
fn init_connection() -> Result<Box<dyn SerialPort>, String> { fn init_connection() -> Result<Box<dyn SerialPort>, Error> {
let devices = B15F::get_devices(); let devices = B15F::get_devices();
let device = match devices.first() { let device = match devices.first() {
@ -96,8 +97,7 @@ impl B15F {
let port = serialport::new(device, 57_600) let port = serialport::new(device, 57_600)
.timeout(Duration::from_millis(1000)) .timeout(Duration::from_millis(1000))
.open() .open()?;
.map_err(|err| err.to_string())?;
log_end!("Ok!"); log_end!("Ok!");
Ok(port) Ok(port)
@ -112,27 +112,28 @@ impl B15F {
/// ``` /// ```
/// use b15f::B15F; /// use b15f::B15F;
/// ///
/// let drv = B15F::new().unwrap(); /// let mut drv = B15F::new().unwrap();
/// ///
/// // Print each bit of information on a new line /// // Print each bit of information on a new line
/// drv.get_board_info() /// drv.get_board_info()
/// .unwrap()
/// .iter() /// .iter()
/// .for_each(|info| println!("{info}")); /// .for_each(|info| println!("{info}"));
/// ``` /// ```
pub fn get_board_info(&mut self) -> Vec<String> { pub fn get_board_info(&mut self) -> Result<Vec<String>, Error> {
let mut info: Vec<String> = vec![]; let mut info: Vec<String> = vec![];
self.usart.write(build_request!(Request::Info)).unwrap(); self.usart.write(build_request!(Request::Info))?;
let mut data_count: [u8; 1] = [0;1]; let mut data_count: [u8; 1] = [0;1];
self.usart.read(&mut data_count).unwrap(); self.usart.read(&mut data_count)?;
while data_count[0] > 0 { while data_count[0] > 0 {
let mut len: [u8; 1] = [0;1]; let mut len: [u8; 1] = [0;1];
self.usart.read(&mut len).unwrap(); self.usart.read(&mut len)?;
let mut data: Vec<u8> = vec![0; len[0] as usize]; let mut data: Vec<u8> = vec![0; len[0] as usize];
self.usart.read(data.as_mut_slice()).unwrap(); self.usart.read(data.as_mut_slice())?;
info.push( info.push(
data.into_iter() data.into_iter()
@ -140,37 +141,62 @@ impl B15F {
.collect::<String>() .collect::<String>()
); );
sleep(Duration::from_millis(4)); // Add delay to give the board time to catch up with our requests
data_count[0] -= 1; data_count[0] -= 1;
} }
info let mut aw: [u8; 1] = [0; 1];
self.usart.read(&mut aw)?;
if aw[0] != B15F::MSG_OK {
return Err(format!("Board info is faulty: code {}", aw[0]).into());
}
Ok(info)
} }
/// Clears data in the USART buffers on this device and on the B15 /// Clears data in the USART buffers on this device and on the B15
pub fn discard(&mut self) { pub fn discard(&mut self) -> Result<(), Error> {
// TODO: In general, unwrap() will cause panic on failure and crash the application. self.usart.clear(serialport::ClearBuffer::Output)?;
// It would be better to implement error handling
self.usart.clear(serialport::ClearBuffer::Output).unwrap();
for _ in 0..16 { for _ in 0..16 {
self.usart.write(build_request![Request::Discard]).unwrap(); self.usart.write(build_request![Request::Discard])?;
sleep(Duration::from_millis(4)); sleep(Duration::from_millis(4));
} }
self.usart.clear(serialport::ClearBuffer::Input).unwrap() self.usart.clear(serialport::ClearBuffer::Input)?;
Ok(())
} }
/// Tests the connetion to the B15 /// Tests the connetion to the B15
pub fn test_connection(&mut self) -> Result<(), String> { ///
/// To test the connection a `Request::Test` request will be sent
/// to the board together with a randomly generated value. If the
/// board returns that value the connection is working correctly.
///
/// ## Examples
/// ```
/// use b15f::B15F;
///
/// fn main() {
/// let mut drv = B15F::new().unwrap();
///
/// if let Err(err) = drv.test_connection() {
/// panic!("Connection is not working: {err}");
/// }
/// }
/// ```
pub fn test_connection(&mut self) -> Result<(), Error> {
let dummy: u8 = rand::thread_rng().gen_range(0x00..=0xFF); let dummy: u8 = rand::thread_rng().gen_range(0x00..=0xFF);
self.usart.write(build_request![Request::Test, dummy]).unwrap(); self.usart.write(build_request![Request::Test, dummy])?;
let mut buffer: [u8; 2]= [0; 2]; let mut buffer: [u8; 2]= [0; 2];
self.usart.read(&mut buffer).unwrap(); self.usart.read(&mut buffer)?;
if buffer[0] != B15F::MSG_OK || buffer[1] != dummy { if buffer[0] != B15F::MSG_OK || buffer[1] != dummy {
panic!("Test request failed"); return Err("Test request failed".into());
} }
Ok(()) Ok(())

42
src/error.rs Normal file
View file

@ -0,0 +1,42 @@
use std::fmt::Display;
#[derive(Debug)]
pub struct Error {
message: String
}
impl Error {
pub fn new(what: &str) -> Error {
Error { message: String::from(what) }
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error { message: err.to_string() }
}
}
impl From<serialport::Error> for Error {
fn from(err: serialport::Error) -> Self {
Error { message: err.to_string() }
}
}
impl From<&str> for Error {
fn from(msg: &str) -> Self {
Error::new(msg)
}
}
impl From<String> for Error {
fn from(msg: String) -> Self {
Error { message: msg }
}
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}

View file

@ -15,5 +15,6 @@
pub mod b15f; pub mod b15f;
mod request; mod request;
mod error;
pub use crate::b15f::B15F; pub use crate::b15f::B15F;