diff --git a/Cargo.toml b/Cargo.toml index 47c03a2..7e31d49 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ async-std = { version = "1.4.0", optional = true } libc = { version = "0.2.71", optional = true } byteorder = { version = "1.3.4", optional = true } rolling-stats = { version = "0.3.0", optional = true } - +thiserror = { version = "1.0.30", optional = true } [dependencies.chrono] version = "0.4.19" @@ -39,3 +39,5 @@ default_features = false version = "0.7.1" optional = true +[dev-dependencies] +anyhow = "1.0.44" diff --git a/src/blocking.rs b/src/blocking.rs index b2f7ea4..d104259 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -1,26 +1,27 @@ //! Blocking APIs on top of the base radio traits -//! -//! These implementations use the radio's DelayUs implementation to +//! +//! These implementations use the radio's DelayUs implementation to //! poll on completion of operations. -//! +//! //! ## https://github.com/ryankurte/rust-radio //! ## Copyright 2020 Ryan Kurte +use core::fmt::Debug; use core::time::Duration; use embedded_hal::delay::blocking::DelayUs; -#[cfg(feature="structopt")] +#[cfg(feature = "structopt")] use structopt::StructOpt; -#[cfg(feature="std")] +#[cfg(feature = "std")] use crate::std::string::ToString; -use crate::{Transmit, Receive, State}; +use crate::{Receive, State, Transmit}; /// BlockingOptions for blocking radio functions #[derive(Clone, PartialEq, Debug)] -#[cfg_attr(feature="structopt", derive(StructOpt))] +#[cfg_attr(feature = "structopt", derive(StructOpt))] pub struct BlockingOptions { /// Interval for polling for device state #[cfg_attr(feature="structopt", structopt(long, default_value="100us", parse(try_from_str=crate::duration_from_str)))] @@ -42,20 +43,25 @@ impl Default for BlockingOptions { /// BlockingError wraps radio error type to provie a `Timeout` variant #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "thiserror", derive(thiserror::Error))] pub enum BlockingError { + #[cfg_attr(feature = "thiserror", error("Inner: {0}"))] Inner(E), + #[cfg_attr(feature = "thiserror", error("Timeout"))] Timeout, } -impl From for BlockingError { +impl From for BlockingError { fn from(e: E) -> Self { BlockingError::Inner(e) } } -/// Blocking transmit function implemented over `radio::Transmit` and `radio::Power` using the provided +/// Blocking transmit function implemented over `radio::Transmit` and `radio::Power` using the provided /// `BlockingOptions` and radio-internal `DelayUs` impl to poll for completion -#[cfg_attr(feature = "mock", doc = r##" +#[cfg_attr( + feature = "mock", + doc = r##" ``` # use radio::*; # use radio::mock::*; @@ -75,18 +81,27 @@ assert_eq!(res, Ok(())); # radio.done(); ``` -"##)] +"## +)] /// -pub trait BlockingTransmit { - fn do_transmit(&mut self, data: &[u8], tx_options: BlockingOptions) -> Result<(), BlockingError>; +pub trait BlockingTransmit { + fn do_transmit( + &mut self, + data: &[u8], + tx_options: BlockingOptions, + ) -> Result<(), BlockingError>; } -impl BlockingTransmit for T -where +impl BlockingTransmit for T +where T: Transmit + DelayUs, - E: core::fmt::Debug, + E: Debug, { - fn do_transmit(&mut self, data: &[u8], tx_options: BlockingOptions) -> Result<(), BlockingError> { + fn do_transmit( + &mut self, + data: &[u8], + tx_options: BlockingOptions, + ) -> Result<(), BlockingError> { // Enter transmit mode self.start_transmit(data)?; @@ -98,12 +113,12 @@ where debug!("Blocking send complete"); break; } - + // Update poll time and timeout if overrun c += tx_options.poll_interval.as_micros(); if c > t { debug!("Blocking send timeout"); - return Err(BlockingError::Timeout) + return Err(BlockingError::Timeout); } // Wait for next poll @@ -114,9 +129,11 @@ where } } -/// Blocking receive function implemented over `radio::Receive` using the provided `BlockingOptions` +/// Blocking receive function implemented over `radio::Receive` using the provided `BlockingOptions` /// and radio-internal `DelayUs` impl to poll for completion -#[cfg_attr(feature = "mock", doc = r##" +#[cfg_attr( + feature = "mock", + doc = r##" ``` # use radio::*; # use radio::mock::*; @@ -125,7 +142,6 @@ use radio::blocking::{BlockingReceive, BlockingOptions}; let data = [0xaa, 0xbb]; let info = BasicInfo::new(-81, 0); - # let mut radio = MockRadio::new(&[ # Transaction::start_receive(None), # Transaction::check_receive(true, Ok(false)), @@ -135,30 +151,42 @@ let info = BasicInfo::new(-81, 0); # ]); # +// Setup buffer to read into let mut buff = [0u8; 128]; -let mut i = BasicInfo::new(0, 0); // Receive using a blocking call -let res = radio.do_receive(&mut buff, &mut i, BlockingOptions::default()); +let (n, info) = radio.do_receive(&mut buff, BlockingOptions::default())?; -assert_eq!(res, Ok(data.len())); +assert_eq!(n, data.len()); assert_eq!(&buff[..data.len()], &data); # radio.done(); + +# Ok::<(), anyhow::Error>(()) ``` -"##)] -/// +"## +)] +/// pub trait BlockingReceive { - fn do_receive(&mut self, buff: &mut [u8], info: &mut I, rx_options: BlockingOptions) -> Result>; + fn do_receive( + &mut self, + buff: &mut [u8], + rx_options: BlockingOptions, + ) -> Result<(usize, I), BlockingError>; } -impl BlockingReceive for T +impl BlockingReceive for T where - T: Receive + DelayUs, - I: core::fmt::Debug, - E: core::fmt::Debug, + T: Receive + DelayUs, + ::Info: Debug, + I: Debug, + E: Debug, { - fn do_receive(&mut self, buff: &mut [u8], info: &mut I, rx_options: BlockingOptions) -> Result> { + fn do_receive( + &mut self, + buff: &mut [u8], + rx_options: BlockingOptions, + ) -> Result<(usize, I), BlockingError> { // Start receive mode self.start_receive()?; @@ -166,14 +194,14 @@ where let mut c = 0; loop { if self.check_receive(true)? { - let n = self.get_received(info, buff)?; - return Ok(n) + let (n, i) = self.get_received(buff)?; + return Ok((n, i)); } c += rx_options.poll_interval.as_micros(); if c > t { debug!("Blocking receive timeout"); - return Err(BlockingError::Timeout) + return Err(BlockingError::Timeout); } let _ = self.delay_us(rx_options.poll_interval.as_micros() as u32); @@ -183,16 +211,24 @@ where /// BlockingSetState sets the radio state and polls until command completion pub trait BlockingSetState { - fn set_state_checked(&mut self, state: S, options: BlockingOptions) -> Result<(), BlockingError>; + fn set_state_checked( + &mut self, + state: S, + options: BlockingOptions, + ) -> Result<(), BlockingError>; } -impl BlockingSetState for T -where - T: State + DelayUs, - S: core::fmt::Debug + core::cmp::PartialEq + Copy, - E: core::fmt::Debug, +impl BlockingSetState for T +where + T: State + DelayUs, + S: Debug + core::cmp::PartialEq + Copy, + E: Debug, { - fn set_state_checked(&mut self, state: S, options: BlockingOptions) -> Result<(), BlockingError> { + fn set_state_checked( + &mut self, + state: S, + options: BlockingOptions, + ) -> Result<(), BlockingError> { // Send set state command self.set_state(state)?; @@ -205,20 +241,18 @@ where // Check for expected state if state == s { - return Ok(()) + return Ok(()); } // Timeout eventually c += options.poll_interval.as_micros(); if c > t { debug!("Blocking receive timeout"); - return Err(BlockingError::Timeout) + return Err(BlockingError::Timeout); } // Delay before next loop let _ = self.delay_us(options.poll_interval.as_micros() as u32); } - } } - diff --git a/src/config.rs b/src/config.rs index 2cf1eb6..f89741d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,7 +24,6 @@ pub enum ConfigOption { /// Transmit power (dBm) TXPower(i16), - /// Await Clear Channel before TX (if supported) AwaitCCA(bool), /// CCA threshold in dBm (used if AwaitCCA is set) @@ -45,7 +44,7 @@ pub enum ConfigError { NotSupported, /// Other (device, non-configuration errors) - Other(E) + Other(E), } /// Configure trait implemented by configurable radios @@ -62,4 +61,3 @@ pub trait Configure { /// Returns Ok(true) on successful get, Ok(false) for unsupported options, Err(Self::Error) for errors fn get_option(&mut self, o: &mut ConfigOption) -> Result<(), ConfigError>; } - diff --git a/src/helpers.rs b/src/helpers.rs index b01414b..12ea881 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,72 +1,75 @@ //! Provides common helpers for implementing radio utilities -//! +//! //! ## https://github.com/ryankurte/rust-radio //! ## Copyright 2020 Ryan Kurte +use embedded_hal::delay::blocking::DelayUs; +use humantime::Duration as HumanDuration; use structopt::StructOpt; -use humantime::{Duration as HumanDuration}; -use embedded_hal::blocking::delay::DelayUs; extern crate std; -use std::prelude::v1::*; -use std::time::{SystemTime}; -use std::fs::{File, OpenOptions}; use std::ffi::CString; +use std::fs::{File, OpenOptions}; +use std::prelude::v1::*; use std::string::String; +use std::time::SystemTime; use libc::{self}; -use pcap_file::{PcapWriter, DataLink, pcap::PcapHeader}; -use byteorder::{NetworkEndian, ByteOrder}; +use byteorder::{ByteOrder, NetworkEndian}; +use pcap_file::{pcap::PcapHeader, DataLink, PcapWriter}; use rolling_stats::Stats; -use crate::{Transmit, Receive, ReceiveInfo, Power, Rssi}; use crate::blocking::*; - +use crate::{Power, Receive, ReceiveInfo, Rssi, Transmit}; /// Basic operations supported by the helpers package #[derive(Clone, StructOpt, PartialEq, Debug)] pub enum Operation { - #[structopt(name="tx")] + #[structopt(name = "tx")] /// Transmit a packet Transmit(TransmitOptions), - #[structopt(name="rx")] + #[structopt(name = "rx")] /// Receive a packet Receive(ReceiveOptions), - #[structopt(name="rssi")] + #[structopt(name = "rssi")] /// Poll RSSI on the configured channel Rssi(RssiOptions), - #[structopt(name="echo")] + #[structopt(name = "echo")] /// Echo back received messages (useful with Link Test mode) Echo(EchoOptions), - #[structopt(name="ping-pong")] + #[structopt(name = "ping-pong")] /// Link test (ping-pong) mode LinkTest(PingPongOptions), } -pub fn do_operation(radio: &mut T, operation: Operation) -> Result<(), BlockingError> +pub fn do_operation(radio: &mut T, operation: Operation) -> Result<(), BlockingError> where - T: Transmit + Power + Receive + Rssi + Power + DelayUs, + T: Transmit + + Power + + Receive + + Rssi + + Power + + DelayUs, I: ReceiveInfo + Default + std::fmt::Debug, E: std::fmt::Debug, { let mut buff = [0u8; 1024]; - let mut info = I::default(); // TODO: the rest match operation { Operation::Transmit(options) => do_transmit(radio, options)?, - Operation::Receive(options) => do_receive(radio, &mut buff, &mut info, options).map(|_| ())?, - Operation::Echo(options) => do_echo(radio, &mut buff, &mut info, options).map(|_| ())?, + Operation::Receive(options) => do_receive(radio, &mut buff, options).map(|_| ())?, + Operation::Echo(options) => do_echo(radio, &mut buff, options).map(|_| ())?, Operation::Rssi(options) => do_rssi(radio, options).map(|_| ())?, Operation::LinkTest(options) => do_ping_pong(radio, options).map(|_| ())?, //_ => warn!("unsuppored command: {:?}", opts.command), } - + Ok(()) } @@ -89,9 +92,9 @@ pub struct TransmitOptions { pub blocking_options: BlockingOptions, } -pub fn do_transmit(radio: &mut T, options: TransmitOptions) -> Result<(), BlockingError> +pub fn do_transmit(radio: &mut T, options: TransmitOptions) -> Result<(), BlockingError> where - T: Transmit + Power + DelayUs, + T: Transmit + Power + DelayUs, E: core::fmt::Debug, { // Set output power if specified @@ -105,7 +108,7 @@ where // Delay for repeated transmission or exit match &options.period { - Some(p) => radio.try_delay_us(p.as_micros() as u32).unwrap(), + Some(p) => radio.delay_us(p.as_micros() as u32).unwrap(), None => break, } } @@ -131,52 +134,51 @@ pub struct ReceiveOptions { pub struct PcapOptions { /// Create and write capture output to a PCAP file - #[structopt(long, group="1")] + #[structopt(long, group = "1")] pub pcap_file: Option, /// Create and write to a unix pipe for connection to wireshark - #[structopt(long, group="1")] + #[structopt(long, group = "1")] pub pcap_pipe: Option, } impl PcapOptions { pub fn open(&self) -> Result>, std::io::Error> { - // Open file or pipe if specified let pcap_file = match (&self.pcap_file, &self.pcap_pipe) { // Open as file (Some(file), None) => { let f = File::create(file)?; Some(f) - }, + } // Open as pipe - #[cfg(target_family="unix")] + #[cfg(target_family = "unix")] (None, Some(pipe)) => { // Ensure file doesn't already exist let _ = std::fs::remove_file(pipe); - + // Create pipe let n = CString::new(pipe.as_str()).unwrap(); let status = unsafe { libc::mkfifo(n.as_ptr(), 0o644) }; - + // Manual status code handling // TODO: return io::Error if status != 0 { panic!("Error creating fifo: {}", status); } - + // Open pipe let f = OpenOptions::new() .write(true) .open(pipe) .expect("Error opening PCAP pipe"); - + Some(f) } (None, None) => None, - - _ => unimplemented!() + + _ => unimplemented!(), }; info!("pcap pipe open, awaiting connection"); @@ -201,41 +203,58 @@ impl PcapOptions { } /// Receive from the radio using the provided configuration -pub fn do_receive(radio: &mut T, mut buff: &mut [u8], mut info: &mut I, options: ReceiveOptions) -> Result +pub fn do_receive( + radio: &mut T, + mut buff: &mut [u8], + options: ReceiveOptions, +) -> Result where - T: Receive + DelayUs, + T: Receive + DelayUs, I: std::fmt::Debug, E: std::fmt::Debug, { // Create and open pcap file for writing - let mut pcap_writer = options.pcap_options.open().expect("Error opening pcap file / pipe"); + let mut pcap_writer = options + .pcap_options + .open() + .expect("Error opening pcap file / pipe"); // Start receive mode radio.start_receive()?; loop { if radio.check_receive(true)? { - let n = radio.get_received(&mut info, &mut buff)?; + let (n, i) = radio.get_received(&mut buff)?; match std::str::from_utf8(&buff[0..n as usize]) { - Ok(s) => info!("Received: '{}' info: {:?}", s, info), - Err(_) => info!("Received: '{:x?}' info: {:?}", &buff[0..n as usize], info), + Ok(s) => info!("Received: '{}' info: {:?}", s, i), + Err(_) => info!("Received: '{:x?}' info: {:?}", &buff[0..n as usize], i), } if let Some(p) = &mut pcap_writer { - let t = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); - - p.write(t.as_secs() as u32, t.as_nanos() as u32 % 1_000_000, &buff[0..n], n as u32).expect("Error writing pcap file"); + let t = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap(); + + p.write( + t.as_secs() as u32, + t.as_nanos() as u32 % 1_000_000, + &buff[0..n], + n as u32, + ) + .expect("Error writing pcap file"); } - - if !options.continuous { - return Ok(n) + + if !options.continuous { + return Ok(n); } radio.start_receive()?; } - radio.try_delay_us(options.blocking_options.poll_interval.as_micros() as u32).unwrap(); + radio + .delay_us(options.blocking_options.poll_interval.as_micros() as u32) + .unwrap(); } } @@ -243,7 +262,7 @@ where #[derive(Clone, StructOpt, PartialEq, Debug)] pub struct RssiOptions { /// Specify period for RSSI polling - #[structopt(long = "period", default_value="1s")] + #[structopt(long = "period", default_value = "1s")] pub period: HumanDuration, /// Run continuously @@ -251,9 +270,9 @@ pub struct RssiOptions { pub continuous: bool, } -pub fn do_rssi(radio: &mut T, options: RssiOptions) -> Result<(), E> +pub fn do_rssi(radio: &mut T, options: RssiOptions) -> Result<(), E> where - T: Receive + Rssi + DelayUs, + T: Receive + Rssi + DelayUs, I: std::fmt::Debug, E: std::fmt::Debug, { @@ -268,10 +287,10 @@ where radio.check_receive(true)?; - radio.try_delay_us(options.period.as_micros() as u32).unwrap(); + radio.delay_us(options.period.as_micros() as u32).unwrap(); if !options.continuous { - break + break; } } @@ -284,13 +303,13 @@ pub struct EchoOptions { /// Run continuously #[structopt(long = "continuous")] pub continuous: bool, - + /// Power in dBm (range -18dBm to 13dBm) #[structopt(long = "power")] pub power: Option, /// Specify delay for response message - #[structopt(long = "delay", default_value="100ms")] + #[structopt(long = "delay", default_value = "100ms")] pub delay: HumanDuration, /// Append RSSI and LQI to repeated message @@ -301,14 +320,20 @@ pub struct EchoOptions { pub blocking_options: BlockingOptions, } - -pub fn do_echo(radio: &mut T, mut buff: &mut [u8], mut info: &mut I, options: EchoOptions) -> Result> +pub fn do_echo( + radio: &mut T, + mut buff: &mut [u8], + options: EchoOptions, +) -> Result> where - T: Receive + Transmit + Power + DelayUs, + T: Receive + + Transmit + + Power + + DelayUs, I: ReceiveInfo + std::fmt::Debug, E: std::fmt::Debug, { - // Set output power if specified + // Set output power if specified if let Some(p) = options.power { radio.set_power(p)?; } @@ -319,36 +344,39 @@ where loop { if radio.check_receive(true)? { // Fetch received packet - let mut n = radio.get_received(&mut info, &mut buff)?; + let (mut n, i) = radio.get_received(&mut buff)?; // Parse out string if possible, otherwise print hex match std::str::from_utf8(&buff[0..n as usize]) { - Ok(s) => info!("Received: '{}' info: {:?}", s, info), - Err(_) => info!("Received: '{:02x?}' info: {:?}", &buff[0..n as usize], info), + Ok(s) => info!("Received: '{}' info: {:?}", s, i), + Err(_) => info!("Received: '{:02x?}' info: {:?}", &buff[0..n as usize], i), } // Append info if provided if options.append_info { - NetworkEndian::write_i16(&mut buff[n..], info.rssi()); + NetworkEndian::write_i16(&mut buff[n..], i.rssi()); n += 2; } // Wait for turnaround delay - radio.try_delay_us(options.delay.as_micros() as u32).unwrap(); + radio.delay_us(options.delay.as_micros() as u32).unwrap(); // Transmit respobnse radio.do_transmit(&buff[..n], options.blocking_options.clone())?; - + // Exit if non-continuous - if !options.continuous { return Ok(n) } + if !options.continuous { + return Ok(n); + } } // Wait for poll delay - radio.try_delay_us(options.blocking_options.poll_interval.as_micros() as u32).unwrap(); + radio + .delay_us(options.blocking_options.poll_interval.as_micros() as u32) + .unwrap(); } } - /// Configuration for Echo operation #[derive(Clone, StructOpt, PartialEq, Debug)] pub struct PingPongOptions { @@ -361,7 +389,7 @@ pub struct PingPongOptions { pub power: Option, /// Specify delay for response message - #[structopt(long, default_value="100ms")] + #[structopt(long, default_value = "100ms")] pub delay: HumanDuration, /// Parse RSSI and other info from response messages @@ -380,24 +408,28 @@ pub struct LinkTestInfo { pub remote_rssi: Stats, } - -pub fn do_ping_pong(radio: &mut T, options: PingPongOptions) -> Result> +pub fn do_ping_pong( + radio: &mut T, + options: PingPongOptions, +) -> Result> where - T: Receive + Transmit + Power + DelayUs, - I: ReceiveInfo + Default + std::fmt::Debug, + T: Receive + + Transmit + + Power + + DelayUs, + I: ReceiveInfo, E: std::fmt::Debug, { - let mut link_info = LinkTestInfo{ + let mut link_info = LinkTestInfo { sent: options.rounds, received: 0, local_rssi: Stats::new(), remote_rssi: Stats::new(), }; - let mut info = I::default(); let mut buff = [0u8; 32]; - // Set output power if specified + // Set output power if specified if let Some(p) = options.power { radio.set_power(p)?; } @@ -413,12 +445,12 @@ where radio.do_transmit(&buff[0..n], options.blocking_options.clone())?; // Await response - let n = match radio.do_receive(&mut buff, &mut info, options.blocking_options.clone()) { - Ok(n) => n, + let (n, info) = match radio.do_receive(&mut buff, options.blocking_options.clone()) { + Ok(r) => r, Err(BlockingError::Timeout) => { debug!("Timeout awaiting response {}", i); - continue - }, + continue; + } Err(e) => return Err(e), }; @@ -434,7 +466,12 @@ where false => None, }; - debug!("Received response {} with local rssi: {} and remote rssi: {:?}", receive_index, info.rssi(), remote_rssi); + debug!( + "Received response {} with local rssi: {} and remote rssi: {:?}", + receive_index, + info.rssi(), + remote_rssi + ); link_info.received += 1; link_info.local_rssi.update(info.rssi() as f32); @@ -443,7 +480,7 @@ where } // Wait for send delay - radio.try_delay_us(options.delay.as_micros() as u32).unwrap(); + radio.delay_us(options.delay.as_micros() as u32).unwrap(); } Ok(link_info) diff --git a/src/lib.rs b/src/lib.rs index 39014fc..0b45445 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,105 +1,106 @@ //! Abstract packet radio interfaces -//! +//! //! This package defines traits for packet radio devices, as well as blocking and async //! implementations using these traits, and a mock device to support application level testing. -//! +//! //! ## https://github.com/ryankurte/rust-radio //! ## Copyright 2020 Ryan Kurte #![no_std] -extern crate nb; +use core::fmt::Debug; + extern crate chrono; +extern crate nb; #[macro_use] extern crate log; extern crate embedded_hal; - -#[cfg(feature="std")] +#[cfg(feature = "std")] extern crate std; pub mod config; pub mod blocking; -#[cfg(feature="nonblocking")] -pub mod nonblocking; -#[cfg(feature="helpers")] +#[cfg(feature = "helpers")] pub mod helpers; -#[cfg(feature="mock")] +#[cfg(feature = "mock")] pub mod mock; +#[cfg(feature = "nonblocking")] +pub mod nonblocking; /// Radio trait combines Base, Configure, Send and Receive for a generic radio object pub trait Radio: Transmit + Receive + State {} /// Transmit trait for radios that can transmit packets -/// +/// /// `start_transmit` should be called to load data into the radio, with `check_transmit` called /// periodically (or using interrupts) to continue and finalise the transmission. pub trait Transmit { /// Radio error - type Error; + type Error: Debug; /// Start sending a packet on the provided channel - /// + /// /// Returns an error if send was not started fn start_transmit(&mut self, data: &[u8]) -> Result<(), Self::Error>; /// Check for send completion - /// + /// /// Returns true for send complete, false otherwise fn check_transmit(&mut self) -> Result; } /// Receive trait for radios that can receive packets -/// +/// /// `start_receive` should be used to setup the radio in receive mode, with `check_receive` called /// periodically (or using interrupts) to poll for packet reception. Once a packet has been received, /// `get_received` fetches the received packet (and associated info) from the radio. pub trait Receive { /// Radio error - type Error; + type Error: Debug; /// Packet received info - type Info; + type Info: ReceiveInfo; /// Set receiving on the specified channel - /// + /// /// Returns an error if receive mode was not entered fn start_receive(&mut self) -> Result<(), Self::Error>; /// Check for reception - /// + /// /// The restart flag indicates on (recoverable) error conditions (such as invalid CRC) /// the radio should re-enter receive mode if required and continue reception. - /// + /// /// This returns true for received, false for not received, or the provided error fn check_receive(&mut self, restart: bool) -> Result; /// Fetch a received packet if rx is complete - /// + /// /// This copies received data into the provided buffer and returns the number of bytes received /// as well as information about the received packet - fn get_received(&mut self, info: &mut Self::Info, buff: &mut [u8]) -> Result; + fn get_received(&mut self, buff: &mut [u8]) -> Result<(usize, Self::Info), Self::Error>; } /// ReceiveInfo trait for receive information objects -/// +/// /// This sup[ports the constraint of generic `Receive::Info`, allowing generic middleware /// to access the rssi of received packets -pub trait ReceiveInfo { +pub trait ReceiveInfo: Debug + Default { fn rssi(&self) -> i16; } -/// Default / Standard packet information structure for radio devices that provide only rssi +/// Default / Standard packet information structure for radio devices that provide only rssi /// and lqi information #[derive(Debug, Clone, PartialEq)] pub struct BasicInfo { /// Received Signal Strength Indicator (RSSI) of received packet in dBm - rssi: i16, + rssi: i16, /// Link Quality Indicator (LQI) of received packet - lqi: u16, + lqi: u16, } impl Default for BasicInfo { @@ -113,7 +114,7 @@ impl Default for BasicInfo { impl BasicInfo { pub fn new(rssi: i16, lqi: u16) -> Self { - Self {rssi, lqi} + Self { rssi, lqi } } } @@ -126,7 +127,7 @@ impl ReceiveInfo for BasicInfo { /// Default / Standard radio channel object for radio devices with integer channels #[derive(Debug, Clone, PartialEq)] -pub struct BasicChannel (pub u16); +pub struct BasicChannel(pub u16); impl From for BasicChannel { fn from(u: u16) -> Self { @@ -143,9 +144,9 @@ impl Into for BasicChannel { /// Channel trait for configuring radio channelization pub trait Channel { /// Channel information - type Channel; + type Channel: Debug; /// Radio error type - type Error; + type Error: Debug; /// Set the radio channel for future transmit and receive operations fn set_channel(&mut self, channel: &Self::Channel) -> Result<(), Self::Error>; @@ -154,19 +155,19 @@ pub trait Channel { /// Power trait for configuring radio power pub trait Power { /// Radio error type - type Error; + type Error: Debug; /// Set the radio power in dBm fn set_power(&mut self, power: i8) -> Result<(), Self::Error>; } /// Rssi trait allows polling for RSSI on the current channel -/// +/// /// Note that the radio should be in receive mode prior to polling for this. pub trait Rssi { /// Radio error - type Error; - + type Error: Debug; + /// Fetch the current RSSI value from the radio /// Note that the radio MUST be in RX mode (or capable of measuring RSSI) when this is called /// or an error should be returned @@ -174,14 +175,14 @@ pub trait Rssi { } /// State trait for configuring and reading radio states -/// +/// /// Note that drivers will internally configure and read radio states to manage /// radio operations. pub trait State { /// Radio state - type State; + type State: RadioState; /// Radio error type - type Error; + type Error: Debug; /// Set the radio to a specified state fn set_state(&mut self, state: Self::State) -> Result<(), Self::Error>; @@ -190,7 +191,7 @@ pub trait State { fn get_state(&mut self) -> Result; } -pub trait RadioState { +pub trait RadioState: Debug { fn idle() -> Self; fn sleep() -> Self; @@ -200,7 +201,7 @@ pub trait RadioState { /// and should not be interrupted pub trait Busy { /// Radio error type - type Error; + type Error: Debug; /// Indicates the radio is busy in the current state /// (for example, currently transmitting or receiving) @@ -209,33 +210,33 @@ pub trait Busy { /// Interrupts trait allows for reading interrupt state from the device, /// as well as configuring interrupt pins. -/// +/// /// Note that drivers may internally use interrupts and interrupt states /// to manage radio operations. pub trait Interrupts { /// Interrupt object - type Irq; + type Irq: Debug; /// Radio error - type Error; - + type Error: Debug; + /// Fetch any pending interrupts from the device /// If the clear option is set, this will also clear any returned flags fn get_interrupts(&mut self, clear: bool) -> Result; } /// Registers trait provides register level access to the radio device. -/// +/// /// This is generally too low level for use by higher abstractions, however, /// is provided for completeness. pub trait Registers { - type Error; + type Error: Debug; /// Read a register value fn reg_read(&mut self, reg: R) -> Result; /// Write a register value fn reg_write(&mut self, reg: R, value: u8) -> Result<(), Self::Error>; - + /// Update a register value fn reg_update(&mut self, reg: R, mask: u8, value: u8) -> Result { let existing = self.reg_read(reg)?; @@ -245,10 +246,10 @@ pub trait Registers { } } -#[cfg(feature="structopt")] +#[cfg(feature = "structopt")] use crate::std::str::FromStr; -#[cfg(feature="structopt")] +#[cfg(feature = "structopt")] fn duration_from_str(s: &str) -> Result { let d = humantime::Duration::from_str(s)?; Ok(*d) diff --git a/src/mock.rs b/src/mock.rs index 7e4f513..4af862d 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -1,39 +1,42 @@ //! Mock radio driver for application testing -//! +//! //! This provides a generic and specific mock implementation of the radio traits //! to support network and application level testing. -//! +//! //! ## https://github.com/ryankurte/rust-radio //! ## Copyright 2020 Ryan Kurte extern crate std; -use std::vec::Vec; -use std::fmt::Debug; use std::convert::Infallible; +use std::fmt::Debug; +use std::vec::Vec; -use embedded_hal::blocking::delay::DelayUs; +use embedded_hal::delay::blocking::DelayUs; extern crate embedded_hal_mock; use embedded_hal_mock::common::Generic; -use crate::{State, Busy, Transmit, Receive, Power, Channel, Rssi, Interrupts, BasicInfo}; +use crate::{ + BasicInfo, Busy, Channel, Interrupts, Power, RadioState, Receive, ReceiveInfo, Rssi, State, + Transmit, +}; /// Generic mock radio -/// +/// /// Based on `embedded_hal_mock::common::Generic` #[derive(Debug, Clone)] pub struct Radio< - St: Debug + Clone + PartialEq, - Reg: Debug + Clone + PartialEq, - Ch: Debug + Clone + PartialEq, - Inf: Debug + Clone + PartialEq, - Irq: Debug + Clone + PartialEq, - E: Debug + Clone + PartialEq + St: Debug + Clone + PartialEq, + Reg: Debug + Clone + PartialEq, + Ch: Debug + Clone + PartialEq, + Inf: Debug + Clone + PartialEq, + Irq: Debug + Clone + PartialEq, + E: Debug + Clone + PartialEq, > { - inner: Generic> + inner: Generic>, } -impl Radio +impl Radio where St: PartialEq + Debug + Clone, Reg: PartialEq + Debug + Clone, @@ -44,8 +47,8 @@ where { pub fn new(expectations: &[Transaction]) -> Self { let inner = Generic::new(expectations); - - Self{inner} + + Self { inner } } pub fn expect(&mut self, expectations: &[Transaction]) { @@ -64,7 +67,6 @@ where /// Concrete mock radio using mock types pub type MockRadio = Radio; - /// MockState for use with mock radio #[derive(Debug, Clone, PartialEq)] pub enum MockState { @@ -87,7 +89,9 @@ impl crate::RadioState for MockState { /// MockError for use with mock radio #[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "thiserror", derive(thiserror::Error))] pub enum MockError { + #[cfg_attr(feature = "thiserror", error("Timeout"))] Timeout, } @@ -98,10 +102,10 @@ pub struct Transaction { response: Response, } -impl Transaction { +impl Transaction { /// Set the radio state pub fn set_state(state: St, err: Option) -> Self { - Self{ + Self { request: Request::SetState(state), response: err.into(), } @@ -125,7 +129,7 @@ impl Transaction { /// Set a radio register pub fn set_register(reg: Reg, value: u8, err: Option) -> Self { - Self{ + Self { request: Request::SetRegister(reg, value), response: err.into(), } @@ -141,7 +145,7 @@ impl Transaction { /// Set a radio channel pub fn set_channel(ch: Ch, err: Option) -> Self { - Self{ + Self { request: Request::SetChannel(ch), response: err.into(), } @@ -149,7 +153,7 @@ impl Transaction { /// Set radio power pub fn set_power(power: i8, err: Option) -> Self { - Self{ + Self { request: Request::SetPower(power), response: err.into(), } @@ -157,7 +161,7 @@ impl Transaction { /// Start radio transmission pub fn start_transmit(data: Vec, err: Option) -> Self { - Self{ + Self { request: Request::StartTransmit(data), response: err.into(), } @@ -165,7 +169,7 @@ impl Transaction { /// Check for transmission completed pub fn check_transmit(res: Result) -> Self { - Self{ + Self { request: Request::CheckTransmit, response: res.map_or_else(Response::Err, Response::Bool), } @@ -173,7 +177,7 @@ impl Transaction { /// Start radio reception pub fn start_receive(err: Option) -> Self { - Self{ + Self { request: Request::StartReceive, response: err.into(), } @@ -189,7 +193,7 @@ impl Transaction { /// Fetch received data and information pub fn get_received(res: Result<(Vec, Inf), E>) -> Self { - Self{ + Self { request: Request::GetReceived, response: res.map_or_else(Response::Err, |(d, i)| Response::Received(d, i)), } @@ -234,7 +238,7 @@ enum Request { SetChannel(Ch), SetPower(i8), - + StartTransmit(Vec), CheckTransmit, @@ -257,7 +261,7 @@ enum Response { Err(E), } -impl From> for Response { +impl From> for Response { fn from(e: Option) -> Self { match e { Some(v) => Response::Err(v), @@ -266,7 +270,7 @@ impl From> for Response { } } -impl DelayUs for Radio +impl DelayUs for Radio where St: PartialEq + Debug + Clone, Reg: PartialEq + Debug + Clone, @@ -277,7 +281,7 @@ where { type Error = Infallible; - fn try_delay_us(&mut self, ms: u32) -> Result<(), Self::Error> { + fn delay_us(&mut self, ms: u32) -> Result<(), Self::Error> { let n = self.next().expect("no expectation for delay_us call"); assert_eq!(&n.request, &Request::DelayUs(ms)); @@ -286,9 +290,9 @@ where } } -impl State for Radio +impl State for Radio where - St: PartialEq + Debug + Clone, + St: RadioState + PartialEq + Debug + Clone, Reg: PartialEq + Debug + Clone, Ch: PartialEq + Debug + Clone, Inf: PartialEq + Debug + Clone, @@ -299,7 +303,9 @@ where type Error = E; fn set_state(&mut self, state: Self::State) -> Result<(), Self::Error> { - let n = self.next().expect("no expectation for State::set_state call"); + let n = self + .next() + .expect("no expectation for State::set_state call"); assert_eq!(&n.request, &Request::SetState(state.clone())); @@ -315,8 +321,9 @@ where } fn get_state(&mut self) -> Result { - - let n = self.next().expect("no expectation for State::get_state call"); + let n = self + .next() + .expect("no expectation for State::get_state call"); assert_eq!(&n.request, &Request::GetState); @@ -330,10 +337,9 @@ where res } - } -impl Busy for Radio +impl Busy for Radio where St: PartialEq + Debug + Clone, Reg: PartialEq + Debug + Clone, @@ -361,7 +367,7 @@ where } } -impl Channel for Radio +impl Channel for Radio where St: PartialEq + Debug + Clone, Reg: PartialEq + Debug + Clone, @@ -376,7 +382,9 @@ where fn set_channel(&mut self, channel: &Self::Channel) -> Result<(), Self::Error> { debug!("Set channel {:?}", channel); - let n = self.next().expect("no expectation for State::set_channel call"); + let n = self + .next() + .expect("no expectation for State::set_channel call"); assert_eq!(&n.request, &Request::SetChannel(channel.clone())); @@ -388,7 +396,7 @@ where } } -impl Power for Radio +impl Power for Radio where St: PartialEq + Debug + Clone, Reg: PartialEq + Debug + Clone, @@ -402,7 +410,9 @@ where fn set_power(&mut self, power: i8) -> Result<(), Self::Error> { debug!("Set power {:?}", power); - let n = self.next().expect("no expectation for Power::set_power call"); + let n = self + .next() + .expect("no expectation for Power::set_power call"); assert_eq!(&n.request, &Request::SetPower(power)); @@ -414,7 +424,7 @@ where } } -impl Rssi for Radio +impl Rssi for Radio where St: PartialEq + Debug + Clone, Reg: PartialEq + Debug + Clone, @@ -426,7 +436,9 @@ where type Error = E; fn poll_rssi(&mut self) -> Result { - let n = self.next().expect("no expectation for Rssi::poll_rssi call"); + let n = self + .next() + .expect("no expectation for Rssi::poll_rssi call"); assert_eq!(&n.request, &Request::PollRssi); @@ -442,7 +454,7 @@ where } } -impl Interrupts for Radio +impl Interrupts for Radio where St: PartialEq + Debug + Clone, Reg: PartialEq + Debug + Clone, @@ -455,7 +467,9 @@ where type Irq = Irq; fn get_interrupts(&mut self, clear: bool) -> Result { - let n = self.next().expect("no expectation for Transmit::check_transmit call"); + let n = self + .next() + .expect("no expectation for Transmit::check_transmit call"); assert_eq!(&n.request, &Request::GetIrq(clear)); @@ -471,7 +485,7 @@ where } } -impl Transmit for Radio +impl Transmit for Radio where St: PartialEq + Debug + Clone, Reg: PartialEq + Debug + Clone, @@ -483,7 +497,9 @@ where type Error = E; fn start_transmit(&mut self, data: &[u8]) -> Result<(), Self::Error> { - let n = self.next().expect("no expectation for Transmit::start_transmit call"); + let n = self + .next() + .expect("no expectation for Transmit::start_transmit call"); assert_eq!(&n.request, &Request::StartTransmit(data.to_vec())); @@ -499,7 +515,9 @@ where } fn check_transmit(&mut self) -> Result { - let n = self.next().expect("no expectation for Transmit::check_transmit call"); + let n = self + .next() + .expect("no expectation for Transmit::check_transmit call"); assert_eq!(&n.request, &Request::CheckTransmit); @@ -515,12 +533,12 @@ where } } -impl Receive for Radio +impl Receive for Radio where St: PartialEq + Debug + Clone, Reg: PartialEq + Debug + Clone, Ch: PartialEq + Debug + Clone, - Inf: PartialEq + Debug + Clone, + Inf: ReceiveInfo + PartialEq + Debug + Clone, Irq: PartialEq + Debug + Clone, E: PartialEq + Debug + Clone, { @@ -528,7 +546,9 @@ where type Error = E; fn start_receive(&mut self) -> Result<(), Self::Error> { - let n = self.next().expect("no expectation for Receive::start_receive call"); + let n = self + .next() + .expect("no expectation for Receive::start_receive call"); assert_eq!(&n.request, &Request::StartReceive); @@ -544,7 +564,9 @@ where } fn check_receive(&mut self, restart: bool) -> Result { - let n = self.next().expect("no expectation for Receive::check_receive call"); + let n = self + .next() + .expect("no expectation for Receive::check_receive call"); assert_eq!(&n.request, &Request::CheckReceive(restart)); @@ -559,18 +581,19 @@ where res } - fn get_received(&mut self, info: &mut Self::Info, buff: &mut [u8]) -> Result { - let n = self.next().expect("no expectation for Receive::get_received call"); + fn get_received(&mut self, buff: &mut [u8]) -> Result<(usize, Self::Info), Self::Error> { + let n = self + .next() + .expect("no expectation for Receive::get_received call"); assert_eq!(&n.request, &Request::GetReceived); let res = match &n.response { Response::Received(d, i) => { - &mut buff[..d.len()].copy_from_slice(&d); - *info = i.clone(); + buff[..d.len()].copy_from_slice(&d); - Ok(d.len()) - }, + Ok((d.len(), i.clone())) + } Response::Err(e) => Err(e.clone()), _ => unreachable!(), }; @@ -636,7 +659,8 @@ mod test { #[test] fn test_radio_mock_start_transmit() { - let mut radio = MockRadio::new(&[Transaction::start_transmit(vec![0xaa, 0xbb, 0xcc], None)]); + let mut radio = + MockRadio::new(&[Transaction::start_transmit(vec![0xaa, 0xbb, 0xcc], None)]); let _res = radio.start_transmit(&[0xaa, 0xbb, 0xcc]).unwrap(); @@ -645,7 +669,10 @@ mod test { #[test] fn test_radio_mock_check_transmit() { - let mut radio = MockRadio::new(&[Transaction::check_transmit(Ok(false)), Transaction::check_transmit(Ok(true))]); + let mut radio = MockRadio::new(&[ + Transaction::check_transmit(Ok(false)), + Transaction::check_transmit(Ok(true)), + ]); let res = radio.check_transmit().unwrap(); assert_eq!(false, res); @@ -667,12 +694,15 @@ mod test { #[test] fn test_radio_mock_check_receive() { - let mut radio = MockRadio::new(&[Transaction::check_receive(true, Ok(false)), Transaction::check_receive(true, Ok(true))]); + let mut radio = MockRadio::new(&[ + Transaction::check_receive(true, Ok(false)), + Transaction::check_receive(true, Ok(true)), + ]); - let res = radio.check_receive(true, ).unwrap(); + let res = radio.check_receive(true).unwrap(); assert_eq!(false, res); - let res = radio.check_receive(true, ).unwrap(); + let res = radio.check_receive(true).unwrap(); assert_eq!(true, res); radio.done(); @@ -680,17 +710,18 @@ mod test { #[test] fn test_radio_mock_get_received() { - let mut radio = MockRadio::new(&[Transaction::get_received(Ok((vec![0xaa, 0xbb], BasicInfo::new(10, 12))))]); + let mut radio = MockRadio::new(&[Transaction::get_received(Ok(( + vec![0xaa, 0xbb], + BasicInfo::new(10, 12), + )))]); let mut buff = vec![0u8; 3]; - let mut info = BasicInfo::new(0, 0); - let res = radio.get_received(&mut info, &mut buff).unwrap(); + let (n, _i) = radio.get_received(&mut buff).unwrap(); - assert_eq!(2, res); + assert_eq!(2, n); assert_eq!(&buff[..2], &[0xaa, 0xbb]); - radio.done(); } } diff --git a/src/nonblocking.rs b/src/nonblocking.rs index ad08fe8..513aedb 100644 --- a/src/nonblocking.rs +++ b/src/nonblocking.rs @@ -4,26 +4,27 @@ //! ## https://github.com/ryankurte/rust-radio //! ## Copyright 2020 Ryan Kurte -use core::time::Duration; +use core::fmt::Debug; use core::future::Future; use core::marker::PhantomData; -use core::task::{Context, Poll}; use core::pin::Pin; +use core::task::{Context, Poll}; +use core::time::Duration; -use crate::{Transmit, Receive, Power}; +use crate::{Power, Receive, ReceiveInfo, Transmit}; /// Options for async driver calls pub struct AsyncOptions { /// Power option, for transmit operations pub power: Option, - + /// Timeout option for underlying radio operations #[deprecated(note = "Timeouts must (currently) be implemented outside this module")] pub timeout: Option, - + /// Period for polling on operation status with custom wakers pub poll_period: Duration, - + /// Waker function to be called in the `Poll` method pub wake_fn: Option<&'static fn(cx: &mut Context, d: Duration)>, } @@ -31,7 +32,7 @@ pub struct AsyncOptions { impl Default for AsyncOptions { #[allow(deprecated)] fn default() -> Self { - Self { + Self { power: None, timeout: None, poll_period: Duration::from_millis(10), @@ -42,20 +43,25 @@ impl Default for AsyncOptions { /// AsyncError wraps radio errors and provides notification of timeouts #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "thiserror", derive(thiserror::Error))] pub enum AsyncError { + #[cfg_attr(feature = "thiserror", error("Inner: {0}"))] Inner(E), + #[cfg_attr(feature = "thiserror", error("Timeout"))] Timeout, } -impl From for AsyncError { +impl From for AsyncError { fn from(e: E) -> Self { AsyncError::Inner(e) } } /// Async transmit function implemented over `radio::Transmit` and `radio::Power` using the provided `AsyncOptions` -/// -#[cfg_attr(feature = "mock", doc = r##" +/// +#[cfg_attr( + feature = "mock", + doc = r##" ``` extern crate async_std; use async_std::task; @@ -79,16 +85,20 @@ assert_eq!(res, Ok(())); # radio.done(); ``` -"##)] +"## +)] -/// AsyncTransmit function provides an async implementation for transmitting packets +/// AsyncTransmit function provides an async implementation for transmitting packets pub trait AsyncTransmit<'a, E> { - type Output: Future>>; + type Output: Future>>; - fn async_transmit(&'a mut self, data: &'a [u8], tx_options: AsyncOptions) -> Result; + fn async_transmit( + &'a mut self, + data: &'a [u8], + tx_options: AsyncOptions, + ) -> Result; } - /// Future object containing a radio for transmit operation pub struct TransmitFuture<'a, T, E> { radio: &'a mut T, @@ -97,15 +107,18 @@ pub struct TransmitFuture<'a, T, E> { } /// `AsyncTransmit` object for all `Transmit` devices -impl <'a, T, E> AsyncTransmit<'a, E> for T +impl<'a, T, E> AsyncTransmit<'a, E> for T where T: Transmit + Power + 'a, - E: core::fmt::Debug + Unpin, + E: Debug + Unpin, { type Output = TransmitFuture<'a, T, E>; - fn async_transmit(&'a mut self, data: &'a [u8], tx_options: AsyncOptions) -> Result - { + fn async_transmit( + &'a mut self, + data: &'a [u8], + tx_options: AsyncOptions, + ) -> Result { // Set output power if specified if let Some(p) = tx_options.power { self.set_power(p)?; @@ -115,21 +128,20 @@ where self.start_transmit(data)?; // Create transmit future - let f: TransmitFuture<_, E> = TransmitFuture{ - radio: self, + let f: TransmitFuture<_, E> = TransmitFuture { + radio: self, options: tx_options, - _err: PhantomData + _err: PhantomData, }; Ok(f) } } - -impl <'a, T, E> Future for TransmitFuture<'a, T, E> -where +impl<'a, T, E> Future for TransmitFuture<'a, T, E> +where T: Transmit + Power, - E: core::fmt::Debug + Unpin, + E: Debug + Unpin, { type Output = Result<(), AsyncError>; @@ -139,9 +151,9 @@ where // Check for completion if s.radio.check_transmit()? { - return Poll::Ready(Ok(())) + return Poll::Ready(Ok(())); }; - + // Spawn task to re-execute waker if let Some(w) = s.options.wake_fn { w(cx, period); @@ -154,10 +166,11 @@ where } } - /// Async transmit function implemented over `radio::Transmit` and `radio::Power` using the provided `AsyncOptions` -/// -#[cfg_attr(feature = "mock", doc = r##" +/// +#[cfg_attr( + feature = "mock", + doc = r##" ``` extern crate async_std; use async_std::task; @@ -181,68 +194,78 @@ let info = BasicInfo::new(-81, 0); let mut buff = [0u8; 128]; let mut i = BasicInfo::new(0, 0); -let res = task::block_on(async { +let (n, i) = task::block_on(async { // Receive using a future - radio.async_receive(&mut i, &mut buff, AsyncOptions::default())?.await -}); + radio.async_receive(&mut buff, AsyncOptions::default())?.await +})?; -assert_eq!(res, Ok(data.len())); +assert_eq!(n, data.len()); assert_eq!(&buff[..data.len()], &data); # radio.done(); + +Ok::<(), anyhow::Error>(()) ``` -"##)] +"## +)] /// AsyncReceive trait support futures-based polling on receive pub trait AsyncReceive<'a, I, E> { - type Output: Future>>; + type Output: Future>>; - fn async_receive(&'a mut self, info: &'a mut I, buff: &'a mut [u8], rx_options: AsyncOptions) -> Result; + fn async_receive( + &'a mut self, + buff: &'a mut [u8], + rx_options: AsyncOptions, + ) -> Result; } /// Receive future wraps a radio and buffer to provide a pollable future for receiving packets pub struct ReceiveFuture<'a, T, I, E> { radio: &'a mut T, - info: &'a mut I, buff: &'a mut [u8], options: AsyncOptions, + _inf: PhantomData, _err: PhantomData, } - /// Generic implementation of `AsyncReceive` for all `Receive` capable radio devices -impl <'a, T, I, E> AsyncReceive<'a, I, E> for T +impl<'a, T, I, E> AsyncReceive<'a, I, E> for T where T: Receive + 'a, - I: core::fmt::Debug + 'a, - E: core::fmt::Debug + Unpin, + I: ReceiveInfo + Unpin + 'a, + E: Debug + Unpin, { type Output = ReceiveFuture<'a, T, I, E>; - fn async_receive(&'a mut self, info: &'a mut I, buff: &'a mut [u8], rx_options: AsyncOptions) -> Result { + fn async_receive( + &'a mut self, + buff: &'a mut [u8], + rx_options: AsyncOptions, + ) -> Result { // Start receive mode self.start_receive()?; // Create receive future let f: ReceiveFuture<_, I, E> = ReceiveFuture { - radio: self, - info, - buff, + radio: self, + buff, options: rx_options, - _err: PhantomData + _inf: PhantomData, + _err: PhantomData, }; Ok(f) } } -impl <'a, T, I, E> Future for ReceiveFuture<'a, T, I, E> -where +impl<'a, T, I, E> Future for ReceiveFuture<'a, T, I, E> +where T: Receive, - I: core::fmt::Debug, - E: core::fmt::Debug + Unpin, + I: ReceiveInfo + Unpin, + E: Debug + Unpin, { - type Output = Result>; + type Output = Result<(usize, I), AsyncError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let s = self.get_mut(); @@ -250,9 +273,9 @@ where // Check for completion if s.radio.check_receive(true)? { // Retrieve data - let n = s.radio.get_received(s.info, s.buff)?; + let (n, i) = s.radio.get_received(s.buff)?; - return Poll::Ready(Ok(n)); + return Poll::Ready(Ok((n, i))); } // TODO: should timeouts be internal or external? @@ -271,7 +294,7 @@ where /// Task waker using async_std task::spawn with a task::sleep. /// Note that this cannot be relied on for accurate timing -#[cfg(feature="async-std")] +#[cfg(feature = "async-std")] pub fn async_std_task_waker(cx: &mut Context, period: Duration) { let waker = cx.waker().clone(); async_std::task::spawn(async move {