diff options
Diffstat (limited to 'crates/windlass/src')
-rw-r--r-- | crates/windlass/src/dictionary.rs | 28 | ||||
-rw-r--r-- | crates/windlass/src/encoding.rs | 45 | ||||
-rw-r--r-- | crates/windlass/src/lib.rs | 8 | ||||
-rw-r--r-- | crates/windlass/src/macros.rs | 4 | ||||
-rw-r--r-- | crates/windlass/src/mcu.rs | 142 | ||||
-rw-r--r-- | crates/windlass/src/messages.rs | 4 | ||||
-rw-r--r-- | crates/windlass/src/transport/frame.rs | 28 | ||||
-rw-r--r-- | crates/windlass/src/transport/mod.rs | 59 | ||||
-rw-r--r-- | crates/windlass/src/transport/rtt.rs | 5 |
9 files changed, 172 insertions, 151 deletions
diff --git a/crates/windlass/src/dictionary.rs b/crates/windlass/src/dictionary.rs index 270b0e5..2ff9481 100644 --- a/crates/windlass/src/dictionary.rs +++ b/crates/windlass/src/dictionary.rs @@ -71,21 +71,23 @@ impl Dictionary { } /// Extra data returned in the dictionary response - pub fn extra(&self) -> &BTreeMap<String, serde_json::Value> { + pub const fn extra(&self) -> &BTreeMap<String, serde_json::Value> { &self.extra } /// Figure out the actual value of a command tag - fn map_tag(tag: i16) -> Result<u16, DictionaryError> { + #[allow(clippy::cast_sign_loss, clippy::cast_possible_wrap)] + fn map_tag(tag: i16) -> Result<u16, Error> { + // TODO: Hacky, and heap allocation is inefficient let mut buf = vec![]; encode_vlq_int(&mut buf, tag as u32); let v = if buf.len() > 1 { - ((buf[0] as u16) & 0x7F) << 7 | (buf[1] as u16) & 0x7F + ((u16::from(buf[0])) & 0x7F) << 7 | (u16::from(buf[1])) & 0x7F } else { - (buf[0] as u16) & 0x7F + (u16::from(buf[0])) & 0x7F }; if v >= 1 << 14 { - Err(DictionaryError::InvalidCommandTag(v)) + Err(Error::InvalidCommandTag(v)) } else { Ok(v) } @@ -93,7 +95,7 @@ impl Dictionary { } impl TryFrom<RawDictionary> for Dictionary { - type Error = DictionaryError; + type Error = Error; fn try_from(raw: RawDictionary) -> Result<Self, Self::Error> { let mut message_ids = BTreeMap::new(); @@ -101,9 +103,9 @@ impl TryFrom<RawDictionary> for Dictionary { for (cmd, tag) in raw.commands { let mut split = cmd.split(' '); - let name = split.next().ok_or(DictionaryError::EmptyCommand)?; + let name = split.next().ok_or(Error::EmptyCommand)?; let parser = MessageParser::new(name, split) - .map_err(|e| DictionaryError::InvalidCommandFormat(name.to_string(), e))?; + .map_err(|e| Error::InvalidCommandFormat(name.to_string(), e))?; let tag = Self::map_tag(tag)?; message_parsers.insert(tag, parser); message_ids.insert(name.to_string(), tag); @@ -111,9 +113,9 @@ impl TryFrom<RawDictionary> for Dictionary { for (resp, tag) in raw.responses { let mut split = resp.split(' '); - let name = split.next().ok_or(DictionaryError::EmptyCommand)?; + let name = split.next().ok_or(Error::EmptyCommand)?; let parser = MessageParser::new(name, split) - .map_err(|e| DictionaryError::InvalidCommandFormat(name.to_string(), e))?; + .map_err(|e| Error::InvalidCommandFormat(name.to_string(), e))?; let tag = Self::map_tag(tag)?; message_parsers.insert(tag, parser); message_ids.insert(name.to_string(), tag); @@ -121,12 +123,12 @@ impl TryFrom<RawDictionary> for Dictionary { for (msg, tag) in raw.output { let parser = MessageParser::new_output(&msg) - .map_err(|e| DictionaryError::InvalidCommandFormat(msg.to_string(), e))?; + .map_err(|e| Error::InvalidCommandFormat(msg.to_string(), e))?; let tag = Self::map_tag(tag)?; message_parsers.insert(tag, parser); } - Ok(Dictionary { + Ok(Self { message_ids, message_parsers, config: raw.config, @@ -189,7 +191,7 @@ impl EnumDef { /// Error encountered when getting dictionary from microcontroller #[derive(thiserror::Error, Debug)] -pub enum DictionaryError { +pub enum Error { /// Found an empty command #[error("empty command found")] EmptyCommand, diff --git a/crates/windlass/src/encoding.rs b/crates/windlass/src/encoding.rs index 27238c0..6b50c3c 100644 --- a/crates/windlass/src/encoding.rs +++ b/crates/windlass/src/encoding.rs @@ -8,15 +8,20 @@ pub(crate) fn crc16(buf: &[u8]) -> u16 { for b in buf { let b = *b ^ ((crc & 0xFF) as u8); let b = b ^ (b << 4); - let b16 = b as u16; + let b16 = u16::from(b); crc = (b16 << 8 | crc >> 8) ^ (b16 >> 4) ^ (b16 << 3); } crc } /// Encode the given integer as a [VLQ](https://www.klipper3d.org/Protocol.html#variable-length-quantities), pushing it to the back of `output`. +#[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)] pub(crate) fn encode_vlq_int(output: &mut Vec<u8>, v: u32) { + // This is ok, as we're just doing bit fiddling and the signedness + // will be figured out at the other side again. let sv = v as i32; + + // Likewise for these casts if !(-(1 << 26)..(3 << 26)).contains(&sv) { output.push(((sv >> 28) & 0x7F) as u8 | 0x80); } @@ -33,14 +38,16 @@ pub(crate) fn encode_vlq_int(output: &mut Vec<u8>, v: u32) { } /// Parse a [VLQ](https://www.klipper3d.org/Protocol.html#variable-length-quantities) from the top of `data`. +#[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)] pub(crate) fn parse_vlq_int(data: &mut &[u8]) -> Result<u32, MessageDecodeError> { - let mut c = next_byte(data)? as u32; + // Casting is fine here, as we're just doing bit fiddling + let mut c = u32::from(next_byte(data)?); let mut v = c & 0x7F; if (c & 0x60) == 0x60 { v |= (-0x20i32) as u32; } while c & 0x80 != 0 { - c = next_byte(data)? as u32; + c = u32::from(next_byte(data)?); v = (v << 7) | (c & 0x7F); } @@ -97,7 +104,7 @@ pub enum FieldType { impl FieldType { /// Skip over a field of this type at the top of `input`. - pub(crate) fn skip(&self, input: &mut &[u8]) -> Result<(), MessageDecodeError> { + pub(crate) fn skip(self, input: &mut &[u8]) -> Result<(), MessageDecodeError> { match self { Self::U32 => <u32 as Readable>::skip(input), Self::I32 => <i32 as Readable>::skip(input), @@ -110,7 +117,7 @@ impl FieldType { } /// Read a field of this type from the top of `input`. - pub(crate) fn read(&self, input: &mut &[u8]) -> Result<FieldValue, MessageDecodeError> { + pub(crate) fn read(self, input: &mut &[u8]) -> Result<FieldValue, MessageDecodeError> { Ok(match self { Self::U32 => FieldValue::U32(<u32 as Readable>::read(input)?), Self::I32 => FieldValue::I32(<i32 as Readable>::read(input)?), @@ -131,13 +138,13 @@ impl FieldType { "%hi" => Ok(Self::I16), "%c" => Ok(Self::U8), "%s" => Ok(Self::String), - "%*s" => Ok(Self::ByteArray), - "%.*s" => Ok(Self::ByteArray), + "%*s" | "%.*s" => Ok(Self::ByteArray), s => Err(MessageSkipperError::InvalidFormatFieldType(s.to_string())), } } /// Deserialise the next field type from a printf style declaration, also returning the rest of the string. + #[allow(clippy::option_if_let_else)] pub(crate) fn from_format(s: &str) -> Result<(Self, &str), MessageSkipperError> { if let Some(rest) = s.strip_prefix("%u") { Ok((Self::U32, rest)) @@ -174,13 +181,13 @@ pub enum FieldValue { impl Display for FieldValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::U32(v) => write!(f, "{}", v), - Self::I32(v) => write!(f, "{}", v), - Self::U16(v) => write!(f, "{}", v), - Self::I16(v) => write!(f, "{}", v), - Self::U8(v) => write!(f, "{}", v), - Self::String(v) => write!(f, "{}", v), - Self::ByteArray(v) => write!(f, "{:?}", v), + Self::U32(v) => write!(f, "{v}"), + Self::I32(v) => write!(f, "{v}"), + Self::U16(v) => write!(f, "{v}"), + Self::I16(v) => write!(f, "{v}"), + Self::U8(v) => write!(f, "{v}"), + Self::String(v) => write!(f, "{v}"), + Self::ByteArray(v) => write!(f, "{v:?}"), } } } @@ -196,12 +203,18 @@ macro_rules! int_readwrite { ( $type:tt, $field_type:expr ) => { impl Readable<'_> for $type { fn read(data: &mut &[u8]) -> Result<Self, MessageDecodeError> { + #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] parse_vlq_int(data).map(|v| v as $type) } } impl Writable for $type { fn write(&self, output: &mut Vec<u8>) { + #[allow( + clippy::cast_possible_wrap, + clippy::cast_lossless, + clippy::cast_sign_loss + )] encode_vlq_int(output, *self as u32) } } @@ -235,7 +248,7 @@ impl Readable<'_> for bool { impl Writable for bool { fn write(&self, output: &mut Vec<u8>) { - encode_vlq_int(output, u32::from(*self)) + encode_vlq_int(output, u32::from(*self)); } } @@ -277,6 +290,7 @@ impl<'de> Readable<'de> for &'de [u8] { impl Writable for &[u8] { fn write(&self, output: &mut Vec<u8>) { + #[allow(clippy::cast_possible_truncation)] encode_vlq_int(output, self.len() as u32); output.extend_from_slice(self); } @@ -321,6 +335,7 @@ impl<'de> Readable<'de> for &'de str { impl Writable for &str { fn write(&self, output: &mut Vec<u8>) { let bytes = self.as_bytes(); + #[allow(clippy::cast_possible_truncation)] encode_vlq_int(output, bytes.len() as u32); output.extend_from_slice(bytes); } diff --git a/crates/windlass/src/lib.rs b/crates/windlass/src/lib.rs index 66a7674..2b27f79 100644 --- a/crates/windlass/src/lib.rs +++ b/crates/windlass/src/lib.rs @@ -1,4 +1,10 @@ //! Windlass is an implementation of the host side of the Klipper protocol. +#![deny(clippy::all, clippy::pedantic, clippy::nursery)] +#![allow( + clippy::must_use_candidate, + clippy::missing_errors_doc, + clippy::future_not_send +)] #[macro_use] #[doc(hidden)] @@ -10,4 +16,4 @@ pub mod messages; mod mcu; mod transport; -pub use mcu::{McuConnection, McuConnectionError}; +pub use mcu::{Connection, ConnectionError}; diff --git a/crates/windlass/src/macros.rs b/crates/windlass/src/macros.rs index f17c66d..c118ada 100644 --- a/crates/windlass/src/macros.rs +++ b/crates/windlass/src/macros.rs @@ -8,7 +8,7 @@ /// /// A struct with the given name will be defined, with relevant interfaces. The message name and /// arguments will be matched to the MCU at runtime. The struct name has no restrictions, but it is -/// recommended to pick a SnakeCased version of the command name. +/// recommended to pick a `SnakeCased` version of the command name. /// /// Optionally an `id` can be directly specified. Generally this is not needed. When not specified, /// it will be automatically inferred at runtime and matched to the dictionary retrieved from the @@ -53,7 +53,7 @@ macro_rules! mcu_command { /// mcu_reply!(<struct name>, "<reply name>"( = <id>) [, arg: type, ..]); /// ``` /// -/// For more information on the various fields, see the documentation for [mcu_command]. +/// For more information on the various fields, see the documentation for [`mcu_command`]. /// /// # Examples /// diff --git a/crates/windlass/src/mcu.rs b/crates/windlass/src/mcu.rs index c2ca54a..8bc725f 100644 --- a/crates/windlass/src/mcu.rs +++ b/crates/windlass/src/mcu.rs @@ -20,8 +20,8 @@ use tracing::{debug, error, trace}; use crate::messages::{format_command_args, EncodedMessage, Message, WithOid, WithoutOid}; use crate::transport::{Transport, TransportReceiver}; use crate::{ - dictionary::{Dictionary, DictionaryError, RawDictionary}, - transport::TransportError, + dictionary::{self, Dictionary, RawDictionary}, + transport::Error, }; use crate::{ encoding::{parse_vlq_int, MessageDecodeError}, @@ -38,7 +38,7 @@ mcu_reply!( /// MCU Connection Errors #[derive(thiserror::Error, Debug)] -pub enum McuConnectionError { +pub enum ConnectionError { /// Encoding a message was attempted but a command with a matching name doesn't exist in the /// dictionary. #[error("unknown message ID for command '{0}'")] @@ -51,7 +51,7 @@ pub enum McuConnectionError { DictionaryFetch(Box<dyn std::error::Error + Send + Sync>), /// An error was encountered while parsing the dictionary #[error("dictionary issue: {0}")] - Dictionary(#[from] DictionaryError), + Dictionary(#[from] dictionary::Error), /// Received an unknown command from the MCU #[error("unknown command {0}")] UnknownCommand(u16), @@ -60,7 +60,7 @@ pub enum McuConnectionError { CommandMismatch(&'static str, String, String), /// There was a transport-level issue #[error("transport error: {0}")] - Transport(#[from] TransportError), + Transport(#[from] Error), } #[derive(thiserror::Error, Debug)] @@ -68,7 +68,7 @@ pub enum SendReceiveError { #[error("timeout")] Timeout, #[error("mcu connection error: {0}")] - McuConnection(#[from] McuConnectionError), + McuConnection(#[from] ConnectionError), } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] @@ -94,7 +94,7 @@ impl std::fmt::Debug for Handlers { let h = self .handlers .try_lock() - .map(|h| h.keys().cloned().collect::<BTreeSet<_>>()) + .map(|h| h.keys().copied().collect::<BTreeSet<_>>()) .unwrap_or_default(); f.debug_struct("Handlers") .field("handlers", &h) @@ -158,11 +158,11 @@ struct McuConnectionInner { } impl McuConnectionInner { - async fn receive_loop(&self, mut inbox: TransportReceiver) -> Result<(), McuConnectionError> { + async fn receive_loop(&self, mut inbox: TransportReceiver) -> Result<(), ConnectionError> { while let Some(frame) = inbox.recv().await { let frame = match frame { Ok(frame) => frame, - Err(e) => return Err(McuConnectionError::Transport(e)), + Err(e) => return Err(ConnectionError::Transport(e)), }; let frame = &mut frame.as_slice(); self.parse_frame(frame)?; @@ -170,9 +170,9 @@ impl McuConnectionInner { Ok(()) } - fn parse_frame(&self, frame: &mut &[u8]) -> Result<(), McuConnectionError> { + fn parse_frame(&self, frame: &mut &[u8]) -> Result<(), ConnectionError> { while !frame.is_empty() { - let cmd = parse_vlq_int(frame)? as u16; + let cmd = u16::try_from(parse_vlq_int(frame)?).expect("command handler overflow"); // If a raw handler is registered for this, call it if self.handlers.call(cmd, None, frame) { @@ -206,7 +206,7 @@ impl McuConnectionInner { ); } } else { - return Err(McuConnectionError::UnknownCommand(cmd)); + return Err(ConnectionError::UnknownCommand(cmd)); } } else { break; @@ -220,7 +220,7 @@ impl McuConnectionInner { /// /// #[derive(Debug)] -pub struct McuConnection { +pub struct Connection { inner: Arc<McuConnectionInner>, transport: Transport, _receiver: JoinHandle<()>, @@ -229,22 +229,24 @@ pub struct McuConnection { #[derive(Debug)] enum ExitCode { - Waiting(oneshot::Receiver<Result<(), McuConnectionError>>), - Exited(Result<(), McuConnectionError>), + Waiting(oneshot::Receiver<Result<(), ConnectionError>>), + Exited(Result<(), ConnectionError>), } -impl McuConnection { +impl Connection { /// Connect to an MCU /// /// Attempts to connect to a MCU on the given data stream interface. The MCU is contacted and - /// the dictionary is loaded and applied. The returned [McuConnection] is fully ready to + /// the dictionary is loaded and applied. The returned [`McuConnection`] is fully ready to /// communicate with the MCU. - pub async fn connect<R, W>(rdr: R, wr: W) -> Result<Self, McuConnectionError> + /// # Panics + /// Panics if the MCU attempts to send an identify payload with size > [`u32::MAX`] + pub async fn connect<R, W>(rdr: R, wr: W) -> Result<Self, ConnectionError> where R: AsyncBufRead + Send + Unpin + 'static, W: AsyncWrite + Send + Unpin + 'static, { - let (transport, inbox) = Transport::connect(rdr, wr).await; + let (transport, inbox) = Transport::connect(rdr, wr); let (exit_tx, exit_rx) = oneshot::channel(); let inner = Arc::new(McuConnectionInner { @@ -258,7 +260,7 @@ impl McuConnection { let _ = exit_tx.send(receiver_inner.receive_loop(inbox).await); }); - let conn = McuConnection { + let conn = Self { inner, transport, _receiver: receiver, @@ -269,16 +271,16 @@ impl McuConnection { let identify_start = Instant::now(); loop { let data = conn - .send_receive( - Identify::encode(identify_data.len() as u32, 40), - IdentifyResponse, - ) + .send_receive::<_, IdentifyResponse>(Identify::encode( + u32::try_from(identify_data.len()).expect("identify data too long"), + 40, + )) .await; let mut data = match data { Ok(data) => data, Err(e) => { if identify_start.elapsed() > Duration::from_secs(10) { - return Err(McuConnectionError::DictionaryFetch(Box::new(e))); + return Err(ConnectionError::DictionaryFetch(Box::new(e))); } continue; } @@ -294,20 +296,19 @@ impl McuConnection { let mut buf = Vec::new(); decoder .read_to_end(&mut buf) - .map_err(|err| McuConnectionError::DictionaryFetch(Box::new(err)))?; + .map_err(|err| ConnectionError::DictionaryFetch(Box::new(err)))?; let raw_dict: RawDictionary = serde_json::from_slice(&buf) - .map_err(|err| McuConnectionError::DictionaryFetch(Box::new(err)))?; - let dict = Dictionary::try_from(raw_dict).map_err(McuConnectionError::Dictionary)?; + .map_err(|err| ConnectionError::DictionaryFetch(Box::new(err)))?; + let dict = Dictionary::try_from(raw_dict).map_err(ConnectionError::Dictionary)?; debug!(dictionary = ?dict, "MCU dictionary"); - conn.inner - .dictionary - .set(dict) - .expect("Dictionary already set"); + let Ok(()) = conn.inner.dictionary.set(dict) else { + unreachable!() + }; Ok(conn) } - fn verify_command_matches<C: Message>(&self) -> Result<(), McuConnectionError> { + fn verify_command_matches<C: Message>(&self) -> Result<(), ConnectionError> { // Special handling for identify/identify_response if C::get_id(None) == Some(0) || C::get_id(None) == Some(1) { return Ok(()); @@ -327,7 +328,7 @@ impl McuConnection { // tested before then. let dictionary = self.inner.dictionary.get().unwrap(); let id = C::get_id(Some(dictionary)) - .ok_or_else(|| McuConnectionError::UnknownMessageId(C::get_name()))?; + .ok_or_else(|| ConnectionError::UnknownMessageId(C::get_name()))?; // Must exist because we know the tag let parser = dictionary.message_parser(id).unwrap(); @@ -335,7 +336,7 @@ impl McuConnection { let local_fields = C::fields().into_iter(); if !remote_fields.eq(local_fields) { - return Err(McuConnectionError::CommandMismatch( + return Err(ConnectionError::CommandMismatch( C::get_name(), format_command_args(parser.fields.iter().map(|(s, t)| (s.as_str(), *t))), format_command_args(C::fields().into_iter()), @@ -354,9 +355,9 @@ impl McuConnection { fn encode_command<C: Message>( &self, command: EncodedMessage<C>, - ) -> Result<FrontTrimmableBuffer, McuConnectionError> { + ) -> Result<FrontTrimmableBuffer, ConnectionError> { let id = C::get_id(self.inner.dictionary.get()) - .ok_or_else(|| McuConnectionError::UnknownMessageId(C::get_name()))?; + .ok_or_else(|| ConnectionError::UnknownMessageId(C::get_name()))?; let mut payload = command.payload; if id >= 0x80 { payload.content[0] = ((id >> 7) & 0x7F) as u8 | 0x80; @@ -368,24 +369,20 @@ impl McuConnection { } /// Sends a command to the MCU - pub async fn send<C: Message>( - &self, - command: EncodedMessage<C>, - ) -> Result<(), McuConnectionError> { + pub fn send<C: Message>(&self, command: EncodedMessage<C>) -> Result<(), ConnectionError> { let cmd = self.encode_command(command)?; self.transport .send(cmd.as_slice()) - .map_err(|e| McuConnectionError::Transport(TransportError::Transmitter(e))) + .map_err(|e| ConnectionError::Transport(Error::Transmitter(e))) } async fn send_receive_impl<C: Message, R: Message>( &self, command: EncodedMessage<C>, - reply: R, oid: Option<u8>, ) -> Result<R::PodOwned, SendReceiveError> { struct RespHandler<R: Message>( - Option<oneshot::Sender<Result<R::PodOwned, McuConnectionError>>>, + Option<oneshot::Sender<Result<R::PodOwned, ConnectionError>>>, ); impl<R: Message> Handler for RespHandler<R> { @@ -393,7 +390,7 @@ impl McuConnection { if let Some(tx) = self.0.take() { let _ = match R::decode(data) { Ok(msg) => tx.send(Ok(msg.into())), - Err(e) => tx.send(Err(McuConnectionError::DecodingError(e))), + Err(e) => tx.send(Err(ConnectionError::DecodingError(e))), }; } HandlerResult::Deregister @@ -406,13 +403,13 @@ impl McuConnection { self.verify_command_matches::<R>()?; let (tx, mut rx) = tokio::sync::oneshot::channel::<Result<R::PodOwned, _>>(); - self.register_raw_response(reply, oid, Box::new(RespHandler::<R>(Some(tx))))?; + self.register_raw_response::<R>(oid, Box::new(RespHandler::<R>(Some(tx))))?; let mut retry_delay = 0.01; for _retry in 0..=5 { self.transport .send(cmd.as_slice()) - .map_err(|e| McuConnectionError::Transport(TransportError::Transmitter(e)))?; + .map_err(|e| ConnectionError::Transport(Error::Transmitter(e)))?; let sleep = tokio::time::sleep(Duration::from_secs_f32(retry_delay)); tokio::pin!(sleep); @@ -425,7 +422,7 @@ impl McuConnection { Err(_) => Err(SendReceiveError::Timeout) }; }, - _ = &mut sleep => {}, + () = &mut sleep => {}, } retry_delay *= 2.0; @@ -441,10 +438,9 @@ impl McuConnection { pub async fn send_receive_oid<C: Message, R: Message + WithOid>( &self, command: EncodedMessage<C>, - reply: R, oid: u8, ) -> Result<R::PodOwned, SendReceiveError> { - self.send_receive_impl(command, reply, Some(oid)).await + self.send_receive_impl::<C, R>(command, Some(oid)).await } /// Sends a message to the MCU, and await a reply @@ -454,27 +450,24 @@ impl McuConnection { pub async fn send_receive<C: Message, R: Message + WithoutOid>( &self, command: EncodedMessage<C>, - reply: R, ) -> Result<R::PodOwned, SendReceiveError> { - self.send_receive_impl(command, reply, None).await + self.send_receive_impl::<C, R>(command, None).await } fn register_raw_response<R: Message>( &self, - _reply: R, oid: Option<u8>, handler: Box<dyn Handler>, - ) -> Result<RegisteredResponseHandle, McuConnectionError> { + ) -> Result<RegisteredResponseHandle, ConnectionError> { let id = R::get_id(self.inner.dictionary.get()) - .ok_or_else(|| McuConnectionError::UnknownMessageId(R::get_name()))?; + .ok_or_else(|| ConnectionError::UnknownMessageId(R::get_name()))?; Ok(self.inner.handlers.register(id, oid, handler)) } fn register_response_impl<R: Message>( &self, - reply: R, oid: Option<u8>, - ) -> Result<UnboundedReceiver<R::PodOwned>, McuConnectionError> { + ) -> Result<UnboundedReceiver<R::PodOwned>, ConnectionError> { struct RespHandler<R: Message>(UnboundedSender<R::PodOwned>, Option<oneshot::Sender<()>>); impl<R: Message> Drop for RespHandler<R> { @@ -489,7 +482,7 @@ impl McuConnection { .expect("Parser should already have assured this could parse") .into(); match self.0.send(msg) { - Ok(_) => HandlerResult::Continue, + Ok(()) => HandlerResult::Continue, Err(_) => HandlerResult::Deregister, } } @@ -500,11 +493,8 @@ impl McuConnection { let (tx, rx) = unbounded_channel(); let tx_closed = tx.clone(); let (closer_tx, closer_rx) = oneshot::channel(); - let uniq = self.register_raw_response( - reply, - oid, - Box::new(RespHandler::<R>(tx, Some(closer_tx))), - )?; + let uniq = + self.register_raw_response::<R>(oid, Box::new(RespHandler::<R>(tx, Some(closer_tx))))?; // Safe because register_raw_response already verified this let id = R::get_id(self.inner.dictionary.get()).unwrap(); @@ -512,7 +502,7 @@ impl McuConnection { let inner = self.inner.clone(); spawn(async move { select! { - _ = tx_closed.closed() => {}, + () = tx_closed.closed() => {}, _ = closer_rx => {}, } inner.handlers.remove_handler(id, oid, Some(uniq)); @@ -528,10 +518,9 @@ impl McuConnection { /// This version works with replies that have an `oid` field. pub fn register_response_oid<R: Message + WithOid>( &self, - reply: R, oid: u8, - ) -> Result<UnboundedReceiver<R::PodOwned>, McuConnectionError> { - self.register_response_impl(reply, Some(oid)) + ) -> Result<UnboundedReceiver<R::PodOwned>, ConnectionError> { + self.register_response_impl::<R>(Some(oid)) } /// Registers a subscriber for a reply message @@ -541,14 +530,16 @@ impl McuConnection { /// This version works with replies that do not have an `oid` field. pub fn register_response<R: Message + WithoutOid>( &self, - reply: R, - ) -> Result<UnboundedReceiver<R::PodOwned>, McuConnectionError> { - self.register_response_impl(reply, None) + ) -> Result<UnboundedReceiver<R::PodOwned>, ConnectionError> { + self.register_response_impl::<R>(None) } /// Returns a reference the dictionary the MCU returned during initial handshake pub fn dictionary(&self) -> &Dictionary { - self.inner.dictionary.get().unwrap() + self.inner + .dictionary + .get() + .unwrap_or_else(|| unreachable!()) } /// Closes the transport and ends all subscriptions. Returns when the transport is closed. @@ -557,16 +548,13 @@ impl McuConnection { } /// Waits for the connection to close, returning the error that closed it if any. - pub async fn closed(&mut self) -> &Result<(), McuConnectionError> { + pub async fn closed(&mut self) -> &Result<(), ConnectionError> { if let ExitCode::Waiting(chan) = &mut self.exit_code { - self.exit_code = ExitCode::Exited(match chan.await { - Ok(r) => r, - Err(_) => Ok(()), - }); + self.exit_code = ExitCode::Exited(chan.await.unwrap_or_else(|_| Ok(()))); } match self.exit_code { ExitCode::Exited(ref val) => val, - _ => unreachable!(), + ExitCode::Waiting(_) => unreachable!(), } } } diff --git a/crates/windlass/src/messages.rs b/crates/windlass/src/messages.rs index f08a3ca..bf2ca67 100644 --- a/crates/windlass/src/messages.rs +++ b/crates/windlass/src/messages.rs @@ -20,7 +20,7 @@ impl MessageParser { pub(crate) fn new<'a>( name: &str, parts: impl Iterator<Item = &'a str>, - ) -> Result<MessageParser, MessageSkipperError> { + ) -> Result<Self, MessageSkipperError> { let mut fields = vec![]; for part in parts { let (arg, ty) = part @@ -38,7 +38,7 @@ impl MessageParser { } /// Create a parser for a message type with the given printf-style specifier - pub(crate) fn new_output(msg: &str) -> Result<MessageParser, MessageSkipperError> { + pub(crate) fn new_output(msg: &str) -> Result<Self, MessageSkipperError> { let mut fields = vec![]; let mut parts = vec![]; diff --git a/crates/windlass/src/transport/frame.rs b/crates/windlass/src/transport/frame.rs index d33a2d8..4b0c321 100644 --- a/crates/windlass/src/transport/frame.rs +++ b/crates/windlass/src/transport/frame.rs @@ -14,7 +14,8 @@ use crate::{ /// A link-level frame. See [klipper docs](https://www.klipper3d.org/Protocol.html#message-blocks) #[derive(Debug)] -pub(crate) struct ReceivedFrame { +#[allow(clippy::module_name_repetitions)] +pub struct ReceivedFrame { pub receive_time: Instant, pub sequence: u8, pub payload: Vec<u8>, @@ -22,12 +23,12 @@ pub(crate) struct ReceivedFrame { /// Buffers and reads link-level frames. See [`ReceivedFrame`]. #[derive(Debug, Default)] -pub struct FrameReader { +pub struct Reader { buf: VecDeque<ReceivedFrame>, partial: PartialFrameState, } -impl FrameReader { +impl Reader { /// Process new data from `b` pub fn receive_data(&mut self, mut b: &[u8]) { while !b.is_empty() { @@ -78,7 +79,7 @@ impl PartialFrameState { b = &b[idx + 1..]; } } - PartialFrameState::Synced => { + Self::Synced => { // Attempt to start a new frame let len = b[0] as usize; if !(MESSAGE_LENGTH_MIN..=MESSAGE_LENGTH_MAX).contains(&len) { @@ -97,7 +98,7 @@ impl PartialFrameState { b = &b[1..]; } - PartialFrameState::Receiving { len, so_far, .. } => { + Self::Receiving { len, so_far, .. } => { // Continue to receive data for frame let len = *len; let take = len - so_far.len(); @@ -118,8 +119,8 @@ impl PartialFrameState { } let actual_crc = crc16(so_far); - let frame_crc = (so_far[len - MESSAGE_TRAILER_CRC] as u16) << 8 - | (so_far[len - MESSAGE_TRAILER_CRC + 1] as u16); + let frame_crc = (u16::from(so_far[len - MESSAGE_TRAILER_CRC])) << 8 + | (u16::from(so_far[len - MESSAGE_TRAILER_CRC + 1])); if frame_crc != actual_crc { *self = Self::Unsynced; continue; @@ -153,6 +154,7 @@ impl PartialFrameState { /// A frame to be sent to the MCU #[derive(Debug, Clone)] +#[allow(clippy::module_name_repetitions)] pub struct SentFrame { /// When the frame was first sent pub sent_at: Instant, @@ -172,10 +174,16 @@ impl SentFrame { } let mut payload = Vec::with_capacity(len); - payload.push(len as u8); - payload.push(MESSAGE_DEST | ((sequence as u8) & MESSAGE_SEQ_MASK)); + let Ok(len) = u8::try_from(len) else { + unreachable!() + }; + payload.push(len); + payload.push( + MESSAGE_DEST + | ((u8::try_from(sequence).expect("sequence number overflow")) & MESSAGE_SEQ_MASK), + ); payload.extend_from_slice(data); - let crc = crc16(&payload[0..len - MESSAGE_TRAILER_SIZE]); + let crc = crc16(&payload[0..len as usize - MESSAGE_TRAILER_SIZE]); payload.push(((crc >> 8) & 0xFF) as u8); payload.push((crc & 0xFF) as u8); payload.push(MESSAGE_VALUE_SYNC); diff --git a/crates/windlass/src/transport/mod.rs b/crates/windlass/src/transport/mod.rs index eecb98d..8a24b8a 100644 --- a/crates/windlass/src/transport/mod.rs +++ b/crates/windlass/src/transport/mod.rs @@ -1,4 +1,4 @@ -use frame::{FrameReader, MessageEncodeError, ReceivedFrame, SentFrame}; +use frame::{MessageEncodeError, Reader, ReceivedFrame, SentFrame}; use rtt::RttState; use std::collections::VecDeque; use tokio::io::AsyncReadExt; @@ -14,16 +14,16 @@ use tokio_util::{bytes::BytesMut, sync::CancellationToken}; mod frame; mod rtt; -pub(crate) const MESSAGE_HEADER_SIZE: usize = 2; -pub(crate) const MESSAGE_TRAILER_SIZE: usize = 3; -pub(crate) const MESSAGE_LENGTH_MIN: usize = MESSAGE_HEADER_SIZE + MESSAGE_TRAILER_SIZE; -pub(crate) const MESSAGE_LENGTH_MAX: usize = 64; -pub(crate) const MESSAGE_LENGTH_PAYLOAD_MAX: usize = MESSAGE_LENGTH_MAX - MESSAGE_LENGTH_MIN; -pub(crate) const MESSAGE_POSITION_SEQ: usize = 1; -pub(crate) const MESSAGE_TRAILER_CRC: usize = 3; -pub(crate) const MESSAGE_VALUE_SYNC: u8 = 0x7E; -pub(crate) const MESSAGE_DEST: u8 = 0x10; -pub(crate) const MESSAGE_SEQ_MASK: u8 = 0x0F; +pub const MESSAGE_HEADER_SIZE: usize = 2; +pub const MESSAGE_TRAILER_SIZE: usize = 3; +pub const MESSAGE_LENGTH_MIN: usize = MESSAGE_HEADER_SIZE + MESSAGE_TRAILER_SIZE; +pub const MESSAGE_LENGTH_MAX: usize = 64; +pub const MESSAGE_LENGTH_PAYLOAD_MAX: usize = MESSAGE_LENGTH_MAX - MESSAGE_LENGTH_MIN; +pub const MESSAGE_POSITION_SEQ: usize = 1; +pub const MESSAGE_TRAILER_CRC: usize = 3; +pub const MESSAGE_VALUE_SYNC: u8 = 0x7E; +pub const MESSAGE_DEST: u8 = 0x10; +pub const MESSAGE_SEQ_MASK: u8 = 0x0F; /// Wrapper around a connection to a klipper firmware MCU, which deals with /// retransmission, flow control, etc. @@ -46,13 +46,14 @@ enum TransportCommand { Exit, } -pub(crate) type TransportReceiver = UnboundedReceiver<Result<Vec<u8>, TransportError>>; +#[allow(clippy::module_name_repetitions)] +pub type TransportReceiver = UnboundedReceiver<Result<Vec<u8>, Error>>; impl Transport { - pub(crate) async fn connect( + pub(crate) fn connect( rdr: impl AsyncBufRead + Unpin + Send + 'static, wr: impl AsyncWrite + Unpin + Send + 'static, - ) -> (Transport, TransportReceiver) { + ) -> (Self, TransportReceiver) { let (data_send, data_recv) = unbounded_channel(); let (cmd_send, cmd_recv) = unbounded_channel(); @@ -65,7 +66,7 @@ impl Transport { }); ( - Transport { + Self { task_inner, cmd_send, }, @@ -86,7 +87,7 @@ impl Transport { } #[derive(thiserror::Error, Debug)] -pub enum TransportError { +pub enum Error { #[error("message encoding failed: {0}")] MessageEncode(#[from] MessageEncodeError), @@ -94,17 +95,17 @@ pub enum TransportError { Transmitter(#[from] TransmitterError), #[error("io error: {0}")] - IOError(#[from] std::io::Error), + IO(#[from] std::io::Error), } /// State for the task which deals with transport state #[derive(Debug)] struct TransportState<R, W> { - frdr: FrameReader, + frdr: Reader, rdr: R, wr: W, - data_send: UnboundedSender<Result<Vec<u8>, TransportError>>, + data_send: UnboundedSender<Result<Vec<u8>, Error>>, cmd_recv: UnboundedReceiver<TransportCommand>, cancel: CancellationToken, @@ -128,12 +129,12 @@ impl<R: AsyncBufRead + Unpin, W: AsyncWrite + Unpin> TransportState<R, W> { fn new( rdr: R, wr: W, - data_send: UnboundedSender<Result<Vec<u8>, TransportError>>, + data_send: UnboundedSender<Result<Vec<u8>, Error>>, cmd_recv: UnboundedReceiver<TransportCommand>, cancel: CancellationToken, ) -> Self { Self { - frdr: FrameReader::default(), + frdr: Reader::default(), rdr, wr, data_send, @@ -156,7 +157,7 @@ impl<R: AsyncBufRead + Unpin, W: AsyncWrite + Unpin> TransportState<R, W> { } } - async fn run(&mut self) -> Result<(), TransportError> { + async fn run(&mut self) -> Result<(), Error> { let mut buf = BytesMut::with_capacity(MESSAGE_LENGTH_MAX); loop { if self.retransmit_now { @@ -210,7 +211,7 @@ impl<R: AsyncBufRead + Unpin, W: AsyncWrite + Unpin> TransportState<R, W> { // Timeout for when we are able to send again } - _ = self.cancel.cancelled() => { + () = self.cancel.cancelled() => { break; }, } @@ -223,9 +224,9 @@ impl<R: AsyncBufRead + Unpin, W: AsyncWrite + Unpin> TransportState<R, W> { let rseq = self.receive_sequence; // wrap-around logic(?) - let mut sequence = (rseq & !(MESSAGE_SEQ_MASK as u64)) | (frame.sequence as u64); + let mut sequence = (rseq & !u64::from(MESSAGE_SEQ_MASK)) | u64::from(frame.sequence); if sequence < rseq { - sequence += (MESSAGE_SEQ_MASK as u64) + 1; + sequence += u64::from(MESSAGE_SEQ_MASK) + 1; } // Frame acknowledges some messages @@ -284,7 +285,7 @@ impl<R: AsyncBufRead + Unpin, W: AsyncWrite + Unpin> TransportState<R, W> { } /// Send as many more frames as possible from [`self.pending_messages`] - async fn send_more_frames(&mut self) -> Result<(), TransportError> { + async fn send_more_frames(&mut self) -> Result<(), Error> { while self.can_send() && !self.pending_messages.is_empty() { self.send_new_frame().await?; } @@ -293,7 +294,7 @@ impl<R: AsyncBufRead + Unpin, W: AsyncWrite + Unpin> TransportState<R, W> { } /// Send a single new frame from [`self.pending_messages`] - async fn send_new_frame(&mut self) -> Result<(), TransportError> { + async fn send_new_frame(&mut self) -> Result<(), Error> { let mut buf = Vec::new(); while let Some(next) = self.pending_messages.front() { if !buf.is_empty() && buf.len() + next.len() <= MESSAGE_LENGTH_PAYLOAD_MAX { @@ -316,7 +317,7 @@ impl<R: AsyncBufRead + Unpin, W: AsyncWrite + Unpin> TransportState<R, W> { } /// Retransmit all inflight messages - async fn retransmit_pending(&mut self) -> Result<(), TransportError> { + async fn retransmit_pending(&mut self) -> Result<(), Error> { let len: usize = self .inflight_messages .iter() @@ -325,7 +326,7 @@ impl<R: AsyncBufRead + Unpin, W: AsyncWrite + Unpin> TransportState<R, W> { let mut buf = Vec::with_capacity(1 + len); buf.push(MESSAGE_VALUE_SYNC); let now = Instant::now(); - for msg in self.inflight_messages.iter_mut() { + for msg in &mut self.inflight_messages { buf.extend_from_slice(&msg.payload); msg.is_retransmit = true; msg.sent_at = now; diff --git a/crates/windlass/src/transport/rtt.rs b/crates/windlass/src/transport/rtt.rs index 69841f6..bb45b9e 100644 --- a/crates/windlass/src/transport/rtt.rs +++ b/crates/windlass/src/transport/rtt.rs @@ -5,6 +5,7 @@ const MAX_RTO: f32 = 5.000; /// State for estimating the round trip time of the connection #[derive(Debug)] +#[allow(clippy::module_name_repetitions)] pub struct RttState { srtt: f32, rttvar: f32, @@ -34,8 +35,8 @@ impl RttState { self.rttvar = r / 2.0; self.srtt = r * 10.0; // Klipper uses this, we'll copy it } else { - self.rttvar = (3.0 * self.rttvar + (self.srtt - r).abs()) / 4.0; - self.srtt = (7.0 * self.srtt + r) / 8.0; + self.rttvar = 3.0f32.mul_add(self.rttvar, (self.srtt - r).abs()); + self.srtt = 7.0f32.mul_add(self.srtt, r); } let rttvar4 = (self.rttvar * 4.0).max(0.001); self.rto = (self.srtt + rttvar4).clamp(MIN_RTO, MAX_RTO); |